diff --git a/WhirlyGlobe.podspec b/WhirlyGlobe.podspec index 1502b87c72..df7dfa023f 100644 --- a/WhirlyGlobe.podspec +++ b/WhirlyGlobe.podspec @@ -26,25 +26,31 @@ Pod::Spec.new do |s| s.source = { :git => 'https://github.com/mousebird/WhirlyGlobe.git', :branch => 'develop' } - s.compiler_flags = '-D__USE_SDL_GLES__ -D__IPHONEOS__ -DSQLITE_OPEN_READONLY -DHAVE_PTHREAD=1 -DUNORDERED=1 -DLASZIPDLL_EXPORTS=1' - s.xcconfig = { "HEADER_SEARCH_PATHS" => " \"$(PODS_ROOT)/WhirlyGlobe/common/local_libs/eigen/\" \"$(PODS_ROOT)/KissXML/KissXML/\" \"${PODS_ROOT}/WhirlyGlobe/common/local_libs/nanopb/\" \"${PODS_ROOT}/WhirlyGlobe/common/local_libs/clipper\" \"${PODS_ROOT}/WhirlyGlobe/common/local_libs/lodepng\" \"$(SDKROOT)/usr/include/libxml2\" \"${PODS_ROOT}/WhirlyGlobe/common/local_libs/glues/include/\" \"$(PODS_ROOT)/WhirlyGlobe/ios/library/WhirlyGlobe-MaplyComponent/include/private/\" \"$(PODS_ROOT)/WhirlyGlobe/ios/library/WhirlyGlobe-MaplyComponent/include/\" \"$(PODS_ROOT)/WhirlyGlobe/ios/library/WhirlyGlobe-MaplyComponent/include/vector_tiles/\" " } + s.compiler_flags = '-D__USE_SDL_GLES__ -D__IPHONEOS__ -DSQLITE_OPEN_READONLY -DHAVE_PTHREAD=1 -DUNORDERED=1 ' + s.xcconfig = { "HEADER_SEARCH_PATHS" => " \"$(SDKROOT)/usr/include/libxml2\" \"$(PODS_ROOT)/KissXML/KissXML/\" \"$(PODS_ROOT)/WhirlyGlobe/common/local_libs/eigen/\" \"${PODS_ROOT}/WhirlyGlobe/common/local_libs/nanopb/\" \"${PODS_ROOT}/WhirlyGlobe/common/local_libs/clipper\" \"${PODS_ROOT}/WhirlyGlobe/common/local_libs/lodepng\" \"${PODS_ROOT}/WhirlyGlobe/common/local_libs/glues/include/\" \"$(PODS_ROOT)/WhirlyGlobe/common/local_libs/GeographicLib/include/\" \"$(PODS_ROOT)/WhirlyGlobe/ios/library/WhirlyGlobe-MaplyComponent/include/private/\" \"$(PODS_ROOT)/WhirlyGlobe/ios/library/WhirlyGlobe-MaplyComponent/include/\" \"$(PODS_ROOT)/WhirlyGlobe/ios/library/WhirlyGlobe-MaplyComponent/include/vector_tiles/\" " } s.default_subspec = 'MaplyComponent' s.subspec 'locallibs' do |ll| - ll.source_files = 'common/local_libs/aaplus/**/*.{h,cpp}', + ll.source_files = + 'common/local_libs/aaplus/**/*.{h,cpp}', 'common/local_libs/clipper/cpp/*.{cpp,hpp}', 'common/local_libs/shapefile/**/*.{c,h}', 'common/local_libs/lodepng/*.{cpp,h}', - 'common/local_libs/nanopb/*.{c,h}' - ll.preserve_paths = 'common/local_libs/laszip/include/laszip/*.h', 'common/local_libs/laszip/src/*.hpp', - 'common/local_libs/eigen/Eigen/*', 'common/local_libs/eigen/Eigen/**/*.h', + 'common/local_libs/nanopb/*.{c,h}', + 'common/local_libs/GeographicLib/src/*.cpp' + ll.preserve_paths = + 'common/local_libs/eigen/Eigen/*', + 'common/local_libs/eigen/Eigen/**/*.h', 'common/local_libs/lodepng/*.h', - 'common/local_libs/nanopb/*.h' - ll.private_header_files = 'common/local_libs/aaplus/**/*.h', + 'common/local_libs/nanopb/*.h', + 'common/local_libs/GeographicLib/include/GeographicLib/*.{h,hpp}' + ll.private_header_files = + 'common/local_libs/aaplus/**/*.h', 'common/local_libs/clipper/cpp/*.hpp', 'common/local_libs/shapefile/**/*.h', - 'common/local_libs/nanopb/*.h' + 'common/local_libs/nanopb/*.h', + 'common/local_libs/GeographicLib/include/GeographicLib/*.{h,hpp}' end s.subspec 'glues' do |gl| @@ -54,9 +60,21 @@ Pod::Spec.new do |s| end s.subspec 'MaplyComponent' do |mc| - mc.source_files = 'common/WhirlyGlobeLib/include/*.h', 'common/WhirlyGlobeLib/src/*.{c,cpp}', 'ios/library/WhirlyGlobeLib/src/*.{mm,m,cpp,metal}', 'ios/library/WhirlyGlobeLib/include/*.h', 'ios/library/WhirlyGlobe-MaplyComponent/include/**/*.h', 'ios/library/WhirlyGlobe-MaplyComponent/src/**/*.{mm,m,cpp,metal}' - mc.public_header_files = 'ios/library/WhirlyGlobe-MaplyComponent/include/*.h', "ios/library/WhirlyGlobe-MaplyComponent/include/vector_tiles/*.h" - mc.private_header_files = 'ios/library/WhirlyGlobeLib/include/*.h', 'ios/**/vector_tile.pb.h', 'ios/**/MaplyBridge.h' + mc.source_files = + 'common/WhirlyGlobeLib/include/*.h', + 'common/WhirlyGlobeLib/src/*.{c,cpp}', + 'ios/library/WhirlyGlobeLib/src/*.{mm,m,cpp,metal}', + 'ios/library/WhirlyGlobeLib/include/*.h', + 'ios/library/WhirlyGlobe-MaplyComponent/include/**/*.h', + 'ios/library/WhirlyGlobe-MaplyComponent/src/**/*.{mm,m,cpp,metal}' + mc.public_header_files = + 'ios/library/WhirlyGlobe-MaplyComponent/include/*.h', + "ios/library/WhirlyGlobe-MaplyComponent/include/vector_tiles/*.h", + 'ios/library/WhirlyGlobeLib/include/GeographicLib.h' # That we have to name it here means it probably belongs somewhere else... + mc.private_header_files = + 'ios/library/WhirlyGlobeLib/include/*.h', + 'ios/**/vector_tile.pb.h', + 'ios/**/MaplyBridge.h' mc.dependency 'WhirlyGlobe/locallibs' mc.dependency 'WhirlyGlobe/glues' mc.dependency 'SMCalloutView' diff --git a/android/apps/AutoTesterAndroid/app/build.gradle b/android/apps/AutoTesterAndroid/app/build.gradle index 6aa7ac25c4..bfa924f14f 100644 --- a/android/apps/AutoTesterAndroid/app/build.gradle +++ b/android/apps/AutoTesterAndroid/app/build.gradle @@ -4,8 +4,11 @@ apply plugin: 'kotlin-android-extensions' android { compileOptions { - sourceCompatibility 1.8 - targetCompatibility 1.8 + sourceCompatibility JavaVersion.VERSION_1_8 + targetCompatibility JavaVersion.VERSION_1_8 + } + kotlinOptions { + jvmTarget = JavaVersion.VERSION_1_8 } signingConfigs { config { @@ -18,7 +21,7 @@ android { compileSdkVersion 28 defaultConfig { applicationId "com.mousebirdconsulting.autotester" - minSdkVersion 19 + minSdkVersion 21 targetSdkVersion 28 versionCode 1 versionName "1.0" diff --git a/android/apps/AutoTesterAndroid/app/src/debug/AndroidManifest.xml b/android/apps/AutoTesterAndroid/app/src/debug/AndroidManifest.xml new file mode 100644 index 0000000000..7c0c4e13fe --- /dev/null +++ b/android/apps/AutoTesterAndroid/app/src/debug/AndroidManifest.xml @@ -0,0 +1,6 @@ + + + + diff --git a/android/apps/AutoTesterAndroid/app/src/main/AndroidManifest.xml b/android/apps/AutoTesterAndroid/app/src/main/AndroidManifest.xml index fffcf1ce0b..83b0cac4fa 100644 --- a/android/apps/AutoTesterAndroid/app/src/main/AndroidManifest.xml +++ b/android/apps/AutoTesterAndroid/app/src/main/AndroidManifest.xml @@ -2,6 +2,8 @@ + + © MapTiler © OpenStreetMap contributors", - "type":"vector" - }, - "openmaptiles":{ - "type":"vector", - "url":"https://api.maptiler.com/tiles/v3/tiles.json?key=MapTilerKey" - } - }, - "version":8, - "zoom":1 -} - diff --git a/android/apps/AutoTesterAndroid/app/src/main/assets/maptiler_basic.json b/android/apps/AutoTesterAndroid/app/src/main/assets/maptiler_basic.json new file mode 120000 index 0000000000..2e12a2da9d --- /dev/null +++ b/android/apps/AutoTesterAndroid/app/src/main/assets/maptiler_basic.json @@ -0,0 +1 @@ +../../../../../../../resources/vectors/styles/maptiler_basic.json \ No newline at end of file diff --git a/android/apps/AutoTesterAndroid/app/src/main/assets/maptiler_expr_test.json b/android/apps/AutoTesterAndroid/app/src/main/assets/maptiler_expr_test.json new file mode 120000 index 0000000000..1770193a11 --- /dev/null +++ b/android/apps/AutoTesterAndroid/app/src/main/assets/maptiler_expr_test.json @@ -0,0 +1 @@ +../../../../../../../resources/vectors/styles/maptiler_expr_test.json \ No newline at end of file diff --git a/android/apps/AutoTesterAndroid/app/src/main/assets/maptiler_hybrid_satellite.json b/android/apps/AutoTesterAndroid/app/src/main/assets/maptiler_hybrid_satellite.json new file mode 120000 index 0000000000..b6f5a4c3e6 --- /dev/null +++ b/android/apps/AutoTesterAndroid/app/src/main/assets/maptiler_hybrid_satellite.json @@ -0,0 +1 @@ +../../../../../../../resources/vectors/styles/maptiler_hybrid_satellite.json \ No newline at end of file diff --git a/android/apps/AutoTesterAndroid/app/src/main/assets/maptiler_streets.json b/android/apps/AutoTesterAndroid/app/src/main/assets/maptiler_streets.json deleted file mode 100644 index 1e75f5ac0b..0000000000 --- a/android/apps/AutoTesterAndroid/app/src/main/assets/maptiler_streets.json +++ /dev/null @@ -1,6630 +0,0 @@ -{ - "bearing":0, - "center":[ - 0, - 5.684341886080802e-14 - ], - "glyphs":"https://api.maptiler.com/fonts/{fontstack}/{range}.pbf?key=MapTilerKey", - "id":"streets", - "layers":[ - { - "id":"background", - "layout":{ - "visibility":"visible" - }, - "paint":{ - "background-color":{ - "stops":[ - [ - 6, - "rgba(252, 247, 229, 1)" - ], - [ - 10, - "rgba(252, 247, 229, 1)" - ], - [ - 14, - "rgba(246, 241, 229, 1)" - ], - [ - 15, - "rgba(246, 241, 229, 1)" - ] - ] - } - }, - "type":"background" - }, - { - "filter":[ - "all", - [ - "==", - "class", - "crop" - ] - ], - "id":"landcover_cropland", - "layout":{ - "visibility":"visible" - }, - "paint":{ - "fill-color":"rgba(243, 235, 195, 1)", - "fill-opacity":{ - "base":1, - "stops":[ - [ - 4, - 1 - ], - [ - 8, - 0 - ] - ] - } - }, - "source":"openmaptiles", - "source-layer":"globallandcover", - "type":"fill" - }, - { - "filter":[ - "all", - [ - "==", - "class", - "grass" - ] - ], - "id":"landcover_grassland", - "paint":{ - "fill-color":"rgba(226, 236, 197, 1)", - "fill-opacity":{ - "base":1, - "stops":[ - [ - 4, - 1 - ], - [ - 8, - 0 - ] - ] - } - }, - "source":"openmaptiles", - "source-layer":"globallandcover", - "type":"fill" - }, - { - "filter":[ - "all", - [ - "==", - "class", - "scrub" - ] - ], - "id":"landcover_scrubland", - "paint":{ - "fill-color":"rgba(199, 230, 179, 1)", - "fill-opacity":{ - "base":1, - "stops":[ - [ - 4, - 1 - ], - [ - 8, - 0 - ] - ] - } - }, - "source":"openmaptiles", - "source-layer":"globallandcover", - "type":"fill" - }, - { - "filter":[ - "all", - [ - "==", - "class", - "tree" - ] - ], - "id":"landcover_treeland", - "paint":{ - "fill-color":"rgba(190, 222, 173, 1)", - "fill-opacity":{ - "base":1, - "stops":[ - [ - 4, - 1 - ], - [ - 8, - 0 - ] - ] - } - }, - "source":"openmaptiles", - "source-layer":"globallandcover", - "type":"fill" - }, - { - "filter":[ - "all", - [ - "==", - "class", - "forest" - ] - ], - "id":"landcover_forestland", - "paint":{ - "fill-color":"rgba(171, 217, 170, 1)", - "fill-opacity":{ - "base":1, - "stops":[ - [ - 4, - 1 - ], - [ - 8, - 0 - ] - ] - } - }, - "source":"openmaptiles", - "source-layer":"globallandcover", - "type":"fill" - }, - { - "filter":[ - "all", - [ - "==", - "class", - "snow" - ] - ], - "id":"landcover_snowland", - "layout":{ - "visibility":"visible" - }, - "paint":{ - "fill-color":"hsl(0, 0%, 100%)", - "fill-opacity":{ - "base":1, - "stops":[ - [ - 4, - 1 - ], - [ - 8, - 0 - ] - ] - } - }, - "source":"openmaptiles", - "source-layer":"globallandcover", - "type":"fill" - }, - { - "filter":[ - "all" - ], - "id":"park_outline", - "layout":{ - "visibility":"visible" - }, - "paint":{ - "line-color":"rgba(228, 241, 215, 1)", - "line-dasharray":[ - 1, - 1.5 - ], - "line-opacity":1 - }, - "source":"openmaptiles", - "source-layer":"park", - "type":"line" - }, - { - "filter":[ - "all", - [ - "in", - "class", - "residential", - "suburbs", - "neighbourhood" - ] - ], - "id":"landuse_residential", - "layout":{ - "visibility":"visible" - }, - "maxzoom":24, - "metadata":{ - - }, - "paint":{ - "fill-color":{ - "base":1, - "stops":[ - [ - 9, - "rgba(233, 227, 210, 0.80)" - ], - [ - 12, - "rgba(233, 227, 210, 0.55)" - ], - [ - 16, - "rgba(233, 227, 210, 0.35)" - ] - ] - } - }, - "source":"openmaptiles", - "source-layer":"landuse", - "type":"fill" - }, - { - "filter":[ - "all", - [ - "==", - "class", - "wood" - ] - ], - "id":"landcover_wood", - "layout":{ - "visibility":"visible" - }, - "metadata":{ - - }, - "paint":{ - "fill-antialias":false, - "fill-color":"hsla(98, 61%, 72%, 0.7)", - "fill-opacity":0.4 - }, - "source":"openmaptiles", - "source-layer":"landcover", - "type":"fill" - }, - { - "filter":[ - "all", - [ - "==", - "class", - "grass" - ] - ], - "id":"landcover_grass", - "layout":{ - "visibility":"visible" - }, - "metadata":{ - - }, - "paint":{ - "fill-antialias":false, - "fill-color":"rgba(224, 232, 201, 1)", - "fill-opacity":0.3 - }, - "source":"openmaptiles", - "source-layer":"landcover", - "type":"fill" - }, - { - "filter":[ - "==", - "class", - "cemetery" - ], - "id":"landuse_cemetery", - "layout":{ - "visibility":"visible" - }, - "metadata":{ - - }, - "paint":{ - "fill-color":"hsl(75, 37%, 81%)" - }, - "source":"openmaptiles", - "source-layer":"landuse", - "type":"fill" - }, - { - "filter":[ - "==", - "class", - "hospital" - ], - "id":"landuse_hospital", - "layout":{ - "visibility":"visible" - }, - "metadata":{ - - }, - "paint":{ - "fill-color":"rgba(249, 225, 220, 1)" - }, - "source":"openmaptiles", - "source-layer":"landuse", - "type":"fill" - }, - { - "filter":[ - "==", - "class", - "school" - ], - "id":"landuse_school", - "layout":{ - "visibility":"visible" - }, - "metadata":{ - - }, - "paint":{ - "fill-color":"rgb(236,238,204)" - }, - "source":"openmaptiles", - "source-layer":"landuse", - "type":"fill" - }, - { - "filter":[ - "all", - [ - "in", - "class", - "stadium", - "pitch", - "track" - ] - ], - "id":"landuse_stadium", - "layout":{ - "visibility":"visible" - }, - "metadata":{ - - }, - "paint":{ - "fill-color":"rgb(236,238,204)" - }, - "source":"openmaptiles", - "source-layer":"landuse", - "type":"fill" - }, - { - "filter":[ - "all", - [ - "in", - "class", - "garages" - ] - ], - "id":"landuse_garages", - "layout":{ - "visibility":"visible" - }, - "metadata":{ - - }, - "paint":{ - "fill-color":"rgba(236, 236, 236, 1)" - }, - "source":"openmaptiles", - "source-layer":"landuse", - "type":"fill" - }, - { - "filter":[ - "all", - [ - "in", - "class", - "dam" - ] - ], - "id":"landuse_dam", - "layout":{ - "visibility":"visible" - }, - "metadata":{ - - }, - "paint":{ - "fill-color":"rgba(209, 212, 190, 1)" - }, - "source":"openmaptiles", - "source-layer":"landuse", - "type":"fill" - }, - { - "filter":[ - "all", - [ - "==", - "brunnel", - "tunnel" - ] - ], - "id":"waterway_tunnel", - "layout":{ - "line-cap":"round", - "visibility":"visible" - }, - "minzoom":14, - "paint":{ - "line-color":"#a0c8f0", - "line-dasharray":[ - 2, - 4 - ], - "line-width":{ - "base":1.3, - "stops":[ - [ - 13, - 0.5 - ], - [ - 20, - 6 - ] - ] - } - }, - "source":"openmaptiles", - "source-layer":"waterway", - "type":"line" - }, - { - "filter":[ - "all", - [ - "==", - "class", - "river" - ], - [ - "!=", - "brunnel", - "tunnel" - ], - [ - "!=", - "intermittent", - 1 - ] - ], - "id":"waterway_river", - "layout":{ - "line-cap":"round", - "visibility":"visible" - }, - "metadata":{ - - }, - "paint":{ - "line-color":"#a0c8f0", - "line-width":{ - "base":1.2, - "stops":[ - [ - 11, - 0.5 - ], - [ - 20, - 6 - ] - ] - } - }, - "source":"openmaptiles", - "source-layer":"waterway", - "type":"line" - }, - { - "filter":[ - "all", - [ - "==", - "class", - "river" - ], - [ - "!=", - "brunnel", - "tunnel" - ], - [ - "==", - "intermittent", - 1 - ] - ], - "id":"waterway_river_intermittent", - "layout":{ - "line-cap":"round" - }, - "metadata":{ - - }, - "paint":{ - "line-color":"#a0c8f0", - "line-dasharray":[ - 3, - 2 - ], - "line-width":{ - "base":1.2, - "stops":[ - [ - 11, - 0.5 - ], - [ - 20, - 6 - ] - ] - } - }, - "source":"openmaptiles", - "source-layer":"waterway", - "type":"line" - }, - { - "filter":[ - "all", - [ - "!=", - "class", - "river" - ], - [ - "!=", - "brunnel", - "tunnel" - ], - [ - "!=", - "intermittent", - 1 - ] - ], - "id":"waterway_other", - "layout":{ - "line-cap":"round", - "visibility":"visible" - }, - "metadata":{ - - }, - "paint":{ - "line-color":"#a0c8f0", - "line-width":{ - "base":1.3, - "stops":[ - [ - 13, - 0.5 - ], - [ - 20, - 6 - ] - ] - } - }, - "source":"openmaptiles", - "source-layer":"waterway", - "type":"line" - }, - { - "filter":[ - "all", - [ - "!=", - "class", - "river" - ], - [ - "!=", - "brunnel", - "tunnel" - ], - [ - "==", - "intermittent", - 1 - ] - ], - "id":"waterway_other_intermittent", - "layout":{ - "line-cap":"round", - "visibility":"visible" - }, - "metadata":{ - - }, - "paint":{ - "line-color":"#a0c8f0", - "line-dasharray":[ - 4, - 3 - ], - "line-width":{ - "base":1.3, - "stops":[ - [ - 13, - 0.5 - ], - [ - 20, - 6 - ] - ] - } - }, - "source":"openmaptiles", - "source-layer":"waterway", - "type":"line" - }, - { - "filter":[ - "all", - [ - "!=", - "intermittent", - 1 - ], - [ - "!=", - "brunnel", - "tunnel" - ] - ], - "id":"water", - "layout":{ - "visibility":"visible" - }, - "metadata":{ - - }, - "paint":{ - "fill-color":"rgba(134, 204, 250, 1)" - }, - "source":"openmaptiles", - "source-layer":"water", - "type":"fill" - }, - { - "filter":[ - "all", - [ - "==", - "intermittent", - 1 - ] - ], - "id":"water_intermittent", - "layout":{ - "visibility":"visible" - }, - "metadata":{ - - }, - "paint":{ - "fill-color":"rgba(172, 218, 251, 1)", - "fill-opacity":0.85 - }, - "source":"openmaptiles", - "source-layer":"water", - "type":"fill" - }, - { - "filter":[ - "all", - [ - "in", - "class", - "sand" - ] - ], - "id":"landcover_sand", - "layout":{ - "visibility":"visible" - }, - "metadata":{ - - }, - "paint":{ - "fill-antialias":false, - "fill-color":"#f2e9ac", - "fill-opacity":1 - }, - "source":"openmaptiles", - "source-layer":"landcover", - "type":"fill" - }, - { - "filter":[ - "all", - [ - "in", - "class", - "sand" - ] - ], - "id":"landcover_sand_outline", - "layout":{ - "visibility":"visible" - }, - "metadata":{ - - }, - "paint":{ - "line-color":"#f2e9ac", - "line-width":2 - }, - "source":"openmaptiles", - "source-layer":"landcover", - "type":"line" - }, - { - "filter":[ - "==", - "$type", - "Polygon" - ], - "id":"aeroway_fill", - "layout":{ - "visibility":"visible" - }, - "metadata":{ - - }, - "minzoom":11, - "paint":{ - "fill-color":"rgba(229, 228, 224, 1)", - "fill-opacity":0.7 - }, - "source":"openmaptiles", - "source-layer":"aeroway", - "type":"fill" - }, - { - "filter":[ - "all", - [ - "==", - "$type", - "LineString" - ], - [ - "==", - "class", - "runway" - ] - ], - "id":"aeroway_runway", - "layout":{ - "visibility":"visible" - }, - "metadata":{ - - }, - "minzoom":11, - "paint":{ - "line-color":"#f0ede9", - "line-width":{ - "base":1.2, - "stops":[ - [ - 11, - 3 - ], - [ - 20, - 16 - ] - ] - } - }, - "source":"openmaptiles", - "source-layer":"aeroway", - "type":"line" - }, - { - "filter":[ - "all", - [ - "==", - "$type", - "LineString" - ], - [ - "==", - "class", - "taxiway" - ] - ], - "id":"aeroway_taxiway", - "layout":{ - "visibility":"visible" - }, - "metadata":{ - - }, - "minzoom":11, - "paint":{ - "line-color":"#f0ede9", - "line-width":{ - "base":1.2, - "stops":[ - [ - 11, - 0.5 - ], - [ - 20, - 6 - ] - ] - } - }, - "source":"openmaptiles", - "source-layer":"aeroway", - "type":"line" - }, - { - "filter":[ - "all", - [ - "in", - "class", - "ferry" - ] - ], - "id":"ferry", - "layout":{ - "line-join":"round", - "visibility":"visible" - }, - "paint":{ - "line-color":"rgba(108, 159, 182, 1)", - "line-dasharray":[ - 2, - 2 - ], - "line-width":1.1 - }, - "source":"openmaptiles", - "source-layer":"transportation", - "type":"line" - }, - { - "filter":[ - "all", - [ - "==", - "class", - "motorway" - ], - [ - "==", - "ramp", - 1 - ], - [ - "==", - "brunnel", - "tunnel" - ] - ], - "id":"tunnel_motorway_link_casing", - "layout":{ - "line-join":"round", - "visibility":"visible" - }, - "metadata":{ - - }, - "paint":{ - "line-color":"#e9ac77", - "line-dasharray":[ - 0.5, - 0.25 - ], - "line-opacity":1, - "line-width":{ - "base":1.2, - "stops":[ - [ - 12, - 1 - ], - [ - 13, - 3 - ], - [ - 14, - 4 - ], - [ - 20, - 15 - ] - ] - } - }, - "source":"openmaptiles", - "source-layer":"transportation", - "type":"line" - }, - { - "filter":[ - "all", - [ - "==", - "brunnel", - "tunnel" - ], - [ - "in", - "class", - "service", - "track" - ] - ], - "id":"tunnel_service_track_casing", - "layout":{ - "line-join":"round", - "visibility":"visible" - }, - "metadata":{ - - }, - "paint":{ - "line-color":"#cfcdca", - "line-dasharray":[ - 0.5, - 0.25 - ], - "line-width":{ - "base":1.2, - "stops":[ - [ - 15, - 1 - ], - [ - 16, - 4 - ], - [ - 20, - 11 - ] - ] - } - }, - "source":"openmaptiles", - "source-layer":"transportation", - "type":"line" - }, - { - "filter":[ - "all", - [ - "==", - "ramp", - "1" - ], - [ - "==", - "brunnel", - "tunnel" - ], - [ - ">", - "layer", - 0 - ] - ], - "id":"tunnel_link_casing", - "layout":{ - "line-join":"round", - "visibility":"visible" - }, - "metadata":{ - - }, - "paint":{ - "line-color":"#e9ac77", - "line-opacity":1, - "line-width":{ - "base":1.2, - "stops":[ - [ - 12, - 1 - ], - [ - 13, - 3 - ], - [ - 14, - 4 - ], - [ - 20, - 15 - ] - ] - } - }, - "source":"openmaptiles", - "source-layer":"transportation", - "type":"line" - }, - { - "filter":[ - "all", - [ - "==", - "brunnel", - "tunnel" - ], - [ - "in", - "class", - "street", - "street_limited" - ] - ], - "id":"tunnel_street_casing", - "layout":{ - "line-join":"round", - "visibility":"visible" - }, - "metadata":{ - - }, - "paint":{ - "line-color":"#cfcdca", - "line-opacity":{ - "stops":[ - [ - 12, - 0 - ], - [ - 12.5, - 1 - ] - ] - }, - "line-width":{ - "base":1.2, - "stops":[ - [ - 12, - 0.5 - ], - [ - 13, - 1 - ], - [ - 14, - 4 - ], - [ - 20, - 15 - ] - ] - } - }, - "source":"openmaptiles", - "source-layer":"transportation", - "type":"line" - }, - { - "filter":[ - "all", - [ - "==", - "brunnel", - "tunnel" - ], - [ - "in", - "class", - "secondary", - "tertiary" - ] - ], - "id":"tunnel_secondary_tertiary_casing", - "layout":{ - "line-join":"round", - "visibility":"visible" - }, - "metadata":{ - - }, - "paint":{ - "line-color":"rgba(195, 189, 187, 1)", - "line-dasharray":[ - 0.5, - 0.25 - ], - "line-opacity":1, - "line-width":{ - "base":1.2, - "stops":[ - [ - 8, - 1.5 - ], - [ - 20, - 17 - ] - ] - } - }, - "source":"openmaptiles", - "source-layer":"transportation", - "type":"line" - }, - { - "filter":[ - "all", - [ - "==", - "brunnel", - "tunnel" - ], - [ - "in", - "class", - "primary", - "trunk" - ] - ], - "id":"tunnel_trunk_primary_casing", - "layout":{ - "line-join":"round", - "visibility":"visible" - }, - "metadata":{ - - }, - "paint":{ - "line-color":"#e9ac77", - "line-dasharray":[ - 0.5, - 0.25 - ], - "line-width":{ - "base":1.2, - "stops":[ - [ - 5, - 0.4 - ], - [ - 6, - 0.7 - ], - [ - 7, - 1.75 - ], - [ - 20, - 22 - ] - ] - } - }, - "source":"openmaptiles", - "source-layer":"transportation", - "type":"line" - }, - { - "filter":[ - "all", - [ - "==", - "class", - "motorway" - ], - [ - "==", - "brunnel", - "tunnel" - ] - ], - "id":"tunnel_motorway_casing", - "layout":{ - "line-join":"round", - "visibility":"visible" - }, - "metadata":{ - - }, - "paint":{ - "line-color":"#e9ac77", - "line-dasharray":[ - 0.5, - 0.25 - ], - "line-width":{ - "base":1.2, - "stops":[ - [ - 5, - 0.4 - ], - [ - 6, - 0.7 - ], - [ - 7, - 1.75 - ], - [ - 20, - 22 - ] - ] - } - }, - "source":"openmaptiles", - "source-layer":"transportation", - "type":"line" - }, - { - "filter":[ - "all", - [ - "==", - "$type", - "LineString" - ], - [ - "==", - "brunnel", - "tunnel" - ], - [ - "in", - "class", - "path", - "pedestrian" - ] - ], - "id":"tunnel_path_pedestrian", - "layout":{ - "visibility":"visible" - }, - "metadata":{ - - }, - "paint":{ - "line-color":"rgba(204, 196, 176, 0.45)", - "line-dasharray":[ - 1, - 0.75 - ], - "line-width":{ - "base":1.2, - "stops":[ - [ - 14, - 0.5 - ], - [ - 20, - 6 - ] - ] - } - }, - "source":"openmaptiles", - "source-layer":"transportation", - "type":"line" - }, - { - "filter":[ - "all", - [ - "==", - "class", - "motorway_link" - ], - [ - "==", - "ramp", - 1 - ], - [ - "==", - "brunnel", - "tunnel" - ] - ], - "id":"tunnel_motorway_link", - "layout":{ - "line-join":"round", - "visibility":"visible" - }, - "metadata":{ - - }, - "paint":{ - "line-color":"#fc8", - "line-width":{ - "base":1.2, - "stops":[ - [ - 12.5, - 0 - ], - [ - 13, - 1.5 - ], - [ - 14, - 2.5 - ], - [ - 20, - 11.5 - ] - ] - } - }, - "source":"openmaptiles", - "source-layer":"transportation", - "type":"line" - }, - { - "filter":[ - "all", - [ - "==", - "brunnel", - "tunnel" - ], - [ - "in", - "class", - "service", - "track" - ] - ], - "id":"tunnel_service_track", - "layout":{ - "line-join":"round", - "visibility":"visible" - }, - "metadata":{ - - }, - "paint":{ - "line-color":"#fff", - "line-width":{ - "base":1.2, - "stops":[ - [ - 15.5, - 0 - ], - [ - 16, - 2 - ], - [ - 20, - 7.5 - ] - ] - } - }, - "source":"openmaptiles", - "source-layer":"transportation", - "type":"line" - }, - { - "filter":[ - "all", - [ - "==", - "brunnel", - "tunnel" - ], - [ - "in", - "class", - "service_construction", - "track_construction" - ] - ], - "id":"tunnel_service_track_construction", - "layout":{ - "line-join":"round", - "visibility":"visible" - }, - "metadata":{ - - }, - "paint":{ - "line-color":"#fff", - "line-dasharray":[ - 2, - 2 - ], - "line-width":{ - "base":1.2, - "stops":[ - [ - 15.5, - 0 - ], - [ - 16, - 2 - ], - [ - 20, - 7.5 - ] - ] - } - }, - "source":"openmaptiles", - "source-layer":"transportation", - "type":"line" - }, - { - "filter":[ - "all", - [ - "==", - "ramp", - "1" - ], - [ - "==", - "brunnel", - "tunnel" - ], - [ - ">", - "layer", - 0 - ] - ], - "id":"tunnel_link", - "layout":{ - "line-join":"round", - "visibility":"visible" - }, - "metadata":{ - - }, - "paint":{ - "line-color":"#fff4c6", - "line-width":{ - "base":1.2, - "stops":[ - [ - 12.5, - 0 - ], - [ - 13, - 1.5 - ], - [ - 14, - 2.5 - ], - [ - 20, - 11.5 - ] - ] - } - }, - "source":"openmaptiles", - "source-layer":"transportation", - "type":"line" - }, - { - "filter":[ - "all", - [ - "==", - "brunnel", - "tunnel" - ], - [ - "in", - "class", - "minor" - ] - ], - "id":"tunnel_minor", - "layout":{ - "line-join":"round", - "visibility":"visible" - }, - "metadata":{ - - }, - "paint":{ - "line-color":"#fff", - "line-opacity":1, - "line-width":{ - "base":1.2, - "stops":[ - [ - 13.5, - 0 - ], - [ - 14, - 2.5 - ], - [ - 20, - 11.5 - ] - ] - } - }, - "source":"openmaptiles", - "source-layer":"transportation", - "type":"line" - }, - { - "filter":[ - "all", - [ - "==", - "brunnel", - "tunnel" - ], - [ - "in", - "class", - "minor_construction" - ] - ], - "id":"tunnel_minor_construction", - "layout":{ - "line-join":"round", - "visibility":"visible" - }, - "metadata":{ - - }, - "paint":{ - "line-color":"#fff", - "line-dasharray":[ - 2, - 2 - ], - "line-opacity":1, - "line-width":{ - "base":1.2, - "stops":[ - [ - 13.5, - 0 - ], - [ - 14, - 2.5 - ], - [ - 20, - 11.5 - ] - ] - } - }, - "source":"openmaptiles", - "source-layer":"transportation", - "type":"line" - }, - { - "filter":[ - "all", - [ - "==", - "brunnel", - "tunnel" - ], - [ - "in", - "class", - "secondary", - "tertiary" - ] - ], - "id":"tunnel_secondary_tertiary", - "layout":{ - "line-join":"round", - "visibility":"visible" - }, - "metadata":{ - - }, - "paint":{ - "line-color":"rgba(245, 245, 243, 1)", - "line-width":{ - "base":1.2, - "stops":[ - [ - 6.5, - 0 - ], - [ - 8, - 0.5 - ], - [ - 20, - 13 - ] - ] - } - }, - "source":"openmaptiles", - "source-layer":"transportation", - "type":"line" - }, - { - "filter":[ - "all", - [ - "==", - "brunnel", - "tunnel" - ], - [ - "in", - "class", - "secondary_construction", - "tertiary_construction" - ] - ], - "id":"tunnel_secondary_tertiary_construction", - "layout":{ - "line-join":"round", - "visibility":"visible" - }, - "metadata":{ - - }, - "paint":{ - "line-color":"#fff", - "line-dasharray":[ - 2, - 2 - ], - "line-width":{ - "base":1.2, - "stops":[ - [ - 6.5, - 0 - ], - [ - 8, - 0.5 - ], - [ - 20, - 13 - ] - ] - } - }, - "source":"openmaptiles", - "source-layer":"transportation", - "type":"line" - }, - { - "filter":[ - "all", - [ - "==", - "brunnel", - "tunnel" - ], - [ - "in", - "class", - "primary", - "trunk" - ] - ], - "id":"tunnel_trunk_primary", - "layout":{ - "line-join":"round", - "visibility":"visible" - }, - "metadata":{ - - }, - "paint":{ - "line-color":"#fff4c6", - "line-width":{ - "base":1.2, - "stops":[ - [ - 5, - 0 - ], - [ - 7, - 1 - ], - [ - 20, - 18 - ] - ] - } - }, - "source":"openmaptiles", - "source-layer":"transportation", - "type":"line" - }, - { - "filter":[ - "all", - [ - "==", - "brunnel", - "tunnel" - ], - [ - "in", - "class", - "primary_construction", - "trunk_construction" - ] - ], - "id":"tunnel_trunk_primary_construction", - "layout":{ - "line-join":"round", - "visibility":"visible" - }, - "metadata":{ - - }, - "paint":{ - "line-color":"#fff4c6", - "line-dasharray":[ - 2, - 2 - ], - "line-width":{ - "base":1.2, - "stops":[ - [ - 5, - 0 - ], - [ - 7, - 1 - ], - [ - 20, - 18 - ] - ] - } - }, - "source":"openmaptiles", - "source-layer":"transportation", - "type":"line" - }, - { - "filter":[ - "all", - [ - "==", - "class", - "motorway" - ], - [ - "==", - "brunnel", - "tunnel" - ] - ], - "id":"tunnel_motorway", - "layout":{ - "line-join":"round", - "visibility":"visible" - }, - "metadata":{ - - }, - "paint":{ - "line-color":"#ffdaa6", - "line-width":{ - "base":1.2, - "stops":[ - [ - 5, - 0 - ], - [ - 7, - 1 - ], - [ - 20, - 18 - ] - ] - } - }, - "source":"openmaptiles", - "source-layer":"transportation", - "type":"line" - }, - { - "filter":[ - "all", - [ - "==", - "class", - "motorway_construction" - ], - [ - "==", - "brunnel", - "tunnel" - ] - ], - "id":"tunnel_motorway_construction", - "layout":{ - "line-join":"round", - "visibility":"visible" - }, - "metadata":{ - - }, - "paint":{ - "line-color":"#ffdaa6", - "line-dasharray":[ - 2, - 2 - ], - "line-width":{ - "base":1.2, - "stops":[ - [ - 5, - 0 - ], - [ - 7, - 1 - ], - [ - 20, - 18 - ] - ] - } - }, - "source":"openmaptiles", - "source-layer":"transportation", - "type":"line" - }, - { - "filter":[ - "all", - [ - "==", - "brunnel", - "tunnel" - ], - [ - "in", - "class", - "rail" - ] - ], - "id":"tunnel_major_rail", - "layout":{ - "visibility":"visible" - }, - "metadata":{ - - }, - "paint":{ - "line-color":"#bbb", - "line-width":{ - "base":1.4, - "stops":[ - [ - 14, - 0.4 - ], - [ - 15, - 0.75 - ], - [ - 20, - 2 - ] - ] - } - }, - "source":"openmaptiles", - "source-layer":"transportation", - "type":"line" - }, - { - "filter":[ - "all", - [ - "==", - "brunnel", - "tunnel" - ], - [ - "==", - "class", - "rail" - ] - ], - "id":"tunnel_major_rail_hatching", - "layout":{ - "visibility":"visible" - }, - "metadata":{ - - }, - "paint":{ - "line-color":"#bbb", - "line-dasharray":[ - 0.2, - 8 - ], - "line-width":{ - "base":1.4, - "stops":[ - [ - 14.5, - 0 - ], - [ - 15, - 3 - ], - [ - 20, - 8 - ] - ] - } - }, - "source":"openmaptiles", - "source-layer":"transportation", - "type":"line" - }, - { - "filter":[ - "all", - [ - "==", - "$type", - "Polygon" - ], - [ - "==", - "class", - "pier" - ] - ], - "id":"road_area_pier", - "layout":{ - "visibility":"visible" - }, - "metadata":{ - - }, - "paint":{ - "fill-antialias":true, - "fill-color":"rgba(246, 241, 229, 1)" - }, - "source":"openmaptiles", - "source-layer":"transportation", - "type":"fill" - }, - { - "filter":[ - "all", - [ - "==", - "$type", - "LineString" - ], - [ - "in", - "class", - "pier" - ] - ], - "id":"road_pier", - "layout":{ - "line-cap":"round", - "line-join":"round" - }, - "metadata":{ - - }, - "paint":{ - "line-color":"rgba(246, 241, 229, 1)", - "line-width":{ - "base":1.2, - "stops":[ - [ - 15, - 1 - ], - [ - 17, - 4 - ] - ] - } - }, - "source":"openmaptiles", - "source-layer":"transportation", - "type":"line" - }, - { - "filter":[ - "all", - [ - "==", - "$type", - "Polygon" - ], - [ - "==", - "brunnel", - "bridge" - ] - ], - "id":"road_area_bridge", - "layout":{ - "visibility":"visible" - }, - "metadata":{ - - }, - "paint":{ - "fill-antialias":true, - "fill-color":"rgba(246, 241, 229, 0.6)" - }, - "source":"openmaptiles", - "source-layer":"transportation", - "type":"fill" - }, - { - "filter":[ - "all", - [ - "==", - "$type", - "Polygon" - ], - [ - "!has", - "brunnel" - ], - [ - "!in", - "class", - "bridge", - "pier" - ] - ], - "id":"road_area_pattern", - "layout":{ - "visibility":"visible" - }, - "metadata":{ - - }, - "paint":{ - "fill-pattern":"pedestrian_polygon" - }, - "source":"openmaptiles", - "source-layer":"transportation", - "type":"fill" - }, - { - "filter":[ - "all", - [ - "!in", - "brunnel", - "bridge", - "tunnel" - ], - [ - "in", - "class", - "service", - "track" - ] - ], - "id":"road_service_track_casing", - "layout":{ - "line-cap":"round", - "line-join":"round" - }, - "metadata":{ - - }, - "paint":{ - "line-color":"#cfcdca", - "line-width":{ - "base":1.2, - "stops":[ - [ - 15, - 1 - ], - [ - 16, - 4 - ], - [ - 20, - 11 - ] - ] - } - }, - "source":"openmaptiles", - "source-layer":"transportation", - "type":"line" - }, - { - "filter":[ - "all", - [ - "!in", - "brunnel", - "bridge", - "tunnel" - ], - [ - "!in", - "class", - "motorway", - "trunk", - "primary", - "pedestrian", - "path", - "track", - "service" - ], - [ - "==", - "ramp", - 1 - ] - ], - "id":"road_link_casing", - "layout":{ - "line-cap":"round", - "line-join":"round", - "visibility":"visible" - }, - "metadata":{ - - }, - "minzoom":13, - "paint":{ - "line-color":"#e9ac77", - "line-opacity":1, - "line-width":{ - "base":1.2, - "stops":[ - [ - 12, - 1 - ], - [ - 13, - 3 - ], - [ - 14, - 4 - ], - [ - 20, - 15 - ] - ] - } - }, - "source":"openmaptiles", - "source-layer":"transportation", - "type":"line" - }, - { - "filter":[ - "all", - [ - "!in", - "brunnel", - "bridge", - "tunnel" - ], - [ - "==", - "ramp", - 1 - ], - [ - "in", - "class", - "primary", - "trunk" - ] - ], - "id":"road_trunk_primary_link_casing", - "layout":{ - "line-cap":"butt", - "line-join":"round", - "visibility":"visible" - }, - "metadata":{ - - }, - "paint":{ - "line-color":"#e9ac77", - "line-width":{ - "base":1.2, - "stops":[ - [ - 5, - 0 - ], - [ - 7, - 0.7 - ], - [ - 20, - 14 - ] - ] - } - }, - "source":"openmaptiles", - "source-layer":"transportation", - "type":"line" - }, - { - "filter":[ - "all", - [ - "!in", - "brunnel", - "bridge", - "tunnel" - ], - [ - "==", - "class", - "motorway" - ], - [ - "==", - "ramp", - 1 - ] - ], - "id":"road_motorway_link_casing", - "layout":{ - "line-cap":"butt", - "line-join":"round", - "visibility":"visible" - }, - "metadata":{ - - }, - "minzoom":12, - "paint":{ - "line-color":"#e9ac77", - "line-opacity":1, - "line-width":{ - "base":1.2, - "stops":[ - [ - 12, - 1 - ], - [ - 13, - 3 - ], - [ - 14, - 4 - ], - [ - 20, - 15 - ] - ] - } - }, - "source":"openmaptiles", - "source-layer":"transportation", - "type":"line" - }, - { - "filter":[ - "all", - [ - "==", - "$type", - "LineString" - ], - [ - "!in", - "brunnel", - "bridge", - "tunnel" - ], - [ - "in", - "class", - "minor" - ], - [ - "!=", - "ramp", - "1" - ] - ], - "id":"road_minor_casing", - "layout":{ - "line-cap":"round", - "line-join":"round", - "visibility":"visible" - }, - "metadata":{ - - }, - "paint":{ - "line-color":"#cfcdca", - "line-opacity":{ - "stops":[ - [ - 12, - 0 - ], - [ - 12.5, - 1 - ] - ] - }, - "line-width":{ - "base":1.2, - "stops":[ - [ - 12, - 0.5 - ], - [ - 13, - 1 - ], - [ - 14, - 4 - ], - [ - 20, - 20 - ] - ] - } - }, - "source":"openmaptiles", - "source-layer":"transportation", - "type":"line" - }, - { - "filter":[ - "all", - [ - "!in", - "brunnel", - "bridge", - "tunnel" - ], - [ - "in", - "class", - "secondary", - "tertiary" - ], - [ - "!=", - "ramp", - 1 - ] - ], - "id":"road_secondary_tertiary_casing-copy", - "layout":{ - "line-cap":"round", - "line-join":"round", - "visibility":"visible" - }, - "metadata":{ - - }, - "paint":{ - "line-color":"rgba(195, 189, 187, 1)", - "line-opacity":1, - "line-width":{ - "base":1.2, - "stops":[ - [ - 8, - 1.5 - ], - [ - 20, - 17 - ] - ] - } - }, - "source":"openmaptiles", - "source-layer":"transportation", - "type":"line" - }, - { - "filter":[ - "all", - [ - "!in", - "brunnel", - "bridge", - "tunnel" - ], - [ - "!=", - "ramp", - 1 - ], - [ - "in", - "class", - "primary", - "trunk" - ], - [ - "!=", - "ramp", - 1 - ] - ], - "id":"road_trunk_primary_casing", - "layout":{ - "line-cap":"butt", - "line-join":"round", - "visibility":"visible" - }, - "metadata":{ - - }, - "paint":{ - "line-color":"#e9ac77", - "line-opacity":1, - "line-width":{ - "base":1.2, - "stops":[ - [ - 5, - 0.4 - ], - [ - 6, - 0.7 - ], - [ - 7, - 1.75 - ], - [ - 20, - 22 - ] - ] - } - }, - "source":"openmaptiles", - "source-layer":"transportation", - "type":"line" - }, - { - "filter":[ - "all", - [ - "!in", - "brunnel", - "bridge", - "tunnel" - ], - [ - "==", - "class", - "motorway" - ], - [ - "!=", - "ramp", - 1 - ] - ], - "id":"road_motorway_casing", - "layout":{ - "line-cap":"butt", - "line-join":"round", - "visibility":"visible" - }, - "metadata":{ - - }, - "minzoom":5, - "paint":{ - "line-color":"#e9ac77", - "line-width":{ - "base":1.2, - "stops":[ - [ - 5, - 0.4 - ], - [ - 6, - 0.7 - ], - [ - 7, - 1.75 - ], - [ - 20, - 22 - ] - ] - } - }, - "source":"openmaptiles", - "source-layer":"transportation", - "type":"line" - }, - { - "filter":[ - "all", - [ - "==", - "$type", - "LineString" - ], - [ - "!in", - "brunnel", - "bridge", - "tunnel" - ], - [ - "in", - "class", - "path", - "pedestrian" - ] - ], - "id":"road_path_pedestrian", - "layout":{ - "line-join":"round", - "visibility":"visible" - }, - "metadata":{ - - }, - "minzoom":14, - "paint":{ - "line-color":"rgba(204, 196, 176, 0.45)", - "line-dasharray":[ - 1, - 1 - ], - "line-width":{ - "base":1.2, - "stops":[ - [ - 14, - 1 - ], - [ - 20, - 6 - ] - ] - } - }, - "source":"openmaptiles", - "source-layer":"transportation", - "type":"line" - }, - { - "filter":[ - "all", - [ - "!in", - "brunnel", - "bridge", - "tunnel" - ], - [ - "==", - "ramp", - 1 - ], - [ - "!in", - "class", - "motorway", - "trunk", - "primary", - "pedestrian", - "path", - "track", - "service" - ] - ], - "id":"road_link", - "layout":{ - "line-cap":"round", - "line-join":"round", - "visibility":"visible" - }, - "metadata":{ - - }, - "minzoom":13, - "paint":{ - "line-color":"#fea", - "line-width":{ - "base":1.2, - "stops":[ - [ - 12.5, - 0 - ], - [ - 13, - 1.5 - ], - [ - 14, - 2.5 - ], - [ - 20, - 11.5 - ] - ] - } - }, - "source":"openmaptiles", - "source-layer":"transportation", - "type":"line" - }, - { - "filter":[ - "all", - [ - "!in", - "brunnel", - "bridge", - "tunnel" - ], - [ - "==", - "ramp", - 1 - ], - [ - "in", - "class", - "primary", - "trunk" - ] - ], - "id":"road_trunk_primary_link", - "layout":{ - "line-cap":"round", - "line-join":"round", - "visibility":"visible" - }, - "metadata":{ - - }, - "paint":{ - "line-color":"#fea", - "line-width":{ - "base":1.2, - "stops":[ - [ - 5, - 0 - ], - [ - 7, - 0.5 - ], - [ - 20, - 10 - ] - ] - } - }, - "source":"openmaptiles", - "source-layer":"transportation", - "type":"line" - }, - { - "filter":[ - "all", - [ - "!in", - "brunnel", - "bridge", - "tunnel" - ], - [ - "==", - "class", - "motorway" - ], - [ - "==", - "ramp", - 1 - ] - ], - "id":"road_motorway_link", - "layout":{ - "line-cap":"round", - "line-join":"round", - "visibility":"visible" - }, - "metadata":{ - - }, - "minzoom":12, - "paint":{ - "line-color":"#fc8", - "line-width":{ - "base":1.2, - "stops":[ - [ - 12.5, - 0 - ], - [ - 13, - 1.5 - ], - [ - 14, - 2.5 - ], - [ - 20, - 11.5 - ] - ] - } - }, - "source":"openmaptiles", - "source-layer":"transportation", - "type":"line" - }, - { - "filter":[ - "all", - [ - "!in", - "brunnel", - "bridge", - "tunnel" - ], - [ - "in", - "class", - "service", - "track" - ] - ], - "id":"road_service_track", - "layout":{ - "line-cap":"round", - "line-join":"round", - "visibility":"visible" - }, - "metadata":{ - - }, - "paint":{ - "line-color":"#fff", - "line-width":{ - "base":1.2, - "stops":[ - [ - 15.5, - 0 - ], - [ - 16, - 2 - ], - [ - 20, - 7.5 - ] - ] - } - }, - "source":"openmaptiles", - "source-layer":"transportation", - "type":"line" - }, - { - "filter":[ - "all", - [ - "!in", - "brunnel", - "bridge", - "tunnel" - ], - [ - "in", - "class", - "service_construction", - "track_construction" - ] - ], - "id":"road_service_track_construction", - "layout":{ - "line-cap":"round", - "line-join":"round", - "visibility":"visible" - }, - "metadata":{ - - }, - "paint":{ - "line-color":"#fff", - "line-dasharray":[ - 2, - 2 - ], - "line-width":{ - "base":1.2, - "stops":[ - [ - 15.5, - 0 - ], - [ - 16, - 2 - ], - [ - 20, - 7.5 - ] - ] - } - }, - "source":"openmaptiles", - "source-layer":"transportation", - "type":"line" - }, - { - "filter":[ - "all", - [ - "==", - "$type", - "LineString" - ], - [ - "all", - [ - "!in", - "brunnel", - "bridge", - "tunnel" - ], - [ - "in", - "class", - "minor" - ] - ] - ], - "id":"road_minor", - "layout":{ - "line-cap":"round", - "line-join":"round", - "visibility":"visible" - }, - "metadata":{ - - }, - "paint":{ - "line-color":"#fff", - "line-opacity":1, - "line-width":{ - "base":1.2, - "stops":[ - [ - 13.5, - 0 - ], - [ - 14, - 2.5 - ], - [ - 20, - 18 - ] - ] - } - }, - "source":"openmaptiles", - "source-layer":"transportation", - "type":"line" - }, - { - "filter":[ - "all", - [ - "==", - "$type", - "LineString" - ], - [ - "all", - [ - "!in", - "brunnel", - "bridge", - "tunnel" - ], - [ - "in", - "class", - "minor_construction" - ] - ] - ], - "id":"road_minor_construction", - "layout":{ - "line-cap":"round", - "line-join":"round", - "visibility":"visible" - }, - "metadata":{ - - }, - "paint":{ - "line-color":"#fff", - "line-dasharray":[ - 2, - 2 - ], - "line-opacity":1, - "line-width":{ - "base":1.2, - "stops":[ - [ - 13.5, - 0 - ], - [ - 14, - 2.5 - ], - [ - 20, - 18 - ] - ] - } - }, - "source":"openmaptiles", - "source-layer":"transportation", - "type":"line" - }, - { - "filter":[ - "all", - [ - "!in", - "brunnel", - "bridge", - "tunnel" - ], - [ - "in", - "class", - "secondary", - "tertiary" - ] - ], - "id":"road_secondary_tertiary", - "layout":{ - "line-cap":"round", - "line-join":"round", - "visibility":"visible" - }, - "metadata":{ - - }, - "paint":{ - "line-color":"rgba(245, 245, 243, 1)", - "line-width":{ - "base":1.2, - "stops":[ - [ - 6.5, - 0 - ], - [ - 8, - 0.5 - ], - [ - 20, - 13 - ] - ] - } - }, - "source":"openmaptiles", - "source-layer":"transportation", - "type":"line" - }, - { - "filter":[ - "all", - [ - "!in", - "brunnel", - "bridge", - "tunnel" - ], - [ - "in", - "class", - "secondary_construction", - "tertiary_construction" - ] - ], - "id":"road_secondary_tertiary_construction", - "layout":{ - "line-cap":"round", - "line-join":"round", - "visibility":"visible" - }, - "metadata":{ - - }, - "paint":{ - "line-color":"#fff", - "line-dasharray":[ - 2, - 2 - ], - "line-width":{ - "base":1.2, - "stops":[ - [ - 6.5, - 0 - ], - [ - 8, - 0.5 - ], - [ - 20, - 13 - ] - ] - } - }, - "source":"openmaptiles", - "source-layer":"transportation", - "type":"line" - }, - { - "filter":[ - "all", - [ - "!in", - "brunnel", - "bridge", - "tunnel" - ], - [ - "!=", - "ramp", - 1 - ], - [ - "in", - "class", - "primary", - "trunk" - ] - ], - "id":"road_trunk_primary", - "layout":{ - "line-cap":"round", - "line-join":"round", - "visibility":"visible" - }, - "metadata":{ - - }, - "paint":{ - "line-color":"#fea", - "line-width":{ - "base":1.2, - "stops":[ - [ - 5, - 0 - ], - [ - 7, - 1 - ], - [ - 20, - 18 - ] - ] - } - }, - "source":"openmaptiles", - "source-layer":"transportation", - "type":"line" - }, - { - "filter":[ - "all", - [ - "!in", - "brunnel", - "bridge", - "tunnel" - ], - [ - "!=", - "ramp", - 1 - ], - [ - "in", - "class", - "primary_construction", - "trunk_construction" - ] - ], - "id":"road_trunk_primary_construction", - "layout":{ - "line-cap":"round", - "line-join":"round", - "visibility":"visible" - }, - "metadata":{ - - }, - "paint":{ - "line-color":"#fea", - "line-dasharray":[ - 2, - 2 - ], - "line-width":{ - "base":1.2, - "stops":[ - [ - 5, - 0 - ], - [ - 7, - 1 - ], - [ - 20, - 18 - ] - ] - } - }, - "source":"openmaptiles", - "source-layer":"transportation", - "type":"line" - }, - { - "filter":[ - "all", - [ - "!in", - "brunnel", - "bridge", - "tunnel" - ], - [ - "==", - "class", - "motorway" - ], - [ - "!=", - "ramp", - 1 - ] - ], - "id":"road_motorway", - "layout":{ - "line-cap":"round", - "line-join":"round", - "visibility":"visible" - }, - "metadata":{ - - }, - "minzoom":5, - "paint":{ - "line-color":{ - "base":1, - "stops":[ - [ - 5, - "hsl(26, 87%, 62%)" - ], - [ - 6, - "#fc8" - ] - ] - }, - "line-width":{ - "base":1.2, - "stops":[ - [ - 5, - 0 - ], - [ - 7, - 1 - ], - [ - 20, - 18 - ] - ] - } - }, - "source":"openmaptiles", - "source-layer":"transportation", - "type":"line" - }, - { - "filter":[ - "all", - [ - "!in", - "brunnel", - "bridge", - "tunnel" - ], - [ - "==", - "class", - "motorway_construction" - ], - [ - "!=", - "ramp", - 1 - ] - ], - "id":"road_motorway_construction", - "layout":{ - "line-cap":"round", - "line-join":"round", - "visibility":"visible" - }, - "metadata":{ - - }, - "minzoom":5, - "paint":{ - "line-color":{ - "base":1, - "stops":[ - [ - 5, - "hsl(26, 87%, 62%)" - ], - [ - 6, - "#fc8" - ] - ] - }, - "line-dasharray":[ - 2, - 2 - ], - "line-width":{ - "base":1.2, - "stops":[ - [ - 5, - 0 - ], - [ - 7, - 1 - ], - [ - 20, - 18 - ] - ] - } - }, - "source":"openmaptiles", - "source-layer":"transportation", - "type":"line" - }, - { - "filter":[ - "all", - [ - "!in", - "brunnel", - "bridge", - "tunnel" - ], - [ - "in", - "class", - "rail" - ] - ], - "id":"road_major_rail", - "metadata":{ - - }, - "paint":{ - "line-color":"#bbb", - "line-width":{ - "base":1.4, - "stops":[ - [ - 14, - 0.4 - ], - [ - 15, - 0.75 - ], - [ - 20, - 2 - ] - ] - } - }, - "source":"openmaptiles", - "source-layer":"transportation", - "type":"line" - }, - { - "filter":[ - "all", - [ - "!in", - "brunnel", - "bridge", - "tunnel" - ], - [ - "==", - "class", - "rail" - ] - ], - "id":"road_major_rail_hatching", - "metadata":{ - - }, - "paint":{ - "line-color":"#bbb", - "line-dasharray":[ - 0.2, - 8 - ], - "line-width":{ - "base":1.4, - "stops":[ - [ - 14.5, - 0 - ], - [ - 15, - 3 - ], - [ - 20, - 8 - ] - ] - } - }, - "source":"openmaptiles", - "source-layer":"transportation", - "type":"line" - }, - { - "filter":[ - "all", - [ - "!in", - "brunnel", - "bridge", - "tunnel" - ], - [ - "in", - "subclass", - "tram", - "light_rail" - ] - ], - "id":"road_minor_rail", - "metadata":{ - - }, - "paint":{ - "line-color":"#bbb", - "line-width":{ - "base":1.4, - "stops":[ - [ - 14, - 0.4 - ], - [ - 15, - 0.75 - ], - [ - 20, - 2 - ] - ] - } - }, - "source":"openmaptiles", - "source-layer":"transportation", - "type":"line" - }, - { - "filter":[ - "all", - [ - "!in", - "brunnel", - "bridge", - "tunnel" - ], - [ - "in", - "subclass", - "tram", - "light_rail" - ] - ], - "id":"road_minor_rail_hatching", - "metadata":{ - - }, - "paint":{ - "line-color":"#bbb", - "line-dasharray":[ - 0.2, - 4 - ], - "line-width":{ - "base":1.4, - "stops":[ - [ - 14.5, - 0 - ], - [ - 15, - 2 - ], - [ - 20, - 6 - ] - ] - } - }, - "source":"openmaptiles", - "source-layer":"transportation", - "type":"line" - }, - { - "id":"building", - "layout":{ - "visibility":"visible" - }, - "maxzoom":14, - "metadata":{ - - }, - "minzoom":13, - "paint":{ - "fill-color":"rgba(189, 185, 181, 0.3)", - "fill-outline-color":{ - "base":1, - "stops":[ - [ - 13, - "hsla(35, 6%, 79%, 0.32)" - ], - [ - 14, - "hsl(35, 6%, 79%)" - ] - ] - } - }, - "source":"openmaptiles", - "source-layer":"building", - "type":"fill" - }, - { - "filter":[ - "all", - [ - "!has", - "hide_3d" - ] - ], - "id":"building-3d", - "layout":{ - "visibility":"visible" - }, - "metadata":{ - - }, - "minzoom":14, - "paint":{ - "fill-extrusion-base":{ - "property":"render_min_height", - "type":"identity" - }, - "fill-extrusion-color":"rgba(189, 185, 181, 1)", - "fill-extrusion-height":{ - "property":"render_height", - "type":"identity" - }, - "fill-extrusion-opacity":0.3 - }, - "source":"openmaptiles", - "source-layer":"building", - "type":"fill-extrusion" - }, - { - "filter":[ - "all", - [ - "==", - "$type", - "LineString" - ], - [ - "==", - "brunnel", - "bridge" - ] - ], - "id":"waterway-bridge-case", - "layout":{ - "line-cap":"butt", - "line-join":"miter" - }, - "paint":{ - "line-color":"#bbbbbb", - "line-gap-width":{ - "base":1.3, - "stops":[ - [ - 13, - 0.5 - ], - [ - 20, - 6 - ] - ] - }, - "line-width":{ - "base":1.6, - "stops":[ - [ - 12, - 0.5 - ], - [ - 20, - 5 - ] - ] - } - }, - "source":"openmaptiles", - "source-layer":"waterway", - "type":"line" - }, - { - "filter":[ - "all", - [ - "==", - "$type", - "LineString" - ], - [ - "==", - "brunnel", - "bridge" - ] - ], - "id":"waterway-bridge", - "layout":{ - "line-cap":"round", - "line-join":"round" - }, - "paint":{ - "line-color":"rgba(134, 204, 250, 1)", - "line-width":{ - "base":1.3, - "stops":[ - [ - 13, - 0.5 - ], - [ - 20, - 6 - ] - ] - } - }, - "source":"openmaptiles", - "source-layer":"waterway", - "type":"line" - }, - { - "filter":[ - "all", - [ - "==", - "class", - "motorway" - ], - [ - "==", - "ramp", - 1 - ], - [ - "==", - "brunnel", - "bridge" - ] - ], - "id":"bridge_motorway_link_casing", - "layout":{ - "line-join":"round" - }, - "metadata":{ - - }, - "paint":{ - "line-color":"#e9ac77", - "line-opacity":1, - "line-width":{ - "base":1.2, - "stops":[ - [ - 12, - 1 - ], - [ - 13, - 3 - ], - [ - 14, - 4 - ], - [ - 20, - 15 - ] - ] - } - }, - "source":"openmaptiles", - "source-layer":"transportation", - "type":"line" - }, - { - "filter":[ - "all", - [ - "==", - "brunnel", - "bridge" - ], - [ - "in", - "class", - "service", - "track" - ] - ], - "id":"bridge_service_track_casing", - "layout":{ - "line-join":"round" - }, - "metadata":{ - - }, - "paint":{ - "line-color":"#cfcdca", - "line-width":{ - "base":1.2, - "stops":[ - [ - 15, - 1 - ], - [ - 16, - 4 - ], - [ - 20, - 11 - ] - ] - } - }, - "source":"openmaptiles", - "source-layer":"transportation", - "type":"line" - }, - { - "filter":[ - "all", - [ - "==", - "class", - "link" - ], - [ - "==", - "brunnel", - "bridge" - ] - ], - "id":"bridge_link_casing", - "layout":{ - "line-join":"round" - }, - "metadata":{ - - }, - "paint":{ - "line-color":"#e9ac77", - "line-opacity":1, - "line-width":{ - "base":1.2, - "stops":[ - [ - 12, - 1 - ], - [ - 13, - 3 - ], - [ - 14, - 4 - ], - [ - 20, - 15 - ] - ] - } - }, - "source":"openmaptiles", - "source-layer":"transportation", - "type":"line" - }, - { - "filter":[ - "all", - [ - "==", - "brunnel", - "bridge" - ], - [ - "in", - "class", - "street", - "street_limited" - ] - ], - "id":"bridge_street_casing", - "layout":{ - "line-join":"round" - }, - "metadata":{ - - }, - "paint":{ - "line-color":"hsl(36, 6%, 74%)", - "line-opacity":{ - "stops":[ - [ - 12, - 0 - ], - [ - 12.5, - 1 - ] - ] - }, - "line-width":{ - "base":1.2, - "stops":[ - [ - 12, - 0.5 - ], - [ - 13, - 1 - ], - [ - 14, - 4 - ], - [ - 20, - 25 - ] - ] - } - }, - "source":"openmaptiles", - "source-layer":"transportation", - "type":"line" - }, - { - "filter":[ - "all", - [ - "==", - "$type", - "LineString" - ], - [ - "==", - "brunnel", - "bridge" - ], - [ - "in", - "class", - "path", - "pedestrian" - ] - ], - "id":"bridge_path_pedestrian_casing", - "layout":{ - "line-join":"miter", - "visibility":"visible" - }, - "metadata":{ - - }, - "paint":{ - "line-color":"hsl(35, 6%, 80%)", - "line-dasharray":[ - 1, - 0 - ], - "line-width":{ - "base":1.2, - "stops":[ - [ - 14, - 1.5 - ], - [ - 20, - 18 - ] - ] - } - }, - "source":"openmaptiles", - "source-layer":"transportation", - "type":"line" - }, - { - "filter":[ - "all", - [ - "==", - "brunnel", - "bridge" - ], - [ - "in", - "class", - "secondary", - "tertiary" - ] - ], - "id":"bridge_secondary_tertiary_casing", - "layout":{ - "line-join":"round" - }, - "metadata":{ - - }, - "paint":{ - "line-color":"rgba(195, 189, 187, 1)", - "line-opacity":1, - "line-width":{ - "base":1.2, - "stops":[ - [ - 8, - 1.5 - ], - [ - 20, - 17 - ] - ] - } - }, - "source":"openmaptiles", - "source-layer":"transportation", - "type":"line" - }, - { - "filter":[ - "all", - [ - "==", - "brunnel", - "bridge" - ], - [ - "in", - "class", - "primary", - "trunk" - ] - ], - "id":"bridge_trunk_primary_casing", - "layout":{ - "line-join":"round" - }, - "metadata":{ - - }, - "paint":{ - "line-color":"#e9ac77", - "line-width":{ - "base":1.2, - "stops":[ - [ - 5, - 0.4 - ], - [ - 6, - 0.7 - ], - [ - 7, - 1.75 - ], - [ - 20, - 22 - ] - ] - } - }, - "source":"openmaptiles", - "source-layer":"transportation", - "type":"line" - }, - { - "filter":[ - "all", - [ - "==", - "class", - "motorway" - ], - [ - "==", - "brunnel", - "bridge" - ], - [ - "!=", - "ramp", - 1 - ] - ], - "id":"bridge_motorway_casing", - "layout":{ - "line-join":"round" - }, - "metadata":{ - - }, - "paint":{ - "line-color":"#e9ac77", - "line-width":{ - "base":1.2, - "stops":[ - [ - 5, - 0.4 - ], - [ - 6, - 0.7 - ], - [ - 7, - 1.75 - ], - [ - 20, - 22 - ] - ] - } - }, - "source":"openmaptiles", - "source-layer":"transportation", - "type":"line" - }, - { - "filter":[ - "all", - [ - "==", - "$type", - "LineString" - ], - [ - "==", - "brunnel", - "bridge" - ], - [ - "in", - "class", - "path", - "pedestrian" - ] - ], - "id":"bridge_path_pedestrian", - "metadata":{ - - }, - "paint":{ - "line-color":"hsl(0, 0%, 100%)", - "line-dasharray":[ - 1, - 0.3 - ], - "line-width":{ - "base":1.2, - "stops":[ - [ - 14, - 0.5 - ], - [ - 20, - 10 - ] - ] - } - }, - "source":"openmaptiles", - "source-layer":"transportation", - "type":"line" - }, - { - "filter":[ - "all", - [ - "==", - "class", - "motorway" - ], - [ - "==", - "ramp", - 1 - ], - [ - "==", - "brunnel", - "bridge" - ] - ], - "id":"bridge_motorway_link", - "layout":{ - "line-join":"round" - }, - "metadata":{ - - }, - "paint":{ - "line-color":"#fc8", - "line-width":{ - "base":1.2, - "stops":[ - [ - 12.5, - 0 - ], - [ - 13, - 1.5 - ], - [ - 14, - 2.5 - ], - [ - 20, - 11.5 - ] - ] - } - }, - "source":"openmaptiles", - "source-layer":"transportation", - "type":"line" - }, - { - "filter":[ - "all", - [ - "==", - "brunnel", - "bridge" - ], - [ - "in", - "class", - "service", - "track" - ] - ], - "id":"bridge_service_track", - "layout":{ - "line-join":"round", - "visibility":"visible" - }, - "metadata":{ - - }, - "paint":{ - "line-color":"#fff", - "line-width":{ - "base":1.2, - "stops":[ - [ - 15.5, - 0 - ], - [ - 16, - 2 - ], - [ - 20, - 7.5 - ] - ] - } - }, - "source":"openmaptiles", - "source-layer":"transportation", - "type":"line" - }, - { - "filter":[ - "all", - [ - "==", - "brunnel", - "bridge" - ], - [ - "in", - "class", - "service_construction", - "track_construction" - ] - ], - "id":"bridge_service_track_construction", - "layout":{ - "line-join":"round", - "visibility":"visible" - }, - "metadata":{ - - }, - "paint":{ - "line-color":"#fff", - "line-dasharray":[ - 2, - 2 - ], - "line-width":{ - "base":1.2, - "stops":[ - [ - 15.5, - 0 - ], - [ - 16, - 2 - ], - [ - 20, - 7.5 - ] - ] - } - }, - "source":"openmaptiles", - "source-layer":"transportation", - "type":"line" - }, - { - "filter":[ - "all", - [ - "==", - "class", - "link" - ], - [ - "==", - "brunnel", - "bridge" - ] - ], - "id":"bridge_link", - "layout":{ - "line-join":"round", - "visibility":"visible" - }, - "metadata":{ - - }, - "paint":{ - "line-color":"#fea", - "line-width":{ - "base":1.2, - "stops":[ - [ - 12.5, - 0 - ], - [ - 13, - 1.5 - ], - [ - 14, - 2.5 - ], - [ - 20, - 11.5 - ] - ] - } - }, - "source":"openmaptiles", - "source-layer":"transportation", - "type":"line" - }, - { - "filter":[ - "all", - [ - "==", - "brunnel", - "bridge" - ], - [ - "in", - "class", - "minor" - ] - ], - "id":"bridge_minor", - "layout":{ - "line-join":"round" - }, - "metadata":{ - - }, - "paint":{ - "line-color":"#fff", - "line-opacity":1, - "line-width":{ - "base":1.2, - "stops":[ - [ - 13.5, - 0 - ], - [ - 14, - 2.5 - ], - [ - 20, - 18 - ] - ] - } - }, - "source":"openmaptiles", - "source-layer":"transportation", - "type":"line" - }, - { - "filter":[ - "all", - [ - "==", - "brunnel", - "bridge" - ], - [ - "in", - "class", - "minor_construction" - ] - ], - "id":"bridge_minor_construction", - "layout":{ - "line-join":"round", - "visibility":"visible" - }, - "metadata":{ - - }, - "paint":{ - "line-color":"#fff", - "line-dasharray":[ - 2, - 2 - ], - "line-opacity":1, - "line-width":{ - "base":1.2, - "stops":[ - [ - 13.5, - 0 - ], - [ - 14, - 2.5 - ], - [ - 20, - 18 - ] - ] - } - }, - "source":"openmaptiles", - "source-layer":"transportation", - "type":"line" - }, - { - "filter":[ - "all", - [ - "==", - "brunnel", - "bridge" - ], - [ - "in", - "class", - "secondary", - "tertiary" - ] - ], - "id":"bridge_secondary_tertiary", - "layout":{ - "line-join":"round" - }, - "metadata":{ - - }, - "paint":{ - "line-color":"rgba(245, 245, 243, 1)", - "line-width":{ - "base":1.2, - "stops":[ - [ - 6.5, - 0 - ], - [ - 8, - 0.5 - ], - [ - 20, - 13 - ] - ] - } - }, - "source":"openmaptiles", - "source-layer":"transportation", - "type":"line" - }, - { - "filter":[ - "all", - [ - "==", - "brunnel", - "bridge" - ], - [ - "in", - "class", - "secondary_construction", - "tertiary_construction" - ] - ], - "id":"bridge_secondary_tertiary_construction", - "layout":{ - "line-join":"round" - }, - "metadata":{ - - }, - "paint":{ - "line-color":"#fff", - "line-dasharray":[ - 2, - 2 - ], - "line-width":{ - "base":1.2, - "stops":[ - [ - 6.5, - 0 - ], - [ - 8, - 0.5 - ], - [ - 20, - 13 - ] - ] - } - }, - "source":"openmaptiles", - "source-layer":"transportation", - "type":"line" - }, - { - "filter":[ - "all", - [ - "==", - "brunnel", - "bridge" - ], - [ - "in", - "class", - "primary", - "trunk" - ] - ], - "id":"bridge_trunk_primary", - "layout":{ - "line-join":"round", - "visibility":"visible" - }, - "metadata":{ - - }, - "paint":{ - "line-color":"#fea", - "line-width":{ - "base":1.2, - "stops":[ - [ - 5, - 0 - ], - [ - 7, - 1 - ], - [ - 20, - 18 - ] - ] - } - }, - "source":"openmaptiles", - "source-layer":"transportation", - "type":"line" - }, - { - "filter":[ - "all", - [ - "==", - "brunnel", - "bridge" - ], - [ - "in", - "class", - "primary_construction", - "trunk_construction" - ] - ], - "id":"bridge_trunk_primary_construction", - "layout":{ - "line-join":"round", - "visibility":"visible" - }, - "metadata":{ - - }, - "paint":{ - "line-color":"#fea", - "line-dasharray":[ - 2, - 2 - ], - "line-width":{ - "base":1.2, - "stops":[ - [ - 5, - 0 - ], - [ - 7, - 1 - ], - [ - 20, - 18 - ] - ] - } - }, - "source":"openmaptiles", - "source-layer":"transportation", - "type":"line" - }, - { - "filter":[ - "all", - [ - "==", - "class", - "motorway" - ], - [ - "==", - "brunnel", - "bridge" - ], - [ - "!=", - "ramp", - 1 - ] - ], - "id":"bridge_motorway", - "layout":{ - "line-join":"round" - }, - "metadata":{ - - }, - "paint":{ - "line-color":"#fc8", - "line-width":{ - "base":1.2, - "stops":[ - [ - 5, - 0 - ], - [ - 7, - 1 - ], - [ - 20, - 18 - ] - ] - } - }, - "source":"openmaptiles", - "source-layer":"transportation", - "type":"line" - }, - { - "filter":[ - "all", - [ - "==", - "class", - "motorway_construction" - ], - [ - "==", - "brunnel", - "bridge" - ], - [ - "!=", - "ramp", - 1 - ] - ], - "id":"bridge_motorway_construction", - "layout":{ - "line-join":"round" - }, - "metadata":{ - - }, - "paint":{ - "line-color":"#fc8", - "line-dasharray":[ - 2, - 2 - ], - "line-width":{ - "base":1.2, - "stops":[ - [ - 5, - 0 - ], - [ - 7, - 1 - ], - [ - 20, - 18 - ] - ] - } - }, - "source":"openmaptiles", - "source-layer":"transportation", - "type":"line" - }, - { - "filter":[ - "all", - [ - "==", - "class", - "rail" - ], - [ - "==", - "brunnel", - "bridge" - ] - ], - "id":"bridge_major_rail", - "metadata":{ - - }, - "paint":{ - "line-color":"#bbb", - "line-width":{ - "base":1.4, - "stops":[ - [ - 14, - 0.4 - ], - [ - 15, - 0.75 - ], - [ - 20, - 2 - ] - ] - } - }, - "source":"openmaptiles", - "source-layer":"transportation", - "type":"line" - }, - { - "filter":[ - "all", - [ - "==", - "class", - "rail" - ], - [ - "==", - "brunnel", - "bridge" - ] - ], - "id":"bridge_major_rail_hatching", - "metadata":{ - - }, - "paint":{ - "line-color":"#bbb", - "line-dasharray":[ - 0.2, - 8 - ], - "line-width":{ - "base":1.4, - "stops":[ - [ - 14.5, - 0 - ], - [ - 15, - 3 - ], - [ - 20, - 8 - ] - ] - } - }, - "source":"openmaptiles", - "source-layer":"transportation", - "type":"line" - }, - { - "filter":[ - "==", - "class", - "cable_car" - ], - "id":"cablecar", - "layout":{ - "line-cap":"round", - "visibility":"visible" - }, - "minzoom":13, - "paint":{ - "line-color":"hsl(0, 0%, 70%)", - "line-width":{ - "base":1, - "stops":[ - [ - 11, - 1 - ], - [ - 19, - 2.5 - ] - ] - } - }, - "source":"openmaptiles", - "source-layer":"transportation", - "type":"line" - }, - { - "filter":[ - "==", - "class", - "cable_car" - ], - "id":"cablecar-dash", - "layout":{ - "line-cap":"round", - "visibility":"visible" - }, - "minzoom":13, - "paint":{ - "line-color":"hsl(0, 0%, 70%)", - "line-dasharray":[ - 2, - 3 - ], - "line-width":{ - "base":1, - "stops":[ - [ - 11, - 3 - ], - [ - 19, - 5.5 - ] - ] - } - }, - "source":"openmaptiles", - "source-layer":"transportation", - "type":"line" - }, - { - "filter":[ - "all", - [ - "in", - "admin_level", - 3, - 4 - ] - ], - "id":"boundary_3", - "layout":{ - "line-join":"round", - "visibility":"visible" - }, - "metadata":{ - - }, - "paint":{ - "line-color":"#9e9cab", - "line-dasharray":[ - 5, - 3 - ], - "line-width":{ - "base":1, - "stops":[ - [ - 4, - 0.4 - ], - [ - 5, - 1 - ], - [ - 12, - 1.8 - ] - ] - } - }, - "source":"openmaptiles", - "source-layer":"boundary", - "type":"line" - }, - { - "filter":[ - "all", - [ - "==", - "admin_level", - 2 - ], - [ - "!has", - "claimed_by" - ], - [ - "==", - "disputed", - 0 - ] - ], - "id":"boundary_2_z0-4", - "layout":{ - "line-cap":"round", - "line-join":"round", - "visibility":"visible" - }, - "maxzoom":5, - "metadata":{ - - }, - "minzoom":0, - "paint":{ - "line-color":{ - "stops":[ - [ - 1, - "rgba(145, 145, 145, 1)" - ], - [ - 6, - "rgba(130, 130, 130, 1)" - ] - ] - }, - "line-opacity":1, - "line-width":{ - "base":1, - "stops":[ - [ - 3, - 1 - ], - [ - 5, - 1.2 - ] - ] - } - }, - "source":"openmaptiles", - "source-layer":"boundary", - "type":"line" - }, - { - "filter":[ - "all", - [ - "==", - "admin_level", - 2 - ], - [ - "!has", - "claimed_by" - ], - [ - "==", - "disputed", - 1 - ] - ], - "id":"boundary_2_z0-4_disputed", - "layout":{ - "line-cap":"round", - "line-join":"round", - "visibility":"visible" - }, - "maxzoom":5, - "metadata":{ - - }, - "minzoom":0, - "paint":{ - "line-color":"rgba(120, 120, 120, 1)", - "line-dasharray":[ - 4, - 3 - ], - "line-opacity":1, - "line-width":{ - "base":1, - "stops":[ - [ - 3, - 1 - ], - [ - 5, - 1.2 - ] - ] - } - }, - "source":"openmaptiles", - "source-layer":"boundary", - "type":"line" - }, - { - "filter":[ - "all", - [ - "==", - "admin_level", - 2 - ], - [ - "==", - "disputed", - 0 - ] - ], - "id":"boundary_2_z5-", - "layout":{ - "line-cap":"round", - "line-join":"round", - "visibility":"visible" - }, - "metadata":{ - - }, - "minzoom":5, - "paint":{ - "line-color":{ - "stops":[ - [ - 5, - "rgba(120, 120, 120, 1)" - ], - [ - 12, - "rgba(153, 153, 153, 1)" - ] - ] - }, - "line-opacity":1, - "line-width":{ - "base":1, - "stops":[ - [ - 5, - 1.2 - ], - [ - 12, - 3 - ] - ] - } - }, - "source":"openmaptiles", - "source-layer":"boundary", - "type":"line" - }, - { - "filter":[ - "all", - [ - "==", - "admin_level", - 2 - ], - [ - "==", - "disputed", - 1 - ] - ], - "id":"boundary_2_z5-_disputed", - "layout":{ - "line-cap":"round", - "line-join":"round", - "visibility":"visible" - }, - "metadata":{ - - }, - "minzoom":5, - "paint":{ - "line-color":{ - "stops":[ - [ - 5, - "rgba(120, 120, 120, 1)" - ], - [ - 12, - "rgba(153, 153, 153, 1)" - ] - ] - }, - "line-dasharray":[ - 4, - 3 - ], - "line-opacity":1, - "line-width":{ - "base":1, - "stops":[ - [ - 5, - 1.2 - ], - [ - 12, - 3 - ] - ] - } - }, - "source":"openmaptiles", - "source-layer":"boundary", - "type":"line" - }, - { - "filter":[ - "all", - [ - "==", - "$type", - "LineString" - ] - ], - "id":"water_name_line", - "layout":{ - "symbol-placement":"line", - "text-field":"{name:latin}\n{name:nonlatin}", - "text-font":[ - "Roboto Regular", - "Noto Sans Regular" - ], - "text-max-width":5, - "text-size":12, - "visibility":"visible" - }, - "metadata":{ - - }, - "minzoom":0, - "paint":{ - "text-color":"#5d60be", - "text-halo-color":"rgba(255,255,255,0.7)", - "text-halo-width":1 - }, - "source":"openmaptiles", - "source-layer":"water_name", - "type":"symbol" - }, - { - "filter":[ - "all", - [ - "==", - "$type", - "Point" - ], - [ - "!=", - "class", - "ocean" - ] - ], - "id":"water_name_point", - "layout":{ - "text-field":"{name:latin}\n{name:nonlatin}", - "text-font":[ - "Roboto Regular", - "Noto Sans Regular" - ], - "text-max-width":5, - "text-size":12, - "visibility":"visible" - }, - "maxzoom":24, - "metadata":{ - - }, - "minzoom":2, - "paint":{ - "text-color":"rgba(76, 125, 173, 1)", - "text-halo-color":"rgba(255,255,255,0)", - "text-halo-width":1 - }, - "source":"openmaptiles", - "source-layer":"water_name", - "type":"symbol" - }, - { - "filter":[ - "all", - [ - "==", - "$type", - "Point" - ], - [ - "==", - "class", - "ocean" - ] - ], - "id":"water_ocean_name_point", - "layout":{ - "text-field":"{name:latin}\n{name:nonlatin}", - "text-font":[ - "Roboto Regular", - "Noto Sans Regular" - ], - "text-max-width":5, - "text-size":12, - "visibility":"visible" - }, - "metadata":{ - - }, - "minzoom":0, - "paint":{ - "text-color":"rgba(76, 125, 173, 1)", - "text-halo-color":"rgba(255,255,255,0)", - "text-halo-width":1 - }, - "source":"openmaptiles", - "source-layer":"water_name", - "type":"symbol" - }, - { - "filter":[ - "all", - [ - "==", - "$type", - "Point" - ], - [ - ">=", - "rank", - 20 - ], - [ - "any", - [ - "all", - [ - "in", - "class", - "pitch" - ], - [ - "in", - "subclass", - "soccer", - "tennis", - "baseball", - "basketball", - "swimming", - "golf" - ] - ] - ], - [ - "any", - [ - "!has", - "level" - ], - [ - "==", - "level", - 0 - ] - ] - ], - "id":"poi_z16_subclass", - "layout":{ - "icon-image":"{subclass}_11", - "text-anchor":"top", - "text-field":"{name:latin}\n{name:nonlatin}", - "text-font":[ - "Roboto Condensed Italic", - "Noto Sans Italic" - ], - "text-max-width":9, - "text-offset":[ - 0, - 0.6 - ], - "text-padding":2, - "text-size":12, - "visibility":"visible" - }, - "metadata":{ - - }, - "minzoom":16, - "paint":{ - "text-color":"#666", - "text-halo-blur":0.5, - "text-halo-color":"#ffffff", - "text-halo-width":1 - }, - "source":"openmaptiles", - "source-layer":"poi", - "type":"symbol" - }, - { - "filter":[ - "all", - [ - "==", - "$type", - "Point" - ], - [ - ">=", - "rank", - 20 - ], - [ - "none", - [ - "all", - [ - "in", - "class", - "pitch" - ], - [ - "in", - "subclass", - "soccer", - "tennis", - "baseball", - "basketball", - "swimming", - "golf" - ] - ] - ], - [ - "any", - [ - "!has", - "level" - ], - [ - "==", - "level", - 0 - ] - ] - ], - "id":"poi_z16", - "layout":{ - "icon-image":"{class}_11", - "text-anchor":"top", - "text-field":"{name:latin}\n{name:nonlatin}", - "text-font":[ - "Roboto Condensed Italic", - "Noto Sans Italic" - ], - "text-max-width":9, - "text-offset":[ - 0, - 0.6 - ], - "text-padding":2, - "text-size":12, - "visibility":"visible" - }, - "metadata":{ - - }, - "minzoom":16, - "paint":{ - "text-color":"#666", - "text-halo-blur":0.5, - "text-halo-color":"#ffffff", - "text-halo-width":1 - }, - "source":"openmaptiles", - "source-layer":"poi", - "type":"symbol" - }, - { - "filter":[ - "all", - [ - "==", - "$type", - "Point" - ], - [ - ">=", - "rank", - 7 - ], - [ - "<", - "rank", - 20 - ], - [ - "any", - [ - "!has", - "level" - ], - [ - "==", - "level", - 0 - ] - ] - ], - "id":"poi_z15", - "layout":{ - "icon-image":"{class}_11", - "text-anchor":"top", - "text-field":"{name:latin}\n{name:nonlatin}", - "text-font":[ - "Roboto Condensed Italic", - "Noto Sans Italic" - ], - "text-max-width":9, - "text-offset":[ - 0, - 0.6 - ], - "text-padding":2, - "text-size":12, - "visibility":"visible" - }, - "metadata":{ - - }, - "minzoom":15, - "paint":{ - "text-color":"#666", - "text-halo-blur":0.5, - "text-halo-color":"#ffffff", - "text-halo-width":1 - }, - "source":"openmaptiles", - "source-layer":"poi", - "type":"symbol" - }, - { - "filter":[ - "all", - [ - "==", - "$type", - "Point" - ], - [ - "any", - [ - "<", - "rank", - 7 - ] - ], - [ - "any", - [ - "!has", - "level" - ], - [ - "==", - "level", - 0 - ] - ] - ], - "id":"poi_z14", - "layout":{ - "icon-image":"{class}_11", - "icon-size":0.9, - "text-anchor":"top", - "text-field":"{name:latin}\n{name:nonlatin}", - "text-font":[ - "Roboto Condensed Italic", - "Noto Sans Italic" - ], - "text-max-width":9, - "text-offset":[ - 0, - 0.6 - ], - "text-padding":2, - "text-size":12, - "visibility":"visible" - }, - "metadata":{ - - }, - "minzoom":14.2, - "paint":{ - "text-color":"#666", - "text-halo-blur":0.5, - "text-halo-color":"#ffffff", - "text-halo-width":1 - }, - "source":"openmaptiles", - "source-layer":"poi", - "type":"symbol" - }, - { - "filter":[ - "all", - [ - "in", - "class", - "bus", - "railway", - "airport" - ], - [ - "==", - "subclass", - "station" - ] - ], - "id":"poi_transit", - "layout":{ - "icon-image":"{class}_11", - "icon-size":0.9, - "text-anchor":"left", - "text-field":"{name:latin}\n{name:nonlatin}", - "text-font":[ - "Roboto Condensed Italic", - "Noto Sans Italic" - ], - "text-max-width":9, - "text-offset":[ - 0.9, - 0 - ], - "text-padding":2, - "text-size":12, - "visibility":"visible" - }, - "metadata":{ - - }, - "minzoom":15, - "paint":{ - "text-color":"rgba(102, 102, 102, 1)", - "text-halo-blur":0.5, - "text-halo-color":"#ffffff", - "text-halo-width":1 - }, - "source":"openmaptiles", - "source-layer":"poi", - "type":"symbol" - }, - { - "filter":[ - "all", - [ - "==", - "oneway", - 1 - ], - [ - "in", - "class", - "motorway", - "trunk", - "primary", - "secondary", - "tertiary", - "minor", - "service" - ] - ], - "id":"road_oneway", - "layout":{ - "icon-image":"oneway", - "icon-padding":2, - "icon-rotate":90, - "icon-rotation-alignment":"map", - "icon-size":{ - "stops":[ - [ - 15, - 0.5 - ], - [ - 19, - 1 - ] - ] - }, - "symbol-placement":"line", - "symbol-spacing":75, - "visibility":"visible" - }, - "minzoom":15, - "paint":{ - "icon-opacity":0.5 - }, - "source":"openmaptiles", - "source-layer":"transportation", - "type":"symbol" - }, - { - "filter":[ - "all", - [ - "==", - "oneway", - -1 - ], - [ - "in", - "class", - "motorway", - "trunk", - "primary", - "secondary", - "tertiary", - "minor", - "service" - ] - ], - "id":"road_oneway_opposite", - "layout":{ - "icon-image":"oneway", - "icon-padding":2, - "icon-rotate":-90, - "icon-rotation-alignment":"map", - "icon-size":{ - "stops":[ - [ - 15, - 0.5 - ], - [ - 19, - 1 - ] - ] - }, - "symbol-placement":"line", - "symbol-spacing":75 - }, - "minzoom":15, - "paint":{ - "icon-opacity":0.5 - }, - "source":"openmaptiles", - "source-layer":"transportation", - "type":"symbol" - }, - { - "filter":[ - "all" - ], - "id":"road_label", - "layout":{ - "symbol-placement":"line", - "text-anchor":"center", - "text-field":"{name:latin} {name:nonlatin}", - "text-font":[ - "Roboto Regular", - "Noto Sans Regular" - ], - "text-offset":[ - 0, - 0.15 - ], - "text-size":{ - "base":1, - "stops":[ - [ - 13, - 12 - ], - [ - 14, - 13 - ] - ] - } - }, - "metadata":{ - - }, - "paint":{ - "text-color":"#765", - "text-halo-blur":0.5, - "text-halo-width":1 - }, - "source":"openmaptiles", - "source-layer":"transportation_name", - "type":"symbol" - }, - { - "filter":[ - "all", - [ - "<=", - "ref_length", - 6 - ], - [ - "==", - "$type", - "LineString" - ], - [ - "!in", - "network", - "us-interstate", - "us-highway", - "us-state" - ] - ], - "id":"highway-shield", - "layout":{ - "icon-image":"road_{ref_length}", - "icon-rotation-alignment":"viewport", - "icon-size":1, - "symbol-avoid-edges":true, - "symbol-placement":{ - "base":1, - "stops":[ - [ - 10, - "point" - ], - [ - 11, - "line" - ] - ] - }, - "symbol-spacing":200, - "text-field":"{ref}", - "text-font":[ - "Noto Sans Regular" - ], - "text-rotation-alignment":"viewport", - "text-size":10 - }, - "minzoom":8, - "paint":{ - "text-color":"rgba(37, 36, 36, 1)" - }, - "source":"openmaptiles", - "source-layer":"transportation_name", - "type":"symbol" - }, - { - "filter":[ - "all", - [ - "<=", - "ref_length", - 6 - ], - [ - "==", - "$type", - "LineString" - ], - [ - "in", - "network", - "us-interstate" - ] - ], - "id":"highway-shield-us-interstate", - "layout":{ - "icon-image":"{network}_{ref_length}", - "icon-rotation-alignment":"viewport", - "icon-size":1, - "symbol-avoid-edges":true, - "symbol-placement":{ - "base":1, - "stops":[ - [ - 7, - "point" - ], - [ - 7, - "line" - ], - [ - 8, - "line" - ] - ] - }, - "symbol-spacing":200, - "text-field":"{ref}", - "text-font":[ - "Noto Sans Regular" - ], - "text-rotation-alignment":"viewport", - "text-size":9 - }, - "minzoom":7, - "paint":{ - "text-color":"rgba(255, 255, 255, 1)" - }, - "source":"openmaptiles", - "source-layer":"transportation_name", - "type":"symbol" - }, - { - "filter":[ - "all", - [ - "<=", - "ref_length", - 6 - ], - [ - "==", - "$type", - "LineString" - ], - [ - "in", - "network", - "us-highway", - "us-state" - ] - ], - "id":"highway-shield-us-other", - "layout":{ - "icon-image":"{network}_{ref_length}", - "icon-rotation-alignment":"viewport", - "icon-size":1, - "symbol-avoid-edges":true, - "symbol-placement":{ - "base":1, - "stops":[ - [ - 10, - "point" - ], - [ - 11, - "line" - ] - ] - }, - "symbol-spacing":200, - "text-field":"{ref}", - "text-font":[ - "Noto Sans Regular" - ], - "text-rotation-alignment":"viewport", - "text-size":9 - }, - "minzoom":9, - "paint":{ - "text-color":"rgba(37, 36, 36, 1)" - }, - "source":"openmaptiles", - "source-layer":"transportation_name", - "type":"symbol" - }, - { - "filter":[ - "all", - [ - "has", - "iata" - ] - ], - "id":"airport-label-major", - "layout":{ - "icon-image":"airport_11", - "icon-size":1, - "text-anchor":"top", - "text-field":"{name:latin}\n{name:nonlatin}", - "text-font":[ - "Noto Sans Regular" - ], - "text-max-width":9, - "text-offset":[ - 0, - 0.6 - ], - "text-optional":true, - "text-padding":2, - "text-size":12, - "visibility":"visible" - }, - "minzoom":10, - "paint":{ - "text-color":"#666", - "text-halo-blur":0.5, - "text-halo-color":"#ffffff", - "text-halo-width":1 - }, - "source":"openmaptiles", - "source-layer":"aerodrome_label", - "type":"symbol" - }, - { - "filter":[ - "all", - [ - "in", - "class", - "hamlet", - "island", - "islet", - "neighbourhood", - "suburb" - ] - ], - "id":"place_other", - "layout":{ - "text-field":"{name:latin}\n{name:nonlatin}", - "text-font":[ - "Roboto Condensed Italic", - "Noto Sans Italic" - ], - "text-letter-spacing":0.1, - "text-max-width":9, - "text-size":{ - "base":1.2, - "stops":[ - [ - 12, - 10 - ], - [ - 15, - 14 - ] - ] - }, - "text-transform":"uppercase", - "visibility":"visible" - }, - "metadata":{ - - }, - "paint":{ - "text-color":"rgba(66, 62, 62, 1)", - "text-halo-color":"rgba(255,255,255,0.8)", - "text-halo-width":1.2 - }, - "source":"openmaptiles", - "source-layer":"place", - "type":"symbol" - }, - { - "filter":[ - "all", - [ - "==", - "class", - "village" - ] - ], - "id":"place_village", - "layout":{ - "text-field":"{name:latin}\n{name:nonlatin}", - "text-font":[ - "Roboto Regular", - "Noto Sans Regular" - ], - "text-max-width":8, - "text-size":{ - "base":1.2, - "stops":[ - [ - 10, - 12 - ], - [ - 15, - 22 - ] - ] - } - }, - "metadata":{ - - }, - "paint":{ - "text-color":"#333", - "text-halo-color":"rgba(255,255,255,0.8)", - "text-halo-width":1.2 - }, - "source":"openmaptiles", - "source-layer":"place", - "type":"symbol" - }, - { - "filter":[ - "all", - [ - "==", - "class", - "town" - ] - ], - "id":"place_town", - "layout":{ - "icon-image":{ - "base":1, - "stops":[ - [ - 0, - "circle-stroked_16" - ], - [ - 10, - "" - ] - ] - }, - "text-anchor":"bottom", - "text-field":"{name:latin}\n{name:nonlatin}", - "text-font":[ - "Roboto Regular", - "Noto Sans Regular" - ], - "text-max-width":8, - "text-offset":[ - 0, - 0 - ], - "text-size":{ - "base":1.2, - "stops":[ - [ - 7, - 12 - ], - [ - 11, - 16 - ] - ] - } - }, - "metadata":{ - - }, - "paint":{ - "text-color":"#333", - "text-halo-color":"rgba(255,255,255,0.8)", - "text-halo-width":1.2 - }, - "source":"openmaptiles", - "source-layer":"place", - "type":"symbol" - }, - { - "filter":[ - "all", - [ - "==", - "class", - "city" - ] - ], - "id":"place_city", - "layout":{ - "icon-allow-overlap":true, - "icon-image":{ - "base":1, - "stops":[ - [ - 0, - "circle-stroked_16" - ], - [ - 10, - "" - ] - ] - }, - "icon-optional":false, - "text-anchor":"bottom", - "text-field":"{name:latin}\n{name:nonlatin}", - "text-font":[ - "Roboto Medium", - "Noto Sans Regular" - ], - "text-max-width":8, - "text-offset":[ - 0, - 0 - ], - "text-size":{ - "base":1.2, - "stops":[ - [ - 7, - 14 - ], - [ - 11, - 24 - ] - ] - } - }, - "metadata":{ - - }, - "minzoom":5, - "paint":{ - "text-color":"#333", - "text-halo-color":"rgba(255,255,255,0.8)", - "text-halo-width":1.2 - }, - "source":"openmaptiles", - "source-layer":"place", - "type":"symbol" - }, - { - "filter":[ - "all", - [ - "==", - "class", - "state" - ] - ], - "id":"state", - "layout":{ - "text-field":"{name:latin}\n{name:nonlatin}", - "text-font":[ - "Roboto Medium", - "Noto Sans Regular" - ], - "text-letter-spacing":0.1, - "text-padding":2, - "text-size":{ - "stops":[ - [ - 4, - 11 - ], - [ - 6, - 15 - ] - ] - }, - "text-transform":"uppercase" - }, - "maxzoom":6, - "metadata":{ - - }, - "paint":{ - "text-color":"rgba(74, 72, 66, 1)", - "text-halo-color":"rgba(255,255,255,0.7)", - "text-halo-width":0.8 - }, - "source":"openmaptiles", - "source-layer":"place", - "type":"symbol" - }, - { - "filter":[ - "all", - [ - "==", - "class", - "country" - ], - [ - "!has", - "iso_a2" - ] - ], - "id":"country_other", - "layout":{ - "text-field":"{name:latin}", - "text-font":[ - "Roboto Condensed Italic", - "Noto Sans Italic" - ], - "text-max-width":6.25, - "text-size":{ - "stops":[ - [ - 3, - 9 - ], - [ - 7, - 15 - ] - ] - }, - "text-transform":"none" - }, - "metadata":{ - - }, - "paint":{ - "text-color":"#334", - "text-halo-blur":1, - "text-halo-color":"rgba(255,255,255,0.8)", - "text-halo-width":0.8 - }, - "source":"openmaptiles", - "source-layer":"place", - "type":"symbol" - }, - { - "filter":[ - "all", - [ - ">=", - "rank", - 3 - ], - [ - "==", - "class", - "country" - ], - [ - "has", - "iso_a2" - ] - ], - "id":"country_3", - "layout":{ - "text-field":"{name:latin}", - "text-font":[ - "Roboto Condensed Italic", - "Noto Sans Italic" - ], - "text-max-width":6.25, - "text-size":{ - "stops":[ - [ - 3, - 11 - ], - [ - 7, - 17 - ] - ] - }, - "text-transform":"none" - }, - "metadata":{ - - }, - "paint":{ - "text-color":"#334", - "text-halo-blur":1, - "text-halo-color":"rgba(255,255,255,0.8)", - "text-halo-width":0.8 - }, - "source":"openmaptiles", - "source-layer":"place", - "type":"symbol" - }, - { - "filter":[ - "all", - [ - "==", - "rank", - 2 - ], - [ - "==", - "class", - "country" - ], - [ - "has", - "iso_a2" - ] - ], - "id":"country_2", - "layout":{ - "text-field":"{name:latin}", - "text-font":[ - "Roboto Condensed Italic", - "Noto Sans Italic" - ], - "text-max-width":6.25, - "text-size":{ - "stops":[ - [ - 2, - 11 - ], - [ - 5, - 17 - ] - ] - }, - "text-transform":"none" - }, - "metadata":{ - - }, - "paint":{ - "text-color":"#334", - "text-halo-blur":1, - "text-halo-color":"rgba(255,255,255,0.8)", - "text-halo-width":0.8 - }, - "source":"openmaptiles", - "source-layer":"place", - "type":"symbol" - }, - { - "filter":[ - "all", - [ - "==", - "rank", - 1 - ], - [ - "==", - "class", - "country" - ], - [ - "has", - "iso_a2" - ] - ], - "id":"country_1", - "layout":{ - "text-field":"{name:latin}", - "text-font":[ - "Roboto Condensed Italic", - "Noto Sans Italic" - ], - "text-max-width":6.25, - "text-size":{ - "stops":[ - [ - 1, - 11 - ], - [ - 4, - 17 - ] - ] - }, - "text-transform":"none" - }, - "metadata":{ - - }, - "paint":{ - "text-color":"#334", - "text-halo-blur":1, - "text-halo-color":"rgba(255,255,255,0.8)", - "text-halo-width":0.8 - }, - "source":"openmaptiles", - "source-layer":"place", - "type":"symbol" - }, - { - "filter":[ - "all", - [ - "==", - "class", - "continent" - ] - ], - "id":"continent", - "layout":{ - "text-field":"{name:latin}", - "text-font":[ - "Roboto Condensed Italic", - "Noto Sans Italic" - ], - "text-justify":"center", - "text-size":13, - "text-transform":"uppercase" - }, - "maxzoom":1, - "metadata":{ - - }, - "paint":{ - "text-color":"#633", - "text-halo-color":"rgba(255,255,255,0.7)", - "text-halo-width":1 - }, - "source":"openmaptiles", - "source-layer":"place", - "type":"symbol" - }, - { - "id":"housenumber", - "layout":{ - "text-field":"{housenumber}", - "text-font":[ - "Roboto Medium", - "Noto Sans Regular" - ], - "text-size":10 - }, - "minzoom":17.5, - "paint":{ - "text-color":"rgba(119, 102, 85, 0.69)" - }, - "source":"openmaptiles", - "source-layer":"housenumber", - "type":"symbol" - } - ], - "metadata":{ - "mapbox:type":"template", - "maptiler:copyright":"This style was generated on MapTiler Cloud. Usage outside of MapTiler Cloud requires valid OpenMapTiles Production Package: https://openmaptiles.com/production-package/ -- please contact us.", - "openmaptiles:version":"3.x" - }, - "name":"Streets", - "pitch":0, - "sources":{ - "maptiler_attribution":{ - "attribution":"© MapTiler © OpenStreetMap contributors", - "type":"vector" - }, - "openmaptiles":{ - "type":"vector", - "url":"https://api.maptiler.com/tiles/v3/tiles.json?key=MapTilerKey" - } - }, - "sprite":"https://api.maptiler.com/maps/streets/sprite", - "version":8, - "zoom":1.284245729371679 -} diff --git a/android/apps/AutoTesterAndroid/app/src/main/assets/maptiler_streets.json b/android/apps/AutoTesterAndroid/app/src/main/assets/maptiler_streets.json new file mode 120000 index 0000000000..74680cae56 --- /dev/null +++ b/android/apps/AutoTesterAndroid/app/src/main/assets/maptiler_streets.json @@ -0,0 +1 @@ +../../../../../../../resources/vectors/styles/maptiler_streets.json \ No newline at end of file diff --git a/android/apps/AutoTesterAndroid/app/src/main/assets/maptiler_test_circles.json b/android/apps/AutoTesterAndroid/app/src/main/assets/maptiler_test_circles.json new file mode 120000 index 0000000000..7158f0971a --- /dev/null +++ b/android/apps/AutoTesterAndroid/app/src/main/assets/maptiler_test_circles.json @@ -0,0 +1 @@ +../../../../../../../resources/vectors/styles/maptiler_test_circles.json \ No newline at end of file diff --git a/android/apps/AutoTesterAndroid/app/src/main/assets/maptiler_topo.json b/android/apps/AutoTesterAndroid/app/src/main/assets/maptiler_topo.json new file mode 120000 index 0000000000..a2b539d656 --- /dev/null +++ b/android/apps/AutoTesterAndroid/app/src/main/assets/maptiler_topo.json @@ -0,0 +1 @@ +../../../../../../../resources/vectors/styles/maptiler_topo.json \ No newline at end of file diff --git a/android/apps/AutoTesterAndroid/app/src/main/assets/mbtiles/overlay_obstacles.mbtiles b/android/apps/AutoTesterAndroid/app/src/main/assets/mbtiles/overlay_obstacles.mbtiles new file mode 100644 index 0000000000..8a01237444 Binary files /dev/null and b/android/apps/AutoTesterAndroid/app/src/main/assets/mbtiles/overlay_obstacles.mbtiles differ diff --git a/android/apps/AutoTesterAndroid/app/src/main/assets/tall.png b/android/apps/AutoTesterAndroid/app/src/main/assets/tall.png new file mode 100644 index 0000000000..7e397c1a66 Binary files /dev/null and b/android/apps/AutoTesterAndroid/app/src/main/assets/tall.png differ diff --git a/android/apps/AutoTesterAndroid/app/src/main/assets/wide.png b/android/apps/AutoTesterAndroid/app/src/main/assets/wide.png new file mode 100644 index 0000000000..85add68593 Binary files /dev/null and b/android/apps/AutoTesterAndroid/app/src/main/assets/wide.png differ diff --git a/android/apps/AutoTesterAndroid/app/src/main/assets/wide_vecs/line.geojson b/android/apps/AutoTesterAndroid/app/src/main/assets/wide_vecs/line.geojson new file mode 100644 index 0000000000..3c5c0518c6 --- /dev/null +++ b/android/apps/AutoTesterAndroid/app/src/main/assets/wide_vecs/line.geojson @@ -0,0 +1 @@ +{"type": "FeatureCollection", "features": [{"geometry": {"type": "MultiLineString", "coordinates": [[[-80.290556, 25.793333, 1247.9, 1409427928.0], [-122.309444, 47.448889, 1243.4, 1409427935.0]]]}, "type": "Feature", "properties": {"updated_date": "2014-09-03T22:56:12.674", "color": "#6750b4", "track_type": "", "time_created": "2014-09-03T22:55:18.016", "id": "bb7d9234f9b813b35527654c6aa46ccb", "title": "mowing lawn", "average_speed": 0, "stopped_time": null, "latitude": 46.03257254601325, "db_insert_date": "2014-09-03T22:55:18.016", "public": false, "moving_speed": 0, "revision": 3078, "total_time": null, "deleted": false, "is_active": true, "moving_time": 0, "flag": null, "preferred_link": "/public/eILNubIb6SvrfDEQ4JZUN205", "total_descent": null, "distance": 4043.36301379455, "notes": "", "longitude": -114.19690560964163, "total_ascent": null}}]} diff --git a/android/apps/AutoTesterAndroid/app/src/main/java/com/mousebirdconsulting/autotester/Fragments/TestListFragment.java b/android/apps/AutoTesterAndroid/app/src/main/java/com/mousebirdconsulting/autotester/Fragments/TestListFragment.java index 4d9d6597d2..a4350aeffe 100644 --- a/android/apps/AutoTesterAndroid/app/src/main/java/com/mousebirdconsulting/autotester/Fragments/TestListFragment.java +++ b/android/apps/AutoTesterAndroid/app/src/main/java/com/mousebirdconsulting/autotester/Fragments/TestListFragment.java @@ -1,5 +1,7 @@ package com.mousebirdconsulting.autotester.Fragments; +import android.app.Activity; +import android.content.Context; import android.os.Bundle; import androidx.annotation.Nullable; import androidx.fragment.app.Fragment; @@ -17,38 +19,12 @@ import com.mousebirdconsulting.autotester.Framework.MaplyTestCase; import com.mousebirdconsulting.autotester.MainActivity; import com.mousebirdconsulting.autotester.R; -import com.mousebirdconsulting.autotester.TestCases.AnimatedBaseMapTestCase; -import com.mousebirdconsulting.autotester.TestCases.AnimatedMarkersTestCase; -import com.mousebirdconsulting.autotester.TestCases.AnimatedScreenMarkersTestCase; -import com.mousebirdconsulting.autotester.TestCases.CartoTestCase; -import com.mousebirdconsulting.autotester.TestCases.ClusteredMarkersTestCase; -import com.mousebirdconsulting.autotester.TestCases.CustomBNGCoordAdapter; -import com.mousebirdconsulting.autotester.TestCases.CustomBNGTileSource; -//import com.mousebirdconsulting.autotester.TestCases.GreatCircleTestCase; -import com.mousebirdconsulting.autotester.TestCases.FindHeightTestCase; -import com.mousebirdconsulting.autotester.TestCases.GeographyClass; -import com.mousebirdconsulting.autotester.TestCases.GreatCircleTestCase; -import com.mousebirdconsulting.autotester.TestCases.ImageReloadTestCase; -import com.mousebirdconsulting.autotester.TestCases.LoftedPolyTestCase; -import com.mousebirdconsulting.autotester.TestCases.MapTilerTestCase; -import com.mousebirdconsulting.autotester.TestCases.MarkersTestCase; -import com.mousebirdconsulting.autotester.TestCases.OpenMapTilesHybridTestCase; -import com.mousebirdconsulting.autotester.TestCases.PagingLayerTestCase; -import com.mousebirdconsulting.autotester.TestCases.SLDTestCase; -import com.mousebirdconsulting.autotester.TestCases.ScreenLabelsTestCase; -import com.mousebirdconsulting.autotester.TestCases.ScreenMarkersTestCase; -import com.mousebirdconsulting.autotester.TestCases.ShapefileTestCase; -import com.mousebirdconsulting.autotester.TestCases.ShapesTestCase; -import com.mousebirdconsulting.autotester.TestCases.StamenRemoteTestCase; -import com.mousebirdconsulting.autotester.TestCases.StickersTestCase; -import com.mousebirdconsulting.autotester.TestCases.TextureVectorTestCase; -import com.mousebirdconsulting.autotester.TestCases.VectorHoleTestCase; -import com.mousebirdconsulting.autotester.TestCases.VectorMBTilesTestCase; -import com.mousebirdconsulting.autotester.TestCases.VectorStyleTestCase; -import com.mousebirdconsulting.autotester.TestCases.VectorsTestCase; -import com.mousebirdconsulting.autotester.TestCases.WideVectorsTestCase; +import com.mousebirdconsulting.autotester.TestCases.*; + +import org.jetbrains.annotations.NotNull; import java.util.ArrayList; +import java.util.Objects; public class TestListFragment extends Fragment { @@ -76,7 +52,7 @@ public void onDestroyView() { } private RecyclerView.LayoutManager createLayoutManager() { - return new LinearLayoutManager(getActivity().getApplicationContext()); + return new LinearLayoutManager(Objects.requireNonNull(getActivity()).getApplicationContext()); } public void changeItemsState(boolean selected) { @@ -99,67 +75,74 @@ public void downloadResources() { private class TestListAdapter extends RecyclerView.Adapter { - private ArrayList testCases; + final private ArrayList testCases = new ArrayList<>(); TestListAdapter() { - testCases = new ArrayList<>(); - testCases.add(new StamenRemoteTestCase(getActivity())); - testCases.add(new GeographyClass(getActivity())); - testCases.add(new AnimatedBaseMapTestCase(getActivity())); - testCases.add(new ImageReloadTestCase(getActivity())); - testCases.add(new CustomBNGCoordAdapter(getActivity())); - testCases.add(new CustomBNGTileSource(getActivity())); - testCases.add(new ScreenLabelsTestCase(getActivity())); - testCases.add(new ScreenMarkersTestCase(getActivity())); - testCases.add(new MarkersTestCase(getActivity())); - testCases.add(new AnimatedScreenMarkersTestCase(getActivity())); - testCases.add(new AnimatedMarkersTestCase(getActivity())); - testCases.add(new ClusteredMarkersTestCase(getActivity())); - testCases.add(new VectorsTestCase(getActivity())); - testCases.add(new GreatCircleTestCase(getActivity())); - testCases.add(new VectorStyleTestCase(getActivity())); - testCases.add(new VectorHoleTestCase(getActivity())); - testCases.add(new ShapefileTestCase(getActivity())); - testCases.add(new WideVectorsTestCase(getActivity())); - testCases.add(new TextureVectorTestCase(getActivity())); - testCases.add(new SLDTestCase(getActivity())); - testCases.add(new LoftedPolyTestCase(getActivity())); - testCases.add(new StickersTestCase(getActivity())); - testCases.add(new PagingLayerTestCase(getActivity())); - testCases.add(new VectorMBTilesTestCase(getActivity())); - testCases.add(new MapTilerTestCase(getActivity())); - testCases.add(new OpenMapTilesHybridTestCase(getActivity())); - testCases.add(new CartoTestCase(getActivity())); - testCases.add(new ShapesTestCase(getActivity())); + Activity a = Objects.requireNonNull(getActivity()); + testCases.add(new StamenRemoteTestCase(a)); + testCases.add(new GeographyClass(a)); + testCases.add(new AnimatedBaseMapTestCase(a)); + testCases.add(new ImageReloadTestCase(a)); + testCases.add(new CustomBNGCoordAdapter(a)); + testCases.add(new CustomBNGTileSource(a)); + testCases.add(new ScreenLabelsTestCase(a)); + testCases.add(new ScreenMarkersTestCase(a)); + testCases.add(new MarkersTestCase(a)); + testCases.add(new AnimatedScreenMarkersTestCase(a)); + testCases.add(new AnimatedMarkersTestCase(a)); + testCases.add(new ClusteredMarkersTestCase(a)); + testCases.add(new MovingScreenMarkersTestCase(a)); + testCases.add(new VectorsTestCase(a)); + testCases.add(new GreatCircleTestCase(a)); + testCases.add(new SimpleStyleTestCase(a)); + testCases.add(new VectorStyleTestCase(a)); + testCases.add(new VectorHoleTestCase(a)); + testCases.add(new ShapefileTestCase(a)); + testCases.add(new WideVectorsTestCase(a)); + testCases.add(new TextureVectorTestCase(a)); + testCases.add(new SLDTestCase(a)); + testCases.add(new LoftedPolyTestCase(a)); + testCases.add(new StickersTestCase(a)); + testCases.add(new PagingLayerTestCase(a)); + testCases.add(new VectorMBTilesTestCase(a)); + testCases.add(new MapTilerTestCase(a)); + testCases.add(new MapTilerCircleTestCase(a)); + testCases.add(new OpenMapTilesHybridTestCase(a)); + testCases.add(new CartoTestCase(a)); + testCases.add(new ShapesTestCase(a)); // Extruded Model (Arrows) // Models -// testCases.add(new MaplyStarModelTestCase(getActivity())); - testCases.add(new FindHeightTestCase(getActivity())); +// testCases.add(new MaplyStarModelTestCase(a)); + testCases.add(new FindHeightTestCase(a)); // Animating Position -// testCases.add(new GestureFeedbackTestCase(getActivity())); -// testCases.add(new ComponentObjectLeakTestCase(getActivity())); -// testCases.add(new LightingTestCase(getActivity())); -// testCases.add( new BillboardTestCase(getActivity())); -// testCases.add(new CoordConversionTestCase(getActivity())); -// testCases.add(new StartupShutdownTestCase(getActivity())); -// testCases.add(new MarkersAndLinesTestCase(getActivity())); -// testCases.add(new BoundsTestCase(getActivity())); -// testCases.add(new LayerShutdownTestCase(getActivity())); -// testCases.add(new GeomPointsTestCase(getActivity())); -// testCases.add(new AutoRotateTestCase(getActivity())); -// testCases.add(new ArealTestCase(getActivity())); +// testCases.add(new GestureFeedbackTestCase(a)); +// testCases.add(new ComponentObjectLeakTestCase(a)); +// testCases.add(new LightingTestCase(a)); +// testCases.add( new BillboardTestCase(a)); +// testCases.add(new CoordConversionTestCase(a)); +// testCases.add(new StartupShutdownTestCase(a)); +// testCases.add(new MarkersAndLinesTestCase(a)); +// testCases.add(new BoundsTestCase(a)); +// testCases.add(new LayerShutdownTestCase(a)); +// testCases.add(new GeomPointsTestCase(a)); +// testCases.add(new AutoRotateTestCase(a)); +// testCases.add(new ArealTestCase(a)); + testCases.add(new LocationTrackingRealTestCase(a)); + testCases.add(new LocationTrackingSimTestCase(a)); + testCases.add(new GlobeRotationTestCase(a)); } public void downloadResources() { ArrayList incompleteTest = new ArrayList<>(); + Context context = Objects.requireNonNull(getContext()); for (MaplyTestCase testCase : this.testCases) { if (!testCase.areResourcesDownloaded()) { incompleteTest.add(testCase); - ConfigOptions.setTestState(getContext(), testCase.getTestName(), ConfigOptions.TestState.Downloading); + ConfigOptions.setTestState(context, testCase.getTestName(), ConfigOptions.TestState.Downloading); } else { - if (ConfigOptions.getTestState(getContext(), testCase.getTestName()) != ConfigOptions.TestState.Selected) { - ConfigOptions.setTestState(getContext(), testCase.getTestName(), ConfigOptions.TestState.Ready); + if (ConfigOptions.getTestState(context, testCase.getTestName()) != ConfigOptions.TestState.Selected) { + ConfigOptions.setTestState(context, testCase.getTestName(), ConfigOptions.TestState.Ready); } } adapter.notifyDataSetChanged(); @@ -202,14 +185,15 @@ public void onTestFinished(MaplyTestCase testCase) { manager.execute(); } + @NotNull @Override - public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { + public RecyclerView.ViewHolder onCreateViewHolder(@NotNull ViewGroup parent, int viewType) { View view = LayoutInflater.from(getContext()).inflate(R.layout.testlistitemview, parent, false); return new TestViewHolder(view); } @Override - public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) { + public void onBindViewHolder(@NotNull RecyclerView.ViewHolder holder, int position) { ((TestViewHolder) holder).bindViewHolder(testCases.get(position), position); } @@ -219,9 +203,10 @@ public int getItemCount() { } public void changeItemsState(boolean selected) { + Context context = Objects.requireNonNull(getContext()); for (MaplyTestCase testCase : testCases) { ConfigOptions.TestState state = selected ? ConfigOptions.TestState.Ready : ConfigOptions.TestState.Selected; - ConfigOptions.setTestState(getContext(), testCase.getTestName(), state); + ConfigOptions.setTestState(context, testCase.getTestName(), state); } notifyDataSetChanged(); } @@ -233,10 +218,8 @@ public ArrayList getTestCases() { private class TestViewHolder extends RecyclerView.ViewHolder { - private TextView label; - private ImageView selected, map, globe, retry, download; - private View self; - private MaplyTestCase testCase; + final private TextView label; + final private ImageView selected, map, globe, retry, download; private int index; public int getIndex() { @@ -251,33 +234,29 @@ public TestViewHolder(View itemView) { globe = (ImageView) itemView.findViewById(R.id.globe_icon); retry = (ImageView) itemView.findViewById(R.id.retryDownload); download = (ImageView) itemView.findViewById(R.id.downloading); - self = itemView; + //self = itemView; } public void bindViewHolder(final MaplyTestCase testCase, final int index) { - this.testCase = testCase; + //final private View self; this.index = index; - this.label.setText(this.testCase.getTestName()); - final MainActivity activity = (MainActivity) getActivity(); + this.label.setText(testCase.getTestName()); + final MainActivity activity = Objects.requireNonNull((MainActivity)getActivity()); + final Context context = Objects.requireNonNull(getContext()); //if error - switch (ConfigOptions.getTestState(getContext(), testCase.getTestName())) { + switch (ConfigOptions.getTestState(context, testCase.getTestName())) { case Error: - itemView.setBackgroundColor(ContextCompat.getColor(getActivity(), R.color.colorRed)); + itemView.setBackgroundColor(ContextCompat.getColor(activity, R.color.colorRed)); retry.setVisibility(View.VISIBLE); selected.setVisibility(View.INVISIBLE); map.setVisibility(View.INVISIBLE); globe.setVisibility(View.INVISIBLE); download.setVisibility(View.INVISIBLE); - retry.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View v) { - adapter.downloadTestResources(index); - } - }); + retry.setOnClickListener(v -> adapter.downloadTestResources(index)); break; case Downloading: - itemView.setBackgroundColor(ContextCompat.getColor(getActivity(), R.color.colorGreen)); + itemView.setBackgroundColor(ContextCompat.getColor(activity, R.color.colorGreen)); retry.setVisibility(View.INVISIBLE); selected.setVisibility(View.INVISIBLE); map.setVisibility(View.INVISIBLE); @@ -289,58 +268,49 @@ public void onClick(View v) { case Ready: retry.setVisibility(View.INVISIBLE); download.setVisibility(View.INVISIBLE); - itemView.setBackgroundColor(ContextCompat.getColor(getActivity(), R.color.colorWhite)); - switch (ConfigOptions.getExecutionMode(getContext())) { + itemView.setBackgroundColor(ContextCompat.getColor(activity, R.color.colorWhite)); + switch (ConfigOptions.getExecutionMode(context)) { case Multiple: - changeItemState(ConfigOptions.getTestState(getContext(),testCase.getTestName()) == ConfigOptions.TestState.Selected); + changeItemState(ConfigOptions.getTestState(context,testCase.getTestName()) == ConfigOptions.TestState.Selected); map.setVisibility(View.INVISIBLE); globe.setVisibility(View.INVISIBLE); - itemView.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View v) { - if (ConfigOptions.getTestState(getContext(), testCase.getTestName()) == ConfigOptions.TestState.Ready){ - ConfigOptions.setTestState(getContext(), testCase.getTestName(), ConfigOptions.TestState.Selected); - } else { - ConfigOptions.setTestState(getContext(), testCase.getTestName(), ConfigOptions.TestState.Ready); - } - changeItemState(ConfigOptions.getTestState(getContext(),testCase.getTestName()) == ConfigOptions.TestState.Selected); - notifyItemChanged(index); + itemView.setOnClickListener(v -> { + if (ConfigOptions.getTestState(context, testCase.getTestName()) == ConfigOptions.TestState.Ready){ + ConfigOptions.setTestState(context, testCase.getTestName(), ConfigOptions.TestState.Selected); + } else { + ConfigOptions.setTestState(context, testCase.getTestName(), ConfigOptions.TestState.Ready); } + changeItemState(ConfigOptions.getTestState(context,testCase.getTestName()) == ConfigOptions.TestState.Selected); + notifyItemChanged(index); }); break; case Interactive: selected.setVisibility(View.INVISIBLE); - if (this.testCase.getImplementation() == MaplyTestCase.TestExecutionImplementation.Both || this.testCase.getImplementation() == MaplyTestCase.TestExecutionImplementation.Map) { + if (testCase.getImplementation() == MaplyTestCase.TestExecutionImplementation.Both || testCase.getImplementation() == MaplyTestCase.TestExecutionImplementation.Map) { map.setVisibility(View.VISIBLE); } else { map.setVisibility(View.INVISIBLE); } - if (this.testCase.getImplementation() == MaplyTestCase.TestExecutionImplementation.Both || this.testCase.getImplementation() == MaplyTestCase.TestExecutionImplementation.Globe) { + if (testCase.getImplementation() == MaplyTestCase.TestExecutionImplementation.Both || testCase.getImplementation() == MaplyTestCase.TestExecutionImplementation.Globe) { globe.setVisibility(View.VISIBLE); } else { globe.setVisibility(View.INVISIBLE); } - map.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View v) { - ConfigOptions.setTestType(getContext(), ConfigOptions.TestType.MapTest); - if (!activity.isExecuting()) { - activity.prepareTest(testCase); - activity.runTest(testCase); - } + map.setOnClickListener(v -> { + ConfigOptions.setTestType(getContext(), ConfigOptions.TestType.MapTest); + if (!activity.isExecuting()) { + activity.prepareTest(testCase); + activity.runTest(testCase); } }); - globe.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View v) { - ConfigOptions.setTestType(getContext(), ConfigOptions.TestType.GlobeTest); - if (!activity.isExecuting()) { - activity.prepareTest(testCase); - activity.runTest(testCase); - } + globe.setOnClickListener(v -> { + ConfigOptions.setTestType(getContext(), ConfigOptions.TestType.GlobeTest); + if (!activity.isExecuting()) { + activity.prepareTest(testCase); + activity.runTest(testCase); } }); break; @@ -349,13 +319,10 @@ public void onClick(View v) { selected.setVisibility(View.INVISIBLE); map.setVisibility(View.INVISIBLE); globe.setVisibility(View.INVISIBLE); - itemView.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View v) { - if (!activity.isExecuting()) { - activity.prepareTest(testCase); - activity.runTest(testCase); - } + itemView.setOnClickListener(v -> { + if (!activity.isExecuting()) { + activity.prepareTest(testCase); + activity.runTest(testCase); } }); break; @@ -368,12 +335,11 @@ public void onClick(View v) { globe.setVisibility(View.INVISIBLE); retry.setVisibility(View.INVISIBLE); download.setVisibility(View.INVISIBLE); - switch (ConfigOptions.getExecutionMode(getContext())) { + switch (ConfigOptions.getExecutionMode(context)) { case Multiple: this.selected.setImageDrawable(getResources().getDrawable(R.drawable.ic_options_action)); break; case Interactive: - break; case Single: break; } diff --git a/android/apps/AutoTesterAndroid/app/src/main/java/com/mousebirdconsulting/autotester/Framework/MaplyTestCase.java b/android/apps/AutoTesterAndroid/app/src/main/java/com/mousebirdconsulting/autotester/Framework/MaplyTestCase.java index 39e902fb84..0e4ddb10a0 100644 --- a/android/apps/AutoTesterAndroid/app/src/main/java/com/mousebirdconsulting/autotester/Framework/MaplyTestCase.java +++ b/android/apps/AutoTesterAndroid/app/src/main/java/com/mousebirdconsulting/autotester/Framework/MaplyTestCase.java @@ -1,12 +1,15 @@ package com.mousebirdconsulting.autotester.Framework; - import android.app.Activity; +import android.content.res.Resources; import android.graphics.Color; import android.os.AsyncTask; import android.os.Handler; +import android.util.DisplayMetrics; import android.util.Log; +import android.view.Display; import android.view.View; +import android.view.WindowManager; import com.mousebird.maply.GlobeController; import com.mousebird.maply.MapController; @@ -18,6 +21,7 @@ import com.mousebirdconsulting.autotester.ConfigOptions; import com.mousebirdconsulting.autotester.R; +import java.lang.ref.WeakReference; import java.io.DataInputStream; import java.io.File; import java.io.FileOutputStream; @@ -27,9 +31,7 @@ public class MaplyTestCase extends AsyncTask implements GlobeController.GestureDelegate, MapController.GestureDelegate { - public enum TestExecutionImplementation { - Globe, Map, Both, None; - } + public enum TestExecutionImplementation { Globe, Map, Both, None } public interface MaplyTestCaseListener { void onStart(View view); @@ -42,7 +44,7 @@ public interface MaplyTestCaseListener { protected String testName; protected int icon = R.drawable.ic_action_selectall; protected ConfigOptions.TestType options; - protected Activity activity; + protected WeakReference weakActivity; protected BaseController controller; protected GlobeController globeController; protected MapController mapController; @@ -55,12 +57,22 @@ public interface MaplyTestCaseListener { public MaplyTestCase(Activity activity) { super(); - this.activity = activity; + weakActivity = new WeakReference<>(activity); + } + + public MaplyTestCase(Activity activity, String testName) { + this(activity, testName, TestExecutionImplementation.Both); + } + + public MaplyTestCase(Activity activity, String testName, TestExecutionImplementation impl) { + this(activity); + this.testName = testName; + this.implementation = impl; } public boolean areResourcesDownloaded(){ for (int ii = 0; ii < remoteResources.size(); ii++){ - File file = new File(ConfigOptions.getCacheDir(activity) + "/" + getFileName(remoteResources.get(ii))); + File file = new File(ConfigOptions.getCacheDir(getActivity()) + "/" + getFileName(remoteResources.get(ii))); if (!file.exists()){ return false; } @@ -80,7 +92,7 @@ public void downloadResources() { } private void downloadFromServer(String url) { - String filename = ConfigOptions.getCacheDir(activity).getPath() + "/" + getFileName(url); + String filename = ConfigOptions.getCacheDir(getActivity()).getPath() + "/" + getFileName(url); Log.e("Download", "Downloading " + url); try { @@ -111,10 +123,10 @@ private void downloadFromServer(String url) { } } - private void deleteFile(String name){ + private Boolean deleteFile(String name){ - File file = new File(ConfigOptions.getCacheDir(activity) + "/" + name); - file.delete(); + File file = new File(ConfigOptions.getCacheDir(getActivity()) + "/" + name); + return file.delete(); } // Change this to set transparent backgrounds int clearColor = Color.BLACK; @@ -123,7 +135,7 @@ private void deleteFile(String name){ boolean success = false; // Build the globe controller for use later - // This can be overriden if we're doing something tricky + // This can be overridden if we're doing something tricky protected GlobeController makeGlobeController() { GlobeController.Settings settings = new GlobeController.Settings(); @@ -132,26 +144,48 @@ protected GlobeController makeGlobeController() settings.clearColor = clearColor; // settings.width = 512; // settings.height = 512; - GlobeController globeControl = new GlobeController(activity,settings); + GlobeController globeControl = new GlobeController(getActivity(),settings); globeControl.gestureDelegate = this; return globeControl; } // Build the map controller for use later - // This can be overriden if we're doing something tricky + // This can be overridden if we're doing something tricky protected MapController makeMapController() { MapController.Settings settings = new MapController.Settings(); settings.clearColor = clearColor; // settings.width = 512; // settings.height = 512; - MapController mapControl = new MapController(activity,settings); + MapController mapControl = new MapController(getActivity(),settings); mapControl.gestureDelegate = this; return mapControl; } + /** + * Find the display DPI + */ + public Point2d getDisplayDensity() { + Activity activity = getActivity(); + WindowManager windowManager = (activity != null) ? activity.getWindowManager() : null; + Display display = (windowManager != null) ? windowManager.getDefaultDisplay() : null; + if (display != null) { + DisplayMetrics dm = new DisplayMetrics(); + display.getRealMetrics(dm); + return new Point2d(dm.xdpi, dm.ydpi); + } + Resources resources = (activity != null) ? activity.getResources() : null; + if (resources != null) { + DisplayMetrics dm = resources.getDisplayMetrics(); + if (dm != null) { + return new Point2d(dm.xdpi, dm.ydpi); + } + } + return null; + } + int numRuns = 0; // If set we'll run startup/shutdown tests boolean multiTest = false; @@ -164,20 +198,11 @@ public void start() startControl(); Handler handler = new Handler(); - handler.postDelayed(new Runnable() { - @Override - public void run() { - shutdown(); - - Handler handler = new Handler(); - handler.postDelayed(new Runnable() { - @Override - public void run() { - - start(); - } - }, runDelay); - } + handler.postDelayed(() -> { + shutdown(); + + Handler handler1 = new Handler(); + handler1.postDelayed(this::start, runDelay); }, runDelay); } else { startControl(); @@ -205,12 +230,7 @@ public void startControl() } success = true; listener.onStart(controller.getContentView()); - controller.addPostSurfaceRunnable(new Runnable() { - @Override - public void run() { - implementationTest(); - } - }); + controller.addPostSurfaceRunnable(this::implementationTest); } private void implementationTest() { @@ -247,35 +267,32 @@ protected void onPreExecute() { protected Void doInBackground(Void... params) { if (success) { publishProgress(controller.getContentView()); - if (ConfigOptions.getExecutionMode(activity.getApplicationContext()) == ConfigOptions.ExecutionMode.Interactive) { + if (ConfigOptions.getExecutionMode(getActivity().getApplicationContext()) == ConfigOptions.ExecutionMode.Interactive) { + //noinspection InfiniteLoopStatement while (true) { try { + //noinspection BusyWait Thread.sleep(500); - } catch (InterruptedException e) { + } catch (InterruptedException ignored) { } } } - if (ConfigOptions.getViewSetting(activity.getApplicationContext()) == ConfigOptions.ViewMapOption.ViewMap) { + if (ConfigOptions.getViewSetting(getActivity().getApplicationContext()) == ConfigOptions.ViewMapOption.ViewMap) { if (this.mapController != null && listener != null) { try { Thread.sleep(delay * 1000); } - catch (InterruptedException ex) { + catch (InterruptedException ignored) { } } if (this.globeController != null && listener != null) { if (options == ConfigOptions.TestType.BothTest) { - activity.runOnUiThread(new Runnable() { - @Override - public void run() { - listener.onExecute(globeController.getContentView()); - } - }); + getActivity().runOnUiThread(() -> listener.onExecute(globeController.getContentView())); } try { Thread.sleep(delay * 1000); } - catch (InterruptedException ex) { + catch (InterruptedException ignored) { } } } @@ -333,11 +350,11 @@ public void setIcon(int value) { } public Activity getActivity() { - return activity; + return weakActivity.get(); } public void setActivity(Activity value) { - activity = value; + weakActivity = new WeakReference<>(value); } public String getTestName() { @@ -352,9 +369,7 @@ public void setDelay(int value) { delay = value; } - public int getDelay() { - return delay; - } + public int getDelay() { return delay; } public TestExecutionImplementation getImplementation() { return implementation; @@ -370,12 +385,14 @@ public void userDidSelect(GlobeController globeControl, SelectedObject[] selObjs public void userDidTap(GlobeController globeControl,Point2d loc,Point2d screenLoc) { - Log.d("Maply","User tapped at (" + loc.getX()*180/Math.PI + "," + loc.getY()*180/Math.PI + ") on screen (" + screenLoc.getX() + "," + screenLoc.getY() + ")"); - Point2d newScreenPt = globeControl.screenPointFromGeo(loc); - Point2d newGeo = globeControl.geoPointFromScreen(newScreenPt); + Log.d("Maply", String.format("User tapped at screen(%f,%f) = geo(%f,%f)", + screenLoc.getX(), screenLoc.getY(), loc.getX()*180/Math.PI, loc.getY()*180/Math.PI)); + //Point2d newScreenPt = globeControl.screenPointFromGeo(loc); + //Point2d newGeo = globeControl.geoPointFromScreen(newScreenPt); Mbr mbr = globeControl.getCurrentViewGeo(); - if (mbr != null) - Log.d("Maply","User is looking at bounding box: " + mbr); + if (mbr != null) { + Log.d("Maply", "User is looking at bounding box: " + mbr); + } } public void userDidTapOutside(GlobeController globeControl,Point2d screenLoc) @@ -389,21 +406,21 @@ public void userDidLongPress(GlobeController globeControl, SelectedObject[] selO public void globeDidStartMoving(GlobeController globeControl, boolean userMotion) {} - public void globeDidStopMoving(GlobeController globeControl, Point3d corners[], boolean userMotion) + public void globeDidStopMoving(GlobeController globeControl, Point3d[] corners, boolean userMotion) {} - public void globeDidMove(GlobeController globeControl,Point3d corners[], boolean userMotion) + public void globeDidMove(GlobeController globeControl,Point3d[] corners, boolean userMotion) {} public void mapDidStartMoving(MapController mapControl, boolean userMotion) {} - public void mapDidStopMoving(MapController mapControl, Point3d corners[], boolean userMotion) + public void mapDidStopMoving(MapController mapControl, Point3d[] corners, boolean userMotion) { } - public void mapDidMove(MapController mapControl,Point3d corners[], boolean userMotion) + public void mapDidMove(MapController mapControl,Point3d[] corners, boolean userMotion) { } @@ -418,9 +435,14 @@ public void userDidSelect(MapController mapControl,SelectedObject[] selObjs,Poin public void userDidTap(MapController mapControl,Point2d loc,Point2d screenLoc) { - Log.d("Maply","User tapped at (" + loc.getX()*180/Math.PI + "," + loc.getY()*180/Math.PI + ") on screen (" + screenLoc.getX() + "," + screenLoc.getY() + ")"); - Point2d newScreenPt = mapControl.screenPointFromGeo(loc); - Point2d newGeo = mapControl.geoPointFromScreen(newScreenPt); + Log.d("Maply", String.format("User tapped at screen(%f,%f) = geo(%f,%f)", + screenLoc.getX(), screenLoc.getY(), loc.getX()*180/Math.PI, loc.getY()*180/Math.PI)); + //Point2d newScreenPt = mapControl.screenPointFromGeo(loc); + //Point2d newGeo = mapControl.geoPointFromScreen(newScreenPt); + Mbr mbr = mapControl.getCurrentViewGeo(); + if (mbr != null) { + Log.d("Maply", "User is looking at bounding box: " + mbr); + } } public void userDidLongPress(MapController mapController, SelectedObject[] selObjs, Point2d loc, Point2d screenLoc) diff --git a/android/apps/AutoTesterAndroid/app/src/main/java/com/mousebirdconsulting/autotester/ResultActivity.java b/android/apps/AutoTesterAndroid/app/src/main/java/com/mousebirdconsulting/autotester/ResultActivity.java index 3a69544d44..8da1aceeb4 100644 --- a/android/apps/AutoTesterAndroid/app/src/main/java/com/mousebirdconsulting/autotester/ResultActivity.java +++ b/android/apps/AutoTesterAndroid/app/src/main/java/com/mousebirdconsulting/autotester/ResultActivity.java @@ -34,6 +34,7 @@ protected void onCreate(Bundle savedInstanceState) { try { Bundle bundle = getIntent().getExtras(); + @SuppressWarnings("unchecked") // we'll catch the cast exception if there's a problem ArrayList listResults = (ArrayList) bundle.getSerializable("arraylist"); ResultsTestsAdapter adapter = new ResultsTestsAdapter(listResults, this); resultsList.setAdapter(adapter); diff --git a/android/apps/AutoTesterAndroid/app/src/main/java/com/mousebirdconsulting/autotester/TestCases/ClusteredMarkersTestCase.java b/android/apps/AutoTesterAndroid/app/src/main/java/com/mousebirdconsulting/autotester/TestCases/ClusteredMarkersTestCase.java index 59135885cd..00ae4e95ba 100644 --- a/android/apps/AutoTesterAndroid/app/src/main/java/com/mousebirdconsulting/autotester/TestCases/ClusteredMarkersTestCase.java +++ b/android/apps/AutoTesterAndroid/app/src/main/java/com/mousebirdconsulting/autotester/TestCases/ClusteredMarkersTestCase.java @@ -1,9 +1,8 @@ -/* - * ClusteredMarkersTestCase.java +/* ClusteredMarkersTestCase.java * WhirlyGlobeLib * * Created by jmnavarro - * Copyright 2011-2016 mousebird consulting + * Copyright 2011-2021 mousebird consulting * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -15,7 +14,6 @@ * 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. - * */ package com.mousebirdconsulting.autotester.TestCases; @@ -75,13 +73,12 @@ public boolean setUpWithGlobe(GlobeController globeVC) throws Exception { private void insertClusteredMarkers(List vectors, BaseController inController) { Point2d size = new Point2d(32, 32); - List markers = new ArrayList<>(); + List markers = new ArrayList<>(vectors.size()); Bitmap icon = BitmapFactory.decodeResource(getActivity().getResources(), R.drawable.sticker); MaplyTexture tex = inController.addTexture(icon,new RenderController.TextureSettings(), RenderController.ThreadMode.ThreadCurrent); // inController.addClusterGenerator(new BasicClusterGenerator(new int[]{Color.argb(165, 255, 255, 0)}, 1, new Point2d(64, 64), inController, inController.getActivity())); - int which = 0; for (VectorObject v : vectors) { // Note: Increase this to test capacity for (int ii=0;ii<1;ii++) { @@ -90,15 +87,15 @@ private void insertClusteredMarkers(List vectors, BaseController i marker.loc = v.centroid(); marker.size = size; marker.selectable = true; + marker.layoutImportance = ii; if (marker.loc != null) markers.add(marker); } - which++; } MarkerInfo info = new MarkerInfo(); - info.setLayoutImportance(1.f); + //info.setLayoutImportance(1.f); // info.setClusterGroup(1); info.setClusterGroup(0); diff --git a/android/apps/AutoTesterAndroid/app/src/main/java/com/mousebirdconsulting/autotester/TestCases/CustomBNGCoordAdapter.java b/android/apps/AutoTesterAndroid/app/src/main/java/com/mousebirdconsulting/autotester/TestCases/CustomBNGCoordAdapter.java index 150435ee84..35806bce58 100644 --- a/android/apps/AutoTesterAndroid/app/src/main/java/com/mousebirdconsulting/autotester/TestCases/CustomBNGCoordAdapter.java +++ b/android/apps/AutoTesterAndroid/app/src/main/java/com/mousebirdconsulting/autotester/TestCases/CustomBNGCoordAdapter.java @@ -50,7 +50,7 @@ protected MapController makeMapController() MapController.Settings settings = new MapController.Settings(); settings.coordSys = coordSys; - MapController mapControl = new MapController(activity,settings); + MapController mapControl = new MapController(getActivity(),settings); mapControl.gestureDelegate = this; return mapControl; diff --git a/android/apps/AutoTesterAndroid/app/src/main/java/com/mousebirdconsulting/autotester/TestCases/FindHeightTestCase.kt b/android/apps/AutoTesterAndroid/app/src/main/java/com/mousebirdconsulting/autotester/TestCases/FindHeightTestCase.kt index 6a7fb8d001..7635b84592 100644 --- a/android/apps/AutoTesterAndroid/app/src/main/java/com/mousebirdconsulting/autotester/TestCases/FindHeightTestCase.kt +++ b/android/apps/AutoTesterAndroid/app/src/main/java/com/mousebirdconsulting/autotester/TestCases/FindHeightTestCase.kt @@ -33,7 +33,7 @@ class FindHeightTestCase : MaplyTestCase { val height = vc.findHeightToViewBounds(bbox, center) vc.animatePositionGeo(center.x,center.y,height,1.0) } - }, 10000) + }, 1000) } var baseCase : MaplyTestCase? = null diff --git a/android/apps/AutoTesterAndroid/app/src/main/java/com/mousebirdconsulting/autotester/TestCases/GeographyClass.java b/android/apps/AutoTesterAndroid/app/src/main/java/com/mousebirdconsulting/autotester/TestCases/GeographyClass.java index 15aa36991f..ee9edfb15e 100644 --- a/android/apps/AutoTesterAndroid/app/src/main/java/com/mousebirdconsulting/autotester/TestCases/GeographyClass.java +++ b/android/apps/AutoTesterAndroid/app/src/main/java/com/mousebirdconsulting/autotester/TestCases/GeographyClass.java @@ -133,7 +133,7 @@ private void setupImageLoader(BaseController baseController, String mbTilesName, Log.d(TAG, String.format("Obtained MBTiles SQLLite database \"%s\"", mbTiles.getAbsolutePath())); // The fetcher fetches tile from the MBTiles file - MBTileFetcher mbTileFetcher = new MBTileFetcher(mbTiles); + MBTileFetcher mbTileFetcher = new MBTileFetcher(baseController, mbTiles); if (mbTileFetcher == null) return; diff --git a/android/apps/AutoTesterAndroid/app/src/main/java/com/mousebirdconsulting/autotester/TestCases/GlobeRotationTestCase.kt b/android/apps/AutoTesterAndroid/app/src/main/java/com/mousebirdconsulting/autotester/TestCases/GlobeRotationTestCase.kt new file mode 100644 index 0000000000..4aa87e00e4 --- /dev/null +++ b/android/apps/AutoTesterAndroid/app/src/main/java/com/mousebirdconsulting/autotester/TestCases/GlobeRotationTestCase.kt @@ -0,0 +1,50 @@ +/* + * GlobeRotationTestCase.kt + * WhirlyGlobeLib + * + * Created by Tim Sylvester on 9 Feb 2021. + * Copyright 2021 mousebird consulting + * + * 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. + * + */ + +package com.mousebirdconsulting.autotester.TestCases + +import android.app.Activity +import com.mousebird.maply.GlobeController +import com.mousebirdconsulting.autotester.Framework.MaplyTestCase + +public class GlobeRotationTestCase : MaplyTestCase { + + constructor(activity: Activity) : super(activity) { + testName = "Globe Rotation (#1286)" + implementation = TestExecutionImplementation.Globe + + baseCase = VectorsTestCase(activity) + } + + override fun setUpWithGlobe(globeVC: GlobeController?): Boolean { + if (!baseCase.setUpWithGlobe(globeVC)) { + return false + } + globeVC?.animatePositionGeo(0.0, Math.PI/8, 0.75, 0.0, 0.5) + return true + } + + override fun shutdown() { + baseCase.shutdown() + super.shutdown() + } + + private var baseCase: VectorsTestCase +} diff --git a/android/apps/AutoTesterAndroid/app/src/main/java/com/mousebirdconsulting/autotester/TestCases/LocationTrackingRealTestCase.kt b/android/apps/AutoTesterAndroid/app/src/main/java/com/mousebirdconsulting/autotester/TestCases/LocationTrackingRealTestCase.kt new file mode 100644 index 0000000000..cd46daa92a --- /dev/null +++ b/android/apps/AutoTesterAndroid/app/src/main/java/com/mousebirdconsulting/autotester/TestCases/LocationTrackingRealTestCase.kt @@ -0,0 +1,155 @@ +package com.mousebirdconsulting.autotester.TestCases + +import android.Manifest +import android.annotation.SuppressLint +import android.app.Activity +import android.content.pm.PackageManager +import android.graphics.Color +import android.widget.Button +import android.widget.FrameLayout +import android.widget.LinearLayout +import androidx.annotation.RequiresPermission +import androidx.core.app.ActivityCompat +import com.mousebird.maply.* +import com.mousebirdconsulting.autotester.Framework.MaplyTestCase +import com.mousebirdconsulting.autotester.R + +open class LocationTrackingRealTestCase(activity: Activity) : + MaplyTestCase(activity, "Location Tracking - Real"), + LocationTrackerDelegate { + + override fun onPreExecute() { + + activity.findViewById(R.id.content_frame)?.let { frame -> + + val lin = LinearLayout(activity) + lin.setBackgroundColor(Color.WHITE) + lin.layoutParams = LinearLayout.LayoutParams(LinearLayout.LayoutParams.MATCH_PARENT, LinearLayout.LayoutParams.WRAP_CONTENT) + + val btnLayout = LinearLayout.LayoutParams(frame.width / 5, LinearLayout.LayoutParams.WRAP_CONTENT) + + val noneBtn = Button(activity.applicationContext) + noneBtn.text = "No Lock" + noneBtn.layoutParams = btnLayout + noneBtn.textSize = frame.width / 120f + noneBtn.isAllCaps = false + noneBtn.setOnClickListener { _ -> onTrackNone() } + lin.addView(noneBtn) + + val northBtn = Button(activity.applicationContext) + northBtn.text = "North Up" + northBtn.layoutParams = btnLayout + northBtn.textSize = frame.width / 120f + northBtn.isAllCaps = false + northBtn.setOnClickListener { _ -> onTrackNorth() } + lin.addView(northBtn) + + val hdgBtn = Button(activity.applicationContext) + hdgBtn.text = "Heading Up" + hdgBtn.layoutParams = btnLayout + hdgBtn.textSize = frame.width / 120f + hdgBtn.isAllCaps = false + hdgBtn.setOnClickListener { _ -> onTrackHeading() } + lin.addView(hdgBtn) + + val hdgFwdBtn = Button(activity.applicationContext) + hdgFwdBtn.text = "Heading Up Forward" + hdgFwdBtn.layoutParams = btnLayout + hdgFwdBtn.textSize = frame.width / 120f + hdgFwdBtn.isAllCaps = false + hdgFwdBtn.setOnClickListener { _ -> onTrackHeadingForward() } + lin.addView(hdgFwdBtn) + + frame.addView(lin) + } + } + + open fun setUp() { + val context = activity.applicationContext + if (ActivityCompat.checkSelfPermission(context, Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED && + ActivityCompat.checkSelfPermission(context, Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED) { + + ActivityCompat.requestPermissions(activity, arrayOf(Manifest.permission.ACCESS_FINE_LOCATION, Manifest.permission.ACCESS_COARSE_LOCATION), 0) + return + } + + setUpTracker() + } + + @RequiresPermission(anyOf = [Manifest.permission.ACCESS_COARSE_LOCATION, Manifest.permission.ACCESS_FINE_LOCATION]) + open fun setUpTracker() { + tracker?.stop() + tracker = null + + val context = activity.applicationContext + baseViewC?.let { vc -> + tracker = LocationTracker(vc, this, useHeading = true).apply { + lockType = MaplyLocationLockType.MaplyLocationLockNorthUp + markerSize = 48 + start(context) + } + } + } + + // results of `ActivityCompat.requestPermissions` + @Suppress("UNUSED_PARAMETER") + @SuppressLint("MissingPermission") + fun onRequestPermissionsResult(requestCode: Int, permissions: Array, grantResults: Array) { + if (grantResults.contains(PackageManager.PERMISSION_GRANTED)) { + setUpTracker() + } + } + + override fun setUpWithMap(mapVC: MapController?): Boolean { + baseViewC = mapVC + baseCase = CartoLightTestCase(getActivity()).apply { + setUpWithMap(mapVC) + } + mapVC?.setPositionGeo(-100 * Math.PI / 180.0, 40 * Math.PI / 180.0, 0.00005) + setUp() + return true + } + + override fun setUpWithGlobe(globeVC: GlobeController?): Boolean { + baseViewC = globeVC + baseCase = CartoLightTestCase(getActivity()).apply { + setUpWithGlobe(globeVC) + } + globeVC?.keepNorthUp = false + globeVC?.setPositionGeo(-100 * Math.PI / 180.0, 40 * Math.PI / 180.0, 0.0001) + setUp() + return true + } + + override fun shutdown() { + tracker?.stop() + tracker = null + super.shutdown() + } + + private fun onTrackNone() { + tracker?.let { + it.lockType = MaplyLocationLockType.MaplyLocationLockNone + } + } + private fun onTrackNorth() { + tracker?.let { + it.lockType = MaplyLocationLockType.MaplyLocationLockNorthUp + } + } + private fun onTrackHeading() { + tracker?.let { + it.lockType = MaplyLocationLockType.MaplyLocationLockHeadingUp + } + } + private fun onTrackHeadingForward() { + tracker?.let { + it.lockType = MaplyLocationLockType.MaplyLocationLockHeadingUpOffset + it.forwardTrackOffset = 500 + } + } + + var baseCase: MaplyTestCase? = null + var baseViewC: BaseController? = null + var tracker: LocationTracker? = null +} \ No newline at end of file diff --git a/android/apps/AutoTesterAndroid/app/src/main/java/com/mousebirdconsulting/autotester/TestCases/LocationTrackingSimTestCase.kt b/android/apps/AutoTesterAndroid/app/src/main/java/com/mousebirdconsulting/autotester/TestCases/LocationTrackingSimTestCase.kt new file mode 100644 index 0000000000..9263386ea6 --- /dev/null +++ b/android/apps/AutoTesterAndroid/app/src/main/java/com/mousebirdconsulting/autotester/TestCases/LocationTrackingSimTestCase.kt @@ -0,0 +1,599 @@ +package com.mousebirdconsulting.autotester.TestCases + +import android.annotation.SuppressLint +import android.app.Activity +import android.graphics.Color +import com.mousebird.maply.LocationSimulatorDelegate +import com.mousebird.maply.LocationTracker +import com.mousebird.maply.LocationTrackerPoint +import com.mousebird.maply.MaplyLocationLockType + +class LocationTrackingSimTestCase : LocationTrackingRealTestCase, LocationSimulatorDelegate { + + constructor(activity: Activity) : super(activity) + { + setTestName("Location Tracking - Simulated") + implementation = TestExecutionImplementation.Both + } + + @SuppressLint("MissingPermission") + override fun setUpTracker() { + tracker?.also { + it.stop() + } + tracker = null + + val context = activity.applicationContext + baseViewC?.also { vc -> + tracker = LocationTracker(vc, this, this, + updateInterval = 1.0, useHeading = true).apply { + lockType = MaplyLocationLockType.MaplyLocationLockNorthUp + //markerColorInner = Color.YELLOW + markerColorOuter = Color.MAGENTA + //markerColorShadow = Color.argb(16, 0, 0, 255) + //markerColorOutline = Color.BLACK + //accuracyCircleColor = Color.argb(10, 0, 0, 0) + start(context) + } + } + } + + override fun locationSimulatorGetLocation(): LocationTrackerPoint? { + markerSize = (markerSize + 3) % 250 + tracker?.markerSize = markerSize + return if ((++simFailIndex % 20) != 0) convert(simPointData[simPointIndex++ % simPointData.size]) else null + } + + private fun convert(p: Array): LocationTrackerPoint { + return LocationTrackerPoint().apply { + lonDeg = p[0] + latDeg = p[1] + headingDeg = p[2] + horizontalAccuracy = p[2] / 3 + } + } + + private var simPointIndex = 0 + private var simFailIndex = 0 + private var markerSize = 32 + private val simPointData = arrayOf( + arrayOf(16.382910,48.211350,275.700000), + arrayOf(16.382870,48.211250,194.900000), + arrayOf(16.382830,48.211150,194.900000), + arrayOf(16.382797,48.211023,189.900000), + arrayOf(16.382763,48.210897,189.900000), + arrayOf(16.382730,48.210770,189.900000), + arrayOf(16.382705,48.210647,187.700000), + arrayOf(16.382680,48.210525,187.700000), + arrayOf(16.382655,48.210403,187.700000), + arrayOf(16.382630,48.210280,187.700000), + arrayOf(16.382600,48.210135,187.900000), + arrayOf(16.382570,48.209990,187.900000), + arrayOf(16.382520,48.209830,191.800000), + arrayOf(16.382484,48.209729,193.200000), + arrayOf(16.382449,48.209628,193.200000), + arrayOf(16.382413,48.209527,193.200000), + arrayOf(16.382378,48.209426,193.200000), + arrayOf(16.382342,48.209324,193.200000), + arrayOf(16.382307,48.209223,193.200000), + arrayOf(16.382271,48.209122,193.200000), + arrayOf(16.382236,48.209021,193.200000), + arrayOf(16.382200,48.208920,193.200000), + arrayOf(16.382115,48.208829,211.800000), + arrayOf(16.382030,48.208737,211.800000), + arrayOf(16.381945,48.208646,211.800000), + arrayOf(16.381860,48.208555,211.800000), + arrayOf(16.381775,48.208464,211.800000), + arrayOf(16.381690,48.208373,211.800000), + arrayOf(16.381605,48.208281,211.800000), + arrayOf(16.381520,48.208190,211.800000), + arrayOf(16.381437,48.208105,213.100000), + arrayOf(16.381354,48.208020,213.100000), + arrayOf(16.381271,48.207935,213.100000), + arrayOf(16.381188,48.207850,213.100000), + arrayOf(16.381105,48.207765,213.100000), + arrayOf(16.381022,48.207680,213.100000), + arrayOf(16.380939,48.207595,213.100000), + arrayOf(16.380856,48.207510,213.100000), + arrayOf(16.380773,48.207425,213.100000), + arrayOf(16.380690,48.207340,213.100000), + arrayOf(16.380565,48.207410,310.000000), + arrayOf(16.380440,48.207480,310.000000), + arrayOf(16.380320,48.207370,216.000000), + arrayOf(16.380170,48.207230,215.500000), + arrayOf(16.380320,48.207185,114.200000), + arrayOf(16.380470,48.207140,114.200000), + arrayOf(16.380383,48.207058,215.300000), + arrayOf(16.380297,48.206977,215.300000), + arrayOf(16.380210,48.206895,215.300000), + arrayOf(16.380123,48.206813,215.300000), + arrayOf(16.380037,48.206732,215.300000), + arrayOf(16.379950,48.206650,215.300000), + arrayOf(16.379860,48.206564,214.800000), + arrayOf(16.379770,48.206478,214.800000), + arrayOf(16.379680,48.206391,214.800000), + arrayOf(16.379590,48.206305,214.800000), + arrayOf(16.379500,48.206219,214.800000), + arrayOf(16.379410,48.206132,214.800000), + arrayOf(16.379320,48.206046,214.800000), + arrayOf(16.379230,48.205960,214.800000), + arrayOf(16.379127,48.205862,215.000000), + arrayOf(16.379025,48.205765,215.000000), + arrayOf(16.378923,48.205668,215.000000), + arrayOf(16.378820,48.205570,215.000000), + arrayOf(16.378730,48.205478,213.200000), + arrayOf(16.378640,48.205387,213.200000), + arrayOf(16.378550,48.205295,213.200000), + arrayOf(16.378460,48.205203,213.200000), + arrayOf(16.378370,48.205112,213.200000), + arrayOf(16.378280,48.205020,213.200000), + arrayOf(16.378185,48.204923,213.200000), + arrayOf(16.378090,48.204827,213.200000), + arrayOf(16.377995,48.204730,213.200000), + arrayOf(16.377900,48.204633,213.200000), + arrayOf(16.377805,48.204537,213.200000), + arrayOf(16.377710,48.204440,213.200000), + arrayOf(16.377620,48.204347,212.700000), + arrayOf(16.377530,48.204253,212.700000), + arrayOf(16.377440,48.204160,212.700000), + arrayOf(16.377350,48.204067,212.700000), + arrayOf(16.377260,48.203973,212.700000), + arrayOf(16.377170,48.203880,212.700000), + arrayOf(16.377050,48.203760,213.700000), + arrayOf(16.376930,48.203640,213.700000), + arrayOf(16.376823,48.203533,213.700000), + arrayOf(16.376717,48.203427,213.700000), + arrayOf(16.376610,48.203320,213.700000), + arrayOf(16.376570,48.203340,306.900000), + arrayOf(16.376350,48.203450,306.900000), + arrayOf(16.376258,48.203365,215.700000), + arrayOf(16.376167,48.203280,215.700000), + arrayOf(16.376075,48.203195,215.700000), + arrayOf(16.375983,48.203110,215.700000), + arrayOf(16.375892,48.203025,215.700000), + arrayOf(16.375800,48.202940,215.700000), + arrayOf(16.375706,48.202849,214.600000), + arrayOf(16.375611,48.202758,214.600000), + arrayOf(16.375517,48.202667,214.600000), + arrayOf(16.375422,48.202576,214.600000), + arrayOf(16.375328,48.202484,214.600000), + arrayOf(16.375233,48.202393,214.600000), + arrayOf(16.375139,48.202302,214.600000), + arrayOf(16.375044,48.202211,214.600000), + arrayOf(16.374950,48.202120,214.600000), + arrayOf(16.374810,48.202000,217.900000), + arrayOf(16.374680,48.201950,240.000000), + arrayOf(16.374550,48.201900,240.000000), + arrayOf(16.374387,48.201858,248.600000), + arrayOf(16.374225,48.201815,248.600000), + arrayOf(16.374063,48.201772,248.600000), + arrayOf(16.373900,48.201730,248.600000), + arrayOf(16.373890,48.201730,270.000000), + arrayOf(16.373695,48.201720,265.600000), + arrayOf(16.373500,48.201710,265.600000), + arrayOf(16.373353,48.201740,287.100000), + arrayOf(16.373207,48.201770,287.100000), + arrayOf(16.373060,48.201800,287.100000), + arrayOf(16.372917,48.201832,288.400000), + arrayOf(16.372774,48.201863,288.400000), + arrayOf(16.372631,48.201895,288.400000), + arrayOf(16.372488,48.201927,288.400000), + arrayOf(16.372345,48.201959,288.400000), + arrayOf(16.372202,48.201990,288.400000), + arrayOf(16.372059,48.202022,288.400000), + arrayOf(16.371916,48.202054,288.400000), + arrayOf(16.371773,48.202086,288.400000), + arrayOf(16.371630,48.202117,288.400000), + arrayOf(16.371487,48.202149,288.400000), + arrayOf(16.371343,48.202181,288.400000), + arrayOf(16.371200,48.202213,288.400000), + arrayOf(16.371057,48.202244,288.400000), + arrayOf(16.370914,48.202276,288.400000), + arrayOf(16.370771,48.202308,288.400000), + arrayOf(16.370628,48.202340,288.400000), + arrayOf(16.370485,48.202371,288.400000), + arrayOf(16.370342,48.202403,288.400000), + arrayOf(16.370199,48.202435,288.400000), + arrayOf(16.370056,48.202467,288.400000), + arrayOf(16.369913,48.202498,288.400000), + arrayOf(16.369770,48.202530,288.400000), + arrayOf(16.369610,48.202564,287.700000), + arrayOf(16.369450,48.202598,287.700000), + arrayOf(16.369290,48.202632,287.700000), + arrayOf(16.369130,48.202666,287.700000), + arrayOf(16.368970,48.202700,287.700000), + arrayOf(16.368822,48.202735,289.500000), + arrayOf(16.368673,48.202770,289.500000), + arrayOf(16.368525,48.202805,289.500000), + arrayOf(16.368377,48.202840,289.500000), + arrayOf(16.368228,48.202875,289.500000), + arrayOf(16.368080,48.202910,289.500000), + arrayOf(16.368020,48.202740,193.200000), + arrayOf(16.367872,48.202773,288.300000), + arrayOf(16.367724,48.202805,288.300000), + arrayOf(16.367576,48.202838,288.300000), + arrayOf(16.367428,48.202871,288.300000), + arrayOf(16.367280,48.202903,288.300000), + arrayOf(16.367132,48.202936,288.300000), + arrayOf(16.366984,48.202969,288.300000), + arrayOf(16.366836,48.203001,288.300000), + arrayOf(16.366688,48.203034,288.300000), + arrayOf(16.366540,48.203067,288.300000), + arrayOf(16.366392,48.203099,288.300000), + arrayOf(16.366244,48.203132,288.300000), + arrayOf(16.366096,48.203165,288.300000), + arrayOf(16.365948,48.203197,288.300000), + arrayOf(16.365800,48.203230,288.300000), + arrayOf(16.365657,48.203262,288.600000), + arrayOf(16.365513,48.203294,288.600000), + arrayOf(16.365370,48.203327,288.600000), + arrayOf(16.365227,48.203359,288.600000), + arrayOf(16.365083,48.203391,288.600000), + arrayOf(16.364940,48.203423,288.600000), + arrayOf(16.364797,48.203456,288.600000), + arrayOf(16.364653,48.203488,288.600000), + arrayOf(16.364510,48.203520,288.600000), + arrayOf(16.364350,48.203580,299.400000), + arrayOf(16.364226,48.203658,313.300000), + arrayOf(16.364102,48.203736,313.300000), + arrayOf(16.363978,48.203814,313.300000), + arrayOf(16.363854,48.203892,313.300000), + arrayOf(16.363730,48.203970,313.300000), + arrayOf(16.363628,48.204045,317.800000), + arrayOf(16.363525,48.204121,317.800000), + arrayOf(16.363423,48.204196,317.800000), + arrayOf(16.363321,48.204271,317.800000), + arrayOf(16.363218,48.204346,317.800000), + arrayOf(16.363116,48.204422,317.800000), + arrayOf(16.363014,48.204497,317.800000), + arrayOf(16.362911,48.204572,317.800000), + arrayOf(16.362809,48.204648,317.800000), + arrayOf(16.362706,48.204723,317.800000), + arrayOf(16.362604,48.204798,317.800000), + arrayOf(16.362502,48.204874,317.800000), + arrayOf(16.362399,48.204949,317.800000), + arrayOf(16.362297,48.205024,317.800000), + arrayOf(16.362195,48.205099,317.800000), + arrayOf(16.362092,48.205175,317.800000), + arrayOf(16.361990,48.205250,317.800000), + arrayOf(16.361889,48.205327,318.800000), + arrayOf(16.361788,48.205404,318.800000), + arrayOf(16.361686,48.205481,318.800000), + arrayOf(16.361585,48.205558,318.800000), + arrayOf(16.361484,48.205635,318.800000), + arrayOf(16.361383,48.205712,318.800000), + arrayOf(16.361282,48.205789,318.800000), + arrayOf(16.361181,48.205866,318.800000), + arrayOf(16.361079,48.205944,318.800000), + arrayOf(16.360978,48.206021,318.800000), + arrayOf(16.360877,48.206098,318.800000), + arrayOf(16.360776,48.206175,318.800000), + arrayOf(16.360675,48.206252,318.800000), + arrayOf(16.360574,48.206329,318.800000), + arrayOf(16.360472,48.206406,318.800000), + arrayOf(16.360371,48.206483,318.800000), + arrayOf(16.360270,48.206560,318.800000), + arrayOf(16.360162,48.206640,318.000000), + arrayOf(16.360054,48.206720,318.000000), + arrayOf(16.359946,48.206800,318.000000), + arrayOf(16.359838,48.206880,318.000000), + arrayOf(16.359730,48.206960,318.000000), + arrayOf(16.359680,48.207030,334.500000), + arrayOf(16.359685,48.207155,1.500000), + arrayOf(16.359690,48.207280,1.500000), + arrayOf(16.359727,48.207383,13.300000), + arrayOf(16.359763,48.207487,13.300000), + arrayOf(16.359800,48.207590,13.300000), + arrayOf(16.359837,48.207693,13.300000), + arrayOf(16.359873,48.207797,13.300000), + arrayOf(16.359910,48.207900,13.300000), + arrayOf(16.359944,48.207998,13.000000), + arrayOf(16.359977,48.208095,13.000000), + arrayOf(16.360011,48.208193,13.000000), + arrayOf(16.360045,48.208290,13.000000), + arrayOf(16.360079,48.208388,13.000000), + arrayOf(16.360112,48.208485,13.000000), + arrayOf(16.360146,48.208582,13.000000), + arrayOf(16.360180,48.208680,13.000000), + arrayOf(16.359950,48.208720,284.600000), + arrayOf(16.359796,48.208739,280.600000), + arrayOf(16.359641,48.208759,280.600000), + arrayOf(16.359487,48.208778,280.600000), + arrayOf(16.359333,48.208797,280.600000), + arrayOf(16.359179,48.208816,280.600000), + arrayOf(16.359024,48.208836,280.600000), + arrayOf(16.358870,48.208855,280.600000), + arrayOf(16.358716,48.208874,280.600000), + arrayOf(16.358561,48.208894,280.600000), + arrayOf(16.358407,48.208913,280.600000), + arrayOf(16.358253,48.208932,280.600000), + arrayOf(16.358099,48.208951,280.600000), + arrayOf(16.357944,48.208971,280.600000), + arrayOf(16.357790,48.208990,280.600000), + arrayOf(16.357755,48.208880,192.000000), + arrayOf(16.357720,48.208770,192.000000), + arrayOf(16.357685,48.208660,192.000000), + arrayOf(16.357650,48.208550,192.000000), + arrayOf(16.357610,48.208433,192.900000), + arrayOf(16.357570,48.208317,192.900000), + arrayOf(16.357530,48.208200,192.900000), + arrayOf(16.357370,48.208220,280.600000), + arrayOf(16.357210,48.208240,280.600000), + arrayOf(16.357050,48.208260,280.600000), + arrayOf(16.356890,48.208280,280.600000), + arrayOf(16.356730,48.208300,280.600000), + arrayOf(16.356570,48.208320,280.600000), + arrayOf(16.356410,48.208344,282.900000), + arrayOf(16.356250,48.208369,282.900000), + arrayOf(16.356090,48.208393,282.900000), + arrayOf(16.355930,48.208418,282.900000), + arrayOf(16.355770,48.208442,282.900000), + arrayOf(16.355610,48.208467,282.900000), + arrayOf(16.355450,48.208491,282.900000), + arrayOf(16.355290,48.208516,282.900000), + arrayOf(16.355130,48.208540,282.900000), + arrayOf(16.355115,48.208660,355.200000), + arrayOf(16.355100,48.208780,355.200000), + arrayOf(16.355122,48.208887,7.700000), + arrayOf(16.355143,48.208993,7.700000), + arrayOf(16.355165,48.209100,7.700000), + arrayOf(16.355187,48.209207,7.700000), + arrayOf(16.355208,48.209313,7.700000), + arrayOf(16.355230,48.209420,7.700000), + arrayOf(16.355255,48.209530,8.600000), + arrayOf(16.355280,48.209640,8.600000), + arrayOf(16.355305,48.209750,8.600000), + arrayOf(16.355330,48.209860,8.600000), + arrayOf(16.355355,48.209970,8.600000), + arrayOf(16.355380,48.210080,8.600000), + arrayOf(16.355405,48.210190,8.600000), + arrayOf(16.355430,48.210300,8.600000), + arrayOf(16.355456,48.210402,9.600000), + arrayOf(16.355482,48.210504,9.600000), + arrayOf(16.355508,48.210606,9.600000), + arrayOf(16.355534,48.210708,9.600000), + arrayOf(16.355560,48.210810,9.600000), + arrayOf(16.355585,48.210918,8.800000), + arrayOf(16.355610,48.211026,8.800000), + arrayOf(16.355635,48.211134,8.800000), + arrayOf(16.355660,48.211242,8.800000), + arrayOf(16.355685,48.211350,8.800000), + arrayOf(16.355710,48.211458,8.800000), + arrayOf(16.355735,48.211566,8.800000), + arrayOf(16.355760,48.211674,8.800000), + arrayOf(16.355785,48.211782,8.800000), + arrayOf(16.355810,48.211890,8.800000), + arrayOf(16.355836,48.211997,9.100000), + arrayOf(16.355861,48.212103,9.100000), + arrayOf(16.355887,48.212210,9.100000), + arrayOf(16.355912,48.212317,9.100000), + arrayOf(16.355938,48.212423,9.100000), + arrayOf(16.355963,48.212530,9.100000), + arrayOf(16.355989,48.212637,9.100000), + arrayOf(16.356014,48.212743,9.100000), + arrayOf(16.356040,48.212850,9.100000), + arrayOf(16.356064,48.212958,8.400000), + arrayOf(16.356088,48.213066,8.400000), + arrayOf(16.356112,48.213174,8.400000), + arrayOf(16.356136,48.213282,8.400000), + arrayOf(16.356160,48.213390,8.400000), + arrayOf(16.356187,48.213500,9.300000), + arrayOf(16.356214,48.213610,9.300000), + arrayOf(16.356241,48.213720,9.300000), + arrayOf(16.356269,48.213830,9.300000), + arrayOf(16.356296,48.213940,9.300000), + arrayOf(16.356323,48.214050,9.300000), + arrayOf(16.356350,48.214160,9.300000), + arrayOf(16.356397,48.214273,15.300000), + arrayOf(16.356443,48.214387,15.300000), + arrayOf(16.356490,48.214500,15.300000), + arrayOf(16.356280,48.214550,289.700000), + arrayOf(16.356310,48.214580,33.700000), + arrayOf(16.356467,48.214551,105.500000), + arrayOf(16.356624,48.214522,105.500000), + arrayOf(16.356781,48.214493,105.500000), + arrayOf(16.356938,48.214464,105.500000), + arrayOf(16.357095,48.214435,105.500000), + arrayOf(16.357252,48.214406,105.500000), + arrayOf(16.357409,48.214377,105.500000), + arrayOf(16.357566,48.214348,105.500000), + arrayOf(16.357723,48.214319,105.500000), + arrayOf(16.357880,48.214290,105.500000), + arrayOf(16.358033,48.214265,103.800000), + arrayOf(16.358185,48.214240,103.800000), + arrayOf(16.358338,48.214215,103.800000), + arrayOf(16.358490,48.214190,103.800000), + arrayOf(16.358643,48.214165,103.800000), + arrayOf(16.358795,48.214140,103.800000), + arrayOf(16.358948,48.214115,103.800000), + arrayOf(16.359100,48.214090,103.800000), + arrayOf(16.359252,48.214062,105.500000), + arrayOf(16.359404,48.214034,105.500000), + arrayOf(16.359556,48.214006,105.500000), + arrayOf(16.359708,48.213978,105.500000), + arrayOf(16.359860,48.213950,105.500000), + arrayOf(16.360029,48.213931,99.400000), + arrayOf(16.360197,48.213913,99.400000), + arrayOf(16.360366,48.213894,99.400000), + arrayOf(16.360534,48.213876,99.400000), + arrayOf(16.360703,48.213857,99.400000), + arrayOf(16.360871,48.213839,99.400000), + arrayOf(16.361040,48.213820,99.400000), + arrayOf(16.361270,48.213750,114.500000), + arrayOf(16.361440,48.213640,134.200000), + arrayOf(16.361605,48.213725,52.300000), + arrayOf(16.361770,48.213810,52.300000), + arrayOf(16.361912,48.213873,56.600000), + arrayOf(16.362055,48.213935,56.600000), + arrayOf(16.362198,48.213998,56.600000), + arrayOf(16.362340,48.214060,56.600000), + arrayOf(16.362590,48.214170,56.600000), + arrayOf(16.362620,48.214130,153.400000), + arrayOf(16.362750,48.214010,144.200000), + arrayOf(16.362880,48.213890,144.200000), + arrayOf(16.362660,48.213790,235.700000), + arrayOf(16.362590,48.213870,329.800000), + arrayOf(16.362530,48.213940,330.300000), + arrayOf(16.362670,48.214010,53.100000), + arrayOf(16.362804,48.214073,55.000000), + arrayOf(16.362938,48.214135,55.000000), + arrayOf(16.363071,48.214197,55.000000), + arrayOf(16.363205,48.214260,55.000000), + arrayOf(16.363339,48.214323,55.000000), + arrayOf(16.363473,48.214385,55.000000), + arrayOf(16.363606,48.214447,55.000000), + arrayOf(16.363740,48.214510,55.000000), + arrayOf(16.363871,48.214569,55.800000), + arrayOf(16.364002,48.214628,55.800000), + arrayOf(16.364133,48.214687,55.800000), + arrayOf(16.364263,48.214747,55.800000), + arrayOf(16.364394,48.214806,55.800000), + arrayOf(16.364525,48.214865,55.800000), + arrayOf(16.364656,48.214924,55.800000), + arrayOf(16.364787,48.214983,55.800000), + arrayOf(16.364918,48.215043,55.800000), + arrayOf(16.365048,48.215102,55.800000), + arrayOf(16.365179,48.215161,55.800000), + arrayOf(16.365310,48.215220,55.800000), + arrayOf(16.365446,48.215284,54.600000), + arrayOf(16.365581,48.215349,54.600000), + arrayOf(16.365717,48.215413,54.600000), + arrayOf(16.365853,48.215477,54.600000), + arrayOf(16.365989,48.215541,54.600000), + arrayOf(16.366124,48.215606,54.600000), + arrayOf(16.366260,48.215670,54.600000), + arrayOf(16.366391,48.215729,55.900000), + arrayOf(16.366522,48.215788,55.900000), + arrayOf(16.366653,48.215847,55.900000), + arrayOf(16.366784,48.215906,55.900000), + arrayOf(16.366915,48.215965,55.900000), + arrayOf(16.367046,48.216024,55.900000), + arrayOf(16.367177,48.216083,55.900000), + arrayOf(16.367308,48.216142,55.900000), + arrayOf(16.367439,48.216201,55.900000), + arrayOf(16.367570,48.216260,55.900000), + arrayOf(16.367698,48.216319,55.200000), + arrayOf(16.367827,48.216379,55.200000), + arrayOf(16.367956,48.216438,55.200000), + arrayOf(16.368084,48.216498,55.200000), + arrayOf(16.368212,48.216558,55.200000), + arrayOf(16.368341,48.216617,55.200000), + arrayOf(16.368469,48.216676,55.200000), + arrayOf(16.368598,48.216736,55.200000), + arrayOf(16.368727,48.216795,55.200000), + arrayOf(16.368855,48.216855,55.200000), + arrayOf(16.368983,48.216915,55.200000), + arrayOf(16.369112,48.216974,55.200000), + arrayOf(16.369241,48.217033,55.200000), + arrayOf(16.369369,48.217093,55.200000), + arrayOf(16.369498,48.217152,55.200000), + arrayOf(16.369626,48.217212,55.200000), + arrayOf(16.369754,48.217272,55.200000), + arrayOf(16.369883,48.217331,55.200000), + arrayOf(16.370012,48.217391,55.200000), + arrayOf(16.370140,48.217450,55.200000), + arrayOf(16.370310,48.217510,62.100000), + arrayOf(16.370540,48.217540,78.900000), + arrayOf(16.370705,48.217530,95.200000), + arrayOf(16.370870,48.217520,95.200000), + arrayOf(16.371100,48.217500,97.400000), + arrayOf(16.371250,48.217410,132.000000), + arrayOf(16.371400,48.217320,132.000000), + arrayOf(16.371580,48.217200,135.000000), + arrayOf(16.371675,48.217118,142.500000), + arrayOf(16.371770,48.217035,142.500000), + arrayOf(16.371865,48.216952,142.500000), + arrayOf(16.371960,48.216870,142.500000), + arrayOf(16.372055,48.216788,142.500000), + arrayOf(16.372150,48.216705,142.500000), + arrayOf(16.372245,48.216622,142.500000), + arrayOf(16.372340,48.216540,142.500000), + arrayOf(16.372417,48.216450,150.400000), + arrayOf(16.372493,48.216360,150.400000), + arrayOf(16.372570,48.216270,150.400000), + arrayOf(16.372647,48.216180,150.400000), + arrayOf(16.372723,48.216090,150.400000), + arrayOf(16.372800,48.216000,150.400000), + arrayOf(16.372877,48.215910,150.400000), + arrayOf(16.372953,48.215820,150.400000), + arrayOf(16.373030,48.215730,150.400000), + arrayOf(16.373100,48.215632,154.500000), + arrayOf(16.373170,48.215534,154.500000), + arrayOf(16.373240,48.215436,154.500000), + arrayOf(16.373310,48.215338,154.500000), + arrayOf(16.373380,48.215240,154.500000), + arrayOf(16.373455,48.215100,160.400000), + arrayOf(16.373530,48.214960,160.400000), + arrayOf(16.373592,48.214860,157.700000), + arrayOf(16.373653,48.214760,157.700000), + arrayOf(16.373715,48.214660,157.700000), + arrayOf(16.373777,48.214560,157.700000), + arrayOf(16.373838,48.214460,157.700000), + arrayOf(16.373900,48.214360,157.700000), + arrayOf(16.373970,48.214270,152.600000), + arrayOf(16.374040,48.214180,152.600000), + arrayOf(16.374110,48.214090,152.600000), + arrayOf(16.374185,48.213997,151.600000), + arrayOf(16.374260,48.213905,151.600000), + arrayOf(16.374335,48.213813,151.600000), + arrayOf(16.374410,48.213720,151.600000), + arrayOf(16.374507,48.213630,144.200000), + arrayOf(16.374605,48.213540,144.200000), + arrayOf(16.374703,48.213450,144.200000), + arrayOf(16.374800,48.213360,144.200000), + arrayOf(16.374909,48.213286,135.400000), + arrayOf(16.375018,48.213213,135.400000), + arrayOf(16.375127,48.213139,135.400000), + arrayOf(16.375236,48.213065,135.400000), + arrayOf(16.375345,48.212992,135.400000), + arrayOf(16.375455,48.212918,135.400000), + arrayOf(16.375564,48.212845,135.400000), + arrayOf(16.375673,48.212771,135.400000), + arrayOf(16.375782,48.212697,135.400000), + arrayOf(16.375891,48.212624,135.400000), + arrayOf(16.376000,48.212550,135.400000), + arrayOf(16.376133,48.212480,128.400000), + arrayOf(16.376265,48.212410,128.400000), + arrayOf(16.376397,48.212340,128.400000), + arrayOf(16.376530,48.212270,128.400000), + arrayOf(16.376690,48.212210,119.400000), + arrayOf(16.376850,48.212150,119.400000), + arrayOf(16.377003,48.212106,113.500000), + arrayOf(16.377156,48.212061,113.500000), + arrayOf(16.377309,48.212017,113.500000), + arrayOf(16.377461,48.211973,113.500000), + arrayOf(16.377614,48.211929,113.500000), + arrayOf(16.377767,48.211884,113.500000), + arrayOf(16.377920,48.211840,113.500000), + arrayOf(16.378082,48.211796,112.200000), + arrayOf(16.378244,48.211752,112.200000), + arrayOf(16.378406,48.211708,112.200000), + arrayOf(16.378568,48.211664,112.200000), + arrayOf(16.378730,48.211620,112.200000), + arrayOf(16.378910,48.211580,108.400000), + arrayOf(16.379090,48.211540,108.400000), + arrayOf(16.379240,48.211520,101.300000), + arrayOf(16.379390,48.211500,101.300000), + arrayOf(16.379542,48.211490,95.600000), + arrayOf(16.379695,48.211480,95.600000), + arrayOf(16.379848,48.211470,95.600000), + arrayOf(16.380000,48.211460,95.600000), + arrayOf(16.380153,48.211453,94.000000), + arrayOf(16.380306,48.211446,94.000000), + arrayOf(16.380459,48.211439,94.000000), + arrayOf(16.380611,48.211431,94.000000), + arrayOf(16.380764,48.211424,94.000000), + arrayOf(16.380917,48.211417,94.000000), + arrayOf(16.381070,48.211410,94.000000), + arrayOf(16.381233,48.211406,92.300000), + arrayOf(16.381397,48.211401,92.300000), + arrayOf(16.381560,48.211397,92.300000), + arrayOf(16.381723,48.211392,92.300000), + arrayOf(16.381887,48.211388,92.300000), + arrayOf(16.382050,48.211383,92.300000), + arrayOf(16.382213,48.211379,92.300000), + arrayOf(16.382377,48.211374,92.300000), + arrayOf(16.382540,48.211370,92.300000) + ) + +} \ No newline at end of file diff --git a/android/apps/AutoTesterAndroid/app/src/main/java/com/mousebirdconsulting/autotester/TestCases/MapTilerCircleTestCase.kt b/android/apps/AutoTesterAndroid/app/src/main/java/com/mousebirdconsulting/autotester/TestCases/MapTilerCircleTestCase.kt new file mode 100644 index 0000000000..e0f0dbe44b --- /dev/null +++ b/android/apps/AutoTesterAndroid/app/src/main/java/com/mousebirdconsulting/autotester/TestCases/MapTilerCircleTestCase.kt @@ -0,0 +1,15 @@ +package com.mousebirdconsulting.autotester.TestCases + +import android.app.Activity + +class MapTilerCircleTestCase(activity: Activity) : + MapTilerTestCase(activity, "MapTiler Circles") +{ + override fun getMaps() = listOf("maptiler_test_circles.json") + + //override fun getStyleJson(whichMap: Int) = + //override fun setup(map: MapboxKindaMap) { + // super.setup(map) + // map.styleSettings.markerImportance = 200000.0 //Float.MAX_VALUE.toDouble() + //} +} \ No newline at end of file diff --git a/android/apps/AutoTesterAndroid/app/src/main/java/com/mousebirdconsulting/autotester/TestCases/MapTilerTestCase.kt b/android/apps/AutoTesterAndroid/app/src/main/java/com/mousebirdconsulting/autotester/TestCases/MapTilerTestCase.kt index a29d8f5d7c..077a8f6962 100644 --- a/android/apps/AutoTesterAndroid/app/src/main/java/com/mousebirdconsulting/autotester/TestCases/MapTilerTestCase.kt +++ b/android/apps/AutoTesterAndroid/app/src/main/java/com/mousebirdconsulting/autotester/TestCases/MapTilerTestCase.kt @@ -2,79 +2,137 @@ package com.mousebirdconsulting.autotester.TestCases import android.app.Activity import android.net.Uri +import android.util.Log import com.mousebird.maply.* -import com.mousebirdconsulting.autotester.ConfigOptions import com.mousebirdconsulting.autotester.Framework.MaplyTestCase import okio.Okio import java.io.IOException +import kotlin.math.min -class MapTilerTestCase : MaplyTestCase { - constructor(activity: Activity) : super(activity) { - setTestName("MapTiler") - implementation = TestExecutionImplementation.Both +open class MapTilerTestCase : MaplyTestCase +{ + constructor(activity: Activity) : + this(activity, "MapTiler", TestExecutionImplementation.Both) { } - - var map: MapboxKindaMap? = null - + + protected constructor(activity: Activity, name: String, impl: TestExecutionImplementation = TestExecutionImplementation.Both) : + super(activity, name, impl) { + } + + // Maptiler token + // Go to maptiler.com, setup an account and get your own + private val token = "GetYerOwnToken" + // Set up the loader (and all the stuff it needs) for the map tiles private fun setupLoader(control: BaseController, whichMap: Int) { - // Third map is no map - if (whichMap > 1) - return; - - val mapName = if (whichMap == 0) "maptiler_basic.json" else "maptiler_streets.json" - - val assetMgr = getActivity().assets - val stream = assetMgr.open(mapName) - var polyStyle: MapboxVectorStyleSet? - - // Maptiler token - // Go to maptiler.com, setup an account and get your own - val token = "GetYerOwnToken" - - try { - val json = Okio.buffer(Okio.source(stream)).readUtf8() - map = object: MapboxKindaMap(json,control) { - override fun mapboxURLFor(file: Uri): Uri { - val str = file.toString() - return Uri.parse(str.replace("MapTilerKey",token)) - } + // Approximate the effect of `UIScreen.scale` on iOS + val dpi = displayDensity ?: Point2d(200.0,200.0) + val scale = min(dpi.x,dpi.y) / 225 + + getStyleJson(whichMap)?.let { json -> + val settings = VectorStyleSettings(scale).apply { + baseDrawPriority = QuadImageLoaderBase.BaseDrawPriorityDefault + 1000 + drawPriorityPerLevel = 100 + } + map = MapboxKindaMap(json, control, settings).apply { + mapboxURLFor = { Uri.parse(it.toString().replace("MapTilerKey", token)) } + backgroundAllPolys = (control is GlobeController) + imageVectorHybrid = true + minImportance = 768.0 * 768.0 + setup(this) + start() } - if (map == null) - return - map?.backgroundAllPolys = true - map?.start() } - catch (e: IOException) { - return + + // Set up an overlay with the same importance showing the + // tile boundaries, for debugging/troubleshooting purposes + map?.let { + // Describes how to break down the space + val params = SamplingParams().apply { + minZoom = 1 + maxZoom = 20 + singleLevel = true + minImportance = it.minImportance + coordSystem = SphericalMercatorCoordSystem() + } + loader = QuadPagingLoader(params, OvlDebugImageLoaderInterpreter(), control) } } + protected open fun setup(map: MapboxKindaMap) { + map.styleSettings.drawPriorityPerLevel = 100 + } + + protected open fun getStyleJson(whichMap: Int): String? = + getMaps().elementAt(whichMap)?.let { + Log.i(javaClass.name, "Loading $it") + try { + Okio.buffer(Okio.source(getActivity().assets.open(it))).readUtf8() + } catch (e: IOException) { + Log.w(javaClass.simpleName, "Failed to load style $it", e) + return null + } + } ?: customStyle + // Switch maps on long press + override fun userDidLongPress(globeControl: GlobeController?, selObjs: Array?, loc: Point2d?, screenLoc: Point2d?) { + switchMaps() + } override fun userDidLongPress(mapController: MapController?, selObjs: Array?, loc: Point2d?, screenLoc: Point2d?) { - map?.stop() - - currentMap = currentMap + 1 - if (currentMap > 2) - currentMap = 0 - setupLoader(baseViewC!!, currentMap) + switchMaps() } - var currentMap = 0 - var baseViewC : BaseController? = null - + private fun switchMaps() { + loader = null + map?.stop() + map = null + currentMap = (currentMap + 1) % getMaps().size + baseViewC?.let { setupLoader(it, currentMap) } + } + override fun setUpWithGlobe(globeVC: GlobeController?): Boolean { - baseViewC = globeVC - setupLoader(baseViewC!!, currentMap) - + baseViewC = globeVC?.also { + setupLoader(it, currentMap) + it.animatePositionGeo(Point2d.FromDegrees(-100.0, 40.0), 0.5, 0.0, 0.5) + } return true } override fun setUpWithMap(mapVC: MapController?): Boolean { - baseViewC = mapVC - setupLoader(baseViewC!!, currentMap) - + baseViewC = mapVC?.also { + setupLoader(it, currentMap) + it.animatePositionGeo(Point2d.FromDegrees(-100.0, 40.0), 0.5, 0.0, 0.5) + } return true } - + + protected open fun getMaps(): Collection = listOf( + "maptiler_basic.json", + "maptiler_streets.json", + "maptiler_topo.json", + "maptiler_hybrid_satellite.json", + "maptiler_expr_test.json", + null // placeholder for custom stylesheet below + ) + + private var currentMap = 0 + private var map: MapboxKindaMap? = null + private var baseViewC : BaseController? = null + private var loader : QuadPagingLoader? = null + + // Use this to test out a single or small number of elements alone. + // This can be helpful when you want to set a breakpoint on something that's normally used by many styles. + private val customStyle = """ + { "name":"test","version":8,"layers":[ + { "id":"background","type":"background","paint":{"background-color":"#aaa"} + },{"id":"road_motorway","type":"line","source":"openmaptiles", + "source-layer":"transportation","filter":["all",["==","class","motorway"]], + "paint":{ + "line-color":{"base":1,"stops":[[5,"hsl(26,87%,62%)"],[16,"#0f0"]]}, + "line-width":{"base":1.2,"stops":[[5,0],[16,60]]} + } + } + ],"sources":{"openmaptiles":{"type":"vector", + "url":"https://api.maptiler.com/tiles/v3/tiles.json?key=MapTilerKey"}}} + """.trimIndent() } \ No newline at end of file diff --git a/android/apps/AutoTesterAndroid/app/src/main/java/com/mousebirdconsulting/autotester/TestCases/MovingScreenMarkersTestCase.kt b/android/apps/AutoTesterAndroid/app/src/main/java/com/mousebirdconsulting/autotester/TestCases/MovingScreenMarkersTestCase.kt new file mode 100644 index 0000000000..69d6d9cce4 --- /dev/null +++ b/android/apps/AutoTesterAndroid/app/src/main/java/com/mousebirdconsulting/autotester/TestCases/MovingScreenMarkersTestCase.kt @@ -0,0 +1,123 @@ +/* + * MovingScreenMarkersTestCase.kt + * WhirlyGlobeLib + * + * Created by Tim Sylvester on 9 Feb 2021. + * Copyright 2021 mousebird consulting + * + * 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. + * + */ + +package com.mousebirdconsulting.autotester.TestCases + +import android.app.Activity +import android.graphics.BitmapFactory +import android.os.Handler +import com.mousebird.maply.* +import com.mousebirdconsulting.autotester.Framework.MaplyTestCase +import com.mousebirdconsulting.autotester.R + +public class MovingScreenMarkersTestCase : MaplyTestCase { + + constructor(activity: Activity) : super(activity) { + testName = "Moving Screen Markers" + implementation = TestExecutionImplementation.Both + + baseCase = VectorsTestCase(activity) + } + + override fun setUpWithGlobe(globeVC: GlobeController?): Boolean { + if (!baseCase.setUpWithGlobe(globeVC)) { + return false + } + setUp() + globeVC?.animatePositionGeo(0.1, 0.1, 0.5, 0.0, 0.5) + return true + } + + override fun setUpWithMap(mapVC: MapController?): Boolean { + if (!baseCase.setUpWithMap(mapVC)) { + return false + } + setUp() + mapVC?.animatePositionGeo(0.0, 0.1, 0.5, 0.0, 0.5) + return true + } + + private fun setUp() { + val icon0 = BitmapFactory.decodeResource(getActivity().resources, R.drawable.teardrop) + val icon1 = BitmapFactory.decodeResource(getActivity().resources, R.drawable.teardrop_stroked) + textures = arrayOf( + controller.addTexture(icon0, null, threadCurrent)!!, + controller.addTexture(icon1, null, threadCurrent)!!) + + timerRunnable = Runnable { + clearMarkers() + markerObj = makeMarkers() + if (timerRunnable != null) { + timerHandler.postDelayed(timerRunnable, (1000 * duration).toLong()) + } + } + timerHandler.postDelayed(timerRunnable, 0) + } + + override fun shutdown() { + timerRunnable.also { + timerRunnable = null + timerHandler.removeCallbacks(it) + } + + baseCase.shutdown() + super.shutdown() + } + + private fun clearMarkers() { + markerObj?.also { + controller.removeObject(it, threadCurrent) + markerObj = null + } + } + + private fun makeMarkers(): ComponentObject? { + val pts = arrayOf( + Point2d.FromDegrees(0.0, 0.0), + Point2d.FromDegrees(10.0, 10.0), + Point2d.FromDegrees(0.0, 20.0), + Point2d.FromDegrees(-10.0, 10.0) + ) + + val markers = pts.indices.map { + ScreenMovingMarker().apply { + duration = duration + period = duration / 2 + loc = pts[it] + endLoc = pts[(it + 1) % pts.size] + size = Point2d(32.0, 32.0) + images = textures + layoutImportance = Float.MAX_VALUE + } + } + return controller.addScreenMovingMarkers(markers, MarkerInfo(), threadCurrent) + } + + private val threadCurrent = RenderControllerInterface.ThreadMode.ThreadCurrent + + private var baseCase: VectorsTestCase + private val duration = 5.0 + + private var timerRunnable: Runnable? = null + private val timerHandler = Handler() + + private var textures: Array? = null + private var markerObj: ComponentObject? = null +} diff --git a/android/apps/AutoTesterAndroid/app/src/main/java/com/mousebirdconsulting/autotester/TestCases/OpenMapTilesHybridTestCase.kt b/android/apps/AutoTesterAndroid/app/src/main/java/com/mousebirdconsulting/autotester/TestCases/OpenMapTilesHybridTestCase.kt index 0a784b2cf9..018e6b1e7d 100644 --- a/android/apps/AutoTesterAndroid/app/src/main/java/com/mousebirdconsulting/autotester/TestCases/OpenMapTilesHybridTestCase.kt +++ b/android/apps/AutoTesterAndroid/app/src/main/java/com/mousebirdconsulting/autotester/TestCases/OpenMapTilesHybridTestCase.kt @@ -69,7 +69,7 @@ class OpenMapTilesHybridTestCase : MaplyTestCase { polyStyleGen = VectorStyleSimpleGenerator(control) lineStyleGen = VectorStyleSimpleGenerator(control) - // The interpeter renders some of the data into images and overlays the rest + // The interpreter renders some of the data into images and overlays the rest interp = MapboxVectorInterpreter(polyStyleGen, tileRenderer, lineStyleGen, control) // Finally the loader asks for tiles diff --git a/android/apps/AutoTesterAndroid/app/src/main/java/com/mousebirdconsulting/autotester/TestCases/PagingLayerTestCase.kt b/android/apps/AutoTesterAndroid/app/src/main/java/com/mousebirdconsulting/autotester/TestCases/PagingLayerTestCase.kt index 33c0821fee..083bd31571 100644 --- a/android/apps/AutoTesterAndroid/app/src/main/java/com/mousebirdconsulting/autotester/TestCases/PagingLayerTestCase.kt +++ b/android/apps/AutoTesterAndroid/app/src/main/java/com/mousebirdconsulting/autotester/TestCases/PagingLayerTestCase.kt @@ -36,7 +36,7 @@ class PagingLayerTestCase(activity: Activity) : MaplyTestCase(activity) { fun setupLayer(vc: BaseController) { // Describes how to break down the space val params = SamplingParams() - params.minZoom = 4 + params.minZoom = 0 params.maxZoom = 22 params.minImportance = 256.0*256.0 params.singleLevel = true diff --git a/android/apps/AutoTesterAndroid/app/src/main/java/com/mousebirdconsulting/autotester/TestCases/SLDTestCase.java b/android/apps/AutoTesterAndroid/app/src/main/java/com/mousebirdconsulting/autotester/TestCases/SLDTestCase.java index 5e92470195..44c1266016 100644 --- a/android/apps/AutoTesterAndroid/app/src/main/java/com/mousebirdconsulting/autotester/TestCases/SLDTestCase.java +++ b/android/apps/AutoTesterAndroid/app/src/main/java/com/mousebirdconsulting/autotester/TestCases/SLDTestCase.java @@ -93,11 +93,11 @@ public void run() { "belfast_ireland_roads.geojson", "belfast_ireland_amenities.geojson"}; - AssetWrapper assetWrap = new AssetWrapper(activity.getAssets()); + AssetWrapper assetWrap = new AssetWrapper(getActivity().getAssets()); for (int i=0; i objects, BaseController baseVC labelInfo.setBackgroundColor(Color.BLUE); labelInfo.setTypeface(Typeface.DEFAULT); labelInfo.setLayoutImportance(1.f); +// labelInfo.setLayoutImportance(Float.MAX_VALUE); labelInfo.setLayoutPlacement(LabelInfo.LayoutRight | LabelInfo.LayoutCenter); labelInfo.setTextJustify(LabelInfo.TextJustify.TextLeft); // labelInfo.setMinVis(0.f); @@ -97,7 +98,10 @@ private void insertLabels(ArrayList objects, BaseController baseVC label.text = labelName; label.loc = object.centroid(); label.selectable = true; - label.layoutImportance = 1.f; +// label.layoutImportance = 1.f; +// label.layoutImportance = Float.MAX_VALUE; +// label.layoutSize = new Point2d(1000.0f, 256.f ); +// label.offset = new Point2d(0.0,-300); // label.rotation = Math.PI/4.0; labels.add(label); diff --git a/android/apps/AutoTesterAndroid/app/src/main/java/com/mousebirdconsulting/autotester/TestCases/ScreenMarkersTestCase.java b/android/apps/AutoTesterAndroid/app/src/main/java/com/mousebirdconsulting/autotester/TestCases/ScreenMarkersTestCase.java index ffebfe1d10..2c2d0f8ed3 100644 --- a/android/apps/AutoTesterAndroid/app/src/main/java/com/mousebirdconsulting/autotester/TestCases/ScreenMarkersTestCase.java +++ b/android/apps/AutoTesterAndroid/app/src/main/java/com/mousebirdconsulting/autotester/TestCases/ScreenMarkersTestCase.java @@ -78,7 +78,8 @@ private void insertMarkers(ArrayList vectors, BaseController baseV marker.size = new Point2d(128, 128); marker.rotation = Math.random() * 2.f * Math.PI; marker.selectable = true; -// marker.offset = new Point2d(-64,-64); +// marker.offset = new Point2d(0.0,-200); +// marker.layoutSize = new Point2d(0.0, 64.0); AttrDictionary attrs = vector.getAttributes(); if (attrs != null) { marker.userObject = attrs.getString("ADMIN"); diff --git a/android/apps/AutoTesterAndroid/app/src/main/java/com/mousebirdconsulting/autotester/TestCases/ShapefileTestCase.java b/android/apps/AutoTesterAndroid/app/src/main/java/com/mousebirdconsulting/autotester/TestCases/ShapefileTestCase.java index 18dd565c18..3d749739f1 100644 --- a/android/apps/AutoTesterAndroid/app/src/main/java/com/mousebirdconsulting/autotester/TestCases/ShapefileTestCase.java +++ b/android/apps/AutoTesterAndroid/app/src/main/java/com/mousebirdconsulting/autotester/TestCases/ShapefileTestCase.java @@ -3,12 +3,13 @@ import android.app.Activity; import android.content.Context; import android.content.ContextWrapper; -import android.graphics.Color; import android.util.Log; import com.mousebird.maply.GlobeController; import com.mousebird.maply.MapController; import com.mousebird.maply.BaseController; +import com.mousebird.maply.Mbr; +import com.mousebird.maply.Point2d; import com.mousebird.maply.RenderController; import com.mousebird.maply.VectorInfo; import com.mousebird.maply.VectorObject; @@ -59,7 +60,7 @@ private File copyFile(String srcName, String destName) throws IOException { return of; } - public void addShapeFile(BaseController baseVC) { + public Mbr addShapeFile(BaseController baseVC) { try { File dbfFile = copyFile("sf_roads/tl_2013_06075_roads.dbf", "sf_roads.dbf"); File shpFile = copyFile("sf_roads/tl_2013_06075_roads.shp", "sf_roads.shp"); @@ -67,15 +68,21 @@ public void addShapeFile(BaseController baseVC) { if (dbfFile != null && shpFile != null && shxFile != null) { VectorObject vecObj = new VectorObject(); vecObj.fromShapeFile(shpFile.getAbsolutePath()); - int numPoints = vecObj.countPoints(); + //int numPoints = vecObj.countPoints(); VectorInfo vecInfo = new VectorInfo(); - vecInfo.setColor(Color.RED); + //vecInfo.setColor(Color.RED); baseVC.addVector(vecObj,vecInfo, RenderController.ThreadMode.ThreadAny); + + Point2d ll = new Point2d(); + Point2d ur = new Point2d(); + vecObj.boundingBox(ll, ur); + return new Mbr(ll, ur); } } catch (Exception e) { Log.d("Maply", e.toString()); } + return null; } @Override @@ -83,7 +90,12 @@ public boolean setUpWithGlobe(GlobeController globeVC) throws Exception { StamenRemoteTestCase baseView = new StamenRemoteTestCase(getActivity()); baseView.setUpWithGlobe(globeVC); - addShapeFile(globeVC); + Mbr bbox = addShapeFile(globeVC); + if (bbox != null) { + Point2d center = bbox.middle(); + double height = globeVC.findHeightToViewBounds(bbox, center); + globeVC.animatePositionGeo(center.getX(),center.getY(),height,2.0); + } return true; } @@ -93,7 +105,12 @@ public boolean setUpWithMap(MapController mapVC) throws Exception { StamenRemoteTestCase baseView = new StamenRemoteTestCase(getActivity()); baseView.setUpWithMap(mapVC); - addShapeFile(mapVC); + Mbr bbox = addShapeFile(mapVC); + if (bbox != null) { + Point2d center = bbox.middle(); + double height = mapVC.findHeightToViewBounds(bbox, center); + mapVC.animatePositionGeo(center.getX(),center.getY(),height,2.0); + } return true; } diff --git a/android/apps/AutoTesterAndroid/app/src/main/java/com/mousebirdconsulting/autotester/TestCases/SimpleStyleTestCase.kt b/android/apps/AutoTesterAndroid/app/src/main/java/com/mousebirdconsulting/autotester/TestCases/SimpleStyleTestCase.kt new file mode 100644 index 0000000000..1676a20959 --- /dev/null +++ b/android/apps/AutoTesterAndroid/app/src/main/java/com/mousebirdconsulting/autotester/TestCases/SimpleStyleTestCase.kt @@ -0,0 +1,243 @@ +/* SimpleStyleTestCase.kt + * AutoTesterAndroid.app + * + * Created by Tim Sylvester on 12/02/2021 + * Copyright © 2021 mousebird consulting, inc. + * + * 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. + */ + +package com.mousebirdconsulting.autotester.TestCases + +import android.app.Activity +import android.util.Log +import com.mousebird.maply.* +import com.mousebirdconsulting.autotester.Framework.MaplyTestCase + +class SimpleStyleTestCase(activity: Activity) : MaplyTestCase(activity, "Simple Vector Styles") { + + override fun setUpWithGlobe(globeVC: GlobeController): Boolean { + baseCase.setUpWithGlobe(globeVC) + runExamples(globeVC) + globeVC.animatePositionGeo(Point2d.FromDegrees(145.0, -33.0), 0.2, 0.0, 0.5) + return true + } + + override fun setUpWithMap(mapVC: MapController): Boolean { + baseCase.setUpWithMap(mapVC) + runExamples(mapVC) + mapVC.animatePositionGeo(Point2d.FromDegrees(145.0, -33.0), 0.2, 0.0, 0.5) + return true + } + + override fun shutdown() { + cleanup() + super.shutdown() + } + + private fun cleanup() { + componentObjects?.let { + controller?.removeObjects(it, threadCurrent) + componentObjects = null + } + styleManager?.shutdown() + styleManager = null + } + + private fun runExamples(vc: BaseController) { + cleanup() + styleManager = SimpleStyleManager(activity.applicationContext, vc).apply { + medSize = Point2d(42.0, 36.0) + largeSize = Point2d(64.0, 80.0) + objectLocator = object : SimpleStyleManager.StyleObjectLocator { + override fun locate(name: String): Collection { + return listOf("$name.png", "maki icons/$name-24@2x.png") + } + } + onAddMarker = { obj, marker, info, style -> + marker.userObject = obj + if (style.labelOffset != null) { + info.setLayoutImportance(Float.MAX_VALUE) + } + true + } + }.also { styleMan -> + componentObjects = arrayOf(vectorGeoJson1, vectorGeoJson2).flatMap { json -> + VectorObject().let { obj -> + if (obj.fromGeoJSON(json)) { + styleMan.addFeatures(obj, threadAny) + } else { + Log.e(javaClass.name, "Failed to parse JSON") + sequenceOf() + } + }.toList() + } + } + } + + private fun prop(name: String, value: String?, quote: Boolean): String? { + return if (value == null) null else ("\"$name\": " + (if (quote) "\"$value\"" else value)) + } + + private fun marker(title: String, lat: Double, lon: Double, m: String? = null, bg: String? = null, + c: Boolean? = null, mC: String? = null, mA: Double? = null, fC: String? = null, + fA: Double? = null, s: Double? = null, sC: String? = null, sA: Double? = null, + mSz: String? = null, ox: Double? = null, oy: Double? = null, lC: String? = null, + lSz: Double? = null, lx: Double? = null, ly: Double? = null): String { + return """ + { + "type": "Feature", + "properties": { + """ + + arrayOf(prop("title", title, true), + prop("marker-size", mSz ?: "large", true), + prop("marker-color", mC, true), + prop("marker-opacity", "$mA", false), + prop("marker-symbol", m, true), + prop("marker-background-symbol", bg, true), + prop("marker-circle", "${!(c ?: true)}", false), + prop("marker-offset-x", if (ox != null) "$ox" else null, false), + prop("marker-offset-y", if (oy != null) "$oy" else null, false), + prop("fill", fC, true), + prop("fill-opacity", if (fA != null) "$fA" else null, false), + prop("stroke-width", if (s != null) "$s" else null, false), + prop("stroke", sC, true), + prop("stroke-opacity", if (sA != null) "$sA" else null, false), + prop("label", lC, true), + prop("label-size", if (lSz != null) "$lSz" else null, false), + prop("label-offset-x", if (lx != null) "$lx" else null, false), + prop("label-offset-y", if (ly != null) "$ly" else null, false) + ).filterNotNull().joinToString(",") + + """ + }, + "geometry": { "type": "Point", "coordinates": [ $lon, $lat ] } + } + """ + } + + private fun markers1(): String { + val startLon = 142.0 + var lat = -30.0 + var lon = startLon + val latStep = 0.05 + val lonStep = 0.05 + var n = 0 + val rowSize = 64 + return arrayOf(null, "bar").flatMap { m -> + arrayOf(null, "marker-stroked").flatMap { bg -> + arrayOf("small", "medium", "large").flatMap { mSz -> + arrayOf(0.0, 2.0, 5.0).flatMap { sWidth -> + arrayOf(0.0, 0.5, 1.0).flatMap { fillA -> + arrayOf(true, false).flatMap { circle -> + arrayOf("f0f", "#0f0").flatMap { mColor -> + arrayOf("0fa", "#a0f").flatMap { fColor -> + arrayOf("fa0", "#0af").map { sColor -> + lon += lonStep + if ((n++ % rowSize) == 0) { lat -= latStep; lon = startLon } + marker("", lat, lon, m, bg, circle, mColor, null, fColor, + fillA, sWidth, sColor, 0.8, mSz) + } } } } } } } } + }.joinToString(",") + } + + private fun fmt(v: Double): String { + return Regex("\\.0+$").replace("$v", "") + } + private fun markers2(): String { + return arrayOf(-4.0, -1.5, 0.0, 1.0).flatMap { ox -> + arrayOf(-5.0, -1.5, 0.0, 1.0).map { oy -> + marker("${fmt(ox)},${fmt(oy)}", -30.0, 140.0,null, "marker-stroked", + false, "f0f", null, "0fa", 0.7, 0.2, "#0af", 0.8, + "medium", ox, oy, "#f50", 5.0, ox * 45 / 5, oy * 38 / 5) + } + }.joinToString(",") + } + + private fun markers3(): String { + return arrayOf( + marker("",-33.0,140.0,"square","wide",false,"#f00",0.7,"#00f",null, + null,null,null,"large",null,null,null,null,null,null), + marker("",-33.1,140.0,"square","tall",false,"#00f",0.7,"#00f",null, + null,null,null,"large",null,null,null,null,null,null), + marker("",-33.0,140.1,"wide","square",false,"#0f0",0.7,"#00f",null, + null,null,null,"large",null,null,null,null,null,null), + marker("",-33.1,140.1,"tall","square",false,"#f0f",0.7,"#00f",null, + null,null,null,"large",null,null,null,null,null,null) + ).joinToString(",") + } + + private val vectorGeoJson1 = """ + { "type": "FeatureCollection", + "features": [{ + "type": "Feature", + "properties": { + "title": "line", + "stroke": "#0000ff", + "stroke-width": 10.0 + }, "geometry": { + "type": "LineString", + "coordinates": [ + [ 140.0677490234375, -31.97871614600332 ], + [ 130.55389404296875, -31.764181375100804 ] + ] } } ] + }""" + + private val vectorGeoJson2 = """ + { "type": "FeatureCollection", + "features": [ + ${markers1()}, + ${markers2()}, + ${markers3()}, + { "type": "Feature", + "properties": { + "title": "poly", + "fill": "#ff0000", + "stroke": "#ffffff" + }, "geometry": { + "type": "Polygon", + "coordinates": [ [ + [ 150.40283203125, -33.504759069226075 ], + [ 151.10595703125, -33.504759069226075 ], + [ 151.10595703125, -33.16974360021616 ], + [ 150.40283203125, -33.16974360021616 ], + [ 150.40283203125, -33.504759069226075 ] + ] ] } }, { + "type": "Feature", + "properties": { + "title": "line", + "stroke": "#0000ff", + "stroke-width": 10.0 + }, "geometry": { + "type": "LineString", + "coordinates": [ + [ 150.0677490234375, -32.97871614600332 ], + [ 150.14190673828125, -32.97641208290518 ], + [ 150.5731201171875, -32.95797741405951 ], + [ 150.98236083984375, -33.01096671579776 ], + [ 150.75164794921875, -33.08463802391686 ], + [ 149.91943359375, -33.27084277265288 ], + [ 149.77935791015625, -32.9049563191375 ], + [ 150.24078369140625, -32.669436832605314 ], + [ 150.55389404296875, -32.764181375100804 ] + ] } } ] + }""" + + private val baseCase: MaplyTestCase + + private var styleManager: SimpleStyleManager? = null + private var componentObjects: List? = null + + private val threadAny = ThreadMode.ThreadAny + private val threadCurrent = ThreadMode.ThreadCurrent + + init { + baseCase = StamenRemoteTestCase(activity) + } +} diff --git a/android/apps/AutoTesterAndroid/app/src/main/java/com/mousebirdconsulting/autotester/TestCases/TextureVectorTestCase.kt b/android/apps/AutoTesterAndroid/app/src/main/java/com/mousebirdconsulting/autotester/TestCases/TextureVectorTestCase.kt index 5fa93cf79a..6bcd414605 100644 --- a/android/apps/AutoTesterAndroid/app/src/main/java/com/mousebirdconsulting/autotester/TestCases/TextureVectorTestCase.kt +++ b/android/apps/AutoTesterAndroid/app/src/main/java/com/mousebirdconsulting/autotester/TestCases/TextureVectorTestCase.kt @@ -22,6 +22,7 @@ package com.mousebirdconsulting.autotester.TestCases import android.app.Activity import android.graphics.BitmapFactory import android.graphics.Color +import android.util.Log import com.mousebird.maply.* import com.mousebirdconsulting.autotester.Framework.MaplyTestCase import com.mousebirdconsulting.autotester.R @@ -32,104 +33,97 @@ import java.util.* import kotlin.math.PI import kotlin.math.abs -class TextureVectorTestCase : MaplyTestCase { - - constructor(activity: Activity) : super(activity) { - setTestName("Textured Vectors") - implementation = TestExecutionImplementation.Both - } - +class TextureVectorTestCase(activity: Activity) : MaplyTestCase(activity, "Textured Vectors") { + // Grid size to use for clipping // Smaller is going to be more triangles, but look better // Might be better to use a bigger size for the poles - val ClipGridSize = 2.0/180.0* PI + private val ClipGridSize = 2.0/180.0* PI - fun buildCountries(control: BaseController) : ComponentObject { - val icon = BitmapFactory.decodeResource(getActivity().resources, R.drawable.testtarget) - val tex = control.addTexture(icon, null, RenderControllerInterface.ThreadMode.ThreadCurrent) + private fun buildCountries(control: BaseController) : ComponentObject { + val assetMgr = activity.assets + val isGlobe = control is GlobeController + + val icon = BitmapFactory.decodeResource(activity.resources, R.drawable.testtarget) + val tex = control.addTexture(icon, null, ThreadMode.ThreadCurrent) + val paths = assetMgr.list("country_json_50m")?.sortedDescending() ?: emptyList() - val assetMgr = getActivity().assets - val paths = assetMgr.list("country_json_50m")!! + val tessObjs = ArrayList() - var tessObjs = ArrayList() - - // Load in 20 contries to apply a texture to - var count = 0 - for (path in paths) { - val stream = assetMgr.open("country_json_50m/" + path) + // Load in 20 countries to apply a texture to + for (path in paths.take(20)) { try { - val vecObj = VectorObject() - vecObj.selectable = true - val json = Okio.buffer(Okio.source(stream)).readUtf8() - if (vecObj.fromGeoJSON(json)) { - // Work through each individual loop - for (thisVecObj in vecObj) { - val loopObj = thisVecObj.deepCopy() - - // Center of the texture application - val center = loopObj.center() - val attrs = loopObj.attributes - attrs.setDouble("veccenterx", center.x) - attrs.setDouble("veccentery", center.y) - - var tessObj : VectorObject? - if (control is GlobeController) { - // We adjust the grid clipping size based on the latitude - // This helps a lot near the poles. Otherwise we're way oversampling - var thisClipGridLon = ClipGridSize - if (abs(center.x) > 60.0/180.0 * PI) - thisClipGridLon *= 4.0 - else if (abs(center.y) > 45.0/180.0 * PI) - thisClipGridLon *= 2.0; - - // We clip the vector to a grid and then tesselate the results - // This forms the vector closer to the globe, make it look nicer - tessObj = loopObj.clipToGrid(Point2d(thisClipGridLon,ClipGridSize)).tesselate() - } else { - tessObj = loopObj.tesselate(); + assetMgr.open("country_json_50m/$path").use { stream -> + Okio.source(stream).use { source -> + Okio.buffer(source).use { buffer -> + VectorObject.createFromGeoJSON(buffer.readUtf8())?.let { vecObj -> + vecObj.selectable = true + + // Work through each individual loop + for (thisVecObj in vecObj) { + val loopObj = thisVecObj.deepCopy() + + // Center of the texture application + val center = loopObj.center() + val attrs = loopObj.attributes + attrs.setDouble("veccenterx", center.x) + attrs.setDouble("veccentery", center.y) + + var thisClipGridLon = ClipGridSize + if (isGlobe) { + // We adjust the grid clipping size based on the latitude + // This helps a lot near the poles. Otherwise we're way oversampling + if (abs(center.x) > 60.0 / 180.0 * PI) + thisClipGridLon *= 4.0 + else if (abs(center.y) > 45.0 / 180.0 * PI) + thisClipGridLon *= 2.0; + + } + loopObj.clipToGrid(Point2d(thisClipGridLon, ClipGridSize))?.tesselate()?.let { + tessObjs.add(it) + } + } + } } - - if (tessObj != null) - tessObjs.add(tessObj) } } + } catch (e: Exception) { + Log.e(javaClass.simpleName, "Failed to load $path", e) } - catch (e: Exception) - { - } - - if (count++ > 20) - break } - val vecInfo = VectorInfo() - vecInfo.setFilled(true) - vecInfo.setTexture(tex) - vecInfo.setTextureProjection(VectorInfo.TextureProjection.TangentPlane) - vecInfo.setTexScale(16.0, 16.0) - vecInfo.setColor(Color.WHITE) + val vecInfo = VectorInfo().apply { + setFilled(true) + setTexture(tex) + // todo: TextureProjection.Screen doesn't work? + setTextureProjection(if (isGlobe) VectorInfo.TextureProjection.TangentPlane else VectorInfo.TextureProjection.None) + setTexScale(16.0, 16.0) + setColor(Color.WHITE) + } // Add all the vectors at once to be more efficient // The geometry gets grouped together, which is nice and fast - return control.addVectors(tessObjs, vecInfo, RenderControllerInterface.ThreadMode.ThreadAny) + return control.addVectors(tessObjs, vecInfo, ThreadMode.ThreadAny) } var baseCase : CartoLightTestCase? = null override fun setUpWithMap(mapVC: MapController?): Boolean { - baseCase = CartoLightTestCase(getActivity()) + baseCase = CartoLightTestCase(activity) baseCase?.setUpWithMap(mapVC) buildCountries(mapVC!!) + mapVC.animatePositionGeo(-Math.PI/2,0.0,1.0,1.0) return true } override fun setUpWithGlobe(globeVC: GlobeController?): Boolean { - baseCase = CartoLightTestCase(getActivity()) + baseCase = CartoLightTestCase(activity) baseCase?.setUpWithGlobe(globeVC) buildCountries(globeVC!!) + globeVC.animatePositionGeo(-Math.PI/2,0.0,1.0,1.0) return true } diff --git a/android/apps/AutoTesterAndroid/app/src/main/java/com/mousebirdconsulting/autotester/TestCases/VectorMBTilesTestCase.kt b/android/apps/AutoTesterAndroid/app/src/main/java/com/mousebirdconsulting/autotester/TestCases/VectorMBTilesTestCase.kt index d6afd45e09..39095b2e2e 100644 --- a/android/apps/AutoTesterAndroid/app/src/main/java/com/mousebirdconsulting/autotester/TestCases/VectorMBTilesTestCase.kt +++ b/android/apps/AutoTesterAndroid/app/src/main/java/com/mousebirdconsulting/autotester/TestCases/VectorMBTilesTestCase.kt @@ -23,8 +23,8 @@ import android.app.Activity import android.content.Context import android.content.ContextWrapper import android.graphics.Color +import android.os.Handler import com.mousebird.maply.* -import com.mousebirdconsulting.autotester.ConfigOptions import com.mousebirdconsulting.autotester.Framework.MaplyTestCase import java.io.File import java.io.FileNotFoundException @@ -61,7 +61,7 @@ class VectorMBTilesTestCase : MaplyTestCase { // The fetcher fetches tile from the MBTiles file // The fetcher fetches tile from the MBTiles file - val mbTileFetcher = MBTileFetcher(mbTiles) + val mbTileFetcher = MBTileFetcher(control, mbTiles) // Set up the parameters to match the MBTile file // Set up the parameters to match the MBTile file @@ -93,7 +93,7 @@ class VectorMBTilesTestCase : MaplyTestCase { } // The fetcher fetches tile from the MBTiles file - val mbTileFetcher = MBTileFetcher(mbTiles) + val mbTileFetcher = MBTileFetcher(control, mbTiles) // Set up the parameters to match the MBTile file val params = SamplingParams() @@ -121,7 +121,7 @@ class VectorMBTilesTestCase : MaplyTestCase { fun setupFranceVector(control: BaseController) { val mbTileFile = getFile("mbtiles", "mbtiles/France.mbtiles", "France.mbtiles") - val fetcher = MBTileFetcher(mbTileFile) + val fetcher = MBTileFetcher(control, mbTileFile) // Sampling params define how the globe is broken up, including the depth var params = SamplingParams() @@ -143,6 +143,19 @@ class VectorMBTilesTestCase : MaplyTestCase { val loader = QuadPagingLoader(params, fetcher.tileInfo, interp, control) loader.setTileFetcher(fetcher) this.loader = loader + + // Shut down the loader after a short period + // Useful for debugging +// val handler = Handler() +// handler.postDelayed({ +// loader.shutdown() +// this.loader = null +// +// val handler = Handler() +// handler.postDelayed({ +// this.setupFranceVector(control) +// }, 1000) +// }, 500) } fun setupShapefile(control: BaseController) { @@ -155,7 +168,7 @@ class VectorMBTilesTestCase : MaplyTestCase { val vecInfo = VectorInfo() vecInfo.setColor(Color.MAGENTA) vecInfo.drawPriority = 1000000 - control.addVector(shpData,vecInfo,RenderControllerInterface.ThreadMode.ThreadAny) + control.addVector(shpData, vecInfo, RenderControllerInterface.ThreadMode.ThreadAny) } var baseCase: GeographyClass? = null diff --git a/android/apps/AutoTesterAndroid/app/src/main/java/com/mousebirdconsulting/autotester/TestCases/WideVectorsTestCase.java b/android/apps/AutoTesterAndroid/app/src/main/java/com/mousebirdconsulting/autotester/TestCases/WideVectorsTestCase.java index 0268283ce7..f1bce1d276 100644 --- a/android/apps/AutoTesterAndroid/app/src/main/java/com/mousebirdconsulting/autotester/TestCases/WideVectorsTestCase.java +++ b/android/apps/AutoTesterAndroid/app/src/main/java/com/mousebirdconsulting/autotester/TestCases/WideVectorsTestCase.java @@ -3,6 +3,7 @@ import android.app.Activity; import android.graphics.Bitmap; import android.graphics.Color; +import android.util.Log; import com.mousebird.maply.ComponentObject; import com.mousebird.maply.GlobeController; @@ -10,6 +11,7 @@ import com.mousebird.maply.MapController; import com.mousebird.maply.BaseController; import com.mousebird.maply.MaplyTexture; +import com.mousebird.maply.Point2d; import com.mousebird.maply.RenderController; import com.mousebird.maply.VectorInfo; import com.mousebird.maply.VectorObject; @@ -31,53 +33,71 @@ public WideVectorsTestCase(Activity activity) { this.implementation = TestExecutionImplementation.Both; } - void addGeoJSON(BaseController baseController, String name) { - // Build a dashed pattern - LinearTextureBuilder texBuild = new LinearTextureBuilder(); - int[] pattern = new int[2]; - pattern[0] = 4; - pattern[1] = 4; - texBuild.setPattern(pattern); - Bitmap patternImage = texBuild.makeImage(); - RenderController.TextureSettings texSet = new RenderController.TextureSettings(); - texSet.wrapU = true; texSet.wrapV = true; - MaplyTexture tex = baseController.addTexture(patternImage, texSet, RenderController.ThreadMode.ThreadCurrent); + ArrayList addGeoJSON(BaseController baseController, String name, + float width, int drawPriority, int color, + boolean usePattern, double edge) { + MaplyTexture tex = null; + if (usePattern) { + // Build a dashed pattern + LinearTextureBuilder texBuild = new LinearTextureBuilder(); + int[] pattern = new int[2]; + pattern[0] = 4; + pattern[1] = 4; + texBuild.setPattern(pattern); + Bitmap patternImage = texBuild.makeImage(); + RenderController.TextureSettings texSet = new RenderController.TextureSettings(); + texSet.wrapU = true; + texSet.wrapV = true; + tex = baseController.addTexture(patternImage, texSet, RenderController.ThreadMode.ThreadCurrent); + } WideVectorInfo wideVecInfo = new WideVectorInfo(); - wideVecInfo.setColor(Color.BLUE); - wideVecInfo.setLineWidth(20.0f); - wideVecInfo.setTexture(tex); - wideVecInfo.setTextureRepeatLength(8.0); + wideVecInfo.setColor(color); + wideVecInfo.setLineWidth(width); + wideVecInfo.setDrawPriority(drawPriority); + if (tex != null) { + wideVecInfo.setTexture(tex); + wideVecInfo.setTextureRepeatLength(8.0); + } + + wideVecInfo.setEdgeFalloff(edge); VectorInfo vecInfo = new VectorInfo(); vecInfo.setLineWidth(4.0f); vecInfo.setColor(Color.BLACK); - ArrayList compObjs = new ArrayList(); - try { InputStream stream = getActivity().getAssets().open("wide_vecs/" + name); String json = Okio.buffer(Okio.source(stream)).readUtf8(); VectorObject vecObj = new VectorObject(); vecObj.fromGeoJSON(json); + vecObj = vecObj.subdivideToGlobeGreatCircle(0.0001f); ComponentObject compObj = baseController.addVector(vecObj, vecInfo, RenderController.ThreadMode.ThreadAny); - compObjs.add(compObj); ComponentObject compObj2 = baseController.addWideVector(vecObj, wideVecInfo, RenderController.ThreadMode.ThreadAny); + + ArrayList compObjs = new ArrayList<>(); + compObjs.add(compObj); compObjs.add(compObj2); + return compObjs; } catch (Exception e) { + Log.e(getClass().getSimpleName(), "Failed", e); + return null; } } void wideVecTest(BaseController baseController) { -// addGeoJSON(baseController, "sawtooth.geojson"); - addGeoJSON(baseController, "mowing-lawn.geojson"); - addGeoJSON(baseController, "spiral.geojson"); - addGeoJSON(baseController, "square.geojson"); - addGeoJSON(baseController, "track.geojson"); - addGeoJSON(baseController, "uturn2.geojson"); - addGeoJSON(baseController, "testJson.geojson"); + addGeoJSON(baseController, "mowing-lawn.geojson", 20.0f, VectorInfo.VectorPriorityDefault, Color.BLUE, true, 1); + addGeoJSON(baseController, "spiral.geojson", 20.0f, VectorInfo.VectorPriorityDefault, Color.BLUE, true, 1); + addGeoJSON(baseController, "square.geojson", 20.0f, VectorInfo.VectorPriorityDefault, Color.BLUE, true, 1); + addGeoJSON(baseController, "line.geojson", 100.0f, VectorInfo.VectorPriorityDefault, Color.BLUE, true, 1); + addGeoJSON(baseController, "line.geojson", 60.0f, VectorInfo.VectorPriorityDefault+10, Color.RED, true, 1); + addGeoJSON(baseController, "track.geojson", 20.0f, VectorInfo.VectorPriorityDefault, Color.BLUE, true, 1); + addGeoJSON(baseController, "uturn2.geojson", 20.0f, VectorInfo.VectorPriorityDefault, Color.BLUE, true, 1); + addGeoJSON(baseController, "testJson.geojson", 20.0f, VectorInfo.VectorPriorityDefault, Color.BLUE, true, 1); + + addGeoJSON(baseController, "sawtooth.geojson", 50.0f, VectorInfo.VectorPriorityDefault, Color.RED, false,20); } @Override @@ -87,6 +107,7 @@ public boolean setUpWithMap(MapController mapVC) throws Exception { wideVecTest(mapVC); + mapVC.animatePositionGeo(Point2d.FromDegrees(-100,40),0.5,0.0,1.0); return true; } diff --git a/android/apps/AutoTesterAndroid/build.gradle b/android/apps/AutoTesterAndroid/build.gradle index a9fee38a6e..7cc0c6476b 100644 --- a/android/apps/AutoTesterAndroid/build.gradle +++ b/android/apps/AutoTesterAndroid/build.gradle @@ -7,7 +7,7 @@ buildscript { google() } dependencies { - classpath 'com.android.tools.build:gradle:4.0.1' + classpath 'com.android.tools.build:gradle:4.1.1' classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" // NOTE: Do not place your application dependencies here; they belong diff --git a/android/apps/AutoTesterAndroid/gradle/wrapper/gradle-wrapper.properties b/android/apps/AutoTesterAndroid/gradle/wrapper/gradle-wrapper.properties index 3cf147cd11..5f978e2dae 100644 --- a/android/apps/AutoTesterAndroid/gradle/wrapper/gradle-wrapper.properties +++ b/android/apps/AutoTesterAndroid/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ -#Thu Aug 06 09:59:49 PDT 2020 +#Tue Feb 23 10:02:53 PST 2021 distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-6.1.1-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-6.5-all.zip diff --git a/android/library/maply/CMakeLists.txt b/android/library/maply/CMakeLists.txt index 3e5ee4325e..4bb6f04a71 100644 --- a/android/library/maply/CMakeLists.txt +++ b/android/library/maply/CMakeLists.txt @@ -46,6 +46,7 @@ include("${LOCALLIBS_DIR}/nanopb/wgmaplyCMakeLists.txt") include("${LOCALLIBS_DIR}/shapefile/wgmaplyCMakeLists.txt") include("${LOCALLIBS_DIR}/glues/wgmaplyCMakeLists.txt") include("${LOCALLIBS_DIR}/libjson/wgmaplyCMakeLists.txt") +include("${LOCALLIBS_DIR}/lodepng/wgmaplyCMakeLists.txt") include("${COMMON_DIR}/WhirlyGlobeLib/src/CMakeLists.txt") include("${WGLIBANDROID}/src/CMakeLists.txt") include("${JNIDIR}/CMakeLists.txt") diff --git a/android/library/maply/WhirlyGlobeLib/include/ComponentManager_Android.h b/android/library/maply/WhirlyGlobeLib/include/ComponentManager_Android.h index 3a17a83ef9..a6783131de 100644 --- a/android/library/maply/WhirlyGlobeLib/include/ComponentManager_Android.h +++ b/android/library/maply/WhirlyGlobeLib/include/ComponentManager_Android.h @@ -40,9 +40,10 @@ class ComponentManager_Android : public ComponentManager virtual void removeComponentObjects(PlatformThreadInfo *threadInfo,const SimpleIDSet &compIDs,ChangeSet &changes) override; protected: - virtual ComponentObjectRef makeComponentObject() override; + virtual ComponentObjectRef makeComponentObject(const Dictionary *desc = nullptr) override; jobject compManagerObj; jmethodID objectsRemovedMethod; }; +typedef std::shared_ptr ComponentManager_AndroidRef; } diff --git a/android/library/maply/WhirlyGlobeLib/include/Dictionary_Android.h b/android/library/maply/WhirlyGlobeLib/include/Dictionary_Android.h index 29fa66a295..5901b688fe 100644 --- a/android/library/maply/WhirlyGlobeLib/include/Dictionary_Android.h +++ b/android/library/maply/WhirlyGlobeLib/include/Dictionary_Android.h @@ -1,9 +1,8 @@ -/* - * Dictionary.h +/* Dictionary.h * WhirlyGlobeLib * * Created by Steve Gifford on 12/16/13. - * Copyright 2011-2013 mousebird consulting + * Copyright 2011-2021 mousebird consulting * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -15,7 +14,6 @@ * 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. - * */ #import @@ -38,86 +36,91 @@ typedef std::shared_ptr DictionaryEntry_AndroidRef; class MutableDictionary_Android : public MutableDictionary { public: - MutableDictionary_Android(); + MutableDictionary_Android() {} // Construct from a raw data buffer MutableDictionary_Android(RawData *rawData); // Copy constructor MutableDictionary_Android(const MutableDictionary_Android &that); + MutableDictionary_Android(MutableDictionary_Android &&that) noexcept; MutableDictionary_Android(const Dictionary &that); // Assignment operator - MutableDictionary_Android &operator = (const MutableDictionary_Android &that); - virtual MutableDictionaryRef copy() const; - virtual ~MutableDictionary_Android(); + MutableDictionary_Android &operator=(const MutableDictionary_Android &that); + MutableDictionary_Android &operator=(MutableDictionary_Android &&that) noexcept; + virtual MutableDictionaryRef copy() const override; + virtual ~MutableDictionary_Android() = default; - class Value; + struct Value; typedef std::shared_ptr ValueRef; // Parse from a JSON string - bool parseJSON(const std::string jsonString); + bool parseJSON(const std::string &jsonString); bool parseJSONNode(JSONNode &node); ValueRef parseJSONValue(JSONNode::iterator &nodeIt); + virtual int count() const override { return fields.size(); } + + virtual bool empty() const override { return fields.empty(); } + /// Clean out the contents - void clear(); + void clear() override; /// Number of fields being represented int numFields() const; /// Returns true if the field exists - virtual bool hasField(const std::string &name) const; + virtual bool hasField(const std::string &name) const override; /// Returns the field type - virtual DictionaryType getType(const std::string &name) const; + virtual DictionaryType getType(const std::string &name) const override; /// Remove the given field by name - void removeField(const std::string &name); + void removeField(const std::string &name) override; /// Return an int, using the default if it's missing - virtual int getInt(const std::string &name,int defVal=0.0) const; - virtual int64_t getInt64(const std::string &name,int64_t defVal=0) const; + virtual int getInt(const std::string &name,int defVal) const override; + virtual int64_t getInt64(const std::string &name,int64_t defVal) const override; /// Return a 64 bit unique identity or 0 if missing - virtual SimpleIdentity getIdentity(const std::string &name) const; + virtual SimpleIdentity getIdentity(const std::string &name) const override; /// Interpret an int as a boolean - virtual bool getBool(const std::string &name,bool defVal=false) const; + virtual bool getBool(const std::string &name,bool defVal) const override; /// Interpret an int as a RGBA color - virtual RGBAColor getColor(const std::string &name,const RGBAColor &defVal) const; + virtual RGBAColor getColor(const std::string &name,const RGBAColor &defVal) const override; /// Return a double, using the default if it's missing - virtual double getDouble(const std::string &name,double defVal=0.0) const; + virtual double getDouble(const std::string &name,double defVal) const override; /// Return a string, or empty if it's missing - virtual std::string getString(const std::string &name) const; + virtual std::string getString(const std::string &name) const override; /// Return a string, using the default if it's missing - virtual std::string getString(const std::string &name,const std::string &defVal) const; + virtual std::string getString(const std::string &name,const std::string &defVal) const override; /// Return an object pointer DelayedDeletableRef getObject(const std::string &name); /// Return a dictionary as an entry - virtual DictionaryRef getDict(const std::string &name) const; + virtual DictionaryRef getDict(const std::string &name) const override; // Return a generic entry - virtual DictionaryEntryRef getEntry(const std::string &name) const; + virtual DictionaryEntryRef getEntry(const std::string &name) const override; // Return an array (if it is an array) - virtual std::vector getArray(const std::string &name) const; + virtual std::vector getArray(const std::string &name) const override; // Return an array of keys - virtual std::vector getKeys() const; + virtual std::vector getKeys() const override; /// Set field as int - void setInt(const std::string &name,int val); - void setInt64(const std::string &name,int64_t val); + void setInt(const std::string &name,int val) override; + virtual void setInt64(const std::string &name,int64_t val) override; /// Set field as 64 bit unique value - void setIdentifiable(const std::string &name,SimpleIdentity val); + void setIdentifiable(const std::string &name,SimpleIdentity val) override; /// Set field as double - void setDouble(const std::string &name,double val); + void setDouble(const std::string &name,double val) override; /// Set field as string - void setString(const std::string &name,const std::string &val); + void setString(const std::string &name,const std::string &val) override; /// Set the dictionary at the given attribute name - void setDict(const std::string &name,MutableDictionary_AndroidRef dict); + void setDict(const std::string &name,const MutableDictionary_AndroidRef &dict); /// Set the entry at the given attribute name - void setEntry(const std::string &name,DictionaryEntry_AndroidRef entry); - void setEntry(const std::string &name,DictionaryEntryRef entry); + void setEntry(const std::string &name,const DictionaryEntryRef &entry); /// Set the array at the given attribute name - void setArray(const std::string &name,std::vector &entries); + void setArray(const std::string &name,const std::vector &entries); /// Set the array at the given attribute name - void setArray(const std::string &name,std::vector &entries); + void setArray(const std::string &name,const std::vector &entries); /// Set field as pointer - void setObject(const std::string &name,DelayedDeletableRef obj); + void setObject(const std::string &name,const DelayedDeletableRef &obj); // Write data to a raw data buffer void asRawData(MutableRawData *rawData); @@ -126,167 +129,186 @@ class MutableDictionary_Android : public MutableDictionary std::string toString() const; // Merge in key-value pairs from another dictionary - void addEntries(const Dictionary *other); + void addEntries(const Dictionary *other) override; - // Make a generic ValueRef from a generic entry (yeah, they're different - static ValueRef makeValueRef(DictionaryEntry_AndroidRef entry); - static ValueRef makeValueRef(DictionaryEntryRef entry); + // Make a generic ValueRef from a generic entry (yeah, they're different) + static ValueRef makeValueRef(const DictionaryEntry_AndroidRef &entry); + static ValueRef makeValueRef(const DictionaryEntryRef &entry); - class Value + struct Value { - public: - Value() { } - virtual ~Value() { } - - virtual DictionaryType type() = 0; - virtual ValueRef copy() = 0; - virtual int asInt() = 0; - virtual int64_t asInt64() = 0; - virtual SimpleIdentity asIdentity() = 0; - virtual void asString(std::string &retStr) = 0; - virtual double asDouble() = 0; - virtual DelayedDeletableRef asObject() { return DelayedDeletableRef(); } - virtual DictionaryRef asDict() { return DictionaryRef(); } + virtual ~Value() = default; + virtual DictionaryType type() const = 0; + virtual ValueRef copy() const = 0; + virtual int asInt() const = 0; + virtual int64_t asInt64() const = 0; + virtual SimpleIdentity asIdentity() const = 0; + virtual double asDouble() const = 0; + virtual void asString(std::string &retStr) const = 0; + virtual std::string asString() const = 0; + virtual DelayedDeletableRef asObject() const { return DelayedDeletableRef(); } + virtual DictionaryRef asDict() const { return DictionaryRef(); } + + virtual bool isEqual(const DictionaryEntry& other) const = 0; + virtual bool isEqual(const DictionaryEntry_Android& other) const = 0; }; - class StringValue : public Value + struct StringValue : public Value { - public: - StringValue() { } StringValue(const std::string &inVal) : val(inVal) { } + StringValue(std::string &&inVal) noexcept : val(std::move(inVal)) { } - virtual DictionaryType type() { return DictTypeString; } - virtual ValueRef copy() { return ValueRef(new StringValue(val)); } - virtual int asInt(); - virtual int64_t asInt64(); - virtual SimpleIdentity asIdentity(); - virtual void asString(std::string &retStr) { retStr = val; } - virtual double asDouble(); + virtual DictionaryType type() const override { return DictTypeString; } + virtual ValueRef copy() const override { return std::make_shared(val); } + virtual int asInt() const override; + virtual int64_t asInt64() const override; + virtual SimpleIdentity asIdentity() const override; + virtual void asString(std::string &retStr) const override { retStr = val; } + virtual std::string asString() const override { return val; } + virtual double asDouble() const override; + + virtual bool isEqual(const DictionaryEntry& other) const override; + virtual bool isEqual(const DictionaryEntry_Android& other) const override; std::string val; }; - class IntValue : public Value + struct IntValue : public Value { - public: - IntValue() : val(0) { } IntValue(int inVal) : val(inVal) { } - virtual DictionaryType type() { return DictTypeInt; } - virtual ValueRef copy() { return ValueRef(new IntValue(val)); } - virtual int asInt() { return val; } - virtual int64_t asInt64() { return (int64_t)val; } - virtual SimpleIdentity asIdentity() { return val; } - virtual void asString(std::string &retStr); - virtual double asDouble() { return (double)val; } + virtual DictionaryType type() const override { return DictTypeInt; } + virtual ValueRef copy() const override { return std::make_shared(val); } + virtual int asInt() const override { return val; } + virtual int64_t asInt64() const override { return (int64_t)val; } + virtual SimpleIdentity asIdentity() const override { return val; } + virtual void asString(std::string &retStr) const override; + virtual std::string asString() const override; + virtual double asDouble() const override { return (double)val; } + + virtual bool isEqual(const DictionaryEntry& other) const override { return val == other.getInt(); } + virtual bool isEqual(const DictionaryEntry_Android& other) const override; int val; }; - class Int64Value : public Value + struct Int64Value : public Value { - public: - Int64Value() : val(0) { } Int64Value(int64_t inVal) : val(inVal) { } - virtual DictionaryType type() { return DictTypeInt64; } - virtual ValueRef copy() { return ValueRef(new Int64Value(val)); } - virtual int asInt() { return (int)val; } - virtual int64_t asInt64() { return val; } - virtual SimpleIdentity asIdentity() { return (SimpleIdentity)val; } - virtual void asString(std::string &retStr); - virtual double asDouble() { return (double)val; } + virtual DictionaryType type() const override { return DictTypeInt64; } + virtual ValueRef copy() const override { return std::make_shared(val); } + virtual int asInt() const override { return (int)val; } + virtual int64_t asInt64() const override { return val; } + virtual SimpleIdentity asIdentity() const override { return (SimpleIdentity)val; } + virtual void asString(std::string &retStr) const override; + virtual std::string asString() const override; + virtual double asDouble() const override { return (double)val; } + + virtual bool isEqual(const DictionaryEntry& other) const override { return val == other.getIdentity(); } + virtual bool isEqual(const DictionaryEntry_Android& other) const override; int64_t val; }; - class DoubleValue : public Value + struct DoubleValue : public Value { - public: - DoubleValue() : val(0.0) { } DoubleValue(double inVal) : val(inVal) { } - virtual DictionaryType type() { return DictTypeDouble; } - virtual ValueRef copy() { return ValueRef(new DoubleValue(val)); } - virtual int asInt() { return (int)val; } - virtual int64_t asInt64() { return (int64_t)val; } - virtual SimpleIdentity asIdentity() { return EmptyIdentity; } - virtual void asString(std::string &retStr); - virtual double asDouble() { return val; } + virtual DictionaryType type() const override { return DictTypeDouble; } + virtual ValueRef copy() const override { return std::make_shared(val); } + virtual int asInt() const override { return (int)val; } + virtual int64_t asInt64() const override { return (int64_t)val; } + virtual SimpleIdentity asIdentity() const override { return EmptyIdentity; } + virtual void asString(std::string &retStr) const override; + virtual std::string asString() const override; + virtual double asDouble() const override { return val; } + + virtual bool isEqual(const DictionaryEntry& other) const override { return val == other.getDouble(); } + virtual bool isEqual(const DictionaryEntry_Android& other) const override; double val; }; - class IdentityValue : public Value + struct IdentityValue : public Value { - public: - IdentityValue() : val(0) { } IdentityValue(SimpleIdentity inVal) : val(inVal) { } - virtual DictionaryType type() { return DictTypeIdentity; } - virtual ValueRef copy() { return ValueRef(new IdentityValue(val)); } - virtual int asInt() { return (int)val; } - virtual int64_t asInt64() { return (int64_t)val; } - virtual SimpleIdentity asIdentity() { return val; } - virtual void asString(std::string &retStr); - virtual double asDouble() { return (double)val; } + virtual DictionaryType type() const override { return DictTypeIdentity; } + virtual ValueRef copy() const override { return std::make_shared(val); } + virtual int asInt() const override { return (int)val; } + virtual int64_t asInt64() const override { return (int64_t)val; } + virtual SimpleIdentity asIdentity() const override { return val; } + virtual void asString(std::string &retStr) const override; + virtual std::string asString() const override; + virtual double asDouble() const override { return (double)val; } + + virtual bool isEqual(const DictionaryEntry& other) const override { return val == other.getIdentity(); } + virtual bool isEqual(const DictionaryEntry_Android& other) const override; SimpleIdentity val; }; - class ObjectValue : public Value + struct ObjectValue : public Value { - public: - ObjectValue() { } - ObjectValue(DelayedDeletableRef inVal) : val(inVal) { } - - virtual DictionaryType type() { return DictTypeObject; } - virtual ValueRef copy() { return ValueRef(new ObjectValue(val)); } - virtual int asInt() { return 0; } - virtual int64_t asInt64() { return 0; } - virtual void asString(std::string &retStr) { } - virtual SimpleIdentity asIdentity() { return EmptyIdentity; } - virtual double asDouble() { return 0.0; } - virtual DelayedDeletableRef asObject() { return val; } + ObjectValue(const DelayedDeletableRef &inVal) : val(inVal) { } + + virtual DictionaryType type() const override { return DictTypeObject; } + virtual ValueRef copy() const override { return std::make_shared(val); } + virtual int asInt() const override { return 0; } + virtual int64_t asInt64() const override { return 0; } + virtual void asString(std::string &retStr) const override { } + virtual std::string asString() const override { return std::string(); } + virtual SimpleIdentity asIdentity() const override { return EmptyIdentity; } + virtual double asDouble() const override { return 0.0; } + virtual DelayedDeletableRef asObject() const override { return val; } + + virtual bool isEqual(const DictionaryEntry& other) const override { return false; } + virtual bool isEqual(const DictionaryEntry_Android& other) const override { return false; } DelayedDeletableRef val; }; - class DictionaryValue : public Value + struct DictionaryValue : public Value { - public: - DictionaryValue() { } - DictionaryValue(MutableDictionary_AndroidRef inVal) : val(inVal) { } - - virtual DictionaryType type() { return DictTypeDictionary; } - virtual ValueRef copy() { return ValueRef(new DictionaryValue(val)); } - virtual int asInt() { return 0; } - virtual int64_t asInt64() { return 0; } - virtual void asString(std::string &retStr) { } - virtual SimpleIdentity asIdentity() { return EmptyIdentity; } - virtual double asDouble() { return 0.0; } - virtual DictionaryRef asDict(); + DictionaryValue(const MutableDictionary_AndroidRef &inVal) : val(inVal) { } + + virtual DictionaryType type() const override { return DictTypeDictionary; } + virtual ValueRef copy() const override { return std::make_shared(val); } + virtual int asInt() const override { return 0; } + virtual int64_t asInt64() const override { return 0; } + virtual void asString(std::string &retStr) const override { } + virtual std::string asString() const override { return std::string(); } + virtual SimpleIdentity asIdentity() const override { return EmptyIdentity; } + virtual double asDouble() const override { return 0.0; } + virtual DictionaryRef asDict() const override { return val; } + + virtual bool isEqual(const DictionaryEntry& other) const override { return false; } + virtual bool isEqual(const DictionaryEntry_Android& other) const override { return false; } MutableDictionary_AndroidRef val; }; typedef std::shared_ptr DictionaryValueRef; - class ArrayValue : public Value + struct ArrayValue : public Value { - public: - ArrayValue() { } - ~ArrayValue() { } - ArrayValue(std::vector &inVal) : val(inVal) { } - ArrayValue(std::vector &inVal); - ArrayValue(std::vector &inVal); - - virtual DictionaryType type() { return DictTypeArray; } - virtual ValueRef copy() { return ValueRef(new ArrayValue(val)); } - virtual int asInt() { return 0; } - virtual int64_t asInt64() { return 0; } - virtual void asString(std::string &retStr) { } - virtual SimpleIdentity asIdentity() { return EmptyIdentity; } - virtual double asDouble() { return 0.0; } + ArrayValue(ArrayValue &&inVal) noexcept : val(std::move(inVal.val)) { } + ArrayValue(const std::vector &inVal) : val(inVal) { } + ArrayValue(std::vector &&inVal) noexcept : val(std::move(inVal)) { } + ArrayValue(const std::vector &inVal); + ArrayValue(const std::vector &inVal); + + virtual DictionaryType type() const override { return DictTypeArray; } + virtual ValueRef copy() const override { return std::make_shared(val); } + virtual int asInt() const override { return 0; } + virtual int64_t asInt64() const override { return 0; } + virtual void asString(std::string &retStr) const override { } + virtual std::string asString() const override { return std::string(); } + virtual SimpleIdentity asIdentity() const override { return EmptyIdentity; } + virtual double asDouble() const override { return 0.0; } + + virtual bool isEqual(const DictionaryEntry& other) const override { return false; } + virtual bool isEqual(const DictionaryEntry_Android& other) const override { return false; } std::vector val; }; @@ -301,33 +323,50 @@ class MutableDictionary_Android : public MutableDictionary class DictionaryEntry_Android : public DictionaryEntry { public: - DictionaryEntry_Android() : type(DictTypeNone) { }; - DictionaryEntry_Android(MutableDictionary_Android::ValueRef val) : val(val) { type = val->type(); } - DictionaryEntry_Android &operator = (const DictionaryEntry_Android &that) { val = that.val; type = that.type; return *this; } + DictionaryEntry_Android() + : type(DictTypeNone) + { + } + + DictionaryEntry_Android(MutableDictionary_Android::ValueRef val) + : val(val), type(val->type()) + { + } + + virtual ~DictionaryEntry_Android() = default; + + DictionaryEntry_Android &operator= (const DictionaryEntry_Android &that) + { + val = that.val; + type = that.type; + return *this; + } /// Returns the field type - virtual DictionaryType getType() const; + virtual DictionaryType getType() const { return type; } /// Return an int, using the default if it's missing - virtual int getInt() const; + virtual int getInt() const { return val ? val->asInt() : 0; } /// Return an int64, using the default if it's missing - virtual int64_t getInt64() const; + virtual int64_t getInt64() const { return val ? val->asInt64() : 0; } /// Return a 64 bit unique identity or 0 if missing - virtual SimpleIdentity getIdentity() const; + virtual SimpleIdentity getIdentity() const { return getInt64(); } /// Interpret an int as a boolean - virtual bool getBool() const; + virtual bool getBool() const { return getInt() != 0; } /// Interpret an int as a RGBA color virtual RGBAColor getColor() const; /// Return a double, using the default if it's missing - virtual double getDouble() const; + virtual double getDouble() const { return val ? val->asDouble() : 0; } /// Return a string, or empty if it's missing - virtual std::string getString() const; + virtual std::string getString() const { return val ? val->asString() : std::string(); } /// Return a dictionary as an entry - virtual DictionaryRef getDict() const; + virtual DictionaryRef getDict() const { return val ? val->asDict() : DictionaryRef(); } /// Return an array of refs virtual std::vector getArray() const; /// Compare to other virtual bool isEqual(const DictionaryEntryRef &other) const; + const MutableDictionary_Android::ValueRef &getValue() const { return val; } + protected: DictionaryType type; MutableDictionary_Android::ValueRef val; diff --git a/android/library/maply/WhirlyGlobeLib/include/FontTextureManager_Android.h b/android/library/maply/WhirlyGlobeLib/include/FontTextureManager_Android.h index b71f07d277..0f529febdd 100644 --- a/android/library/maply/WhirlyGlobeLib/include/FontTextureManager_Android.h +++ b/android/library/maply/WhirlyGlobeLib/include/FontTextureManager_Android.h @@ -1,9 +1,8 @@ -/* - * FontTextureManagerAndroid.h +/* FontTextureManagerAndroid.h * WhirlyGlobeLib * * Created by Steve Gifford on 6/2/14. - * Copyright 2011-2016 mousebird consulting + * Copyright 2011-2021 mousebird consulting * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -15,10 +14,8 @@ * 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. - * */ -#import #import "Maply_jni.h" #import "WhirlyGlobe.h" @@ -33,28 +30,34 @@ class LabelInfoAndroid; class FontTextureManager_Android : public FontTextureManager { public: - FontTextureManager_Android(JNIEnv *env,SceneRenderer *sceneRender,Scene *scene,jobject charRenderObj); + FontTextureManager_Android(PlatformThreadInfo *,SceneRenderer *sceneRender,Scene *scene,jobject charRenderObj); ~FontTextureManager_Android(); // Wrapper for FontManager. class FontManager_Android : public FontManager { public: - FontManager_Android(JNIEnv *env,jobject typefaceObj); + FontManager_Android(PlatformThreadInfo *inst,jobject typefaceObj); FontManager_Android(); - ~FontManager_Android(); + virtual ~FontManager_Android(); + + virtual bool operator <(const FontManager &that) const override + { + return false; // todo: this isn't really ok + } // Clear out global refs to Java objects we may be sitting on - void clearRefs(JNIEnv *env); + virtual void teardown(PlatformThreadInfo*) override; jobject typefaceObj; }; typedef std::shared_ptr FontManager_AndroidRef; - /// Add the given string. Caller is responsible for deleting - /// the DrawableString + /// Add the given string. Caller is responsible for deleting the DrawableString DrawableString *addString(PlatformThreadInfo *threadInfo,const std::vector &codePoints,const LabelInfoAndroid *,ChangeSet &changes); - + + virtual void teardown(PlatformThreadInfo*) override; + protected: // Find the appropriate font manager FontManager_AndroidRef findFontManagerForFont(PlatformInfo_Android *threadInfo,jobject typefaceObj,const LabelInfo &labelInfo); @@ -67,5 +70,6 @@ class FontTextureManager_Android : public FontTextureManager jmethodID renderMethodID; jfieldID bitmapID,sizeXID,sizeYID,glyphSizeXID,glyphSizeYID,offsetXID,offsetYID,textureOffsetXID,textureOffsetYID; }; +typedef std::shared_ptr FontTextureManager_AndroidRef; } diff --git a/android/library/maply/WhirlyGlobeLib/include/ImageTile_Android.h b/android/library/maply/WhirlyGlobeLib/include/ImageTile_Android.h index 1deea1c29d..2148523051 100644 --- a/android/library/maply/WhirlyGlobeLib/include/ImageTile_Android.h +++ b/android/library/maply/WhirlyGlobeLib/include/ImageTile_Android.h @@ -37,6 +37,7 @@ class ImageTile_Android : public ImageTile { public: ImageTile_Android(); + ImageTile_Android(const std::string &name,const RawDataRef &); virtual ~ImageTile_Android(); /// Scoop the contents out of a Bitmap diff --git a/android/library/maply/WhirlyGlobeLib/include/LabelInfo_Android.h b/android/library/maply/WhirlyGlobeLib/include/LabelInfo_Android.h index 2280d07b47..d831308b4a 100644 --- a/android/library/maply/WhirlyGlobeLib/include/LabelInfo_Android.h +++ b/android/library/maply/WhirlyGlobeLib/include/LabelInfo_Android.h @@ -35,7 +35,8 @@ class LabelInfoAndroid : public LabelInfo { public: LabelInfoAndroid(bool screenObject); - LabelInfoAndroid(const LabelInfoAndroid &that); + LabelInfoAndroid(LabelInfoAndroid &&that) noexcept; + ~LabelInfoAndroid(); // Clear any global refs we may be holding void clearRefs(PlatformInfo_Android *threadInfo); diff --git a/android/library/maply/WhirlyGlobeLib/include/MapboxVectorStyleSet_Android.h b/android/library/maply/WhirlyGlobeLib/include/MapboxVectorStyleSet_Android.h index ea4363adb6..018a60b075 100644 --- a/android/library/maply/WhirlyGlobeLib/include/MapboxVectorStyleSet_Android.h +++ b/android/library/maply/WhirlyGlobeLib/include/MapboxVectorStyleSet_Android.h @@ -53,13 +53,13 @@ class MapboxVectorStyleSetImpl_Android : public MapboxVectorStyleSetImpl virtual SingleLabelRef makeSingleLabel(PlatformThreadInfo *inst,const std::string &text) override; /// Create a local platform component object - virtual ComponentObjectRef makeComponentObject(PlatformThreadInfo *inst) override; + virtual ComponentObjectRef makeComponentObject(PlatformThreadInfo *inst, const Dictionary *desc = nullptr) override; /// Return the width of the given line of text - virtual double calculateTextWidth(PlatformThreadInfo *inInst,LabelInfoRef labelInfo,const std::string &testStr) override; + virtual double calculateTextWidth(PlatformThreadInfo *inInst,const LabelInfoRef &labelInfo,const std::string &testStr) override; /// Associate the given selection ID with a vector object - virtual void addSelectionObject(SimpleIdentity selectID,VectorObjectRef vecObj,ComponentObjectRef compObj) override; + virtual void addSelectionObject(SimpleIdentity selectID,const VectorObjectRef &vecObj,const ComponentObjectRef &compObj) override; /// Set up the Java side method references void setupMethods(JNIEnv *env); diff --git a/android/library/maply/WhirlyGlobeLib/include/QuadImageFrameLoader_Android.h b/android/library/maply/WhirlyGlobeLib/include/QuadImageFrameLoader_Android.h index 5d3461fd53..192d58832e 100644 --- a/android/library/maply/WhirlyGlobeLib/include/QuadImageFrameLoader_Android.h +++ b/android/library/maply/WhirlyGlobeLib/include/QuadImageFrameLoader_Android.h @@ -1,9 +1,8 @@ -/* - * QuadImageFrameLoader_Android.h +/* QuadImageFrameLoader_Android.h * WhirlyGlobeLib * * Created by Steve Gifford on 3/22/19. - * Copyright 2011-2019 mousebird consulting + * Copyright 2011-2021 mousebird consulting * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -15,7 +14,6 @@ * 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. - * */ #import @@ -40,7 +38,7 @@ class QIFBatchOps_Android : public QIFBatchOps jobject batchOpsObj; }; -// Android verison of the frame asset +// Android version of the frame asset class QIFFrameAsset_Android : public QIFFrameAsset { public: @@ -94,13 +92,13 @@ class QuadImageFrameLoader_Android : public QuadImageFrameLoader { public: // Displaying a single frame - QuadImageFrameLoader_Android(PlatformInfo_Android *threadInfo,const SamplingParams ¶ms,int numFrames,Mode mode,JNIEnv *env); - ~QuadImageFrameLoader_Android(); + QuadImageFrameLoader_Android(PlatformInfo_Android *threadInfo,const SamplingParams ¶ms,int numFrames,Mode mode); + virtual ~QuadImageFrameLoader_Android(); /// Number of frames we're representing virtual int getNumFrames() override; - // Contruct a platform specific BatchOps for passing to tile fetcher + // Construct a platform specific BatchOps for passing to tile fetcher // (we don't know about tile fetchers down here) virtual QIFBatchOps *makeBatchOps(PlatformThreadInfo *threadInfo) override; diff --git a/android/library/maply/WhirlyGlobeLib/include/SceneRenderer_Android.h b/android/library/maply/WhirlyGlobeLib/include/SceneRenderer_Android.h index a37e50c9c7..3520f7d982 100644 --- a/android/library/maply/WhirlyGlobeLib/include/SceneRenderer_Android.h +++ b/android/library/maply/WhirlyGlobeLib/include/SceneRenderer_Android.h @@ -28,6 +28,8 @@ class SceneRendererGLES_Android; // Snapshot base class for Android class Snapshot_Android { public: + virtual ~Snapshot_Android() = default; + // Return true if we want a snapshot this frame virtual bool needsSnapshot(TimeInterval val) { return true; } diff --git a/android/library/maply/WhirlyGlobeLib/include/ScopedEnv_Android.h b/android/library/maply/WhirlyGlobeLib/include/ScopedEnv_Android.h new file mode 100644 index 0000000000..0fcde57184 --- /dev/null +++ b/android/library/maply/WhirlyGlobeLib/include/ScopedEnv_Android.h @@ -0,0 +1,91 @@ +/* ScopedEnv_Android.h + * WhirlyGlobeLib + * + * Created by Tim Sylvester on 3/4/2021 + * Copyright 2021-2021 mousebird consulting + * + * 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. + */ + +#import + +namespace WhirlyKit +{ + +/** + * Attach the current thread to the JNI environment for the duration of the object's lifetime + */ +struct ScopedEnv +{ + ScopedEnv(JavaVM *jvm) + : jvm(jvm), env(nullptr), attached(false) + { + std::tie(env, attached) = GetJniEnv(jvm); + } + + ScopedEnv(const ScopedEnv&) = delete; + ScopedEnv& operator=(const ScopedEnv&) = delete; + + ScopedEnv(ScopedEnv&& other) : + jvm(other.jvm), env(other.env), attached(other.attached) + { + other.env = nullptr; + other.attached = false; + } + ScopedEnv& operator=(ScopedEnv&& other) + { + if (this != &other) + { + jvm = other.jvm; + env = other.env; + attached = other.attached; + other.attached = false; + } + return *this; + } + + virtual ~ScopedEnv() + { + if (attached) + { + attached = false; + env = nullptr; + jvm->DetachCurrentThread(); + } + } + + operator JNIEnv *() const { return env; } + JNIEnv* operator->() const { return env; } + + static std::pair GetJniEnv(JavaVM *vm) + { + // Check if the current thread is attached to the VM + JNIEnv* env = nullptr; + const auto result = vm->GetEnv((void**)&env, JNI_VERSION_1_6); + if (result == JNI_OK) + { + return std::make_pair(env,false); + } + if (result == JNI_EDETACHED && + vm->AttachCurrentThread(&env, nullptr) == JNI_OK) + { + return std::make_pair(env, true); + } + return std::make_pair(nullptr, false); + } +private: + bool attached; + JavaVM *jvm; + JNIEnv *env; +}; + +} diff --git a/android/library/maply/WhirlyGlobeLib/include/SingleLabel_Android.h b/android/library/maply/WhirlyGlobeLib/include/SingleLabel_Android.h index d06f1abf2e..9d7dbb0e32 100644 --- a/android/library/maply/WhirlyGlobeLib/include/SingleLabel_Android.h +++ b/android/library/maply/WhirlyGlobeLib/include/SingleLabel_Android.h @@ -1,9 +1,8 @@ -/* - * SingleLabelAndroid.h +/* SingleLabelAndroid.h * WhirlyGlobeLib * * Created by Steve Gifford on 6/2/14. - * Copyright 2011-2016 mousebird consulting + * Copyright 2011-2021 mousebird consulting * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -15,7 +14,6 @@ * 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. - * */ #import "WhirlyGlobe.h" @@ -30,7 +28,7 @@ namespace WhirlyKit class SingleLabelAndroid : public SingleLabel { public: - std::vector generateDrawableStrings(PlatformThreadInfo *threadInfo,const LabelInfo *inLabelInfo,FontTextureManager *fontTexManager,float &lineHeight,ChangeSet &changes); + std::vector generateDrawableStrings(PlatformThreadInfo *threadInfo,const LabelInfo *inLabelInfo,const FontTextureManagerRef &fontTexManager,float &lineHeight,ChangeSet &changes); // Sometimes rather than strings, we pass around the code points std::vector> codePointsLines; diff --git a/android/library/maply/WhirlyGlobeLib/include/VectorStyleSet_Android.h b/android/library/maply/WhirlyGlobeLib/include/VectorStyleSet_Android.h index ff1697d81e..01be1a90c7 100644 --- a/android/library/maply/WhirlyGlobeLib/include/VectorStyleSet_Android.h +++ b/android/library/maply/WhirlyGlobeLib/include/VectorStyleSet_Android.h @@ -47,7 +47,8 @@ class VectorStyleImpl_Android : public VectorStyleImpl { virtual bool geomAdditive(PlatformThreadInfo *inst) override; /// Construct objects related to this style based on the input data. - virtual void buildObjects(PlatformThreadInfo *inst, std::vector &vecObjs,VectorTileDataRef tileInfo) override; + virtual void buildObjects(PlatformThreadInfo *inst, const std::vector &vecObjs, + const VectorTileDataRef &tileInfo, const Dictionary *desc) override; protected: SimpleIdentity uuid; // ID of this style on the Java side @@ -62,10 +63,10 @@ class VectorStyleSetWrapper_Android : public VectorStyleDelegateImpl { VectorStyleSetWrapper_Android(PlatformThreadInfo *inst, jobject obj, - const std::vector uuids, - const std::vector categories, + const std::vector &uuids, + const std::vector &categories, const std::vector &geomAdditive); - virtual ~VectorStyleSetWrapper_Android() { } + virtual ~VectorStyleSetWrapper_Android(); /// Return the styles that apply to the given feature (attributes). virtual std::vector stylesForFeature(PlatformThreadInfo *inst, @@ -94,7 +95,8 @@ class VectorStyleSetWrapper_Android : public VectorStyleDelegateImpl { void shutdown(PlatformThreadInfo *inst); public: - void buildObjects(PlatformThreadInfo *inst,SimpleIdentity styleID,std::vector &vecObjs,VectorTileDataRef tileInfo); + void buildObjects(PlatformThreadInfo *inst,SimpleIdentity styleID,const std::vector &vecObjs, + const VectorTileDataRef &tileInfo,const Dictionary *desc); jobject wrapperObj; jmethodID layerShouldDisplayMethod; diff --git a/android/library/maply/WhirlyGlobeLib/src/ComponentManager_Android.cpp b/android/library/maply/WhirlyGlobeLib/src/ComponentManager_Android.cpp index a16b20f33e..90df929ccb 100644 --- a/android/library/maply/WhirlyGlobeLib/src/ComponentManager_Android.cpp +++ b/android/library/maply/WhirlyGlobeLib/src/ComponentManager_Android.cpp @@ -26,13 +26,14 @@ namespace WhirlyKit // The scene wants a component manager early in the process // This gives it an Android specific one -ComponentManager *MakeComponentManager() +ComponentManagerRef MakeComponentManager() { - return new ComponentManager_Android(); + return std::make_shared(); } -ComponentManager_Android::ComponentManager_Android() -: compManagerObj(NULL), objectsRemovedMethod(NULL) +ComponentManager_Android::ComponentManager_Android() : + compManagerObj(nullptr), + objectsRemovedMethod(nullptr) { } @@ -47,13 +48,16 @@ void ComponentManager_Android::clearJNI(JNIEnv *env) { if (compManagerObj) { env->DeleteGlobalRef(compManagerObj); - compManagerObj = NULL; + compManagerObj = nullptr; } - objectsRemovedMethod = NULL; + objectsRemovedMethod = nullptr; } ComponentManager_Android::~ComponentManager_Android() { + if (compManagerObj) { + wkLogLevel(Warn, "ComponentManager_Android not cleaned up"); + } } void ComponentManager_Android::removeComponentObjects(PlatformThreadInfo *inThreadInfo,const SimpleIDSet &compIDs,ChangeSet &changes) @@ -75,9 +79,9 @@ void ComponentManager_Android::removeComponentObjects(PlatformThreadInfo *inThre threadInfo->env->DeleteLocalRef(idsArray); } -ComponentObjectRef ComponentManager_Android::makeComponentObject() +ComponentObjectRef ComponentManager_Android::makeComponentObject(const Dictionary *desc) { - return ComponentObjectRef(new ComponentObject()); + return std::make_shared(); } } diff --git a/android/library/maply/WhirlyGlobeLib/src/Dictionary_Android.cpp b/android/library/maply/WhirlyGlobeLib/src/Dictionary_Android.cpp index 5af14681f9..b47f095f65 100644 --- a/android/library/maply/WhirlyGlobeLib/src/Dictionary_Android.cpp +++ b/android/library/maply/WhirlyGlobeLib/src/Dictionary_Android.cpp @@ -1,9 +1,8 @@ -/* - * Dictionary.cpp +/* Dictionary.cpp * WhirlyGlobeLib * * Created by Steve Gifford on 12/16/13. - * Copyright 2011-2013 mousebird consulting + * Copyright 2011-2021 mousebird consulting * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -15,18 +14,23 @@ * 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. - * */ #import #import "Dictionary_Android.h" +#import "WhirlyKitLog.h" +#import "DictionaryC.h" namespace WhirlyKit { - + +// from DictionaryC +extern RGBAColor ARGBtoRGBAColor(uint32_t v); +extern RGBAColor parseColor(const char* p, RGBAColor ret); + MutableDictionaryRef MutableDictionaryMake() { - return MutableDictionaryRef(new MutableDictionary_Android()); + return std::make_shared(); } template @@ -44,137 +48,173 @@ static T genericFromString(std::string const &string, T defValue) return (stream >> result) ? result : defValue; } -int MutableDictionary_Android::StringValue::asInt() -{ - return genericFromString(val, 0); -} - -int64_t MutableDictionary_Android::StringValue::asInt64() -{ - return genericFromString(val, 0); -} +int MutableDictionary_Android::StringValue::asInt() const { return genericFromString(val, 0); } +int64_t MutableDictionary_Android::StringValue::asInt64() const { return genericFromString(val, 0); } +SimpleIdentity MutableDictionary_Android::StringValue::asIdentity() const { return genericFromString(val, 0); } +double MutableDictionary_Android::StringValue::asDouble() const { return genericFromString(val, 0.0); } +void MutableDictionary_Android::IntValue::asString(std::string &retStr) const { retStr = genericToString(val); } +std::string MutableDictionary_Android::IntValue::asString() const { return genericToString(val); } +void MutableDictionary_Android::Int64Value::asString(std::string &retStr) const { retStr = genericToString(val); } +std::string MutableDictionary_Android::Int64Value::asString() const { return genericToString(val); } +void MutableDictionary_Android::DoubleValue::asString(std::string &retStr) const { retStr = genericToString(val); } +std::string MutableDictionary_Android::DoubleValue::asString() const { return genericToString(val); } +void MutableDictionary_Android::IdentityValue::asString(std::string &retStr) const { retStr = genericToString(val); } +std::string MutableDictionary_Android::IdentityValue::asString() const { return genericToString(val); } -SimpleIdentity MutableDictionary_Android::StringValue::asIdentity() +MutableDictionary_Android::ArrayValue::ArrayValue(const std::vector &entries) { - return genericFromString(val, 0); + for (const auto &entry : entries) + { + if (const auto valRef = makeValueRef(std::dynamic_pointer_cast(entry))) + { + val.push_back(valRef); + } + else + { + wkLogLevel(Warn, "Unsupported entry type"); + } + } } -double MutableDictionary_Android::StringValue::asDouble() +MutableDictionary_Android::ArrayValue::ArrayValue(const std::vector &entries) { - return genericFromString(val, 0.0); -} - -void MutableDictionary_Android::IntValue::asString(std::string &retStr) -{ - retStr = genericToString(val); + for (const auto &entry : entries) + { + if (const auto valRef = std::make_shared(std::dynamic_pointer_cast(entry))) + { + val.push_back(valRef); + } + else + { + wkLogLevel(Warn, "Unsupported entry type"); + } + } } -void MutableDictionary_Android::Int64Value::asString(std::string &retStr) +bool MutableDictionary_Android::IntValue::isEqual(const DictionaryEntry_Android& other) const { - retStr = genericToString(val); + return val == other.getInt(); } - -void MutableDictionary_Android::DoubleValue::asString(std::string &retStr) +bool MutableDictionary_Android::Int64Value::isEqual(const DictionaryEntry_Android& other) const { - retStr = genericToString(val); + return val == other.getInt64(); } - -void MutableDictionary_Android::IdentityValue::asString(std::string &retStr) +bool MutableDictionary_Android::IdentityValue::isEqual(const DictionaryEntry_Android& other) const { - retStr = genericToString(val); + return val == other.getIdentity(); } - -DictionaryRef MutableDictionary_Android::DictionaryValue::asDict() +bool MutableDictionary_Android::DoubleValue::isEqual(const DictionaryEntry_Android& other) const { - return val; + return val == other.getDouble(); } -MutableDictionary_Android::ArrayValue::ArrayValue(std::vector &entries) +bool MutableDictionary_Android::StringValue::isEqual(const DictionaryEntry& other) const { - for (auto entry: entries) { - ValueRef valRef = makeValueRef(std::dynamic_pointer_cast(entry)); - if (valRef) - val.push_back(valRef); + switch (other.getType()) + { + case DictTypeString: + if (const auto p = dynamic_cast(&other)) + { + return val == p->getStringRef(); + } + case DictTypeInt: + case DictTypeInt64: + case DictTypeIdentity: + return val == other.getString(); + case DictTypeDouble: + // todo: should really parse to double and compare with epsilon + return val == other.getString(); + case DictTypeObject: + case DictTypeDictionary: + case DictTypeArray: + default: + return false; } } - -MutableDictionary_Android::ArrayValue::ArrayValue(std::vector &entries) +bool MutableDictionary_Android::StringValue::isEqual(const DictionaryEntry_Android& other) const { - for (auto entry: entries) { - ValueRef valRef(new DictionaryValue(std::dynamic_pointer_cast(entry))); - if (valRef) - val.push_back(valRef); + switch (other.getType()) + { + case DictTypeString: + if (const auto p = dynamic_cast(other.getValue().get())) + { + return val == p->val; + } + case DictTypeInt: + case DictTypeInt64: + case DictTypeIdentity: + return val == other.getString(); + case DictTypeDouble: + // todo: should really parse to double and compare with epsilon + return val == other.getString(); + case DictTypeObject: + case DictTypeDictionary: + case DictTypeArray: + default: + return false; } } - MutableDictionaryRef MutableDictionary_Android::copy() const { - return MutableDictionaryRef(new MutableDictionary_Android(*this)); + return std::make_shared(*this); } - -MutableDictionary_Android::MutableDictionary_Android() + +MutableDictionary_Android::MutableDictionary_Android(const MutableDictionary_Android &that) { + for (const auto &kv : that.fields) + { + fields[kv.first] = kv.second->copy(); + } } - -MutableDictionary_Android::MutableDictionary_Android(const MutableDictionary_Android &that) + +MutableDictionary_Android::MutableDictionary_Android(MutableDictionary_Android &&that) noexcept + : fields(std::move(that.fields)) { - for (FieldMap::const_iterator it = that.fields.begin();it != that.fields.end();++it) - fields[it->first] = it->second->copy(); } MutableDictionary_Android::MutableDictionary_Android(const Dictionary &that) { - auto keys = that.getKeys(); - for (auto key: keys) + for (const auto &key : that.getKeys()) + { setEntry(key,that.getEntry(key)); -} - -MutableDictionary_Android::~MutableDictionary_Android() -{ - clear(); + } } -bool MutableDictionary_Android::parseJSON(const std::string jsonString) +bool MutableDictionary_Android::parseJSON(const std::string &jsonString) { - json_string json = jsonString; - - JSONNode topNode = libjson::parse(json); + JSONNode topNode = libjson::parse(jsonString); return parseJSONNode(topNode); } MutableDictionary_Android::ValueRef MutableDictionary_Android::parseJSONValue(JSONNode::iterator &nodeIt) { - switch (nodeIt->type()) { - case JSON_NULL: - break; - case JSON_STRING: - return ValueRef(new StringValue(nodeIt->as_string())); - break; - case JSON_NUMBER: - return ValueRef(new DoubleValue(nodeIt->as_float())); - break; - case JSON_BOOL: - return ValueRef(new IntValue(nodeIt->as_bool())); - break; + switch (nodeIt->type()) + { + case JSON_NULL: return ValueRef(); + case JSON_STRING: return std::make_shared(nodeIt->as_string()); + case JSON_NUMBER: return std::make_shared(nodeIt->as_float()); + case JSON_BOOL: return std::make_shared(nodeIt->as_bool()); case JSON_ARRAY: { auto nodes = nodeIt->as_array(); std::vector values; - for (JSONNode::iterator arrNodeIt = nodes.begin(); arrNodeIt != nodes.end(); ++arrNodeIt) { + values.reserve(nodes.size()); + for (auto arrNodeIt = nodes.begin(); arrNodeIt != nodes.end(); ++arrNodeIt) + { values.push_back(parseJSONValue(arrNodeIt)); } - return ArrayValueRef(new ArrayValue(values)); + return std::make_shared(values); } - break; case JSON_NODE: { - MutableDictionary_AndroidRef dict(new MutableDictionary_Android()); + auto dict = std::make_shared(); auto node = nodeIt->as_node(); dict->parseJSONNode(node); - return DictionaryValueRef(new DictionaryValue(dict)); + return std::make_shared(dict); } - break; + default: + wkLogLevel(Warn, "Unsupported type conversion from type %d to JSON", nodeIt->type()); } return ValueRef(); @@ -182,14 +222,15 @@ MutableDictionary_Android::ValueRef MutableDictionary_Android::parseJSONValue(JS bool MutableDictionary_Android::parseJSONNode(JSONNode &node) { - for (JSONNode::iterator nodeIt = node.begin(); nodeIt != node.end(); ++nodeIt) { - auto name = nodeIt->name(); - ValueRef val = parseJSONValue(nodeIt); + for (auto nodeIt = node.begin(); nodeIt != node.end(); ++nodeIt) { + const auto name = nodeIt->name(); + const auto val = parseJSONValue(nodeIt); if (name.empty() || !val) + { return false; + } fields[name] = val; } - return true; } @@ -198,15 +239,29 @@ void MutableDictionary_Android::clear() fields.clear(); } -MutableDictionary_Android &MutableDictionary_Android::operator = (const MutableDictionary_Android &that) +MutableDictionary_Android &MutableDictionary_Android::operator=(const MutableDictionary_Android &that) { - clear(); - for (FieldMap::const_iterator it = that.fields.begin();it != that.fields.end();++it) - fields[it->first] = it->second->copy(); - + if (this != &that) + { + clear(); + for (const auto &kv : that.fields) + { + fields[kv.first] = kv.second->copy(); + } + } return *this; } - + +MutableDictionary_Android &MutableDictionary_Android::operator=(MutableDictionary_Android &&that) noexcept +{ + if (this != &that) + { + clear(); + fields = std::move(that.fields); + } + return *this; +} + MutableDictionary_Android::MutableDictionary_Android(RawData *rawData) { RawDataReader dataRead(rawData); @@ -214,45 +269,65 @@ MutableDictionary_Android::MutableDictionary_Android(RawData *rawData) { int type; if (!dataRead.getInt(type)) + { + wkLogLevel(Warn, "Unable to parse dictionary: no type"); return; + } std::string attrName; if (!dataRead.getString(attrName)) + { + wkLogLevel(Warn, "Unable to parse: no attribute name"); return; + } switch (type) { case DictTypeString: { std::string sVal; if (!dataRead.getString(sVal)) + { + wkLogLevel(Warn, "Unable to parse: no string value"); return; - setString(attrName, sVal); - } + } + // N.B.: virtual functions don't work from constructors + this->MutableDictionary_Android::setString(attrName, sVal); break; + } case DictTypeInt: { int iVal; if (!dataRead.getInt(iVal)) + { + wkLogLevel(Warn, "Unable to parse: no int value"); return; - setInt(attrName, iVal); - } + } + this->MutableDictionary_Android::setInt(attrName, iVal); break; + } case DictTypeInt64: { int64_t iVal; if (!dataRead.getInt64(iVal)) + { + wkLogLevel(Warn, "Unable to parse: no int64 value"); return; - setInt64(attrName, iVal); - } + } + this->MutableDictionary_Android::setInt64(attrName, iVal); break; + } case DictTypeDouble: { double dVal; if (!dataRead.getDouble(dVal)) + { + wkLogLevel(Warn, "Unable to parse: no double value"); return; - setDouble(attrName, dVal); - } + } + this->MutableDictionary_Android::setDouble(attrName, dVal); break; + } default: + wkLogLevel(Warn, "Unrecognized dictionary type %d", type); return; } } @@ -260,13 +335,16 @@ MutableDictionary_Android::MutableDictionary_Android(RawData *rawData) void MutableDictionary_Android::asRawData(MutableRawData *rawData) { - for (FieldMap::iterator it = fields.begin(); it != fields.end(); ++it) + for (const auto &kv : fields) { - ValueRef val = it->second; + auto const &val = kv.second; if (val->type() == DictTypeObject) + { + wkLogLevel(Warn, "Unsupported entry type %d", val->type()); continue; + } rawData->addInt(val->type()); - rawData->addString(it->first); + rawData->addString(kv.first); switch (val->type()) { case DictTypeString: @@ -274,8 +352,8 @@ void MutableDictionary_Android::asRawData(MutableRawData *rawData) std::string str; val->asString(str); rawData->addString(str); - } break; + } case DictTypeInt: rawData->addInt(val->asInt()); break; @@ -286,8 +364,7 @@ void MutableDictionary_Android::asRawData(MutableRawData *rawData) rawData->addDouble(val->asDouble()); break; default: - throw 1; - break; + assert(!"Unsupported type"); } } } @@ -296,198 +373,161 @@ int MutableDictionary_Android::numFields() const { return (int)fields.size(); } - + bool MutableDictionary_Android::hasField(const std::string &name) const { - FieldMap::const_iterator it = fields.find(name); - return (it != fields.end()); + const auto it = fields.find(name); + return (it != fields.end()); } - + DictionaryType MutableDictionary_Android::getType(const std::string &name) const { - FieldMap::const_iterator it = fields.find(name); - if (it == fields.end()) - return DictTypeNone; - - return it->second->type(); + const auto it = fields.find(name); + return (it == fields.end()) ? DictTypeNone : it->second->type(); } - + void MutableDictionary_Android::removeField(const std::string &name) { - FieldMap::iterator it = fields.find(name); + const auto it = fields.find(name); if (it != fields.end()) + { fields.erase(it); + } } - + int MutableDictionary_Android::getInt(const std::string &name,int defVal) const { - FieldMap::const_iterator it = fields.find(name); - if (it == fields.end()) - return defVal; - - return it->second->asInt(); + const auto it = fields.find(name); + return (it == fields.end()) ? defVal : it->second->asInt(); } int64_t MutableDictionary_Android::getInt64(const std::string &name,int64_t defVal) const { - FieldMap::const_iterator it = fields.find(name); - if (it == fields.end()) - return defVal; - - return it->second->asInt(); + const auto it = fields.find(name); + return (it == fields.end()) ? defVal : it->second->asInt(); } SimpleIdentity MutableDictionary_Android::getIdentity(const std::string &name) const { - FieldMap::const_iterator it = fields.find(name); - if (it == fields.end()) - return EmptyIdentity; - - return it->second->asIdentity(); + const auto it = fields.find(name); + return (it == fields.end()) ? EmptyIdentity : it->second->asIdentity(); } - + bool MutableDictionary_Android::getBool(const std::string &name,bool defVal) const { - FieldMap::const_iterator it = fields.find(name); - if (it == fields.end()) - return defVal; - - return (bool)it->second->asInt(); + const auto it = fields.find(name); + return (it == fields.end()) ? defVal : (it->second->asInt() != 0); } RGBAColor MutableDictionary_Android::getColor(const std::string &name,const RGBAColor &defVal) const { - FieldMap::const_iterator it = fields.find(name); + const auto it = fields.find(name); if (it == fields.end()) + { return defVal; + } + + const Value &val = *it->second; - switch (it->second->type()) + switch (val.type()) { case DictTypeString: { std::string str; it->second->asString(str); - // We're looking for a #RRGGBBAA - if (str.length() < 1 || str[0] != '#') - return defVal; - - int iVal = atoi(&str.c_str()[1]); - RGBAColor ret; - ret.b = iVal & 0xFF; - ret.g = (iVal >> 8) & 0xFF; - ret.r = (iVal >> 16) & 0xFF; - ret.a = (iVal >> 24) & 0xFF; - return ret; - } - break; - case DictTypeInt: - { - int iVal = it->second->asInt(); - RGBAColor ret; - ret.b = iVal & 0xFF; - ret.g = (iVal >> 8) & 0xFF; - ret.r = (iVal >> 16) & 0xFF; - ret.a = (iVal >> 24) & 0xFF; - return ret; + return parseColor(str.c_str(), defVal); } - break; - // No idea what this means - case DictTypeInt64: - case DictTypeDouble: + case DictTypeInt: return ARGBtoRGBAColor(it->second->asInt()); default: + wkLogLevel(Warn, "Unhandled conversion from type %d to color", val.type()); return defVal; - break; } - - return defVal; } - + double MutableDictionary_Android::getDouble(const std::string &name,double defVal) const { - FieldMap::const_iterator it = fields.find(name); - if (it == fields.end()) - return defVal; - - return it->second->asDouble(); + const auto it = fields.find(name); + return (it == fields.end()) ? defVal : it->second->asDouble(); } std::string MutableDictionary_Android::getString(const std::string &name) const { - FieldMap::const_iterator it = fields.find(name); - if (it == fields.end()) - return ""; - - std::string retStr; - it->second->asString(retStr); - return retStr; + const auto it = fields.find(name); + return (it == fields.end()) ? std::string() : it->second->asString(); } std::string MutableDictionary_Android::getString(const std::string &name,const std::string &defVal) const { - FieldMap::const_iterator it = fields.find(name); - if (it == fields.end()) - return defVal; - - std::string retStr; - it->second->asString(retStr); - return retStr; + auto const it = fields.find(name); + return (it == fields.end()) ? defVal : it->second->asString(); } - + DelayedDeletableRef MutableDictionary_Android::getObject(const std::string &name) { - FieldMap::const_iterator it = fields.find(name); - if (it == fields.end()) - return DelayedDeletableRef(); - - return DelayedDeletableRef(it->second->asObject()); + const auto it = fields.find(name); + return (it == fields.end()) ? DelayedDeletableRef() : it->second->asObject(); } DictionaryRef MutableDictionary_Android::getDict(const std::string &name) const { - FieldMap::const_iterator it = fields.find(name); - if (it == fields.end()) - return DictionaryRef(); - - MutableDictionary_Android::DictionaryValueRef dictVal = std::dynamic_pointer_cast(it->second); - if (dictVal) - return dictVal->val; - + const auto it = fields.find(name); + if (it != fields.end()) + { + if (const auto dictVal = dynamic_cast(it->second.get())) + { + return dictVal->val; + } + else if (const auto val = dynamic_cast(it->second.get())) + { + wkLogLevel(Warn, "Unsupported entry type %d for entry '%s'", val->type(), name.c_str()); + } + else if (it->second) + { + wkLogLevel(Warn, "Unsupported entry type ? for entry '%s'", name.c_str()); + } + } return DictionaryRef(); } DictionaryEntryRef MutableDictionary_Android::getEntry(const std::string &name) const { - FieldMap::const_iterator it = fields.find(name); - if (it == fields.end()) - return DictionaryEntryRef(); - - return DictionaryEntryRef(new DictionaryEntry_Android(it->second)); + const auto it = fields.find(name); + return (it == fields.end()) ? DictionaryEntryRef() : std::make_shared(it->second); } std::vector MutableDictionary_Android::getArray(const std::string &name) const { - FieldMap::const_iterator it = fields.find(name); - if (it == fields.end()) - return std::vector(); - - if (it->second->type() == DictTypeArray) { - ArrayValueRef val = std::dynamic_pointer_cast(it->second); - if (val) { - std::vector ret; - for (auto entry: val->val) - ret.push_back(DictionaryEntryRef(new DictionaryEntry_Android(entry))); - return ret; + const auto it = fields.find(name); + if (it != fields.end()) + { + const auto gval = it->second.get(); + if (gval && gval->type() == DictTypeArray) + { + if (const auto val = dynamic_cast(gval)) + { + std::vector ret; + ret.reserve(val->val.size()); + for (const auto &entry : val->val) + { + ret.push_back(std::make_shared(entry)); + } + return ret; + } + wkLogLevel(Warn,"Unsupported conversion to array"); } + wkLogLevel(Warn,"Unsupported conversion from type %d to array", gval->type()); } - return std::vector(); } std::vector MutableDictionary_Android::getKeys() const { std::vector keys; - for (auto it: fields) + keys.reserve(fields.size()); + for (const auto &it : fields) + { keys.push_back(it.first); + } return keys; } @@ -495,289 +535,196 @@ std::vector MutableDictionary_Android::getKeys() const void MutableDictionary_Android::setInt(const std::string &name,int val) { removeField(name); - - IntValue *iVal = new IntValue(); - iVal->val = val; - fields[name] = ValueRef(iVal); + fields[name] = std::make_shared(val); } void MutableDictionary_Android::setInt64(const std::string &name,int64_t val) { removeField(name); - - Int64Value *iVal = new Int64Value(); - iVal->val = val; - fields[name] = ValueRef(iVal); + fields[name] = std::make_shared(val); } void MutableDictionary_Android::setIdentifiable(const std::string &name,SimpleIdentity val) { removeField(name); - - IdentityValue *iVal = new IdentityValue(); - iVal->val = val; - fields[name] = ValueRef(iVal); + fields[name] = std::make_shared(val); } void MutableDictionary_Android::setDouble(const std::string &name,double val) { removeField(name); - - DoubleValue *dVal = new DoubleValue(); - dVal->val = val; - fields[name] = ValueRef(dVal); + fields[name] = std::make_shared(val); } void MutableDictionary_Android::setString(const std::string &name,const std::string &val) { removeField(name); - - StringValue *sVal = new StringValue(); - sVal->val = val; - fields[name] = ValueRef(sVal); + fields[name] = std::make_shared(val); } -void MutableDictionary_Android::setDict(const std::string &name,MutableDictionary_AndroidRef dict) +void MutableDictionary_Android::setDict(const std::string &name,const MutableDictionary_AndroidRef &dict) { removeField(name); - - DictionaryValue *dVal = new DictionaryValue(); - dVal->val = dict; - fields[name] = ValueRef(dVal); + fields[name] = std::make_shared(dict); } -MutableDictionary_Android::ValueRef MutableDictionary_Android::makeValueRef(DictionaryEntry_AndroidRef entry) +MutableDictionary_Android::ValueRef MutableDictionary_Android::makeValueRef(const DictionaryEntry_AndroidRef &entry) { - Value *value = NULL; - - switch(entry->getType()) { - case DictTypeNone: - default: + switch(entry->getType()) + { case DictTypeArray: { + const auto arr = entry->getArray(); std::vector entries; - for (auto thisEntry: entry->getArray()) { + entries.reserve(arr.size()); + for (const auto &thisEntry : arr) + { entries.push_back(thisEntry); } - value = new ArrayValue(entries); + return std::make_shared(std::move(entries)); } - break; case DictTypeDictionary: - value = new DictionaryValue(std::dynamic_pointer_cast(entry->getDict())); - break; - case DictTypeIdentity: - value = new IdentityValue(entry->getIdentity()); - break; - case DictTypeInt: - value = new IntValue(entry->getInt()); - break; - case DictTypeInt64: - value = new Int64Value(entry->getInt64()); - break; - case DictTypeDouble: - value = new DoubleValue(entry->getDouble()); - break; - case DictTypeString: - value = new StringValue(entry->getString()); - break; + { + if (const auto e = std::dynamic_pointer_cast(entry->getDict())) + { + return std::make_shared(e); + } + wkLogLevel(Warn, "Unsupported dictionary conversion"); + return ValueRef(); + } + case DictTypeIdentity: return std::make_shared(entry->getIdentity()); + case DictTypeInt: return std::make_shared(entry->getInt()); + case DictTypeInt64: return std::make_shared(entry->getInt64()); + case DictTypeDouble: return std::make_shared(entry->getDouble()); + case DictTypeString: return std::make_shared(entry->getString()); + default: + wkLogLevel(Warn, "Unsupported conversion from type %d", entry->getType()); + return ValueRef(); } - - return ValueRef(value); } -MutableDictionary_Android::ValueRef MutableDictionary_Android::makeValueRef(DictionaryEntryRef entry) +MutableDictionary_Android::ValueRef MutableDictionary_Android::makeValueRef(const DictionaryEntryRef &entry) { - Value *value = NULL; - - switch(entry->getType()) { - case DictTypeNone: - default: - case DictTypeArray: { + switch(entry->getType()) + { + case DictTypeArray: + { + const auto arr = entry->getArray(); std::vector entries; - for (auto thisEntry: entry->getArray()) { + entries.reserve(arr.size()); + for (const auto &thisEntry : arr) { entries.push_back(thisEntry); } - value = new ArrayValue(entries); + return std::make_shared(std::move(entries)); } - break; case DictTypeDictionary: - value = new DictionaryValue(std::dynamic_pointer_cast(entry->getDict())); - break; - case DictTypeIdentity: - value = new IdentityValue(entry->getIdentity()); - break; - case DictTypeInt: - value = new IntValue(entry->getInt()); - break; - case DictTypeInt64: - value = new Int64Value(entry->getIdentity()); - break; - case DictTypeDouble: - value = new DoubleValue(entry->getDouble()); - break; - case DictTypeString: - value = new StringValue(entry->getString()); - break; + { + if (const auto e = std::dynamic_pointer_cast(entry->getDict())) + { + return std::make_shared(e); + } + wkLogLevel(Warn, "Unsupported dictionary conversion"); + return ValueRef(); + } + case DictTypeIdentity: return std::make_shared(entry->getIdentity()); + case DictTypeInt: return std::make_shared(entry->getInt()); + case DictTypeInt64: return std::make_shared(entry->getIdentity()); + case DictTypeDouble: return std::make_shared(entry->getDouble()); + case DictTypeString: return std::make_shared(entry->getString()); + default: + wkLogLevel(Warn, "Unsupported conversion from type %d", entry->getType()); + return ValueRef(); } - - return ValueRef(value); } -void MutableDictionary_Android::setEntry(const std::string &name,DictionaryEntry_AndroidRef entry) +void MutableDictionary_Android::setEntry(const std::string &name,const DictionaryEntryRef &entry) { removeField(name); - fields[name] = makeValueRef(entry); } -void MutableDictionary_Android::setEntry(const std::string &name,DictionaryEntryRef entry) +void MutableDictionary_Android::setArray(const std::string &name,const std::vector &entries) { removeField(name); - - fields[name] = makeValueRef(entry); + fields[name] = std::make_shared(entries); } -void MutableDictionary_Android::setArray(const std::string &name,std::vector &entries) +void MutableDictionary_Android::setArray(const std::string &name,const std::vector &entries) { removeField(name); - - ArrayValue *aVal = new ArrayValue(entries); - fields[name] = ValueRef(aVal); + fields[name] = std::make_shared(entries); } -void MutableDictionary_Android::setArray(const std::string &name,std::vector &entries) +void MutableDictionary_Android::setObject(const std::string &name, const DelayedDeletableRef &obj) { removeField(name); - - ArrayValue *aVal = new ArrayValue(entries); - fields[name] = ValueRef(aVal); + fields[name] = std::make_shared(obj); } - -void MutableDictionary_Android::setObject(const std::string &name, DelayedDeletableRef obj) -{ - removeField(name); - - ObjectValue *oVal = new ObjectValue(); - oVal->val = obj; - fields[name] = ValueRef(oVal); -} - + std::string MutableDictionary_Android::toString() const { std::string str; - for (const auto it : fields) + str.reserve(numFields() * 10); + for (const auto &it : fields) { - std::string valStr; - it.second->asString(valStr); - str += it.first + ":" + valStr + "\n"; + const auto valStr = it.second->asString(); + str.reserve(str.length() + it.first.length() + valStr.length() + 2); + str.append(it.first); + str.append(":"); + str.append(valStr); + str.append("\n"); } - return str; } void MutableDictionary_Android::addEntries(const Dictionary *inOther) { - const MutableDictionary_Android *other = dynamic_cast(inOther); - - for (FieldMap::const_iterator it = other->fields.begin();it != other->fields.end();++it) - fields[it->first] = it->second->copy(); - -} - -DictionaryType DictionaryEntry_Android::getType() const -{ - return type; -} - -int DictionaryEntry_Android::getInt() const -{ - return val->asInt(); -} - -int64_t DictionaryEntry_Android::getInt64() const -{ - return val->asInt64(); -} - -SimpleIdentity DictionaryEntry_Android::getIdentity() const -{ - return val->asIdentity(); -} - -bool DictionaryEntry_Android::getBool() const -{ - return val->asInt() != 0; + if (const auto other = dynamic_cast(inOther)) + { + for (const auto &kv : other->fields) + { + fields[kv.first] = kv.second->copy(); + } + } + else + { + wkLogLevel(Warn, "Unsupported dictionary type"); + } } RGBAColor DictionaryEntry_Android::getColor() const { switch (type) { - case DictTypeString: - { - std::string str; - val->asString(str); - // We're looking for a #RRGGBBAA - if (str.length() < 1 || str[0] != '#') - return RGBAColor::white(); - - int iVal = atoi(&str.c_str()[1]); - RGBAColor ret; - ret.b = iVal & 0xFF; - ret.g = (iVal >> 8) & 0xFF; - ret.r = (iVal >> 16) & 0xFF; - ret.a = (iVal >> 24) & 0xFF; - return ret; - } - break; - case DictTypeInt: - { - int iVal = val->asInt(); - RGBAColor ret; - ret.b = iVal & 0xFF; - ret.g = (iVal >> 8) & 0xFF; - ret.r = (iVal >> 16) & 0xFF; - ret.a = (iVal >> 24) & 0xFF; - return ret; - } - break; - // No idea what this mean - case DictTypeInt64: - case DictTypeDouble: + case DictTypeString: return parseColor(val->asString().c_str(), RGBAColor::white()); + case DictTypeInt: return ARGBtoRGBAColor(val->asInt()); default: + wkLogLevel(Warn, "Unsupported conversion from type %d to color", type); return RGBAColor::white(); - break; } } -double DictionaryEntry_Android::getDouble() const -{ - return val->asDouble(); -} - -std::string DictionaryEntry_Android::getString() const -{ - std::string str; - val->asString(str); - - return str; -} - -DictionaryRef DictionaryEntry_Android::getDict() const -{ - return val->asDict(); -} - std::vector DictionaryEntry_Android::getArray() const { - if (type != DictTypeArray) - return std::vector(); - - MutableDictionary_Android::ArrayValueRef theVal = std::dynamic_pointer_cast(val); - if (theVal) { - std::vector ret; - for (auto entry: theVal->val) - ret.push_back(DictionaryEntryRef(new DictionaryEntry_Android(entry))); - return ret; + if (type == DictTypeArray) + { + if (const auto theVal = dynamic_cast(val.get())) + { + std::vector ret; + ret.reserve(theVal->val.size()); + for (auto entry: theVal->val) + { + ret.push_back(std::make_shared(entry)); + } + return ret; + } + else if (val) + { + wkLogLevel(Warn, "Unsupported conversion to array"); + } + } + else + { + wkLogLevel(Warn, "Unsupported conversion from type %d to array", type); } return std::vector(); @@ -785,38 +732,19 @@ std::vector DictionaryEntry_Android::getArray() const bool DictionaryEntry_Android::isEqual(const DictionaryEntryRef &inOther) const { - DictionaryEntry_AndroidRef other = std::dynamic_pointer_cast(inOther); - if (!other) - return false; - - if (type != other->getType()) - return false; - - switch (type) { - case DictTypeString: - return getString() == other->getString(); - break; - case DictTypeInt: - return val->asInt() == other->getInt(); - break; - case DictTypeInt64: - return val->asInt64() == other->getInt64(); - break; - case DictTypeIdentity: - return val->asIdentity() == other->getIdentity(); - break; - case DictTypeDouble: - return val->asDouble() == other->getDouble(); - break; - case DictTypeDictionary: - return false; - break; - case DictTypeNone: - case DictTypeObject: - case DictTypeArray: - return false; - break; + if (const auto other = dynamic_cast(inOther.get())) + { + return val->isEqual(other->val); + } + else if (const auto other = dynamic_cast(inOther.get())) + { + return val->isEqual(*other); + } + else if (inOther) + { + wkLogLevel(Warn, "Unsupported dictionary entry comparison"); } + return false; } } diff --git a/android/library/maply/WhirlyGlobeLib/src/FontTextureManager_Android.cpp b/android/library/maply/WhirlyGlobeLib/src/FontTextureManager_Android.cpp index 85e32efdf8..5572c884ff 100644 --- a/android/library/maply/WhirlyGlobeLib/src/FontTextureManager_Android.cpp +++ b/android/library/maply/WhirlyGlobeLib/src/FontTextureManager_Android.cpp @@ -1,9 +1,8 @@ -/* - * FontTextureManagerAndroid.cpp +/* FontTextureManagerAndroid.cpp * WhirlyGlobeLib * * Created by Steve Gifford on 6/2/14. - * Copyright 2011-2016 mousebird consulting + * Copyright 2011-2021 mousebird consulting * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -15,71 +14,114 @@ * 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. - * */ #import "FontTextureManager_Android.h" #import "LabelInfo_Android.h" #import "LabelsAndMarkers_jni.h" +#import "ScopedEnv_Android.h" +#import "Exceptions_jni.h" #import namespace WhirlyKit { -static const float BogusFontScale = 1.0; +static const float BogusFontScale = 1.0f; -FontTextureManager_Android::FontManager_Android::FontManager_Android(JNIEnv *env,jobject inTypefaceObj) +FontTextureManager_Android::FontManager_Android::FontManager_Android(PlatformThreadInfo *inst,jobject inTypefaceObj) : + typefaceObj(((PlatformInfo_Android*)inst)->env->NewGlobalRef(inTypefaceObj)) { - typefaceObj = env->NewGlobalRef(inTypefaceObj); } -FontTextureManager_Android::FontManager_Android::FontManager_Android() -: typefaceObj(NULL) +FontTextureManager_Android::FontManager_Android::FontManager_Android() : + typefaceObj(nullptr) { } -void FontTextureManager_Android::FontManager_Android::clearRefs(JNIEnv *savedEnv) +FontTextureManager_Android::FontManager_Android::~FontManager_Android() { - if (typefaceObj) - { - savedEnv->DeleteGlobalRef(typefaceObj); - typefaceObj = NULL; + // should have been cleaned up by now through teardown. + // We can't clean it up for lack of a JNIEnv, so it'll leak. + if (typefaceObj) { + wkLogLevel(Warn, "FontManager_Android not cleaned up"); } } -FontTextureManager_Android::FontManager_Android::~FontManager_Android() +void FontTextureManager_Android::FontManager_Android::teardown(PlatformThreadInfo* inst) { + if (typefaceObj) + { + ((PlatformInfo_Android*)inst)->env->DeleteGlobalRef(typefaceObj); + typefaceObj = nullptr; + } } -FontTextureManager_Android::FontTextureManager_Android(JNIEnv *env,SceneRenderer *sceneRender,Scene *scene,jobject inCharRenderObj) - : FontTextureManager(sceneRender,scene), charRenderObj(NULL) +FontTextureManager_Android::FontTextureManager_Android(PlatformThreadInfo *inst,SceneRenderer *sceneRender,Scene *scene,jobject inCharRenderObj) : + FontTextureManager(sceneRender,scene) { - // TODO: Porting. This will leak + const auto env = ((PlatformInfo_Android*)inst)->env; + charRenderObj = env->NewGlobalRef(inCharRenderObj); - jclass charRenderClass = env->GetObjectClass(charRenderObj); - renderMethodID = env->GetMethodID(charRenderClass, "renderChar", "(ILcom/mousebird/maply/LabelInfo;F)Lcom/mousebird/maply/CharRenderer$Glyph;"); - jclass glyphClass = env->FindClass("com/mousebird/maply/CharRenderer$Glyph"); - bitmapID = env->GetFieldID(glyphClass,"bitmap","Landroid/graphics/Bitmap;"); - sizeXID = env->GetFieldID(glyphClass,"sizeX","F"); - sizeYID = env->GetFieldID(glyphClass,"sizeY","F"); - glyphSizeXID = env->GetFieldID(glyphClass,"glyphSizeX","F"); - glyphSizeYID = env->GetFieldID(glyphClass,"glyphSizeY","F"); - offsetXID = env->GetFieldID(glyphClass,"offsetX","F"); - offsetYID = env->GetFieldID(glyphClass,"offsetY","F"); - textureOffsetXID = env->GetFieldID(glyphClass,"textureOffsetX","F"); - textureOffsetYID = env->GetFieldID(glyphClass,"textureOffsetY","F"); - env->DeleteLocalRef(glyphClass); - env->DeleteLocalRef(charRenderClass); + + if (const jclass charRenderClass = env->GetObjectClass(charRenderObj)) + { + renderMethodID = env->GetMethodID(charRenderClass, "renderChar", + "(ILcom/mousebird/maply/LabelInfo;F)Lcom/mousebird/maply/CharRenderer$Glyph;"); + env->DeleteLocalRef(charRenderClass); + } + + if (const jclass glyphClass = env->FindClass("com/mousebird/maply/CharRenderer$Glyph")) + { + bitmapID = env->GetFieldID(glyphClass, "bitmap", "Landroid/graphics/Bitmap;"); + sizeXID = env->GetFieldID(glyphClass, "sizeX", "F"); + sizeYID = env->GetFieldID(glyphClass, "sizeY", "F"); + glyphSizeXID = env->GetFieldID(glyphClass, "glyphSizeX", "F"); + glyphSizeYID = env->GetFieldID(glyphClass, "glyphSizeY", "F"); + offsetXID = env->GetFieldID(glyphClass, "offsetX", "F"); + offsetYID = env->GetFieldID(glyphClass, "offsetY", "F"); + textureOffsetXID = env->GetFieldID(glyphClass, "textureOffsetX", "F"); + textureOffsetYID = env->GetFieldID(glyphClass, "textureOffsetY", "F"); + env->DeleteLocalRef(glyphClass); + } } FontTextureManager_Android::~FontTextureManager_Android() { + if (charRenderObj) { + wkLogLevel(Warn, "FontTextureManager_Android not cleaned up"); + } } -DrawableString *FontTextureManager_Android::addString(PlatformThreadInfo *inThreadInfo,const std::vector &codePoints,const LabelInfoAndroid *labelInfo,ChangeSet &changes) +void FontTextureManager_Android::teardown(PlatformThreadInfo* threadInfo) { - LabelInfoClassInfo *classInfo = LabelInfoClassInfo::getClassInfo(); - PlatformInfo_Android *threadInfo = (PlatformInfo_Android *)inThreadInfo; + const auto env = ((PlatformInfo_Android*)threadInfo)->env; + + for (const auto &kv : fontManagers) + { + if (const auto afm = dynamic_cast(kv.second.get())) + { + afm->teardown(threadInfo); + } +#if DEBUG + else wkLogLevel(Warn,"Unexpected type skipped in teardown"); +#endif + } + fontManagers.clear(); + + if (charRenderObj) + { + env->DeleteGlobalRef(charRenderObj); + charRenderObj = nullptr; + } +} + +DrawableString *FontTextureManager_Android::addString( + PlatformThreadInfo *inThreadInfo, + const std::vector &codePoints, + const LabelInfoAndroid *labelInfo, + ChangeSet &changes) +{ + auto threadInfo = (PlatformInfo_Android *)inThreadInfo; // Could be more granular if this slows things down std::lock_guard guardLock(lock); @@ -87,36 +129,44 @@ DrawableString *FontTextureManager_Android::addString(PlatformThreadInfo *inThre // If not initialized, set up texture atlas and such init(); - DrawableString *drawString = new DrawableString(); - DrawStringRep *drawStringRep = new DrawStringRep(drawString->getId()); + auto drawString = new DrawableString(); + auto drawStringRep = new DrawStringRep(drawString->getId()); // Look for the font manager that manages the typeface/attribute combo we need - FontManager_AndroidRef fm = findFontManagerForFont(threadInfo,labelInfo->typefaceObj,*labelInfo); - - JavaIntegerClassInfo *intClassInfo = JavaIntegerClassInfo::getClassInfo(threadInfo->env); + auto fm = findFontManagerForFont(threadInfo,labelInfo->typefaceObj,*labelInfo); // Work through the characters GlyphSet glyphsUsed; float offsetX = 0.0; - for (int glyph : codePoints) + for (const int glyph : codePoints) { // Look for an existing glyph - FontManager::GlyphInfo *glyphInfo = fm->findGlyph(glyph); + auto glyphInfo = fm->findGlyph(glyph); if (!glyphInfo) { // Call the renderer - jobject glyphObj = threadInfo->env->CallObjectMethod(charRenderObj,renderMethodID,glyph,labelInfo->labelInfoObj,labelInfo->fontSize); - if (!glyphObj) { - wkLogLevel(Warn,"Bad glyph passed into FontTextureManager_Android: %d",glyph); + const jobject glyphObj = threadInfo->env->CallObjectMethod(charRenderObj,renderMethodID,glyph,labelInfo->labelInfoObj,labelInfo->fontSize); + if (!glyphObj) + { + wkLogLevel(Warn,"Glyph render failed from FontTextureManager_Android: %d",glyph); + logAndClearJVMException(threadInfo->env, "addString"); continue; } + jobject bitmapObj = threadInfo->env->GetObjectField(glyphObj,bitmapID); + if (!bitmapObj) + { + wkLogLevel(Error, "Glyph render produced no output"); + logAndClearJVMException(threadInfo->env, "addString"); + continue; + } try { // Got a bitmap, so merge that in with our texture atlas AndroidBitmapInfo info; - if (bitmapObj && (AndroidBitmap_getInfo(threadInfo->env, bitmapObj, &info) >= 0)) + const auto getInfoRes = AndroidBitmap_getInfo(threadInfo->env, bitmapObj, &info); + if (getInfoRes == ANDROID_BITMAP_RESULT_SUCCESS) { Point2f texSize,glyphSize; Point2f offset,textureOffset; @@ -132,27 +182,56 @@ DrawableString *FontTextureManager_Android::addString(PlatformThreadInfo *inThre textureOffset.y() = threadInfo->env->GetFloatField(glyphObj,textureOffsetYID); // Create a texture - void* bitmapPixels; - if (AndroidBitmap_lockPixels(threadInfo->env, bitmapObj, &bitmapPixels) < 0) - throw 1; - MutableRawData *rawData = new MutableRawData(bitmapPixels,info.height*info.width*4); - TextureGLES tex("FontTextureManager"); - tex.setRawData(rawData,info.width,info.height); - - // Add it to the texture atlas - SubTexture subTex; - Point2f realSize(glyphSize.x()+2*textureOffset.x(),glyphSize.y()+2*textureOffset.y()); - std::vector texs; - texs.push_back(&tex); - if (texAtlas->addTexture(sceneRender, texs, -1, &realSize, NULL, subTex, changes, 0, 0, NULL)) - glyphInfo = fm->addGlyph(glyph, subTex, Point2f(glyphSize.x(),glyphSize.y()), Point2f(offset.x(),offset.y()), Point2f(textureOffset.x(),textureOffset.y())); - + void* bitmapPixels = nullptr; + const auto lockRes = AndroidBitmap_lockPixels(threadInfo->env, bitmapObj, &bitmapPixels); + if (lockRes != ANDROID_BITMAP_RESULT_SUCCESS) + { + throw std::runtime_error("Unable to lock bitmap pixels"); + } + try + { + assert(info.width * 4 == info.stride); + + MutableRawData *rawData = new MutableRawData(bitmapPixels, + info.height * info.width * 4); + TextureGLES tex("FontTextureManager"); + tex.setRawData(rawData, info.width, info.height); + + // Add it to the texture atlas + SubTexture subTex; + const Point2f realSize(glyphSize.x() + 2 * textureOffset.x(), + glyphSize.y() + 2 * textureOffset.y()); + std::vector texs{&tex}; + if (texAtlas->addTexture(sceneRender, texs, -1, &realSize, nullptr, subTex, + changes, 0, 0, nullptr)) + { + glyphInfo = fm->addGlyph(glyph, subTex, + Point2f(glyphSize.x(), glyphSize.y()), + Point2f(offset.x(), offset.y()), + Point2f(textureOffset.x(), textureOffset.y())); + } + else + { + wkLogLevel(Error, "Failed to add glyph texture for %d/%c in %s", glyph, glyph, fm->fontName.c_str()); + } + } + catch (...) + { + // finally... + AndroidBitmap_unlockPixels(threadInfo->env, bitmapObj); + throw; + } AndroidBitmap_unlockPixels(threadInfo->env, bitmapObj); } + else + { + wkLogLevel(Error, "Glyph AndroidBitmap_getInfo failed (%d)", getInfoRes); + } } catch (...) { // Just don't add the glyph, for now + wkLogLevel(Error, "Exception in addString %d/%c/%s", glyph, glyph, fm->fontName.c_str()); } threadInfo->env->DeleteLocalRef(glyphObj); @@ -162,9 +241,8 @@ DrawableString *FontTextureManager_Android::addString(PlatformThreadInfo *inThre { // Now we make a rectangle that covers the glyph in its texture atlas DrawableString::Rect rect; - Point2f offset(offsetX,-glyphInfo->offset.y()); - - float scale = 1.0/BogusFontScale; + const Point2f offset(offsetX,-glyphInfo->offset.y()); + const float scale = 1.0/BogusFontScale; // Note: was -1,-1 rect.pts[0] = Point2f(glyphInfo->offset.x()*scale-glyphInfo->textureOffset.x()*scale,glyphInfo->offset.y()*scale-glyphInfo->textureOffset.y()*scale)+offset; @@ -207,19 +285,21 @@ FontTextureManager_Android::FontManager_AndroidRef FontTextureManager_Android::f for (auto it : fontManagers) { - FontManager_AndroidRef fm = std::dynamic_pointer_cast(it.second); - - if (labelInfo.typefaceIsSame(threadInfo,fm->typefaceObj) && - fm->pointSize == labelInfo.fontSize && + if (const auto fm = std::dynamic_pointer_cast(it.second)) + { + if (fm->pointSize == labelInfo.fontSize && fm->color == labelInfo.textColor && fm->outlineColor == labelInfo.outlineColor && - fm->outlineSize == labelInfo.outlineSize) - return fm; + fm->outlineSize == labelInfo.outlineSize && + labelInfo.typefaceIsSame(threadInfo, fm->typefaceObj)) + { + return fm; + } + } } // Didn't find it, so create it - FontManager_AndroidRef fm(new FontManager_Android(threadInfo->env,typefaceObj)); - fm->fontName = ""; + const auto fm = std::make_shared(threadInfo,typefaceObj); fm->color = labelInfo.textColor; fm->pointSize = labelInfo.fontSize; fm->outlineColor = labelInfo.outlineColor; diff --git a/android/library/maply/WhirlyGlobeLib/src/ImageTile_Android.cpp b/android/library/maply/WhirlyGlobeLib/src/ImageTile_Android.cpp index 0f7150c054..94d0513748 100644 --- a/android/library/maply/WhirlyGlobeLib/src/ImageTile_Android.cpp +++ b/android/library/maply/WhirlyGlobeLib/src/ImageTile_Android.cpp @@ -29,6 +29,11 @@ ImageTile_Android::ImageTile_Android() { } +ImageTile_Android::ImageTile_Android(const std::string &name,const RawDataRef &rawData) +: ImageTile(name), rawData(rawData), tex(NULL), type(MaplyImgTypeRawImage) +{ +} + ImageTile_Android::~ImageTile_Android() { if (tex) @@ -62,8 +67,8 @@ void ImageTile_Android::setBitmap(JNIEnv *env,jobject bitmapObj) if (info.height > 0 && info.width > 0) { - uint32_t* src = (uint32_t*) bitmapPixels; - rawData = RawDataRef(new MutableRawData(bitmapPixels,info.height*info.width*4)); + //uint32_t* src = (uint32_t*) bitmapPixels; + rawData = std::make_shared(bitmapPixels,info.height*info.width*4); } type = MaplyImgTypeRawImage; @@ -81,7 +86,7 @@ Texture *ImageTile_Android::buildTexture() return tex; if (type == MaplyImgTypeNone) - return NULL; + return nullptr; int destWidth = targetWidth; int destHeight = targetHeight; diff --git a/android/library/maply/WhirlyGlobeLib/src/LabelInfo_Android.cpp b/android/library/maply/WhirlyGlobeLib/src/LabelInfo_Android.cpp index 1ab249164d..43afcc4c0a 100644 --- a/android/library/maply/WhirlyGlobeLib/src/LabelInfo_Android.cpp +++ b/android/library/maply/WhirlyGlobeLib/src/LabelInfo_Android.cpp @@ -23,14 +23,29 @@ namespace WhirlyKit { -LabelInfoAndroid::LabelInfoAndroid(bool screenObject) -: LabelInfo(screenObject), typefaceObj(NULL), labelInfoObj(NULL) +LabelInfoAndroid::LabelInfoAndroid(bool screenObject) : + LabelInfo(screenObject), + typefaceObj(nullptr), + labelInfoObj(nullptr) { } -LabelInfoAndroid::LabelInfoAndroid(const LabelInfoAndroid &that) -: LabelInfo(that), typefaceObj(that.typefaceObj), fontSize(that.fontSize), labelInfoObj(that.labelInfoObj) +LabelInfoAndroid::LabelInfoAndroid(LabelInfoAndroid &&that) noexcept : + LabelInfo(that), + typefaceObj(that.typefaceObj), + fontSize(that.fontSize), + labelInfoObj(that.labelInfoObj) { + // ensure that the reference isn't released twice + that.typefaceObj = nullptr; +} + +LabelInfoAndroid::~LabelInfoAndroid() +{ + // should have been cleaned up through clearRefs() + if (labelInfoObj) { + wkLogLevel(Warn, "LabelInfoAndroid not cleaned up"); + } } void LabelInfoAndroid::clearRefs(PlatformInfo_Android *threadInfo) @@ -38,7 +53,7 @@ void LabelInfoAndroid::clearRefs(PlatformInfo_Android *threadInfo) if (typefaceObj) { threadInfo->env->DeleteGlobalRef(typefaceObj); - typefaceObj = NULL; + typefaceObj = nullptr; } } @@ -62,8 +77,9 @@ void LabelInfoAndroid::setTypeface(PlatformInfo_Android *threadInfo,jobject inTy if (typefaceObj) clearRefs(threadInfo); - if (inTypefaceObj) + if (inTypefaceObj) { typefaceObj = threadInfo->env->NewGlobalRef(inTypefaceObj); + } } } diff --git a/android/library/maply/WhirlyGlobeLib/src/MapboxVectorStyleSet_Android.cpp b/android/library/maply/WhirlyGlobeLib/src/MapboxVectorStyleSet_Android.cpp index bdaea44c72..8fe77541c1 100644 --- a/android/library/maply/WhirlyGlobeLib/src/MapboxVectorStyleSet_Android.cpp +++ b/android/library/maply/WhirlyGlobeLib/src/MapboxVectorStyleSet_Android.cpp @@ -1,9 +1,8 @@ -/* - * MapboxVectorStyleSet_Android.cpp +/* MapboxVectorStyleSet_Android.cpp * WhirlyGlobeLib * * Created by Steve Gifford on 4/29/20. - * Copyright 2011-2020 mousebird consulting + * Copyright 2011-2021 mousebird consulting * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -15,54 +14,60 @@ * 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. - * */ #import "MapboxVectorStyleSet_Android.h" +#import "Formats_jni.h" +#import "LabelsAndMarkers_jni.h" +#import "Exceptions_jni.h" +#import "classInfo/Geometry_jni.h" #import #import #import #import #import -#import "Formats_jni.h" -#import "LabelsAndMarkers_jni.h" namespace WhirlyKit { -MapboxVectorStyleSetImpl_Android::MapboxVectorStyleSetImpl_Android(Scene *scene,CoordSystem *coordSys,VectorStyleSettingsImplRef settings) -: MapboxVectorStyleSetImpl(scene,coordSys,settings), thisObj(NULL), - makeLabelInfoMethod(NULL), makeCircleTextureMethod(NULL), makeLineTextureMethod(NULL) +MapboxVectorStyleSetImpl_Android::MapboxVectorStyleSetImpl_Android(Scene *scene,CoordSystem *coordSys,VectorStyleSettingsImplRef settings) : + MapboxVectorStyleSetImpl(scene,coordSys,std::move(settings)), + thisObj(nullptr), + makeLabelInfoMethod(nullptr), + makeCircleTextureMethod(nullptr), + makeLineTextureMethod(nullptr), + calculateTextWidthMethod(nullptr) { } void MapboxVectorStyleSetImpl_Android::setupMethods(JNIEnv *env) { - // TODO: Turn global reference into local reference for this object - - if (!makeLabelInfoMethod) { + if (!makeLabelInfoMethod) + { jclass thisClass = MapboxVectorStyleSetClassInfo::getClassInfo()->getClass(); - // TODO: Get the right signatures - makeLabelInfoMethod = env->GetMethodID(thisClass, "labelInfoForFont", - "(Ljava/lang/String;F)Lcom/mousebird/maply/LabelInfo;"); - calculateTextWidthMethod = env->GetMethodID(thisClass, "calculateTextWidth", - "(Ljava/lang/String;Lcom/mousebird/maply/LabelInfo;)D"); -// makeCircleTextureMethod = env->GetMethodID(thisClass, "makeLabelInfo", -// ""); -// makeLineTextureMethod = env->GetMethodID(thisClass, "makeLabelInfo", -// ""); + makeLabelInfoMethod = env->GetMethodID(thisClass,"labelInfoForFont", "(Ljava/lang/String;F)Lcom/mousebird/maply/LabelInfo;"); + calculateTextWidthMethod = env->GetMethodID(thisClass,"calculateTextWidth","(Ljava/lang/String;Lcom/mousebird/maply/LabelInfo;)D"); + makeCircleTextureMethod = env->GetMethodID(thisClass,"makeCircleTexture", "(DIIFLcom/mousebird/maply/Point2d;)J"); + makeLineTextureMethod = env->GetMethodID(thisClass,"makeLineTexture", "([D)J"); } } void MapboxVectorStyleSetImpl_Android::cleanup(JNIEnv *env) { - for (auto labelInfo : labelInfos) + for (auto &labelInfo : labelInfos) + { env->DeleteGlobalRef(labelInfo.second->labelInfoObj); + labelInfo.second->labelInfoObj = nullptr; + } labelInfos.clear(); } MapboxVectorStyleSetImpl_Android::~MapboxVectorStyleSetImpl_Android() { + if (thisObj) + { + wkLogLevel(Warn, "Failed to clean up MapboxVectorStyleSetImpl_Android Java ref"); + } } SimpleIdentity MapboxVectorStyleSetImpl_Android::makeCircleTexture(PlatformThreadInfo *inInst, @@ -72,77 +77,148 @@ SimpleIdentity MapboxVectorStyleSetImpl_Android::makeCircleTexture(PlatformThrea float strokeWidth, Point2f *circleSize) { - // TODO: Implement + auto inst = (PlatformInfo_Android *)inInst; + auto env = inst->env; + try + { + setupMethods(env); + jobject pointObj = nullptr; + if (circleSize) + { + pointObj = MakePoint2d(env); + if (!pointObj) + { + return EmptyIdentity; + } + } + + const auto id = env->CallLongMethod(thisObj, makeCircleTextureMethod, + radius, fillColor.asInt(), strokeColor.asInt(), strokeWidth, pointObj); + + if (circleSize) + { + auto point = Point2dClassInfo::getClassInfo()->getObject(env,pointObj); + circleSize->x() = (float)point->x(); + circleSize->y() = (float)point->y(); + + // local ref, no need for `finally` style cleanup + env->DeleteLocalRef(pointObj); + } + + return id; + } + catch (...) + { + __android_log_print(ANDROID_LOG_ERROR, "Maply", "Crash in makeCircleTexture()"); + } return EmptyIdentity; } SimpleIdentity MapboxVectorStyleSetImpl_Android::makeLineTexture(PlatformThreadInfo *inInst,const std::vector &dashComponents) { - // TODO: Implement + auto inst = (PlatformInfo_Android *)inInst; + auto env = inst->env; + try + { + setupMethods(env); + if (auto arrayObj = BuildDoubleArray(env, dashComponents)) + { + const auto id = env->CallLongMethod(thisObj, makeLineTextureMethod, arrayObj); + env->DeleteLocalRef(arrayObj); + return id; + } + } + catch (...) + { + __android_log_print(ANDROID_LOG_ERROR, "Maply", "Crash in makeLineTexture()"); + } return EmptyIdentity; } LabelInfoRef MapboxVectorStyleSetImpl_Android::makeLabelInfo(PlatformThreadInfo *inInst,const std::vector &fontNames,float fontSize) { - PlatformInfo_Android *inst = (PlatformInfo_Android *)inInst; - std::pair entry(fontNames[0],fontSize); - - LabelInfoAndroidRef refLabelInfo = NULL; - auto it = labelInfos.find(entry); - if (it != labelInfos.end()) - refLabelInfo = it->second; - else { + auto inst = (PlatformInfo_Android *)inInst; + + if (fontNames.empty()) + { + return LabelInfoRef(); + } + + try + { + const auto key = std::make_pair(fontNames[0],fontSize); + const auto result = labelInfos.insert(std::make_pair(key, LabelInfoAndroidRef())); + if (!result.second) + { + // Already present, return it + return result.first->second; + } + jstring jFontNameStr = inst->env->NewStringUTF(fontNames[0].c_str()); - jobject labelInfoGlobeObj = inst->env->NewGlobalRef(inst->env->CallObjectMethod(thisObj,makeLabelInfoMethod,jFontNameStr,2.0*fontSize)); + jobject labelInfo = inst->env->CallObjectMethod(thisObj, makeLabelInfoMethod, jFontNameStr, 2.0 * fontSize); + logAndClearJVMException(inst->env, "labelInfoForFont"); inst->env->DeleteLocalRef(jFontNameStr); - LabelInfoAndroidRef *newRef = LabelInfoClassInfo::getClassInfo()->getObject(inst->env,labelInfoGlobeObj); - refLabelInfo = *newRef; - refLabelInfo->labelInfoObj = labelInfoGlobeObj; - refLabelInfo->programID = screenMarkerProgramID; - labelInfos[entry] = refLabelInfo; - } - return refLabelInfo; + if (jobject labelInfoGlobeObj = inst->env->NewGlobalRef(labelInfo)) + { + const auto newRef = LabelInfoClassInfo::getClassInfo()->getObject(inst->env,labelInfoGlobeObj); + const auto refLabelInfo = *newRef; + refLabelInfo->labelInfoObj = labelInfoGlobeObj; + refLabelInfo->programID = screenMarkerProgramID; + + // Save it to the cache map + result.first->second = refLabelInfo; + + return refLabelInfo; + } + } + catch (...) + { + __android_log_print(ANDROID_LOG_ERROR, "Maply", "Crash in makeLabelInfo()"); + } + return LabelInfoRef(); } SingleLabelRef MapboxVectorStyleSetImpl_Android::makeSingleLabel(PlatformThreadInfo *inInst,const std::string &text) { - SingleLabelAndroid *label = new SingleLabelAndroid(); + auto label = std::make_shared(); // Break up the lines and turn the characters into code points for Java std::istringstream ss(text); std::string line; - while (std::getline(ss, line, ss.widen('\n'))) { - std::wstring_convert, char16_t> utf16conv; - std::u16string utf16 = utf16conv.from_bytes(line); - - std::vector codePoints; - for (auto it = utf16.begin(); it != utf16.end(); ++it) - codePoints.push_back(*it); - label->codePointsLines.push_back(codePoints); + std::wstring_convert, char16_t> utf16conv; + while (std::getline(ss, line, ss.widen('\n'))) + { + const std::u16string utf16 = utf16conv.from_bytes(line); + label->codePointsLines.emplace_back(utf16.begin(), utf16.end()); } - return SingleLabelRef(label); + return std::move(label); } -ComponentObjectRef MapboxVectorStyleSetImpl_Android::makeComponentObject(PlatformThreadInfo *inInst) +// NOLINTNEXTLINE(google-default-arguments) +ComponentObjectRef MapboxVectorStyleSetImpl_Android::makeComponentObject(PlatformThreadInfo *inInst, const Dictionary *desc) { - return ComponentObjectRef(new ComponentObject()); + return desc ? std::make_shared(false, false, *desc) + : std::make_shared(false, false); } -double MapboxVectorStyleSetImpl_Android::calculateTextWidth(PlatformThreadInfo *inInst,LabelInfoRef inLabelInfo,const std::string &text) +double MapboxVectorStyleSetImpl_Android::calculateTextWidth(PlatformThreadInfo *inInst,const LabelInfoRef &inLabelInfo,const std::string &text) { - PlatformInfo_Android *inst = (PlatformInfo_Android *)inInst; - LabelInfoAndroidRef labelInfo = std::dynamic_pointer_cast(inLabelInfo); + auto inst = (PlatformInfo_Android *)inInst; jstring jText = inst->env->NewStringUTF(text.c_str()); - jdouble width = inst->env->CallDoubleMethod(thisObj,calculateTextWidthMethod,jText,labelInfo->labelInfoObj); + jdouble width = 0; + if (auto labelInfo = dynamic_cast(inLabelInfo.get())) + { + width = inst->env->CallDoubleMethod(thisObj,calculateTextWidthMethod,jText,labelInfo->labelInfoObj); + } inst->env->DeleteLocalRef(jText); return width; } -void MapboxVectorStyleSetImpl_Android::addSelectionObject(SimpleIdentity selectID,VectorObjectRef vecObj,ComponentObjectRef compObj) +void MapboxVectorStyleSetImpl_Android::addSelectionObject(SimpleIdentity selectID,const VectorObjectRef &vecObj,const ComponentObjectRef &compObj) { if (!compManage) return; diff --git a/android/library/maply/WhirlyGlobeLib/src/QuadImageFrameLoader_Android.cpp b/android/library/maply/WhirlyGlobeLib/src/QuadImageFrameLoader_Android.cpp index 6cbb7fcaf5..a2b22f0ee4 100644 --- a/android/library/maply/WhirlyGlobeLib/src/QuadImageFrameLoader_Android.cpp +++ b/android/library/maply/WhirlyGlobeLib/src/QuadImageFrameLoader_Android.cpp @@ -40,13 +40,14 @@ QIFFrameAsset_Android::QIFFrameAsset_Android(PlatformInfo_Android *threadInfo,Qu QIFFrameAsset_Android::~QIFFrameAsset_Android() { - if (frameAssetObj) - wkLog("Failed to clean up QIFFrameAsset on Java side"); + if (frameAssetObj) { + wkLogLevel(Warn,"Failed to clean up QIFFrameAsset on Java side"); + } } void QIFFrameAsset_Android::cancelFetchJava(PlatformInfo_Android *threadInfo,QuadImageFrameLoader_Android *loader,QIFBatchOps_Android *batchOps) { - threadInfo->env->CallVoidMethod(frameAssetObj,loader->cancelFrameFetchMethod,loader->frameLoaderObj,batchOps->batchOpsObj); + threadInfo->env->CallVoidMethod(frameAssetObj,loader->cancelFrameFetchMethod,batchOps->batchOpsObj); } void QIFFrameAsset_Android::clearFrameAssetJava(PlatformInfo_Android *threadInfo,QuadImageFrameLoader_Android *loader,QIFBatchOps_Android *batchOps) @@ -83,10 +84,16 @@ bool QIFFrameAsset_Android::updateFetching(PlatformThreadInfo *inThreadInfo,Quad void QIFFrameAsset_Android::cancelFetch(PlatformThreadInfo *threadInfo,QuadImageFrameLoader *inLoader,QIFBatchOps *inBatchOps) { - QuadImageFrameLoader_Android *loader = (QuadImageFrameLoader_Android *)inLoader; - QIFBatchOps_Android *batchOps = (QIFBatchOps_Android *)inBatchOps; + auto *loader = (QuadImageFrameLoader_Android *)inLoader; + auto *batchOps = (QIFBatchOps_Android *)inBatchOps; + + QIFFrameAsset::cancelFetch(threadInfo,loader,batchOps); + + if (loadReturnRef) { + loadReturnRef->cancel = true; + } - QIFFrameAsset::cancelFetch(threadInfo,loader, batchOps); + cancelFetchJava((PlatformInfo_Android*)threadInfo,loader,batchOps); } void QIFFrameAsset_Android::loadSuccess(PlatformThreadInfo *threadInfo,QuadImageFrameLoader *loader,const std::vector &texs) @@ -112,15 +119,15 @@ QIFTileAsset_Android::~QIFTileAsset_Android() { } -QIFFrameAssetRef QIFTileAsset_Android::makeFrameAsset(PlatformThreadInfo *inThreadInfo,QuadFrameInfoRef frameInfo,QuadImageFrameLoader *inLoader) +QIFFrameAssetRef QIFTileAsset_Android::makeFrameAsset(PlatformThreadInfo *inThreadInfo,QuadFrameInfoRef frameInfo,QuadImageFrameLoader *) { - QuadImageFrameLoader_Android *loader = (QuadImageFrameLoader_Android *)inLoader; - PlatformInfo_Android *threadInfo = (PlatformInfo_Android *)inThreadInfo; + //const auto loader = (QuadImageFrameLoader_Android *)inLoader; + const auto threadInfo = (PlatformInfo_Android *)inThreadInfo; - QIFFrameAsset_Android *frame = new QIFFrameAsset_Android((PlatformInfo_Android *)threadInfo,frameInfo); - MakeQIFFrameAsset(threadInfo->env,frame); + auto frame = std::make_shared(threadInfo,frameInfo); + MakeQIFFrameAsset(threadInfo->env,frame.get()); - return QIFFrameAssetRef(frame); + return frame; } void QIFTileAsset_Android::startFetching(PlatformThreadInfo *inThreadInfo,QuadImageFrameLoader *inLoader,QuadFrameInfoRef frameToLoad,QIFBatchOps *inBatchOps,ChangeSet &changes) @@ -152,8 +159,12 @@ void QIFTileAsset_Android::startFetching(PlatformThreadInfo *inThreadInfo,QuadIm threadInfo->env->DeleteLocalRef(frameArray); } -QuadImageFrameLoader_Android::QuadImageFrameLoader_Android(PlatformInfo_Android *threadInfo,const SamplingParams ¶ms,int numFrames,Mode mode,JNIEnv *inEnv) - : QuadImageFrameLoader(params,mode), numFrames(numFrames), frameLoaderObj(NULL) +QuadImageFrameLoader_Android::QuadImageFrameLoader_Android(PlatformInfo_Android *threadInfo, + const SamplingParams ¶ms, + int numFrames,Mode mode) : + QuadImageFrameLoader(params,mode), + numFrames(numFrames), + frameLoaderObj(nullptr) { jclass thisClass = QuadImageFrameLoaderClassInfo::getClassInfo()->getClass(); processBatchOpsMethod = threadInfo->env->GetMethodID(thisClass,"processBatchOps","(Lcom/mousebird/maply/QIFBatchOps;)V"); @@ -168,7 +179,7 @@ QuadImageFrameLoader_Android::QuadImageFrameLoader_Android(PlatformInfo_Android frames.resize(numFrames); // TODO: Shouldn't we be creating Android side objects for this? for (unsigned int ii=0;ii!=numFrames;ii++) { - auto frameInfo = QuadFrameInfoRef(new QuadFrameInfo()); + auto frameInfo = std::make_shared(); frameInfo->frameIndex = ii; frames[ii] = frameInfo; } @@ -176,13 +187,16 @@ QuadImageFrameLoader_Android::QuadImageFrameLoader_Android(PlatformInfo_Android QuadImageFrameLoader_Android::~QuadImageFrameLoader_Android() { - + // global ref should have been released by now + if (frameLoaderObj) { + wkLogLevel(Warn, "QuadImageFrameLoader_Android not cleaned up"); + } } QIFTileAssetRef QuadImageFrameLoader_Android::makeTileAsset(PlatformThreadInfo *inThreadInfo,const QuadTreeNew::ImportantNode &ident) { PlatformInfo_Android *threadInfo = (PlatformInfo_Android *)inThreadInfo; - auto tileAsset = QIFTileAssetRef(new QIFTileAsset_Android(threadInfo,ident)); + auto tileAsset = std::make_shared(threadInfo,ident); tileAsset->setupFrames(threadInfo,this,numFrames); return tileAsset; } diff --git a/android/library/maply/WhirlyGlobeLib/src/SingleLabel_Android.cpp b/android/library/maply/WhirlyGlobeLib/src/SingleLabel_Android.cpp index 5136f034b0..c4dbd9d71a 100644 --- a/android/library/maply/WhirlyGlobeLib/src/SingleLabel_Android.cpp +++ b/android/library/maply/WhirlyGlobeLib/src/SingleLabel_Android.cpp @@ -1,9 +1,8 @@ -/* - * SingleLabelAndroid.cpp +/* SingleLabelAndroid.cpp * WhirlyGlobeLib * * Created by Steve Gifford on 6/2/14. - * Copyright 2011-2016 mousebird consulting + * Copyright 2011-2021 mousebird consulting * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -15,7 +14,6 @@ * 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. - * */ #import "SingleLabel_Android.h" @@ -25,33 +23,40 @@ namespace WhirlyKit { -std::vector SingleLabelAndroid::generateDrawableStrings(PlatformThreadInfo *inThreadInfo,const LabelInfo *inLabelInfo,FontTextureManager *inFontTexManager,float &lineHeight,ChangeSet &changes) +std::vector SingleLabelAndroid::generateDrawableStrings( + PlatformThreadInfo *inThreadInfo, + const LabelInfo *inLabelInfo, + const FontTextureManagerRef &inFontTexManager, + float &lineHeight, + ChangeSet &changes) { - FontTextureManager_Android *fontTexManager = (FontTextureManager_Android *)inFontTexManager; - const LabelInfoAndroid *labelInfo = (LabelInfoAndroid *)inLabelInfo; - PlatformInfo_Android *threadInfo = (PlatformInfo_Android *)inThreadInfo; + auto fontTexManager = std::dynamic_pointer_cast(inFontTexManager); + auto labelInfo = (const LabelInfoAndroid *)inLabelInfo; + auto threadInfo = (PlatformInfo_Android *)inThreadInfo; // May need the line height for multi-line labels lineHeight = labelInfo->lineHeight; std::vector drawStrs; + drawStrs.reserve(codePointsLines.size()); + int whichLine = 0; - for (std::vector &codePoints : codePointsLines) + for (const auto &codePoints : codePointsLines) { - DrawableString *drawStr = fontTexManager->addString(threadInfo,codePoints,labelInfo,changes); - if (drawStr) + if (auto drawStr = fontTexManager->addString(threadInfo,codePoints,labelInfo,changes)) + { drawStrs.push_back(drawStr); - // Modify the MBR if this is a multi-line label - if (whichLine > 0) { - drawStr->mbr.ll().y() += lineHeight * whichLine; - drawStr->mbr.ur().y() += lineHeight * whichLine; + // Modify the MBR if this is a multi-line label + if (whichLine > 0) + { + drawStr->mbr.ll().y() += lineHeight * whichLine; + drawStr->mbr.ur().y() += lineHeight * whichLine; + } } whichLine++; } - - return drawStrs; } diff --git a/android/library/maply/WhirlyGlobeLib/src/VectorStyleSet_Android.cpp b/android/library/maply/WhirlyGlobeLib/src/VectorStyleSet_Android.cpp index c36f6becc9..5e0cafa2fe 100644 --- a/android/library/maply/WhirlyGlobeLib/src/VectorStyleSet_Android.cpp +++ b/android/library/maply/WhirlyGlobeLib/src/VectorStyleSet_Android.cpp @@ -1,9 +1,8 @@ -/* - * VectorStyleSet_Android.cpp +/* VectorStyleSet_Android.cpp * WhirlyGlobeLib * * Created by Steve Gifford on 8/10/20. - * Copyright 2011-2020 mousebird consulting + * Copyright 2011-2021 mousebird consulting * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -15,7 +14,6 @@ * 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. - * */ #include "../include/VectorStyleSet_Android.h" @@ -33,30 +31,27 @@ long long VectorStyleImpl_Android::getUuid(PlatformThreadInfo *inst) std::string VectorStyleImpl_Android::getCategory(PlatformThreadInfo *inst) { - auto entry = styleSet->styles.find(uuid); - if (entry == styleSet->styles.end()) - return ""; - return entry->second.category; + const auto entry = styleSet->styles.find(uuid); + return (entry != styleSet->styles.end()) ? entry->second.category : std::string(); } bool VectorStyleImpl_Android::geomAdditive(PlatformThreadInfo *inst) { - auto entry = styleSet->styles.find(uuid); - if (entry == styleSet->styles.end()) - return false; - return entry->second.geomAdditive; + const auto entry = styleSet->styles.find(uuid); + return (entry != styleSet->styles.end()) && entry->second.geomAdditive; } -void VectorStyleImpl_Android::buildObjects(PlatformThreadInfo *inst, std::vector &vecObjs,VectorTileDataRef tileInfo) +void VectorStyleImpl_Android::buildObjects(PlatformThreadInfo *inst, const std::vector &vecObjs, + const VectorTileDataRef &tileInfo, const Dictionary *desc) { // Pass through to the parent object, since we're just a wrapper - styleSet->buildObjects(inst,uuid,vecObjs,tileInfo); + styleSet->buildObjects(inst,uuid,vecObjs,tileInfo,desc); } VectorStyleSetWrapper_Android::VectorStyleSetWrapper_Android(PlatformThreadInfo *platformInfo, jobject obj, - const std::vector uuids, - const std::vector categories, + const std::vector &uuids, + const std::vector &categories, const std::vector &geomAdditive ) { @@ -71,14 +66,21 @@ VectorStyleSetWrapper_Android::VectorStyleSetWrapper_Android(PlatformThreadInfo StyleEntry entry; entry.category = categories[ii]; entry.geomAdditive = geomAdditive[ii]; - entry.style = VectorStyleImpl_AndroidRef(new VectorStyleImpl_Android()); + entry.style = std::make_shared(); entry.style->styleSet = this; - auto uuid = uuids[ii]; + const auto uuid = uuids[ii]; entry.style->uuid = uuid; styles[uuid] = entry; } } +VectorStyleSetWrapper_Android::~VectorStyleSetWrapper_Android() +{ + if (wrapperObj) { + wkLogLevel(Warn, "VectorStyleSetWrapper_Android not cleaned up"); + } +} + std::vector VectorStyleSetWrapper_Android::stylesForFeature(PlatformThreadInfo *platformInfo, const Dictionary &attrs, const QuadTreeIdentifier &tileID, @@ -106,7 +108,7 @@ std::vector VectorStyleSetWrapper_Android::stylesForFeature( for (auto uuid: uuids) { auto entry = styles.find(uuid); if (entry == styles.end()) { - wkLogLevel(Warn,"Failed to find style for UUID in VectorStyleSet_Android. Features will dissappear."); + wkLogLevel(Warn,"Failed to find style for UUID in VectorStyleSet_Android. Features will disappear."); continue; } retStyles.push_back(entry->second.style); @@ -120,10 +122,10 @@ bool VectorStyleSetWrapper_Android::layerShouldDisplay(PlatformThreadInfo *platf const std::string &name, const QuadTreeNew::Node &tileID) { - PlatformInfo_Android *threadInfo = (PlatformInfo_Android *)platformInfo; + auto threadInfo = (PlatformInfo_Android *)platformInfo; jobject jStr = threadInfo->env->NewStringUTF(name.c_str()); - bool val = threadInfo->env->CallBooleanMethod(wrapperObj,layerShouldDisplayMethod,jStr,tileID.x,tileID.y,tileID.level); + const bool val = threadInfo->env->CallBooleanMethod(wrapperObj,layerShouldDisplayMethod,jStr,tileID.x,tileID.y,tileID.level); threadInfo->env->DeleteLocalRef(jStr); return val; @@ -131,17 +133,15 @@ bool VectorStyleSetWrapper_Android::layerShouldDisplay(PlatformThreadInfo *platf VectorStyleImplRef VectorStyleSetWrapper_Android::styleForUUID(PlatformThreadInfo *inst,long long uuid) { - auto entry = styles.find(uuid); - if (entry == styles.end()) - return VectorStyleImplRef(); - - return entry->second.style; + const auto entry = styles.find(uuid); + return (entry != styles.end()) ? entry->second.style : VectorStyleImplRef(); } std::vector VectorStyleSetWrapper_Android::allStyles(PlatformThreadInfo *inst) { std::vector retStyles; - for (auto style: styles) + retStyles.reserve(styles.size()); + for (const auto &style: styles) retStyles.push_back(style.second.style); return retStyles; @@ -155,7 +155,7 @@ VectorStyleImplRef VectorStyleSetWrapper_Android::backgroundStyle(PlatformThread RGBAColorRef VectorStyleSetWrapper_Android::backgroundColor(PlatformThreadInfo *inst,double zoom) { - return RGBAColorRef(new RGBAColor(0,0,0,0)); + return std::make_shared(0,0,0,0); } // Break the vectors up into groups of this size before calling back to Java @@ -164,8 +164,9 @@ static const int VecBatchSize = 500; void VectorStyleSetWrapper_Android::buildObjects(PlatformThreadInfo *platformInfo, SimpleIdentity styleID, - std::vector &vecObjs, - VectorTileDataRef tileInfo) + const std::vector &vecObjs, + const VectorTileDataRef &tileInfo, + const Dictionary *desc) { PlatformInfo_Android *threadInfo = (PlatformInfo_Android *)platformInfo; @@ -174,10 +175,11 @@ void VectorStyleSetWrapper_Android::buildObjects(PlatformThreadInfo *platformInf for (unsigned int ii=0;ii vecObjVec; + vecObjVec.reserve(VecBatchSize); for (unsigned int jj=0;jj= vecObjs.size()) break; - auto vecObj = vecObjs[ii+jj]; + const auto &vecObj = vecObjs[ii+jj]; jobject newObj = MakeVectorObject(threadInfo->env,vecObj); vecObjVec.push_back(newObj); } @@ -199,7 +201,7 @@ void VectorStyleSetWrapper_Android::shutdown(PlatformThreadInfo *platformInfo) if (wrapperObj) { info->env->DeleteGlobalRef(wrapperObj); - wrapperObj = NULL; + wrapperObj = nullptr; } styles.clear(); diff --git a/android/library/maply/WhirlyGlobeLib/src/WhirlyKitLog.cpp b/android/library/maply/WhirlyGlobeLib/src/WhirlyKitLog.cpp index b7ef8bd4d2..24ae6967eb 100644 --- a/android/library/maply/WhirlyGlobeLib/src/WhirlyKitLog.cpp +++ b/android/library/maply/WhirlyGlobeLib/src/WhirlyKitLog.cpp @@ -33,7 +33,7 @@ void wkLog(const char *formatStr,...) va_end(args); } -static const char *levels[] = {"Verbose","Debug","Info","Warn","Error"}; +//static const char *levels[] = {"Verbose","Debug","Info","Warn","Error"}; void wkLogLevel_(WKLogLevel level,const char *formatStr,...) { diff --git a/android/library/maply/build.gradle b/android/library/maply/build.gradle index d2d5108162..648ecce9e2 100644 --- a/android/library/maply/build.gradle +++ b/android/library/maply/build.gradle @@ -26,7 +26,7 @@ apply plugin: 'kotlin-android-extensions' android { compileSdkVersion 28 defaultConfig { - minSdkVersion 19 + minSdkVersion 21 targetSdkVersion 28 versionCode 1 versionName "1.0" @@ -35,16 +35,53 @@ android { buildTypes { debug { + debuggable true + jniDebuggable true ndk { - abiFilters 'arm64-v8a', 'armeabi-v7a', 'x86' + abiFilters 'arm64-v8a', 'armeabi-v7a', 'x86', 'x86_64' + debuggable true + } + externalNativeBuild { + cmake { + cppFlags "-g", // enable debug info + "-DDEBUG=1", // Debug flags + "-DCMAKE_BUILD_TYPE=Debug", + "-O0", + //"-O1", // Minimal optimize. (-Wuninitialized requires -O1) + // Change to -O0 if you're having trouble debugging + "-Wall", "-Wextra", // warnings + + "-Wuninitialized", // requires -O1 or above, but doesn't seem to work anyway + "-Wmissing-field-initializers", + "-Woverloaded-virtual", + + "-Wno-unused-parameter", + "-Wno-unused-private-field", + "-Wno-reorder", + "-Wno-sign-compare", + "-Wno-missing-braces", + "-Wno-null-pointer-arithmetic" + } + } + } + release { + debuggable false + jniDebuggable false + ndk { + abiFilters 'arm64-v8a', 'armeabi-v7a', 'x86', 'x86_64' debuggable false } + externalNativeBuild { + cmake { + cppFlags "-O2", "-DNDEBUG=1", "-DCMAKE_BUILD_TYPE=Release" + } + } } } externalNativeBuild { cmake { - cppFlags "-frtti -fexceptions" + cppFlags "-frtti", "-fexceptions", "-std=gnu++14" } } @@ -64,6 +101,9 @@ android { sourceCompatibility JavaVersion.VERSION_1_8 targetCompatibility JavaVersion.VERSION_1_8 } + kotlinOptions { + jvmTarget = JavaVersion.VERSION_1_8 + } ndkVersion '21.3.6528147' } @@ -76,6 +116,8 @@ dependencies { implementation 'com.squareup.okhttp3:okhttp:3.14.9' implementation 'androidx.core:core-ktx:+' implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.4.10" + //implementation 'com.google.android.gms:play-services:12.0.1' + api 'com.google.android.gms:play-services-location:17.1.0' } repositories { mavenCentral() diff --git a/android/library/maply/jni/CMakeLists.txt b/android/library/maply/jni/CMakeLists.txt index c528de0ed4..e763c6cefb 100644 --- a/android/library/maply/jni/CMakeLists.txt +++ b/android/library/maply/jni/CMakeLists.txt @@ -1,5 +1,8 @@ cmake_minimum_required(VERSION 3.4.1) +#SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -v") +#SET(CMAKE_LINKER_FLAGS "${CMAKE_CXX_FLAGS} -v") + target_include_directories( ${WGTARGET} @@ -35,6 +38,7 @@ target_sources( "${CMAKE_CURRENT_LIST_DIR}/include/classInfo/View_jni.h" "${CMAKE_CURRENT_LIST_DIR}/src/jni_util/Maply_jni.cpp" + "${CMAKE_CURRENT_LIST_DIR}/src/jni_util/Exceptions_jni.cpp" "${CMAKE_CURRENT_LIST_DIR}/src/math/AngleAxis_jni.cpp" "${CMAKE_CURRENT_LIST_DIR}/src/math/Matrix3d_jni.cpp" @@ -145,6 +149,7 @@ target_sources( "${CMAKE_CURRENT_LIST_DIR}/src/quadLoading/QuadImageLoaderBase_jni.cpp" "${CMAKE_CURRENT_LIST_DIR}/src/quadLoading/QuadImageFrameLoader_jni.cpp" "${CMAKE_CURRENT_LIST_DIR}/src/quadLoading/QuadSamplingLayer_jni.cpp" + "${CMAKE_CURRENT_LIST_DIR}/src/quadLoading/RawPNGImageLoaderInterpreter_jni.cpp" "${CMAKE_CURRENT_LIST_DIR}/src/renderer/RenderController_jni.cpp" diff --git a/android/library/maply/jni/include/classInfo/Billboard_jni.h b/android/library/maply/jni/include/classInfo/Billboard_jni.h index 129187f3fa..317bdf96df 100644 --- a/android/library/maply/jni/include/classInfo/Billboard_jni.h +++ b/android/library/maply/jni/include/classInfo/Billboard_jni.h @@ -23,7 +23,7 @@ typedef JavaClassInfo BillboardInfoClassInfo; typedef JavaClassInfo BillboardClassInfo; -typedef JavaClassInfo BillboardManagerClassInfo; +typedef JavaClassInfo BillboardManagerClassInfo; typedef JavaClassInfo ScreenObjectClassInfo; typedef JavaClassInfo StringWrapperClassInfo; typedef JavaClassInfo SimplePolyClassInfo; diff --git a/android/library/maply/jni/include/classInfo/Components_jni.h b/android/library/maply/jni/include/classInfo/Components_jni.h index a696427cd6..4bcd0f52fc 100644 --- a/android/library/maply/jni/include/classInfo/Components_jni.h +++ b/android/library/maply/jni/include/classInfo/Components_jni.h @@ -24,7 +24,7 @@ typedef JavaClassInfo BaseInfoClassInfo; typedef JavaClassInfo SingleVertexAttributeClassInfo; typedef JavaClassInfo ComponentObjectRefClassInfo; -typedef JavaClassInfo ComponentManagerClassInfo; +typedef JavaClassInfo ComponentManagerClassInfo; // Build a Java-side object to wrap the given ComponentObject JNIEXPORT jobject JNICALL MakeComponentObjectWrapper(JNIEnv *env,ComponentObjectRefClassInfo *classInfo,WhirlyKit::ComponentObjectRef compObj); diff --git a/android/library/maply/jni/include/classInfo/Exceptions_jni.h b/android/library/maply/jni/include/classInfo/Exceptions_jni.h new file mode 100644 index 0000000000..164a9c3555 --- /dev/null +++ b/android/library/maply/jni/include/classInfo/Exceptions_jni.h @@ -0,0 +1,41 @@ +/* Exceptions_jni.h + * WhirlyGlobeLib + * + * Created by Tim Sylvester on 3/8/2021 + * Copyright 2021-2021 mousebird consulting + * + * 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. + */ + +#import +#import +#import + +namespace WhirlyKit { + +/// Build a string from the throwable stack trace info, from ExceptionOccurred(). +/// The exception must be cleared with env.ExceptionClear() first. +extern void appendExceptionTraceMessages(JNIEnv* env, std::ostringstream& msg, jthrowable ex); + +/// Build a string from the throwable stack trace info, from ExceptionOccurred(). +/// The exception must be cleared with env.ExceptionClear() first. +extern std::string getExceptionTraceMessages(JNIEnv* env, jthrowable ex); + +extern void logJVMException(JNIEnv* env, jthrowable throwable, const char* where, android_LogPriority priority); + +extern bool logAndClearJVMException(JNIEnv* env, const char* where, android_LogPriority = ANDROID_LOG_ERROR); + +extern bool logStackTrace(JNIEnv* env, const char* where, android_LogPriority = ANDROID_LOG_ERROR); + +extern std::string getStackTrace(JNIEnv* env); + +} \ No newline at end of file diff --git a/android/library/maply/jni/include/classInfo/Formats_jni.h b/android/library/maply/jni/include/classInfo/Formats_jni.h index 58b64f3328..878994b804 100644 --- a/android/library/maply/jni/include/classInfo/Formats_jni.h +++ b/android/library/maply/jni/include/classInfo/Formats_jni.h @@ -27,4 +27,4 @@ typedef JavaClassInfo VectorStyleSe typedef JavaClassInfo MapboxVectorStyleSetClassInfo; typedef JavaClassInfo VectorStyleSettingsClassInfo; -JNIEXPORT jobject JNICALL MakeVectorTileDataObject(JNIEnv *env,WhirlyKit::VectorTileDataRef tileData); \ No newline at end of file +JNIEXPORT jobject JNICALL MakeVectorTileDataObject(JNIEnv *env,const WhirlyKit::VectorTileDataRef &tileData); \ No newline at end of file diff --git a/android/library/maply/jni/include/classInfo/GeometryManager_jni.h b/android/library/maply/jni/include/classInfo/GeometryManager_jni.h index 5da5d26ee9..45cf0b1e57 100644 --- a/android/library/maply/jni/include/classInfo/GeometryManager_jni.h +++ b/android/library/maply/jni/include/classInfo/GeometryManager_jni.h @@ -1,9 +1,8 @@ -/* - * GeometryManager_jni.h +/* GeometryManager_jni.h * WhirlyGlobeLib * * Created by Steve Gifford on 3/7/19. - * Copyright 2011-2019 mousebird consulting + * Copyright 2011-2021 mousebird consulting * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -15,13 +14,12 @@ * 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. - * */ #import "Maply_jni.h" #import "WhirlyGlobe_Android.h" -typedef JavaClassInfo GeometryManagerClassInfo; +typedef JavaClassInfo GeometryManagerClassInfo; typedef JavaClassInfo GeometryInfoClassInfo; typedef JavaClassInfo GeometryRawClassInfo; typedef JavaClassInfo GeometryRawPointsClassInfo; diff --git a/android/library/maply/jni/include/classInfo/Geometry_jni.h b/android/library/maply/jni/include/classInfo/Geometry_jni.h index 9466384cb7..e015ca28ad 100644 --- a/android/library/maply/jni/include/classInfo/Geometry_jni.h +++ b/android/library/maply/jni/include/classInfo/Geometry_jni.h @@ -1,9 +1,8 @@ -/* - * Geometry_jni.h +/* Geometry_jni.h * WhirlyGlobeLib * * Created by Steve Gifford on 3/7/19. - * Copyright 2011-2019 mousebird consulting + * Copyright 2011-2021 mousebird consulting * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -15,7 +14,6 @@ * 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. - * */ #import "Maply_jni.h" @@ -29,7 +27,6 @@ namespace WhirlyKit { typedef Eigen::Vector4d Point4d; -typedef Eigen::Vector4f Point4f; } typedef JavaClassInfo Point2dClassInfo; @@ -41,6 +38,7 @@ typedef JavaClassInfo QuaternionClassInfo; typedef JavaClassInfo AngleAxisClassInfo; // Construct a Java-side Point2d +JNIEXPORT jobject JNICALL MakePoint2d(JNIEnv *env); JNIEXPORT jobject JNICALL MakePoint2d(JNIEnv *env,const WhirlyKit::Point2d &pt); // Construct a Java-side Point3d diff --git a/android/library/maply/jni/include/classInfo/LabelsAndMarkers_jni.h b/android/library/maply/jni/include/classInfo/LabelsAndMarkers_jni.h index 9fcf136d96..d754362cf2 100644 --- a/android/library/maply/jni/include/classInfo/LabelsAndMarkers_jni.h +++ b/android/library/maply/jni/include/classInfo/LabelsAndMarkers_jni.h @@ -23,7 +23,7 @@ typedef JavaClassInfo MarkerInfoClassInfo; typedef JavaClassInfo MarkerClassInfo; -typedef JavaClassInfo MarkerManagerClassInfo; +typedef JavaClassInfo MarkerManagerClassInfo; typedef JavaClassInfo LabelInfoClassInfo; typedef JavaClassInfo LabelClassInfo; diff --git a/android/library/maply/jni/include/classInfo/LayoutSelection_jni.h b/android/library/maply/jni/include/classInfo/LayoutSelection_jni.h index cb23c713a5..a260e122cd 100644 --- a/android/library/maply/jni/include/classInfo/LayoutSelection_jni.h +++ b/android/library/maply/jni/include/classInfo/LayoutSelection_jni.h @@ -23,6 +23,6 @@ typedef JavaClassInfo LayoutManagerClassInfo; typedef JavaClassInfo SelectedObjectClassInfo; -typedef JavaClassInfo SelectionManagerClassInfo; +typedef JavaClassInfo SelectionManagerClassInfo; JNIEXPORT jobject JNICALL MakeSelectedObject(JNIEnv *env,const WhirlyKit::SelectionManager::SelectedObject &selObj); diff --git a/android/library/maply/jni/include/classInfo/Maply_jni.h b/android/library/maply/jni/include/classInfo/Maply_jni.h index 38bbdee85d..d520a911a9 100644 --- a/android/library/maply/jni/include/classInfo/Maply_jni.h +++ b/android/library/maply/jni/include/classInfo/Maply_jni.h @@ -1,5 +1,4 @@ -/* - * Maply_jni.h +/* Maply_jni.h * WhirlyGlobeLib * * Created by Steve Gifford on 6/2/14. @@ -15,7 +14,6 @@ * 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. - * */ #ifndef Maply_JNI_h_ @@ -36,20 +34,24 @@ template class JavaClassInfo { public: - // Don't use this constructor - JavaClassInfo() { } // Construct with environment and class - JavaClassInfo(JNIEnv *env,jclass inClass) - : theClass(NULL), nativeHandleField(NULL) + JavaClassInfo(JNIEnv *env,jclass inClass) : + theClass((jclass)env->NewGlobalRef(inClass)), + nativeHandleField(nullptr) { - theClass = (jclass)env->NewGlobalRef(inClass); initMethodID = env->GetMethodID(theClass, "", "()V"); } + virtual ~JavaClassInfo() { + if (theClass) { + wkLogLevel(Warn, "JavaClassInfo not cleaned up"); + } + } // Clear references to JNI data virtual void clear(JNIEnv *env) { env->DeleteGlobalRef(theClass); + theClass = nullptr; } // Make an object of the type and point it to the C++ object given @@ -70,7 +72,7 @@ template class JavaClassInfo } // Return the field ID for the handle we use - jfieldID getHandleField(JNIEnv *env,jobject obj) + jfieldID getHandleField(JNIEnv *env) { if (!nativeHandleField) { @@ -84,13 +86,19 @@ template class JavaClassInfo { if (!obj) { - __android_log_print(ANDROID_LOG_VERBOSE, "Maply", "Null object handle in getHandle()."); - return NULL; + // wkLog doesn't work from here? why? + __android_log_print(ANDROID_LOG_VERBOSE, "Maply", "Null object handle in getHandle() for '%s'", typeid(T).name()); + return nullptr; } - jlong handle = env->GetLongField(obj, getHandleField(env, obj)); + jlong handle = env->GetLongField(obj, getHandleField(env)); return reinterpret_cast(handle); } + static T* get(JNIEnv *env,jobject obj) + { + return getClassInfo()->getObject(env,obj); + } + // Set the handle for a Java wrapper to its C++ object void setHandle(JNIEnv *env, jobject obj, T *t) { @@ -100,13 +108,13 @@ template class JavaClassInfo return; } jlong handle = reinterpret_cast(t); - env->SetLongField(obj, getHandleField(env, obj), handle); + env->SetLongField(obj, getHandleField(env), handle); } // Clear the handle out of a Java wrapper object void clearHandle(JNIEnv *env, jobject obj) { - env->SetLongField(obj, getHandleField(env, obj), 0); + env->SetLongField(obj, getHandleField(env), 0); } // Just one class info object for the whole class @@ -141,9 +149,11 @@ template class JavaClassInfo } // Return the Java class - jclass getClass() { return theClass; } + jclass getClass() const { return theClass; } protected: + JavaClassInfo() = default; + jclass theClass; jfieldID nativeHandleField; jmethodID initMethodID; @@ -162,23 +172,21 @@ class JavaIntegerClassInfo env->DeleteLocalRef(intLocalClass); } -public: - // Don't use this constructor - JavaIntegerClassInfo() { } + ~JavaIntegerClassInfo() + { + // We don't expect this because this should only be used by static instances + wkLogLevel(Warn, "Global ref not cleaned up"); + } +public: // Make an Integer with the given value - jobject makeInteger(JNIEnv *env,int iVal) - { - return env->NewObject(integerClass, integerClassInitID, iVal); - } + jobject makeInteger(JNIEnv *env,int iVal) { return env->NewObject(integerClass, integerClassInitID, iVal); } + static jobject make(JNIEnv *env,int iVal) { return getClassInfo(env)->makeInteger(env,iVal); } // Return the value of an Integer object - int getInteger(JNIEnv *env,jobject intObj) - { - return env->CallIntMethod(intObj,integerGetID); - } + int getInteger(JNIEnv *env,jobject intObj) { return env->CallIntMethod(intObj,integerGetID); } + static int get(JNIEnv *env,jobject intObj) { return getClassInfo(env)->getInteger(env,intObj); } - static JavaIntegerClassInfo *classInfoObj; static JavaIntegerClassInfo *getClassInfo(JNIEnv *env) { if (!classInfoObj) @@ -189,6 +197,8 @@ class JavaIntegerClassInfo protected: jclass integerClass; jmethodID integerClassInitID,integerGetID; + + static JavaIntegerClassInfo *classInfoObj; }; // Wrapper for creating Java Long objects @@ -204,23 +214,22 @@ class JavaLongClassInfo env->DeleteLocalRef(longLocalClass); } + ~JavaLongClassInfo() + { + // We don't expect this because this should only be used by static instances + wkLogLevel(Warn, "Global ref not cleaned up"); + } + public: - // Don't use this constructor - JavaLongClassInfo() { } // Make an Integer with the given value - jobject makeLong(JNIEnv *env,long long lVal) - { - return env->NewObject(longClass, longClassInitID, lVal); - } + jobject makeLong(JNIEnv *env,long long lVal) { return env->NewObject(longClass, longClassInitID, lVal); } + static jobject make(JNIEnv *env,long long lVal) { return getClassInfo(env)->makeLong(env,lVal); } // Return the value of an Integer object - int getLong(JNIEnv *env,jobject longObj) - { - return env->CallLongMethod(longObj,longGetID); - } + int getLong(JNIEnv *env,jobject longObj) { return env->CallLongMethod(longObj,longGetID); } + static int get(JNIEnv *env,jobject longObj) { return getClassInfo(env)->getLong(env,longObj); } - static JavaLongClassInfo *classInfoObj; static JavaLongClassInfo *getClassInfo(JNIEnv *env) { if (!classInfoObj) @@ -231,6 +240,8 @@ class JavaLongClassInfo protected: jclass longClass; jmethodID longClassInitID,longGetID; + + static JavaLongClassInfo *classInfoObj; }; // Wrapper for creating Java Double objects @@ -242,20 +253,25 @@ class JavaDoubleClassInfo jclass doubleLocalClass = env->FindClass("java/lang/Double"); doubleClass = (jclass)env->NewGlobalRef(doubleLocalClass); doubleClassInitID = env->GetMethodID(doubleClass, "", "(D)V"); + doubleGetID = env->GetMethodID(doubleClass,"doubleValue","()D"); env->DeleteLocalRef(doubleLocalClass); } -public: - // Don't use this constructor - JavaDoubleClassInfo() { } + ~JavaDoubleClassInfo() + { + // We don't expect this because this should only be used by static instances + wkLogLevel(Warn, "Global ref not cleaned up"); + } +public: // Make a Double with the given value - jobject makeDouble(JNIEnv *env,int iVal) - { - return env->NewObject(doubleClass, doubleClassInitID, iVal); - } + jobject makeDouble(JNIEnv *env,int iVal) { return env->NewObject(doubleClass, doubleClassInitID, iVal); } + static jobject make(JNIEnv *env,int iVal) { return getClassInfo(env)->makeDouble(env,iVal); } + + // Return the value of an Double object + double getDouble(JNIEnv *env,jobject dblObj) { return env->CallDoubleMethod(dblObj,doubleGetID); } + static double get(JNIEnv *env,jobject dblObj) { return getClassInfo(env)->getDouble(env,dblObj); } - static JavaDoubleClassInfo *classInfoObj; static JavaDoubleClassInfo *getClassInfo(JNIEnv *env) { if (!classInfoObj) @@ -266,6 +282,8 @@ class JavaDoubleClassInfo protected: jclass doubleClass; jmethodID doubleClassInitID; + jmethodID doubleGetID; + static JavaDoubleClassInfo *classInfoObj; }; // Wrapper for HashMap @@ -281,11 +299,24 @@ class JavaHashMapInfo env->DeleteLocalRef(localMapClass); } + ~JavaHashMapInfo() + { + // singleton pattern, so we don't expect this except at process stop + if (mapClass) { + wkLogLevel(Warn, "Global ref not cleaned up"); + } + } + public: // Create a new hash map - jobject makeHashMap(JNIEnv *env) - { - return env->NewObject(mapClass, mapInitMethodID, 1); + jobject makeHashMap(JNIEnv *env) { return env->NewObject(mapClass, mapInitMethodID, 1); } + static jobject make(JNIEnv *env) { return getClassInfo(env)->makeHashMap(env); } + + void teardown(JNIEnv *env) { + if (mapClass) { + env->DeleteGlobalRef(mapClass); + mapClass = nullptr; + } } // Add an object to an existing hash map @@ -294,7 +325,6 @@ class JavaHashMapInfo env->CallObjectMethod(hashMap, putMethodID, key, val); } - static JavaHashMapInfo *classInfoObj; static JavaHashMapInfo *getClassInfo(JNIEnv *env) { if (!classInfoObj) @@ -306,6 +336,7 @@ class JavaHashMapInfo jclass mapClass; jmethodID mapInitMethodID; jmethodID putMethodID; + static JavaHashMapInfo *classInfoObj; }; // Wrapper for List @@ -325,28 +356,32 @@ class JavaListInfo env->DeleteLocalRef(localListClass); } + ~JavaListInfo() + { + // singleton pattern, so we don't expect this except at process stop + if (iterClass || listClass) { + wkLogLevel(Warn, "JavaListInfo not cleaned up"); + } + } + public: // Return the iterator for a given list object - jobject getIter(JNIEnv *env,jobject listObj) - { - return env->CallObjectMethod(listObj,literMethodID); - } + jobject getIter(JNIEnv *env,jobject listObj) const { return env->CallObjectMethod(listObj,literMethodID); } // See if there's a next object - bool hasNext(JNIEnv *env,jobject listObj,jobject iterObj) - { - return env->CallBooleanMethod(iterObj, hasNextID); - } + bool hasNext(JNIEnv *env,jobject listObj,jobject iterObj) const { return env->CallBooleanMethod(iterObj, hasNextID); } // Get the next object with an iterator - jobject getNext(JNIEnv *env,jobject listObj,jobject iterObj) - { - return env->CallObjectMethod(iterObj, nextID); + jobject getNext(JNIEnv *env,jobject listObj,jobject iterObj) { return env->CallObjectMethod(iterObj, nextID); } + + void teardown(JNIEnv *env) { + if (iterClass) { + env->DeleteGlobalRef(iterClass); + env->DeleteGlobalRef(listClass); + iterClass = nullptr; + listClass = nullptr; + } } - -public: - - static JavaListInfo *classInfoObj; static JavaListInfo *getClassInfo(JNIEnv *env) { if (!classInfoObj) @@ -357,22 +392,30 @@ class JavaListInfo protected: jclass listClass,iterClass; jmethodID literMethodID,hasNextID,nextID; + static JavaListInfo *classInfoObj; }; // Wrapper for Java string. Destructor releases. -class JavaString +// These are not meant to be long-lived, and should not be composed into other objects. +struct JavaString { -public: // Construct with the string we want to wrap - // Only allocate these on the heap. - JavaString(JNIEnv *env,jstring &str); - + JavaString(JNIEnv *env,jstring str); + JavaString(const JavaString &other) = delete; + JavaString(JavaString &&other) noexcept; + // This cleans up the reference. ~JavaString(); - - JNIEnv *env; - jstring &str; - const char *cStr; + + operator const char*() const { return cStr; } + operator bool() const { return cStr != nullptr; } + + const char *getCString() const { return cStr; } + +private: + const char *cStr; + JNIEnv *env; + jstring str; }; // Wrapper for Java boolean array. Destructor cleans up. @@ -444,15 +487,26 @@ class JavaDoubleArray * Used to iterate over the elements of an object array. * This cleans up the previous object when you get the next * and cleans up the last one on destruction. + * + * Retains the JNIEnv, and so must not be held across JNI calls. */ class JavaObjectArrayHelper { public: JavaObjectArrayHelper(JNIEnv *env,jobjectArray objArray); + JavaObjectArrayHelper(const JavaObjectArrayHelper &) = delete; ~JavaObjectArrayHelper(); + JavaObjectArrayHelper& operator=(const JavaObjectArrayHelper&) = delete; + // Total number of objects - int numObjects(); + int numObjects() const { return count; } + + operator bool() const { return count > 0; } + + bool hasNextObject() const { return nextIndex < count; } + + jobject getCurrentObject() const { return curObj; } // Return the next object, if there is one. NULL otherwise. jobject getNextObject(); @@ -461,7 +515,7 @@ class JavaObjectArrayHelper JNIEnv *env; jobjectArray objArray; int count; - int which; + int nextIndex; jobject curObj; }; @@ -502,14 +556,15 @@ void ConvertLongArrayToSet(JNIEnv *env,jlongArray &longArray,std::set &strVec); // Return a Java long array -jlongArray BuildLongArray(JNIEnv *env,std::vector &longVec); +jlongArray BuildLongArray(JNIEnv *env,const std::vector &longVec); // Return a Java double array -jdoubleArray BuildDoubleArray(JNIEnv *env,std::vector &doubleVec); +jdoubleArray BuildDoubleArray(JNIEnv *env,const std::vector &doubleVec); // Return a Java int array -jintArray BuildIntArray(JNIEnv *env,std::vector &longVec); +jintArray BuildIntArray(JNIEnv *env,const std::vector &longVec); // Return a new Java object array -jobjectArray BuildObjectArray(JNIEnv *env,jclass cls,std::vector &objVec); +jobjectArray BuildObjectArray(JNIEnv *env,jclass cls,jobject singleObj); +jobjectArray BuildObjectArray(JNIEnv *env,jclass cls,const std::vector &objVec); // Return new Java string array -jobjectArray BuildStringArray(JNIEnv *env,std::vector &objVec); +jobjectArray BuildStringArray(JNIEnv *env,const std::vector &objVec); #endif /* Maply_JNI_h_ */ diff --git a/android/library/maply/jni/include/classInfo/Particles_jni.h b/android/library/maply/jni/include/classInfo/Particles_jni.h index d15a23f079..34e49ae134 100644 --- a/android/library/maply/jni/include/classInfo/Particles_jni.h +++ b/android/library/maply/jni/include/classInfo/Particles_jni.h @@ -22,5 +22,5 @@ #import "WhirlyGlobe_Android.h" typedef JavaClassInfo ParticleSystemClassInfo; -typedef JavaClassInfo ParticleSystemManagerClassInfo; +typedef JavaClassInfo ParticleSystemManagerClassInfo; typedef JavaClassInfo ParticleBatchClassInfo; diff --git a/android/library/maply/jni/include/classInfo/Shapes_jni.h b/android/library/maply/jni/include/classInfo/Shapes_jni.h index b3299b6a1c..c0c4f9fc20 100644 --- a/android/library/maply/jni/include/classInfo/Shapes_jni.h +++ b/android/library/maply/jni/include/classInfo/Shapes_jni.h @@ -30,4 +30,4 @@ typedef JavaClassInfo LinearClassInfo; typedef JavaClassInfo ShapeInfoClassInfo; typedef JavaClassInfo SphereClassInfo; typedef JavaClassInfo RectangleClassInfo; -typedef JavaClassInfo ShapeManagerClassInfo; +typedef JavaClassInfo ShapeManagerClassInfo; diff --git a/android/library/maply/jni/include/classInfo/Stickers_jni.h b/android/library/maply/jni/include/classInfo/Stickers_jni.h index 786128cf9a..a933525736 100644 --- a/android/library/maply/jni/include/classInfo/Stickers_jni.h +++ b/android/library/maply/jni/include/classInfo/Stickers_jni.h @@ -23,4 +23,4 @@ typedef JavaClassInfo SphericalChunkInfoClassInfo; typedef JavaClassInfo SphericalChunkClassInfo; -typedef JavaClassInfo StickerManagerClassInfo; +typedef JavaClassInfo StickerManagerClassInfo; diff --git a/android/library/maply/jni/include/classInfo/Vectors_jni.h b/android/library/maply/jni/include/classInfo/Vectors_jni.h index ffdde44803..4bff966328 100644 --- a/android/library/maply/jni/include/classInfo/Vectors_jni.h +++ b/android/library/maply/jni/include/classInfo/Vectors_jni.h @@ -30,12 +30,12 @@ typedef JavaClassInfo LoftedPolyInfoClassInfo; //typedef JavaClassInfo MapboxVectorTileParserClassInfo; // Construct a Java-side AttrDictionary wrapper -JNIEXPORT jobject JNICALL MakeAttrDictionary(JNIEnv *env,WhirlyKit::MutableDictionary_AndroidRef dict); -JNIEXPORT jobject JNICALL MakeAttrDictionaryEntry(JNIEnv *env,WhirlyKit::DictionaryEntry_AndroidRef dict); +JNIEXPORT jobject JNICALL MakeAttrDictionary(JNIEnv *env,const WhirlyKit::MutableDictionary_AndroidRef &dict); +JNIEXPORT jobject JNICALL MakeAttrDictionaryEntry(JNIEnv *env,const WhirlyKit::DictionaryEntry_AndroidRef &dict); // Construct a Java-side Vector Object JNIEXPORT jobject JNICALL MakeVectorObject(JNIEnv *env,WhirlyKit::VectorObject *vec); -JNIEXPORT jobject JNICALL MakeVectorObject(JNIEnv *env,WhirlyKit::VectorObjectRef vec); +JNIEXPORT jobject JNICALL MakeVectorObject(JNIEnv *env,const WhirlyKit::VectorObjectRef &vec); // This one takes the classInfo object which skips a lookup -JNIEXPORT jobject JNICALL MakeVectorObjectWrapper(JNIEnv *env,VectorObjectClassInfo *classInfo,WhirlyKit::VectorObjectRef vecObj); \ No newline at end of file +JNIEXPORT jobject JNICALL MakeVectorObjectWrapper(JNIEnv *env,VectorObjectClassInfo *classInfo,const WhirlyKit::VectorObjectRef &vecObj); \ No newline at end of file diff --git a/android/library/maply/jni/include/generated/com_mousebird_maply_BaseInfo.h b/android/library/maply/jni/include/generated/com_mousebird_maply_BaseInfo.h index eba6339e4b..f3ee3ef6d5 100644 --- a/android/library/maply/jni/include/generated/com_mousebird_maply_BaseInfo.h +++ b/android/library/maply/jni/include/generated/com_mousebird_maply_BaseInfo.h @@ -122,10 +122,10 @@ JNIEXPORT jboolean JNICALL Java_com_mousebird_maply_BaseInfo_getEnable /* * Class: com_mousebird_maply_BaseInfo * Method: setFade - * Signature: (F)V + * Signature: (D)V */ JNIEXPORT void JNICALL Java_com_mousebird_maply_BaseInfo_setFade - (JNIEnv *, jobject, jfloat); + (JNIEnv *, jobject, jdouble); /* * Class: com_mousebird_maply_BaseInfo diff --git a/android/library/maply/jni/include/generated/com_mousebird_maply_ComponentManager.h b/android/library/maply/jni/include/generated/com_mousebird_maply_ComponentManager.h index 6155e5a9c0..169a747294 100644 --- a/android/library/maply/jni/include/generated/com_mousebird_maply_ComponentManager.h +++ b/android/library/maply/jni/include/generated/com_mousebird_maply_ComponentManager.h @@ -10,10 +10,10 @@ extern "C" { /* * Class: com_mousebird_maply_ComponentManager * Method: addComponentObject - * Signature: (Lcom/mousebird/maply/ComponentObject;)V + * Signature: (Lcom/mousebird/maply/ComponentObject;Lcom/mousebird/maply/ChangeSet;)V */ JNIEXPORT void JNICALL Java_com_mousebird_maply_ComponentManager_addComponentObject - (JNIEnv *, jobject, jobject); + (JNIEnv *, jobject, jobject, jobject); /* * Class: com_mousebird_maply_ComponentManager diff --git a/android/library/maply/jni/include/generated/com_mousebird_maply_InternalMarker.h b/android/library/maply/jni/include/generated/com_mousebird_maply_InternalMarker.h index bbf38efa8e..3666c0f5fc 100644 --- a/android/library/maply/jni/include/generated/com_mousebird_maply_InternalMarker.h +++ b/android/library/maply/jni/include/generated/com_mousebird_maply_InternalMarker.h @@ -111,6 +111,13 @@ JNIEXPORT void JNICALL Java_com_mousebird_maply_InternalMarker_setPeriod JNIEXPORT void JNICALL Java_com_mousebird_maply_InternalMarker_setVertexAttributes (JNIEnv *, jobject, jobjectArray); +/* + * Class: com_mousebird_maply_InternalMarker + * Method: setLayoutImportance + * Signature: (F)V + */ +JNIEXPORT void JNICALL Java_com_mousebird_maply_InternalMarker_setLayoutImportance + (JNIEnv *env, jobject obj, jfloat importance); /* * Class: com_mousebird_maply_InternalMarker * Method: nativeInit diff --git a/android/library/maply/jni/include/generated/com_mousebird_maply_MapboxVectorStyleSet.h b/android/library/maply/jni/include/generated/com_mousebird_maply_MapboxVectorStyleSet.h index ef9d50f6e4..4d62f80f11 100644 --- a/android/library/maply/jni/include/generated/com_mousebird_maply_MapboxVectorStyleSet.h +++ b/android/library/maply/jni/include/generated/com_mousebird_maply_MapboxVectorStyleSet.h @@ -23,6 +23,22 @@ JNIEXPORT void JNICALL Java_com_mousebird_maply_MapboxVectorStyleSet_setArealSha JNIEXPORT jint JNICALL Java_com_mousebird_maply_MapboxVectorStyleSet_backgroundColorForZoomNative (JNIEnv *, jobject, jdouble); +/* + * Class: com_mousebird_maply_MapboxVectorStyleSet + * Method: getZoomSlot + * Signature: ()I + */ +JNIEXPORT jint JNICALL Java_com_mousebird_maply_MapboxVectorStyleSet_getZoomSlot + (JNIEnv *, jobject); + +/* + * Class: com_mousebird_maply_MapboxVectorStyleSet + * Method: setZoomSlot + * Signature: (I)V + */ +JNIEXPORT void JNICALL Java_com_mousebird_maply_MapboxVectorStyleSet_setZoomSlot + (JNIEnv *, jobject, jint); + /* * Class: com_mousebird_maply_MapboxVectorStyleSet * Method: initialise diff --git a/android/library/maply/jni/include/generated/com_mousebird_maply_QuadImageFrameLoader.h b/android/library/maply/jni/include/generated/com_mousebird_maply_QuadImageFrameLoader.h index dff27ff4da..c6f59b3866 100644 --- a/android/library/maply/jni/include/generated/com_mousebird_maply_QuadImageFrameLoader.h +++ b/android/library/maply/jni/include/generated/com_mousebird_maply_QuadImageFrameLoader.h @@ -10,11 +10,19 @@ extern "C" { /* * Class: com_mousebird_maply_QuadImageFrameLoader * Method: setLoadFrameModeNative - * Signature: (I)V + * Signature: (I)Z */ -JNIEXPORT void JNICALL Java_com_mousebird_maply_QuadImageFrameLoader_setLoadFrameModeNative +JNIEXPORT jboolean JNICALL Java_com_mousebird_maply_QuadImageFrameLoader_setLoadFrameModeNative (JNIEnv *, jobject, jint); +/* + * Class: com_mousebird_maply_QuadImageFrameLoader + * Method: updatePriorities + * Signature: ()V + */ +JNIEXPORT void JNICALL Java_com_mousebird_maply_QuadImageFrameLoader_updatePriorities + (JNIEnv *, jobject); + /* * Class: com_mousebird_maply_QuadImageFrameLoader * Method: addFocus @@ -34,9 +42,9 @@ JNIEXPORT jint JNICALL Java_com_mousebird_maply_QuadImageFrameLoader_getNumFocus /* * Class: com_mousebird_maply_QuadImageFrameLoader * Method: setCurrentImageNative - * Signature: (ID)V + * Signature: (ID)Z */ -JNIEXPORT void JNICALL Java_com_mousebird_maply_QuadImageFrameLoader_setCurrentImageNative +JNIEXPORT jboolean JNICALL Java_com_mousebird_maply_QuadImageFrameLoader_setCurrentImageNative (JNIEnv *, jobject, jint, jdouble); /* @@ -63,6 +71,14 @@ JNIEXPORT void JNICALL Java_com_mousebird_maply_QuadImageFrameLoader_setRequireT JNIEXPORT void JNICALL Java_com_mousebird_maply_QuadImageFrameLoader_setRenderTargetIDNative (JNIEnv *, jobject, jint, jlong); +/* + * Class: com_mousebird_maply_QuadImageFrameLoader + * Method: setTextureSize + * Signature: (II)V + */ +JNIEXPORT void JNICALL Java_com_mousebird_maply_QuadImageFrameLoader_setTextureSize + (JNIEnv *, jobject, jint, jint); + /* * Class: com_mousebird_maply_QuadImageFrameLoader * Method: setShaderIDNative diff --git a/android/library/maply/jni/include/generated/com_mousebird_maply_QuadLoaderBase.h b/android/library/maply/jni/include/generated/com_mousebird_maply_QuadLoaderBase.h index aa1284a52b..061d4902d2 100644 --- a/android/library/maply/jni/include/generated/com_mousebird_maply_QuadLoaderBase.h +++ b/android/library/maply/jni/include/generated/com_mousebird_maply_QuadLoaderBase.h @@ -111,6 +111,22 @@ JNIEXPORT jint JNICALL Java_com_mousebird_maply_QuadLoaderBase_getGeneration JNIEXPORT void JNICALL Java_com_mousebird_maply_QuadLoaderBase_reloadNative (JNIEnv *, jobject, jobject); +/* + * Class: com_mousebird_maply_QuadLoaderBase + * Method: reloadAreaNative + * Signature: (Lcom/mousebird/maply/ChangeSet;[Lcom/mousebird/maply/Mbr;)V + */ +JNIEXPORT void JNICALL Java_com_mousebird_maply_QuadLoaderBase_reloadAreaNative + (JNIEnv *, jobject, jobject, jobjectArray); + +/* + * Class: com_mousebird_maply_QuadLoaderBase + * Method: getZoomSlot + * Signature: ()I + */ +JNIEXPORT jint JNICALL Java_com_mousebird_maply_QuadLoaderBase_getZoomSlot + (JNIEnv *, jobject); + /* * Class: com_mousebird_maply_QuadLoaderBase * Method: nativeInit diff --git a/android/library/maply/jni/include/generated/com_mousebird_maply_RawPNGImageLoaderInterpreter.h b/android/library/maply/jni/include/generated/com_mousebird_maply_RawPNGImageLoaderInterpreter.h new file mode 100644 index 0000000000..23670962cb --- /dev/null +++ b/android/library/maply/jni/include/generated/com_mousebird_maply_RawPNGImageLoaderInterpreter.h @@ -0,0 +1,53 @@ +/* DO NOT EDIT THIS FILE - it is machine generated */ +#include +/* Header for class com_mousebird_maply_RawPNGImageLoaderInterpreter */ + +#ifndef _Included_com_mousebird_maply_RawPNGImageLoaderInterpreter +#define _Included_com_mousebird_maply_RawPNGImageLoaderInterpreter +#ifdef __cplusplus +extern "C" { +#endif +/* + * Class: com_mousebird_maply_RawPNGImageLoaderInterpreter + * Method: addMappingFrom + * Signature: (II)V + */ +JNIEXPORT void JNICALL Java_com_mousebird_maply_RawPNGImageLoaderInterpreter_addMappingFrom + (JNIEnv *, jobject, jint, jint); + +/* + * Class: com_mousebird_maply_RawPNGImageLoaderInterpreter + * Method: dataForTileNative + * Signature: ([BLcom/mousebird/maply/LoaderReturn;)V + */ +JNIEXPORT void JNICALL Java_com_mousebird_maply_RawPNGImageLoaderInterpreter_dataForTileNative + (JNIEnv *, jobject, jbyteArray, jobject); + +/* + * Class: com_mousebird_maply_RawPNGImageLoaderInterpreter + * Method: nativeInit + * Signature: ()V + */ +JNIEXPORT void JNICALL Java_com_mousebird_maply_RawPNGImageLoaderInterpreter_nativeInit + (JNIEnv *, jclass); + +/* + * Class: com_mousebird_maply_RawPNGImageLoaderInterpreter + * Method: initialise + * Signature: ()V + */ +JNIEXPORT void JNICALL Java_com_mousebird_maply_RawPNGImageLoaderInterpreter_initialise + (JNIEnv *, jobject); + +/* + * Class: com_mousebird_maply_RawPNGImageLoaderInterpreter + * Method: dispose + * Signature: ()V + */ +JNIEXPORT void JNICALL Java_com_mousebird_maply_RawPNGImageLoaderInterpreter_dispose + (JNIEnv *, jobject); + +#ifdef __cplusplus +} +#endif +#endif diff --git a/android/library/maply/jni/include/generated/com_mousebird_maply_Scene.h b/android/library/maply/jni/include/generated/com_mousebird_maply_Scene.h index c637c1fcdc..c03e18e4a7 100644 --- a/android/library/maply/jni/include/generated/com_mousebird_maply_Scene.h +++ b/android/library/maply/jni/include/generated/com_mousebird_maply_Scene.h @@ -26,10 +26,10 @@ JNIEXPORT void JNICALL Java_com_mousebird_maply_Scene_removeShaderProgram /* * Class: com_mousebird_maply_Scene * Method: addRenderTargetNative - * Signature: (JIIJZZFFFF)V + * Signature: (JIIJZFZFFFF)V */ JNIEXPORT void JNICALL Java_com_mousebird_maply_Scene_addRenderTargetNative - (JNIEnv *, jobject, jlong, jint, jint, jlong, jboolean, jboolean, jfloat, jfloat, jfloat, jfloat); + (JNIEnv *, jobject, jlong, jint, jint, jlong, jboolean, jfloat, jboolean, jfloat, jfloat, jfloat, jfloat); /* * Class: com_mousebird_maply_Scene diff --git a/android/library/maply/jni/include/generated/com_mousebird_maply_VectorObject.h b/android/library/maply/jni/include/generated/com_mousebird_maply_VectorObject.h index 399027527c..65691b926e 100644 --- a/android/library/maply/jni/include/generated/com_mousebird_maply_VectorObject.h +++ b/android/library/maply/jni/include/generated/com_mousebird_maply_VectorObject.h @@ -263,6 +263,14 @@ JNIEXPORT jboolean JNICALL Java_com_mousebird_maply_VectorObject_fromGeoJSON JNIEXPORT jboolean JNICALL Java_com_mousebird_maply_VectorObject_fromShapeFile (JNIEnv *, jobject, jstring); +/* + * Class: com_mousebird_maply_VectorObject + * Method: canSplit + * Signature: ()Z + */ +JNIEXPORT jboolean JNICALL Java_com_mousebird_maply_VectorObject_canSplit + (JNIEnv *, jobject); + /* * Class: com_mousebird_maply_VectorObject * Method: deepCopyNative diff --git a/android/library/maply/jni/include/generated/com_mousebird_maply_VectorStyleSettings.h b/android/library/maply/jni/include/generated/com_mousebird_maply_VectorStyleSettings.h index 0dee3a7008..f431b3b743 100644 --- a/android/library/maply/jni/include/generated/com_mousebird_maply_VectorStyleSettings.h +++ b/android/library/maply/jni/include/generated/com_mousebird_maply_VectorStyleSettings.h @@ -113,10 +113,10 @@ JNIEXPORT jboolean JNICALL Java_com_mousebird_maply_VectorStyleSettings_getUseZo /* * Class: com_mousebird_maply_VectorStyleSettings - * Method: setUseZoomLabels + * Method: setUseZoomLevels * Signature: (Z)V */ -JNIEXPORT void JNICALL Java_com_mousebird_maply_VectorStyleSettings_setUseZoomLabels +JNIEXPORT void JNICALL Java_com_mousebird_maply_VectorStyleSettings_setUseZoomLevels (JNIEnv *, jobject, jboolean); /* @@ -314,10 +314,10 @@ JNIEXPORT void JNICALL Java_com_mousebird_maply_VectorStyleSettings_setZBufferWr /* * Class: com_mousebird_maply_VectorStyleSettings * Method: initialise - * Signature: ()V + * Signature: (D)V */ JNIEXPORT void JNICALL Java_com_mousebird_maply_VectorStyleSettings_initialise - (JNIEnv *, jobject); + (JNIEnv *, jobject, jdouble); /* * Class: com_mousebird_maply_VectorStyleSettings diff --git a/android/library/maply/jni/src/base/BaseInfo_jni.cpp b/android/library/maply/jni/src/base/BaseInfo_jni.cpp index d51bcaf3a2..ded3023681 100644 --- a/android/library/maply/jni/src/base/BaseInfo_jni.cpp +++ b/android/library/maply/jni/src/base/BaseInfo_jni.cpp @@ -26,6 +26,7 @@ using namespace WhirlyKit; template<> BaseInfoClassInfo *BaseInfoClassInfo::classInfoObj = NULL; +extern "C" JNIEXPORT void JNICALL Java_com_mousebird_maply_BaseInfo_nativeInit (JNIEnv *env, jclass cls) { @@ -33,6 +34,7 @@ JNIEXPORT void JNICALL Java_com_mousebird_maply_BaseInfo_nativeInit } +extern "C" JNIEXPORT void JNICALL Java_com_mousebird_maply_BaseInfo_setVisibleHeightRange (JNIEnv *env, jobject obj, jdouble minVis, jdouble maxVis) { @@ -51,6 +53,7 @@ JNIEXPORT void JNICALL Java_com_mousebird_maply_BaseInfo_setVisibleHeightRange } } +extern "C" JNIEXPORT jdouble JNICALL Java_com_mousebird_maply_BaseInfo_getVisibleHeightMin (JNIEnv *env, jobject obj) { @@ -70,6 +73,7 @@ JNIEXPORT jdouble JNICALL Java_com_mousebird_maply_BaseInfo_getVisibleHeightMin return DrawVisibleInvalid; } +extern "C" JNIEXPORT jdouble JNICALL Java_com_mousebird_maply_BaseInfo_getVisibleHeightMax (JNIEnv *env, jobject obj) { @@ -89,6 +93,7 @@ JNIEXPORT jdouble JNICALL Java_com_mousebird_maply_BaseInfo_getVisibleHeightMax return DrawVisibleInvalid; } +extern "C" JNIEXPORT void JNICALL Java_com_mousebird_maply_BaseInfo_setViewDistRange (JNIEnv *env, jobject obj, jdouble viewMin, jdouble viewMax) { @@ -107,6 +112,7 @@ JNIEXPORT void JNICALL Java_com_mousebird_maply_BaseInfo_setViewDistRange } } +extern "C" JNIEXPORT jdouble JNICALL Java_com_mousebird_maply_BaseInfo_getViewDistRangeMin (JNIEnv *env, jobject obj) { @@ -126,6 +132,7 @@ JNIEXPORT jdouble JNICALL Java_com_mousebird_maply_BaseInfo_getViewDistRangeMin return DrawVisibleInvalid; } +extern "C" JNIEXPORT jdouble JNICALL Java_com_mousebird_maply_BaseInfo_getViewDistRangeMax (JNIEnv *env, jobject obj) { @@ -145,6 +152,7 @@ JNIEXPORT jdouble JNICALL Java_com_mousebird_maply_BaseInfo_getViewDistRangeMax return DrawVisibleInvalid; } +extern "C" JNIEXPORT void JNICALL Java_com_mousebird_maply_BaseInfo_setViewerCenter (JNIEnv *env, jobject obj, jobject ptObj) { @@ -164,6 +172,7 @@ JNIEXPORT void JNICALL Java_com_mousebird_maply_BaseInfo_setViewerCenter } } +extern "C" JNIEXPORT jobject JNICALL Java_com_mousebird_maply_BaseInfo_getViewerCenter (JNIEnv *env, jobject obj) { @@ -184,6 +193,7 @@ JNIEXPORT jobject JNICALL Java_com_mousebird_maply_BaseInfo_getViewerCenter return NULL; } +extern "C" JNIEXPORT void JNICALL Java_com_mousebird_maply_BaseInfo_setDrawOffset (JNIEnv *env, jobject obj, jfloat drawOffset) { @@ -201,6 +211,7 @@ JNIEXPORT void JNICALL Java_com_mousebird_maply_BaseInfo_setDrawOffset } } +extern "C" JNIEXPORT jdouble JNICALL Java_com_mousebird_maply_BaseInfo_getDrawOffset (JNIEnv *env, jobject obj) { @@ -220,6 +231,7 @@ JNIEXPORT jdouble JNICALL Java_com_mousebird_maply_BaseInfo_getDrawOffset return 0.0; } +extern "C" JNIEXPORT void JNICALL Java_com_mousebird_maply_BaseInfo_setDrawPriority (JNIEnv *env, jobject obj, jint drawPriority) { @@ -237,6 +249,7 @@ JNIEXPORT void JNICALL Java_com_mousebird_maply_BaseInfo_setDrawPriority } } +extern "C" JNIEXPORT jint JNICALL Java_com_mousebird_maply_BaseInfo_getDrawPriority (JNIEnv *env, jobject obj) { @@ -256,6 +269,7 @@ JNIEXPORT jint JNICALL Java_com_mousebird_maply_BaseInfo_getDrawPriority return 0; } +extern "C" JNIEXPORT void JNICALL Java_com_mousebird_maply_BaseInfo_setEnable (JNIEnv *env, jobject obj, jboolean enable) { @@ -273,6 +287,7 @@ JNIEXPORT void JNICALL Java_com_mousebird_maply_BaseInfo_setEnable } } +extern "C" JNIEXPORT jboolean JNICALL Java_com_mousebird_maply_BaseInfo_getEnable (JNIEnv *env, jobject obj) { @@ -292,6 +307,7 @@ JNIEXPORT jboolean JNICALL Java_com_mousebird_maply_BaseInfo_getEnable return false; } +extern "C" JNIEXPORT void JNICALL Java_com_mousebird_maply_BaseInfo_setFade (JNIEnv *env, jobject obj, jdouble fade) { @@ -309,6 +325,7 @@ JNIEXPORT void JNICALL Java_com_mousebird_maply_BaseInfo_setFade } } +extern "C" JNIEXPORT void JNICALL Java_com_mousebird_maply_BaseInfo_setFadeInOut (JNIEnv *env, jobject obj, jdouble fadeIn, jdouble fadeOut) { @@ -327,16 +344,17 @@ JNIEXPORT void JNICALL Java_com_mousebird_maply_BaseInfo_setFadeInOut } } -JNIEXPORT jfloat JNICALL Java_com_mousebird_maply_BaseInfo_getFadeIn - (JNIEnv *env, jobject obj) +extern "C" +JNIEXPORT jfloat JNICALL Java_com_mousebird_maply_BaseInfo_getFadeIn(JNIEnv *env, jobject obj) { try { BaseInfoClassInfo *classInfo = BaseInfoClassInfo::getClassInfo(); BaseInfoRef *info = classInfo->getObject(env,obj); - if (!info) - return 0.0; - return (*info)->fadeIn; + if (info) + { + return (jfloat)(*info)->fadeIn; + } } catch (...) { @@ -346,16 +364,17 @@ JNIEXPORT jfloat JNICALL Java_com_mousebird_maply_BaseInfo_getFadeIn return 0.0; } -JNIEXPORT jfloat JNICALL Java_com_mousebird_maply_BaseInfo_getFadeOut - (JNIEnv *env, jobject obj) +extern "C" +JNIEXPORT jfloat JNICALL Java_com_mousebird_maply_BaseInfo_getFadeOut(JNIEnv *env, jobject obj) { try { BaseInfoClassInfo *classInfo = BaseInfoClassInfo::getClassInfo(); BaseInfoRef *info = classInfo->getObject(env,obj); - if (!info) - return 0.0; - return (*info)->fadeOut; + if (info) + { + return (jfloat)(*info)->fadeOut; + } } catch (...) { @@ -365,6 +384,7 @@ JNIEXPORT jfloat JNICALL Java_com_mousebird_maply_BaseInfo_getFadeOut return 0.0; } +extern "C" JNIEXPORT void JNICALL Java_com_mousebird_maply_BaseInfo_setFadeOutTime (JNIEnv *env, jobject obj, jdouble fadeOutTime) { @@ -387,6 +407,7 @@ JNIEXPORT void JNICALL Java_com_mousebird_maply_BaseInfo_setFadeOutTime * Method: getFadeOutTime * Signature: ()D */ +extern "C" JNIEXPORT jdouble JNICALL Java_com_mousebird_maply_BaseInfo_getFadeOutTime (JNIEnv *env, jobject obj) { @@ -406,6 +427,7 @@ JNIEXPORT jdouble JNICALL Java_com_mousebird_maply_BaseInfo_getFadeOutTime return 0.0; } +extern "C" JNIEXPORT void JNICALL Java_com_mousebird_maply_BaseInfo_setEnableTimes (JNIEnv *env, jobject obj, jdouble startEnable, jdouble endEnable) { @@ -425,6 +447,7 @@ JNIEXPORT void JNICALL Java_com_mousebird_maply_BaseInfo_setEnableTimes } } +extern "C" JNIEXPORT void JNICALL Java_com_mousebird_maply_BaseInfo_setShaderID (JNIEnv *env, jobject obj, jlong shaderID) { @@ -443,6 +466,7 @@ JNIEXPORT void JNICALL Java_com_mousebird_maply_BaseInfo_setShaderID } } +extern "C" JNIEXPORT jlong JNICALL Java_com_mousebird_maply_BaseInfo_getShaderID (JNIEnv *env, jobject obj) { @@ -463,6 +487,7 @@ JNIEXPORT jlong JNICALL Java_com_mousebird_maply_BaseInfo_getShaderID return EmptyIdentity; } +extern "C" JNIEXPORT void JNICALL Java_com_mousebird_maply_BaseInfo_setZBufferRead (JNIEnv *env, jobject obj, jboolean val) { @@ -481,6 +506,7 @@ JNIEXPORT void JNICALL Java_com_mousebird_maply_BaseInfo_setZBufferRead } } +extern "C" JNIEXPORT void JNICALL Java_com_mousebird_maply_BaseInfo_setZBufferWrite (JNIEnv *env, jobject obj, jboolean val) { @@ -499,6 +525,7 @@ JNIEXPORT void JNICALL Java_com_mousebird_maply_BaseInfo_setZBufferWrite } } +extern "C" JNIEXPORT void JNICALL Java_com_mousebird_maply_BaseInfo_setRenderTargetNative (JNIEnv *env, jobject obj, jlong targetID) { diff --git a/android/library/maply/jni/src/base/VertexAttribute_jni.cpp b/android/library/maply/jni/src/base/VertexAttribute_jni.cpp index a17264650b..b02835f4f4 100644 --- a/android/library/maply/jni/src/base/VertexAttribute_jni.cpp +++ b/android/library/maply/jni/src/base/VertexAttribute_jni.cpp @@ -1,9 +1,8 @@ -/* - * VertexAttribute_jni.cpp +/* VertexAttribute_jni.cpp * WhirlyGlobeLib * * Created by Steve Gifford on 3/17/16. - * Copyright 2011-2016 mousebird consulting + * Copyright 2011-2021 mousebird consulting * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -15,7 +14,6 @@ * 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. - * */ #import "Base_jni.h" @@ -24,21 +22,20 @@ using namespace Eigen; using namespace WhirlyKit; -template<> SingleVertexAttributeClassInfo *SingleVertexAttributeClassInfo::classInfoObj = NULL; +template<> SingleVertexAttributeClassInfo *SingleVertexAttributeClassInfo::classInfoObj = nullptr; -JNIEXPORT void JNICALL Java_com_mousebird_maply_VertexAttribute_nativeInit -(JNIEnv *env, jclass cls) +extern "C" +JNIEXPORT void JNICALL Java_com_mousebird_maply_VertexAttribute_nativeInit(JNIEnv *env, jclass cls) { SingleVertexAttributeClassInfo::getClassInfo(env,cls); } -JNIEXPORT void JNICALL Java_com_mousebird_maply_VertexAttribute_initialise -(JNIEnv *env, jobject obj) +extern "C" +JNIEXPORT void JNICALL Java_com_mousebird_maply_VertexAttribute_initialise(JNIEnv *env, jobject obj) { try { SingleVertexAttribute *vertAttr = new SingleVertexAttribute(); - SingleVertexAttributeClassInfo::getClassInfo()->setHandle(env,obj,vertAttr); } catch (...) @@ -47,38 +44,30 @@ JNIEXPORT void JNICALL Java_com_mousebird_maply_VertexAttribute_initialise } } -JNIEXPORT void JNICALL Java_com_mousebird_maply_VertexAttribute_setName -(JNIEnv *env, jobject obj, jstring str) +extern "C" +JNIEXPORT void JNICALL Java_com_mousebird_maply_VertexAttribute_setName(JNIEnv *env, jobject obj, jstring str) { - try - { - SingleVertexAttributeClassInfo *classInfo = SingleVertexAttributeClassInfo::getClassInfo(); - SingleVertexAttribute *inst = classInfo->getObject(env,obj); - if (!inst) - return; - JavaString jStr(env,str); - inst->nameID = StringIndexer::getStringID(jStr.cStr); - } - catch (...) - { + try { + auto classInfo = SingleVertexAttributeClassInfo::getClassInfo(); + if (SingleVertexAttribute *inst = classInfo->getObject(env,obj)) { + JavaString jStr(env, str); + inst->nameID = StringIndexer::getStringID(jStr.getCString()); + } + } catch (...) { __android_log_print(ANDROID_LOG_VERBOSE, "Maply", "Crash in VertexAttribute::setName()"); } } static std::mutex disposeMutex; -JNIEXPORT void JNICALL Java_com_mousebird_maply_VertexAttribute_dispose -(JNIEnv *env, jobject obj) +extern "C" +JNIEXPORT void JNICALL Java_com_mousebird_maply_VertexAttribute_dispose(JNIEnv *env, jobject obj) { try { - SingleVertexAttributeClassInfo *classInfo = SingleVertexAttributeClassInfo::getClassInfo(); - { - std::lock_guard lock(disposeMutex); - SingleVertexAttribute *inst = classInfo->getObject(env,obj); - if (!inst) - return; + const auto classInfo = SingleVertexAttributeClassInfo::getClassInfo(); + std::lock_guard lock(disposeMutex); + if (auto inst = classInfo->getObject(env,obj)) { delete inst; - classInfo->clearHandle(env,obj); } } catch (...) { @@ -86,82 +75,77 @@ JNIEXPORT void JNICALL Java_com_mousebird_maply_VertexAttribute_dispose } } -JNIEXPORT void JNICALL Java_com_mousebird_maply_VertexAttribute_setFloat -(JNIEnv *env, jobject obj, jfloat val) +extern "C" +JNIEXPORT void JNICALL Java_com_mousebird_maply_VertexAttribute_setFloat(JNIEnv *env, jobject obj, jfloat val) { try { - SingleVertexAttributeClassInfo *classInfo = SingleVertexAttributeClassInfo::getClassInfo(); - SingleVertexAttribute *inst = classInfo->getObject(env,obj); - if (!inst) - return; - inst->type = BDFloatType; - inst->data.floatVal = val; + const auto classInfo = SingleVertexAttributeClassInfo::getClassInfo(); + if (auto inst = classInfo->getObject(env,obj)) { + inst->type = BDFloatType; + inst->data.floatVal = val; + } } catch (...) { __android_log_print(ANDROID_LOG_VERBOSE, "Maply", "Crash in VertexAttribute::setFloat()"); } } -JNIEXPORT void JNICALL Java_com_mousebird_maply_VertexAttribute_setInt -(JNIEnv *env, jobject obj, jint val) +extern "C" +JNIEXPORT void JNICALL Java_com_mousebird_maply_VertexAttribute_setInt(JNIEnv *env, jobject obj, jint val) { try { - SingleVertexAttributeClassInfo *classInfo = SingleVertexAttributeClassInfo::getClassInfo(); - SingleVertexAttribute *inst = classInfo->getObject(env,obj); - if (!inst) - return; - inst->type = BDIntType; - inst->data.intVal = val; + const auto classInfo = SingleVertexAttributeClassInfo::getClassInfo(); + if (auto inst = classInfo->getObject(env,obj)) { + inst->type = BDIntType; + inst->data.intVal = val; + } } catch (...) { __android_log_print(ANDROID_LOG_VERBOSE, "Maply", "Crash in VertexAttribute::setInt()"); } } -JNIEXPORT void JNICALL Java_com_mousebird_maply_VertexAttribute_setVec2 -(JNIEnv *env, jobject obj, jfloat x, jfloat y) +extern "C" +JNIEXPORT void JNICALL Java_com_mousebird_maply_VertexAttribute_setVec2(JNIEnv *env, jobject obj, jfloat x, jfloat y) { try { - SingleVertexAttributeClassInfo *classInfo = SingleVertexAttributeClassInfo::getClassInfo(); - SingleVertexAttribute *inst = classInfo->getObject(env,obj); - if (!inst) - return; - inst->type = BDFloat2Type; - inst->data.vec2[0] = x; - inst->data.vec2[1] = y; + const auto classInfo = SingleVertexAttributeClassInfo::getClassInfo(); + if (auto inst = classInfo->getObject(env,obj)) { + inst->type = BDFloat2Type; + inst->data.vec2[0] = x; + inst->data.vec2[1] = y; + } } catch (...) { __android_log_print(ANDROID_LOG_VERBOSE, "Maply", "Crash in VertexAttribute::setVec2()"); } } -JNIEXPORT void JNICALL Java_com_mousebird_maply_VertexAttribute_setVec3 -(JNIEnv *env, jobject obj, jfloat x, jfloat y, jfloat z) +extern "C" +JNIEXPORT void JNICALL Java_com_mousebird_maply_VertexAttribute_setVec3(JNIEnv *env, jobject obj, jfloat x, jfloat y, jfloat z) { try { - SingleVertexAttributeClassInfo *classInfo = SingleVertexAttributeClassInfo::getClassInfo(); - SingleVertexAttribute *inst = classInfo->getObject(env,obj); - if (!inst) - return; - inst->type = BDFloat3Type; - inst->data.vec3[0] = x; - inst->data.vec3[1] = y; - inst->data.vec3[2] = z; + const auto classInfo = SingleVertexAttributeClassInfo::getClassInfo(); + if (auto inst = classInfo->getObject(env,obj)) { + inst->type = BDFloat3Type; + inst->data.vec3[0] = x; + inst->data.vec3[1] = y; + inst->data.vec3[2] = z; + } } catch (...) { __android_log_print(ANDROID_LOG_VERBOSE, "Maply", "Crash in VertexAttribute::setVec3()"); } } -JNIEXPORT void JNICALL Java_com_mousebird_maply_VertexAttribute_setColor -(JNIEnv *env, jobject obj, jint r, jint g, jint b, jint a) +extern "C" +JNIEXPORT void JNICALL Java_com_mousebird_maply_VertexAttribute_setColor(JNIEnv *env, jobject obj, jint r, jint g, jint b, jint a) { try { - SingleVertexAttributeClassInfo *classInfo = SingleVertexAttributeClassInfo::getClassInfo(); - SingleVertexAttribute *inst = classInfo->getObject(env,obj); - if (!inst) - return; - inst->type = BDChar4Type; - inst->data.color[0] = r; - inst->data.color[1] = g; - inst->data.color[2] = b; - inst->data.color[3] = a; + const auto classInfo = SingleVertexAttributeClassInfo::getClassInfo(); + if (auto inst = classInfo->getObject(env,obj)) { + inst->type = BDChar4Type; + inst->data.color[0] = r; + inst->data.color[1] = g; + inst->data.color[2] = b; + inst->data.color[3] = a; + } } catch (...) { __android_log_print(ANDROID_LOG_VERBOSE, "Maply", "Crash in VertexAttribute::setColor()"); } diff --git a/android/library/maply/jni/src/billboard/BillboardManager_jni.cpp b/android/library/maply/jni/src/billboard/BillboardManager_jni.cpp index e3b37a1143..dfd00e35f6 100644 --- a/android/library/maply/jni/src/billboard/BillboardManager_jni.cpp +++ b/android/library/maply/jni/src/billboard/BillboardManager_jni.cpp @@ -26,7 +26,7 @@ using namespace WhirlyKit; -typedef JavaClassInfo BillboardManagerClassInfo; +typedef JavaClassInfo BillboardManagerClassInfo; template<> BillboardManagerClassInfo *BillboardManagerClassInfo::classInfoObj = NULL; JNIEXPORT void JNICALL Java_com_mousebird_maply_BillboardManager_nativeInit @@ -44,8 +44,8 @@ JNIEXPORT void JNICALL Java_com_mousebird_maply_BillboardManager_initialise Scene *scene = SceneClassInfo::getClassInfo()->getObject(env, sceneObj); if (!scene) return; - BillboardManager *billManager = dynamic_cast(scene->getManager(kWKBillboardManager)); - classInfo->setHandle(env, obj, billManager); + BillboardManagerRef billManager = std::dynamic_pointer_cast(scene->getManager(kWKBillboardManager)); + classInfo->setHandle(env, obj, new BillboardManagerRef(billManager)); } catch (...) { @@ -61,6 +61,9 @@ JNIEXPORT void JNICALL Java_com_mousebird_maply_BillboardManager_dispose try { BillboardManagerClassInfo *classInfo = BillboardManagerClassInfo::getClassInfo(); + BillboardManagerRef *billManager = classInfo->getObject(env, obj); + if (billManager) + delete billManager; classInfo->clearHandle(env, obj); } catch (...) @@ -75,7 +78,7 @@ JNIEXPORT jlong JNICALL Java_com_mousebird_maply_BillboardManager_addBillboards try { BillboardManagerClassInfo *classInfo = BillboardManagerClassInfo::getClassInfo(); - BillboardManager *billManager = classInfo->getObject(env, obj); + BillboardManagerRef *billManager = classInfo->getObject(env, obj); BillboardInfoRef *billInfo = BillboardInfoClassInfo::getClassInfo()->getObject(env, infoObj); ChangeSetRef *changeSet = ChangeSetClassInfo::getClassInfo()->getObject(env, changeObj); if (!billManager || !billInfo || !changeSet) @@ -95,15 +98,15 @@ JNIEXPORT jlong JNICALL Java_com_mousebird_maply_BillboardManager_addBillboards { Program *prog = NULL; if ((*billInfo)->orient == BillboardInfo::Orient::Eye) - prog = billManager->getScene()->findProgramByName(MaplyBillboardEyeShader); + prog = (*billManager)->getScene()->findProgramByName(MaplyBillboardEyeShader); else - prog = billManager->getScene()->findProgramByName(MaplyBillboardGroundShader); + prog = (*billManager)->getScene()->findProgramByName(MaplyBillboardGroundShader); if (prog) (*billInfo)->programID = prog->getId(); } - SimpleIdentity billId = billManager->addBillboards(bills, *(*billInfo), *(changeSet->get())); + SimpleIdentity billId = (*billManager)->addBillboards(bills, *(*billInfo), *(changeSet->get())); return billId; } @@ -120,7 +123,7 @@ JNIEXPORT void JNICALL Java_com_mousebird_maply_BillboardManager_enableBillboard try { BillboardManagerClassInfo *classInfo = BillboardManagerClassInfo::getClassInfo(); - BillboardManager *inst = classInfo->getObject(env, obj); + BillboardManagerRef *inst = classInfo->getObject(env, obj); ChangeSetRef *changeSet = ChangeSetClassInfo::getClassInfo()->getObject(env, changeObj); if (!inst || !changeSet) return; @@ -131,7 +134,7 @@ JNIEXPORT void JNICALL Java_com_mousebird_maply_BillboardManager_enableBillboard { idSet.insert(ids.rawLong[ii]); } - inst->enableBillboards(idSet, enable, *(changeSet->get())); + (*inst)->enableBillboards(idSet, enable, *(changeSet->get())); } catch (...) { @@ -145,7 +148,7 @@ JNIEXPORT void JNICALL Java_com_mousebird_maply_BillboardManager_removeBillboard try { BillboardManagerClassInfo *classInfo = BillboardManagerClassInfo::getClassInfo(); - BillboardManager *inst = classInfo->getObject(env, obj); + BillboardManagerRef *inst = classInfo->getObject(env, obj); ChangeSetRef *changeSet = ChangeSetClassInfo::getClassInfo()->getObject(env, changeObj); if (!inst || !changeSet) return; @@ -156,7 +159,7 @@ JNIEXPORT void JNICALL Java_com_mousebird_maply_BillboardManager_removeBillboard { idSet.insert(ids.rawLong[ii]); } - inst->removeBillboards(idSet, *(changeSet->get())); + (*inst)->removeBillboards(idSet, *(changeSet->get())); } catch (...) { diff --git a/android/library/maply/jni/src/billboard/ScreenObject_jni.cpp b/android/library/maply/jni/src/billboard/ScreenObject_jni.cpp index 1d48b4df8d..2e32238757 100644 --- a/android/library/maply/jni/src/billboard/ScreenObject_jni.cpp +++ b/android/library/maply/jni/src/billboard/ScreenObject_jni.cpp @@ -1,9 +1,8 @@ -/* - * ScreenObject_jni.cpp +/* ScreenObject_jni.cpp * WhirlyGlobeLib * * Created by jmnavarro - * Copyright 2011-2016 mousebird consulting + * Copyright 2011-2021 mousebird consulting * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -15,26 +14,25 @@ * 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. - * */ #import "Billboard_jni.h" #import "Geometry_jni.h" #import "com_mousebird_maply_ScreenObject.h" #import -template<> ScreenObjectClassInfo *ScreenObjectClassInfo::classInfoObj = NULL; +template<> ScreenObjectClassInfo *ScreenObjectClassInfo::classInfoObj = nullptr; using namespace Eigen; using namespace WhirlyKit; -JNIEXPORT void JNICALL Java_com_mousebird_maply_ScreenObject_nativeInit -(JNIEnv *env, jclass cls) +extern "C" +JNIEXPORT void JNICALL Java_com_mousebird_maply_ScreenObject_nativeInit(JNIEnv *env, jclass cls) { ScreenObjectClassInfo::getClassInfo(env, cls); } -JNIEXPORT void JNICALL Java_com_mousebird_maply_ScreenObject_initialise -(JNIEnv *env, jobject obj) +extern "C" +JNIEXPORT void JNICALL Java_com_mousebird_maply_ScreenObject_initialise(JNIEnv *env, jobject obj) { try { @@ -50,8 +48,8 @@ JNIEXPORT void JNICALL Java_com_mousebird_maply_ScreenObject_initialise static std::mutex disposeMutex; -JNIEXPORT void JNICALL Java_com_mousebird_maply_ScreenObject_dispose -(JNIEnv *env, jobject obj) +extern "C" +JNIEXPORT void JNICALL Java_com_mousebird_maply_ScreenObject_dispose(JNIEnv *env, jobject obj) { try { @@ -71,6 +69,7 @@ JNIEXPORT void JNICALL Java_com_mousebird_maply_ScreenObject_dispose } } +extern "C" JNIEXPORT void JNICALL Java_com_mousebird_maply_ScreenObject_addPoly (JNIEnv *env, jobject obj, jobject polyObj) { @@ -91,6 +90,7 @@ JNIEXPORT void JNICALL Java_com_mousebird_maply_ScreenObject_addPoly } } +extern "C" JNIEXPORT void JNICALL Java_com_mousebird_maply_ScreenObject_addString (JNIEnv *env, jobject obj, jobject stringObj) { @@ -111,6 +111,7 @@ JNIEXPORT void JNICALL Java_com_mousebird_maply_ScreenObject_addString } } +extern "C" JNIEXPORT void JNICALL Java_com_mousebird_maply_ScreenObject_addTextureNative (JNIEnv *env, jobject obj, jlong texID, jfloat r, jfloat g, jfloat b, jfloat a, jfloat width, jfloat height) { @@ -148,17 +149,19 @@ JNIEXPORT void JNICALL Java_com_mousebird_maply_ScreenObject_addTextureNative } } +extern "C" JNIEXPORT void JNICALL Java_com_mousebird_maply_ScreenObject_addScreenObject (JNIEnv *env, jobject obj, jobject screenObj) { try { ScreenObjectClassInfo *classInfo = ScreenObjectClassInfo::getClassInfo(); - ScreenObject *inst = classInfo->getObject(env, obj); - ScreenObject *newScreen = classInfo->getObject(env, screenObj); - if (!inst || !newScreen) - return; - inst = newScreen; + if (auto inst = classInfo->getObject(env, obj)) + if (auto newScreen = classInfo->getObject(env, screenObj)) + { + inst->polys.insert(inst->polys.end(), newScreen->polys.begin(), newScreen->polys.end()); + inst->strings.insert(inst->strings.end(), newScreen->strings.begin(), newScreen->strings.end()); + } } catch (...) { @@ -166,6 +169,7 @@ JNIEXPORT void JNICALL Java_com_mousebird_maply_ScreenObject_addScreenObject } } +extern "C" JNIEXPORT void JNICALL Java_com_mousebird_maply_ScreenObject_getSizeNative (JNIEnv *env, jobject obj, jobject llObj, jobject urObj) { @@ -206,8 +210,8 @@ JNIEXPORT void JNICALL Java_com_mousebird_maply_ScreenObject_getSizeNative } } -JNIEXPORT void JNICALL Java_com_mousebird_maply_ScreenObject_transform -(JNIEnv *env, jobject obj, jobject matObj) +extern "C" +JNIEXPORT void JNICALL Java_com_mousebird_maply_ScreenObject_transform(JNIEnv *env, jobject obj, jobject matObj) { try { diff --git a/android/library/maply/jni/src/billboard/SimplePoly_jni.cpp b/android/library/maply/jni/src/billboard/SimplePoly_jni.cpp index 6c6bcc82aa..4934d1e9fe 100644 --- a/android/library/maply/jni/src/billboard/SimplePoly_jni.cpp +++ b/android/library/maply/jni/src/billboard/SimplePoly_jni.cpp @@ -53,9 +53,7 @@ JNIEXPORT void JNICALL Java_com_mousebird_maply_SimplePoly_initialise__JFFFF_3Lc { SimplePolyClassInfo *classInfo = SimplePolyClassInfo::getClassInfo(); SimplePoly *inst = new SimplePoly(); - if (!inst) - return; - + inst->texID = texID; //color diff --git a/android/library/maply/jni/src/components/ComponentManager_jni.cpp b/android/library/maply/jni/src/components/ComponentManager_jni.cpp index ed34dc6433..c09e4501a7 100644 --- a/android/library/maply/jni/src/components/ComponentManager_jni.cpp +++ b/android/library/maply/jni/src/components/ComponentManager_jni.cpp @@ -40,9 +40,9 @@ JNIEXPORT void JNICALL Java_com_mousebird_maply_ComponentManager_initialise Scene *scene = SceneClassInfo::getClassInfo()->getObject(env, sceneObj); if (!scene) return; - ComponentManager_Android *compManager = dynamic_cast(scene->getManager(kWKComponentManager)); + ComponentManager_AndroidRef compManager = std::dynamic_pointer_cast(scene->getManager(kWKComponentManager)); compManager->setupJNI(env,obj); - ComponentManagerClassInfo::getClassInfo()->setHandle(env,obj,compManager); + ComponentManagerClassInfo::getClassInfo()->setHandle(env,obj,new ComponentManager_AndroidRef(compManager)); } catch (...) { @@ -60,9 +60,10 @@ JNIEXPORT void JNICALL Java_com_mousebird_maply_ComponentManager_dispose ComponentManagerClassInfo *classInfo = ComponentManagerClassInfo::getClassInfo(); { std::lock_guard lock(disposeMutex); - ComponentManager_Android *compManager = classInfo->getObject(env,obj); + ComponentManager_AndroidRef *compManager = classInfo->getObject(env,obj); if (compManager) { - compManager->clearJNI(env); + (*compManager)->clearJNI(env); + delete compManager; } classInfo->clearHandle(env,obj); } @@ -74,16 +75,17 @@ JNIEXPORT void JNICALL Java_com_mousebird_maply_ComponentManager_dispose } JNIEXPORT void JNICALL Java_com_mousebird_maply_ComponentManager_addComponentObject - (JNIEnv *env, jobject obj, jobject compObjObj) + (JNIEnv *env, jobject obj, jobject compObjObj, jobject changeSetObj) { try { - ComponentManager *compManager = ComponentManagerClassInfo::getClassInfo()->getObject(env,obj); + ComponentManager_AndroidRef *compManager = ComponentManagerClassInfo::getClassInfo()->getObject(env,obj); ComponentObjectRef *compObj = ComponentObjectRefClassInfo::getClassInfo()->getObject(env,compObjObj); - if (!compManager || !compObj) + ChangeSetRef *changeSet = ChangeSetClassInfo::getClassInfo()->getObject(env,changeSetObj); + if (!compManager || !compObj || !changeSet) return; - compManager->addComponentObject(*compObj); + (*compManager)->addComponentObject(*compObj, **changeSet); } catch (...) { @@ -96,11 +98,11 @@ JNIEXPORT jboolean JNICALL Java_com_mousebird_maply_ComponentManager_hasComponen { try { - ComponentManager *compManager = ComponentManagerClassInfo::getClassInfo()->getObject(env,obj); + ComponentManager_AndroidRef *compManager = ComponentManagerClassInfo::getClassInfo()->getObject(env,obj); if (!compManager) return false; - return compManager->hasComponentObject(compObjID); + return (*compManager)->hasComponentObject(compObjID); } catch (...) { @@ -115,7 +117,7 @@ JNIEXPORT void JNICALL Java_com_mousebird_maply_ComponentManager_removeComponent { try { - ComponentManager *compManager = ComponentManagerClassInfo::getClassInfo()->getObject(env,obj); + ComponentManager_AndroidRef *compManager = ComponentManagerClassInfo::getClassInfo()->getObject(env,obj); ChangeSetRef *changeSet = ChangeSetClassInfo::getClassInfo()->getObject(env,changeSetObj); if (!compManager || !changeSet) return; @@ -131,7 +133,7 @@ JNIEXPORT void JNICALL Java_com_mousebird_maply_ComponentManager_removeComponent } PlatformInfo_Android platformInfo(env); - compManager->removeComponentObjects(&platformInfo,compObjIDs,*(changeSet->get())); + (*compManager)->removeComponentObjects(&platformInfo,compObjIDs,*(changeSet->get())); } catch (...) { @@ -144,7 +146,7 @@ JNIEXPORT void JNICALL Java_com_mousebird_maply_ComponentManager_enableComponent { try { - ComponentManager *compManager = ComponentManagerClassInfo::getClassInfo()->getObject(env,obj); + ComponentManager_AndroidRef *compManager = ComponentManagerClassInfo::getClassInfo()->getObject(env,obj); ChangeSetRef *changeSet = ChangeSetClassInfo::getClassInfo()->getObject(env,changeSetObj); if (!compManager || !changeSet) return; @@ -159,7 +161,7 @@ JNIEXPORT void JNICALL Java_com_mousebird_maply_ComponentManager_enableComponent compObjIDs.insert((*compObj)->getId()); } - compManager->enableComponentObjects(compObjIDs,enable,*(changeSet->get())); + (*compManager)->enableComponentObjects(compObjIDs,enable,*(changeSet->get())); } catch (...) { diff --git a/android/library/maply/jni/src/coordSystem/Proj4CoordSystem_jni.cpp b/android/library/maply/jni/src/coordSystem/Proj4CoordSystem_jni.cpp index ad2a8b2047..2cb6319573 100644 --- a/android/library/maply/jni/src/coordSystem/Proj4CoordSystem_jni.cpp +++ b/android/library/maply/jni/src/coordSystem/Proj4CoordSystem_jni.cpp @@ -1,9 +1,8 @@ -/* - * Proj4CoordSystem_jni.cpp +/* Proj4CoordSystem_jni.cpp * WhirlyGlobeLib * * Created by Steve Gifford on 2/13/16. - * Copyright 2011-2016 mousebird consulting + * Copyright 2011-2021 mousebird consulting * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -15,7 +14,6 @@ * 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. - * */ #import @@ -24,18 +22,18 @@ using namespace WhirlyKit; -JNIEXPORT void JNICALL Java_com_mousebird_maply_Proj4CoordSystem_nativeInit -(JNIEnv *env, jclass cls) +extern "C" +JNIEXPORT void JNICALL Java_com_mousebird_maply_Proj4CoordSystem_nativeInit(JNIEnv *env, jclass cls) { } -JNIEXPORT void JNICALL Java_com_mousebird_maply_Proj4CoordSystem_initialise -(JNIEnv *env, jobject obj, jstring str) +extern "C" +JNIEXPORT void JNICALL Java_com_mousebird_maply_Proj4CoordSystem_initialise(JNIEnv *env, jobject obj, jstring str) { try { JavaString jstr(env,str); - Proj4CoordSystem *coordSystem = new Proj4CoordSystem((std::string)jstr.cStr); + Proj4CoordSystem *coordSystem = new Proj4CoordSystem(jstr.getCString()); CoordSystemRefClassInfo::getClassInfo()->setHandle(env,obj,new CoordSystemRef(coordSystem)); } catch (...) diff --git a/android/library/maply/jni/src/formats/MapboxVectorStyleSet_jni.cpp b/android/library/maply/jni/src/formats/MapboxVectorStyleSet_jni.cpp index 658022ba3c..6890f3e014 100644 --- a/android/library/maply/jni/src/formats/MapboxVectorStyleSet_jni.cpp +++ b/android/library/maply/jni/src/formats/MapboxVectorStyleSet_jni.cpp @@ -1,9 +1,8 @@ -/* - * MapboxVectorStyleSet_jni.cpp +/* MapboxVectorStyleSet_jni.cpp * WhirlyGlobeLib * * Created by sjg - * Copyright 2011-2020 mousebird consulting + * Copyright 2011-2021 mousebird consulting * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -15,7 +14,6 @@ * 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. - * */ #import @@ -26,14 +24,15 @@ using namespace WhirlyKit; -template<> MapboxVectorStyleSetClassInfo *MapboxVectorStyleSetClassInfo::classInfoObj = NULL; +template<> MapboxVectorStyleSetClassInfo *MapboxVectorStyleSetClassInfo::classInfoObj = nullptr; -JNIEXPORT void JNICALL Java_com_mousebird_maply_MapboxVectorStyleSet_nativeInit -(JNIEnv *env, jclass cls) +extern "C" +JNIEXPORT void JNICALL Java_com_mousebird_maply_MapboxVectorStyleSet_nativeInit(JNIEnv *env, jclass cls) { MapboxVectorStyleSetClassInfo::getClassInfo(env,cls); } +extern "C" JNIEXPORT void JNICALL Java_com_mousebird_maply_MapboxVectorStyleSet_initialise (JNIEnv *env, jobject obj, jobject sceneObj, jobject coordSysObj, jobject settingsObj, jobject attrObj) { @@ -49,25 +48,33 @@ JNIEXPORT void JNICALL Java_com_mousebird_maply_MapboxVectorStyleSet_initialise VectorStyleSettingsImplRef settings; if (settingsObj) { settings = *(VectorStyleSettingsClassInfo::getClassInfo()->getObject(env,settingsObj)); - } else - settings = VectorStyleSettingsImplRef(new VectorStyleSettingsImpl(1.0)); - MapboxVectorStyleSetImpl_AndroidRef *inst = new MapboxVectorStyleSetImpl_AndroidRef(new MapboxVectorStyleSetImpl_Android(scene,(*coordSystem).get(),settings)); + } else { + settings = std::make_shared(1.0); + } + + auto inst = new MapboxVectorStyleSetImpl_AndroidRef(new MapboxVectorStyleSetImpl_Android(scene,(*coordSystem).get(),settings)); // Need a pointer to this JNIEnv for low level parsing callbacks PlatformInfo_Android threadInst(env); - bool success = (*inst)->parse(&threadInst,*attrDict); (*inst)->thisObj = env->NewGlobalRef(obj); MapboxVectorStyleSetClassInfo::getClassInfo()->setHandle(env,obj,inst); + + const bool success = (*inst)->parse(&threadInst,*attrDict); + if (!success) + { + __android_log_print(ANDROID_LOG_WARN, "Maply", "Failed to parse attrs in MapboxVectorStyleSet::initialise()"); + } } catch (...) { - __android_log_print(ANDROID_LOG_VERBOSE, "Maply", "Crash in MapboxVectorStyleSet::initialise()"); + __android_log_print(ANDROID_LOG_ERROR, "Maply", "Crash in MapboxVectorStyleSet::initialise()"); } } static std::mutex disposeMutex; +extern "C" JNIEXPORT void JNICALL Java_com_mousebird_maply_MapboxVectorStyleSet_dispose (JNIEnv *env, jobject obj) { @@ -81,6 +88,7 @@ JNIEXPORT void JNICALL Java_com_mousebird_maply_MapboxVectorStyleSet_dispose return; (*inst)->cleanup(env); env->DeleteGlobalRef((*inst)->thisObj); + (*inst)->thisObj = nullptr; delete inst; } @@ -92,6 +100,7 @@ JNIEXPORT void JNICALL Java_com_mousebird_maply_MapboxVectorStyleSet_dispose } } +extern "C" JNIEXPORT jint JNICALL Java_com_mousebird_maply_MapboxVectorStyleSet_backgroundColorForZoomNative (JNIEnv *env, jobject obj, jdouble zoom) { @@ -118,6 +127,54 @@ JNIEXPORT jint JNICALL Java_com_mousebird_maply_MapboxVectorStyleSet_backgroundC return 0; } +/* + * Class: com_mousebird_maply_MapboxVectorStyleSet + * Method: getZoomSlot + * Signature: ()I + */ +extern "C" +JNIEXPORT jint JNICALL Java_com_mousebird_maply_MapboxVectorStyleSet_getZoomSlot(JNIEnv *env, jobject obj) +{ + try + { + auto classInfo = MapboxVectorStyleSetClassInfo::getClassInfo(); + auto instPtr = classInfo->getObject(env,obj); + if (auto inst = instPtr ? *instPtr : nullptr) + { + return inst->getZoomSlot(); + } + } + catch (...) + { + __android_log_print(ANDROID_LOG_ERROR, "Maply", "Crash in MapboxVectorStyleSet::getZoomSlot()"); + } + return -1; +} + +/* + * Class: com_mousebird_maply_MapboxVectorStyleSet + * Method: setZoomSlot + * Signature: (I)V + */ +extern "C" +JNIEXPORT void JNICALL Java_com_mousebird_maply_MapboxVectorStyleSet_setZoomSlot(JNIEnv *env, jobject obj, jint slot) +{ + try + { + auto classInfo = MapboxVectorStyleSetClassInfo::getClassInfo(); + auto instPtr = classInfo->getObject(env,obj); + if (auto inst = instPtr ? *instPtr : nullptr) + { + inst->setZoomSlot(slot); + } + } + catch (...) + { + __android_log_print(ANDROID_LOG_ERROR, "Maply", "Crash in MapboxVectorStyleSet::setZoomSlot()"); + } +} + +extern "C" JNIEXPORT void JNICALL Java_com_mousebird_maply_MapboxVectorStyleSet_setArealShaderNative (JNIEnv *env, jobject obj, jlong shaderID) { diff --git a/android/library/maply/jni/src/formats/MapboxVectorTileParser_jni.cpp b/android/library/maply/jni/src/formats/MapboxVectorTileParser_jni.cpp index 1532312c01..096a5669aa 100644 --- a/android/library/maply/jni/src/formats/MapboxVectorTileParser_jni.cpp +++ b/android/library/maply/jni/src/formats/MapboxVectorTileParser_jni.cpp @@ -1,9 +1,8 @@ -/* - * MapboxVectorTileParser_jni.cpp +/* MapboxVectorTileParser_jni.cpp * WhirlyGlobeLib * * Created by sjg - * Copyright 2011-2016 mousebird consulting + * Copyright 2011-2021 mousebird consulting * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -15,7 +14,6 @@ * 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. - * */ #import @@ -27,16 +25,17 @@ using namespace WhirlyKit; -template<> MapboxVectorTileParserClassInfo *MapboxVectorTileParserClassInfo::classInfoObj = NULL; +template<> MapboxVectorTileParserClassInfo *MapboxVectorTileParserClassInfo::classInfoObj = nullptr; -JNIEXPORT void JNICALL Java_com_mousebird_maply_MapboxVectorTileParser_nativeInit - (JNIEnv *env, jclass cls) +extern "C" +JNIEXPORT void JNICALL Java_com_mousebird_maply_MapboxVectorTileParser_nativeInit(JNIEnv *env, jclass cls) { MapboxVectorTileParserClassInfo::getClassInfo(env,cls); } +extern "C" JNIEXPORT void JNICALL Java_com_mousebird_maply_MapboxVectorTileParser_initialise -(JNIEnv *env, jobject obj, jobject vecStyleObj, jboolean isMapboxStyle) + (JNIEnv *env, jobject obj, jobject vecStyleObj, jboolean isMapboxStyle) { try { @@ -66,8 +65,8 @@ JNIEXPORT void JNICALL Java_com_mousebird_maply_MapboxVectorTileParser_initialis static std::mutex disposeMutex; -JNIEXPORT void JNICALL Java_com_mousebird_maply_MapboxVectorTileParser_dispose -(JNIEnv *env, jobject obj) +extern "C" +JNIEXPORT void JNICALL Java_com_mousebird_maply_MapboxVectorTileParser_dispose(JNIEnv *env, jobject obj) { try { @@ -88,15 +87,16 @@ JNIEXPORT void JNICALL Java_com_mousebird_maply_MapboxVectorTileParser_dispose } } +extern "C" JNIEXPORT void JNICALL Java_com_mousebird_maply_MapboxVectorTileParser_setLocalCoords - (JNIEnv *env, jobject obj, jboolean localCoords) + (JNIEnv *env, jobject obj, jboolean localCoords) { try { MapboxVectorTileParser *inst = MapboxVectorTileParserClassInfo::getClassInfo()->getObject( env, obj); if (!obj) return; - inst->localCoords = localCoords; + inst->setLocalCoords(localCoords); } catch (...) { __android_log_print(ANDROID_LOG_VERBOSE, "Maply", @@ -104,8 +104,9 @@ JNIEXPORT void JNICALL Java_com_mousebird_maply_MapboxVectorTileParser_setLocalC } } +extern "C" JNIEXPORT jboolean JNICALL Java_com_mousebird_maply_MapboxVectorTileParser_parseDataNative - (JNIEnv *env, jobject obj, jbyteArray data, jobject vecTileDataObj) + (JNIEnv *env, jobject obj, jbyteArray data, jobject vecTileDataObj) { try { @@ -115,21 +116,31 @@ JNIEXPORT jboolean JNICALL Java_com_mousebird_maply_MapboxVectorTileParser_parse return false; // Notify the style delegate of the new environment so it can make Java calls if need be - MapboxVectorStyleSetImpl_AndroidRef theStyleDelegate = std::dynamic_pointer_cast(inst->styleDelegate); - - if (theStyleDelegate) + const auto style = inst->getStyleDelegate(); + if (const auto theStyleDelegate = dynamic_cast(style.get())) { theStyleDelegate->setupMethods(env); + } // Need a pointer to this JNIEnv for low level parsing callbacks PlatformInfo_Android platformInfo(env); // Copy data into a temporary buffer (must we?) - int len = env->GetArrayLength(data); + const int len = env->GetArrayLength(data); jbyte *rawData = env->GetByteArrayElements(data,NULL); - RawDataWrapper rawDataWrap(rawData,len,false); - bool ret = inst->parse(&platformInfo,&rawDataWrap,(*tileData).get(),NULL); - if (rawData) - env->ReleaseByteArrayElements(data,rawData,JNI_ABORT); + bool ret = false; + try { + RawDataWrapper rawDataWrap(rawData, len, false); + ret = inst->parse(&platformInfo, &rawDataWrap, (*tileData).get(), NULL); + } catch (...) { + // since we can't `finally{}`, handle and re-throw. todo: RAII wrapper + if (rawData) { + env->ReleaseByteArrayElements(data, rawData, JNI_ABORT); + } + throw; + } + if (rawData) { + env->ReleaseByteArrayElements(data, rawData, JNI_ABORT); + } return ret; } diff --git a/android/library/maply/jni/src/formats/VectorStyleSettings_jni.cpp b/android/library/maply/jni/src/formats/VectorStyleSettings_jni.cpp index 5a8bf8db98..408623d9c3 100644 --- a/android/library/maply/jni/src/formats/VectorStyleSettings_jni.cpp +++ b/android/library/maply/jni/src/formats/VectorStyleSettings_jni.cpp @@ -1,9 +1,8 @@ -/* - * VectorStyleSettings_jni.cpp +/* VectorStyleSettings_jni.cpp * WhirlyGlobeLib * * Created by sjg - * Copyright 2011-2020 mousebird consulting + * Copyright 2011-2021 mousebird consulting * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -15,7 +14,6 @@ * 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. - * */ #import @@ -24,660 +22,681 @@ using namespace WhirlyKit; using namespace Eigen; -template<> VectorStyleSettingsClassInfo *VectorStyleSettingsClassInfo::classInfoObj = NULL; +template<> VectorStyleSettingsClassInfo *VectorStyleSettingsClassInfo::classInfoObj = nullptr; -JNIEXPORT void JNICALL Java_com_mousebird_maply_VectorStyleSettings_nativeInit - (JNIEnv *env, jclass cls) +extern "C" +JNIEXPORT void JNICALL Java_com_mousebird_maply_VectorStyleSettings_nativeInit(JNIEnv *env, jclass cls) { VectorStyleSettingsClassInfo::getClassInfo(env,cls); } -JNIEXPORT void JNICALL Java_com_mousebird_maply_VectorStyleSettings_initialise - (JNIEnv *env, jobject obj) +extern "C" +JNIEXPORT void JNICALL Java_com_mousebird_maply_VectorStyleSettings_initialise(JNIEnv *env, jobject obj, jdouble scale) { try { - VectorStyleSettingsImplRef *inst = new VectorStyleSettingsImplRef(new VectorStyleSettingsImpl(1.0)); + auto inst = new VectorStyleSettingsImplRef(new VectorStyleSettingsImpl(scale)); VectorStyleSettingsClassInfo::getClassInfo()->setHandle(env,obj,inst); } catch (...) { - __android_log_print(ANDROID_LOG_VERBOSE, "Maply", "Crash in VectorStyleSettings::initialise()"); + __android_log_print(ANDROID_LOG_ERROR, "Maply", "Crash in VectorStyleSettings::initialise()"); } } static std::mutex disposeMutex; -JNIEXPORT void JNICALL Java_com_mousebird_maply_VectorStyleSettings_dispose - (JNIEnv *env, jobject obj) +extern "C" +JNIEXPORT void JNICALL Java_com_mousebird_maply_VectorStyleSettings_dispose(JNIEnv *env, jobject obj) { try { - VectorStyleSettingsClassInfo *classInfo = VectorStyleSettingsClassInfo::getClassInfo(); + const auto classInfo = VectorStyleSettingsClassInfo::getClassInfo(); { std::lock_guard lock(disposeMutex); - VectorStyleSettingsImplRef *inst = classInfo->getObject(env,obj); - if (!inst) - return; + auto inst = classInfo->getObject(env,obj); delete inst; } - classInfo->clearHandle(env,obj); } catch (...) { - __android_log_print(ANDROID_LOG_VERBOSE, "Maply", "Crash in VectorStyleSettings::dispose()"); + __android_log_print(ANDROID_LOG_ERROR, "Maply", "Crash in VectorStyleSettings::dispose()"); } } -JNIEXPORT jdouble JNICALL Java_com_mousebird_maply_VectorStyleSettings_getLineScale -(JNIEnv *env, jobject obj) +extern "C" +JNIEXPORT jdouble JNICALL Java_com_mousebird_maply_VectorStyleSettings_getLineScale(JNIEnv *env, jobject obj) { try { - VectorStyleSettingsImplRef *inst = VectorStyleSettingsClassInfo::getClassInfo()->getObject(env,obj); - if (inst) + if (const auto inst = VectorStyleSettingsClassInfo::get(env,obj)) + { return (*inst)->lineScale; + } } catch (...) { - __android_log_print(ANDROID_LOG_VERBOSE, "Maply", "Crash in VectorStyleSettings::getLineScale()"); + __android_log_print(ANDROID_LOG_ERROR, "Maply", "Crash in VectorStyleSettings::getLineScale()"); } return 1.0; } -JNIEXPORT void JNICALL Java_com_mousebird_maply_VectorStyleSettings_setLineScale -(JNIEnv *env, jobject obj, jdouble scale) +extern "C" +JNIEXPORT void JNICALL Java_com_mousebird_maply_VectorStyleSettings_setLineScale(JNIEnv *env, jobject obj, jdouble scale) { try { - VectorStyleSettingsImplRef *inst = VectorStyleSettingsClassInfo::getClassInfo()->getObject(env,obj); - if (inst) + if (auto inst = VectorStyleSettingsClassInfo::get(env,obj)) + { (*inst)->lineScale = scale; + } } catch (...) { - __android_log_print(ANDROID_LOG_VERBOSE, "Maply", "Crash in VectorStyleSettings::setLineScale()"); + __android_log_print(ANDROID_LOG_ERROR, "Maply", "Crash in VectorStyleSettings::setLineScale()"); } } -JNIEXPORT jdouble JNICALL Java_com_mousebird_maply_VectorStyleSettings_getTextScale -(JNIEnv *env, jobject obj) +extern "C" +JNIEXPORT jdouble JNICALL Java_com_mousebird_maply_VectorStyleSettings_getTextScale(JNIEnv *env, jobject obj) { try { - VectorStyleSettingsImplRef *inst = VectorStyleSettingsClassInfo::getClassInfo()->getObject(env,obj); - if (inst) + if (auto inst = VectorStyleSettingsClassInfo::get(env,obj)) + { return (*inst)->textScale; + } } catch (...) { - __android_log_print(ANDROID_LOG_VERBOSE, "Maply", "Crash in VectorStyleSettings::getTextScale()"); + __android_log_print(ANDROID_LOG_ERROR, "Maply", "Crash in VectorStyleSettings::getTextScale()"); } return 1.0; } -JNIEXPORT void JNICALL Java_com_mousebird_maply_VectorStyleSettings_setTextScale -(JNIEnv *env, jobject obj, jdouble scale) +extern "C" +JNIEXPORT void JNICALL Java_com_mousebird_maply_VectorStyleSettings_setTextScale(JNIEnv *env, jobject obj, jdouble scale) { try { - VectorStyleSettingsImplRef *inst = VectorStyleSettingsClassInfo::getClassInfo()->getObject(env,obj); - if (inst) + if (auto inst = VectorStyleSettingsClassInfo::get(env,obj)) { (*inst)->textScale = scale; + } } catch (...) { - __android_log_print(ANDROID_LOG_VERBOSE, "Maply", "Crash in VectorStyleSettings::setTextScale()"); + __android_log_print(ANDROID_LOG_ERROR, "Maply", "Crash in VectorStyleSettings::setTextScale()"); } } -JNIEXPORT jdouble JNICALL Java_com_mousebird_maply_VectorStyleSettings_getMarkerScale -(JNIEnv *env, jobject obj) +extern "C" +JNIEXPORT jdouble JNICALL Java_com_mousebird_maply_VectorStyleSettings_getMarkerScale(JNIEnv *env, jobject obj) { try { - VectorStyleSettingsImplRef *inst = VectorStyleSettingsClassInfo::getClassInfo()->getObject(env,obj); - if (inst) + if (auto inst = VectorStyleSettingsClassInfo::get(env,obj)) + { return (*inst)->markerScale; + } } catch (...) { - __android_log_print(ANDROID_LOG_VERBOSE, "Maply", "Crash in VectorStyleSettings::getMarkerScale()"); + __android_log_print(ANDROID_LOG_ERROR, "Maply", "Crash in VectorStyleSettings::getMarkerScale()"); } return 1.0; } -JNIEXPORT void JNICALL Java_com_mousebird_maply_VectorStyleSettings_setMarkerScale -(JNIEnv *env, jobject obj, jdouble scale) +extern "C" +JNIEXPORT void JNICALL Java_com_mousebird_maply_VectorStyleSettings_setMarkerScale(JNIEnv *env, jobject obj, jdouble scale) { try { - VectorStyleSettingsImplRef *inst = VectorStyleSettingsClassInfo::getClassInfo()->getObject(env,obj); - if (inst) + if (auto inst = VectorStyleSettingsClassInfo::get(env,obj)) + { (*inst)->markerScale = scale; + } } catch (...) { - __android_log_print(ANDROID_LOG_VERBOSE, "Maply", "Crash in VectorStyleSettings::setMarkerScale()"); + __android_log_print(ANDROID_LOG_ERROR, "Maply", "Crash in VectorStyleSettings::setMarkerScale()"); } } -JNIEXPORT jdouble JNICALL Java_com_mousebird_maply_VectorStyleSettings_getMarkerImportance -(JNIEnv *env, jobject obj) +extern "C" +JNIEXPORT jdouble JNICALL Java_com_mousebird_maply_VectorStyleSettings_getMarkerImportance(JNIEnv *env, jobject obj) { try { - VectorStyleSettingsImplRef *inst = VectorStyleSettingsClassInfo::getClassInfo()->getObject(env,obj); - if (inst) + if (auto inst = VectorStyleSettingsClassInfo::get(env,obj)) + { return (*inst)->markerImportance; + } } catch (...) { - __android_log_print(ANDROID_LOG_VERBOSE, "Maply", "Crash in VectorStyleSettings::getMarkerImportance()"); + __android_log_print(ANDROID_LOG_ERROR, "Maply", "Crash in VectorStyleSettings::getMarkerImportance()"); } return 1.0; } -JNIEXPORT void JNICALL Java_com_mousebird_maply_VectorStyleSettings_setMarkerImportance -(JNIEnv *env, jobject obj, jdouble import) +extern "C" +JNIEXPORT void JNICALL Java_com_mousebird_maply_VectorStyleSettings_setMarkerImportance(JNIEnv *env, jobject obj, jdouble import) { try { - VectorStyleSettingsImplRef *inst = VectorStyleSettingsClassInfo::getClassInfo()->getObject(env,obj); - if (inst) + if (auto inst = VectorStyleSettingsClassInfo::get(env,obj)) + { (*inst)->markerImportance = import; + } } catch (...) { - __android_log_print(ANDROID_LOG_VERBOSE, "Maply", "Crash in VectorStyleSettings::setMarkerImportance()"); + __android_log_print(ANDROID_LOG_ERROR, "Maply", "Crash in VectorStyleSettings::setMarkerImportance()"); } } -JNIEXPORT jdouble JNICALL Java_com_mousebird_maply_VectorStyleSettings_getMarkerSize -(JNIEnv *env, jobject obj) +extern "C" +JNIEXPORT jdouble JNICALL Java_com_mousebird_maply_VectorStyleSettings_getMarkerSize(JNIEnv *env, jobject obj) { try { - VectorStyleSettingsImplRef *inst = VectorStyleSettingsClassInfo::getClassInfo()->getObject(env,obj); - if (inst) + if (auto inst = VectorStyleSettingsClassInfo::get(env,obj)) + { return (*inst)->markerScale; + } } catch (...) { - __android_log_print(ANDROID_LOG_VERBOSE, "Maply", "Crash in VectorStyleSettings::getMarkerSize()"); + __android_log_print(ANDROID_LOG_ERROR, "Maply", "Crash in VectorStyleSettings::getMarkerSize()"); } - return 1.0; } -JNIEXPORT void JNICALL Java_com_mousebird_maply_VectorStyleSettings_setMarkerSize -(JNIEnv *env, jobject obj, jdouble size) +extern "C" +JNIEXPORT void JNICALL Java_com_mousebird_maply_VectorStyleSettings_setMarkerSize(JNIEnv *env, jobject obj, jdouble size) { try { - VectorStyleSettingsImplRef *inst = VectorStyleSettingsClassInfo::getClassInfo()->getObject(env,obj); - if (inst) + if (auto inst = VectorStyleSettingsClassInfo::get(env,obj)) + { (*inst)->markerSize = size; + } } catch (...) { - __android_log_print(ANDROID_LOG_VERBOSE, "Maply", "Crash in VectorStyleSettings::setMarkerSize()"); + __android_log_print(ANDROID_LOG_ERROR, "Maply", "Crash in VectorStyleSettings::setMarkerSize()"); } } -JNIEXPORT jdouble JNICALL Java_com_mousebird_maply_VectorStyleSettings_getLabelImportance -(JNIEnv *env, jobject obj) +extern "C" +JNIEXPORT jdouble JNICALL Java_com_mousebird_maply_VectorStyleSettings_getLabelImportance(JNIEnv *env, jobject obj) { try { - VectorStyleSettingsImplRef *inst = VectorStyleSettingsClassInfo::getClassInfo()->getObject(env,obj); - if (inst) + if (auto inst = VectorStyleSettingsClassInfo::get(env,obj)) + { return (*inst)->labelImportance; + } } catch (...) { - __android_log_print(ANDROID_LOG_VERBOSE, "Maply", "Crash in VectorStyleSettings::getLabelImportance()"); + __android_log_print(ANDROID_LOG_ERROR, "Maply", "Crash in VectorStyleSettings::getLabelImportance()"); } - return 1.0; } -JNIEXPORT void JNICALL Java_com_mousebird_maply_VectorStyleSettings_setLabelImportance -(JNIEnv *env, jobject obj, jdouble import) +extern "C" +JNIEXPORT void JNICALL Java_com_mousebird_maply_VectorStyleSettings_setLabelImportance(JNIEnv *env, jobject obj, jdouble import) { try { - VectorStyleSettingsImplRef *inst = VectorStyleSettingsClassInfo::getClassInfo()->getObject(env,obj); - if (inst) + if (auto inst = VectorStyleSettingsClassInfo::get(env,obj)) + { (*inst)->labelImportance = import; + } } catch (...) { - __android_log_print(ANDROID_LOG_VERBOSE, "Maply", "Crash in VectorStyleSettings::setMarkerSize()"); + __android_log_print(ANDROID_LOG_ERROR, "Maply", "Crash in VectorStyleSettings::setMarkerSize()"); } } -JNIEXPORT jboolean JNICALL Java_com_mousebird_maply_VectorStyleSettings_getUseZoomLevels -(JNIEnv *env, jobject obj) +extern "C" +JNIEXPORT jboolean JNICALL Java_com_mousebird_maply_VectorStyleSettings_getUseZoomLevels(JNIEnv *env, jobject obj) { try { - VectorStyleSettingsImplRef *inst = VectorStyleSettingsClassInfo::getClassInfo()->getObject(env,obj); - if (inst) + if (auto inst = VectorStyleSettingsClassInfo::get(env,obj)) + { return (*inst)->useZoomLevels; + } } catch (...) { - __android_log_print(ANDROID_LOG_VERBOSE, "Maply", "Crash in VectorStyleSettings::getUseZoomLevels()"); + __android_log_print(ANDROID_LOG_ERROR, "Maply", "Crash in VectorStyleSettings::getUseZoomLevels()"); } - return false; } -JNIEXPORT void JNICALL Java_com_mousebird_maply_VectorStyleSettings_setUseZoomLabels -(JNIEnv *env, jobject obj, jboolean zoomLevels) +extern "C" +JNIEXPORT void JNICALL Java_com_mousebird_maply_VectorStyleSettings_setUseZoomLevels(JNIEnv *env, jobject obj, jboolean zoomLevels) { try { - VectorStyleSettingsImplRef *inst = VectorStyleSettingsClassInfo::getClassInfo()->getObject(env,obj); - if (inst) + if (auto inst = VectorStyleSettingsClassInfo::get(env,obj)) + { (*inst)->useZoomLevels = zoomLevels; + } } catch (...) { - __android_log_print(ANDROID_LOG_VERBOSE, "Maply", "Crash in VectorStyleSettings::setUseZoomLabels()"); + __android_log_print(ANDROID_LOG_ERROR, "Maply", "Crash in VectorStyleSettings::setUseZoomLevels()"); } } -JNIEXPORT jstring JNICALL Java_com_mousebird_maply_VectorStyleSettings_getUuidField -(JNIEnv *env, jobject obj) +extern "C" +JNIEXPORT jstring JNICALL Java_com_mousebird_maply_VectorStyleSettings_getUuidField(JNIEnv *env, jobject obj) { try { - VectorStyleSettingsImplRef *inst = VectorStyleSettingsClassInfo::getClassInfo()->getObject(env,obj); - if (inst) - return env->NewStringUTF((*inst)->uuidField.c_str()); + auto inst = VectorStyleSettingsClassInfo::get(env,obj); + return inst ? env->NewStringUTF((*inst)->uuidField.c_str()) : nullptr; } catch (...) { - __android_log_print(ANDROID_LOG_VERBOSE, "Maply", "Crash in VectorStyleSettings::getUuidField()"); + __android_log_print(ANDROID_LOG_ERROR, "Maply", "Crash in VectorStyleSettings::getUuidField()"); } - - return NULL; + return nullptr; } -JNIEXPORT void JNICALL Java_com_mousebird_maply_VectorStyleSettings_setUuidField -(JNIEnv *env, jobject obj, jstring fieldStr) +extern "C" +JNIEXPORT void JNICALL Java_com_mousebird_maply_VectorStyleSettings_setUuidField(JNIEnv *env, jobject obj, jstring fieldStr) { try { - VectorStyleSettingsImplRef *inst = VectorStyleSettingsClassInfo::getClassInfo()->getObject(env,obj); - if (inst) { - JavaString jStr(env,fieldStr); - (*inst)->uuidField = jStr.cStr; + if (auto inst = VectorStyleSettingsClassInfo::get(env,obj)) + { + if (const auto jStr = JavaString(env, fieldStr)) + { + (*inst)->uuidField = jStr.getCString(); + } + else + { + (*inst)->uuidField.clear(); + } } } catch (...) { - __android_log_print(ANDROID_LOG_VERBOSE, "Maply", "Crash in VectorStyleSettings::setUuidField()"); + __android_log_print(ANDROID_LOG_ERROR, "Maply", "Crash in VectorStyleSettings::setUuidField()"); } } -JNIEXPORT jint JNICALL Java_com_mousebird_maply_VectorStyleSettings_getBaseDrawPriority -(JNIEnv *env, jobject obj) +extern "C" +JNIEXPORT jint JNICALL Java_com_mousebird_maply_VectorStyleSettings_getBaseDrawPriority(JNIEnv *env, jobject obj) { try { - VectorStyleSettingsImplRef *inst = VectorStyleSettingsClassInfo::getClassInfo()->getObject(env,obj); - if (inst) + if (auto inst = VectorStyleSettingsClassInfo::get(env,obj)) + { return (*inst)->baseDrawPriority; + } } catch (...) { - __android_log_print(ANDROID_LOG_VERBOSE, "Maply", "Crash in VectorStyleSettings::getBaseDrawPriority()"); + __android_log_print(ANDROID_LOG_ERROR, "Maply", "Crash in VectorStyleSettings::getBaseDrawPriority()"); } - return 0; } -JNIEXPORT void JNICALL Java_com_mousebird_maply_VectorStyleSettings_setBaseDrawPriority -(JNIEnv *env, jobject obj, jint drawPriority) +extern "C" +JNIEXPORT void JNICALL Java_com_mousebird_maply_VectorStyleSettings_setBaseDrawPriority(JNIEnv *env, jobject obj, jint drawPriority) { try { - VectorStyleSettingsImplRef *inst = VectorStyleSettingsClassInfo::getClassInfo()->getObject(env,obj); - if (inst) + if (auto inst = VectorStyleSettingsClassInfo::get(env,obj)) + { (*inst)->baseDrawPriority = drawPriority; + } } catch (...) { - __android_log_print(ANDROID_LOG_VERBOSE, "Maply", "Crash in VectorStyleSettings::setBaseDrawPriority()"); + __android_log_print(ANDROID_LOG_ERROR, "Maply", "Crash in VectorStyleSettings::setBaseDrawPriority()"); } } -JNIEXPORT jint JNICALL Java_com_mousebird_maply_VectorStyleSettings_getDrawPriorityPerLevel -(JNIEnv *env, jobject obj) +extern "C" +JNIEXPORT jint JNICALL Java_com_mousebird_maply_VectorStyleSettings_getDrawPriorityPerLevel(JNIEnv *env, jobject obj) { try { - VectorStyleSettingsImplRef *inst = VectorStyleSettingsClassInfo::getClassInfo()->getObject(env,obj); - if (inst) + if (auto inst = VectorStyleSettingsClassInfo::get(env,obj)) + { return (*inst)->drawPriorityPerLevel; + } } catch (...) { - __android_log_print(ANDROID_LOG_VERBOSE, "Maply", "Crash in VectorStyleSettings::getDrawPriorityPerLevel()"); + __android_log_print(ANDROID_LOG_ERROR, "Maply", "Crash in VectorStyleSettings::getDrawPriorityPerLevel()"); } return 0; } -JNIEXPORT void JNICALL Java_com_mousebird_maply_VectorStyleSettings_setDrawPriorityPerLevel -(JNIEnv *env, jobject obj, jint drawPriority) +extern "C" +JNIEXPORT void JNICALL Java_com_mousebird_maply_VectorStyleSettings_setDrawPriorityPerLevel(JNIEnv *env, jobject obj, jint drawPriority) { try { - VectorStyleSettingsImplRef *inst = VectorStyleSettingsClassInfo::getClassInfo()->getObject(env,obj); - if (inst) + if (auto inst = VectorStyleSettingsClassInfo::get(env,obj)) + { (*inst)->drawPriorityPerLevel = drawPriority; + } } catch (...) { - __android_log_print(ANDROID_LOG_VERBOSE, "Maply", "Crash in VectorStyleSettings::setDrawPriorityPerLevel()"); + __android_log_print(ANDROID_LOG_ERROR, "Maply", "Crash in VectorStyleSettings::setDrawPriorityPerLevel()"); } } -JNIEXPORT jdouble JNICALL Java_com_mousebird_maply_VectorStyleSettings_getMapScaleScale -(JNIEnv *env, jobject obj) +extern "C" +JNIEXPORT jdouble JNICALL Java_com_mousebird_maply_VectorStyleSettings_getMapScaleScale(JNIEnv *env, jobject obj) { try { - VectorStyleSettingsImplRef *inst = VectorStyleSettingsClassInfo::getClassInfo()->getObject(env,obj); - if (inst) + if (auto inst = VectorStyleSettingsClassInfo::get(env,obj)) + { return (*inst)->mapScaleScale; + } } catch (...) { - __android_log_print(ANDROID_LOG_VERBOSE, "Maply", "Crash in VectorStyleSettings::getMapScaleScale()"); + __android_log_print(ANDROID_LOG_ERROR, "Maply", "Crash in VectorStyleSettings::getMapScaleScale()"); } - return 1.0; } -JNIEXPORT void JNICALL Java_com_mousebird_maply_VectorStyleSettings_setMapScaleScale -(JNIEnv *env, jobject obj, jdouble scale) +extern "C" +JNIEXPORT void JNICALL Java_com_mousebird_maply_VectorStyleSettings_setMapScaleScale(JNIEnv *env, jobject obj, jdouble scale) { try { - VectorStyleSettingsImplRef *inst = VectorStyleSettingsClassInfo::getClassInfo()->getObject(env,obj); + auto inst = VectorStyleSettingsClassInfo::get(env,obj); if (inst) (*inst)->mapScaleScale = scale; } catch (...) { - __android_log_print(ANDROID_LOG_VERBOSE, "Maply", "Crash in VectorStyleSettings::setMapScaleScale()"); + __android_log_print(ANDROID_LOG_ERROR, "Maply", "Crash in VectorStyleSettings::setMapScaleScale()"); } } -JNIEXPORT jdouble JNICALL Java_com_mousebird_maply_VectorStyleSettings_getDashPatternScale -(JNIEnv *env, jobject obj) +extern "C" +JNIEXPORT jdouble JNICALL Java_com_mousebird_maply_VectorStyleSettings_getDashPatternScale(JNIEnv *env, jobject obj) { try { - VectorStyleSettingsImplRef *inst = VectorStyleSettingsClassInfo::getClassInfo()->getObject(env,obj); - if (inst) + if (auto inst = VectorStyleSettingsClassInfo::get(env,obj)) + { return (*inst)->dashPatternScale; + } } catch (...) { - __android_log_print(ANDROID_LOG_VERBOSE, "Maply", "Crash in VectorStyleSettings::getDashPatternScale()"); + __android_log_print(ANDROID_LOG_ERROR, "Maply", "Crash in VectorStyleSettings::getDashPatternScale()"); } - return 1.0; } -JNIEXPORT void JNICALL Java_com_mousebird_maply_VectorStyleSettings_setDashPatternScale -(JNIEnv *env, jobject obj, jdouble scale) +extern "C" +JNIEXPORT void JNICALL Java_com_mousebird_maply_VectorStyleSettings_setDashPatternScale(JNIEnv *env, jobject obj, jdouble scale) { try { - VectorStyleSettingsImplRef *inst = VectorStyleSettingsClassInfo::getClassInfo()->getObject(env,obj); - if (inst) + if (auto inst = VectorStyleSettingsClassInfo::get(env,obj)) + { (*inst)->dashPatternScale = scale; + } } catch (...) { - __android_log_print(ANDROID_LOG_VERBOSE, "Maply", "Crash in VectorStyleSettings::setDashPatternScale()"); + __android_log_print(ANDROID_LOG_ERROR, "Maply", "Crash in VectorStyleSettings::setDashPatternScale()"); } } -JNIEXPORT jboolean JNICALL Java_com_mousebird_maply_VectorStyleSettings_getUseWideVectors -(JNIEnv *env, jobject obj) +extern "C" +JNIEXPORT jboolean JNICALL Java_com_mousebird_maply_VectorStyleSettings_getUseWideVectors(JNIEnv *env, jobject obj) { try { - VectorStyleSettingsImplRef *inst = VectorStyleSettingsClassInfo::getClassInfo()->getObject(env,obj); - if (inst) + if (auto inst = VectorStyleSettingsClassInfo::get(env,obj)) + { return (*inst)->useWideVectors; + } } catch (...) { - __android_log_print(ANDROID_LOG_VERBOSE, "Maply", "Crash in VectorStyleSettings::getUseWideVectors()"); + __android_log_print(ANDROID_LOG_ERROR, "Maply", "Crash in VectorStyleSettings::getUseWideVectors()"); } - return false; } -JNIEXPORT void JNICALL Java_com_mousebird_maply_VectorStyleSettings_setUseWideVectors -(JNIEnv *env, jobject obj, jboolean useWideVectors) +extern "C" +JNIEXPORT void JNICALL Java_com_mousebird_maply_VectorStyleSettings_setUseWideVectors(JNIEnv *env, jobject obj, jboolean useWideVectors) { try { - VectorStyleSettingsImplRef *inst = VectorStyleSettingsClassInfo::getClassInfo()->getObject(env,obj); - if (inst) + if (auto inst = VectorStyleSettingsClassInfo::get(env,obj)) + { (*inst)->useWideVectors = useWideVectors; + } } catch (...) { - __android_log_print(ANDROID_LOG_VERBOSE, "Maply", "Crash in VectorStyleSettings::setUseWideVectors()"); + __android_log_print(ANDROID_LOG_ERROR, "Maply", "Crash in VectorStyleSettings::setUseWideVectors()"); } } -JNIEXPORT jdouble JNICALL Java_com_mousebird_maply_VectorStyleSettings_getOldVecWidthScale -(JNIEnv *env, jobject obj) +extern "C" +JNIEXPORT jdouble JNICALL Java_com_mousebird_maply_VectorStyleSettings_getOldVecWidthScale(JNIEnv *env, jobject obj) { try { - VectorStyleSettingsImplRef *inst = VectorStyleSettingsClassInfo::getClassInfo()->getObject(env,obj); - if (inst) + if (auto inst = VectorStyleSettingsClassInfo::get(env,obj)) + { return (*inst)->oldVecWidthScale; + } } catch (...) { - __android_log_print(ANDROID_LOG_VERBOSE, "Maply", "Crash in VectorStyleSettings::getOldVecWidthScale()"); + __android_log_print(ANDROID_LOG_ERROR, "Maply", "Crash in VectorStyleSettings::getOldVecWidthScale()"); } - return 1.0; } -JNIEXPORT void JNICALL Java_com_mousebird_maply_VectorStyleSettings_setOldVecWidthScale -(JNIEnv *env, jobject obj, jdouble scale) +extern "C" +JNIEXPORT void JNICALL Java_com_mousebird_maply_VectorStyleSettings_setOldVecWidthScale(JNIEnv *env, jobject obj, jdouble scale) { try { - VectorStyleSettingsImplRef *inst = VectorStyleSettingsClassInfo::getClassInfo()->getObject(env,obj); - if (inst) + if (auto inst = VectorStyleSettingsClassInfo::get(env,obj)) + { (*inst)->oldVecWidthScale = scale; + } } catch (...) { - __android_log_print(ANDROID_LOG_VERBOSE, "Maply", "Crash in VectorStyleSettings::setOldVecWidthScale()"); + __android_log_print(ANDROID_LOG_ERROR, "Maply", "Crash in VectorStyleSettings::setOldVecWidthScale()"); } } -JNIEXPORT jdouble JNICALL Java_com_mousebird_maply_VectorStyleSettings_getWideVecCutoff -(JNIEnv *env, jobject obj) +extern "C" +JNIEXPORT jdouble JNICALL Java_com_mousebird_maply_VectorStyleSettings_getWideVecCutoff(JNIEnv *env, jobject obj) { try { - VectorStyleSettingsImplRef *inst = VectorStyleSettingsClassInfo::getClassInfo()->getObject(env,obj); - if (inst) + if (auto inst = VectorStyleSettingsClassInfo::get(env,obj)) + { return (*inst)->wideVecCuttoff; + } } catch (...) { - __android_log_print(ANDROID_LOG_VERBOSE, "Maply", "Crash in VectorStyleSettings::getWideVecCutoff()"); + __android_log_print(ANDROID_LOG_ERROR, "Maply", "Crash in VectorStyleSettings::getWideVecCutoff()"); } - return 1.0; } -JNIEXPORT void JNICALL Java_com_mousebird_maply_VectorStyleSettings_setWideVecCutoff -(JNIEnv *env, jobject obj, jdouble cutoff) +extern "C" +JNIEXPORT void JNICALL Java_com_mousebird_maply_VectorStyleSettings_setWideVecCutoff(JNIEnv *env, jobject obj, jdouble cutoff) { try { - VectorStyleSettingsImplRef *inst = VectorStyleSettingsClassInfo::getClassInfo()->getObject(env,obj); - if (inst) + if (auto inst = VectorStyleSettingsClassInfo::get(env,obj)) + { (*inst)->wideVecCuttoff = cutoff; + } } catch (...) { - __android_log_print(ANDROID_LOG_VERBOSE, "Maply", "Crash in VectorStyleSettings::setWideVecCutoff()"); + __android_log_print(ANDROID_LOG_ERROR, "Maply", "Crash in VectorStyleSettings::setWideVecCutoff()"); } } -JNIEXPORT jboolean JNICALL Java_com_mousebird_maply_VectorStyleSettings_getSelectable -(JNIEnv *env, jobject obj) +extern "C" +JNIEXPORT jboolean JNICALL Java_com_mousebird_maply_VectorStyleSettings_getSelectable(JNIEnv *env, jobject obj) { try { - VectorStyleSettingsImplRef *inst = VectorStyleSettingsClassInfo::getClassInfo()->getObject(env,obj); - if (inst) + if (auto inst = VectorStyleSettingsClassInfo::get(env,obj)) + { return (*inst)->selectable; + } } catch (...) { - __android_log_print(ANDROID_LOG_VERBOSE, "Maply", "Crash in VectorStyleSettings::getSelectable()"); + __android_log_print(ANDROID_LOG_ERROR, "Maply", "Crash in VectorStyleSettings::getSelectable()"); } - return 1.0; } -JNIEXPORT void JNICALL Java_com_mousebird_maply_VectorStyleSettings_setSelectable -(JNIEnv *env, jobject obj, jboolean selectable) +extern "C" +JNIEXPORT void JNICALL Java_com_mousebird_maply_VectorStyleSettings_setSelectable(JNIEnv *env, jobject obj, jboolean selectable) { try { - VectorStyleSettingsImplRef *inst = VectorStyleSettingsClassInfo::getClassInfo()->getObject(env,obj); - if (inst) + if (auto inst = VectorStyleSettingsClassInfo::get(env,obj)) + { (*inst)->selectable = selectable; + } } catch (...) { - __android_log_print(ANDROID_LOG_VERBOSE, "Maply", "Crash in VectorStyleSettings::setSelectable()"); + __android_log_print(ANDROID_LOG_ERROR, "Maply", "Crash in VectorStyleSettings::setSelectable()"); } } -JNIEXPORT jstring JNICALL Java_com_mousebird_maply_VectorStyleSettings_getIconDirectory -(JNIEnv *env, jobject obj) +extern "C" +JNIEXPORT jstring JNICALL Java_com_mousebird_maply_VectorStyleSettings_getIconDirectory(JNIEnv *env, jobject obj) { try { - VectorStyleSettingsImplRef *inst = VectorStyleSettingsClassInfo::getClassInfo()->getObject(env,obj); - if (inst) + if (auto inst = VectorStyleSettingsClassInfo::get(env,obj)) + { return env->NewStringUTF((*inst)->iconDirectory.c_str()); + } } catch (...) { - __android_log_print(ANDROID_LOG_VERBOSE, "Maply", "Crash in VectorStyleSettings::getIconDirectory()"); + __android_log_print(ANDROID_LOG_ERROR, "Maply", "Crash in VectorStyleSettings::getIconDirectory()"); } - - return NULL; + return nullptr; } -JNIEXPORT void JNICALL Java_com_mousebird_maply_VectorStyleSettings_setIconDirectory -(JNIEnv *env, jobject obj, jstring iconDirStr) +extern "C" +JNIEXPORT void JNICALL Java_com_mousebird_maply_VectorStyleSettings_setIconDirectory(JNIEnv *env, jobject obj, jstring iconDirStr) { try { - VectorStyleSettingsImplRef *inst = VectorStyleSettingsClassInfo::getClassInfo()->getObject(env,obj); - if (inst) { - JavaString jStr(env,iconDirStr); - (*inst)->iconDirectory = jStr.cStr; + if (auto inst = VectorStyleSettingsClassInfo::get(env,obj)) + { + const JavaString jStr(env,iconDirStr); + (*inst)->iconDirectory = jStr.getCString(); } } catch (...) { - __android_log_print(ANDROID_LOG_VERBOSE, "Maply", "Crash in VectorStyleSettings::setIconDirectory()"); + __android_log_print(ANDROID_LOG_ERROR, "Maply", "Crash in VectorStyleSettings::setIconDirectory()"); } } -JNIEXPORT jstring JNICALL Java_com_mousebird_maply_VectorStyleSettings_getFontName -(JNIEnv *env, jobject obj) +extern "C" +JNIEXPORT jstring JNICALL Java_com_mousebird_maply_VectorStyleSettings_getFontName(JNIEnv *env, jobject obj) { try { - VectorStyleSettingsImplRef *inst = VectorStyleSettingsClassInfo::getClassInfo()->getObject(env,obj); - if (inst) + if (auto inst = VectorStyleSettingsClassInfo::get(env,obj)) + { return env->NewStringUTF((*inst)->fontName.c_str()); + } } catch (...) { - __android_log_print(ANDROID_LOG_VERBOSE, "Maply", "Crash in VectorStyleSettings::getFontName()"); + __android_log_print(ANDROID_LOG_ERROR, "Maply", "Crash in VectorStyleSettings::getFontName()"); } - - return NULL; + return nullptr; } -JNIEXPORT void JNICALL Java_com_mousebird_maply_VectorStyleSettings_setFontName -(JNIEnv *env, jobject obj, jstring fontNameStr) +extern "C" +JNIEXPORT void JNICALL Java_com_mousebird_maply_VectorStyleSettings_setFontName(JNIEnv *env, jobject obj, jstring fontNameStr) { try { - VectorStyleSettingsImplRef *inst = VectorStyleSettingsClassInfo::getClassInfo()->getObject(env,obj); - if (inst) { - JavaString jStr(env,fontNameStr); - (*inst)->fontName = jStr.cStr; + if (auto inst = VectorStyleSettingsClassInfo::get(env,obj)) + { + const JavaString jStr(env,fontNameStr); + (*inst)->fontName = jStr.getCString(); } } catch (...) { - __android_log_print(ANDROID_LOG_VERBOSE, "Maply", "Crash in VectorStyleSettings::setFontName()"); + __android_log_print(ANDROID_LOG_ERROR, "Maply", "Crash in VectorStyleSettings::setFontName()"); } } -JNIEXPORT void JNICALL Java_com_mousebird_maply_VectorStyleSettings_setZBufferRead - (JNIEnv *env, jobject obj, jboolean val) +extern "C" +JNIEXPORT void JNICALL Java_com_mousebird_maply_VectorStyleSettings_setZBufferRead(JNIEnv *env, jobject obj, jboolean val) { try { - VectorStyleSettingsImplRef *inst = VectorStyleSettingsClassInfo::getClassInfo()->getObject(env,obj); - if (inst) + if (auto inst = VectorStyleSettingsClassInfo::get(env,obj)) + { (*inst)->zBufferRead = val; + } } catch (...) { - __android_log_print(ANDROID_LOG_VERBOSE, "Maply", "Crash in VectorStyleSettings::setZBufferRead()"); + __android_log_print(ANDROID_LOG_ERROR, "Maply", "Crash in VectorStyleSettings::setZBufferRead()"); } } -JNIEXPORT void JNICALL Java_com_mousebird_maply_VectorStyleSettings_setZBufferWrite - (JNIEnv *env, jobject obj, jboolean val) +extern "C" +JNIEXPORT void JNICALL Java_com_mousebird_maply_VectorStyleSettings_setZBufferWrite(JNIEnv *env, jobject obj, jboolean val) { try { - VectorStyleSettingsImplRef *inst = VectorStyleSettingsClassInfo::getClassInfo()->getObject(env,obj); - if (inst) + if (const auto inst = VectorStyleSettingsClassInfo::get(env,obj)) + { (*inst)->zBufferWrite = val; + } } catch (...) { - __android_log_print(ANDROID_LOG_VERBOSE, "Maply", "Crash in VectorStyleSettings::setZBufferWrite()"); + __android_log_print(ANDROID_LOG_ERROR, "Maply", "Crash in VectorStyleSettings::setZBufferWrite()"); } } diff --git a/android/library/maply/jni/src/formats/VectorStyleWrapper_jni.cpp b/android/library/maply/jni/src/formats/VectorStyleWrapper_jni.cpp index dc628f8b8b..1a1a43eeb3 100644 --- a/android/library/maply/jni/src/formats/VectorStyleWrapper_jni.cpp +++ b/android/library/maply/jni/src/formats/VectorStyleWrapper_jni.cpp @@ -71,16 +71,15 @@ JNIEXPORT void JNICALL Java_com_mousebird_maply_VectorStyleWrapper_dispose { VectorStyleSetWrapperClassInfo *classInfo = VectorStyleSetWrapperClassInfo::getClassInfo(); - { - PlatformInfo_Android threadInst(env); + PlatformInfo_Android threadInst(env); - std::lock_guard lock(disposeMutex); - VectorStyleSetWrapper_AndroidRef *inst = classInfo->getObject(env,obj); + std::lock_guard lock(disposeMutex); + VectorStyleSetWrapper_AndroidRef *inst = classInfo->getObject(env,obj); + if (inst && *inst) + { (*inst)->shutdown(&threadInst); - if (!inst) - return; - delete inst; } + delete inst; classInfo->clearHandle(env,obj); } diff --git a/android/library/maply/jni/src/formats/VectorTileData_jni.cpp b/android/library/maply/jni/src/formats/VectorTileData_jni.cpp index 7ac9cfa7c9..3b149ba0fe 100644 --- a/android/library/maply/jni/src/formats/VectorTileData_jni.cpp +++ b/android/library/maply/jni/src/formats/VectorTileData_jni.cpp @@ -1,9 +1,8 @@ -/* - * VectorTileData_jni.cpp +/* VectorTileData_jni.cpp * WhirlyGlobeLib * * Created by sjg - * Copyright 2011-2016 mousebird consulting + * Copyright 2011-2021 mousebird consulting * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -15,7 +14,6 @@ * 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. - * */ #import @@ -28,36 +26,40 @@ using namespace WhirlyKit; using namespace Eigen; -template<> VectorTileDataClassInfo *VectorTileDataClassInfo::classInfoObj = NULL; +template<> VectorTileDataClassInfo *VectorTileDataClassInfo::classInfoObj = nullptr; -JNIEXPORT void JNICALL Java_com_mousebird_maply_VectorTileData_nativeInit -(JNIEnv *env, jclass cls) +extern "C" +JNIEXPORT void JNICALL Java_com_mousebird_maply_VectorTileData_nativeInit(JNIEnv *env, jclass cls) { VectorTileDataClassInfo::getClassInfo(env, cls); } -JNIEXPORT jobject JNICALL MakeVectorTileDataObject(JNIEnv *env,VectorTileDataRef tileData) +JNIEXPORT jobject JNICALL MakeVectorTileDataObject(JNIEnv *env,const VectorTileDataRef &tileData) { - VectorTileDataClassInfo *classInfo = VectorTileDataClassInfo::getClassInfo(env,"com/mousebird/maply/VectorTileData"); + const auto classInfo = VectorTileDataClassInfo::getClassInfo(env,"com/mousebird/maply/VectorTileData"); return classInfo->makeWrapperObject(env,new VectorTileDataRef(tileData)); } -JNIEXPORT void JNICALL Java_com_mousebird_maply_VectorTileData_initialise__ -(JNIEnv *env, jobject obj) +extern "C" +JNIEXPORT void JNICALL Java_com_mousebird_maply_VectorTileData_initialise__(JNIEnv *env, jobject obj) { - try { + try + { VectorTileDataRef *tileData = new VectorTileDataRef(new VectorTileData()); VectorTileDataClassInfo::getClassInfo()->setHandle(env,obj,tileData); } - catch (...) { - __android_log_print(ANDROID_LOG_VERBOSE, "Maply", "Crash in VectorTileData::initialise()"); + catch (...) + { + __android_log_print(ANDROID_LOG_ERROR, "Maply", "Crash in VectorTileData::initialise()"); } } +extern "C" JNIEXPORT void JNICALL Java_com_mousebird_maply_VectorTileData_initialise__IIILcom_mousebird_maply_Point2d_2Lcom_mousebird_maply_Point2d_2Lcom_mousebird_maply_Point2d_2Lcom_mousebird_maply_Point2d_2 -(JNIEnv *env, jobject obj, jint x, jint y, jint level, jobject bllobj, jobject burobj, jobject geollobj, jobject geourobj) + (JNIEnv *env, jobject obj, jint x, jint y, jint level, jobject bllobj, jobject burobj, jobject geollobj, jobject geourobj) { - try { + try + { Point2dClassInfo *pt2dClassInfo = Point2dClassInfo::getClassInfo(); Point2d *boundLL = pt2dClassInfo->getObject(env,bllobj); Point2d *boundUR = pt2dClassInfo->getObject(env,burobj); @@ -73,25 +75,23 @@ JNIEXPORT void JNICALL Java_com_mousebird_maply_VectorTileData_initialise__IIILc (*tileData)->geoBBox.ur() = *geoUR; VectorTileDataClassInfo::getClassInfo()->setHandle(env,obj,tileData); } - catch (...) { - __android_log_print(ANDROID_LOG_VERBOSE, "Maply", "Crash in VectorTileData::initialise()"); + catch (...) + { + __android_log_print(ANDROID_LOG_ERROR, "Maply", "Crash in VectorTileData::initialise()"); } } static std::mutex disposeMutex; -JNIEXPORT void JNICALL Java_com_mousebird_maply_VectorTileData_dispose -(JNIEnv *env, jobject obj) +extern "C" +JNIEXPORT void JNICALL Java_com_mousebird_maply_VectorTileData_dispose(JNIEnv *env, jobject obj) { try { - VectorTileDataClassInfo *classInfo = VectorTileDataClassInfo::getClassInfo(); - + const auto classInfo = VectorTileDataClassInfo::getClassInfo(); { std::lock_guard lock(disposeMutex); - VectorTileDataRef *inst = classInfo->getObject(env,obj); - if (!inst) - return; + auto inst = classInfo->getObject(env,obj); delete inst; } @@ -99,204 +99,223 @@ JNIEXPORT void JNICALL Java_com_mousebird_maply_VectorTileData_dispose } catch (...) { - __android_log_print(ANDROID_LOG_VERBOSE, "Maply", "Crash in VectorTileData::dispose()"); + __android_log_print(ANDROID_LOG_ERROR, "Maply", "Crash in VectorTileData::dispose()"); } } -JNIEXPORT jintArray JNICALL Java_com_mousebird_maply_VectorTileData_getTileIDNative - (JNIEnv *env, jobject obj) +extern "C" +JNIEXPORT jintArray JNICALL Java_com_mousebird_maply_VectorTileData_getTileIDNative(JNIEnv *env, jobject obj) { try { - VectorTileDataRef *tileData = VectorTileDataClassInfo::getClassInfo()->getObject(env,obj); - if (!tileData) - return NULL; - std::vector ints(3); - auto tileID = (*tileData)->ident; - ints[0] = tileID.x; - ints[1] = tileID.y; - ints[2] = tileID.level; - - return BuildIntArray(env,ints); + const auto tileData = VectorTileDataClassInfo::get(env,obj); + if (tileData) + { + const auto tileID = (*tileData)->ident; + const std::vector ints{tileID.x, tileID.y, tileID.level}; + return BuildIntArray(env, ints); + } } catch (...) { - __android_log_print(ANDROID_LOG_VERBOSE, "Maply", "Crash in VectorTileData::getTileIDNative"); + __android_log_print(ANDROID_LOG_ERROR, "Maply", "Crash in VectorTileData::getTileIDNative"); } - - return NULL; + return nullptr; } -JNIEXPORT void JNICALL Java_com_mousebird_maply_VectorTileData_getBoundsNative -(JNIEnv *env, jobject obj, jobject llObj, jobject urObj) +extern "C" +JNIEXPORT void JNICALL Java_com_mousebird_maply_VectorTileData_getBoundsNative(JNIEnv *env, jobject obj, jobject llObj, jobject urObj) { try { - VectorTileDataRef *tileData = VectorTileDataClassInfo::getClassInfo()->getObject(env,obj); - Point2dClassInfo *pt2dClassInfo = Point2dClassInfo::getClassInfo(); - Point2d *ll = pt2dClassInfo->getObject(env,llObj); - Point2d *ur = pt2dClassInfo->getObject(env,urObj); - if (!tileData || !ll || !ur) - return; - *ll = (*tileData)->bbox.ll(); - *ur = (*tileData)->bbox.ur(); + if (const auto tileData = VectorTileDataClassInfo::get(env,obj)) + { + if (auto ll = Point2dClassInfo::get(env, llObj)) + { + if (auto ur = Point2dClassInfo::get(env, urObj)) + { + *ll = (*tileData)->bbox.ll(); + *ur = (*tileData)->bbox.ur(); + } + } + } } - catch (...) { - __android_log_print(ANDROID_LOG_VERBOSE, "Maply","Crash in VectorTileData::getBoundsNative"); + catch (...) + { + __android_log_print(ANDROID_LOG_ERROR, "Maply","Crash in VectorTileData::getBoundsNative"); } } -JNIEXPORT void JNICALL Java_com_mousebird_maply_VectorTileData_getGeoBoundsNative -(JNIEnv *env, jobject obj, jobject llObj, jobject urObj) +extern "C" +JNIEXPORT void JNICALL Java_com_mousebird_maply_VectorTileData_getGeoBoundsNative(JNIEnv *env, jobject obj, jobject llObj, jobject urObj) { try { - VectorTileDataRef *tileData = VectorTileDataClassInfo::getClassInfo()->getObject(env,obj); - Point2dClassInfo *pt2dClassInfo = Point2dClassInfo::getClassInfo(); - Point2d *ll = pt2dClassInfo->getObject(env,llObj); - Point2d *ur = pt2dClassInfo->getObject(env,urObj); - if (!tileData || !ll || !ur) - return; - *ll = (*tileData)->geoBBox.ll(); - *ur = (*tileData)->geoBBox.ur(); + if (const auto tileData = VectorTileDataClassInfo::get(env,obj)) + { + if (auto ll = Point2dClassInfo::get(env, llObj)) + { + if (auto ur = Point2dClassInfo::get(env, urObj)) + { + *ll = (*tileData)->geoBBox.ll(); + *ur = (*tileData)->geoBBox.ur(); + } + } + } } - catch (...) { - __android_log_print(ANDROID_LOG_VERBOSE, "Maply","Crash in VectorTileData::getGeoBoundsNative"); + catch (...) + { + __android_log_print(ANDROID_LOG_ERROR, "Maply","Crash in VectorTileData::getGeoBoundsNative"); } } -JNIEXPORT jobjectArray JNICALL Java_com_mousebird_maply_VectorTileData_getComponentObjects__ - (JNIEnv *env, jobject obj) +extern "C" +JNIEXPORT jobjectArray JNICALL Java_com_mousebird_maply_VectorTileData_getComponentObjects__(JNIEnv *env, jobject obj) { try { - VectorTileDataRef *tileData = VectorTileDataClassInfo::getClassInfo()->getObject(env,obj); - if (!tileData) - return NULL; - ComponentObjectRefClassInfo *classInfo = ComponentObjectRefClassInfo::getClassInfo(); - std::vector compObjs; - for (ComponentObjectRef compObj : (*tileData)->compObjs) - compObjs.push_back(MakeComponentObjectWrapper(env,classInfo,compObj)); + if (const auto tileData = VectorTileDataClassInfo::get(env,obj)) + { + const auto classInfo = ComponentObjectRefClassInfo::getClassInfo(); - jobjectArray retArray = BuildObjectArray(env,classInfo->getClass(),compObjs); - for (auto objRef: compObjs) { - env->DeleteLocalRef(objRef); - } - compObjs.clear(); + std::vector compObjs; + compObjs.reserve((*tileData)->compObjs.size()); + for (const auto &compObj : (*tileData)->compObjs) + { + compObjs.push_back(MakeComponentObjectWrapper(env, classInfo, compObj)); + } + + jobjectArray retArray = BuildObjectArray(env, classInfo->getClass(), compObjs); + for (auto objRef : compObjs) + { + env->DeleteLocalRef(objRef); + } - return retArray; + return retArray; + } } - catch (...) { - __android_log_print(ANDROID_LOG_VERBOSE, "Maply", "Crash in VectorTileData::getComponentObjects"); + catch (...) + { + __android_log_print(ANDROID_LOG_ERROR, "Maply", "Crash in VectorTileData::getComponentObjects"); } - - return NULL; + return nullptr; } -JNIEXPORT jobjectArray JNICALL Java_com_mousebird_maply_VectorTileData_getComponentObjects__Ljava_lang_String_2 - (JNIEnv *env, jobject obj, jstring jStr) +extern "C" +JNIEXPORT jobjectArray JNICALL Java_com_mousebird_maply_VectorTileData_getComponentObjects__Ljava_lang_String_2(JNIEnv *env, jobject obj, jstring jStr) { try { - VectorTileDataRef *tileData = VectorTileDataClassInfo::getClassInfo()->getObject(env,obj); - if (!tileData) - return NULL; + if (const auto tileData = VectorTileDataClassInfo::get(env,obj)) + { + const JavaString str(env, jStr); + const auto it = (*tileData)->categories.find(str.getCString()); + if (it == (*tileData)->categories.end()) + { + return nullptr; + } + + const auto &compObjs = it->second; - JavaString str(env,jStr); - auto it = (*tileData)->categories.find((std::string)str.cStr); - if (it == (*tileData)->categories.end()) - return NULL; - std::vector &compObjs = it->second; + const auto classInfo = ComponentObjectRefClassInfo::getClassInfo(); - ComponentObjectRefClassInfo *classInfo = ComponentObjectRefClassInfo::getClassInfo(); - std::vector outCompObjs; - for (ComponentObjectRef compObj : compObjs) { - outCompObjs.push_back(MakeComponentObjectWrapper(env,classInfo,compObj)); - } + std::vector outCompObjs; + outCompObjs.reserve(compObjs.size()); + for (const auto &compObj : compObjs) + { + outCompObjs.push_back(MakeComponentObjectWrapper(env, classInfo, compObj)); + } - return BuildObjectArray(env,classInfo->getClass(),outCompObjs); + return BuildObjectArray(env, classInfo->getClass(), outCompObjs); + } } - catch (...) { - __android_log_print(ANDROID_LOG_VERBOSE, "Maply","Crash in VectorTileData::getComponentObjects (by category)"); + catch (...) + { + __android_log_print(ANDROID_LOG_ERROR, "Maply","Crash in VectorTileData::getComponentObjects (by category)"); } - - return NULL; + return nullptr; } -JNIEXPORT void JNICALL Java_com_mousebird_maply_VectorTileData_addComponentObject -(JNIEnv *env, jobject obj, jobject compObjObj) +extern "C" +JNIEXPORT void JNICALL Java_com_mousebird_maply_VectorTileData_addComponentObject(JNIEnv *env, jobject obj, jobject compObjObj) { try { - VectorTileDataRef *tileData = VectorTileDataClassInfo::getClassInfo()->getObject(env,obj); - ComponentObjectRef *compObj = ComponentObjectRefClassInfo::getClassInfo()->getObject(env,compObjObj); - if (!tileData || !compObj) - return; - (*tileData)->compObjs.push_back(*compObj); + if (const auto tileData = VectorTileDataClassInfo::get(env,obj)) + { + if (const auto compObj = ComponentObjectRefClassInfo::get(env,compObjObj)) + { + (*tileData)->compObjs.push_back(*compObj); + } + } } - catch (...) { - __android_log_print(ANDROID_LOG_VERBOSE, "Maply", "Crash in VectorTileData::addComponentObject"); + catch (...) + { + __android_log_print(ANDROID_LOG_ERROR, "Maply", "Crash in VectorTileData::addComponentObject"); } } -JNIEXPORT void JNICALL Java_com_mousebird_maply_VectorTileData_addComponentObjects -(JNIEnv *env, jobject obj, jobjectArray compObjArray) +extern "C" +JNIEXPORT void JNICALL Java_com_mousebird_maply_VectorTileData_addComponentObjects(JNIEnv *env, jobject obj, jobjectArray compObjArray) { try { - VectorTileDataRef *tileData = VectorTileDataClassInfo::getClassInfo()->getObject(env,obj); - ComponentObjectRefClassInfo *classInfo = ComponentObjectRefClassInfo::getClassInfo(); - if (!tileData) - return; - JavaObjectArrayHelper compObjHelp(env,compObjArray); - while (jobject compObjObj = compObjHelp.getNextObject()) { - ComponentObjectRef *compObj = classInfo->getObject(env,compObjObj); - (*tileData)->compObjs.push_back(*compObj); + if (const auto tileData = VectorTileDataClassInfo::get(env,obj)) + { + JavaObjectArrayHelper compObjHelp(env, compObjArray); + while (jobject compObjObj = compObjHelp.getNextObject()) + { + const auto compObj = ComponentObjectRefClassInfo::get(env, compObjObj); + (*tileData)->compObjs.push_back(*compObj); + } } } - catch (...) { - __android_log_print(ANDROID_LOG_VERBOSE, "Maply", "Crash in VectorTileData::addComponentObjects"); + catch (...) + { + __android_log_print(ANDROID_LOG_ERROR, "Maply", "Crash in VectorTileData::addComponentObjects"); } } -JNIEXPORT jobjectArray JNICALL Java_com_mousebird_maply_VectorTileData_getVectors - (JNIEnv *env, jobject obj) +extern "C" +JNIEXPORT jobjectArray JNICALL Java_com_mousebird_maply_VectorTileData_getVectors(JNIEnv *env, jobject obj) { try { - VectorTileDataRef *tileData = VectorTileDataClassInfo::getClassInfo()->getObject(env,obj); - VectorObjectClassInfo *classInfo = VectorObjectClassInfo::getClassInfo(); - if (!tileData) - return NULL; - std::vector outVecs; - for (VectorObjectRef vecObjRef : (*tileData)->vecObjs) { - outVecs.push_back(MakeVectorObjectWrapper(env,classInfo,vecObjRef)); + if (const auto tileData = VectorTileDataClassInfo::get(env,obj)) + { + const auto vecObjClassInfo = VectorObjectClassInfo::getClassInfo(); + + std::vector outVecs; + outVecs.reserve((*tileData)->vecObjs.size()); + for (const auto &vecObjRef : (*tileData)->vecObjs) + { + outVecs.push_back(MakeVectorObjectWrapper(env, vecObjClassInfo, vecObjRef)); + } + return BuildObjectArray(env, vecObjClassInfo->getClass(), outVecs); } - return BuildObjectArray(env,classInfo->getClass(),outVecs); } - catch (...) { - __android_log_print(ANDROID_LOG_VERBOSE, "Maply", "Crash in VectorTileData::getVectors"); + catch (...) + { + __android_log_print(ANDROID_LOG_ERROR, "Maply", "Crash in VectorTileData::getVectors"); } - - return NULL; + return nullptr; } -JNIEXPORT jobject JNICALL Java_com_mousebird_maply_VectorTileData_getChangeSet - (JNIEnv *env, jobject obj) +extern "C" +JNIEXPORT jobject JNICALL Java_com_mousebird_maply_VectorTileData_getChangeSet(JNIEnv *env, jobject obj) { try { - VectorTileDataRef *tileData = VectorTileDataClassInfo::getClassInfo()->getObject(env,obj); - VectorObjectClassInfo *classInfo = VectorObjectClassInfo::getClassInfo(); - if (!tileData) - return NULL; - jobject newObj = MakeChangeSet(env,(*tileData)->changes); - (*tileData)->changes.clear(); - return newObj; + if (const auto tileData = VectorTileDataClassInfo::get(env,obj)) + { + const jobject newObj = MakeChangeSet(env, (*tileData)->changes); + (*tileData)->changes.clear(); + return newObj; + } } - catch (...) { - __android_log_print(ANDROID_LOG_VERBOSE, "Maply", "Crash in VectorTileData::getChangeSet"); + catch (...) + { + __android_log_print(ANDROID_LOG_ERROR, "Maply", "Crash in VectorTileData::getChangeSet"); } - - return NULL; + return nullptr; } diff --git a/android/library/maply/jni/src/geometryManager/GeometryManager_jni.cpp b/android/library/maply/jni/src/geometryManager/GeometryManager_jni.cpp index 932f62774d..e5a12e4b7a 100644 --- a/android/library/maply/jni/src/geometryManager/GeometryManager_jni.cpp +++ b/android/library/maply/jni/src/geometryManager/GeometryManager_jni.cpp @@ -42,8 +42,8 @@ JNIEXPORT void JNICALL Java_com_mousebird_maply_GeometryManager_initialise Scene *scene = SceneClassInfo::getClassInfo()->getObject(env, sceneObj); if (!scene) return; - GeometryManager *geomManager = dynamic_cast(scene->getManager(kWKGeometryManager)); - GeometryManagerClassInfo::getClassInfo()->setHandle(env,obj,geomManager); + GeometryManagerRef geomManager = std::dynamic_pointer_cast(scene->getManager(kWKGeometryManager)); + GeometryManagerClassInfo::getClassInfo()->setHandle(env,obj,new GeometryManagerRef(geomManager)); } catch (...) { @@ -59,6 +59,9 @@ JNIEXPORT void JNICALL Java_com_mousebird_maply_GeometryManager_dispose try { GeometryManagerClassInfo *classInfo = GeometryManagerClassInfo::getClassInfo(); + GeometryManagerRef *geomManager = GeometryManagerClassInfo::getClassInfo()->getObject(env, obj); + if (geomManager) + delete geomManager; classInfo->clearHandle(env, obj); } catch (...) @@ -72,7 +75,7 @@ JNIEXPORT jlong JNICALL Java_com_mousebird_maply_GeometryManager_addGeometry { try { - GeometryManager *geomManager = GeometryManagerClassInfo::getClassInfo()->getObject(env, obj); + GeometryManagerRef *geomManager = GeometryManagerClassInfo::getClassInfo()->getObject(env, obj); ChangeSetRef *changeSet = ChangeSetClassInfo::getClassInfo()->getObject(env,changeSetObj); GeometryInfoRef *geomInfo = GeometryInfoClassInfo::getClassInfo()->getObject(env,geomInfoObj); if (!geomManager || !changeSet || !geomInfo) @@ -104,13 +107,13 @@ JNIEXPORT jlong JNICALL Java_com_mousebird_maply_GeometryManager_addGeometry // Resolve a missing program if ((*geomInfo)->programID == EmptyIdentity) { - Program *prog = geomManager->getScene()->findProgramByName(MaplyDefaultTriangleShader); + Program *prog = (*geomManager)->getScene()->findProgramByName(MaplyDefaultTriangleShader); if (prog) (*geomInfo)->programID = prog->getId(); } - return geomManager->addGeometry(geoms,geomInsts,*(*geomInfo),*(changeSet->get())); + return (*geomManager)->addGeometry(geoms,geomInsts,*(*geomInfo),*(changeSet->get())); } catch (...) { @@ -125,7 +128,7 @@ JNIEXPORT jlong JNICALL Java_com_mousebird_maply_GeometryManager_addBaseGeometry { try { - GeometryManager *geomManager = GeometryManagerClassInfo::getClassInfo()->getObject(env, obj); + GeometryManagerRef *geomManager = GeometryManagerClassInfo::getClassInfo()->getObject(env, obj); ChangeSetRef *changeSet = ChangeSetClassInfo::getClassInfo()->getObject(env,changeSetObj); if (!geomManager || !changeSet) { @@ -145,7 +148,7 @@ JNIEXPORT jlong JNICALL Java_com_mousebird_maply_GeometryManager_addBaseGeometry // TODO: Pass in the geomInfo GeometryInfo geomInfo; - return geomManager->addBaseGeometry(geoms,geomInfo,*(changeSet->get())); + return (*geomManager)->addBaseGeometry(geoms,geomInfo,*(changeSet->get())); } catch (...) { @@ -160,7 +163,7 @@ JNIEXPORT jlong JNICALL Java_com_mousebird_maply_GeometryManager_addGeometryInst { try { - GeometryManager *geomManager = GeometryManagerClassInfo::getClassInfo()->getObject(env, obj); + GeometryManagerRef *geomManager = GeometryManagerClassInfo::getClassInfo()->getObject(env, obj); ChangeSetRef *changeSet = ChangeSetClassInfo::getClassInfo()->getObject(env,changeSetObj); GeometryInfoRef *geomInfo = GeometryInfoClassInfo::getClassInfo()->getObject(env,geomInfoObj); if (!geomManager || !changeSet || !geomInfo) @@ -179,7 +182,7 @@ JNIEXPORT jlong JNICALL Java_com_mousebird_maply_GeometryManager_addGeometryInst geomInsts.push_back(*geomInst); } - return geomManager->addGeometryInstances(baseGeomID,geomInsts,*(*geomInfo),*(changeSet->get())); + return (*geomManager)->addGeometryInstances(baseGeomID,geomInsts,*(*geomInfo),*(changeSet->get())); } catch (...) { @@ -195,7 +198,7 @@ JNIEXPORT jlong JNICALL Java_com_mousebird_maply_GeometryManager_addGeometryPoin try { GeometryManagerClassInfo *classInfo = GeometryManagerClassInfo::getClassInfo(); - GeometryManager *geomManager = classInfo->getObject(env, obj); + GeometryManagerRef *geomManager = classInfo->getObject(env, obj); ChangeSetRef *changeSet = ChangeSetClassInfo::getClassInfo()->getObject(env,changeSetObj); GeometryRawPoints *rawPoints = GeometryRawPointsClassInfo::getClassInfo()->getObject(env,pointsObj); Matrix4d *mat = Matrix4dClassInfo::getClassInfo()->getObject(env,matObj); @@ -207,7 +210,7 @@ JNIEXPORT jlong JNICALL Java_com_mousebird_maply_GeometryManager_addGeometryPoin return EmptyIdentity; } - return geomManager->addGeometryPoints(*rawPoints,*mat,*(*geomInfo),*(changeSet->get())); + return (*geomManager)->addGeometryPoints(*rawPoints,*mat,*(*geomInfo),*(changeSet->get())); } catch (...) { @@ -223,7 +226,7 @@ JNIEXPORT void JNICALL Java_com_mousebird_maply_GeometryManager_enableGeometry try { GeometryManagerClassInfo *classInfo = GeometryManagerClassInfo::getClassInfo(); - GeometryManager *geomManager = classInfo->getObject(env, obj); + GeometryManagerRef *geomManager = classInfo->getObject(env, obj); ChangeSetRef *changeSet = ChangeSetClassInfo::getClassInfo()->getObject(env,changeSetObj); if (!geomManager || !changeSet) return; @@ -231,7 +234,7 @@ JNIEXPORT void JNICALL Java_com_mousebird_maply_GeometryManager_enableGeometry SimpleIDSet idSet; ConvertLongArrayToSet(env,geomIDs,idSet); - geomManager->enableGeometry(idSet,enable,*(changeSet->get())); + (*geomManager)->enableGeometry(idSet,enable,*(changeSet->get())); } catch (...) { @@ -245,7 +248,7 @@ JNIEXPORT void JNICALL Java_com_mousebird_maply_GeometryManager_removeGeometry try { GeometryManagerClassInfo *classInfo = GeometryManagerClassInfo::getClassInfo(); - GeometryManager *geomManager = classInfo->getObject(env, obj); + GeometryManagerRef *geomManager = classInfo->getObject(env, obj); ChangeSetRef *changeSet = ChangeSetClassInfo::getClassInfo()->getObject(env,changeSetObj); if (!geomManager || !changeSet) return; @@ -253,7 +256,7 @@ JNIEXPORT void JNICALL Java_com_mousebird_maply_GeometryManager_removeGeometry SimpleIDSet idSet; ConvertLongArrayToSet(env,geomIDs,idSet); - geomManager->removeGeometry(idSet,*(changeSet->get())); + (*geomManager)->removeGeometry(idSet,*(changeSet->get())); } catch (...) { diff --git a/android/library/maply/jni/src/geometryManager/GeometryRawPoints_jni.cpp b/android/library/maply/jni/src/geometryManager/GeometryRawPoints_jni.cpp index 176db01a0f..15505b84cd 100644 --- a/android/library/maply/jni/src/geometryManager/GeometryRawPoints_jni.cpp +++ b/android/library/maply/jni/src/geometryManager/GeometryRawPoints_jni.cpp @@ -1,9 +1,8 @@ -/* - * GeometryRawPoints_jni.cpp +/* GeometryRawPoints_jni.cpp * WhirlyGlobeLib * * Created by sjg - * Copyright 2011-2016 mousebird consulting + * Copyright 2011-2021 mousebird consulting * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -15,7 +14,6 @@ * 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. - * */ #import "GeometryManager_jni.h" #import "com_mousebird_maply_GeometryRawPoints.h" @@ -23,16 +21,16 @@ using namespace WhirlyKit; using namespace Maply; -template<> GeometryRawPointsClassInfo *GeometryRawPointsClassInfo::classInfoObj = NULL; +template<> GeometryRawPointsClassInfo *GeometryRawPointsClassInfo::classInfoObj = nullptr; -JNIEXPORT void JNICALL Java_com_mousebird_maply_GeometryRawPoints_nativeInit -(JNIEnv *env, jclass cls) +extern "C" +JNIEXPORT void JNICALL Java_com_mousebird_maply_GeometryRawPoints_nativeInit(JNIEnv *env, jclass cls) { GeometryRawPointsClassInfo::getClassInfo(env, cls); } -JNIEXPORT void JNICALL Java_com_mousebird_maply_GeometryRawPoints_initialise -(JNIEnv *env, jobject obj) +extern "C" +JNIEXPORT void JNICALL Java_com_mousebird_maply_GeometryRawPoints_initialise(JNIEnv *env, jobject obj) { try { @@ -47,12 +45,12 @@ JNIEXPORT void JNICALL Java_com_mousebird_maply_GeometryRawPoints_initialise static std::mutex disposeMutex; -JNIEXPORT void JNICALL Java_com_mousebird_maply_GeometryRawPoints_dispose -(JNIEnv *env, jobject obj) +extern "C" +JNIEXPORT void JNICALL Java_com_mousebird_maply_GeometryRawPoints_dispose(JNIEnv *env, jobject obj) { try { - GeometryRawPointsClassInfo *classInfo = GeometryRawPointsClassInfo::getClassInfo(); + const auto classInfo = GeometryRawPointsClassInfo::getClassInfo(); classInfo->clearHandle(env, obj); } catch (...) @@ -61,17 +59,15 @@ JNIEXPORT void JNICALL Java_com_mousebird_maply_GeometryRawPoints_dispose } } -JNIEXPORT jboolean JNICALL Java_com_mousebird_maply_GeometryRawPoints_valid -(JNIEnv *env, jobject obj) +extern "C" +JNIEXPORT jboolean JNICALL Java_com_mousebird_maply_GeometryRawPoints_valid(JNIEnv *env, jobject obj) { try { - GeometryRawPointsClassInfo *classInfo = GeometryRawPointsClassInfo::getClassInfo(); - GeometryRawPoints *rawGeom = classInfo->getObject(env, obj); - if (!rawGeom) - return false; - - return rawGeom->valid(); + if (auto rawGeom = GeometryRawPointsClassInfo::get(env, obj)) + { + return rawGeom->valid(); + } } catch (...) { @@ -81,25 +77,22 @@ JNIEXPORT jboolean JNICALL Java_com_mousebird_maply_GeometryRawPoints_valid return false; } -JNIEXPORT void JNICALL Java_com_mousebird_maply_GeometryRawPoints_addIntValues -(JNIEnv *env, jobject obj, jstring nameStr, jintArray intArray) +extern "C" +JNIEXPORT void JNICALL Java_com_mousebird_maply_GeometryRawPoints_addIntValues(JNIEnv *env, jobject obj, jstring nameStr, jintArray intArray) { try { - GeometryRawPointsClassInfo *classInfo = GeometryRawPointsClassInfo::getClassInfo(); - GeometryRawPoints *rawGeom = classInfo->getObject(env, obj); - - if (!rawGeom) - return; - JavaString name(env,nameStr); - int attrId = rawGeom->findAttribute(StringIndexer::getStringID(name.cStr)); - if (attrId < 0) - return; - - std::vector intVec; - ConvertIntArray(env,intArray,intVec); - - rawGeom->addValues(attrId,intVec); + if (auto rawGeom = GeometryRawPointsClassInfo::get(env, obj)) + { + const JavaString name(env, nameStr); + const int attrId = rawGeom->findAttribute(StringIndexer::getStringID(name.getCString())); + if (attrId >= 0) + { + std::vector intVec; + ConvertIntArray(env, intArray, intVec); + rawGeom->addValues(attrId, intVec); + } + } } catch (...) { @@ -107,25 +100,22 @@ JNIEXPORT void JNICALL Java_com_mousebird_maply_GeometryRawPoints_addIntValues } } -JNIEXPORT void JNICALL Java_com_mousebird_maply_GeometryRawPoints_addFloatValues -(JNIEnv *env, jobject obj, jstring nameStr, jfloatArray floatArray) +extern "C" +JNIEXPORT void JNICALL Java_com_mousebird_maply_GeometryRawPoints_addFloatValues(JNIEnv *env, jobject obj, jstring nameStr, jfloatArray floatArray) { try { - GeometryRawPointsClassInfo *classInfo = GeometryRawPointsClassInfo::getClassInfo(); - GeometryRawPoints *rawGeom = classInfo->getObject(env, obj); - - if (!rawGeom) - return; - JavaString name(env,nameStr); - int attrId = rawGeom->findAttribute(StringIndexer::getStringID(name.cStr)); - if (attrId < 0) - return; - - std::vector floatVec; - ConvertFloatArray(env,floatArray,floatVec); - - rawGeom->addValues(attrId,floatVec); + if (auto rawGeom = GeometryRawPointsClassInfo::get(env, obj)) + { + const JavaString name(env, nameStr); + const int attrId = rawGeom->findAttribute(StringIndexer::getStringID(name.getCString())); + if (attrId >= 0) + { + std::vector floatVec; + ConvertFloatArray(env, floatArray, floatVec); + rawGeom->addValues(attrId, floatVec); + } + } } catch (...) { @@ -133,25 +123,22 @@ JNIEXPORT void JNICALL Java_com_mousebird_maply_GeometryRawPoints_addFloatValues } } -JNIEXPORT void JNICALL Java_com_mousebird_maply_GeometryRawPoints_addPoint2fValues -(JNIEnv *env, jobject obj, jstring nameStr, jfloatArray floatArray) +extern "C" +JNIEXPORT void JNICALL Java_com_mousebird_maply_GeometryRawPoints_addPoint2fValues(JNIEnv *env, jobject obj, jstring nameStr, jfloatArray floatArray) { try { - GeometryRawPointsClassInfo *classInfo = GeometryRawPointsClassInfo::getClassInfo(); - GeometryRawPoints *rawGeom = classInfo->getObject(env, obj); - - if (!rawGeom) - return; - JavaString name(env,nameStr); - int attrId = rawGeom->findAttribute(StringIndexer::getStringID(name.cStr)); - if (attrId < 0) - return; - - Point2fVector ptVec; - ConvertFloat2fArray(env,floatArray,ptVec); - - rawGeom->addPoints(attrId,ptVec); + if (auto rawGeom = GeometryRawPointsClassInfo::get(env, obj)) + { + const JavaString name(env, nameStr); + const int attrId = rawGeom->findAttribute(StringIndexer::getStringID(name.getCString())); + if (attrId >= 0) + { + Point2fVector ptVec; + ConvertFloat2fArray(env, floatArray, ptVec); + rawGeom->addPoints(attrId, ptVec); + } + } } catch (...) { @@ -159,25 +146,22 @@ JNIEXPORT void JNICALL Java_com_mousebird_maply_GeometryRawPoints_addPoint2fValu } } -JNIEXPORT void JNICALL Java_com_mousebird_maply_GeometryRawPoints_addPoint3fValues -(JNIEnv *env, jobject obj, jstring nameStr, jfloatArray floatArray) +extern "C" +JNIEXPORT void JNICALL Java_com_mousebird_maply_GeometryRawPoints_addPoint3fValues(JNIEnv *env, jobject obj, jstring nameStr, jfloatArray floatArray) { try { - GeometryRawPointsClassInfo *classInfo = GeometryRawPointsClassInfo::getClassInfo(); - GeometryRawPoints *rawGeom = classInfo->getObject(env, obj); - - if (!rawGeom) - return; - JavaString name(env,nameStr); - int attrId = rawGeom->findAttribute(StringIndexer::getStringID(name.cStr)); - if (attrId < 0) - return; - - Point3fVector ptVec; - ConvertFloat3fArray(env,floatArray,ptVec); - - rawGeom->addPoints(attrId,ptVec); + if (auto rawGeom = GeometryRawPointsClassInfo::get(env, obj)) + { + const JavaString name(env, nameStr); + const int attrId = rawGeom->findAttribute(StringIndexer::getStringID(name.getCString())); + if (attrId >= 0) + { + Point3fVector ptVec; + ConvertFloat3fArray(env, floatArray, ptVec); + rawGeom->addPoints(attrId, ptVec); + } + } } catch (...) { @@ -185,25 +169,22 @@ JNIEXPORT void JNICALL Java_com_mousebird_maply_GeometryRawPoints_addPoint3fValu } } -JNIEXPORT void JNICALL Java_com_mousebird_maply_GeometryRawPoints_addPoint3dValues -(JNIEnv *env, jobject obj, jstring nameStr, jdoubleArray doubleArray) +extern "C" +JNIEXPORT void JNICALL Java_com_mousebird_maply_GeometryRawPoints_addPoint3dValues(JNIEnv *env, jobject obj, jstring nameStr, jdoubleArray doubleArray) { try { - GeometryRawPointsClassInfo *classInfo = GeometryRawPointsClassInfo::getClassInfo(); - GeometryRawPoints *rawGeom = classInfo->getObject(env, obj); - - if (!rawGeom) - return; - JavaString name(env,nameStr); - int attrId = rawGeom->findAttribute(StringIndexer::getStringID(name.cStr)); - if (attrId < 0) - return; - - Point3dVector ptVec; - ConvertFloat3dArray(env,doubleArray,ptVec); - - rawGeom->addPoints(attrId,ptVec); + if (auto rawGeom = GeometryRawPointsClassInfo::get(env, obj)) + { + const JavaString name(env, nameStr); + const int attrId = rawGeom->findAttribute(StringIndexer::getStringID(name.getCString())); + if (attrId >= 0) + { + Point3dVector ptVec; + ConvertFloat3dArray(env, doubleArray, ptVec); + rawGeom->addPoints(attrId, ptVec); + } + } } catch (...) { @@ -211,25 +192,23 @@ JNIEXPORT void JNICALL Java_com_mousebird_maply_GeometryRawPoints_addPoint3dValu } } +extern "C" JNIEXPORT void JNICALL Java_com_mousebird_maply_GeometryRawPoints_addPoint4fValues -(JNIEnv *env, jobject obj, jstring nameStr, jfloatArray floatArray) + (JNIEnv *env, jobject obj, jstring nameStr, jfloatArray floatArray) { try { - GeometryRawPointsClassInfo *classInfo = GeometryRawPointsClassInfo::getClassInfo(); - GeometryRawPoints *rawGeom = classInfo->getObject(env, obj); - - if (!rawGeom) - return; - JavaString name(env,nameStr); - int attrId = rawGeom->findAttribute(StringIndexer::getStringID(name.cStr)); - if (attrId < 0) - return; - - Vector4fVector ptVec; - ConvertFloat4fArray(env,floatArray,ptVec); - - rawGeom->addPoints(attrId,ptVec); + if (auto rawGeom = GeometryRawPointsClassInfo::get(env, obj)) + { + const JavaString name(env, nameStr); + const int attrId = rawGeom->findAttribute(StringIndexer::getStringID(name.getCString())); + if (attrId >= 0) + { + Vector4fVector ptVec; + ConvertFloat4fArray(env, floatArray, ptVec); + rawGeom->addPoints(attrId, ptVec); + } + } } catch (...) { @@ -237,24 +216,20 @@ JNIEXPORT void JNICALL Java_com_mousebird_maply_GeometryRawPoints_addPoint4fValu } } -JNIEXPORT jint JNICALL Java_com_mousebird_maply_GeometryRawPoints_addAttributeNative -(JNIEnv *env, jobject obj, jstring nameStr, int type) +extern "C" +JNIEXPORT jint JNICALL Java_com_mousebird_maply_GeometryRawPoints_addAttributeNative(JNIEnv *env, jobject obj, jstring nameStr, int type) { try { - GeometryRawPointsClassInfo *classInfo = GeometryRawPointsClassInfo::getClassInfo(); - GeometryRawPoints *rawGeom = classInfo->getObject(env, obj); - - if (!rawGeom) - return -1; - - JavaString name(env,nameStr); - return rawGeom->addAttribute(StringIndexer::getStringID(name.cStr),(GeomRawDataType)type); + if (auto rawGeom = GeometryRawPointsClassInfo::get(env, obj)) + { + const JavaString name(env, nameStr); + return rawGeom->addAttribute(StringIndexer::getStringID(name.getCString()), (GeomRawDataType) type); + } } catch (...) { __android_log_print(ANDROID_LOG_VERBOSE, "Maply", "Crash in GeometryRawPoints::addAttribute()"); } - return -1; } diff --git a/android/library/maply/jni/src/jni_util/Exceptions_jni.cpp b/android/library/maply/jni/src/jni_util/Exceptions_jni.cpp new file mode 100644 index 0000000000..f737a0e13e --- /dev/null +++ b/android/library/maply/jni/src/jni_util/Exceptions_jni.cpp @@ -0,0 +1,152 @@ +/* Exceptions_jni.cpp + * WhirlyGlobeLib + * + * Created by Tim Sylvester on 3/8/2021 + * Copyright 2021-2021 mousebird consulting + * + * 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. + */ + +#import "Maply_jni.h" + +namespace WhirlyKit { + +// https://stackoverflow.com/a/10410117/135138 +static void appendExceptionTraceMessages( + JNIEnv* env, + std::ostringstream& msg, + jthrowable ex, + jmethodID throwable_getCause, + jmethodID throwable_getStackTrace, + jmethodID throwable_toString, + jmethodID frame_toString) +{ + if (!ex) + { + return; + } + + // Get the array of StackTraceElements. + const auto frames = (jobjectArray)env->CallObjectMethod(ex, throwable_getStackTrace); + + // Add Throwable.toString() before descending + // stack trace messages. + if (frames) + { + if (const auto msg_obj = (jstring)env->CallObjectMethod(ex, throwable_toString)) + { + if (const char* msg_str = env->GetStringUTFChars(msg_obj, nullptr)) + { + msg << msg_str; + env->ReleaseStringUTFChars(msg_obj, msg_str); + } + env->DeleteLocalRef(msg_obj); + } + } + + // Append stack trace messages if there are any. + const jsize frames_length = frames ? env->GetArrayLength(frames) : 0; + for (jsize i = 0; i < frames_length; i++) + { + // Get the string returned from the 'toString()' method of the next frame and append it to the error message. + if (const auto frame = env->GetObjectArrayElement(frames, i)) + { + if (const auto msg_obj = (jstring) env->CallObjectMethod(frame, frame_toString)) + { + if (const char* msg_str = env->GetStringUTFChars(msg_obj, nullptr)) + { + msg << "\n "; + msg << msg_str; + env->ReleaseStringUTFChars(msg_obj, msg_str); + } + env->DeleteLocalRef(msg_obj); + } + env->DeleteLocalRef(frame); + } + } + + // If ot has a cause then append the stack trace messages from the cause. + if (frames) + { + if (auto cause = (jthrowable)env->CallObjectMethod(ex, throwable_getCause)) + { + appendExceptionTraceMessages(env, msg, cause, throwable_getCause, + throwable_getStackTrace, throwable_toString, + frame_toString); + } + } +} +static jmethodID mid_throwable_getCause = nullptr; +static jmethodID mid_throwable_getStackTrace = nullptr; +static jmethodID mid_throwable_toString = nullptr; +static jmethodID mid_frame_toString = nullptr; + +static jthrowable makeThrowable(JNIEnv* env) +{ + auto tc = env->FindClass("java/lang/Throwable"); + auto ctor = env->GetMethodID(tc, "", "()V"); + return (jthrowable)env->NewObject(tc,ctor); +} + +void appendExceptionTraceMessages(JNIEnv* env, std::ostringstream& msg, jthrowable ex) +{ + if (!mid_throwable_getCause) + { + jclass throwable_class = env->FindClass("java/lang/Throwable"); + jclass frame_class = env->FindClass("java/lang/StackTraceElement"); + + mid_throwable_getCause = env->GetMethodID(throwable_class, "getCause", "()Ljava/lang/Throwable;"); + mid_throwable_getStackTrace = env->GetMethodID(throwable_class, "getStackTrace", "()[Ljava/lang/StackTraceElement;"); + mid_throwable_toString = env->GetMethodID(throwable_class, "toString", "()Ljava/lang/String;"); + mid_frame_toString = env->GetMethodID(frame_class, "toString", "()Ljava/lang/String;"); + } + appendExceptionTraceMessages(env, msg, ex, mid_throwable_getCause, + mid_throwable_getStackTrace, mid_throwable_toString, mid_frame_toString); +} + +std::string getExceptionTraceMessages(JNIEnv* env, jthrowable ex) +{ + std::ostringstream ss; + appendExceptionTraceMessages(env, ss, ex); + ss.flush(); + return ss.str(); +} + +void logJVMException(JNIEnv* env, jthrowable throwable, const char* where, android_LogPriority priority) +{ + const auto trace = getExceptionTraceMessages(env, throwable); + __android_log_print(priority, "Maply", where ? "Exception in %s:\n%s" : "%s%s", + where ? where : "", trace.c_str()); +} + +bool logAndClearJVMException(JNIEnv* env, const char* where, android_LogPriority priority) +{ + if (auto ex = env->ExceptionOccurred()) + { + env->ExceptionClear(); + logJVMException(env,ex,where,priority); + return true; + } + return false; +} + +void logStackTrace(JNIEnv* env, const char* where, android_LogPriority priority) +{ + logJVMException(env,makeThrowable(env),where,priority); +} + +std::string getStackTrace(JNIEnv* env) +{ + return getExceptionTraceMessages(env, makeThrowable(env)); +} + +} \ No newline at end of file diff --git a/android/library/maply/jni/src/jni_util/Maply_jni.cpp b/android/library/maply/jni/src/jni_util/Maply_jni.cpp index 5a12cc306f..8a27580397 100644 --- a/android/library/maply/jni/src/jni_util/Maply_jni.cpp +++ b/android/library/maply/jni/src/jni_util/Maply_jni.cpp @@ -1,9 +1,8 @@ -/* - * Maply_jni.cpp +/* Maply_jni.cpp * WhirlyGlobeLib * * Created by Steve Gifford on 6/2/14. - * Copyright 2011-2016 mousebird consulting + * Copyright 2011-2021 mousebird consulting * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -15,7 +14,6 @@ * 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. - * */ #import "Maply_jni.h" @@ -23,10 +21,13 @@ using namespace Eigen; using namespace WhirlyKit; -JavaObjectArrayHelper::JavaObjectArrayHelper(JNIEnv *env,jobjectArray objArray) -: env(env), objArray(objArray), which(0), curObj(NULL) +JavaObjectArrayHelper::JavaObjectArrayHelper(JNIEnv *env,jobjectArray objArray) : + env(env), + objArray(objArray), + nextIndex(0), + curObj(nullptr), + count((env && objArray) ? env->GetArrayLength(objArray) : 0) { - count = env->GetArrayLength(objArray); } JavaObjectArrayHelper::~JavaObjectArrayHelper() @@ -37,43 +38,33 @@ JavaObjectArrayHelper::~JavaObjectArrayHelper() } } -int JavaObjectArrayHelper::numObjects() -{ - return count; -} - jobject JavaObjectArrayHelper::getNextObject() { - if (which >= count) - return NULL; if (curObj) + { env->DeleteLocalRef(curObj); - curObj = env->GetObjectArrayElement(objArray,which); - which++; - - return curObj; + curObj = nullptr; + } + if (nextIndex >= count) + { + return nullptr; + } + return curObj = env->GetObjectArrayElement(objArray,nextIndex++); } // Have to instantiate the static members somewhere // But just some of the general ones. The rest are in their own modules. -JavaDoubleClassInfo *JavaDoubleClassInfo::classInfoObj = NULL; -JavaIntegerClassInfo *JavaIntegerClassInfo::classInfoObj = NULL; -JavaLongClassInfo *JavaLongClassInfo::classInfoObj = NULL; -JavaHashMapInfo *JavaHashMapInfo::classInfoObj = NULL; -JavaListInfo *JavaListInfo::classInfoObj = NULL; - -// Note: Move these out -/* -template<> MaplySceneRendererInfo *MaplySceneRendererInfo::classInfoObj = NULL; -template<> MapboxVectorTileParserClassInfo *MapboxVectorTileParserClassInfo::classInfoObj = NULL; -template<> GeoJSONSourceClassInfo *GeoJSONSourceClassInfo::classInfoObj = NULL; -*/ +JavaDoubleClassInfo *JavaDoubleClassInfo::classInfoObj = nullptr; +JavaIntegerClassInfo *JavaIntegerClassInfo::classInfoObj = nullptr; +JavaLongClassInfo *JavaLongClassInfo::classInfoObj = nullptr; +JavaHashMapInfo *JavaHashMapInfo::classInfoObj = nullptr; +JavaListInfo *JavaListInfo::classInfoObj = nullptr; void ConvertIntArray(JNIEnv *env,jintArray &intArray,std::vector &intVec) { - int *ints = env->GetIntArrayElements(intArray, NULL); - int len = env->GetArrayLength(intArray); + int *ints = env->GetIntArrayElements(intArray, nullptr); + const int len = env->GetArrayLength(intArray); intVec.resize(len); for (int ii=0;ii &intVec) void ConvertLongLongArray(JNIEnv *env,jlongArray &longArray,std::vector &longVec) { jlong *longs = env->GetLongArrayElements(longArray, NULL); - int len = env->GetArrayLength(longArray); + const int len = env->GetArrayLength(longArray); longVec.resize(len); for (int ii=0;ii &floatVec) { float *floats = env->GetFloatArrayElements(floatArray, NULL); - int len = env->GetArrayLength(floatArray); + const int len = env->GetArrayLength(floatArray); floatVec.resize(len); for (int ii=0;ii &f void ConvertDoubleArray(JNIEnv *env,jdoubleArray &doubleArray,std::vector &doubleVec) { double *doubles = env->GetDoubleArrayElements(doubleArray, NULL); - int len = env->GetArrayLength(doubleArray); + const int len = env->GetArrayLength(doubleArray); doubleVec.resize(len); for (int ii=0;ii &boolVec) { jboolean *bools = env->GetBooleanArrayElements(boolArray, NULL); - int len = env->GetArrayLength(boolArray); + const int len = env->GetArrayLength(boolArray); boolVec.resize(len); for (int ii=0;ii &bo void ConvertStringArray(JNIEnv *env,jobjectArray &objArray,std::vector &strVec) { - jsize count = env->GetArrayLength(objArray); - for (unsigned int ii=0;iiGetObjectArrayElement(objArray,ii); - if (string) - strVec.push_back(env->GetStringUTFChars(string, NULL)); - else - strVec.push_back(""); + const jsize count = env->GetArrayLength(objArray); + for (unsigned int ii=0;iiGetObjectArrayElement(objArray,ii); + strVec.emplace_back(string ? env->GetStringUTFChars(string, nullptr) : std::string()); } } void ConvertFloat2fArray(JNIEnv *env,jfloatArray &floatArray,Point2fVector &ptVec) { float *floats = env->GetFloatArrayElements(floatArray, NULL); - int len = env->GetArrayLength(floatArray)/2; + const int len = env->GetArrayLength(floatArray)/2; ptVec.resize(len); for (int ii=0;iiGetFloatArrayElements(floatArray, NULL); - int len = env->GetArrayLength(floatArray)/3; + const int len = env->GetArrayLength(floatArray)/3; ptVec.resize(len); for (int ii=0;iiGetDoubleArrayElements(doubleArray, NULL); - int len = env->GetArrayLength(doubleArray)/34; + const int len = env->GetArrayLength(doubleArray)/34; ptVec.resize(len); for (int ii=0;iiGetFloatArrayElements(floatArray, NULL); - int len = env->GetArrayLength(floatArray)/4; + const int len = env->GetArrayLength(floatArray)/4; ptVec.resize(len); for (int ii=0;ii &intSet) { - int idCount = env->GetArrayLength(idArrayObj); - jlong *ids = env->GetLongArrayElements(idArrayObj, NULL); + const int idCount = env->GetArrayLength(idArrayObj); if (idCount == 0) return; - + + jlong *ids = env->GetLongArrayElements(idArrayObj, NULL); for (int ii=0;iiReleaseLongArrayElements(idArrayObj,ids, 0); } -JavaString::JavaString(JNIEnv *env,jstring &str) -: str(str), env(env) +JavaString::JavaString(JNIEnv *env,jstring str) : str(str), env(env) { - cStr = env->GetStringUTFChars(str,0); + cStr = str ? env->GetStringUTFChars(str,nullptr) : nullptr; +} + +JavaString::JavaString(JavaString &&other) noexcept : + env(other.env), + str(other.str), + cStr(other.cStr) +{ + other.env = nullptr; + other.cStr = nullptr; + other.str = nullptr; } JavaString::~JavaString() { - env->ReleaseStringUTFChars(str, cStr); + if (cStr) + { + env->ReleaseStringUTFChars(str, cStr); + } } JavaBooleanArray::JavaBooleanArray(JNIEnv *env,jbooleanArray &array) @@ -257,69 +257,79 @@ JavaDoubleArray::~JavaDoubleArray() env->ReleaseDoubleArrayElements(array,rawDouble, 0); } -jlongArray BuildLongArray(JNIEnv *env,std::vector &longVec) +jlongArray BuildLongArray(JNIEnv *env,const std::vector &longVec) { if (longVec.empty()) - return NULL; - - jlongArray newArray = env->NewLongArray(longVec.size()); - if (!newArray) - return NULL; + return nullptr; - env->SetLongArrayRegion(newArray, 0, longVec.size(), (jlong *)&longVec[0]); - return newArray; + if (jlongArray newArray = env->NewLongArray(longVec.size())) + { + env->SetLongArrayRegion(newArray, 0, longVec.size(), (jlong *)&longVec[0]); + return newArray; + } + return nullptr; } -jdoubleArray BuildDoubleArray(JNIEnv *env,std::vector &doubleVec) +jdoubleArray BuildDoubleArray(JNIEnv *env,const std::vector &doubleVec) { if (doubleVec.empty()) - return NULL; - - jdoubleArray newArray = env->NewDoubleArray(doubleVec.size()); - if (!newArray) - return NULL; + return nullptr; - env->SetDoubleArrayRegion(newArray, 0, doubleVec.size(), (jdouble *)&doubleVec[0]); - return newArray; + if (jdoubleArray newArray = env->NewDoubleArray(doubleVec.size())) + { + env->SetDoubleArrayRegion(newArray, 0, doubleVec.size(), (jdouble *)&doubleVec[0]); + return newArray; + } + return nullptr; } -jintArray BuildIntArray(JNIEnv *env,std::vector &intVec) +jintArray BuildIntArray(JNIEnv *env,const std::vector &intVec) { if (intVec.empty()) - return NULL; + return nullptr; - jintArray newArray = env->NewIntArray(intVec.size()); - if (!newArray) - return NULL; + if (jintArray newArray = env->NewIntArray(intVec.size())) + { + env->SetIntArrayRegion(newArray, 0, intVec.size(), (jint *)&intVec[0]); + return newArray; + } + return nullptr; +} - env->SetIntArrayRegion(newArray, 0, intVec.size(), (jint *)&intVec[0]); - return newArray; +jobjectArray BuildObjectArray(JNIEnv *env,jclass cls,jobject singleObj) +{ + if (auto newArray = env->NewObjectArray(1,cls,nullptr)) { + env->SetObjectArrayElement(newArray,0,singleObj); + return newArray; + } + return nullptr; } -jobjectArray BuildObjectArray(JNIEnv *env,jclass cls,std::vector &objVec) +jobjectArray BuildObjectArray(JNIEnv *env,jclass cls,const std::vector &objVec) { if (objVec.empty()) - return NULL; - - jobjectArray newArray = env->NewObjectArray(objVec.size(),cls,NULL); - if (!newArray) - return NULL; + return nullptr; - for (unsigned int ii=0;iiSetObjectArrayElement(newArray,ii,objVec[ii]); - return newArray; + if (jobjectArray newArray = env->NewObjectArray(objVec.size(),cls,nullptr)) + { + for (unsigned int ii=0;iiSetObjectArrayElement(newArray,ii,objVec[ii]); + return newArray; + } + return nullptr; } -jobjectArray BuildStringArray(JNIEnv *env,std::vector &objVec) +jobjectArray BuildStringArray(JNIEnv *env,const std::vector &objVec) { if (objVec.empty()) - return NULL; + return nullptr; - jobjectArray newArray = env->NewObjectArray(objVec.size(),env->FindClass("java/lang/String"),NULL); - if (!newArray) - return NULL; - - for (unsigned int ii=0;iiSetObjectArrayElement(newArray,ii,env->NewStringUTF(objVec[ii].c_str())); - return newArray; + if (jobjectArray newArray = env->NewObjectArray(objVec.size(),env->FindClass("java/lang/String"),nullptr)) + { + for (unsigned int ii=0;iiSetObjectArrayElement(newArray,ii,env->NewStringUTF(objVec[ii].c_str())); + return newArray; + } + return nullptr; } + diff --git a/android/library/maply/jni/src/labelsMarkers/InternalLabel_jni.cpp b/android/library/maply/jni/src/labelsMarkers/InternalLabel_jni.cpp index 65d88a02ef..4a63760f41 100644 --- a/android/library/maply/jni/src/labelsMarkers/InternalLabel_jni.cpp +++ b/android/library/maply/jni/src/labelsMarkers/InternalLabel_jni.cpp @@ -1,9 +1,8 @@ -/* - * InternalLabel_jni.cpp +/* InternalLabel_jni.cpp * WhirlyGlobeLib * * Created by Steve Gifford on 6/2/14. - * Copyright 2011-2016 mousebird consulting + * Copyright 2011-2021 mousebird consulting * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -15,7 +14,6 @@ * 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. - * */ #import "LabelsAndMarkers_jni.h" @@ -25,16 +23,16 @@ using namespace WhirlyKit; typedef JavaClassInfo LabelClassInfo; -template<> LabelClassInfo *LabelClassInfo::classInfoObj = NULL; +template<> LabelClassInfo *LabelClassInfo::classInfoObj = nullptr; -JNIEXPORT void JNICALL Java_com_mousebird_maply_InternalLabel_nativeInit - (JNIEnv *env, jclass cls) +extern "C" +JNIEXPORT void JNICALL Java_com_mousebird_maply_InternalLabel_nativeInit(JNIEnv *env, jclass cls) { LabelClassInfo::getClassInfo(env,cls); } -JNIEXPORT void JNICALL Java_com_mousebird_maply_InternalLabel_initialise - (JNIEnv *env, jobject obj) +extern "C" +JNIEXPORT void JNICALL Java_com_mousebird_maply_InternalLabel_initialise(JNIEnv *env, jobject obj) { try { @@ -43,258 +41,244 @@ JNIEXPORT void JNICALL Java_com_mousebird_maply_InternalLabel_initialise } catch (...) { - __android_log_print(ANDROID_LOG_VERBOSE, "Maply", "Crash in InternalLabel::initialise()"); + __android_log_print(ANDROID_LOG_ERROR, "Maply", "Crash in InternalLabel::initialise()"); } } static std::mutex disposeMutex; -JNIEXPORT void JNICALL Java_com_mousebird_maply_InternalLabel_dispose - (JNIEnv *env, jobject obj) +extern "C" +JNIEXPORT void JNICALL Java_com_mousebird_maply_InternalLabel_dispose(JNIEnv *env, jobject obj) { try { - LabelClassInfo *classInfo = LabelClassInfo::getClassInfo(); - { - std::lock_guard lock(disposeMutex); - SingleLabel *label = classInfo->getObject(env,obj); - if (!label) - return; - delete label; - - classInfo->clearHandle(env,obj); - } + const auto classInfo = LabelClassInfo::getClassInfo(); + std::lock_guard lock(disposeMutex); + if (auto label = LabelClassInfo::get(env,obj)) + { + delete label; + classInfo->clearHandle(env, obj); + } } catch (...) { - __android_log_print(ANDROID_LOG_VERBOSE, "Maply", "Crash in InternalLabel::dispose()"); + __android_log_print(ANDROID_LOG_ERROR, "Maply", "Crash in InternalLabel::dispose()"); } } -JNIEXPORT void JNICALL Java_com_mousebird_maply_InternalLabel_setSelectID -(JNIEnv *env, jobject obj, jlong newID) +extern "C" +JNIEXPORT void JNICALL Java_com_mousebird_maply_InternalLabel_setSelectID(JNIEnv *env, jobject obj, jlong newID) { try { - LabelClassInfo *classInfo = LabelClassInfo::getClassInfo(); - SingleLabel *label = classInfo->getObject(env,obj); - if (!label) - return; - - label->selectID = newID; + if (auto label = LabelClassInfo::get(env,obj)) + { + label->selectID = newID; + } } catch (...) { - __android_log_print(ANDROID_LOG_VERBOSE, "Maply", "Crash in InternalLabel::setSelectID()"); + __android_log_print(ANDROID_LOG_ERROR, "Maply", "Crash in InternalLabel::setSelectID()"); } } -JNIEXPORT void JNICALL Java_com_mousebird_maply_InternalLabel_setLoc - (JNIEnv *env, jobject obj, jobject ptObj) +extern "C" +JNIEXPORT void JNICALL Java_com_mousebird_maply_InternalLabel_setLoc(JNIEnv *env, jobject obj, jobject ptObj) { try { - LabelClassInfo *classInfo = LabelClassInfo::getClassInfo(); - SingleLabel *label = classInfo->getObject(env,obj); - Point2d *pt = Point2dClassInfo::getClassInfo()->getObject(env,ptObj); - if (!label || !pt) - return; - - label->loc.x() = pt->x(); - label->loc.y() = pt->y(); + if (auto label = LabelClassInfo::get(env,obj)) + { + if (const auto pt = Point2dClassInfo::getClassInfo()->getObject(env, ptObj)) + { + label->loc.x() = pt->x(); + label->loc.y() = pt->y(); + } + } } catch (...) { - __android_log_print(ANDROID_LOG_VERBOSE, "Maply", "Crash in InternalLabel::setLoc()"); + __android_log_print(ANDROID_LOG_ERROR, "Maply", "Crash in InternalLabel::setLoc()"); } } -JNIEXPORT void JNICALL Java_com_mousebird_maply_InternalLabel_setEndLoc - (JNIEnv *env, jobject obj, jobject ptObj) +extern "C" +JNIEXPORT void JNICALL Java_com_mousebird_maply_InternalLabel_setEndLoc(JNIEnv *env, jobject obj, jobject ptObj) { try { - LabelClassInfo *classInfo = LabelClassInfo::getClassInfo(); - SingleLabel *label = classInfo->getObject(env,obj); - Point2d *pt = Point2dClassInfo::getClassInfo()->getObject(env,ptObj); - if (!label || !pt) - return; - - label->endLoc.x() = pt->x(); - label->endLoc.y() = pt->y(); + if (auto label = LabelClassInfo::get(env,obj)) + { + if (const auto pt = Point2dClassInfo::getClassInfo()->getObject(env,ptObj)) + { + label->endLoc.x() = pt->x(); + label->endLoc.y() = pt->y(); + } + } } catch (...) { - __android_log_print(ANDROID_LOG_VERBOSE, "Maply", "Crash in InternalLabel::setEndLoc()"); + __android_log_print(ANDROID_LOG_ERROR, "Maply", "Crash in InternalLabel::setEndLoc()"); } } -JNIEXPORT void JNICALL Java_com_mousebird_maply_InternalLabel_setAnimationRange - (JNIEnv *env, jobject obj, jdouble startTime, jdouble endTime) +extern "C" +JNIEXPORT void JNICALL Java_com_mousebird_maply_InternalLabel_setAnimationRange(JNIEnv *env, jobject obj, jdouble startTime, jdouble endTime) { try { - LabelClassInfo *classInfo = LabelClassInfo::getClassInfo(); - SingleLabel *label = classInfo->getObject(env,obj); - if (!label) - return; - - label->startTime = startTime; - label->endTime = endTime; + if (auto label = LabelClassInfo::get(env,obj)) + { + label->startTime = startTime; + label->endTime = endTime; + } } catch (...) { - __android_log_print(ANDROID_LOG_VERBOSE, "Maply", "Crash in InternalLabel::setAnimationRange()"); + __android_log_print(ANDROID_LOG_ERROR, "Maply", "Crash in InternalLabel::setAnimationRange()"); } } -JNIEXPORT void JNICALL Java_com_mousebird_maply_InternalLabel_setRotation - (JNIEnv *env, jobject obj, jdouble rot) +extern "C" +JNIEXPORT void JNICALL Java_com_mousebird_maply_InternalLabel_setRotation(JNIEnv *env, jobject obj, jdouble rot) { try { - LabelClassInfo *classInfo = LabelClassInfo::getClassInfo(); - SingleLabel *label = classInfo->getObject(env,obj); - if (!label) - return; - - label->rotation = rot; + if (auto label = LabelClassInfo::get(env,obj)) + { + label->rotation = rot; + } } catch (...) { - __android_log_print(ANDROID_LOG_VERBOSE, "Maply", "Crash in InternalLabel::setRotation()"); + __android_log_print(ANDROID_LOG_ERROR, "Maply", "Crash in InternalLabel::setRotation()"); } } -JNIEXPORT void JNICALL Java_com_mousebird_maply_InternalLabel_setLockRotation - (JNIEnv *env, jobject obj, jboolean lockRotation) +extern "C" +JNIEXPORT void JNICALL Java_com_mousebird_maply_InternalLabel_setLockRotation(JNIEnv *env, jobject obj, jboolean lockRotation) { try { - LabelClassInfo *classInfo = LabelClassInfo::getClassInfo(); - SingleLabel *label = classInfo->getObject(env,obj); - if (!label) - return; - - label->keepUpright = !lockRotation; + if (auto label = LabelClassInfo::get(env,obj)) + { + label->keepUpright = !lockRotation; + } } catch (...) { - __android_log_print(ANDROID_LOG_VERBOSE, "Maply", "Crash in InternalLabel::setLockRotation()"); + __android_log_print(ANDROID_LOG_ERROR, "Maply", "Crash in InternalLabel::setLockRotation()"); } } -JNIEXPORT void JNICALL Java_com_mousebird_maply_InternalLabel_addText -(JNIEnv *env, jobject obj, jintArray textArray, jint len) +extern "C" +JNIEXPORT void JNICALL Java_com_mousebird_maply_InternalLabel_addText(JNIEnv *env, jobject obj, jintArray textArray, jint len) { try { - LabelClassInfo *classInfo = LabelClassInfo::getClassInfo(); - SingleLabelAndroid *label = classInfo->getObject(env,obj); - if (!label) - return; - - JavaIntArray intArray(env,textArray); - std::vector codePoints; - codePoints.resize(len); - for (int ii=0;iicodePointsLines.push_back(codePoints); + if (auto label = LabelClassInfo::get(env,obj)) + { + JavaIntArray intArray(env, textArray); + std::vector codePoints; + codePoints.resize(len); + for (int ii = 0; ii < intArray.len; ii++) + { + codePoints[ii] = intArray.rawInt[ii]; + } + label->codePointsLines.push_back(codePoints); + } } catch (...) { - __android_log_print(ANDROID_LOG_VERBOSE, "Maply", "Crash in InternalLabel::addText()"); + __android_log_print(ANDROID_LOG_ERROR, "Maply", "Crash in InternalLabel::addText()"); } } -JNIEXPORT void JNICALL Java_com_mousebird_maply_InternalLabel_setOffset - (JNIEnv *env, jobject obj, jobject ptObj) +extern "C" +JNIEXPORT void JNICALL Java_com_mousebird_maply_InternalLabel_setOffset(JNIEnv *env, jobject obj, jobject ptObj) { try { - LabelClassInfo *classInfo = LabelClassInfo::getClassInfo(); - SingleLabelAndroid *label = classInfo->getObject(env,obj); - Point2d *pt = Point2dClassInfo::getClassInfo()->getObject(env,ptObj); - if (!label || !pt) - return; - - label->screenOffset.x() = pt->x(); - label->screenOffset.y() = pt->y(); + if (auto label = LabelClassInfo::get(env,obj)) + { + if (const auto pt = Point2dClassInfo::get(env, ptObj)) + { + label->screenOffset.x() = pt->x(); + label->screenOffset.y() = pt->y(); + } + } } catch (...) { - __android_log_print(ANDROID_LOG_VERBOSE, "Maply", "Crash in InternalLabel::setOffset()"); + __android_log_print(ANDROID_LOG_ERROR, "Maply", "Crash in InternalLabel::setOffset()"); } } -JNIEXPORT void JNICALL Java_com_mousebird_maply_InternalLabel_setLayoutPlacement - (JNIEnv *env, jobject obj, jint layoutPlacement) +extern "C" +JNIEXPORT void JNICALL Java_com_mousebird_maply_InternalLabel_setLayoutPlacement(JNIEnv *env, jobject obj, jint layoutPlacement) { try { - LabelClassInfo *classInfo = LabelClassInfo::getClassInfo(); - SingleLabel *label = classInfo->getObject(env,obj); - if (!label) - return; - label->layoutPlacement = layoutPlacement; + if (auto label = LabelClassInfo::get(env,obj)) + { + label->layoutPlacement = layoutPlacement; + } } catch (...) { - __android_log_print(ANDROID_LOG_VERBOSE, "Maply", "Crash in InternalLabel::setLayoutPlacement()"); + __android_log_print(ANDROID_LOG_ERROR, "Maply", "Crash in InternalLabel::setLayoutPlacement()"); } - } -JNIEXPORT void JNICALL Java_com_mousebird_maply_InternalLabel_setLayoutImportance -(JNIEnv *env, jobject obj, jfloat layoutImportance) +extern "C" +JNIEXPORT void JNICALL Java_com_mousebird_maply_InternalLabel_setLayoutImportance(JNIEnv *env, jobject obj, jfloat layoutImportance) { try { - LabelClassInfo *classInfo = LabelClassInfo::getClassInfo(); - SingleLabel *label = classInfo->getObject(env,obj); - if (!label) - return; - label->layoutImportance = layoutImportance; - if (layoutImportance < MAXFLOAT) - label->layoutEngine = true; + if (auto label = LabelClassInfo::get(env,obj)) + { + label->layoutImportance = layoutImportance; + if (layoutImportance < MAXFLOAT) + { + label->layoutEngine = true; + } + } } catch (...) { - __android_log_print(ANDROID_LOG_VERBOSE, "Maply", "Crash in InternalLabel::setLayoutImportance()"); + __android_log_print(ANDROID_LOG_ERROR, "Maply", "Crash in InternalLabel::setLayoutImportance()"); } } -JNIEXPORT void JNICALL Java_com_mousebird_maply_InternalLabel_setLayoutSize - (JNIEnv *env, jobject obj, jdouble sizeX, jdouble sizeY) +extern "C" +JNIEXPORT void JNICALL Java_com_mousebird_maply_InternalLabel_setLayoutSize(JNIEnv *env, jobject obj, jdouble sizeX, jdouble sizeY) { try { - LabelClassInfo *classInfo = LabelClassInfo::getClassInfo(); - SingleLabel *label = classInfo->getObject(env,obj); - if (!label) - return; - label->layoutSize = Point2d(sizeX,sizeY); + if (auto label = LabelClassInfo::get(env,obj)) + { + label->layoutSize = Point2d(sizeX,sizeY); + } } catch (...) { - __android_log_print(ANDROID_LOG_VERBOSE, "Maply", "Crash in InternalLabel::setLayoutSize()"); + __android_log_print(ANDROID_LOG_ERROR, "Maply", "Crash in InternalLabel::setLayoutSize()"); } } -JNIEXPORT void JNICALL Java_com_mousebird_maply_InternalLabel_setUniqueID - (JNIEnv *env, jobject obj, jstring uniqueStr) +extern "C" +JNIEXPORT void JNICALL Java_com_mousebird_maply_InternalLabel_setUniqueID(JNIEnv *env, jobject obj, jstring uniqueStr) { try { - LabelClassInfo *classInfo = LabelClassInfo::getClassInfo(); - SingleLabel *label = classInfo->getObject(env,obj); - if (!label) - return; - JavaString jStr(env,uniqueStr); - label->uniqueID = jStr.cStr; + if (auto label = LabelClassInfo::get(env,obj)) + { + const JavaString jStr(env, uniqueStr); + label->uniqueID = jStr.getCString(); + } } catch (...) { - __android_log_print(ANDROID_LOG_VERBOSE, "Maply", "Crash in InternalLabel::setUniqueID()"); + __android_log_print(ANDROID_LOG_ERROR, "Maply", "Crash in InternalLabel::setUniqueID()"); } } diff --git a/android/library/maply/jni/src/labelsMarkers/InternalMarker_jni.cpp b/android/library/maply/jni/src/labelsMarkers/InternalMarker_jni.cpp index d8677e5a9e..28f682abac 100644 --- a/android/library/maply/jni/src/labelsMarkers/InternalMarker_jni.cpp +++ b/android/library/maply/jni/src/labelsMarkers/InternalMarker_jni.cpp @@ -1,9 +1,8 @@ -/* - * InternalMarker_jni.cpp +/* InternalMarker_jni.cpp * WhirlyGlobeLib * * Created by Steve Gifford on 6/2/14. - * Copyright 2011-2016 mousebird consulting + * Copyright 2011-2021 mousebird consulting * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -15,7 +14,6 @@ * 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. - * */ #import "LabelsAndMarkers_jni.h" @@ -25,297 +23,306 @@ using namespace WhirlyKit; -template<> MarkerClassInfo *MarkerClassInfo::classInfoObj = NULL; +template<> MarkerClassInfo *MarkerClassInfo::classInfoObj = nullptr; -JNIEXPORT void JNICALL Java_com_mousebird_maply_InternalMarker_nativeInit - (JNIEnv *env, jclass cls) +extern "C" +JNIEXPORT void JNICALL Java_com_mousebird_maply_InternalMarker_nativeInit(JNIEnv *env, jclass cls) { MarkerClassInfo::getClassInfo(env,cls); } -JNIEXPORT void JNICALL Java_com_mousebird_maply_InternalMarker_initialise - (JNIEnv *env, jobject obj) +extern "C" +JNIEXPORT void JNICALL Java_com_mousebird_maply_InternalMarker_initialise(JNIEnv *env, jobject obj) { try { - Marker *marker = new Marker(); + auto marker = new Marker(); marker->rotation = 0.0; marker->lockRotation = false; MarkerClassInfo::getClassInfo()->setHandle(env,obj,marker); } catch (...) { - __android_log_print(ANDROID_LOG_VERBOSE, "Maply", "Crash in InternalMarker::initialise()"); + __android_log_print(ANDROID_LOG_ERROR, "Maply", "Crash in InternalMarker::initialise()"); } } static std::mutex disposeMutex; -JNIEXPORT void JNICALL Java_com_mousebird_maply_InternalMarker_dispose - (JNIEnv *env, jobject obj) +extern "C" +JNIEXPORT void JNICALL Java_com_mousebird_maply_InternalMarker_dispose(JNIEnv *env, jobject obj) { try { - MarkerClassInfo *classInfo = MarkerClassInfo::getClassInfo(); - { - std::lock_guard lock(disposeMutex); - Marker *marker = classInfo->getObject(env,obj); - if (!marker) - return; - delete marker; - - classInfo->clearHandle(env,obj); - } + const auto classInfo = MarkerClassInfo::getClassInfo(); + std::lock_guard lock(disposeMutex); + auto marker = classInfo->getObject(env,obj); + delete marker; + classInfo->clearHandle(env,obj); } catch (...) { - __android_log_print(ANDROID_LOG_VERBOSE, "Maply", "Crash in InternalMarker::dispose()"); + __android_log_print(ANDROID_LOG_ERROR, "Maply", "Crash in InternalMarker::dispose()"); } } +extern "C" JNIEXPORT void JNICALL Java_com_mousebird_maply_InternalMarker_setSelectID - (JNIEnv *env, jobject obj, jlong selectID) + (JNIEnv *env, jobject obj, jlong selectID) { try { - MarkerClassInfo *classInfo = MarkerClassInfo::getClassInfo(); - Marker *marker = classInfo->getObject(env,obj); - if (!marker) - return; - - marker->isSelectable = true; - marker->selectID = selectID; + if (auto marker = MarkerClassInfo::get(env,obj)) + { + marker->isSelectable = true; + marker->selectID = selectID; + } } catch (...) { - __android_log_print(ANDROID_LOG_VERBOSE, "Maply", "Crash in InternalMarker::setSelectID()"); + __android_log_print(ANDROID_LOG_ERROR, "Maply", "Crash in InternalMarker::setSelectID()"); } } +extern "C" JNIEXPORT void JNICALL Java_com_mousebird_maply_InternalMarker_setLoc (JNIEnv *env, jobject obj, jobject ptObj) { try { - MarkerClassInfo *classInfo = MarkerClassInfo::getClassInfo(); - Marker *marker = classInfo->getObject(env,obj); - Point2d *pt = Point2dClassInfo::getClassInfo()->getObject(env,ptObj); - if (!marker || !pt) - return; - - marker->loc.x() = pt->x(); - marker->loc.y() = pt->y(); + if (auto marker = MarkerClassInfo::get(env,obj)) + { + if (auto pt = Point2dClassInfo::get(env, ptObj)) + { + marker->loc.x() = pt->x(); + marker->loc.y() = pt->y(); + } + } } catch (...) { - __android_log_print(ANDROID_LOG_VERBOSE, "Maply", "Crash in InternalMarker::setLoc()"); + __android_log_print(ANDROID_LOG_ERROR, "Maply", "Crash in InternalMarker::setLoc()"); } } +extern "C" JNIEXPORT void JNICALL Java_com_mousebird_maply_InternalMarker_setEndLoc - (JNIEnv *env, jobject obj, jobject ptObj) + (JNIEnv *env, jobject obj, jobject ptObj) { try { - MarkerClassInfo *classInfo = MarkerClassInfo::getClassInfo(); - Marker *marker = classInfo->getObject(env,obj); - Point2d *pt = Point2dClassInfo::getClassInfo()->getObject(env,ptObj); - if (!marker || !pt) - return; - - marker->endLoc.x() = pt->x(); - marker->endLoc.y() = pt->y(); + if (auto marker = MarkerClassInfo::get(env,obj)) + { + if (auto pt = Point2dClassInfo::get(env, ptObj)) + { + marker->endLoc.x() = pt->x(); + marker->endLoc.y() = pt->y(); + } + } } catch (...) { - __android_log_print(ANDROID_LOG_VERBOSE, "Maply", "Crash in InternalMarker::setEndLoc()"); + __android_log_print(ANDROID_LOG_ERROR, "Maply", "Crash in InternalMarker::setEndLoc()"); } } +extern "C" JNIEXPORT void JNICALL Java_com_mousebird_maply_InternalMarker_setAnimationRange - (JNIEnv *env, jobject obj, jdouble startTime, jdouble endTime) + (JNIEnv *env, jobject obj, jdouble startTime, jdouble endTime) { try { - MarkerClassInfo *classInfo = MarkerClassInfo::getClassInfo(); - Marker *marker = classInfo->getObject(env,obj); - if (!marker) - return; - - marker->startTime = startTime; - marker->endTime = endTime; + if (auto marker = MarkerClassInfo::get(env,obj)) + { + marker->startTime = startTime; + marker->endTime = endTime; + } } catch (...) { - __android_log_print(ANDROID_LOG_VERBOSE, "Maply", "Crash in InternalMarker::setAnimationRange()"); + __android_log_print(ANDROID_LOG_ERROR, "Maply", "Crash in InternalMarker::setAnimationRange()"); } } +extern "C" JNIEXPORT void JNICALL Java_com_mousebird_maply_InternalMarker_setColor - (JNIEnv *env, jobject obj, jfloat r, jfloat g, jfloat b, jfloat a) + (JNIEnv *env, jobject obj, jfloat r, jfloat g, jfloat b, jfloat a) { try { - MarkerClassInfo *classInfo = MarkerClassInfo::getClassInfo(); - Marker *marker = classInfo->getObject(env,obj); - if (!marker) - return; - - marker->color = RGBAColor(r*255,g*255,b*255,a*255); + if (auto marker = MarkerClassInfo::get(env,obj)) + { + marker->color = RGBAColor(r*255,g*255,b*255,a*255); + } } catch (...) { - __android_log_print(ANDROID_LOG_VERBOSE, "Maply", "Crash in InternalMarker::setColor()"); + __android_log_print(ANDROID_LOG_ERROR, "Maply", "Crash in InternalMarker::setColor()"); } } +extern "C" JNIEXPORT void JNICALL Java_com_mousebird_maply_InternalMarker_addTexID - (JNIEnv *env, jobject obj, jlong texID) + (JNIEnv *env, jobject obj, jlong texID) { try { - MarkerClassInfo *classInfo = MarkerClassInfo::getClassInfo(); - Marker *marker = classInfo->getObject(env,obj); - if (!marker) - return; - - marker->texIDs.push_back(texID); + if (auto marker = MarkerClassInfo::get(env,obj)) + { + marker->texIDs.push_back(texID); + } } catch (...) { - __android_log_print(ANDROID_LOG_VERBOSE, "Maply", "Crash in InternalMarker::addTexID()"); + __android_log_print(ANDROID_LOG_ERROR, "Maply", "Crash in InternalMarker::addTexID()"); } } +extern "C" JNIEXPORT void JNICALL Java_com_mousebird_maply_InternalMarker_setRotation - (JNIEnv *env, jobject obj, jdouble rot) + (JNIEnv *env, jobject obj, jdouble rot) { try { - MarkerClassInfo *classInfo = MarkerClassInfo::getClassInfo(); - Marker *marker = classInfo->getObject(env,obj); - if (!marker) - return; - - marker->lockRotation = true; - marker->rotation = rot; + if (auto marker = MarkerClassInfo::get(env,obj)) + { + marker->lockRotation = true; + marker->rotation = rot; + } } catch (...) { - __android_log_print(ANDROID_LOG_VERBOSE, "Maply", "Crash in InternalMarker::setRotation()"); + __android_log_print(ANDROID_LOG_ERROR, "Maply", "Crash in InternalMarker::setRotation()"); } } +extern "C" JNIEXPORT void JNICALL Java_com_mousebird_maply_InternalMarker_setLockRotation - (JNIEnv *env, jobject obj, jboolean lockRot) + (JNIEnv *env, jobject obj, jboolean lockRot) { try { - MarkerClassInfo *classInfo = MarkerClassInfo::getClassInfo(); - Marker *marker = classInfo->getObject(env,obj); - if (!marker) - return; - - marker->lockRotation = lockRot; + if (auto marker = MarkerClassInfo::get(env,obj)) + { + marker->lockRotation = lockRot; + } } catch (...) { - __android_log_print(ANDROID_LOG_VERBOSE, "Maply", "Crash in InternalMarker::setLockRotation()"); + __android_log_print(ANDROID_LOG_ERROR, "Maply", "Crash in InternalMarker::setLockRotation()"); } } +extern "C" JNIEXPORT void JNICALL Java_com_mousebird_maply_InternalMarker_setSize - (JNIEnv *env, jobject obj, jdouble width, jdouble height) + (JNIEnv *env, jobject obj, jdouble width, jdouble height) { try { - MarkerClassInfo *classInfo = MarkerClassInfo::getClassInfo(); - Marker *marker = classInfo->getObject(env,obj); - if (!marker) - return; - - marker->width = width; - marker->height = height; + if (auto marker = MarkerClassInfo::get(env,obj)) + { + marker->width = width; + marker->height = height; + } } catch (...) { - __android_log_print(ANDROID_LOG_VERBOSE, "Maply", "Crash in InternalMarker::setSize()"); + __android_log_print(ANDROID_LOG_ERROR, "Maply", "Crash in InternalMarker::setSize()"); } } +extern "C" JNIEXPORT void JNICALL Java_com_mousebird_maply_InternalMarker_setLayoutSize - (JNIEnv *env, jobject obj, jdouble width, jdouble height) + (JNIEnv *env, jobject obj, jdouble width, jdouble height) { try { - MarkerClassInfo *classInfo = MarkerClassInfo::getClassInfo(); - Marker *marker = classInfo->getObject(env,obj); - if (!marker) - return; - - marker->layoutWidth = width; - marker->layoutHeight = height; + if (auto marker = MarkerClassInfo::get(env,obj)) + { + marker->layoutWidth = width; + marker->layoutHeight = height; + } } catch (...) { - __android_log_print(ANDROID_LOG_VERBOSE, "Maply", "Crash in InternalMarker::setLayoutSize()"); + __android_log_print(ANDROID_LOG_ERROR, "Maply", "Crash in InternalMarker::setLayoutSize()"); } } +extern "C" JNIEXPORT void JNICALL Java_com_mousebird_maply_InternalMarker_setOffset -(JNIEnv *env, jobject obj, jdouble offX,jdouble offY) + (JNIEnv *env, jobject obj, jdouble offX,jdouble offY) { try { - MarkerClassInfo *classInfo = MarkerClassInfo::getClassInfo(); - Marker *marker = classInfo->getObject(env,obj); - if (!marker) - return; - - marker->offset.x() = offX; - marker->offset.y() = offY; + if (auto marker = MarkerClassInfo::get(env,obj)) + { + marker->offset.x() = offX; + marker->offset.y() = offY; + } } catch (...) { - __android_log_print(ANDROID_LOG_VERBOSE, "Maply", "Crash in InternalMarker::setOffset()"); + __android_log_print(ANDROID_LOG_ERROR, "Maply", "Crash in InternalMarker::setOffset()"); } } +extern "C" JNIEXPORT void JNICALL Java_com_mousebird_maply_InternalMarker_setPeriod -(JNIEnv *env, jobject obj, jdouble period) + (JNIEnv *env, jobject obj, jdouble period) { try { - MarkerClassInfo *classInfo = MarkerClassInfo::getClassInfo(); - Marker *marker = classInfo->getObject(env,obj); - if (!marker) - return; - - marker->period = period; + if (auto marker = MarkerClassInfo::get(env,obj)) + { + marker->period = period; + } } catch (...) { - __android_log_print(ANDROID_LOG_VERBOSE, "Maply", "Crash in InternalMarker::setPeriod()"); + __android_log_print(ANDROID_LOG_ERROR, "Maply", "Crash in InternalMarker::setPeriod()"); } } +extern "C" JNIEXPORT void JNICALL Java_com_mousebird_maply_InternalMarker_setVertexAttributes -(JNIEnv *env, jobject obj, jobjectArray vertAttrArray) + (JNIEnv *env, jobject obj, jobjectArray vertAttrArray) { - try { - MarkerClassInfo *classInfo = MarkerClassInfo::getClassInfo(); - Marker *marker = classInfo->getObject(env,obj); - if (!marker) - return; - - marker->vertexAttrs.clear(); - SingleVertexAttributeClassInfo *vertClassInfo = SingleVertexAttributeClassInfo::getClassInfo(); - JavaObjectArrayHelper vertAttrHelp(env,vertAttrArray); - while (jobject vertAttrObj = vertAttrHelp.getNextObject()) { - SingleVertexAttribute *vertAttr = vertClassInfo->getObject(env,vertAttrObj); - marker->vertexAttrs.insert(*vertAttr); - } - } catch (...) { - __android_log_print(ANDROID_LOG_VERBOSE, "Maply", "Crash in InternalMarker::setVertexAttributes()"); + try + { + if (auto marker = MarkerClassInfo::get(env,obj)) + { + marker->vertexAttrs.clear(); + + const auto vertClassInfo = SingleVertexAttributeClassInfo::getClassInfo(); + + JavaObjectArrayHelper vertAttrHelp(env, vertAttrArray); + while (jobject vertAttrObj = vertAttrHelp.getNextObject()) + { + auto vertAttr = vertClassInfo->getObject(env, vertAttrObj); + marker->vertexAttrs.insert(*vertAttr); + } + } } + catch (...) + { + __android_log_print(ANDROID_LOG_ERROR, "Maply", "Crash in InternalMarker::setVertexAttributes()"); + } +} + +extern "C" +JNIEXPORT void JNICALL Java_com_mousebird_maply_InternalMarker_setLayoutImportance + (JNIEnv *env, jobject obj, jfloat importance) +{ + try + { + if (auto marker = MarkerClassInfo::get(env,obj)) + { + marker->layoutImportance = importance; + } + } + catch (...) + { + __android_log_print(ANDROID_LOG_ERROR, "Maply", "Crash in InternalMarker::setLayoutImportance()"); + } } diff --git a/android/library/maply/jni/src/labelsMarkers/LabelInfo_jni.cpp b/android/library/maply/jni/src/labelsMarkers/LabelInfo_jni.cpp index 8cd6361893..eddd81e2d2 100644 --- a/android/library/maply/jni/src/labelsMarkers/LabelInfo_jni.cpp +++ b/android/library/maply/jni/src/labelsMarkers/LabelInfo_jni.cpp @@ -1,9 +1,8 @@ -/* - * LabelInfo_jni.cpp +/* LabelInfo_jni.cpp * WhirlyGlobeLib * * Created by Steve Gifford on 6/2/14. - * Copyright 2011-2016 mousebird consulting + * Copyright 2011-2021 mousebird consulting * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -15,7 +14,6 @@ * 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. - * */ #import "LabelsAndMarkers_jni.h" @@ -23,16 +21,16 @@ using namespace WhirlyKit; -template<> LabelInfoClassInfo *LabelInfoClassInfo::classInfoObj = NULL; +template<> LabelInfoClassInfo *LabelInfoClassInfo::classInfoObj = nullptr; -JNIEXPORT void JNICALL Java_com_mousebird_maply_LabelInfo_nativeInit - (JNIEnv *env, jclass cls) +extern "C" +JNIEXPORT void JNICALL Java_com_mousebird_maply_LabelInfo_nativeInit(JNIEnv *env, jclass cls) { LabelInfoClassInfo::getClassInfo(env,cls); } -JNIEXPORT void JNICALL Java_com_mousebird_maply_LabelInfo_initialise - (JNIEnv *env, jobject obj) +extern "C" +JNIEXPORT void JNICALL Java_com_mousebird_maply_LabelInfo_initialise(JNIEnv *env, jobject obj) { try { @@ -47,8 +45,8 @@ JNIEXPORT void JNICALL Java_com_mousebird_maply_LabelInfo_initialise static std::mutex disposeMutex; -JNIEXPORT void JNICALL Java_com_mousebird_maply_LabelInfo_dispose - (JNIEnv *env, jobject obj) +extern "C" +JNIEXPORT void JNICALL Java_com_mousebird_maply_LabelInfo_dispose(JNIEnv *env, jobject obj) { try { @@ -56,13 +54,12 @@ JNIEXPORT void JNICALL Java_com_mousebird_maply_LabelInfo_dispose { std::lock_guard lock(disposeMutex); LabelInfoAndroidRef *info = classInfo->getObject(env,obj); - if (!info) - return; - PlatformInfo_Android platformInfo(env); - (*info)->clearRefs(&platformInfo); - delete info; - - classInfo->clearHandle(env,obj); + if (info) { + PlatformInfo_Android platformInfo(env); + (*info)->clearRefs(&platformInfo); + delete info; + classInfo->clearHandle(env,obj); + } } } catch (...) @@ -71,8 +68,8 @@ JNIEXPORT void JNICALL Java_com_mousebird_maply_LabelInfo_dispose } } -JNIEXPORT jint JNICALL Java_com_mousebird_maply_LabelInfo_getTextColor - (JNIEnv *env, jobject obj) +extern "C" +JNIEXPORT jint JNICALL Java_com_mousebird_maply_LabelInfo_getTextColor(JNIEnv *env, jobject obj) { try { @@ -92,6 +89,7 @@ JNIEXPORT jint JNICALL Java_com_mousebird_maply_LabelInfo_getTextColor return 0; } +extern "C" JNIEXPORT void JNICALL Java_com_mousebird_maply_LabelInfo_setTextColor (JNIEnv *env, jobject obj, jfloat r, jfloat g, jfloat b, jfloat a) { @@ -109,6 +107,7 @@ JNIEXPORT void JNICALL Java_com_mousebird_maply_LabelInfo_setTextColor } } +extern "C" JNIEXPORT void JNICALL Java_com_mousebird_maply_LabelInfo_setBackgroundColor (JNIEnv *env, jobject obj, jfloat r, jfloat g, jfloat b, jfloat a) { @@ -126,6 +125,7 @@ JNIEXPORT void JNICALL Java_com_mousebird_maply_LabelInfo_setBackgroundColor } } +extern "C" JNIEXPORT jint JNICALL Java_com_mousebird_maply_LabelInfo_getBackgroundColor (JNIEnv *env, jobject obj) { @@ -147,6 +147,7 @@ JNIEXPORT jint JNICALL Java_com_mousebird_maply_LabelInfo_getBackgroundColor return 0; } +extern "C" JNIEXPORT void JNICALL Java_com_mousebird_maply_LabelInfo_setTypefaceNative (JNIEnv *env, jobject obj, jobject typefaceObj) { @@ -165,6 +166,7 @@ JNIEXPORT void JNICALL Java_com_mousebird_maply_LabelInfo_setTypefaceNative } } +extern "C" JNIEXPORT jobject JNICALL Java_com_mousebird_maply_LabelInfo_getTypeface (JNIEnv *env, jobject obj) { @@ -185,6 +187,7 @@ JNIEXPORT jobject JNICALL Java_com_mousebird_maply_LabelInfo_getTypeface return NULL; } +extern "C" JNIEXPORT void JNICALL Java_com_mousebird_maply_LabelInfo_setFontSizeNative (JNIEnv *env, jobject obj, jfloat fontSize) { @@ -203,6 +206,7 @@ JNIEXPORT void JNICALL Java_com_mousebird_maply_LabelInfo_setFontSizeNative } } +extern "C" JNIEXPORT void JNICALL Java_com_mousebird_maply_LabelInfo_setOutlineColor (JNIEnv *env, jobject obj, jfloat r, jfloat g, jfloat b, jfloat a) { @@ -220,6 +224,7 @@ JNIEXPORT void JNICALL Java_com_mousebird_maply_LabelInfo_setOutlineColor } } +extern "C" JNIEXPORT jint JNICALL Java_com_mousebird_maply_LabelInfo_getOutlineColor (JNIEnv *env, jobject obj) { @@ -241,6 +246,7 @@ JNIEXPORT jint JNICALL Java_com_mousebird_maply_LabelInfo_getOutlineColor return 0; } +extern "C" JNIEXPORT void JNICALL Java_com_mousebird_maply_LabelInfo_setOutlineSize (JNIEnv *env, jobject obj, jfloat outlineSize) { @@ -259,6 +265,7 @@ JNIEXPORT void JNICALL Java_com_mousebird_maply_LabelInfo_setOutlineSize } } +extern "C" JNIEXPORT jfloat JNICALL Java_com_mousebird_maply_LabelInfo_getOutlineSize (JNIEnv *env, jobject obj) { @@ -280,6 +287,7 @@ JNIEXPORT jfloat JNICALL Java_com_mousebird_maply_LabelInfo_getOutlineSize return 0; } +extern "C" JNIEXPORT void JNICALL Java_com_mousebird_maply_LabelInfo_setShadowColor (JNIEnv *env, jobject obj, jfloat r, jfloat g, jfloat b, jfloat a) { @@ -297,8 +305,8 @@ JNIEXPORT void JNICALL Java_com_mousebird_maply_LabelInfo_setShadowColor } } -JNIEXPORT void JNICALL Java_com_mousebird_maply_LabelInfo_setShadowSize -(JNIEnv *env, jobject obj, jfloat shadowSize) +extern "C" +JNIEXPORT void JNICALL Java_com_mousebird_maply_LabelInfo_setShadowSize(JNIEnv *env, jobject obj, jfloat shadowSize) { try { @@ -315,8 +323,8 @@ JNIEXPORT void JNICALL Java_com_mousebird_maply_LabelInfo_setShadowSize } } -JNIEXPORT void JNICALL Java_com_mousebird_maply_LabelInfo_setTextJustifyNative -(JNIEnv *env, jobject obj, jint textLayout) +extern "C" +JNIEXPORT void JNICALL Java_com_mousebird_maply_LabelInfo_setTextJustifyNative(JNIEnv *env, jobject obj, jint textLayout) { try { @@ -333,8 +341,8 @@ JNIEXPORT void JNICALL Java_com_mousebird_maply_LabelInfo_setTextJustifyNative } } -JNIEXPORT void JNICALL Java_com_mousebird_maply_LabelInfo_setLineHeight -(JNIEnv *env, jobject obj, jfloat lineSize) +extern "C" +JNIEXPORT void JNICALL Java_com_mousebird_maply_LabelInfo_setLineHeight(JNIEnv *env, jobject obj, jfloat lineSize) { try { diff --git a/android/library/maply/jni/src/labelsMarkers/LabelManager_jni.cpp b/android/library/maply/jni/src/labelsMarkers/LabelManager_jni.cpp index 96c9a7d3d7..7c1b7e8cfa 100644 --- a/android/library/maply/jni/src/labelsMarkers/LabelManager_jni.cpp +++ b/android/library/maply/jni/src/labelsMarkers/LabelManager_jni.cpp @@ -1,9 +1,8 @@ -/* - * LabelManager_jni.cpp +/* LabelManager_jni.cpp * WhirlyGlobeLib * * Created by Steve Gifford on 6/2/14. - * Copyright 2011-2016 mousebird consulting + * Copyright 2011-2021 mousebird consulting * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -15,7 +14,6 @@ * 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. - * */ #import "LabelsAndMarkers_jni.h" @@ -26,27 +24,27 @@ using namespace WhirlyKit; using namespace Maply; -static const char *SceneHandleName = "nativeSceneHandle"; +//static const char *SceneHandleName = "nativeSceneHandle"; -typedef JavaClassInfo LabelManagerClassInfo; -template<> LabelManagerClassInfo *LabelManagerClassInfo::classInfoObj = NULL; +typedef JavaClassInfo LabelManagerClassInfo; +template<> LabelManagerClassInfo *LabelManagerClassInfo::classInfoObj = nullptr; -JNIEXPORT void JNICALL Java_com_mousebird_maply_LabelManager_nativeInit - (JNIEnv *env, jclass cls) +extern "C" +JNIEXPORT void JNICALL Java_com_mousebird_maply_LabelManager_nativeInit(JNIEnv *env, jclass cls) { LabelManagerClassInfo::getClassInfo(env,cls); } -JNIEXPORT void JNICALL Java_com_mousebird_maply_LabelManager_initialise - (JNIEnv *env, jobject obj, jobject sceneObj) +extern "C" +JNIEXPORT void JNICALL Java_com_mousebird_maply_LabelManager_initialise(JNIEnv *env, jobject obj, jobject sceneObj) { try { - Scene *scene = SceneClassInfo::getClassInfo()->getObject(env, sceneObj); - if (!scene) - return; - LabelManager *labelManager = dynamic_cast(scene->getManager(kWKLabelManager)); - LabelManagerClassInfo::getClassInfo()->setHandle(env,obj,labelManager); + if (auto scene = SceneClassInfo::get(env, sceneObj)) + { + auto labelManager = scene->getManager(kWKLabelManager); + LabelManagerClassInfo::getClassInfo()->setHandle(env, obj,new LabelManagerRef(labelManager)); + } } catch (...) { @@ -56,12 +54,15 @@ JNIEXPORT void JNICALL Java_com_mousebird_maply_LabelManager_initialise static std::mutex disposeMutex; -JNIEXPORT void JNICALL Java_com_mousebird_maply_LabelManager_dispose - (JNIEnv *env, jobject obj) +extern "C" +JNIEXPORT void JNICALL Java_com_mousebird_maply_LabelManager_dispose(JNIEnv *env, jobject obj) { try { - LabelManagerClassInfo::getClassInfo()->clearHandle(env,obj); + LabelManagerClassInfo *classInfo = LabelManagerClassInfo::getClassInfo(); + LabelManagerRef *labelManager = classInfo->getObject(env,obj); + delete labelManager; + classInfo->clearHandle(env,obj); } catch (...) { @@ -69,13 +70,14 @@ JNIEXPORT void JNICALL Java_com_mousebird_maply_LabelManager_dispose } } +extern "C" JNIEXPORT jlong JNICALL Java_com_mousebird_maply_LabelManager_addLabels (JNIEnv *env, jobject obj, jobjectArray labelArray, jobject labelInfoObj, jobject changeSetObj) { try { LabelManagerClassInfo *classInfo = LabelManagerClassInfo::getClassInfo(); - LabelManager *labelManager = classInfo->getObject(env,obj); + LabelManagerRef *labelManager = classInfo->getObject(env,obj); LabelInfoAndroidRef *labelInfo = LabelInfoClassInfo::getClassInfo()->getObject(env,labelInfoObj); ChangeSetRef *changeSet = ChangeSetClassInfo::getClassInfo()->getObject(env,changeSetObj); if (!labelManager || !labelInfo || !changeSet) @@ -102,18 +104,16 @@ JNIEXPORT jlong JNICALL Java_com_mousebird_maply_LabelManager_addLabels // Resolve a missing program if ((*labelInfo)->programID == EmptyIdentity) { - ProgramGLES *prog = NULL; - if (isMoving) - prog = (ProgramGLES *)labelManager->getScene()->findProgramByName(MaplyScreenSpaceDefaultMotionShader); - else - prog = (ProgramGLES *)labelManager->getScene()->findProgramByName(MaplyScreenSpaceDefaultShader); - if (prog) - (*labelInfo)->programID = prog->getId(); + if (auto prog = (ProgramGLES *)(*labelManager)->getScene()->findProgramByName( + isMoving ? MaplyScreenSpaceDefaultMotionShader : MaplyScreenSpaceDefaultShader)) + { + (*labelInfo)->programID = prog->getId(); + } } PlatformInfo_Android platformInfo(env); - SimpleIdentity labelId = labelManager->addLabels(&platformInfo,labels,*(*labelInfo),*(changeSet->get())); + SimpleIdentity labelId = (*labelManager)->addLabels(&platformInfo,labels,*(*labelInfo),*(changeSet->get())); - (*labelInfo)->labelInfoObj = NULL; + (*labelInfo)->labelInfoObj = nullptr; return labelId; } @@ -125,13 +125,14 @@ JNIEXPORT jlong JNICALL Java_com_mousebird_maply_LabelManager_addLabels return EmptyIdentity; } +extern "C" JNIEXPORT void JNICALL Java_com_mousebird_maply_LabelManager_removeLabels (JNIEnv *env, jobject obj, jlongArray idArrayObj, jobject changeSetObj) { try { LabelManagerClassInfo *classInfo = LabelManagerClassInfo::getClassInfo(); - LabelManager *labelManager = classInfo->getObject(env,obj); + LabelManagerRef *labelManager = classInfo->getObject(env,obj); ChangeSetRef *changeSet = ChangeSetClassInfo::getClassInfo()->getObject(env,changeSetObj); if (!labelManager || !changeSet) return; @@ -140,7 +141,7 @@ JNIEXPORT void JNICALL Java_com_mousebird_maply_LabelManager_removeLabels ConvertLongArrayToSet(env,idArrayObj,idSet); PlatformInfo_Android platformInfo(env); - labelManager->removeLabels(&platformInfo,idSet,*(changeSet->get())); + (*labelManager)->removeLabels(&platformInfo,idSet,*(changeSet->get())); } catch (...) { @@ -148,13 +149,14 @@ JNIEXPORT void JNICALL Java_com_mousebird_maply_LabelManager_removeLabels } } +extern "C" JNIEXPORT void JNICALL Java_com_mousebird_maply_LabelManager_enableLabels (JNIEnv *env, jobject obj, jlongArray idArrayObj, jboolean enable, jobject changeSetObj) { try { LabelManagerClassInfo *classInfo = LabelManagerClassInfo::getClassInfo(); - LabelManager *labelManager = classInfo->getObject(env,obj); + LabelManagerRef *labelManager = classInfo->getObject(env,obj); ChangeSetRef *changeSet = ChangeSetClassInfo::getClassInfo()->getObject(env,changeSetObj); if (!labelManager || !changeSet) return; @@ -162,7 +164,7 @@ JNIEXPORT void JNICALL Java_com_mousebird_maply_LabelManager_enableLabels SimpleIDSet idSet; ConvertLongArrayToSet(env,idArrayObj,idSet); - labelManager->enableLabels(idSet,enable,*(changeSet->get())); + (*labelManager)->enableLabels(idSet,enable,*(changeSet->get())); } catch (...) { diff --git a/android/library/maply/jni/src/labelsMarkers/MarkerManager_jni.cpp b/android/library/maply/jni/src/labelsMarkers/MarkerManager_jni.cpp index 9dfc575c72..db4da9031a 100644 --- a/android/library/maply/jni/src/labelsMarkers/MarkerManager_jni.cpp +++ b/android/library/maply/jni/src/labelsMarkers/MarkerManager_jni.cpp @@ -26,7 +26,7 @@ using namespace WhirlyKit; using namespace Maply; -static const char *SceneHandleName = "nativeSceneHandle"; +//static const char *SceneHandleName = "nativeSceneHandle"; template<> MarkerManagerClassInfo *MarkerManagerClassInfo::classInfoObj = NULL; @@ -42,8 +42,8 @@ JNIEXPORT void JNICALL Java_com_mousebird_maply_MarkerManager_initialise try { Scene *scene = SceneClassInfo::getClassInfo()->getObject(env,sceneObj); - MarkerManager *markerManager = dynamic_cast(scene->getManager(kWKMarkerManager)); - MarkerManagerClassInfo::getClassInfo()->setHandle(env,obj,markerManager); + MarkerManagerRef markerManager = std::dynamic_pointer_cast(scene->getManager(kWKMarkerManager)); + MarkerManagerClassInfo::getClassInfo()->setHandle(env,obj,new MarkerManagerRef(markerManager)); } catch (...) { @@ -58,7 +58,11 @@ JNIEXPORT void JNICALL Java_com_mousebird_maply_MarkerManager_dispose { try { - MarkerManagerClassInfo::getClassInfo()->clearHandle(env,obj); + MarkerManagerClassInfo *classInfo = MarkerManagerClassInfo::getClassInfo(); + MarkerManagerRef *markerManager = classInfo->getObject(env,obj); + if (markerManager) + delete markerManager; + classInfo->clearHandle(env,obj); } catch (...) { @@ -72,7 +76,7 @@ JNIEXPORT jlong JNICALL Java_com_mousebird_maply_MarkerManager_addMarkers try { MarkerManagerClassInfo *classInfo = MarkerManagerClassInfo::getClassInfo(); - MarkerManager *markerManager = classInfo->getObject(env,obj); + MarkerManagerRef *markerManager = classInfo->getObject(env,obj); MarkerInfoRef *markerInfo = MarkerInfoClassInfo::getClassInfo()->getObject(env,markerInfoObj); ChangeSetRef *changeSet = ChangeSetClassInfo::getClassInfo()->getObject(env,changeSetObj); if (!markerManager || !markerInfo || !changeSet) @@ -99,14 +103,14 @@ JNIEXPORT jlong JNICALL Java_com_mousebird_maply_MarkerManager_addMarkers { Program *prog = NULL; if (hasMultiTex) - prog = markerManager->getScene()->findProgramByName(MaplyDefaultMarkerShader); + prog = (*markerManager)->getScene()->findProgramByName(MaplyDefaultMarkerShader); else - prog = markerManager->getScene()->findProgramByName(MaplyDefaultTriangleShader); + prog = (*markerManager)->getScene()->findProgramByName(MaplyDefaultTriangleShader); if (prog) (*markerInfo)->programID = prog->getId(); } - SimpleIdentity markerId = markerManager->addMarkers(markers,*(*markerInfo),*(changeSet->get())); + SimpleIdentity markerId = (*markerManager)->addMarkers(markers,*(*markerInfo),*(changeSet->get())); return markerId; } @@ -124,7 +128,7 @@ JNIEXPORT jlong JNICALL Java_com_mousebird_maply_MarkerManager_addScreenMarkers try { MarkerManagerClassInfo *classInfo = MarkerManagerClassInfo::getClassInfo(); - MarkerManager *markerManager = classInfo->getObject(env,obj); + MarkerManagerRef *markerManager = classInfo->getObject(env,obj); MarkerInfoRef *markerInfo = MarkerInfoClassInfo::getClassInfo()->getObject(env,markerInfoObj); ChangeSetRef *changeSet = ChangeSetClassInfo::getClassInfo()->getObject(env,changeSetObj); if (!markerManager || !markerInfo || !changeSet) @@ -151,14 +155,14 @@ JNIEXPORT jlong JNICALL Java_com_mousebird_maply_MarkerManager_addScreenMarkers { Program *prog = NULL; if (isMoving) - prog = markerManager->getScene()->findProgramByName(MaplyScreenSpaceDefaultMotionShader); + prog = (*markerManager)->getScene()->findProgramByName(MaplyScreenSpaceDefaultMotionShader); else - prog = markerManager->getScene()->findProgramByName(MaplyScreenSpaceDefaultShader); + prog = (*markerManager)->getScene()->findProgramByName(MaplyScreenSpaceDefaultShader); if (prog) (*markerInfo)->programID = prog->getId(); } - SimpleIdentity markerId = markerManager->addMarkers(markers,*(*markerInfo),*(changeSet->get())); + SimpleIdentity markerId = (*markerManager)->addMarkers(markers,*(*markerInfo),*(changeSet->get())); return markerId; } @@ -176,7 +180,7 @@ JNIEXPORT void JNICALL Java_com_mousebird_maply_MarkerManager_removeMarkers try { MarkerManagerClassInfo *classInfo = MarkerManagerClassInfo::getClassInfo(); - MarkerManager *markerManager = classInfo->getObject(env,obj); + MarkerManagerRef *markerManager = classInfo->getObject(env,obj); ChangeSetRef *changeSet = ChangeSetClassInfo::getClassInfo()->getObject(env,changeSetObj); if (!markerManager || !changeSet) return; @@ -184,7 +188,7 @@ JNIEXPORT void JNICALL Java_com_mousebird_maply_MarkerManager_removeMarkers SimpleIDSet idSet; ConvertLongArrayToSet(env,idArrayObj,idSet); - markerManager->removeMarkers(idSet,*(changeSet->get())); + (*markerManager)->removeMarkers(idSet,*(changeSet->get())); } catch (...) { @@ -198,7 +202,7 @@ JNIEXPORT void JNICALL Java_com_mousebird_maply_MarkerManager_enableMarkers try { MarkerManagerClassInfo *classInfo = MarkerManagerClassInfo::getClassInfo(); - MarkerManager *markerManager = classInfo->getObject(env,obj); + MarkerManagerRef *markerManager = classInfo->getObject(env,obj); ChangeSetRef *changeSet = ChangeSetClassInfo::getClassInfo()->getObject(env,changeSetObj); if (!markerManager || !changeSet) return; @@ -211,7 +215,7 @@ JNIEXPORT void JNICALL Java_com_mousebird_maply_MarkerManager_enableMarkers // else // __android_log_print(ANDROID_LOG_VERBOSE, "Maply", "Disabling marker: %d",(int)ids[0]); - markerManager->enableMarkers(idSet,enable,*(changeSet->get())); + (*markerManager)->enableMarkers(idSet,enable,*(changeSet->get())); } catch (...) { diff --git a/android/library/maply/jni/src/layoutSelection/LayoutManager_jni.cpp b/android/library/maply/jni/src/layoutSelection/LayoutManager_jni.cpp index 8c399fcce4..c316586d55 100644 --- a/android/library/maply/jni/src/layoutSelection/LayoutManager_jni.cpp +++ b/android/library/maply/jni/src/layoutSelection/LayoutManager_jni.cpp @@ -1,9 +1,8 @@ -/* - * LayoutManager_jni.cpp +/* LayoutManager_jni.cpp * WhirlyGlobeLib * * Created by Steve Gifford on 6/2/14. - * Copyright 2011-2016 mousebird consulting + * Copyright 2011-2021 mousebird consulting * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -15,7 +14,6 @@ * 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. - * */ #import @@ -35,14 +33,43 @@ class LayoutManagerWrapper : public ClusterGenerator public: // Used to keep track of cluster objects for callbacks - class ClusterInfo + struct ClusterInfo { - public: - bool operator < (const ClusterInfo &that) const + ClusterInfo() : clusterObj(nullptr) { - return clusterID < that.clusterID; } - + + // Move only + ClusterInfo(const ClusterInfo &) = delete; + ClusterInfo(ClusterInfo &&other) noexcept + { + copy(other); + other.clusterObj = nullptr; + } + + ~ClusterInfo() + { + if (clusterObj) { + wkLogLevel(Warn, "ClusterInfo not cleaned up"); + } + } + + bool operator < (const ClusterInfo &that) const { return clusterID < that.clusterID; } + + // Move only + ClusterInfo& operator =(const ClusterInfo &) = delete; + ClusterInfo& operator =(ClusterInfo &&other) noexcept { + if (this != &other) { + if (clusterObj) { + // Replacing this one leaks it + wkLogLevel(Warn, "ClusterInfo not cleaned up"); + } + copy(other); + other.clusterObj = nullptr; + } + return *this; + } + void init(JNIEnv *env,int inClusterID,const Point2d &inLayoutSize,jobject inClusterObj) { clusterID = inClusterID; @@ -50,6 +77,7 @@ class LayoutManagerWrapper : public ClusterGenerator clusterObj = env->NewGlobalRef(inClusterObj); // Methods can be saved without consequence + // (as long as the class isn't unloaded and reloaded) jclass theClass = env->GetObjectClass(clusterObj); startClusterGroupJava = env->GetMethodID(theClass, "startClusterGroup", "()V"); makeClusterGroupJNIJava = env->GetMethodID(theClass, "makeClusterGroupJNI", "(I)J"); @@ -57,9 +85,10 @@ class LayoutManagerWrapper : public ClusterGenerator env->DeleteLocalRef(theClass); } - void clear(JNIEnv *env) const + void clear(JNIEnv *env) { env->DeleteGlobalRef(clusterObj); + clusterObj = nullptr; } int clusterID; @@ -71,17 +100,29 @@ class LayoutManagerWrapper : public ClusterGenerator jmethodID startClusterGroupJava; jmethodID makeClusterGroupJNIJava; jmethodID endClusterGroupJava; + + private: + void copy(const ClusterInfo &other) { + clusterID = other.clusterID; + layoutSize = other.layoutSize; + clusterObj = other.clusterObj; + selectable = other.selectable; + startClusterGroupJava = other.startClusterGroupJava; + makeClusterGroupJNIJava = other.makeClusterGroupJNIJava; + endClusterGroupJava = other.endClusterGroupJava; + } + friend class LayoutManagerWrapper; }; typedef std::set ClusterInfoSet; - LayoutManagerWrapper(Scene *scene,LayoutManager *layoutManager) - : layoutManager(layoutManager), env(NULL), motionShaderID(EmptyIdentity) + LayoutManagerWrapper(PlatformThreadInfo *threadInfo, LayoutManagerRef layoutManager) + : layoutManager(layoutManager), motionShaderID(EmptyIdentity) { - layoutManager->addClusterGenerator(this); + layoutManager->addClusterGenerator(threadInfo,this); } - ~LayoutManagerWrapper() { - // Note: Should clean up Java refs + virtual ~LayoutManagerWrapper() + { } void updateShader() @@ -93,71 +134,78 @@ class LayoutManagerWrapper : public ClusterGenerator motionShaderID = program->getId(); } } - - void setEnv(JNIEnv *inEnv) - { - env = inEnv; - } - + // Add a cluster generator for callback - void addClusterGenerator(jobject clusterObj, jint clusterID,bool selectable, double sizeX, double sizeY) + void addClusterGenerator(JNIEnv* env, jobject clusterObj, jint clusterID,bool selectable, double sizeX, double sizeY) { ClusterInfo clusterInfo; clusterInfo.init(env,clusterID,Point2d(sizeX,sizeY),clusterObj); clusterInfo.selectable = selectable; - clusterGens.insert(clusterInfo); + clusterGens.insert(std::move(clusterInfo)); } - void clearClusterGenerators() + void clearClusterGenerators(JNIEnv* env) { for (auto &ci : clusterGens) - ci.clear(env); + { + // why TF does this produce a const ref? + // (answer: std::set has `typedef typename __base::const_iterator iterator`, bug?) + const_cast(ci).clear(env); + } clusterGens.clear(); } /** ClusterGenerator virtual methods. */ // Called right before we start generating layout objects - void startLayoutObjects() + virtual void startLayoutObjects(PlatformThreadInfo *threadInfo) override { + const auto env = ((PlatformInfo_Android*)threadInfo)->env; + oldClusterTex = currentClusterTex; currentClusterTex.clear(); // Notify all the cluster generators for (const auto &clusterGen : clusterGens) + { env->CallVoidMethod(clusterGen.clusterObj,clusterGen.startClusterGroupJava); + } } // Ask the appropriate cluster generator to make a cluster image - void makeLayoutObject(int clusterID, const std::vector &layoutObjects, LayoutObject &retObj) + virtual void makeLayoutObject(PlatformThreadInfo *threadInfo,int clusterID, + const std::vector &layoutObjects, + LayoutObject &retObj) override { + const auto env = ((PlatformInfo_Android*)threadInfo)->env; + ClusterInfo dummyInfo; dummyInfo.clusterID = clusterID; - auto it = clusterGens.find(dummyInfo); + const auto it = clusterGens.find(dummyInfo); if (it == clusterGens.end()) return; const ClusterInfo &clusterGenerator = *it; - // Pick a representive screen object + // Pick a representative screen object int drawPriority = -1; - LayoutObject *sampleObj = NULL; + LayoutObject *sampleObj = nullptr; for (auto obj : layoutObjects) { if (obj->obj.getDrawPriority() > drawPriority) { drawPriority = obj->obj.getDrawPriority(); sampleObj = &obj->obj; } } - SimpleIdentity progID = sampleObj->getTypicalProgramID(); + const SimpleIdentity progID = sampleObj->getTypicalProgramID(); // The texture gets created on the Java side, so we'll just use the ID - long texID = env->CallLongMethod(clusterGenerator.clusterObj, clusterGenerator.makeClusterGroupJNIJava, layoutObjects.size()); + const long texID = env->CallLongMethod(clusterGenerator.clusterObj, clusterGenerator.makeClusterGroupJNIJava, layoutObjects.size()); - Point2d size = clusterGenerator.layoutSize; + const Point2d size = clusterGenerator.layoutSize; // Geometry for the new cluster object - ScreenSpaceObject::ConvexGeometry smGeom; + ScreenSpaceConvexGeometry smGeom; smGeom.progID = progID; smGeom.coords.push_back(Point2d(- size.x()/2.0,-size.y()/2.0)); smGeom.texCoords.push_back(TexCoord(0,1)); @@ -178,14 +226,18 @@ class LayoutManagerWrapper : public ClusterGenerator } // Called right after all the layout objects are generated - virtual void endLayoutObjects() + virtual void endLayoutObjects(PlatformThreadInfo *threadInfo) override { + const auto env = ((PlatformInfo_Android*)threadInfo)->env; + // Notify all the cluster generators for (const auto &clusterGen : clusterGens) + { env->CallVoidMethod(clusterGen.clusterObj,clusterGen.endClusterGroupJava); + } } - virtual void paramsForClusterClass(int clusterID,ClusterClassParams &clusterParams) + virtual void paramsForClusterClass(PlatformThreadInfo *,int clusterID,ClusterClassParams &clusterParams) override { ClusterInfo dummyInfo; dummyInfo.clusterID = clusterID; @@ -196,165 +248,156 @@ class LayoutManagerWrapper : public ClusterGenerator const ClusterInfo &clusterGenerator = *it; clusterParams.motionShaderID = motionShaderID; - clusterParams.selectable = it->selectable; + clusterParams.selectable = clusterGenerator.selectable; // Note: Make this selectable clusterParams.markerAnimationTime = 0.2; - clusterParams.clusterSize = it->layoutSize; + clusterParams.clusterSize = clusterGenerator.layoutSize; } public: - LayoutManager *layoutManager; + LayoutManagerRef layoutManager; SimpleIDSet currentClusterTex,oldClusterTex; SimpleIdentity motionShaderID; ClusterInfoSet clusterGens; - JNIEnv *env; }; typedef JavaClassInfo LayoutManagerWrapperClassInfo; -template<> LayoutManagerWrapperClassInfo *LayoutManagerWrapperClassInfo::classInfoObj = NULL; +template<> LayoutManagerWrapperClassInfo *LayoutManagerWrapperClassInfo::classInfoObj = nullptr; -JNIEXPORT void JNICALL Java_com_mousebird_maply_LayoutManager_nativeInit - (JNIEnv *env, jclass cls) +extern "C" +JNIEXPORT void JNICALL Java_com_mousebird_maply_LayoutManager_nativeInit(JNIEnv *env, jclass cls) { LayoutManagerWrapperClassInfo::getClassInfo(env,cls); } -JNIEXPORT void JNICALL Java_com_mousebird_maply_LayoutManager_initialise - (JNIEnv *env, jobject obj,jobject sceneObj) +extern "C" +JNIEXPORT void JNICALL Java_com_mousebird_maply_LayoutManager_initialise(JNIEnv *env, jobject obj,jobject sceneObj) { try { - Scene *scene = SceneClassInfo::getClassInfo()->getObject(env,sceneObj); - LayoutManager *layoutManager = dynamic_cast(scene->getManager(kWKLayoutManager)); - LayoutManagerWrapper *wrap = new LayoutManagerWrapper(scene,layoutManager); + const auto scene = SceneClassInfo::get(env,sceneObj); + const auto layoutManager = scene->getManager(kWKLayoutManager); + PlatformInfo_Android threadInfo(env); + auto wrap = new LayoutManagerWrapper(&threadInfo, layoutManager); LayoutManagerWrapperClassInfo::getClassInfo()->setHandle(env, obj, wrap); } catch (...) { - __android_log_print(ANDROID_LOG_VERBOSE, "Maply", "Crash in LayoutManager::initialise()"); + __android_log_print(ANDROID_LOG_ERROR, "Maply", "Crash in LayoutManager::initialise()"); } } static std::mutex disposeMutex; -JNIEXPORT void JNICALL Java_com_mousebird_maply_LayoutManager_dispose - (JNIEnv *env, jobject obj) +extern "C" +JNIEXPORT void JNICALL Java_com_mousebird_maply_LayoutManager_dispose(JNIEnv *env, jobject obj) { try { - LayoutManagerWrapperClassInfo *classInfo = LayoutManagerWrapperClassInfo::getClassInfo(); + auto classInfo = LayoutManagerWrapperClassInfo::getClassInfo(); { std::lock_guard lock(disposeMutex); LayoutManagerWrapper *wrap = classInfo->getObject(env, obj); classInfo->clearHandle(env, obj); - if (!wrap) - return; delete wrap; } } catch (...) { - __android_log_print(ANDROID_LOG_VERBOSE, "Maply", "Crash in LayoutManager::dispose()"); + __android_log_print(ANDROID_LOG_ERROR, "Maply", "Crash in LayoutManager::dispose()"); } } -JNIEXPORT void JNICALL Java_com_mousebird_maply_LayoutManager_setMaxDisplayObjects - (JNIEnv *env, jobject obj, jint maxObjs) +extern "C" +JNIEXPORT void JNICALL Java_com_mousebird_maply_LayoutManager_setMaxDisplayObjects(JNIEnv *env, jobject obj, jint maxObjs) { try { - LayoutManagerWrapperClassInfo *classInfo = LayoutManagerWrapperClassInfo::getClassInfo(); - LayoutManagerWrapper *wrap = classInfo->getObject(env, obj); - if (!wrap) - return - - wrap->layoutManager->setMaxDisplayObjects(maxObjs); + if (auto wrap = LayoutManagerWrapperClassInfo::get(env, obj)) + { + wrap->layoutManager->setMaxDisplayObjects(maxObjs); + } } catch (...) { - __android_log_print(ANDROID_LOG_VERBOSE, "Maply", "Crash in LayoutManager::setMaxDisplayObjects()"); + __android_log_print(ANDROID_LOG_ERROR, "Maply", "Crash in LayoutManager::setMaxDisplayObjects()"); } } +extern "C" JNIEXPORT void JNICALL Java_com_mousebird_maply_LayoutManager_updateLayout (JNIEnv *env, jobject obj, jobject viewStateObj, jobject changeSetObj) { try { - LayoutManagerWrapperClassInfo *classInfo = LayoutManagerWrapperClassInfo::getClassInfo(); - LayoutManagerWrapper *wrap = classInfo->getObject(env, obj); - ViewStateRef *viewState = ViewStateRefClassInfo::getClassInfo()->getObject(env,viewStateObj); - ChangeSetRef *changeSet = ChangeSetClassInfo::getClassInfo()->getObject(env,changeSetObj); - if (!wrap || !viewState || !changeSet) - return; - wrap->setEnv(env); - wrap->updateShader(); + if (auto wrap = LayoutManagerWrapperClassInfo::get(env, obj)) + { + if (auto viewState = ViewStateRefClassInfo::get(env, viewStateObj)) + { + if (auto changeSet = ChangeSetClassInfo::get(env, changeSetObj)) + { + wrap->updateShader(); - wrap->layoutManager->updateLayout(*viewState,*(changeSet->get())); + PlatformInfo_Android threadInfo(env); + wrap->layoutManager->updateLayout(&threadInfo,*viewState,**changeSet); + } + } + } } catch (...) { - __android_log_print(ANDROID_LOG_VERBOSE, "Maply", "Crash in LayoutManager::updateLayout()"); + __android_log_print(ANDROID_LOG_ERROR, "Maply", "Crash in LayoutManager::updateLayout()"); } } -JNIEXPORT jboolean JNICALL Java_com_mousebird_maply_LayoutManager_hasChanges - (JNIEnv *env, jobject obj) +extern "C" +JNIEXPORT jboolean JNICALL Java_com_mousebird_maply_LayoutManager_hasChanges(JNIEnv *env, jobject obj) { try { - LayoutManagerWrapperClassInfo *classInfo = LayoutManagerWrapperClassInfo::getClassInfo(); - LayoutManagerWrapper *wrap = classInfo->getObject(env, obj); - if (!wrap) - return false; - wrap->setEnv(env); - - return wrap->layoutManager->hasChanges(); + if (auto wrap = LayoutManagerWrapperClassInfo::get(env, obj)) + { + return wrap->layoutManager->hasChanges(); + } } catch (...) { - __android_log_print(ANDROID_LOG_VERBOSE, "Maply", "Crash in LayoutManager::hasChanges()"); + __android_log_print(ANDROID_LOG_ERROR, "Maply", "Crash in LayoutManager::hasChanges()"); } - return false; } +extern "C" JNIEXPORT void JNICALL Java_com_mousebird_maply_LayoutManager_addClusterGenerator -(JNIEnv *env, jobject obj, jobject clusterObj, jint clusterID, jboolean selectable, jdouble sizeX, jdouble sizeY) + (JNIEnv *env, jobject obj, jobject clusterObj, jint clusterID, jboolean selectable, jdouble sizeX, jdouble sizeY) { try { - LayoutManagerWrapperClassInfo *classInfo = LayoutManagerWrapperClassInfo::getClassInfo(); - LayoutManagerWrapper *wrap = classInfo->getObject(env, obj); - if (!wrap) - return; - wrap->setEnv(env); - - wrap->addClusterGenerator(clusterObj,clusterID,selectable,sizeX,sizeY); + if (auto wrap = LayoutManagerWrapperClassInfo::get(env, obj)) + { + wrap->addClusterGenerator(env, clusterObj, clusterID, selectable, sizeX, sizeY); + } } catch (...) { - __android_log_print(ANDROID_LOG_VERBOSE, "Maply", "Crash in LayoutManager::addClusterGenerator()"); + __android_log_print(ANDROID_LOG_ERROR, "Maply", "Crash in LayoutManager::addClusterGenerator()"); } } -JNIEXPORT void JNICALL Java_com_mousebird_maply_LayoutManager_clearClusterGenerators -(JNIEnv *env, jobject obj) +extern "C" +JNIEXPORT void JNICALL Java_com_mousebird_maply_LayoutManager_clearClusterGenerators(JNIEnv *env, jobject obj) { try { - LayoutManagerWrapperClassInfo *classInfo = LayoutManagerWrapperClassInfo::getClassInfo(); - LayoutManagerWrapper *wrap = classInfo->getObject(env, obj); - if (!wrap) - return; - wrap->setEnv(env); - - wrap->clearClusterGenerators(); + if (auto wrap = LayoutManagerWrapperClassInfo::get(env, obj)) + { + wrap->clearClusterGenerators(env); + } } catch (...) { - __android_log_print(ANDROID_LOG_VERBOSE, "Maply", "Crash in LayoutManager::addClusterGenerator()"); + __android_log_print(ANDROID_LOG_ERROR, "Maply", "Crash in LayoutManager::addClusterGenerator()"); } } diff --git a/android/library/maply/jni/src/layoutSelection/SelectionManager_jni.cpp b/android/library/maply/jni/src/layoutSelection/SelectionManager_jni.cpp index 5a8e85f6de..07c66ca124 100644 --- a/android/library/maply/jni/src/layoutSelection/SelectionManager_jni.cpp +++ b/android/library/maply/jni/src/layoutSelection/SelectionManager_jni.cpp @@ -28,7 +28,7 @@ using namespace WhirlyKit; using namespace Maply; -static const char *SceneHandleName = "nativeSceneHandle"; +//static const char *SceneHandleName = "nativeSceneHandle"; template<> SelectionManagerClassInfo *SelectionManagerClassInfo::classInfoObj = NULL; @@ -46,8 +46,8 @@ JNIEXPORT void JNICALL Java_com_mousebird_maply_SelectionManager_initialise Scene *scene = SceneClassInfo::getClassInfo()->getObject(env, sceneObj); if (!scene) return; - SelectionManager *selectionManager = dynamic_cast(scene->getManager(kWKSelectionManager)); - SelectionManagerClassInfo::getClassInfo()->setHandle(env,obj,selectionManager); + SelectionManagerRef selectionManager = std::dynamic_pointer_cast(scene->getManager(kWKSelectionManager)); + SelectionManagerClassInfo::getClassInfo()->setHandle(env,obj,new SelectionManagerRef(selectionManager)); } catch (...) { @@ -62,7 +62,11 @@ JNIEXPORT void JNICALL Java_com_mousebird_maply_SelectionManager_dispose { try { - SelectionManagerClassInfo::getClassInfo()->clearHandle(env,obj); + SelectionManagerClassInfo *classInfo = SelectionManagerClassInfo::getClassInfo(); + SelectionManagerRef *selectionManager = classInfo->getObject(env,obj); + if (selectionManager) + delete selectionManager; + classInfo->clearHandle(env,obj); } catch (...) { @@ -76,7 +80,7 @@ JNIEXPORT jlong JNICALL Java_com_mousebird_maply_SelectionManager_pickObject try { SelectionManagerClassInfo *classInfo = SelectionManagerClassInfo::getClassInfo(); - SelectionManager *selectionManager = classInfo->getObject(env,obj); + SelectionManagerRef *selectionManager = classInfo->getObject(env,obj); ViewStateRefClassInfo *viewStateRefClassInfo = ViewStateRefClassInfo::getClassInfo(); ViewStateRef *mapViewState = viewStateRefClassInfo->getObject(env,viewStateObj); Point2dClassInfo *point2DclassInfo = Point2dClassInfo::getClassInfo(); @@ -84,7 +88,7 @@ JNIEXPORT jlong JNICALL Java_com_mousebird_maply_SelectionManager_pickObject if (!selectionManager || !mapViewState || !point) return EmptyIdentity; - return (jlong)selectionManager->pickObject(Point2f(point->x(),point->y()),10.0,*mapViewState); + return (jlong)(*selectionManager)->pickObject(Point2f(point->x(),point->y()),10.0,*mapViewState); } catch (...) { @@ -99,8 +103,8 @@ JNIEXPORT jobjectArray JNICALL Java_com_mousebird_maply_SelectionManager_pickObj { try { - SelectionManager *selectionManager = SelectionManagerClassInfo::getClassInfo()->getObject(env,selManageObj); - ComponentManager *compManager = ComponentManagerClassInfo::getClassInfo()->getObject(env,compManageObj); + SelectionManagerRef *selectionManager = SelectionManagerClassInfo::getClassInfo()->getObject(env,selManageObj); + ComponentManager_AndroidRef *compManager = ComponentManagerClassInfo::getClassInfo()->getObject(env,compManageObj); ViewStateRefClassInfo *viewStateRefClassInfo = ViewStateRefClassInfo::getClassInfo(); ViewStateRef *mapViewState = viewStateRefClassInfo->getObject(env,viewStateObj); Point2dClassInfo *point2DclassInfo = Point2dClassInfo::getClassInfo(); @@ -110,12 +114,12 @@ JNIEXPORT jobjectArray JNICALL Java_com_mousebird_maply_SelectionManager_pickObj std::vector selObjs; - Point2f frameBufferSizeScaled = selectionManager->getSceneRenderer()->getFramebufferSizeScaled(); - Point2f frameBufferSize = selectionManager->getSceneRenderer()->getFramebufferSize(); + Point2f frameBufferSizeScaled = (*selectionManager)->getSceneRenderer()->getFramebufferSizeScaled(); + Point2f frameBufferSize = (*selectionManager)->getSceneRenderer()->getFramebufferSize(); // This takes care of labels, markers, billboards, 3D objects and such. Point2f pt2f(point->x(),point->y()); - selectionManager->pickObjects(pt2f,10.0,*mapViewState,selObjs); + (*selectionManager)->pickObjects(pt2f,10.0,*mapViewState,selObjs); // Need the point in geographic WhirlyGlobe::GlobeViewState *globeViewState = dynamic_cast((*mapViewState).get()); @@ -129,7 +133,7 @@ JNIEXPORT jobjectArray JNICALL Java_com_mousebird_maply_SelectionManager_pickObj Point3d locPoint = (*mapViewState)->coordAdapter->displayToLocal(dispPt); // This one does vector features - auto vecObjs = compManager->findVectors(Point2d(locPoint.x(),locPoint.y()),20.0,*mapViewState,frameBufferSizeScaled,true); + auto vecObjs = (*compManager)->findVectors(Point2d(locPoint.x(),locPoint.y()),20.0,*mapViewState,frameBufferSizeScaled,true); for (auto vecObj : vecObjs) { SelectionManager::SelectedObject selObj; selObj.distIn3D = 0.0; diff --git a/android/library/maply/jni/src/math/Matrix3d_jni.cpp b/android/library/maply/jni/src/math/Matrix3d_jni.cpp index 269190fb92..b6b7c8eab5 100644 --- a/android/library/maply/jni/src/math/Matrix3d_jni.cpp +++ b/android/library/maply/jni/src/math/Matrix3d_jni.cpp @@ -1,9 +1,8 @@ -/* - * Matrix3d_jni.cpp +/* Matrix3d_jni.cpp * WhirlyGlobeLib * * Created by jmnavarro - * Copyright 2011-2016 mousebird consulting + * Copyright 2011-2021 mousebird consulting * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -15,7 +14,6 @@ * 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. - * */ #import @@ -25,20 +23,20 @@ using namespace Eigen; using namespace WhirlyKit; -template<> Matrix3dClassInfo *Matrix3dClassInfo::classInfoObj = NULL; +template<> Matrix3dClassInfo *Matrix3dClassInfo::classInfoObj = nullptr; -JNIEXPORT void JNICALL Java_com_mousebird_maply_Matrix3d_nativeInit -(JNIEnv *env, jclass cls) +extern "C" +JNIEXPORT void JNICALL Java_com_mousebird_maply_Matrix3d_nativeInit(JNIEnv *env, jclass cls) { Matrix3dClassInfo::getClassInfo(env,cls); } -JNIEXPORT void JNICALL Java_com_mousebird_maply_Matrix3d_initialise -(JNIEnv *env, jobject obj) +extern "C" +JNIEXPORT void JNICALL Java_com_mousebird_maply_Matrix3d_initialise(JNIEnv *env, jobject obj) { try { - Matrix3d *mat = new Matrix3d(); + auto mat = new Matrix3d(); *mat = Matrix3d::Identity(); Matrix3dClassInfo::getClassInfo()->setHandle(env,obj,mat); } @@ -50,8 +48,8 @@ JNIEXPORT void JNICALL Java_com_mousebird_maply_Matrix3d_initialise static std::mutex disposeMutex; -JNIEXPORT void JNICALL Java_com_mousebird_maply_Matrix3d_dispose -(JNIEnv *env, jobject obj) +extern "C" +JNIEXPORT void JNICALL Java_com_mousebird_maply_Matrix3d_dispose(JNIEnv *env, jobject obj) { try { @@ -59,10 +57,7 @@ JNIEXPORT void JNICALL Java_com_mousebird_maply_Matrix3d_dispose { std::lock_guard lock(disposeMutex); Matrix3d *inst = classInfo->getObject(env,obj); - if (!inst) - return; delete inst; - classInfo->clearHandle(env,obj); } } @@ -72,15 +67,15 @@ JNIEXPORT void JNICALL Java_com_mousebird_maply_Matrix3d_dispose } } -JNIEXPORT jobject JNICALL Java_com_mousebird_maply_Matrix3d_inverse -(JNIEnv *env, jobject obj) +extern "C" +JNIEXPORT jobject JNICALL Java_com_mousebird_maply_Matrix3d_inverse(JNIEnv *env, jobject obj) { try { Matrix3dClassInfo *classInfo = Matrix3dClassInfo::getClassInfo(); Matrix3d *inst = classInfo->getObject(env,obj); if (!inst) - return NULL; + return nullptr; Matrix3d matInv = inst->inverse(); return MakeMatrix3d(env,matInv); @@ -90,18 +85,18 @@ JNIEXPORT jobject JNICALL Java_com_mousebird_maply_Matrix3d_inverse __android_log_print(ANDROID_LOG_VERBOSE, "Maply", "Crash in Matrix3d::inverse()"); } - return NULL; + return nullptr; } -JNIEXPORT jobject JNICALL Java_com_mousebird_maply_Matrix3d_transpose -(JNIEnv *env, jobject obj) +extern "C" +JNIEXPORT jobject JNICALL Java_com_mousebird_maply_Matrix3d_transpose(JNIEnv *env, jobject obj) { try { Matrix3dClassInfo *classInfo = Matrix3dClassInfo::getClassInfo(); Matrix3d *inst = classInfo->getObject(env,obj); if (!inst) - return NULL; + return nullptr; Matrix3d matTrans = inst->transpose(); return MakeMatrix3d(env,matTrans); @@ -111,11 +106,11 @@ JNIEXPORT jobject JNICALL Java_com_mousebird_maply_Matrix3d_transpose __android_log_print(ANDROID_LOG_VERBOSE, "Maply", "Crash in Matrix3d::transpose()"); } - return NULL; + return nullptr; } -JNIEXPORT jobject JNICALL Java_com_mousebird_maply_Matrix3d_multiply__Lcom_mousebird_maply_Point3d_2 -(JNIEnv *env, jobject obj, jobject ptObj) +extern "C" +JNIEXPORT jobject JNICALL Java_com_mousebird_maply_Matrix3d_multiply__Lcom_mousebird_maply_Point3d_2(JNIEnv *env, jobject obj, jobject ptObj) { try { @@ -124,7 +119,7 @@ JNIEXPORT jobject JNICALL Java_com_mousebird_maply_Matrix3d_multiply__Lcom_mouse Point3dClassInfo *ptClassInfo = Point3dClassInfo::getClassInfo(); Point3d *pt = ptClassInfo->getObject(env,ptObj); if (!mat || !pt) - return NULL; + return nullptr; Point3d ret = (*mat) * (*pt); @@ -135,38 +130,37 @@ JNIEXPORT jobject JNICALL Java_com_mousebird_maply_Matrix3d_multiply__Lcom_mouse __android_log_print(ANDROID_LOG_VERBOSE, "Maply", "Crash in Matrix3d::multiply()"); } - return NULL; + return nullptr; } -JNIEXPORT jobject JNICALL Java_com_mousebird_maply_Matrix3d_multiply__Lcom_mousebird_maply_Matrix3d_2 -(JNIEnv *env, jobject obj, jobject mtxObj) +extern "C" +JNIEXPORT jobject JNICALL Java_com_mousebird_maply_Matrix3d_multiply__Lcom_mousebird_maply_Matrix3d_2(JNIEnv *env, jobject obj, jobject mtxObj) { try { Matrix3dClassInfo *classInfo = Matrix3dClassInfo::getClassInfo(); - Matrix3d *mat = classInfo->getObject(env,obj); - Matrix3d *mtx = classInfo->getObject(env,mtxObj); - if (!mat || !mtx) - return NULL; - - Matrix3d ret = (*mat) * (*mtx); - - return MakeMatrix3d(env,ret); + if (auto mat = classInfo->getObject(env,obj)) + { + if (auto mtx = classInfo->getObject(env, mtxObj)) + { + return MakeMatrix3d(env, (*mat) * (*mtx)); + } + } } catch (...) { __android_log_print(ANDROID_LOG_VERBOSE, "Maply", "Crash in Matrix3d::multiply()"); } - return NULL; + return nullptr; } -JNIEXPORT jobject JNICALL Java_com_mousebird_maply_Matrix3d_translate -(JNIEnv *env, jclass cls, jdouble x, jdouble y) +extern "C" +JNIEXPORT jobject JNICALL Java_com_mousebird_maply_Matrix3d_translate(JNIEnv *env, jclass, jdouble x, jdouble y) { try { - Matrix3dClassInfo *classInfo = Matrix3dClassInfo::getClassInfo(env, cls); + //Matrix3dClassInfo *classInfo = Matrix3dClassInfo::getClassInfo(env, cls); Affine2d trans(Eigen::Translation2d(x,y)); Matrix3d mat = trans.matrix(); @@ -178,15 +172,15 @@ JNIEXPORT jobject JNICALL Java_com_mousebird_maply_Matrix3d_translate __android_log_print(ANDROID_LOG_VERBOSE, "Maply", "Crash in Matrix3d::translateX()"); } - return NULL; + return nullptr; } -JNIEXPORT jobject JNICALL Java_com_mousebird_maply_Matrix3d_scale -(JNIEnv *env, jclass cls, jdouble x, jdouble y) +extern "C" +JNIEXPORT jobject JNICALL Java_com_mousebird_maply_Matrix3d_scale(JNIEnv *env, jclass, jdouble x, jdouble y) { try { - Matrix3dClassInfo *classInfo = Matrix3dClassInfo::getClassInfo(env, cls); + //Matrix3dClassInfo *classInfo = Matrix3dClassInfo::getClassInfo(env, cls); Affine2d trans(Eigen::Scaling(x,y)); Matrix3d mat = trans.matrix(); @@ -198,7 +192,7 @@ JNIEXPORT jobject JNICALL Java_com_mousebird_maply_Matrix3d_scale __android_log_print(ANDROID_LOG_VERBOSE, "Maply", "Crash in Matrix3d::scaleX()"); } - return NULL; + return nullptr; } jobject MakeMatrix3d(JNIEnv *env,const Eigen::Matrix3d &mat) diff --git a/android/library/maply/jni/src/math/Matrix4d_jni.cpp b/android/library/maply/jni/src/math/Matrix4d_jni.cpp index b6506b3013..ba7b1754e9 100644 --- a/android/library/maply/jni/src/math/Matrix4d_jni.cpp +++ b/android/library/maply/jni/src/math/Matrix4d_jni.cpp @@ -116,11 +116,11 @@ JNIEXPORT jobject JNICALL Java_com_mousebird_maply_Matrix4d_transpose } JNIEXPORT jobject JNICALL Java_com_mousebird_maply_Matrix4d_translate -(JNIEnv *env, jclass cls, jdouble x, jdouble y, jdouble z) +(JNIEnv *env, jclass, jdouble x, jdouble y, jdouble z) { try { - Matrix4dClassInfo *classInfo = Matrix4dClassInfo::getClassInfo(env, cls); + //Matrix4dClassInfo *classInfo = Matrix4dClassInfo::getClassInfo(env, cls); Affine3d trans(Eigen::Translation3d(x,y,z)); Matrix4d mat = trans.matrix(); @@ -136,11 +136,11 @@ JNIEXPORT jobject JNICALL Java_com_mousebird_maply_Matrix4d_translate } JNIEXPORT jobject JNICALL Java_com_mousebird_maply_Matrix4d_scale -(JNIEnv *env, jclass cls, jdouble x, jdouble y, jdouble z) +(JNIEnv *env, jclass, jdouble x, jdouble y, jdouble z) { try { - Matrix4dClassInfo *classInfo = Matrix4dClassInfo::getClassInfo(env, cls); + //Matrix4dClassInfo *classInfo = Matrix4dClassInfo::getClassInfo(env, cls); Affine3d trans(Eigen::Scaling(x,y,z)); Matrix4d mat = trans.matrix(); diff --git a/android/library/maply/jni/src/math/Point2d_jni.cpp b/android/library/maply/jni/src/math/Point2d_jni.cpp index c1a7bd8b0a..52713c1289 100644 --- a/android/library/maply/jni/src/math/Point2d_jni.cpp +++ b/android/library/maply/jni/src/math/Point2d_jni.cpp @@ -1,9 +1,8 @@ -/* - * Point2d_jni.cpp +/* Point2d_jni.cpp * WhirlyGlobeLib * * Created by Steve Gifford on 6/2/14. - * Copyright 2011-2016 mousebird consulting + * Copyright 2011-2021 mousebird consulting * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -15,52 +14,55 @@ * 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. - * */ #import #import "Geometry_jni.h" #import "com_mousebird_maply_Point2d.h" -template<> Point2dClassInfo *Point2dClassInfo::classInfoObj = NULL; +template<> Point2dClassInfo *Point2dClassInfo::classInfoObj = nullptr; using namespace Eigen; using namespace WhirlyKit; -JNIEXPORT void JNICALL Java_com_mousebird_maply_Point2d_nativeInit - (JNIEnv *env, jclass cls) +extern "C" +JNIEXPORT void JNICALL Java_com_mousebird_maply_Point2d_nativeInit(JNIEnv *env, jclass cls) { Point2dClassInfo::getClassInfo(env,cls); } +JNIEXPORT jobject JNICALL MakePoint2d(JNIEnv *env) +{ + auto classInfo = Point2dClassInfo::getClassInfo(env,"com/mousebird/maply/Point2d"); + return classInfo->makeWrapperObject(env,nullptr); +} + JNIEXPORT jobject JNICALL MakePoint2d(JNIEnv *env,const WhirlyKit::Point2d &pt) { - Point2dClassInfo *classInfo = Point2dClassInfo::getClassInfo(env,"com/mousebird/maply/Point2d"); - jobject newObj = classInfo->makeWrapperObject(env,NULL); - WhirlyKit::Point2d *inst = classInfo->getObject(env,newObj); + jobject newObj = MakePoint2d(env); + auto inst = Point2dClassInfo::getClassInfo()->getObject(env,newObj); *inst = pt; - return newObj; } -JNIEXPORT void JNICALL Java_com_mousebird_maply_Point2d_initialise - (JNIEnv *env, jobject obj) +extern "C" +JNIEXPORT void JNICALL Java_com_mousebird_maply_Point2d_initialise(JNIEnv *env, jobject obj) { try { - Point2d *pt = new Point2d(); + auto pt = new Point2d(); Point2dClassInfo::getClassInfo()->setHandle(env,obj,pt); } catch (...) { - __android_log_print(ANDROID_LOG_VERBOSE, "Maply", "Crash in Point2d::initialise()"); + __android_log_print(ANDROID_LOG_ERROR, "Maply", "Crash in Point2d::initialise()"); } } static std::mutex disposeMutex; -JNIEXPORT void JNICALL Java_com_mousebird_maply_Point2d_dispose - (JNIEnv *env, jobject obj) +extern "C" +JNIEXPORT void JNICALL Java_com_mousebird_maply_Point2d_dispose(JNIEnv *env, jobject obj) { try { @@ -77,64 +79,57 @@ JNIEXPORT void JNICALL Java_com_mousebird_maply_Point2d_dispose } catch (...) { - __android_log_print(ANDROID_LOG_VERBOSE, "Maply", "Crash in Point2d::dispose()"); + __android_log_print(ANDROID_LOG_ERROR, "Maply", "Crash in Point2d::dispose()"); } } -JNIEXPORT jdouble JNICALL Java_com_mousebird_maply_Point2d_getX - (JNIEnv *env, jobject obj) +extern "C" +JNIEXPORT jdouble JNICALL Java_com_mousebird_maply_Point2d_getX(JNIEnv *env, jobject obj) { try { Point2dClassInfo *classInfo = Point2dClassInfo::getClassInfo(); Point2d *pt = classInfo->getObject(env,obj); - if (!pt) - return 0.0; - - return pt->x(); + return pt ? pt->x() : 0.0; } catch (...) { - __android_log_print(ANDROID_LOG_VERBOSE, "Maply", "Crash in Point2d::getX()"); + __android_log_print(ANDROID_LOG_ERROR, "Maply", "Crash in Point2d::getX()"); } - return 0.0; } -JNIEXPORT jdouble JNICALL Java_com_mousebird_maply_Point2d_getY - (JNIEnv *env, jobject obj) +extern "C" +JNIEXPORT jdouble JNICALL Java_com_mousebird_maply_Point2d_getY(JNIEnv *env, jobject obj) { try { Point2dClassInfo *classInfo = Point2dClassInfo::getClassInfo(); Point2d *pt = classInfo->getObject(env,obj); - if (!pt) - return 0.0; - - return pt->y(); + return pt ? pt->y() : 0.0; } catch (...) { - __android_log_print(ANDROID_LOG_VERBOSE, "Maply", "Crash in Point2d::getY()"); + __android_log_print(ANDROID_LOG_ERROR, "Maply", "Crash in Point2d::getY()"); } return 0.0; } -JNIEXPORT void JNICALL Java_com_mousebird_maply_Point2d_setValue - (JNIEnv *env, jobject obj, jdouble x, jdouble y) +extern "C" +JNIEXPORT void JNICALL Java_com_mousebird_maply_Point2d_setValue(JNIEnv *env, jobject obj, jdouble x, jdouble y) { try { Point2dClassInfo *classInfo = Point2dClassInfo::getClassInfo(); - Point2d *pt = classInfo->getObject(env,obj); - if (!pt) - return; - pt->x() = x; - pt->y() = y; + if (auto pt = classInfo->getObject(env,obj)) + { + pt->x() = x; + pt->y() = y; + } } catch (...) { - __android_log_print(ANDROID_LOG_VERBOSE, "Maply", "Crash in Point2d::setValue()"); + __android_log_print(ANDROID_LOG_ERROR, "Maply", "Crash in Point2d::setValue()"); } } diff --git a/android/library/maply/jni/src/particles/ParticleBatch_jni.cpp b/android/library/maply/jni/src/particles/ParticleBatch_jni.cpp index a827ede853..bf0df051c1 100644 --- a/android/library/maply/jni/src/particles/ParticleBatch_jni.cpp +++ b/android/library/maply/jni/src/particles/ParticleBatch_jni.cpp @@ -1,9 +1,8 @@ -/* - * ParticleBatch_jni.cpp +/* ParticleBatch_jni.cpp * WhirlyGlobeLib * * Created by jmnavarro on 23/1/16. - * Copyright 2011-2016 mousebird consulting + * Copyright 2011-2021 mousebird consulting * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -15,23 +14,22 @@ * 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. - * */ #import "Particles_jni.h" #import "com_mousebird_maply_ParticleBatch.h" using namespace WhirlyKit; -template<> ParticleBatchClassInfo *ParticleBatchClassInfo::classInfoObj = NULL; +template<> ParticleBatchClassInfo *ParticleBatchClassInfo::classInfoObj = nullptr; -JNIEXPORT void JNICALL Java_com_mousebird_maply_ParticleBatch_nativeInit -(JNIEnv *env, jclass cls) +extern "C" +JNIEXPORT void JNICALL Java_com_mousebird_maply_ParticleBatch_nativeInit(JNIEnv *env, jclass cls) { ParticleBatchClassInfo::getClassInfo(env, cls); } -JNIEXPORT void JNICALL Java_com_mousebird_maply_ParticleBatch_initialise -(JNIEnv *env, jobject obj) +extern "C" +JNIEXPORT void JNICALL Java_com_mousebird_maply_ParticleBatch_initialise(JNIEnv *env, jobject obj) { try { ParticleBatchClassInfo *info = ParticleBatchClassInfo::getClassInfo(); @@ -44,8 +42,8 @@ JNIEXPORT void JNICALL Java_com_mousebird_maply_ParticleBatch_initialise static std::mutex disposeMutex; -JNIEXPORT void JNICALL Java_com_mousebird_maply_ParticleBatch_dispose -(JNIEnv *env, jobject obj) +extern "C" +JNIEXPORT void JNICALL Java_com_mousebird_maply_ParticleBatch_dispose(JNIEnv *env, jobject obj) { try { ParticleBatchClassInfo *info = ParticleBatchClassInfo::getClassInfo(); @@ -64,8 +62,8 @@ JNIEXPORT void JNICALL Java_com_mousebird_maply_ParticleBatch_dispose } } -JNIEXPORT void JNICALL Java_com_mousebird_maply_ParticleBatch_setPartSysNative - (JNIEnv *env, jobject obj, jobject partSysObj) +extern "C" +JNIEXPORT void JNICALL Java_com_mousebird_maply_ParticleBatch_setPartSysNative(JNIEnv *env, jobject obj, jobject partSysObj) { try { ParticleBatchClassInfo *classInfo = ParticleBatchClassInfo::getClassInfo(); @@ -81,8 +79,8 @@ JNIEXPORT void JNICALL Java_com_mousebird_maply_ParticleBatch_setPartSysNative } } -JNIEXPORT void JNICALL Java_com_mousebird_maply_ParticleBatch_setTime - (JNIEnv *env, jobject obj, jdouble baseTime) +extern "C" +JNIEXPORT void JNICALL Java_com_mousebird_maply_ParticleBatch_setTime(JNIEnv *env, jobject obj, jdouble baseTime) { try { ParticleBatchClassInfo *classInfo = ParticleBatchClassInfo::getClassInfo(); @@ -96,8 +94,8 @@ JNIEXPORT void JNICALL Java_com_mousebird_maply_ParticleBatch_setTime } } -JNIEXPORT jdouble JNICALL Java_com_mousebird_maply_ParticleBatch_getTime - (JNIEnv *env, jobject obj) +extern "C" +JNIEXPORT jdouble JNICALL Java_com_mousebird_maply_ParticleBatch_getTime(JNIEnv *env, jobject obj) { try { ParticleBatchClassInfo *classInfo = ParticleBatchClassInfo::getClassInfo(); @@ -113,6 +111,7 @@ JNIEXPORT jdouble JNICALL Java_com_mousebird_maply_ParticleBatch_getTime return 0.0; } +extern "C" JNIEXPORT jboolean JNICALL Java_com_mousebird_maply_ParticleBatch_addAttribute__Ljava_lang_String_2_3F (JNIEnv *env, jobject obj, jstring inName, jfloatArray floatArray) { @@ -127,7 +126,7 @@ JNIEXPORT jboolean JNICALL Java_com_mousebird_maply_ParticleBatch_addAttribute__ jfloat *body = env->GetFloatArrayElements(floatArray, 0); jsize len = env->GetArrayLength(floatArray); JavaString name(env,inName); - ret = batch->addAttributeDataFloat(name.cStr,body,len); + ret = batch->addAttributeDataFloat(name.getCString(),body,len); env->ReleaseFloatArrayElements(floatArray, body, 0); } catch (...) { __android_log_print(ANDROID_LOG_VERBOSE, "Maply", "Crash in ParticleBatch::addAttribute()"); @@ -136,6 +135,7 @@ JNIEXPORT jboolean JNICALL Java_com_mousebird_maply_ParticleBatch_addAttribute__ return ret; } +extern "C" JNIEXPORT jboolean JNICALL Java_com_mousebird_maply_ParticleBatch_addAttribute__Ljava_lang_String_2_3C (JNIEnv *env, jobject obj, jstring inName, jcharArray charArray) { @@ -150,7 +150,7 @@ JNIEXPORT jboolean JNICALL Java_com_mousebird_maply_ParticleBatch_addAttribute__ jchar *body = env->GetCharArrayElements(charArray, 0); jsize len = env->GetArrayLength(charArray); JavaString name(env,inName); - ret = batch->addAttributeDataChar(name.cStr,(const char *)body,len); + ret = batch->addAttributeDataChar(name.getCString(),(const char *)body,len); env->ReleaseCharArrayElements(charArray, body, 0); } catch (...) { __android_log_print(ANDROID_LOG_VERBOSE, "Maply", "Crash in ParticleBatch::addAttribute()"); @@ -159,8 +159,8 @@ JNIEXPORT jboolean JNICALL Java_com_mousebird_maply_ParticleBatch_addAttribute__ return ret; } -JNIEXPORT jboolean JNICALL Java_com_mousebird_maply_ParticleBatch_isValid - (JNIEnv *env, jobject obj) +extern "C" +JNIEXPORT jboolean JNICALL Java_com_mousebird_maply_ParticleBatch_isValid(JNIEnv *env, jobject obj) { bool ret = false; diff --git a/android/library/maply/jni/src/particles/ParticleSystemManager_jni.cpp b/android/library/maply/jni/src/particles/ParticleSystemManager_jni.cpp index a3cd9620b1..673d47a23f 100644 --- a/android/library/maply/jni/src/particles/ParticleSystemManager_jni.cpp +++ b/android/library/maply/jni/src/particles/ParticleSystemManager_jni.cpp @@ -1,9 +1,8 @@ -/* - * ParticleSystemManager_jni.cpp +/* ParticleSystemManager_jni.cpp * WhirlyGlobeLib * * Created by jmnavarro on 23/1/16. - * Copyright 2011-2016 mousebird consulting + * Copyright 2011-2021 mousebird consulting * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -15,128 +14,119 @@ * 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. - * */ #import "Particles_jni.h" #import "Scene_jni.h" #import "com_mousebird_maply_ParticleSystemManager.h" - using namespace WhirlyKit; using namespace Maply; -template<> ParticleSystemManagerClassInfo * ParticleSystemManagerClassInfo::classInfoObj = NULL; +template<> ParticleSystemManagerClassInfo * ParticleSystemManagerClassInfo::classInfoObj = nullptr; -JNIEXPORT void JNICALL Java_com_mousebird_maply_ParticleSystemManager_nativeInit -(JNIEnv *env, jclass cls) +extern "C" +JNIEXPORT void JNICALL Java_com_mousebird_maply_ParticleSystemManager_nativeInit(JNIEnv *env, jclass cls) { ParticleSystemManagerClassInfo::getClassInfo(env, cls); } -JNIEXPORT void JNICALL Java_com_mousebird_maply_ParticleSystemManager_initialize -(JNIEnv *env, jobject obj, jobject sceneObj) +extern "C" +JNIEXPORT void JNICALL Java_com_mousebird_maply_ParticleSystemManager_initialize(JNIEnv *env, jobject obj, jobject sceneObj) { - try { - Scene *scene = SceneClassInfo::getClassInfo()->getObject(env, sceneObj); - if (!scene) - return; - ParticleSystemManager *particleSystemManager = dynamic_cast(scene->getManager(kWKParticleSystemManager)); - ParticleSystemManagerClassInfo::getClassInfo()->setHandle(env, obj, particleSystemManager); + if (auto scene = SceneClassInfo::get(env, sceneObj)) { + const auto manager = scene->getManager(kWKParticleSystemManager); + ParticleSystemManagerClassInfo::getClassInfo()->setHandle(env, obj, + new ParticleSystemManagerRef(manager)); + } } catch (...) { - __android_log_print(ANDROID_LOG_VERBOSE, "Maply", "Crash in ParticleSystemManager::initialise()"); + __android_log_print(ANDROID_LOG_ERROR, "Maply", "Crash in ParticleSystemManager::initialise()"); } } static std::mutex disposeMutex; -JNIEXPORT void JNICALL Java_com_mousebird_maply_ParticleSystemManager_dispose -(JNIEnv *env, jobject obj) +extern "C" +JNIEXPORT void JNICALL Java_com_mousebird_maply_ParticleSystemManager_dispose(JNIEnv *env, jobject obj) { try { - ParticleSystemManagerClassInfo *classInfo = ParticleSystemManagerClassInfo::getClassInfo(); - classInfo->clearHandle(env, obj); + auto manager = ParticleSystemManagerClassInfo::get(env, obj); + delete manager; + ParticleSystemManagerClassInfo::getClassInfo()->clearHandle(env, obj); } catch(...) { - __android_log_print(ANDROID_LOG_VERBOSE, "Maply", "Crash in ParticleSystemManager::dispose()"); + __android_log_print(ANDROID_LOG_ERROR, "Maply", "Crash in ParticleSystemManager::dispose()"); } } - + +extern "C" JNIEXPORT jlong JNICALL Java_com_mousebird_maply_ParticleSystemManager_addParticleSystem -(JNIEnv *env, jobject obj, jobject parSysObj, jobject changesObj) + (JNIEnv *env, jobject obj, jobject parSysObj, jobject changesObj) { try { - ParticleSystemManagerClassInfo *classInfo = ParticleSystemManagerClassInfo::getClassInfo(); - ParticleSystemManager *particleSystemManager = classInfo->getObject(env, obj); - - ParticleSystem *parSys = ParticleSystemClassInfo::getClassInfo()->getObject(env, parSysObj); - ChangeSetRef *changes = ChangeSetClassInfo::getClassInfo()->getObject(env, changesObj); - - if (!particleSystemManager || !parSys || !changes) - return EmptyIdentity; - - return particleSystemManager->addParticleSystem(*parSys, *(changes->get())); + if (const auto manager = ParticleSystemManagerClassInfo::get(env, obj)) { + if (const auto parSys = ParticleSystemClassInfo::get(env, parSysObj)) { + if (const auto changes = ChangeSetClassInfo::get(env, changesObj)) { + return (*manager)->addParticleSystem(*parSys, **changes); + } + } + } } catch(...) { - __android_log_print(ANDROID_LOG_VERBOSE, "Maply", "Crash in ParticleSystemManager::addParticleSystem"); + __android_log_print(ANDROID_LOG_ERROR, "Maply", "Crash in ParticleSystemManager::addParticleSystem"); } return EmptyIdentity; } - + +extern "C" JNIEXPORT void JNICALL Java_com_mousebird_maply_ParticleSystemManager_addParticleBatch -(JNIEnv *env, jobject obj, jlong id, jobject batchObj, jobject changeObj) + (JNIEnv *env, jobject obj, jlong id, jobject batchObj, jobject changeObj) { try { - ParticleSystemManagerClassInfo *classInfo = ParticleSystemManagerClassInfo::getClassInfo(); - ParticleSystemManager *particleSystemManager = classInfo->getObject(env, obj); - - ParticleBatch *batch = ParticleBatchClassInfo::getClassInfo()->getObject(env, batchObj); - ChangeSetRef *changes = ChangeSetClassInfo::getClassInfo()->getObject(env, changeObj); - - if (!particleSystemManager || !batch || !changes) - return; - - particleSystemManager->addParticleBatch(id, *batch, *(changes->get())); + if (const auto manager = ParticleSystemManagerClassInfo::get(env, obj)) { + if (const auto batch = ParticleBatchClassInfo::get(env, batchObj)) { + if (const auto changes = ChangeSetClassInfo::get(env, changeObj)) { + (*manager)->addParticleBatch(id, *batch, **changes); + } + } + } } catch(...) { - __android_log_print(ANDROID_LOG_VERBOSE, "Maply", "Crash in ParticleSystemManager::addParticleBatch"); + __android_log_print(ANDROID_LOG_ERROR, "Maply", "Crash in ParticleSystemManager::addParticleBatch"); } } +extern "C" JNIEXPORT void JNICALL Java_com_mousebird_maply_ParticleSystemManager_enableParticleSystem (JNIEnv *env, jobject obj, jlong id, jboolean enable, jobject changeObj) { try { - ParticleSystemManagerClassInfo *classInfo = ParticleSystemManagerClassInfo::getClassInfo(); - ParticleSystemManager *particleSystemManager = classInfo->getObject(env, obj); - - ChangeSetRef *changes = ChangeSetClassInfo::getClassInfo()->getObject(env, changeObj); - if (!particleSystemManager || !changes) - return; - - particleSystemManager->enableParticleSystem(id, enable, *(changes->get())); + if (const auto manager = ParticleSystemManagerClassInfo::get(env, obj)) { + if (const auto changes = ChangeSetClassInfo::get(env, changeObj)) { + (*manager)->enableParticleSystem(id, enable, **changes); + } + } } catch(...) { - __android_log_print(ANDROID_LOG_VERBOSE, "Maply", "Crash in ParticleSystemManager::enableParticleSystem"); + __android_log_print(ANDROID_LOG_ERROR, "Maply", "Crash in ParticleSystemManager::enableParticleSystem"); } } +extern "C" JNIEXPORT void JNICALL Java_com_mousebird_maply_ParticleSystemManager_removeParticleSystem -(JNIEnv *env, jobject obj, jlong sysID, jobject changeObj) + (JNIEnv *env, jobject obj, jlong sysID, jobject changeObj) { try { - ParticleSystemManagerClassInfo *classInfo = ParticleSystemManagerClassInfo::getClassInfo(); - ParticleSystemManager *particleSystemManager = classInfo->getObject(env, obj); - - ChangeSetRef *changes = ChangeSetClassInfo::getClassInfo()->getObject(env, changeObj); - if (!particleSystemManager || !changes) - return; - particleSystemManager->removeParticleSystem(sysID, *(changes->get())); + if (const auto manager = ParticleSystemManagerClassInfo::get(env, obj)) { + if (const auto changes = ChangeSetClassInfo::get(env, changeObj)) { + (*manager)->removeParticleSystem(sysID, **changes); + } + } } catch(...) { - __android_log_print(ANDROID_LOG_VERBOSE, "Maply", "Crash in ParticleSystemManager::removeParticleSystems"); + __android_log_print(ANDROID_LOG_ERROR, "Maply", "Crash in ParticleSystemManager::removeParticleSystems"); } } diff --git a/android/library/maply/jni/src/particles/ParticleSystem_jni.cpp b/android/library/maply/jni/src/particles/ParticleSystem_jni.cpp index 70c6344b0d..f58490cb96 100644 --- a/android/library/maply/jni/src/particles/ParticleSystem_jni.cpp +++ b/android/library/maply/jni/src/particles/ParticleSystem_jni.cpp @@ -1,9 +1,8 @@ -/* - * ParticleSystem_jni.cpp +/* ParticleSystem_jni.cpp * WhirlyGlobeLib * * Created by jmnavarro on 23/1/16. - * Copyright 2011-2016 mousebird consulting + * Copyright 2011-2021 mousebird consulting * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -15,28 +14,26 @@ * 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. - * */ #import "Particles_jni.h" #import "com_mousebird_maply_ParticleSystem.h" using namespace WhirlyKit; -template<> ParticleSystemClassInfo *ParticleSystemClassInfo::classInfoObj = NULL; +template<> ParticleSystemClassInfo *ParticleSystemClassInfo::classInfoObj = nullptr; -JNIEXPORT void JNICALL Java_com_mousebird_maply_ParticleSystem_nativeInit -(JNIEnv *env, jclass cls) +extern "C" +JNIEXPORT void JNICALL Java_com_mousebird_maply_ParticleSystem_nativeInit(JNIEnv *env, jclass cls) { ParticleSystemClassInfo::getClassInfo(env, cls); } -JNIEXPORT void JNICALL Java_com_mousebird_maply_ParticleSystem_initialise -(JNIEnv *env, jobject obj) +extern "C" +JNIEXPORT void JNICALL Java_com_mousebird_maply_ParticleSystem_initialise(JNIEnv *env, jobject obj) { try { - ParticleSystemClassInfo *classInfo = ParticleSystemClassInfo::getClassInfo(); - ParticleSystem *inst = new ParticleSystem(); - classInfo->setHandle(env, obj, inst); + const auto classInfo = ParticleSystemClassInfo::getClassInfo(); + classInfo->setHandle(env, obj, new ParticleSystem()); } catch (...) { __android_log_print(ANDROID_LOG_VERBOSE, "Maply", "Crash in ParticleSystem::initialise()"); @@ -45,18 +42,14 @@ JNIEXPORT void JNICALL Java_com_mousebird_maply_ParticleSystem_initialise static std::mutex disposeMutex; -JNIEXPORT void JNICALL Java_com_mousebird_maply_ParticleSystem_dispose -(JNIEnv *env, jobject obj) +extern "C" +JNIEXPORT void JNICALL Java_com_mousebird_maply_ParticleSystem_dispose(JNIEnv *env, jobject obj) { try { - ParticleSystemClassInfo *classInfo = ParticleSystemClassInfo::getClassInfo(); - { - std::lock_guard lock(disposeMutex); - ParticleSystem *inst = classInfo->getObject(env, obj); - if (!inst) - return; + const auto classInfo = ParticleSystemClassInfo::getClassInfo(); + std::lock_guard lock(disposeMutex); + if (auto inst = classInfo->getObject(env, obj)) { delete inst; - classInfo->clearHandle(env, obj); } } @@ -65,31 +58,28 @@ JNIEXPORT void JNICALL Java_com_mousebird_maply_ParticleSystem_dispose } } -JNIEXPORT void JNICALL Java_com_mousebird_maply_ParticleSystem_setName -(JNIEnv *env, jobject obj, jstring name) +extern "C" +JNIEXPORT void JNICALL Java_com_mousebird_maply_ParticleSystem_setName(JNIEnv *env, jobject obj, jstring name) { try { - ParticleSystemClassInfo *classInfo = ParticleSystemClassInfo::getClassInfo(); - ParticleSystem *inst = classInfo->getObject(env, obj); - if (!inst) - return; - JavaString jstr(env, name); - inst->name = jstr.cStr; + const auto classInfo = ParticleSystemClassInfo::getClassInfo(); + if (auto inst = classInfo->getObject(env, obj)) { + const JavaString jstr(env, name); + inst->name = jstr.getCString(); + } } catch (...) { __android_log_print(ANDROID_LOG_VERBOSE, "Maply", "Crash in ParticleSystem::setName()"); } } -JNIEXPORT jlong JNICALL Java_com_mousebird_maply_ParticleSystem_getID -(JNIEnv *env, jobject obj) +extern "C" +JNIEXPORT jlong JNICALL Java_com_mousebird_maply_ParticleSystem_getID(JNIEnv *env, jobject obj) { try { - ParticleSystemClassInfo *classInfo = ParticleSystemClassInfo::getClassInfo(); - ParticleSystem *inst = classInfo->getObject(env, obj); - if (!inst) - return EmptyIdentity; - return inst->getId(); + const auto classInfo = ParticleSystemClassInfo::getClassInfo(); + const auto inst = classInfo->getObject(env, obj); + return inst ? inst->getId() : EmptyIdentity; } catch (...) { __android_log_print(ANDROID_LOG_VERBOSE, "Maply", "Crash in ParticleSystem::getID()"); @@ -98,24 +88,17 @@ JNIEXPORT jlong JNICALL Java_com_mousebird_maply_ParticleSystem_getID return EmptyIdentity; } -JNIEXPORT void JNICALL Java_com_mousebird_maply_ParticleSystem_setParticleSystemTypeNative -(JNIEnv *env, jobject obj, jint type) +extern "C" +JNIEXPORT void JNICALL Java_com_mousebird_maply_ParticleSystem_setParticleSystemTypeNative(JNIEnv *env, jobject obj, jint type) { try { - ParticleSystemClassInfo *classInfo = ParticleSystemClassInfo::getClassInfo(); - ParticleSystem *inst = classInfo->getObject(env, obj); - if (!inst) - return; - switch (type) { - case 0: - inst->type = ParticleSystemPoint; - break; - case 1: - inst->type = ParticleSystemRectangle; - break; - default: - inst->type = ParticleSystemPoint; - break; + const auto classInfo = ParticleSystemClassInfo::getClassInfo(); + if (auto inst = classInfo->getObject(env, obj)) { + switch (type) { + default: + case 0: inst->type = ParticleSystemPoint; break; + case 1: inst->type = ParticleSystemRectangle; break; + } } } catch (...) { @@ -123,166 +106,140 @@ JNIEXPORT void JNICALL Java_com_mousebird_maply_ParticleSystem_setParticleSystem } } -JNIEXPORT void JNICALL Java_com_mousebird_maply_ParticleSystem_setPositionShaderID - (JNIEnv *env, jobject obj, jlong shaderID) +extern "C" +JNIEXPORT void JNICALL Java_com_mousebird_maply_ParticleSystem_setPositionShaderID(JNIEnv *env, jobject obj, jlong shaderID) { try { - ParticleSystemClassInfo *classInfo = ParticleSystemClassInfo::getClassInfo(); - ParticleSystem *inst = classInfo->getObject(env, obj); - if (!inst) - return; - - inst->calcShaderID = shaderID; + const auto classInfo = ParticleSystemClassInfo::getClassInfo(); + if (auto inst = classInfo->getObject(env, obj)) { + inst->calcShaderID = shaderID; + } } catch (...) { __android_log_print(ANDROID_LOG_VERBOSE, "Maply", "Crash in ParticleSystem::setPositionShaderID()"); } } -JNIEXPORT void JNICALL Java_com_mousebird_maply_ParticleSystem_setRenderShaderID - (JNIEnv *env, jobject obj, jlong shaderID) +extern "C" +JNIEXPORT void JNICALL Java_com_mousebird_maply_ParticleSystem_setRenderShaderID(JNIEnv *env, jobject obj, jlong shaderID) { try { - ParticleSystemClassInfo *classInfo = ParticleSystemClassInfo::getClassInfo(); - ParticleSystem *inst = classInfo->getObject(env, obj); - if (!inst) - return; - - inst->renderShaderID = shaderID; + const auto classInfo = ParticleSystemClassInfo::getClassInfo(); + if (auto inst = classInfo->getObject(env, obj)) { + inst->renderShaderID = shaderID; + } } catch (...) { __android_log_print(ANDROID_LOG_VERBOSE, "Maply", "Crash in ParticleSystem::setRenderShaderID()"); } } -JNIEXPORT void JNICALL Java_com_mousebird_maply_ParticleSystem_setLifetime -(JNIEnv *env, jobject obj, jdouble lifeTime) +extern "C" +JNIEXPORT void JNICALL Java_com_mousebird_maply_ParticleSystem_setLifetime(JNIEnv *env, jobject obj, jdouble lifeTime) { try { - ParticleSystemClassInfo *classInfo = ParticleSystemClassInfo::getClassInfo(); - ParticleSystem *inst = classInfo->getObject(env, obj); - if (!inst) - return; - - inst->lifetime = lifeTime; + const auto classInfo = ParticleSystemClassInfo::getClassInfo(); + if (auto inst = classInfo->getObject(env, obj)) { + inst->lifetime = lifeTime; + } } catch (...) { __android_log_print(ANDROID_LOG_VERBOSE, "Maply", "Crash in ParticleSystem::setLifetime()"); } } -JNIEXPORT void JNICALL Java_com_mousebird_maply_ParticleSystem_setBasetime -(JNIEnv *env, jobject obj, jdouble baseTime) +extern "C" +JNIEXPORT void JNICALL Java_com_mousebird_maply_ParticleSystem_setBasetime(JNIEnv *env, jobject obj, jdouble baseTime) { try { - ParticleSystemClassInfo *classInfo = ParticleSystemClassInfo::getClassInfo(); - ParticleSystem *inst = classInfo->getObject(env, obj); - if (!inst) - return; - - inst->baseTime = baseTime; + const auto classInfo = ParticleSystemClassInfo::getClassInfo(); + if (auto inst = classInfo->getObject(env, obj)) { + inst->baseTime = baseTime; + } } catch (...) { __android_log_print(ANDROID_LOG_VERBOSE, "Maply", "Crash in ParticleSystem::setBasetime()"); } } -JNIEXPORT jdouble JNICALL Java_com_mousebird_maply_ParticleSystem_getBasetime -(JNIEnv *env, jobject obj) +extern "C" +JNIEXPORT jdouble JNICALL Java_com_mousebird_maply_ParticleSystem_getBasetime(JNIEnv *env, jobject obj) { try { - ParticleSystemClassInfo *classInfo = ParticleSystemClassInfo::getClassInfo(); - ParticleSystem *inst = classInfo->getObject(env, obj); - if (!inst) - return 0.0; - - return inst->baseTime; + const auto classInfo = ParticleSystemClassInfo::getClassInfo(); + const auto inst = classInfo->getObject(env, obj); + return inst ? inst->baseTime : 0.0; } catch (...) { __android_log_print(ANDROID_LOG_VERBOSE, "Maply", "Crash in ParticleSystem::getBasetime()"); } - return 0.0; } -JNIEXPORT void JNICALL Java_com_mousebird_maply_ParticleSystem_setTotalParticles -(JNIEnv *env, jobject obj, jint totalParticles) +extern "C" +JNIEXPORT void JNICALL Java_com_mousebird_maply_ParticleSystem_setTotalParticles(JNIEnv *env, jobject obj, jint totalParticles) { try { - ParticleSystemClassInfo *classInfo = ParticleSystemClassInfo::getClassInfo(); - ParticleSystem *inst = classInfo->getObject(env, obj); - if (!inst) - return; - - inst->totalParticles = totalParticles; + const auto classInfo = ParticleSystemClassInfo::getClassInfo(); + if (auto inst = classInfo->getObject(env, obj)) { + inst->totalParticles = totalParticles; + } } catch (...) { __android_log_print(ANDROID_LOG_VERBOSE, "Maply", "Crash in ParticleSystem::setTotalParticles()"); } } -JNIEXPORT jint JNICALL Java_com_mousebird_maply_ParticleSystem_getTotalParticles - (JNIEnv *env, jobject obj) +extern "C" +JNIEXPORT jint JNICALL Java_com_mousebird_maply_ParticleSystem_getTotalParticles(JNIEnv *env, jobject obj) { try { - ParticleSystemClassInfo *classInfo = ParticleSystemClassInfo::getClassInfo(); - ParticleSystem *inst = classInfo->getObject(env, obj); - if (!inst) - return 0; - - return inst->totalParticles; + const auto classInfo = ParticleSystemClassInfo::getClassInfo(); + const auto inst = classInfo->getObject(env, obj); + return inst ? inst->totalParticles : 0; } catch (...) { __android_log_print(ANDROID_LOG_VERBOSE, "Maply", "Crash in ParticleSystem::getTotalParticles()"); } - return 0; } -JNIEXPORT void JNICALL Java_com_mousebird_maply_ParticleSystem_setBatchSize -(JNIEnv *env, jobject obj, jint batchSize) +extern "C" +JNIEXPORT void JNICALL Java_com_mousebird_maply_ParticleSystem_setBatchSize(JNIEnv *env, jobject obj, jint batchSize) { try { - ParticleSystemClassInfo *classInfo = ParticleSystemClassInfo::getClassInfo(); - ParticleSystem *inst = classInfo->getObject(env, obj); - if (!inst) - return; - - inst->batchSize = batchSize; + const auto classInfo = ParticleSystemClassInfo::getClassInfo(); + if (auto inst = classInfo->getObject(env, obj)) { + inst->batchSize = batchSize; + } } catch (...) { __android_log_print(ANDROID_LOG_VERBOSE, "Maply", "Crash in ParticleSystem::setBatchSize()"); } } -JNIEXPORT jint JNICALL Java_com_mousebird_maply_ParticleSystem_getBatchSize -(JNIEnv *env, jobject obj) +extern "C" +JNIEXPORT jint JNICALL Java_com_mousebird_maply_ParticleSystem_getBatchSize(JNIEnv *env, jobject obj) { try { - ParticleSystemClassInfo *classInfo = ParticleSystemClassInfo::getClassInfo(); - ParticleSystem *inst = classInfo->getObject(env, obj); - if (!inst) - return 0; - - return inst->batchSize; + const auto classInfo = ParticleSystemClassInfo::getClassInfo(); + const auto inst = classInfo->getObject(env, obj); + return inst ? inst->batchSize : 0; } catch (...) { __android_log_print(ANDROID_LOG_VERBOSE, "Maply", "Crash in ParticleSystem::getBatchSize()"); } - return 0; } -JNIEXPORT void JNICALL Java_com_mousebird_maply_ParticleSystem_setContinuousUpdate -(JNIEnv *env, jobject obj, jboolean newVal) +extern "C" +JNIEXPORT void JNICALL Java_com_mousebird_maply_ParticleSystem_setContinuousUpdate(JNIEnv *env, jobject obj, jboolean newVal) { try { - ParticleSystemClassInfo *classInfo = ParticleSystemClassInfo::getClassInfo(); - ParticleSystem *inst = classInfo->getObject(env, obj); - if (!inst) - return; - - inst->continuousUpdate = newVal; + const auto classInfo = ParticleSystemClassInfo::getClassInfo(); + if (auto inst = classInfo->getObject(env, obj)) { + inst->continuousUpdate = newVal; + } } catch (...) { __android_log_print(ANDROID_LOG_VERBOSE, "Maply", "Crash in ParticleSystem::setContinuousRender()"); @@ -291,136 +248,123 @@ JNIEXPORT void JNICALL Java_com_mousebird_maply_ParticleSystem_setContinuousUpda -JNIEXPORT void JNICALL Java_com_mousebird_maply_ParticleSystem_setDrawPriority -(JNIEnv * env, jobject obj, jint drawPriority) +extern "C" +JNIEXPORT void JNICALL Java_com_mousebird_maply_ParticleSystem_setDrawPriority(JNIEnv * env, jobject obj, jint drawPriority) { try { - ParticleSystemClassInfo *classInfo = ParticleSystemClassInfo::getClassInfo(); - ParticleSystem *inst = classInfo->getObject(env, obj); - if (!inst) - return; - - inst->drawPriority = drawPriority; + const auto classInfo = ParticleSystemClassInfo::getClassInfo(); + if (auto inst = classInfo->getObject(env, obj)) { + inst->drawPriority = drawPriority; + } } catch (...) { __android_log_print(ANDROID_LOG_VERBOSE, "Maply", "Crash in ParticleSystem::setDrawPriority()"); } } -JNIEXPORT void JNICALL Java_com_mousebird_maply_ParticleSystem_setPointSize -(JNIEnv *env, jobject obj, jfloat pointSize) +extern "C" +JNIEXPORT void JNICALL Java_com_mousebird_maply_ParticleSystem_setPointSize(JNIEnv *env, jobject obj, jfloat pointSize) { try { - ParticleSystemClassInfo *classInfo = ParticleSystemClassInfo::getClassInfo(); - ParticleSystem *inst = classInfo->getObject(env, obj); - if (!inst) - return; - - inst->pointSize = pointSize; + const auto classInfo = ParticleSystemClassInfo::getClassInfo(); + if (auto inst = classInfo->getObject(env, obj)) { + inst->pointSize = pointSize; + } } catch (...) { __android_log_print(ANDROID_LOG_VERBOSE, "Maply", "Crash in ParticleSystem::setPointSize()"); } } -JNIEXPORT void JNICALL Java_com_mousebird_maply_ParticleSystem_addAttributeNative -(JNIEnv *env, jobject obj, jstring name, jint type) +extern "C" +JNIEXPORT void JNICALL Java_com_mousebird_maply_ParticleSystem_addAttributeNative(JNIEnv *env, jobject obj, jstring name, jint type) { try { - ParticleSystemClassInfo *classInfo = ParticleSystemClassInfo::getClassInfo(); - ParticleSystem *inst = classInfo->getObject(env, obj); - if (!inst) - return; - SingleVertexAttributeInfo attr; - JavaString jstr(env, name); - attr.nameID = StringIndexer::getStringID(jstr.cStr); - attr.type = (BDAttributeDataType) type; - inst->vertAttrs.push_back(attr); + const auto classInfo = ParticleSystemClassInfo::getClassInfo(); + if (auto inst = classInfo->getObject(env, obj)) { + SingleVertexAttributeInfo attr; + const JavaString jstr(env, name); + attr.nameID = StringIndexer::getStringID(jstr.getCString()); + attr.type = (BDAttributeDataType) type; + inst->vertAttrs.push_back(attr); + } } catch (...) { __android_log_print(ANDROID_LOG_VERBOSE, "Maply", "Crash in ParticleSystem::addParticleSystemAttribute()"); } } +extern "C" JNIEXPORT void JNICALL Java_com_mousebird_maply_ParticleSystem_addVaryingNative (JNIEnv *env, jobject obj, jstring inName, jstring inVaryAttrName, jint type) { try { - ParticleSystemClassInfo *classInfo = ParticleSystemClassInfo::getClassInfo(); - ParticleSystem *inst = classInfo->getObject(env, obj); - if (!inst) - return; - SingleVertexAttributeInfo attr; - JavaString name(env, inName); - JavaString varyName(env, inVaryAttrName); - attr.nameID = StringIndexer::getStringID(name.cStr); - attr.type = (BDAttributeDataType) type; - inst->varyingAttrs.push_back(attr); - inst->varyNames.push_back(StringIndexer::getStringID(varyName.cStr)); + const auto classInfo = ParticleSystemClassInfo::getClassInfo(); + if (auto inst = classInfo->getObject(env, obj)) { + SingleVertexAttributeInfo attr; + const JavaString name(env, inName); + const JavaString varyName(env, inVaryAttrName); + attr.nameID = StringIndexer::getStringID(name.getCString()); + attr.type = (BDAttributeDataType) type; + inst->varyingAttrs.push_back(attr); + inst->varyNames.push_back(StringIndexer::getStringID(varyName.getCString())); + } } catch (...) { __android_log_print(ANDROID_LOG_VERBOSE, "Maply", "Crash in ParticleSystem::addVaryingNative()"); } } -JNIEXPORT void JNICALL Java_com_mousebird_maply_ParticleSystem_addTextureID -(JNIEnv *env, jobject obj, jlong texID) +extern "C" +JNIEXPORT void JNICALL Java_com_mousebird_maply_ParticleSystem_addTextureID(JNIEnv *env, jobject obj, jlong texID) { try { - ParticleSystemClassInfo *classInfo = ParticleSystemClassInfo::getClassInfo(); - ParticleSystem *inst = classInfo->getObject(env, obj); - if (!inst) - return; - - inst->texIDs.push_back(texID); + const auto classInfo = ParticleSystemClassInfo::getClassInfo(); + if (auto inst = classInfo->getObject(env, obj)) { + inst->texIDs.push_back(texID); + } } catch (...) { __android_log_print(ANDROID_LOG_VERBOSE, "Maply", "Crash in ParticleSystem::addTexID()"); } } -JNIEXPORT void JNICALL Java_com_mousebird_maply_ParticleSystem_setRenderTargetNative - (JNIEnv *env, jobject obj, jlong targetID) +extern "C" +JNIEXPORT void JNICALL Java_com_mousebird_maply_ParticleSystem_setRenderTargetNative(JNIEnv *env, jobject obj, jlong targetID) { try { - ParticleSystemClassInfo *classInfo = ParticleSystemClassInfo::getClassInfo(); - ParticleSystem *inst = classInfo->getObject(env, obj); - if (!inst) - return; - - inst->renderTargetID = targetID; + const auto classInfo = ParticleSystemClassInfo::getClassInfo(); + if (auto inst = classInfo->getObject(env, obj)) { + inst->renderTargetID = targetID; + } } catch (...) { __android_log_print(ANDROID_LOG_VERBOSE, "Maply", "Crash in ParticleSystem::setRenderTargetNative()"); } } -JNIEXPORT void JNICALL Java_com_mousebird_maply_ParticleSystem_setZBufferRead - (JNIEnv *env, jobject obj, jboolean val) +extern "C" +JNIEXPORT void JNICALL Java_com_mousebird_maply_ParticleSystem_setZBufferRead(JNIEnv *env, jobject obj, jboolean val) { try { - ParticleSystemClassInfo *classInfo = ParticleSystemClassInfo::getClassInfo(); - ParticleSystem *inst = classInfo->getObject(env, obj); - if (!inst) - return; - - inst->zBufferRead = val; + const auto classInfo = ParticleSystemClassInfo::getClassInfo(); + if (auto inst = classInfo->getObject(env, obj)) { + inst->zBufferRead = val; + } } catch (...) { __android_log_print(ANDROID_LOG_VERBOSE, "Maply", "Crash in ParticleSystem::setZBufferRead()"); } } -JNIEXPORT void JNICALL Java_com_mousebird_maply_ParticleSystem_setZBufferWrite - (JNIEnv *env, jobject obj, jboolean val) +extern "C" +JNIEXPORT void JNICALL Java_com_mousebird_maply_ParticleSystem_setZBufferWrite(JNIEnv *env, jobject obj, jboolean val) { try { - ParticleSystemClassInfo *classInfo = ParticleSystemClassInfo::getClassInfo(); - ParticleSystem *inst = classInfo->getObject(env, obj); - if (!inst) - return; - - inst->zBufferWrite = val; + const auto classInfo = ParticleSystemClassInfo::getClassInfo(); + if (auto inst = classInfo->getObject(env, obj)) { + inst->zBufferWrite = val; + } } catch (...) { __android_log_print(ANDROID_LOG_VERBOSE, "Maply", "Crash in ParticleSystem::setZBufferWrite()"); diff --git a/android/library/maply/jni/src/quadLoading/ImageLoaderReturn_jni.cpp b/android/library/maply/jni/src/quadLoading/ImageLoaderReturn_jni.cpp index 65b7ab0845..0b1f672363 100644 --- a/android/library/maply/jni/src/quadLoading/ImageLoaderReturn_jni.cpp +++ b/android/library/maply/jni/src/quadLoading/ImageLoaderReturn_jni.cpp @@ -1,9 +1,8 @@ -/* - * ImageLoaderReturn_jni.cpp +/* ImageLoaderReturn_jni.cpp * WhirlyGlobeLib * * Created by sjg on 3/20/19. - * Copyright 2011-2019 mousebird consulting + * Copyright 2011-2021 mousebird consulting * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -15,7 +14,6 @@ * 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. - * */ #import "QuadLoading_jni.h" @@ -25,6 +23,7 @@ using namespace Eigen; using namespace WhirlyKit; +extern "C" JNIEXPORT void JNICALL Java_com_mousebird_maply_ImageLoaderReturn_addImageTile (JNIEnv *env, jobject obj, jobject imageObj) { @@ -43,13 +42,12 @@ JNIEXPORT void JNICALL Java_com_mousebird_maply_ImageLoaderReturn_addImageTile } } -JNIEXPORT jobjectArray JNICALL Java_com_mousebird_maply_ImageLoaderReturn_getImages - (JNIEnv *env, jobject obj) +extern "C" +JNIEXPORT jobjectArray JNICALL Java_com_mousebird_maply_ImageLoaderReturn_getImages(JNIEnv *env, jobject obj) { try { QuadLoaderReturnRef *loadReturn = LoaderReturnClassInfo::getClassInfo()->getObject(env,obj); - ImageTileClassInfo *tileClassInfo = ImageTileClassInfo::getClassInfo(); if (!loadReturn) return NULL; @@ -70,8 +68,8 @@ JNIEXPORT jobjectArray JNICALL Java_com_mousebird_maply_ImageLoaderReturn_getIma return NULL; } -JNIEXPORT void JNICALL Java_com_mousebird_maply_ImageLoaderReturn_clearImages - (JNIEnv *env, jobject obj) +extern "C" +JNIEXPORT void JNICALL Java_com_mousebird_maply_ImageLoaderReturn_clearImages(JNIEnv *env, jobject obj) { try { diff --git a/android/library/maply/jni/src/quadLoading/LoaderReturn_jni.cpp b/android/library/maply/jni/src/quadLoading/LoaderReturn_jni.cpp index 80aa2d0949..4463abdf11 100644 --- a/android/library/maply/jni/src/quadLoading/LoaderReturn_jni.cpp +++ b/android/library/maply/jni/src/quadLoading/LoaderReturn_jni.cpp @@ -1,9 +1,8 @@ -/* - * LoaderReturn_jni.cpp +/* LoaderReturn_jni.cpp * WhirlyGlobeLib * * Created by sjg on 3/20/19. - * Copyright 2011-2019 mousebird consulting + * Copyright 2011-2021 mousebird consulting * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -15,7 +14,6 @@ * 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. - * */ #import "QuadLoading_jni.h" @@ -27,248 +25,222 @@ using namespace Eigen; using namespace WhirlyKit; -template<> LoaderReturnClassInfo *LoaderReturnClassInfo::classInfoObj = NULL; +template<> LoaderReturnClassInfo *LoaderReturnClassInfo::classInfoObj = nullptr; -JNIEXPORT void JNICALL Java_com_mousebird_maply_LoaderReturn_nativeInit - (JNIEnv *env, jclass cls) +extern "C" +JNIEXPORT void JNICALL Java_com_mousebird_maply_LoaderReturn_nativeInit(JNIEnv *env, jclass cls) { LoaderReturnClassInfo::getClassInfo(env,cls); } -JNIEXPORT void JNICALL Java_com_mousebird_maply_LoaderReturn_initialise - (JNIEnv *env, jobject obj) +extern "C" +JNIEXPORT void JNICALL Java_com_mousebird_maply_LoaderReturn_initialise(JNIEnv *env, jobject obj) { try { - QuadLoaderReturnRef *load = new QuadLoaderReturnRef(new QuadLoaderReturn(0)); - (*load)->frame = QuadFrameInfoRef(new QuadFrameInfo()); + auto load = new QuadLoaderReturnRef(new QuadLoaderReturn(0)); + (*load)->frame = std::make_shared(); (*load)->frame->frameIndex = 0; LoaderReturnClassInfo::getClassInfo()->setHandle(env,obj,load); } catch (...) { - __android_log_print(ANDROID_LOG_VERBOSE, "Maply", "Crash in LoaderReturn::initialise()"); + __android_log_print(ANDROID_LOG_ERROR, "Maply", "Crash in LoaderReturn::initialise()"); } } static std::mutex disposeMutex; -JNIEXPORT void JNICALL Java_com_mousebird_maply_LoaderReturn_dispose - (JNIEnv *env, jobject obj) +extern "C" +JNIEXPORT void JNICALL Java_com_mousebird_maply_LoaderReturn_dispose(JNIEnv *env, jobject obj) { try { LoaderReturnClassInfo *classInfo = LoaderReturnClassInfo::getClassInfo(); - { - std::lock_guard lock(disposeMutex); - QuadLoaderReturnRef *loader = classInfo->getObject(env,obj); - if (!loader) - return; - delete loader; - classInfo->clearHandle(env,obj); - } + std::lock_guard lock(disposeMutex); + auto loader = classInfo->getObject(env,obj); + delete loader; + classInfo->clearHandle(env, obj); } catch (...) { - __android_log_print(ANDROID_LOG_VERBOSE, "Maply", "Crash in LoaderReturn::dispose()"); + __android_log_print(ANDROID_LOG_ERROR, "Maply", "Crash in LoaderReturn::dispose()"); } } +extern "C" JNIEXPORT void JNICALL Java_com_mousebird_maply_LoaderReturn_setTileID (JNIEnv *env, jobject obj, jint tileX, jint tileY, jint tileLevel) { try { - QuadLoaderReturnRef *loadReturn = LoaderReturnClassInfo::getClassInfo()->getObject(env,obj); - if (!loadReturn) - return; - (*loadReturn)->ident.x = tileX; - (*loadReturn)->ident.y = tileY; - (*loadReturn)->ident.level = tileLevel; + if (auto loadReturn = LoaderReturnClassInfo::get(env,obj)) { + (*loadReturn)->ident.x = tileX; + (*loadReturn)->ident.y = tileY; + (*loadReturn)->ident.level = tileLevel; + } } catch (...) { - __android_log_print(ANDROID_LOG_VERBOSE, "Maply", "Crash in LoaderReturn::setTileID()"); + __android_log_print(ANDROID_LOG_ERROR, "Maply", "Crash in LoaderReturn::setTileID()"); } } -JNIEXPORT void JNICALL Java_com_mousebird_maply_LoaderReturn_setFrame - (JNIEnv *env, jobject obj, jlong frameID, jint frameIndex) +extern "C" +JNIEXPORT void JNICALL Java_com_mousebird_maply_LoaderReturn_setFrame(JNIEnv *env, jobject obj, jlong frameID, jint frameIndex) { try { - QuadLoaderReturnRef *loadReturn = LoaderReturnClassInfo::getClassInfo()->getObject(env,obj); - if (!loadReturn) - return; - (*loadReturn)->frame = QuadFrameInfoRef(new QuadFrameInfo()); - (*loadReturn)->frame->setId(frameID); - (*loadReturn)->frame->frameIndex = frameIndex; + if (auto loadReturn = LoaderReturnClassInfo::get(env,obj)) { + (*loadReturn)->frame = std::make_shared(); + (*loadReturn)->frame->setId(frameID); + (*loadReturn)->frame->frameIndex = frameIndex; + } } catch (...) { - __android_log_print(ANDROID_LOG_VERBOSE, "Maply", "Crash in LoaderReturn::setTileID()"); + __android_log_print(ANDROID_LOG_ERROR, "Maply", "Crash in LoaderReturn::setTileID()"); } } -JNIEXPORT void JNICALL Java_com_mousebird_maply_LoaderReturn_setFrame - (JNIEnv *env, jobject obj, jint frame) -{ - try - { - QuadLoaderReturnRef *loadReturn = LoaderReturnClassInfo::getClassInfo()->getObject(env,obj); - if (!loadReturn) - return; - if ((*loadReturn)->frame) - (*loadReturn)->frame->frameIndex = frame; - } - catch (...) - { - __android_log_print(ANDROID_LOG_VERBOSE, "Maply", "Crash in LoaderReturn::setFrame()"); - } -} - -JNIEXPORT jintArray JNICALL Java_com_mousebird_maply_LoaderReturn_getTileIDNative - (JNIEnv *env, jobject obj) +extern "C" +JNIEXPORT jintArray JNICALL Java_com_mousebird_maply_LoaderReturn_getTileIDNative(JNIEnv *env, jobject obj) { try { - QuadLoaderReturnRef *loadReturn = LoaderReturnClassInfo::getClassInfo()->getObject(env,obj); - if (!loadReturn) - return NULL; - std::vector rets(3); - rets[0] = (*loadReturn)->ident.x; - rets[1] = (*loadReturn)->ident.y; - rets[2] = (*loadReturn)->ident.level; - - return BuildIntArray(env,rets); + if (auto loadReturn = LoaderReturnClassInfo::get(env,obj)) { + const std::vector rets { + (*loadReturn)->ident.x, + (*loadReturn)->ident.y, + (*loadReturn)->ident.level, + }; + return BuildIntArray(env, rets); + } } catch (...) { - __android_log_print(ANDROID_LOG_VERBOSE, "Maply", "Crash in LoaderReturn::getTileIDNative()"); + __android_log_print(ANDROID_LOG_ERROR, "Maply", "Crash in LoaderReturn::getTileIDNative()"); } - - return NULL; + return nullptr; } -JNIEXPORT jint JNICALL Java_com_mousebird_maply_LoaderReturn_getFrame - (JNIEnv *env, jobject obj) +extern "C" +JNIEXPORT jint JNICALL Java_com_mousebird_maply_LoaderReturn_getFrame(JNIEnv *env, jobject obj) { try { - QuadLoaderReturnRef *loadReturn = LoaderReturnClassInfo::getClassInfo()->getObject(env,obj); - if (!loadReturn) - return -1; - return (*loadReturn)->frame->frameIndex; + if (auto loadReturn = LoaderReturnClassInfo::get(env,obj)) { + return (*loadReturn)->frame->frameIndex; + } } catch (...) { - __android_log_print(ANDROID_LOG_VERBOSE, "Maply", "Crash in LoaderReturn::getFrame()"); + __android_log_print(ANDROID_LOG_ERROR, "Maply", "Crash in LoaderReturn::getFrame()"); } - return -1; } -JNIEXPORT void JNICALL Java_com_mousebird_maply_LoaderReturn_mergeChanges - (JNIEnv *env, jobject obj, jobject changeObj) +extern "C" +JNIEXPORT void JNICALL Java_com_mousebird_maply_LoaderReturn_mergeChanges(JNIEnv *env, jobject obj, jobject changeObj) { try { - QuadLoaderReturnRef *loadReturn = LoaderReturnClassInfo::getClassInfo()->getObject(env,obj); - ChangeSetRef *changeSet = ChangeSetClassInfo::getClassInfo()->getObject(env,changeObj); - if (!loadReturn || !changeSet) - return; - - (*loadReturn)->changes.insert((*loadReturn)->changes.end(),(*changeSet)->begin(),(*changeSet)->end()); - (*changeSet)->clear(); + if (auto loadReturn = LoaderReturnClassInfo::get(env,obj)) { + if (auto changeSet = ChangeSetClassInfo::get(env, changeObj)) { + (*loadReturn)->changes.insert((*loadReturn)->changes.end(), + (*changeSet)->begin(), + (*changeSet)->end()); + (*changeSet)->clear(); + } + } } catch (...) { - __android_log_print(ANDROID_LOG_VERBOSE, "Maply", "Crash in LoaderReturn::mergeChanges()"); + __android_log_print(ANDROID_LOG_ERROR, "Maply", "Crash in LoaderReturn::mergeChanges()"); } } -JNIEXPORT void JNICALL Java_com_mousebird_maply_LoaderReturn_setGeneration - (JNIEnv *env, jobject obj, jint generation) +extern "C" +JNIEXPORT void JNICALL Java_com_mousebird_maply_LoaderReturn_setGeneration(JNIEnv *env, jobject obj, jint generation) { try { - QuadLoaderReturnRef *loadReturn = LoaderReturnClassInfo::getClassInfo()->getObject(env,obj); - if (!loadReturn) - return; - (*loadReturn)->generation = generation; + if (auto loadReturn = LoaderReturnClassInfo::get(env,obj)) { + (*loadReturn)->generation = generation; + } } catch (...) { - __android_log_print(ANDROID_LOG_VERBOSE, "Maply", "Crash in LoaderReturn::setGeneration()"); + __android_log_print(ANDROID_LOG_ERROR, "Maply", "Crash in LoaderReturn::setGeneration()"); } } -JNIEXPORT jint JNICALL Java_com_mousebird_maply_LoaderReturn_getGeneration - (JNIEnv *env, jobject obj) +extern "C" +JNIEXPORT jint JNICALL Java_com_mousebird_maply_LoaderReturn_getGeneration(JNIEnv *env, jobject obj) { try { - QuadLoaderReturnRef *loadReturn = LoaderReturnClassInfo::getClassInfo()->getObject(env,obj); - if (!loadReturn) - return 0; - return (*loadReturn)->generation; + if (auto loadReturn = LoaderReturnClassInfo::get(env,obj)) { + return (*loadReturn)->generation; + } } catch (...) { - __android_log_print(ANDROID_LOG_VERBOSE, "Maply", "Crash in LoaderReturn::getGeneration()"); + __android_log_print(ANDROID_LOG_ERROR, "Maply", "Crash in LoaderReturn::getGeneration()"); } return 0; } +extern "C" JNIEXPORT void JNICALL Java_com_mousebird_maply_LoaderReturn_addComponentObjects (JNIEnv *env, jobject obj, jobjectArray compObjs, jboolean isOverlay) { try { - QuadLoaderReturnRef *loadReturn = LoaderReturnClassInfo::getClassInfo()->getObject(env,obj); - if (!loadReturn || !compObjs) + if (!compObjs) return; - - // Work through the component object array - ComponentObjectRefClassInfo *compObjClassInfo = ComponentObjectRefClassInfo::getClassInfo(); - JavaObjectArrayHelper compObjHelp(env,compObjs); - if (compObjHelp.numObjects() == 0) - return; - while (jobject compObjObj = compObjHelp.getNextObject()) { - ComponentObjectRef *compObj = compObjClassInfo->getObject(env,compObjObj); - if (isOverlay) - (*loadReturn)->ovlCompObjs.push_back(*compObj); - else - (*loadReturn)->compObjs.push_back(*compObj); + if (auto loadReturn = LoaderReturnClassInfo::get(env,obj)) { + // Work through the component object array + const auto compObjClassInfo = ComponentObjectRefClassInfo::getClassInfo(); + JavaObjectArrayHelper compObjHelp(env, compObjs); + if (compObjHelp.numObjects() == 0) + return; + while (jobject compObjObj = compObjHelp.getNextObject()) { + ComponentObjectRef *compObj = compObjClassInfo->getObject(env, compObjObj); + if (isOverlay) + (*loadReturn)->ovlCompObjs.push_back(*compObj); + else + (*loadReturn)->compObjs.push_back(*compObj); + } } } catch (...) { - __android_log_print(ANDROID_LOG_VERBOSE, "Maply", "Crash in LoaderReturn::addComponentObjects()"); + __android_log_print(ANDROID_LOG_ERROR, "Maply", "Crash in LoaderReturn::addComponentObjects()"); } } +extern "C" JNIEXPORT void JNICALL Java_com_mousebird_maply_LoaderReturn_clearComponentObjectsNative (JNIEnv *env, jobject obj, jboolean isOverlay) { try { - QuadLoaderReturnRef *loadReturn = LoaderReturnClassInfo::getClassInfo()->getObject(env,obj); - if (!loadReturn) - return; - - if (isOverlay) - (*loadReturn)->ovlCompObjs.clear(); - else - (*loadReturn)->compObjs.clear(); + if (auto loadReturn = LoaderReturnClassInfo::get(env,obj)) { + if (isOverlay) + (*loadReturn)->ovlCompObjs.clear(); + else + (*loadReturn)->compObjs.clear(); + } } catch (...) { - __android_log_print(ANDROID_LOG_VERBOSE, "Maply", "Crash in LoaderReturn::clearCompObjs()"); + __android_log_print(ANDROID_LOG_ERROR, "Maply", "Crash in LoaderReturn::clearCompObjs()"); } } +extern "C" JNIEXPORT void JNICALL Java_com_mousebird_maply_LoaderReturn_deleteComponentObjects (JNIEnv *env, jobject obj, jobject renderControlObj, jobject compManagerObj, jobject changeSetObj) { @@ -276,7 +248,7 @@ JNIEXPORT void JNICALL Java_com_mousebird_maply_LoaderReturn_deleteComponentObje { QuadLoaderReturnRef *loadReturn = LoaderReturnClassInfo::getClassInfo()->getObject(env,obj); SceneRendererGLES_Android *renderer = SceneRendererInfo::getClassInfo()->getObject(env,renderControlObj); - ComponentManager *compManager = ComponentManagerClassInfo::getClassInfo()->getObject(env,compManagerObj); + ComponentManager_AndroidRef *compManager = ComponentManagerClassInfo::getClassInfo()->getObject(env,compManagerObj); ChangeSetRef *changeSet = ChangeSetClassInfo::getClassInfo()->getObject(env,changeSetObj); if (!loadReturn || !renderer || !compManager || !changeSet) return; @@ -296,10 +268,10 @@ JNIEXPORT void JNICALL Java_com_mousebird_maply_LoaderReturn_deleteComponentObje idSet.insert(compObj->getId()); PlatformInfo_Android platformInfo(env); - compManager->removeComponentObjects(&platformInfo, idSet, *(*changeSet)); + (*compManager)->removeComponentObjects(&platformInfo, idSet, *(*changeSet)); } catch (...) { - __android_log_print(ANDROID_LOG_VERBOSE, "Maply", "Crash in LoaderReturn::deleteComponentObjects()"); + __android_log_print(ANDROID_LOG_ERROR, "Maply", "Crash in LoaderReturn::deleteComponentObjects()"); } } diff --git a/android/library/maply/jni/src/quadLoading/QIFBatchOps_jni.cpp b/android/library/maply/jni/src/quadLoading/QIFBatchOps_jni.cpp index 8f22de6db7..a540fddab4 100644 --- a/android/library/maply/jni/src/quadLoading/QIFBatchOps_jni.cpp +++ b/android/library/maply/jni/src/quadLoading/QIFBatchOps_jni.cpp @@ -55,7 +55,7 @@ JNIEXPORT void JNICALL Java_com_mousebird_maply_QIFBatchOps_dispose QIFBatchOpsClassInfo *info = QIFBatchOpsClassInfo::getClassInfo(); { std::lock_guard lock(disposeMutex); - QIFBatchOps_Android *batchOps = info->getObject(env,obj); + //QIFBatchOps_Android *batchOps = info->getObject(env,obj); // We don't actually delete the batch ops here. They're deleted the in the same method they're created // if (!batchOps) // return; diff --git a/android/library/maply/jni/src/quadLoading/QIFFrameAsset_jni.cpp b/android/library/maply/jni/src/quadLoading/QIFFrameAsset_jni.cpp index 639a0a1172..630aefbeb7 100644 --- a/android/library/maply/jni/src/quadLoading/QIFFrameAsset_jni.cpp +++ b/android/library/maply/jni/src/quadLoading/QIFFrameAsset_jni.cpp @@ -1,9 +1,8 @@ -/* - * QIFFrameAsset_jni.cpp +/* QIFFrameAsset_jni.cpp * WhirlyGlobeLib * * Created by sjg on 3/29/19. - * Copyright 2011-2019 mousebird consulting + * Copyright 2011-2021 mousebird consulting * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -15,7 +14,6 @@ * 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. - * */ #import "QuadLoading_jni.h" @@ -23,33 +21,32 @@ using namespace WhirlyKit; -template<> QIFFrameAssetClassInfo *QIFFrameAssetClassInfo::classInfoObj = NULL; - +template<> QIFFrameAssetClassInfo *QIFFrameAssetClassInfo::classInfoObj = nullptr; -JNIEXPORT void JNICALL Java_com_mousebird_maply_QIFFrameAsset_nativeInit - (JNIEnv *env, jclass cls) +extern "C" +JNIEXPORT void JNICALL Java_com_mousebird_maply_QIFFrameAsset_nativeInit(JNIEnv *env, jclass cls) { QIFFrameAssetClassInfo::getClassInfo(env, cls); } -JNIEXPORT void JNICALL Java_com_mousebird_maply_QIFFrameAsset_initialise - (JNIEnv *env, jobject obj) +extern "C" +JNIEXPORT void JNICALL Java_com_mousebird_maply_QIFFrameAsset_initialise(JNIEnv *env, jobject obj) { - try { - QIFFrameAssetClassInfo *info = QIFFrameAssetClassInfo::getClassInfo(); - PlatformInfo_Android platformInfo(env); - QIFFrameAsset_Android *frame = new QIFFrameAsset_Android(&platformInfo,NULL); +// try { +// QIFFrameAssetClassInfo *info = QIFFrameAssetClassInfo::getClassInfo(); +// PlatformInfo_Android platformInfo(env); +// QIFFrameAsset_Android *frame = new QIFFrameAsset_Android(&platformInfo,nullptr); // frame->frameAssetObj = env->NewGlobalRef(obj); // info->setHandle(env, obj, frame); - } catch (...) { - __android_log_print(ANDROID_LOG_VERBOSE, "Maply", "Crash in QIFFrameAsset::initialise()"); - } +// } catch (...) { +// __android_log_print(ANDROID_LOG_VERBOSE, "Maply", "Crash in QIFFrameAsset::initialise()"); +// } } static std::mutex disposeMutex; -JNIEXPORT void JNICALL Java_com_mousebird_maply_QIFFrameAsset_dispose - (JNIEnv *env, jobject obj) +extern "C" +JNIEXPORT void JNICALL Java_com_mousebird_maply_QIFFrameAsset_dispose(JNIEnv *env, jobject obj) { try { QIFFrameAssetClassInfo *info = QIFFrameAssetClassInfo::getClassInfo(); @@ -60,7 +57,7 @@ JNIEXPORT void JNICALL Java_com_mousebird_maply_QIFFrameAsset_dispose return; if (frame->frameAssetObj) { env->DeleteGlobalRef(frame->frameAssetObj); - frame->frameAssetObj = NULL; + frame->frameAssetObj = nullptr; } // These frames are actually reference counted by the TileAsset so we don't delete them here // delete frame; @@ -79,21 +76,18 @@ jobject MakeQIFFrameAsset(JNIEnv *env,QIFFrameAsset_Android *frame) jobject obj = classInfo->makeWrapperObject(env,frame); frame->frameAssetObj = env->NewGlobalRef(obj); env->DeleteLocalRef(obj); - - return obj; + return frame->frameAssetObj; } -JNIEXPORT jint JNICALL Java_com_mousebird_maply_QIFFrameAsset_getPriority - (JNIEnv *env, jobject obj) +extern "C" +JNIEXPORT jint JNICALL Java_com_mousebird_maply_QIFFrameAsset_getPriority(JNIEnv *env, jobject obj) { try { - QIFFrameAsset_Android *frame = QIFFrameAssetClassInfo::getClassInfo()->getObject(env,obj); - if (!frame) - return 0; - return frame->getPriority(); + if (const auto frame = QIFFrameAssetClassInfo::getClassInfo()->getObject(env,obj)) { + return frame->getPriority(); + } } catch (...) { __android_log_print(ANDROID_LOG_VERBOSE, "Maply", "Crash in QIFFrameAsset::getPriority()"); } - return 0; } diff --git a/android/library/maply/jni/src/quadLoading/QuadImageFrameLoader_jni.cpp b/android/library/maply/jni/src/quadLoading/QuadImageFrameLoader_jni.cpp index 678d468ef5..e660c64b51 100644 --- a/android/library/maply/jni/src/quadLoading/QuadImageFrameLoader_jni.cpp +++ b/android/library/maply/jni/src/quadLoading/QuadImageFrameLoader_jni.cpp @@ -26,21 +26,28 @@ using namespace WhirlyKit; -JNIEXPORT void JNICALL Java_com_mousebird_maply_QuadImageFrameLoader_setLoadFrameModeNative +JNIEXPORT jboolean JNICALL Java_com_mousebird_maply_QuadImageFrameLoader_setLoadFrameModeNative (JNIEnv *env, jobject obj, jint mode) { try { QuadImageFrameLoader_AndroidRef *loader = QuadImageFrameLoaderClassInfo::getClassInfo()->getObject(env,obj); if (!loader) - return; + return false; + + if ((*loader)->getLoadMode() == (QuadImageFrameLoader::LoadMode)mode) + return false; + PlatformInfo_Android platformInfo(env); (*loader)->setLoadMode((QuadImageFrameLoader::LoadMode)mode); + + return true; } catch (...) { __android_log_print(ANDROID_LOG_VERBOSE, "Maply", "Crash in QuadImageFrameLoader::setLoadFrameModeNative()"); } + return false; } JNIEXPORT void JNICALL Java_com_mousebird_maply_QuadImageFrameLoader_addFocus @@ -77,21 +84,26 @@ JNIEXPORT jint JNICALL Java_com_mousebird_maply_QuadImageFrameLoader_getNumFocus return 0; } - -JNIEXPORT void JNICALL Java_com_mousebird_maply_QuadImageFrameLoader_setCurrentImageNative +JNIEXPORT jboolean JNICALL Java_com_mousebird_maply_QuadImageFrameLoader_setCurrentImageNative (JNIEnv *env, jobject obj, jint focusID, jdouble curImage) { try { QuadImageFrameLoader_AndroidRef *loader = QuadImageFrameLoaderClassInfo::getClassInfo()->getObject(env,obj); if (!loader) - return; + return false; + + PlatformInfo_Android platformInfo(env); + double oldPos = (*loader)->getCurFrame(focusID); + (*loader)->setCurFrame(&platformInfo,focusID,curImage); - (*loader)->setCurFrame(focusID,curImage); + return (int)oldPos != (int)curImage; } catch (...) { __android_log_print(ANDROID_LOG_VERBOSE, "Maply", "Crash in QuadImageFrameLoader::setCurrentImageNative()"); } + + return false; } JNIEXPORT jdouble JNICALL Java_com_mousebird_maply_QuadImageFrameLoader_getCurrentImage @@ -145,6 +157,22 @@ JNIEXPORT void JNICALL Java_com_mousebird_maply_QuadImageFrameLoader_setRenderTa } } +JNIEXPORT void JNICALL Java_com_mousebird_maply_QuadImageFrameLoader_setTextureSize + (JNIEnv *env, jobject obj, jint texSize, jint borderSize) +{ + try { + QuadImageFrameLoader_AndroidRef *loader = QuadImageFrameLoaderClassInfo::getClassInfo()->getObject(env,obj); + if (!loader) + return; + + (*loader)->setTexSize(texSize,borderSize); + } + catch (...) + { + __android_log_print(ANDROID_LOG_VERBOSE, "Maply", "Crash in QuadImageFrameLoader::setTextureSize()"); + } +} + JNIEXPORT void JNICALL Java_com_mousebird_maply_QuadImageFrameLoader_setShaderIDNative (JNIEnv *env, jobject obj, jint focusID, jlong shaderID) { @@ -189,3 +217,20 @@ JNIEXPORT jint JNICALL Java_com_mousebird_maply_QuadImageFrameLoader_getStatsNat return 0; } + +JNIEXPORT void JNICALL Java_com_mousebird_maply_QuadImageFrameLoader_updatePriorities + (JNIEnv *env, jobject obj) +{ + try { + QuadImageFrameLoader_AndroidRef *loader = QuadImageFrameLoaderClassInfo::getClassInfo()->getObject(env,obj); + if (!loader) + return; + + PlatformInfo_Android platformInfo(env); + (*loader)->updatePriorities(&platformInfo); + } + catch (...) + { + __android_log_print(ANDROID_LOG_VERBOSE, "Maply", "Crash in QuadImageFrameLoader::updatePriorities()"); + } +} \ No newline at end of file diff --git a/android/library/maply/jni/src/quadLoading/QuadLoaderBase_jni.cpp b/android/library/maply/jni/src/quadLoading/QuadLoaderBase_jni.cpp index 81e2982d89..a3288e4990 100644 --- a/android/library/maply/jni/src/quadLoading/QuadLoaderBase_jni.cpp +++ b/android/library/maply/jni/src/quadLoading/QuadLoaderBase_jni.cpp @@ -1,9 +1,8 @@ -/* - * QuadLoaderBase_jni.cpp +/* QuadLoaderBase_jni.cpp * WhirlyGlobeLib * * Created by sjg on 3/25/19. - * Copyright 2011-2019 mousebird consulting + * Copyright 2011-2021 mousebird consulting * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -15,7 +14,6 @@ * 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. - * */ #import "QuadLoading_jni.h" @@ -26,22 +24,35 @@ using namespace Eigen; using namespace WhirlyKit; -template<> QuadImageFrameLoaderClassInfo *QuadImageFrameLoaderClassInfo::classInfoObj = NULL; +template<> QuadImageFrameLoaderClassInfo *QuadImageFrameLoaderClassInfo::classInfoObj = nullptr; + +static jclass mbrClass = nullptr; +static jfieldID llID = nullptr; +static jfieldID urID = nullptr; -JNIEXPORT void JNICALL Java_com_mousebird_maply_QuadLoaderBase_nativeInit - (JNIEnv *env, jclass cls) +extern "C" +JNIEXPORT void JNICALL Java_com_mousebird_maply_QuadLoaderBase_nativeInit(JNIEnv *env, jclass cls) { QuadImageFrameLoaderClassInfo::getClassInfo(env, cls); + + if (!mbrClass) + { + mbrClass = env->FindClass("com/mousebird/maply/Mbr"); + llID = env->GetFieldID(mbrClass, "ll", "Lcom/mousebird/maply/Point2d;"); + urID = env->GetFieldID(mbrClass, "ur", "Lcom/mousebird/maply/Point2d;"); + } } +extern "C" JNIEXPORT void JNICALL Java_com_mousebird_maply_QuadLoaderBase_initialise (JNIEnv *env, jobject obj, jobject sampleObj, jint numFrames, jint mode) { try { - QuadImageFrameLoaderClassInfo *info = QuadImageFrameLoaderClassInfo::getClassInfo(); - SamplingParams *params = SamplingParamsClassInfo::getClassInfo()->getObject(env,sampleObj); + auto info = QuadImageFrameLoaderClassInfo::getClassInfo(); + auto params = SamplingParamsClassInfo::getClassInfo()->getObject(env,sampleObj); PlatformInfo_Android platformInfo(env); - QuadImageFrameLoader_AndroidRef *loader = new QuadImageFrameLoader_AndroidRef(new QuadImageFrameLoader_Android(&platformInfo,*params,numFrames,(QuadImageFrameLoader::Mode)mode,env)); + auto loader = new QuadImageFrameLoader_AndroidRef( + new QuadImageFrameLoader_Android(&platformInfo,*params,numFrames,(QuadImageFrameLoader::Mode)mode)); (*loader)->frameLoaderObj = env->NewGlobalRef(obj); (*loader)->setFlipY(true); info->setHandle(env, obj, loader); @@ -52,8 +63,8 @@ JNIEXPORT void JNICALL Java_com_mousebird_maply_QuadLoaderBase_initialise static std::mutex disposeMutex; -JNIEXPORT void JNICALL Java_com_mousebird_maply_QuadLoaderBase_dispose - (JNIEnv *env, jobject obj) +extern "C" +JNIEXPORT void JNICALL Java_com_mousebird_maply_QuadLoaderBase_dispose(JNIEnv *env, jobject obj) { try { QuadImageFrameLoaderClassInfo *info = QuadImageFrameLoaderClassInfo::getClassInfo(); @@ -62,8 +73,10 @@ JNIEXPORT void JNICALL Java_com_mousebird_maply_QuadLoaderBase_dispose QuadImageFrameLoader_AndroidRef *loader = info->getObject(env,obj); if (!loader) return; - if ((*loader)->frameLoaderObj) + if ((*loader)->frameLoaderObj) { env->DeleteGlobalRef((*loader)->frameLoaderObj); + (*loader)->frameLoaderObj = nullptr; + } delete loader; info->clearHandle(env, obj); @@ -74,8 +87,8 @@ JNIEXPORT void JNICALL Java_com_mousebird_maply_QuadLoaderBase_dispose } } -JNIEXPORT void JNICALL Java_com_mousebird_maply_QuadLoaderBase_setFlipY - (JNIEnv *env, jobject obj, jboolean flipY) +extern "C" +JNIEXPORT void JNICALL Java_com_mousebird_maply_QuadLoaderBase_setFlipY(JNIEnv *env, jobject obj, jboolean flipY) { try { QuadImageFrameLoader_AndroidRef *loader = QuadImageFrameLoaderClassInfo::getClassInfo()->getObject(env,obj); @@ -89,8 +102,8 @@ JNIEXPORT void JNICALL Java_com_mousebird_maply_QuadLoaderBase_setFlipY } } -JNIEXPORT jboolean JNICALL Java_com_mousebird_maply_QuadLoaderBase_getFlipY - (JNIEnv *env, jobject obj) +extern "C" +JNIEXPORT jboolean JNICALL Java_com_mousebird_maply_QuadLoaderBase_getFlipY(JNIEnv *env, jobject obj) { try { QuadImageFrameLoader_AndroidRef *loader = QuadImageFrameLoaderClassInfo::getClassInfo()->getObject(env,obj); @@ -106,8 +119,8 @@ JNIEXPORT jboolean JNICALL Java_com_mousebird_maply_QuadLoaderBase_getFlipY return false; } -JNIEXPORT void JNICALL Java_com_mousebird_maply_QuadLoaderBase_setDebugMode - (JNIEnv *env, jobject obj, jboolean debugMode) +extern "C" +JNIEXPORT void JNICALL Java_com_mousebird_maply_QuadLoaderBase_setDebugMode(JNIEnv *env, jobject obj, jboolean debugMode) { try { QuadImageFrameLoader_AndroidRef *loader = QuadImageFrameLoaderClassInfo::getClassInfo()->getObject(env,obj); @@ -121,6 +134,7 @@ JNIEXPORT void JNICALL Java_com_mousebird_maply_QuadLoaderBase_setDebugMode } } +extern "C" JNIEXPORT void JNICALL Java_com_mousebird_maply_QuadLoaderBase_geoBoundsForTileNative (JNIEnv *env, jobject obj, jint tileX, jint tileY, jint tileLevel, jobject llObj, jobject urObj) { @@ -138,6 +152,8 @@ JNIEXPORT void JNICALL Java_com_mousebird_maply_QuadLoaderBase_geoBoundsForTileN MbrD mbrD = control->getQuadTree()->generateMbrForNode(WhirlyKit::QuadTreeNew::Node(tileX,tileY,tileLevel)); CoordSystem *wkCoordSys = control->getCoordSys(); + if (!wkCoordSys) + return; Point2d pts[4]; pts[0] = wkCoordSys->localToGeographicD(Point3d(mbrD.ll().x(),mbrD.ll().y(),0.0)); pts[1] = wkCoordSys->localToGeographicD(Point3d(mbrD.ur().x(),mbrD.ll().y(),0.0)); @@ -160,6 +176,7 @@ JNIEXPORT void JNICALL Java_com_mousebird_maply_QuadLoaderBase_geoBoundsForTileN } } +extern "C" JNIEXPORT void JNICALL Java_com_mousebird_maply_QuadLoaderBase_boundsForTileNative (JNIEnv *env, jobject obj, jint tileX, jint tileY, jint tileLevel, jobject llObj, jobject urObj) { @@ -185,6 +202,7 @@ JNIEXPORT void JNICALL Java_com_mousebird_maply_QuadLoaderBase_boundsForTileNati } } +extern "C" JNIEXPORT void JNICALL Java_com_mousebird_maply_QuadLoaderBase_displayCenterForTileNative (JNIEnv *env, jobject obj, jint tileX, jint tileY, jint tileLevel, jobject ptObj) { @@ -210,6 +228,7 @@ JNIEXPORT void JNICALL Java_com_mousebird_maply_QuadLoaderBase_displayCenterForT } } +extern "C" JNIEXPORT void JNICALL Java_com_mousebird_maply_QuadLoaderBase_cleanupNative (JNIEnv *env, jobject obj, jobject changeObj) { @@ -232,10 +251,17 @@ JNIEXPORT void JNICALL Java_com_mousebird_maply_QuadLoaderBase_cleanupNative } } +extern "C" JNIEXPORT void JNICALL Java_com_mousebird_maply_QuadLoaderBase_mergeLoaderReturn (JNIEnv *env, jobject obj, jobject loadRetObj, jobject changeObj) { try { + if (!loadRetObj) { + // Load failed, add any changes appropriate for a failure. + // For now, that's ... nothing. + return; + } + QuadImageFrameLoader_AndroidRef *loader = QuadImageFrameLoaderClassInfo::getClassInfo()->getObject(env,obj); QuadLoaderReturnRef *loadReturn = LoaderReturnClassInfo::getClassInfo()->getObject(env,loadRetObj); ChangeSetRef *changes = ChangeSetClassInfo::getClassInfo()->getObject(env,changeObj); @@ -254,6 +280,7 @@ JNIEXPORT void JNICALL Java_com_mousebird_maply_QuadLoaderBase_mergeLoaderReturn } } +extern "C" JNIEXPORT void JNICALL Java_com_mousebird_maply_QuadLoaderBase_samplingLayerConnectNative (JNIEnv *env, jobject obj, jobject layerObj, jobject changeObj) { @@ -267,7 +294,6 @@ JNIEXPORT void JNICALL Java_com_mousebird_maply_QuadLoaderBase_samplingLayerConn PlatformInfo_Android platformInfo(env); if (control->addBuilderDelegate(&platformInfo,*loader)) { // This will result in callbacks to the Java side - PlatformInfo_Android platformInfo(env); control->notifyDelegateStartup(&platformInfo,((QuadTileBuilderDelegate *) (*loader).get())->getId(), *(changes->get())); } @@ -278,13 +304,14 @@ JNIEXPORT void JNICALL Java_com_mousebird_maply_QuadLoaderBase_samplingLayerConn } } +extern "C" JNIEXPORT void JNICALL Java_com_mousebird_maply_QuadLoaderBase_samplingLayerDisconnectNative (JNIEnv *env, jobject obj, jobject layerObj, jobject changeObj) { try { QuadImageFrameLoader_AndroidRef *loader = QuadImageFrameLoaderClassInfo::getClassInfo()->getObject(env,obj); QuadSamplingController_Android *control = QuadSamplingControllerInfo::getClassInfo()->getObject(env,layerObj); - ChangeSetRef *changes = ChangeSetClassInfo::getClassInfo()->getObject(env,changeObj); + //ChangeSetRef *changes = ChangeSetClassInfo::getClassInfo()->getObject(env,changeObj); if (!loader || !control) return; @@ -297,6 +324,7 @@ JNIEXPORT void JNICALL Java_com_mousebird_maply_QuadLoaderBase_samplingLayerDisc } } +extern "C" JNIEXPORT jlong JNICALL Java_com_mousebird_maply_QuadLoaderBase_getFrameID (JNIEnv *env, jobject obj, jint frameIndex) { @@ -316,8 +344,8 @@ JNIEXPORT jlong JNICALL Java_com_mousebird_maply_QuadLoaderBase_getFrameID return 0; } -JNIEXPORT jint JNICALL Java_com_mousebird_maply_QuadLoaderBase_getGeneration - (JNIEnv *env, jobject obj) +extern "C" +JNIEXPORT jint JNICALL Java_com_mousebird_maply_QuadLoaderBase_getGeneration(JNIEnv *env, jobject obj) { try { QuadImageFrameLoader_AndroidRef *loader = QuadImageFrameLoaderClassInfo::getClassInfo()->getObject(env,obj); @@ -334,20 +362,82 @@ JNIEXPORT jint JNICALL Java_com_mousebird_maply_QuadLoaderBase_getGeneration return 0; } +/* + * Class: com_mousebird_maply_QuadLoaderBase + * Method: getZoomSlot + * Signature: ()I + */ +extern "C" +JNIEXPORT jint JNICALL Java_com_mousebird_maply_QuadLoaderBase_getZoomSlot(JNIEnv *env, jobject obj) +{ + try + { + const auto ptr = QuadImageFrameLoaderClassInfo::getClassInfo()->getObject(env,obj); + if (const auto inst = ptr ? *ptr : nullptr) + { + if (const auto dc = inst->getController()) + { + return dc->getZoomSlot(); + } + } + } + catch (...) + { + __android_log_print(ANDROID_LOG_ERROR, "Maply", "Crash in QuadLoaderBase::getGeneration()"); + } + + return -1; +} + +extern "C" JNIEXPORT void JNICALL Java_com_mousebird_maply_QuadLoaderBase_reloadNative (JNIEnv *env, jobject obj, jobject changeSetObj) { - try { + Java_com_mousebird_maply_QuadLoaderBase_reloadAreaNative(env, obj, changeSetObj, nullptr); +} + +/* + * Class: com_mousebird_maply_QuadLoaderBase + * Method: reloadAreaNative + * Signature: (Lcom/mousebird/maply/ChangeSet;[Lcom/mousebird/maply/Mbr;)V + */ +extern "C" +JNIEXPORT void JNICALL Java_com_mousebird_maply_QuadLoaderBase_reloadAreaNative + (JNIEnv *env, jobject obj, jobject changeSetObj, jobjectArray mbrArrayObj) +{ + try + { QuadImageFrameLoader_AndroidRef *loader = QuadImageFrameLoaderClassInfo::getClassInfo()->getObject(env,obj); ChangeSetRef *changeSet = ChangeSetClassInfo::getClassInfo()->getObject(env,changeSetObj); if (!loader || !changeSet) return; + std::vector mbrs; + for (JavaObjectArrayHelper mbrObjs(env, mbrArrayObj); mbrObjs.getNextObject(); ) + { + const auto llObj = env->GetObjectField(mbrObjs.getCurrentObject(), llID); + const auto urObj = env->GetObjectField(mbrObjs.getCurrentObject(), urID); + + const auto llx = Java_com_mousebird_maply_Point2d_getX(env, llObj); + const auto lly = Java_com_mousebird_maply_Point2d_getY(env, llObj); + const auto urx = Java_com_mousebird_maply_Point2d_getX(env, urObj); + const auto ury = Java_com_mousebird_maply_Point2d_getY(env, urObj); + + env->DeleteLocalRef(llObj); + env->DeleteLocalRef(urObj); + + if (mbrs.empty()) + { + mbrs.reserve(mbrObjs.numObjects()); + } + mbrs.emplace_back(Point2f(llx, lly), Point2f(urx, ury)); + } + PlatformInfo_Android platformInfo(env); - (*loader)->reload(&platformInfo,-1, *(changeSet->get())); + (*loader)->reload(&platformInfo,-1,mbrs.empty() ? nullptr : &mbrs[0],(int)mbrs.size(), **changeSet); } catch (...) { - __android_log_print(ANDROID_LOG_VERBOSE, "Maply", "Crash in QuadLoaderBase::samplingLayerDisconnectNative()"); + __android_log_print(ANDROID_LOG_ERROR, "Maply", "Crash in QuadLoaderBase::reloadAreaNative()"); } -} \ No newline at end of file +} diff --git a/android/library/maply/jni/src/quadLoading/RawPNGImageLoaderInterpreter_jni.cpp b/android/library/maply/jni/src/quadLoading/RawPNGImageLoaderInterpreter_jni.cpp new file mode 100644 index 0000000000..87bd71c9e2 --- /dev/null +++ b/android/library/maply/jni/src/quadLoading/RawPNGImageLoaderInterpreter_jni.cpp @@ -0,0 +1,138 @@ +/* + * ImageLoaderReturn_jni.cpp + * WhirlyGlobeLib + * + * Created by sjg on 3/20/19. + * Copyright 2011-2019 mousebird consulting + * + * 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. + * + */ + +#import "QuadLoading_jni.h" +#import "Components_jni.h" +#import "com_mousebird_maply_RawPNGImageLoaderInterpreter.h" + +using namespace Eigen; +using namespace WhirlyKit; + +namespace WhirlyKit { + +// Stores value mappings for raw PNGs +class RawPNGImage { +public: + std::vector valueMap; +}; + +} + +typedef JavaClassInfo RawPNGImageClassInfo; +template<> RawPNGImageClassInfo *RawPNGImageClassInfo::classInfoObj = NULL; + +JNIEXPORT void JNICALL Java_com_mousebird_maply_RawPNGImageLoaderInterpreter_nativeInit +(JNIEnv *env, jclass cls) +{ + RawPNGImageClassInfo::getClassInfo(env,cls); +} + +JNIEXPORT void JNICALL Java_com_mousebird_maply_RawPNGImageLoaderInterpreter_initialise +(JNIEnv *env, jobject obj) +{ + try + { + RawPNGImage *rawImage = new RawPNGImage(); + RawPNGImageClassInfo::getClassInfo()->setHandle(env,obj,rawImage); + } + catch (...) + { + __android_log_print(ANDROID_LOG_VERBOSE, "Maply", "Crash in RawPNGImage::initialise()"); + } +} + +static std::mutex disposeMutex; + +JNIEXPORT void JNICALL Java_com_mousebird_maply_RawPNGImageLoaderInterpreter_dispose +(JNIEnv *env, jobject obj) +{ + try + { + RawPNGImageClassInfo *classInfo = RawPNGImageClassInfo::getClassInfo(); + std::lock_guard lock(disposeMutex); + RawPNGImage *inst = classInfo->getObject(env,obj); + if (!inst) + return; + delete inst; + + classInfo->clearHandle(env,obj); + } + catch (...) + { + __android_log_print(ANDROID_LOG_VERBOSE, "Maply", "Crash in RawPNGImage::dispose()"); + } +} + +JNIEXPORT void JNICALL Java_com_mousebird_maply_RawPNGImageLoaderInterpreter_dataForTileNative +(JNIEnv *env, jobject obj, jbyteArray inImage,jobject loadReturnObj) +{ + try + { + RawPNGImage *rawImage = RawPNGImageClassInfo::getClassInfo()->getObject(env,obj); + QuadLoaderReturnRef *loadReturn = LoaderReturnClassInfo::getClassInfo()->getObject(env,loadReturnObj); + if (!rawImage || !loadReturn) + return; + + jbyte *bytes = env->GetByteArrayElements(inImage,NULL); + jsize len = env->GetArrayLength(inImage); + + unsigned int width=0,height=0; + unsigned int err = 0; + int byteWidth = -1; + unsigned char *outData = RawPNGImageLoaderInterpreter(width,height, + (const unsigned char *)bytes,len, + rawImage->valueMap, + byteWidth, err); + + env->ReleaseByteArrayElements(inImage,bytes, 0); + + if (err != 0 && !outData) { + wkLogLevel(Warn, "Failed to read PNG in MaplyRawPNGImageLoaderInterpreter for tile %d: (%d,%d)",(*loadReturn)->ident.level,(*loadReturn)->ident.x,(*loadReturn)->ident.y); + } else { + RawDataWrapperRef wrap = std::make_shared(outData,width*height*byteWidth,true); + ImageTileRef imgTile = std::make_shared("Raw PNG",wrap); + imgTile->width = width; imgTile->height = height; + imgTile->components = byteWidth; + (*loadReturn)->images.push_back(imgTile); + } + } + catch (...) { + __android_log_print(ANDROID_LOG_VERBOSE, "Maply", "Crash in RawPNGImage::dataForTileNative()"); + } +} + +JNIEXPORT void JNICALL Java_com_mousebird_maply_RawPNGImageLoaderInterpreter_addMappingFrom +(JNIEnv *env, jobject obj, jint inVal, jint outVal) +{ + try + { + RawPNGImage *rawImage = RawPNGImageClassInfo::getClassInfo()->getObject(env,obj); + if (!rawImage) + return; + + if (rawImage->valueMap.empty()) + rawImage->valueMap.resize(256,-1); + if (inVal < 256) + rawImage->valueMap[inVal] = outVal; + } + catch (...) { + __android_log_print(ANDROID_LOG_VERBOSE, "Maply", "Crash in RawPNGImage::addMappingFrom()"); + } +} diff --git a/android/library/maply/jni/src/renderer/RenderController_jni.cpp b/android/library/maply/jni/src/renderer/RenderController_jni.cpp index c904907f8e..0f3d19cd91 100644 --- a/android/library/maply/jni/src/renderer/RenderController_jni.cpp +++ b/android/library/maply/jni/src/renderer/RenderController_jni.cpp @@ -378,7 +378,7 @@ JNIEXPORT void JNICALL Java_com_mousebird_maply_RenderController_renderToBitmapN if (!renderer) return; - Snapshot_AndroidRef snapshot(new Snapshot_Android()); + auto snapshot = std::make_shared(); renderer->addSnapshotDelegate(snapshot); renderer->forceDrawNextFrame(); @@ -416,12 +416,12 @@ JNIEXPORT void JNICALL Java_com_mousebird_maply_RenderController_renderToBitmapN { for(int j=0; j>8) & 0xff; - int pb = (pix>>16) & 0xff; - int pa = (pix>>24) & 0xff; - bt[(height-k-1)*width+j] = (pa << 24) | (pb << 16) | (pg << 8) | pr; + const unsigned pix=b[i*width+j]; + const unsigned pr = pix & 0xffU; + const unsigned pg = (pix>>8U) & 0xffU; + const unsigned pb = (pix>>16U) & 0xffU; + const unsigned pa = (pix>>24U) & 0xffU; + bt[(height-k-1)*width+j] = (pa << 24U) | (pb << 16U) | (pg << 8U) | pr; } } // memmove(bitmapPixels,snapshot->data->getRawData(),snapshot->data->getLen()); @@ -455,7 +455,7 @@ JNIEXPORT jboolean JNICALL Java_com_mousebird_maply_RenderController_hasChanges renderer->extraFrameCount = 4; } else { // No changes, make sure we don't have extra frames to draw - changes = renderer->extraFrameCount > 0;; + changes = renderer->extraFrameCount > 0; } } diff --git a/android/library/maply/jni/src/scene/ChangeSet_jni.cpp b/android/library/maply/jni/src/scene/ChangeSet_jni.cpp index ddf542a0f5..6d41124044 100644 --- a/android/library/maply/jni/src/scene/ChangeSet_jni.cpp +++ b/android/library/maply/jni/src/scene/ChangeSet_jni.cpp @@ -168,6 +168,7 @@ JNIEXPORT void JNICALL Java_com_mousebird_maply_ChangeSet_addTexture case 0: texture->setInterpType(TexInterpNearest); break; + default: case 1: texture->setInterpType(TexInterpLinear); break; diff --git a/android/library/maply/jni/src/scene/Scene_jni.cpp b/android/library/maply/jni/src/scene/Scene_jni.cpp index 27dfde9a11..751d6ca692 100644 --- a/android/library/maply/jni/src/scene/Scene_jni.cpp +++ b/android/library/maply/jni/src/scene/Scene_jni.cpp @@ -1,9 +1,8 @@ -/* - * Scene_jni.cpp +/* Scene_jni.cpp * WhirlyGlobeLib * * Created by Steve Gifford on 3/17/15. - * Copyright 2011-2016 mousebird consulting + * Copyright 2011-2021 mousebird consulting * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -15,7 +14,6 @@ * 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. - * */ #import "Scene_jni.h" @@ -25,14 +23,15 @@ using namespace WhirlyKit; -template<> SceneClassInfo *SceneClassInfo::classInfoObj = NULL; +template<> SceneClassInfo *SceneClassInfo::classInfoObj = nullptr; -JNIEXPORT void JNICALL Java_com_mousebird_maply_Scene_nativeInit - (JNIEnv *env, jclass cls) +extern "C" +JNIEXPORT void JNICALL Java_com_mousebird_maply_Scene_nativeInit(JNIEnv *env, jclass cls) { SceneClassInfo::getClassInfo(env,cls); } +extern "C" JNIEXPORT void JNICALL Java_com_mousebird_maply_Scene_initialise (JNIEnv *env, jobject obj, jobject coordAdapterObj, jobject renderControlObj, jobject charRendererObj) { @@ -41,7 +40,8 @@ JNIEXPORT void JNICALL Java_com_mousebird_maply_Scene_initialise CoordSystemDisplayAdapter *coordAdapter = CoordSystemDisplayAdapterInfo::getClassInfo()->getObject(env,coordAdapterObj); SceneGLES *scene = new SceneGLES(coordAdapter); SceneRendererGLES_Android *sceneRender = SceneRendererInfo::getClassInfo()->getObject(env,renderControlObj); - scene->setFontTextureManager(FontTextureManagerRef(new FontTextureManager_Android(env,sceneRender,scene,charRendererObj))); + PlatformInfo_Android inst(env); + scene->setFontTextureManager(std::make_shared(&inst,sceneRender,scene,charRendererObj)); SceneClassInfo::getClassInfo()->setHandle(env,obj,scene); } catch (...) @@ -52,19 +52,16 @@ JNIEXPORT void JNICALL Java_com_mousebird_maply_Scene_initialise static std::mutex disposeMutex; -JNIEXPORT void JNICALL Java_com_mousebird_maply_Scene_dispose - (JNIEnv *env, jobject obj) +extern "C" +JNIEXPORT void JNICALL Java_com_mousebird_maply_Scene_dispose(JNIEnv *env, jobject obj) { try { SceneClassInfo *classInfo = SceneClassInfo::getClassInfo(); + std::lock_guard lock(disposeMutex); + if (Scene *inst = classInfo->getObject(env,obj)) { - std::lock_guard lock(disposeMutex); - Scene *inst = classInfo->getObject(env,obj); - if (!inst) - return; delete inst; - classInfo->clearHandle(env,obj); } } @@ -74,17 +71,20 @@ JNIEXPORT void JNICALL Java_com_mousebird_maply_Scene_dispose } } - -JNIEXPORT void JNICALL Java_com_mousebird_maply_Scene_addChangesNative - (JNIEnv *env, jobject obj, jobject changesObj) +extern "C" +JNIEXPORT void JNICALL Java_com_mousebird_maply_Scene_addChangesNative(JNIEnv *env, jobject obj, jobject changesObj) { try { SceneClassInfo *classInfo = SceneClassInfo::getClassInfo(); - Scene *scene = classInfo->getObject(env,obj); - ChangeSetRef *changes = ChangeSetClassInfo::getClassInfo()->getObject(env,changesObj); - scene->addChangeRequests(*(changes->get())); - (*changes)->clear(); + if (auto changes = ChangeSetClassInfo::getClassInfo()->getObject(env, changesObj)) + { + if (Scene *scene = classInfo->getObject(env, obj)) + { + scene->addChangeRequests(*(changes->get())); + (*changes)->clear(); + } + } } catch (...) { @@ -92,8 +92,8 @@ JNIEXPORT void JNICALL Java_com_mousebird_maply_Scene_addChangesNative } } -JNIEXPORT void JNICALL Java_com_mousebird_maply_Scene_addShaderProgram -(JNIEnv *env, jobject obj, jobject shaderObj) +extern "C" +JNIEXPORT void JNICALL Java_com_mousebird_maply_Scene_addShaderProgram(JNIEnv *env, jobject obj, jobject shaderObj) { try { @@ -102,10 +102,10 @@ JNIEXPORT void JNICALL Java_com_mousebird_maply_Scene_addShaderProgram ShaderClassInfo *shaderClassInfo = ShaderClassInfo::getClassInfo(); Shader_AndroidRef *shader = shaderClassInfo->getObject(env,shaderObj); - if (!scene || !shader) - return; - - scene->addProgram((*shader)->prog); + if (scene && shader) + { + scene->addProgram((*shader)->prog); + } } catch (...) { @@ -113,18 +113,16 @@ JNIEXPORT void JNICALL Java_com_mousebird_maply_Scene_addShaderProgram } } -JNIEXPORT void JNICALL Java_com_mousebird_maply_Scene_removeShaderProgram - (JNIEnv *env, jobject obj, jlong shaderID) +extern "C" +JNIEXPORT void JNICALL Java_com_mousebird_maply_Scene_removeShaderProgram(JNIEnv *env, jobject obj, jlong shaderID) { try { SceneClassInfo *classInfo = SceneClassInfo::getClassInfo(); - Scene *scene = classInfo->getObject(env,obj); - - if (!scene) - return; - - scene->removeProgram(shaderID,NULL); + if (Scene *scene = classInfo->getObject(env,obj)) + { + scene->removeProgram(shaderID,NULL); + } } catch (...) { @@ -132,17 +130,17 @@ JNIEXPORT void JNICALL Java_com_mousebird_maply_Scene_removeShaderProgram } } -JNIEXPORT void JNICALL Java_com_mousebird_maply_Scene_teardownGL -(JNIEnv *env, jobject obj) +extern "C" +JNIEXPORT void JNICALL Java_com_mousebird_maply_Scene_teardownGL(JNIEnv *env, jobject obj) { try { SceneClassInfo *classInfo = SceneClassInfo::getClassInfo(); - Scene *scene = classInfo->getObject(env,obj); - if (!scene) - return; - - scene->teardown(); + if (Scene *scene = classInfo->getObject(env,obj)) + { + PlatformInfo_Android platformInfo(env); + scene->teardown(&platformInfo); + } } catch (...) { @@ -150,21 +148,24 @@ JNIEXPORT void JNICALL Java_com_mousebird_maply_Scene_teardownGL } } +extern "C" JNIEXPORT void JNICALL Java_com_mousebird_maply_Scene_addRenderTargetNative - (JNIEnv *env, jobject obj, jlong renderTargetID, jint width, jint height, jlong texID, jboolean clearEveryFrame, jboolean blend, jfloat r, jfloat g, jfloat b, jfloat a) + (JNIEnv *env, jobject obj, jlong renderTargetID, jint width, jint height, jlong texID, + jboolean clearEveryFrame, jfloat clearVal, jboolean blend, jfloat r, jfloat g, jfloat b, jfloat a) { try { SceneClassInfo *classInfo = SceneClassInfo::getClassInfo(); - Scene *scene = classInfo->getObject(env,obj); - if (!scene) - return; - - ChangeSet changes; - RGBAColor color(r*255.0,g*255.0,b*255.0,a*255.0); - changes.push_back(new AddRenderTargetReq(renderTargetID,width,height,texID,clearEveryFrame,blend,color,0.0,RenderTargetMipmapNone,false)); - - scene->addChangeRequests(changes); + if (Scene *scene = classInfo->getObject(env,obj)) + { + ChangeSet changes; + const RGBAColor color(r * 255.0, g * 255.0, b * 255.0, a * 255.0); + changes.push_back( + new AddRenderTargetReq(renderTargetID, width, height, texID, clearEveryFrame, + blend, color, clearVal, RenderTargetMipmapNone, false)); + + scene->addChangeRequests(changes); + } } catch (...) { @@ -172,19 +173,18 @@ JNIEXPORT void JNICALL Java_com_mousebird_maply_Scene_addRenderTargetNative } } -JNIEXPORT void JNICALL Java_com_mousebird_maply_Scene_changeRenderTarget - (JNIEnv *env, jobject obj, jlong renderTargetID, jlong texID) +extern "C" +JNIEXPORT void JNICALL Java_com_mousebird_maply_Scene_changeRenderTarget(JNIEnv *env, jobject obj, jlong renderTargetID, jlong texID) { try { SceneClassInfo *classInfo = SceneClassInfo::getClassInfo(); - Scene *scene = classInfo->getObject(env,obj); - if (!scene) - return; - - ChangeSet changes; - changes.push_back(new ChangeRenderTargetReq(renderTargetID,texID)); - scene->addChangeRequests(changes); + if (Scene *scene = classInfo->getObject(env,obj)) + { + ChangeSet changes; + changes.push_back(new ChangeRenderTargetReq(renderTargetID, texID)); + scene->addChangeRequests(changes); + } } catch (...) { @@ -192,20 +192,18 @@ JNIEXPORT void JNICALL Java_com_mousebird_maply_Scene_changeRenderTarget } } -JNIEXPORT void JNICALL Java_com_mousebird_maply_Scene_removeRenderTargetNative -(JNIEnv *env, jobject obj, jlong targetID) +extern "C" +JNIEXPORT void JNICALL Java_com_mousebird_maply_Scene_removeRenderTargetNative(JNIEnv *env, jobject obj, jlong targetID) { try { SceneClassInfo *classInfo = SceneClassInfo::getClassInfo(); - Scene *scene = classInfo->getObject(env,obj); - if (!scene) - return; - - ChangeSet changes; - changes.push_back(new RemRenderTargetReq(targetID)); - - scene->addChangeRequests(changes); + if (Scene *scene = classInfo->getObject(env,obj)) + { + ChangeSet changes; + changes.push_back(new RemRenderTargetReq(targetID)); + scene->addChangeRequests(changes); + } } catch (...) { diff --git a/android/library/maply/jni/src/scene/Shader_jni.cpp b/android/library/maply/jni/src/scene/Shader_jni.cpp index b1bc0cfb94..0c3bf67d8d 100644 --- a/android/library/maply/jni/src/scene/Shader_jni.cpp +++ b/android/library/maply/jni/src/scene/Shader_jni.cpp @@ -2,7 +2,7 @@ * WhirlyGlobeLib * * Created by Steve Gifford on 8/216/15. - * Copyright 2011-2016 mousebird consulting + * Copyright 2011-2021 mousebird consulting * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -14,7 +14,6 @@ * 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. - * */ #import @@ -25,14 +24,15 @@ using namespace WhirlyKit; using namespace Maply; using namespace Eigen; -template<> ShaderClassInfo *ShaderClassInfo::classInfoObj = NULL; +template<> ShaderClassInfo *ShaderClassInfo::classInfoObj = nullptr; -JNIEXPORT void JNICALL Java_com_mousebird_maply_Shader_nativeInit - (JNIEnv *env, jclass cls) +extern "C" +JNIEXPORT void JNICALL Java_com_mousebird_maply_Shader_nativeInit(JNIEnv *env, jclass cls) { ShaderClassInfo::getClassInfo(env,cls); } +extern "C" JNIEXPORT void JNICALL Java_com_mousebird_maply_Shader_initialise__Ljava_lang_String_2Ljava_lang_String_2Ljava_lang_String_2 (JNIEnv *env, jobject obj, jstring nameStr, jstring vertStr, jstring fragStr) { @@ -57,8 +57,8 @@ JNIEXPORT void JNICALL Java_com_mousebird_maply_Shader_initialise__Ljava_lang_St } } -JNIEXPORT void JNICALL Java_com_mousebird_maply_Shader_initialise__ - (JNIEnv *env, jobject obj) +extern "C" +JNIEXPORT void JNICALL Java_com_mousebird_maply_Shader_initialise__(JNIEnv *env, jobject obj) { try { @@ -77,6 +77,7 @@ jobject MakeShader(JNIEnv *env,Shader_AndroidRef shader) return classInfo->makeWrapperObject(env,new Shader_AndroidRef(shader)); } +extern "C" JNIEXPORT void JNICALL Java_com_mousebird_maply_Shader_delayedSetupNative (JNIEnv *env, jobject obj, jstring nameStr, jstring vertStr, jstring fragStr) { @@ -106,8 +107,8 @@ JNIEXPORT void JNICALL Java_com_mousebird_maply_Shader_delayedSetupNative static std::mutex disposeMutex; -JNIEXPORT void JNICALL Java_com_mousebird_maply_Shader_dispose - (JNIEnv *env, jobject obj) +extern "C" +JNIEXPORT void JNICALL Java_com_mousebird_maply_Shader_dispose(JNIEnv *env, jobject obj) { try { @@ -128,8 +129,8 @@ JNIEXPORT void JNICALL Java_com_mousebird_maply_Shader_dispose } } -JNIEXPORT jboolean JNICALL Java_com_mousebird_maply_Shader_valid - (JNIEnv *env, jobject obj) +extern "C" +JNIEXPORT jboolean JNICALL Java_com_mousebird_maply_Shader_valid(JNIEnv *env, jobject obj) { try { @@ -147,8 +148,8 @@ JNIEXPORT jboolean JNICALL Java_com_mousebird_maply_Shader_valid return false; } -JNIEXPORT jstring JNICALL Java_com_mousebird_maply_Shader_getName -(JNIEnv *env, jobject obj) +extern "C" +JNIEXPORT jstring JNICALL Java_com_mousebird_maply_Shader_getName(JNIEnv *env, jobject obj) { try { @@ -164,8 +165,8 @@ JNIEXPORT jstring JNICALL Java_com_mousebird_maply_Shader_getName return NULL; } -JNIEXPORT jlong JNICALL Java_com_mousebird_maply_Shader_getID -(JNIEnv *env, jobject obj) +extern "C" +JNIEXPORT jlong JNICALL Java_com_mousebird_maply_Shader_getID(JNIEnv *env, jobject obj) { try { @@ -183,6 +184,7 @@ JNIEXPORT jlong JNICALL Java_com_mousebird_maply_Shader_getID return EmptyIdentity; } +extern "C" JNIEXPORT void JNICALL Java_com_mousebird_maply_Shader_addTextureNative (JNIEnv *env, jobject obj, jobject changeSetObj, jstring nameStr, jlong texID) { @@ -196,7 +198,8 @@ JNIEXPORT void JNICALL Java_com_mousebird_maply_Shader_addTextureNative JavaString name(env,nameStr); // Do this on the rendering thread so we don't get ahead of ourselves - (*changes)->push_back(new ShaderAddTextureReq((*inst)->prog->getId(),StringIndexer::getStringID(name.cStr),texID,-1)); + const auto idx = StringIndexer::getStringID(name.getCString()); + (*changes)->push_back(new ShaderAddTextureReq((*inst)->prog->getId(),idx,texID,-1)); } catch (...) { @@ -205,6 +208,7 @@ JNIEXPORT void JNICALL Java_com_mousebird_maply_Shader_addTextureNative } +extern "C" JNIEXPORT jboolean JNICALL Java_com_mousebird_maply_Shader_setUniformNative__Ljava_lang_String_2D (JNIEnv *env, jobject obj, jstring nameStr, jdouble uni) { @@ -232,6 +236,7 @@ JNIEXPORT jboolean JNICALL Java_com_mousebird_maply_Shader_setUniformNative__Lja return false; } +extern "C" JNIEXPORT jboolean JNICALL Java_com_mousebird_maply_Shader_setUniformByIndexNative (JNIEnv *env, jobject obj, jstring nameStr, jdouble uni, jint index) { @@ -260,6 +265,7 @@ JNIEXPORT jboolean JNICALL Java_com_mousebird_maply_Shader_setUniformByIndexNati } +extern "C" JNIEXPORT jboolean JNICALL Java_com_mousebird_maply_Shader_setUniformNative__Ljava_lang_String_2I (JNIEnv *env, jobject obj, jstring nameStr, jint uni) { @@ -287,6 +293,7 @@ JNIEXPORT jboolean JNICALL Java_com_mousebird_maply_Shader_setUniformNative__Lja return false; } +extern "C" JNIEXPORT jboolean JNICALL Java_com_mousebird_maply_Shader_setUniformNative__Ljava_lang_String_2DD (JNIEnv *env, jobject obj, jstring nameStr, jdouble x, jdouble y) { @@ -314,6 +321,7 @@ JNIEXPORT jboolean JNICALL Java_com_mousebird_maply_Shader_setUniformNative__Lja return false; } +extern "C" JNIEXPORT jboolean JNICALL Java_com_mousebird_maply_Shader_setUniformNative__Ljava_lang_String_2DDD (JNIEnv *env, jobject obj, jstring nameStr, jdouble x, jdouble y, jdouble z) { @@ -341,6 +349,7 @@ JNIEXPORT jboolean JNICALL Java_com_mousebird_maply_Shader_setUniformNative__Lja return false; } +extern "C" JNIEXPORT jboolean JNICALL Java_com_mousebird_maply_Shader_setUniformNative__Ljava_lang_String_2DDDD (JNIEnv *env, jobject obj, jstring nameStr, jdouble x, jdouble y, jdouble z, jdouble w) { @@ -368,6 +377,7 @@ JNIEXPORT jboolean JNICALL Java_com_mousebird_maply_Shader_setUniformNative__Lja return false; } +extern "C" JNIEXPORT jboolean JNICALL Java_com_mousebird_maply_Shader_setUniformColorByIndexNative (JNIEnv *env, jobject obj, jstring nameStr, jint colorInt, jint index) { @@ -399,6 +409,7 @@ JNIEXPORT jboolean JNICALL Java_com_mousebird_maply_Shader_setUniformColorByInde return false; } +extern "C" JNIEXPORT jboolean JNICALL Java_com_mousebird_maply_Shader_setUniformColorNative (JNIEnv *env, jobject obj, jstring nameStr, jint colorInt) { @@ -430,8 +441,8 @@ JNIEXPORT jboolean JNICALL Java_com_mousebird_maply_Shader_setUniformColorNative return false; } -JNIEXPORT void JNICALL Java_com_mousebird_maply_Shader_addVarying - (JNIEnv *env, jobject obj, jstring nameStr) +extern "C" +JNIEXPORT void JNICALL Java_com_mousebird_maply_Shader_addVarying(JNIEnv *env, jobject obj, jstring nameStr) { try { diff --git a/android/library/maply/jni/src/scene/Texture_jni.cpp b/android/library/maply/jni/src/scene/Texture_jni.cpp index ac6c24ff50..d5a9473878 100644 --- a/android/library/maply/jni/src/scene/Texture_jni.cpp +++ b/android/library/maply/jni/src/scene/Texture_jni.cpp @@ -91,7 +91,7 @@ JNIEXPORT jboolean JNICALL Java_com_mousebird_maply_Texture_setBitmap if (AndroidBitmap_lockPixels(env, bitmapObj, &bitmapPixels) < 0) return false; - uint32_t* src = (uint32_t*) bitmapPixels; + //uint32_t* src = (uint32_t*) bitmapPixels; MutableRawData *rawData = new MutableRawData(bitmapPixels,info.height*info.width*4); tex->setRawData(rawData, info.width, info.height); AndroidBitmap_unlockPixels(env, bitmapObj); diff --git a/android/library/maply/jni/src/shapes/ShapeManager_jni.cpp b/android/library/maply/jni/src/shapes/ShapeManager_jni.cpp index cb102fb514..2caff43642 100644 --- a/android/library/maply/jni/src/shapes/ShapeManager_jni.cpp +++ b/android/library/maply/jni/src/shapes/ShapeManager_jni.cpp @@ -42,8 +42,8 @@ JNIEXPORT void JNICALL Java_com_mousebird_maply_ShapeManager_initialise Scene *scene = SceneClassInfo::getClassInfo()->getObject(env, sceneObj); if (!scene) return; - ShapeManager *shapeManager = dynamic_cast(scene->getManager(kWKShapeManager)); - ShapeManagerClassInfo::getClassInfo()->setHandle(env,obj,shapeManager); + ShapeManagerRef shapeManager = std::dynamic_pointer_cast(scene->getManager(kWKShapeManager)); + ShapeManagerClassInfo::getClassInfo()->setHandle(env,obj,new ShapeManagerRef(shapeManager)); } catch (...) { @@ -59,6 +59,9 @@ JNIEXPORT void JNICALL Java_com_mousebird_maply_ShapeManager_dispose try { ShapeManagerClassInfo *classInfo = ShapeManagerClassInfo::getClassInfo(); + ShapeManagerRef *inst = classInfo->getObject(env, obj); + if (inst) + delete inst; classInfo->clearHandle(env, obj); } catch (...) @@ -73,7 +76,7 @@ JNIEXPORT jlong JNICALL Java_com_mousebird_maply_ShapeManager_addShapes try { ShapeManagerClassInfo *classInfo = ShapeManagerClassInfo::getClassInfo(); - ShapeManager *inst = classInfo->getObject(env, obj); + ShapeManagerRef *inst = classInfo->getObject(env, obj); ShapeInfoRef *shapeInfo = ShapeInfoClassInfo::getClassInfo()->getObject(env, shapeInfoObj); ChangeSetRef *changeSet = ChangeSetClassInfo::getClassInfo()->getObject(env, changeObj); @@ -92,7 +95,7 @@ JNIEXPORT jlong JNICALL Java_com_mousebird_maply_ShapeManager_addShapes GreatCircle_Android *greatCircle = dynamic_cast(shape); if (greatCircle) { - Linear *lin = greatCircle->asLinear(inst->getScene()->getCoordAdapter()); + Linear *lin = greatCircle->asLinear((*inst)->getScene()->getCoordAdapter()); if (lin) shapes.push_back(lin); } else { @@ -101,12 +104,12 @@ JNIEXPORT jlong JNICALL Java_com_mousebird_maply_ShapeManager_addShapes } if ((*shapeInfo)->programID == EmptyIdentity) { - ProgramGLES *prog = (ProgramGLES *)inst->getScene()->findProgramByName(MaplyDefaultModelTriShader); + ProgramGLES *prog = (ProgramGLES *)(*inst)->getScene()->findProgramByName(MaplyDefaultModelTriShader); if (prog) (*shapeInfo)->programID = prog->getId(); } - SimpleIdentity shapeId = inst->addShapes(shapes, *(*shapeInfo), *(changeSet->get())); + SimpleIdentity shapeId = (*inst)->addShapes(shapes, *(*shapeInfo), *(changeSet->get())); return shapeId; } catch (...) @@ -123,7 +126,7 @@ JNIEXPORT void JNICALL Java_com_mousebird_maply_ShapeManager_removeShapes try { ShapeManagerClassInfo *classInfo = ShapeManagerClassInfo::getClassInfo(); - ShapeManager *inst = classInfo->getObject(env, obj); + ShapeManagerRef *inst = classInfo->getObject(env, obj); ChangeSetRef *changeSet = ChangeSetClassInfo::getClassInfo()->getObject(env, changeObj); if (!inst || !changeSet) return; @@ -135,7 +138,7 @@ JNIEXPORT void JNICALL Java_com_mousebird_maply_ShapeManager_removeShapes idSet.insert(ids.rawLong[ii]); } - inst->removeShapes(idSet, *(changeSet->get())); + (*inst)->removeShapes(idSet, *(changeSet->get())); } catch (...) @@ -150,7 +153,7 @@ JNIEXPORT void JNICALL Java_com_mousebird_maply_ShapeManager_enableShapes try { ShapeManagerClassInfo *classInfo = ShapeManagerClassInfo::getClassInfo(); - ShapeManager *inst = classInfo->getObject(env, obj); + ShapeManagerRef *inst = classInfo->getObject(env, obj); ChangeSetRef *changeSet = ChangeSetClassInfo::getClassInfo()->getObject(env, changeObj); if (!inst || !changeSet) return; @@ -162,7 +165,7 @@ JNIEXPORT void JNICALL Java_com_mousebird_maply_ShapeManager_enableShapes idSet.insert(ids.rawLong[ii]); } - inst->enableShapes(idSet, enable, *(changeSet->get())); + (*inst)->enableShapes(idSet, enable, *(changeSet->get())); } catch (...) diff --git a/android/library/maply/jni/src/stickers/StickerManager_jni.cpp b/android/library/maply/jni/src/stickers/StickerManager_jni.cpp index 18c2bd4456..50584eaf08 100644 --- a/android/library/maply/jni/src/stickers/StickerManager_jni.cpp +++ b/android/library/maply/jni/src/stickers/StickerManager_jni.cpp @@ -1,9 +1,8 @@ -/* - * StickerMAnager_jni.cpp +/* StickerMAnager_jni.cpp * WhirlyGlobeLib * * Created by Steve Gifford on 11/18/15. - * Copyright 2011-2016 mousebird consulting + * Copyright 2011-2021 mousebird consulting * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -15,7 +14,6 @@ * 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. - * */ #import "Stickers_jni.h" @@ -25,26 +23,26 @@ using namespace WhirlyKit; using namespace Maply; -static const char *SceneHandleName = "nativeSceneHandle"; +//static const char *SceneHandleName = "nativeSceneHandle"; -template<> StickerManagerClassInfo *StickerManagerClassInfo::classInfoObj = NULL; +template<> StickerManagerClassInfo *StickerManagerClassInfo::classInfoObj = nullptr; -JNIEXPORT void JNICALL Java_com_mousebird_maply_StickerManager_nativeInit -(JNIEnv *env, jclass cls) +extern "C" +JNIEXPORT void JNICALL Java_com_mousebird_maply_StickerManager_nativeInit(JNIEnv *env, jclass cls) { StickerManagerClassInfo::getClassInfo(env,cls); } -JNIEXPORT void JNICALL Java_com_mousebird_maply_StickerManager_initialise -(JNIEnv *env, jobject obj, jobject sceneObj) +extern "C" +JNIEXPORT void JNICALL Java_com_mousebird_maply_StickerManager_initialise(JNIEnv *env, jobject obj, jobject sceneObj) { try { Scene *scene = SceneClassInfo::getClassInfo()->getObject(env, sceneObj); if (!scene) return; - SphericalChunkManager *chunkManager = dynamic_cast(scene->getManager(kWKSphericalChunkManager)); - StickerManagerClassInfo::getClassInfo()->setHandle(env,obj,chunkManager); + SphericalChunkManagerRef chunkManager = std::dynamic_pointer_cast(scene->getManager(kWKSphericalChunkManager)); + StickerManagerClassInfo::getClassInfo()->setHandle(env,obj,new SphericalChunkManagerRef(chunkManager)); } catch (...) { @@ -54,11 +52,13 @@ JNIEXPORT void JNICALL Java_com_mousebird_maply_StickerManager_initialise static std::mutex disposeMutex; -JNIEXPORT void JNICALL Java_com_mousebird_maply_StickerManager_dispose -(JNIEnv *env, jobject obj) +extern "C" +JNIEXPORT void JNICALL Java_com_mousebird_maply_StickerManager_dispose(JNIEnv *env, jobject obj) { try { + SphericalChunkManagerRef *chunkManager = StickerManagerClassInfo::getClassInfo()->getObject(env,obj); + delete chunkManager; StickerManagerClassInfo::getClassInfo()->clearHandle(env,obj); } catch (...) @@ -67,12 +67,13 @@ JNIEXPORT void JNICALL Java_com_mousebird_maply_StickerManager_dispose } } +extern "C" JNIEXPORT jlong JNICALL Java_com_mousebird_maply_StickerManager_addStickers (JNIEnv *env, jobject obj, jobjectArray stickerArr, jobject stickerInfoObj, jobject changeSetObj) { try { - SphericalChunkManager *chunkManager = StickerManagerClassInfo::getClassInfo()->getObject(env,obj); + SphericalChunkManagerRef *chunkManager = StickerManagerClassInfo::getClassInfo()->getObject(env,obj); SphericalChunkClassInfo *chunkClassInfo = SphericalChunkClassInfo::getClassInfo(); SphericalChunkInfoRef *chunkInfo = SphericalChunkInfoClassInfo::getClassInfo()->getObject(env,stickerInfoObj); ChangeSetRef *changeSet = ChangeSetClassInfo::getClassInfo()->getObject(env,changeSetObj); @@ -83,7 +84,7 @@ JNIEXPORT jlong JNICALL Java_com_mousebird_maply_StickerManager_addStickers } if ((*chunkInfo)->programID == EmptyIdentity) { - ProgramGLES *prog = (ProgramGLES *)chunkManager->getScene()->findProgramByName(MaplyDefaultTriangleShader); + ProgramGLES *prog = (ProgramGLES *)(*chunkManager)->getScene()->findProgramByName(MaplyDefaultTriangleShader); if (prog) (*chunkInfo)->programID = prog->getId(); } @@ -96,7 +97,7 @@ JNIEXPORT jlong JNICALL Java_com_mousebird_maply_StickerManager_addStickers chunks.push_back(*chunk); } - SimpleIdentity chunkId = chunkManager->addChunks(chunks,*(*chunkInfo),*(changeSet->get())); + SimpleIdentity chunkId = (*chunkManager)->addChunks(chunks,*(*chunkInfo),*(changeSet->get())); return chunkId; } @@ -108,19 +109,20 @@ JNIEXPORT jlong JNICALL Java_com_mousebird_maply_StickerManager_addStickers return EmptyIdentity; } +extern "C" JNIEXPORT jboolean JNICALL Java_com_mousebird_maply_StickerManager_modifyChunkTextures -(JNIEnv *env, jobject obj, jlong stickerID, jobject stickerInfoObj, jobject changeSetObj) + (JNIEnv *env, jobject obj, jlong stickerID, jobject stickerInfoObj, jobject changeSetObj) { try { StickerManagerClassInfo *classInfo = StickerManagerClassInfo::getClassInfo(); - SphericalChunkManager *chunkManager = classInfo->getObject(env,obj); + SphericalChunkManagerRef *chunkManager = classInfo->getObject(env,obj); SphericalChunkInfoRef *chunkInfo = SphericalChunkInfoClassInfo::getClassInfo()->getObject(env,stickerInfoObj); ChangeSetRef *changeSet = ChangeSetClassInfo::getClassInfo()->getObject(env,changeSetObj); if (!chunkManager || !chunkInfo || !changeSet) return false; - chunkManager->modifyChunkTextures(stickerID,(*chunkInfo)->texIDs,*(changeSet->get())); + (*chunkManager)->modifyChunkTextures(stickerID,(*chunkInfo)->texIDs,*(changeSet->get())); return true; } @@ -132,18 +134,19 @@ JNIEXPORT jboolean JNICALL Java_com_mousebird_maply_StickerManager_modifyChunkTe return false; } +extern "C" JNIEXPORT jboolean JNICALL Java_com_mousebird_maply_StickerManager_modifyDrawPriority -(JNIEnv *env, jobject obj, jlong stickerID, jint drawPriority, jobject changeSetObj) + (JNIEnv *env, jobject obj, jlong stickerID, jint drawPriority, jobject changeSetObj) { try { StickerManagerClassInfo *classInfo = StickerManagerClassInfo::getClassInfo(); - SphericalChunkManager *chunkManager = classInfo->getObject(env,obj); + SphericalChunkManagerRef *chunkManager = classInfo->getObject(env,obj); ChangeSetRef *changeSet = ChangeSetClassInfo::getClassInfo()->getObject(env,changeSetObj); if (!chunkManager || !changeSet) return false; - chunkManager->modifyDrawPriority(stickerID,drawPriority,*(changeSet->get())); + (*chunkManager)->modifyDrawPriority(stickerID,drawPriority,*(changeSet->get())); return true; } @@ -155,13 +158,14 @@ JNIEXPORT jboolean JNICALL Java_com_mousebird_maply_StickerManager_modifyDrawPri return false; } +extern "C" JNIEXPORT void JNICALL Java_com_mousebird_maply_StickerManager_enableStickers -(JNIEnv *env, jobject obj, jlongArray idArrayObj, jboolean enable, jobject changeSetObj) + (JNIEnv *env, jobject obj, jlongArray idArrayObj, jboolean enable, jobject changeSetObj) { try { StickerManagerClassInfo *classInfo = StickerManagerClassInfo::getClassInfo(); - SphericalChunkManager *chunkManager = classInfo->getObject(env,obj); + SphericalChunkManagerRef *chunkManager = classInfo->getObject(env,obj); ChangeSetRef *changeSet = ChangeSetClassInfo::getClassInfo()->getObject(env,changeSetObj); if (!chunkManager || !changeSet) return; @@ -170,7 +174,7 @@ JNIEXPORT void JNICALL Java_com_mousebird_maply_StickerManager_enableStickers SimpleIDSet ids; for (int ii=0;iienableChunk(idArray.rawLong[ii],enable,*(changeSet->get())); + (*chunkManager)->enableChunk(idArray.rawLong[ii],enable,*(changeSet->get())); } } catch (...) @@ -179,13 +183,14 @@ JNIEXPORT void JNICALL Java_com_mousebird_maply_StickerManager_enableStickers } } +extern "C" JNIEXPORT void JNICALL Java_com_mousebird_maply_StickerManager_removeStickers -(JNIEnv *env, jobject obj, jlongArray idArrayObj, jobject changeSetObj) + (JNIEnv *env, jobject obj, jlongArray idArrayObj, jobject changeSetObj) { try { StickerManagerClassInfo *classInfo = StickerManagerClassInfo::getClassInfo(); - SphericalChunkManager *chunkManager = classInfo->getObject(env,obj); + SphericalChunkManagerRef *chunkManager = classInfo->getObject(env,obj); ChangeSetRef *changeSet = ChangeSetClassInfo::getClassInfo()->getObject(env,changeSetObj); if (!chunkManager || !changeSet) return; @@ -195,7 +200,7 @@ JNIEXPORT void JNICALL Java_com_mousebird_maply_StickerManager_removeStickers for (int ii=0;iiremoveChunks(ids,*(changeSet->get())); + (*chunkManager)->removeChunks(ids,*(changeSet->get())); } catch (...) { diff --git a/android/library/maply/jni/src/vectors/AttrDictionaryEntry_jni.cpp b/android/library/maply/jni/src/vectors/AttrDictionaryEntry_jni.cpp index 83dba1ce23..58bac7d1a0 100644 --- a/android/library/maply/jni/src/vectors/AttrDictionaryEntry_jni.cpp +++ b/android/library/maply/jni/src/vectors/AttrDictionaryEntry_jni.cpp @@ -34,7 +34,7 @@ JNIEXPORT void JNICALL Java_com_mousebird_maply_AttrDictionaryEntry_nativeInit AttrDictEntryClassInfo::getClassInfo(env,theClass); } -JNIEXPORT jobject JNICALL MakeAttrDictionaryEntry(JNIEnv *env,DictionaryEntry_AndroidRef entry) +JNIEXPORT jobject JNICALL MakeAttrDictionaryEntry(JNIEnv *env,const DictionaryEntry_AndroidRef &entry) { AttrDictEntryClassInfo *classInfo = AttrDictEntryClassInfo::getClassInfo(env,"com/mousebird/maply/AttrDictionaryEntry"); @@ -187,7 +187,7 @@ JNIEXPORT jlong JNICALL Java_com_mousebird_maply_AttrDictionaryEntry_getIdentity { DictionaryEntry_AndroidRef *entry = AttrDictEntryClassInfo::getClassInfo()->getObject(env,obj); if (!entry) - return 0.0; + return 0; DictionaryType type = (*entry)->getType(); if (type != DictTypeDouble && type != DictTypeInt && type != DictTypeIdentity) return 0; @@ -201,8 +201,7 @@ JNIEXPORT jlong JNICALL Java_com_mousebird_maply_AttrDictionaryEntry_getIdentity return 0; } -JNIEXPORT jobjectArray JNICALL Java_com_mousebird_maply_AttrDictionaryEntry_getArray - (JNIEnv *env, jobject obj) +JNIEXPORT jobjectArray JNICALL Java_com_mousebird_maply_AttrDictionaryEntry_getArray(JNIEnv *env, jobject obj) { try { diff --git a/android/library/maply/jni/src/vectors/AttrDictionary_jni.cpp b/android/library/maply/jni/src/vectors/AttrDictionary_jni.cpp index 60acf99dbb..b1b92d19b1 100644 --- a/android/library/maply/jni/src/vectors/AttrDictionary_jni.cpp +++ b/android/library/maply/jni/src/vectors/AttrDictionary_jni.cpp @@ -1,9 +1,8 @@ -/* - * AttrDictionary_jni.cpp +/* AttrDictionary_jni.cpp * WhirlyGlobeLib * * Created by Steve Gifford on 6/2/14. - * Copyright 2011-2016 mousebird consulting + * Copyright 2011-2021 mousebird consulting * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -15,7 +14,6 @@ * 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. - * */ #import @@ -24,33 +22,33 @@ #import "Vectors_jni.h" #import "WhirlyGlobe_Android.h" -template<> AttrDictClassInfo *AttrDictClassInfo::classInfoObj = NULL; +template<> AttrDictClassInfo *AttrDictClassInfo::classInfoObj = nullptr; using namespace WhirlyKit; -JNIEXPORT void JNICALL Java_com_mousebird_maply_AttrDictionary_nativeInit - (JNIEnv *env, jclass theClass) +extern "C" +JNIEXPORT void JNICALL Java_com_mousebird_maply_AttrDictionary_nativeInit(JNIEnv *env, jclass theClass) { AttrDictClassInfo::getClassInfo(env,theClass); } -JNIEXPORT jobject JNICALL MakeAttrDictionary(JNIEnv *env,MutableDictionary_AndroidRef dict) +JNIEXPORT jobject JNICALL MakeAttrDictionary(JNIEnv *env,const MutableDictionary_AndroidRef &dict) { AttrDictClassInfo *classInfo = AttrDictClassInfo::getClassInfo(env,"com/mousebird/maply/AttrDictionary"); - jobject dictObj = classInfo->makeWrapperObject(env,NULL); - MutableDictionary_AndroidRef *inst = classInfo->getObject(env,dictObj); + jobject dictObj = classInfo->makeWrapperObject(env,nullptr); + auto inst = classInfo->getObject(env,dictObj); *(inst->get()) = *(dict.get()); return dictObj; } -JNIEXPORT void JNICALL Java_com_mousebird_maply_AttrDictionary_initialise - (JNIEnv *env, jobject obj) +extern "C" +JNIEXPORT void JNICALL Java_com_mousebird_maply_AttrDictionary_initialise(JNIEnv *env, jobject obj) { try { - MutableDictionary_AndroidRef *dict = new MutableDictionary_AndroidRef(new MutableDictionary_Android()); + auto dict = new MutableDictionary_AndroidRef(new MutableDictionary_Android()); AttrDictClassInfo::getClassInfo()->setHandle(env,obj,dict); } catch (...) @@ -61,8 +59,8 @@ JNIEXPORT void JNICALL Java_com_mousebird_maply_AttrDictionary_initialise static std::mutex disposeMutex; -JNIEXPORT void JNICALL Java_com_mousebird_maply_AttrDictionary_dispose - (JNIEnv *env, jobject obj) +extern "C" +JNIEXPORT void JNICALL Java_com_mousebird_maply_AttrDictionary_dispose(JNIEnv *env, jobject obj) { try { @@ -83,8 +81,8 @@ JNIEXPORT void JNICALL Java_com_mousebird_maply_AttrDictionary_dispose } } -JNIEXPORT jboolean JNICALL Java_com_mousebird_maply_AttrDictionary_parseFromJSON - (JNIEnv *env, jobject obj, jstring inJsonStr) +extern "C" +JNIEXPORT jboolean JNICALL Java_com_mousebird_maply_AttrDictionary_parseFromJSON(JNIEnv *env, jobject obj, jstring inJsonStr) { try { @@ -95,18 +93,18 @@ JNIEXPORT jboolean JNICALL Java_com_mousebird_maply_AttrDictionary_parseFromJSON JavaString jsonStr(env,inJsonStr); - return (*dict)->parseJSON(jsonStr.cStr); + return (*dict)->parseJSON(jsonStr.getCString()); } catch (...) { __android_log_print(ANDROID_LOG_VERBOSE, "Maply", "Crash in Dictionary::parseFromJSON()"); } - return true; + return true; // ? } -JNIEXPORT jboolean JNICALL Java_com_mousebird_maply_AttrDictionary_hasField - (JNIEnv *env, jobject obj, jstring attrNameStr) +extern "C" +JNIEXPORT jboolean JNICALL Java_com_mousebird_maply_AttrDictionary_hasField(JNIEnv *env, jobject obj, jstring attrNameStr) { try { @@ -117,7 +115,7 @@ JNIEXPORT jboolean JNICALL Java_com_mousebird_maply_AttrDictionary_hasField JavaString attrName(env,attrNameStr); - return (*dict)->hasField(attrName.cStr); + return (*dict)->hasField(attrName.getCString()); } catch (...) { @@ -127,19 +125,19 @@ JNIEXPORT jboolean JNICALL Java_com_mousebird_maply_AttrDictionary_hasField return false; } -JNIEXPORT jstring JNICALL Java_com_mousebird_maply_AttrDictionary_getString - (JNIEnv *env, jobject obj, jstring attrNameStr) +extern "C" +JNIEXPORT jstring JNICALL Java_com_mousebird_maply_AttrDictionary_getString(JNIEnv *env, jobject obj, jstring attrNameStr) { try { AttrDictClassInfo *classInfo = AttrDictClassInfo::getClassInfo(); MutableDictionary_AndroidRef *dict = classInfo->getObject(env,obj); if (!dict) - return NULL; + return nullptr; JavaString attrName(env,attrNameStr); - std::string str = (*dict)->getString(attrName.cStr); + std::string str = (*dict)->getString(attrName.getCString()); if (!str.empty()) { return env->NewStringUTF(str.c_str()); @@ -150,24 +148,24 @@ JNIEXPORT jstring JNICALL Java_com_mousebird_maply_AttrDictionary_getString __android_log_print(ANDROID_LOG_VERBOSE, "Maply", "Crash in Dictionary::getString()"); } - return NULL; + return nullptr; } -JNIEXPORT jobject JNICALL Java_com_mousebird_maply_AttrDictionary_getInt - (JNIEnv *env, jobject obj, jstring attrNameStr) +extern "C" +JNIEXPORT jobject JNICALL Java_com_mousebird_maply_AttrDictionary_getInt(JNIEnv *env, jobject obj, jstring attrNameStr) { try { AttrDictClassInfo *classInfo = AttrDictClassInfo::getClassInfo(); MutableDictionary_AndroidRef *dict = classInfo->getObject(env,obj); if (!dict) - return NULL; + return nullptr; JavaString attrName(env,attrNameStr); - if ((*dict)->hasField(attrName.cStr)) + if ((*dict)->hasField(attrName.getCString())) { - int val = (*dict)->getInt(attrName.cStr); + int val = (*dict)->getInt(attrName.getCString(), 0); return JavaIntegerClassInfo::getClassInfo(env)->makeInteger(env,val); } } @@ -176,26 +174,27 @@ JNIEXPORT jobject JNICALL Java_com_mousebird_maply_AttrDictionary_getInt __android_log_print(ANDROID_LOG_VERBOSE, "Maply", "Crash in Dictionary::getInt()"); } - return NULL; + return nullptr; } -JNIEXPORT jobjectArray JNICALL Java_com_mousebird_maply_AttrDictionary_getArray - (JNIEnv *env, jobject obj, jstring attrNameStr) +extern "C" +JNIEXPORT jobjectArray JNICALL Java_com_mousebird_maply_AttrDictionary_getArray(JNIEnv *env, jobject obj, jstring attrNameStr) { try { AttrDictClassInfo *classInfo = AttrDictClassInfo::getClassInfo(); MutableDictionary_AndroidRef *dict = classInfo->getObject(env,obj); if (!dict) - return NULL; + return nullptr; JavaString attrName(env,attrNameStr); - if ((*dict)->getType(attrName.cStr) == DictTypeArray) + if ((*dict)->getType(attrName.getCString()) == DictTypeArray) { std::vector retObjs; - auto arr = (*dict)->getArray(attrName.cStr); - for (auto arrEntry: arr) { + auto arr = (*dict)->getArray(attrName.getCString()); + retObjs.reserve(arr.size()); + for (const auto &arrEntry : arr) { jobject newObj = MakeAttrDictionaryEntry(env,std::dynamic_pointer_cast(arrEntry)); retObjs.push_back(newObj); } @@ -216,21 +215,21 @@ JNIEXPORT jobjectArray JNICALL Java_com_mousebird_maply_AttrDictionary_getArray return NULL; } -JNIEXPORT jobject JNICALL Java_com_mousebird_maply_AttrDictionary_getDouble - (JNIEnv *env, jobject obj, jstring attrNameStr) +extern "C" +JNIEXPORT jobject JNICALL Java_com_mousebird_maply_AttrDictionary_getDouble(JNIEnv *env, jobject obj, jstring attrNameStr) { try { AttrDictClassInfo *classInfo = AttrDictClassInfo::getClassInfo(); MutableDictionary_AndroidRef *dict = classInfo->getObject(env,obj); if (!dict) - return NULL; + return nullptr; JavaString attrName(env,attrNameStr); - if ((*dict)->hasField(attrName.cStr)) + if ((*dict)->hasField(attrName.getCString())) { - double val = (*dict)->getDouble(attrName.cStr); + double val = (*dict)->getDouble(attrName.getCString(), 0); return JavaDoubleClassInfo::getClassInfo(env)->makeDouble(env,val); } } @@ -239,24 +238,24 @@ JNIEXPORT jobject JNICALL Java_com_mousebird_maply_AttrDictionary_getDouble __android_log_print(ANDROID_LOG_VERBOSE, "Maply", "Crash in Dictionary::getDouble()"); } - return NULL; + return nullptr; } -JNIEXPORT jobject JNICALL Java_com_mousebird_maply_AttrDictionary_getIdentity - (JNIEnv *env, jobject obj, jstring attrNameStr) +extern "C" +JNIEXPORT jobject JNICALL Java_com_mousebird_maply_AttrDictionary_getIdentity(JNIEnv *env, jobject obj, jstring attrNameStr) { try { AttrDictClassInfo *classInfo = AttrDictClassInfo::getClassInfo(); MutableDictionary_AndroidRef *dict = classInfo->getObject(env,obj); if (!dict) - return NULL; + return nullptr; JavaString attrName(env,attrNameStr); - if ((*dict)->hasField(attrName.cStr)) + if ((*dict)->hasField(attrName.getCString())) { - long long val = (*dict)->getIdentity(attrName.cStr); + long long val = (*dict)->getIdentity(attrName.getCString()); return JavaLongClassInfo::getClassInfo(env)->makeLong(env,val); } } @@ -265,22 +264,22 @@ JNIEXPORT jobject JNICALL Java_com_mousebird_maply_AttrDictionary_getIdentity __android_log_print(ANDROID_LOG_VERBOSE, "Maply", "Crash in Dictionary::getIdentity()"); } - return NULL; + return nullptr; } -JNIEXPORT jobject JNICALL Java_com_mousebird_maply_AttrDictionary_getDict - (JNIEnv *env, jobject obj, jstring attrNameStr) +extern "C" +JNIEXPORT jobject JNICALL Java_com_mousebird_maply_AttrDictionary_getDict(JNIEnv *env, jobject obj, jstring attrNameStr) { try { AttrDictClassInfo *classInfo = AttrDictClassInfo::getClassInfo(); MutableDictionary_AndroidRef *dict = classInfo->getObject(env,obj); if (!dict) - return NULL; + return nullptr; JavaString attrName(env,attrNameStr); - MutableDictionary_AndroidRef subDict = std::dynamic_pointer_cast((*dict)->getDict(attrName.cStr)); + MutableDictionary_AndroidRef subDict = std::dynamic_pointer_cast((*dict)->getDict(attrName.getCString())); if (subDict) return MakeAttrDictionary(env,subDict); } @@ -289,22 +288,22 @@ JNIEXPORT jobject JNICALL Java_com_mousebird_maply_AttrDictionary_getDict __android_log_print(ANDROID_LOG_VERBOSE, "Maply", "Crash in Dictionary::getDict()"); } - return NULL; + return nullptr; } -JNIEXPORT jobject JNICALL Java_com_mousebird_maply_AttrDictionary_getEntry - (JNIEnv *env, jobject obj, jstring attrNameStr) +extern "C" +JNIEXPORT jobject JNICALL Java_com_mousebird_maply_AttrDictionary_getEntry(JNIEnv *env, jobject obj, jstring attrNameStr) { try { AttrDictClassInfo *classInfo = AttrDictClassInfo::getClassInfo(); MutableDictionary_AndroidRef *dict = classInfo->getObject(env,obj); if (!dict) - return NULL; + return nullptr; JavaString attrName(env,attrNameStr); - DictionaryEntry_AndroidRef entry = std::dynamic_pointer_cast((*dict)->getEntry(attrName.cStr)); + DictionaryEntry_AndroidRef entry = std::dynamic_pointer_cast((*dict)->getEntry(attrName.getCString())); if (entry) return MakeAttrDictionaryEntry(env,entry); } @@ -313,18 +312,18 @@ JNIEXPORT jobject JNICALL Java_com_mousebird_maply_AttrDictionary_getEntry __android_log_print(ANDROID_LOG_VERBOSE, "Maply", "Crash in Dictionary::getEntry()"); } - return NULL; + return nullptr; } -JNIEXPORT jobjectArray JNICALL Java_com_mousebird_maply_AttrDictionary_getKeys - (JNIEnv *env, jobject obj) +extern "C" +JNIEXPORT jobjectArray JNICALL Java_com_mousebird_maply_AttrDictionary_getKeys(JNIEnv *env, jobject obj) { try { AttrDictClassInfo *classInfo = AttrDictClassInfo::getClassInfo(); MutableDictionary_AndroidRef *dict = classInfo->getObject(env,obj); if (!dict) - return NULL; + return nullptr; auto keys = (*dict)->getKeys(); return BuildStringArray(env,keys); @@ -334,12 +333,12 @@ JNIEXPORT jobjectArray JNICALL Java_com_mousebird_maply_AttrDictionary_getKeys __android_log_print(ANDROID_LOG_VERBOSE, "Maply", "Crash in Dictionary::getKeys()"); } - return NULL; + return nullptr; } -JNIEXPORT void JNICALL Java_com_mousebird_maply_AttrDictionary_setString -(JNIEnv *env, jobject obj, jstring attrNameObj, jstring strValObj) +extern "C" +JNIEXPORT void JNICALL Java_com_mousebird_maply_AttrDictionary_setString(JNIEnv *env, jobject obj, jstring attrNameObj, jstring strValObj) { try { @@ -351,7 +350,7 @@ JNIEXPORT void JNICALL Java_com_mousebird_maply_AttrDictionary_setString JavaString attrName(env,attrNameObj); JavaString attrVal(env,strValObj); - (*dict)->setString(attrName.cStr,attrVal.cStr); + (*dict)->setString(attrName.getCString(),attrVal.getCString()); } catch (...) { @@ -359,8 +358,8 @@ JNIEXPORT void JNICALL Java_com_mousebird_maply_AttrDictionary_setString } } -JNIEXPORT void JNICALL Java_com_mousebird_maply_AttrDictionary_setInt -(JNIEnv *env, jobject obj, jstring attrNameObj, jint iVal) +extern "C" +JNIEXPORT void JNICALL Java_com_mousebird_maply_AttrDictionary_setInt(JNIEnv *env, jobject obj, jstring attrNameObj, jint iVal) { try { @@ -371,7 +370,7 @@ JNIEXPORT void JNICALL Java_com_mousebird_maply_AttrDictionary_setInt JavaString attrName(env,attrNameObj); - (*dict)->setInt(attrName.cStr,iVal); + (*dict)->setInt(attrName.getCString(),iVal); } catch (...) { @@ -379,8 +378,8 @@ JNIEXPORT void JNICALL Java_com_mousebird_maply_AttrDictionary_setInt } } -JNIEXPORT void JNICALL Java_com_mousebird_maply_AttrDictionary_setDouble -(JNIEnv *env, jobject obj, jstring attrNameObj, jdouble dVal) +extern "C" +JNIEXPORT void JNICALL Java_com_mousebird_maply_AttrDictionary_setDouble(JNIEnv *env, jobject obj, jstring attrNameObj, jdouble dVal) { try { @@ -391,7 +390,7 @@ JNIEXPORT void JNICALL Java_com_mousebird_maply_AttrDictionary_setDouble JavaString attrName(env,attrNameObj); - (*dict)->setDouble(attrName.cStr,dVal); + (*dict)->setDouble(attrName.getCString(),dVal); } catch (...) { @@ -399,8 +398,8 @@ JNIEXPORT void JNICALL Java_com_mousebird_maply_AttrDictionary_setDouble } } -JNIEXPORT void JNICALL Java_com_mousebird_maply_AttrDictionary_setDict - (JNIEnv *env, jobject obj, jstring attrNameObj, jobject dictObj) +extern "C" +JNIEXPORT void JNICALL Java_com_mousebird_maply_AttrDictionary_setDict(JNIEnv *env, jobject obj, jstring attrNameObj, jobject dictObj) { try { @@ -412,7 +411,7 @@ JNIEXPORT void JNICALL Java_com_mousebird_maply_AttrDictionary_setDict JavaString attrName(env,attrNameObj); - (*dict)->setDict(attrName.cStr,(*otherDict)); + (*dict)->setDict(attrName.getCString(),*otherDict); } catch (...) { @@ -420,8 +419,8 @@ JNIEXPORT void JNICALL Java_com_mousebird_maply_AttrDictionary_setDict } } -JNIEXPORT void JNICALL Java_com_mousebird_maply_AttrDictionary_setArray__Ljava_lang_String_2_3Lcom_mousebird_maply_AttrDictionaryEntry_2 - (JNIEnv *env, jobject obj, jstring attrNameObj, jobjectArray entryArrObj) +extern "C" +JNIEXPORT void JNICALL Java_com_mousebird_maply_AttrDictionary_setArray__Ljava_lang_String_2_3Lcom_mousebird_maply_AttrDictionaryEntry_2(JNIEnv *env, jobject obj, jstring attrNameObj, jobjectArray entryArrObj) { try { @@ -441,7 +440,7 @@ JNIEXPORT void JNICALL Java_com_mousebird_maply_AttrDictionary_setArray__Ljava_l entries.push_back(DictionaryEntry_AndroidRef(*entry)); } - (*dict)->setArray(attrName.cStr,entries); + (*dict)->setArray(attrName.getCString(),entries); } catch (...) { @@ -449,8 +448,8 @@ JNIEXPORT void JNICALL Java_com_mousebird_maply_AttrDictionary_setArray__Ljava_l } } -JNIEXPORT void JNICALL Java_com_mousebird_maply_AttrDictionary_setArray__Ljava_lang_String_2_3Lcom_mousebird_maply_AttrDictionary_2 - (JNIEnv *env, jobject obj, jstring attrNameObj, jobjectArray dictArrayObj) +extern "C" +JNIEXPORT void JNICALL Java_com_mousebird_maply_AttrDictionary_setArray__Ljava_lang_String_2_3Lcom_mousebird_maply_AttrDictionary_2(JNIEnv *env, jobject obj, jstring attrNameObj, jobjectArray dictArrayObj) { try { @@ -470,7 +469,7 @@ JNIEXPORT void JNICALL Java_com_mousebird_maply_AttrDictionary_setArray__Ljava_l entries.push_back(MutableDictionary_AndroidRef(*entry)); } - (*dict)->setArray(attrName.cStr,entries); + (*dict)->setArray(attrName.getCString(),entries); } catch (...) { @@ -478,15 +477,15 @@ JNIEXPORT void JNICALL Java_com_mousebird_maply_AttrDictionary_setArray__Ljava_l } } -JNIEXPORT jstring JNICALL Java_com_mousebird_maply_AttrDictionary_toString -(JNIEnv *env, jobject obj) +extern "C" +JNIEXPORT jstring JNICALL Java_com_mousebird_maply_AttrDictionary_toString(JNIEnv *env, jobject obj) { try { AttrDictClassInfo *classInfo = AttrDictClassInfo::getClassInfo(); MutableDictionary_AndroidRef *dict = classInfo->getObject(env,obj); if (!dict) - return NULL; + return nullptr; std::string str = (*dict)->toString(); return env->NewStringUTF(str.c_str()); @@ -496,28 +495,29 @@ JNIEXPORT jstring JNICALL Java_com_mousebird_maply_AttrDictionary_toString __android_log_print(ANDROID_LOG_VERBOSE, "Maply", "Crash in Dictionary::setDouble()"); } - return NULL; + return nullptr; } - -JNIEXPORT jobject JNICALL Java_com_mousebird_maply_AttrDictionary_get - (JNIEnv *env, jobject obj, jstring attrNameStr) +extern "C" +JNIEXPORT jobject JNICALL Java_com_mousebird_maply_AttrDictionary_get(JNIEnv *env, jobject obj, jstring attrNameStr) { try { AttrDictClassInfo *classInfo = AttrDictClassInfo::getClassInfo(); MutableDictionary_AndroidRef *dict = classInfo->getObject(env,obj); if (!dict) - return NULL; + { + return nullptr; + } JavaString attrName(env,attrNameStr); - if ((*dict)->hasField(attrName.cStr)) + if ((*dict)->hasField(attrName.getCString())) { - DictionaryType dictType = (*dict)->getType(attrName.cStr); + const DictionaryType dictType = (*dict)->getType(attrName.getCString()); if (dictType == DictTypeString) { - std::string str = (*dict)->getString(attrName.cStr); + const std::string str = (*dict)->getString(attrName.getCString()); if (!str.empty()) { return env->NewStringUTF(str.c_str()); @@ -525,40 +525,36 @@ JNIEXPORT jobject JNICALL Java_com_mousebird_maply_AttrDictionary_get } else if (dictType == DictTypeInt) { - int val = (*dict)->getInt(attrName.cStr); + const int val = (*dict)->getInt(attrName.getCString(), 0); return JavaIntegerClassInfo::getClassInfo(env)->makeInteger(env,val); } else if (dictType == DictTypeDouble) { - double val = (*dict)->getDouble(attrName.cStr); + const double val = (*dict)->getDouble(attrName.getCString(), 0); return JavaDoubleClassInfo::getClassInfo(env)->makeDouble(env,val); } - else - return NULL; - } - } catch (...) { __android_log_print(ANDROID_LOG_VERBOSE, "Maply", "Crash in Dictionary::get()"); } - return NULL; + return nullptr; } -JNIEXPORT void JNICALL Java_com_mousebird_maply_AttrDictionary_addEntries -(JNIEnv *env, jobject obj, jobject other) +extern "C" +JNIEXPORT void JNICALL Java_com_mousebird_maply_AttrDictionary_addEntries(JNIEnv *env, jobject obj, jobject other) { try { AttrDictClassInfo *classInfo = AttrDictClassInfo::getClassInfo(); MutableDictionary_AndroidRef *dict = classInfo->getObject(env,obj); MutableDictionary_AndroidRef *otherDict = classInfo->getObject(env,other); - if (!dict || !other) - return; - - (*dict)->addEntries(otherDict->get()); + if (dict && other) + { + (*dict)->addEntries(otherDict->get()); + } } catch (...) { diff --git a/android/library/maply/jni/src/vectors/LoftedPolyManager_jni.cpp b/android/library/maply/jni/src/vectors/LoftedPolyManager_jni.cpp index fc1481b6f0..4bcdf8ce69 100644 --- a/android/library/maply/jni/src/vectors/LoftedPolyManager_jni.cpp +++ b/android/library/maply/jni/src/vectors/LoftedPolyManager_jni.cpp @@ -26,7 +26,7 @@ using namespace WhirlyKit; using namespace Eigen; -typedef JavaClassInfo LoftManagerClassInfo; +typedef JavaClassInfo LoftManagerClassInfo; template<> LoftManagerClassInfo *LoftManagerClassInfo::classInfoObj = NULL; JNIEXPORT void JNICALL Java_com_mousebird_maply_LoftedPolyManager_nativeInit @@ -43,8 +43,8 @@ JNIEXPORT void JNICALL Java_com_mousebird_maply_LoftedPolyManager_initialise Scene *scene = SceneClassInfo::getClassInfo()->getObject(env, sceneObj); if (!scene) return; - LoftManager *loftManager = dynamic_cast(scene->getManager(kWKLoftedPolyManager)); - LoftManagerClassInfo::getClassInfo()->setHandle(env,obj,loftManager); + LoftManagerRef loftManager = std::dynamic_pointer_cast(scene->getManager(kWKLoftedPolyManager)); + LoftManagerClassInfo::getClassInfo()->setHandle(env,obj,new LoftManagerRef(loftManager)); } catch (...) { @@ -62,6 +62,9 @@ JNIEXPORT void JNICALL Java_com_mousebird_maply_LoftedPolyManager_dispose LoftManagerClassInfo *classInfo = LoftManagerClassInfo::getClassInfo(); { std::lock_guard lock(disposeMutex); + LoftManagerRef *loftManager = classInfo->getObject(env,obj); + if (loftManager) + delete loftManager; classInfo->clearHandle(env,obj); } } @@ -76,7 +79,7 @@ JNIEXPORT jlong JNICALL Java_com_mousebird_maply_LoftedPolyManager_addPolys { try { - LoftManager *loftManager = LoftManagerClassInfo::getClassInfo()->getObject(env,obj); + LoftManagerRef *loftManager = LoftManagerClassInfo::getClassInfo()->getObject(env,obj); LoftedPolyInfoRef *loftInfo = LoftedPolyInfoClassInfo::getClassInfo()->getObject(env,loftInfoObj); ChangeSetRef *changeSet = ChangeSetClassInfo::getClassInfo()->getObject(env,changeSetObj); if (!loftManager || !loftInfo || !changeSet) @@ -96,12 +99,12 @@ JNIEXPORT jlong JNICALL Java_com_mousebird_maply_LoftedPolyManager_addPolys if ((*loftInfo)->programID == EmptyIdentity) { ProgramGLES *prog = NULL; - prog = (ProgramGLES *)loftManager->getScene()->findProgramByName(MaplyDefaultTriangleShader); + prog = (ProgramGLES *)(*loftManager)->getScene()->findProgramByName(MaplyDefaultTriangleShader); if (prog) (*loftInfo)->programID = prog->getId(); } - SimpleIdentity loftID = loftManager->addLoftedPolys(&shapes,*(*loftInfo),*(changeSet->get())); + SimpleIdentity loftID = (*loftManager)->addLoftedPolys(&shapes,*(*loftInfo),*(changeSet->get())); return loftID; } diff --git a/android/library/maply/jni/src/vectors/VectorManager_jni.cpp b/android/library/maply/jni/src/vectors/VectorManager_jni.cpp index a60bd15fb0..0a87fa7026 100644 --- a/android/library/maply/jni/src/vectors/VectorManager_jni.cpp +++ b/android/library/maply/jni/src/vectors/VectorManager_jni.cpp @@ -26,7 +26,7 @@ using namespace WhirlyKit; using namespace Maply; -typedef JavaClassInfo VectorManagerClassInfo; +typedef JavaClassInfo VectorManagerClassInfo; template<> VectorManagerClassInfo *VectorManagerClassInfo::classInfoObj = NULL; JNIEXPORT void JNICALL Java_com_mousebird_maply_VectorManager_nativeInit @@ -43,8 +43,8 @@ JNIEXPORT void JNICALL Java_com_mousebird_maply_VectorManager_initialise Scene *scene = SceneClassInfo::getClassInfo()->getObject(env, sceneObj); if (!scene) return; - VectorManager *vecManager = dynamic_cast(scene->getManager(kWKVectorManager)); - VectorManagerClassInfo::getClassInfo()->setHandle(env,obj,vecManager); + VectorManagerRef vecManager = std::dynamic_pointer_cast(scene->getManager(kWKVectorManager)); + VectorManagerClassInfo::getClassInfo()->setHandle(env,obj,new VectorManagerRef(vecManager)); } catch (...) { @@ -62,6 +62,9 @@ JNIEXPORT void JNICALL Java_com_mousebird_maply_VectorManager_dispose VectorManagerClassInfo *classInfo = VectorManagerClassInfo::getClassInfo(); { std::lock_guard lock(disposeMutex); + VectorManagerRef *vecManager = VectorManagerClassInfo::getClassInfo()->getObject(env,obj); + if (vecManager) + delete vecManager; classInfo->clearHandle(env,obj); } } @@ -76,7 +79,7 @@ JNIEXPORT jlong JNICALL Java_com_mousebird_maply_VectorManager_addVectors { try { - VectorManager *vecManager = VectorManagerClassInfo::getClassInfo()->getObject(env,obj); + VectorManagerRef *vecManager = VectorManagerClassInfo::getClassInfo()->getObject(env,obj); VectorInfoRef *vecInfo = VectorInfoClassInfo::getClassInfo()->getObject(env,vecInfoObj); ChangeSetRef *changeSet = ChangeSetClassInfo::getClassInfo()->getObject(env,changeSetObj); if (!vecManager || !vecInfo || !changeSet) @@ -97,14 +100,14 @@ JNIEXPORT jlong JNICALL Java_com_mousebird_maply_VectorManager_addVectors { ProgramGLES *prog = NULL; if ((*vecInfo)->filled) - prog = (ProgramGLES *)vecManager->getScene()->findProgramByName(MaplyDefaultTriangleShader); + prog = (ProgramGLES *)(*vecManager)->getScene()->findProgramByName(MaplyDefaultTriangleShader); else - prog = (ProgramGLES *)vecManager->getScene()->findProgramByName(MaplyDefaultLineShader); + prog = (ProgramGLES *)(*vecManager)->getScene()->findProgramByName(MaplyDefaultLineShader); if (prog) (*vecInfo)->programID = prog->getId(); } - SimpleIdentity vecID = vecManager->addVectors(&shapes,*(*vecInfo),*(changeSet->get())); + SimpleIdentity vecID = (*vecManager)->addVectors(&shapes,*(*vecInfo),*(changeSet->get())); return vecID; } @@ -120,7 +123,7 @@ JNIEXPORT void JNICALL Java_com_mousebird_maply_VectorManager_changeVectors (JNIEnv *env, jobject obj, jlongArray idArrayObj, jobject vecInfoObj, jobject changeSetObj) { try { - VectorManager *vecManager = VectorManagerClassInfo::getClassInfo()->getObject(env,obj); + VectorManagerRef *vecManager = VectorManagerClassInfo::getClassInfo()->getObject(env,obj); VectorInfoRef *vecInfo = VectorInfoClassInfo::getClassInfo()->getObject(env,vecInfoObj); ChangeSetRef *changeSet = ChangeSetClassInfo::getClassInfo()->getObject(env,changeSetObj); if (!vecManager || !vecInfo || !changeSet) @@ -130,7 +133,7 @@ JNIEXPORT void JNICALL Java_com_mousebird_maply_VectorManager_changeVectors SimpleIDSet idSet; for (unsigned int ii=0;iichangeVectors(ids.rawLong[ii],*(*vecInfo),*(changeSet->get())); + (*vecManager)->changeVectors(ids.rawLong[ii],*(*vecInfo),*(changeSet->get())); } } catch (...) @@ -144,7 +147,7 @@ JNIEXPORT void JNICALL Java_com_mousebird_maply_VectorManager_removeVectors { try { - VectorManager *vecManager = VectorManagerClassInfo::getClassInfo()->getObject(env,obj); + VectorManagerRef *vecManager = VectorManagerClassInfo::getClassInfo()->getObject(env,obj); ChangeSetRef *changeSet = ChangeSetClassInfo::getClassInfo()->getObject(env,changeSetObj); if (!vecManager || !changeSet) return; @@ -152,7 +155,7 @@ JNIEXPORT void JNICALL Java_com_mousebird_maply_VectorManager_removeVectors SimpleIDSet idSet; ConvertLongArrayToSet(env,idArrayObj,idSet); - vecManager->removeVectors(idSet,*(changeSet->get())); + (*vecManager)->removeVectors(idSet,*(changeSet->get())); } catch (...) { @@ -165,7 +168,7 @@ JNIEXPORT void JNICALL Java_com_mousebird_maply_VectorManager_enableVectors { try { - VectorManager *vecManager = VectorManagerClassInfo::getClassInfo()->getObject(env,obj); + VectorManagerRef *vecManager = VectorManagerClassInfo::getClassInfo()->getObject(env,obj); ChangeSetRef *changeSet = ChangeSetClassInfo::getClassInfo()->getObject(env,changeSetObj); if (!vecManager || !changeSet) return; @@ -173,7 +176,7 @@ JNIEXPORT void JNICALL Java_com_mousebird_maply_VectorManager_enableVectors SimpleIDSet idSet; ConvertLongArrayToSet(env,idArrayObj,idSet); - vecManager->enableVectors(idSet,enable,*(changeSet->get())); + (*vecManager)->enableVectors(idSet,enable,*(changeSet->get())); } catch (...) { @@ -186,13 +189,13 @@ JNIEXPORT jlong JNICALL Java_com_mousebird_maply_VectorManager_instanceVectors { try { - VectorManager *vecManager = VectorManagerClassInfo::getClassInfo()->getObject(env,obj); + VectorManagerRef *vecManager = VectorManagerClassInfo::getClassInfo()->getObject(env,obj); VectorInfoRef *vecInfo = VectorInfoClassInfo::getClassInfo()->getObject(env,vecInfoObj); ChangeSetRef *changeSet = ChangeSetClassInfo::getClassInfo()->getObject(env,changeSetObj); if (!vecManager || !vecInfo || !changeSet) return EmptyIdentity; - return vecManager->instanceVectors(vecID,*(*vecInfo),*(changeSet->get())); + return (*vecManager)->instanceVectors(vecID,*(*vecInfo),*(changeSet->get())); } catch (...) { diff --git a/android/library/maply/jni/src/vectors/VectorObject_jni.cpp b/android/library/maply/jni/src/vectors/VectorObject_jni.cpp index 20f509eb26..021187958e 100644 --- a/android/library/maply/jni/src/vectors/VectorObject_jni.cpp +++ b/android/library/maply/jni/src/vectors/VectorObject_jni.cpp @@ -1,9 +1,8 @@ -/* - * VectorObject_jni.cpp +/* VectorObject_jni.cpp * WhirlyGlobeLib * * Created by Steve Gifford on 6/2/14. - * Copyright 2011-2016 mousebird consulting + * Copyright 2011-2021 mousebird consulting * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -15,7 +14,6 @@ * 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. - * */ #import "Vectors_jni.h" @@ -25,39 +23,37 @@ using namespace WhirlyKit; -template<> VectorObjectClassInfo *VectorObjectClassInfo::classInfoObj = NULL; +template<> VectorObjectClassInfo *VectorObjectClassInfo::classInfoObj = nullptr; -JNIEXPORT void JNICALL Java_com_mousebird_maply_VectorObject_nativeInit - (JNIEnv *env, jclass cls) +extern "C" +JNIEXPORT void JNICALL Java_com_mousebird_maply_VectorObject_nativeInit(JNIEnv *env, jclass cls) { VectorObjectClassInfo::getClassInfo(env,cls); } -JNIEXPORT jobject JNICALL MakeVectorObject(JNIEnv *env,VectorObjectRef vec) +JNIEXPORT jobject JNICALL MakeVectorObject(JNIEnv *env,const VectorObjectRef &vec) { VectorObjectClassInfo *classInfo = VectorObjectClassInfo::getClassInfo(env,"com/mousebird/maply/VectorObject"); return MakeVectorObjectWrapper(env,classInfo,vec); } -JNIEXPORT jobject JNICALL MakeVectorObjectWrapper(JNIEnv *env,VectorObjectClassInfo *classInfo,VectorObjectRef vec) +JNIEXPORT jobject JNICALL MakeVectorObjectWrapper(JNIEnv *env,VectorObjectClassInfo *classInfo,const VectorObjectRef &vec) { jobject newObj = classInfo->makeWrapperObject(env); VectorObjectRef *oldRef = classInfo->getObject(env,newObj); vec->setId((*oldRef)->getId()); classInfo->setHandle(env,newObj,new VectorObjectRef(vec)); - if (oldRef) - delete oldRef; - + delete oldRef; return newObj; } -void Java_com_mousebird_maply_VectorObject_initialise - (JNIEnv *env, jobject obj, jlong ident) +extern "C" +void Java_com_mousebird_maply_VectorObject_initialise(JNIEnv *env, jobject obj, jlong ident) { try { VectorObjectClassInfo *classInfo = VectorObjectClassInfo::getClassInfo(); - VectorObjectRef *inst = new VectorObjectRef(new VectorObject(ident)); + auto inst = new VectorObjectRef(new VectorObject(ident)); classInfo->setHandle(env,obj,inst); } catch (...) @@ -68,8 +64,8 @@ void Java_com_mousebird_maply_VectorObject_initialise static std::mutex disposeMutex; -void Java_com_mousebird_maply_VectorObject_dispose - (JNIEnv *env, jobject obj) +extern "C" +void Java_com_mousebird_maply_VectorObject_dispose(JNIEnv *env, jobject obj) { try { @@ -90,8 +86,8 @@ void Java_com_mousebird_maply_VectorObject_dispose } } -JNIEXPORT jint JNICALL Java_com_mousebird_maply_VectorObject_getVectorTypeNative -(JNIEnv *env, jobject obj) +extern "C" +JNIEXPORT jint JNICALL Java_com_mousebird_maply_VectorObject_getVectorTypeNative(JNIEnv *env, jobject obj) { try { @@ -108,8 +104,8 @@ JNIEXPORT jint JNICALL Java_com_mousebird_maply_VectorObject_getVectorTypeNative return VectorNoneType; } -JNIEXPORT void JNICALL Java_com_mousebird_maply_VectorObject_setSelectable - (JNIEnv *env, jobject obj, jboolean newVal) +extern "C" +JNIEXPORT void JNICALL Java_com_mousebird_maply_VectorObject_setSelectable(JNIEnv *env, jobject obj, jboolean newVal) { try { @@ -125,8 +121,8 @@ JNIEXPORT void JNICALL Java_com_mousebird_maply_VectorObject_setSelectable } } -JNIEXPORT jboolean JNICALL Java_com_mousebird_maply_VectorObject_getSelectable - (JNIEnv *env, jobject obj) +extern "C" +JNIEXPORT jboolean JNICALL Java_com_mousebird_maply_VectorObject_getSelectable(JNIEnv *env, jobject obj) { try { @@ -144,23 +140,25 @@ JNIEXPORT jboolean JNICALL Java_com_mousebird_maply_VectorObject_getSelectable return false; } -JNIEXPORT jobject JNICALL Java_com_mousebird_maply_VectorObject_getAttributes - (JNIEnv *env, jobject obj) +extern "C" +JNIEXPORT jobject JNICALL Java_com_mousebird_maply_VectorObject_getAttributes(JNIEnv *env, jobject obj) { try { VectorObjectClassInfo *classInfo = VectorObjectClassInfo::getClassInfo(); VectorObjectRef *vecObj = classInfo->getObject(env,obj); if (!vecObj) - return NULL; + return nullptr; MutableDictionary_AndroidRef dict = std::dynamic_pointer_cast((*vecObj)->getAttributes()); // Have to convert to our sort of attributes if (!dict) { + if (!(*vecObj)->getAttributes()) + return nullptr; dict = std::make_shared(*((*vecObj)->getAttributes().get())); } if (!dict) - return NULL; + return nullptr; jobject dictObj = MakeAttrDictionary(env,dict); return dictObj; } @@ -169,11 +167,11 @@ JNIEXPORT jobject JNICALL Java_com_mousebird_maply_VectorObject_getAttributes __android_log_print(ANDROID_LOG_VERBOSE, "Maply", "Crash in VectorObject::getAttributes()"); } - return NULL; + return nullptr; } -JNIEXPORT void JNICALL Java_com_mousebird_maply_VectorObject_setAttributes - (JNIEnv *env, jobject obj, jobject attrObj) +extern "C" +JNIEXPORT void JNICALL Java_com_mousebird_maply_VectorObject_setAttributes(JNIEnv *env, jobject obj, jobject attrObj) { try { @@ -190,8 +188,8 @@ JNIEXPORT void JNICALL Java_com_mousebird_maply_VectorObject_setAttributes } } -JNIEXPORT void JNICALL Java_com_mousebird_maply_VectorObject_addPoint - (JNIEnv *env, jobject obj, jobject ptObj) +extern "C" +JNIEXPORT void JNICALL Java_com_mousebird_maply_VectorObject_addPoint(JNIEnv *env, jobject obj, jobject ptObj) { try { @@ -211,8 +209,8 @@ JNIEXPORT void JNICALL Java_com_mousebird_maply_VectorObject_addPoint } } -JNIEXPORT void JNICALL Java_com_mousebird_maply_VectorObject_addLinear - (JNIEnv *env, jobject obj, jobjectArray ptsObj) +extern "C" +JNIEXPORT void JNICALL Java_com_mousebird_maply_VectorObject_addLinear(JNIEnv *env, jobject obj, jobjectArray ptsObj) { try { @@ -235,8 +233,8 @@ JNIEXPORT void JNICALL Java_com_mousebird_maply_VectorObject_addLinear } } -JNIEXPORT void JNICALL Java_com_mousebird_maply_VectorObject_addAreal___3Lcom_mousebird_maply_Point2d_2 - (JNIEnv *env, jobject obj, jobjectArray ptsObj) +extern "C" +JNIEXPORT void JNICALL Java_com_mousebird_maply_VectorObject_addAreal___3Lcom_mousebird_maply_Point2d_2(JNIEnv *env, jobject obj, jobjectArray ptsObj) { try { @@ -261,8 +259,9 @@ JNIEXPORT void JNICALL Java_com_mousebird_maply_VectorObject_addAreal___3Lcom_mo } } +extern "C" JNIEXPORT void JNICALL Java_com_mousebird_maply_VectorObject_addAreal___3Lcom_mousebird_maply_Point2d_2_3_3Lcom_mousebird_maply_Point2d_2 -(JNIEnv *env, jobject obj, jobjectArray outerLoopObj, jobjectArray holesArray) + (JNIEnv *env, jobject obj, jobjectArray outerLoopObj, jobjectArray holesArray) { try { @@ -297,8 +296,8 @@ JNIEXPORT void JNICALL Java_com_mousebird_maply_VectorObject_addAreal___3Lcom_mo } } -JNIEXPORT void JNICALL Java_com_mousebird_maply_VectorObject_mergeVectorsFrom - (JNIEnv *env, jobject obj, jobject otherObj) +extern "C" +JNIEXPORT void JNICALL Java_com_mousebird_maply_VectorObject_mergeVectorsFrom(JNIEnv *env, jobject obj, jobject otherObj) { try { @@ -316,56 +315,57 @@ JNIEXPORT void JNICALL Java_com_mousebird_maply_VectorObject_mergeVectorsFrom } } -JNIEXPORT jobject JNICALL Java_com_mousebird_maply_VectorObject_center - (JNIEnv *env, jobject obj) +extern "C" +JNIEXPORT jobject JNICALL Java_com_mousebird_maply_VectorObject_center(JNIEnv *env, jobject obj) { try { VectorObjectRef *vecObj = VectorObjectClassInfo::getClassInfo()->getObject(env,obj); if (!vecObj) - return NULL; + return nullptr; Point2d center; if ((*vecObj)->center(center)) { return MakePoint2d(env,center); } else - return NULL; + return nullptr; } catch (...) { __android_log_print(ANDROID_LOG_VERBOSE, "Maply", "Crash in VectorObject::center()"); } - return NULL; + return nullptr; } -JNIEXPORT jobject JNICALL Java_com_mousebird_maply_VectorObject_centroid - (JNIEnv *env, jobject obj) +extern "C" +JNIEXPORT jobject JNICALL Java_com_mousebird_maply_VectorObject_centroid(JNIEnv *env, jobject obj) { try { VectorObjectRef *vecObj = VectorObjectClassInfo::getClassInfo()->getObject(env,obj); if (!vecObj) - return NULL; + return nullptr; Point2d center; if ((*vecObj)->centroid(center)) { return MakePoint2d(env,center); } else - return NULL; + return nullptr; } catch (...) { __android_log_print(ANDROID_LOG_VERBOSE, "Maply", "Crash in VectorObject::centroid()"); } - return NULL; + return nullptr; } +extern "C" JNIEXPORT jobject JNICALL Java_com_mousebird_maply_VectorObject_largestLoopCenter - (JNIEnv *env, jobject obj, jobject llObj, jobject urObj) + (JNIEnv *env, jobject obj, jobject llObj, jobject urObj) { try { @@ -374,25 +374,25 @@ JNIEXPORT jobject JNICALL Java_com_mousebird_maply_VectorObject_largestLoopCente Point2d *ll = pt2dClassInfo->getObject(env,llObj); Point2d *ur = pt2dClassInfo->getObject(env,urObj); if (!vecObj || !ll || !ur) - return NULL; + return nullptr; Point2d center; if ((*vecObj)->largestLoopCenter(center,*ll,*ur)) { return MakePoint2d(env,center); } else - return NULL; + return nullptr; } catch (...) { __android_log_print(ANDROID_LOG_VERBOSE, "Maply", "Crash in VectorObject::largestLoopCenter()"); } - return NULL; + return nullptr; } -JNIEXPORT jdouble JNICALL Java_com_mousebird_maply_VectorObject_linearMiddle__Lcom_mousebird_maply_Point2d_2 - (JNIEnv *env, jobject obj, jobject midObj) +extern "C" +JNIEXPORT jdouble JNICALL Java_com_mousebird_maply_VectorObject_linearMiddle__Lcom_mousebird_maply_Point2d_2(JNIEnv *env, jobject obj, jobject midObj) { try { @@ -422,6 +422,7 @@ JNIEXPORT jdouble JNICALL Java_com_mousebird_maply_VectorObject_linearMiddle__Lc * Method: linearMiddle * Signature: (Lcom/mousebird/maply/Point2d;Lcom/mousebird/maply/CoordSystem;)D */ +extern "C" JNIEXPORT jdouble JNICALL Java_com_mousebird_maply_VectorObject_linearMiddle__Lcom_mousebird_maply_Point2d_2Lcom_mousebird_maply_CoordSystem_2 (JNIEnv *env, jobject obj, jobject midObj, jobject coordSysObj) { @@ -449,22 +450,18 @@ JNIEXPORT jdouble JNICALL Java_com_mousebird_maply_VectorObject_linearMiddle__Lc return 0.0; } -JNIEXPORT jboolean JNICALL Java_com_mousebird_maply_VectorObject_middleCoordinate - (JNIEnv *env, jobject obj, jobject midObj) +extern "C" +JNIEXPORT jboolean JNICALL Java_com_mousebird_maply_VectorObject_middleCoordinate(JNIEnv *env, jobject obj, jobject midObj) { try { - Point2dClassInfo *pt2dClassInfo = Point2dClassInfo::getClassInfo(); - VectorObjectRef *vecObj = VectorObjectClassInfo::getClassInfo()->getObject(env,obj); - Point2d *mid = pt2dClassInfo->getObject(env,midObj); - if (!vecObj || !mid) - return false; - - if ((*vecObj)->middleCoordinate(*mid)) - { - return true; - } else - return false; + if (const auto vecObj = VectorObjectClassInfo::get(env,obj)) + { + if (const auto mid = Point2dClassInfo::get(env,midObj)) + { + return (*vecObj)->middleCoordinate(*mid); + } + } } catch (...) { @@ -474,8 +471,8 @@ JNIEXPORT jboolean JNICALL Java_com_mousebird_maply_VectorObject_middleCoordinat return false; } -JNIEXPORT jboolean JNICALL Java_com_mousebird_maply_VectorObject_pointInside -(JNIEnv *env, jobject obj, jobject ptObj) +extern "C" +JNIEXPORT jboolean JNICALL Java_com_mousebird_maply_VectorObject_pointInside(JNIEnv *env, jobject obj, jobject ptObj) { try { @@ -495,8 +492,8 @@ JNIEXPORT jboolean JNICALL Java_com_mousebird_maply_VectorObject_pointInside return false; } -JNIEXPORT jdouble JNICALL Java_com_mousebird_maply_VectorObject_areaOfOuterLoops - (JNIEnv *env, jobject obj) +extern "C" +JNIEXPORT jdouble JNICALL Java_com_mousebird_maply_VectorObject_areaOfOuterLoops(JNIEnv *env, jobject obj) { try { @@ -514,38 +511,29 @@ JNIEXPORT jdouble JNICALL Java_com_mousebird_maply_VectorObject_areaOfOuterLoops return 0.0; } -JNIEXPORT jint JNICALL Java_com_mousebird_maply_VectorObject_countPoints -(JNIEnv *env, jobject obj) +extern "C" +JNIEXPORT jint JNICALL Java_com_mousebird_maply_VectorObject_countPoints(JNIEnv *env, jobject obj) { try { - Point2dClassInfo *pt2dClassInfo = Point2dClassInfo::getClassInfo(); + //Point2dClassInfo *pt2dClassInfo = Point2dClassInfo::getClassInfo(); VectorObjectRef *vecObj = VectorObjectClassInfo::getClassInfo()->getObject(env,obj); if (!vecObj) return 0; int numPts = 0; - for (auto shape : (*vecObj)->shapes) + for (const auto &shape : (*vecObj)->shapes) { - if(std::dynamic_pointer_cast(shape) != NULL) + if (const auto linear = dynamic_cast(shape.get())) { - VectorLinearRef linear = std::dynamic_pointer_cast(shape); numPts += linear->pts.size(); - } else if(std::dynamic_pointer_cast(shape) != NULL) - { - } else if(std::dynamic_pointer_cast(shape) != NULL) - { - VectorArealRef ar = std::dynamic_pointer_cast(shape); - if (ar) + //} else if(const auto vec3d = dynamic_cast(shape.get())) { + } else if(const auto ar = dynamic_cast(shape.get())) { + for (auto & loop : ar->loops) { - for (int ii=0;iiloops.size();ii++) - { - numPts += ar->loops[ii].size(); - } + numPts += loop.size(); } - } else if(std::dynamic_pointer_cast(shape) != NULL) - { - VectorPointsRef points = std::dynamic_pointer_cast(shape); + } else if (const auto points = dynamic_cast(shape.get())) { numPts += points->pts.size(); } } @@ -560,8 +548,8 @@ JNIEXPORT jint JNICALL Java_com_mousebird_maply_VectorObject_countPoints return false; } -JNIEXPORT jboolean JNICALL Java_com_mousebird_maply_VectorObject_boundingBox - (JNIEnv *env, jobject obj, jobject llObj, jobject urObj) +extern "C" +JNIEXPORT jboolean JNICALL Java_com_mousebird_maply_VectorObject_boundingBox(JNIEnv *env, jobject obj, jobject llObj, jobject urObj) { try { @@ -582,8 +570,9 @@ JNIEXPORT jboolean JNICALL Java_com_mousebird_maply_VectorObject_boundingBox return 0.0; } +extern "C" JNIEXPORT jboolean JNICALL Java_com_mousebird_maply_VectorObject_subdivideToGlobeNative -(JNIEnv *env, jobject obj, jobject retObj, jdouble epsilon) + (JNIEnv *env, jobject obj, jobject retObj, jdouble epsilon) { try { @@ -606,8 +595,9 @@ JNIEXPORT jboolean JNICALL Java_com_mousebird_maply_VectorObject_subdivideToGlob return false; } +extern "C" JNIEXPORT jboolean JNICALL Java_com_mousebird_maply_VectorObject_subdivideToGlobeGreatCircleNative -(JNIEnv *env, jobject obj, jobject retObj, jdouble epsilon) + (JNIEnv *env, jobject obj, jobject retObj, jdouble epsilon) { try { @@ -630,8 +620,9 @@ JNIEXPORT jboolean JNICALL Java_com_mousebird_maply_VectorObject_subdivideToGlob return false; } +extern "C" JNIEXPORT jboolean JNICALL Java_com_mousebird_maply_VectorObject_subdivideToFlatGreatCircleNative -(JNIEnv *env, jobject obj, jobject retObj, jdouble epsilon) + (JNIEnv *env, jobject obj, jobject retObj, jdouble epsilon) { try { @@ -654,8 +645,9 @@ JNIEXPORT jboolean JNICALL Java_com_mousebird_maply_VectorObject_subdivideToFlat return false; } +extern "C" JNIEXPORT jboolean JNICALL Java_com_mousebird_maply_VectorObject_tesselateNative -(JNIEnv *env, jobject obj, jobject retObj) + (JNIEnv *env, jobject obj, jobject retObj) { try { @@ -678,8 +670,9 @@ JNIEXPORT jboolean JNICALL Java_com_mousebird_maply_VectorObject_tesselateNative return false; } +extern "C" JNIEXPORT jboolean JNICALL Java_com_mousebird_maply_VectorObject_clipToGridNative -(JNIEnv *env, jobject obj, jobject retObj, jdouble sizeX, jdouble sizeY) + (JNIEnv *env, jobject obj, jobject retObj, jdouble sizeX, jdouble sizeY) { try { @@ -702,8 +695,9 @@ JNIEXPORT jboolean JNICALL Java_com_mousebird_maply_VectorObject_clipToGridNativ return false; } +extern "C" JNIEXPORT jboolean JNICALL Java_com_mousebird_maply_VectorObject_clipToMbrNative -(JNIEnv *env, jobject obj, jobject retObj, jdouble llX, jdouble llY, jdouble urX, jdouble urY) + (JNIEnv *env, jobject obj, jobject retObj, jdouble llX, jdouble llY, jdouble urX, jdouble urY) { try { @@ -727,8 +721,9 @@ JNIEXPORT jboolean JNICALL Java_com_mousebird_maply_VectorObject_clipToMbrNative return false; } +extern "C" JNIEXPORT jboolean JNICALL Java_com_mousebird_maply_VectorObject_reprojectNative - (JNIEnv *env, jobject obj, jobject retObj, jobject srcSystemObj, jdouble scale, jobject destSystemObj) + (JNIEnv *env, jobject obj, jobject retObj, jobject srcSystemObj, jdouble scale, jobject destSystemObj) { try { @@ -755,8 +750,9 @@ JNIEXPORT jboolean JNICALL Java_com_mousebird_maply_VectorObject_reprojectNative return false; } +extern "C" JNIEXPORT jboolean JNICALL Java_com_mousebird_maply_VectorObject_filterClippedEdgesNative - (JNIEnv *env, jobject obj, jobject retObj) + (JNIEnv *env, jobject obj, jobject retObj) { try { @@ -779,8 +775,9 @@ JNIEXPORT jboolean JNICALL Java_com_mousebird_maply_VectorObject_filterClippedEd return false; } +extern "C" JNIEXPORT jboolean JNICALL Java_com_mousebird_maply_VectorObject_linearsToArealsNative - (JNIEnv *env, jobject obj, jobject retObj) + (JNIEnv *env, jobject obj, jobject retObj) { try { @@ -803,8 +800,9 @@ JNIEXPORT jboolean JNICALL Java_com_mousebird_maply_VectorObject_linearsToAreals return false; } +extern "C" JNIEXPORT jboolean JNICALL Java_com_mousebird_maply_VectorObject_arealsToLinearsNative - (JNIEnv *env, jobject obj, jobject retObj) + (JNIEnv *env, jobject obj, jobject retObj) { try { @@ -827,8 +825,8 @@ JNIEXPORT jboolean JNICALL Java_com_mousebird_maply_VectorObject_arealsToLinears return false; } -jboolean Java_com_mousebird_maply_VectorObject_fromGeoJSON - (JNIEnv *env, jobject obj, jstring jstr) +extern "C" +jboolean Java_com_mousebird_maply_VectorObject_fromGeoJSON(JNIEnv *env, jobject obj, jstring jstr) { try { @@ -837,16 +835,14 @@ jboolean Java_com_mousebird_maply_VectorObject_fromGeoJSON if (!vecObj) return false; - const char *cStr = env->GetStringUTFChars(jstr,0); + const char *cStr = env->GetStringUTFChars(jstr,nullptr); if (!cStr) return false; std::string jsonStr(cStr); env->ReleaseStringUTFChars(jstr, cStr); std::string crs; - bool ret = (*vecObj)->fromGeoJSON(jsonStr,crs); - - return ret; + return (*vecObj)->fromGeoJSON(jsonStr,crs); } catch (...) { @@ -856,8 +852,8 @@ jboolean Java_com_mousebird_maply_VectorObject_fromGeoJSON return false; } -JNIEXPORT jboolean JNICALL Java_com_mousebird_maply_VectorObject_fromShapeFile -(JNIEnv *env, jobject obj, jstring jstr) +extern "C" +JNIEXPORT jboolean JNICALL Java_com_mousebird_maply_VectorObject_fromShapeFile(JNIEnv *env, jobject obj, jstring jstr) { try { @@ -866,15 +862,13 @@ JNIEXPORT jboolean JNICALL Java_com_mousebird_maply_VectorObject_fromShapeFile if (!vecObj) return false; - const char *cStr = env->GetStringUTFChars(jstr,0); + const char *cStr = env->GetStringUTFChars(jstr,nullptr); if (!cStr) return false; std::string jsonStr(cStr); env->ReleaseStringUTFChars(jstr, cStr); - bool ret = (*vecObj)->fromShapeFile(jsonStr); - - return true; + return (*vecObj)->fromShapeFile(jsonStr); } catch (...) { @@ -884,6 +878,26 @@ JNIEXPORT jboolean JNICALL Java_com_mousebird_maply_VectorObject_fromShapeFile return false; } +/* + * Class: com_mousebird_maply_VectorObject + * Method: canSplit + * Signature: ()Z + */ +extern "C" +JNIEXPORT jboolean JNICALL Java_com_mousebird_maply_VectorObject_canSplit(JNIEnv *env, jobject obj) { + try { + if (auto vecObjPtr = VectorObjectClassInfo::getClassInfo()->getObject(env,obj)) { + return ((*vecObjPtr)->shapes.size() > 1); + } + } + catch (...) + { + __android_log_print(ANDROID_LOG_VERBOSE, "Maply", "Crash in VectorObject::canSplit()"); + } + return false; +} + +extern "C" JNIEXPORT void JNICALL Java_com_mousebird_maply_VectorObject_deepCopyNative (JNIEnv *env, jobject obj, jobject destObj) { @@ -904,29 +918,26 @@ JNIEXPORT void JNICALL Java_com_mousebird_maply_VectorObject_deepCopyNative } } -JNIEXPORT jobject JNICALL Java_com_mousebird_maply_VectorObject_FromGeoJSONAssembly - (JNIEnv *env, jclass vecObjClass, jstring jstr) +extern "C" +JNIEXPORT jobject JNICALL Java_com_mousebird_maply_VectorObject_FromGeoJSONAssembly(JNIEnv *env, jclass /*vecObjClass*/, jstring jstr) { try { - const char *cStr = env->GetStringUTFChars(jstr,0); + const char *cStr = env->GetStringUTFChars(jstr,nullptr); if (!cStr) - return NULL; + return nullptr; std::string jsonStr(cStr); env->ReleaseStringUTFChars(jstr, cStr); std::map vecData; - bool ret = VectorObject::FromGeoJSONAssembly(jsonStr,vecData); - - if (ret) + if (VectorObject::FromGeoJSONAssembly(jsonStr,vecData)) { JavaHashMapInfo *hashMapClassInfo = JavaHashMapInfo::getClassInfo(env); jobject hashMap = hashMapClassInfo->makeHashMap(env); - for (std::map::iterator it = vecData.begin(); - it != vecData.end(); ++it) + for (const auto &kvp : vecData) { - jstring key = env->NewStringUTF(it->first.c_str()); - jobject vecObj = MakeVectorObject(env,VectorObjectRef(it->second)); + jstring key = env->NewStringUTF(kvp.first.c_str()); + jobject vecObj = MakeVectorObject(env,VectorObjectRef(kvp.second)); hashMapClassInfo->addObject(env, hashMap, key, vecObj); } @@ -938,7 +949,7 @@ JNIEXPORT jobject JNICALL Java_com_mousebird_maply_VectorObject_FromGeoJSONAssem __android_log_print(ANDROID_LOG_VERBOSE, "Maply", "Crash in VectorObject::FromGeoJSONAssembly()"); } - return NULL; + return nullptr; } diff --git a/android/library/maply/jni/src/vectors/WideVectorInfo_jni.cpp b/android/library/maply/jni/src/vectors/WideVectorInfo_jni.cpp index 2d0fa73bf2..12d369b273 100644 --- a/android/library/maply/jni/src/vectors/WideVectorInfo_jni.cpp +++ b/android/library/maply/jni/src/vectors/WideVectorInfo_jni.cpp @@ -1,9 +1,8 @@ -/* - * WideVectorInfo_jni.cpp +/* WideVectorInfo_jni.cpp * WhirlyGlobeLib * * Created by Steve Gifford on 3/8/17. - * Copyright 2011-2017 mousebird consulting + * Copyright 2011-2021 mousebird consulting * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -15,23 +14,25 @@ * 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. - * */ +#include #import "Vectors_jni.h" #import "com_mousebird_maply_WideVectorInfo.h" using namespace Eigen; using namespace WhirlyKit; -template<> WideVectorInfoClassInfo *WideVectorInfoClassInfo::classInfoObj = NULL; +template<> WideVectorInfoClassInfo *WideVectorInfoClassInfo::classInfoObj = nullptr; +extern "C" JNIEXPORT void JNICALL Java_com_mousebird_maply_WideVectorInfo_nativeInit (JNIEnv *env, jclass cls) { WideVectorInfoClassInfo::getClassInfo(env,cls); } +extern "C" JNIEXPORT void JNICALL Java_com_mousebird_maply_WideVectorInfo_initialise (JNIEnv *env, jobject obj) { @@ -48,6 +49,7 @@ JNIEXPORT void JNICALL Java_com_mousebird_maply_WideVectorInfo_initialise static std::mutex disposeMutex; +extern "C" JNIEXPORT void JNICALL Java_com_mousebird_maply_WideVectorInfo_dispose (JNIEnv *env, jobject obj) { @@ -70,6 +72,7 @@ JNIEXPORT void JNICALL Java_com_mousebird_maply_WideVectorInfo_dispose } } +extern "C" JNIEXPORT void JNICALL Java_com_mousebird_maply_WideVectorInfo_setColor (JNIEnv *env, jobject obj, jfloat r, jfloat g, jfloat b, jfloat a) { @@ -87,6 +90,7 @@ JNIEXPORT void JNICALL Java_com_mousebird_maply_WideVectorInfo_setColor } } +extern "C" JNIEXPORT void JNICALL Java_com_mousebird_maply_WideVectorInfo_setLineWidth (JNIEnv *env, jobject obj, jfloat val) { @@ -104,6 +108,7 @@ JNIEXPORT void JNICALL Java_com_mousebird_maply_WideVectorInfo_setLineWidth } } +extern "C" JNIEXPORT void JNICALL Java_com_mousebird_maply_WideVectorInfo_setTextureRepeatLength (JNIEnv *env, jobject obj, jdouble repeatLen) { @@ -121,6 +126,7 @@ JNIEXPORT void JNICALL Java_com_mousebird_maply_WideVectorInfo_setTextureRepeatL } } +extern "C" JNIEXPORT void JNICALL Java_com_mousebird_maply_WideVectorInfo_setEdgeFalloff (JNIEnv *env, jobject obj, jdouble edgeFalloff) { @@ -138,6 +144,7 @@ JNIEXPORT void JNICALL Java_com_mousebird_maply_WideVectorInfo_setEdgeFalloff } } +extern "C" JNIEXPORT void JNICALL Java_com_mousebird_maply_WideVectorInfo_setJoinTypeNative (JNIEnv *env, jobject obj, jint joinType) { @@ -155,6 +162,7 @@ JNIEXPORT void JNICALL Java_com_mousebird_maply_WideVectorInfo_setJoinTypeNative } } +extern "C" JNIEXPORT void JNICALL Java_com_mousebird_maply_WideVectorInfo_setMitreLimit (JNIEnv *env, jobject obj, jdouble mitreLimit) { @@ -172,6 +180,7 @@ JNIEXPORT void JNICALL Java_com_mousebird_maply_WideVectorInfo_setMitreLimit } } +extern "C" JNIEXPORT void JNICALL Java_com_mousebird_maply_WideVectorInfo_setTexID (JNIEnv *env, jobject obj, jlong texID) { diff --git a/android/library/maply/jni/src/vectors/WideVectorManager_jni.cpp b/android/library/maply/jni/src/vectors/WideVectorManager_jni.cpp index 598f198573..caf016f28c 100644 --- a/android/library/maply/jni/src/vectors/WideVectorManager_jni.cpp +++ b/android/library/maply/jni/src/vectors/WideVectorManager_jni.cpp @@ -27,7 +27,7 @@ using namespace Eigen; using namespace WhirlyKit; using namespace Maply; -typedef JavaClassInfo WideVectorManagerClassInfo; +typedef JavaClassInfo WideVectorManagerClassInfo; template<> WideVectorManagerClassInfo *WideVectorManagerClassInfo::classInfoObj = NULL; JNIEXPORT void JNICALL Java_com_mousebird_maply_WideVectorManager_nativeInit @@ -44,8 +44,8 @@ JNIEXPORT void JNICALL Java_com_mousebird_maply_WideVectorManager_initialise Scene *scene = SceneClassInfo::getClassInfo()->getObject(env, sceneObj); if (!scene) return; - WideVectorManager *vecManager = dynamic_cast(scene->getManager(kWKWideVectorManager)); - WideVectorManagerClassInfo::getClassInfo()->setHandle(env,obj,vecManager); + WideVectorManagerRef vecManager = std::dynamic_pointer_cast(scene->getManager(kWKWideVectorManager)); + WideVectorManagerClassInfo::getClassInfo()->setHandle(env,obj,new WideVectorManagerRef(vecManager)); } catch (...) { @@ -63,7 +63,7 @@ JNIEXPORT void JNICALL Java_com_mousebird_maply_WideVectorManager_dispose WideVectorManagerClassInfo *classInfo = WideVectorManagerClassInfo::getClassInfo(); { std::lock_guard lock(disposeMutex); - WideVectorManager *vecManage = classInfo->getObject(env,obj); + WideVectorManagerRef *vecManage = classInfo->getObject(env,obj); if (!vecManage) return; classInfo->clearHandle(env,obj); @@ -81,7 +81,7 @@ JNIEXPORT jlong JNICALL Java_com_mousebird_maply_WideVectorManager_addVectors { try { - WideVectorManager *vecManager = WideVectorManagerClassInfo::getClassInfo()->getObject(env,obj); + WideVectorManagerRef *vecManager = WideVectorManagerClassInfo::getClassInfo()->getObject(env,obj); WideVectorInfoRef *vecInfo = WideVectorInfoClassInfo::getClassInfo()->getObject(env,vecInfoObj); ChangeSetRef *changeSet = ChangeSetClassInfo::getClassInfo()->getObject(env,changeSetObj); if (!vecManager || !vecInfo || !changeSet) @@ -89,23 +89,23 @@ JNIEXPORT jlong JNICALL Java_com_mousebird_maply_WideVectorManager_addVectors // Collect up all the shapes to add at once VectorObjectClassInfo *vecObjClassInfo = VectorObjectClassInfo::getClassInfo(); - ShapeSet shapes; - JavaObjectArrayHelper vecHelper(env,vecObjArray); + std::vector shapes; + JavaObjectArrayHelper vecHelper(env,vecObjArray); while (jobject vecObjObj = vecHelper.getNextObject()) { VectorObjectRef *vecObj = vecObjClassInfo->getObject(env,vecObjObj); if (vecObj) - shapes.insert((*vecObj)->shapes.begin(),(*vecObj)->shapes.end()); + shapes.insert(shapes.end(),(*vecObj)->shapes.begin(),(*vecObj)->shapes.end()); } // Resolve a missing program if ((*vecInfo)->programID == EmptyIdentity) { - ProgramGLES *prog = (ProgramGLES *)vecManager->getScene()->findProgramByName(MaplyDefaultWideVectorShader); + ProgramGLES *prog = (ProgramGLES *)(*vecManager)->getScene()->findProgramByName(MaplyDefaultWideVectorShader); if (prog) (*vecInfo)->programID = prog->getId(); } - SimpleIdentity vecID = vecManager->addVectors(shapes,*(*vecInfo),*(changeSet->get())); + SimpleIdentity vecID = (*vecManager)->addVectors(shapes,*(*vecInfo),*(changeSet->get())); return vecID; } @@ -122,7 +122,7 @@ JNIEXPORT void JNICALL Java_com_mousebird_maply_WideVectorManager_removeVectors { try { - WideVectorManager *vecManager = WideVectorManagerClassInfo::getClassInfo()->getObject(env,obj); + WideVectorManagerRef *vecManager = WideVectorManagerClassInfo::getClassInfo()->getObject(env,obj); ChangeSetRef *changeSet = ChangeSetClassInfo::getClassInfo()->getObject(env,changeSetObj); if (!vecManager || !changeSet) return; @@ -130,7 +130,7 @@ JNIEXPORT void JNICALL Java_com_mousebird_maply_WideVectorManager_removeVectors SimpleIDSet idSet; ConvertLongArrayToSet(env,idArrayObj,idSet); - vecManager->removeVectors(idSet,*(changeSet->get())); + (*vecManager)->removeVectors(idSet,*(changeSet->get())); } catch (...) { @@ -143,7 +143,7 @@ JNIEXPORT void JNICALL Java_com_mousebird_maply_WideVectorManager_enableVectors { try { - WideVectorManager *vecManager = WideVectorManagerClassInfo::getClassInfo()->getObject(env,obj); + WideVectorManagerRef *vecManager = WideVectorManagerClassInfo::getClassInfo()->getObject(env,obj); ChangeSetRef *changeSet = ChangeSetClassInfo::getClassInfo()->getObject(env,changeSetObj); if (!vecManager || !changeSet) return; @@ -151,7 +151,7 @@ JNIEXPORT void JNICALL Java_com_mousebird_maply_WideVectorManager_enableVectors SimpleIDSet idSet; ConvertLongArrayToSet(env,idArrayObj,idSet); - vecManager->enableVectors(idSet,enable,*(changeSet->get())); + (*vecManager)->enableVectors(idSet,enable,*(changeSet->get())); } catch (...) { @@ -164,13 +164,13 @@ JNIEXPORT jlong JNICALL Java_com_mousebird_maply_WideVectorManager_instanceVecto { try { - WideVectorManager *vecManager = WideVectorManagerClassInfo::getClassInfo()->getObject(env,obj); + WideVectorManagerRef *vecManager = WideVectorManagerClassInfo::getClassInfo()->getObject(env,obj); WideVectorInfoRef *vecInfo = WideVectorInfoClassInfo::getClassInfo()->getObject(env,vecInfoObj); ChangeSetRef *changeSet = ChangeSetClassInfo::getClassInfo()->getObject(env,changeSetObj); if (!vecManager || !vecInfo || !changeSet) return EmptyIdentity; - return vecManager->instanceVectors(vecID,*(*vecInfo),*(changeSet->get())); + return (*vecManager)->instanceVectors(vecID,*(*vecInfo),*(changeSet->get())); } catch (...) { diff --git a/android/library/maply/src/main/java/com/mousebird/maply/BaseController.java b/android/library/maply/src/main/java/com/mousebird/maply/BaseController.java index 6b6a91cb69..177aeb0742 100644 --- a/android/library/maply/src/main/java/com/mousebird/maply/BaseController.java +++ b/android/library/maply/src/main/java/com/mousebird/maply/BaseController.java @@ -102,7 +102,7 @@ public Scene getScene() /** * Return the current coordinate system. */ - public CoordSystem getCoordSystem() { return coordAdapter.coordSys; } + public CoordSystem getCoordSystem() { return (coordAdapter != null) ? coordAdapter.coordSys : null; } public void takeScreenshot(ScreenshotListener listener) { @@ -237,7 +237,7 @@ public static class Settings * at startup. These are fully capable of adding geometry to the * system on their own (via ThreadCurrent). */ - public int numWorkingThreads = 8; + public int numWorkingThreads = 16; /** * If set we'll override the width of the rendering surface. * @@ -563,14 +563,28 @@ public void removeLayerThread(LayerThread layerThread) @details This converts from a coordinate (3d) in the given coordinate system to the view controller's display space. For the globe, display space is based on a radius of 1.0. */ - public Point3d displayCoord (Point3d localCoord, CoordSystem fromSystem){ - + public Point3d displayCoord (Point3d localCoord, CoordSystem fromSystem) + { Point3d loc3d = CoordSystem.CoordSystemConvert3d(fromSystem, coordAdapter.getCoordSystem(), localCoord); Point3d pt = coordAdapter.localToDisplay(loc3d); return pt; } - + + /** + * Return a point in display space. Display space is close to what's rendered. + * For the globe it's a model space based on a radius of 1.0. + */ + public Point3d displayPointFromGeo(Point3d geoPt) + { + CoordSystemDisplayAdapter coordAdapter = (view != null) ? view.getCoordAdapter() : null; + if (coordAdapter == null) { + return null; + } + Point3d localPt = coordAdapter.getCoordSystem().geographicToLocal(geoPt); + return (localPt != null) ? coordAdapter.localToDisplay(localPt) : null; + } + /** * Return the main content view used to represent the Maply Control. */ @@ -1080,6 +1094,16 @@ public void setPerfInterval(int inPerfInterval) renderControl.setPerfInterval(perfInterval); } + /** + * Get the zoom limits for the globe. + */ + public /*abstract*/ double getZoomLimitMin() { return 0.0; } + + /** + * Get the zoom limits for the globe. + */ + public /*abstract*/ double getZoomLimitMax() { return 0.0; } + /** Calculate the height that corresponds to a given Mapnik-style map scale. *
* Figure out the viewer height that corresponds to a given scale denominator (ala Mapnik). @@ -2059,11 +2083,11 @@ public void run() { if (particleSystemID != RenderController.EmptyIdentity) { compObj.addParticleSystemID(particleSystemID); } + renderControl.componentManager.addComponentObject(compObj, changes); if (scene != null) { changes.process(renderControl, scene); changes.dispose(); } - renderControl.componentManager.addComponentObject(compObj); } }; diff --git a/android/library/maply/src/main/java/com/mousebird/maply/BaseInfo.java b/android/library/maply/src/main/java/com/mousebird/maply/BaseInfo.java index 1e33e94079..9f9d1df1b6 100644 --- a/android/library/maply/src/main/java/com/mousebird/maply/BaseInfo.java +++ b/android/library/maply/src/main/java/com/mousebird/maply/BaseInfo.java @@ -64,7 +64,7 @@ public void setMaxVis(double maxVis) public native void setViewDistRange(double minViewerDist,double maxViewerDist); /** - * @return Minimim distance from the user that an object will be visible. + * @return Minimum distance from the user that an object will be visible. */ public native double getViewDistRangeMin(); @@ -123,7 +123,7 @@ public void setMaxVis(double maxVis) * The amount of time (in seconds) it takes for new geometry * to fade in and fade out. By default, fade is off. */ - public native void setFade(float fade); + public native void setFade(double fade); /** * Geometry can be made to fade in when it appears and fade out when diff --git a/android/library/maply/src/main/java/com/mousebird/maply/CharRenderer.java b/android/library/maply/src/main/java/com/mousebird/maply/CharRenderer.java index 0bfd067715..10e1d0298c 100644 --- a/android/library/maply/src/main/java/com/mousebird/maply/CharRenderer.java +++ b/android/library/maply/src/main/java/com/mousebird/maply/CharRenderer.java @@ -1,9 +1,8 @@ -/* - * CharRenderer.java +/* CharRenderer.java * WhirlyGlobeLib * * Created by Steve Gifford on 6/2/14. - * Copyright 2011-2014 mousebird consulting + * Copyright 2011-2021 mousebird consulting * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -15,7 +14,6 @@ * 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. - * */ package com.mousebird.maply; @@ -46,7 +44,8 @@ public class Glyph CharRenderer() { } - + + // called from C++ code Glyph renderChar(int charInt,LabelInfo labelInfo,float fontSize) { Paint textFillPaint = new Paint(); diff --git a/android/library/maply/src/main/java/com/mousebird/maply/ComponentManager.java b/android/library/maply/src/main/java/com/mousebird/maply/ComponentManager.java index aaf003130b..d9b0b9db2a 100644 --- a/android/library/maply/src/main/java/com/mousebird/maply/ComponentManager.java +++ b/android/library/maply/src/main/java/com/mousebird/maply/ComponentManager.java @@ -50,7 +50,7 @@ public ComponentObject makeComponentObject() /** * Hand a component object over to be managed. */ - public native void addComponentObject(ComponentObject compObj); + public native void addComponentObject(ComponentObject compObj,ChangeSet changes); /** * Return true if the given component object still exists. diff --git a/android/library/maply/src/main/java/com/mousebird/maply/DebugImageLoaderInterpreter.java b/android/library/maply/src/main/java/com/mousebird/maply/DebugImageLoaderInterpreter.java index b5562b9aff..9fbdaa8dbb 100644 --- a/android/library/maply/src/main/java/com/mousebird/maply/DebugImageLoaderInterpreter.java +++ b/android/library/maply/src/main/java/com/mousebird/maply/DebugImageLoaderInterpreter.java @@ -1,9 +1,8 @@ -/* - * DebugImageLoaderInterpreter.java +/* DebugImageLoaderInterpreter.java * WhirlyGlobeLib * * Created by jmnavarro on 3/20/19. - * Copyright 2011-2019 mousebird consulting + * Copyright 2011-2021 mousebird consulting * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -15,7 +14,6 @@ * 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. - * */ package com.mousebird.maply; diff --git a/android/library/maply/src/main/java/com/mousebird/maply/GlobeAnimateRotation.java b/android/library/maply/src/main/java/com/mousebird/maply/GlobeAnimateRotation.java index 1cdef3007b..1bbd4a000d 100644 --- a/android/library/maply/src/main/java/com/mousebird/maply/GlobeAnimateRotation.java +++ b/android/library/maply/src/main/java/com/mousebird/maply/GlobeAnimateRotation.java @@ -21,7 +21,7 @@ package com.mousebird.maply; /** - * Implements a rotation with momentum on the globe. + * Implements a rotation on the globe using a quaternion. *

* *

@@ -36,6 +36,8 @@ public class GlobeAnimateRotation implements GlobeView.AnimationDelegate Quaternion startQuat = null; Quaternion endQuat = null; double startHeight,endHeight; + Double startRot = null; + Double dRot = null; double startTime,animTime; public GlobeAnimateRotation(GlobeView inGlobeView,RenderController inRender,Quaternion newQuat,double newHeight,double animLen) @@ -50,27 +52,52 @@ public GlobeAnimateRotation(GlobeView inGlobeView,RenderController inRender,Quat endHeight = newHeight; endQuat = newQuat; } - + + public GlobeAnimateRotation(GlobeView inGlobeView,RenderController inRender,Quaternion newQuat,double newHeight,Double heading,double animLen) + { + this(inGlobeView,inRender,newQuat,newHeight,animLen); + + if (inGlobeView != null && heading != null && !inGlobeView.northUp) { + startRot = inGlobeView.getHeading(); + + // If the old and new rotations are within 180 degrees, just interpolate. + // Otherwise, we need to go around the other way. + // Note that we assume both angles are normalized. + dRot = heading - startRot; + if (Math.abs(dRot) < 1.0e-6) { + // Don't generate a bunch of rotations for minuscule offsets that won't make any difference + dRot = 0.0; + } else if (Math.abs(dRot) > Math.PI) { + dRot += ((dRot > 0) ? -2 : 2) * Math.PI; + } + } + } + @Override public void updateView(GlobeView view) { - if (startTime == 0.0) + if (startTime <= 0 || animTime <= 0 || globeView == null || renderer == null) return; - - double sinceStart = System.currentTimeMillis()/1000.0-startTime; + + double curTime = Math.min(startTime + animTime, System.currentTimeMillis()/1000.0); + double t = (curTime-startTime)/animTime; + + Quaternion newQuat = startQuat.slerp(endQuat,t); + globeView.setRotQuat(newQuat); + + double height = (endHeight-startHeight)*t + startHeight; + globeView.setHeight(height); + + if (startRot != null && dRot != null && dRot != 0.0) { + view.setHeading(startRot + t * dRot); + } + // Reached the end of the allotted time - if (sinceStart > animTime) + if (t >= 1.0) { - sinceStart = animTime; startTime = 0; view.cancelAnimation(); } - - double t = sinceStart/animTime; - Quaternion newQuat = startQuat.slerp(endQuat,t); - double height = (endHeight-startHeight)*t + startHeight; - globeView.setRotQuat(newQuat); - globeView.setHeight(height); } } diff --git a/android/library/maply/src/main/java/com/mousebird/maply/GlobeController.java b/android/library/maply/src/main/java/com/mousebird/maply/GlobeController.java index e295394547..4ffd24f42a 100644 --- a/android/library/maply/src/main/java/com/mousebird/maply/GlobeController.java +++ b/android/library/maply/src/main/java/com/mousebird/maply/GlobeController.java @@ -22,6 +22,7 @@ import android.app.Activity; import android.graphics.Color; +import android.graphics.Point; import android.view.Choreographer; import android.view.MotionEvent; import android.view.View; @@ -184,6 +185,24 @@ public void setZoomLimits(double inMin,double inMax) gestureHandler.setZoomLimits(inMin,inMax); } + /** + * Get the zoom (height) limits for the globe. + */ + @Override + public double getZoomLimitMin() + { + return (gestureHandler != null) ? gestureHandler.zoomLimitMin : 0.0; + } + + /** + * Get the zoom (height) limits for the globe. + */ + @Override + public double getZoomLimitMax() + { + return (gestureHandler != null) ? gestureHandler.zoomLimitMax : Double.POSITIVE_INFINITY; + } + /** * Return the geographic point (radians) corresponding to the screen point. * @@ -287,22 +306,6 @@ private Point2d screenPointFromGeo(GlobeView theGlobeView,Point2d geoCoord) return theGlobeView.pointOnScreenFromSphere(dispPt, modelMat, renderWrapper.maplyRender.get().frameSize); } - /** - * Return a point in display space. Display space is close to what's rendered. - * For the globe it's a model space based on a radius of 1.0. - */ - public Point3d displayPointFromGeo(Point3d geoPt) - { - CoordSystemDisplayAdapter coordAdapter = globeView.getCoordAdapter(); - CoordSystem coordSys = coordAdapter.getCoordSystem(); - Point3d localPt = coordSys.geographicToLocal(geoPt); - if (localPt == null) - return null; - Point3d dispPt = coordAdapter.localToDisplay(localPt); - - return dispPt; - } - boolean checkCoverage(Mbr mbr,GlobeView theGlobeView,double height) { Point2d centerLoc = mbr.middle(); @@ -554,6 +557,29 @@ protected boolean isCompletelySetup() * @param howLong Time (in seconds) to animate. */ public void animatePositionGeo(final double x,final double y,final double z,final double howLong) + { + animatePositionGeo(x,y,z,null,howLong); + } + + /** + * Animate to a new view position + * @param targetGeoLoc Location of the center of the screen in geographic radians (not degrees). + * @param hdg New heading + * @param howLong Time (in seconds) to animate. + */ + public void animatePositionGeo(final Point3d targetGeoLoc,Double hdg,final double howLong) { + animatePositionGeo(targetGeoLoc.getX(),targetGeoLoc.getY(),targetGeoLoc.getZ(),hdg,howLong); + } + + /** + * Animate to a new view position + * @param x Horizontal location of the center of the screen in geographic radians (not degrees). + * @param y Vertical location of the center of the screen in geographic radians (not degrees). + * @param z Height above the map in display units. + * @param hdg New heading + * @param howLong Time (in seconds) to animate. + */ + public void animatePositionGeo(final double x,final double y,final double z,Double hdg,final double howLong) { if (!isCompletelySetup()) { if (!rendererAttached) { @@ -567,12 +593,106 @@ public void run() { return; } + hdg = globeView.northUp ? 0 : ((hdg != null) ? hdg : globeView.getHeading()); + globeView.cancelAnimation(); Point3d geoCoord = globeView.coordAdapter.coordSys.geographicToLocal(new Point3d(x,y,0.0)); if (geoCoord != null) { Quaternion newQuat = globeView.makeRotationToGeoCoord(x, y, globeView.northUp); if (newQuat != null) - globeView.setAnimationDelegate(new GlobeAnimateRotation(globeView, renderControl, newQuat, z, howLong)); + globeView.setAnimationDelegate(new GlobeAnimateRotation(globeView, renderControl, newQuat, z, hdg, howLong)); + } + } + + /** + * Animate to a new view position + * @param x Horizontal location in geographic radians (not degrees). + * @param y Vertical location in geographic radians (not degrees). + * @param z Height above the map in display units. + * @param offset Screen offset for the target point + * @param hdg New heading + * @param howLong Time (in seconds) to animate. + */ + public void animatePositionGeo( double x, double y, double z,Point2d offset,Double hdg, double howLong) { + animatePositionGeo(new Point3d(x,y,z),offset,hdg,howLong); + } + + /** + * Animate to a new view position + * @param xy Location of the in geographic radians (not degrees). + * @param height Height above the map in display units. + * @param hdg New heading + * @param howLong Time (in seconds) to animate. + */ + public void animatePositionGeo(Point2d xy, Double height, Double hdg, double howLong) { + animatePositionGeo(xy.getX(), xy.getY(), height, hdg, howLong); + } + + /** + * Animate to a new view position + * @param targetGeoLoc Location of the in geographic radians (not degrees). + * @param offset Screen offset for the target point + * @param hdg New heading + * @param howLong Time (in seconds) to animate. + */ + public void animatePositionGeo(final Point3d targetGeoLoc,final Point2d offset,Double hdg,final double howLong) + { + if (!isCompletelySetup()) { + if (!rendererAttached) { + addPostSurfaceRunnable(new Runnable() { + @Override + public void run() { + animatePositionGeo(targetGeoLoc,offset,hdg,howLong); + } + }); + } + return; + } + + globeView.cancelAnimation(); + + CoordSystemDisplayAdapter coordAdapter = globeView.coordAdapter; + CoordSystem coordSys = (coordAdapter != null) ? coordAdapter.coordSys : null; + if (coordSys == null) { + return; + } + + Matrix4d matrix = globeView.calcModelViewMatrix(); + + int[] frameBufSizeArr = getFrameBufferSize(); + if (frameBufSizeArr == null || frameBufSizeArr.length != 2) { + return; + } + Point2d frameSize = new Point2d(frameBufSizeArr[0], frameBufSizeArr[1]); + + Point2d localOffset = frameSize.multiplyBy(0.5); + if (offset != null) { + localOffset = localOffset.addTo(offset.multiplyBy(-1)); + } + + // check that the offset is actually within the globe, or we can't do it anyway + Point3d offsetLoc = globeView.pointOnSphereFromScreen(localOffset, matrix, frameSize, true); + if (offsetLoc == null) { + return; + } + + Point3d destPt = coordAdapter.localToDisplay(coordSys.geographicToLocal(targetGeoLoc)); + Quaternion newRotQuat = globeView.getRotQuat().multiply(new Quaternion(destPt, offsetLoc)); + + if (newRotQuat != null && globeView.northUp) { + // See where the north pole is going. + Point3d northPole = newRotQuat.multiply(new Point3d(0,0,1)).normalized(); + if (northPole.getY() != 0.0) { + // Not straight up, rotate it back to vertical. + Double angle = Math.atan(northPole.getX() / northPole.getY()) + ((northPole.getY() < 0) ? Math.PI : 0); + newRotQuat = newRotQuat.multiply(new AngleAxis(angle, destPt)); + } + } + + if (newRotQuat != null) { + GlobeAnimateRotation dg = new GlobeAnimateRotation(globeView, renderControl, newRotQuat, + targetGeoLoc.getZ(), hdg, howLong); + globeView.setAnimationDelegate(dg); } } diff --git a/android/library/maply/src/main/java/com/mousebird/maply/ImageLoaderInterpreter.java b/android/library/maply/src/main/java/com/mousebird/maply/ImageLoaderInterpreter.java index aea5c88260..8289516878 100644 --- a/android/library/maply/src/main/java/com/mousebird/maply/ImageLoaderInterpreter.java +++ b/android/library/maply/src/main/java/com/mousebird/maply/ImageLoaderInterpreter.java @@ -72,7 +72,7 @@ public void dataForTile(LoaderReturn inLoadReturn,QuadLoaderBase loader) if (hasPremultiplyOption && usePremultiply) options.inPremultiplied = false; - byte[][] images =loadReturn.getTileData(); + byte[][] images = loadReturn.getTileData(); for (byte[] image : images) { Bitmap bm = BitmapFactory.decodeByteArray(image,0, image.length,options); if (bm != null) diff --git a/android/library/maply/src/main/java/com/mousebird/maply/InternalLabel.java b/android/library/maply/src/main/java/com/mousebird/maply/InternalLabel.java index 382d56c35b..5571d616fd 100644 --- a/android/library/maply/src/main/java/com/mousebird/maply/InternalLabel.java +++ b/android/library/maply/src/main/java/com/mousebird/maply/InternalLabel.java @@ -66,6 +66,7 @@ private void setupScreenLabel(ScreenLabel label,LabelInfo labelInfo) if (label.layoutPlacement != -1) setLayoutPlacement(label.layoutPlacement); + setLayoutImportance(Float.MAX_VALUE); if (labelInfo.layoutImportance != Float.MAX_VALUE) setLayoutImportance(labelInfo.layoutImportance); if (label.layoutImportance != Float.MAX_VALUE) diff --git a/android/library/maply/src/main/java/com/mousebird/maply/InternalMarker.java b/android/library/maply/src/main/java/com/mousebird/maply/InternalMarker.java index c9ca0f1ddf..43927c4b07 100644 --- a/android/library/maply/src/main/java/com/mousebird/maply/InternalMarker.java +++ b/android/library/maply/src/main/java/com/mousebird/maply/InternalMarker.java @@ -1,9 +1,8 @@ -/* - * InternalMarker.java +/* InternalMarker.java * WhirlyGlobeLib * * Created by Steve Gifford on 6/2/14. - * Copyright 2011-2014 mousebird consulting + * Copyright 2011-2021 mousebird consulting * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -15,31 +14,28 @@ * 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. - * */ - package com.mousebird.maply; import android.graphics.Color; /** * An internal representation for the markers. Toolkit users use ScreenMarker or Marker instead of this. - * */ class InternalMarker { - InternalMarker() + protected InternalMarker() { initialise(); } // Basic setup for both types of screen markers - private void screenMakerSetup(ScreenMarker marker) + private void screenMarkerSetup(ScreenMarker marker) { if (marker.selectable) setSelectID(marker.ident); setLoc(marker.loc); - setColor(Color.red(marker.color)/255.f,Color.green(marker.color)/255.f,Color.blue(marker.color)/255.f,Color.alpha(marker.color)/255.f); + setColor(marker.color); if (marker.rotation != 0.0) setRotation(marker.rotation); // Note: Lock rotation? @@ -51,19 +47,19 @@ private void screenMakerSetup(ScreenMarker marker) setPeriod(marker.period); if (marker.vertexAttributes != null) setVertexAttributes(marker.vertexAttributes.toArray()); + setLayoutImportance(marker.layoutImportance); } /** * Construct with the screen marker we want to represent and how it looks. * * @param marker Screen marker to represent. - * @param info How the screen marker should look. */ InternalMarker(ScreenMarker marker) { initialise(); - screenMakerSetup(marker); + screenMarkerSetup(marker); } /** @@ -73,7 +69,7 @@ private void screenMakerSetup(ScreenMarker marker) { initialise(); - screenMakerSetup(marker); + screenMarkerSetup(marker); setEndLoc(marker.endLoc); setAnimationRange(startTime,startTime+marker.duration); @@ -89,11 +85,18 @@ private void screenMakerSetup(ScreenMarker marker) if (marker.selectable) setSelectID(marker.ident); setLoc(marker.loc); - setColor(Color.red(marker.color)/255.f,Color.green(marker.color)/255.f,Color.blue(marker.color)/255.f,Color.alpha(marker.color)/255.f); + setColor(marker.color); setSize(marker.size.getX(),marker.size.getY()); setPeriod(marker.period); } + void setColor(int color) { + setColor(Color.red(color)/255.f, + Color.green(color)/255.f, + Color.blue(color)/255.f, + Color.alpha(color)/255.f); + } + public void finalize() { dispose(); @@ -111,7 +114,8 @@ public void finalize() public native void setLayoutSize(double width,double height); public native void setOffset(double offX,double offY); public native void setPeriod(double period); - public native void setVertexAttributes(Object vertAttrs[]); + public native void setVertexAttributes(Object[] vertexAttributes); + public native void setLayoutImportance(float importance); static { diff --git a/android/library/maply/src/main/java/com/mousebird/maply/LabelInfo.java b/android/library/maply/src/main/java/com/mousebird/maply/LabelInfo.java index f5ce734050..f4bd87f2e0 100644 --- a/android/library/maply/src/main/java/com/mousebird/maply/LabelInfo.java +++ b/android/library/maply/src/main/java/com/mousebird/maply/LabelInfo.java @@ -50,6 +50,7 @@ public LabelInfo() setTypeface(Typeface.DEFAULT); setFontSize(24.f); setTextJustify(TextJustify.TextLeft); + setLayoutPlacement(LabelInfo.LayoutRight | LabelInfo.LayoutLeft | LabelInfo.LayoutCenter | LabelInfo.LayoutBelow); setDrawPriority(LabelPriorityDefault); } diff --git a/android/library/maply/src/main/java/com/mousebird/maply/LayerThread.java b/android/library/maply/src/main/java/com/mousebird/maply/LayerThread.java index da64f09a4d..2db531c5b6 100644 --- a/android/library/maply/src/main/java/com/mousebird/maply/LayerThread.java +++ b/android/library/maply/src/main/java/com/mousebird/maply/LayerThread.java @@ -182,11 +182,68 @@ public void run() { }); } } + + // Used to shut down cleaning without cutting off outstanding work threads + private boolean isShuttingDown = false; + private Semaphore workLock = new Semaphore(1, true); + private int numActiveWorkers = 0; + + // Something is requesting a lock on shutting down while working + public boolean startOfWork() + { + if (isShuttingDown) + return false; + + try { + workLock.acquire(); + // Check it again + if (isShuttingDown) { + workLock.release(); + return false; + } + numActiveWorkers = numActiveWorkers + 1; + } + catch (Exception exp) { + return false; + } + + workLock.release(); + return true; + } + + // End of an external work block. Safe to shut down. + public void endOfWork() + { + try { + workLock.acquire(); + numActiveWorkers = numActiveWorkers - 1; + workLock.release(); + } + catch (Exception exp) { + } + } // Called on the main thread *after* the thread has quit safely void shutdown() { final Semaphore endLock = new Semaphore(0, true); + isShuttingDown = true; + + // Wait for anything outstanding to finish before we shut down + try { + do { + workLock.acquire(); + if (numActiveWorkers == 0) { + workLock.release(); + break; + } + workLock.release(); + sleep(10); + } while (true); + } + catch (Exception exp) { + // Not sure why this would ever happen + } // Run the shutdowns on the thread itself addTask(new Runnable() { @@ -194,7 +251,6 @@ void shutdown() public void run() { EGL10 egl = (EGL10) EGLContext.getEGL(); - valid = false; ArrayList layersToRemove = null; synchronized (layers) { layersToRemove = new ArrayList(layers); @@ -203,6 +259,7 @@ public void run() { layer.shutdown(); } + valid = false; egl.eglMakeCurrent(renderer.display, egl.EGL_NO_SURFACE, egl.EGL_NO_SURFACE, egl.EGL_NO_CONTEXT); layers.clear(); diff --git a/android/library/maply/src/main/java/com/mousebird/maply/LocationSimulatorDelegate.kt b/android/library/maply/src/main/java/com/mousebird/maply/LocationSimulatorDelegate.kt new file mode 100644 index 0000000000..e66e4e4ef5 --- /dev/null +++ b/android/library/maply/src/main/java/com/mousebird/maply/LocationSimulatorDelegate.kt @@ -0,0 +1,27 @@ +/* + * LocationSimulatorDelegate.kt + * WhirlyGlobeLib + * + * Created by Tim Sylvester + * Copyright 2021 mousebird consulting + * + * 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. + * + */ + +package com.mousebird.maply + +interface LocationSimulatorDelegate { + fun locationSimulatorGetLocation(): LocationTrackerPoint? { + return null + } +} diff --git a/android/library/maply/src/main/java/com/mousebird/maply/LocationTracker.kt b/android/library/maply/src/main/java/com/mousebird/maply/LocationTracker.kt new file mode 100644 index 0000000000..85c9e8de25 --- /dev/null +++ b/android/library/maply/src/main/java/com/mousebird/maply/LocationTracker.kt @@ -0,0 +1,659 @@ +/* + * LocationTracker.kt + * WhirlyGlobeLib + * + * Created by Tim Sylvester + * Copyright 2021 mousebird consulting + * + * 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. + */ + +package com.mousebird.maply + +import android.Manifest.permission +import android.content.Context +import android.graphics.* +import android.graphics.Shader +import android.os.* +import androidx.annotation.RequiresPermission +import androidx.core.graphics.* +import com.google.android.gms.location.* +import com.google.android.gms.location.LocationRequest.* +import com.google.android.gms.tasks.Task +import java.lang.ref.WeakReference +import kotlin.math.* + +class LocationTracker : LocationCallback { + + /** + * MaplyLocationTracker constructor + * + * @param mapController The globe or map view controller + * @param trackerDelegate Delegate for location tracking + * @param useHeading Use location services heading information + */ + constructor(mapController: BaseController, + trackerDelegate: LocationTrackerDelegate? = null, + useHeading: Boolean = true) : + this(mapController, trackerDelegate, null, 1.0, useHeading) + + /** + * MaplyLocationTracker constructor + * + * @param mapController The globe or map view controller + * @param trackerDelegate Delegate for location tracking + * @param simulatorDelegate Delegate for simulated location + * @param updateInterval Seconds between simulation updates + * @param useHeading Use location services heading information (requires physical magnetometer) + */ + constructor(mapController: BaseController, + trackerDelegate: LocationTrackerDelegate? = null, + simulatorDelegate: LocationSimulatorDelegate? = null, + updateInterval: Double = 1.0, + useHeading: Boolean = true) + { + this.baseController = WeakReference(mapController) + this.trackerDelegate = WeakReference(trackerDelegate) + this.simulatorDelegate = WeakReference(simulatorDelegate) + this.updateInterval = updateInterval + this.useHeading = useHeading + } + + /** + * Start location tracking/simulation + */ + @RequiresPermission(anyOf = [permission.ACCESS_COARSE_LOCATION, permission.ACCESS_FINE_LOCATION]) + fun start(context: Context, looper: Looper? = null, request: LocationRequest? = null) { + stop() + + val defLooper = baseController.get()?.workingThread?.looper ?: Looper.myLooper()!! + handler = Handler(looper ?: defLooper) + + simulatorDelegate.get()?.let { + simulating = true + handler?.post(simTask) + } ?: trackerDelegate.get()?.let { + locationClient = LocationServices.getFusedLocationProviderClient(context)?.also { client -> + val req = request ?: LocationRequest().apply { + priority = PRIORITY_BALANCED_POWER_ACCURACY + interval = (1000.0 * updateInterval).toLong() + maxWaitTime = 2 * interval - 1 + numUpdates = Int.MAX_VALUE + } + locationTask = client.requestLocationUpdates(req, this, handler?.looper) + } + } + } + + /** + * Stop location tracking/simulation + */ + fun stop() { + + simulating = false + handler?.removeCallbacks(simTask) + handler = null + + if (locationTask != null) { + locationClient?.removeLocationUpdates(this) + locationTask = null + locationClient = null + } + + removeComponentObjects() + clearInfoObjects() + clearMarkerImages() + } + + /** + * Min visibility for the marker assigned to follow location. + */ + var markerMinVis = 0.0 + set(value) { + if (field != value) { + field = value + clearInfoObjects() + } + } + + /** + * Max visibility for the marker assigned to follow location. + */ + var markerMaxVis = 1.0 + set(value) { + if (field != value) { + field = value + clearInfoObjects() + } + } + + var markerSize = 32 + set(value) { + // Limit to at least 16 and ensure that it's even + val newValue = (value.coerceAtLeast(8)) + if (newValue != field) { + field = newValue; + imagesInvalidated = true + } + } + + var markerColorInner = Color.WHITE + set(value) { + if (field != value) { + field = value + imagesInvalidated = true + } + } + + var markerColorOuter = Color.argb(255, 0, 192, 255) + set(value) { + if (field != value) { + field = value + imagesInvalidated = true + } + } + + var markerColorOutline = Color.WHITE + set(value) { + if (field != value) { + field = value + imagesInvalidated = true + } + } + + var markerColorShadow = Color.argb(48, 0, 0, 0) + set(value) { + if (field != value) { + field = value + imagesInvalidated = true + } + } + + var accuracyCircleColor = Color.argb(52, 15, 15, 26) + set(value) { + if (field != value) { + field = value + clearInfoObjects() + } + } + + var trackerDelegate = WeakReference(null) + + var simulatorDelegate = WeakReference(null) + + /** + * Draw priority for the marker assigned to follow location. + */ + var markerDrawPriority: Int = RenderController.VectorDrawPriorityDefault + 1 + set(value) { + if (field != value) { + field = value + clearInfoObjects() + } + } + + /** + * Location lock type + */ + var lockType: MaplyLocationLockType = MaplyLocationLockType.MaplyLocationLockNone + + /** + * Forward track offset, for lock type MaplyLocationLockHeadingUpOffset + */ + var forwardTrackOffset: Int = 0 + + /** + * Get the current device location + */ + var lastLocation: LocationTrackerPoint? = null + + /** + * Set the current location. + */ + fun updateLocation(location: LocationTrackerPoint?) { + if (!locationUpdatePending) { + locationUpdatePending = true + handler?.post { + try { + updateLocationInternal(location) + } catch (ex: Exception) { + println(ex.localizedMessage) + } + } + } + } + + override fun onLocationAvailability(availability: LocationAvailability?) { + availability.let { super.onLocationAvailability(it) } + + if (baseController.get() == null) { + stop() + return + } + + // Call the delegate if its present, allowing it to update the result + val tracker = trackerDelegate.get() + val a = if (tracker != null) tracker.locationManagerDidChangeAuthorizationStatus(this, availability) else availability + if (a?.isLocationAvailable != true) { + updateLocation(null) + } + } + + override fun onLocationResult(location: LocationResult?) { + super.onLocationResult(location) + + if (baseController.get() != null) { + updateLocation(convertIf(location?.lastLocation)) + } else { + stop() + } + } + + private fun convertIf(loc: android.location.Location?): LocationTrackerPoint? { + return if (loc != null) convert(loc) else null + } + private fun convert(loc: android.location.Location): LocationTrackerPoint { + return LocationTrackerPoint().apply { + latDeg = loc.latitude + lonDeg = loc.longitude + horizontalAccuracy = if (loc.hasAccuracy()) loc.accuracy.toDouble() else null + elevation = if (loc.hasAltitude()) loc.altitude else null + headingDeg = if (loc.hasBearing()) loc.bearing.toDouble() else null + speed = if (loc.hasSpeed()) loc.speed.toDouble() else null + + if (Build.VERSION.SDK_INT > 26) { + verticalAccuracy = if (loc.hasVerticalAccuracy()) loc.verticalAccuracyMeters.toDouble() else null + headingAccuracy = if (loc.hasBearingAccuracy()) loc.bearingAccuracyDegrees.toDouble() else null + speedAccuracy = if (loc.hasSpeedAccuracy()) loc.speedAccuracyMetersPerSecond.toDouble() else null + } + } + } + + private fun updateLocationInternal(location: LocationTrackerPoint?) { + locationUpdatePending = false + + val vc = baseController.get() + if (vc == null) { + stop() + return + } + + // Call the delegate, allowing it to update the result + val tracker = trackerDelegate.get() + val endLoc = if (tracker != null) tracker.locationManagerDidUpdateLocation(this, location) else location + + prevLocation = lastLocation + lastLocation = endLoc + + // If the latest is no location, we're done + if (endLoc == null) { + removeComponentObjects() + return + } + + // If something has changed, we need to remove and re-create the marker image textures. + // Hold onto them until after the objects using them are removed, though. + var oldMarkerImages: Array? = null + var oldDirectionalImages: Array? = null + if (imagesInvalidated) { + oldMarkerImages = markerImages + oldDirectionalImages = directionalImages + markerImages = null + directionalImages = null + imagesInvalidated = false; + } + + try { + // Create textures and info objects, if they don't already exist + setupMarkerImages() + val circleInfo = circleInfo ?: return + val markerInfo = markerInfo ?: return + val movingMarkerInfo = movingMarkerInfo ?: return + + val now = System.currentTimeMillis() / 1000.0 + + // If we have a previous location, animate starting there + val startLoc: LocationTrackerPoint = prevLocation ?: endLoc + + val circle = circleForLocation(endLoc) ?: return + + val orientation = if (useHeading) endLoc.headingDeg else null + + val movingMarker = ScreenMovingMarker() + movingMarker.loc = Point2d.FromDegrees(startLoc.lonDeg, startLoc.latDeg) + movingMarker.endLoc = Point2d.FromDegrees(endLoc.lonDeg, endLoc.latDeg) + movingMarker.duration = updateInterval / 2.0 + movingMarker.period = 1.0 + movingMarker.size = Point2d(markerSize.toDouble(), + markerSize.toDouble()) + movingMarker.rotation = degToRad(if (orientation != null) -orientation else 0.0) + movingMarker.images = if (orientation != null) directionalImages else markerImages + movingMarker.layoutImportance = Float.MAX_VALUE + movingMarkerInfo.setEnableTimes(now, now + movingMarker.duration) + + val marker = ScreenMarker() + marker.loc = movingMarker.endLoc + marker.period = 1.0 + marker.size = movingMarker.size + marker.rotation = movingMarker.rotation + marker.images = movingMarker.images + marker.layoutImportance = Float.MAX_VALUE + markerInfo.setEnableTimes(now + movingMarker.duration, 0.0) + + // Capture old object refs + val objs = arrayOf(markerObj, movingMarkerObj, circleObj).filterNotNull() + + try { + // Add new objects + circleObj = vc.addShapes(listOf(circle), circleInfo, threadCurrent) + movingMarkerObj = vc.addScreenMovingMarkers(listOf(movingMarker), movingMarkerInfo, threadCurrent) + markerObj = vc.addScreenMarker(marker, markerInfo, threadCurrent) + } finally { + // Remove old objects + if (objs.isNotEmpty()) { + vc.removeObjects(objs, threadCurrent) + } + } + } finally { + // Clean up the old versions of the marker image textures + oldMarkerImages?.let { + vc.removeTextures(it.toMutableList(), threadAny) + } + oldDirectionalImages?.let { + vc.removeTextures(it.toMutableList(), threadAny) + } + } + + lockToLocation(endLoc) + } + + private fun lockToLocation(loc: LocationTrackerPoint) { + val map = (baseController.get() as? MapController) + val globe = (baseController.get() as? GlobeController) + if (map == null && globe == null) { + stop() + return + } + + val gp = Point2d.FromDegrees(loc.lonDeg, loc.latDeg) + val animTime = updateInterval / 2.0 + val hdg = -degToRad((360 + (loc.headingDeg ?: 0.0)) % 360) + + var lockType = lockType + if (loc.headingDeg == null && + (lockType == MaplyLocationLockType.MaplyLocationLockHeadingUp || + lockType == MaplyLocationLockType.MaplyLocationLockHeadingUpOffset)) { + lockType = MaplyLocationLockType.MaplyLocationLockNorthUp + } + + when (lockType) { + MaplyLocationLockType.MaplyLocationLockNorthUp -> { + if (map != null) { + map.animatePositionGeo(gp.x, gp.y, map.positionGeo.z, animTime) + } else { + globe?.animatePositionGeo(gp.x, gp.y, globe.viewState.height, animTime) + } + } + MaplyLocationLockType.MaplyLocationLockHeadingUp -> { + if (map != null) { + map.animatePositionGeo(gp.x, gp.y, map.positionGeo.z, hdg, animTime) + } else { + globe?.animatePositionGeo(gp.x, gp.y, globe.viewState.height, -hdg, animTime) + } + } + MaplyLocationLockType.MaplyLocationLockHeadingUpOffset -> { + val offset = Point2d(0.0, -forwardTrackOffset.toDouble()) + if (map != null) { + val target = Point3d(gp.x, gp.y, map.positionGeo.z) + map.animatePositionGeo(target, offset, hdg, animTime) + } else { + // TODO: Add animateToPosition:onScreen + val target = Point3d(gp.x, gp.y, globe!!.viewState.height) + globe.animatePositionGeo(target, offset, -hdg, animTime) + } + } + else -> {} + } + } + + private fun setupMarkerImages() { + if (markerImages == null || directionalImages == null) { + baseController.get()?.let { vc -> + markerImages = (0..16).map { + radialGradientMarker(vc, markerSize * 2, it, false) + }.toTypedArray() + directionalImages = (0..16).map { + radialGradientMarker(vc, markerSize * 2, it, true) + }.toTypedArray() + } + } + + if (markerInfo == null) { + markerInfo = MarkerInfo().apply { + setVisibleHeightRange(markerMinVis, markerMaxVis) + setFade(0.0) + drawPriority = markerDrawPriority + setEnableTimes(0.0, Double.MAX_VALUE) + } + } + if (movingMarkerInfo == null) { + movingMarkerInfo = MarkerInfo().apply { + setVisibleHeightRange(markerMinVis, markerMaxVis) + setFade(0.0) + drawPriority = markerDrawPriority + setEnableTimes(0.0, Double.MAX_VALUE) + } + } + if (circleInfo == null) { + circleInfo = ShapeInfo().apply { + drawPriority = markerDrawPriority - 1 + setColor(accuracyCircleColor.red / 255f, accuracyCircleColor.green / 255f, + accuracyCircleColor.blue / 255f, accuracyCircleColor.alpha / 255f) + setFade(0.0) + // Prevent the circle from being occluded by other stuff + setZBufferRead(false) + // Use a no-light shader so the circle doesn't look different in different geographic locations + setShader(baseController.get()?.getShader(com.mousebird.maply.Shader.NoLightTriangleShader)) + } + } + } + + private fun clearMarkerImages() { + baseController.get()?.let { vc -> + markerImages?.let { + vc.removeTextures(it.toMutableList(), threadAny) + } + directionalImages?.let { + vc.removeTextures(it.toMutableList(), threadAny) + } + } + markerImages = null + directionalImages = null + } + + private fun clearInfoObjects() { + markerInfo = null + movingMarkerInfo = null + circleInfo = null + } + + private fun removeComponentObjects() { + val objs = arrayOf(markerObj, movingMarkerObj, circleObj).filterNotNull() + baseController.get()?.removeObjects(objs, threadCurrent) + markerObj = null + movingMarkerObj = null + circleObj = null + } + + @Suppress("SameParameterValue") + private fun markerGradLoc(n: Int, radius: Int, fraction: Int = 4): Float { + return (radius / fraction - abs(radius / fraction - n)) * fraction.toFloat() / radius + } + @Suppress("SameParameterValue") + private fun markerGradRad(n: Int, size: Int, radius: Int, fraction: Int = 4): Float { + return (size - radius - abs((radius / fraction) - n)) / 2.0f + } + + @Suppress("SameParameterValue") + private fun radialGradientMarker(vc: BaseController, size: Int, + idx: Int, directional: Boolean): MaplyTexture { + val gradLoc = markerGradLoc(idx, markerSize, 4) + val gradRad = markerGradRad(idx, size, markerSize, 4) + val outlineWidth = (markerSize / 8f).coerceAtLeast(1f).coerceAtMost(10f) + val image = radialGradientMarkerImage(size, gradLoc, gradRad, outlineWidth, directional) + return vc.addTexture(image, RenderControllerInterface.TextureSettings(), RenderControllerInterface.ThreadMode.ThreadCurrent) + } + + @Suppress("SameParameterValue") + private fun radialGradientMarkerImage(size: Int, + gradLocation: Float, gradRadius: Float, + outlineWidth: Float, + directional: Boolean): Bitmap { + + val image = Bitmap.createBitmap(size, size, Bitmap.Config.ARGB_8888) + image.eraseColor(Color.TRANSPARENT) + + val canvas = Canvas(image) + + val paint = Paint(Paint.ANTI_ALIAS_FLAG.or(Paint.FILTER_BITMAP_FLAG)) + paint.color = markerColorShadow + canvas.drawOval(0f, 0f, size.toFloat(), size.toFloat(), paint) + + val radius = size / 2.0f + + if (directional) { + val len = size * 5 / 16f + val width = size * 3 / 16f + //paint.color = markerColorOuter + //paint.alpha = Color.alpha(markerColorOuter) + val vertexes = floatArrayOf(radius, radius - gradRadius - len, + radius - width, radius - gradRadius, + radius + width, radius - gradRadius) + val indexes = shortArrayOf(0, 1, 2) + val colors = intArrayOf(markerColorOuter, markerColorOuter, markerColorOuter) + canvas.drawVertices(Canvas.VertexMode.TRIANGLES, + vertexes.size, vertexes, 0, + null, 0, + colors, 0, + indexes, 0, indexes.size, + paint) + } + + paint.color = markerColorOutline + canvas.drawOval(radius - gradRadius - outlineWidth, radius - gradRadius - outlineWidth, + radius + gradRadius + outlineWidth, radius + gradRadius + outlineWidth, + paint) + + val gradientPaint = Paint(Paint.ANTI_ALIAS_FLAG.or(Paint.FILTER_BITMAP_FLAG)) + gradientPaint.shader = RadialGradient(radius, radius, + (gradLocation * radius).coerceAtLeast(0.1f), + markerColorInner, markerColorOuter, Shader.TileMode.CLAMP) + canvas.drawOval(radius - gradRadius, radius - gradRadius, + radius + gradRadius, radius + gradRadius, + gradientPaint) + + return image + } + + private fun circleForLocation(location: LocationTrackerPoint?, defRadius: Double? = null): ShapeCircle? { + if (location == null) return null + val vc = baseController.get() ?: return null + val center = Point2d.FromDegrees(location.lonDeg, location.latDeg) + + val radius = location.horizontalAccuracy ?: defRadius ?: 0.0 + if (radius <= 0.0) { + return null + } + + val circle = ShapeCircle() + circle.setLoc(center) + circle.setSample(100) + + val top = coordOfPointAtTrueCourse(center, courseDeg = 0.0, distanceMeters = radius) + val right = coordOfPointAtTrueCourse(center, courseDeg = 90.0, distanceMeters = radius) + + val displayPtCenter = vc.displayPointFromGeo(Point3d(center.x, center.y, 0.0)) + val displayPtTop = vc.displayPointFromGeo(Point3d(top.x, top.y, 0.0)) + val displayPtRight = vc.displayPointFromGeo(Point3d(right.x, right.y, 0.0)) + + val vRad = hypot(displayPtTop.x - displayPtCenter.x, displayPtTop.y - displayPtCenter.y) + val hRad = hypot(displayPtRight.x - displayPtCenter.x, displayPtRight.y - displayPtCenter.y) + circle.setRadius((vRad + hRad) / 2.0) + + circle.setHeight(vc.zoomLimitMin / 100.0) + + return circle + } + + private fun degToRad(deg: Double): Double { return deg * Math.PI / 180.0 } + //private fun radToDeg(rad: Double): Double { return rad * 180.0 / Math.PI } + + private fun coordOfPointAtTrueCourse(center: Point2d, courseDeg: Double, distanceMeters: Double): Point2d { + // http://www.movable-type.co.uk/scripts/latlong.html + val tcRad = degToRad(courseDeg) + val lat1 = center.y + val lon1 = center.x + val earthRadMeters = 6371008.7714 + val dRad = distanceMeters / earthRadMeters + val latRad = asin(sin(lat1) * cos(dRad) + cos(lat1) * sin(dRad) * cos(tcRad)) + val cosLat = cos(latRad) + val lonRad = if (cosLat == 0.0) lon1 else ((lon1 - asin(sin(tcRad) * sin(dRad) / cos(latRad)) + Math.PI) % (2.0 * Math.PI)) - Math.PI + return Point2d(lonRad, latRad) + } + + private val simTask = object : Runnable { + override fun run() { + if (baseController.get() == null) { + stop() + return + } + + simulatorDelegate.get()?.let { + updateLocation(it.locationSimulatorGetLocation()) + } + if (simulating) { + handler?.postDelayed(this, (updateInterval * 1000).toLong()) + } + } + } + + private val baseController: WeakReference + private val useHeading: Boolean + + private var markerImages: Array? = null + private var directionalImages: Array? = null + private var imagesInvalidated = false + + private var markerInfo: MarkerInfo? = null + private var movingMarkerInfo: MarkerInfo? = null + private var circleInfo: ShapeInfo? = null + + private var markerObj: ComponentObject? = null + private var movingMarkerObj: ComponentObject? = null + private var circleObj: ComponentObject? = null + + private var simulating = false + private var updateInterval = 1.0 + set (value) { + field = value.coerceAtLeast(0.1) + } + + private var locationClient: FusedLocationProviderClient? = null + private var locationTask: Task? = null + private var locationUpdatePending = false + private var prevLocation: LocationTrackerPoint? = null + + private var handler: Handler? = null + + private val threadCurrent = RenderControllerInterface.ThreadMode.ThreadCurrent + private val threadAny = RenderControllerInterface.ThreadMode.ThreadAny +} \ No newline at end of file diff --git a/android/library/maply/src/main/java/com/mousebird/maply/LocationTrackerDelegate.kt b/android/library/maply/src/main/java/com/mousebird/maply/LocationTrackerDelegate.kt new file mode 100644 index 0000000000..3952575078 --- /dev/null +++ b/android/library/maply/src/main/java/com/mousebird/maply/LocationTrackerDelegate.kt @@ -0,0 +1,33 @@ +/* + * LocationTrackerDelegate.kt + * WhirlyGlobeLib + * + * Created by Tim Sylvester + * Copyright 2021 mousebird consulting + * + * 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. + * + */ +package com.mousebird.maply + +import com.google.android.gms.location.LocationAvailability + +interface LocationTrackerDelegate { + fun locationManagerDidFailWithError(tracker: LocationTracker, error: String) { + } + fun locationManagerDidChangeAuthorizationStatus(tracker: LocationTracker, availability: LocationAvailability?): LocationAvailability? { + return availability + } + fun locationManagerDidUpdateLocation(tracker: LocationTracker, location: LocationTrackerPoint?): LocationTrackerPoint? { + return location + } +} \ No newline at end of file diff --git a/android/library/maply/src/main/java/com/mousebird/maply/LocationTrackerPoint.kt b/android/library/maply/src/main/java/com/mousebird/maply/LocationTrackerPoint.kt new file mode 100644 index 0000000000..c5e58128d9 --- /dev/null +++ b/android/library/maply/src/main/java/com/mousebird/maply/LocationTrackerPoint.kt @@ -0,0 +1,32 @@ +/* + * LocationTrackerPoint.kt + * WhirlyGlobeLib + * + * Created by Tim Sylvester + * Copyright 2021 mousebird consulting + * + * 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. + * + */ +package com.mousebird.maply + +class LocationTrackerPoint { + var lonDeg: Double = 0.0 + var latDeg: Double = 0.0 + var elevation: Double? = null + var headingDeg: Double? = null + var speed: Double? = null + var horizontalAccuracy: Double? = null + var verticalAccuracy: Double? = null + var headingAccuracy: Double? = null + var speedAccuracy: Double? = null +} diff --git a/android/library/maply/src/main/java/com/mousebird/maply/MBTileFetcher.java b/android/library/maply/src/main/java/com/mousebird/maply/MBTileFetcher.java index 483de430fd..fc3fa8a59a 100644 --- a/android/library/maply/src/main/java/com/mousebird/maply/MBTileFetcher.java +++ b/android/library/maply/src/main/java/com/mousebird/maply/MBTileFetcher.java @@ -1,9 +1,8 @@ -/* - * MBTileFetcher.java +/* MBTileFetcher.java * WhirlyGlobeLib * * Created by jmnavarro on 3/21/19. - * Copyright 2011-2019 mousebird consulting + * Copyright 2011-2021 mousebird consulting * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -15,21 +14,19 @@ * 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. - * */ package com.mousebird.maply; import java.io.File; -import java.util.HashMap; -import java.util.TreeSet; -import java.util.concurrent.Semaphore; +import java.lang.reflect.Field; import android.database.Cursor; +import android.database.CursorWindow; +import android.database.sqlite.SQLiteBlobTooBigException; +import android.database.sqlite.SQLiteCursor; import android.database.sqlite.SQLiteDatabase; -import android.os.AsyncTask; -import android.os.Handler; -import android.os.HandlerThread; +import android.os.Build; import android.util.Log; /** @@ -46,14 +43,14 @@ public class MBTileFetcher extends SimpleTileFetcher /** * Coordinate system (probably Spherical Mercator) */ - CoordSystem coordSys = null; + CoordSystem coordSys; /** * Construct with the location of an MBTiles file. */ - public MBTileFetcher(File mbTileFile) + public MBTileFetcher(BaseController control,File mbTileFile) { - super("MBTiles Fetcher"); + super(control,"MBTiles Fetcher"); neverFail = true; coordSys = new SphericalMercatorCoordSystem(); @@ -76,35 +73,38 @@ public MBTileFetcher(File mbTileFile) // Query parameters and such - private static String TAG = MBTiles.class.getSimpleName(); - - private static String BOUNDS = "bounds"; - private static String MINZOOM = "minzoom"; - private static String MAXZOOM = "maxzoom"; - private static String MINZOOMLEVEL = "minZoomLevel"; - private static String MAXZOOMLEVEL = "maxZoomLevel"; - private static String TYPE = "type"; - private static String DESCRIPTION = "description"; - private static String FORMAT = "format"; - - private static String NAME = "name"; - private static String VALUE = "value"; - private static String PNG = "png"; - private static String JPG = "jpg"; - private static String JPEG = "jpeg"; - - private static String GET_TILE_SQL = "SELECT tile_data from tiles where zoom_level = ? AND tile_column = ? AND tile_row= ?;"; - private static String TILE_DATA = "tile_data"; + private static final String TAG = MBTiles.class.getSimpleName(); + + //private static final String BOUNDS = "bounds"; + private static final String MINZOOM = "minzoom"; + private static final String MAXZOOM = "maxzoom"; + private static final String MINZOOMLEVEL = "minZoomLevel"; + private static final String MAXZOOMLEVEL = "maxZoomLevel"; + //private static final String TYPE = "type"; + private static final String DESCRIPTION = "description"; + private static final String FORMAT = "format"; + + private static final String NAME = "name"; + private static final String VALUE = "value"; + //private static final String PNG = "png"; + private static final String JPG = "jpg"; + private static final String JPEG = "jpeg"; + + private static final String TILE_DATA = "tile_data"; + private static final String GET_TILE_SQL = + "SELECT " + TILE_DATA + " from tiles where zoom_level=? AND tile_column=? AND tile_row=?"; + final static String GET_META_SQL = "SELECT name, value FROM metadata"; + final static String GET_ZOOM_LIMITS_SQL = "SELECT MIN(zoom_level) AS minzoom, MAX(zoom_level) as maxzoom FROM tiles;"; + final static String GET_TILE_SIZE_SQL = "select max(length(" + TILE_DATA + ")) from tiles"; private SQLiteDatabase mbTileDb; - private boolean initialized; // Have we been correctly initialized - private boolean oldStyleDB; // Are we managing an old style database private boolean isJpg; // Are we containing jpg tiles (or png tiles) + @SuppressWarnings("FieldCanBeLocal") private String name = "UNSET"; // Name of the tile dataset private String description = "UNSET"; // Description of the tile dataset - private String type = "UNSET"; // Type of tile (overlay | baselayer) + //private String type = "UNSET"; // Type of tile (overlay | baselayer) /** @@ -113,23 +113,16 @@ public MBTileFetcher(File mbTileFile) */ private void init(File sqliteDb) { + name = sqliteDb.getName(); mbTileDb = SQLiteDatabase.openDatabase(sqliteDb.getAbsolutePath(), null, SQLiteDatabase.OPEN_READONLY); // We read metadata - String sql = "SELECT name, value FROM metadata;"; - - Cursor c = mbTileDb.rawQuery(sql, null); - - if (c.getCount() > 0) { - c.moveToFirst(); - - int nameIdx = c.getColumnIndexOrThrow(NAME); - int valueIdx = c.getColumnIndexOrThrow(VALUE); - - while (!c.isAfterLast()) { - + try (Cursor c = mbTileDb.rawQuery(GET_META_SQL, null)) { + final int nameIdx = c.getColumnIndexOrThrow(NAME); + final int valueIdx = c.getColumnIndexOrThrow(VALUE); + while (c.moveToNext()) { // What parameter are we reading - String meta = c.getString(nameIdx); + final String meta = c.getString(nameIdx); if (MAXZOOM.equals(meta)) { maxZoom = c.getInt(valueIdx); @@ -143,13 +136,9 @@ private void init(File sqliteDb) minZoom = c.getInt(valueIdx); } - if (FORMAT.equals(meta)) { + if (!isJpg && FORMAT.equals(meta)) { String format = c.getString(valueIdx); - if (JPG.equals(format)) { - isJpg = true; - } else if (JPEG.equals(format)) { - isJpg = true; - } + isJpg = (JPG.equals(format) || JPEG.equals(format)); } if (NAME.equals(meta)) { @@ -157,38 +146,27 @@ private void init(File sqliteDb) } if (DESCRIPTION.equals(meta)) { - description = c.getString(valueIdx); + description = c.getString(valueIdx); } - - c.moveToNext(); } } - c.close(); - - // If we did not get a minZoom and maxZoom, we need to get them the hard way if (minZoom == -1 || maxZoom == -1) { - - sql = "SELECT MIN(zoom_level) AS minzoom, MAX(zoom_level) as maxzoom FROM tiles;"; - - c = mbTileDb.rawQuery(sql, null); - - if (c.getCount() > 0) { - - c.moveToFirst(); - - minZoom = c.getInt(c.getColumnIndexOrThrow(MINZOOM)); - maxZoom = c.getInt(c.getColumnIndexOrThrow(MAXZOOM)); + try (Cursor c = mbTileDb.rawQuery(GET_ZOOM_LIMITS_SQL, null)) { + if (c.moveToNext()) { + minZoom = c.getInt(c.getColumnIndexOrThrow(MINZOOM)); + maxZoom = c.getInt(c.getColumnIndexOrThrow(MAXZOOM)); + Log.i(TAG, "Got MIN & MAX zoom the hard way"); + } } - - c.close(); - Log.i(TAG, "Got MIN & MAX zoom the hard way..."); } + super.initWithName(name, minZoom, maxZoom); + Log.v(TAG, String.format("Initialized MBTilesSource %s (%s)", name, description)); Log.v(TAG, String.format(" > Zoom %d -> %d", minZoom, maxZoom)); - Log.v(TAG, String.format(" > Type \"%s\"", type)); + //Log.v(TAG, String.format(" > Type \"%s\"", type)); Log.v(TAG, String.format(" > Format \"%s\"", (isJpg ? "jpg" : "png"))); } @@ -202,21 +180,76 @@ private void init(File sqliteDb) params[1] = Integer.toString(tileID.x); params[2] = Integer.toString(tileID.y); - Cursor c = mbTileDb.rawQuery(GET_TILE_SQL, params); - - int tileDataIdx = c.getColumnIndex(TILE_DATA); + // try to handle large blobs on old builds + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.P) { + if (!windowAdjusted) { + try (Cursor c = mbTileDb.rawQuery(GET_TILE_SIZE_SQL, new String[0])) { + if (c.moveToNext()) { + final int rowSize = c.getInt(0) + 1024; + final Field field = CursorWindow.class.getDeclaredField("sCursorWindowSize"); + if (field != null) { + field.setAccessible(true); + final int windowSize = field.getInt(null); + if (windowSize < rowSize) { + field.setInt(null, rowSize); + Log.v(tag, String.format("Adjusting default cursor window size from %d to %d", windowSize, rowSize)); + } + } + } + } catch (Exception ex) { + Log.w(tag, "Failed to adjust cursor window size", ex); + } + windowAdjusted = true; + } + } - if (c.getCount() > 0) { - c.moveToFirst(); + while (true) { + boolean usingWindow = false; + try (Cursor c = mbTileDb.rawQuery(GET_TILE_SQL, params)) { + + // Set up a custom cursor window, if necessary, and possible. + if (maxWindowBytes > 0 && + Build.VERSION.SDK_INT >= Build.VERSION_CODES.P && + c instanceof SQLiteCursor) { + SQLiteCursor cc = (SQLiteCursor)c; + cc.setFillWindowForwardOnly(true); + + // Can we reuse these? + CursorWindow window = new CursorWindow("mbtile", maxWindowBytes); + cc.setWindow(window); + usingWindow = true; + } - byte[] data = c.getBlob(tileDataIdx); - c.close(); - return data; -// Log.v(TAG, String.format("Returned tile for Z=%s, X=%d, Y=%d", tileID.level, tileID.x, tileID.y)); + if (c.moveToNext()) { + final int tileDataIdx = c.getColumnIndexOrThrow(TILE_DATA); + final byte[] data = c.getBlob(tileDataIdx); + //Log.v(tag, String.format("Returned tile for Z=%s, X=%d, Y=%d (%d bytes)", tileID.level, tileID.x, tileID.y, data.length)); + return data; + } + } catch (SQLiteBlobTooBigException ignored) { + // Grow the size and try again, up to a reasonable limit + if (usingWindow && maxWindowBytes < maxWindowSize) { + maxWindowBytes *= 2; + Log.w(tag, String.format("Adjusting cursor window size to %d", maxWindowBytes)); + continue; + } else if (maxWindowBytes < 0) { + // The default window size didn't work, so try setting one explicitly. + Log.w(tag, String.format("Adjusting cursor window size to %d", defWindowSize)); + maxWindowBytes = defWindowSize; + continue; + } + } + break; } - c.close(); - return null; } + + private int maxWindowBytes = -1; + private boolean windowAdjusted = false; + + // see `com.android.internal.R.integer.config_cursorWindowSize` + private final static int defWindowSize = 4 * 1024 * 1024; + private final static int maxWindowSize = 32 * defWindowSize; + private final static String tag = MBTileFetcher.class.getSimpleName(); } diff --git a/android/library/maply/src/main/java/com/mousebird/maply/MapAnimateTranslate.java b/android/library/maply/src/main/java/com/mousebird/maply/MapAnimateTranslate.java index 270a6e697d..39631d80ec 100644 --- a/android/library/maply/src/main/java/com/mousebird/maply/MapAnimateTranslate.java +++ b/android/library/maply/src/main/java/com/mousebird/maply/MapAnimateTranslate.java @@ -34,12 +34,45 @@ public class MapAnimateTranslate implements MapView.AnimationDelegate Point3d startLoc = null; Point3d endLoc = null; Point2d viewBounds[] = null; + Double startRot = null; + Double dRot = null; double startTime,endTime; - + /** * Construct a translation with input parameters. You would set this up and then hand it * over to a view for use. - * + * + * @param inView The view we're tied to. + * @param inRender Renderer we're using. + * @param newLoc New location we're translating to. + * @param newRot New rotation we want + * @param duration How long we want the animation to go. + * @param inBounds Bounding box we want to keep the animation within. + */ + MapAnimateTranslate(MapView inView,RenderController inRender,Point3d newLoc,Double newRot,float duration,Point2d inBounds[]) + { + this(inView,inRender,newLoc,duration,inBounds); + + if (view != null && newRot != null) { + startRot = view.getRot(); + + // If the old and new rotations are within 180 degrees, just interpolate. + // Otherwise, we need to go around the other way. + // Note that we assume both angles are normalized. + dRot = newRot - startRot; + if (Math.abs(dRot) < 1.0e-6) { + // Don't generate a bunch of rotations for minuscule offsets that won't make any difference + dRot = 0.0; + } else if (Math.abs(dRot) > Math.PI) { + dRot += ((dRot > 0) ? -2 : 2) * Math.PI; + } + } + } + + /** + * Construct a translation with input parameters. You would set this up and then hand it + * over to a view for use. + * * @param inView The view we're tied to. * @param inRender Renderer we're using. * @param newLoc New location we're translating to. @@ -53,7 +86,8 @@ public class MapAnimateTranslate implements MapView.AnimationDelegate endLoc = newLoc; viewBounds = inBounds; startLoc = view.getLoc(); - + startRot = view.getRot(); + startTime = System.currentTimeMillis()/1000.0; endTime = startTime+duration; } @@ -61,21 +95,30 @@ public class MapAnimateTranslate implements MapView.AnimationDelegate @Override public void updateView(MapView view) { - if (startTime == 0.0 || renderer == null) + if (startTime == 0.0 || renderer == null || endTime == startTime) return; - double curTime = System.currentTimeMillis()/1000.0; - if (curTime > endTime) - { - curTime = endTime; - startTime = 0; - view.cancelAnimation(); - } - + double curTime = Math.min(endTime, System.currentTimeMillis()/1000.0); + // Calculate location double t = (curTime-startTime)/(endTime-startTime); Point3d newPos = endLoc.subtract(startLoc).multiplyBy(t).addTo(startLoc); - if (MapGestureHandler.withinBounds(view, renderer.frameSize, newPos, viewBounds)) + if (endLoc.getZ() <= 0.0) { + // Not doing height, leave it alone. + newPos.setValue(newPos.getX(), newPos.getY(), startLoc.getZ()); + } + if (MapGestureHandler.withinBounds(view, renderer.frameSize, newPos, viewBounds)) { view.setLoc(newPos); + } + + if (startRot != null && dRot != null && dRot != 0.0) { + view.setRot(startRot + t * dRot); + } + + if (curTime >= endTime) + { + startTime = 0; + view.cancelAnimation(); + } } } diff --git a/android/library/maply/src/main/java/com/mousebird/maply/MapController.java b/android/library/maply/src/main/java/com/mousebird/maply/MapController.java index bf3e722cf8..b758a7a810 100644 --- a/android/library/maply/src/main/java/com/mousebird/maply/MapController.java +++ b/android/library/maply/src/main/java/com/mousebird/maply/MapController.java @@ -3,7 +3,7 @@ * WhirlyGlobeLib * * Created by Steve Gifford on 6/2/14. - * Copyright 2011-2014 mousebird consulting + * Copyright 2011-2021 mousebird consulting * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -284,6 +284,24 @@ boolean checkCoverage(Mbr mbr,MapView theMapView,double height) return true; } + /** + * Get the zoom limits for the map. + */ + @Override + public double getZoomLimitMin() + { + return (gestureHandler != null) ? gestureHandler.zoomLimitMin : 0.0; + } + + /** + * Get the zoom limits for the map. + */ + @Override + public double getZoomLimitMax() + { + return (gestureHandler != null) ? gestureHandler.zoomLimitMin : Double.POSITIVE_INFINITY; + } + /** * Set the zoom limits for the globe. * @param inMin Closest the user is allowed to zoom in. @@ -404,7 +422,20 @@ public Point3d getPositionGeo() Point3d geoLoc = mapView.coordAdapter.coordSys.localToGeographic(loc); return new Point3d(geoLoc.getX(),geoLoc.getY(),loc.getZ()); } - + + /** + * Animate to a new view position + * @param x Horizontal location of the center of the screen in geographic radians (not degrees). + * @param y Vertical location of the center of the screen in geographic radians (not degrees). + * @param howLong Time (in seconds) to animate. + */ + public void animatePositionGeo(final double x,final double y,final double howLong) + { + if (mapView != null) { + animatePositionGeo(x, y, null, null, howLong); + } + } + /** * Animate to a new view position * @param x Horizontal location of the center of the screen in geographic radians (not degrees). @@ -413,6 +444,86 @@ public Point3d getPositionGeo() * @param howLong Time (in seconds) to animate. */ public void animatePositionGeo(final double x,final double y,final double z,final double howLong) + { + if (mapView != null) { + animatePositionGeo(x, y, z, null, howLong); + } + } + + /** + * Animate to a new view position + * @param x Horizontal location of the center of the screen in geographic radians (not degrees). + * @param y Vertical location of the center of the screen in geographic radians (not degrees). + * @param z Height above the map in display units. + * @param rot Map rotation in radians + * @param howLong Time (in seconds) to animate. + */ + public void animatePositionGeo(final double x,final double y,final Double z,final Double rot,final double howLong) + { + animatePositionGeo(new Point3d(x,y,(z != null)?z:0.0),rot,howLong); + } + + /** + * Animate to a new view position + * @param x Horizontal location of the center of the screen in geographic radians (not degrees). + * @param y Vertical location of the center of the screen in geographic radians (not degrees). + * @param z Height above the map in display units. + * @param rot Map rotation in radians + * @param howLong Time (in seconds) to animate. + */ + public void animatePositionGeo(final Point2d loc,final Double z,final Double rot,final double howLong) + { + animatePositionGeo(new Point3d(loc.getX(),loc.getY(),(z != null)?z:0.0),rot,howLong); + } + + /** + * Animate to a new view position + * @param targetGeoLoc Location of the center of the screen in geographic radians (not degrees). z = height above the map in display units. + * @param rot Map rotation in radians + * @param howLong Time (in seconds) to animate. + */ + public void animatePositionGeo(final Point3d targetGeoLoc,final Double rot,final double howLong) + { + if (targetGeoLoc == null || !running || mapView == null || renderWrapper == null || + renderWrapper.maplyRender == null || renderControl.frameSize == null) + return; + + if (!rendererAttached) { + addPostSurfaceRunnable(new Runnable() { + @Override + public void run() { + animatePositionGeo(targetGeoLoc,rot,howLong); + } + }); + return; + } + + Point3d localCoord = mapView.coordAdapter.coordSys.geographicToLocal(targetGeoLoc); + Point3d newPoint = new Point3d(localCoord.getX(),localCoord.getY(), targetGeoLoc.getZ()); + MapAnimateTranslate dg = new MapAnimateTranslate(mapView, renderControl, newPoint, rot, (float)howLong, viewBounds); + + mapView.cancelAnimation(); + mapView.setAnimationDelegate(dg); + } + + /** + * Animate to a new location, placing that location at a specified position on the screen relative to the normal center position + * @param geoLoc Location in geographic radians (not degrees), z = height in display units + * @param offset The offset from the viewport center + * @param howLong Time (in seconds) to animate. + */ + public void animatePositionGeo(final Point3d geoLoc,final Point2d offset,final double howLong) { + animatePositionGeo(geoLoc,offset,null,howLong); + } + + /** + * Animate to a new location, placing that location at a specified position on the screen relative to the normal center position + * @param targetGeoLoc Location in geographic radians (not degrees), z = height in display units + * @param offset The offset from the viewport center + * @param targetRot Map rotation in radians + * @param howLong Time (in seconds) to animate. + */ + public void animatePositionGeo(final Point3d targetGeoLoc,final Point2d offset,final Double targetRot,final double howLong) { if (!running || mapView == null || renderWrapper == null || renderWrapper.maplyRender == null || renderControl.frameSize == null) return; @@ -421,15 +532,48 @@ public void animatePositionGeo(final double x,final double y,final double z,fina addPostSurfaceRunnable(new Runnable() { @Override public void run() { - animatePositionGeo(x,y,z,howLong); + animatePositionGeo(targetGeoLoc,offset,targetRot,howLong); } }); return; } mapView.cancelAnimation(); - Point3d geoCoord = mapView.coordAdapter.coordSys.geographicToLocal(new Point3d(x,y,0.0)); - mapView.setAnimationDelegate(new MapAnimateTranslate(mapView, renderControl, new Point3d(geoCoord.getX(),geoCoord.getY(),z), (float) howLong, viewBounds)); + + // save current view state + Point3d curLoc = mapView.getLoc(); + double curRot = mapView.getRot(); + + CoordSystemDisplayAdapter coordAdapter = mapView.coordAdapter; + CoordSystem coordSys = (coordAdapter != null) ? coordAdapter.coordSys : null; + if (curLoc == null || coordSys == null) { + return; + } + + // Convert to local + Point3d targetLocalCoord = coordSys.geographicToLocal(targetGeoLoc); + // The height has been discarded, reset it + targetLocalCoord.setValue(targetLocalCoord.getX(), targetLocalCoord.getY(), targetGeoLoc.getZ()); + // Go there + mapView.setLoc(targetLocalCoord,false); + if (targetRot != null) { + mapView.setRot(targetRot); + } + + // Find the location of the offset point in the new state + Point2d geoCoord = geoPointFromScreen(getFrameSize().multiplyBy(0.5).addTo(offset)); + // Fix z + + // todo: check if within bounds +// nextState.pos = MaplyCoordinateDMakeWithMaplyCoordinate(geoCoord); +// [self setViewStateInternal:nextState runViewUpdates:false]; +// bool valid = [self withinBounds:oldLoc view:wrapView renderer:sceneRenderer mapView:mapView newCenter:&newCenter]; + + // restore current view state + mapView.setLoc(curLoc,false); + mapView.setRot(curRot); + + animatePositionGeo(geoCoord.getX(), geoCoord.getY(), targetGeoLoc.getZ(), targetRot, howLong); } /** diff --git a/android/library/maply/src/main/java/com/mousebird/maply/MapView.java b/android/library/maply/src/main/java/com/mousebird/maply/MapView.java index 9c2fab6896..c3f33877e4 100644 --- a/android/library/maply/src/main/java/com/mousebird/maply/MapView.java +++ b/android/library/maply/src/main/java/com/mousebird/maply/MapView.java @@ -134,16 +134,24 @@ public boolean isAnimating() // Set the view location from a Point3d void setLoc(Point3d loc) + { + setLoc(loc,true); + } + + // Set the view location from a Point3d + void setLoc(Point3d loc, boolean runViewUpdates) { double z = loc.getZ(); z = Math.min(maxHeightAboveSurface(), z); z = Math.max(minHeightAboveSurface(), z); - + setLoc(loc.getX(),loc.getY(),z); - - runViewUpdates(); + + if (runViewUpdates) { + runViewUpdates(); + } } - + // Calculate the point on the view plane given the screen location native Point3d pointOnPlaneFromScreen(Point2d screenPt,Matrix4d viewModelMatrix,Point2d frameSize,boolean clip); // Calculate the point on the screen from a point on the view plane diff --git a/android/library/maply/src/main/java/com/mousebird/maply/MapboxKindaMap.kt b/android/library/maply/src/main/java/com/mousebird/maply/MapboxKindaMap.kt index 0d36b91e1a..ed5a8822be 100644 --- a/android/library/maply/src/main/java/com/mousebird/maply/MapboxKindaMap.kt +++ b/android/library/maply/src/main/java/com/mousebird/maply/MapboxKindaMap.kt @@ -15,6 +15,7 @@ import java.io.IOException import java.io.OutputStream import java.lang.ref.WeakReference import java.net.URL +import kotlin.collections.ArrayList /** * Convenience class for loading a Mapbox-style vector tiles-probably kinda map. @@ -22,128 +23,121 @@ import java.net.URL * Set the various settings before it gets going to modify how it works. * Callbacks control various pieces that might need to be intercepted. */ -public open class MapboxKindaMap { - var styleURL : Uri? = null - var control : WeakReference? = null +open class MapboxKindaMap( + var styleSheetJSON: String?, + var styleURL: Uri?, + var localMBTiles: Sequence? = null, + inControl: BaseController, + var styleSettings: VectorStyleSettings = defaultStyle()) { + + companion object { + fun defaultStyle() = VectorStyleSettings().apply { + baseDrawPriority = QuadImageLoaderBase.BaseDrawPriorityDefault + 1000 + drawPriorityPerLevel = 100 + } + } + + constructor(styleURL: Uri, control: BaseController, styleSettings: VectorStyleSettings = defaultStyle()) : + this(null, styleURL, null, control, styleSettings) + constructor(styleJSON: String, control: BaseController, styleSettings: VectorStyleSettings = defaultStyle()) : + this(styleJSON, null, null, control, styleSettings) + constructor(styleURL: Uri, localMBTilesFile: File, control: BaseController, styleSettings: VectorStyleSettings = defaultStyle()) : + this(null, styleURL, sequenceOf(localMBTilesFile), control, styleSettings) + constructor(styleJSON: String, localMBTilesFile: File, control: BaseController, styleSettings: VectorStyleSettings = defaultStyle()) : + this(styleJSON, null, sequenceOf(localMBTilesFile), control, styleSettings) + + var styleSheet: MapboxVectorStyleSet? = null + var styleSheetImage: MapboxVectorStyleSet? = null + var styleSheetVector: MapboxVectorStyleSet? = null + var spriteJSON: ByteArray? = null + var spritePNG: Bitmap? = null + var mapboxInterp: MapboxVectorInterpreter? = null + var loader: QuadLoaderBase? = null + var offlineRender: RenderController? = null + var lineScale = 0.0 + var textScale = 0.0 /* If set, we build an image/vector hybrid where the polygons go into * the image layer and the linears and points are represented as vectors * Otherwise, it's all put in a PagingLayer as vectors. This is better for an overlay. */ - public var imageVectorHybrid = true + var imageVectorHybrid = true - /* If set, we'll sort all polygons into the background - * Works well zoomed out, less enticing zoomed in + /* If set, we'll sort all polygons into the background. + * Works well zoomed out, less enticing zoomed in. */ - public var backgroundAllPolys = true + var backgroundAllPolys = true - // If set, a top level directory where we'll cache everything - public var cacheDir: File? = null + /** + * If set, we'll fetch and use the sources from the style sheet. + * If not set, the sources have to be provided externally. + */ + var fetchSources = true - /* You can override the file loaded for a particular purpose. + /** + * If set, a top level directory where we'll cache everything + */ + var cacheDir: File? = null + + /** + * You can override the file loaded for a particular purpose. * This includes: the TileJSON files, sprite sheets, and the style sheet itself * For example, if you want to load from the bundle, but not have to change * anything in the style sheet, just do this */ - public open fun mapboxURLFor(file: Uri) : Uri { - return file + var mapboxURLFor: (Uri) -> Uri = { + file: Uri -> + file } - /* You can override the font to use for a given - * font name in the style. Font names in the style often don't map - * directly to local font names. + /** + * You can override the font to use for a given font name in the style. + * Font names in the style often don't map directly to local font names. */ - public open fun mapboxFontFor(name: String) : String { - return name + var mapboxFontFor: (String) -> String = { + name: String -> + name } - /* This is the importance value used in the sampler for loading - * It's roughly the maximum number of pixels you want a tile to be on the screen - * before you load its children. 1024 is good for vector tiles, 256 good for image tiles + /** + * If set, this will be called right after everything is set up + * This is after all the configuration files are fetched so + * you can make any final tweaks to loading objects here */ - public var minImportance = 1024.0 * 1024.0 + var postSetup: (MapboxKindaMap) -> Unit = { } - constructor(styleURL: Uri, control: BaseController) { - this.control = WeakReference(control) - this.styleURL = styleURL - styleSettings.baseDrawPriority = QuadImageLoaderBase.BaseDrawPriorityDefault+1000 - styleSettings.drawPriorityPerLevel = 1 - } - - constructor(styleJSON: String, control: BaseController) { - this.control = WeakReference(control) - this.styleSheetJSON = styleJSON - styleSettings.baseDrawPriority = QuadImageLoaderBase.BaseDrawPriorityDefault+1000 - styleSettings.drawPriorityPerLevel = 1 - } - - public var styleSettings = VectorStyleSettings() - public var styleSheet: MapboxVectorStyleSet? = null - public var styleSheetImage: MapboxVectorStyleSet? = null - public var styleSheetVector: MapboxVectorStyleSet? = null - public var styleSheetJSON: String? = null - public var spriteJSON: ByteArray? = null - public var spritePNG: Bitmap? = null + /** + * This is the importance value used in the sampler for loading + * It's roughly the maximum number of pixels you want a tile to be on the screen + * before you load its children. + * 1024^2 is good for vector tiles, 256^2 is good for image tiles + */ + var minImportance = 1024.0 * 1024.0 // Information about the sources as we fetch them - protected var outstandingFetches: ArrayList = ArrayList() private fun addTask(task: Call) { - val theControl = control?.get() - if (theControl == null) - return - - theControl.getActivity().runOnUiThread { + control.get()?.getActivity()?.runOnUiThread { outstandingFetches.add(task) } } private fun clearTask(task: Call) { - val theControl = control?.get() - if (theControl == null) - return - - theControl.getActivity().runOnUiThread { + control.get()?.getActivity()?.runOnUiThread { outstandingFetches.remove(task) } } - private var finished = false - // Check if we've finished loading stuff protected fun checkFinished() { - if (finished) - return - - val theControl = control?.get() - if (theControl == null) - return - - theControl.getActivity().runOnUiThread(object: Runnable { - override fun run() { - if (finished) - return - - var done = true - - // If any of the outstanding fetches are running, don't start the map - outstandingFetches.forEach { - if (it != null) - done = false - } - - // All done, so start - if (done) { - finished = true - startLoader() - } + // Start the map if no outstanding fetches are running + control.get()?.getActivity()?.runOnUiThread { + if (!finished && outstandingFetches.all { it == null }) { + finished = true + startLoader() } - }) + } } - public var mapboxInterp: MapboxVectorInterpreter? = null - public var loader: QuadLoaderBase? = null - public var offlineRender: RenderController? = null - // If we're using a cache dir, look for the file there protected fun cacheResolve(url: Uri) : URL { val fileRef = cacheName(url) @@ -156,9 +150,7 @@ public open class MapboxKindaMap { // Generate a workable cache file path protected fun cacheName(url: Uri) : File? { - val theCacheDir = cacheDir - if (theCacheDir == null) - return null + val theCacheDir = cacheDir ?: return null // If the cache dir doesn't exist, we need to create it if (!theCacheDir.exists() && !theCacheDir.createNewFile()) @@ -169,7 +161,7 @@ public open class MapboxKindaMap { return url.toFile() // Make up a cache name from the URL - val cacheName = url.toString().replace("/","_").replace(":","_").replace("?","_").replace(".","_") + val cacheName = cacheNamePattern.replace(url.toString(), "_") return File(theCacheDir,cacheName) } @@ -199,18 +191,14 @@ public open class MapboxKindaMap { * It'll go fetch whatever it needs to fetch until it gets everything. * Then it'll start the actual loader. */ - public fun start() { - val theControl = control?.get() - if (theControl == null) - return + fun start() { + val theControl = control.get() ?: return if (styleSheetJSON != null) { // Style sheet is already loaded, so skip that part processStyleSheet() } else { - val theStyleURL = styleURL - if (theStyleURL == null) - return + val theStyleURL = styleURL ?: return // Dev might be overriding the source val resolvedURL = cacheResolve(mapboxURLFor(theStyleURL)) @@ -254,7 +242,7 @@ public open class MapboxKindaMap { // Style sheet has been loaded fun processStyleSheet() { - val theControl = control?.get() + val theControl = control.get() if (theControl == null || (styleURL == null && styleSheetJSON == null)) return val client = theControl.getHttpClient() @@ -262,11 +250,11 @@ public open class MapboxKindaMap { // Fetch what we need to for the sources var success = true - styleSheet?.sources?.forEach { - val source = it + val sources = if (fetchSources) styleSheet?.sources else null + sources?.forEach { source -> // If the tile spec isn't embedded, we need to go get it if (source.tileSpec == null && success) { - if (source.url.isEmpty()) { + if (source.url?.isEmpty() != false) { Log.w("Maply", "Expecting either URL or tile info for a source. Giving up.") success = false } @@ -285,10 +273,13 @@ public open class MapboxKindaMap { } override fun onResponse(call: Call, response: Response) { - val jsonStr2 = response.body()?.string() - val resp = AttrDictionary() - resp.parseFromJSON(jsonStr2) - source.tileSpec = resp + response.body()?.string()?.let { jsonStr2 -> + val resp = AttrDictionary() + // todo: find a better way to convert from `AttrDictionary` to `AttrDictionaryEntry`, like an `asEntry` method. + if (resp.parseFromJSON("{\"tileSpec\":[$jsonStr2]}")) { + source.tileSpec = resp.getArray("tileSpec") + } + } val newUri = Uri.parse(url.toString()) cacheFile(newUri,response.body()!!.bytes()) @@ -306,20 +297,32 @@ public open class MapboxKindaMap { // Everything has been fetched, so fire up the loader protected fun startLoader() { - val theControl = control?.get() - val theStyleSheet = styleSheet - if (theControl == null || theStyleSheet == null) - return + val theControl = control.get() ?: return // Figure out overall min/max zoom var minZoom = 10000 var maxZoom = -1 - styleSheet?.sources?.forEach { - val source = it - if (source.tileSpec.hasField("minzoom")) - minZoom = source.tileSpec.getInt("minzoom") - if (source.tileSpec.hasField("maxzoom")) - maxZoom = source.tileSpec.getInt("maxzoom") + if (fetchSources) { + styleSheet?.sources?.forEach { source -> + source.tileSpec?.forEach { specItem -> + specItem.dict?.let { + minZoom = (it.getInt("minzoom") ?: minZoom).coerceAtMost(minZoom) + maxZoom = (it.getInt("maxzoom") ?: maxZoom).coerceAtLeast(maxZoom) + } + } + } + } + + val tileInfos = ArrayList() + val localFetchers = ArrayList() + localMBTiles?.forEach { item -> + val fetcher = MBTileFetcher(theControl,item) + localFetchers.add(fetcher) + fetcher.tileInfo?.also { + tileInfos.add(it) + minZoom = it.minZoom.coerceAtMost(minZoom) + maxZoom = it.maxZoom.coerceAtLeast(maxZoom) + } } // Sources probably weren't set up @@ -328,138 +331,169 @@ public open class MapboxKindaMap { return } + // Adjustment for loading (512 vs 1024 or so) + styleSettings.lineScale = if (lineScale > 0) lineScale else minImportance / (512.0 * 512.0) / 2 + + // Similar adjustment for text + styleSettings.textScale = if (textScale > 0) textScale else minImportance / (768.0 * 768.0) / 2 + + // Parameters describing how we want a globe broken down + val sampleParams = SamplingParams() + sampleParams.coordSystem = SphericalMercatorCoordSystem() + sampleParams.minImportance = minImportance + sampleParams.singleLevel = true + sampleParams.coverPoles = (theControl is GlobeController) + sampleParams.edgeMatching = (theControl is GlobeController) + sampleParams.minZoom = minZoom + sampleParams.maxZoom = maxZoom + //sampleParams.reportedMaxZoom = 24 + // If we don't have a solid under-layer for each tile, we can't really + // keep level 0 around all the time + if (!backgroundAllPolys) { + sampleParams.setForceMinLevel(false) + } else { + sampleParams.minImportanceTop = 0.0 + } + // Image/vector hybrids draw the polygons into a background image if (imageVectorHybrid) { - // Put together the tileInfoNew objects - var tileInfos: ArrayList = ArrayList() - styleSheet?.sources?.forEach { - val source = it - if (source.tileSpec.hasField("tiles")) { - val tiles = source.tileSpec.getArray("tiles") - val tileSrc = tiles.get(0).string - val tileSource = RemoteTileInfoNew(tileSrc, source.tileSpec.getInt("minzoom"), source.tileSpec.getInt("maxzoom")) - if (cacheDir != null) { - tileSource.cacheDir = File(cacheDir!!,tileSrc.replace("/","_"). - replace(":","_").replace("?","_").replace(".","_"). - replace("{","_").replace("}","_")) - } - tileInfos.add(tileSource) - } else { - Log.w("Maply", "TileInfo source missing tiles. Skipping.") - } + startHybridLoader(sampleParams, tileInfos) + } else { + startSimpleLoader(sampleParams, tileInfos, localFetchers) + } + + (control.get() as? MapController)?.let { mc -> + // Set the background clear to the color at level 0 + // TODO: Make this change by level + styleSheetVector?.backgroundColorForZoom(0.0)?.let { + mc.setClearColor(it) } + } - // Parameters describing how we want a globe broken down - val sampleParams = SamplingParams() - sampleParams.coordSystem = SphericalMercatorCoordSystem() - sampleParams.minImportance = minImportance - sampleParams.singleLevel = true - // If we don't have a solid underlayer for each tile, we can't really - // keep level 0 around all the time - if (!backgroundAllPolys) { - sampleParams.setForceMinLevel(false) - } - if (control?.get() is GlobeController) { - sampleParams.coverPoles = true - sampleParams.edgeMatching = true - } else { - sampleParams.coverPoles = false - sampleParams.edgeMatching = false - } - sampleParams.minZoom = minZoom - sampleParams.maxZoom = maxZoom - - if (styleSheetJSON == null) - return - - if (backgroundAllPolys) { - // Set up an offline renderer and a Mapbox vector style handler to render to it - val imageSizeWidth = 512 - val imageSizeHeight = 512 - val offlineRender = RenderController(theControl.renderControl, imageSizeWidth,imageSizeHeight) - this.offlineRender = offlineRender - val imageStyleSettings = VectorStyleSettings() - imageStyleSettings.baseDrawPriority = styleSettings.baseDrawPriority - // TODO: Do we need this? -// imageStyleSettings.arealShaderName = kMaplyShaderDefaultTriNoLighting + postSetup(this) + } - // We only want the polygons in the image - val imageStyleDict = AttrDictionary() - imageStyleDict.parseFromJSON(styleSheetJSON) - val imageLayers = imageStyleDict.getArray("layers") - var newImageLayers = ArrayList() - for (layer in imageLayers) { - if (layer.type == AttrDictionaryEntry.Type.DictTypeDictionary) { - val layerDict = layer.dict - val type = layerDict.getString("type") - if (type != null && (type == "background" || type == "fill")) - newImageLayers.add(layer) - } - } - imageStyleDict.setArray("layers",newImageLayers.toTypedArray()) - styleSheetImage = MapboxVectorStyleSet(imageStyleDict, styleSettings, theControl.activity.resources.displayMetrics, offlineRender) - offlineRender.setClearColor(styleSheetImage!!.backgroundColorForZoom(0.0)) - } + private fun startSimpleLoader(sampleParams: SamplingParams, + tileInfos: ArrayList, + localFetchers: ArrayList) { + val control = control.get() ?: return - val vectorStyleDict = AttrDictionary() - vectorStyleDict.parseFromJSON(styleSheetJSON) - - // The polygons only go into the background in this case - if (backgroundAllPolys) { - val vectorLayers = vectorStyleDict.getArray("layers") - var newVectorLayers = ArrayList() - for (layer in vectorLayers) { - if (layer.type == AttrDictionaryEntry.Type.DictTypeDictionary) { - val layerDict = layer.dict - val type = layerDict.getString("type") - if (type != null && (type != "background" && type != "fill")) - newVectorLayers.add(layer) + // todo: deal with sprite sheets + + val vectorStyleDict = AttrDictionary().apply { + parseFromJSON(styleSheetJSON) + } + + val metrics = control.activity.resources.displayMetrics + styleSheetVector = MapboxVectorStyleSet(vectorStyleDict, styleSettings, metrics, control) + + mapboxInterp = MapboxVectorInterpreter(styleSheetVector, control) + loader = QuadPagingLoader(sampleParams, tileInfos.toTypedArray(), mapboxInterp, control).also { + it.flipY = false + if (localFetchers.isNotEmpty()) { + it.setTileFetcher(localFetchers[0]) + } + } + } + + private fun startHybridLoader(sampleParams: SamplingParams, tileInfos: ArrayList) { + val control = control.get() ?: return + // Put together the tileInfoNew objects + styleSheet?.sources?.forEach { source -> + source.tileSpec?.forEach { tileSpecEntry -> + tileSpecEntry.dict?.also { tileSpec -> + if (tileSpec.hasField("tiles")) { + val minZoom = tileSpec.getInt("minzoom") + val maxZoom = tileSpec.getInt("maxzoom") + val tiles = tileSpec.getArray("tiles") + val tileSrc = tiles[0].string + val tileSource = RemoteTileInfoNew(tileSrc, minZoom, maxZoom) + if (cacheDir != null) { + val cacheName = cacheNamePattern.replace(tileSrc, "_") + tileSource.cacheDir = File(cacheDir!!, cacheName) + } + tileInfos.add(tileSource) + } else { + Log.w("Maply", "TileInfo source missing tiles. Skipping.") } } - vectorStyleDict.setArray("layers", newVectorLayers.toTypedArray()) - } - styleSheetVector = MapboxVectorStyleSet(vectorStyleDict, styleSettings, theControl.activity.resources.displayMetrics, theControl) - - if (control?.get() !is GlobeController) { - // Set the background clear to the color at level 0 - // TODO: Make this change by level - val color = styleSheetVector?.backgroundColorForZoom(0.0) - if (color != null) - theControl.setClearColor(color) } + } - if (offlineRender != null && styleSheetImage != null) { - mapboxInterp = MapboxVectorInterpreter(styleSheetImage, offlineRender, styleSheetVector, theControl) - } else { - mapboxInterp = MapboxVectorInterpreter(styleSheetVector, theControl) - } - if (mapboxInterp == null) { - Log.w("Maply", "Failed to set up Mapbox interpreter. Nothing will appear.") - stop() + if (styleSheetJSON == null) + return + + if (backgroundAllPolys) { + // Set up an offline renderer and a Mapbox vector style handler to render to it + val imageSizeWidth = 512 + val imageSizeHeight = 512 + val offlineRender = RenderController(control.renderControl, imageSizeWidth,imageSizeHeight) + this.offlineRender = offlineRender + val imageStyleSettings = VectorStyleSettings() + imageStyleSettings.baseDrawPriority = styleSettings.baseDrawPriority + // TODO: Do we need this? +// imageStyleSettings.arealShaderName = kMaplyShaderDefaultTriNoLighting + + // We only want the polygons in the image + val imageStyleDict = AttrDictionary() + imageStyleDict.parseFromJSON(styleSheetJSON) + val imageLayers = imageStyleDict.getArray("layers") + val newImageLayers = ArrayList() + for (layer in imageLayers) { + if (layer.type == AttrDictionaryEntry.Type.DictTypeDictionary) { + val layerDict = layer.dict + val type = layerDict.getString("type") + if (type != null && (type == "background" || type == "fill")) + newImageLayers.add(layer) + } } + imageStyleDict.setArray("layers",newImageLayers.toTypedArray()) + styleSheetImage = MapboxVectorStyleSet(imageStyleDict, styleSettings, control.activity.resources.displayMetrics, offlineRender) + offlineRender.setClearColor(styleSheetImage!!.backgroundColorForZoom(0.0)) + } - // TODO: Handle more than one source - if (backgroundAllPolys) { - val imageLoader = QuadImageLoader(sampleParams, tileInfos[0], theControl) - imageLoader.setLoaderInterpreter(mapboxInterp!!) - loader = imageLoader - } else { - val vecLoader = QuadPagingLoader(sampleParams, tileInfos[0], mapboxInterp, theControl) - loader = vecLoader + val vectorStyleDict = AttrDictionary() + vectorStyleDict.parseFromJSON(styleSheetJSON) + + // The polygons only go into the background in this case + if (backgroundAllPolys) { + val vectorLayers = vectorStyleDict.getArray("layers") + val newVectorLayers = ArrayList() + for (layer in vectorLayers) { + if (layer.type == AttrDictionaryEntry.Type.DictTypeDictionary) { + val layerDict = layer.dict + val type = layerDict.getString("type") + if (type != null && (type != "background" && type != "fill")) + newVectorLayers.add(layer) + } } + vectorStyleDict.setArray("layers", newVectorLayers.toTypedArray()) + } + val metrics = control.activity.resources.displayMetrics + styleSheetVector = MapboxVectorStyleSet(vectorStyleDict, styleSettings, metrics, control) + mapboxInterp = if (offlineRender != null && styleSheetImage != null) { + MapboxVectorInterpreter(styleSheetImage, offlineRender, styleSheetVector, control) } else { - Log.w("Maply", "Non-hybrid case not currently hooked up for 3.0") + MapboxVectorInterpreter(styleSheetVector, control) } + + if (mapboxInterp == null) { + Log.w("Maply", "Failed to set up Mapbox interpreter. Nothing will appear.") + stop() + } + + // TODO: Handle more than one source + val imageLoader = QuadImageLoader(sampleParams, tileInfos[0], control) + imageLoader.setLoaderInterpreter(mapboxInterp) + loader = imageLoader } // Stop trying to load data if we're doing that // or shutdown the loader if we've gotten to that point fun stop() { - val theControl = control?.get() - if (theControl == null) - return - + val theControl = control.get() ?: return + // Gotta run on the main thread if (Looper.getMainLooper().thread != Thread.currentThread()) { theControl.getActivity().runOnUiThread { stop() } @@ -475,8 +509,11 @@ public open class MapboxKindaMap { loader?.shutdown() loader = null mapboxInterp = null - - control = null + control.clear() } - -} \ No newline at end of file + + private val control : WeakReference = WeakReference(inControl) + private val outstandingFetches = ArrayList() + private val cacheNamePattern = Regex("[/:?.{}]") + private var finished = false +} diff --git a/android/library/maply/src/main/java/com/mousebird/maply/MapboxVectorInterpreter.java b/android/library/maply/src/main/java/com/mousebird/maply/MapboxVectorInterpreter.java index 0b98d01fb9..b9f811e930 100644 --- a/android/library/maply/src/main/java/com/mousebird/maply/MapboxVectorInterpreter.java +++ b/android/library/maply/src/main/java/com/mousebird/maply/MapboxVectorInterpreter.java @@ -1,9 +1,8 @@ -/* - * MapboxVectorInterpreter.java +/* MapboxVectorInterpreter.java * WhirlyGlobeLib * * Created by Steve Gifford on 4/16/19. - * Copyright 2011-2019 mousebird consulting + * Copyright 2011-2021 mousebird consulting * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -15,7 +14,6 @@ * 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. - * */ package com.mousebird.maply; @@ -25,6 +23,7 @@ import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; +import java.io.IOException; import java.lang.ref.WeakReference; import java.util.ArrayList; import java.util.Collections; @@ -55,7 +54,7 @@ public class MapboxVectorInterpreter implements LoaderInterpreter */ public MapboxVectorInterpreter(VectorStyleInterface inStyleInter,BaseController inVC) { styleGen = inStyleInter; - vc = new WeakReference(inVC); + vc = new WeakReference<>(inVC); parser = new MapboxVectorTileParser(inStyleInter,inVC); } @@ -77,7 +76,7 @@ public MapboxVectorInterpreter(VectorStyleInterface inImageStyle,RenderControlle imageStyleGen = inImageStyle; styleGen = inVectorStyle; tileRender = inTileRender; - vc = new WeakReference(inVC); + vc = new WeakReference<>(inVC); tileRender.clearLights(); parser = new MapboxVectorTileParser(styleGen,inVC); @@ -92,128 +91,188 @@ public MapboxVectorInterpreter(VectorStyleInterface inImageStyle,RenderControlle public void setLoader(QuadLoaderBase inLoader) { if (inLoader instanceof QuadPagingLoader) { - objectLoader = new WeakReference((QuadPagingLoader)inLoader); + objectLoader = new WeakReference<>((QuadPagingLoader)inLoader); } else if (inLoader instanceof QuadImageLoaderBase) { - imageLoader = new WeakReference((QuadImageLoaderBase)inLoader); + imageLoader = new WeakReference<>((QuadImageLoaderBase)inLoader); } } - static double MAX_EXTENT = 20037508.342789244; + static final double MAX_EXTENT = 20037508.342789244; + static final double WGS84_a = 6378137.0; // meters + static final double WGS84_a_2 = WGS84_a / 2.0; // Convert to spherical mercator directly - Point2d toMerc(Point2d pt) + static Point2d toMerc(Point2d pt) { - Point2d newPt = new Point2d(); - newPt.setValue(Math.toDegrees(pt.getX()) * MAX_EXTENT / 180.0, - 3189068.5 * Math.log((1.0 + Math.sin(pt.getY())) / (1.0 - Math.sin(pt.getY())))); - - return newPt; + return new Point2d( + Math.toDegrees(pt.getX()) * MAX_EXTENT / 180.0, + WGS84_a_2 * Math.log((1.0 + Math.sin(pt.getY())) / (1.0 - Math.sin(pt.getY())))); } public void dataForTile(LoaderReturn loadReturn,QuadLoaderBase loader) { + if (styleGen != null) styleGen.setZoomSlot(loader.getZoomSlot()); + if (imageStyleGen != null) imageStyleGen.setZoomSlot(loader.getZoomSlot()); + byte[] data = loadReturn.getFirstData(); if (data == null) return; + GZIPInputStream in = null; + ByteArrayOutputStream bout = null; + ByteArrayInputStream bin = null; try { // Unzip if it's compressed - ByteArrayInputStream bin = new ByteArrayInputStream(data); - GZIPInputStream in = new GZIPInputStream(bin); - ByteArrayOutputStream bout = new ByteArrayOutputStream(data.length * 2); + bin = new ByteArrayInputStream(data); + in = new GZIPInputStream(bin, data.length); + // Bail as soon as possible if there's a problem + if (in.available() != 0) { + bout = new ByteArrayOutputStream(data.length * 2); - ZipEntry ze; - byte[] buffer = new byte[1024]; - int count; - while ((count = in.read(buffer)) != -1) - bout.write(buffer, 0, count); + byte[] buffer = new byte[1024]; + for (int count; (count = in.read(buffer)) != -1; ) { + bout.write(buffer, 0, count); + } - data = bout.toByteArray(); + data = bout.toByteArray(); + } } catch (Exception ex) { // We'll try the raw data if we can't decompress it - } + } finally { + // Clean everything up in the opposite order they were created. + if (bout != null) try { + bout.close (); + } catch (IOException ignore){} - // Parse the data into vectors - // This will skip layers we don't care about - TileID tileID = loadReturn.getTileID(); - Mbr locBounds = loader.geoBoundsForTile(tileID); - locBounds.ll = toMerc(locBounds.ll); - locBounds.ur = toMerc(locBounds.ur); - VectorTileData tileData = new VectorTileData(tileID,locBounds,loader.geoBoundsForTile(tileID)); - parser.parseData(data,tileData); - BaseController theVC = vc.get(); - ArrayList ovlObjs = new ArrayList(); - if (theVC != null) { - ComponentObject[] thisOvjObjs = tileData.getComponentObjects("overlay"); - if (thisOvjObjs != null) - Collections.addAll(ovlObjs,thisOvjObjs); - loadReturn.mergeChanges(tileData.getChangeSet()); + if (in != null) try { + in.close (); + } catch (IOException ignore){} + + if (bin != null) try { + bin.close (); + } catch (IOException ignore){} } - // If we have a tile renderer, draw the data into that - Bitmap tileBitmap = null; - if (tileRender != null) { - synchronized (tileRender) { - tileRender.setClearColor(imageStyleGen.backgroundColorForZoom(tileID.level)); - Mbr imageBounds = new Mbr(new Point2d(0.0,0.0), tileRender.frameSize); - VectorTileData imageTileData = new VectorTileData(tileID,imageBounds,locBounds); - - // Need to activate the renderer, add the data, enable the objects and then clean it all up - // We need to use a specific context that comes with the tile renderer - RenderControllerInterface.ContextInfo cInfo = RenderController.getEGLContext(); - tileRender.setEGLContext(null); - imageParser.parseData(data,imageTileData); - ChangeSet changes = imageTileData.getChangeSet(); - changes.process(tileRender,tileRender.getScene()); - changes.dispose(); - tileRender.enableObjects(imageTileData.getComponentObjects(), RenderControllerInterface.ThreadMode.ThreadCurrent); - tileBitmap = tileRender.renderToBitmap(); - tileRender.removeObjects(imageTileData.getComponentObjects(), RenderControllerInterface.ThreadMode.ThreadCurrent); - tileRender.clearContext(); - imageTileData.dispose(); - - // Reset the OpenGL context back to what it was before - // It would have been set up by our own renderer for us on a specific thread - theVC.renderControl.setEGLContext(cInfo); + // Don't let the sampling layer shut down while we're working + QuadSamplingLayer samplingLayer = loader.samplingLayer.get(); + if (samplingLayer == null || !samplingLayer.layerThread.startOfWork()) + return; + + try { + // Parse the data into vectors + // This will skip layers we don't care about + TileID tileID = loadReturn.getTileID(); + Mbr locBounds = loader.geoBoundsForTile(tileID); + locBounds.ll = toMerc(locBounds.ll); + locBounds.ur = toMerc(locBounds.ur); + VectorTileData tileData = new VectorTileData(tileID, locBounds, loader.geoBoundsForTile(tileID)); + parser.parseData(data, tileData); + BaseController theVC = vc.get(); + ArrayList ovlObjs = new ArrayList<>(); + if (theVC != null) { + ComponentObject[] thisOvjObjs = tileData.getComponentObjects("overlay"); + if (thisOvjObjs != null) + Collections.addAll(ovlObjs, thisOvjObjs); + loadReturn.mergeChanges(tileData.getChangeSet()); } - } - // Sort out overlays if they're there - ComponentObject[] regObjs = tileData.getComponentObjects(); - if (!ovlObjs.isEmpty()) { - // Filter the overlays out of regular objects - ArrayList minusOvls = new ArrayList(); - for (ComponentObject compObj : regObjs) { - // Look for it in the overlays - boolean found = false; - for (ComponentObject ovlObj : ovlObjs) { - if (ovlObj.getID() == compObj.getID()) { - found = true; - break; + // If we have a tile renderer, draw the data into that + Bitmap tileBitmap = null; + if (tileRender != null) { + synchronized (tileRender) { + tileRender.setClearColor(imageStyleGen.backgroundColorForZoom(tileID.level)); + Mbr imageBounds = new Mbr(new Point2d(0.0, 0.0), tileRender.frameSize); + VectorTileData imageTileData = new VectorTileData(tileID, imageBounds, locBounds); + + // Need to activate the renderer, add the data, enable the objects and then clean it all up + // We need to use a specific context that comes with the tile renderer + RenderControllerInterface.ContextInfo cInfo = RenderController.getEGLContext(); + tileRender.setEGLContext(null); + imageParser.parseData(data, imageTileData); + ChangeSet changes = imageTileData.getChangeSet(); + changes.process(tileRender, tileRender.getScene()); + changes.dispose(); + tileRender.enableObjects(imageTileData.getComponentObjects(), RenderControllerInterface.ThreadMode.ThreadCurrent); + tileBitmap = tileRender.renderToBitmap(); + tileRender.removeObjects(imageTileData.getComponentObjects(), RenderControllerInterface.ThreadMode.ThreadCurrent); + tileRender.clearContext(); + imageTileData.dispose(); + + // Reset the OpenGL context back to what it was before + // It would have been set up by our own renderer for us on a specific thread + theVC.renderControl.setEGLContext(cInfo); + } + } + + // Sort out overlays if they're there + ComponentObject[] regObjs = tileData.getComponentObjects(); + if (!ovlObjs.isEmpty()) { + // Filter the overlays out of regular objects + ArrayList minusOvls = new ArrayList<>(); + for (ComponentObject compObj : regObjs) { + // Look for it in the overlays + boolean found = false; + for (ComponentObject ovlObj : ovlObjs) { + if (ovlObj.getID() == compObj.getID()) { + found = true; + break; + } } + + if (!found) + minusOvls.add(compObj); } - if (!found) - minusOvls.add(compObj); + regObjs = minusOvls.toArray(new ComponentObject[1]); } - regObjs = minusOvls.toArray(new ComponentObject[1]); - } + // Merge the results into the loadReturn + if (objectLoader != null) { + ObjectLoaderReturn objLoadReturn = (ObjectLoaderReturn) loadReturn; + if (!ovlObjs.isEmpty()) + objLoadReturn.addOverlayComponentObjects(ovlObjs.toArray(new ComponentObject[1])); + objLoadReturn.addComponentObjects(regObjs); + } else if (imageLoader != null) { + ImageLoaderReturn imgLoadReturn = (ImageLoaderReturn) loadReturn; + if (!ovlObjs.isEmpty()) + imgLoadReturn.addOverlayComponentObjects(ovlObjs.toArray(new ComponentObject[1])); + imgLoadReturn.addComponentObjects(regObjs); + if (tileBitmap != null) { + imgLoadReturn.addBitmap(tileBitmap); + } else if (imgLoadReturn.getImages().length == 0) { + // Make a single color background image + // We have to do this each time because it can change per level + final int bgColor = styleGen.backgroundColorForZoom(tileID.level); - // Merge the results into the loadReturn - if (objectLoader != null) { - ObjectLoaderReturn objLoadReturn = (ObjectLoaderReturn)loadReturn; - if (!ovlObjs.isEmpty()) - objLoadReturn.addOverlayComponentObjects(ovlObjs.toArray(new ComponentObject[1])); - objLoadReturn.addComponentObjects(regObjs); - } else if (imageLoader != null) { - ImageLoaderReturn imgLoadReturn = (ImageLoaderReturn)loadReturn; - if (!ovlObjs.isEmpty()) - imgLoadReturn.addOverlayComponentObjects(ovlObjs.toArray(new ComponentObject[1])); - imgLoadReturn.addComponentObjects(regObjs); - if (tileBitmap != null) - imgLoadReturn.addBitmap(tileBitmap); + // The color will be the same for all the tiles in a level, try to reuse it + Bitmap img; + synchronized (backgroundLock) { + if (lastBackground != null && lastBackgroundColor == bgColor) { + img = lastBackground; + } else { + img = Bitmap.createBitmap(BackImageSize, BackImageSize, Bitmap.Config.ARGB_8888); + img.eraseColor(bgColor); + lastBackground = img; + lastBackgroundColor = bgColor; + } + } + + ImageTile tile = new ImageTile(img); + // We're on a background thread, so set up the texture now + tile.preprocessTexture(); + + imgLoadReturn.addImageTile(tile); + } + } + } finally { + // Let the sampling layer shut down + samplingLayer.layerThread.endOfWork(); } } -} + private Bitmap lastBackground = null; + private int lastBackgroundColor = -1; + private final Object backgroundLock = new Object(); + + private static final int BackImageSize = 16; +} \ No newline at end of file diff --git a/android/library/maply/src/main/java/com/mousebird/maply/MapboxVectorStyleSet.java b/android/library/maply/src/main/java/com/mousebird/maply/MapboxVectorStyleSet.java deleted file mode 100644 index 562ca2e943..0000000000 --- a/android/library/maply/src/main/java/com/mousebird/maply/MapboxVectorStyleSet.java +++ /dev/null @@ -1,201 +0,0 @@ -package com.mousebird.maply; - -import android.graphics.Color; -import android.graphics.Paint; -import android.graphics.Rect; -import android.graphics.Typeface; -import android.util.DisplayMetrics; -import android.util.Log; - -import java.lang.ref.WeakReference; -import java.util.ArrayList; - -/** - * Mapbox Vector Style Set. - * This parses a Mapbox style sheet and interfaces with the vector parser - */ -public class MapboxVectorStyleSet implements VectorStyleInterface { - - public MapboxVectorStyleSet(String styleJSON,VectorStyleSettings inSettings,DisplayMetrics inDisplayMetrics,RenderControllerInterface inControl) { - AttrDictionary styleDict = new AttrDictionary(); - if (!styleDict.parseFromJSON(styleJSON)) { - throw new IllegalArgumentException("Bad JSON for style sheet in MapboxVectorStyleSet"); - } - - combinedInit(styleDict,inSettings,inDisplayMetrics,inControl); - } - - // Construct with the JSON data from a string - public MapboxVectorStyleSet(AttrDictionary styleDict,VectorStyleSettings inSettings,DisplayMetrics inDisplayMetrics,RenderControllerInterface inControl) { - combinedInit(styleDict,inSettings,inDisplayMetrics,inControl); - } - - // Used by both constructors - private void combinedInit(AttrDictionary styleDict,VectorStyleSettings inSettings,DisplayMetrics inDisplayMetrics,RenderControllerInterface inControl) - { - // Fault in the ComponentObject native implementation. - // Because the first time it can be called in this case is C++ side - ComponentObject testObj = new ComponentObject(); - - control = new WeakReference(inControl); - if (inSettings == null) - inSettings = new VectorStyleSettings(); - settings = inSettings; - - spriteURL = styleDict.getString("sprite"); - - // Sources tell us where to get tiles - AttrDictionary sourcesDict = styleDict.getDict("sources"); - if (sourcesDict != null) { - String[] keys = sourcesDict.getKeys(); - for (String key : keys) { - try { - Source source = new Source(key, sourcesDict.getDict(key), this); - sources.add(source); - } - catch (Exception e) { - } - } - } - - displayMetrics = inDisplayMetrics; - - initialise(inControl.getScene(),inControl.getCoordSystem(),settings,styleDict); - } - - /** - * Set this to override the regular fill shader. - * Useful if you're going to mix something else into the polygons. - */ - public void setArealShader(Shader shader) { - setArealShaderNative(shader.getID()); - } - - native private void setArealShaderNative(long shaderID); - - DisplayMetrics displayMetrics; - VectorStyleSettings settings; - WeakReference control; - - // Calculate an appropriate background color given the zoom level - public int backgroundColorForZoom(double zoom) - { - return backgroundColorForZoomNative(zoom); - } - - public native int backgroundColorForZoomNative(double zoom); - - ArrayList labelInfos = new ArrayList(); - - // Return a label info - public LabelInfo labelInfoForFont(String fontName,float fontSize) { - synchronized (this) { - for (LabelInfo labelInfo: labelInfos) { - if (labelInfo.fontSize == fontSize && labelInfo.fontName.equals(fontName)) - return labelInfo; - } - - // Didn't find it, so make one up - // TODO: What about bold, italic, etc?? - Typeface typeface = Typeface.create(fontName,Typeface.NORMAL); - LabelInfo labelInfo = new LabelInfo(); - labelInfo.setTypeface(typeface); - labelInfo.setFontSize(fontSize); - labelInfo.fontName = fontName; - labelInfos.add(labelInfo); - - return labelInfo; - } - } - - // Calculate text width based on the typeface - public double calculateTextWidth(String text,LabelInfo labelInfo) - { - Paint paint = new Paint(); - paint.setTextSize(labelInfo.fontSize); - paint.setTypeface(labelInfo.getTypeface()); - Rect bounds = new Rect(); - paint.getTextBounds(text,0,text.length(), bounds); - - return bounds.right - bounds.left; - } - - // If there's a sprite sheet, where it's at - public String spriteURL; - - public enum SourceType {Vector,Raster}; - - // Source for vector tile (or raster) data - public class Source { - // Name as it appears in the file - String name; - - // Either vector or raster at present - SourceType type; - - // TileJSON URL, if present - String url; - - // If the TileJSON spec is inline, it's here - AttrDictionary tileSpec; - - Source(String inName,AttrDictionary styleEntry, MapboxVectorStyleSet styleSet) { - name = inName; - - String typeStr = styleEntry.getString("type"); - if (typeStr.equals("vector")) { - type = SourceType.Vector; - } else if (typeStr.equals("raster")) { - type = SourceType.Raster; - } else { - throw new IllegalArgumentException("Unexpected type string in Mapbox Source"); - } - - url = styleEntry.getString("url"); - tileSpec = styleEntry.getDict("tiles"); - - if (url == null && tileSpec == null) { - Log.w("Maply", "Expecting either URL or tileSpec in source " + name); - throw new IllegalArgumentException("Expecting either URL or tileSpec in source " + name); - } - } - } - - public ArrayList sources = new ArrayList(); - - /** - * These are actually implemented on the C++ side, which communicates - * with itself. But we need to here to appear to be using the standard - * interface. - */ - public VectorStyle[] stylesForFeature(AttrDictionary attrs,TileID tileID,String layerName,RenderControllerInterface controller) - { - return null; - } - public VectorStyle[] allStyles() - { - return null; - } - public boolean layerShouldDisplay(String layerName,TileID tileID) - { - return false; - } - public VectorStyle styleForUUID(long uuid,RenderControllerInterface controller) - { - return null; - } - - public void finalize() - { - dispose(); - } - - static - { - nativeInit(); - } - native void initialise(Scene scene,CoordSystem coordSystem,VectorStyleSettings settings,AttrDictionary styleDict); - native void dispose(); - private static native void nativeInit(); - protected long nativeHandle; -} diff --git a/android/library/maply/src/main/java/com/mousebird/maply/MapboxVectorStyleSet.kt b/android/library/maply/src/main/java/com/mousebird/maply/MapboxVectorStyleSet.kt new file mode 100644 index 0000000000..06b28853c5 --- /dev/null +++ b/android/library/maply/src/main/java/com/mousebird/maply/MapboxVectorStyleSet.kt @@ -0,0 +1,358 @@ +/* MapboxVectorStyleSet.kt + * WhirlyGlobeLib + * + * Created by Steve Gifford + * Copyright 2011-2021 mousebird consulting + * + * 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. + */ + +package com.mousebird.maply + +import android.graphics.* +import android.util.DisplayMetrics +import android.util.Log +import com.mousebird.maply.RenderController.EmptyIdentity +import java.lang.ref.WeakReference +import java.util.* +import java.util.concurrent.ConcurrentMap +import java.util.concurrent.ConcurrentSkipListMap +import java.util.regex.Pattern +import kotlin.math.ceil + +/** + * Mapbox Vector Style Set. + * This parses a Mapbox style sheet and interfaces with the vector parser + */ +class MapboxVectorStyleSet : VectorStyleInterface { + + constructor( + styleJSON: String?, + inSettings: VectorStyleSettings, + inDisplayMetrics: DisplayMetrics, + inControl: RenderControllerInterface + ) { + val styleDict = AttrDictionary() + require(styleDict.parseFromJSON(styleJSON)) { "Bad JSON for style sheet in MapboxVectorStyleSet" } + combinedInit(styleDict, inSettings, inDisplayMetrics, inControl) + } + + // Construct with the JSON data from a string + constructor( + styleDict: AttrDictionary, + inSettings: VectorStyleSettings, + inDisplayMetrics: DisplayMetrics, + inControl: RenderControllerInterface + ) { + combinedInit(styleDict, inSettings, inDisplayMetrics, inControl) + } + + /** + * Allows the application to override the handling of typeface lookups. + */ + interface TypefaceDelegate { + /** + * Provide a specific typeface from whatever source in response to a name from the styles. + * The result is cached. + */ + fun getTypeface(name: String?): Typeface? + + /** + * If not providing a Typeface, modify the name passed to the Typeface.create + */ + fun mapTypefaceName(name: String?): String? + } + + fun setTypefaceDelegate(delegate: TypefaceDelegate?) { + typefaceDelegate = delegate + } + + // Used by both constructors + private fun combinedInit( + styleDict: AttrDictionary, + inSettings: VectorStyleSettings?, + inDisplayMetrics: DisplayMetrics, + inControl: RenderControllerInterface + ) { + // Fault in the ComponentObject native implementation. + // Because the first time it can be called in this case is C++ side + val testObj = ComponentObject() + + control = WeakReference(inControl) + settings = inSettings ?: VectorStyleSettings() + spriteURL = styleDict.getString("sprite") + displayMetrics = inDisplayMetrics + + // Sources tell us where to get tiles + styleDict.getDict("sources")?.let { sourcesDict -> + val keys = sourcesDict.keys + for (key in keys) { + try { + sources.add(Source(key, sourcesDict.getDict(key), this)) + } catch (e: Exception) { + Log.w(javaClass.simpleName, "Error while adding source '" + key + "' : " + e.message) + } + } + } + initialise(inControl.scene, inControl.coordSystem, settings, styleDict) + } + + /** + * Set this to override the regular fill shader. + * Useful if you're going to mix something else into the polygons. + */ + fun setArealShader(shader: Shader) { + setArealShaderNative(shader.id) + } + + // Calculate an appropriate background color given the zoom level + override fun backgroundColorForZoom(zoom: Double): Int { + return backgroundColorForZoomNative(zoom) + } + + // Return a label info + // Called from JNI + fun labelInfoForFont(fontName: String, fontSize: Float): LabelInfo { + labelInfoMap[SizedTypeface(fontName, fontSize)]?.let { return it } + + // Give the delegate a chance to produce a typeface or modify the name. + val typeface = typefaceDelegate?.getTypeface(fontName) ?: + typefaceDelegate?.mapTypefaceName(fontName)?.let { + Typeface.create(it, Typeface.NORMAL) + } ?: run { + // We need to create something directly from the style, so try to do something + // reasonable for the style parameter. + // todo: would it work better to remove bold, italic, normal, regular, etc., from the name? + var style = Typeface.NORMAL + if (boldPattern.matcher(fontName).matches()) { + style = style or Typeface.BOLD + } + if (italicPattern.matcher(fontName).matches()) { + style = style or Typeface.ITALIC + } + Typeface.create(fontName, style) + } + + // Add the typeface to the cache map. If it's already there, a concurrent thread beat + // us to it; use that result instead for consistent results. + + val labelInfo = LabelInfo() + labelInfo.typeface = typefaceMap.putIfAbsent(fontName, typeface) ?: typeface + labelInfo.setFontSize(fontSize) + labelInfo.fontName = fontName + + // Same with the size-specific label info + return labelInfoMap.putIfAbsent(SizedTypeface(fontName, fontSize), labelInfo) ?: labelInfo + } + + // Calculate text width based on the typeface + // Called from JNI + fun calculateTextWidth(text: String, labelInfo: LabelInfo): Double { + val paint = Paint() + paint.textSize = labelInfo.fontSize + paint.typeface = labelInfo.typeface + val bounds = Rect() + paint.getTextBounds(text, 0, text.length, bounds) + return (bounds.right - bounds.left).toDouble() + } + + fun makeCircleTexture(inRadius: Double, + fillColor: Int, + strokeColor: Int, + inStrokeWidth: Float, + /* out */ circleSize: Point2d?): Long /*Identity*/ { + val control = control?.get() ?: return EmptyIdentity + + // We want the texture a bit bigger than specified + val scale = (settings?.markerScale ?: 1.0) * 2.0 + + // Build an image for the circle + val buffer = 1.0 + val radius = inRadius * scale + val strokeWidth = inStrokeWidth * scale + val size = ceil((buffer + radius + strokeWidth) * 2.0).toInt() + + circleSize?.setValue(size / 2.0, size / 2.0) + + val bitmap = Bitmap.createBitmap(size, size, Bitmap.Config.ARGB_8888) + bitmap.eraseColor(Color.TRANSPARENT) + + val canvas = Canvas(bitmap) + + val paint = Paint(Paint.FILTER_BITMAP_FLAG.or(Paint.ANTI_ALIAS_FLAG)) + + // Outer stroke + if (strokeWidth > 0) { + paint.style = Paint.Style.FILL + paint.color = strokeColor + canvas.drawCircle(size/2.0f,size/2.0f,(radius+strokeWidth).toFloat(),paint) + } + + // Inner circle + paint.style = Paint.Style.FILL + paint.color = fillColor + canvas.drawCircle(size/2.0f,size/2.0f, radius.toFloat(),paint) + + val texSettings = RenderControllerInterface.TextureSettings().apply { + filterType = RenderControllerInterface.TextureSettings.FilterType.FilterLinear + imageFormat = RenderController.ImageFormat.MaplyImage4Layer8Bit + } + val tex = control.addTexture(bitmap, texSettings, ThreadMode.ThreadCurrent) + return tex?.texID ?: EmptyIdentity + } + + fun makeLineTexture(comp: DoubleArray): Long /*Identity*/ { +// NSMutableArray *dashComp = [NSMutableArray array]; +// for (double comp: inComp) +// [dashComp addObject:[NSNumber numberWithDouble:comp]]; +// +// MaplyLinearTextureBuilder *lineTexBuilder = [[MaplyLinearTextureBuilder alloc] init]; +// [lineTexBuilder setPattern:dashComp]; +// UIImage *lineImage = [lineTexBuilder makeImage]; +// MaplyTexture *tex = [viewC addTexture:lineImage +// desc:@{kMaplyTexFormat: @(MaplyImageIntRGBA), +// kMaplyTexWrapY: @(MaplyImageWrapY) +// } +// mode:MaplyThreadCurrent]; +// textures.push_back(tex); +// +// return tex.texID; + return EmptyIdentity + } + + enum class SourceType { + Vector, Raster + } + + // Source for vector tile (or raster) data + inner class Source internal constructor(// Name as it appears in the file + var name: String, styleEntry: AttrDictionary, styleSet: MapboxVectorStyleSet? + ) { + // Either vector or raster at present + var type: SourceType? = null + + // TileJSON URL, if present + var url: String? + + // If the TileJSON spec is inline, it's here + var tileSpec: Array? + + init { + val typeStr = styleEntry.getString("type") + type = if (typeStr == "vector") { + SourceType.Vector + } else if (typeStr == "raster") { + SourceType.Raster + } else { + throw IllegalArgumentException("Unexpected type string in Mapbox Source") + } + url = styleEntry.getString("url") + tileSpec = styleEntry.getArray("tiles") + if (url == null && tileSpec == null) { + Log.w("Maply", "Expecting either URL or tileSpec in source $name") + throw IllegalArgumentException("Expecting either URL or tileSpec in source $name") + } + } + } + + /** + * These are actually implemented on the C++ side, which communicates + * with itself. But we need to here to appear to be using the standard + * interface. + */ + override fun stylesForFeature( + attrs: AttrDictionary, + tileID: TileID, + layerName: String, + controller: RenderControllerInterface + ): Array? { + return null + } + + override fun allStyles(): Array? { + return null + } + + override fun layerShouldDisplay(layerName: String, tileID: TileID): Boolean { + return false + } + + override fun styleForUUID(uuid: Long, controller: RenderControllerInterface): VectorStyle? { + return null + } + + /** + * Get the zoom slot, or -1 + */ + external override fun getZoomSlot(): Int + + /** + * Capture the zoom slot if you're going use it + */ + external override fun setZoomSlot(inZoomSlot: Int) + + fun finalize() { + dispose() + } + + companion object { + private val boldPattern = Pattern.compile("[\\s-_]bold\\b", Pattern.CASE_INSENSITIVE) + private val italicPattern = Pattern.compile("[\\s-_]italic\\b", Pattern.CASE_INSENSITIVE) + + @JvmStatic + private external fun nativeInit() + + init { + nativeInit() + } + } + + var sources = ArrayList() + + // If there's a sprite sheet, where it's at + var spriteURL: String? = null + + var settings: VectorStyleSettings? = null + + private var displayMetrics: DisplayMetrics? = null + + private var control: WeakReference? = null + + private var typefaceDelegate: TypefaceDelegate? = null + + private val typefaceMap: ConcurrentMap = + ConcurrentSkipListMap(String.CASE_INSENSITIVE_ORDER) + + private data class SizedTypeface(val fontName: String, val size: Float) : Comparable { + override fun compareTo(other: SizedTypeface): Int { + val result = fontName.compareTo(other.fontName) + return if (result == 0) size.compareTo(other.size) else result + } + } + + private val labelInfoMap: ConcurrentMap = + ConcurrentSkipListMap() + + // JNI stuff + + private external fun setArealShaderNative(shaderID: Long) + private external fun backgroundColorForZoomNative(zoom: Double): Int + private external fun initialise( + scene: Scene?, + coordSystem: CoordSystem?, + settings: VectorStyleSettings?, + styleDict: AttrDictionary? + ) + + external fun dispose() + protected var nativeHandle: Long = 0 +} diff --git a/android/library/maply/src/main/java/com/mousebird/maply/MaplyLocationLockType.kt b/android/library/maply/src/main/java/com/mousebird/maply/MaplyLocationLockType.kt new file mode 100644 index 0000000000..e51491d2a4 --- /dev/null +++ b/android/library/maply/src/main/java/com/mousebird/maply/MaplyLocationLockType.kt @@ -0,0 +1,8 @@ +package com.mousebird.maply + +enum class MaplyLocationLockType { + MaplyLocationLockNone, + MaplyLocationLockNorthUp, + MaplyLocationLockHeadingUp, + MaplyLocationLockHeadingUpOffset +} \ No newline at end of file diff --git a/android/library/maply/src/main/java/com/mousebird/maply/MetroThread.java b/android/library/maply/src/main/java/com/mousebird/maply/MetroThread.java index 35456ef498..8e9a49814b 100644 --- a/android/library/maply/src/main/java/com/mousebird/maply/MetroThread.java +++ b/android/library/maply/src/main/java/com/mousebird/maply/MetroThread.java @@ -1,9 +1,8 @@ -/* - * MetroThread.java +/* MetroThread.java * WhirlyGlobeLib * * Created by Steve Gifford on 3/21/15. - * Copyright 2011-2015 mousebird consulting + * Copyright 2011-2021 mousebird consulting * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -15,100 +14,103 @@ * 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. - * */ package com.mousebird.maply; +import android.annotation.SuppressLint; import android.opengl.GLSurfaceView; import android.os.Handler; import android.os.HandlerThread; +import android.util.Log; import android.view.Choreographer; - import java.lang.ref.WeakReference; public class MetroThread extends HandlerThread implements Choreographer.FrameCallback { - Choreographer ch; - WeakReference control; - int frameInterval; - + public boolean requestRender = true; + public MetroThread(String name,BaseController inControl,int inFrameInterval) { super(name); - control = new WeakReference(inControl); + control = new WeakReference<>(inControl); frameInterval = inFrameInterval; - + start(); - final MetroThread metThread = this; - Handler handler = new Handler(this.getLooper()); - handler.post(new Runnable() - { - @Override - public void run() - { - Choreographer.getInstance().postFrameCallback(metThread); - } - }); + + // We have a looper now, but if we call postFrameCallback directly, + // the Choreographer init throws an exception about not having a looper. + new Handler(getLooper()).post(this::postFrameCallback); } - + + @SuppressLint("ObsoleteSdkInt") public void shutdown() { + removeFrameCallback(); + // Note: Is this blocking? try { - quit(); + if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.JELLY_BEAN_MR2) { + quitSafely(); + } else { + quit(); + } } catch (Exception e) - { - } + { + Log.w(Tag, "shutdown error", e); + } } // Set the frame rate. Will take effect on the next frame - public void setFrameRate(int newRate) - { - frameInterval = newRate; - } - - RenderController renderer = null; + public void setFrameRate(int newRate) { frameInterval = newRate; } // Set the renderer (and scene) we'll look at render requests - public void setRenderer(RenderController inRenderer) - { - renderer = inRenderer; - } - - int frameCount = 0; - public boolean requestRender = true; + public void setRenderer(RenderController inRenderer) { renderer = inRenderer; } // Request a render, filtered through the regular frame rate - public void requestRender() - { - requestRender = true; - } + public void requestRender() { requestRender = true; } // Called by the Choreographer to render a frame @Override - public void doFrame(long frameTimeNanos) + public void doFrame(long frameTimeNanos) { // Nudge the renderer - if (control.get().baseView != null && (frameCount % frameInterval == 0)) { - if (requestRender || - (renderer != null && (renderer.hasChanges() || renderer.activeObjectsHaveChanges() || renderer.view.isAnimating()))) { - if (control.get().baseView instanceof GLSurfaceView) { - GLSurfaceView glSurfaceView = (GLSurfaceView)control.get().baseView; - glSurfaceView.requestRender(); - } else { - GLTextureView glTextureView = (GLTextureView) control.get().baseView; - glTextureView.requestRender(); + if ((frameCount % frameInterval == 0)) { + BaseController theControl = control.get(); + android.view.View baseView = (theControl != null) ? theControl.baseView : null; + if (baseView != null) { + if (requestRender || (renderer != null && (renderer.hasChanges() || + renderer.activeObjectsHaveChanges() || + renderer.view.isAnimating()))) { + if (baseView instanceof GLSurfaceView) { + GLSurfaceView glSurfaceView = (GLSurfaceView)baseView; + glSurfaceView.requestRender(); + } else if (baseView instanceof GLTextureView) { + GLTextureView glTextureView = (GLTextureView)baseView; + glTextureView.requestRender(); + } else { + Log.w(Tag, String.format("Bad surface type %s", baseView.getClass().getName())); + } + requestRender = false; } } - requestRender = false; } // Need to do this every frame - Choreographer.getInstance().postFrameCallback(this); + Choreographer.getInstance().postFrameCallback(this); - frameCount++; + frameCount++; } + + private void postFrameCallback() { Choreographer.getInstance().postFrameCallback(this); } + + private void removeFrameCallback() { Choreographer.getInstance().removeFrameCallback(this); } + + private final WeakReference control; + private RenderController renderer = null; + private int frameInterval; + private int frameCount = 0; + private final static String Tag = MetroThread.class.getSimpleName(); } diff --git a/android/library/maply/src/main/java/com/mousebird/maply/ObjectLoaderReturn.java b/android/library/maply/src/main/java/com/mousebird/maply/ObjectLoaderReturn.java index d87afb7ae8..0b5b76e76b 100644 --- a/android/library/maply/src/main/java/com/mousebird/maply/ObjectLoaderReturn.java +++ b/android/library/maply/src/main/java/com/mousebird/maply/ObjectLoaderReturn.java @@ -24,7 +24,7 @@ /** * This version of the loader return is used by the ImageLoaderInterpreter. * - * When image tiles load, the interpeter fills in these contents, which can + * When image tiles load, the interpreter fills in these contents, which can * include any sort of ComponentObject and, of course, images. */ public class ObjectLoaderReturn extends LoaderReturn diff --git a/android/library/maply/src/main/java/com/mousebird/maply/QIFBatchOps.java b/android/library/maply/src/main/java/com/mousebird/maply/QIFBatchOps.java index 2dd3165b07..4db647dbbe 100644 --- a/android/library/maply/src/main/java/com/mousebird/maply/QIFBatchOps.java +++ b/android/library/maply/src/main/java/com/mousebird/maply/QIFBatchOps.java @@ -57,8 +57,16 @@ void addToStart(TileFetchRequest request) */ void process(TileFetcher fetcher) { - if (fetcher == null) + // Just run the logic ourselves + if (fetcher == null) { + // Don't do anything for cancel + + for (TileFetchRequest request: toStart) { + request.callback.success(request, null); + } + return; + } if (!toCancel.isEmpty()) { fetcher.cancelTileFetches(toCancel.toArray(new TileFetchRequest[0])); diff --git a/android/library/maply/src/main/java/com/mousebird/maply/QuadImageFrameLoader.java b/android/library/maply/src/main/java/com/mousebird/maply/QuadImageFrameLoader.java index 8a704ca28a..4717aca1ac 100644 --- a/android/library/maply/src/main/java/com/mousebird/maply/QuadImageFrameLoader.java +++ b/android/library/maply/src/main/java/com/mousebird/maply/QuadImageFrameLoader.java @@ -48,10 +48,22 @@ public enum FrameLoadMode {Broad,Narrow}; */ public void setLoadFrameMode(FrameLoadMode mode) { - setLoadFrameModeNative(mode.ordinal()); + if (setLoadFrameModeNative(mode.ordinal()) && samplingLayer != null) { + // If we changed the frame mode we may need to refresh the priorities + QuadSamplingLayer layer = samplingLayer.get(); + if (layer == null || layer.layerThread == null) + return; + layer.layerThread.addTask(new Runnable() { + @Override + public void run() { + updatePriorities(); + } + }); + } } - protected native void setLoadFrameModeNative(int mode); + protected native boolean setLoadFrameModeNative(int mode); + protected native void updatePriorities(); /** * Add another rendering focus to the frame loader. @@ -94,10 +106,21 @@ public void setCurrentImage(int focusID,double where) // double curFrame = std::min(std::max(where,0.0),(double)([loader->frameInfos count]-1)); double curFrame = Math.min(Math.max(where,0.0),(double)(tileInfos.length-1)); - setCurrentImageNative(focusID,where); + if (setCurrentImageNative(focusID,where) && samplingLayer != null) { + // setCurrentImage tells us if we changed the actual image + QuadSamplingLayer layer = samplingLayer.get(); + if (layer == null || layer.layerThread == null) + return; + layer.layerThread.addTask(new Runnable() { + @Override + public void run() { + updatePriorities(); + } + }); + } } - protected native void setCurrentImageNative(int focusID,double where); + protected native boolean setCurrentImageNative(int focusID,double where); /** * Return the interpolated location within the array of frames. @@ -135,6 +158,14 @@ public void setRenderTarget(int focusID,RenderTarget renderTarget) protected native void setRenderTargetIDNative(int focusID,long renderTargetID); + /** + * In special cases we may have tiles that already have borders baked in. In that case, call this + * method to set both the total textures size and the number of border pixels around the outside. + * + * By default this functionality is off. + */ + public native void setTextureSize(int tileSize,int borderSize); + /** * Shader to use for rendering the image frames for a particular focus. * @@ -154,6 +185,13 @@ public int getNumFrames() { return tileInfos.length; } + /** + * Change the tile sources all at once. This also forces a reload. + */ + public void changeTileInfo(final TileInfoNew[] newTileInfo) { + super.changeTileInfo(newTileInfo); + } + @Override public void shutdown() { valid = false; diff --git a/android/library/maply/src/main/java/com/mousebird/maply/QuadImageLoaderBase.java b/android/library/maply/src/main/java/com/mousebird/maply/QuadImageLoaderBase.java index 9739e40db8..cd2c6c187c 100644 --- a/android/library/maply/src/main/java/com/mousebird/maply/QuadImageLoaderBase.java +++ b/android/library/maply/src/main/java/com/mousebird/maply/QuadImageLoaderBase.java @@ -208,7 +208,7 @@ protected LoaderReturn makeLoaderReturn() */ protected void changeTileInfo(final TileInfoNew[] newTileInfo) { - if(samplingLayer.get() == null) + if(samplingLayer == null || samplingLayer.get() == null) return; samplingLayer.get().layerThread.addTask(new Runnable() { diff --git a/android/library/maply/src/main/java/com/mousebird/maply/QuadLoaderBase.java b/android/library/maply/src/main/java/com/mousebird/maply/QuadLoaderBase.java index 6505b8e6e6..af13652243 100644 --- a/android/library/maply/src/main/java/com/mousebird/maply/QuadLoaderBase.java +++ b/android/library/maply/src/main/java/com/mousebird/maply/QuadLoaderBase.java @@ -40,12 +40,12 @@ protected QuadLoaderBase() { } protected QuadLoaderBase(BaseController inControl) { - control = new WeakReference(inControl); + control = new WeakReference<>(inControl); } protected QuadLoaderBase(BaseController inControl,SamplingParams params,int numFrames,Mode mode) { - control = new WeakReference(inControl); + control = new WeakReference<>(inControl); initialise(params,numFrames,mode.ordinal()); } @@ -82,9 +82,14 @@ protected QuadLoaderBase(BaseController inControl,SamplingParams params,int numF * Controller associated with this quad loader. * This is where you send geometry and such. */ - public BaseController getController() - { - return control.get(); + public BaseController getController() { + WeakReference wr = control; + return (wr != null) ? wr.get() : null; + } + + public QuadSamplingLayer getSamplingLayer() { + WeakReference wr = samplingLayer; + return (wr != null) ? wr.get() : null; } /** @@ -102,7 +107,6 @@ public Mbr geoBoundsForTile(TileID tileID) Mbr mbr = new Mbr(); mbr.initialize(); geoBoundsForTileNative(tileID.x,tileID.y,tileID.level,mbr.ll,mbr.ur); - return mbr; } @@ -117,6 +121,7 @@ public Mbr geoBoundsForTile(TileID tileID) * * @return The lower left and upper right corner of the tile in local coordinates. */ + @SuppressWarnings({"unused", "RedundantSuppression"}) public Mbr boundsForTile(TileID tileID) { Mbr mbr = new Mbr(); @@ -135,6 +140,7 @@ public Mbr boundsForTile(TileID tileID) * * @return Return the center in display space for the given tile. */ + @SuppressWarnings({"unused", "RedundantSuppression"}) public Point3d displayCenterForTile(TileID tileID) { Point3d pt = new Point3d(); @@ -165,23 +171,25 @@ public void setLoaderInterpreter(LoaderInterpreter newInterp) { /** * Change the interpreter for the data coming back. This will force a reload. - * @param newInterp + * @param newInterpreter the new instance */ - public void changeLoaderInterpreter(final LoaderInterpreter newInterp) { - if (samplingLayer.get() == null) + @SuppressWarnings({"unused", "RedundantSuppression"}) + public void changeLoaderInterpreter(final LoaderInterpreter newInterpreter) { + QuadSamplingLayer layer = getSamplingLayer(); + if (layer == null) return; final QuadLoaderBase theLoader = this; // Make this change on the layer thread - samplingLayer.get().layerThread.addTask(new Runnable() { - @Override - public void run() { - loadInterp = newInterp; - newInterp.setLoader(theLoader); - ChangeSet changes = new ChangeSet(); - reloadNative(changes); - samplingLayer.get().layerThread.addChanges(changes); + layer.layerThread.addTask(() -> { + loadInterp = newInterpreter; + newInterpreter.setLoader(theLoader); + ChangeSet changes = new ChangeSet(); + reloadNative(changes); + QuadSamplingLayer layerInner = getSamplingLayer(); + if (layerInner != null) { + layerInner.layerThread.addChanges(changes); } }); } @@ -199,6 +207,8 @@ protected LoaderReturn makeLoaderReturn() return null; } + protected boolean isShuttingDown = false; + /** * Turn off the loader and shut things down. *
@@ -206,40 +216,50 @@ protected LoaderReturn makeLoaderReturn() */ public void shutdown() { - tileFetcher = null; loadInterp = null; - if (samplingLayer == null || samplingLayer.get() == null || control == null || control.get() == null) + QuadSamplingLayer layer = getSamplingLayer(); + if (layer == null || control == null || getController() == null) { return; + } - samplingLayer.get().removeClient(this); + isShuttingDown = true; + layer.removeClient(this); final QuadLoaderBase loaderBase = this; // Do all the shutdown on the layer thread - samplingLayer.get().layerThread.addTask(new Runnable() { - @Override - public void run() { - // Clean things up - ChangeSet changes = new ChangeSet(); - cleanupNative(changes); - - samplingLayer.get().layerThread.addChanges(changes); - - // Back to the main thread for the sampling layer stuff - Handler handler = new Handler(Looper.getMainLooper()); - handler.post(new Runnable() { - @Override - public void run() { - if (control.get() != null) - control.get().releaseSamplingLayer(samplingLayer.get(),loaderBase); - - samplingLayer.clear(); - control.clear(); - } - }); + layer.layerThread.addTask(() -> { + // Clean things up + ChangeSet changes = new ChangeSet(); + cleanupNative(changes); + + QuadSamplingLayer layerInner = getSamplingLayer(); + if (layerInner != null) { + layerInner.layerThread.addChanges(changes); } + + // Back to the main thread for the sampling layer stuff + Handler handler = new Handler(Looper.getMainLooper()); + handler.post(() -> { + BaseController ctrl = getController(); + if (ctrl != null) { + QuadSamplingLayer layerInner2 = getSamplingLayer(); + ctrl.releaseSamplingLayer(layerInner2, loaderBase); + } + + clear(samplingLayer); + clear(control); + + tileFetcher = null; + }); }); } + private static void clear(WeakReference weakRef) { + if (weakRef != null) { + weakRef.clear(); + } + } + protected native void cleanupNative(ChangeSet changes); protected native void mergeLoaderReturn(LoaderReturn loadReturn,ChangeSet changes); @@ -261,11 +281,12 @@ public void samplingLayerDisconnect(QuadSamplingLayer layer,ChangeSet changes) private native void samplingLayerDisconnectNative(QuadSamplingLayer layer,ChangeSet changes); // Used to initialize the loader for certain types of data. - public enum Mode {SingleFrame,MultiFrame,Object}; + public enum Mode {SingleFrame,MultiFrame,Object} /* --- Callback from C++ side --- */ // Process the cancels and starts we get from the C++ side + @SuppressWarnings({"unused", "RedundantSuppression"}) // Called from C++ public void processBatchOps(QIFBatchOps batchOps) { batchOps.process(tileFetcher); @@ -273,7 +294,7 @@ public void processBatchOps(QIFBatchOps batchOps) // Frame assets are used C++ side, but we have to hold a reference to them // or they disappear at inopportune times. We don't look inside them here. - HashSet frameAssets = new HashSet(); + HashSet frameAssets = new HashSet<>(); // Stop tracking a frame asset public void clearFrameAsset(QIFFrameAsset frameAsset) @@ -283,7 +304,8 @@ public void clearFrameAsset(QIFFrameAsset frameAsset) // Start off fetches for all the frames within a given tile // Return an array of corresponding frame assets - public void startTileFetch(QIFBatchOps batchOps,QIFFrameAsset[] inFrameAssets, final int tileX, final int tileY, final int tileLevel, int priority, double importance) + @SuppressWarnings({"unused", "RedundantSuppression"}) // Called from C++ + public void startTileFetch(QIFBatchOps batchOps, QIFFrameAsset[] inFrameAssets, final int tileX, final int tileY, final int tileLevel, int priority, double importance) { if (tileInfos.length == 0 || tileInfos.length != inFrameAssets.length) return; @@ -291,14 +313,14 @@ public void startTileFetch(QIFBatchOps batchOps,QIFFrameAsset[] inFrameAssets, f TileID tileID = new TileID(); tileID.x = tileX; tileID.y = tileY; tileID.level = tileLevel; - final WeakReference holdControl = new WeakReference(control.get()); + final WeakReference holdControl = new WeakReference<>(getController()); - QIFFrameAsset[] frames = new QIFFrameAsset[tileInfos.length]; + //QIFFrameAsset[] frames = new QIFFrameAsset[tileInfos.length]; int frame = 0; final QuadLoaderBase loaderBase = this; for (TileInfoNew tileInfo : tileInfos) { final int fFrame = frame; - final int dispFrame = tileInfos.length > 1 ? frame : -1; + //final int dispFrame = tileInfos.length > 1 ? frame : -1; // Put together a fetch request for, you now, fetching final TileFetchRequest fetchRequest = new TileFetchRequest(); @@ -324,17 +346,16 @@ public void success(TileFetchRequest fetchRequest, byte[] data) { theLoadInterp.dataForTile(loadReturn,loaderBase); // Merge the data back in on the sampling layer's thread - final QuadSamplingLayer layer = samplingLayer.get(); - if (layer != null) { - layer.layerThread.addTask(new Runnable() { - @Override - public void run() { - if (loadInterp != null) { - ChangeSet changes = new ChangeSet(); - mergeLoaderReturn(loadReturn, changes); - layer.layerThread.addChanges(changes); - loadReturn.dispose(); - } + final QuadSamplingLayer layer = getSamplingLayer(); + if (layer != null && !layer.isShuttingDown && !isShuttingDown) { + layer.layerThread.addTask(() -> { + if (loadInterp != null && !isShuttingDown) { + ChangeSet changes = new ChangeSet(); + mergeLoaderReturn(loadReturn, changes); + layer.layerThread.addChanges(changes); + loadReturn.dispose(); + } else { + cleanupLoadedData(holdControl, loadReturn); } }); } else { @@ -344,14 +365,15 @@ public void run() { @Override public void failure(TileFetchRequest fetchRequest, String errorStr) { - final QuadSamplingLayer layer = samplingLayer.get(); + final QuadSamplingLayer layer = getSamplingLayer(); if (layer != null) { - layer.layerThread.addTask(new Runnable() { - @Override - public void run() { + layer.layerThread.addTask(() -> { + QuadSamplingLayer layerInner = getSamplingLayer(); + if (layerInner != null) { + // Give the C++ code a chance to add changes in this case ChangeSet changes = new ChangeSet(); mergeLoaderReturn(null, changes); - layer.layerThread.addChanges(changes); + layerInner.layerThread.addChanges(changes); } }); } @@ -371,13 +393,8 @@ public void run() { batchOps.addToStart(fetchRequest); } else { // There's no fetching to do, so we'll short circuit it - new AsyncTask() { - protected Void doInBackground(Void... unused) { - fetchRequest.callback.success(null, null); - - return null; - } - }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, (Void)null); + new BackgroundFetch(fetchRequest) + .executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, (Void)null); } @@ -385,10 +402,22 @@ protected Void doInBackground(Void... unused) { } } + // This resolves "Warning: This AsyncTask class should be static or leaks might occur" on inline task + private static class BackgroundFetch extends AsyncTask { + public BackgroundFetch(TileFetchRequest request) { + this.fetchRequest = request; + } + protected Void doInBackground(Void... unused) { + fetchRequest.callback.success(fetchRequest, null); + return null; + } + private final TileFetchRequest fetchRequest; + } + // Clean up data that's been processed but we shut down the loader before it got back private void cleanupLoadedData(WeakReference inControl,LoaderReturn loadReturn) { - BaseController theControl = inControl.get(); + BaseController theControl = (inControl != null) ? inControl.get() : null; if (theControl == null) return; @@ -414,21 +443,29 @@ private void cleanupLoadedData(WeakReference inControl,LoaderRet */ public void reload() { - if (samplingLayer.get() == null) - return; - - samplingLayer.get().layerThread.addTask(new Runnable() { - @Override - public void run() { - ChangeSet changes = new ChangeSet(); - reloadNative(changes); - samplingLayer.get().layerThread.addChanges(changes); - } - }); + reloadArea(null); } protected native void reloadNative(ChangeSet changes); + public void reloadArea(Mbr[] areas) { + QuadSamplingLayer layer = getSamplingLayer(); + if (layer != null) { + layer.layerThread.addTask(() -> { + QuadSamplingLayer layerInner = getSamplingLayer(); + if (layerInner != null) { + ChangeSet changes = new ChangeSet(); + reloadAreaNative(changes,areas); + layerInner.layerThread.addChanges(changes); + } + }); + } + } + + protected native void reloadAreaNative(ChangeSet changes,Mbr[] areas); + + public native int getZoomSlot(); + public void finalize() { dispose(); @@ -440,5 +477,6 @@ public void finalize() private static native void nativeInit(); native void initialise(SamplingParams params,int numFrames,int mode); native void dispose(); + @SuppressWarnings({"unused", "RedundantSuppression"}) // Used from C++ private long nativeHandle; } diff --git a/android/library/maply/src/main/java/com/mousebird/maply/QuadPagingLoader.java b/android/library/maply/src/main/java/com/mousebird/maply/QuadPagingLoader.java index bd6d1a8a19..c413759756 100644 --- a/android/library/maply/src/main/java/com/mousebird/maply/QuadPagingLoader.java +++ b/android/library/maply/src/main/java/com/mousebird/maply/QuadPagingLoader.java @@ -32,6 +32,8 @@ public QuadPagingLoader(final SamplingParams params,TileInfoNew tileInfo,LoaderI this(params,new TileInfoNew[]{tileInfo},inInterp,control); } + protected boolean noFetcher = false; + /** * Initialize with the objects needed to run. * @@ -41,7 +43,8 @@ public QuadPagingLoader(final SamplingParams params,TileInfoNew tileInfo,LoaderI */ public QuadPagingLoader(final SamplingParams params,LoaderInterpreter inInterp,BaseController control) { - this(params,new TileInfoNew[] { },inInterp,control); + this(params,new TileInfoNew(params.getMinZoom(),params.getMaxZoom()),inInterp,control); + noFetcher = true; } /** @@ -75,11 +78,11 @@ public void run() { // Called a tick after creation to let users modify settings before we start public void delayedInit(final SamplingParams params) { - if (tileFetcher == null) { + if (tileFetcher == null && !noFetcher) { tileFetcher = getController().addTileFetcher("Image Fetcher"); } - samplingLayer = new WeakReference(getController().findSamplingLayer(params, this)); + samplingLayer = new WeakReference<>(getController().findSamplingLayer(params, this)); loadInterp.setLoader(this); } diff --git a/android/library/maply/src/main/java/com/mousebird/maply/RawPNGImageLoaderInterpreter.java b/android/library/maply/src/main/java/com/mousebird/maply/RawPNGImageLoaderInterpreter.java new file mode 100644 index 0000000000..14ddbed819 --- /dev/null +++ b/android/library/maply/src/main/java/com/mousebird/maply/RawPNGImageLoaderInterpreter.java @@ -0,0 +1,41 @@ +package com.mousebird.maply; + +/** + * This loader interpreter treats input image data objects as PNGs containing raw data. + * The difference is we'll use a direct PNG reader to tease it out, rather than Bitmap. + */ +public class RawPNGImageLoaderInterpreter implements LoaderInterpreter { + + /** + * Create one. + */ + public RawPNGImageLoaderInterpreter() { initialise(); } + + public void setLoader(QuadLoaderBase loader) { + } + + /** + * Pull the data out of the input image as raw PNG. + */ + public void dataForTile(LoaderReturn loadReturn,QuadLoaderBase loader) { + byte[][] images = loadReturn.getTileData(); + for (byte[] image : images) + dataForTileNative(image,loadReturn); + } + + /** + * In some cases we just want to pick values out of the input. + */ + public native void addMappingFrom(int fromVal,int toVal); + + native void dataForTileNative(byte[] image,LoaderReturn loaderReturn); + + static + { + nativeInit(); + } + private static native void nativeInit(); + native void initialise(); + native void dispose(); + private long nativeHandle; +} diff --git a/android/library/maply/src/main/java/com/mousebird/maply/RemoteTileFetcher.java b/android/library/maply/src/main/java/com/mousebird/maply/RemoteTileFetcher.java index 54f5bf56b2..aaa3b89732 100644 --- a/android/library/maply/src/main/java/com/mousebird/maply/RemoteTileFetcher.java +++ b/android/library/maply/src/main/java/com/mousebird/maply/RemoteTileFetcher.java @@ -1,9 +1,8 @@ -/* - * RemoteTileFetcher.java +/* RemoteTileFetcher.java * WhirlyGlobeLib * * Created by jmnavarro on 3/21/19. - * Copyright 2011-2019 mousebird consulting + * Copyright 2011-2021 mousebird consulting * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -15,7 +14,6 @@ * 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. - * */ package com.mousebird.maply; @@ -23,32 +21,35 @@ import android.os.AsyncTask; import android.os.Handler; import android.os.HandlerThread; -import android.service.quicksettings.Tile; import android.util.Log; +import org.jetbrains.annotations.NotNull; + import java.io.BufferedInputStream; import java.io.FileInputStream; -import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import java.io.OutputStream; import java.lang.ref.WeakReference; +import java.text.SimpleDateFormat; import java.util.Date; import java.util.HashMap; -import java.util.HashSet; +import java.util.Locale; +import java.util.NoSuchElementException; import java.util.TreeSet; +import java.util.concurrent.ConcurrentSkipListMap; +import java.util.concurrent.ConcurrentSkipListSet; import okhttp3.Call; import okhttp3.Callback; import okhttp3.OkHttpClient; -import okhttp3.Request; import okhttp3.Response; public class RemoteTileFetcher extends HandlerThread implements TileFetcher { - protected boolean valid = false; + protected boolean valid; - String name = null; + String name; /** * Set this to get way too much debugging output. @@ -73,7 +74,7 @@ enum TileInfoState {ToLoad,Loading,None} /** * A single tile that we're supposed to be loading. */ - public class TileInfo implements Comparable + public static class TileInfo implements Comparable { TileInfoState state; @@ -161,9 +162,9 @@ void clear() { /** * Stats collected by the fetcher */ - public class Stats { + public static class Stats { // Start of stats collection - public Date startDate; + public Date startDate = new Date(); // Total requests, remote and cached public int totalRequests; @@ -205,7 +206,8 @@ public void addStats(Stats that) { // Print out the stats public void dump(String name) { - Log.v("Maply", String.format("---MaplyTileFetcher %s Stats---",name) ); + String date = new SimpleDateFormat("c",Locale.getDefault()).format(startDate); + Log.v("Maply", String.format("---MaplyTileFetcher %s Stats since %s---",name,date)); Log.v("Maply", String.format(" Active Requests = %d",activeRequests) ); Log.v("Maply", String.format(" Max Active Requests = %d",maxActiveRequests) ); Log.v("Maply", String.format(" Total Requests = %d",totalRequests) ); @@ -220,24 +222,19 @@ public void dump(String name) { } } - protected Stats allStats = null; - protected Stats recentStats = null; + protected Stats allStats; + protected Stats recentStats; /** Return the stats (recent or for all time */ - public Stats getStats(boolean allTime) - { - if (allTime) - return allStats; - else - return recentStats; + public Stats getStats(boolean allTime) { + return allTime ? allStats : recentStats; } /** * Reset the stats keeping back to zero */ - public void resetStats() - { + public void resetStats() { recentStats = new Stats(); } @@ -250,12 +247,9 @@ public void resetActiveStats() { Handler handler = new Handler(getLooper()); - handler.post(new Runnable() { - @Override - public void run() { - recentStats.activeRequests = toLoad.size() + loading.size(); - recentStats.maxActiveRequests = recentStats.activeRequests; - } + handler.post(() -> { + recentStats.activeRequests = toLoad.size() + loading.size(); + recentStats.maxActiveRequests = recentStats.activeRequests; }); } @@ -265,13 +259,14 @@ public void updateActiveStats() { recentStats.maxActiveRequests = recentStats.activeRequests; } - WeakReference control = null; + final WeakReference control; RemoteTileFetcher(BaseController baseController, String name) { super(name); + this.name = name; - control = new WeakReference(baseController); + control = new WeakReference<>(baseController); client = baseController.getHttpClient(); valid = true; @@ -283,10 +278,10 @@ public void updateActiveStats() { } // Tiles sorted by priority, importance etc... - TreeSet loading = new TreeSet(); - TreeSet toLoad = new TreeSet(); + final TreeSet loading = new TreeSet<>(); + final TreeSet toLoad = new TreeSet<>(); // Tiles sorted by fetch request - HashMap tilesByFetchRequest = new HashMap(); + final HashMap tilesByFetchRequest = new HashMap<>(); /** * Add a whole group of requests at once. @@ -309,38 +304,41 @@ public void startTileFetches(final TileFetchRequest[] requests) // Have to run on our own thread Handler handler = new Handler(getLooper()); - handler.post(new Runnable() { - @Override - public void run() { - allStats.totalRequests = allStats.totalRequests + requests.length; - recentStats.totalRequests = recentStats.totalRequests + requests.length; - - for (TileFetchRequest request : requests) { - // Set up a new request - TileInfo tile = new TileInfo(); - tile.tileSource = request.tileSource; - tile.importance = request.importance; - tile.priority = request.priority; - tile.group = request.group; - tile.state = TileInfoState.ToLoad; - tile.request = request; - tile.fetchInfo = (RemoteTileFetchInfo)request.fetchInfo; - - if (debugMode) - Log.d("RemoteTileFetcher","Requesting fetch for " + tile.fetchInfo.urlReq); - - // If it's already cached, let's mark that - tile.isLocal = tile.fetchInfo.cacheFile != null && tile.fetchInfo.cacheFile.exists(); - - tilesByFetchRequest.put(request,tile); - toLoad.add(tile); - } + handler.post(() -> { + allStats.totalRequests = allStats.totalRequests + requests.length; + recentStats.totalRequests = recentStats.totalRequests + requests.length; + + for (TileFetchRequest request : requests) { + // Set up a new request + TileInfo tile = new TileInfo(); + tile.tileSource = request.tileSource; + tile.importance = request.importance; + tile.priority = request.priority; + tile.group = request.group; + tile.state = TileInfoState.ToLoad; + tile.request = request; + tile.fetchInfo = (RemoteTileFetchInfo)request.fetchInfo; if (debugMode) - Log.d("RemoteTileFetcher","Added (number) tile requests: " + requests.length); + Log.d("RemoteTileFetcher","Requesting fetch for " + tile.fetchInfo.urlReq); - scheduleLoading(); + // If it's already cached, let's mark that + tile.isLocal = tile.fetchInfo.cacheFile != null && tile.fetchInfo.cacheFile.exists(); + + synchronized (tilesByFetchRequest) { + tilesByFetchRequest.put(request, tile); + } + synchronized (toLoad) { + if (!toLoad.add(tile)) { + Log.w("RemoteTileFetcher", "Duplicate Tile: " + tile.toString()); + } + } } + + if (debugMode) + Log.d("RemoteTileFetcher","Added (number) tile requests: " + requests.length); + + scheduleLoading(); }); } @@ -354,16 +352,11 @@ protected void scheduleLoading() if (!scheduled) { Handler handler = new Handler(getLooper()); - handler.post(new Runnable() { - @Override - public void run() { - updateLoading(); - } - }); + handler.post(this::updateLoading); } } - OkHttpClient client = null; + OkHttpClient client; // Load a tile and pass off the results protected void updateLoading() @@ -376,14 +369,25 @@ protected void updateLoading() while (loading.size() < numConnections) { updateActiveStats(); - if (toLoad.isEmpty()) + final TileInfo tile; + synchronized (toLoad) { + if (toLoad.isEmpty()) + break; + tile = toLoad.last(); + if (tile != null) { + toLoad.remove(tile); + } + } + if (tile == null) { break; + } - // Move one over to loading - final TileInfo tile = toLoad.last(); - toLoad.remove(tile); tile.state = TileInfoState.Loading; - loading.add(tile); + synchronized (loading) { + if (!loading.add(tile)) { + Log.w("RemoteTileFetcher", "Tile already loading: " + tile.toString()); + } + } if (debugMode) Log.d("RemoteTileFetcher","Starting load of request: " + tile.fetchInfo.urlReq); @@ -393,13 +397,7 @@ protected void updateLoading() if (tile.isLocal) { // Try reading the data in the background - new AsyncTask() { - protected Void doInBackground(Void... unused) { - handleCache(tile); - - return null; - } - }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, (Void)null); + new CacheTask(this,tile).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR,(Void)null); } else { startFetch(tile); } @@ -408,6 +406,24 @@ protected Void doInBackground(Void... unused) { updateActiveStats(); } + // Fixes "This AsyncTask class should be static or leaks might occur" on inline anonymous AsyncTask + private static class CacheTask extends AsyncTask { + private final TileInfo tile; + private final WeakReference fetcher; + CacheTask(RemoteTileFetcher fetcher,TileInfo tile) { + this.fetcher = new WeakReference<>(fetcher); + this.tile = tile; + } + @Override + protected Void doInBackground(Void... unused) { + RemoteTileFetcher theFetcher = fetcher.get(); + if (theFetcher != null) { + theFetcher.handleCache(tile); + } + return null; + } + } + // Kick off a network fetch with the appropriate callbacks protected void startFetch(final TileInfo tile) { @@ -415,7 +431,7 @@ protected void startFetch(final TileInfo tile) tile.task.enqueue(new Callback() { @Override - public void onFailure(Call call, IOException e) { + public void onFailure(@NotNull Call call, @NotNull IOException e) { if (!valid) return; @@ -433,13 +449,12 @@ public void onFailure(Call call, IOException e) { } @Override - public void onResponse(Call call, Response response) throws IOException { - if (!valid) { + public void onResponse(@NotNull Call call, @NotNull Response response) { + if (valid) { + finishedLoading(tile,response,null, fetchStartTime); + } else { response.body().close(); - return; } - - finishedLoading(tile,response,null, fetchStartTime); } }); } @@ -453,66 +468,65 @@ protected void finishedLoading(final TileInfo inTile, final Response response, f // Have to run on our own thread Handler handler = new Handler(getLooper()); - handler.post(new Runnable() { - @Override - public void run() { - // Make sure we still care - TileInfo tile = tilesByFetchRequest.get(inTile.request); - if (tile == null) { - if (debugMode) - Log.d("RemoteTileFetcher","Dropping a tile request because it was cancelled: " + inTile.fetchInfo.urlReq); + handler.post(() -> { + // Make sure we still care + final TileInfo tile; + synchronized (tilesByFetchRequest) { + tile = tilesByFetchRequest.get(inTile.request); + } + if (tile == null) { + if (debugMode) + Log.d("RemoteTileFetcher","Dropping a tile request because it was cancelled: " + inTile.fetchInfo.urlReq); + if (response != null) { try { - if (response != null) - response.body().close(); - } - catch (Exception fooE) { + response.body().close(); + } catch (Exception ignored) { } - - return; } - boolean success = true; - Exception e = inE; + return; + } - if (debugMode) - Log.d("RemoteTileFetcher","Got response for: " + response.request()); + boolean success = true; + Exception e = inE; - if (response != null) { - try { - long length = response.body().contentLength(); - allStats.remoteRequests = allStats.remoteRequests + 1; - recentStats.remoteRequests = recentStats.remoteRequests + 1; - allStats.remoteData = allStats.remoteData + length; - recentStats.remoteData = recentStats.remoteData + length; - double howLong = System.currentTimeMillis()/1000.0 - fetchStartTile; - allStats.totalLatency = allStats.totalLatency + howLong; - recentStats.totalLatency = recentStats.totalLatency + howLong; - - handleFinishLoading(tile, response.body().bytes(), null); - } - catch (Exception thisE) - { - success = false; - e = thisE; - } - } else { + if (debugMode) + Log.d("RemoteTileFetcher","Got response for: " + response.request()); + + if (response != null) { + try { + long length = response.body().contentLength(); + allStats.remoteRequests = allStats.remoteRequests + 1; + recentStats.remoteRequests = recentStats.remoteRequests + 1; + allStats.remoteData = allStats.remoteData + length; + recentStats.remoteData = recentStats.remoteData + length; + double howLong = System.currentTimeMillis()/1000.0 - fetchStartTile; + allStats.totalLatency = allStats.totalLatency + howLong; + recentStats.totalLatency = recentStats.totalLatency + howLong; + + handleFinishLoading(tile, response.body().bytes(), null); + } + catch (Exception thisE) + { success = false; + e = thisE; } + } else { + success = false; + } - if (!success) { - allStats.totalFails = allStats.totalFails + 1; - recentStats.totalFails = recentStats.totalFails + 1; + if (!success) { + allStats.totalFails = allStats.totalFails + 1; + recentStats.totalFails = recentStats.totalFails + 1; - handleFinishLoading(tile, null, e); - } + handleFinishLoading(tile, null, e); + } + if (response != null) { try { - if (response != null) - response.body().close(); - } - catch (Exception fooE) - { + response.body().close(); + } catch (Exception ignored) { } } }); @@ -524,15 +538,20 @@ protected void handleCache(final TileInfo tile) if (!valid) return; - boolean success = true; + boolean success = false; final int size = (int) tile.fetchInfo.cacheFile.length(); final byte[] data = new byte[size]; try { - BufferedInputStream buf = new BufferedInputStream(new FileInputStream(tile.fetchInfo.cacheFile)); - buf.read(data, 0, data.length); - buf.close(); + try (FileInputStream fileStream = new FileInputStream(tile.fetchInfo.cacheFile)) { + try (BufferedInputStream buf = new BufferedInputStream(fileStream)) { + int bytesRead = buf.read(data, 0, data.length); + if (bytesRead == data.length) { + success = true; + } + } + } } catch (Exception e) { - success = false; + Log.w("RemoteTileFetcher", "Failed to read cache", e); } if (success) { @@ -540,14 +559,11 @@ protected void handleCache(final TileInfo tile) return; Handler handler = new Handler(getLooper()); - handler.post(new Runnable() { - @Override - public void run() { - allStats.localData = allStats.localData + size; - recentStats.localData = recentStats.localData + size; + handler.post(() -> { + allStats.localData = allStats.localData + size; + recentStats.localData = recentStats.localData + size; - handleFinishLoading(tile,data,null); - } + handleFinishLoading(tile,data,null); }); if (debugMode) @@ -566,46 +582,42 @@ public void run() { protected void handleFinishLoading(TileInfo inTile,final byte[] data,final Exception error) { // Make sure we still want it - final TileInfo tile = tilesByFetchRequest.get(inTile.request); - if (tile == null) - return; + final TileInfo tile; + synchronized (tilesByFetchRequest) { + tile = tilesByFetchRequest.get(inTile.request); + } // Might have been cancelled - if (tile.state == TileInfoState.None) + if (tile == null || tile.state == TileInfoState.None) { return; + } BaseController theControl = control.get(); // Let the caller know on a random thread because parsing may take a while // Has to be a worker thread because we need an OpenGL context LayerThread backThread = theControl.getWorkingThread(); - backThread.addTask(new Runnable() { - @Override - public void run() { - if (!valid) - return; + backThread.addTask(() -> { + if (!valid) + return; - if (debugMode) - Log.d("RemoteTileFetcher","Returning fetch: " + tile.fetchInfo.urlReq); + if (debugMode) + Log.d("RemoteTileFetcher","Returning fetch: " + tile.fetchInfo.urlReq); - if (error == null) { - writeToCache(tile,data); - tile.request.callback.success(tile.request, data); - } else - tile.request.callback.failure(tile.request,error.toString()); + if (error == null) { + writeToCache(tile,data); + tile.request.callback.success(tile.request, data); + } else + tile.request.callback.failure(tile.request,error.toString()); - if (!valid) - return; + if (!valid) + return; - // Now get rid of the tile and kick off a new request - Handler handler = new Handler(getLooper()); - handler.post(new Runnable() { - @Override - public void run() { - finishTile(tile); - scheduleLoading(); - } - }); - } + // Now get rid of the tile and kick off a new request + Handler handler = new Handler(getLooper()); + handler.post(() -> { + finishTile(tile); + scheduleLoading(); + }); }); } @@ -615,37 +627,35 @@ protected void writeToCache(TileInfo tile,byte[] data) if (tile.fetchInfo.cacheFile == null) return; - OutputStream fOut = null; - try { - fOut = new FileOutputStream(tile.fetchInfo.cacheFile); + try (OutputStream fOut = new FileOutputStream(tile.fetchInfo.cacheFile)) { fOut.write(data); - } - catch (Exception e) - { - } - finally { - try { - fOut.close(); - } - catch (Exception e) - { - } + } catch (Exception e) { + Log.w("RemoteTileFetcher", "Failed to write cache", e); } } protected void finishTile(TileInfo inTile) { // Make sure we still want it - final TileInfo tile = tilesByFetchRequest.get(inTile.request); + final TileInfo tile; + synchronized (tilesByFetchRequest) { + tile = tilesByFetchRequest.get(inTile.request); + if (tile != null) { + tilesByFetchRequest.remove(tile.request); + } + } if (tile == null) { if (debugMode) Log.d("RemoteTileFetcher","Dropping fetch: " + inTile.fetchInfo.urlReq); return; } - tilesByFetchRequest.remove(tile.request); - loading.remove(tile); - toLoad.remove(tile); + synchronized (loading) { + loading.remove(tile); + } + synchronized (toLoad) { + toLoad.remove(tile); + } updateActiveStats(); } @@ -660,18 +670,23 @@ public Object updateTileFetch(final Object fetchRequest, final int priority, fin // Have to run on our own thread Handler handler = new Handler(getLooper()); - handler.post(new Runnable() { - @Override - public void run() { - TileInfo tile = tilesByFetchRequest.get(fetchRequest); - if (tile == null) - return; + handler.post(() -> { + if (fetchRequest instanceof TileFetchRequest) { + final TileInfo tile; + synchronized (tilesByFetchRequest) { + tile = tilesByFetchRequest.get((TileFetchRequest)fetchRequest); + } // Only mess with tiles that are actually loading - if (tile.state == TileInfoState.ToLoad) { - toLoad.remove(tile); - tile.priority = priority; - tile.importance = importance; - toLoad.add(tile); + if (tile != null && tile.state == TileInfoState.ToLoad) { + synchronized (toLoad) { + // do we still want to re-add it if remove fails, i.e., it's already been removed? + toLoad.remove(tile); + tile.priority = priority; + tile.importance = importance; + if (!toLoad.add(tile)) { + Log.w("RemoteTileFetcher", "Duplicate tile: " + tile.toString()); + } + } } } }); @@ -693,22 +708,30 @@ public void cancelTileFetches(final Object[] fetchRequests) // Have to run on our own thread Handler handler = new Handler(getLooper()); - handler.post(new Runnable() { - @Override - public void run() { - allStats.totalCancels = allStats.totalCancels + 1; - recentStats.totalCancels = recentStats.totalCancels + 1; - - for (Object fetchRequest : fetchRequests) { - TileInfo tile = tilesByFetchRequest.get(fetchRequest); + handler.post(() -> { + allStats.totalCancels = allStats.totalCancels + 1; + recentStats.totalCancels = recentStats.totalCancels + 1; + + for (Object fetchRequest : fetchRequests) { + if (fetchRequest instanceof TileFetchRequest) { + final TileInfo tile; + synchronized (tilesByFetchRequest) { + tile = tilesByFetchRequest.get((TileFetchRequest)fetchRequest); + } if (tile == null) continue; if (tile.task != null) tile.task.cancel(); tile.state = TileInfoState.None; - toLoad.remove(tile); - loading.remove(tile); - tilesByFetchRequest.remove(fetchRequest); + synchronized (toLoad) { + toLoad.remove(tile); + } + synchronized (loading) { + loading.remove(tile); + } + synchronized (tilesByFetchRequest) { + tilesByFetchRequest.remove(fetchRequest); + } } } }); @@ -722,8 +745,14 @@ public void shutdown() valid = false; quitSafely(); - loading.clear(); - toLoad.clear(); - tilesByFetchRequest.clear(); + synchronized (loading) { + loading.clear(); + } + synchronized (toLoad) { + toLoad.clear(); + } + synchronized (tilesByFetchRequest) { + tilesByFetchRequest.clear(); + } } } diff --git a/android/library/maply/src/main/java/com/mousebird/maply/RenderController.java b/android/library/maply/src/main/java/com/mousebird/maply/RenderController.java index 3ab4730d10..a857c1bba6 100644 --- a/android/library/maply/src/main/java/com/mousebird/maply/RenderController.java +++ b/android/library/maply/src/main/java/com/mousebird/maply/RenderController.java @@ -1,18 +1,26 @@ +/* RenderController.java + * AutoTesterAndroid.maply + * + * Created by Tim Sylvester on 24/03/2021 + * Copyright © 2021 mousebird consulting, inc. + * + * 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. + */ + package com.mousebird.maply; -import android.app.ActivityManager; -import android.content.Context; -import android.content.pm.ConfigurationInfo; import android.graphics.Bitmap; import android.graphics.Color; -import android.graphics.PixelFormat; -import android.graphics.drawable.ColorDrawable; import android.opengl.EGL14; import android.opengl.EGLExt; -import android.opengl.GLSurfaceView; -import android.os.Build; import android.util.Log; -import android.widget.Toast; import java.lang.ref.WeakReference; import java.util.ArrayList; @@ -22,7 +30,6 @@ import javax.microedition.khronos.egl.EGLConfig; import javax.microedition.khronos.egl.EGLContext; import javax.microedition.khronos.egl.EGLDisplay; -import javax.microedition.khronos.egl.EGLSurface; /** * The Render Controller handles the object manipulation and rendering interface. @@ -39,6 +46,7 @@ public class RenderController implements RenderControllerInterface public static final int MarkerDrawPriorityDefault = 40000; public static final int LabelDrawPriorityDefault = 60000; public static final int ParticleDrawPriorityDefault = 1000; + public static final int VectorDrawPriorityDefault = 50000; Point2d frameSize = new Point2d(0.0, 0.0); @@ -48,7 +56,8 @@ public class RenderController implements RenderControllerInterface /** * Enumerated values for image types. */ - public enum ImageFormat {MaplyImageIntRGBA, + public enum ImageFormat { + MaplyImageIntRGBA, MaplyImageUShort565, MaplyImageUShort4444, MaplyImageUShort5551, @@ -56,7 +65,8 @@ public enum ImageFormat {MaplyImageIntRGBA, MaplyImageUByteRGB, MaplyImageETC2RGB8,MaplyImageETC2RGBA8,MaplyImageETC2RGBPA8, MaplyImageEACR11,MaplyImageEACR11S,MaplyImageEACRG11,MaplyImageEACRG11S, - MaplyImage4Layer8Bit}; + MaplyImage4Layer8Bit + } /** * If set, we'll explicitly call dispose on any objects that were @@ -108,26 +118,26 @@ public RenderController(RenderController baseControl,int width,int height) // Set up our own EGL context for offline work EGL10 egl = (EGL10) EGLContext.getEGL(); - int[] attrib_list = {BaseController.EGL_CONTEXT_CLIENT_VERSION, 2, EGL10.EGL_NONE}; + final int[] attributeList = {BaseController.EGL_CONTEXT_CLIENT_VERSION, 2, EGL10.EGL_NONE}; offlineGLContext = new ContextInfo(); - offlineGLContext.eglContext = egl.eglCreateContext(display, config, context, attrib_list); - int[] surface_attrs = - { - EGL10.EGL_WIDTH, 32, - EGL10.EGL_HEIGHT, 32, - // EGL10.EGL_COLORSPACE, GL10.GL_RGB, - // EGL10.EGL_TEXTURE_FORMAT, EGL_TEXTURE_RGB, - // EGL10.EGL_TEXTURE_TARGET, EGL_TEXTURE_2D, - // EGL10.EGL_LARGEST_PBUFFER, GL10.GL_TRUE, - EGL10.EGL_NONE - }; + offlineGLContext.eglContext = egl.eglCreateContext(display, config, context, attributeList); + final int[] surface_attrs = + { + EGL10.EGL_WIDTH, 32, + EGL10.EGL_HEIGHT, 32, + // EGL10.EGL_COLORSPACE, GL10.GL_RGB, + // EGL10.EGL_TEXTURE_FORMAT, EGL_TEXTURE_RGB, + // EGL10.EGL_TEXTURE_TARGET, EGL_TEXTURE_2D, + // EGL10.EGL_LARGEST_PBUFFER, GL10.GL_TRUE, + EGL10.EGL_NONE + }; offlineGLContext.eglSurface = egl.eglCreatePbufferSurface(display, config, surface_attrs); setEGLContext(offlineGLContext); initialise(width,height); - // Set up a passthrough coordinate system, view, and so on + // Set up a pass-through coordinate system, view, and so on CoordSystem coordSys = new PassThroughCoordSystem(); Point3d ll = new Point3d(0.0,0.0,0.0); Point3d ur = new Point3d(width,height,0.0); @@ -144,14 +154,10 @@ public RenderController(RenderController baseControl,int width,int height) // Need a task manager that just runs things on the current thread // after setting the proper context for rendering - TaskManager taskMan = new TaskManager() { - @Override - public void addTask(Runnable run, ThreadMode mode) { - EGL10 egl = (EGL10) EGLContext.getEGL(); - setEGLContext(offlineGLContext); - - run.run(); - } + TaskManager taskMan = (run, mode) -> { + EGL10 egl1 = (EGL10) EGLContext.getEGL(); + setEGLContext(offlineGLContext); + run.run(); }; // This will properly wire things up @@ -179,7 +185,7 @@ public RenderController(int width,int height) * to hand over the runnables. */ public interface TaskManager { - public void addTask(Runnable run,ThreadMode mode); + void addTask(Runnable run,ThreadMode mode); } TaskManager taskMan = null; @@ -213,7 +219,7 @@ public void setView(View inView) setViewNative(inView); } - ArrayList activeObjects = new ArrayList(); + final ArrayList activeObjects = new ArrayList<>(); /** * Add an active object that will be called right before the render (on the render thread). @@ -417,7 +423,7 @@ public void removeLight(final Light light) { // Lights have to be rebuilt every time they change private void updateLights() { - List theLights = new ArrayList<>(); + List theLights = new ArrayList<>(lights.size()); for (Light light : lights) { DirectionalLight theLight = new DirectionalLight(); theLight.setPos(light.getPos()); @@ -429,8 +435,9 @@ private void updateLights() { replaceLights(theLights.toArray(new DirectionalLight[0])); // Clean up lights - for (DirectionalLight light : theLights) + for (DirectionalLight light : theLights) { light.dispose(); + } } /** @@ -475,71 +482,63 @@ public ComponentObject addScreenMarkers(final List markers,final M final RenderController renderControl = this; // Do the actual work on the layer thread - Runnable run = - new Runnable() + taskMan.addTask(() -> { + ChangeSet changes = new ChangeSet(); + + // Convert to the internal representation of the engine + ArrayList intMarkers = new ArrayList<>(markers.size()); + for (ScreenMarker marker : markers) + { + if (marker.loc == null) { - @Override - public void run() - { - ChangeSet changes = new ChangeSet(); - - // Convert to the internal representation of the engine - ArrayList intMarkers = new ArrayList(); - for (ScreenMarker marker : markers) - { - if (marker.loc == null) - { - Log.d("Maply","Missing location for marker. Skipping."); - return; - } - - InternalMarker intMarker = new InternalMarker(marker); - long texID = EmptyIdentity; - if (marker.image != null) { - texID = texManager.addTexture(marker.image, scene, changes); - if (texID != EmptyIdentity) - intMarker.addTexID(texID); - } else if (marker.tex != null) { - texID = marker.tex.texID; - intMarker.addTexID(texID); - } else if (marker.images != null) - { - for (MaplyTexture tex : marker.images) { - intMarker.addTexID(tex.texID); - } - } - if (marker.vertexAttributes != null) - intMarker.setVertexAttributes(marker.vertexAttributes.toArray()); - - intMarkers.add(intMarker); - - // Keep track of this one for selection - if (marker.selectable) - { - componentManager.addSelectableObject(marker.ident,marker,compObj); - } - } - - // Add the markers and flush the changes - long markerId = markerManager.addScreenMarkers(intMarkers.toArray(new InternalMarker[0]), markerInfo, changes); - if (scene != null) { - changes.process(renderControl, scene); - changes.dispose(); - } - - if (markerId != EmptyIdentity) - { - compObj.addMarkerID(markerId); - } - - for (InternalMarker marker : intMarkers) - marker.dispose(); - - componentManager.addComponentObject(compObj); + Log.d("Maply","Missing location for marker. Skipping."); + continue; + } + + InternalMarker intMarker = new InternalMarker(marker); + if (marker.image != null) { + long texID = texManager.addTexture(marker.image, scene, changes); + if (texID != EmptyIdentity) { + intMarker.addTexID(texID); + } + } else if (marker.tex != null) { + long texID = marker.tex.texID; + if (texID != EmptyIdentity) { + intMarker.addTexID(texID); + } + } else if (marker.images != null) { + for (MaplyTexture tex : marker.images) { + intMarker.addTexID(tex.texID); } - }; + } + if (marker.vertexAttributes != null) { + intMarker.setVertexAttributes(marker.vertexAttributes.toArray()); + } + + intMarkers.add(intMarker); + + // Keep track of this one for selection + if (marker.selectable) + { + componentManager.addSelectableObject(marker.ident,marker,compObj); + } + } + + // Add the markers and flush the changes + long markerId = markerManager.addScreenMarkers(intMarkers.toArray(new InternalMarker[0]), markerInfo, changes); + + if (markerId != EmptyIdentity) + { + compObj.addMarkerID(markerId); + } + + for (InternalMarker marker : intMarkers) { + marker.dispose(); + } - taskMan.addTask(run, mode); + componentManager.addComponentObject(compObj, changes); + renderControl.processChangeSet(changes); + }, mode); return compObj; } @@ -555,71 +554,60 @@ public ComponentObject addScreenMovingMarkers(final List mar final RenderController renderControl = this; // Do the actual work on the layer thread - Runnable run = - new Runnable() + taskMan.addTask(() -> { + ChangeSet changes = new ChangeSet(); + + // Convert to the internal representation of the engine + ArrayList intMarkers = new ArrayList<>(markers.size()); + for (ScreenMovingMarker marker : markers) + { + if (marker.loc == null) { - @Override - public void run() - { - ChangeSet changes = new ChangeSet(); - - // Convert to the internal representation of the engine - ArrayList intMarkers = new ArrayList(); - for (ScreenMovingMarker marker : markers) - { - if (marker.loc == null) - { - Log.d("Maply","Missing location for marker. Skipping."); - return; - } - - InternalMarker intMarker = new InternalMarker(marker,now); - long texID = EmptyIdentity; - if (marker.image != null) { - texID = texManager.addTexture(marker.image, scene, changes); - if (texID != EmptyIdentity) - intMarker.addTexID(texID); - } else if (marker.tex != null) { - texID = marker.tex.texID; - intMarker.addTexID(texID); - } else if (marker.images != null) - { - for (MaplyTexture tex : marker.images) { - intMarker.addTexID(tex.texID); - } - } - if (marker.vertexAttributes != null) - intMarker.setVertexAttributes(marker.vertexAttributes.toArray()); - - intMarkers.add(intMarker); - - // Keep track of this one for selection - if (marker.selectable) - { - componentManager.addSelectableObject(marker.ident,marker,compObj); - } - } - - // Add the markers and flush the changes - long markerId = markerManager.addScreenMarkers(intMarkers.toArray(new InternalMarker[0]), markerInfo, changes); - if (scene != null) { - changes.process(renderControl, scene); - changes.dispose(); - } - - if (markerId != EmptyIdentity) - { - compObj.addMarkerID(markerId); - } - - for (InternalMarker marker : intMarkers) - marker.dispose(); - - componentManager.addComponentObject(compObj); + Log.d("Maply","Missing location for marker. Skipping."); + continue; + } + + InternalMarker intMarker = new InternalMarker(marker,now); + if (marker.image != null) { + long texID = texManager.addTexture(marker.image, scene, changes); + if (texID != EmptyIdentity) + intMarker.addTexID(texID); + } else if (marker.tex != null) { + long texID = marker.tex.texID; + intMarker.addTexID(texID); + } else if (marker.images != null) + { + for (MaplyTexture tex : marker.images) { + intMarker.addTexID(tex.texID); } - }; + } + if (marker.vertexAttributes != null) + intMarker.setVertexAttributes(marker.vertexAttributes.toArray()); + + intMarkers.add(intMarker); + + // Keep track of this one for selection + if (marker.selectable) + { + componentManager.addSelectableObject(marker.ident,marker,compObj); + } + } + + // Add the markers and flush the changes + long markerId = markerManager.addScreenMarkers(intMarkers.toArray(new InternalMarker[0]), markerInfo, changes); + + if (markerId != EmptyIdentity) + { + compObj.addMarkerID(markerId); + } + + for (InternalMarker marker : intMarkers) { + marker.dispose(); + } - taskMan.addTask(run, mode); + componentManager.addComponentObject(compObj, changes); + renderControl.processChangeSet(changes); + }, mode); return compObj; } @@ -640,70 +628,57 @@ public ComponentObject addMarkers(final List markers,final MarkerInfo ma final RenderController renderControl = this; // Do the actual work on the layer thread - Runnable run = - new Runnable() + taskMan.addTask(() -> { + ChangeSet changes = new ChangeSet(); + + // Convert to the internal representation of the engine + ArrayList intMarkers = new ArrayList<>(markers.size()); + for (Marker marker : markers) + { + if (marker.loc == null) { - @Override - public void run() - { - ChangeSet changes = new ChangeSet(); - - // Convert to the internal representation of the engine - ArrayList intMarkers = new ArrayList(); - for (Marker marker : markers) - { - if (marker.loc == null) - { - Log.d("Maply","Missing location for marker. Skipping."); - return; - } - - InternalMarker intMarker = new InternalMarker(marker); - // Map the bitmap to a texture ID - long texID = EmptyIdentity; - if (marker.image != null) { - texID = texManager.addTexture(marker.image, scene, changes); - if (texID != EmptyIdentity) - intMarker.addTexID(texID); - } else if (marker.images != null) - { - for (MaplyTexture tex : marker.images) { - intMarker.addTexID(tex.texID); - } - } else if (marker.tex != null) - { - intMarker.addTexID(marker.tex.texID); - } - - intMarkers.add(intMarker); - - // Keep track of this one for selection - if (marker.selectable) - { - componentManager.addSelectableObject(marker.ident,marker,compObj); - } - } - - // Add the markers and flush the changes - long markerId = markerManager.addMarkers(intMarkers.toArray(new InternalMarker[0]), markerInfo, changes); - if (scene != null) { - changes.process(renderControl, scene); - changes.dispose(); - } - - if (markerId != EmptyIdentity) - { - compObj.addMarkerID(markerId); - } - - for (InternalMarker marker : intMarkers) - marker.dispose(); - - componentManager.addComponentObject(compObj); + Log.d("Maply","Missing location for marker. Skipping."); + continue; + } + + InternalMarker intMarker = new InternalMarker(marker); + // Map the bitmap to a texture ID + if (marker.image != null) { + long texID = texManager.addTexture(marker.image, scene, changes); + if (texID != EmptyIdentity) + intMarker.addTexID(texID); + } else if (marker.images != null) { + for (MaplyTexture tex : marker.images) { + intMarker.addTexID(tex.texID); } - }; + } else if (marker.tex != null) { + intMarker.addTexID(marker.tex.texID); + } + + intMarkers.add(intMarker); + + // Keep track of this one for selection + if (marker.selectable) + { + componentManager.addSelectableObject(marker.ident,marker,compObj); + } + } + + // Add the markers and flush the changes + long markerId = markerManager.addMarkers(intMarkers.toArray(new InternalMarker[0]), markerInfo, changes); + + if (markerId != EmptyIdentity) + { + compObj.addMarkerID(markerId); + } + + for (InternalMarker marker : intMarkers) { + marker.dispose(); + } - taskMan.addTask(run, mode); + componentManager.addComponentObject(compObj, changes); + renderControl.processChangeSet(changes); + }, mode); return compObj; } @@ -722,55 +697,46 @@ public ComponentObject addScreenLabels(final List labels,final Labe { final ComponentObject compObj = componentManager.makeComponentObject(); final RenderController renderControl = this; + final LabelManager labelManager = this.labelManager; // Do the actual work on the layer thread - Runnable run = - new Runnable() - { - @Override - public void run() - { - ChangeSet changes = new ChangeSet(); - - // Convert to the internal representation for the engine - ArrayList intLabels = new ArrayList(); - for (ScreenLabel label : labels) - { - if (label.text != null && label.text.length() > 0) { - InternalLabel intLabel = new InternalLabel(label,labelInfo); - intLabels.add(intLabel); - - // Keep track of this one for selection - if (label.selectable) { - componentManager.addSelectableObject(label.ident, label, compObj); - } - } - } - - long labelId = EmptyIdentity; - // Note: We can't run multiple of these at once. The problem is that - // we need to pass the JNIEnv deep inside the toolkit and we're setting - // on JNIEnv at a time for the CharRenderer callback. - synchronized (labelManager) { - labelId = labelManager.addLabels(intLabels.toArray(new InternalLabel[0]), labelInfo, changes); - } - if (labelId != EmptyIdentity) - compObj.addLabelID(labelId); - - // Flush the text changes - if (scene != null) { - changes.process(renderControl, scene); - changes.dispose(); - } - - for (InternalLabel label : intLabels) - label.dispose(); - - componentManager.addComponentObject(compObj); + taskMan.addTask(() -> { + ChangeSet changes = new ChangeSet(); + + // Convert to the internal representation for the engine + ArrayList intLabels = new ArrayList<>(labels.size()); + for (ScreenLabel label : labels) + { + if (label.text != null && label.text.length() > 0) { + InternalLabel intLabel = new InternalLabel(label,labelInfo); + intLabels.add(intLabel); + + // Keep track of this one for selection + if (label.selectable) { + componentManager.addSelectableObject(label.ident, label, compObj); } - }; + } + } + + long labelId; + // Note: We can't run multiple of these at once. The problem is that + // we need to pass the JNIEnv deep inside the toolkit and we're setting + // on JNIEnv at a time for the CharRenderer callback. + InternalLabel[] intLabelArr = intLabels.toArray(new InternalLabel[0]); + synchronized (labelManager) { + labelId = labelManager.addLabels(intLabelArr, labelInfo, changes); + } + if (labelId != EmptyIdentity) { + compObj.addLabelID(labelId); + } + + for (InternalLabel label : intLabels) { + label.dispose(); + } - taskMan.addTask(run, mode); + componentManager.addComponentObject(compObj, changes); + renderControl.processChangeSet(changes); + }, mode); return compObj; } @@ -790,55 +756,45 @@ public ComponentObject addScreenMovingLabels(final List label final ComponentObject compObj = componentManager.makeComponentObject(); final double now = System.currentTimeMillis() / 1000.0; final RenderController renderControl = this; + final LabelManager labelManager = this.labelManager; // Do the actual work on the layer thread - Runnable run = - new Runnable() - { - @Override - public void run() - { - ChangeSet changes = new ChangeSet(); - - // Convert to the internal representation for the engine - ArrayList intLabels = new ArrayList(); - for (ScreenMovingLabel label : labels) - { - if (label.text != null && label.text.length() > 0) { - InternalLabel intLabel = new InternalLabel(label,labelInfo,now); - intLabels.add(intLabel); - - // Keep track of this one for selection - if (label.selectable) { - componentManager.addSelectableObject(label.ident, label, compObj); - } - } - } - - long labelId = EmptyIdentity; - // Note: We can't run multiple of these at once. The problem is that - // we need to pass the JNIEnv deep inside the toolkit and we're setting - // on JNIEnv at a time for the CharRenderer callback. - synchronized (labelManager) { - labelId = labelManager.addLabels(intLabels.toArray(new InternalLabel[0]), labelInfo, changes); - } - if (labelId != EmptyIdentity) - compObj.addLabelID(labelId); - - // Flush the text changes - if (scene != null) { - changes.process(renderControl, scene); - changes.dispose(); - } - - for (InternalLabel label : intLabels) - label.dispose(); - - componentManager.addComponentObject(compObj); + taskMan.addTask(() -> { + ChangeSet changes = new ChangeSet(); + + // Convert to the internal representation for the engine + ArrayList intLabels = new ArrayList<>(labels.size()); + for (ScreenMovingLabel label : labels) { + if (label.text != null && label.text.length() > 0) { + InternalLabel intLabel = new InternalLabel(label,labelInfo,now); + intLabels.add(intLabel); + + // Keep track of this one for selection + if (label.selectable) { + componentManager.addSelectableObject(label.ident, label, compObj); } - }; + } + } + + long labelId; + // Note: We can't run multiple of these at once. The problem is that + // we need to pass the JNIEnv deep inside the toolkit and we're setting + // on JNIEnv at a time for the CharRenderer callback. + InternalLabel[] intLabelArr = intLabels.toArray(new InternalLabel[0]); + synchronized (labelManager) { + labelId = labelManager.addLabels(intLabelArr, labelInfo, changes); + } + if (labelId != EmptyIdentity) { + compObj.addLabelID(labelId); + } - taskMan.addTask(run, mode); + for (InternalLabel label : intLabels) { + label.dispose(); + } + + componentManager.addComponentObject(compObj, changes); + renderControl.processChangeSet(changes); + }, mode); return compObj; } @@ -860,43 +816,35 @@ public ComponentObject addVectors(final List vecs,final VectorInfo final RenderController renderControl = this; // Do the actual work on the layer thread - Runnable run = - new Runnable() - { - @Override - public void run() - { - // Vectors are simple enough to just add - ChangeSet changes = new ChangeSet(); - long vecId = vecManager.addVectors(vecs.toArray(new VectorObject[0]), vecInfo, changes); - if (scene != null) { - changes.process(renderControl, scene); - changes.dispose(); - } - - // Track the vector ID for later use - if (vecId != EmptyIdentity) - compObj.addVectorID(vecId); - - // Keep track of this one for selection - for (VectorObject vecObj : vecs) - { - if (vecObj.getSelectable()) { - compObj.addVector(vecObj); - componentManager.addSelectableObject(vecObj.ident, vecObj, compObj); - } - } - - if (vecInfo.disposeAfterUse || disposeAfterRemoval) - for (VectorObject vecObj : vecs) - if (!vecObj.getSelectable()) - vecObj.dispose(); - - componentManager.addComponentObject(compObj); + taskMan.addTask(() -> { + // Vectors are simple enough to just add + ChangeSet changes = new ChangeSet(); + long vecId = vecManager.addVectors(vecs.toArray(new VectorObject[0]), vecInfo, changes); + + // Track the vector ID for later use + if (vecId != EmptyIdentity) + compObj.addVectorID(vecId); + + // Keep track of this one for selection + for (VectorObject vecObj : vecs) + { + if (vecObj.getSelectable()) { + compObj.addVector(vecObj); + componentManager.addSelectableObject(vecObj.ident, vecObj, compObj); + } + } + + if (vecInfo.disposeAfterUse || disposeAfterRemoval) { + for (VectorObject vecObj : vecs) { + if (!vecObj.getSelectable()) { + vecObj.dispose(); } - }; + } + } - taskMan.addTask(run, mode); + componentManager.addComponentObject(compObj, changes); + renderControl.processChangeSet(changes); + }, mode); return compObj; } @@ -919,42 +867,34 @@ public ComponentObject addLoftedPolys(final List vecs, final Lofte final RenderController renderControl = this; // Do the actual work on the layer thread - Runnable run = - new Runnable() - { - @Override - public void run() - { - // Vectors are simple enough to just add - ChangeSet changes = new ChangeSet(); - long loftID = loftManager.addPolys(vecs.toArray(new VectorObject[0]), loftInfo, changes); - if (scene != null) { - changes.process(renderControl, scene); - changes.dispose(); - } - - // Track the vector ID for later use - if (loftID != EmptyIdentity) - compObj.addLoftID(loftID); - - for (VectorObject vecObj : vecs) - { - // TODO: Porting - // Keep track of this one for selection -// if (vecObj.getSelectable()) -// compObj.addSelectID(vecObj.getID()); - } - - if (loftInfo.disposeAfterUse || disposeAfterRemoval) - for (VectorObject vecObj : vecs) - if (!vecObj.getSelectable()) - vecObj.dispose(); - - componentManager.addComponentObject(compObj); + taskMan.addTask(() -> { + // Vectors are simple enough to just add + ChangeSet changes = new ChangeSet(); + long loftID = loftManager.addPolys(vecs.toArray(new VectorObject[0]), loftInfo, changes); + + // Track the vector ID for later use + if (loftID != EmptyIdentity) + compObj.addLoftID(loftID); + + // TODO: Porting + //for (VectorObject vecObj : vecs) + //{ + // // Keep track of this one for selection + // if (vecObj.getSelectable()) + // compObj.addSelectID(vecObj.getID()); + //} + + if (loftInfo.disposeAfterUse || disposeAfterRemoval) { + for (VectorObject vecObj : vecs) { + if (!vecObj.getSelectable()) { + vecObj.dispose(); } - }; + } + } - taskMan.addTask(run, mode); + componentManager.addComponentObject(compObj, changes); + renderControl.processChangeSet(changes); + }, mode); return compObj; } @@ -973,25 +913,15 @@ public void changeVector(final ComponentObject vecObj,final VectorInfo vecInfo,T final RenderController renderControl = this; // Do the actual work on the layer thread - Runnable run = - new Runnable() - { - @Override - public void run() - { - // Vectors are simple enough to just add - ChangeSet changes = new ChangeSet(); - long[] vecIDs = vecObj.getVectorIDs(); - if (vecIDs != null) { - vecManager.changeVectors(vecIDs, vecInfo, changes); - if (scene != null) { - changes.process(renderControl, scene); - changes.dispose(); - } - } - } - }; - taskMan.addTask(run, mode); + taskMan.addTask(() -> { + // Vectors are simple enough to just add + ChangeSet changes = new ChangeSet(); + long[] vecIDs = vecObj.getVectorIDs(); + if (vecIDs != null) { + vecManager.changeVectors(vecIDs, vecInfo, changes); + renderControl.processChangeSet(changes); + } + }, mode); } /** @@ -1008,31 +938,21 @@ public ComponentObject instanceVectors(final ComponentObject vecObj, final Vecto final RenderController renderControl = this; // Do the actual work on the layer thread - Runnable run = - new Runnable() - { - @Override - public void run() - { - // Vectors are simple enough to just add - ChangeSet changes = new ChangeSet(); - long[] vecIDs = vecObj.getVectorIDs(); - if (vecIDs != null) { - for (long vecID : vecIDs) { - long newID = vecManager.instanceVectors(vecID, vecInfo, changes); - if (newID != EmptyIdentity) - compObj.addVectorID(newID); - } - if (scene != null) { - changes.process(renderControl, scene); - changes.dispose(); - } - } - - componentManager.addComponentObject(compObj); - } - }; - taskMan.addTask(run, mode); + taskMan.addTask(() -> { + // Vectors are simple enough to just add + ChangeSet changes = new ChangeSet(); + long[] vecIDs = vecObj.getVectorIDs(); + if (vecIDs != null) { + for (long vecID : vecIDs) { + long newID = vecManager.instanceVectors(vecID, vecInfo, changes); + if (newID != EmptyIdentity) + compObj.addVectorID(newID); + } + } + + componentManager.addComponentObject(compObj, changes); + renderControl.processChangeSet(changes); + }, mode); return compObj; } @@ -1058,42 +978,35 @@ public ComponentObject addWideVectors(final List vecs,final WideVe final RenderController renderControl = this; // Do the actual work on the layer thread - Runnable run = - new Runnable() - { - @Override - public void run() - { - // Vectors are simple enough to just add - ChangeSet changes = new ChangeSet(); - long vecId = wideVecManager.addVectors(vecs.toArray(new VectorObject[0]), wideVecInfo, changes); - if (scene != null) { - changes.process(renderControl, scene); - changes.dispose(); - } - - // Track the vector ID for later use - if (vecId != EmptyIdentity) - compObj.addWideVectorID(vecId); - - for (VectorObject vecObj : vecs) - { - // TODO: Porting - // Keep track of this one for selection -// if (vecObj.getSelectable()) -// compObj.addVector(vecObj); - } - - if (wideVecInfo.disposeAfterUse || disposeAfterRemoval) - for (VectorObject vecObj : vecs) - if (!vecObj.getSelectable()) - vecObj.dispose(); - - componentManager.addComponentObject(compObj); + taskMan.addTask(() -> { + // Vectors are simple enough to just add + ChangeSet changes = new ChangeSet(); + long vecId = wideVecManager.addVectors(vecs.toArray(new VectorObject[0]), wideVecInfo, changes); + + // Track the vector ID for later use + if (vecId != EmptyIdentity) { + compObj.addWideVectorID(vecId); + } + + // TODO: Porting + //for (VectorObject vecObj : vecs) + //{ + // // Keep track of this one for selection + // if (vecObj.getSelectable()) + // compObj.addVector(vecObj); + //} + + if (wideVecInfo.disposeAfterUse || disposeAfterRemoval) { + for (VectorObject vecObj : vecs) { + if (!vecObj.getSelectable()) { + vecObj.dispose(); } - }; + } + } - taskMan.addTask(run, mode); + componentManager.addComponentObject(compObj, changes); + renderControl.processChangeSet(changes); + }, mode); return compObj; } @@ -1116,35 +1029,24 @@ public ComponentObject instanceWideVectors(final ComponentObject inCompObj,final final RenderController renderControl = this; // Do the actual work on the layer thread - Runnable run = - new Runnable() - { - @Override - public void run() - { - // Vectors are simple enough to just add - ChangeSet changes = new ChangeSet(); - - for (long vecID : inCompObj.getWideVectorIDs()) { - long instID = wideVecManager.instanceVectors(vecID,wideVecInfo,changes); + taskMan.addTask(() -> { + // Vectors are simple enough to just add + ChangeSet changes = new ChangeSet(); - if (instID != EmptyIdentity) - compObj.addWideVectorID(instID); - } + for (long vecID : inCompObj.getWideVectorIDs()) { + long instID = wideVecManager.instanceVectors(vecID,wideVecInfo,changes); - if (scene != null) { - changes.process(renderControl, scene); - changes.dispose(); - } - - componentManager.addComponentObject(compObj); - } - }; + if (instID != EmptyIdentity) + compObj.addWideVectorID(instID); + } - taskMan.addTask(run, mode); + componentManager.addComponentObject(compObj, changes); + renderControl.processChangeSet(changes); + }, mode); return compObj; } + // TODO: Fill this in // public ComponentObject addModelInstances(); @@ -1162,32 +1064,27 @@ public ComponentObject addShapes(final List shapes, final ShapeInfo shape final ChangeSet changes = new ChangeSet(); final RenderController renderControl = this; - Runnable run = new Runnable() { - @Override - public void run() { - long shapeId = shapeManager.addShapes(shapes.toArray(new Shape[0]), shapeInfo, changes); - if (shapeId != EmptyIdentity) - compObj.addShapeID(shapeId); - if (scene != null) { - changes.process(renderControl, scene); - changes.dispose(); - } - - for (Shape shape : shapes) - if (shape.isSelectable()) - { - componentManager.addSelectableObject(shape.getSelectID(), shape, compObj); - } + taskMan.addTask(() -> { + long shapeId = shapeManager.addShapes(shapes.toArray(new Shape[0]), shapeInfo, changes); + if (shapeId != EmptyIdentity) + compObj.addShapeID(shapeId); - if (shapeInfo.disposeAfterUse || disposeAfterRemoval) - for (Shape shape : shapes) - shape.dispose(); + for (Shape shape : shapes) { + if (shape.isSelectable()) { + componentManager.addSelectableObject(shape.getSelectID(), shape, compObj); + } + } - componentManager.addComponentObject(compObj); + if (shapeInfo.disposeAfterUse || disposeAfterRemoval) { + for (Shape shape : shapes) { + shape.dispose(); + } } - }; - taskMan.addTask(run, mode); + componentManager.addComponentObject(compObj, changes); + renderControl.processChangeSet(changes); + }, mode); + return compObj; } @@ -1206,34 +1103,25 @@ public ComponentObject addStickers(final List stickers,final StickerInf final RenderController renderControl = this; // Do the actual work on the layer thread - Runnable run = - new Runnable() - { - @Override - public void run() - { - ChangeSet changes = new ChangeSet(); - - // Stickers are added one at a time for some reason - long stickerID = stickerManager.addStickers(stickers.toArray(new Sticker[0]), stickerInfo, changes); - - if (stickerID != EmptyIdentity) { - compObj.addStickerID(stickerID); - } - if (scene != null) { - changes.process(renderControl, scene); - changes.dispose(); - } - - if (stickerInfo.disposeAfterUse || disposeAfterRemoval) - for (Sticker sticker : stickers) - sticker.dispose(); - - componentManager.addComponentObject(compObj); - } - }; + taskMan.addTask(() -> { + ChangeSet changes = new ChangeSet(); + + // Stickers are added one at a time for some reason + long stickerID = stickerManager.addStickers(stickers.toArray(new Sticker[0]), stickerInfo, changes); + + if (stickerID != EmptyIdentity) { + compObj.addStickerID(stickerID); + } + + if (stickerInfo.disposeAfterUse || disposeAfterRemoval) { + for (Sticker sticker : stickers) { + sticker.dispose(); + } + } - taskMan.addTask(run, mode); + componentManager.addComponentObject(compObj, changes); + renderControl.processChangeSet(changes); + }, mode); return compObj; } @@ -1252,30 +1140,18 @@ public ComponentObject changeSticker(final ComponentObject stickerObj,final Stic final RenderController renderControl = this; // Do the actual work on the layer thread - Runnable run = - new Runnable() - { - @Override - public void run() - { - ChangeSet changes = new ChangeSet(); - - long[] stickerIDs = stickerObj.getStickerIDs(); - if (stickerIDs != null && stickerIDs.length > 0) { - for (long stickerID : stickerIDs) - stickerManager.changeSticker(stickerID, stickerInfo, changes); - } - - if (scene != null) { - changes.process(renderControl, scene); - changes.dispose(); - } - - componentManager.addComponentObject(compObj); - } - }; + taskMan.addTask(() -> { + ChangeSet changes = new ChangeSet(); + + long[] stickerIDs = stickerObj.getStickerIDs(); + if (stickerIDs != null && stickerIDs.length > 0) { + for (long stickerID : stickerIDs) + stickerManager.changeSticker(stickerID, stickerInfo, changes); + } - taskMan.addTask(run, mode); + componentManager.addComponentObject(compObj, changes); + renderControl.processChangeSet(changes); + }, mode); return compObj; } @@ -1289,65 +1165,48 @@ public ComponentObject addBillboards(final List bills, final Billboar final RenderController renderControl = this; // Do the actual work on the layer thread - Runnable run = - new Runnable() - { - @Override - public void run() - { - ChangeSet changes = new ChangeSet(); - - // Have to set the shader ID if it's not already - long shaderID = info.getShaderID(); - if (info.getShaderID() == 0) { - String shaderName = null; - // TODO: Share these constants with the c++ code - if (info.getOrient() == BillboardInfo.Orient.Eye) - shaderName = "billboardorienteye"; - else - shaderName = "billboardorientground"; - Shader shader = getShader(shaderName); - - shaderID = shader.getID(); - info.setShaderID(shaderID); - } - - for (Billboard bill : bills) { - // Convert to display space - Point3d center = bill.getCenter(); - Point3d localPt =coordAdapter.getCoordSystem().geographicToLocal(new Point3d(center.getX(),center.getY(),0.0)); - Point3d dispTmp =coordAdapter.localToDisplay(localPt); - Point3d dispPt = dispTmp.multiplyBy(center.getZ()/6371000.0+1.0); - bill.setCenter(dispPt); - - if (bill.getSelectable()) { - bill.setSelectID(Identifiable.genID()); - componentManager.addSelectableObject(bill.getSelectID(), bill, compObj); - } - - // Turn any screen objects into billboard polygons - bill.flatten(); - } - - long billId = billboardManager.addBillboards(bills.toArray(new Billboard[0]), info, changes); - compObj.addBillboardID(billId); - - // Flush the text changes - if (scene != null) { - changes.process(renderControl, scene); - changes.dispose(); - } - - if (info.disposeAfterUse || disposeAfterRemoval) - for (Billboard bill : bills) - if (!bill.getSelectable()) - bill.dispose(); - - componentManager.addComponentObject(compObj); + taskMan.addTask(() -> { + ChangeSet changes = new ChangeSet(); + + // Have to set the shader ID if it's not already + if (info.getShaderID() == 0) { + // TODO: Share these constants with the c++ code + String shaderName = (info.getOrient() == BillboardInfo.Orient.Eye) ? "billboardorienteye" : "billboardorientground"; + Shader shader = getShader(shaderName); + info.setShaderID(shader.getID()); + } + + for (Billboard bill : bills) { + // Convert to display space + Point3d center = bill.getCenter(); + Point3d localPt =coordAdapter.getCoordSystem().geographicToLocal(new Point3d(center.getX(),center.getY(),0.0)); + Point3d dispTmp =coordAdapter.localToDisplay(localPt); + Point3d dispPt = dispTmp.multiplyBy(center.getZ()/6371000.0+1.0); + bill.setCenter(dispPt); + + if (bill.getSelectable()) { + bill.setSelectID(Identifiable.genID()); + componentManager.addSelectableObject(bill.getSelectID(), bill, compObj); + } + + // Turn any screen objects into billboard polygons + bill.flatten(); + } + + long billId = billboardManager.addBillboards(bills.toArray(new Billboard[0]), info, changes); + compObj.addBillboardID(billId); + + if (info.disposeAfterUse || disposeAfterRemoval) { + for (Billboard bill : bills) { + if (!bill.getSelectable()) { + bill.dispose(); } - }; + } + } - taskMan.addTask(run, threadMode); + componentManager.addComponentObject(compObj, changes); + renderControl.processChangeSet(changes); + }, threadMode); return compObj; } @@ -1366,38 +1225,28 @@ public ComponentObject addPoints(final List ptList,final GeometryInfo ge final RenderController renderControl = this; // Do the actual work on the layer thread - Runnable run = - new Runnable() - { - @Override - public void run() - { - ChangeSet changes = new ChangeSet(); - - // Stickers are added one at a time for some reason - for (Points pts: ptList) { - Matrix4d mat = pts.mat != null ? pts.mat : new Matrix4d(); - long geomID = geomManager.addGeometryPoints(pts.rawPoints,pts.mat,geomInfo,changes); - - if (geomID != EmptyIdentity) { - compObj.addGeometryID(geomID); - } - } - - if (scene != null) { - changes.process(renderControl, scene); - changes.dispose(); - } - - if (geomInfo.disposeAfterUse || disposeAfterRemoval) - for (Points pts: ptList) - pts.rawPoints.dispose(); - - componentManager.addComponentObject(compObj); - } - }; + taskMan.addTask(() -> { + ChangeSet changes = new ChangeSet(); + + // Stickers are added one at a time for some reason + for (Points pts: ptList) { + //Matrix4d mat = pts.mat != null ? pts.mat : new Matrix4d(); + long geomID = geomManager.addGeometryPoints(pts.rawPoints,pts.mat,geomInfo,changes); + + if (geomID != EmptyIdentity) { + compObj.addGeometryID(geomID); + } + } + + if (geomInfo.disposeAfterUse || disposeAfterRemoval) { + for (Points pts : ptList) { + pts.rawPoints.dispose(); + } + } - taskMan.addTask(run, mode); + componentManager.addComponentObject(compObj, changes); + renderControl.processChangeSet(changes); + }, mode); return compObj; } @@ -1416,27 +1265,14 @@ public MaplyTexture addTexture(final Bitmap image,final RenderController.Texture final RenderController renderControl = this; // Possibly do the work somewhere else - Runnable run = - new Runnable() - { - @Override - public void run() - { - ChangeSet changes = new ChangeSet(); - - rawTex.setBitmap(image,settings.imageFormat.ordinal()); - rawTex.setSettings(settings.wrapU,settings.wrapV); - changes.addTexture(rawTex, scene, settings.filterType.ordinal()); - - // Flush the texture changes - if (scene != null) { - changes.process(renderControl, scene); - changes.dispose(); - } - } - }; + taskMan.addTask(() -> { + ChangeSet changes = new ChangeSet(); - taskMan.addTask(run, mode); + rawTex.setBitmap(image,settings.imageFormat.ordinal()); + rawTex.setSettings(settings.wrapU,settings.wrapV); + changes.addTexture(rawTex, scene, settings.filterType.ordinal()); + renderControl.processChangeSet(changes); + }, mode); return texture; } @@ -1453,25 +1289,12 @@ public MaplyTexture addTexture(final Texture rawTex,final TextureSettings settin final RenderController renderControl = this; // Possibly do the work somewhere else - Runnable run = - new Runnable() - { - @Override - public void run() - { - ChangeSet changes = new ChangeSet(); - texture.texID = rawTex.getID(); - changes.addTexture(rawTex, scene, settings.filterType.ordinal()); - - // Flush the texture changes - if (scene != null) { - changes.process(renderControl, scene); - changes.dispose(); - } - } - }; - - taskMan.addTask(run, mode); + taskMan.addTask(() -> { + ChangeSet changes = new ChangeSet(); + texture.texID = rawTex.getID(); + changes.addTexture(rawTex, scene, settings.filterType.ordinal()); + renderControl.processChangeSet(changes); + }, mode); return texture; } @@ -1494,27 +1317,14 @@ public MaplyTexture createTexture(final int width,final int height,final Texture final RenderController renderControl = this; // Possibly do the work somewhere else - Runnable run = - new Runnable() - { - @Override - public void run() - { - ChangeSet changes = new ChangeSet(); - - rawTex.setSize(width,height); - rawTex.setIsEmpty(true); - changes.addTexture(rawTex, scene, settings.filterType.ordinal()); - - // Flush the texture changes - if (scene != null) { - changes.process(renderControl, scene); - changes.dispose(); - } - } - }; + taskMan.addTask(() -> { + ChangeSet changes = new ChangeSet(); - taskMan.addTask(run, mode); + rawTex.setSize(width,height); + rawTex.setIsEmpty(true); + changes.addTexture(rawTex, scene, settings.filterType.ordinal()); + renderControl.processChangeSet(changes); + }, mode); return texture; } @@ -1529,31 +1339,19 @@ public void removeTextures(final List texs,ThreadMode mode) final RenderController renderControl = this; // Do the actual work on the layer thread - Runnable run = - new Runnable() - { - @Override - public void run() - { - ChangeSet changes = new ChangeSet(); - - for (MaplyTexture tex : texs) - changes.removeTexture(tex.texID); - - // Flush the texture changes - if (scene != null) { - changes.process(renderControl, scene); - changes.dispose(); - } - } - }; + taskMan.addTask(() -> { + ChangeSet changes = new ChangeSet(); - taskMan.addTask(run, mode); + for (MaplyTexture tex : texs) { + changes.removeTexture(tex.texID); + } + renderControl.processChangeSet(changes); + }, mode); } public void removeTexture(final MaplyTexture tex,ThreadMode mode) { - ArrayList texs = new ArrayList(); + ArrayList texs = new ArrayList<>(1); texs.add(tex); removeTextures(texs,mode); @@ -1571,26 +1369,15 @@ public void removeTexturesByID(final List texIDs,ThreadMode mode) final RenderController renderControl = this; // Do the actual work on the layer thread - Runnable run = - new Runnable() - { - @Override - public void run() - { - ChangeSet changes = new ChangeSet(); - - for (Long texID : texIDs) - changes.removeTexture(texID); - - // Flush the texture changes - if (scene != null) { - changes.process(renderControl, scene); - changes.dispose(); - } - } - }; + taskMan.addTask(() -> { + ChangeSet changes = new ChangeSet(); + + for (Long texID : texIDs) { + changes.removeTexture(texID); + } - taskMan.addTask(run, mode); + renderControl.processChangeSet(changes); + }, mode); } /** Add a render target to the system @@ -1604,6 +1391,7 @@ public void addRenderTarget(RenderTarget renderTarget) renderTarget.texture.width,renderTarget.texture.height, renderTarget.texture.texID, renderTarget.clearEveryFrame, + renderTarget.clearVal, renderTarget.blend, Color.red(renderTarget.color)/255.f,Color.green(renderTarget.color)/255.f,Color.blue(renderTarget.color)/255.f,Color.alpha(renderTarget.color)/255.f); } @@ -1632,21 +1420,11 @@ public void clearRenderTarget(final RenderTarget renderTarget, ThreadMode mode) { final RenderController renderControl = this; - Runnable run = new Runnable() - { - @Override - public void run() - { - ChangeSet changes = new ChangeSet(); - changes.clearRenderTarget(renderTarget.renderTargetID); - if (scene != null) { - changes.process(renderControl, scene); - changes.dispose(); - } - } - }; - - taskMan.addTask(run, mode); + taskMan.addTask(() -> { + ChangeSet changes = new ChangeSet(); + changes.clearRenderTarget(renderTarget.renderTargetID); + renderControl.processChangeSet(changes); + }, mode); } /** @@ -1662,26 +1440,18 @@ public void disableObjects(final List compObjs,ThreadMode mode) if (compObjs == null || compObjs.size() == 0) return; - final ComponentObject[] localCompObjs = compObjs.toArray(new ComponentObject[compObjs.size()]); + final ComponentObject[] localCompObjs = compObjs.toArray(new ComponentObject[0]); final RenderController renderControl = this; - Runnable run = new Runnable() - { - @Override - public void run() - { - ChangeSet changes = new ChangeSet(); - for (ComponentObject compObj : localCompObjs) - if (compObj != null) - componentManager.enableComponentObject(compObj,false,changes); - if (scene != null) { - changes.process(renderControl, scene); - changes.dispose(); + taskMan.addTask(() -> { + ChangeSet changes = new ChangeSet(); + for (ComponentObject compObj : localCompObjs) { + if (compObj != null) { + componentManager.enableComponentObject(compObj, false, changes); } } - }; - - taskMan.addTask(run, mode); + renderControl.processChangeSet(changes); + }, mode); } /** @@ -1693,10 +1463,10 @@ public void run() */ public void enableObjects(final List compObjs,ThreadMode mode) { - if (compObjs == null || compObjs == null) + if (compObjs == null) return; - final ComponentObject[] localCompObjs = compObjs.toArray(new ComponentObject[compObjs.size()]); + final ComponentObject[] localCompObjs = compObjs.toArray(new ComponentObject[0]); enableObjects(localCompObjs,mode); } @@ -1713,24 +1483,15 @@ public void enableObjects(final ComponentObject[] compObjs,ThreadMode mode) { final RenderController renderControl = this; - Runnable run = - new Runnable() - { - @Override - public void run() - { - ChangeSet changes = new ChangeSet(); - for (ComponentObject compObj : compObjs) - if (compObj != null) - componentManager.enableComponentObject(compObj,true,changes); - if (scene != null) { - changes.process(renderControl, scene); - changes.dispose(); - } - } - }; - - taskMan.addTask(run, mode); + taskMan.addTask(() -> { + ChangeSet changes = new ChangeSet(); + for (ComponentObject compObj : compObjs) { + if (compObj != null) { + componentManager.enableComponentObject(compObj, true, changes); + } + } + renderControl.processChangeSet(changes); + }, mode); } /** @@ -1742,7 +1503,7 @@ public void run() */ public void removeObjects(final List compObjs,ThreadMode mode) { - if (compObjs == null || compObjs == null) + if (compObjs == null) return; removeObjects(compObjs.toArray(new ComponentObject[0]),mode); @@ -1761,23 +1522,12 @@ public void removeObjects(final ComponentObject[] compObjs,ThreadMode mode) { final RenderController renderControl = this; - Runnable run = new Runnable() - { - @Override - public void run() - { - ChangeSet changes = new ChangeSet(); - - componentManager.removeComponentObjects(compObjs,changes,disposeAfterRemoval); + taskMan.addTask(() -> { + ChangeSet changes = new ChangeSet(); - if (scene != null) { - changes.process(renderControl, scene); - changes.dispose(); - } - } - }; - - taskMan.addTask(run, mode); + componentManager.removeComponentObjects(compObjs,changes,disposeAfterRemoval); + renderControl.processChangeSet(changes); + }, mode); } /** @@ -1788,14 +1538,14 @@ public void run() */ public void removeObject(final ComponentObject compObj,ThreadMode mode) { - List compObjs = new ArrayList(); + List compObjs = new ArrayList<>(1); compObjs.add(compObj); removeObjects(compObjs,mode); } // All the shaders currently in use - private ArrayList shaders = new ArrayList<>(); + final private ArrayList shaders = new ArrayList<>(50); /** * Associate a shader with the given scene name. These names let us override existing shaders, as well as adding our own. @@ -1819,7 +1569,7 @@ protected void addPreBuiltShader(Shader shader) synchronized (shaders) { shaders.add(shader); } - shader.control = new WeakReference(this); + shader.control = new WeakReference<>(this); } /** @@ -1936,8 +1686,12 @@ static public ContextInfo getEGLContext() { public void processChangeSet(ChangeSet changes) { - changes.process(this, scene); - changes.dispose(); + if (changes != null) { + if (scene != null) { + changes.process(this, scene); + } + changes.dispose(); + } } // Don't need this in standalone mode @@ -1998,5 +1752,7 @@ public void finalize() native void initialise(int width,int height); native void initialise(); native void dispose(); + + @SuppressWarnings({"unused", "RedundantSuppression"}) private long nativeHandle; } diff --git a/android/library/maply/src/main/java/com/mousebird/maply/RenderTarget.java b/android/library/maply/src/main/java/com/mousebird/maply/RenderTarget.java index 04688ea116..f4eb1a8f34 100644 --- a/android/library/maply/src/main/java/com/mousebird/maply/RenderTarget.java +++ b/android/library/maply/src/main/java/com/mousebird/maply/RenderTarget.java @@ -28,6 +28,12 @@ public class RenderTarget */ public boolean clearEveryFrame = true; + /** + * Clear the render target to this value on every frame. + * This is for render targets that are not purely color, such as multiple floats. + */ + public float clearVal = 0.0f; + /** * Clear the render target to this color every frame. * Default is clear black. diff --git a/android/library/maply/src/main/java/com/mousebird/maply/Scene.java b/android/library/maply/src/main/java/com/mousebird/maply/Scene.java index 46499e5eea..6944ab591a 100644 --- a/android/library/maply/src/main/java/com/mousebird/maply/Scene.java +++ b/android/library/maply/src/main/java/com/mousebird/maply/Scene.java @@ -27,7 +27,7 @@ public void addChanges(ChangeSet changes) addChangesNative(changes); } - // Overriden by subclass + // Overridden by subclass public void shutdown() { dispose(); @@ -39,7 +39,7 @@ public void shutdown() */ public native void addShaderProgram(Shader shader); public native void removeShaderProgram(long shaderID); - public native void addRenderTargetNative(long renderTargetID,int width,int height,long texID,boolean clearEveryFrame,boolean blend,float red,float green,float blue,float alpha); + public native void addRenderTargetNative(long renderTargetID,int width,int height,long texID,boolean clearEveryFrame,float clearVal,boolean blend,float red,float green,float blue,float alpha); public native void changeRenderTarget(long renderTargetID,long texID); public native void removeRenderTargetNative(long renderTargetID); diff --git a/android/library/maply/src/main/java/com/mousebird/maply/ScreenMarker.java b/android/library/maply/src/main/java/com/mousebird/maply/ScreenMarker.java index 012db65849..624aac8458 100644 --- a/android/library/maply/src/main/java/com/mousebird/maply/ScreenMarker.java +++ b/android/library/maply/src/main/java/com/mousebird/maply/ScreenMarker.java @@ -1,9 +1,8 @@ -/* - * ScreenMarker.java +/* ScreenMarker.java * WhirlyGlobeLib * * Created by Steve Gifford on 6/2/14. - * Copyright 2011-2014 mousebird consulting + * Copyright 2011-2021 mousebird consulting * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -15,7 +14,6 @@ * 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. - * */ package com.mousebird.maply; diff --git a/android/library/maply/src/main/java/com/mousebird/maply/SimpleStyle.kt b/android/library/maply/src/main/java/com/mousebird/maply/SimpleStyle.kt new file mode 100644 index 0000000000..aae800ae81 --- /dev/null +++ b/android/library/maply/src/main/java/com/mousebird/maply/SimpleStyle.kt @@ -0,0 +1,57 @@ +/* SimpleStyle.kt + * WhirlyGlobeLib + * + * Created by Tim Sylvester on 11/02/2021 + * Copyright © 2021 mousebird consulting, inc. + * + * 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. + */ + +package com.mousebird.maply + +import android.util.Size +import androidx.annotation.ColorInt + +class SimpleStyle { + var title: String? = null + var description: String? = null + + var markerSymbol: String? = null + var backgroundSymbol: String? = null + var markerString: String? = null + var markerSize: Point2d? = null + var markerCenter: Point2d? = null + var backgroundCenter: Point2d? = null + var markerOffset: Point2d? = null + @ColorInt + var markerColor: Int? = null + var markerScale: Double? = null + var backgroundScale: Double? = null + + var clearBackground: Boolean? = null + + var fillOpacity: Float? = null + @ColorInt + var fillColor: Int? = null + + var strokeWidth: Float? = null + var strokeOpacity: Float? = null + @ColorInt + var strokeColor: Int? = null + + @ColorInt + var labelColor: Int? = null + var labelSize: Float? = null + var labelOffset: Point2d? = null + + var layoutSize: Size? = null + + var markerTexture: MaplyTexture? = null +} \ No newline at end of file diff --git a/android/library/maply/src/main/java/com/mousebird/maply/SimpleStyleManager.kt b/android/library/maply/src/main/java/com/mousebird/maply/SimpleStyleManager.kt new file mode 100644 index 0000000000..e3ba97717a --- /dev/null +++ b/android/library/maply/src/main/java/com/mousebird/maply/SimpleStyleManager.kt @@ -0,0 +1,552 @@ +/* SimpleStyleManager.kt + * WhirlyGlobeLib + * + * Created by Tim Sylvester on 11/02/2021 + * Copyright © 2021 mousebird consulting, inc. + * + * 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. + */ + +package com.mousebird.maply + +import android.content.Context +import android.content.res.AssetManager +import android.graphics.* +import android.util.Log +import android.util.Size +import androidx.annotation.ColorInt +import androidx.collection.LruCache +import androidx.core.graphics.* +import java.io.* +import java.lang.ref.WeakReference +import java.util.* +import kotlin.math.ceil +import kotlin.math.min + +class SimpleStyleManager(context: Context, vc: RenderControllerInterface, assetManager: AssetManager? = null) { + + var smallSize = Point2d(16.0, 16.0) + var medSize = Point2d(32.0, 32.0) + var largeSize = Point2d(64.0, 64.0) + + @ColorInt + var defaultColor = 0xFF555555.toInt() + var filterAlpha = 127 + var filterMode = PorterDuff.Mode.MULTIPLY + + var defaultMarkerStrokeWidth = 2.0f + + var markerScale = 1.0 + var markerBackgroundScale = 1.0 + + var sharedCacheSize: Int + get() { return Shared.cacheSize } + set(value) { Shared.cacheSize = value } + + interface StyleObjectLocator { + fun locate(name: String): Collection + } + var objectLocator: StyleObjectLocator = object : StyleObjectLocator { + override fun locate(name: String): Collection { + return listOf(name, "$name.png") + } + } + + // Callbacks give the caller a chance to intervene after the ScreenMarker, etc., + // is created but before it's added to the map. Return false to skip the item. + var onAddMarker: ((VectorObject,ScreenMarker,MarkerInfo,SimpleStyle) -> Boolean)? = null + var onAddLabel: ((VectorObject,ScreenLabel,LabelInfo,SimpleStyle) -> Boolean)? = null + var onAddLine: ((VectorObject,WideVectorInfo,SimpleStyle) -> Boolean)? = null + var onAddArea: ((VectorObject,VectorInfo,SimpleStyle) -> Boolean)? = null + + /** + * Add a styled object, splitting it up if necessary. + */ + fun addFeatures(obj: VectorObject, mode: ThreadMode = threadCurrent): Sequence { + return addFeatures(listOf(obj), null, mode) + } + + /** + * Add a collection of styled objects, splitting each if necessary. + */ + fun addFeatures(objs: Collection, mode: ThreadMode = threadCurrent): Sequence { + return addFeatures(objs, null, mode) + } + + /** + * Add an object with a specific style, ignoring its attributes, splitting it if necessary + */ + fun addFeatures(obj: VectorObject, style: SimpleStyle, mode: ThreadMode = threadCurrent): Sequence { + return addFeatures(listOf(obj), style, mode) + } + + /** + * Add objects with a specific style, ignoring its attributes, splitting it if necessary + */ + fun addFeatures(objs: Collection, style: SimpleStyle? = null, mode: ThreadMode = threadCurrent): Sequence { + return sequence { + for (obj in objs) { + val split = obj.splitVectors() + if (split != null) { + for (vec: VectorObject in split) { + yieldAll(addFeaturesInternal(vec, style, mode)) + } + } else { + yieldAll(addFeaturesInternal(obj, style, mode)) + } + } + } + } + + fun addFeatures(markers: Sequence, attrs: AttrDictionary, info: MarkerInfo? = null, mode: ThreadMode = threadCurrent): ComponentObject? = + addFeatures(markers, makeStyle(attrs), info, mode) + + fun addFeatures(markers: Sequence, style: SimpleStyle, inInfo: MarkerInfo? = null, mode: ThreadMode = threadCurrent): ComponentObject? { + val vc = this.vc.get() ?: return null + val tex = style.markerTexture ?: return null + markers.forEach { marker -> + marker.tex = tex + marker.size = style.markerSize + style.markerOffset?.let { + marker.offset = it + } + } + val info = inInfo ?: MarkerInfo().apply { + enable = true + } + return vc.addScreenMarkers(markers.toList(), info, mode) + } + + fun addFeatures(labels: Sequence, attrs: AttrDictionary, info: LabelInfo? = null, mode: ThreadMode = threadCurrent): ComponentObject? = + addFeatures(labels, makeStyle(attrs), info, mode) + + fun addFeatures(labels: Sequence, style: SimpleStyle, inInfo: LabelInfo? = null, mode: ThreadMode = threadCurrent): ComponentObject? { + val vc = this.vc.get() ?: return null + if (style.labelColor != null && style.labelSize != null && (style.title ?: "").isNotEmpty()) { + labels.forEach { label -> + label.text = style.title + style.labelOffset?.let { + label.offset = it + } + label.layoutImportance = Float.MAX_VALUE + } + val info = inInfo ?: LabelInfo() + style.labelSize?.let { + info.fontSize = it + } + style.labelColor?.let { + info.textColor = it + } + return vc.addScreenLabels(labels.toList(), info, mode) + } + return null + } + + fun makeStyle(dict: AttrDictionary): SimpleStyle { + val style = SimpleStyle() + + style.title = dict.getString("title") + style.description = dict.getString("description") + + style.markerSize = medSize + dict.getString("marker-size")?.let { + when (it) { + "small" -> style.markerSize = smallSize + "large" -> style.markerSize = largeSize + } + } + + style.markerSymbol = dict.getString("marker-symbol") + style.backgroundSymbol = dict.getString("marker-background-symbol") + + style.markerSymbol?.let { + if (it.length == 1) { + style.markerString = it + style.markerSymbol = null + } + } + + val markerAlpha = ((dict.getDouble("marker-opacity") ?: 1.0) * 255).toInt() + style.markerColor = parseColor(dict.getString("marker-color"), markerAlpha) + + style.strokeColor = parseColor(dict.getString("stroke")) + style.strokeWidth = dict.getString("stroke-width")?.toFloatOrNull() + style.strokeOpacity = dict.getString("stroke-opacity")?.toFloatOrNull() + + style.fillColor = parseColor(dict["fill"]?.toString()) + style.fillOpacity = dict.getString("fill-opacity")?.toFloatOrNull() + + val cx = dict.getString("marker-center-x")?.toDoubleOrNull() + val cy = dict.getString("marker-center-y")?.toDoubleOrNull() + if (cx != null || cy != null) { + style.markerCenter = Point2d(cx ?: 0.0, cy ?: 0.0) + } + + val bcx = dict.getString("marker-background-center-x")?.toDoubleOrNull() + val bcy = dict.getString("marker-background-center-y")?.toDoubleOrNull() + if (bcx != null || bcy != null) { + style.backgroundCenter = Point2d(bcx ?: 0.0, bcy ?: 0.0) + } + + val ox = dict.getString("marker-offset-x")?.toDoubleOrNull() + val oy = dict.getString("marker-offset-y")?.toDoubleOrNull() + if ((ox != null || oy != null) && style.markerSize != null) { + style.markerSize?.let { + style.markerOffset = Point2d((it.x * (ox ?: 0.0)), + (it.y * (oy ?: 0.0))) + } + } + + style.markerScale = dict.getString("marker-scale")?.toDoubleOrNull() + style.backgroundScale = dict.getString("marker-background-scale")?.toDoubleOrNull() + + style.labelColor = parseColor(dict.getString("label")) + style.labelSize = dict.getString("label-size")?.toFloatOrNull() + + val lx = dict.getString("label-offset-x")?.toDoubleOrNull() + val ly = dict.getString("label-offset-y")?.toDoubleOrNull() + if (lx != null || ly != null) { + style.labelSize?.let { + style.labelOffset = Point2d((it * (lx ?: 0.0)), + (it * (ly ?: 0.0))) + } + } + + style.clearBackground = parseBool(dict.getString("marker-circle")) + + style.markerTexture = textureForStyle(style) + + return style + } + + /** + * Call when done with all the generated objects. + */ + fun shutdown() { + synchronized(textureCache) { + vc.get()?.removeTextures(textureCache.elements().toList(), threadCurrent) + textureCache.clear() + } + } + + private fun addFeaturesInternal(obj: VectorObject, optStyle: SimpleStyle? = null, mode: ThreadMode = threadCurrent): Sequence { + val vc = this.vc.get() ?: return sequenceOf() + val style = optStyle ?: makeStyle(obj.attributes) + when (obj.vectorType) { + VectorObject.MaplyVectorObjectType.MaplyVectorPointType -> { + var markerObj: ComponentObject? = null + var labelObj: ComponentObject? = null + if (style.markerTexture != null) { + val marker = ScreenMarker().apply { + loc = obj.center() + tex = style.markerTexture + size = style.markerSize + offset = style.markerOffset ?: Point2d(0.0, 0.0) + } + val info = MarkerInfo().apply { + enable = true + } + if (onAddMarker?.invoke(obj,marker,info,style) != false) { + markerObj = vc.addScreenMarkers(listOf(marker), info, mode) + } + } + if (style.labelColor != null && style.labelSize != null && (style.title ?: "").isNotEmpty()) { + val label = ScreenLabel().apply { + text = style.title + loc = obj.center() + offset = style.labelOffset ?: Point2d(0.0, 0.0) + layoutImportance = Float.MAX_VALUE + } + val info = LabelInfo().apply { + fontSize = style.labelSize ?: 10f + textColor = style.labelColor ?: defaultColor + } + if (onAddLabel?.invoke(obj,label,info,style) != false) { + labelObj = vc.addScreenLabels(listOf(label), info, mode) + } + } + return sequenceOf(markerObj, labelObj).filterNotNull() + } + VectorObject.MaplyVectorObjectType.MaplyVectorLinearType -> { + val info = WideVectorInfo() + info.setLineWidth(style.strokeWidth ?: 2f) + info.setColor(resolveColor(style.strokeColor, style.strokeOpacity)) + if (onAddLine?.invoke(obj,info,style) != false) { + return sequenceOf(vc.addWideVectors(listOf(obj), info, threadCurrent)) + } + } + VectorObject.MaplyVectorObjectType.MaplyVectorArealType -> { + val info = VectorInfo() + info.setColor(resolveColor(style.fillColor, style.fillOpacity)) + info.setFilled(true) + if (onAddArea?.invoke(obj,info,style) != false) { + return sequenceOf(vc.addVectors(listOf(obj), info, mode)) + } + } + else -> return sequenceOf() + } + return sequenceOf() + } + + private fun tryLoadImage(stream: InputStream): Bitmap { + return BufferedInputStream(stream).use { BitmapFactory.decodeStream(it) } + } + + private fun tryLoadAssetImage(name: String): Bitmap { + return assets.open(name).use { tryLoadImage(it) } + } + + private fun tryLoadFileImage(name: String): Bitmap { + return File(name).inputStream().use { tryLoadImage(it) } + } + + private fun loadImage(name: String): Bitmap? { + try { + for (location in objectLocator.locate(name)) { + try { return tryLoadFileImage(location) } catch (e: FileNotFoundException) { + Log.d(logTag, "Failed to load file $location: ${e.message}") + } + try { return tryLoadAssetImage(location) } catch (e: FileNotFoundException) { + Log.d(logTag, "Failed to load asset $location: ${e.message}") + } + } + } catch (e: Exception) { + Log.w(logTag, String.format("Failed to load '%s': '%s'", name, e.localizedMessage)) + } + return null + } + + private fun loadImageCached(name: String?): Bitmap? { + if (name == null || name.isEmpty()) { + return null + } + + synchronized(Shared.imageCache) { + Shared.imageCache[name]?.let { return it } + } + + return try { + loadImage(name) + } catch (e: Exception) { + Log.w(logTag, String.format("Failed to load '%s': '%s'", name, e.localizedMessage)) + null + }?.also { + synchronized(Shared.imageCache) { + Shared.imageCache.put(name, it) + } + } + } + + private fun iconForName(name: String, size: Size, @ColorInt color: Int, + @ColorInt circleColor: Int, strokeWidth: Float, + @ColorInt strokeColor: Int): Bitmap? { + val cacheKey = "${name}_${size.width}_${size.height}_" + + "${circleColor}_${strokeWidth}_${strokeColor}" + + synchronized(Shared.imageCache) { + Shared.imageCache[cacheKey]?.let { return it } + } + + var image = loadImageCached(name) ?: return null + + val bitmap = Bitmap.createBitmap(size.width, size.height, Bitmap.Config.ARGB_8888) + bitmap.eraseColor(Color.TRANSPARENT) + + val canvas = Canvas(bitmap) + + val paint = Paint(Paint.ANTI_ALIAS_FLAG.or(Paint.FILTER_BITMAP_FLAG)) + canvas.drawBitmap(image, 0f, 0f, paint) + + synchronized(Shared.imageCache) { + Shared.imageCache.put(cacheKey, bitmap) + } + + return bitmap + } + + // Make a cache key from anything that can affect the way the texture looks + private fun styleCacheKey(style: SimpleStyle): String { + return sequenceOf( + style.markerSymbol, + style.backgroundSymbol, + style.markerString, + style.markerCenter?.x, + style.markerCenter?.y, + style.backgroundCenter?.x, + style.backgroundCenter?.y, + style.markerSize?.x, + style.markerSize?.y, + style.markerScale, + style.backgroundScale, + style.markerColor, + style.clearBackground, + style.fillOpacity, + style.fillColor, + style.strokeWidth, + style.strokeOpacity, + style.strokeColor) + .joinToString("_") { it?.toString() ?: "" } + } + private fun textureForStyle(style: SimpleStyle): MaplyTexture? { + val vc = this.vc.get() ?: return null + + val cacheKey = styleCacheKey(style) + + synchronized(textureCache) { + textureCache[cacheKey]?.let { return it } + } + + val mainImage = loadImageCached(style.markerSymbol) + val backImage = loadImageCached(style.backgroundSymbol) + + val renderSize = style.markerSize ?: return null + + val bitmap = Bitmap.createBitmap(ceil(renderSize.x).toInt(), ceil(renderSize.y).toInt(), Bitmap.Config.ARGB_8888) + bitmap.eraseColor(Color.TRANSPARENT) + + val canvas = Canvas(bitmap) + val paint = Paint(Paint.ANTI_ALIAS_FLAG.or(Paint.FILTER_BITMAP_FLAG)) + + val strokeOpacity = style.strokeOpacity ?: 1f + val strokeWidth = if (backImage == null && strokeOpacity > 0f) (style.strokeWidth ?: defaultMarkerStrokeWidth) else 0f + + if (backImage != null) { + val imageSize = Point2d(backImage.getScaledWidth(canvas).toDouble(), + backImage.getScaledHeight(canvas).toDouble()) + + // Scale the larger dimension down into the image + val scaleX = renderSize.x / imageSize.x + val scaleY = renderSize.y / imageSize.y + val scale = min(scaleX, scaleY) * markerScale * (style.markerScale ?: 1.0) + + // Center the scaled rectangle and apply offset + val scaleAdjustX = renderSize.x / 2 + ((style.backgroundCenter?.x ?: 0.0) - (imageSize.x / 2)) * scale + val scaleAdjustY = renderSize.y / 2 + ((style.backgroundCenter?.y ?: 0.0) - (imageSize.y / 2)) * scale + + canvas.withTranslation(scaleAdjustX.toFloat(), scaleAdjustY.toFloat()) { + canvas.withScale(scale.toFloat(), scale.toFloat()) { + val markerColor = style.markerColor ?: defaultColor + val filterColor = Color.argb(filterAlpha, Color.red(markerColor), Color.green(markerColor), Color.blue(markerColor)) + paint.colorFilter = PorterDuffColorFilter(filterColor, filterMode) + canvas.drawBitmap(backImage, 0f, 0f, paint) + paint.colorFilter = null + } + } + } else { + if (style.clearBackground == false) { + paint.color = style.fillColor ?: defaultColor + paint.style = Paint.Style.FILL + paint.alpha = ((style.fillOpacity ?: 0f) * 255f).toInt() + if (paint.alpha > 0) { + canvas.drawOval(strokeWidth + 1, + strokeWidth + 1, + (renderSize.x - strokeWidth - 1).toFloat(), + (renderSize.y - strokeWidth - 1).toFloat(), paint) + } + } + + if (strokeWidth > 0) { + paint.strokeWidth = strokeWidth + paint.color = style.strokeColor ?: defaultColor + paint.style = Paint.Style.STROKE + canvas.drawOval(0.5f * strokeWidth + 1, + 0.5f * strokeWidth + 1, + renderSize.x.toFloat() - 0.5f * strokeWidth - 1, + renderSize.y.toFloat() - 0.5f * strokeWidth - 1, paint) + } + } + + if (mainImage != null) { + val imageSize = Point2d(mainImage.getScaledWidth(canvas).toDouble(), + mainImage.getScaledHeight(canvas).toDouble()) + + // Scale the larger dimension down into the image + val scaleX = renderSize.x / (imageSize.x + 2f * strokeWidth) + val scaleY = renderSize.y / (imageSize.y + 2f * strokeWidth) + val scale = min(scaleX, scaleY) * markerBackgroundScale * (style.backgroundScale ?: 1.0) + + // Center the scaled rectangle and apply offset + val scaleAdjustX = renderSize.x / 2 + ((style.markerCenter?.x ?: 0.0) - (imageSize.x / 2)) * scale + val scaleAdjustY = renderSize.y / 2 + ((style.markerCenter?.y ?: 0.0) - (imageSize.y / 2)) * scale + + canvas.withSave { + canvas.translate(scaleAdjustX.toFloat(), scaleAdjustY.toFloat()) + canvas.scale(scale.toFloat(), scale.toFloat()) + canvas.translate(strokeWidth, strokeWidth) + canvas.drawBitmap(mainImage, 0f, 0f, paint) + } + } + + val settings = RenderControllerInterface.TextureSettings().apply { + imageFormat = RenderController.ImageFormat.MaplyImageIntRGBA // Bitmap.Config.ARGB_8888 + filterType = RenderControllerInterface.TextureSettings.FilterType.FilterLinear + } + return vc.addTexture(bitmap, settings, threadCurrent).also { + synchronized(textureCache) { + textureCache[cacheKey] = it + } + } + } + + @ColorInt private fun resolveColor(@ColorInt color: Int?, alpha: Float?): Int { + val c = color ?: defaultColor + val a = alpha ?: 1.0f + val r = (Color.red(c) * a).toInt() + val g = (Color.green(c) * a).toInt() + val b = (Color.blue(c) * a).toInt() + return Color.argb((a * 255f).toInt(), r, g, b) + } + + private fun parseBool(s: String?): Boolean? { + if (s == null || s.isEmpty()) return null + return (s == "1" || s.toLowerCase(Locale.ROOT) == "true") + } + + @ColorInt private fun parseColor(s: String?, a: Int = 255): Int? { + if (s == null || s.isEmpty()) return null + try { + val c = (if (s[0] == '#') s.substring(1) else s).toInt(16) + if (s.length < 5) { + // handle short colors + val r = c.and(0xF00) + val g = c.and(0x0F0) + val b = c.and(0x00F) + return r.shl(12) // R__ => R_____ + .or(r.shl(8)) // R__ => _R____ + .or(g.shl(8)) // _G_ => __G___ + .or(g.shl(4)) // _G_ => ___G__ + .or(b.shl(4)) // __B => ____B_ + .or(b) // __B => _____B + .or(a.and(255).shl(24)) // alpha = 1 + } + // regular color, already in the correct order for @ColorInt, add alpha and we're done + return c.or(a.and(255).shl(24)) + } catch (e: java.lang.NumberFormatException) { + return null + } + } + + private val vc = WeakReference(vc) + private val assets = assetManager ?: context.assets + private val textureCache = Hashtable() + private val threadCurrent = ThreadMode.ThreadCurrent + private val logTag = javaClass.name + + private object Shared { + var cacheSize = 4 * 1024 * 1024 + set(value) { + field = value + imageCache.resize(value) + } + val imageCache: LruCache by lazy { LruCache(cacheSize) } + } +} + +typealias ThreadMode = RenderControllerInterface.ThreadMode diff --git a/android/library/maply/src/main/java/com/mousebird/maply/SimpleTileFetcher.java b/android/library/maply/src/main/java/com/mousebird/maply/SimpleTileFetcher.java index ab0a0d4101..fc3f0ed1bd 100644 --- a/android/library/maply/src/main/java/com/mousebird/maply/SimpleTileFetcher.java +++ b/android/library/maply/src/main/java/com/mousebird/maply/SimpleTileFetcher.java @@ -1,9 +1,8 @@ -/* - * SimpleTileFetcher.javae +/* SimpleTileFetcher.java * WhirlyGlobe-MaplyComponent * * Created by Steve Gifford on 6/4/19. - * Copyright 2011-2019 mousebird consulting + * Copyright 2011-2021 mousebird consulting * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -15,7 +14,6 @@ * 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. - * */ package com.mousebird.maply; @@ -23,13 +21,18 @@ import android.os.AsyncTask; import android.os.Handler; import android.os.HandlerThread; +import android.os.Looper; import android.util.Log; +import java.lang.ref.WeakReference; import java.util.ArrayList; import java.util.HashMap; import java.util.LinkedList; import java.util.Queue; import java.util.TreeSet; +import java.util.concurrent.ArrayBlockingQueue; +import java.util.concurrent.ThreadPoolExecutor; +import java.util.concurrent.TimeUnit; /** * Simple Tile Fetcher is meant for sub classing. @@ -45,13 +48,15 @@ public class SimpleTileFetcher extends HandlerThread implements TileFetcher protected boolean valid = false; protected String name; public int minZoom = -1,maxZoom = -1; + WeakReference control = new WeakReference(null); /** * Construct with the name, min and max zoom levels. */ - public SimpleTileFetcher(String inName) + public SimpleTileFetcher(BaseController inControl,String inName) { super(inName); + control = new WeakReference(inControl); name = inName; } @@ -199,6 +204,14 @@ public void run() { }); } + protected void initWithName(String name, int minZoom, int maxZoom) + { + this.name = name; + this.minZoom = minZoom; + this.maxZoom = maxZoom; + this.tileInfo = new SimpleTileInfo(minZoom, maxZoom); + } + boolean scheduled = false; // Schedule the next loading update @@ -206,12 +219,7 @@ protected void scheduleLoading() { if (!scheduled) { Handler handler = new Handler(getLooper()); - handler.post(new Runnable() { - @Override - public void run() { - updateLoading(); - } - }); + handler.post(() -> updateLoading()); } } @@ -219,7 +227,7 @@ public void run() { // The ThreadPoolExecutor gets testy beyond a certain number private static final int MaxParsing = 8; private int numParsing = 0; - protected Queue > tasks = new LinkedList<>(); + protected Queue tasks = new LinkedList<>(); // If set by the subclass, we'll just treat null data as valid // This is helpful when you have sparse data sets protected boolean neverFail = false; @@ -241,16 +249,19 @@ protected void updateLoading() // Load the data tile final byte[] data = dataForTile(tileInfo.fetchInfo,tileInfo.fetchInfo.tileID); + final Looper looper = getLooper(); + // We assume they'll be parsing things which will take time - tasks.add(new AsyncTask() { - protected Void doInBackground(Void... unused) { + tasks.add(new Runnable() { + @Override + public void run() { if (data != null || neverFail) tileInfo.request.callback.success(tileInfo.request, data); else tileInfo.request.callback.failure(tileInfo.request,"Failed to read MBTiles File"); // Add more tasks, if there are any - Handler handler = new Handler(getLooper()); + Handler handler = new Handler(looper); handler.post(new Runnable() { @Override public void run() { @@ -258,8 +269,6 @@ public void run() { updateTasks(); } }); - - return null; } }); @@ -271,9 +280,13 @@ public void run() { // Keep a certain number of tasks running for parsing, but no more protected void updateTasks() { + BaseController theControl = control.get(); + if (theControl == null) + return; + if (numParsing < MaxParsing && !tasks.isEmpty()) { - AsyncTask task = tasks.remove(); - task.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, (Void)null); + Runnable task = tasks.remove(); + theControl.getWorkingThread().addTask(task); numParsing++; } } @@ -340,6 +353,7 @@ public void run() { { valid = false; quitSafely(); + control = null; } /** diff --git a/android/library/maply/src/main/java/com/mousebird/maply/TileFetchRequest.java b/android/library/maply/src/main/java/com/mousebird/maply/TileFetchRequest.java index 8eb43d5582..bdd6113614 100644 --- a/android/library/maply/src/main/java/com/mousebird/maply/TileFetchRequest.java +++ b/android/library/maply/src/main/java/com/mousebird/maply/TileFetchRequest.java @@ -20,13 +20,19 @@ package com.mousebird.maply; +import android.annotation.SuppressLint; + +import org.jetbrains.annotations.NotNull; + +import java.util.Objects; + /** * Generic Tile fetcher request. * * A single request for a single tile of data from a single source. * The tile fetcher will... fetch this and call the success or failure callback. */ -public class TileFetchRequest +public class TileFetchRequest implements Comparable { /** * Priority before importance. Less is more important @@ -64,16 +70,72 @@ public interface Callback { * Called on a random thread and won't be marked as loaded until it returns. * This is a good way to limit how many things are loading/parsing at the same time. */ - public void success(TileFetchRequest fetchRequest,byte[] data); + void success(TileFetchRequest fetchRequest,byte[] data); /** * Tile Fetcher failure callback. */ - public void failure(TileFetchRequest fetchRequest,String errorStr); + void failure(TileFetchRequest fetchRequest,String errorStr); }; /** * Fill in these callbacks to get status back from a tile fetch. */ public Callback callback = null; + + /** + * Convenience helper for calling the success callback + */ + public Boolean success(byte[] data) { + if (callback != null) { + callback.success(this, data); + return true; + } + return false; + } + + /** + * Convenience helper for calling the failure callback + */ + public Boolean failure(String errorStr) { + if (callback != null) { + callback.failure(this, errorStr); + return true; + } + return false; + } + + @Override + public int compareTo(TileFetchRequest other) { + int res = priority - other.priority; + if (res == 0) res = -Float.compare(importance, other.importance); + if (res == 0) res = group - other.group; + if (res == 0) res = (int)(tileSource - other.tileSource); + return res; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (!(o instanceof TileFetchRequest)) return false; + TileFetchRequest that = (TileFetchRequest)o; + return priority == that.priority && + Float.compare(that.importance, importance) == 0 && + group == that.group && + tileSource == that.tileSource; + } + + @Override + public int hashCode() { + return Objects.hash(priority, importance, group, tileSource, fetchInfo); + } + + @NotNull + @SuppressLint("DefaultLocale") + @Override + public String toString() { + return String.format("TileFetchRequest{%d,%f,%d,%d,%s}", + priority, importance, group, tileSource, + (fetchInfo != null) ? fetchInfo.toString() : "null"); + } } diff --git a/android/library/maply/src/main/java/com/mousebird/maply/TileInfoNew.java b/android/library/maply/src/main/java/com/mousebird/maply/TileInfoNew.java index 7c37325ee1..720ea113a2 100644 --- a/android/library/maply/src/main/java/com/mousebird/maply/TileInfoNew.java +++ b/android/library/maply/src/main/java/com/mousebird/maply/TileInfoNew.java @@ -33,6 +33,12 @@ public class TileInfoNew protected TileInfoNew() {} + // Don't use this constructor, use a subclass + protected TileInfoNew(int minZoom,int maxZoom) { + this.minZoom = minZoom; + this.maxZoom = maxZoom; + } + /** * A unique ID used for sorting tile info objects when loading. */ diff --git a/android/library/maply/src/main/java/com/mousebird/maply/VariableTarget.java b/android/library/maply/src/main/java/com/mousebird/maply/VariableTarget.java index 164f79783c..d704365b67 100644 --- a/android/library/maply/src/main/java/com/mousebird/maply/VariableTarget.java +++ b/android/library/maply/src/main/java/com/mousebird/maply/VariableTarget.java @@ -128,6 +128,11 @@ public void addVariableTarget(VariableTarget target) */ public Boolean clearEveryFrame = true; + /** + * When we're clearing, use this value. 0 by default + */ + public double clearVal = 0.0; + /** * Call setup explicitly after setting values. */ @@ -155,6 +160,7 @@ protected void delayedSetup() { renderTex = viewC.createTexture(frameSize[0], frameSize[1],settings, RenderControllerInterface.ThreadMode.ThreadCurrent); renderTarget.texture = renderTex; renderTarget.clearEveryFrame = clearEveryFrame; + renderTarget.clearVal = (float)clearVal; viewC.addRenderTarget(renderTarget); // Default shader @@ -169,6 +175,9 @@ protected void delayedSetup() { rect.setClipCoords(true); rect.addTexture(renderTex); for (VariableTarget target : auxTargets) { + // Bit if a cheat, but it should be fine + if (target.renderTex == null) + target.delayedSetup(); rect.addTexture(target.renderTex); } ArrayList shapes = new ArrayList(); diff --git a/android/library/maply/src/main/java/com/mousebird/maply/VectorObject.java b/android/library/maply/src/main/java/com/mousebird/maply/VectorObject.java index f10cde99d3..1e538b0750 100644 --- a/android/library/maply/src/main/java/com/mousebird/maply/VectorObject.java +++ b/android/library/maply/src/main/java/com/mousebird/maply/VectorObject.java @@ -20,6 +20,12 @@ package com.mousebird.maply; +import androidx.annotation.Nullable; + +import org.jetbrains.annotations.NotNull; + +import java.util.ArrayList; +import java.util.Collection; import java.util.Map; import java.util.Vector; @@ -208,7 +214,9 @@ public VectorIterator iterator() /** Subdivide the edges in this feature to a given tolerance. - This will break up long edges in a vector until they lie flat on a globe to a given epsilon. The epislon is in display coordinates (radius = 1.0). This routine breaks this up along geographic boundaries. + This will break up long edges in a vector until they lie flat on a globe to a given epsilon. + The epsilon is in display coordinates (radius = 1.0). + This routine breaks this up along geographic boundaries. */ public VectorObject subdivideToGlobe(double epsilon) { @@ -366,11 +374,22 @@ public VectorObject arealsToLinears() /** * Load vector objects from a GeoJSON string. - * @param json The GeoSJON string, presumably read from a file or over the network + * @param json The GeoJSON string, presumably read from a file or over the network * @return false if we were unable to parse the GeoJSON */ public native boolean fromGeoJSON(String json); + /** + * Load vector objects from a GeoJSON string. + * @param json The GeoJSON string, presumably read from a file or over the network + * @return null if we were unable to parse the GeoJSON + */ + @Nullable + public static VectorObject createFromGeoJSON(@NotNull String json) { + VectorObject vo = new VectorObject(); + return vo.fromGeoJSON(json) ? vo : null; + } + /** * Load vector objects from a Shapefile. * @param fileName The filename of the Shapefile. @@ -378,6 +397,24 @@ public VectorObject arealsToLinears() */ public native boolean fromShapeFile(String fileName); + /** + * Indicates whether the object contains multiple elements that can be split up + */ + public native boolean canSplit(); + + /** + * Split this feature into individual features. + * + * A vector object can represent multiple features. + * This method will make one vector object per feature, allowing you to operate on those individually. + * + * If the object does not contain multiple features, null is returned to avoid unnecessarily + * copying the shape contents. + */ + public VectorIterator splitVectors() { + return canSplit() ? new VectorIterator(this) : null; + } + /** * Make a complete copy of the vector object and return it. */ diff --git a/android/library/maply/src/main/java/com/mousebird/maply/VectorStyleInterface.java b/android/library/maply/src/main/java/com/mousebird/maply/VectorStyleInterface.java index 8b23098843..d784ca2e50 100644 --- a/android/library/maply/src/main/java/com/mousebird/maply/VectorStyleInterface.java +++ b/android/library/maply/src/main/java/com/mousebird/maply/VectorStyleInterface.java @@ -39,12 +39,12 @@ public interface VectorStyleInterface * @param controller Maply base controller where the * @return A list of vector styles that apply to the vector data with the given attributes. */ - public VectorStyle[] stylesForFeature(AttrDictionary attrs,TileID tileID,String layerName,RenderControllerInterface controller); + VectorStyle[] stylesForFeature(AttrDictionary attrs,TileID tileID,String layerName,RenderControllerInterface controller); /** * Return the full list of styles. */ - public VectorStyle[] allStyles(); + VectorStyle[] allStyles(); /** * Returns true if the given layer (by name) should be displayed at all. If you return @@ -52,7 +52,7 @@ public interface VectorStyleInterface * @param layerName Name of the layer to be displayed (or not) * @param tileID If we're using a tile source, this is the tile ID. If not, it will be null. */ - public boolean layerShouldDisplay(String layerName,TileID tileID); + boolean layerShouldDisplay(String layerName,TileID tileID); /** * Returns a vector style corresponding to the given unique ID. Each vector style has to @@ -61,10 +61,20 @@ public interface VectorStyleInterface * @param controller Base controller used for the geometry. * @return Vector Style to be used in displaying geometry. */ - public VectorStyle styleForUUID(long uuid,RenderControllerInterface controller); + VectorStyle styleForUUID(long uuid,RenderControllerInterface controller); /** * Return a color for the background (clear color). */ - public int backgroundColorForZoom(double zoom); + int backgroundColorForZoom(double zoom); + + /** + * Get the zoom slot, or -1 + */ + int getZoomSlot(); + + /** + * Capture the zoom slot if you're going use it + */ + void setZoomSlot(int inZoomSlot); } diff --git a/android/library/maply/src/main/java/com/mousebird/maply/VectorStyleSettings.java b/android/library/maply/src/main/java/com/mousebird/maply/VectorStyleSettings.java index c50e30cda9..2366795489 100644 --- a/android/library/maply/src/main/java/com/mousebird/maply/VectorStyleSettings.java +++ b/android/library/maply/src/main/java/com/mousebird/maply/VectorStyleSettings.java @@ -10,7 +10,10 @@ public class VectorStyleSettings { public VectorStyleSettings() { - initialise(); + this(1.0); + } + public VectorStyleSettings(double scale) { + initialise(scale); } /// Line widths will be scaled by this amount before display. @@ -39,7 +42,7 @@ public VectorStyleSettings() { /// If set we'll use the zoom levels defined in the style native public boolean getUseZoomLevels(); - native public void setUseZoomLabels(boolean useZoomLabels); + native public void setUseZoomLevels(boolean useZoomLabels); /// For symbols we'll try to pull a UUID out of this field to stick in the marker and label uniqueID native public String getUuidField(); @@ -54,47 +57,49 @@ public VectorStyleSettings() { native public void setDrawPriorityPerLevel(int drawPriorityPerLevel); - /** @brief The overall map scale calculations will be scaled by this amount - * @details We use the map scale calculations to figure out what is displayed and when. Not - * what to load in, mind you, that's a separate, but related calculation. This controls the - * scaling of those calculations. Scale it down to load things in later, up to load them in - * sooner. + /** The overall map scale calculations will be scaled by this amount + * We use the map scale calculations to figure out what is displayed and when. Not what to + * load in, mind you, that's a separate, but related calculation. This controls the scaling + * of those calculations. Scale it down to load things in later, up to load them in sooner. */ native public double getMapScaleScale(); native public void setMapScaleScale(double scale); - /// @brief Dashed lines will be scaled by this amount before display. + /** Dashed lines will be scaled by this amount before display. */ native public double getDashPatternScale(); native public void setDashPatternScale(double scale); - /// @brief Use widened vectors (which do anti-aliasing and such) + /** Use widened vectors (which do anti-aliasing and such) */ native public boolean getUseWideVectors(); native public void setUseWideVectors(boolean useWideVectors); - /// @brief Where we're using old vectors (e.g. not wide) scale them by this amount + /** Where we're using old vectors (e.g. not wide) scale them by this amount */ native public double getOldVecWidthScale(); native public void setOldVecWidthScale(double widthScale); - /// @brief If we're using widened vectors, only active them for strokes wider than this. Defaults to zero. + /** + * If we're using widened vectors, only active them for strokes wider than this. + * Defaults to zero. + */ native public double getWideVecCutoff(); native public void setWideVecCutoff(double cutoff); - /// @brief If set, we'll make the areal features selectable. If not, this saves memory. + /** If set, we'll make the areal features selectable. If not, this saves memory. */ native public boolean getSelectable(); native public void setSelectable(boolean selectable); - /// @brief If set, icons will be loaded from this directory + /** If set, icons will be loaded from this directory */ native public String getIconDirectory(); native public void setIconDirectory(String iconDirectory); - /// @brief The default font family for all text + /** The default font family for all text */ native public String getFontName(); native public void setFontName(String fontName); - /// Use the z buffer when rendering polygons + /** Use the z buffer when rendering polygons */ native public void setZBufferRead(boolean val); - /// Write to the z buffer when rendering polygons + /** Write to the z buffer when rendering polygons */ native public void setZBufferWrite(boolean val); public void finalize() @@ -106,7 +111,7 @@ public void finalize() { nativeInit(); } - native void initialise(); + native void initialise(double scale); native void dispose(); private static native void nativeInit(); protected long nativeHandle; diff --git a/android/library/maply/src/main/java/com/mousebird/maply/VectorStyleSimpleGenerator.java b/android/library/maply/src/main/java/com/mousebird/maply/VectorStyleSimpleGenerator.java index e56b20b58c..b73f2954f0 100644 --- a/android/library/maply/src/main/java/com/mousebird/maply/VectorStyleSimpleGenerator.java +++ b/android/library/maply/src/main/java/com/mousebird/maply/VectorStyleSimpleGenerator.java @@ -90,6 +90,7 @@ public void buildObjects(VectorObject vecObjs[], VectorTileData tileData, Render ScreenLabel label = new ScreenLabel(); label.text = name != null ? name : "."; label.loc = pt; + label.layoutImportance = 1.f; labels.add(label); } @@ -239,4 +240,11 @@ public VectorStyle[] allStyles() public int backgroundColorForZoom(double zoom) { return Color.BLACK; } + + @Override + public int getZoomSlot() { return -1; } + + @Override + public void setZoomSlot(int inZoomSlot) { + } } diff --git a/android/library/maply/src/main/java/com/mousebird/maply/VectorTileLineStyle.java b/android/library/maply/src/main/java/com/mousebird/maply/VectorTileLineStyle.java index e7a63cfa42..89408e1f05 100644 --- a/android/library/maply/src/main/java/com/mousebird/maply/VectorTileLineStyle.java +++ b/android/library/maply/src/main/java/com/mousebird/maply/VectorTileLineStyle.java @@ -1,9 +1,8 @@ -/* - * MaplyVectorTileLineStyle.java +/* MaplyVectorTileLineStyle.java * WhirlyGlobeLib * * Created by Ranen Ghosh on 3/27/17. - * Copyright 2011-2017 mousebird consulting + * Copyright 2011-2021 mousebird consulting * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -15,7 +14,6 @@ * 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. - * */ package com.mousebird.maply; @@ -55,10 +53,12 @@ public void buildObjects(VectorObject objects[], VectorTileData tileData, Render ArrayList vecObjs = new ArrayList(Arrays.asList(objects)); ComponentObject compObj = null; - if (useWideVectors) + if (useWideVectors) { compObj = controller.addWideVectors(vecObjs, wideVectorInfo, RenderController.ThreadMode.ThreadCurrent); - else + } + else { compObj = controller.addVectors(vecObjs, vectorInfo, RenderController.ThreadMode.ThreadCurrent); + } tileData.addComponentObject(compObj); } diff --git a/android/library/maply/src/main/java/com/mousebird/maply/sld/sldstyleset/SLDStyleSet.java b/android/library/maply/src/main/java/com/mousebird/maply/sld/sldstyleset/SLDStyleSet.java index 03c179037a..b8066c9d1e 100644 --- a/android/library/maply/src/main/java/com/mousebird/maply/sld/sldstyleset/SLDStyleSet.java +++ b/android/library/maply/src/main/java/com/mousebird/maply/sld/sldstyleset/SLDStyleSet.java @@ -190,4 +190,12 @@ public VectorStyle[] allStyles() @Override public int backgroundColorForZoom(double zoom) { return Color.BLACK; } + + @Override + public int getZoomSlot() { return -1; } + + @Override + public void setZoomSlot(int inZoomSlot) { + throw new UnsupportedOperationException(); + } } diff --git a/common/WhirlyGlobeLib/include/BaseInfo.h b/common/WhirlyGlobeLib/include/BaseInfo.h index 95f0236d3a..8330fd89f2 100644 --- a/common/WhirlyGlobeLib/include/BaseInfo.h +++ b/common/WhirlyGlobeLib/include/BaseInfo.h @@ -58,12 +58,15 @@ class ExpressionInfo : public Identifiable class FloatExpressionInfo: public ExpressionInfo { public: - FloatExpressionInfo(); - FloatExpressionInfo(const FloatExpressionInfo &that); + FloatExpressionInfo() = default; + FloatExpressionInfo(const FloatExpressionInfo &that) = default; // Scale the outputs by the given value void scaleBy(double scale); - + + // Evaluate the expression at the given zoom level + float evaluate(float zoom, float defaultValue); + std::vector stopOutputs; }; typedef std::shared_ptr FloatExpressionInfoRef; @@ -72,9 +75,15 @@ typedef std::shared_ptr FloatExpressionInfoRef; class ColorExpressionInfo: public ExpressionInfo { public: - ColorExpressionInfo(); - ColorExpressionInfo(const ColorExpressionInfo &that); - + ColorExpressionInfo() = default; + ColorExpressionInfo(const ColorExpressionInfo &that) = default; + + // Evaluate the expression at the given zoom level + RGBAColor evaluate(float zoom, RGBAColor defaultValue); + + // Evaluate directly to floats, with no truncation to integer components + Eigen::Vector4f evaluateF(float zoom, RGBAColor defaultValue); + std::vector stopOutputs; }; typedef std::shared_ptr ColorExpressionInfoRef; @@ -95,11 +104,11 @@ class BaseInfo /// Set the various parameters on a basic drawable void setupBasicDrawable(BasicDrawableBuilder *drawBuild) const; - void setupBasicDrawable(BasicDrawableBuilderRef drawBuild) const; + void setupBasicDrawable(const BasicDrawableBuilderRef &drawBuild) const; /// Set the various parameters on a basic drawable instance void setupBasicDrawableInstance(BasicDrawableInstanceBuilder *drawBuild) const; - void setupBasicDrawableInstance(BasicDrawableInstanceBuilderRef drawBuild) const; + void setupBasicDrawableInstance(const BasicDrawableInstanceBuilderRef &drawBuild) const; double minVis,maxVis; double minVisBand,maxVisBand; diff --git a/common/WhirlyGlobeLib/include/BasicDrawable.h b/common/WhirlyGlobeLib/include/BasicDrawable.h index 25a74081e0..8b916b5c20 100644 --- a/common/WhirlyGlobeLib/include/BasicDrawable.h +++ b/common/WhirlyGlobeLib/include/BasicDrawable.h @@ -1,9 +1,8 @@ -/* - * BasicDrawable.h +/* BasicDrawable.h * WhirlyGlobeLib * * Created by Steve Gifford on 2/1/11. - * Copyright 2011-2019 mousebird consulting + * Copyright 2011-2021 mousebird consulting * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -15,13 +14,13 @@ * 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. - * */ #import #import #import #import "Identifiable.h" +#import "BaseInfo.h" #import "WhirlyVector.h" #import "GlobeView.h" #import "Drawable.h" @@ -170,7 +169,25 @@ friend class BasicDrawableBuilderMTL; /// Change all the uniforms applied at once virtual void setUniforms(const SingleVertexAttributeSet &newUniforms); - + + /// Set a float uniform on a BasicDrawable + void setUniform(SimpleIdentity nameID, float val); + + /// Set an int uniform on a BasicDrawable + void setUniform(SimpleIdentity nameID, int val); + + /// Set a float uniform on a BasicDrawable + void setUniform(SimpleIdentity nameID, const Eigen::Vector2f &vec); + + /// Set a float uniform on a BasicDrawable + void setUniform(SimpleIdentity nameID, const Eigen::Vector3f &vec); + + /// Set a float uniform on a BasicDrawable + void setUniform(SimpleIdentity nameID, const Eigen::Vector4f &vec); + + /// Apply a generic single attribute + void setUniform(const SingleVertexAttribute &attr); + // Block of data to be passed into a given buffer ID // We do this in Metal rather than setting individual uniforms (like OpenGL) class UniformBlock @@ -184,7 +201,7 @@ friend class BasicDrawableBuilderMTL; virtual void setUniBlock(const UniformBlock &uniBlock); /// Add a tweaker to be run before each frame - virtual void addTweaker(DrawableTweakerRef tweak); + virtual void addTweaker(const DrawableTweakerRef &tweak); /// Update anything associated with the renderer. Probably renderUntil. virtual void updateRenderer(SceneRenderer *renderer); @@ -256,16 +273,27 @@ friend class BasicDrawableBuilderMTL; bool texturesChanged; }; +struct BasicDrawableTweaker : DrawableTweaker +{ + RGBAColor color = RGBAColor::white(); + ColorExpressionInfoRef colorExp; + FloatExpressionInfoRef opacityExp; + +protected: + virtual float getZoom(const Drawable &inDraw,const Scene &scene,float def) const override; +}; + /** Drawable Tweaker that cycles through textures. Looks at the current time and decides which two textures to use. */ -class BasicDrawableTexTweaker : public DrawableTweaker +struct BasicDrawableTexTweaker : public BasicDrawableTweaker { -public: + BasicDrawableTexTweaker(std::vector &&texIDs,TimeInterval startTime,double period); BasicDrawableTexTweaker(const std::vector &texIDs,TimeInterval startTime,double period); /// Modify the active texture IDs - void tweakForFrame(Drawable *draw,RendererFrameInfo *frame); + virtual void tweakForFrame(Drawable *draw,RendererFrameInfo *frame) override; + protected: std::vector texIDs; TimeInterval startTime; @@ -275,15 +303,15 @@ class BasicDrawableTexTweaker : public DrawableTweaker /** Calculates important values for the screen space texture application and sets the results in the shader. */ -class BasicDrawableScreenTexTweaker : public DrawableTweaker +struct BasicDrawableScreenTexTweaker : public BasicDrawableTweaker { -public: EIGEN_MAKE_ALIGNED_OPERATOR_NEW; BasicDrawableScreenTexTweaker(const Point3d ¢erPt,const Point2d &texScale); /// Modify the active shader - void tweakForFrame(Drawable *draw,RendererFrameInfo *frame); + virtual void tweakForFrame(Drawable *draw,RendererFrameInfo *frame) override; + protected: Point3d centerPt; Point2d texScale; diff --git a/common/WhirlyGlobeLib/include/BasicDrawableBuilder.h b/common/WhirlyGlobeLib/include/BasicDrawableBuilder.h index af7572656b..e7ef906e88 100644 --- a/common/WhirlyGlobeLib/include/BasicDrawableBuilder.h +++ b/common/WhirlyGlobeLib/include/BasicDrawableBuilder.h @@ -1,9 +1,8 @@ -/* - * BasicDrawableBuilder.h +/* BasicDrawableBuilder.h * WhirlyGlobeLib * * Created by Steve Gifford on 5/9/19. - * Copyright 2011-2019 mousebird consulting + * Copyright 2011-2021 mousebird consulting * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -15,7 +14,6 @@ * 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. - * */ #import @@ -30,7 +28,7 @@ namespace WhirlyKit { - + /** Used to construct a BasicDrawable. This is abstracted away from the BasicDrawable itself so we can build drawables for the different renderers. @@ -39,12 +37,12 @@ class BasicDrawableBuilder { public: /// Construct empty - BasicDrawableBuilder(const std::string &name,Scene *scene); - virtual ~BasicDrawableBuilder(); + BasicDrawableBuilder(std::string name,Scene *scene); + virtual ~BasicDrawableBuilder() = default; - /// Reserve the given amount of space (cuts down on reallocs) + /// Reserve the given amount of space (cuts down on allocations) void reserve(int numPoints,int numTris); - + /// True to turn it on, false to turn it off void setOnOff(bool onOff); @@ -99,13 +97,13 @@ class BasicDrawableBuilder /// Set the line width (if using lines) virtual void setLineWidth(float inWidth); - virtual float getLineWidth(); - + virtual float getLineWidth() const; + /// Set the texture ID for a specific slot. You get this from the Texture object. virtual void setTexId(unsigned int which,SimpleIdentity inId); /// Return the ID for the texture, if it's there - virtual SimpleIdentity getTexId(unsigned int which); + virtual SimpleIdentity getTexId(unsigned int which) const; /// Set all the textures at once virtual void setTexIDs(const std::vector &texIDs); @@ -114,12 +112,11 @@ class BasicDrawableBuilder /// We use these to look up parts of a texture at a higher level virtual void setTexRelative(int which,int size,int borderTexel,int relLevel,int relX,int relY); - /// For OpenGLES2, you can set the program to use in rendering void setProgram(SimpleIdentity progId); /// Add a tweaker to this list to be run each frame - void addTweaker(DrawableTweakerRef tweakRef); + void addTweaker(const DrawableTweakerRef &tweakRef); /// Set the geometry type. Probably triangles. virtual void setType(GeometryType inType); @@ -128,16 +125,16 @@ class BasicDrawableBuilder virtual void setColor(RGBAColor inColor); /// Set the color as an array. - virtual void setColor(unsigned char inColor[]); + virtual void setColor(const unsigned char inColor[]); // Set if we're requiring the expression block for the shaders void setIncludeExp(bool newVal); // Apply a dynamic color expression - void setColorExpression(ColorExpressionInfoRef colorExp); + void setColorExpression(const ColorExpressionInfoRef &colorExp); // Apply a dynamic opacity expression - void setOpacityExpression(FloatExpressionInfoRef opacityExp); + void setOpacityExpression(const FloatExpressionInfoRef &opacityExp); /// Number of extra frames to draw after we'd normally stop virtual void setExtraFrames(int numFrames); @@ -169,14 +166,14 @@ class BasicDrawableBuilder virtual unsigned int addPoint(const Point3d &pt); /// Number of points added so far - virtual unsigned int getNumPoints(); - - /// Numer of triangles added so far - virtual unsigned int getNumTris(); + virtual unsigned int getNumPoints() const; + /// Number of triangles added so far + virtual unsigned int getNumTris() const; + /// Return a given point - virtual Point3d getPoint(int which); - + virtual Point3d getPoint(int which) const; + /// Add a texture coordinate. -1 means we add the same /// texture coordinate to all the available texture coordinate sets virtual void addTexCoord(int which,TexCoord coord); @@ -189,7 +186,10 @@ class BasicDrawableBuilder virtual void addNormal(const Point3d &norm); /// Decide if the given list of vertex attributes is the same as the one we have - bool compareVertexAttributes(const SingleVertexAttributeSet &attrs); + bool compareVertexAttributes(const SingleVertexAttributeSet &attrs) const; + + /// Set up the required vertex attribute + void setVertexAttribute(const SingleVertexAttributeInfo &attr); /// Set up the required vertex attribute arrays from the given list void setVertexAttributes(const SingleVertexAttributeInfoSet &attrs); @@ -212,14 +212,25 @@ class BasicDrawableBuilder /// Add a float to the given attribute array virtual void addAttributeValue(int attrId,float val); + /// Add an integer value to the given attribute array + virtual void addAttributeValue(int attrId,int val); + + /// Add an identity-type value to the given attribute array + virtual void addAttributeValue(int attrId,int64_t val); + + /// Find the index of a given attribute + virtual int findAttribute(int nameID); + /// Add a triangle. Should point to the vertex IDs. virtual void addTriangle(BasicDrawable::Triangle tri); + /// TODO: We need a per-triangle attribute instead of stuffing data always into the vertex attributes + /// Set the uniforms applied to the Program before rendering virtual void setUniforms(const SingleVertexAttributeSet &uniforms); /// Run the texture and texture coordinates based on a SubTexture - virtual void applySubTexture(int which,SubTexture subTex,int startingAt=0); + virtual void applySubTexture(int which,const SubTexture &subTex,int startingAt=0); /// Constructs the remaining pieces of the drawable and returns it /// Caller is responsible for deletion @@ -227,34 +238,47 @@ class BasicDrawableBuilder /// Return just the ID of the drawable being created. /// This avoids flushing things out - virtual SimpleIdentity getDrawableID(); + virtual SimpleIdentity getDrawableID() const; /// Return just the draw priority of the drawable being created - virtual int getDrawablePriority(); + virtual int getDrawablePriority() const; /// Check for the given texture coordinate entry and add it if it's not there virtual void setupTexCoordEntry(int which,int numReserve); -public: - Scene *scene; - std::string name; - - // This version is only used by subclasses - BasicDrawableBuilder(); - void setName(const std::string &name); + /// We need slightly different tweakers for the rendering variants + virtual DrawableTweakerRef makeTweaker() const { return {}; } + + /// Create and attach a tweaker, if necessary + virtual void setupTweaker(BasicDrawable &theDraw) const; + + /// Set up a tweaker created by a derived class + virtual void setupTweaker(const DrawableTweakerRef &tweaker) const; + + // Unprocessed data arrays + std::vector points; + std::vector tris; + + // The basic drawable we're building up + BasicDrawableRef basicDraw; + // Used by subclasses to do the standard init virtual void Init(); // Set up the standard vertex attributes we use virtual void setupStandardAttributes(int numReserve=0); + + RGBAColor color = RGBAColor::white(); +protected: + Scene *scene; + std::string name; - // The basic drawable we're building up - BasicDrawableRef basicDraw; + // This version is only used by subclasses + BasicDrawableBuilder(); - // Unprocessed data arrays - std::vector points; - std::vector tris; + void setName(std::string name); + + bool includeExp = false; - bool includeExp; ColorExpressionInfoRef colorExp; FloatExpressionInfoRef opacityExp; }; diff --git a/common/WhirlyGlobeLib/include/BasicDrawableBuilderGLES.h b/common/WhirlyGlobeLib/include/BasicDrawableBuilderGLES.h index 5c370c7c09..4c84b92412 100644 --- a/common/WhirlyGlobeLib/include/BasicDrawableBuilderGLES.h +++ b/common/WhirlyGlobeLib/include/BasicDrawableBuilderGLES.h @@ -1,9 +1,8 @@ -/* - * BasicDrawableBuilderGLES.h +/* BasicDrawableBuilderGLES.h * WhirlyGlobeLib * * Created by Steve Gifford on 5/10/19. - * Copyright 2011-2019 mousebird consulting + * Copyright 2011-2021 mousebird consulting * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -15,7 +14,6 @@ * 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. - * */ #import "BasicDrawableBuilder.h" @@ -23,7 +21,13 @@ namespace WhirlyKit { - + +/// GLES version modifies uniforms +struct BasicDrawableTweakerGLES : public BasicDrawableTweaker +{ + virtual void tweakForFrame(Drawable *inDraw,RendererFrameInfo *frameInfo) override; +}; + /** OpenGL version of BasicDrawable Builder. */ class BasicDrawableBuilderGLES : virtual public BasicDrawableBuilder @@ -35,12 +39,15 @@ class BasicDrawableBuilderGLES : virtual public BasicDrawableBuilder /// Add a new vertex related attribute. Need a data type and the name the shader refers to /// it by. The index returned is how you will access it. - virtual int addAttribute(BDAttributeDataType dataType,StringIdentity nameID,int slot = -1,int numThings = -1); + virtual int addAttribute(BDAttributeDataType dataType,StringIdentity nameID,int slot = -1,int numThings = -1) override; /// Fill out and return the drawable - virtual BasicDrawableRef getDrawable(); - + virtual BasicDrawableRef getDrawable() override; + protected: + virtual DrawableTweakerRef makeTweaker() const override; + virtual void setupTweaker(const DrawableTweakerRef &inTweaker) const override; + bool drawableGotten; }; diff --git a/common/WhirlyGlobeLib/include/BasicDrawableInstance.h b/common/WhirlyGlobeLib/include/BasicDrawableInstance.h index 2e5be566bb..9fb4599e72 100644 --- a/common/WhirlyGlobeLib/include/BasicDrawableInstance.h +++ b/common/WhirlyGlobeLib/include/BasicDrawableInstance.h @@ -109,7 +109,7 @@ friend class BasicDrawableInstanceBuilder; void setDrawPriority(int newPriority); /// Set the line width - void setLineWidth(int newLineWidth); + void setLineWidth(float newLineWidth); // Time we start counting from for motion void setStartTime(TimeInterval inStartTime); @@ -171,6 +171,9 @@ friend class BasicDrawableInstanceBuilder; /// We use these to look up parts of a texture at a higher level virtual void setTexRelative(int which,int size,int borderTexel,int relLevel,int relX,int relY); + /// For Metal, we can set instance data in one big chunk + virtual void setInstanceData(int numInstances,RawDataRef data); + protected: /// Update rendering for this drawable virtual void setValuesChanged(); @@ -211,6 +214,10 @@ friend class BasicDrawableInstanceBuilder; // If set, we'll instance this one multiple times std::vector instances; + + // For Metal, we may do instances this way instead + RawDataRef instData; + // Or we might get the number of instances from a texture (possibly a reduce) SimpleIdentity instanceTexSource; SimpleIdentity instanceTexProg; diff --git a/common/WhirlyGlobeLib/include/BasicDrawableInstanceBuilder.h b/common/WhirlyGlobeLib/include/BasicDrawableInstanceBuilder.h index 80d5164b68..a41f7c5fe4 100644 --- a/common/WhirlyGlobeLib/include/BasicDrawableInstanceBuilder.h +++ b/common/WhirlyGlobeLib/include/BasicDrawableInstanceBuilder.h @@ -1,9 +1,8 @@ -/* - * BasicDrawableInstanceBuilder.h +/* BasicDrawableInstanceBuilder.h * WhirlyGlobeLib * * Created by Steve Gifford on 5/9/19. - * Copyright 2011-2019 mousebird consulting + * Copyright 2011-2021 mousebird consulting * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -15,7 +14,6 @@ * 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. - * */ #import @@ -40,8 +38,8 @@ class BasicDrawableInstanceBuilder EIGEN_MAKE_ALIGNED_OPERATOR_NEW; /// Construct empty - BasicDrawableInstanceBuilder(const std::string &name,Scene *scene); - virtual ~BasicDrawableInstanceBuilder(); + BasicDrawableInstanceBuilder(std::string name,Scene *scene); + virtual ~BasicDrawableInstanceBuilder() = default; /// Set the base draw ID and type void setMasterID(SimpleIdentity baseDrawID,BasicDrawableInstance::Style style); @@ -53,6 +51,7 @@ class BasicDrawableInstanceBuilder void setEnableTimeRange(TimeInterval inStartEnable,TimeInterval inEndEnable); /// Set the fade in and out + // Not supported for instances void setFade(TimeInterval inFadeDown,TimeInterval inFadeUp); /// Set the viewer based visibility @@ -88,7 +87,7 @@ class BasicDrawableInstanceBuilder void setRenderTarget(SimpleIdentity newRenderTarget); /// Add a tweaker to this list to be run each frame - void addTweaker(DrawableTweakerRef tweakRef); + void addTweaker(const DrawableTweakerRef &tweakRef); /// Set this when we're representing moving geometry model instances void setIsMoving(bool inMoving); @@ -114,31 +113,35 @@ class BasicDrawableInstanceBuilder /// Set all the textures at once virtual void setTexIDs(const std::vector &texIDs); - + /// Set the relative offsets for texture usage. /// We use these to look up parts of a texture at a higher level virtual void setTexRelative(int which,int size,int borderTexel,int relLevel,int relX,int relY); - + /// Check for the given texture coordinate entry and add it if it's not there virtual void setupTexCoordEntry(int which,int numReserve); /// Set the shader program void setProgram(SimpleIdentity progID); + /// For Metal, we can set instance data in one big chunk + virtual void setInstanceData(int numInstance,RawDataRef data); + /// Constructs the remaining pieces of the drawable and returns it /// Caller is responsible for deletion virtual BasicDrawableInstanceRef getDrawable() = 0; /// Return just the ID of the drawable being created /// This doesn't flush out the drawable in any way - virtual SimpleIdentity getDrawableID(); + virtual SimpleIdentity getDrawableID() const; -protected: +public: // Called by subclasses void Init(); Scene *scene; BasicDrawableInstanceRef drawInst; + std::string name; }; typedef std::shared_ptr BasicDrawableInstanceBuilderRef; diff --git a/common/WhirlyGlobeLib/include/BasicDrawableInstanceBuilderGLES.h b/common/WhirlyGlobeLib/include/BasicDrawableInstanceBuilderGLES.h index bc9d3f494e..be26b15aa2 100644 --- a/common/WhirlyGlobeLib/include/BasicDrawableInstanceBuilderGLES.h +++ b/common/WhirlyGlobeLib/include/BasicDrawableInstanceBuilderGLES.h @@ -1,9 +1,8 @@ -/* - * BasicDrawableInstanceBuilderGLES.h +/* BasicDrawableInstanceBuilderGLES.h * WhirlyGlobeLib * * Created by Steve Gifford on 5/10/19. - * Copyright 2011-2019 mousebird consulting + * Copyright 2011-2021 mousebird consulting * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -15,7 +14,6 @@ * 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. - * */ #import "BasicDrawableInstanceBuilder.h" @@ -29,11 +27,11 @@ namespace WhirlyKit class BasicDrawableInstanceBuilderGLES : public BasicDrawableInstanceBuilder { public: - BasicDrawableInstanceBuilderGLES(const std::string &name,Scene *scene); + BasicDrawableInstanceBuilderGLES(std::string name,Scene *scene); ~BasicDrawableInstanceBuilderGLES(); /// Fill out and return the drawable - virtual BasicDrawableInstanceRef getDrawable(); + virtual BasicDrawableInstanceRef getDrawable() override; protected: bool drawableGotten; diff --git a/common/WhirlyGlobeLib/include/BillboardDrawableBuilderGLES.h b/common/WhirlyGlobeLib/include/BillboardDrawableBuilderGLES.h index a7375018a6..2b8c0c6c0f 100644 --- a/common/WhirlyGlobeLib/include/BillboardDrawableBuilderGLES.h +++ b/common/WhirlyGlobeLib/include/BillboardDrawableBuilderGLES.h @@ -38,9 +38,9 @@ class BillboardDrawableBuilderGLES : public BasicDrawableBuilderGLES, public Bil public: BillboardDrawableBuilderGLES(const std::string &name,Scene *scene); - virtual int addAttribute(BDAttributeDataType dataType,StringIdentity nameID,int slot = -1,int numThings = -1); + virtual int addAttribute(BDAttributeDataType dataType,StringIdentity nameID,int slot = -1,int numThings = -1) override; - virtual BasicDrawableRef getDrawable(); + virtual BasicDrawableRef getDrawable() override; }; } diff --git a/common/WhirlyGlobeLib/include/BillboardManager.h b/common/WhirlyGlobeLib/include/BillboardManager.h index 79ec609ead..f0c398f681 100644 --- a/common/WhirlyGlobeLib/include/BillboardManager.h +++ b/common/WhirlyGlobeLib/include/BillboardManager.h @@ -85,7 +85,7 @@ class BillboardInfo : public BaseInfo public: BillboardInfo(); BillboardInfo(const Dictionary &); - ~BillboardInfo(){}; + virtual ~BillboardInfo() = default; typedef enum {Eye=0,Ground} Orient; Orient orient; @@ -102,7 +102,7 @@ class BillboardSceneRep : public Identifiable ~BillboardSceneRep(); // Clear the contents out of the scene - void clearContents(SelectionManager *selectManager,ChangeSet &changes,TimeInterval when); + void clearContents(SelectionManagerRef &selectManager,ChangeSet &changes,TimeInterval when); SimpleIDSet drawIDs; // Drawables created for this SimpleIDSet selectIDs; // IDs used for selection @@ -156,8 +156,8 @@ class BillboardManager : public SceneManager void removeBillboards(SimpleIDSet &billIDs,ChangeSet &changes); protected: - std::mutex billLock; BillboardSceneRepSet sceneReps; }; +typedef std::shared_ptr BillboardManagerRef; } diff --git a/common/WhirlyGlobeLib/include/ComponentManager.h b/common/WhirlyGlobeLib/include/ComponentManager.h index 36bee4ce6e..9020e4df6f 100644 --- a/common/WhirlyGlobeLib/include/ComponentManager.h +++ b/common/WhirlyGlobeLib/include/ComponentManager.h @@ -3,7 +3,7 @@ * WhirlyGlobeLib * * Created by Steve Gifford on 2/15/19. - * Copyright 2011-2019 mousebird consulting + * Copyright 2011-2021 mousebird consulting * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -63,6 +63,13 @@ class ComponentObject : public Identifiable std::vector vecObjs; Point2d vectorOffset; + + std::string uuid; + std::string representation; + + // If the object uses masks, these are the masks in use + SimpleIDSet maskIDs; + bool isSelectable; bool enable; bool underConstruction; @@ -72,7 +79,8 @@ class ComponentObject : public Identifiable public: // Don't call this - ComponentObject(); + ComponentObject(bool enable = false, bool selectable = false); + ComponentObject(bool enable, bool selectable, const Dictionary &desc); }; typedef std::shared_ptr ComponentObjectRef; @@ -97,7 +105,7 @@ class ComponentManager : public SceneManager /// Hand a component object over to be managed. /// Return an ID to refer to it in the future - virtual void addComponentObject(ComponentObjectRef compObj); + virtual void addComponentObject(const ComponentObjectRef &compObj, ChangeSet &changes); /// Check if the component object exists virtual bool hasComponentObject(SimpleIdentity compID); @@ -112,41 +120,89 @@ class ComponentManager : public SceneManager virtual void removeComponentObjects(PlatformThreadInfo *threadInfo,const std::vector &compObjs,ChangeSet &changes); /// Enable/disable the contents of a Component Object - virtual void enableComponentObject(SimpleIdentity compID,bool enable,ChangeSet &changes); - + virtual void enableComponentObject(SimpleIdentity compID,bool enable,ChangeSet &changes, bool resolveReps = false); + + /// Enable/disable the contents of a Component Object + virtual void enableComponentObject(const ComponentObjectRef &compID, bool enable, ChangeSet &changes, bool resolveReps = false); + + /// Enable/disable the contents of a collection of Component Objects + virtual void enableComponentObjects(const std::vector &compIDs, bool enable, ChangeSet &changes, bool resolveReps = false); + /// Enable/disable a whole group of Component Objects - virtual void enableComponentObjects(const SimpleIDSet &compIDs,bool enable,ChangeSet &changes); - + virtual void enableComponentObjects(const SimpleIDSet &compIDs,bool enable,ChangeSet &changes, bool resolveReps = false); + + virtual void setRepresentation(const std::string &repName, const std::string &fallback, + const std::vector &uuids, ChangeSet &changes); + + virtual void setRepresentation(const std::string &repName, const std::string &fallback, + const std::set &uuids, ChangeSet &changes); + + virtual void setRepresentation(const std::string &repName, const std::string &fallback, + const std::unordered_set &uuids, ChangeSet &changes); + /// Set a uniform block on the geometry for the given component objects virtual void setUniformBlock(const SimpleIDSet &compIDs,const RawDataRef &uniBlock,int bufferID,ChangeSet &changes); + /// Pass in a mask name, get an ID to render into the mask target + virtual SimpleIdentity retainMaskByName(const std::string &maskName); + + /// We're done with the given mask target + virtual void releaseMaskIDs(const SimpleIDSet &maskIDs); + /// Find all the vectors that fall within or near the given point std::vector > findVectors(const Point2d &pt,double maxDist,ViewStateRef viewState,const Point2f &frameSize,bool muti); // These are here for convenience - LayoutManager *layoutManager; - MarkerManager *markerManager; - LabelManager *labelManager; - VectorManager *vectorManager; - WideVectorManager *wideVectorManager; - ShapeManager *shapeManager; - SphericalChunkManager *chunkManager; - LoftManager *loftManager; - BillboardManager *billManager; - GeometryManager *geomManager; - FontTextureManager *fontTexManager; - ParticleSystemManager *partSysManager; - + LayoutManagerRef layoutManager; + MarkerManagerRef markerManager; + LabelManagerRef labelManager; + VectorManagerRef vectorManager; + WideVectorManagerRef wideVectorManager; + ShapeManagerRef shapeManager; + SphericalChunkManagerRef chunkManager; + LoftManagerRef loftManager; + BillboardManagerRef billManager; + GeometryManagerRef geomManager; + FontTextureManagerRef fontTexManager; + ParticleSystemManagerRef partSysManager; + protected: // Subclass fills this in - virtual ComponentObjectRef makeComponentObject() = 0; - - std::mutex lock; + virtual ComponentObjectRef makeComponentObject(const Dictionary *desc = nullptr) = 0; + + void removeComponentObjects_NoLock(PlatformThreadInfo *threadInfo, + const SimpleIDSet &compIDs, + std::vector &objs); + + template + void setRepresentation(const std::string &repName, + const std::string &fallback, + TIter beg, TIter end, + ChangeSet &changes); + + ComponentObjectMap compObjsById; + + std::unordered_multimap compObjsByUUID; - ComponentObjectMap compObjs; + std::unordered_map representations; + + // Single entry for a mask ID + class MaskEntry { + public: + std::string name; + SimpleIdentity maskID; + unsigned long long refCount; + }; + typedef std::shared_ptr MaskEntryRef; + std::unordered_map maskEntriesByName; + std::unordered_map maskEntriesByID; + // We have 32 bits of range in the mask ID on iOS + unsigned int lastMaskID; + std::mutex maskLock; }; +typedef std::shared_ptr ComponentManagerRef; // Make an OS specific component manager -extern ComponentManager *MakeComponentManager(); +extern ComponentManagerRef MakeComponentManager(); } diff --git a/common/WhirlyGlobeLib/include/Dictionary.h b/common/WhirlyGlobeLib/include/Dictionary.h index ee0350a8c5..06daa47bc8 100644 --- a/common/WhirlyGlobeLib/include/Dictionary.h +++ b/common/WhirlyGlobeLib/include/Dictionary.h @@ -45,6 +45,9 @@ class Dictionary Dictionary(); virtual ~Dictionary() { }; + virtual int count() const = 0; + virtual bool empty() const = 0; + /// Returns true if the field exists virtual bool hasField(const std::string &name) const = 0; @@ -125,6 +128,8 @@ class MutableDictionary : public Dictionary /// Set field as int virtual void setInt(const std::string &name,int val) = 0; + /// Set field as int64 + virtual void setInt64(const std::string &name,int64_t val) = 0; /// Set field as 64 bit unique value virtual void setIdentifiable(const std::string &name,SimpleIdentity val) = 0; /// Set field as double diff --git a/common/WhirlyGlobeLib/include/DictionaryC.h b/common/WhirlyGlobeLib/include/DictionaryC.h index 421e46de9d..5b04e3e392 100644 --- a/common/WhirlyGlobeLib/include/DictionaryC.h +++ b/common/WhirlyGlobeLib/include/DictionaryC.h @@ -47,12 +47,12 @@ class MutableDictionaryC : public MutableDictionary // Copy constructor MutableDictionaryC(const MutableDictionaryC &that); // Move constructor - MutableDictionaryC(MutableDictionaryC &&that); + MutableDictionaryC(MutableDictionaryC &&that) noexcept; // Assignment operator MutableDictionaryC &operator = (const MutableDictionaryC &that); // Move assignment operator - MutableDictionaryC &operator = (MutableDictionaryC &&that); - virtual ~MutableDictionaryC(); + MutableDictionaryC &operator = (MutableDictionaryC &&that) noexcept; + virtual ~MutableDictionaryC() = default; virtual MutableDictionaryRef copy() const override { return std::make_shared(*this); } @@ -61,6 +61,9 @@ class MutableDictionaryC : public MutableDictionary // bool parseJSONNode(JSONNode &node); // ValueRef parseJSONValue(JSONNode::iterator &nodeIt); + virtual int count() const override { return numFields(); } + virtual bool empty() const override { return numFields() == 0; } + /// Clean out the contents void clear() override; @@ -80,23 +83,23 @@ class MutableDictionaryC : public MutableDictionary void removeField(unsigned int key); /// Return an int, using the default if it's missing - virtual int getInt(const std::string &name,int defVal=0.0) const override; - virtual int getInt(unsigned int key,int defVal=0.0) const; + virtual int getInt(const std::string &name,int defVal) const override; + virtual int getInt(unsigned int key,int defVal) const; /// Return a 64 bit unique identity or 0 if missing virtual SimpleIdentity getIdentity(const std::string &name) const override; virtual SimpleIdentity getIdentity(unsigned int key) const; /// Return a 64 bit value or 0 if missing - virtual int64_t getInt64(const std::string &name,int64_t defVal=0) const override; - virtual int64_t getInt64(unsigned int key,int64_t defVal=0) const; + virtual int64_t getInt64(const std::string &name,int64_t defVal) const override; + virtual int64_t getInt64(unsigned int key,int64_t defVal) const; /// Interpret an int as a boolean - virtual bool getBool(const std::string &name,bool defVal=false) const override; - virtual bool getBool(unsigned int key,bool defVal=false) const; + virtual bool getBool(const std::string &name,bool defVal) const override; + virtual bool getBool(unsigned int key,bool defVal) const; /// Interpret an int as a RGBA color virtual RGBAColor getColor(const std::string &name,const RGBAColor &defVal) const override; virtual RGBAColor getColor(unsigned int key,const RGBAColor &defVal) const; - /// Return a double, using the default if it's missingf - virtual double getDouble(const std::string &name,double defVal=0.0) const override; - virtual double getDouble(unsigned int key,double defVal=0.0) const; + /// Return a double, using the default if it's missing + virtual double getDouble(const std::string &name,double defVal) const override; + virtual double getDouble(unsigned int key,double defVal) const; /// Return a string, or empty if it's missing virtual std::string getString(const std::string &name) const override; virtual std::string getString(unsigned int key) const; @@ -121,6 +124,9 @@ class MutableDictionaryC : public MutableDictionary /// Set field as int void setInt(const std::string &name,int val) override; void setInt(unsigned int key,int val); + /// Set field as int64 + virtual void setInt64(const std::string &name,int64_t val) override; + virtual void setInt64(unsigned int key,int64_t val); /// Set field as 64 bit unique value void setIdentifiable(const std::string &name,SimpleIdentity val) override; void setIdentifiable(unsigned int key,SimpleIdentity val); @@ -260,6 +266,7 @@ class DictionaryEntryCBasic : public DictionaryEntryC DictionaryEntryCBasic(int iVal) : DictionaryEntryC(DictTypeInt), val { .iVal = iVal } { } DictionaryEntryCBasic(double dVal) : DictionaryEntryC(DictTypeDouble), val { .dVal = dVal } { } DictionaryEntryCBasic(int64_t iVal) : DictionaryEntryC(DictTypeInt64), val { .i64Val = iVal } { } + virtual ~DictionaryEntryCBasic() = default; /// Return an int, using the default if it's missing virtual int getInt() const override; @@ -292,6 +299,11 @@ class DictionaryEntryCString : public DictionaryEntryC : DictionaryEntryC(DictTypeString), str(str) { } + DictionaryEntryCString(std::string &&str) + : DictionaryEntryC(DictTypeString), str(std::move(str)) + { + } + virtual ~DictionaryEntryCString() = default; const std::string &getStringRef() const { return str; } @@ -317,6 +329,11 @@ class DictionaryEntryCDict : public DictionaryEntryC : DictionaryEntryC(DictTypeDictionary), dict(dict) { } + DictionaryEntryCDict(MutableDictionaryCRef &&dict) + : DictionaryEntryC(DictTypeDictionary), dict(std::move(dict)) + { + } + virtual ~DictionaryEntryCDict() = default; /// Return a dictionary as an entry virtual DictionaryRef getDict() const override { return dict; } @@ -338,7 +355,12 @@ class DictionaryEntryCArray : public DictionaryEntryC : DictionaryEntryC(DictTypeArray), vals(vals) { } + DictionaryEntryCArray(std::vector &&vals) noexcept + : DictionaryEntryC(DictTypeArray), vals(std::move(vals)) + { + } DictionaryEntryCArray(const std::vector &vals); + virtual ~DictionaryEntryCArray() = default; /// Return the array virtual std::vector getArray() const override; diff --git a/common/WhirlyGlobeLib/include/Drawable.h b/common/WhirlyGlobeLib/include/Drawable.h index 74d19716db..ddf6e0636e 100644 --- a/common/WhirlyGlobeLib/include/Drawable.h +++ b/common/WhirlyGlobeLib/include/Drawable.h @@ -1,9 +1,8 @@ -/* - * Drawable.h +/* Drawable.h * WhirlyGlobeLib * * Created by Steve Gifford on 2/1/11. - * Copyright 2011-2019 mousebird consulting + * Copyright 2011-2021 mousebird consulting * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -15,7 +14,6 @@ * 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. - * */ #import @@ -39,14 +37,17 @@ class SceneRenderer; /** Drawable tweakers are called every frame to mess with things. It's up to you to make the changes, just make them quick. */ -class DrawableTweaker : public Identifiable +struct DrawableTweaker : public Identifiable { -public: - virtual ~DrawableTweaker(); + virtual ~DrawableTweaker() = default; + /// Do your tweaking here virtual void tweakForFrame(Drawable *draw,RendererFrameInfo *frame) = 0; + +protected: + virtual float getZoom(const Drawable &inDraw,const Scene &scene,float def) const = 0; }; - + typedef std::shared_ptr DrawableTweakerRef; typedef std::set DrawableTweakerRefSet; diff --git a/common/WhirlyGlobeLib/include/DynamicTextureAtlas.h b/common/WhirlyGlobeLib/include/DynamicTextureAtlas.h index f87c8d5dc1..02bd3f115f 100644 --- a/common/WhirlyGlobeLib/include/DynamicTextureAtlas.h +++ b/common/WhirlyGlobeLib/include/DynamicTextureAtlas.h @@ -200,7 +200,9 @@ class DynamicTextureAtlas void setPixelFudgeFactor(float pixFudge); /// Try to add the texture to one of our dynamic textures, or create one. - bool addTexture(SceneRenderer *sceneRender,const std::vector &textures,int frame,Point2f *realSize,Point2f *realOffset,SubTexture &subTex,ChangeSet &changes,int borderPixels,int bufferPixels=0,TextureRegion *outTexRegion=NULL); + bool addTexture(SceneRenderer *sceneRender,const std::vector &textures,int frame, + const Point2f *realSize,const Point2f *realOffset,SubTexture &subTex,ChangeSet &changes, + int borderPixels,int bufferPixels=0,TextureRegion *outTexRegion=nullptr); /// Update one of the frames of a multi-frame texture atlas bool updateTexture(Texture *,int frame,const TextureRegion &texRegion,ChangeSet &changes); diff --git a/common/WhirlyGlobeLib/include/FontTextureManager.h b/common/WhirlyGlobeLib/include/FontTextureManager.h index aeda00002c..f888fa01d2 100644 --- a/common/WhirlyGlobeLib/include/FontTextureManager.h +++ b/common/WhirlyGlobeLib/include/FontTextureManager.h @@ -1,9 +1,8 @@ -/* - * FontTextureManager.h +/* FontTextureManager.h * WhirlyGlobeLib * * Created by Steve Gifford on 4/15/13. - * Copyright 2011-2019 mousebird consulting + * Copyright 2011-2021 mousebird consulting * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -15,8 +14,8 @@ * 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. - * */ + #import #import #import @@ -50,8 +49,10 @@ class FontManager : public Identifiable // Comparison operator // Subclass fills this in - virtual bool operator < (const FontManager &that) const; - + virtual bool operator < (const FontManager &that) const = 0; + + virtual void teardown(PlatformThreadInfo *) { } + // Mapping info from glyph to location in a dynamic texture class GlyphInfo { @@ -75,7 +76,7 @@ class FontManager : public Identifiable bool operator () (const GlyphInfo *a,const GlyphInfo *b) const { return a->glyph < b->glyph; } } GlyphInfoSorter; - bool empty() { return glyphs.empty(); } + bool empty() const { return glyphs.empty(); } // Look for an existing glyph and return it if it's there GlyphInfo *findGlyph(WKGlyph glyph); @@ -167,11 +168,13 @@ class FontTextureManager typedef std::set DrawStringRepSet; /// Remove resources associated with the given string - void removeString(SimpleIdentity drawStringId,ChangeSet &changes,TimeInterval when); + virtual void removeString(PlatformThreadInfo *,SimpleIdentity drawStringId,ChangeSet &changes,TimeInterval when); // Tear down everything we've built void clear(ChangeSet &changes); - + + virtual void teardown(PlatformThreadInfo*) = 0; + protected: void init(); diff --git a/common/WhirlyGlobeLib/include/GeometryManager.h b/common/WhirlyGlobeLib/include/GeometryManager.h index 551bd96053..06bda06e98 100644 --- a/common/WhirlyGlobeLib/include/GeometryManager.h +++ b/common/WhirlyGlobeLib/include/GeometryManager.h @@ -37,6 +37,7 @@ class GeometryInfo : public BaseInfo GeometryInfo(); GeometryInfo(const Dictionary &); + virtual ~GeometryInfo() = default; bool colorOverride; RGBAColor color; @@ -70,10 +71,10 @@ class GeomSceneRep : public Identifiable float fade; // Remove the contents of this scene rep - void clearContents(SelectionManager *selectManager,ChangeSet &changes,TimeInterval when); + void clearContents(SelectionManagerRef &selectManager,ChangeSet &changes,TimeInterval when); // Enable/disable contents - void enableContents(SelectionManager *selectManager,bool enable,ChangeSet &changes); + void enableContents(SelectionManagerRef &selectManager,bool enable,ChangeSet &changes); }; typedef std::set GeomSceneRepSet; @@ -326,8 +327,8 @@ class GeometryManager : public SceneManager void setUniformBlock(const SimpleIDSet &geomIDs,const RawDataRef &uniBlock,int bufferID,ChangeSet &changes); protected: - std::mutex geomLock; GeomSceneRepSet sceneReps; }; +typedef std::shared_ptr GeometryManagerRef; } diff --git a/common/WhirlyGlobeLib/include/GlobeView.h b/common/WhirlyGlobeLib/include/GlobeView.h index 898b6cd5be..e7e15aa52d 100644 --- a/common/WhirlyGlobeLib/include/GlobeView.h +++ b/common/WhirlyGlobeLib/include/GlobeView.h @@ -207,7 +207,7 @@ class GlobeViewState : public WhirlyKit::ViewState Returns true if we hit and where Returns false if not and the closest point on the sphere */ - bool pointOnSphereFromScreen(const WhirlyKit::Point2f &pt,const Eigen::Matrix4d &transform,const WhirlyKit::Point2f &frameSize,WhirlyKit::Point3d &hit); + bool pointOnSphereFromScreen(const WhirlyKit::Point2f &pt,const Eigen::Matrix4d &transform,const WhirlyKit::Point2f &frameSize,WhirlyKit::Point3d &hit,bool clip = true); }; typedef std::shared_ptr GlobeViewStateRef; diff --git a/common/WhirlyGlobeLib/include/GridClipper.h b/common/WhirlyGlobeLib/include/GridClipper.h index 97c2a3311e..e7b41ff75d 100644 --- a/common/WhirlyGlobeLib/include/GridClipper.h +++ b/common/WhirlyGlobeLib/include/GridClipper.h @@ -31,7 +31,7 @@ namespace WhirlyKit bool ClipLoopToGrid(const VectorRing &ring,Point2f org,Point2f spacing,std::vector &rets); // This version clips a whole group of rings. The first one is the outer, the rest inner. bool ClipLoopsToGrid(const std::vector &rings,Point2f org,Point2f spacing,std::vector &rets); -bool ClipLoopToMbr(const VectorRing &ring,const Mbr &mbr, bool closed,std::vector &rets); -bool ClipLoopsToMbr(const std::vector &rings,const Mbr &mbr, bool closed,std::vector &rets); - +bool ClipLoopToMbr(const VectorRing &ring,const Mbr &mbr, bool closed,std::vector &rets,double polyScale = 0.0); +bool ClipLoopsToMbr(const std::vector &rings,const Mbr &mbr, bool closed,std::vector &rets,double polyScale = 0.0); + } diff --git a/common/WhirlyGlobeLib/include/Identifiable.h b/common/WhirlyGlobeLib/include/Identifiable.h index 8408292d59..f206b5d367 100644 --- a/common/WhirlyGlobeLib/include/Identifiable.h +++ b/common/WhirlyGlobeLib/include/Identifiable.h @@ -1,9 +1,8 @@ -/* - * Identifiable.h +/* Identifiable.h * WhirlyGlobeLib * * Created by Steve Gifford on 2/7/11. - * Copyright 2011-2019 mousebird consulting + * Copyright 2011-2021 mousebird consulting * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -15,7 +14,6 @@ * 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. - * */ #import diff --git a/common/WhirlyGlobeLib/include/IntersectionManager.h b/common/WhirlyGlobeLib/include/IntersectionManager.h index 1a01c56e6e..455ae8838c 100644 --- a/common/WhirlyGlobeLib/include/IntersectionManager.h +++ b/common/WhirlyGlobeLib/include/IntersectionManager.h @@ -66,9 +66,9 @@ class IntersectionManager : public SceneManager bool findIntersection(SceneRenderer *renderer,View *theView,const Point2f &frameSize,const Point2f &touchPt,Point3d &iPt,double &dist); protected: - std::mutex mutex; Scene *scene; std::set intersectables; }; +typedef std::shared_ptr IntersectionManagerRef; } diff --git a/common/WhirlyGlobeLib/include/LabelManager.h b/common/WhirlyGlobeLib/include/LabelManager.h index b8fb3cd0fb..01b966a72f 100644 --- a/common/WhirlyGlobeLib/include/LabelManager.h +++ b/common/WhirlyGlobeLib/include/LabelManager.h @@ -1,9 +1,8 @@ -/* - * LabelManager.h +/* LabelManager.h * WhirlyGlobeLib * * Created by Steve Gifford on 7/22/13. - * Copyright 2011-2019 mousebird consulting + * Copyright 2011-2021 mousebird consulting * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -15,7 +14,6 @@ * 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. - * */ #import @@ -43,7 +41,7 @@ class SingleLabel EIGEN_MAKE_ALIGNED_OPERATOR_NEW; SingleLabel(); - virtual ~SingleLabel() { }; + virtual ~SingleLabel() = default; /// If set, this marker should be made selectable /// and it will be if the selection layer has been set @@ -81,12 +79,23 @@ class SingleLabel float layoutImportance; /// Layout placement int layoutPlacement; + /// Shape for label to follow + VectorRing layoutShape; + + // If set, we'll draw an outline to the mask target + WhirlyKit::SimpleIdentity maskID; + WhirlyKit::SimpleIdentity maskRenderTargetID; /// Some attributes can be overridden per label LabelInfoRef infoOverride; // Used to build the drawable string on specific platforms - virtual std::vector generateDrawableStrings(PlatformThreadInfo *threadInfo,const LabelInfo *,FontTextureManager *fontTexManager,float &lineHeight,ChangeSet &changes) = 0; + virtual std::vector generateDrawableStrings( + PlatformThreadInfo *threadInfo, + const LabelInfo *, + const FontTextureManagerRef &fontTexManager, + float &lineHeight, + ChangeSet &changes) = 0; }; typedef std::shared_ptr SingleLabelRef; @@ -99,27 +108,36 @@ class LabelManager : public SceneManager { public: LabelManager(); - virtual ~LabelManager(); + virtual ~LabelManager() = default; /// Add the given set of labels, returning an ID that represents the whole thing - SimpleIdentity addLabels(PlatformThreadInfo *threadInfo,std::vector &labels,const LabelInfo &desc,ChangeSet &changes); - SimpleIdentity addLabels(PlatformThreadInfo *threadInfo,std::vector &labels,const LabelInfo &desc,ChangeSet &changes); + SimpleIdentity addLabels(PlatformThreadInfo *threadInfo, + const std::vector &labels, + const LabelInfo &desc,ChangeSet &changes); + SimpleIdentity addLabels(PlatformThreadInfo *threadInfo, + const std::vector &labels, + const LabelInfo &desc,ChangeSet &changes); /// Change visual attributes (just the visibility range) - void changeLabel(PlatformThreadInfo *threadInfo,SimpleIdentity labelID,const LabelInfo &desc,ChangeSet &changes); + void changeLabel(PlatformThreadInfo *threadInfo, + SimpleIdentity labelID, + const LabelInfo &desc, + ChangeSet &changes); /// Remove the given label(s) - void removeLabels(PlatformThreadInfo *threadInfo,SimpleIDSet &labelID,ChangeSet &changes); + void removeLabels(PlatformThreadInfo *threadInfo, + const SimpleIDSet &labelID, + ChangeSet &changes); /// Enable/disable labels - void enableLabels(SimpleIDSet labelID,bool enable,ChangeSet &changes); + void enableLabels(const SimpleIDSet &labelID,bool enable,ChangeSet &changes); protected: - std::mutex labelLock; - /// Keep track of labels (or groups of labels) by ID for deletion WhirlyKit::LabelSceneRepSet labelReps; unsigned int textureAtlasSize; + SimpleIdentity maskProgID; }; +typedef std::shared_ptr LabelManagerRef; } diff --git a/common/WhirlyGlobeLib/include/LabelRenderer.h b/common/WhirlyGlobeLib/include/LabelRenderer.h index 0d1f874f50..08f3259709 100644 --- a/common/WhirlyGlobeLib/include/LabelRenderer.h +++ b/common/WhirlyGlobeLib/include/LabelRenderer.h @@ -50,10 +50,9 @@ class SingleLabel; class LabelSceneRep : public Identifiable { public: - LabelSceneRep(); + LabelSceneRep() { } LabelSceneRep(SimpleIdentity theId) : Identifiable(theId) { } - ~LabelSceneRep() { } - + float fadeOut; // Fade interval, for deletion SimpleIDSet texIDs; // Textures we created for this SimpleIDSet drawIDs; // Drawables created for this @@ -74,20 +73,27 @@ class LabelInfo : public BaseInfo LabelInfo(bool screenObject); LabelInfo(const LabelInfo &that); LabelInfo(const Dictionary &dict,bool screenObject); + virtual ~LabelInfo() = default; + + bool hasTextColor = false; + RGBAColor textColor = RGBAColor::white(); + RGBAColor backColor = RGBAColor::clear(); + bool screenObject = true; + float width = 0.0f; + float height = 0.0f; + LabelJustify labelJustify = WhirlyKitLabelMiddle; + TextJustify textJustify = WhirlyKitTextCenter; + RGBAColor shadowColor = RGBAColor::black(); + float shadowSize = -1.0f; + RGBAColor outlineColor = RGBAColor::black(); + float outlineSize = -1.0f; + float lineHeight = 0.0f; + float fontPointSize = 16.0f; + float layoutOffset = 0.0f; + float layoutSpacing = 20.0f; + int layoutRepeat = 0; + bool layoutDebug = false; - bool hasTextColor; - RGBAColor textColor,backColor; - bool screenObject; - float width,height; - LabelJustify labelJustify; - TextJustify textJustify; - RGBAColor shadowColor; - float shadowSize; - RGBAColor outlineColor; - float outlineSize; - float lineHeight; - float fontPointSize; - FloatExpressionInfoRef opacityExp; // ColorExpressionInfoRef colorExp; FloatExpressionInfoRef scaleExp; @@ -102,18 +108,23 @@ typedef std::shared_ptr LabelInfoRef; class LabelRenderer { public: - LabelRenderer(Scene *scene,FontTextureManager *fontTexManager,const LabelInfo *labelInfo); - + LabelRenderer(Scene *scene, + SceneRenderer *renderer, + FontTextureManagerRef fontTexManager, + const LabelInfo *labelInfo, + SimpleIdentity maskProgID); + /// Description of the labels - const LabelInfo *labelInfo; + const LabelInfo *labelInfo = nullptr; /// How big texture atlases should be if we're not using fonts - int textureAtlasSize; - /// Coordinate system display adapater - CoordSystemDisplayAdapter *coordAdapter; - /// Label represention (return value) - LabelSceneRep *labelRep; + int textureAtlasSize = 2048; + /// Coordinate system display adapter + CoordSystemDisplayAdapter *coordAdapter = nullptr; + /// Label representation (return value) + LabelSceneRep *labelRep = nullptr; /// Scene we're building in - Scene *scene; + Scene *scene = nullptr; + SceneRenderer *renderer = nullptr; /// Screen space objects std::vector screenObjects; /// Layout objects (pass these to the layout engine if you want that) @@ -128,14 +139,19 @@ class LabelRenderer /// Change requests to pass to the scene ChangeSet changeRequests; /// Font texture manager to use if we're doing fonts - FontTextureManager *fontTexManager; + FontTextureManagerRef fontTexManager; /// Set if want to use attributed strings (we usually do) - bool useAttributedString; + bool useAttributedString = true; /// Scale, if we're using that - float scale; + float scale = 1.0f; + // Program used to render masks to their target + SimpleIdentity maskProgID = 0; + + /// Convenience routine to convert the points to model space + Point3dVector convertGeoPtsToModelSpace(const VectorRing &inPts); /// Renders the labels into a big texture and stores the resulting info - void render(PlatformThreadInfo *threadInfo,std::vector &labels,ChangeSet &changes); + void render(PlatformThreadInfo *threadInfo,const std::vector &labels,ChangeSet &changes); }; } diff --git a/common/WhirlyGlobeLib/include/LayoutManager.h b/common/WhirlyGlobeLib/include/LayoutManager.h index 39c7130694..f54a50cae8 100644 --- a/common/WhirlyGlobeLib/include/LayoutManager.h +++ b/common/WhirlyGlobeLib/include/LayoutManager.h @@ -1,9 +1,8 @@ -/* - * LayoutManager.h +/* LayoutManager.h * WhirlyGlobeLib * * Created by Steve Gifford on 7/15/13. - * Copyright 2011-2019 mousebird consulting. + * Copyright 2011-2021 mousebird consulting. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -15,7 +14,6 @@ * 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. - * */ #import @@ -28,6 +26,7 @@ #import "ScreenSpaceBuilder.h" #import "SelectionManager.h" #import "OverlapHelper.h" +#import "VectorManager.h" namespace WhirlyKit { @@ -35,15 +34,15 @@ namespace WhirlyKit /// Don't modify it at all #define WhirlyKitLayoutPlacementNone (1<<0) /// Okay to center -#define WhirlyKitLayoutPlacementCenter (1<<1) +#define WhirlyKitLayoutPlacementCenter (1U<<1U) /// Okay to place to the right of a point -#define WhirlyKitLayoutPlacementRight (1<<2) +#define WhirlyKitLayoutPlacementRight (1U<<2U) /// Okay to place it to the left of a point -#define WhirlyKitLayoutPlacementLeft (1<<3) +#define WhirlyKitLayoutPlacementLeft (1U<<3U) /// Okay to place on top of a point -#define WhirlyKitLayoutPlacementAbove (1<<4) +#define WhirlyKitLayoutPlacementAbove (1U<<4U) /// Okay to place below a point -#define WhirlyKitLayoutPlacementBelow (1<<5) +#define WhirlyKitLayoutPlacementBelow (1U<<5U) /** This represents an object in the screen space generator to be laid out by the layout engine. We'll manipulate its offset and enable/disable it @@ -67,17 +66,26 @@ class LayoutObject : public ScreenSpaceObject // Size to use for selection Point2dVector selectPts; - std::string uniqueID; - + /// This is used to sort objects for layout. Bigger is more important. float importance; /// If set, this is clustering group to sort into int clusterGroup; + int layoutRepeat; // How many instances + float layoutOffset; // Offset left/right + float layoutSpacing; // Start/end spacing along line + float layoutWidth; // Used in generalization + bool layoutDebug; // Turn this on for layout debugging + Point3dVector layoutShape; + /// If we're placing glyphs individually we'll do it with matrices + std::vector > layoutPlaces; + std::vector layoutModelPlaces; + /// Options for where to place this object: WhirlyKitLayoutPlacementLeft, WhirlyKitLayoutPlacementRight, /// WhirlyKitLayoutPlacementAbove, WhirlyKitLayoutPlacementBelow - int acceptablePlacement; + unsigned acceptablePlacement; /// Debugging hint std::string hint; }; @@ -115,21 +123,24 @@ typedef std::set LayoutEntrySet; class ClusterGenerator { public: - virtual ~ClusterGenerator() { } + virtual ~ClusterGenerator() = default; // Called right before we start generating layout objects - virtual void startLayoutObjects() = 0; + virtual void startLayoutObjects(PlatformThreadInfo *) = 0; // Generate a layout object (with screen space object and such) for the cluster - virtual void makeLayoutObject(int clusterID,const std::vector &layoutObjects,LayoutObject &newObj) = 0; + virtual void makeLayoutObject( + PlatformThreadInfo *, + int clusterID, + const std::vector &layoutObjects, + LayoutObject &newObj) = 0; // Called right after all the layout objects are generated - virtual void endLayoutObjects() = 0; + virtual void endLayoutObjects(PlatformThreadInfo *) = 0; // Parameters for a particular cluster class needed to make animations and such - class ClusterClassParams + struct ClusterClassParams { - public: SimpleIdentity motionShaderID; bool selectable; double markerAnimationTime; @@ -137,7 +148,7 @@ class ClusterGenerator }; // Return the shader used when moving objects into and out of clusters - virtual void paramsForClusterClass(int clusterID,ClusterClassParams &clusterParams) = 0; + virtual void paramsForClusterClass(PlatformThreadInfo *,int clusterID,ClusterClassParams &clusterParams) = 0; }; #define kWKLayoutManager "WKLayoutManager" @@ -200,7 +211,7 @@ class LayoutManager : public SceneManager void enableLayoutObjects(const SimpleIDSet &layoutObjects,bool enable); /// Run the layout logic for everything we're aware of (thread safe) - void updateLayout(ViewStateRef viewState,ChangeSet &changes); + void updateLayout(PlatformThreadInfo *threadInfo,const ViewStateRef &viewState,ChangeSet &changes); /// True if we've got changes since the last update bool hasChanges(); @@ -209,14 +220,20 @@ class LayoutManager : public SceneManager void getScreenSpaceObjects(const SelectionManager::PlacementInfo &pInfo,std::vector &screenSpaceObjs); /// Add a generator for cluster images - void addClusterGenerator(ClusterGenerator *clusterGen); + void addClusterGenerator(PlatformThreadInfo *,ClusterGenerator *clusterGen); protected: - bool calcScreenPt(Point2f &objPt,LayoutObject *layoutObj,ViewStateRef viewState,const Mbr &screenMbr,const Point2f &frameBufferSize); - Eigen::Matrix2d calcScreenRot(float &screenRot,ViewStateRef viewState,WhirlyGlobe::GlobeViewState *globeViewState,ScreenSpaceObject *ssObj,const Point2f &objPt,const Eigen::Matrix4d &modelTrans,const Eigen::Matrix4d &normalMat,const Point2f &frameBufferSize); - bool runLayoutRules(ViewStateRef viewState,std::vector &clusterEntries,std::vector &clusterParams); + static bool calcScreenPt(Point2f &objPt,LayoutObject *layoutObj,const ViewStateRef &viewState,const Mbr &screenMbr,const Point2f &frameBufferSize); + static Eigen::Matrix2d calcScreenRot(float &screenRot,const ViewStateRef &viewState,WhirlyGlobe::GlobeViewState *globeViewState,ScreenSpaceObject *ssObj,const Point2f &objPt,const Eigen::Matrix4d &modelTrans,const Eigen::Matrix4d &normalMat,const Point2f &frameBufferSize); + + bool runLayoutRules(PlatformThreadInfo *threadInfo, + const ViewStateRef &viewState, + std::vector &clusterEntries, + std::vector &outClusterParams, + ChangeSet &changes); + + VectorManagerRef vecManage; - std::mutex layoutLock; /// If non-zero the maximum number of objects we'll display at once int maxDisplayObjects; /// If there were updates since the last layout @@ -233,6 +250,10 @@ class LayoutManager : public SceneManager ClusterGenerator *clusterGen; /// Features we'll force to always display std::set overrideUUIDs; + + SimpleIDSet debugVecIDs; // Used to display debug lines for text layout + SimpleIdentity vecProgID; }; +typedef std::shared_ptr LayoutManagerRef; } diff --git a/common/WhirlyGlobeLib/include/LinearTextBuilder.h b/common/WhirlyGlobeLib/include/LinearTextBuilder.h new file mode 100644 index 0000000000..3446174d79 --- /dev/null +++ b/common/WhirlyGlobeLib/include/LinearTextBuilder.h @@ -0,0 +1,107 @@ +/* + * LinearTextBuilder.h + * WhirlyGlobeLib + * + * Created by Steve Gifford on 3/3/21. + * Copyright 2011-2021 mousebird consulting + * + * 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. + * + */ + +#import "WhirlyVector.h" +#import "WhirlyGeometry.h" +#import "WhirlyKitView.h" +#import "GlobeMath.h" +#import "LayoutManager.h" +#import "VectorData.h" + +namespace WhirlyKit { + +/// Ye olde Douglas Peucker line generalization algorithm. Mostly. +Point2fVector LineGeneralization(const Point2fVector &screenPts, + float eps, + unsigned int start,unsigned int end); + +/// Used to 'walk' along a linear feature by distance +class LinearWalker { +public: + LinearWalker(const VectorRing &pts); + + /// Return the total length + float getTotalLength(); + + /// Calculate the next point along the line given the distance + /// Or return false if there wasn't anything left + bool nextPoint(double distance,Point2f *retPt,Point2f *norm,bool savePos=true); + +protected: + VectorRing pts; + float totalLength; + int ptSoFar; + float offsetDist; +}; + +/** + Used to lay text out along a line (with or without offset). + A very specific implementation of a wacky algorithm. + */ +class LinearTextBuilder { +public: + LinearTextBuilder(ViewStateRef viewState, + unsigned int offi, + const Point2f &frameBufferSize, + float generalEps, + LayoutObject *layoutObj); + + // Set the starting point, er points + void setPoints(const Point3dVector &pts); + + // Sort the runs by length, toss the ones below the minimum length + void sortRuns(double minLen); + + // Run our crazy stuff + void process(); + + // Return the individual runs to follow + std::vector getScreenVecs(); + + // Visual vectors for debugging + ShapeSet getVisualVecs(); + + // Convert point from screen coordinates back to world coordinates + bool screenToWorld(const Point2f &pt,Point3d &outPt); + Point2f worldToScreen(const Point3d &worldPt); + + // Return the current rotation of the view state + double getViewStateRotation(); + +protected: + CoordSystemDisplayAdapter *coordAdapt; + CoordSystem *coordSys; + ViewStateRef viewState; + WhirlyGlobe::GlobeViewState *globeViewState; + Maply::MapViewState *mapViewState; + + float generalEps; + unsigned int offi; + Mbr screenMbr; + Point2f frameBufferSize; + LayoutObject *layoutObj; + + Point3dVector pts; + bool isClosed; + + std::vector runs; +}; + +} diff --git a/common/WhirlyGlobeLib/include/LoftManager.h b/common/WhirlyGlobeLib/include/LoftManager.h index 99406b0e74..9291ff6be6 100644 --- a/common/WhirlyGlobeLib/include/LoftManager.h +++ b/common/WhirlyGlobeLib/include/LoftManager.h @@ -40,7 +40,8 @@ class LoftedPolyInfo : public BaseInfo LoftedPolyInfo(); LoftedPolyInfo(const Dictionary &dict); - + virtual ~LoftedPolyInfo() = default; + float height; float base; bool top,side; @@ -94,8 +95,8 @@ class LoftManager : public SceneManager protected: void addGeometryToBuilder(LoftedPolySceneRep *sceneRep,const LoftedPolyInfo &polyInfo,GeoMbr &drawMbr,Point3d ¢er,bool centerValid,Point2d &geoCenter,ShapeSet &shapes, VectorTrianglesRef triMesh,std::vector &outlines,ChangeSet &changes); - std::mutex loftLock; LoftedPolySceneRepSet loftReps; }; +typedef std::shared_ptr LoftManagerRef; } diff --git a/common/WhirlyGlobeLib/include/MapboxVectorStyleBackground.h b/common/WhirlyGlobeLib/include/MapboxVectorStyleBackground.h index eb93e69ab8..0ce60ae482 100644 --- a/common/WhirlyGlobeLib/include/MapboxVectorStyleBackground.h +++ b/common/WhirlyGlobeLib/include/MapboxVectorStyleBackground.h @@ -47,13 +47,14 @@ class MapboxVectorLayerBackground : public MapboxVectorStyleLayer MapboxVectorLayerBackground(MapboxVectorStyleSetImpl *styleSet) : MapboxVectorStyleLayer(styleSet) { } virtual bool parse(PlatformThreadInfo *inst, - DictionaryRef styleEntry, - MapboxVectorStyleLayerRef refLayer, - int drawPriority); + const DictionaryRef &styleEntry, + const MapboxVectorStyleLayerRef &refLayer, + int drawPriority) override; virtual void buildObjects(PlatformThreadInfo *inst, - std::vector &vecObjs, - VectorTileDataRef tileInfo); + const std::vector &vecObjs, + const VectorTileDataRef &tileInfo, + const Dictionary *desc) override; public: /// Controls how the background looks. diff --git a/common/WhirlyGlobeLib/include/MapboxVectorStyleCircle.h b/common/WhirlyGlobeLib/include/MapboxVectorStyleCircle.h index bf6516d3cc..b03fe60025 100644 --- a/common/WhirlyGlobeLib/include/MapboxVectorStyleCircle.h +++ b/common/WhirlyGlobeLib/include/MapboxVectorStyleCircle.h @@ -56,15 +56,16 @@ class MapboxVectorLayerCircle : public MapboxVectorStyleLayer MapboxVectorLayerCircle(MapboxVectorStyleSetImpl *styleSet) : MapboxVectorStyleLayer(styleSet) { } virtual bool parse(PlatformThreadInfo *inst, - DictionaryRef styleEntry, - MapboxVectorStyleLayerRef refLayer, - int drawPriority); - + const DictionaryRef &styleEntry, + const MapboxVectorStyleLayerRef &refLayer, + int drawPriority) override; + virtual void buildObjects(PlatformThreadInfo *inst, - std::vector &vecObjs, - VectorTileDataRef tileInfo); - - virtual void cleanup(ChangeSet &changes); + const std::vector &vecObjs, + const VectorTileDataRef &tileInfo, + const Dictionary *desc) override; + + virtual void cleanup(PlatformThreadInfo *inst,ChangeSet &changes) override; public: MapboxVectorCirclePaint paint; @@ -72,7 +73,8 @@ class MapboxVectorLayerCircle : public MapboxVectorStyleLayer SimpleIdentity circleTexID; Point2f circleSize; float importance; - std::string uuidField; + std::string uuidField; // UUID field for markers/labels (from style settings) + std::string repUUIDField; // UUID field for representations (from style layers) }; } diff --git a/common/WhirlyGlobeLib/include/MapboxVectorStyleFill.h b/common/WhirlyGlobeLib/include/MapboxVectorStyleFill.h index 8c3203591e..37e55c4433 100644 --- a/common/WhirlyGlobeLib/include/MapboxVectorStyleFill.h +++ b/common/WhirlyGlobeLib/include/MapboxVectorStyleFill.h @@ -30,7 +30,7 @@ class MapboxVectorFillPaint public: bool parse(PlatformThreadInfo *inst, MapboxVectorStyleSetImpl *styleSet, - DictionaryRef styleEntry); + const DictionaryRef &styleEntry); MapboxTransDoubleRef opacity; MapboxTransColorRef color; @@ -44,13 +44,14 @@ class MapboxVectorLayerFill : public MapboxVectorStyleLayer MapboxVectorLayerFill(MapboxVectorStyleSetImpl *styleSet) : MapboxVectorStyleLayer(styleSet) { } virtual bool parse(PlatformThreadInfo *inst, - DictionaryRef styleEntry, - MapboxVectorStyleLayerRef refLayer, + const DictionaryRef &styleEntry, + const MapboxVectorStyleLayerRef &refLayer, int drawPriority) override; virtual void buildObjects(PlatformThreadInfo *inst, - std::vector &vecObjs, - VectorTileDataRef tileInfo) override; + const std::vector &vecObjs, + const VectorTileDataRef &tileInfo, + const Dictionary *desc) override; virtual void cleanup(PlatformThreadInfo *inst,ChangeSet &changes) override; diff --git a/common/WhirlyGlobeLib/include/MapboxVectorStyleLayer.h b/common/WhirlyGlobeLib/include/MapboxVectorStyleLayer.h index a4f0d24414..444ebe1eb2 100644 --- a/common/WhirlyGlobeLib/include/MapboxVectorStyleLayer.h +++ b/common/WhirlyGlobeLib/include/MapboxVectorStyleLayer.h @@ -34,11 +34,13 @@ typedef std::shared_ptr MapboxVectorStyleLayerRef; class MapboxVectorStyleLayer : public VectorStyleImpl { public: + virtual std::string getIdent() const override { return ident; } + virtual std::string getType() const override { return type; } /// @brief Initialize with the style sheet and the entry for this layer static MapboxVectorStyleLayerRef VectorStyleLayer(PlatformThreadInfo *inst, MapboxVectorStyleSetImpl *styleSet, - DictionaryRef layerDict, + const DictionaryRef &layerDict, int drawPriority); /// @brief Base class initialization. Copies data out of the refLayer @@ -47,8 +49,8 @@ class MapboxVectorStyleLayer : public VectorStyleImpl // Parse the layer entry out of the style sheet virtual bool parse(PlatformThreadInfo *inst, - DictionaryRef styleEntry, - MapboxVectorStyleLayerRef parentLayer, + const DictionaryRef &styleEntry, + const MapboxVectorStyleLayerRef &refLayer, int drawPriority); /// Unique Identifier for this style @@ -63,8 +65,9 @@ class MapboxVectorStyleLayer : public VectorStyleImpl /// Construct objects related to this style based on the input data. virtual void buildObjects(PlatformThreadInfo *inst, - std::vector &vecObjs, - VectorTileDataRef tileInfo) override = 0; + const std::vector &vecObjs, + const VectorTileDataRef &tileInfo, + const Dictionary *desc) override = 0; /// Clean up any objects (textures, probably) virtual void cleanup(PlatformThreadInfo *inst,ChangeSet &changes); @@ -110,6 +113,9 @@ class MapboxVectorStyleLayer : public VectorStyleImpl /// Category value, if set std::string category; + + /// @brief The specific representation for this layer (e.g., "selected") + std::string representation; }; diff --git a/common/WhirlyGlobeLib/include/MapboxVectorStyleLine.h b/common/WhirlyGlobeLib/include/MapboxVectorStyleLine.h index 09ccd680de..4b02cb113b 100644 --- a/common/WhirlyGlobeLib/include/MapboxVectorStyleLine.h +++ b/common/WhirlyGlobeLib/include/MapboxVectorStyleLine.h @@ -1,9 +1,8 @@ -/* - * MapboxVectorStyleLine.h +/* MapboxVectorStyleLine.h * WhirlyGlobe-MaplyComponent * * Created by Steve Gifford on 2/17/15. - * Copyright 2011-2019 mousebird consulting + * Copyright 2011-2021 mousebird consulting * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -15,7 +14,6 @@ * 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. - * */ #import "MapboxVectorStyleSetC.h" @@ -33,7 +31,7 @@ typedef enum {MBLineJoinBevel,MBLineJoinRound,MBLineJoinMiter} MapboxVectorLineJ class MapboxVectorLineLayout { public: - bool parse(PlatformThreadInfo *inst,MapboxVectorStyleSetImpl *styleSet,DictionaryRef styleEntry); + bool parse(PlatformThreadInfo *inst,MapboxVectorStyleSetImpl *styleSet,const DictionaryRef &styleEntry); MapboxVectorLineCap cap; MapboxVectorLineJoin join; @@ -47,11 +45,12 @@ class MapboxVectorLineLayout class MapboxVectorLinePaint { public: - bool parse(PlatformThreadInfo *inst,MapboxVectorStyleSetImpl *styleSet,DictionaryRef styleEntry); + bool parse(PlatformThreadInfo *inst,MapboxVectorStyleSetImpl *styleSet,const DictionaryRef &styleEntry); MapboxTransDoubleRef opacity; MapboxTransColorRef color; MapboxTransDoubleRef width; + MapboxTransDoubleRef offset; std::string pattern; std::vector lineDashArray; }; @@ -63,13 +62,16 @@ class MapboxVectorLayerLine : public MapboxVectorStyleLayer MapboxVectorLayerLine(MapboxVectorStyleSetImpl *styleSet) : MapboxVectorStyleLayer(styleSet) { } virtual bool parse(PlatformThreadInfo *inst, - DictionaryRef styleEntry, - MapboxVectorStyleLayerRef refLayer, - int drawPriority); + const DictionaryRef &styleEntry, + const MapboxVectorStyleLayerRef &refLayer, + int drawPriority) override; - virtual void buildObjects(PlatformThreadInfo *inst,std::vector &vecObjs,VectorTileDataRef tileInfo); + virtual void buildObjects(PlatformThreadInfo *inst, + const std::vector &vecObjs, + const VectorTileDataRef &tileInfo, + const Dictionary *desc) override; - virtual void cleanup(PlatformThreadInfo *inst,ChangeSet &changes); + virtual void cleanup(PlatformThreadInfo *inst,ChangeSet &changes) override; public: MapboxVectorLineLayout layout; @@ -84,6 +86,9 @@ class MapboxVectorLayerLine : public MapboxVectorStyleLayer double totLen; double fade; SimpleIdentity filledLineTexID; + + std::string uuidField; // UUID field for markers/labels (from style settings) + std::string repUUIDField; // UUID field for representations (from style layers) }; } diff --git a/common/WhirlyGlobeLib/include/MapboxVectorStyleRaster.h b/common/WhirlyGlobeLib/include/MapboxVectorStyleRaster.h index 80daf3984a..4d1699cd6d 100644 --- a/common/WhirlyGlobeLib/include/MapboxVectorStyleRaster.h +++ b/common/WhirlyGlobeLib/include/MapboxVectorStyleRaster.h @@ -31,15 +31,16 @@ class MapboxVectorLayerRaster : public MapboxVectorStyleLayer MapboxVectorLayerRaster(MapboxVectorStyleSetImpl *styleSet) : MapboxVectorStyleLayer(styleSet) { } virtual bool parse(PlatformThreadInfo *inst, - DictionaryRef styleEntry, - MapboxVectorStyleLayerRef refLayer, - int drawPriority); + const DictionaryRef &styleEntry, + const MapboxVectorStyleLayerRef &refLayer, + int drawPriority) override; virtual void buildObjects(PlatformThreadInfo *inst, - std::vector &vecObjs, - VectorTileDataRef tileInfo); + const std::vector &vecObjs, + const VectorTileDataRef &tileInfo, + const Dictionary *desc) override; - virtual void cleanup(PlatformThreadInfo *inst,ChangeSet &changes); + virtual void cleanup(PlatformThreadInfo *inst,ChangeSet &changes) override; protected: }; diff --git a/common/WhirlyGlobeLib/include/MapboxVectorStyleSetC.h b/common/WhirlyGlobeLib/include/MapboxVectorStyleSetC.h index 69c97790c8..453d971ef2 100644 --- a/common/WhirlyGlobeLib/include/MapboxVectorStyleSetC.h +++ b/common/WhirlyGlobeLib/include/MapboxVectorStyleSetC.h @@ -1,9 +1,8 @@ -/* -* MapboxVectorStyleSetC.h +/* MapboxVectorStyleSetC.h * WhirlyGlobeLib * * Created by Steve Gifford on 4/8/20. -* Copyright 2011-2020 mousebird consulting +* Copyright 2011-2021 mousebird consulting * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -15,7 +14,6 @@ * 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. -* */ #import "Scene.h" @@ -56,10 +54,10 @@ class MapboxRegexField { // Simpler version that just takes the text string bool parse(const std::string &textVal); // Parse the regex text field out of a field name string - bool parse(const std::string &fieldName,MapboxVectorStyleSetImpl *styleSet,DictionaryRef styleEntry); + bool parse(const std::string &fieldName,MapboxVectorStyleSetImpl *styleSet,const DictionaryRef &styleEntry); // Build the field based on the attributes - std::string build(DictionaryRef attrs); + std::string build(const DictionaryRef &attrs); std::vector chunks; @@ -89,7 +87,7 @@ class MaplyVectorFunctionStop class MaplyVectorFunctionStops { public: - bool parse(DictionaryRef entry,MapboxVectorStyleSetImpl *styleSet,bool isText); + bool parse(const DictionaryRef &entry,MapboxVectorStyleSetImpl *styleSet,bool isText); /// @brief Calculate a value given the zoom level double valueForZoom(double zoom); @@ -200,10 +198,10 @@ class MapboxVectorStyleSetImpl : public VectorStyleDelegateImpl { public: MapboxVectorStyleSetImpl(Scene *scene,CoordSystem *coordSys,VectorStyleSettingsImplRef settings); - virtual ~MapboxVectorStyleSetImpl(); + virtual ~MapboxVectorStyleSetImpl() = default; // Parse the entire style sheet. False on failure - virtual bool parse(PlatformThreadInfo *inst,DictionaryRef dict); + virtual bool parse(PlatformThreadInfo *inst,const DictionaryRef &dict); /// @brief Default settings and scale factor for Mapnik vector geometry. VectorStyleSettingsImplRef tileStyleSettings; @@ -212,58 +210,61 @@ class MapboxVectorStyleSetImpl : public VectorStyleDelegateImpl long long generateID(); /// @brief Return an integer value for the given name, taking the constants into account. - int intValue(const std::string &name,DictionaryRef dict,int defVal); + int intValue(const std::string &name,const DictionaryRef &dict,int defVal); /// @brief Return a double value for the given name, taking the constants into account - double doubleValue(DictionaryEntryRef entry,double defVal); + double doubleValue(const DictionaryEntryRef &entry,double defVal); /// @brief Return a double value for the given name, taking the constants into account - double doubleValue(const std::string &name,DictionaryRef dict,double defVal); + double doubleValue(const std::string &name,const DictionaryRef &dict,double defVal); /// @brief Return a bool for the given name. True if it matches the onString. Default if it's missing - bool boolValue(const std::string &name,DictionaryRef dict,const std::string &onString,bool defVal); + bool boolValue(const std::string &name,const DictionaryRef &dict,const std::string &onString,bool defVal); /// @brief Return a string for the given name, taking the constants into account - std::string stringValue(const std::string &name,DictionaryRef dict,const std::string &defVal); + std::string stringValue(const std::string &name,const DictionaryRef &dict,const std::string &defVal); /// @brief Return an array for the given name, taking the constants into account - std::vector arrayValue(const std::string &name,DictionaryRef dict); + std::vector arrayValue(const std::string &name,const DictionaryRef &dict); /// @brief Return a color for the given name, taking the constants into account - RGBAColorRef colorValue(const std::string &name,DictionaryEntryRef val,DictionaryRef dict,RGBAColorRef defVal,bool multiplyAlpha); - RGBAColorRef colorValue(const std::string &name,DictionaryEntryRef val,DictionaryRef dict,RGBAColor defVal,bool multiplyAlpha); + RGBAColorRef colorValue(const std::string &name,const DictionaryEntryRef &val,const DictionaryRef &dict,const RGBAColorRef &defVal,bool multiplyAlpha); + RGBAColorRef colorValue(const std::string &name,const DictionaryEntryRef &val,const DictionaryRef &dict,const RGBAColor &defVal,bool multiplyAlpha); /// @brief Return the integer corresponding to the name. Basically parse the enumerated type - int enumValue(DictionaryEntryRef entry, const char * const options[],int defVal); + int enumValue(const DictionaryEntryRef &entry, const char * const options[],int defVal); /// Builds a transitionable double object from a style entry and returns that - MapboxTransDoubleRef transDouble(DictionaryEntryRef entry,double defVal); + MapboxTransDoubleRef transDouble(const DictionaryEntryRef &entry,double defVal); /// Builds a transitionable double object from a style entry lookup and returns that - MapboxTransDoubleRef transDouble(const std::string &name,DictionaryRef entry,double defVal); + MapboxTransDoubleRef transDouble(const std::string &name,const DictionaryRef &entry,double defVal); /// Builds a transitionable color object and returns that - MapboxTransColorRef transColor(const std::string &name,DictionaryRef entry,const RGBAColor *); - MapboxTransColorRef transColor(const std::string &name,DictionaryRef entry,const RGBAColor &); + MapboxTransColorRef transColor(const std::string &name,const DictionaryRef &entry,const RGBAColor *); + MapboxTransColorRef transColor(const std::string &name,const DictionaryRef &entry,const RGBAColor &); /// Builds a transitional text object - MapboxTransTextRef transText(const std::string &name,DictionaryRef entry,const std::string &str); + MapboxTransTextRef transText(const std::string &name,const DictionaryRef &entry,const std::string &str); /// Resolve transitionable color and opacity into a single color for the zoom /// If this returns nil, then the object shouldn't appear - RGBAColorRef resolveColor(MapboxTransColorRef color,MapboxTransDoubleRef opacity,double zoom,MBResolveColorType resolveMode); + RGBAColorRef resolveColor(const MapboxTransColorRef &color,const MapboxTransDoubleRef &opacity,double zoom,MBResolveColorType resolveMode); /// @brief Scale the color by the given opacity - RGBAColor color(RGBAColor color,double opacity); + static RGBAColor color(RGBAColor color,double opacity); /// @brief Check for and report an unsupported field - void unsupportedCheck(const char *field,const char *what,DictionaryRef styleEntry); + void unsupportedCheck(const char *field,const char *what,const DictionaryRef &styleEntry); /// Fetch a layer by name virtual MapboxVectorStyleLayerRef getLayer(const std::string &name); - + + /// Set the zoom slot if we've got continuous zoom going on + virtual int getZoomSlot() const override { return zoomSlot; } + /// Set the zoom slot if we've got continuous zoom going on - virtual void setZoomSlot(int zoomSlot) override; + virtual void setZoomSlot(int slot) override { zoomSlot = slot; } /// Get the background style, if any VectorStyleImplRef backgroundStyle(PlatformThreadInfo *inst) const override; @@ -310,16 +311,16 @@ class MapboxVectorStyleSetImpl : public VectorStyleDelegateImpl virtual SingleLabelRef makeSingleLabel(PlatformThreadInfo *inst,const std::string &text) = 0; /// Tie a selection ID to the given vector object - virtual void addSelectionObject(SimpleIdentity selectID,VectorObjectRef vecObj,ComponentObjectRef compObj) = 0; + virtual void addSelectionObject(SimpleIdentity selectID,const VectorObjectRef &vecObj,const ComponentObjectRef &compObj) = 0; /// Return the width of the given line of text - virtual double calculateTextWidth(PlatformThreadInfo *inInst,LabelInfoRef labelInfo,const std::string &testStr) = 0; + virtual double calculateTextWidth(PlatformThreadInfo *inInst,const LabelInfoRef &labelInfo,const std::string &testStr) = 0; /// Add a sprite sheet for use by the layers virtual void addSprites(MapboxVectorStyleSpritesRef newSprites); - + /// Create a local platform component object - virtual ComponentObjectRef makeComponentObject(PlatformThreadInfo *inst) = 0; + virtual ComponentObjectRef makeComponentObject(PlatformThreadInfo *inst, const Dictionary *desc = nullptr) = 0; public: Scene *scene; @@ -336,19 +337,19 @@ class MapboxVectorStyleSetImpl : public VectorStyleDelegateImpl std::vector layers; /// @brief Layers sorted by their ID - std::map layersByName; - + std::unordered_map layersByName; + /// Layers sorted by UUID - std::map layersByUUID; + std::unordered_map layersByUUID; /// @brief Layers sorted by source layer name - std::map > layersBySource; + std::unordered_multimap layersBySource; - VectorManager *vecManage; - WideVectorManager *wideVecManage; - MarkerManager *markerManage; - LabelManager *labelManage; - ComponentManager *compManage; + VectorManagerRef vecManage; + WideVectorManagerRef wideVecManage; + MarkerManagerRef markerManage; + LabelManagerRef labelManage; + ComponentManagerRef compManage; // ID's for the various programs SimpleIdentity screenMarkerProgramID; diff --git a/common/WhirlyGlobeLib/include/MapboxVectorStyleSymbol.h b/common/WhirlyGlobeLib/include/MapboxVectorStyleSymbol.h index ff4474a24d..8358a28818 100644 --- a/common/WhirlyGlobeLib/include/MapboxVectorStyleSymbol.h +++ b/common/WhirlyGlobeLib/include/MapboxVectorStyleSymbol.h @@ -33,7 +33,9 @@ typedef enum {MBTextTransNone,MBTextTransUppercase,MBTextTransLowercase} MapboxT class MapboxVectorSymbolLayout { public: - bool parse(PlatformThreadInfo *inst,MapboxVectorStyleSetImpl *styleSet,DictionaryRef styleEntry); + bool parse(PlatformThreadInfo *inst, + MapboxVectorStyleSetImpl *styleSet, + const DictionaryRef &styleEntry); /// How we place the symbol (at a point, or along a line) MapboxSymbolPlacement placement; @@ -74,7 +76,9 @@ class MapboxVectorSymbolLayout class MapboxVectorSymbolPaint { public: - bool parse(PlatformThreadInfo *inst,MapboxVectorStyleSetImpl *styleSet,DictionaryRef styleEntry); + bool parse(PlatformThreadInfo *inst, + MapboxVectorStyleSetImpl *styleSet, + const DictionaryRef &styleEntry); // Default text color MapboxTransColorRef textColor; @@ -94,27 +98,39 @@ class MapboxVectorLayerSymbol : public MapboxVectorStyleLayer MapboxVectorLayerSymbol(MapboxVectorStyleSetImpl *styleSet) : MapboxVectorStyleLayer(styleSet) { } virtual bool parse(PlatformThreadInfo *inst, - DictionaryRef styleEntry, - MapboxVectorStyleLayerRef refLayer, + const DictionaryRef &styleEntry, + const MapboxVectorStyleLayerRef &refLayer, int drawPriority) override; virtual void buildObjects(PlatformThreadInfo *inst, - std::vector &vecObjs, - VectorTileDataRef tileInfo) override; + const std::vector &vecObjs, + const VectorTileDataRef &tileInfo, + const Dictionary *desc) override; virtual void cleanup(PlatformThreadInfo *inst,ChangeSet &changes) override; public: - std::string breakUpText(PlatformThreadInfo *inst,const std::string &text,double textMaxWidth,LabelInfoRef labelInfo); - SingleLabelRef setupLabel(PlatformThreadInfo *inst,const Point2f &pt,LabelInfoRef labelInfo,MutableDictionaryRef attrs,VectorTileDataRef tileInfo); - Marker *setupMarker(PlatformThreadInfo *inst,const Point2f &pt,VectorObjectRef vecObj,MutableDictionaryRef attrs,ComponentObjectRef compObj,VectorTileDataRef tileInfo); + std::string breakUpText(PlatformThreadInfo *inst, + const std::string &text, + double textMaxWidth, + const LabelInfoRef &labelInfo); + SingleLabelRef setupLabel(PlatformThreadInfo *inst, + const Point2f &pt, + const LabelInfoRef &labelInfo, + const MutableDictionaryRef &attrs, + const VectorTileDataRef &tileInfo); + std::unique_ptr setupMarker(PlatformThreadInfo *inst, + const Point2f &pt, + const MutableDictionaryRef &attrs, + const VectorTileDataRef &tileInfo); MapboxVectorSymbolLayout layout; MapboxVectorSymbolPaint paint; /// If set, only one label with its text will be displayed. Sorted out by the layout manager. bool uniqueLabel; - std::string uuidField; + std::string uuidField; // UUID field for markers/labels (from style settings) + std::string repUUIDField; // UUID field for representations (from style layers) bool useZoomLevels; }; diff --git a/common/WhirlyGlobeLib/include/MapboxVectorTileParser.h b/common/WhirlyGlobeLib/include/MapboxVectorTileParser.h index d57ff678d7..3a599506cc 100644 --- a/common/WhirlyGlobeLib/include/MapboxVectorTileParser.h +++ b/common/WhirlyGlobeLib/include/MapboxVectorTileParser.h @@ -105,45 +105,68 @@ class MapboxVectorTileParser public: MapboxVectorTileParser(PlatformThreadInfo *inst,VectorStyleDelegateImplRef styleDelegate); virtual ~MapboxVectorTileParser(); - + /// If set, we'll parse into local coordinates as specified by the bounding box, rather than geo coords - bool localCoords; - + void setLocalCoords(bool b = true) { localCoords = b; } + /// Keep the vector objects around as we parse them - bool keepVectors; - + void setKeepVectors(bool b = true) { keepVectors = b; } + /// Parse everything, even if there's no style for it - bool parseAll; - - // Add a category for a particulary style ID - // These are used for sorting later on + void setParseAll(bool b = true) { parseAll = b; } + + /// Add a category for a particulary style ID + /// These are used for sorting later on void addCategory(const std::string &category,long long styleID); - - // Parse the vector tile and return a list of vectors. - // Returns false on failure. + + /// Parse the vector tile and return a list of vectors. + /// Returns false on failure or cancellation. virtual bool parse(PlatformThreadInfo *styleInst, RawData *rawData, VectorTileData *tileData, volatile bool *cancelBool); - - // The subclass calls the appropriate style to build component objects - // which are then returned in the VectorTileData + + /// The subclass calls the appropriate style to build component objects + /// which are then returned in the VectorTileData virtual void buildForStyle(PlatformThreadInfo *styleInst, long long styleID, - std::vector &vecObjs, - VectorTileDataRef data); - - // Only include features that have the given name and one of the values - void setUUIDs(const std::string &name,const std::set &uuids); - - // If set, we'll tack a debug label in the middle of the tile + const std::vector &vecObjs, + const VectorTileDataRef &data); + + /// Set the name of the uuid field. + /// When present, the value is set as the kMaplyUUID attribute on generated objects + void setUUIDName(const std::string &name); + + /// Only include features that have the given name and one of the values + void setAttributeFilter(const std::string &name,const std::set &values); + void setAttributeFilter(const std::string &name,std::set &&values); + + /// If set, we'll tack a debug label in the middle of the tile + void setDebugLabel(bool b = true) { debugLabel = b; } + + /// If set, we'll put an outline around the tile + void setDebugOutline(bool b = true) { debugOutline = b; } + + const VectorStyleDelegateImplRef &getStyleDelegate() const { return styleDelegate; } +protected: + /// If set, we'll parse into local coordinates as specified by the bounding box, rather than geo coords + bool localCoords; + + /// Keep the vector objects around as we parse them + bool keepVectors; + + /// Parse everything, even if there's no style for it + bool parseAll; + + /// If set, we'll tack a debug label in the middle of the tile bool debugLabel; - - // If set, we'll put an outline around the tile + + /// If set, we'll put an outline around the tile bool debugOutline; -public: - // Used for feature inclusion. Only keep the features that have this attribute and one of the UUIDs. std::string uuidName; - std::set uuidValues; - + + // Used for feature inclusion. Only keep the features that have this attribute and one of the values. + std::string filterName; + std::set filterValues; + VectorStyleDelegateImplRef styleDelegate; std::map styleCategories; }; diff --git a/common/WhirlyGlobeLib/include/MaplyVectorStyleC.h b/common/WhirlyGlobeLib/include/MaplyVectorStyleC.h index 9cbceabcc8..1c62253141 100644 --- a/common/WhirlyGlobeLib/include/MaplyVectorStyleC.h +++ b/common/WhirlyGlobeLib/include/MaplyVectorStyleC.h @@ -1,9 +1,8 @@ -/* -* MaplyVectorStyleC.h +/* MaplyVectorStyleC.h * WhirlyGlobe-MaplyComponent * * Created by Steve Gifford on 4/9/20. -* Copyright 2011-2020 mousebird consulting +* Copyright 2011-2021 mousebird consulting * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -15,7 +14,6 @@ * 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. -* */ #import "VectorObject.h" @@ -114,8 +112,8 @@ typedef std::shared_ptr VectorStyleImplRef; class VectorStyleDelegateImpl { public: - VectorStyleDelegateImpl() { } - virtual ~VectorStyleDelegateImpl() { } + VectorStyleDelegateImpl() = default; + virtual ~VectorStyleDelegateImpl() = default; /// Return the styles that apply to the given feature (attributes). virtual std::vector stylesForFeature(PlatformThreadInfo *inst, @@ -131,17 +129,20 @@ class VectorStyleDelegateImpl /// Return the style associated with the given UUID. virtual VectorStyleImplRef styleForUUID(PlatformThreadInfo *inst,long long uuid) = 0; - // Return a list of all the styles in no particular order. Needed for categories and indexing + /// Return a list of all the styles in no particular order. Needed for categories and indexing virtual std::vector allStyles(PlatformThreadInfo *inst) = 0; - // Return the style entry for the background, if any + /// Return the style entry for the background, if any virtual VectorStyleImplRef backgroundStyle(PlatformThreadInfo *inst) const = 0; /// Return the background color for a given zoom level virtual RGBAColorRef backgroundColor(PlatformThreadInfo *inst,double zoom) = 0; - + + /// Get the zoom slot, or -1 + virtual int getZoomSlot() const { return -1; } + /// Capture the zoom slot if you're going use it - virtual void setZoomSlot(int zoomSlot) { }; + virtual void setZoomSlot(int zoomSlot) { } }; typedef std::shared_ptr VectorStyleDelegateImplRef; @@ -154,18 +155,24 @@ class VectorStyleImpl VectorStyleImpl() { } virtual ~VectorStyleImpl() { } + virtual std::string getIdent() const { return std::string(); } + virtual std::string getType() const { return std::string(); } + /// Unique Identifier for this style virtual long long getUuid(PlatformThreadInfo *inst) = 0; /// Category used for sorting virtual std::string getCategory(PlatformThreadInfo *inst) = 0; - + // Note: This no longer really holds /// Set if this geometry is additive (e.g. sticks around) rather than replacement virtual bool geomAdditive(PlatformThreadInfo *inst) = 0; /// Construct objects related to this style based on the input data. - virtual void buildObjects(PlatformThreadInfo *inst, std::vector &vecObjs,VectorTileDataRef tileInfo) = 0; + virtual void buildObjects(PlatformThreadInfo *inst, + const std::vector &vecObjs, + const VectorTileDataRef &tileInfo, + const Dictionary *desc) = 0; }; } diff --git a/common/WhirlyGlobeLib/include/MaplyView.h b/common/WhirlyGlobeLib/include/MaplyView.h index 4c2de813cd..97a7b4ed92 100644 --- a/common/WhirlyGlobeLib/include/MaplyView.h +++ b/common/WhirlyGlobeLib/include/MaplyView.h @@ -78,7 +78,7 @@ class MapView : public WhirlyKit::View double maxHeightAboveSurface(); /// Set the location, but we may or may not run updates - void setLoc(WhirlyKit::Point3d &loc,bool runUpdates); + void setLoc(const WhirlyKit::Point3d &loc,bool runUpdates); /// Set the location we're looking from. Always runs updates void setLoc(WhirlyKit::Point3d newLoc); diff --git a/common/WhirlyGlobeLib/include/MarkerManager.h b/common/WhirlyGlobeLib/include/MarkerManager.h index 14f90569fe..2656c0f634 100644 --- a/common/WhirlyGlobeLib/include/MarkerManager.h +++ b/common/WhirlyGlobeLib/include/MarkerManager.h @@ -46,10 +46,10 @@ class MarkerSceneRep : public Identifiable ~MarkerSceneRep() { }; // Clear the contents out of the scene - void clearContents(SelectionManager *selectManager,LayoutManager *layoutManager,ChangeSet &changes,TimeInterval when); + void clearContents(SelectionManagerRef &selectManager,LayoutManagerRef &layoutManager,ChangeSet &changes,TimeInterval when); // Enable/disable marker related features - void enableContents(SelectionManager *selectManager,LayoutManager *layoutManager,bool enable,ChangeSet &changes); + void enableContents(SelectionManagerRef &selectManager,LayoutManagerRef &layoutManager,bool enable,ChangeSet &changes); SimpleIDSet drawIDs; // Drawables created for this SimpleIDSet selectIDs; // IDs used for selection @@ -65,7 +65,8 @@ class MarkerInfo : public BaseInfo public: MarkerInfo(bool screenObject); MarkerInfo(const Dictionary &,bool screenObject); - + virtual ~MarkerInfo() = default; + RGBAColor color; bool screenObject; float width,height; @@ -138,6 +139,11 @@ class Marker long orderBy; /// Passed through the system as a unique identifier std::string uniqueID; + + // If set, we'll draw an outline to the mask target + WhirlyKit::SimpleIdentity maskID; + WhirlyKit::SimpleIdentity maskRenderTargetID; + /// A list of vertex attributes to apply to the marker SingleVertexAttributeSet vertexAttrs; @@ -169,9 +175,11 @@ class MarkerManager : public SceneManager virtual void setScene(Scene *inScene); protected: - std::mutex markerLock; /// Resources associated with given markers MarkerSceneRepSet markerReps; + /// We route the mask polygons to this program, if there are any + SimpleIdentity maskProgID; }; +typedef std::shared_ptr MarkerManagerRef; } diff --git a/common/WhirlyGlobeLib/include/OverlapHelper.h b/common/WhirlyGlobeLib/include/OverlapHelper.h index d2f9ba0dc6..fc537f071b 100644 --- a/common/WhirlyGlobeLib/include/OverlapHelper.h +++ b/common/WhirlyGlobeLib/include/OverlapHelper.h @@ -44,7 +44,13 @@ class OverlapHelper OverlapHelper(const Mbr &mbr,int sizeX,int sizeY); // Try to add an object. Might fail (kind of the whole point). - bool addObject(const Point2dVector &pts); + bool addCheckObject(const Point2dVector &pts); + + // See if there's an object in the way + bool checkObject(const Point2dVector &pts); + + // Force an object in no matter what + void addObject(const Point2dVector &pts); protected: // Object and its bounds @@ -71,7 +77,7 @@ class ClusterHelper ClusterHelper(const Mbr &mbr,int sizeX, int sizeY, float resScale, const Point2d &clusterMarkerSize); // Add an object, possibly forming a group - void addObject(LayoutObjectEntry *objEntry,const Point2dVector pts); + void addObject(LayoutObjectEntry *objEntry,const Point2dVector &pts); // Deal with cluster to cluster overlap void resolveClusters(); diff --git a/common/WhirlyGlobeLib/include/ParticleSystemManager.h b/common/WhirlyGlobeLib/include/ParticleSystemManager.h index 6ee06f6699..f89c0633f9 100644 --- a/common/WhirlyGlobeLib/include/ParticleSystemManager.h +++ b/common/WhirlyGlobeLib/include/ParticleSystemManager.h @@ -117,8 +117,8 @@ class ParticleSystemManager : public SceneManager void setUniformBlock(const SimpleIDSet &partSysIDs,const RawDataRef &uniBlock,int bufferID,ChangeSet &changes); protected: - std::mutex partSysLock; ParticleSystemSceneRepSet sceneReps; }; +typedef std::shared_ptr ParticleSystemManagerRef; } diff --git a/common/WhirlyGlobeLib/include/QuadImageFrameLoader.h b/common/WhirlyGlobeLib/include/QuadImageFrameLoader.h index 39a34408d2..dabd931b80 100644 --- a/common/WhirlyGlobeLib/include/QuadImageFrameLoader.h +++ b/common/WhirlyGlobeLib/include/QuadImageFrameLoader.h @@ -337,6 +337,7 @@ class QuadImageFrameLoader : public QuadTileBuilderDelegate, public ActiveModel /// Set loading mode to Broad (load lowest level first) and Narrow (load current frame first) void setLoadMode(LoadMode newMode); + LoadMode getLoadMode(); /// True if there's loading going on, false if it's settled bool getLoadingStatus(); @@ -365,7 +366,7 @@ class QuadImageFrameLoader : public QuadTileBuilderDelegate, public ActiveModel QuadDisplayControllerNew *getController(); /// Color for polygons created during loading - void setColor(RGBAColor &inColor,ChangeSet *changes); + void setColor(const RGBAColor &inColor,ChangeSet *changes); const RGBAColor &getColor(); /// Render target for the geometry being created @@ -394,7 +395,7 @@ class QuadImageFrameLoader : public QuadTileBuilderDelegate, public ActiveModel void setDrawPriorityPerLevel(int newPrior); // What part of the animation we're displaying - void setCurFrame(int focusID,double curFrame); + void setCurFrame(PlatformThreadInfo *threadInfo, int focusID, double curFrame); double getCurFrame(int focusID); // Need to know how we're loading the tiles to calculate the render state @@ -420,6 +421,9 @@ class QuadImageFrameLoader : public QuadTileBuilderDelegate, public ActiveModel /// Reload matching tiles in the given frame (or everything) virtual void reload(PlatformThreadInfo *threadInfo,int frame,const Mbr *bound,int boundCount,ChangeSet &changes); + + /// Recalculate all the frame/tile priorities + virtual void updatePriorities(PlatformThreadInfo *threadInfo); /// **** QuadTileBuilderDelegate methods **** @@ -518,7 +522,7 @@ class QuadImageFrameLoader : public QuadTileBuilderDelegate, public ActiveModel // Run on the layer thread. Merge the loaded tile into the data. virtual void mergeLoadedTile(PlatformThreadInfo *threadInfo,QuadLoaderReturn *loadReturn,ChangeSet &changes); - ComponentManager *compManager; + ComponentManagerRef compManager; protected: // Return a set of the active frames diff --git a/common/WhirlyGlobeLib/include/QuadSamplingParams.h b/common/WhirlyGlobeLib/include/QuadSamplingParams.h index 76660c4922..019881169c 100644 --- a/common/WhirlyGlobeLib/include/QuadSamplingParams.h +++ b/common/WhirlyGlobeLib/include/QuadSamplingParams.h @@ -43,7 +43,7 @@ class SamplingParams /// Bounding box for the coordinate system MbrD coordBounds; - /// Min zoom level for sampling + /// Min zoom level for sampling. Don't set this to anything other than 0 or 1 int minZoom; /// Max zoom level for sampling int maxZoom; diff --git a/common/WhirlyGlobeLib/include/RawData.h b/common/WhirlyGlobeLib/include/RawData.h index da64c6bfd4..e62738191e 100644 --- a/common/WhirlyGlobeLib/include/RawData.h +++ b/common/WhirlyGlobeLib/include/RawData.h @@ -60,6 +60,7 @@ class RawDataWrapper : public RawData const unsigned char *data; unsigned int len; }; +typedef std::shared_ptr RawDataWrapperRef; // Wrapper on top of a raw data object for reading more structured data class RawDataReader diff --git a/common/WhirlyGlobeLib/include/RawPNGImage.h b/common/WhirlyGlobeLib/include/RawPNGImage.h new file mode 100644 index 0000000000..e7116023f6 --- /dev/null +++ b/common/WhirlyGlobeLib/include/RawPNGImage.h @@ -0,0 +1,36 @@ +/* + * RawPNGImage.h + * WhirlyGlobeLib + * + * Created by Steve Gifford on 12/3/20. + * Copyright 2011-2020 mousebird consulting + * + * 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. + * + */ + +#import + +namespace WhirlyKit +{ + +/** + Pulls the raw data out of a PNG image. + Returns NULL on failure, check the err value. + */ +extern unsigned char *RawPNGImageLoaderInterpreter(unsigned int &width,unsigned int &height, + const unsigned char *data,size_t length, + const std::vector &valueMap, + int &byteWidth, + unsigned int &err); + +} diff --git a/common/WhirlyGlobeLib/include/Scene.h b/common/WhirlyGlobeLib/include/Scene.h index 7296252aa9..8e67c24f04 100644 --- a/common/WhirlyGlobeLib/include/Scene.h +++ b/common/WhirlyGlobeLib/include/Scene.h @@ -36,8 +36,6 @@ namespace WhirlyKit class SceneRenderer; class Scene; class SubTexture; -class ScreenSpaceGenerator; -class ViewPlacementGenerator; class FontTextureManager; typedef std::shared_ptr FontTextureManagerRef; class RenderSetupInfo; @@ -52,7 +50,7 @@ class AddTextureReq : public ChangeRequest AddTextureReq(TextureBase *tex) { texRef = TextureBaseRef(tex); } AddTextureReq(const TextureBaseRef &texRef) : texRef(texRef) { } /// If the texture hasn't been added to the renderer, clean it up. - ~AddTextureReq(); + virtual ~AddTextureReq(); /// Texture creation generally wants a flush virtual bool needsFlush() { return true; } @@ -64,7 +62,7 @@ class AddTextureReq : public ChangeRequest void execute(Scene *scene,SceneRenderer *renderer,View *view); /// Only use this if you've thought it out - TextureBase *getTex(); + TextureBase *getTex() const; protected: TextureBaseRef texRef; @@ -95,7 +93,7 @@ class AddDrawableReq : public ChangeRequest /// Passing by ref means don't worry about it AddDrawableReq(const DrawableRef &drawRef) : drawRef(drawRef) { } /// If the drawable wasn't used, delete it - ~AddDrawableReq(); + virtual ~AddDrawableReq(); /// Drawable creation generally wants a flush virtual bool needsFlush() { return true; } @@ -132,7 +130,7 @@ class AddProgramReq : public ChangeRequest public: // Construct with the program to add AddProgramReq(const std::string &sceneName,ProgramRef prog) : sceneName(sceneName), program(prog) { } - ~AddProgramReq() { } + virtual ~AddProgramReq() = default; /// Remove from the renderer. Never call this. void execute(Scene *scene,SceneRenderer *renderer,View *view); @@ -157,6 +155,7 @@ class RemProgramReq : public ChangeRequest }; // Set a program value, but go through the scene to do it +#if 0 // not implemented class SetProgramValueReq : public ChangeRequest { public: @@ -171,7 +170,8 @@ class SetProgramValueReq : public ChangeRequest std::string u_name; float u_val; }; - +#endif + /// Run a block of code when this change request is executed /// We use this to merge data on the main thread after other requests have been executed. class RunBlockReq : public ChangeRequest @@ -181,7 +181,7 @@ class RunBlockReq : public ChangeRequest // Set up with the function to run RunBlockReq(BlockFunc newFunc); - virtual ~RunBlockReq(); + virtual ~RunBlockReq() = default; // This is probably adding to the change requests and so needs to run first bool needPreExecute() { return true; } @@ -220,25 +220,28 @@ typedef std::map ProgramSet; class SceneManager { public: - SceneManager() : scene(NULL), renderer(NULL) { } - virtual ~SceneManager() { }; + SceneManager(); + virtual ~SceneManager() = default; /// Set (or reset) the current renderer - virtual void setRenderer(SceneRenderer *inRenderer) { renderer = inRenderer; } + virtual void setRenderer(SceneRenderer *inRenderer); /// Set the scene we're part of - virtual void setScene(Scene *inScene) { scene = inScene; } + virtual void setScene(Scene *inScene); /// Return the scene this is part of - Scene *getScene() { return scene; } + Scene *getScene() const; /// Return the renderer - SceneRenderer *getSceneRenderer() { return renderer; } + SceneRenderer *getSceneRenderer() const; protected: + std::mutex lock; + Scene *scene; SceneRenderer *renderer; }; +typedef std::shared_ptr SceneManagerRef; /** This is the top level scene object for WhirlyKit. It keeps track of the drawables and the change requests, which @@ -255,7 +258,7 @@ class Scene : public DelayedDeletable /// Return the coordinate system adapter we're using. /// You can get the coordinate system we're using from that. - CoordSystemDisplayAdapter *getCoordAdapter(); + CoordSystemDisplayAdapter *getCoordAdapter() const; /// Add a single change request. You can call this from any thread, it locks. /// If you have more than one, don't iterate, use the other version. @@ -272,7 +275,7 @@ class Scene : public DelayedDeletable int preProcessChanges(View *view,SceneRenderer *renderer,TimeInterval now); /// True if there are pending updates - bool hasChanges(TimeInterval now); + bool hasChanges(TimeInterval now) const; /// Add sub texture mappings. /// These are mappings from images to parts of texture atlases. @@ -289,7 +292,7 @@ class Scene : public DelayedDeletable /// Return a sub texture by ID. The idea being we can use these /// the same way we use full texture IDs. - SubTexture getSubTexture(SimpleIdentity subTexId); + SubTexture getSubTexture(SimpleIdentity subTexId) const; /// Add a drawable to the scene. /// A subclass can override this to control how this interacts with cullabes. @@ -297,7 +300,7 @@ class Scene : public DelayedDeletable virtual void addDrawable(DrawableRef drawable); /// Look for a Drawable by ID - DrawableRef getDrawable(SimpleIdentity drawId); + DrawableRef getDrawable(SimpleIdentity drawId) const; /// Remove a drawable from the scene virtual void remDrawable(DrawableRef drawable); @@ -306,7 +309,7 @@ class Scene : public DelayedDeletable virtual void addTexture(TextureBaseRef texRef); /// Look for a Texture by ID - TextureBaseRef getTexture(SimpleIdentity texId); + TextureBaseRef getTexture(SimpleIdentity texId) const; /// Remove a texture by ID. Return true if it was there virtual bool removeTexture(SimpleIdentity texID); @@ -315,45 +318,60 @@ class Scene : public DelayedDeletable void setRenderer(SceneRenderer *renderer); /// Return the given manager. This is thread safe; - SceneManager *getManager(const char *name); + SceneManagerRef getManager(const char *name) { return getManager(std::string(name)); } + /// Return the given manager. This is thread safe; + SceneManagerRef getManager(const std::string &name); /// This one can only be called during scene initialization - SceneManager *getManagerNoLock(const char *name); - + SceneManagerRef getManagerNoLock(const char *name) { return getManagerNoLock(std::string(name)); } + /// This one can only be called during scene initialization + SceneManagerRef getManagerNoLock(const std::string &name); + + template + std::shared_ptr getManager(const char *name) { return std::dynamic_pointer_cast(getManager(name)); } + template + std::shared_ptr getManager(const std::string &name) { return std::dynamic_pointer_cast(getManager(name)); } + template + std::shared_ptr getManagerNoLock(const char *name) { return std::dynamic_pointer_cast(getManagerNoLock(name)); } + template + std::shared_ptr getManagerNoLock(const std::string &name) { return std::dynamic_pointer_cast(getManagerNoLock(name)); } + /// Add the given manager. The scene is now responsible for deletion. This is thread safe. - void addManager(const char *name,SceneManager *manager); - + void addManager(const char *name,const SceneManagerRef &manager) { addManager(std::string(name), manager); } + /// Add the given manager. The scene is now responsible for deletion. This is thread safe. + void addManager(const std::string &name,const SceneManagerRef &manager); + /// Add an active model. Only call this on the main thread. void addActiveModel(ActiveModelRef); /// Remove an active model (if it's in here). Only call this on the main thread. - void removeActiveModel(ActiveModelRef); + void removeActiveModel(const ActiveModelRef &); /// Return a dispatch queue that we can use for... stuff. /// The idea here is we'll wait for these to drain when we tear down. // dispatch_queue_t getDispatchQueue() { return dispatchQueue; } // Return all the drawables in a list. Only call this on the main thread. - const std::vector getDrawables(); + const std::vector getDrawables() const; // Used for offline frame by frame rendering void setCurrentTime(TimeInterval newTime); // In general, this is just the system time. // But in offline render mode, we control this carefully - TimeInterval getCurrentTime(); + TimeInterval getCurrentTime() const; // Base time at system initialization - TimeInterval getBaseTime(); + TimeInterval getBaseTime() const; // Used to track overlaps at the edges of a viewable area void addLocalMbr(const Mbr &localMbr); /// Dump out stats on what is currently in the scene. /// Use this sparingly, as it writes to the log. - void dumpStats(); + void dumpStats() const; /// Tear down renderer related assets - virtual void teardown() = 0; + virtual void teardown(PlatformThreadInfo*) = 0; /// Mark any changed programs as acknowledged (used in Metal) void markProgramsUnchanged(); @@ -368,7 +386,7 @@ class Scene : public DelayedDeletable void setZoomSlotValue(int zoomSlot,float zoom); /// Return the given zoom slot value - float getZoomSlotValue(int zoomSlot); + float getZoomSlotValue(int zoomSlot) const; /// Copy all the zoom slots into a destination array void copyZoomSlots(float *dest); @@ -381,7 +399,7 @@ class Scene : public DelayedDeletable Program *getProgram(SimpleIdentity programId); /// Remove the given program by ID (ours, not OpenGL's) - void removeProgram(SimpleIdentity progId,RenderTeardownInfoRef teardown); + void removeProgram(SimpleIdentity progId,const RenderTeardownInfoRef &teardown); /// Look for a program by its name (last to first) Program *findProgramByName(const std::string &name); @@ -391,15 +409,16 @@ class Scene : public DelayedDeletable /// Return the active models (main thread only) std::vector &getActiveModels() { return activeModels; } - + const std::vector &getActiveModels() const { return activeModels; } + /// Return the number of change requests - int getNumChangeRequests(); + int getNumChangeRequests() const; /// Set up the font texture manager. Don't call this yourself. - void setFontTextureManager(FontTextureManagerRef newManager); + void setFontTextureManager(const FontTextureManagerRef &newManager); /// Returns the font texture manager, which is thread safe - FontTextureManager *getFontTextureManager() { return fontTextureManager.get(); } + FontTextureManagerRef getFontTextureManager() const { return fontTextureManager; } protected: /// Don't be calling this @@ -407,8 +426,8 @@ class Scene : public DelayedDeletable /// Passed around to setup and teardown renderer assets const RenderSetupInfo *setupInfo; - - std::mutex coordAdapterLock; + + mutable std::mutex coordAdapterLock; /// The coordinate system display adapter converts from the local space /// to display coordinates. CoordSystemDisplayAdapter *coordAdapter; @@ -417,7 +436,7 @@ class Scene : public DelayedDeletable std::vector activeModels; /// All the drawables we've been handed, sorted by ID - std::mutex drawablesLock; + mutable std::mutex drawablesLock; DrawableRefSet drawables; typedef std::unordered_map TextureRefSet; @@ -425,30 +444,30 @@ class Scene : public DelayedDeletable TextureRefSet textures; /// Mutex for accessing textures - std::mutex textureLock; - - std::mutex changeRequestLock; + mutable std::mutex textureLock; + + mutable std::mutex changeRequestLock; /// We keep a list of change requests to execute /// This can be accessed in multiple threads, so we lock it ChangeSet changeRequests; SortedChangeSet timedChangeRequests; - - std::mutex subTexLock; + + mutable std::mutex subTexLock; typedef std::set SubTextureSet; /// Mappings from images to parts of texture atlases SubTextureSet subTextureMap; /// Lock for accessing managers - std::mutex managerLock; + mutable std::mutex managerLock; /// Managers for various functionality - std::map managers; + std::map managers; /// Lock for accessing programs - std::mutex programLock; + mutable std::mutex programLock; // Sampling layers will set these to talk to shaders - std::mutex zoomSlotLock; + mutable std::mutex zoomSlotLock; float zoomSlots[MaplyMaxZoomSlots]; protected: diff --git a/common/WhirlyGlobeLib/include/SceneGLES.h b/common/WhirlyGlobeLib/include/SceneGLES.h index b2577eaa9c..e1535c4987 100644 --- a/common/WhirlyGlobeLib/include/SceneGLES.h +++ b/common/WhirlyGlobeLib/include/SceneGLES.h @@ -38,7 +38,7 @@ class SceneGLES : public Scene /// Explicitly tear everything down in OpenGL ES. /// We're assuming the context has been set. - virtual void teardown(); + virtual void teardown(PlatformThreadInfo*); /// Get the renderer's buffer/texture ID manager. /// You can use this on any thread. The calls are protected. diff --git a/common/WhirlyGlobeLib/include/SceneRenderer.h b/common/WhirlyGlobeLib/include/SceneRenderer.h index a1a8adb1f3..cac5894726 100644 --- a/common/WhirlyGlobeLib/include/SceneRenderer.h +++ b/common/WhirlyGlobeLib/include/SceneRenderer.h @@ -324,6 +324,9 @@ class SceneRenderer : public DelayedDeletable /// Construct a renderer-specific dynamic texture virtual DynamicTextureRef makeDynamicTexture(const std::string &name) const = 0; + + /// Maps name IDs to slots (slots are just used by Metal) + virtual int getSlotForNameID(SimpleIdentity nameID); /// The pixel width of the CAEAGLLayer. int framebufferWidth; @@ -409,6 +412,9 @@ class SceneRenderer : public DelayedDeletable // Everything torn down until the next frame RenderTeardownInfoRef teardownInfo; + + // Map Name IDs to slots (when using Metal) + std::map slotMap; }; typedef std::shared_ptr SceneRendererRef; diff --git a/common/WhirlyGlobeLib/include/ScreenSpaceBuilder.h b/common/WhirlyGlobeLib/include/ScreenSpaceBuilder.h index 1aa4b9d02b..b36224a6b3 100644 --- a/common/WhirlyGlobeLib/include/ScreenSpaceBuilder.h +++ b/common/WhirlyGlobeLib/include/ScreenSpaceBuilder.h @@ -32,6 +32,7 @@ namespace WhirlyKit { class ScreenSpaceObject; +class ScreenSpaceConvexGeometry; class ScreenSpaceObjectLocation; /** Screen space objects are used for both labels and markers. This builder @@ -65,12 +66,14 @@ class ScreenSpaceBuilder TimeInterval startEnable,endEnable; int64_t drawOrder; int drawPriority; + SimpleIdentity renderTargetID; float minVis,maxVis; int zoomSlot; double minZoomVis,maxZoomVis; bool motion; bool rotation; bool keepUpright; + bool hasMask; FloatExpressionInfoRef opacityExp; ColorExpressionInfoRef colorExp; FloatExpressionInfoRef scaleExp; @@ -92,6 +95,8 @@ class ScreenSpaceBuilder void setDrawOrder(int64_t drawOrder); /// Set the draw priority void setDrawPriority(int drawPriority); + /// Set the render target + void setRenderTarget(SimpleIdentity renderTargetID); /// Set the visibility range void setVisibility(float minVis,float maxVis); /// Set enable based on zoom @@ -118,7 +123,10 @@ class ScreenSpaceBuilder void addScreenObjects(std::vector &screenObjects); /// Add a single screen space object - void addScreenObject(const ScreenSpaceObject &screenObject); + void addScreenObject(const ScreenSpaceObject &screenObject, + const Point3d &worldLoc, + const std::vector &geoms, + const std::vector *places = nullptr); /// Return the drawables constructed. Caller responsible for deletion. void buildDrawables(std::vector &draws); @@ -165,6 +173,31 @@ class ScreenSpaceBuilder DrawableWrapMap drawables; std::vector fullDrawables; }; + +/// Represents a simple set of convex geometry +class ScreenSpaceConvexGeometry +{ +public: + ScreenSpaceConvexGeometry(); + + /// Texture ID used for just this object + std::vector texIDs; + /// Program ID used to render this geometry + SimpleIdentity progID; + /// Color for the geometry + RGBAColor color; + /// Draw order + int64_t drawOrder; + /// Draw priority + int drawPriority; + /// Render target + SimpleIdentity renderTargetID; + /// Vertex attributes applied to this piece of geometry + SingleVertexAttributeSet vertexAttrs; + + Point2dVector coords; + std::vector texCoords; +}; /** Keeps track of the basic information about a screen space object. */ @@ -180,30 +213,7 @@ class ScreenSpaceObject : public Identifiable ScreenSpaceObject(); ScreenSpaceObject(SimpleIdentity theId); virtual ~ScreenSpaceObject(); - - /// Represents a simple set of convex geometry - class ConvexGeometry - { - public: - ConvexGeometry(); - - /// Texture ID used for just this object - std::vector texIDs; - /// Program ID used to render this geometry - SimpleIdentity progID; - /// Color for the geometry - RGBAColor color; - /// Draw order - int64_t drawOrder; - /// Draw priority - int drawPriority; - /// Vertex attributes applied to this piece of geometry - SingleVertexAttributeSet vertexAttrs; - Point2dVector coords; - std::vector texCoords; - }; - /// Center of the object in world coordinates void setWorldLoc(const Point3d &worldLoc); Point3d getWorldLoc(); @@ -225,6 +235,7 @@ class ScreenSpaceObject : public Identifiable int64_t getDrawOrder() const; void setDrawPriority(int drawPriority); int getDrawPriority() const; + void setRenderTarget(SimpleIdentity renderTargetID); void setKeepUpright(bool keepUpright); void setRotation(double rotation); double getRotation() const { return rotation; } @@ -234,8 +245,9 @@ class ScreenSpaceObject : public Identifiable void setPeriod(TimeInterval period); void setOrderBy(long orderBy); - void addGeometry(const ConvexGeometry &geom); - std::vector getGeometry() const { return geometry; } + void addGeometry(const ScreenSpaceConvexGeometry &geom); + void addGeometry(const std::vector &geom); + std::vector getGeometry() const { return geometry; } // Get a program ID either from the drawable state or geometry SimpleIdentity getTypicalProgramID(); @@ -250,7 +262,7 @@ class ScreenSpaceObject : public Identifiable long orderBy; bool keepUpright; ScreenSpaceBuilder::DrawableState state; - std::vector geometry; + std::vector geometry; }; /** We use the screen space object location to communicate where diff --git a/common/WhirlyGlobeLib/include/ScreenSpaceDrawableBuilder.h b/common/WhirlyGlobeLib/include/ScreenSpaceDrawableBuilder.h index e7e097f629..b280acdb20 100644 --- a/common/WhirlyGlobeLib/include/ScreenSpaceDrawableBuilder.h +++ b/common/WhirlyGlobeLib/include/ScreenSpaceDrawableBuilder.h @@ -1,9 +1,8 @@ -/* - * ScreenSpaceDrawable.h +/* ScreenSpaceDrawable.h * WhirlyGlobeLib * * Created by Steve Gifford on 8/24/14. - * Copyright 2011-2019 mousebird consulting. + * Copyright 2011-2021 mousebird consulting. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -15,7 +14,6 @@ * 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. - * */ #import "BasicDrawableBuilder.h" @@ -27,7 +25,7 @@ namespace WhirlyKit // Modifies the uniform values of a given shader right before the // screenspace's Basic Drawables are rendered -class ScreenSpaceTweaker : public DrawableTweaker +class ScreenSpaceTweaker : public BasicDrawableTweaker { public: virtual void tweakForFrame(Drawable *inDraw,RendererFrameInfo *frameInfo) = 0; @@ -46,8 +44,8 @@ class ScreenSpaceDrawableBuilder : virtual public BasicDrawableBuilder // Construct with or without motion support ScreenSpaceDrawableBuilder(); - virtual void Init(bool hasMotion,bool hasRotation, bool buildAnyway = false); - + virtual void ScreenSpaceInit(bool hasMotion,bool hasRotation,bool buildAnyway = false); + // If we've got a rotation, we set this to keep the image facing upright // probably because it's text. void setKeepUpright(bool keepUpright); @@ -70,13 +68,13 @@ class ScreenSpaceDrawableBuilder : virtual public BasicDrawableBuilder // Apply a scale expression void setScaleExpression(FloatExpressionInfoRef scale); - - // Tweaker runs before we draw and we need different versions for the renderers - virtual ScreenSpaceTweaker *makeTweaker() = 0; - - void setupTweaker(BasicDrawable *theDraw); - + + virtual void setupTweaker(const DrawableTweakerRef &inTweaker) const override; + protected: + // Call ScreenSpaceInit instead + virtual void Init() override { BasicDrawableBuilder::Init(); } + bool motion,rotation; bool keepUpright; int offsetIndex; diff --git a/common/WhirlyGlobeLib/include/ScreenSpaceDrawableBuilderGLES.h b/common/WhirlyGlobeLib/include/ScreenSpaceDrawableBuilderGLES.h index 51acd618e7..a46abe9963 100644 --- a/common/WhirlyGlobeLib/include/ScreenSpaceDrawableBuilderGLES.h +++ b/common/WhirlyGlobeLib/include/ScreenSpaceDrawableBuilderGLES.h @@ -1,9 +1,8 @@ -/* - * ScreenSpaceDrawableBuilderGLES.h +/* ScreenSpaceDrawableBuilderGLES.h * WhirlyGlobeLib * * Created by Steve Gifford on 5/14/19. - * Copyright 2011-2019 mousebird consulting + * Copyright 2011-2021 mousebird consulting * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -15,7 +14,6 @@ * 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. - * */ #import "ScreenSpaceDrawableBuilder.h" @@ -37,10 +35,9 @@ ProgramGLES *BuildScreenSpace2DProgramGLES(const std::string &name,SceneRenderer ProgramGLES *BuildScreenSpaceMotion2DProgramGLES(const std::string &name,SceneRenderer *render); /// The OpenGL version sets uniforms -class ScreenSpaceTweakerGLES : public ScreenSpaceTweaker +struct ScreenSpaceTweakerGLES : public ScreenSpaceTweaker { -public: - void tweakForFrame(Drawable *inDraw,RendererFrameInfo *frameInfo); + virtual void tweakForFrame(Drawable *inDraw,RendererFrameInfo *frameInfo) override; }; /** OpenGL version of ScreenSpaceDrawable Builder @@ -50,12 +47,15 @@ class ScreenSpaceDrawableBuilderGLES : virtual public BasicDrawableBuilderGLES, public: ScreenSpaceDrawableBuilderGLES(const std::string &name,Scene *scene); - virtual int addAttribute(BDAttributeDataType dataType,StringIdentity nameID,int slot = -1,int numThings = -1); + virtual int addAttribute(BDAttributeDataType dataType,StringIdentity nameID,int slot = -1,int numThings = -1) override; - virtual ScreenSpaceTweaker *makeTweaker(); - /// Fill out and return the drawable - virtual BasicDrawableRef getDrawable(); + virtual BasicDrawableRef getDrawable() override; + + virtual DrawableTweakerRef makeTweaker() const override; + + virtual void setupTweaker(BasicDrawable &draw) const override { BasicDrawableBuilder::setupTweaker(draw); } + virtual void setupTweaker(const DrawableTweakerRef &inTweaker) const override { BasicDrawableBuilderGLES::setupTweaker(inTweaker); } }; - + } diff --git a/common/WhirlyGlobeLib/include/SelectionManager.h b/common/WhirlyGlobeLib/include/SelectionManager.h index a320910e65..3283d5164c 100644 --- a/common/WhirlyGlobeLib/include/SelectionManager.h +++ b/common/WhirlyGlobeLib/include/SelectionManager.h @@ -298,7 +298,6 @@ class SelectionManager : public SceneManager // Internal object picking method void pickObjects(Point2f touchPt,float maxDist,ViewStateRef viewState,bool multi,std::vector &selObjs); - std::mutex mutex; Scene *scene; /// The selectable objects themselves WhirlyKit::RectSelectable3DSet rect3Dselectables; @@ -309,5 +308,6 @@ class SelectionManager : public SceneManager WhirlyKit::LinearSelectableSet linearSelectables; WhirlyKit::BillboardSelectableSet billboardSelectables; }; +typedef std::shared_ptr SelectionManagerRef; } diff --git a/common/WhirlyGlobeLib/include/ShapeDrawableBuilder.h b/common/WhirlyGlobeLib/include/ShapeDrawableBuilder.h index 3503565739..a07109cd0f 100644 --- a/common/WhirlyGlobeLib/include/ShapeDrawableBuilder.h +++ b/common/WhirlyGlobeLib/include/ShapeDrawableBuilder.h @@ -37,6 +37,7 @@ class ShapeInfo : public BaseInfo ShapeInfo(); ShapeInfo(const Dictionary &); + virtual ~ShapeInfo() = default; public: RGBAColor color; diff --git a/common/WhirlyGlobeLib/include/ShapeManager.h b/common/WhirlyGlobeLib/include/ShapeManager.h index 1b32cf1ed2..ea19c1b592 100644 --- a/common/WhirlyGlobeLib/include/ShapeManager.h +++ b/common/WhirlyGlobeLib/include/ShapeManager.h @@ -39,10 +39,10 @@ class ShapeSceneRep : public Identifiable ~ShapeSceneRep(){}; // Enable/disable the contents - void enableContents(WhirlyKit::SelectionManager *selectManager,bool enable,ChangeSet &changes); + void enableContents(WhirlyKit::SelectionManagerRef &selectManager,bool enable,ChangeSet &changes); // Clear the contents out of the scene - void clearContents(WhirlyKit::SelectionManager *selectManager,ChangeSet &changes,TimeInterval when); + void clearContents(WhirlyKit::SelectionManagerRef &selectManager,ChangeSet &changes,TimeInterval when); SimpleIDSet drawIDs; // Drawables created for this SimpleIDSet selectIDs; // IDs in the selection layer @@ -61,7 +61,7 @@ class Shape Shape(); virtual ~Shape(); - virtual void makeGeometryWithBuilder(WhirlyKit::ShapeDrawableBuilder *regBuilder, WhirlyKit::ShapeDrawableBuilderTri *triBuilder, WhirlyKit::Scene *scene, SelectionManager *selectManager, ShapeSceneRep *sceneRep); + virtual void makeGeometryWithBuilder(WhirlyKit::ShapeDrawableBuilder *regBuilder, WhirlyKit::ShapeDrawableBuilderTri *triBuilder, WhirlyKit::Scene *scene, SelectionManagerRef &selectManager, ShapeSceneRep *sceneRep); virtual Point3d displayCenter(CoordSystemDisplayAdapter *coordAdapter, const ShapeInfo &shapeInfo); public: @@ -81,7 +81,7 @@ class Circle : public Shape { Circle(); virtual ~Circle(); - virtual void makeGeometryWithBuilder(WhirlyKit::ShapeDrawableBuilder *regBuilder, WhirlyKit::ShapeDrawableBuilderTri *triBuilder, WhirlyKit::Scene *scene, SelectionManager *selectManager, ShapeSceneRep *sceneRep); + virtual void makeGeometryWithBuilder(WhirlyKit::ShapeDrawableBuilder *regBuilder, WhirlyKit::ShapeDrawableBuilderTri *triBuilder, WhirlyKit::Scene *scene, SelectionManagerRef &selectManager, ShapeSceneRep *sceneRep); virtual Point3d displayCenter(CoordSystemDisplayAdapter *coordAdapter, const ShapeInfo &shapeInfo); public: @@ -104,7 +104,7 @@ class Sphere : public Shape Sphere(); virtual ~Sphere(); - virtual void makeGeometryWithBuilder(WhirlyKit::ShapeDrawableBuilder *regBuilder, WhirlyKit::ShapeDrawableBuilderTri *triBuilder, WhirlyKit::Scene *scene, SelectionManager *selectManager, ShapeSceneRep *sceneRep); + virtual void makeGeometryWithBuilder(WhirlyKit::ShapeDrawableBuilder *regBuilder, WhirlyKit::ShapeDrawableBuilderTri *triBuilder, WhirlyKit::Scene *scene, SelectionManagerRef &selectManager, ShapeSceneRep *sceneRep); virtual Point3d displayCenter(CoordSystemDisplayAdapter *coordAdapter, const ShapeInfo &shapeInfo); public: @@ -123,7 +123,7 @@ class Cylinder : public Shape Cylinder(); virtual ~Cylinder(); - virtual void makeGeometryWithBuilder(WhirlyKit::ShapeDrawableBuilder *regBuilder, WhirlyKit::ShapeDrawableBuilderTri *triBuilder, WhirlyKit::Scene *scene, SelectionManager *selectManager, ShapeSceneRep *sceneRep); + virtual void makeGeometryWithBuilder(WhirlyKit::ShapeDrawableBuilder *regBuilder, WhirlyKit::ShapeDrawableBuilderTri *triBuilder, WhirlyKit::Scene *scene, SelectionManagerRef &selectManager, ShapeSceneRep *sceneRep); virtual Point3d displayCenter(CoordSystemDisplayAdapter *coordAdapter, const ShapeInfo &shapeInfo); public: @@ -151,7 +151,7 @@ class Linear : public Shape Linear(); virtual ~Linear(); - virtual void makeGeometryWithBuilder(WhirlyKit::ShapeDrawableBuilder *regBuilder, WhirlyKit::ShapeDrawableBuilderTri *triBuilder, WhirlyKit::Scene *scene, SelectionManager *selectManager, ShapeSceneRep *sceneRep); + virtual void makeGeometryWithBuilder(WhirlyKit::ShapeDrawableBuilder *regBuilder, WhirlyKit::ShapeDrawableBuilderTri *triBuilder, WhirlyKit::Scene *scene, SelectionManagerRef &selectManager, ShapeSceneRep *sceneRep); virtual Point3d displayCenter(CoordSystemDisplayAdapter *coordAdapter, const ShapeInfo &shapeInfo); public: @@ -174,7 +174,7 @@ class Extruded : public Shape Extruded(); virtual ~Extruded(); - virtual void makeGeometryWithBuilder(WhirlyKit::ShapeDrawableBuilder *regBuilder, WhirlyKit::ShapeDrawableBuilderTri *triBuilder, WhirlyKit::Scene *scene, SelectionManager *selectManager, ShapeSceneRep *sceneRep); + virtual void makeGeometryWithBuilder(WhirlyKit::ShapeDrawableBuilder *regBuilder, WhirlyKit::ShapeDrawableBuilderTri *triBuilder, WhirlyKit::Scene *scene, SelectionManagerRef &selectManager, ShapeSceneRep *sceneRep); virtual Point3d displayCenter(CoordSystemDisplayAdapter *coordAdapter, const ShapeInfo &shapeInfo); public: @@ -208,7 +208,7 @@ class Rectangle : public Shape void setTexIDs(std::vector inTexIDs) { texIDs = inTexIDs; } - virtual void makeGeometryWithBuilder(WhirlyKit::ShapeDrawableBuilder *regBuilder, WhirlyKit::ShapeDrawableBuilderTri *triBuilder, WhirlyKit::Scene *scene, SelectionManager *selectManager, ShapeSceneRep *sceneRep); + virtual void makeGeometryWithBuilder(WhirlyKit::ShapeDrawableBuilder *regBuilder, WhirlyKit::ShapeDrawableBuilderTri *triBuilder, WhirlyKit::Scene *scene, SelectionManagerRef &selectManager, ShapeSceneRep *sceneRep); virtual Point3d displayCenter(CoordSystemDisplayAdapter *coordAdapter, const ShapeInfo &shapeInfo); public: @@ -244,8 +244,8 @@ class ShapeManager : public SceneManager void setUniformBlock(const SimpleIDSet &shapeIDs,const RawDataRef &uniBlock,int bufferID,ChangeSet &changes); protected: - std::mutex shapeLock; ShapeSceneRepSet shapeReps; }; +typedef std::shared_ptr ShapeManagerRef; } diff --git a/common/WhirlyGlobeLib/include/SharedAttributes.h b/common/WhirlyGlobeLib/include/SharedAttributes.h index 4b52f044c9..4d2962aa05 100644 --- a/common/WhirlyGlobeLib/include/SharedAttributes.h +++ b/common/WhirlyGlobeLib/include/SharedAttributes.h @@ -3,7 +3,7 @@ * WhirlyGlobe-MaplyComponent * * Created by Steve Gifford on 9/19/12. - * Copyright 2011-2019 mousebird consulting + * Copyright 2011-2021 mousebird consulting * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -112,10 +112,12 @@ #define MaplyDefaultWideVectorShader WKString("Default Wide Vector") #define MaplyWideVectorExpShader WKString("Default Wide Vector with expressions") +#define MaplyWideVectorPerformanceShader WKString("Wide Vector performance") #define MaplyDefaultWideVectorGlobeShader WKString("Default Wide Vector Globe") #define MaplyScreenSpaceDefaultMotionShader WKString("Default Screenspace Motion") #define MaplyScreenSpaceDefaultShader WKString("Default Screenspace") +#define MaplyScreenSpaceMaskShader WKString("Screenspace mask") #define MaplyScreenSpaceExpShader WKString("Screenspace with expressions") #define MaplyParticleSystemPointDefaultShader WKString("Default Part Sys (Point)") @@ -163,6 +165,15 @@ #define MaplyTextJustifyLeft WKString("left") /// Justify text to the right #define MaplyTextJustifyRight WKString("right") +/// Layout text to right/left inside/outside by given number of piels +#define MaplyTextLayoutOffset WKString("layoutoffset") +/// Space between instances of text along line/inside polygon if repeating +#define MaplyTextLayoutSpacing WKString("layoutspacing") +/// How many times to repeat laid out text (-1 infinite, 0 not, or a number) +#define MaplyTextLayoutRepeat WKString("layoutrepeat") +/// Turn on or off layout debugging lines +#define MaplyTextLayoutDebug WKString("layoutdebug") + /// If set, we'll draw a shadow behind each label with this as the stroke size #define MaplyShadowSize WKString("shadowSize") /// If shadow size is being used, we can control the shadow color like so @@ -201,6 +212,7 @@ /// If set, the texture to apply to the feature #define MaplyVecTexture WKString("texture") +#define MaplyVecTextureFormat WKString("textureFormat") #define MaplyVecTexScaleX WKString("texscalex") #define MaplyVecTexScaleY WKString("texscaley") @@ -210,6 +222,8 @@ #define MaplyVecProjectionTangentPlane WKString("texprojectiontanplane") /// Screen projection for texture coordinates #define MaplyVecProjectionScreen WKString("texprojectionscreen") +/// No projection for texture coordinates +#define MaplyVecProjectionNone WKString("texprojectionnone") /// Center of the feature, to use for texture calculations #define MaplyVecCenterX WKString("veccenterx") @@ -218,6 +232,12 @@ /// For wide vectors, we can widen them in screen space or display space #define MaplyWideVecCoordType WKString("wideveccoordtype") +/// Basic or performance mode for wide vectors +#define MaplyWideVecImpl WKString("widevecimplementation") + +/// Performance mode for wide vectors +#define MaplyWideVecImplPerf WKString("widevecperformance") + /// Widened vectors are widened in real space. The width is in meters. #define MaplyWideVecCoordTypeReal WKString("real") /// Widened vectors are widened in screen space. The width is in pixels. @@ -258,6 +278,8 @@ /// It's real world coordinates for kMaplyWideVecCoordTypeReal and pixel size for kMaplyWideVecCoordTypeScreen #define MaplyWideVecTexRepeatLen WKString("repeatSize") +/// Offset to left (negative) or right (positive) of the centerline +#define MaplyWideVecOffset WKString("vecOffset") /// If set we'll break up a vector feature to the given epsilon on a globe surface #define MaplySubdivEpsilon WKString("subdivisionepsilon") @@ -331,3 +353,8 @@ /// Used to designate a non-default render target by ID #define MaplyRenderTargetDesc WKString("rendertarget") + +/// Used to refer to component objects by unique ID for, e.g., representation selection +#define MaplyUUIDDesc WKString("uuid") +/// Used to distinguish the particular representation of a unique ID +#define MaplyRepresentationDesc WKString("representation") diff --git a/common/WhirlyGlobeLib/include/SphericalEarthChunkManager.h b/common/WhirlyGlobeLib/include/SphericalEarthChunkManager.h index 80bbde4fc7..e9c2582d1d 100644 --- a/common/WhirlyGlobeLib/include/SphericalEarthChunkManager.h +++ b/common/WhirlyGlobeLib/include/SphericalEarthChunkManager.h @@ -74,6 +74,7 @@ class SphericalChunkInfo : public BaseInfo SphericalChunkInfo(); SphericalChunkInfo(const Dictionary &); + virtual ~SphericalChunkInfo() = default; RGBAColor color; bool doEdgeMatching; @@ -171,9 +172,9 @@ class SphericalChunkManager : public SceneManager void processRequests(ChangeSet &changes); protected: - std::mutex repLock; ChunkRepSet chunkReps; int borderTexel; }; +typedef std::shared_ptr SphericalChunkManagerRef; } diff --git a/common/WhirlyGlobeLib/include/StringIndexer.h b/common/WhirlyGlobeLib/include/StringIndexer.h index 6835a9d81c..9390b9f7ab 100644 --- a/common/WhirlyGlobeLib/include/StringIndexer.h +++ b/common/WhirlyGlobeLib/include/StringIndexer.h @@ -33,6 +33,9 @@ typedef unsigned long StringIdentity; // Speeds things up on the main thread immensely. extern void SetupDrawableStrings(); +// Number of entries for masks used in the mask target +#define WhirlyKitMaxMasks 2 + // Names used for indexing into shaders extern StringIdentity baseMapNameIDs[]; extern StringIdentity hasBaseMapNameIDs[]; @@ -72,6 +75,8 @@ extern StringIdentity u_uprightNameID; extern StringIdentity u_activerotNameID; extern StringIdentity a_rotNameID; extern StringIdentity a_dirNameID; +extern StringIdentity a_maskNameID; +extern StringIdentity a_maskNameIDs[]; extern StringIdentity a_texCoordNameID; extern StringIdentity u_w2NameID; extern StringIdentity u_Realw2NameID; @@ -102,18 +107,19 @@ class StringIndexer // Return the string for a string identity static std::string getString(StringIdentity); -public: +protected: + StringIndexer(); StringIndexer(StringIndexer const&) = delete; void operator=(StringIndexer const&) = delete; + + static StringIndexer &getInstance() { return instance; } -protected: - StringIndexer() { } - - static StringIndexer &getInstance(); - - std::mutex mutex; + mutable std::mutex mutex; std::unordered_map stringToIdent; std::vector identToString; + +private: + static StringIndexer instance; }; } diff --git a/common/WhirlyGlobeLib/include/VectorData.h b/common/WhirlyGlobeLib/include/VectorData.h index 4360a22f3a..9d164e4b5b 100644 --- a/common/WhirlyGlobeLib/include/VectorData.h +++ b/common/WhirlyGlobeLib/include/VectorData.h @@ -97,28 +97,38 @@ struct VectorShapeRefEqual : std::equal_to struct VectorShapeRefHash : std::hash { typedef std::hash super; - bool operator()(const VectorShapeRef &s) const { + size_t operator()(const VectorShapeRef &s) const { return super::operator()(s.get()); } }; -/// We pass the shape set around when returing a group of shapes. +/// We pass the shape set around when returning a group of shapes. /// It's a set of reference counted shapes. You have to dynamically /// cast to get the specfic type. Don't forget to use the std dynamic cast //typedef std::set ShapeSet; typedef std::unordered_set ShapeSet; /// Calculate area of a loop -float CalcLoopArea(const VectorRing &); -/// Calculate area of a loop -double CalcLoopArea(const Point2dVector &); +template double CalcLoopArea(const std::vector>&); + /// Calculate the centroid of a loop -Point2f CalcLoopCentroid(const VectorRing &loop); -/// Calculate the centroid of a bunch of points -Point2d CalcLoopCentroid(const Point2dVector &loop); +template T CalcLoopCentroid(const std::vector>&); + +/// Calculate the centroid of a loop when the area is already known +template T CalcLoopCentroid(const std::vector>&, double loopArea); + /// Calculate the center of mass of the points -Point2d CalcCenterOfMass(const Point2dVector &loop); - +template T CalcCenterOfMass(const std::vector> &loop); + +extern template double CalcLoopArea(const VectorRing&); +extern template double CalcLoopArea(const Point2dVector&); +extern template Point2f CalcLoopCentroid(const VectorRing&); +extern template Point2d CalcLoopCentroid(const Point2dVector&); +extern template Point2f CalcLoopCentroid(const VectorRing&, double); +extern template Point2d CalcLoopCentroid(const Point2dVector&, double); +extern template Point2f CalcCenterOfMass(const VectorRing&); +extern template Point2d CalcCenterOfMass(const Point2dVector&); + /// Collection of triangles forming a mesh class VectorTriangles : public VectorShape { @@ -261,8 +271,6 @@ class VectorPoints : public VectorShape /// A set of strings typedef std::set StringSet; -Point2d CalcCenterOfMass(const Point2dVector &loop); - /// Break any edge longer than the given length. /// Returns true if it broke anything void SubdivideEdges(const VectorRing &inPts,VectorRing &outPts,bool closed,float maxLen); diff --git a/common/WhirlyGlobeLib/include/VectorManager.h b/common/WhirlyGlobeLib/include/VectorManager.h index 63eb7c586f..76affcff49 100644 --- a/common/WhirlyGlobeLib/include/VectorManager.h +++ b/common/WhirlyGlobeLib/include/VectorManager.h @@ -67,7 +67,8 @@ class VectorInfo : public BaseInfo VectorInfo(); VectorInfo(const Dictionary &dict); - + virtual ~VectorInfo() = default; + // Convert contents to a string for debugging virtual std::string toString(); @@ -116,8 +117,8 @@ class VectorManager : public SceneManager void enableVectors(SimpleIDSet &vecIDs,bool enable,ChangeSet &changes); protected: - std::mutex vectorLock; VectorSceneRepSet vectorReps; }; +typedef std::shared_ptr VectorManagerRef; } diff --git a/common/WhirlyGlobeLib/include/VectorOffset.h b/common/WhirlyGlobeLib/include/VectorOffset.h new file mode 100644 index 0000000000..cc5ace5a85 --- /dev/null +++ b/common/WhirlyGlobeLib/include/VectorOffset.h @@ -0,0 +1,34 @@ +/* + * VectorOffset.h + * WhirlyGlobeLib + * + * Created by Steve Gifford on 3/4/21. + * Copyright 2011-2021 mousebird consulting + * + * 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. + * + */ + +#import "WhirlyVector.h" +#import "WhirlyGeometry.h" +#import "VectorData.h" + +namespace WhirlyKit +{ + +/// Offset/buffer the given linear feature +std::vector BufferLinear(const VectorRing &ring, float offset); + +/// Offset/buffer a polygon into one or more loops +std::vector BufferPolygon(const VectorRing &ring, float offset); + +} diff --git a/common/WhirlyGlobeLib/include/VertexAttribute.h b/common/WhirlyGlobeLib/include/VertexAttribute.h index 03957ebc29..7bb88c9ce0 100644 --- a/common/WhirlyGlobeLib/include/VertexAttribute.h +++ b/common/WhirlyGlobeLib/include/VertexAttribute.h @@ -37,6 +37,7 @@ typedef enum { BDFloat2Type = 3, BDFloatType = 4, BDIntType = 5, + BDInt64Type = 6, BDDataTypeMax } BDAttributeDataType; @@ -75,6 +76,8 @@ class VertexAttribute void addFloat(float val); /// Convenience routine to add an int (if the type matches) void addInt(int val); + /// Convenience routine to add an int64 (if the type matches) + void addInt64(int64_t val); /// Reserve size in the data array void reserve(int size); @@ -161,6 +164,7 @@ class SingleVertexAttribute : public SingleVertexAttributeInfo SingleVertexAttribute(); SingleVertexAttribute(StringIdentity nameID,int slot,float floatVal); SingleVertexAttribute(StringIdentity nameID,int slot,int intVal); + SingleVertexAttribute(StringIdentity nameID,int slot,int64_t intVal); SingleVertexAttribute(StringIdentity nameID,int slot,unsigned char colorVal[4]); SingleVertexAttribute(StringIdentity nameID,int slot,float vec0,float vec1); SingleVertexAttribute(StringIdentity nameID,int slot,float vec0,float vec1,float vec2); @@ -174,6 +178,7 @@ class SingleVertexAttribute : public SingleVertexAttributeInfo float floatVal; unsigned char color[4]; int intVal; + int64_t int64Val; } data; }; diff --git a/common/WhirlyGlobeLib/include/WhirlyGlobe.h b/common/WhirlyGlobeLib/include/WhirlyGlobe.h index 16804cda13..e90530c21f 100644 --- a/common/WhirlyGlobeLib/include/WhirlyGlobe.h +++ b/common/WhirlyGlobeLib/include/WhirlyGlobe.h @@ -83,6 +83,7 @@ #import "QuadTileBuilder.h" #import "QuadTreeNew.h" #import "RawData.h" +#import "RawPNGImage.h" #import "RenderTarget.h" #import "Scene.h" #import "SceneGraphManager.h" diff --git a/common/WhirlyGlobeLib/include/WhirlyVector.h b/common/WhirlyGlobeLib/include/WhirlyVector.h index 352c70b5dd..e0017370a3 100644 --- a/common/WhirlyGlobeLib/include/WhirlyVector.h +++ b/common/WhirlyGlobeLib/include/WhirlyVector.h @@ -1,9 +1,8 @@ -/* - * WhirlyVector.h +/* WhirlyVector.h * WhirlyGlobeLib * * Created by Steve Gifford on 1/18/11. - * Copyright 2011-2019 mousebird consulting + * Copyright 2011-2021 mousebird consulting * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -15,7 +14,6 @@ * 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. - * */ // Note: This works around a problem in compilation for the iphone @@ -76,14 +74,17 @@ class GeoCoord : public Eigen::Vector2f typedef std::vector > GeoCoordVector; -/// Color. RGBA, 8 bits per +/// Color. RGBA, 8 bits per channel. class RGBAColor { public: - RGBAColor() { } + RGBAColor() = default; RGBAColor(unsigned char r,unsigned char g,unsigned char b,unsigned char a) : r(r), g(g), b(b), a(a) { } RGBAColor(unsigned char r,unsigned char g,unsigned char b) : r(r), g(g), b(b), a(255) { } - + + RGBAColor withAlpha(int newA) const { return RGBAColor(r,g,b,(uint8_t)newA); } + RGBAColor withAlpha(double newA) const { return RGBAColor(r,g,b,(uint8_t)(newA * 255)); } + // Create an RGBColor from unit floats static RGBAColor FromUnitFloats(float *ret) { return RGBAColor(ret[0] * 255.0,ret[1] * 255.0,ret[2] * 255.0,ret[3] * 255.0); @@ -157,7 +158,9 @@ class RGBAColor /// Returns as a 4 component array of unsigned chars void asUChar4(unsigned char *ret) const { ret[0] = r; ret[1] = g; ret[2] = b; ret[3] = a; } - + + Eigen::Vector4f asRGBAVecF() const { return {r/255.f,g/255.f,b/255.f,a/255.f}; } + bool operator == (const RGBAColor &that) const { return (r == that.r && g == that.g && b == that.b && a == that.a); } // bool operator == (RGBAColor that) const { return (r == that.r && g == that.g && b == that.b && a == that.a); } RGBAColor operator * (float alpha) const { return RGBAColor(r*alpha,g*alpha,b*alpha,a*alpha); } diff --git a/common/WhirlyGlobeLib/include/WideVectorDrawableBuilder.h b/common/WhirlyGlobeLib/include/WideVectorDrawableBuilder.h index d3bf8b03aa..e2edcfb27b 100644 --- a/common/WhirlyGlobeLib/include/WideVectorDrawableBuilder.h +++ b/common/WhirlyGlobeLib/include/WideVectorDrawableBuilder.h @@ -1,9 +1,8 @@ -/* - * WideVectorDrawable.h +/* WideVectorDrawable.h * WhirlyGlobeLib * * Created by Steve Gifford on 5/29/14. - * Copyright 2011-2019 mousebird consulting + * Copyright 2011-2021 mousebird consulting * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -15,47 +14,59 @@ * 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. - * */ #import "BasicDrawableBuilder.h" #import "Program.h" #import "BaseInfo.h" +#import "WideVectorManager.h" namespace WhirlyKit { - + // Modifies the uniform values of a given shader right // before the wide vector drawables are rendered -class WideVectorTweaker : public DrawableTweaker +struct WideVectorTweaker : public BasicDrawableTweaker { -public: // Called right before the drawable is drawn virtual void tweakForFrame(Drawable *inDraw,RendererFrameInfo *frameInfo) = 0; - - bool realWidthSet; - float realWidth; - float edgeSize; - float lineWidth; - float texRepeat; - RGBAColor color; + + float edgeSize = 0.0f; + float lineWidth = 0.0f; + float texRepeat = 0.0f; + float offset = 0.0f; + bool offsetSet = false; + + FloatExpressionInfoRef widthExp; + FloatExpressionInfoRef offsetExp; }; - + // Used to debug the wide vectors //#define WIDEVECDEBUG 1 /** This drawable adds convenience functions for wide vectors. */ -class WideVectorDrawableBuilder : virtual public BasicDrawableBuilder +class WideVectorDrawableBuilder { public: EIGEN_MAKE_ALIGNED_OPERATOR_NEW; - WideVectorDrawableBuilder(); + WideVectorDrawableBuilder(std::string name,const SceneRenderer *sceneRenderer,Scene *scene); virtual ~WideVectorDrawableBuilder(); - virtual void Init(unsigned int numVert,unsigned int numTri,bool globeMode); - + virtual void Init(unsigned int numVert, + unsigned int numTri, + unsigned int numCenterline, + WideVecImplType implType, + bool globeMode, + const WideVectorInfo *vecInfo); + + /// Set the fade in and out + void setFade(TimeInterval inFadeDown,TimeInterval inFadeUp); + + /// Set the bounding box for the data + void setLocalMbr(const Mbr &mbr); + virtual unsigned int addPoint(const Point3f &pt); // Next point, for calculating p1 - p0 void add_p1(const Point3f &vec); @@ -63,17 +74,52 @@ class WideVectorDrawableBuilder : virtual public BasicDrawableBuilder void add_texInfo(float texX,float texYmin,float texYmax,float texOffset); // Vector for 90 deg from line void add_n0(const Point3f &vec); + // Left or right direction vector (needed for offsets) [And a pointer about which side of the line this is on) + void add_offset(const Point3f &nDir); // Complex constant we multiply by width for t void add_c0(float c); // Optional normal void addNormal(const Point3f &norm); void addNormal(const Point3d &norm); + + /// Add the given vertex attributes for the given vertex + void addVertexAttributes(const SingleVertexAttributeSet &attrs); + + /// Add a 2D vector to the given attribute array + virtual void addAttributeValue(int attrId,const Eigen::Vector2f &vec); + + /// Add a 3D vector to the given attribute array + virtual void addAttributeValue(int attrId,const Eigen::Vector3f &vec); + + /// Add a 4D vector to the given attribute array + virtual void addAttributeValue(int attrId,const Eigen::Vector4f &vec); + + /// Add a 4 component char array to the given attribute array + virtual void addAttributeValue(int attrId,const RGBAColor &color); + + /// Add a float to the given attribute array + virtual void addAttributeValue(int attrId,float val); + + /// Add an integer value to the given attribute array + virtual void addAttributeValue(int attrId,int val); + + /// Add an identity-type value to the given attribute array + virtual void addAttributeValue(int attrId,int64_t val); + + /// Add a triangle. Should point to the vertex IDs. + virtual void addTriangle(BasicDrawable::Triangle tri); + + /// Adds a point for instanced geometry and an ID for tracking it in the shader + virtual void addInstancePoint(const Point3f &pt,int vertIndex,int polyIndex); // We set color globally void setColor(RGBAColor inColor); // Line width for vectors is a bit different - void setLineWidth(float inWidth); + virtual void setLineWidth(float inWidth); + + // Line offset for vectors + void setLineOffset(float inOffset); /// How often the texture repeats void setTexRepeat(float inTexRepeat); @@ -81,33 +127,104 @@ class WideVectorDrawableBuilder : virtual public BasicDrawableBuilder /// Number of pixels to interpolate at the edges void setEdgeSize(float inEdgeSize); - /// Fix the width to a real world value, rather than letting it change - void setRealWorldWidth(double width); + // Apply a dynamic color expression + void setColorExpression(ColorExpressionInfoRef colorExp); + // Apply a dynamic opacity expression + void setOpacityExpression(FloatExpressionInfoRef opacityExp); + // Apply a width expression void setWidthExpression(FloatExpressionInfoRef widthExp); + // Apply an offset expression + void setOffsetExpression(FloatExpressionInfoRef offsetExp); + // The tweaker sets up uniforms before a given drawable draws - void setupTweaker(BasicDrawable *theDraw); + virtual void setupTweaker(const DrawableTweakerRef &inTweaker) const; + + // The tweaker sets up uniforms before a given drawable draws + virtual void setupTweaker(BasicDrawable &theDraw) const; + + // For performance mode wide vectors (Metal for now), the center line instances + + void addCenterLine(const Point3d ¢erPt,const Point3d &up,double len,const RGBAColor &color,const std::vector &maskIDs,int prev,int next); + // Number of center-lines defined so far + int getCenterLineCount(); // We need slightly different tweakers for the rendering variants - virtual WideVectorTweaker *makeTweaker() = 0; + virtual DrawableTweakerRef makeTweaker() const = 0; -protected: - float lineWidth; - RGBAColor color; - bool globeMode; - bool realWidthSet; - double realWidth; - bool snapTex; - float texRepeat; - float edgeSize; - int p1_index; - int n0_index; - int c0_index; - int tex_index; + /// Add a new vertex related attribute. Need a data type and the name the shader refers to + /// it by. The index returned is how you will access it. + virtual int addAttribute(BDAttributeDataType dataType,StringIdentity nameID,int slot=-1,int numThings = -1) = 0; + + /// Set the texture ID for a specific slot. You get this from the Texture object. + virtual void setTexId(unsigned int which,SimpleIdentity inId); + /// Set the program used by the drawable + void setProgram(SimpleIdentity progId); + + /// Set the active transform matrix + virtual void setMatrix(const Eigen::Matrix4d *inMat); + + /// Number of points added so far + unsigned int getNumPoints(); + + /// Numer of triangles added so far + unsigned int getNumTris(); + + // Return the basic drawable for the simple and complex cases + virtual BasicDrawableRef getBasicDrawable(); + + virtual SimpleIdentity getBasicDrawableID(); + + // Return the drawable instance for the complec case + virtual BasicDrawableInstanceRef getInstanceDrawable(); + + virtual SimpleIdentity getInstanceDrawableID(); + + // A guess at how many instances we can support (max line length, basically) + virtual int maxInstances() const { return 0; } + +protected: + float lineWidth = 1.0f; + float lineOffset = 0.0f; + bool lineOffsetSet = false; + bool globeMode = true; + float texRepeat = 1.0f; + float edgeSize = 1.0f; + int p1_index = -1; + int n0_index = -1; + int offset_index = -1; + int c0_index = -1; + int tex_index = -1; + int inst_index = -1; + std::string name; + Scene *scene; + const SceneRenderer *renderer; + + // Controls whether we're building basic drawables or instances + // We do instances on Metal + BasicDrawableBuilderRef basicDrawable; + WideVecImplType implType; + BasicDrawableInstanceBuilderRef instDrawable; + + RGBAColor color = RGBAColor::white(); FloatExpressionInfoRef widthExp; + FloatExpressionInfoRef offsetExp; + FloatExpressionInfoRef opacityExp; + ColorExpressionInfoRef colorExp; + + // Centerline structure (for Metal) + typedef struct { + Point3f center; + Point3f up; + float len; + RGBAColor color; + int prev,next; + int maskIDs[2]; + } CenterPoint; + std::vector centerline; #ifdef WIDEVECDEBUG Point3fVector locPts; diff --git a/common/WhirlyGlobeLib/include/WideVectorDrawableBuilderGLES.h b/common/WhirlyGlobeLib/include/WideVectorDrawableBuilderGLES.h index 0049d8bd55..948e784e46 100644 --- a/common/WhirlyGlobeLib/include/WideVectorDrawableBuilderGLES.h +++ b/common/WhirlyGlobeLib/include/WideVectorDrawableBuilderGLES.h @@ -1,9 +1,8 @@ -/* - * WideVectorDrawableBuilderGLES.h +/* WideVectorDrawableBuilderGLES.h * WhirlyGlobeLib * * Created by Steve Gifford on 5/14/19. - * Copyright 2011-2019 mousebird consulting + * Copyright 2011-2021 mousebird consulting * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -15,7 +14,6 @@ * 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. - * */ #import "WideVectorDrawableBuilder.h" @@ -25,9 +23,9 @@ namespace WhirlyKit { /// GLES version modifies uniforms -class WideVectorTweakerGLES : public WideVectorTweaker +struct WideVectorTweakerGLES : public WideVectorTweaker { - void tweakForFrame(Drawable *inDraw,RendererFrameInfo *frameInfo); + virtual void tweakForFrame(Drawable *inDraw,RendererFrameInfo *frameInfo) override; }; // Shader name @@ -40,19 +38,29 @@ ProgramGLES *BuildWideVectorProgramGLES(const std::string &name,SceneRenderer *r ProgramGLES *BuildWideVectorGlobeProgramGLES(const std::string &name,SceneRenderer *renderer); /// OpenGL version of the WideVectorDrawable Builder -class WideVectorDrawableBuilderGLES : virtual public BasicDrawableBuilderGLES, virtual public WideVectorDrawableBuilder +class WideVectorDrawableBuilderGLES : virtual public WideVectorDrawableBuilder { public: // Initialize with an estimate on the number of vertices and triangles - WideVectorDrawableBuilderGLES(const std::string &name,Scene *scene); + WideVectorDrawableBuilderGLES(const std::string &name,const SceneRenderer *sceneRenderer,Scene *scene); - void Init(unsigned int numVert,unsigned int numTri,bool globeMode); + virtual void Init(unsigned int numVertex,unsigned int numTri,unsigned int numCenterline, + WideVecImplType implType, + bool globeMode, + const WideVectorInfo *vecInfo) override; - virtual int addAttribute(BDAttributeDataType dataType,StringIdentity nameID,int slot = -1,int numThings = -1); + virtual int addAttribute(BDAttributeDataType dataType,StringIdentity nameID,int slot = -1,int numThings = -1) override; + + // Return the basic drawable for the simple and complex cases + virtual BasicDrawableRef getBasicDrawable() override; + + // Return the drawable instance for the complex case + virtual BasicDrawableInstanceRef getInstanceDrawable() override; - virtual WideVectorTweaker *makeTweaker(); + virtual DrawableTweakerRef makeTweaker() const override; - virtual BasicDrawableRef getDrawable(); + bool drawableGotten; + bool instanceGotten; }; - + } diff --git a/common/WhirlyGlobeLib/include/WideVectorManager.h b/common/WhirlyGlobeLib/include/WideVectorManager.h index e953a2ff9e..dcb983b795 100644 --- a/common/WhirlyGlobeLib/include/WideVectorManager.h +++ b/common/WhirlyGlobeLib/include/WideVectorManager.h @@ -3,7 +3,7 @@ * WhirlyGlobeLib * * Created by Steve Gifford on 4/29/14. - * Copyright 2011-2019 mousebird consulting. + * Copyright 2011-2020 mousebird consulting. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -23,7 +23,6 @@ #import #import "Identifiable.h" #import "BasicDrawableInstance.h" -#import "WideVectorDrawableBuilder.h" #import "Scene.h" #import "SelectionManager.h" #import "VectorData.h" @@ -33,6 +32,8 @@ namespace WhirlyKit { +class VectorInfo; + /// Vectors are widened in real world or screen coordinates typedef enum {WideVecCoordReal,WideVecCoordScreen} WideVectorCoordsType; @@ -41,6 +42,9 @@ typedef enum {WideVecMiterJoin,WideVecRoundJoin,WideVecBevelJoin} WideVectorLine /// How the lines begin and end. See: http://www.w3.org/TR/SVG/painting.html#StrokeLinecapProperty typedef enum {WideVecButtCap,WideVecRoundCap,WideVecSquareCap} WideVectorLineCapType; + +/// Performance vs basic wide vector implementation +typedef enum {WideVecImplBasic,WideVecImplPerf} WideVecImplType; /** Used to pass parameters for the wide vectors around. */ @@ -49,9 +53,12 @@ class WideVectorInfo : public BaseInfo public: WideVectorInfo(); WideVectorInfo(const Dictionary &dict); + virtual ~WideVectorInfo() = default; + WideVecImplType implType; RGBAColor color; float width; + float offset; float repeatSize; float edgeSize; float subdivEps; @@ -62,6 +69,7 @@ class WideVectorInfo : public BaseInfo float miterLimit; FloatExpressionInfoRef widthExp; + FloatExpressionInfoRef offsetExp; FloatExpressionInfoRef opacityExp; ColorExpressionInfoRef colorExp; }; @@ -97,7 +105,6 @@ class WideVectorManager : public SceneManager virtual ~WideVectorManager(); /// Add widened vectors for display - SimpleIdentity addVectors(const ShapeSet &shapes,const WideVectorInfo &desc,ChangeSet &changes); SimpleIdentity addVectors(const std::vector &shapes,const WideVectorInfo &desc,ChangeSet &changes); /// Enable/disable active vectors @@ -105,13 +112,16 @@ class WideVectorManager : public SceneManager /// Make an instance of the give vectors with the given attributes and return an ID to identify them. SimpleIdentity instanceVectors(SimpleIdentity vecID,const WideVectorInfo &desc,ChangeSet &changes); - + + /// Change the vector(s) represented by the given ID + void changeVectors(SimpleIdentity vecID,const WideVectorInfo &vecInfo,ChangeSet &changes); + /// Remove a gruop of vectors named by the given ID void removeVectors(SimpleIDSet &vecIDs,ChangeSet &changes); protected: - std::mutex vecLock; WideVectorSceneRepSet sceneReps; }; +typedef std::shared_ptr WideVectorManagerRef; } diff --git a/common/WhirlyGlobeLib/src/BaseInfo.cpp b/common/WhirlyGlobeLib/src/BaseInfo.cpp index 2624ff7fb0..f7d76fa12a 100644 --- a/common/WhirlyGlobeLib/src/BaseInfo.cpp +++ b/common/WhirlyGlobeLib/src/BaseInfo.cpp @@ -1,9 +1,8 @@ -/* - * BaseInfo.mm +/* BaseInfo.mm * WhirlyGlobeLib * * Created by Steve Gifford on 7/6/15. - * Copyright 2011-2019 mousebird consulting + * Copyright 2011-2021 mousebird consulting * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -15,7 +14,6 @@ * 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. - * */ #import "BaseInfo.h" @@ -30,38 +28,110 @@ using namespace Eigen; namespace WhirlyKit { -ExpressionInfo::ExpressionInfo() -: type(ExpressionNone), base(1.0) +ExpressionInfo::ExpressionInfo() : + type(ExpressionNone), + base(1.0) +{ +} + +ExpressionInfo::ExpressionInfo(const ExpressionInfo &that) : // NOLINT(bugprone-copy-constructor-init) + Identifiable(), // Generate a new ID, don't copy + type(that.type), + base(that.base), + stopInputs(that.stopInputs) { } -ExpressionInfo::ExpressionInfo(const ExpressionInfo &that) -: type(that.type), base(that.base), stopInputs(that.stopInputs) +void FloatExpressionInfo::scaleBy(double scale) { + for (float &stopOutput : stopOutputs) + { + stopOutput = (float)(stopOutput * scale); + } +} + +template +static TTo evalExpr(float zoom, float base,TFrom defaultValue, + const std::vector &inputs,const std::vector &outputs, + std::function convert, + std::function interp) +{ + if (inputs.empty() || outputs.empty()) + { + return convert(defaultValue); + } + if (zoom <= inputs.front()) + { + return convert(outputs.front()); + } + if (zoom > inputs.back()) + { + return convert(outputs.back()); + } + + const auto numStops = std::min(inputs.size(), outputs.size()); + for (int i = 0; i < numStops - 1; ++i) + { + if (inputs[i] <= zoom && zoom < inputs[i + 1]) + { + const auto zoomA = inputs[i]; + const auto zoomB = inputs[i + 1]; + const auto valA = outputs[i]; + const auto valB = outputs[i + 1]; + const auto t = (zoom - zoomA) / (zoomB - zoomA); + if (base > 0) + { + // exponential + if (base == 1.0f) + { + return interp(valA, valB, t); + } + const auto a = std::pow(base, zoom - zoomA) - 1.0; + const auto b = std::pow(base, zoomB - zoomA) - 1.0; + return interp(valA, valB, a/b); + } + else + { + // linear + return interp(valA, valB, t); + } + } + } + return convert(outputs.back()); } -FloatExpressionInfo::FloatExpressionInfo() +// std::lerp is C++20 +template T static lerp(T a, T b, double t) { return (T)(t * (b - a) + a); } +static const auto flerp = lerp; + +template T static inline ident(T t) { return t; } + +static inline Vector4f toVec(RGBAColor c) { return c.asRGBAVecF(); } + +static inline RGBAColor lerpRGB(RGBAColor a, RGBAColor b, double t) { + return RGBAColor{lerp(a.r,b.r,t),lerp(a.g,b.g,t),lerp(a.b,b.b,t),lerp(a.a,b.a,t)}; } -FloatExpressionInfo::FloatExpressionInfo(const FloatExpressionInfo &that) -: ExpressionInfo(that), stopOutputs(that.stopOutputs) +static inline Vector4f lerpVec(RGBAColor a, RGBAColor b, double t) { + return Vector4f{flerp(a.r,b.r,t)/255.f,flerp(a.g,b.g,t)/255.f, + flerp(a.b,b.b,t)/255.f,flerp(a.a,b.a,t)/255.f}; } -void FloatExpressionInfo::scaleBy(double scale) +float FloatExpressionInfo::evaluate(float zoom, float defaultValue) { - for (unsigned int ii=0;ii(zoom,base,defaultValue,stopInputs,stopOutputs,ident,flerp); } -ColorExpressionInfo::ColorExpressionInfo() +RGBAColor ColorExpressionInfo::evaluate(float zoom, RGBAColor def) { + return evalExpr(zoom,base,def,stopInputs,stopOutputs,ident,lerpRGB); } -ColorExpressionInfo::ColorExpressionInfo(const ColorExpressionInfo &that) -: ExpressionInfo(that), stopOutputs(that.stopOutputs) +Vector4f ColorExpressionInfo::evaluateF(float zoom, RGBAColor def) { + return evalExpr(zoom,base,def,stopInputs,stopOutputs,toVec,lerpVec); } BaseInfo::BaseInfo() @@ -173,9 +243,9 @@ BaseInfo::BaseInfo(const Dictionary &dict) template std::string to_string(T value) { - std::ostringstream os ; - os << value ; - return os.str() ; + std::ostringstream os; + os << value; + return os.str(); } std::string BaseInfo::toString() @@ -205,7 +275,7 @@ std::string BaseInfo::toString() return outStr; } -void BaseInfo::setupBasicDrawable(BasicDrawableBuilderRef drawBuild) const +void BaseInfo::setupBasicDrawable(const BasicDrawableBuilderRef &drawBuild) const { setupBasicDrawable(drawBuild.get()); } @@ -217,14 +287,13 @@ void BaseInfo::setupBasicDrawable(BasicDrawableBuilder *drawBuild) const drawBuild->setEnableTimeRange(startEnable, endEnable); drawBuild->setDrawOrder(drawOrder); drawBuild->setDrawPriority(drawPriority); - drawBuild->setVisibleRange(minVis,maxVis); + drawBuild->setVisibleRange((float)minVis,(float)maxVis); drawBuild->setViewerVisibility(minViewerDist,maxViewerDist,viewerCenter); drawBuild->setZoomInfo(zoomSlot,minZoomVis,maxZoomVis); drawBuild->setProgram(programID); drawBuild->setUniforms(uniforms); drawBuild->setRequestZBuffer(zBufferRead); drawBuild->setWriteZBuffer(zBufferWrite); - drawBuild->setProgram(programID); drawBuild->setExtraFrames(extraFrames); if (renderTargetID != EmptyIdentity) drawBuild->setRenderTarget(renderTargetID); @@ -232,7 +301,7 @@ void BaseInfo::setupBasicDrawable(BasicDrawableBuilder *drawBuild) const drawBuild->setIncludeExp(true); } -void BaseInfo::setupBasicDrawableInstance(BasicDrawableInstanceBuilderRef drawBuild) const +void BaseInfo::setupBasicDrawableInstance(const BasicDrawableInstanceBuilderRef &drawBuild) const { setupBasicDrawableInstance(drawBuild.get()); } @@ -243,7 +312,7 @@ void BaseInfo::setupBasicDrawableInstance(BasicDrawableInstanceBuilder *drawBuil drawBuild->setEnableTimeRange(startEnable, endEnable); drawBuild->setDrawOrder(drawOrder); drawBuild->setDrawPriority(drawPriority); - drawBuild->setVisibleRange(minVis,maxVis); + drawBuild->setVisibleRange((float)minVis,(float)maxVis); drawBuild->setViewerVisibility(minViewerDist,maxViewerDist,viewerCenter); drawBuild->setZoomInfo(zoomSlot,minZoomVis,maxZoomVis); drawBuild->setUniforms(uniforms); diff --git a/common/WhirlyGlobeLib/src/BasicDrawable.cpp b/common/WhirlyGlobeLib/src/BasicDrawable.cpp index cba4c1ce5b..4385ebec99 100644 --- a/common/WhirlyGlobeLib/src/BasicDrawable.cpp +++ b/common/WhirlyGlobeLib/src/BasicDrawable.cpp @@ -1,9 +1,8 @@ -/* - * BasicDrawable.mm +/* BasicDrawable.cpp * WhirlyGlobeLib * * Created by Steve Gifford on 2/1/11. - * Copyright 2011-2019 mousebird consulting + * Copyright 2011-2021 mousebird consulting * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -15,7 +14,6 @@ * 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. - * */ #import "Program.h" @@ -369,7 +367,76 @@ void BasicDrawable::setUniforms(const SingleVertexAttributeSet &newUniforms) uniforms = newUniforms; } - + +void BasicDrawable::setUniform(SimpleIdentity nameID, float val) +{ + SingleVertexAttribute attr; + attr.nameID = nameID; + attr.slot = -1; + attr.type = BDFloatType; + attr.data.floatVal = val; + + setUniform(attr); +} + +void BasicDrawable::setUniform(SimpleIdentity nameID, int val) +{ + SingleVertexAttribute attr; + attr.nameID = nameID; + attr.slot = -1; + attr.type = BDIntType; + attr.data.intVal = val; + + setUniform(attr); +} + +void BasicDrawable::setUniform(SimpleIdentity nameID, const Eigen::Vector2f &vec) +{ + SingleVertexAttribute attr; + attr.nameID = nameID; + attr.slot = -1; + attr.type = BDFloat2Type; + attr.data.vec2[0] = vec.x(); + attr.data.vec2[1] = vec.y(); + + setUniform(attr); +} + +void BasicDrawable::setUniform(SimpleIdentity nameID, const Eigen::Vector3f &vec) +{ + SingleVertexAttribute attr; + attr.nameID = nameID; + attr.slot = -1; + attr.type = BDFloat2Type; + attr.data.vec3[0] = vec.x(); + attr.data.vec3[1] = vec.y(); + attr.data.vec3[2] = vec.z(); + + setUniform(attr); +} + +void BasicDrawable::setUniform(SimpleIdentity nameID, const Eigen::Vector4f &vec) +{ + SingleVertexAttribute attr; + attr.nameID = nameID; + attr.slot = -1; + attr.type = BDFloat2Type; + attr.data.vec4[0] = vec.x(); + attr.data.vec4[1] = vec.y(); + attr.data.vec4[2] = vec.z(); + attr.data.vec4[3] = vec.w(); + + setUniform(attr); +} + +void BasicDrawable::setUniform(const SingleVertexAttribute &attr) +{ + auto it = uniforms.find(attr); + if (it != uniforms.end()) + uniforms.erase(it); + uniforms.insert(attr); +} + void BasicDrawable::setUniBlock(const UniformBlock &uniBlock) { setValuesChanged(); @@ -383,7 +450,7 @@ void BasicDrawable::setUniBlock(const UniformBlock &uniBlock) uniBlocks.push_back(uniBlock); } -void BasicDrawable::addTweaker(DrawableTweakerRef tweak) +void BasicDrawable::addTweaker(const DrawableTweakerRef &tweak) { setValuesChanged(); @@ -403,9 +470,14 @@ void BasicDrawable::setTexturesChanged() if (renderTargetCon) renderTargetCon->modified = true; } - + +BasicDrawableTexTweaker::BasicDrawableTexTweaker(std::vector &&texIDs,TimeInterval startTime,double period) + : texIDs(std::move(texIDs)), startTime(startTime), period(period) +{ +} + BasicDrawableTexTweaker::BasicDrawableTexTweaker(const std::vector &texIDs,TimeInterval startTime,double period) -: texIDs(texIDs), startTime(startTime), period(period) + : texIDs(texIDs), startTime(startTime), period(period) { } @@ -428,7 +500,7 @@ void BasicDrawableTexTweaker::tweakForFrame(Drawable *draw,RendererFrameInfo *fr // This forces a redraw every frame // Note: There has to be a better way - frame->scene->addChangeRequest(NULL); + frame->scene->addChangeRequest(nullptr); } BasicDrawableScreenTexTweaker::BasicDrawableScreenTexTweaker(const Point3d ¢erPt,const Point2d &texScale) @@ -440,7 +512,7 @@ void BasicDrawableScreenTexTweaker::tweakForFrame(Drawable *draw,RendererFrameIn { BasicDrawable *basicDraw = dynamic_cast(draw); - if (frameInfo->program) + if (basicDraw && frameInfo->program) { Vector4f screenPt = frameInfo->mvpMat * Vector4f(centerPt.x(),centerPt.y(),centerPt.z(),1.0); screenPt /= screenPt.w(); @@ -469,38 +541,34 @@ ColorChangeRequest::ColorChangeRequest(SimpleIdentity drawId,RGBAColor inColor) void ColorChangeRequest::execute2(Scene *scene,SceneRenderer *renderer,DrawableRef draw) { - BasicDrawableRef basicDrawable = std::dynamic_pointer_cast(draw); - if (basicDrawable) + if (auto basicDrawable = dynamic_cast(draw.get())) { basicDrawable->setOverrideColor(color); - } else { - BasicDrawableInstanceRef basicDrawInst = std::dynamic_pointer_cast(draw); - if (basicDrawInst) - basicDrawInst->setColor(RGBAColor(color[0],color[1],color[2],color[3])); + } + else if (auto basicDrawInst = dynamic_cast(draw.get())) + { + basicDrawInst->setColor(RGBAColor(color[0],color[1],color[2],color[3])); } } OnOffChangeRequest::OnOffChangeRequest(SimpleIdentity drawId,bool OnOff) : DrawableChangeRequest(drawId), newOnOff(OnOff) { - } void OnOffChangeRequest::execute2(Scene *scene,SceneRenderer *renderer,DrawableRef draw) { - BasicDrawableRef basicDrawable = std::dynamic_pointer_cast(draw); - if (basicDrawable) { + if (auto basicDrawable = dynamic_cast(draw.get())) + { basicDrawable->setOnOff(newOnOff); } - else { - BasicDrawableInstanceRef basicDrawInst = std::dynamic_pointer_cast(draw); - if (basicDrawInst) - basicDrawInst->setEnable(newOnOff); - else { - ParticleSystemDrawableRef partSys = std::dynamic_pointer_cast(draw); - if (partSys) - partSys->setOnOff(newOnOff); - } + else if (auto basicDrawInst = dynamic_cast(draw.get())) + { + basicDrawInst->setEnable(newOnOff); + } + else if (auto partSys = dynamic_cast(draw.get())) + { + partSys->setOnOff(newOnOff); } } @@ -511,11 +579,12 @@ VisibilityChangeRequest::VisibilityChangeRequest(SimpleIdentity drawId,float min void VisibilityChangeRequest::execute2(Scene *scene,SceneRenderer *renderer,DrawableRef draw) { - BasicDrawableRef basicDrawable = std::dynamic_pointer_cast(draw); - if (basicDrawable) + if (auto basicDrawable = dynamic_cast(draw.get())) + { basicDrawable->setVisibleRange(minVis,maxVis); - else { - BasicDrawableInstanceRef basicDrawInst = std::dynamic_pointer_cast(draw); + } + else if (auto basicDrawInst = dynamic_cast(draw.get())) + { basicDrawInst->setVisibleRange(minVis, maxVis); } } @@ -529,8 +598,7 @@ FadeChangeRequest::FadeChangeRequest(SimpleIdentity drawId,TimeInterval fadeUp,T void FadeChangeRequest::execute2(Scene *scene,SceneRenderer *renderer,DrawableRef draw) { // Fade it out, then remove it - BasicDrawableRef basicDrawable = std::dynamic_pointer_cast(draw); - if (basicDrawable) + if (auto basicDrawable = dynamic_cast(draw.get())) { basicDrawable->setFade(fadeDown, fadeUp); } diff --git a/common/WhirlyGlobeLib/src/BasicDrawableBuilder.cpp b/common/WhirlyGlobeLib/src/BasicDrawableBuilder.cpp index ddfed5987a..d90c68afab 100644 --- a/common/WhirlyGlobeLib/src/BasicDrawableBuilder.cpp +++ b/common/WhirlyGlobeLib/src/BasicDrawableBuilder.cpp @@ -1,9 +1,8 @@ -/* - * BasicDrawableBuilder.cpp +/* BasicDrawableBuilder.cpp * WhirlyGlobeLib * * Created by Steve Gifford on 2/1/11. - * Copyright 2011-2019 mousebird consulting + * Copyright 2011-2021 mousebird consulting * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -15,7 +14,6 @@ * 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. - * */ #import "BasicDrawableBuilder.h" @@ -26,19 +24,26 @@ using namespace Eigen; namespace WhirlyKit { -BasicDrawableBuilder::BasicDrawableBuilder() - : basicDraw(NULL), scene(NULL) +float BasicDrawableTweaker::getZoom(const Drawable &inDraw,const Scene &scene,float def) const +{ + const auto bd = dynamic_cast(&inDraw); + return (bd && bd->zoomSlot >= 0) ? scene.getZoomSlotValue(bd->zoomSlot) : def; +} + +BasicDrawableBuilder::BasicDrawableBuilder() : + scene(nullptr) { } -BasicDrawableBuilder::BasicDrawableBuilder(const std::string &name,Scene *scene) - : name(name), basicDraw(NULL), scene(scene) +BasicDrawableBuilder::BasicDrawableBuilder(std::string name,Scene *scene) : + name(std::move(name)), + scene(scene) { } -void BasicDrawableBuilder::setName(const std::string &name) +void BasicDrawableBuilder::setName(std::string name) { - basicDraw->name = name; + basicDraw->name = std::move(name); } void BasicDrawableBuilder::reserve(int numVert,int numTri) @@ -96,27 +101,27 @@ void BasicDrawableBuilder::setupStandardAttributes(int numReserve) { // setupTexCoordEntry(0,numReserve); - basicDraw->colorEntry = addAttribute(BDChar4Type,a_colorNameID); + basicDraw->colorEntry = findAttribute(a_colorNameID); + if (basicDraw->colorEntry < 0) + basicDraw->colorEntry = addAttribute(BDChar4Type,a_colorNameID); basicDraw->vertexAttributes[basicDraw->colorEntry]->setDefaultColor(RGBAColor(255,255,255,255)); basicDraw->vertexAttributes[basicDraw->colorEntry]->reserve(numReserve); - basicDraw->normalEntry = addAttribute(BDFloat3Type,a_normalNameID); + basicDraw->normalEntry = findAttribute(a_normalNameID); + if (basicDraw->normalEntry < 0) + basicDraw->normalEntry = addAttribute(BDFloat3Type,a_normalNameID); basicDraw->vertexAttributes[basicDraw->normalEntry]->setDefaultVector3f(Vector3f(1.0,1.0,1.0)); basicDraw->vertexAttributes[basicDraw->normalEntry]->reserve(numReserve); } -SimpleIdentity BasicDrawableBuilder::getDrawableID() +SimpleIdentity BasicDrawableBuilder::getDrawableID() const { - if (basicDraw) - return basicDraw->getId(); - return EmptyIdentity; + return basicDraw ? basicDraw->getId() : EmptyIdentity; } -int BasicDrawableBuilder::getDrawablePriority() +int BasicDrawableBuilder::getDrawablePriority() const { - if (basicDraw) - return basicDraw->getDrawPriority(); - return 0; + return basicDraw ? basicDraw->getDrawPriority() : 0; } void BasicDrawableBuilder::setupTexCoordEntry(int which,int numReserve) @@ -136,10 +141,6 @@ void BasicDrawableBuilder::setupTexCoordEntry(int which,int numReserve) } } -BasicDrawableBuilder::~BasicDrawableBuilder() -{ -} - void BasicDrawableBuilder::setOnOff(bool onOff) { basicDraw->on = onOff; @@ -235,7 +236,7 @@ void BasicDrawableBuilder::setLineWidth(float inWidth) basicDraw->lineWidth = inWidth; } -float BasicDrawableBuilder::getLineWidth() +float BasicDrawableBuilder::getLineWidth() const { return basicDraw->lineWidth; } @@ -246,7 +247,7 @@ void BasicDrawableBuilder::setTexId(unsigned int which,SimpleIdentity inId) basicDraw->texInfo[which].texId = inId; } -SimpleIdentity BasicDrawableBuilder::getTexId(unsigned int which) +SimpleIdentity BasicDrawableBuilder::getTexId(unsigned int which) const { if (!basicDraw) return EmptyIdentity; @@ -275,7 +276,26 @@ void BasicDrawableBuilder::setProgram(SimpleIdentity progId) basicDraw->setProgram(progId); } -void BasicDrawableBuilder::addTweaker(DrawableTweakerRef tweakRef) +void BasicDrawableBuilder::setupTweaker(BasicDrawable &theDraw) const +{ + if (auto tweaker = makeTweaker()) + { + setupTweaker(tweaker); + theDraw.addTweaker(tweaker); + } +} + +void BasicDrawableBuilder::setupTweaker(const DrawableTweakerRef &inTweaker) const +{ + if (auto tweak = std::dynamic_pointer_cast(inTweaker)) + { + tweak->color = color; + tweak->colorExp = colorExp; + tweak->opacityExp = opacityExp; + } +} + +void BasicDrawableBuilder::addTweaker(const DrawableTweakerRef &tweakRef) { basicDraw->tweakers.insert(tweakRef); } @@ -290,23 +310,26 @@ void BasicDrawableBuilder::setIncludeExp(bool newVal) includeExp = newVal; } -void BasicDrawableBuilder::setColor(RGBAColor color) +void BasicDrawableBuilder::setColor(RGBAColor inColor) { + color = inColor; if (basicDraw->colorEntry >= 0) + { basicDraw->vertexAttributes[basicDraw->colorEntry]->setDefaultColor(color); + } } -void BasicDrawableBuilder::setColor(unsigned char color[]) +void BasicDrawableBuilder::setColor(const unsigned char color[]) { setColor(RGBAColor(color[0],color[1],color[2],color[3])); } -void BasicDrawableBuilder::setColorExpression(ColorExpressionInfoRef inColorExp) +void BasicDrawableBuilder::setColorExpression(const ColorExpressionInfoRef &inColorExp) { colorExp = inColorExp; } -void BasicDrawableBuilder::setOpacityExpression(FloatExpressionInfoRef inOpacityExp) +void BasicDrawableBuilder::setOpacityExpression(const FloatExpressionInfoRef &inOpacityExp) { opacityExp = inOpacityExp; } @@ -359,17 +382,17 @@ unsigned int BasicDrawableBuilder::addPoint(const Point3d &pt) return (unsigned int)(points.size()-1); } -unsigned int BasicDrawableBuilder::getNumPoints() +unsigned int BasicDrawableBuilder::getNumPoints() const { return points.size(); } -unsigned int BasicDrawableBuilder::getNumTris() +unsigned int BasicDrawableBuilder::getNumTris() const { return tris.size(); } -Point3d BasicDrawableBuilder::getPoint(int which) +Point3d BasicDrawableBuilder::getPoint(int which) const { if (which >= points.size()) return Point3d(0,0,0); @@ -416,42 +439,48 @@ void BasicDrawableBuilder::addNormal(const Point3d &norm) basicDraw->vertexAttributes[basicDraw->normalEntry]->addVector3f(Point3f(norm.x(),norm.y(),norm.z())); } -bool BasicDrawableBuilder::compareVertexAttributes(const SingleVertexAttributeSet &attrs) +bool BasicDrawableBuilder::compareVertexAttributes(const SingleVertexAttributeSet &attrs) const { - for (SingleVertexAttributeSet::iterator it = attrs.begin(); - it != attrs.end(); ++it) + for (const auto &attr : attrs) { - int attrId = -1; + unsigned attrId = -1; for (unsigned int ii=0;iivertexAttributes.size();ii++) - if (basicDraw->vertexAttributes[ii]->nameID == it->nameID) + { + if (basicDraw->vertexAttributes[ii]->nameID == attr.nameID) { attrId = ii; break; } - if (attrId == -1) - return false; - if (basicDraw->vertexAttributes[attrId]->getDataType() != it->type) + } + if (attrId == -1 || basicDraw->vertexAttributes[attrId]->getDataType() != attr.type) + { return false; + } } return true; } +void BasicDrawableBuilder::setVertexAttribute(const SingleVertexAttributeInfo &attr) +{ + addAttribute(attr.type,attr.nameID,attr.slot); +} + void BasicDrawableBuilder::setVertexAttributes(const SingleVertexAttributeInfoSet &attrs) { - for (auto it = attrs.begin(); - it != attrs.end(); ++it) - addAttribute(it->type,it->nameID,it->slot); + for (auto attr : attrs) + { + addAttribute(attr.type,attr.nameID,attr.slot); + } } void BasicDrawableBuilder::addVertexAttributes(const SingleVertexAttributeSet &attrs) { - for (SingleVertexAttributeSet::iterator it = attrs.begin(); - it != attrs.end(); ++it) + for (const auto &attr : attrs) { int attrId = -1; for (unsigned int ii=0;iivertexAttributes.size();ii++) - if (basicDraw->vertexAttributes[ii]->nameID == it->nameID) + if (basicDraw->vertexAttributes[ii]->nameID == attr.nameID) { attrId = ii; break; @@ -460,50 +489,50 @@ void BasicDrawableBuilder::addVertexAttributes(const SingleVertexAttributeSet &a if (attrId == -1) continue; - switch (it->type) + switch (attr.type) { case BDFloatType: - addAttributeValue(attrId, it->data.floatVal); + addAttributeValue(attrId, attr.data.floatVal); break; case BDFloat2Type: { - Vector2f vec; - vec.x() = it->data.vec2[0]; - vec.y() = it->data.vec2[1]; - addAttributeValue(attrId, vec); + addAttributeValue(attrId, Vector2f{attr.data.vec2[0], attr.data.vec2[1]}); } break; case BDFloat3Type: { - Vector3f vec; - vec.x() = it->data.vec3[0]; - vec.y() = it->data.vec3[1]; - vec.z() = it->data.vec3[2]; - addAttributeValue(attrId, vec); + addAttributeValue(attrId, Vector3f{ + attr.data.vec3[0], + attr.data.vec3[1], + attr.data.vec3[2] + }); } break; case BDFloat4Type: { - Vector4f vec; - vec.x() = it->data.vec4[0]; - vec.y() = it->data.vec4[1]; - vec.z() = it->data.vec4[2]; - vec.w() = it->data.vec4[3]; - addAttributeValue(attrId, vec); + addAttributeValue(attrId, Vector4f{ + attr.data.vec4[0], + attr.data.vec4[1], + attr.data.vec4[2], + attr.data.vec4[3] + }); } break; case BDChar4Type: { - RGBAColor color; - color.r = it->data.color[0]; - color.g = it->data.color[1]; - color.b = it->data.color[2]; - color.a = it->data.color[3]; - addAttributeValue(attrId, color); + addAttributeValue(attrId, RGBAColor{ + attr.data.color[0], + attr.data.color[1], + attr.data.color[2], + attr.data.color[3] + }); } break; case BDIntType: - addAttributeValue(attrId, it->data.intVal); + addAttributeValue(attrId, attr.data.intVal); + break; + case BDInt64Type: + addAttributeValue(attrId, attr.data.int64Val); break; case BDDataTypeMax: break; @@ -526,6 +555,24 @@ void BasicDrawableBuilder::addAttributeValue(int attrId,const RGBAColor &color) void BasicDrawableBuilder::addAttributeValue(int attrId,float val) { basicDraw->vertexAttributes[attrId]->addFloat(val); } +void BasicDrawableBuilder::addAttributeValue(int attrId,int val) +{ + basicDraw->vertexAttributes[attrId]->addInt(val); +} + +void BasicDrawableBuilder::addAttributeValue(int attrId,int64_t val) +{ basicDraw->vertexAttributes[attrId]->addFloat(val); } + +int BasicDrawableBuilder::findAttribute(int nameID) +{ + for (unsigned int ii=0;iivertexAttributes.size();ii++) + if (basicDraw->vertexAttributes[ii]->nameID == nameID) + return ii; + + return -1; +} + + void BasicDrawableBuilder::addTriangle(BasicDrawable::Triangle tri) { tris.push_back(tri); } @@ -534,19 +581,23 @@ void BasicDrawableBuilder::setUniforms(const SingleVertexAttributeSet &uniforms) basicDraw->uniforms = uniforms; } -void BasicDrawableBuilder::applySubTexture(int which,SubTexture subTex,int startingAt) +void BasicDrawableBuilder::applySubTexture(int which,const SubTexture &subTex,int startingAt) { if (which == -1) { // Apply the mapping everywhere for (unsigned int ii=0;iitexInfo.size();ii++) + { applySubTexture(ii, subTex, startingAt); - } else { + } + } + else + { setupTexCoordEntry(which, 0); BasicDrawable::TexInfo &thisTexInfo = basicDraw->texInfo[which]; thisTexInfo.texId = subTex.texId; - std::vector *texCoords = (std::vector *)basicDraw->vertexAttributes[thisTexInfo.texCoordEntry]->data; + auto *texCoords = (std::vector *)basicDraw->vertexAttributes[thisTexInfo.texCoordEntry]->data; for (unsigned int ii=startingAt;iisize();ii++) { @@ -555,5 +606,7 @@ void BasicDrawableBuilder::applySubTexture(int which,SubTexture subTex,int start } } } - + } + +#include diff --git a/common/WhirlyGlobeLib/src/BasicDrawableBuilderGLES.cpp b/common/WhirlyGlobeLib/src/BasicDrawableBuilderGLES.cpp index 7b28e70e30..ad2aca5c29 100644 --- a/common/WhirlyGlobeLib/src/BasicDrawableBuilderGLES.cpp +++ b/common/WhirlyGlobeLib/src/BasicDrawableBuilderGLES.cpp @@ -1,9 +1,8 @@ -/* - * BasicDrawableBuilderGLES.cpp +/* BasicDrawableBuilderGLES.cpp * WhirlyGlobeLib * * Created by Steve Gifford on 5/10/19. - * Copyright 2011-2019 mousebird consulting + * Copyright 2011-2021 mousebird consulting * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -15,39 +14,67 @@ * 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. - * */ #import "BasicDrawableBuilderGLES.h" +#import using namespace Eigen; namespace WhirlyKit { +void BasicDrawableTweakerGLES::tweakForFrame(Drawable *inDraw,RendererFrameInfo *frameInfo) +{ + if (colorExp || opacityExp) + if (auto program = dynamic_cast(frameInfo->program)) + if (auto draw = dynamic_cast(inDraw)) + { + const float zoom = getZoom(*inDraw,*frameInfo->scene,-1.0f); + if (zoom >= 0) + { + auto c = colorExp ? colorExp->evaluate(zoom, color) : color; + if (opacityExp) + { + const auto a = (uint8_t) (255.0f * opacityExp->evaluate(zoom, 1.0f)); + c = RGBAColor::FromInt((int)(((uint32_t)c.asInt() & 0x00FFFFFFU) | ((uint32_t)a << 24U))); + } + draw->setOverrideColor(c); + return; + } + else + { + wkLogLevel(Warn, "Failed to get zoom level for tweaker"); + } + } + // No tweaker should be set up if there's no work to do (no expressions) + wkLogLevel(Warn, "Unexpected state for tweaker"); +} + BasicDrawableBuilderGLES::BasicDrawableBuilderGLES(const std::string &name,Scene *scene,bool setupStandard) : BasicDrawableBuilder(name,scene), drawableGotten(false) { basicDraw = std::make_shared(name); BasicDrawableBuilder::Init(); if (setupStandard) - setupStandardAttributes(); + setupStandardAttributes(); // NOLINT: derived virtual not called here } - + BasicDrawableBuilderGLES::~BasicDrawableBuilderGLES() { if (!drawableGotten) basicDraw.reset(); } +// NOLINTNEXTLINE(google-default-arguments) int BasicDrawableBuilderGLES::addAttribute(BDAttributeDataType dataType,StringIdentity nameID,int slot,int numThings) { VertexAttribute *attr = new VertexAttributeGLES(dataType,nameID); if (numThings > 0) attr->reserve(numThings); basicDraw->vertexAttributes.push_back(attr); - - return (unsigned int)(basicDraw->vertexAttributes.size()-1); + + return (int)(basicDraw->vertexAttributes.size()-1); } BasicDrawableRef BasicDrawableBuilderGLES::getDrawable() @@ -57,12 +84,32 @@ BasicDrawableRef BasicDrawableBuilderGLES::getDrawable() if (draw && !drawableGotten) { draw->points = points; draw->tris = tris; - draw->vertexSize = draw->singleVertexSize(); - + draw->vertexSize = (int)draw->singleVertexSize(); + + ((BasicDrawableBuilder*)this)->setupTweaker(*draw); + drawableGotten = true; } - + return draw; } - + +DrawableTweakerRef BasicDrawableBuilderGLES::makeTweaker() const +{ + if (colorExp || opacityExp) + { + return std::make_shared(); + } + return {}; +} + +void BasicDrawableBuilderGLES::setupTweaker(const DrawableTweakerRef &inTweaker) const +{ + if (auto tweaker = std::dynamic_pointer_cast(inTweaker)) + { + tweaker->colorExp = colorExp; + tweaker->opacityExp = opacityExp; + } +} + } diff --git a/common/WhirlyGlobeLib/src/BasicDrawableGLES.cpp b/common/WhirlyGlobeLib/src/BasicDrawableGLES.cpp index 857617d143..ff1a4ab6e0 100644 --- a/common/WhirlyGlobeLib/src/BasicDrawableGLES.cpp +++ b/common/WhirlyGlobeLib/src/BasicDrawableGLES.cpp @@ -555,11 +555,14 @@ void BasicDrawableGLES::draw(RendererFrameInfoGLES *frameInfo,Scene *inScene) } } - // Color has been overriden, so don't use the embedded ones + // Color has been overridden, so don't use the embedded ones if (hasOverrideColor) { const OpenGLESAttribute *colorAttr = prog->findAttribute(a_colorNameID); - if (colorAttr) - glVertexAttrib4f(colorAttr->index, color.r / 255.0, color.g / 255.0, color.b / 255.0, color.a / 255.0); + if (colorAttr) { + glDisableVertexAttribArray(colorAttr->index); + glVertexAttrib4f(colorAttr->index, color.r / 255.0, color.g / 255.0, color.b / 255.0, + color.a / 255.0); + } } diff --git a/common/WhirlyGlobeLib/src/BasicDrawableInstance.cpp b/common/WhirlyGlobeLib/src/BasicDrawableInstance.cpp index 82a03bfe8d..1f9a9e1a2e 100644 --- a/common/WhirlyGlobeLib/src/BasicDrawableInstance.cpp +++ b/common/WhirlyGlobeLib/src/BasicDrawableInstance.cpp @@ -29,7 +29,7 @@ namespace WhirlyKit { BasicDrawableInstance::BasicDrawableInstance(const std::string &name) -: Drawable(name), instanceTexSource(EmptyIdentity), instanceTexProg(EmptyIdentity), valuesChanged(true), texturesChanged(true) +: Drawable(name), numInstances(0), instanceTexSource(EmptyIdentity), instanceTexProg(EmptyIdentity), valuesChanged(true), texturesChanged(true) { } @@ -253,14 +253,15 @@ void BasicDrawableInstance::setDrawPriority(int newPriority) } /// Set the line width -void BasicDrawableInstance::setLineWidth(int newLineWidth) +void BasicDrawableInstance::setLineWidth(float newLineWidth) { if (hasLineWidth && lineWidth == newLineWidth) return; setValuesChanged(); - hasLineWidth = true; lineWidth = newLineWidth; + hasLineWidth = true; + lineWidth = newLineWidth; } void BasicDrawableInstance::setStartTime(TimeInterval inStartTime) @@ -372,4 +373,10 @@ void BasicDrawableInstance::setTexturesChanged() renderTargetCon->modified = true; } +void BasicDrawableInstance::setInstanceData(int numInstances,RawDataRef data) +{ + this->numInstances = numInstances; + this->instData = data; +} + } diff --git a/common/WhirlyGlobeLib/src/BasicDrawableInstanceBuilder.cpp b/common/WhirlyGlobeLib/src/BasicDrawableInstanceBuilder.cpp index b6ff48c0d6..fe23403ab5 100644 --- a/common/WhirlyGlobeLib/src/BasicDrawableInstanceBuilder.cpp +++ b/common/WhirlyGlobeLib/src/BasicDrawableInstanceBuilder.cpp @@ -1,9 +1,8 @@ -/* - * BasicDrawableInstanceBuilder.cpp +/* BasicDrawableInstanceBuilder.cpp * WhirlyGlobeLib * * Created by Steve Gifford on 5/10/19. - * Copyright 2011-2019 mousebird consulting + * Copyright 2011-2021 mousebird consulting * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -15,7 +14,6 @@ * 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. - * */ #import "BasicDrawableInstanceBuilder.h" @@ -23,15 +21,12 @@ namespace WhirlyKit { -BasicDrawableInstanceBuilder::BasicDrawableInstanceBuilder(const std::string &name,Scene *scene) -: scene(scene) -{ -} - -BasicDrawableInstanceBuilder::~BasicDrawableInstanceBuilder() +BasicDrawableInstanceBuilder::BasicDrawableInstanceBuilder(std::string name,Scene *scene) : + scene(scene), + name(std::move(name)) { } - + void BasicDrawableInstanceBuilder::Init() { drawInst->programID = EmptyIdentity; @@ -77,6 +72,12 @@ void BasicDrawableInstanceBuilder::setEnableTimeRange(TimeInterval inStartEnable drawInst->endEnable = inEndEnable; } +void BasicDrawableInstanceBuilder::setFade(TimeInterval inFadeDown,TimeInterval inFadeUp) +{ + // TODO: Add fade to instances +// drawInst->setFade(inFadeDown,inFadeUp); +} + void BasicDrawableInstanceBuilder::setViewerVisibility(double minViewerDist,double maxViewerDist,const Point3d &viewerCenter) { drawInst->minViewerDist = minViewerDist; @@ -104,7 +105,7 @@ void BasicDrawableInstanceBuilder::setDrawOrder(int64_t newOrder) void BasicDrawableInstanceBuilder::setDrawPriority(unsigned int newPriority) { drawInst->hasDrawPriority = true; - drawInst->drawPriority = newPriority; + drawInst->drawPriority = (int)newPriority; } void BasicDrawableInstanceBuilder::setColor(const RGBAColor &color) @@ -115,7 +116,7 @@ void BasicDrawableInstanceBuilder::setColor(const RGBAColor &color) void BasicDrawableInstanceBuilder::setLineWidth(float lineWidth) { - drawInst->hasLineWidth = lineWidth; + drawInst->hasLineWidth = true; drawInst->setLineWidth(lineWidth); } @@ -134,7 +135,7 @@ void BasicDrawableInstanceBuilder::setRenderTarget(SimpleIdentity newRenderTarge drawInst->renderTargetID = newRenderTarget; } -void BasicDrawableInstanceBuilder::addTweaker(DrawableTweakerRef tweakRef) +void BasicDrawableInstanceBuilder::addTweaker(const DrawableTweakerRef &tweakRef) { drawInst->tweakers.insert(tweakRef); } @@ -172,17 +173,17 @@ void BasicDrawableInstanceBuilder::setUniBlock(const BasicDrawable::UniformBlock void BasicDrawableInstanceBuilder::setTexId(unsigned int which,SimpleIdentity inId) { - setupTexCoordEntry(which, 0); - - if (drawInst->texInfo.empty()) - return; + setupTexCoordEntry((int)which, 0); - drawInst->texInfo[which].texId = inId; + if (!drawInst->texInfo.empty()) + { + drawInst->texInfo[which].texId = inId; + } } void BasicDrawableInstanceBuilder::setTexIDs(const std::vector &texIDs) { - for (unsigned int ii=0;iitexInfo[ii].texId = texIDs[ii]; @@ -199,7 +200,7 @@ void BasicDrawableInstanceBuilder::setupTexCoordEntry(int which,int numReserve) if (which < drawInst->texInfo.size()) return; - for (unsigned int ii=(unsigned int)drawInst->texInfo.size();ii<=which;ii++) + for (auto ii=(unsigned int)drawInst->texInfo.size();ii<=which;ii++) { BasicDrawableInstance::TexInfo newInfo; drawInst->texInfo.push_back(newInfo); @@ -211,11 +212,14 @@ void BasicDrawableInstanceBuilder::setProgram(SimpleIdentity progID) drawInst->setProgram(progID); } -SimpleIdentity BasicDrawableInstanceBuilder::getDrawableID() +SimpleIdentity BasicDrawableInstanceBuilder::getDrawableID() const +{ + return drawInst ? drawInst->getId() : EmptyIdentity; +} + +void BasicDrawableInstanceBuilder::setInstanceData(int numInstance,RawDataRef data) { - if (drawInst) - return drawInst->getId(); - return EmptyIdentity; + drawInst->setInstanceData(numInstance, data); } } diff --git a/common/WhirlyGlobeLib/src/BasicDrawableInstanceBuilderGLES.cpp b/common/WhirlyGlobeLib/src/BasicDrawableInstanceBuilderGLES.cpp index 8961487f97..9a460d5907 100644 --- a/common/WhirlyGlobeLib/src/BasicDrawableInstanceBuilderGLES.cpp +++ b/common/WhirlyGlobeLib/src/BasicDrawableInstanceBuilderGLES.cpp @@ -1,9 +1,8 @@ -/* - * BasicDrawableInstanceBuilderGLES.cpp +/* BasicDrawableInstanceBuilderGLES.cpp * WhirlyGlobeLib * * Created by Steve Gifford on 5/10/19. - * Copyright 2011-2019 mousebird consulting + * Copyright 2011-2021 mousebird consulting * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -15,7 +14,6 @@ * 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. - * */ #import "BasicDrawableInstanceBuilderGLES.h" @@ -23,8 +21,9 @@ namespace WhirlyKit { -BasicDrawableInstanceBuilderGLES::BasicDrawableInstanceBuilderGLES(const std::string &name,Scene *scene) -: BasicDrawableInstanceBuilder(name,scene), drawableGotten(false) +BasicDrawableInstanceBuilderGLES::BasicDrawableInstanceBuilderGLES(std::string name,Scene *scene) : + BasicDrawableInstanceBuilder(std::move(name),scene), + drawableGotten(false) { auto drawInstGL = std::make_shared(name); drawInst = drawInstGL; diff --git a/common/WhirlyGlobeLib/src/BillboardManager.cpp b/common/WhirlyGlobeLib/src/BillboardManager.cpp index 58a61aca7c..18ee7483d4 100644 --- a/common/WhirlyGlobeLib/src/BillboardManager.cpp +++ b/common/WhirlyGlobeLib/src/BillboardManager.cpp @@ -73,7 +73,7 @@ BillboardSceneRep::~BillboardSceneRep() { } -void BillboardSceneRep::clearContents(SelectionManager *selectManager,ChangeSet &changes,TimeInterval when) +void BillboardSceneRep::clearContents(SelectionManagerRef &selectManager,ChangeSet &changes,TimeInterval when) { for (const auto it: drawIDs){ changes.push_back(new RemDrawableReq(it,when)); @@ -189,6 +189,8 @@ BillboardManager::BillboardManager() BillboardManager::~BillboardManager() { + std::lock_guard guardLock(lock); + for (BillboardSceneRepSet::iterator it = sceneReps.begin(); it != sceneReps.end(); ++it) delete *it; sceneReps.clear(); @@ -199,8 +201,7 @@ typedef std::map BuilderMap; /// Add billboards for display SimpleIdentity BillboardManager::addBillboards(std::vector billboards,const BillboardInfo &billboardInfo,ChangeSet &changes) { - SelectionManager *selectManager = (SelectionManager *)scene->getManager(kWKSelectionManager); - + SelectionManagerRef selectManager = std::dynamic_pointer_cast(scene->getManager(kWKSelectionManager)); BillboardSceneRep *sceneRep = new BillboardSceneRep(); sceneRep->fade = billboardInfo.fade; @@ -256,7 +257,7 @@ SimpleIdentity BillboardManager::addBillboards(std::vector billboard SimpleIdentity billID = sceneRep->getId(); - std::lock_guard guardLock(billLock); + std::lock_guard guardLock(lock); sceneReps.insert(sceneRep); return billID; @@ -264,9 +265,8 @@ SimpleIdentity BillboardManager::addBillboards(std::vector billboard void BillboardManager::enableBillboards(SimpleIDSet &billIDs,bool enable,ChangeSet &changes) { - SelectionManager *selectManager = (SelectionManager *)scene->getManager(kWKSelectionManager); - - std::lock_guard guardLock(billLock); + SelectionManagerRef selectManager = std::dynamic_pointer_cast(scene->getManager(kWKSelectionManager)); + std::lock_guard guardLock(lock); for (SimpleIDSet::iterator bit = billIDs.begin();bit != billIDs.end();++bit) { @@ -287,9 +287,8 @@ void BillboardManager::enableBillboards(SimpleIDSet &billIDs,bool enable,ChangeS /// Remove a group of billboards named by the given ID void BillboardManager::removeBillboards(SimpleIDSet &billIDs,ChangeSet &changes) { - SelectionManager *selectManager = (SelectionManager *)scene->getManager(kWKSelectionManager); - - std::lock_guard guardLock(billLock); + SelectionManagerRef selectManager = std::dynamic_pointer_cast(scene->getManager(kWKSelectionManager)); + std::lock_guard guardLock(lock); TimeInterval curTime = scene->getCurrentTime(); for (SimpleIDSet::iterator bit = billIDs.begin();bit != billIDs.end();++bit) diff --git a/common/WhirlyGlobeLib/src/CMakeLists.txt b/common/WhirlyGlobeLib/src/CMakeLists.txt index ffa91647bf..f15123ffd5 100644 --- a/common/WhirlyGlobeLib/src/CMakeLists.txt +++ b/common/WhirlyGlobeLib/src/CMakeLists.txt @@ -51,6 +51,7 @@ target_sources( "${CMAKE_CURRENT_LIST_DIR}/../include/LabelRenderer.h" "${CMAKE_CURRENT_LIST_DIR}/../include/LayoutManager.h" "${CMAKE_CURRENT_LIST_DIR}/../include/Lighting.h" + "${CMAKE_CURRENT_LIST_DIR}/../include/LinearTextBuilder.h" "${CMAKE_CURRENT_LIST_DIR}/../include/LineAndPointShadersGLES.h" "${CMAKE_CURRENT_LIST_DIR}/../include/LoadedTileNew.h" "${CMAKE_CURRENT_LIST_DIR}/../include/LoftManager.h" @@ -92,6 +93,7 @@ target_sources( "${CMAKE_CURRENT_LIST_DIR}/../include/QuadTileBuilder.h" "${CMAKE_CURRENT_LIST_DIR}/../include/QuadTreeNew.h" "${CMAKE_CURRENT_LIST_DIR}/../include/RawData.h" + "${CMAKE_CURRENT_LIST_DIR}/../include/RawPNGImage.h" "${CMAKE_CURRENT_LIST_DIR}/../include/RenderTarget.h" "${CMAKE_CURRENT_LIST_DIR}/../include/RenderTargetGLES.h" "${CMAKE_CURRENT_LIST_DIR}/../include/Scene.h" @@ -171,6 +173,7 @@ target_sources( "${CMAKE_CURRENT_LIST_DIR}/LabelRenderer.cpp" "${CMAKE_CURRENT_LIST_DIR}/LayoutManager.cpp" "${CMAKE_CURRENT_LIST_DIR}/Lighting.cpp" + "${CMAKE_CURRENT_LIST_DIR}/LinearTextBuilder.cpp" "${CMAKE_CURRENT_LIST_DIR}/LineAndPointShadersGLES.cpp" "${CMAKE_CURRENT_LIST_DIR}/LoadedTileNew.cpp" "${CMAKE_CURRENT_LIST_DIR}/LoftManager.cpp" @@ -212,6 +215,7 @@ target_sources( "${CMAKE_CURRENT_LIST_DIR}/QuadTileBuilder.cpp" "${CMAKE_CURRENT_LIST_DIR}/QuadTreeNew.cpp" "${CMAKE_CURRENT_LIST_DIR}/RawData.cpp" + "${CMAKE_CURRENT_LIST_DIR}/RawPNGImage.cpp" "${CMAKE_CURRENT_LIST_DIR}/RenderTarget.cpp" "${CMAKE_CURRENT_LIST_DIR}/RenderTargetGLES.cpp" "${CMAKE_CURRENT_LIST_DIR}/Scene.cpp" @@ -242,6 +246,7 @@ target_sources( "${CMAKE_CURRENT_LIST_DIR}/VectorData.cpp" "${CMAKE_CURRENT_LIST_DIR}/VectorManager.cpp" "${CMAKE_CURRENT_LIST_DIR}/VectorObject.cpp" + "${CMAKE_CURRENT_LIST_DIR}/VectorOffset.cpp" "${CMAKE_CURRENT_LIST_DIR}/VertexAttribute.cpp" "${CMAKE_CURRENT_LIST_DIR}/VertexAttributeGLES.cpp" "${CMAKE_CURRENT_LIST_DIR}/WhirlyGeometry.cpp" diff --git a/common/WhirlyGlobeLib/src/ComponentManager.cpp b/common/WhirlyGlobeLib/src/ComponentManager.cpp index 27814cac24..ad62c7d868 100644 --- a/common/WhirlyGlobeLib/src/ComponentManager.cpp +++ b/common/WhirlyGlobeLib/src/ComponentManager.cpp @@ -1,9 +1,8 @@ -/* - * ComponentManager.cpp +/* ComponentManager.cpp * WhirlyGlobeLib * * Created by Steve Gifford on 2/15/19. - * Copyright 2011-2019 mousebird consulting + * Copyright 2011-2021 mousebird consulting * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -15,20 +14,37 @@ * 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. - * */ #import "ComponentManager.h" #import "WhirlyKitLog.h" +#import "SharedAttributes.h" namespace WhirlyKit { -ComponentObject::ComponentObject() -: vectorOffset(0.0,0.0), isSelectable(false), -enable(false), underConstruction(false) + +static constexpr size_t TypicalRepUUIDs = 100; +static constexpr size_t TypicalUUIDComps = 1000; + +ComponentObject::ComponentObject(bool enable, bool selectable) : + enable(enable), + isSelectable(selectable), + underConstruction(false), + vectorOffset(0.0,0.0) { } - + +ComponentObject::ComponentObject(bool enable, bool selectable, const Dictionary &desc) : + ComponentObject(enable, selectable) +{ + if (!desc.empty()) + { + this->enable = desc.getBool(MaplyEnable, enable); + this->uuid = desc.getString(MaplyUUIDDesc); + this->representation = desc.getString(MaplyRepresentationDesc); + } +} + ComponentObject::~ComponentObject() { } @@ -50,98 +66,145 @@ void ComponentObject::clear() } ComponentManager::ComponentManager() - : layoutManager(NULL), markerManager(NULL), labelManager(NULL), vectorManager(NULL), wideVectorManager(NULL), - shapeManager(NULL), loftManager(NULL), billManager(NULL), geomManager(NULL), - fontTexManager(NULL), partSysManager(NULL) +: lastMaskID(0) { } - + ComponentManager::~ComponentManager() { + //std::lock_guard guardLock(lock); } - + void ComponentManager::setScene(Scene *scene) { - layoutManager = (LayoutManager *)scene->getManagerNoLock(kWKLayoutManager); - markerManager = (MarkerManager *)scene->getManagerNoLock(kWKMarkerManager); - labelManager = (LabelManager *)scene->getManagerNoLock(kWKLabelManager); - vectorManager = (VectorManager *)scene->getManagerNoLock(kWKVectorManager); - wideVectorManager = (WideVectorManager *)scene->getManagerNoLock(kWKWideVectorManager); - shapeManager = (ShapeManager *)scene->getManagerNoLock(kWKShapeManager); - chunkManager = (SphericalChunkManager *)scene->getManagerNoLock(kWKSphericalChunkManager); - loftManager = (LoftManager *)scene->getManagerNoLock(kWKLoftedPolyManager); - billManager = (BillboardManager *)scene->getManagerNoLock(kWKBillboardManager); - geomManager = (GeometryManager *)scene->getManagerNoLock(kWKGeometryManager); + layoutManager = scene->getManagerNoLock(kWKLayoutManager); + markerManager = scene->getManagerNoLock(kWKMarkerManager); + labelManager = scene->getManagerNoLock(kWKLabelManager); + vectorManager = scene->getManagerNoLock(kWKVectorManager); + wideVectorManager = scene->getManagerNoLock(kWKWideVectorManager); + shapeManager = scene->getManagerNoLock(kWKShapeManager); + chunkManager = scene->getManagerNoLock(kWKSphericalChunkManager); + loftManager = scene->getManagerNoLock(kWKLoftedPolyManager); + billManager = scene->getManagerNoLock(kWKBillboardManager); + geomManager = scene->getManagerNoLock(kWKGeometryManager); fontTexManager = scene->getFontTextureManager(); - partSysManager = (ParticleSystemManager *)scene->getManagerNoLock(kWKParticleSystemManager); + partSysManager = scene->getManagerNoLock(kWKParticleSystemManager); } -void ComponentManager::addComponentObject(ComponentObjectRef compObj) +void ComponentManager::addComponentObject(const ComponentObjectRef &compObj, ChangeSet &changes) { std::lock_guard guardLock(lock); - + compObj->underConstruction = false; - compObjs[compObj->getId()] = compObj; + compObjsById[compObj->getId()] = compObj; + + // Does the new object have a UUID? + if (!compObj->uuid.empty()) + { + if (compObjsByUUID.empty()) + { + compObjsByUUID.reserve(TypicalUUIDComps); + } + + // track it by that UUID + compObjsByUUID.insert(std::make_pair(compObj->uuid, compObj)); + + // Is the current representation for that UUID already set? + const auto hit = representations.find(compObj->uuid); + + // If a representation is set, show this item if it matches that representation. + // If no representation is set, show this item if its representation is blank. + const bool enable = (hit != representations.end()) ? (compObj->representation == hit->second) : compObj->representation.empty(); + + enableComponentObject(compObj, enable, changes); + } } bool ComponentManager::hasComponentObject(SimpleIdentity compID) { std::lock_guard guardLock(lock); - auto it = compObjs.find(compID); - return it != compObjs.end(); + const auto it = compObjsById.find(compID); + return it != compObjsById.end(); } - + void ComponentManager::removeComponentObject(PlatformThreadInfo *threadInfo,SimpleIdentity compID, ChangeSet &changes) { - SimpleIDSet compIDs; - compIDs.insert(compID); - + SimpleIDSet compIDs { compID }; removeComponentObjects(threadInfo,compIDs, changes); } void ComponentManager::removeComponentObjects(PlatformThreadInfo *threadInfo,const std::vector &compObjs,ChangeSet &changes) { SimpleIDSet compIDs; - + for (auto compObj: compObjs) { compIDs.insert(compObj->getId()); } - + removeComponentObjects(threadInfo,compIDs, changes); } +void ComponentManager::removeComponentObjects_NoLock(PlatformThreadInfo *threadInfo, + const SimpleIDSet &compIDs, + std::vector &objs) +{ + objs.reserve(compIDs.size()); + for (SimpleIdentity compID : compIDs) + { + const auto it = compObjsById.find(compID); + if (it == compObjsById.end()) + { + wkLogLevel(Warn,"Tried to delete object that doesn't exist: %d",compID); + return; + } + + const ComponentObjectRef &compObj = it->second; + + if (compObj->underConstruction) + { + wkLogLevel(Warn,"Deleting an object that's under construction"); + } + + if (!compObj->uuid.empty()) + { + const auto range = compObjsByUUID.equal_range(compObj->uuid); + for (auto i = range.first; i != range.second; ) + { + if (i->second->getId() == compID) + { + // "References and iterators to the erased elements are invalidated. Other references and iterators are not affected." + i = compObjsByUUID.erase(i); + } + else + { + ++i; + } + } + } + + objs.push_back(compObj); + + compObjsById.erase(it); + } +} + void ComponentManager::removeComponentObjects(PlatformThreadInfo *threadInfo,const SimpleIDSet &compIDs,ChangeSet &changes) { if (compIDs.empty()) return; - - std::vector compRefs; + std::vector compRefs; + { // Lock around all component objects std::lock_guard guardLock(lock); - - for (SimpleIdentity compID : compIDs) { - auto it = compObjs.find(compID); - if (it == compObjs.end()) - { - wkLogLevel(Warn,"Tried to delete object that doesn't exist: %d",compID); - return; - } - ComponentObjectRef compObj = it->second; - - if (compObj->underConstruction) { - wkLogLevel(Warn,"Deleting an object that's under construction"); - } - - compRefs.push_back(compObj); - - compObjs.erase(it); - } + removeComponentObjects_NoLock(threadInfo, compIDs, compRefs); } - - for (ComponentObjectRef compObj : compRefs) { + + SimpleIDSet maskIDs; + for (const ComponentObjectRef &compObj : compRefs) + { // Get rid of the various layer objects if (!compObj->markerIDs.empty()) markerManager->removeMarkers(compObj->markerIDs, changes); @@ -166,88 +229,314 @@ void ComponentManager::removeComponentObjects(PlatformThreadInfo *threadInfo,con // Giving the fonts 2s to stick around // This avoids problems with texture being paged out. // Without this we lose the textures before we're done with them - TimeInterval when = scene->getCurrentTime() + 2.0; + const TimeInterval when = scene->getCurrentTime() + 2.0; for (SimpleIdentity dStrID : compObj->drawStringIDs) - fontTexManager->removeString(dStrID, changes, when); + { + fontTexManager->removeString(threadInfo, dStrID, changes, when); + } } - if (!compObj->partSysIDs.empty()) + for (const auto partSysID : compObj->partSysIDs) { - for (SimpleIdentity partSysID : compObj->partSysIDs) - partSysManager->removeParticleSystem(partSysID, changes); + partSysManager->removeParticleSystem(partSysID, changes); } + if (!compObj->maskIDs.empty()) + maskIDs.insert(compObj->maskIDs.begin(),compObj->maskIDs.end()); } + + releaseMaskIDs(maskIDs); } - -void ComponentManager::enableComponentObject(SimpleIdentity compID, bool enable, ChangeSet &changes) + +void ComponentManager::enableComponentObject(SimpleIdentity compID, bool enable, ChangeSet &changes, bool resolveReps) { - SimpleIDSet compIDs; - compIDs.insert(compID); + ComponentObjectRef compRef; + + { + // Lock around all component objects + std::lock_guard guardLock(lock); - enableComponentObjects(compIDs, enable, changes); + const auto it = compObjsById.find(compID); + if (it == compObjsById.end()) + { + wkLogLevel(Warn,"Tried to enable/disable object that doesn't exist"); + return; + } + + compRef = it->second; + + if (compRef->underConstruction) + { + wkLogLevel(Warn,"Disable/enabled an object that's under construction"); + } + } + + enableComponentObject(compRef, enable, changes, resolveReps); } -void ComponentManager::enableComponentObjects(const SimpleIDSet &compIDs,bool enable,ChangeSet &changes) + +void ComponentManager::enableComponentObjects(const SimpleIDSet &compIDs,bool enable,ChangeSet &changes, bool resolveReps) { std::vector compRefs; - + compRefs.reserve(compIDs.size()); + { // Lock around all component objects std::lock_guard guardLock(lock); - + for (SimpleIdentity compID : compIDs) { - auto it = compObjs.find(compID); - if (it == compObjs.end()) + const auto it = compObjsById.find(compID); + if (it == compObjsById.end()) { wkLogLevel(Warn,"Tried to enable/disable object that doesn't exist"); return; } - ComponentObjectRef compObj = it->second; - - if (compObj->underConstruction) { + + const ComponentObjectRef &compObj = it->second; + + if (compObj->underConstruction) + { wkLogLevel(Warn,"Disable/enabled an object that's under construction"); } - + compRefs.push_back(compObj); } } - - for (ComponentObjectRef compObj: compRefs) + + enableComponentObjects(compRefs, enable, changes, resolveReps); +} + +// Determine the new state for "that" given a change to "this." +static bool ResolveRepresentationState(const ComponentObjectRef &thisObj, const ComponentObjectRef &thatObj) +{ + assert(thisObj->uuid == thatObj->uuid && !thisObj->uuid.empty()); + + bool const enable = thisObj->enable; + if (thisObj.get() == thatObj.get()) + { + return enable; + } + else if (thatObj->representation == thisObj->representation) + { + // Another instance of the same representation. + // For example, there may be two versions while transitioning between + // levels, or the same object may appear in multiple tiles of a dataset. + // Apply the same state to it. + return enable; + } + else if (thisObj->representation.empty()) + { + // If the default representation is being enabled, disable others + // If it's being disabled, don't mess with the others. + return !enable || thatObj->enable; + } + // If a non-default state is being enabled, disable others + else if (enable) { - // Note: Should lock just around this component object - // But I'm not sure I want one std::mutex per object - compObj->enable = enable; + return false; + } + // If a non-default state is being disabled, enable the default state + else if (thatObj->representation.empty()) + { + return true; + } - if (!compObj->vectorIDs.empty()) - vectorManager->enableVectors(compObj->vectorIDs, enable, changes); - if (!compObj->wideVectorIDs.empty()) - wideVectorManager->enableVectors(compObj->wideVectorIDs, enable, changes); - if (!compObj->markerIDs.empty()) - markerManager->enableMarkers(compObj->markerIDs, enable, changes); - if (!compObj->labelIDs.empty()) - labelManager->enableLabels(compObj->labelIDs, enable, changes); - if (!compObj->shapeIDs.empty()) - shapeManager->enableShapes(compObj->shapeIDs, enable, changes); - if (!compObj->billIDs.empty()) - billManager->enableBillboards(compObj->billIDs, enable, changes); - if (!compObj->loftIDs.empty()) - loftManager->enableLoftedPolys(compObj->loftIDs, enable, changes); - if (geomManager && !compObj->geomIDs.empty()) - geomManager->enableGeometry(compObj->geomIDs, enable, changes); - if (!compObj->chunkIDs.empty()) + // No change + return thatObj->enable; +} + +void ComponentManager::enableComponentObject(const ComponentObjectRef &compObj, bool enable, ChangeSet &changes, bool resolveReps) +{ + // Note: Should lock just around this component object + // But I'm not sure I want one std::mutex per object + compObj->enable = enable; + + if (!compObj->vectorIDs.empty()) + vectorManager->enableVectors(compObj->vectorIDs, enable, changes); + if (!compObj->wideVectorIDs.empty()) + wideVectorManager->enableVectors(compObj->wideVectorIDs, enable, changes); + if (!compObj->markerIDs.empty()) + markerManager->enableMarkers(compObj->markerIDs, enable, changes); + if (!compObj->labelIDs.empty()) + labelManager->enableLabels(compObj->labelIDs, enable, changes); + if (!compObj->shapeIDs.empty()) + shapeManager->enableShapes(compObj->shapeIDs, enable, changes); + if (!compObj->billIDs.empty()) + billManager->enableBillboards(compObj->billIDs, enable, changes); + if (!compObj->loftIDs.empty()) + loftManager->enableLoftedPolys(compObj->loftIDs, enable, changes); + if (geomManager && !compObj->geomIDs.empty()) + geomManager->enableGeometry(compObj->geomIDs, enable, changes); + if (!compObj->chunkIDs.empty()) + { + for (auto const & it : compObj->chunkIDs) { - for (SimpleIDSet::iterator it = compObj->chunkIDs.begin(); - it != compObj->chunkIDs.end(); ++it) - chunkManager->enableChunk(*it, enable, changes); + chunkManager->enableChunk(it, enable, changes); } - if (partSysManager && !compObj->partSysIDs.empty()) { - for (SimpleIDSet::iterator it = compObj->partSysIDs.begin(); - it != compObj->partSysIDs.end(); ++it) - partSysManager->enableParticleSystem(*it, enable, changes); + } + if (partSysManager && !compObj->partSysIDs.empty()) + { + for (auto const it : compObj->partSysIDs) + { + partSysManager->enableParticleSystem(it, enable, changes); + } + } + + // Handle the other representations of the same thing? + if (resolveReps && !compObj->uuid.empty()) + { + // Consider all the objects we know about with this same uuid + for (auto i = compObjsByUUID.equal_range(compObj->uuid); i.first != i.second; ++i.first) + { + // If the desired state is not the state it's in, toggle it + const auto &otherObj = i.first->second; + if (ResolveRepresentationState(compObj, otherObj) != otherObj->enable) + { + enableComponentObject(otherObj, !otherObj->enable, changes, false); + } } } } - + +static const std::less DefStringLess; +static inline bool HasUUID(const ComponentObjectRef &obj) +{ + return !obj->uuid.empty(); +} +static inline bool ByUUIDThenRep(const ComponentObjectRef &l, const ComponentObjectRef &r) +{ + return DefStringLess(l->uuid, r->uuid) || // less + (!DefStringLess(r->uuid, l->uuid) && // equal + DefStringLess(l->representation, r->representation)); +} + +void ComponentManager::enableComponentObjects(const std::vector &compRefs, bool enable, + ChangeSet &changes, bool resolveReps) +{ + // It's probably worth an array scan here to avoid unnecessary heap allocations + if (resolveReps && compRefs.size() > 1 && + std::any_of(compRefs.begin(), compRefs.end(), HasUUID)) + { + // make a copy, sorted by uuid then by representation + std::vector sorted(compRefs.size()); + std::partial_sort_copy(compRefs.begin(), compRefs.end(), sorted.begin(), sorted.end(), ByUUIDThenRep); + + // Iterate the sorted copy by groups of uuids + for (auto i = sorted.begin(); i != sorted.end(); ) + { + const auto &obj = *i; + const auto groupEnd = std::find_if(i, sorted.end(), [&](const auto &j) { return j->uuid != obj->uuid; }); + const auto next = obj->uuid.empty() ? groupEnd : std::next(i); + + // For items with no uuid, just call each of them normally. + // For items with a uuid, enable or disable the first item in each group, which + // will be the default representation (blank) if it's present. The logic in the + // single-item version of enableComponentObject will take care of the rest. + for (; i != next; ++i) + { + enableComponentObject(*i, enable, changes, true); + } + + i = groupEnd; + } + return; + } + + // Don't resolve individual items unless we skipped the above because there's only one item. + resolveReps = resolveReps && (compRefs.size() == 1); + + for (const auto &compObj : compRefs) + { + enableComponentObject(compObj, enable, changes, resolveReps); + } +} + +template +void ComponentManager::setRepresentation(const std::string &repName, + const std::string &fallback, + TIter beg, TIter end, + ChangeSet &changes) +{ + std::vector enableObjs, disableObjs; + + std::unique_lock guardLock(lock); + + for (; beg != end; ++beg) + { + const std::string &uuid = *beg; + + if (repName.empty()) + { + // Don't store blank entries, remove them to return to default (un-set) state + representations.erase(uuid); + } + else + { + if (representations.empty()) + { + // Now that we know they're using the representation feature, + // bypass the first few allocation cycles when adding items. + representations.reserve(TypicalRepUUIDs); + } + + const auto insertResult = representations.insert(std::make_pair(uuid, repName)); + if (!insertResult.second) + { + // key exists, update the value + insertResult.first->second = repName; + } + } + + // Find all component items with matching UUIDs + const auto range = compObjsByUUID.equal_range(uuid); + + // Put each of them in the enable or disable lists + disableObjs.reserve(std::distance(range.first, range.second)); + for (auto i = range.first; i != range.second; ++i) + { + const auto &obj = i->second; + ((obj->representation == repName) ? enableObjs : disableObjs).push_back(obj); + } + + // If there are no matches, enable the fallback (usually blank) representation instead. + if (enableObjs.empty() && !disableObjs.empty()) + { + for (size_t i = 0; i < disableObjs.size(); ++i) + { + const auto &obj = disableObjs[i]; + if (obj->representation == fallback) + { + // Move the item from the disable to enable list + enableObjs.push_back(obj); + disableObjs.erase(std::next(disableObjs.begin(), i)); + } + } + } + } + + guardLock.unlock(); + + if (!enableObjs.empty()) enableComponentObjects(enableObjs, true, changes); + if (!disableObjs.empty()) enableComponentObjects(disableObjs, false, changes); +} + +void ComponentManager::setRepresentation(const std::string &repName, const std::string &fallback, + const std::vector &uuids, ChangeSet &changes) +{ + setRepresentation(repName, fallback, uuids.begin(), uuids.end(), changes); +} + +void ComponentManager::setRepresentation(const std::string &repName, const std::string &fallback, + const std::set &uuids, ChangeSet &changes) +{ + setRepresentation(repName, fallback, uuids.begin(), uuids.end(), changes); +} + +void ComponentManager::setRepresentation(const std::string &repName, const std::string &fallback, + const std::unordered_set &uuids, ChangeSet &changes) +{ + setRepresentation(repName, fallback, uuids.begin(), uuids.end(), changes); +} + void ComponentManager::setUniformBlock(const SimpleIDSet &compIDs,const RawDataRef &uniBlock,int bufferID,ChangeSet &changes) { std::vector compRefs; @@ -258,19 +547,18 @@ void ComponentManager::setUniformBlock(const SimpleIDSet &compIDs,const RawDataR for (SimpleIdentity compID : compIDs) { - auto it = compObjs.find(compID); - if (it == compObjs.end()) + auto it = compObjsById.find(compID); + if (it == compObjsById.end()) { wkLogLevel(Warn,"Tried to set uniform block on object that doesn't exist"); return; } - ComponentObjectRef compObj = it->second; - - compRefs.push_back(compObj); + compRefs.push_back(it->second); } } - for (auto compObj : compRefs) { + for (const auto &compObj : compRefs) + { if (shapeManager && !compObj->shapeIDs.empty()) { shapeManager->setUniformBlock(compObj->shapeIDs,uniBlock,bufferID,changes); } @@ -283,6 +571,50 @@ void ComponentManager::setUniformBlock(const SimpleIDSet &compIDs,const RawDataR // TODO: Fill this in for the other object types } } + +SimpleIdentity ComponentManager::retainMaskByName(const std::string &maskName) +{ + std::lock_guard guardLock(maskLock); + + // Add an entry or reuse one + MaskEntryRef entry; + auto it = maskEntriesByName.find(maskName); + if (it == maskEntriesByName.end()) { + entry = std::make_shared(); + entry->name = maskName; + entry->maskID = ++lastMaskID; + entry->refCount = 0; + maskEntriesByName[maskName] = entry; + maskEntriesByID[entry->maskID] = entry; + } else { + entry = it->second; + } + entry->refCount++; + + return entry->maskID; +} + +void ComponentManager::releaseMaskIDs(const SimpleIDSet &maskIDs) +{ + std::lock_guard guardLock(maskLock); + + for (auto maskID: maskIDs) { + // Reduce reference count + auto it = maskEntriesByID.find(maskID); + if (it != maskEntriesByID.end()) { + auto entry = it->second; + entry->refCount--; + + // Erase it when we're done + if (entry->refCount <= 0) { + maskEntriesByID.erase(it); + auto it2 = maskEntriesByName.find(entry->name); + if (it2 != maskEntriesByName.end()) + maskEntriesByName.erase(it2); + } + } + } +} std::vector > ComponentManager::findVectors(const Point2d &pt,double maxDist,ViewStateRef viewState,const Point2f &frameSize,bool multi) { @@ -293,32 +625,35 @@ std::vector > ComponentManager::fi { std::lock_guard guardLock(lock); - for (auto it: compObjs) { - auto compObj = it.second; + for (const auto &kvp: compObjsById) + { + const auto &compObj = kvp.second; if (compObj->enable && compObj->isSelectable && !compObj->vecObjs.empty()) + { compRefs.push_back(compObj); + } } } // Work through the vector objects - for (auto compObj: compRefs) { - auto center = compObj->vectorOffset; - Point2d coord; - coord.x() = pt.x()-center.x(); - coord.y() = pt.y()-center.y(); - - for (auto vecObj: compObj->vecObjs) { - if (vecObj->pointInside(pt)) { - + for (const auto &compObj: compRefs) + { + const auto ¢er = compObj->vectorOffset; + const Point2d coord = { pt.x()-center.x(), pt.y()-center.y() }; + + for (auto vecObj: compObj->vecObjs) + { + if (vecObj->pointInside(pt)) + { rets.push_back(std::make_pair(compObj, vecObj)); if (!multi) break; continue; } - if (vecObj->pointNearLinear(coord, maxDist, viewState, frameSize)) { + if (vecObj->pointNearLinear(coord, maxDist, viewState, frameSize)) + { rets.push_back(std::make_pair(compObj, vecObj)); - if (!multi) break; } diff --git a/common/WhirlyGlobeLib/src/DictionaryC.cpp b/common/WhirlyGlobeLib/src/DictionaryC.cpp index 7b9d668179..827a7d6630 100644 --- a/common/WhirlyGlobeLib/src/DictionaryC.cpp +++ b/common/WhirlyGlobeLib/src/DictionaryC.cpp @@ -1,9 +1,8 @@ -/* - * Dictionary.cpp +/* Dictionary.cpp * WhirlyGlobeLib * * Created by Steve Gifford on 12/16/13. - * Copyright 2011-2013 mousebird consulting + * Copyright 2011-2021 mousebird consulting * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -15,11 +14,11 @@ * 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. - * */ #import #import "DictionaryC.h" +#import "WhirlyKitLog.h" namespace WhirlyKit { @@ -48,7 +47,7 @@ MutableDictionaryC::MutableDictionaryC(const MutableDictionaryC &that) { } -MutableDictionaryC::MutableDictionaryC(MutableDictionaryC &&that) +MutableDictionaryC::MutableDictionaryC(MutableDictionaryC &&that) noexcept : intVals(std::move(that.intVals)) , int64Vals(std::move(that.int64Vals)) , dVals(std::move(that.dVals)) @@ -60,11 +59,6 @@ MutableDictionaryC::MutableDictionaryC(MutableDictionaryC &&that) { } -MutableDictionaryC::~MutableDictionaryC() -{ - clear(); -} - //bool MutableDictionaryC::parseJSON(const std::string jsonString) //{ // json_string json = jsonString; @@ -149,7 +143,7 @@ MutableDictionaryC &MutableDictionaryC::operator = (const MutableDictionaryC &th return *this; } -MutableDictionaryC &MutableDictionaryC::operator = (MutableDictionaryC &&that) +MutableDictionaryC &MutableDictionaryC::operator = (MutableDictionaryC &&that) noexcept { intVals = std::move(that.intVals); int64Vals = std::move(that.int64Vals); @@ -270,7 +264,7 @@ void MutableDictionaryC::removeField(const std::string &name) void MutableDictionaryC::removeField(unsigned int key) { - // TODO: WE're "leaking" (not actually) leaking space in the data arrays + // We're "leaking" (via fragmentation) space in the data arrays const auto it = valueMap.find(key); if (it != valueMap.end()) valueMap.erase(it); @@ -293,8 +287,9 @@ int MutableDictionaryC::getInt(unsigned int key,int defVal) const case DictTypeInt: return intVals[val.entry]; case DictTypeInt64: return (int)int64Vals[val.entry]; case DictTypeDouble: return (int)dVals[val.entry]; - // TODO: Maybe parse the string - default: return defVal; + default: + wkLogLevel(Warn, "Unsupported conversion from type %d to int", val.type); + return defVal; } } @@ -316,8 +311,9 @@ SimpleIdentity MutableDictionaryC::getIdentity(unsigned int key) const case DictTypeInt64: case DictTypeIdentity: return int64Vals[val.entry]; case DictTypeDouble: return (SimpleIdentity)dVals[val.entry]; - // TODO: Maybe parse the string - default: return EmptyIdentity; + default: + wkLogLevel(Warn, "Unsupported conversion from type %d to identity", val.type); + return EmptyIdentity; } } @@ -339,18 +335,16 @@ int64_t MutableDictionaryC::getInt64(unsigned int key,int64_t defVal) const case DictTypeInt64: case DictTypeIdentity: return int64Vals[val.entry]; case DictTypeDouble: return (int64_t)dVals[val.entry]; - // TODO: Maybe parse the string - default: return defVal; + default: + wkLogLevel(Warn, "Unsupported conversion from type %d to int64", val.type); + return defVal; } } bool MutableDictionaryC::getBool(const std::string &name,bool defVal) const { const auto it = stringMap.find(name); - if (it == stringMap.end()) - return defVal; - - return getBool(it->second); + return (it == stringMap.end()) ? defVal : getBool(it->second, defVal); } bool MutableDictionaryC::getBool(unsigned int key,bool defVal) const @@ -361,12 +355,10 @@ bool MutableDictionaryC::getBool(unsigned int key,bool defVal) const const auto &val = it->second; switch (val.type) { - case DictTypeInt: - return intVals[val.entry] != 0; - case DictTypeInt64: - return int64Vals[val.entry] != 0; - // TODO: Maybe parse the string + case DictTypeInt: return intVals[val.entry] != 0; + case DictTypeInt64: return int64Vals[val.entry] != 0; default: + wkLogLevel(Warn, "Unsupported conversion from type %d to bool", val.type); return defVal; } } @@ -374,84 +366,31 @@ bool MutableDictionaryC::getBool(unsigned int key,bool defVal) const RGBAColor MutableDictionaryC::getColor(const std::string &name,const RGBAColor &defVal) const { const auto it = stringMap.find(name); - if (it == stringMap.end()) - return defVal; + return (it == stringMap.end()) ? defVal : getColor(it->second,defVal); +} - return getColor(it->second,defVal); +RGBAColor ARGBtoRGBAColor(uint32_t v) +{ + return { (uint8_t)(v >> 16),(uint8_t)(v >> 8),(uint8_t)v,(uint8_t)(v >> 24) }; } -static RGBAColor parseColor(const char* const p, RGBAColor ret) +RGBAColor parseColor(const char* const p, RGBAColor ret) { char* end = nullptr; - const uint32_t iVal = std::strtol(p, &end, 16); - const auto len = end - p; + const auto v = (uint32_t)std::strtol(p, &end, 16); // TODO: These should probably be RRGGBBAA, but they're AARRGGBB for now for backward (probably) compatibility... - switch (len) + switch (end - p) { -#if 1 +#define DUP4(x) (uint8_t)((uint8_t)(x) | ((uint8_t)(x) << 4)) // 0N => NN case 3: // #RGB => R=RR G=GG B=BB A=FF - ret.b = (iVal ) & 0xF; - ret.g = (iVal >> 4) & 0xF; - ret.r = (iVal >> 8) & 0xF; - ret.b |= ret.b << 4; - ret.g |= ret.g << 4; - ret.r |= ret.r << 4; - ret.a = 0xFF; - break; + return RGBAColor(DUP4(v >> 8), DUP4(v >> 4), DUP4(v), 0xFF); case 4: // #ARGB => R=RR G=GG B=BB A=AA - ret.b = (iVal ) & 0xF; - ret.g = (iVal >> 4) & 0xF; - ret.r = (iVal >> 8) & 0xF; - ret.a = (iVal >> 12) & 0xF; - ret.b |= ret.b << 4; - ret.g |= ret.g << 4; - ret.r |= ret.r << 4; - ret.a |= ret.a << 4; - break; - case 6: // #RRGGBB => R=RR G=GG B=BB A=FF - ret.b = (iVal ) & 0xFF; - ret.g = (iVal >> 8) & 0xFF; - ret.r = (iVal >> 16) & 0xFF; - ret.a = 0xFF; - break; - case 8: // #AARRGGBB => R=RR G=GG B=BB A=AA - ret.b = (iVal ) & 0xFF; - ret.g = (iVal >> 8) & 0xFF; - ret.r = (iVal >> 16) & 0xFF; - ret.a = (iVal >> 24) & 0xFF; -#else - case 3: // #RGB => R=RR G=GG B=BB A=FF - ret.b = (iVal ) & 0xF; - ret.g = (iVal >> 4) & 0xF; - ret.r = (iVal >> 8) & 0xF; - ret.b |= ret.b << 4; - ret.g |= ret.g << 4; - ret.r |= ret.r << 4; - ret.a = 0xFF; - break; - case 4: // #RGBA => R=RR G=GG B=BB A=AA - ret.a = (iVal ) & 0xF; - ret.b = (iVal 4) & 0xF; - ret.g = (iVal >> 8) & 0xF; - ret.r = (iVal >> 12) & 0xF; - ret.b |= ret.b << 4; - ret.g |= ret.g << 4; - ret.r |= ret.r << 4; - ret.a |= ret.a << 4; - break; - case 6: // #RRGGBB => R=RR G=GG B=BB A=FF - ret.b = (iVal ) & 0xFF; - ret.g = (iVal >> 8) & 0xFF; - ret.r = (iVal >> 16) & 0xFF; - ret.a = 0xFF; - break; - case 8: // #RRGGBBAA => R=RR G=GG B=BB A=AA - ret.a = (iVal ) & 0xFF; - ret.b = (iVal >> 8) & 0xFF; - ret.g = (iVal >> 16) & 0xFF; - ret.r = (iVal >> 24) & 0xFF; -#endif + return RGBAColor(DUP4(v >> 8), DUP4(v >> 4), DUP4(v), DUP4(v >> 12)); +#undef DUP4 + case 6: return ARGBtoRGBAColor(v | 0xFF000000); // #RRGGBB => R=RR G=GG B=BB A=FF + case 8: return ARGBtoRGBAColor(v); // #AARRGGBB => R=RR G=GG B=BB A=AA + default: break; } return ret; } @@ -476,17 +415,11 @@ RGBAColor MutableDictionaryC::getColor(unsigned int key,const RGBAColor &defVal) } case DictTypeInt: { - const uint32_t iVal = intVals[val.entry]; - RGBAColor ret; - ret.b = iVal & 0xFF; - ret.g = (iVal >> 8) & 0xFF; - ret.r = (iVal >> 16) & 0xFF; - ret.a = (iVal >> 24) & 0xFF; - return ret; + return ARGBtoRGBAColor(intVals[val.entry]); } // No idea what this means - case DictTypeDouble: default: + wkLogLevel(Warn, "Unsupported conversion from type %d to color", val.type); return defVal; } @@ -496,10 +429,7 @@ RGBAColor MutableDictionaryC::getColor(unsigned int key,const RGBAColor &defVal) double MutableDictionaryC::getDouble(const std::string &name,double defVal) const { const auto it = stringMap.find(name); - if (it == stringMap.end()) - return defVal; - - return getDouble(it->second,defVal); + return (it == stringMap.end()) ? defVal : getDouble(it->second,defVal); } double MutableDictionaryC::getDouble(unsigned int key,double defVal) const @@ -510,47 +440,19 @@ double MutableDictionaryC::getDouble(unsigned int key,double defVal) const const auto &val = it->second; switch (val.type) { - case DictTypeInt: - return intVals[val.entry]; + case DictTypeInt: return intVals[val.entry]; case DictTypeInt64: - case DictTypeIdentity: - return int64Vals[val.entry]; - case DictTypeDouble: - return dVals[val.entry]; - // TODO: Maybe parse the string + case DictTypeIdentity: return int64Vals[val.entry]; + case DictTypeDouble: return dVals[val.entry]; default: - return 0; + wkLogLevel(Warn, "Unsupported conversion from type %d to double", val.type); + return defVal; } } std::string MutableDictionaryC::getString(const std::string &name) const { - const auto it = stringMap.find(name); - return (it != stringMap.end()) ? getString(it->second) : std::string(); -} - -std::string MutableDictionaryC::getString(unsigned int key) const -{ - const auto it = valueMap.find(key); - if (it == valueMap.end()) - return std::string(); - auto const &val = it->second; - switch (val.type) { - case DictTypeString: - return stringVals[val.entry]; - case DictTypeInt: - return std::to_string(intVals[val.entry]); - case DictTypeInt64: - case DictTypeIdentity: - return std::to_string(int64Vals[val.entry]); - case DictTypeDouble: - return std::to_string(dVals[val.entry]); - case DictTypeNone: - case DictTypeObject: - case DictTypeDictionary: - case DictTypeArray: - return std::string(); - } + return getString(name, std::string()); } std::string MutableDictionaryC::getString(const std::string &name,const std::string &defVal) const @@ -559,10 +461,33 @@ std::string MutableDictionaryC::getString(const std::string &name,const std::str return (it != stringMap.end()) ? getString(it->second,defVal) : defVal; } +std::string MutableDictionaryC::getString(unsigned int key) const +{ + return getString(key, std::string()); +} + std::string MutableDictionaryC::getString(unsigned int key,const std::string &defVal) const { const auto it = valueMap.find(key); - return (it != valueMap.end()) ? stringVals[it->second.entry] : defVal; + if (it != valueMap.end()) + { + const auto &value = it->second; + switch (value.type) + { + case DictTypeString: return stringVals[value.entry]; + case DictTypeInt: return std::to_string(intVals[value.entry]); + case DictTypeInt64: + case DictTypeIdentity: return std::to_string(int64Vals[value.entry]); + case DictTypeDouble: return std::to_string(dVals[value.entry]); + case DictTypeNone: + case DictTypeObject: + case DictTypeDictionary: + case DictTypeArray: + wkLogLevel(Warn, "Unsupported conversion from type %d to string", value.type); + break; + } + } + return defVal; } DictionaryRef MutableDictionaryC::getDict(const std::string &name) const @@ -574,11 +499,16 @@ DictionaryRef MutableDictionaryC::getDict(const std::string &name) const DictionaryRef MutableDictionaryC::getDict(unsigned int key) const { const auto it = valueMap.find(key); - if (it != valueMap.end()) { + if (it != valueMap.end()) + { const auto &val = it->second; if (val.type == DictTypeDictionary) + { return dictVals[val.entry]; + } + wkLogLevel(Warn, "Unsupported conversion from type %d to dictionary", val.type); } + wkLogLevel(Warn, "Missing key %d", key); return DictionaryRef(); } @@ -605,7 +535,9 @@ DictionaryEntryCRef MutableDictionaryC::makeEntryRef(const Value &val) const case DictTypeDictionary: return std::make_shared(dictVals[val.entry]); case DictTypeArray: return std::make_shared(formArray(val.entry)); case DictTypeObject: - case DictTypeNone: return DictionaryEntryCRef(); + case DictTypeNone: + wkLogLevel(Warn, "Unsupported conversion from type %d to entry", val.type); + return DictionaryEntryCRef(); } } @@ -618,8 +550,9 @@ std::vector MutableDictionaryC::getArray(const std::string & std::vector MutableDictionaryC::getArray(unsigned int key) const { const auto it = valueMap.find(key); - if (it == valueMap.end() || it->second.type != DictTypeArray) + if (it == valueMap.end() || it->second.type != DictTypeArray) { return std::vector(); + } const auto arrayVal = arrayVals[it->second.entry]; @@ -640,7 +573,8 @@ std::vector MutableDictionaryC::formArray(int idx) const std::vector rets; rets.reserve(arrVals.size()); - for (const auto &arrEntry: arrVals) { + for (const auto &arrEntry: arrVals) + { rets.push_back(makeEntryRef(arrEntry)); } @@ -652,7 +586,9 @@ std::vector MutableDictionaryC::getKeys() const std::vector keys; keys.reserve(valueMap.size()); for (const auto &value : valueMap) + { keys.push_back(stringVals[value.first]); + } return keys; } @@ -660,7 +596,7 @@ std::vector MutableDictionaryC::getKeys() const int MutableDictionaryC::getKeyID(const std::string &name) { const auto &it = stringMap.find(name); - return (it != stringMap.end()) ? it->second : -1; + return (it != stringMap.end()) ? (int)it->second : -1; } void MutableDictionaryC::setInt(const std::string &name,int val) @@ -672,6 +608,16 @@ void MutableDictionaryC::setInt(unsigned int key,int val) set(key, val, DictTypeInt, intVals); } +void MutableDictionaryC::setInt64(const std::string &name,int64_t val) +{ + setInt64(addKeyID(name),val); +} + +void MutableDictionaryC::setInt64(unsigned int key,int64_t val) +{ + set(key, val, DictTypeInt64, int64Vals); +} + void MutableDictionaryC::setIdentifiable(const std::string &name,SimpleIdentity val) { setIdentifiable(addKeyID(name),val); @@ -699,7 +645,9 @@ void MutableDictionaryC::setString(unsigned int key,const std::string &val) const auto &it = valueMap.find(key); // Can't reuse string entries because we share them if (it != valueMap.end()) + { valueMap.erase(it); + } // Make a new one (psst, it's the same as the keys) const auto stringID = addString(val); @@ -723,29 +671,26 @@ void MutableDictionaryC::setupArray(const std::vector &entr if (entry) { switch (entry->getType()) { case DictTypeInt: - out.push_back(Value(DictTypeInt,intVals.size())); + out.emplace_back(DictTypeInt,intVals.size()); intVals.push_back(entry->getInt()); break; case DictTypeIdentity: case DictTypeInt64: - out.push_back(Value(DictTypeInt64,int64Vals.size())); + out.emplace_back(DictTypeInt64,int64Vals.size()); int64Vals.push_back(entry->getInt64()); break; case DictTypeDouble: - out.push_back(Value(DictTypeDouble,dVals.size())); + out.emplace_back(DictTypeDouble,dVals.size()); dVals.push_back(entry->getDouble()); break; case DictTypeString: - out.push_back(Value(DictTypeString,addString(entry->getString()))); + out.emplace_back(DictTypeString,addString(entry->getString())); break; case DictTypeDictionary: - { - auto theDict = std::dynamic_pointer_cast(entry->getDict()); - if (theDict) { - out.push_back(Value(DictTypeDictionary,dictVals.size())); + if (auto theDict = std::dynamic_pointer_cast(entry->getDict())) { + out.emplace_back(DictTypeDictionary,dictVals.size()); dictVals.push_back(theDict); } - } break; case DictTypeArray: { @@ -754,13 +699,15 @@ void MutableDictionaryC::setupArray(const std::vector &entr std::vector locArr; setupArray(theArray->getArrayC(), locArr); - out.push_back(Value(DictTypeArray,arrayVals.size())); + out.emplace_back(DictTypeArray,arrayVals.size()); arrayVals.push_back(locArr); } + break; } + case DictTypeObject: + wkLogLevel(Warn, "Unsupported conversion from object to array"); break; case DictTypeNone: - case DictTypeObject: break; } } @@ -780,6 +727,7 @@ void MutableDictionaryC::setArray(unsigned int key,const std::vector theEntries; + theEntries.reserve(entries.size()); for (auto &entry : entries) { if (auto entryC = std::dynamic_pointer_cast(entry)) { theEntries.push_back(entryC); @@ -801,9 +749,12 @@ void MutableDictionaryC::setArray(unsigned int key,const std::vector theEntries; theEntries.reserve(entries.size()); - for (auto &entry : entries) { + for (auto &entry : entries) + { if (auto theEntry = std::dynamic_pointer_cast(entry)) + { theEntries.push_back(theEntry); + } } setArray(key,theEntries); @@ -812,7 +763,9 @@ void MutableDictionaryC::setArray(unsigned int key,const std::vector(inOther)) + { addEntries(other); + } } void MutableDictionaryC::addEntries(const MutableDictionaryC *other) @@ -820,7 +773,8 @@ void MutableDictionaryC::addEntries(const MutableDictionaryC *other) // Map from theirs to our strings std::vector stringRemap; stringRemap.reserve(other->stringVals.size()); - for (const auto &entry : other->stringVals) { + for (const auto &entry : other->stringVals) + { const auto newStringID = addString(entry); stringRemap.push_back(newStringID); } @@ -846,29 +800,31 @@ void MutableDictionaryC::addEntries(const MutableDictionaryC *other) const auto arrayStart = arrayVals.size(); for (const auto &arr: other->arrayVals) { std::vector outArr; + outArr.reserve(arr.size()); for (const auto &arrEntry: arr) { switch (arrEntry.type) { case DictTypeString: - outArr.push_back(Value(DictTypeString,stringRemap[arrEntry.entry])); + outArr.emplace_back(DictTypeString,stringRemap[arrEntry.entry]); break; case DictTypeInt: - outArr.push_back(Value(DictTypeInt,arrEntry.entry+intStart)); + outArr.emplace_back(DictTypeInt,arrEntry.entry+intStart); break; case DictTypeInt64: case DictTypeIdentity: - outArr.push_back(Value(DictTypeInt64,arrEntry.entry+int64Start)); + outArr.emplace_back(DictTypeInt64,arrEntry.entry+int64Start); break; case DictTypeDouble: - outArr.push_back(Value(DictTypeDouble,arrEntry.entry+dStart)); + outArr.emplace_back(DictTypeDouble,arrEntry.entry+dStart); break; case DictTypeDictionary: - outArr.push_back(Value(DictTypeDictionary,arrEntry.entry+dictStart)); + outArr.emplace_back(DictTypeDictionary,arrEntry.entry+dictStart); break; case DictTypeArray: - outArr.push_back(Value(DictTypeArray,arrEntry.entry+arrayStart)); + outArr.emplace_back(DictTypeArray,arrEntry.entry+arrayStart); break; case DictTypeObject: case DictTypeNone: + wkLogLevel(Warn, "Unsupported conversion from type %d to array entry", arrEntry.type); break; } } @@ -878,7 +834,7 @@ void MutableDictionaryC::addEntries(const MutableDictionaryC *other) // Now we map the values into their new locations for (const auto &value: other->valueMap) { unsigned int newKey = stringRemap[value.first]; - Value val = value.second; + Value val = value.second; // copy switch (val.type) { case DictTypeString: val.entry = stringRemap[val.entry]; @@ -901,6 +857,7 @@ void MutableDictionaryC::addEntries(const MutableDictionaryC *other) break; case DictTypeObject: case DictTypeNone: + wkLogLevel(Warn, "Unsupported conversion from type %d to array entry", val.type); break; } valueMap[newKey] = val; @@ -924,19 +881,15 @@ int DictionaryEntryCBasic::getInt() const case DictTypeIdentity: case DictTypeInt64: return val.i64Val; case DictTypeDouble: return (int)val.dVal; - default: return 0; + default: + wkLogLevel(Warn, "Unsupported conversion from type %d to int", type); + return 0; } } SimpleIdentity DictionaryEntryCBasic::getIdentity() const { - switch (type) { - case DictTypeInt: return val.iVal; - case DictTypeIdentity: - case DictTypeInt64: return val.i64Val; - case DictTypeDouble: return val.dVal; - default: return 0; - } + return getInt64(); } int64_t DictionaryEntryCBasic::getInt64() const @@ -946,7 +899,9 @@ int64_t DictionaryEntryCBasic::getInt64() const case DictTypeIdentity: case DictTypeInt64: return val.i64Val; case DictTypeDouble: return val.dVal; - default: return 0; + default: + wkLogLevel(Warn, "Unsupported conversion from type %d to int64", type); + return 0; } } @@ -957,18 +912,15 @@ bool DictionaryEntryCBasic::getBool() const case DictTypeIdentity: case DictTypeInt64: return val.i64Val != 0; case DictTypeDouble: return val.dVal != 0.0; - default: return false; + default: + wkLogLevel(Warn, "Unsupported conversion from type %d to bool", type); + return false; } } RGBAColor DictionaryEntryCBasic::getColor() const { - RGBAColor ret; - ret.b = val.iVal & 0xFF; - ret.g = (val.iVal >> 8) & 0xFF; - ret.r = (val.iVal >> 16) & 0xFF; - ret.a = (val.iVal >> 24) & 0xFF; - return ret; + return ARGBtoRGBAColor(val.iVal); } double DictionaryEntryCBasic::getDouble() const @@ -978,7 +930,9 @@ double DictionaryEntryCBasic::getDouble() const case DictTypeIdentity: case DictTypeInt64: return val.i64Val; case DictTypeDouble: return val.dVal; - default: return 0; + default: + wkLogLevel(Warn, "Unsupported conversion from type %d to double", type); + return 0; } } @@ -989,35 +943,38 @@ bool DictionaryEntryCBasic::isEqual(const DictionaryEntryRef &other) const case DictTypeInt64: case DictTypeIdentity: return val.i64Val == other->getIdentity(); case DictTypeDouble: return val.dVal == other->getDouble(); - default: return false; + default: + wkLogLevel(Warn, "Unsupported comparison of type %d to type %d", type, other->getType()); + return false; } } RGBAColor DictionaryEntryCString::getColor() const { - // We're looking for a #RRGGBBAA - if (str.length() < 1 || str[0] != '#') - return RGBAColor::white(); - - const int iVal = atoi(&str.c_str()[1]); - RGBAColor ret; - ret.b = iVal & 0xFF; - ret.g = (iVal >> 8) & 0xFF; - ret.r = (iVal >> 16) & 0xFF; - ret.a = (iVal >> 24) & 0xFF; - - return ret; + return parseColor(str.c_str(), RGBAColor::white()); } bool DictionaryEntryCString::isEqual(const DictionaryEntryRef &other) const { - const auto otherRef = std::dynamic_pointer_cast(other); - return otherRef && (str == otherRef->str); + // Try to avoid creating a string unnecessarily + if (!other) + { + return str.empty(); + } + else if (const auto otherCString = dynamic_cast(other.get())) + { + return (str == otherCString->str); + } + else + { + return str == other->getString(); + } } bool DictionaryEntryCDict::isEqual(const DictionaryEntryRef &other) const { // TODO: Actually make this work + wkLogLevel(Warn, "Unsupported dictionary comparison"); return false; } @@ -1052,6 +1009,7 @@ std::vector DictionaryEntryCArray::getArrayC() const bool DictionaryEntryCArray::isEqual(const DictionaryEntryRef &other) const { // TODO: Make this work + wkLogLevel(Warn, "Unsupported array comparison"); return false; } diff --git a/common/WhirlyGlobeLib/src/Drawable.cpp b/common/WhirlyGlobeLib/src/Drawable.cpp index c179fa459b..afa82e3bbf 100644 --- a/common/WhirlyGlobeLib/src/Drawable.cpp +++ b/common/WhirlyGlobeLib/src/Drawable.cpp @@ -27,10 +27,6 @@ using namespace Eigen; namespace WhirlyKit { -DrawableTweaker::~DrawableTweaker() -{ -} - Drawable::Drawable(const std::string &name) : name(name) { diff --git a/common/WhirlyGlobeLib/src/DynamicTextureAtlas.cpp b/common/WhirlyGlobeLib/src/DynamicTextureAtlas.cpp index 45c8282ee2..dcd4f220a7 100644 --- a/common/WhirlyGlobeLib/src/DynamicTextureAtlas.cpp +++ b/common/WhirlyGlobeLib/src/DynamicTextureAtlas.cpp @@ -242,7 +242,7 @@ void DynamicTextureAtlas::setPixelFudgeFactor(float pixFudge) pixelFudge = pixFudge; } -bool DynamicTextureAtlas::addTexture(SceneRenderer *sceneRender,const std::vector &newTextures,int frame,Point2f *realSize,Point2f *realOffset,SubTexture &subTex,ChangeSet &changes,int borderPixels,int bufferPixels,TextureRegion *outTexRegion) +bool DynamicTextureAtlas::addTexture(SceneRenderer *sceneRender,const std::vector &newTextures,int frame,const Point2f *realSize,const Point2f *realOffset,SubTexture &subTex,ChangeSet &changes,int borderPixels,int bufferPixels,TextureRegion *outTexRegion) { if (newTextures.size() != imageDepth && frame < 0) return false; @@ -255,7 +255,7 @@ bool DynamicTextureAtlas::addTexture(SceneRenderer *sceneRender,const std::vecto TextureRegion texRegion; // Clear out any released regions - for (DynamicTextureSet::iterator it = textures.begin();it != textures.end(); ++it) + for (auto it = textures.begin();it != textures.end(); ++it) { DynamicTextureVec *dynTexVec = *it; DynamicTextureRef firstDynTex = dynTexVec->at(0); @@ -274,8 +274,7 @@ bool DynamicTextureAtlas::addTexture(SceneRenderer *sceneRender,const std::vecto DynamicTextureVec *dynTexVec = NULL; bool found = false; int numCellX = ceil((firstTex->getWidth()+bufferPixels) / (float)cellSize), numCellY = ceil((firstTex->getHeight()+bufferPixels) / (float)cellSize); - for (DynamicTextureSet::iterator it = textures.begin(); - it != textures.end(); ++it) + for (auto it = textures.begin(); it != textures.end(); ++it) { DynamicTextureVec *dynTex = *it; DynamicTextureRef firstDynTex = dynTex->at(0); diff --git a/common/WhirlyGlobeLib/src/FontTextureManager.cpp b/common/WhirlyGlobeLib/src/FontTextureManager.cpp index d26cd8430d..0d4b4af28b 100644 --- a/common/WhirlyGlobeLib/src/FontTextureManager.cpp +++ b/common/WhirlyGlobeLib/src/FontTextureManager.cpp @@ -1,9 +1,8 @@ -/* - * FontTextureManager.mm +/* FontTextureManager.mm * WhirlyGlobeLib * * Created by Steve Gifford on 4/15/13. - * Copyright 2011-2019 mousebird consulting + * Copyright 2011-2021 mousebird consulting * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -15,7 +14,6 @@ * 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. - * */ #import "FontTextureManager.h" @@ -27,39 +25,30 @@ using namespace WhirlyKit; namespace WhirlyKit { -FontManager::FontManager() -: refCount(0),color(255,255,255,255),outlineColor(0,0,0,0),backColor(0,0,0,0),outlineSize(0.0) +FontManager::FontManager() : + refCount(0), + color(255,255,255,255), + outlineColor(0,0,0,0), + backColor(0,0,0,0), + outlineSize(0.0) { } FontManager::~FontManager() { - for (std::set::iterator it = glyphs.begin(); - it != glyphs.end(); ++it) + for (auto glyph : glyphs) { - delete *it; + delete glyph; } glyphs.clear(); } -// Comparison operator -// Subclass fills this in -bool FontManager::operator < (const FontManager &that) const -{ - return false; -} - // Look for an existing glyph and return it if it's there FontManager::GlyphInfo *FontManager::findGlyph(WKGlyph glyph) { GlyphInfo dummyGlyph(glyph); - GlyphInfoSet::iterator it = glyphs.find(&dummyGlyph); - if (it != glyphs.end()) - { - return *it; - } - - return NULL; + const auto it = glyphs.find(&dummyGlyph); + return (it != glyphs.end()) ? *it : nullptr; } // Add the given glyph info @@ -79,12 +68,10 @@ FontManager::GlyphInfo *FontManager::addGlyph(WKGlyph glyph,SubTexture subTex,co void FontManager::addGlyphRefs(const GlyphSet &usedGlyphs) { refCount++; - for (GlyphSet::iterator it = usedGlyphs.begin(); - it != usedGlyphs.end(); ++it) + for (const auto &theGlyph : usedGlyphs) { - WKGlyph theGlyph = *it; GlyphInfo dummy(theGlyph); - GlyphInfoSet::iterator git = glyphs.find(&dummy); + const auto git = glyphs.find(&dummy); if (git != glyphs.end()) { GlyphInfo *glyphInfo = *git; @@ -97,18 +84,20 @@ void FontManager::addGlyphRefs(const GlyphSet &usedGlyphs) void FontManager::removeGlyphRefs(const GlyphSet &usedGlyphs,std::vector &toRemove) { refCount--; - for (GlyphSet::iterator it = usedGlyphs.begin(); - it != usedGlyphs.end(); ++it) + for (const auto &theGlyph : usedGlyphs) { - WKGlyph theGlyph = *it; GlyphInfo dummy(theGlyph); - GlyphInfoSet::iterator git = glyphs.find(&dummy); + const auto git = glyphs.find(&dummy); if (git != glyphs.end()) { GlyphInfo *glyphInfo = *git; glyphInfo->refCount--; if (glyphInfo->refCount <= 0) { + if (toRemove.empty()) + { + toRemove.reserve(usedGlyphs.size()); + } toRemove.push_back(glyphInfo->subTex); glyphs.erase(git); delete glyphInfo; @@ -119,18 +108,20 @@ void FontManager::removeGlyphRefs(const GlyphSet &usedGlyphs,std::vector guardLock(lock); + + delete texAtlas; + texAtlas = nullptr; + for (auto drawStringRep : drawStringReps) + { + delete drawStringRep; + } drawStringReps.clear(); fontManagers.clear(); } @@ -154,50 +145,57 @@ void FontTextureManager::clear(ChangeSet &changes) { texAtlas->teardown(changes); delete texAtlas; - texAtlas = NULL; + texAtlas = nullptr; + } + for (auto drawStringRep : drawStringReps) + { + delete drawStringRep; } - for (DrawStringRepSet::iterator it = drawStringReps.begin(); - it != drawStringReps.end(); ++it) - delete *it; fontManagers.clear(); } -void FontTextureManager::removeString(SimpleIdentity drawStringId,ChangeSet &changes,TimeInterval when) +void FontTextureManager::removeString(PlatformThreadInfo *inst, SimpleIdentity drawStringId,ChangeSet &changes,TimeInterval when) { std::lock_guard guardLock(lock); - DrawStringRep dummyRep(drawStringId); - DrawStringRepSet::iterator it = drawStringReps.find(&dummyRep); - if (it == drawStringReps.end()) + DrawStringRep *theRep = nullptr; { - return; + DrawStringRep dummyRep(drawStringId); + auto it = drawStringReps.find(&dummyRep); + if (it == drawStringReps.end()) + { + return; + } + + theRep = *it; + drawStringReps.erase(it); } - - DrawStringRep *theRep = *it; - drawStringReps.erase(theRep); - + // Work through the fonts we're using - for (SimpleIDGlyphMap::iterator fit = theRep->fontGlyphs.begin(); - fit != theRep->fontGlyphs.end(); ++fit) + for (const auto &fontGlyph : theRep->fontGlyphs) { - auto fmIt = fontManagers.find(fit->first); - if (fmIt != fontManagers.end()) + const auto fmIt = fontManagers.find(fontGlyph.first); + if (fmIt == fontManagers.end()) { - // Decrement the glyph references - FontManagerRef fm = fmIt->second; - std::vector texRemove; - fm->removeGlyphRefs(fit->second,texRemove); - - // And possibly remove some sub textures - if (!texRemove.empty()) - for (unsigned int ii=0;iiremoveTexture(texRemove[ii], changes, when); - - // Also see if we're done with the font - if (fm->refCount <= 0) - { - fontManagers.erase(fmIt); - } + continue; + } + + // Decrement the glyph references + const FontManagerRef &fm = fmIt->second; + std::vector texRemove; + fm->removeGlyphRefs(fontGlyph.second,texRemove); + + // And possibly remove some sub textures + for (const auto &ii : texRemove) + { + texAtlas->removeTexture(ii, changes, when); + } + + // Also see if we're done with the font + if (fm->refCount <= 0) + { + fm->teardown(inst); + fontManagers.erase(fmIt); } } diff --git a/common/WhirlyGlobeLib/src/GeometryManager.cpp b/common/WhirlyGlobeLib/src/GeometryManager.cpp index 87c8ab0229..e2b54f9a3c 100644 --- a/common/WhirlyGlobeLib/src/GeometryManager.cpp +++ b/common/WhirlyGlobeLib/src/GeometryManager.cpp @@ -57,7 +57,7 @@ GeometryInfo::GeometryInfo(const Dictionary &dict) pointSize = dict.getDouble(MaplyGeomPointSize,1.0); } -void GeomSceneRep::clearContents(SelectionManager *selectManager,ChangeSet &changes,TimeInterval when) +void GeomSceneRep::clearContents(SelectionManagerRef &selectManager,ChangeSet &changes,TimeInterval when) { for (SimpleIDSet::iterator it = drawIDs.begin(); it != drawIDs.end(); ++it) @@ -66,7 +66,7 @@ void GeomSceneRep::clearContents(SelectionManager *selectManager,ChangeSet &chan selectManager->removeSelectables(selectIDs); } -void GeomSceneRep::enableContents(SelectionManager *selectManager,bool enable,ChangeSet &changes) +void GeomSceneRep::enableContents(SelectionManagerRef &selectManager,bool enable,ChangeSet &changes) { for (SimpleIDSet::iterator it = drawIDs.begin(); it != drawIDs.end(); ++it) @@ -618,6 +618,8 @@ GeometryManager::GeometryManager() GeometryManager::~GeometryManager() { + std::lock_guard guardLock(lock); + for (GeomSceneRepSet::iterator it = sceneReps.begin(); it != sceneReps.end(); ++it) delete *it; @@ -626,9 +628,9 @@ GeometryManager::~GeometryManager() SimpleIdentity GeometryManager::addGeometry(std::vector &geom,const std::vector &instances,GeometryInfo &geomInfo,ChangeSet &changes) { - SelectionManager *selectManager = (SelectionManager *)scene->getManager(kWKSelectionManager); + SelectionManagerRef selectManager = std::dynamic_pointer_cast(scene->getManager(kWKSelectionManager)); GeomSceneRep *sceneRep = new GeomSceneRep(); - + // Calculate the bounding box for the whole thing Point3d ll,ur; @@ -706,7 +708,7 @@ SimpleIdentity GeometryManager::addGeometry(std::vector &geom,con SimpleIdentity geomID = sceneRep->getId(); { - std::lock_guard guardLock(geomLock); + std::lock_guard guardLock(lock); sceneReps.insert(sceneRep); } @@ -775,7 +777,7 @@ SimpleIdentity GeometryManager::addBaseGeometry(std::vector &geom SimpleIdentity geomID = sceneRep->getId(); { - std::lock_guard guardLock(geomLock); + std::lock_guard guardLock(lock); sceneReps.insert(sceneRep); } @@ -794,7 +796,7 @@ SimpleIdentity GeometryManager::addBaseGeometry(std::vector &inGeom /// Add instances that reuse base geometry SimpleIdentity GeometryManager::addGeometryInstances(SimpleIdentity baseGeomID,const std::vector &instances,GeometryInfo &geomInfo,ChangeSet &changes) { - std::lock_guard guardLock(geomLock); + std::lock_guard guardLock(lock); TimeInterval startTime = scene->getCurrentTime(); // Look for the scene rep we're basing this on @@ -809,9 +811,9 @@ SimpleIdentity GeometryManager::addGeometryInstances(SimpleIdentity baseGeomID,c return EmptyIdentity; } - SelectionManager *selectManager = (SelectionManager *)scene->getManager(kWKSelectionManager); + SelectionManagerRef selectManager = std::dynamic_pointer_cast(scene->getManager(kWKSelectionManager)); GeomSceneRep *sceneRep = new GeomSceneRep(); - + // Check for moving models bool hasMotion = false; for (const GeometryInstance &inst : instances) @@ -881,7 +883,7 @@ SimpleIdentity GeometryManager::addGeometryInstances(SimpleIdentity baseGeomID,c SimpleIdentity GeometryManager::addGPUGeomInstance(SimpleIdentity baseGeomID,SimpleIdentity programID,SimpleIdentity texSourceID,SimpleIdentity srcProgramID,GeometryInfo &geomInfo,ChangeSet &changes) { - std::lock_guard guardLock(geomLock); + std::lock_guard guardLock(lock); // Look for the scene rep we're basing this on GeomSceneRep *baseSceneRep = NULL; @@ -956,7 +958,7 @@ SimpleIdentity GeometryManager::addGeometryPoints(const GeometryRawPoints &geomP SimpleIdentity geomID = sceneRep->getId(); { - std::lock_guard guardLock(geomLock); + std::lock_guard guardLock(lock); sceneReps.insert(sceneRep); } @@ -965,9 +967,8 @@ SimpleIdentity GeometryManager::addGeometryPoints(const GeometryRawPoints &geomP void GeometryManager::enableGeometry(SimpleIDSet &geomIDs,bool enable,ChangeSet &changes) { - SelectionManager *selectManager = (SelectionManager *)scene->getManager(kWKSelectionManager); - - std::lock_guard guardLock(geomLock); + SelectionManagerRef selectManager = std::dynamic_pointer_cast(scene->getManager(kWKSelectionManager)); + std::lock_guard guardLock(lock); for (SimpleIDSet::iterator git = geomIDs.begin(); git != geomIDs.end(); ++git) { @@ -983,9 +984,8 @@ void GeometryManager::enableGeometry(SimpleIDSet &geomIDs,bool enable,ChangeSet void GeometryManager::removeGeometry(SimpleIDSet &geomIDs,ChangeSet &changes) { - SelectionManager *selectManager = (SelectionManager *)scene->getManager(kWKSelectionManager); - - std::lock_guard guardLock(geomLock); + SelectionManagerRef selectManager = std::dynamic_pointer_cast(scene->getManager(kWKSelectionManager)); + std::lock_guard guardLock(lock); TimeInterval curTime = scene->getCurrentTime(); for (SimpleIDSet::iterator git = geomIDs.begin(); git != geomIDs.end(); ++git) @@ -1016,7 +1016,7 @@ void GeometryManager::removeGeometry(SimpleIDSet &geomIDs,ChangeSet &changes) void GeometryManager::setUniformBlock(const SimpleIDSet &geomID,const RawDataRef &uniBlock,int bufferID,ChangeSet &changes) { - std::lock_guard guardLock(geomLock); + std::lock_guard guardLock(lock); for (auto geomID : geomID) { GeomSceneRep dummyRep(geomID); diff --git a/common/WhirlyGlobeLib/src/GlobeMath.cpp b/common/WhirlyGlobeLib/src/GlobeMath.cpp index 19d0d93ff5..6b70c60d63 100644 --- a/common/WhirlyGlobeLib/src/GlobeMath.cpp +++ b/common/WhirlyGlobeLib/src/GlobeMath.cpp @@ -1,9 +1,8 @@ -/* - * GlobeMath.mm +/* GlobeMath.cpp * WhirlyGlobeLib * * Created by Steve Gifford on 2/2/11. - * Copyright 2011-2019 mousebird consulting + * Copyright 2011-2021 mousebird consulting * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -15,7 +14,6 @@ * 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. - * */ diff --git a/common/WhirlyGlobeLib/src/GlobeView.cpp b/common/WhirlyGlobeLib/src/GlobeView.cpp index 32ed81277b..f635ec750c 100644 --- a/common/WhirlyGlobeLib/src/GlobeView.cpp +++ b/common/WhirlyGlobeLib/src/GlobeView.cpp @@ -382,36 +382,30 @@ float GlobeView::calcZbufferRes() } // Construct a rotation to the given location -// and return it. Doesn't actually do anything yet. Eigen::Quaterniond GlobeView::makeRotationToGeoCoord(const WhirlyKit::Point2d &worldCoord,bool northUp) { - Point3d worldLoc = coordAdapter->localToDisplay(coordAdapter->getCoordSystem()->geographicToLocal(worldCoord)); + const Point3d worldLoc = coordAdapter->localToDisplay(coordAdapter->getCoordSystem()->geographicToLocal(worldCoord)); // Let's rotate to where they tapped over a 1sec period - Vector3d curUp = currentUp(); + const Vector3d curUp = currentUp(); // The rotation from where we are to where we tapped - Eigen::Quaterniond endRot; - endRot = QuatFromTwoVectors(worldLoc,curUp); - Eigen::Quaterniond curRotQuat = rotQuat; - Eigen::Quaterniond newRotQuat = curRotQuat * endRot; + const Eigen::Quaterniond endRot = QuatFromTwoVectors(worldLoc,curUp); + const Eigen::Quaterniond curRotQuat = rotQuat; + const Eigen::Quaterniond newRotQuat = curRotQuat * endRot; if (northUp) { // We'd like to keep the north pole pointed up // So we look at where the north pole is going - Vector3d northPole = (newRotQuat * Vector3d(0,0,1)).normalized(); + const Vector3d northPole = (newRotQuat * Vector3d(0,0,1)).normalized(); if (northPole.y() != 0.0) { - // Then rotate it back on to the YZ axis - // This will keep it upward - float ang = atan(northPole.x()/northPole.y()); - // However, the pole might be down now - // If so, rotate it back up - if (northPole.y() < 0.0) - ang += M_PI; - Eigen::AngleAxisd upRot(ang,worldLoc); - newRotQuat = newRotQuat * upRot; + // Then rotate it back on to the YZ axis, this will keep it upward. + // However, the pole might be down now. If so, rotate it back up. + // todo: would `atan2` be simpler? + const float ang = atan(northPole.x()/northPole.y()) + ((northPole.y() < 0.0) ? M_PI : 0.0); + return newRotQuat * Eigen::AngleAxisd(ang,worldLoc); } } @@ -478,10 +472,10 @@ Eigen::Vector3d GlobeViewState::currentUp() } -bool GlobeViewState::pointOnSphereFromScreen(const Point2f &pt,const Eigen::Matrix4d &modelTrans,const Point2f &frameSize,Point3d &hit) +bool GlobeViewState::pointOnSphereFromScreen(const Point2f &pt,const Eigen::Matrix4d &modelTrans,const Point2f &frameSize,Point3d &hit,bool clip) { // Back project the point from screen space into model space - Point3d screenPt = pointUnproject(Point2d(pt.x(),pt.y()),frameSize.x(),frameSize.y(),true); + Point3d screenPt = pointUnproject(Point2d(pt.x(),pt.y()),frameSize.x(),frameSize.y(),clip); // Run the screen point and the eye point (origin) back through // the model matrix to get a direction and origin in model space diff --git a/common/WhirlyGlobeLib/src/GridClipper.cpp b/common/WhirlyGlobeLib/src/GridClipper.cpp index 538d2f9335..8239c546bb 100644 --- a/common/WhirlyGlobeLib/src/GridClipper.cpp +++ b/common/WhirlyGlobeLib/src/GridClipper.cpp @@ -1,9 +1,8 @@ -/* - * GridClipper.mm +/* GridClipper.cpp * WhirlyGlobeLib * * Created by Steve Gifford on 7/16/11. - * Copyright 2011-2019 mousebird consulting + * Copyright 2011-2021 mousebird consulting * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -15,7 +14,6 @@ * 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. - * */ #import "GridClipper.h" @@ -50,11 +48,13 @@ OutCode ComputeOutCode(double x, double y, const Mbr &mbr) return code; } - // Clip the given loop to the given MBR -bool ClipLoopToMbr(const VectorRing &ring,const Mbr &mbr, bool closed,std::vector &rets) +bool ClipLoopToMbr(const VectorRing &ring,const Mbr &mbr, bool closed,std::vector &rets,double polyScale) { + if (polyScale == 0.0) + polyScale = PolyScale; + if(!closed) { //Cohen-sutherland algorithm based on example implementation from wikipedia @@ -130,13 +130,13 @@ bool ClipLoopToMbr(const VectorRing &ring,const Mbr &mbr, bool closed,std::vecto for (unsigned int ii=0;ii 2) @@ -165,8 +165,11 @@ bool ClipLoopToMbr(const VectorRing &ring,const Mbr &mbr, bool closed,std::vecto } // Clip the given loop to the given MBR -bool ClipLoopsToMbr(const std::vector &rings,const Mbr &mbr, bool closed,std::vector &rets) +bool ClipLoopsToMbr(const std::vector &rings,const Mbr &mbr, bool closed,std::vector &rets,double polyScale) { + if (polyScale == 0.0) + polyScale = PolyScale; + Clipper c; for (const auto &ring: rings) @@ -175,16 +178,16 @@ bool ClipLoopsToMbr(const std::vector &rings,const Mbr &mbr, bool cl for (unsigned int ii=0;ii &rings,const Mbr &mbr, bool cl for (unsigned jj=0;jj 2) diff --git a/common/WhirlyGlobeLib/src/IntersectionManager.cpp b/common/WhirlyGlobeLib/src/IntersectionManager.cpp index 8d5723a039..ec9ba1ec95 100644 --- a/common/WhirlyGlobeLib/src/IntersectionManager.cpp +++ b/common/WhirlyGlobeLib/src/IntersectionManager.cpp @@ -20,6 +20,7 @@ IntersectionManager::IntersectionManager(Scene *scene) IntersectionManager::~IntersectionManager() { + std::lock_guard guardLock(lock); } void IntersectionManager::addIntersectable(Intersectable *intersect) @@ -56,7 +57,7 @@ bool IntersectionManager::findIntersection(SceneRenderer *renderer,View *view,co Vector3d dir(dir4.x(),dir4.y(),dir4.z()); dir.normalize(); - std::lock_guard guardLock(mutex); + std::lock_guard guardLock(lock); for (auto inter : intersectables) { diff --git a/common/WhirlyGlobeLib/src/LabelManager.cpp b/common/WhirlyGlobeLib/src/LabelManager.cpp index a602862770..afa6d3ab2e 100644 --- a/common/WhirlyGlobeLib/src/LabelManager.cpp +++ b/common/WhirlyGlobeLib/src/LabelManager.cpp @@ -1,9 +1,8 @@ -/* - * LabelManager.mm +/* LabelManager.cpp * WhirlyGlobeLib * * Created by Steve Gifford on 2/7/11. - * Copyright 2011-2019 mousebird consulting + * Copyright 2011-2021 mousebird consulting * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -15,7 +14,6 @@ * 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. - * */ #import "LabelRenderer.h" @@ -23,6 +21,7 @@ #import "GlobeMath.h" #import "ScreenSpaceBuilder.h" #import "FontTextureManager.h" +#import "SharedAttributes.h" #import "LabelManager.h" @@ -31,98 +30,122 @@ using namespace Eigen; namespace WhirlyKit { -SingleLabel::SingleLabel() - : isSelectable(true), selectID(EmptyIdentity), loc(0,0), rotation(0), iconTexture(EmptyIdentity), -iconSize(0,0), screenOffset(0,0), layoutSize(-1.0,-1.0), layoutEngine(false), layoutImportance(MAXFLOAT), layoutPlacement(0) +SingleLabel::SingleLabel() : + isSelectable(true), + selectID(EmptyIdentity), + loc(0,0), + hasMotion(false), + endLoc(0,0), + startTime(0), + endTime(0), + rotation(0), + keepUpright(false), + iconTexture(EmptyIdentity), + iconSize(0,0), + screenOffset(0,0), + layoutSize(-1.0,-1.0), + layoutEngine(false), + layoutImportance(MAXFLOAT), + layoutPlacement(0), + maskID(EmptyIdentity), + maskRenderTargetID(EmptyIdentity) { } LabelManager::LabelManager() - : textureAtlasSize(LabelTextureAtlasSizeDefault) + : textureAtlasSize(LabelTextureAtlasSizeDefault), maskProgID(EmptyIdentity) { } -LabelManager::~LabelManager() -{ -} - -SimpleIdentity LabelManager::addLabels(PlatformThreadInfo *threadInfo,std::vector &labels,const LabelInfo &desc,ChangeSet &changes) +SimpleIdentity LabelManager::addLabels(PlatformThreadInfo *threadInfo, + const std::vector &labels, + const LabelInfo &desc,ChangeSet &changes) { std::vector unwrapLabels; - + unwrapLabels.reserve(labels.size()); for (auto label: labels) + { unwrapLabels.push_back(label.get()); + } return addLabels(threadInfo,unwrapLabels, desc, changes); } -SimpleIdentity LabelManager::addLabels(PlatformThreadInfo *threadInfo,std::vector &labels,const LabelInfo &labelInfo,ChangeSet &changes) +SimpleIdentity LabelManager::addLabels(PlatformThreadInfo *threadInfo, + const std::vector &labels, + const LabelInfo &labelInfo,ChangeSet &changes) { - CoordSystemDisplayAdapter *coordAdapter = scene->getCoordAdapter(); + const auto fontTexManager = scene->getFontTextureManager(); // Set up the representation (but then hand it off) - LabelSceneRep *labelRep = new LabelSceneRep(); - if (labelInfo.fadeOut > 0.0 && labelInfo.fadeOutTime != 0.0) - labelRep->fadeOut = labelInfo.fadeOut; - else - labelRep->fadeOut = 0.0; - - FontTextureManager *fontTexManager = scene->getFontTextureManager(); + auto labelRep = new LabelSceneRep(); + labelRep->fadeOut = (float)((labelInfo.fadeOut > 0 && labelInfo.fadeOutTime != 0) ? labelInfo.fadeOut : 0); + if (maskProgID == EmptyIdentity) { + Program *prog = scene->findProgramByName(MaplyScreenSpaceMaskShader); + if (prog) + maskProgID = prog->getId(); + } + // Set up the label renderer - LabelRenderer labelRenderer(scene,fontTexManager,&labelInfo); - labelRenderer.textureAtlasSize = textureAtlasSize; + LabelRenderer labelRenderer(scene,renderer,fontTexManager,&labelInfo,maskProgID); + labelRenderer.textureAtlasSize = (int)textureAtlasSize; labelRenderer.coordAdapter = scene->getCoordAdapter(); labelRenderer.labelRep = labelRep; labelRenderer.scene = scene; - labelRenderer.fontTexManager = (labelInfo.screenObject ? fontTexManager : NULL); + labelRenderer.fontTexManager = (labelInfo.screenObject ? fontTexManager : nullptr); labelRenderer.scale = renderer->getScale(); labelRenderer.render(threadInfo, labels, changes); changes.insert(changes.end(),labelRenderer.changeRequests.begin(), labelRenderer.changeRequests.end()); - SelectionManager *selectManager = (SelectionManager *)scene->getManager(kWKSelectionManager); - LayoutManager *layoutManager = (LayoutManager *)scene->getManager(kWKLayoutManager); - // Create screen shapes if (!labelRenderer.screenObjects.empty()) { - ScreenSpaceBuilder ssBuild(renderer,coordAdapter,renderer->getScale()); - for (unsigned int ii=0;iigetCoordAdapter(); + ScreenSpaceBuilder ssBuild(renderer, coordAdapter, renderer->getScale()); + for (auto & screenObject : labelRenderer.screenObjects) + { + ssBuild.addScreenObject(screenObject,screenObject.getWorldLoc(),screenObject.getGeometry()); + } ssBuild.flushChanges(changes, labelRep->drawIDs); } - + // Hand over some to the layout manager - if (layoutManager && !labelRenderer.layoutObjects.empty()) + if (const auto layoutManager = scene->getManager(kWKLayoutManager)) { - for (unsigned int ii=0;iilayoutIDs.insert(labelRenderer.layoutObjects[ii].getId()); - layoutManager->addLayoutObjects(labelRenderer.layoutObjects); + if (!labelRenderer.layoutObjects.empty()) + { + for (auto & layoutObject : labelRenderer.layoutObjects) + { + labelRep->layoutIDs.insert(layoutObject.getId()); + } + layoutManager->addLayoutObjects(labelRenderer.layoutObjects); + } } - + // Pass on selection data - if (selectManager) + if (const auto selectManager = scene->getManager(kWKSelectionManager)) { for (unsigned int ii=0;ii &selectables2D = labelRenderer.selectables2D; + auto &selectables2D = labelRenderer.selectables2D; RectSelectable2D &sel = selectables2D[ii]; selectManager->addSelectableScreenRect(sel.selectID,sel.center,sel.pts,sel.minVis,sel.maxVis,sel.enable); labelRep->selectIDs.insert(sel.selectID); } for (unsigned int ii=0;ii &movingSelectables2D = labelRenderer.movingSelectables2D; - MovingRectSelectable2D &sel = movingSelectables2D[ii]; + auto &movingSelectables2D = labelRenderer.movingSelectables2D; + auto &sel = movingSelectables2D[ii]; selectManager->addSelectableMovingScreenRect(sel.selectID,sel.center,sel.endCenter,sel.startTime,sel.endTime,sel.pts,sel.minVis,sel.maxVis,sel.enable); labelRep->selectIDs.insert(sel.selectID); } for (unsigned int ii=0;ii &selectables3D = labelRenderer.selectables3D; - RectSelectable3D &sel = selectables3D[ii]; + auto &selectables3D = labelRenderer.selectables3D; + auto &sel = selectables3D[ii]; selectManager->addSelectableRect(sel.selectID,sel.pts,sel.minVis,sel.maxVis,sel.enable); labelRep->selectIDs.insert(sel.selectID); } @@ -130,50 +153,47 @@ SimpleIdentity LabelManager::addLabels(PlatformThreadInfo *threadInfo,std::vecto SimpleIdentity labelID = labelRep->getId(); { - std::lock_guard guardLock(labelLock); + std::lock_guard guardLock(lock); labelReps.insert(labelRep); } return labelID; } -void LabelManager::changeLabel(PlatformThreadInfo *threadInfo,SimpleIdentity labelID,const LabelInfo &labelInfo,ChangeSet &changes) +void LabelManager::changeLabel(PlatformThreadInfo *,SimpleIdentity labelID,const LabelInfo &labelInfo,ChangeSet &changes) { - std::lock_guard guardLock(labelLock); + std::lock_guard guardLock(lock); LabelSceneRep dummyRep(labelID); - LabelSceneRepSet::iterator it = labelReps.find(&dummyRep); - + const auto it = labelReps.find(&dummyRep); if (it != labelReps.end()) { - LabelSceneRep *sceneRep = *it; + const LabelSceneRep *sceneRep = *it; - for (SimpleIDSet::iterator idIt = sceneRep->drawIDs.begin(); - idIt != sceneRep->drawIDs.end(); ++idIt) + for (const auto drawID : sceneRep->drawIDs) { // Changed visibility - changes.push_back(new VisibilityChangeRequest(*idIt, labelInfo.minVis, labelInfo.maxVis)); + changes.push_back(new VisibilityChangeRequest(drawID, (float)labelInfo.minVis, (float)labelInfo.maxVis)); } } } -void LabelManager::enableLabels(SimpleIDSet labelIDs,bool enable,ChangeSet &changes) +void LabelManager::enableLabels(const SimpleIDSet &labelIDs,bool enable,ChangeSet &changes) { - SelectionManager *selectManager = (SelectionManager *)scene->getManager(kWKSelectionManager); - LayoutManager *layoutManager = (LayoutManager *)scene->getManager(kWKLayoutManager); - - std::lock_guard guardLock(labelLock); + auto selectManager = scene->getManager(kWKSelectionManager); + auto layoutManager = scene->getManager(kWKLayoutManager); - for (SimpleIDSet::iterator lit = labelIDs.begin(); lit != labelIDs.end(); ++lit) + std::lock_guard guardLock(lock); + + for (const auto &labelID : labelIDs) { - LabelSceneRep dummyRep(*lit); - LabelSceneRepSet::iterator it = labelReps.find(&dummyRep); + LabelSceneRep dummyRep(labelID); + const auto it = labelReps.find(&dummyRep); if (it != labelReps.end()) { LabelSceneRep *sceneRep = *it; - for (SimpleIDSet::iterator idIt = sceneRep->drawIDs.begin(); - idIt != sceneRep->drawIDs.end(); ++idIt) - changes.push_back(new OnOffChangeRequest(*idIt,enable)); + for (const auto &drawID : sceneRep->drawIDs) + changes.push_back(new OnOffChangeRequest(drawID,enable)); if (!sceneRep->selectIDs.empty() && selectManager) selectManager->enableSelectables(sceneRep->selectIDs, enable); if (!sceneRep->layoutIDs.empty() && layoutManager) @@ -183,19 +203,19 @@ void LabelManager::enableLabels(SimpleIDSet labelIDs,bool enable,ChangeSet &chan } -void LabelManager::removeLabels(PlatformThreadInfo *threadInfo,SimpleIDSet &labelIDs,ChangeSet &changes) +void LabelManager::removeLabels(PlatformThreadInfo *inst,const SimpleIDSet &labelIDs,ChangeSet &changes) { - SelectionManager *selectManager = (SelectionManager *)scene->getManager(kWKSelectionManager); - LayoutManager *layoutManager = (LayoutManager *)scene->getManager(kWKLayoutManager); - FontTextureManager *fontTexManager = scene->getFontTextureManager(); + auto selectManager = scene->getManager(kWKSelectionManager); + auto layoutManager = scene->getManager(kWKLayoutManager); + auto fontTexManager = scene->getFontTextureManager(); - std::lock_guard guardLock(labelLock); + std::lock_guard guardLock(lock); TimeInterval curTime = scene->getCurrentTime(); - for (SimpleIDSet::iterator lit = labelIDs.begin(); lit != labelIDs.end(); ++lit) + for (const auto &lbl : labelIDs) { - LabelSceneRep dummyRep(*lit); - LabelSceneRepSet::iterator it = labelReps.find(&dummyRep); + LabelSceneRep dummyRep(lbl); + const auto it = labelReps.find(&dummyRep); if (it != labelReps.end()) { LabelSceneRep *labelRep = *it; @@ -204,35 +224,36 @@ void LabelManager::removeLabels(PlatformThreadInfo *threadInfo,SimpleIDSet &labe // We need to fade them out, then delete if (labelRep->fadeOut > 0.0) { - for (SimpleIDSet::iterator idIt = labelRep->drawIDs.begin(); - idIt != labelRep->drawIDs.end(); ++idIt) - changes.push_back(new FadeChangeRequest(*idIt,curTime,curTime+labelRep->fadeOut)); - + for (auto id : labelRep->drawIDs) + { + changes.push_back(new FadeChangeRequest(id,curTime,curTime+labelRep->fadeOut)); + } + removeTime = curTime+labelRep->fadeOut; } - for (SimpleIDSet::iterator idIt = labelRep->drawIDs.begin(); - idIt != labelRep->drawIDs.end(); ++idIt) - changes.push_back(new RemDrawableReq(*idIt,removeTime)); - for (SimpleIDSet::iterator idIt = labelRep->texIDs.begin(); - idIt != labelRep->texIDs.end(); ++idIt) - changes.push_back(new RemTextureReq(*idIt,removeTime)); - for (SimpleIDSet::iterator idIt = labelRep->drawStrIDs.begin(); - idIt != labelRep->drawStrIDs.end(); ++idIt) + for (auto drawID : labelRep->drawIDs) + changes.push_back(new RemDrawableReq(drawID,removeTime)); + for (auto texID : labelRep->texIDs) + changes.push_back(new RemTextureReq(texID,removeTime)); + + if (fontTexManager) { - // Give the layout manager a little extra time so we don't pull the - // textures out from underneath it - TimeInterval fontRemoveTime = removeTime; - if (layoutManager && !labelRep->layoutIDs.empty()) - fontRemoveTime = curTime+2.0; - if (fontTexManager) - fontTexManager->removeString(*idIt, changes, fontRemoveTime); + for (auto id : labelRep->drawStrIDs) + { + // Give the layout manager a little extra time so we don't pull the + // textures out from underneath it + TimeInterval fontRemoveTime = removeTime; + if (layoutManager && !labelRep->layoutIDs.empty()) + fontRemoveTime = curTime + 2.0; + fontTexManager->removeString(inst, id, changes, fontRemoveTime); + } } if (selectManager && !labelRep->selectIDs.empty()) selectManager->removeSelectables(labelRep->selectIDs); - // Note: Screenspace Doesn't handle fade + // Note: Screen-space doesn't handle fade if (layoutManager && !labelRep->layoutIDs.empty()) layoutManager->removeLayoutObjects(labelRep->layoutIDs); diff --git a/common/WhirlyGlobeLib/src/LabelRenderer.cpp b/common/WhirlyGlobeLib/src/LabelRenderer.cpp index a98fc5bba2..6e9f90dbe8 100644 --- a/common/WhirlyGlobeLib/src/LabelRenderer.cpp +++ b/common/WhirlyGlobeLib/src/LabelRenderer.cpp @@ -1,9 +1,8 @@ -/* - * LabelRenderer.mm +/* LabelRenderer.cpp * WhirlyGlobeLib * * Created by Steve Gifford on 4/11/13. - * Copyright 2011-2019 mousebird consulting + * Copyright 2011-2021 mousebird consulting * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -15,7 +14,6 @@ * 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. - * */ #import "LabelManager.h" @@ -32,135 +30,136 @@ using namespace WhirlyKit; namespace WhirlyKit { -LabelInfo::LabelInfo(bool screenObject) -: hasTextColor(false), textColor(255,255,255,255), backColor(0,0,0,0), - screenObject(screenObject), - width(-1.0), height(-1.0), - labelJustify(WhirlyKitLabelMiddle), textJustify(WhirlyKitTextCenter), - shadowColor(0,0,0,0), shadowSize(-1.0), - outlineColor(0,0,0,0), outlineSize(-1.0), - lineHeight(0.0), fontPointSize(16.0) +static LabelJustify parseLabelJustify(const std::string &str, LabelJustify def) { - if (screenObject) { - width = 16.0; - height = 16.0; - } else { - width = 0.001; - height = 0.001; - } + if (str == MaplyLabelJustifyNameMiddle) return WhirlyKitLabelMiddle; + if (str == MaplyLabelJustifyNameLeft) return WhirlyKitLabelLeft; + if (str == MaplyLabelJustifyNameRight) return WhirlyKitLabelRight; + return def; } -LabelInfo::LabelInfo(const LabelInfo &that) -: BaseInfo(that), hasTextColor(that.hasTextColor), textColor(that.textColor), backColor(that.backColor), -screenObject(that.screenObject), width(that.width), height(that.height), -labelJustify(that.labelJustify), textJustify(that.textJustify), -shadowColor(that.shadowColor), shadowSize(that.shadowSize), -outlineColor(that.outlineColor), outlineSize(that.outlineSize), -lineHeight(that.lineHeight), fontPointSize(that.fontPointSize) +static TextJustify parseTextJustify(const std::string &str, TextJustify def) { + if (str == MaplyTextJustifyCenter) return WhirlyKitTextCenter; + if (str == MaplyTextJustifyLeft) return WhirlyKitTextLeft; + if (str == MaplyTextJustifyRight) return WhirlyKitTextRight; + return def; } -LabelInfo::LabelInfo(const Dictionary &dict, bool screenObject) -: screenObject(screenObject), fontPointSize(16.0) +LabelInfo::LabelInfo(bool screenObject) : + screenObject(screenObject), + width(screenObject ? 16.0 : 0.001), + height(screenObject ? 16.0 : 0.001) { - hasTextColor = dict.hasField(MaplyTextColor); - textColor = dict.getColor(MaplyTextColor, RGBAColor(255,255,255,255)); - backColor = dict.getColor(MaplyBackgroundColor, RGBAColor(0,0,0,0)); - width = dict.getDouble(MaplyLabelWidth,0.0); - height = dict.getDouble(MaplyLabelHeight,screenObject ? 16.0 : 0.001); - std::string labelJustifyStr = dict.getString(MaplyLabelJustifyName); - std::string textJustifyStr = dict.getString(MaplyTextJustify); - shadowColor = dict.getColor(MaplyShadowColor, RGBAColor(0,0,0,255)); - shadowSize = dict.getDouble(MaplyShadowSize, 0.0); - outlineSize = dict.getDouble(MaplyTextOutlineSize,0.0); - outlineColor = dict.getColor(MaplyTextOutlineColor, RGBAColor(0,0,0,255)); - labelJustify = WhirlyKitLabelMiddle; - if (!labelJustifyStr.compare(MaplyLabelJustifyNameMiddle)) - labelJustify = WhirlyKitLabelMiddle; - else { - if (!labelJustifyStr.compare(MaplyLabelJustifyNameLeft)) - labelJustify = WhirlyKitLabelLeft; - else { - if (!labelJustifyStr.compare(MaplyLabelJustifyNameRight)) - labelJustify = WhirlyKitLabelRight; - } - } - if (!textJustifyStr.compare(MaplyTextJustifyCenter)) - textJustify = WhirlyKitTextCenter; - else { - if (!textJustifyStr.compare(MaplyTextJustifyLeft)) - textJustify = WhirlyKitTextLeft; - else { - if (!textJustifyStr.compare(MaplyTextJustifyRight)) - textJustify = WhirlyKitTextRight; - } - } - lineHeight = dict.getDouble(MaplyTextLineHeight,0.0); } -LabelSceneRep::LabelSceneRep() +LabelInfo::LabelInfo(const LabelInfo &that) : + BaseInfo(that), hasTextColor(that.hasTextColor), textColor(that.textColor), backColor(that.backColor), + screenObject(that.screenObject), width(that.width), height(that.height), + labelJustify(that.labelJustify), textJustify(that.textJustify), + shadowColor(that.shadowColor), shadowSize(that.shadowSize), + outlineColor(that.outlineColor), outlineSize(that.outlineSize), + lineHeight(that.lineHeight), fontPointSize(that.fontPointSize), + layoutOffset(that.layoutOffset), layoutSpacing(that.layoutSpacing), layoutRepeat(that.layoutRepeat) { } - + +LabelInfo::LabelInfo(const Dictionary &dict, bool screenObject) : + BaseInfo(dict), + screenObject(screenObject) +{ + hasTextColor = dict.hasField(MaplyTextColor); + textColor = dict.getColor(MaplyTextColor, textColor); + backColor = dict.getColor(MaplyBackgroundColor, backColor); + width = (float)dict.getDouble(MaplyLabelWidth,0.0); + height = (float)dict.getDouble(MaplyLabelHeight,screenObject ? 16.0 : 0.001); + shadowColor = dict.getColor(MaplyShadowColor, shadowColor); + shadowSize = (float)dict.getDouble(MaplyShadowSize, 0.0); + outlineSize = (float)dict.getDouble(MaplyTextOutlineSize,0.0); + outlineColor = dict.getColor(MaplyTextOutlineColor, outlineColor); + lineHeight = (float)dict.getDouble(MaplyTextLineHeight,0.0); + labelJustify = parseLabelJustify(dict.getString(MaplyLabelJustifyName), WhirlyKitLabelMiddle); + textJustify = parseTextJustify(dict.getString(MaplyTextJustify), WhirlyKitTextLeft); + layoutDebug = dict.getInt(MaplyTextLayoutDebug,false); + layoutRepeat = dict.getInt(MaplyTextLayoutRepeat,-1); + layoutSpacing = (float)dict.getDouble(MaplyTextLayoutSpacing,24.0); + layoutOffset = (float)dict.getDouble(MaplyTextLayoutOffset,0.0); +} + + // We use these for labels that have icons // Don't want to give them their own separate drawable, obviously -typedef std::map IconDrawables; +//typedef std::map IconDrawables; -LabelRenderer::LabelRenderer(Scene *scene,FontTextureManager *fontTexManager,const LabelInfo *labelInfo) - : useAttributedString(true), scene(scene), fontTexManager(fontTexManager), labelInfo(labelInfo), - textureAtlasSize(2048), labelRep(NULL) +LabelRenderer::LabelRenderer(Scene *scene, + SceneRenderer *renderer, + FontTextureManagerRef fontTexManager, + const LabelInfo *labelInfo, + SimpleIdentity maskProgID) : + scene(scene), + renderer(renderer), + coordAdapter(scene->getCoordAdapter()), + fontTexManager(std::move(fontTexManager)), + labelInfo(labelInfo), + maskProgID(maskProgID) { - coordAdapter = scene->getCoordAdapter(); } typedef std::map DrawableIDMap; -void LabelRenderer::render(PlatformThreadInfo *threadInfo,std::vector &labels,ChangeSet &changes) +Point3dVector LabelRenderer::convertGeoPtsToModelSpace(const VectorRing &inPts) { - TimeInterval curTime = scene->getCurrentTime(); + CoordSystemDisplayAdapter *coordAdapt = scene->getCoordAdapter(); + CoordSystem *coordSys = coordAdapt->getCoordSystem(); - // Drawables used for the icons - IconDrawables iconDrawables; + Point3dVector outPts; + outPts.reserve(inPts.size()); + + for (auto pt: inPts) { + auto localPt = coordSys->geographicToLocal3d(GeoCoord(pt.x(),pt.y())); + Point3d pt3d = coordAdapt->localToDisplay(localPt); + outPts.push_back(pt3d); + } - // Drawables we build up as we go - DrawableIDMap drawables; + return outPts; +} - for (unsigned int si=0;si &labels,ChangeSet &changes) +{ + const TimeInterval curTime = scene->getCurrentTime(); + + for (auto label : labels) { - SingleLabel *label = labels[si]; - RGBAColor theTextColor = labelInfo->textColor; const RGBAColor theBackColor = labelInfo->backColor; const RGBAColor theShadowColor = labelInfo->shadowColor; + const RGBAColor theTextColor = (label->infoOverride && label->infoOverride->hasTextColor) ? label->infoOverride->textColor : labelInfo->textColor; const float theShadowSize = labelInfo->shadowSize; - if (label->infoOverride) - { - if (label->infoOverride->hasTextColor) - theTextColor = label->infoOverride->textColor; - } - + // We set this if the color is embedded in the "font" const bool embeddedColor = labelInfo->outlineSize > 0.0 || (label->infoOverride && label->infoOverride->outlineSize > 0.0); // Ask the label to build the strings. There are OS specific things in there // We also need the real line height back (because it's in the font) - float lineHeight=0.0; - std::vector drawStrs = label->generateDrawableStrings(threadInfo,labelInfo,fontTexManager,lineHeight,changes); - Mbr drawMbr; - Mbr layoutMbr; + float lineHeight = 0.0; + const auto drawStrs = label->generateDrawableStrings(threadInfo,labelInfo,fontTexManager,lineHeight,changes); // Calculate total draw and layout MBRs - for (DrawableString *drawStr : drawStrs) + Mbr drawMbr, layoutMbr; + for (const auto drawStr : drawStrs) { drawMbr.expand(drawStr->mbr); layoutMbr.expand(drawStr->mbr); } + +#ifndef __ANDROID__ const float heightAboveBaseline = drawMbr.ur().y(); +#endif // Override the layout size, but do so from the middle - if (label->layoutSize.x() >= 0.0 && label->layoutSize.y() >= 0.0) { - Point2f center = layoutMbr.mid(); - - Point2f layoutSize(label->layoutSize.x(),label->layoutSize.y()); + if (label->layoutSize.x() >= 0.0 && label->layoutSize.y() >= 0.0) + { + const Point2f center = layoutMbr.mid(); + const Point2f layoutSize(label->layoutSize.x(),label->layoutSize.y()); layoutMbr.ll() = center - layoutSize/2.0; layoutMbr.ur() = center + layoutSize/2.0; } @@ -180,31 +179,22 @@ void LabelRenderer::render(PlatformThreadInfo *threadInfo,std::vectorscreenObject) { - float height = drawMbr.ur().y()-drawMbr.ll().y(); + const float height = drawMbr.ur().y()-drawMbr.ll().y(); - switch (labelInfo->labelJustify) + // If we're doing layout, don't justify it + if (!layoutEngine) { - case WhirlyKitLabelLeft: - justifyOff = Point2d(0,0.0); - break; - case WhirlyKitLabelMiddle: - justifyOff = Point2d(-(drawMbr.ur().x()-drawMbr.ll().x())/2.0,0.0); - break; - case WhirlyKitLabelRight: - justifyOff = Point2d(-(drawMbr.ur().x()-drawMbr.ll().x()),0.0); - break; + const auto width = drawMbr.ur().x() - drawMbr.ll().x(); + switch (labelInfo->labelJustify) + { + case WhirlyKitLabelRight: justifyOff.x() = -width; break; + case WhirlyKitLabelMiddle: justifyOff.x() = -width / 2.0; break; + default: break; + } } - if (layoutEngine) - { - screenShape = layoutObject = &scratchLayoutObject; - } else { - screenShape = &scratchScreenSpaceObject; - } - - // If we're doing layout, don't justify it - if (layoutEngine) - justifyOff = Point2d(0,0); + screenShape = layoutEngine ? &scratchLayoutObject : &scratchScreenSpaceObject; + layoutObject = layoutEngine ? &scratchLayoutObject : nullptr; #ifndef __ANDROID__ // Except we do need to tweak things a little, even for the layout engine @@ -230,43 +220,54 @@ void LabelRenderer::render(PlatformThreadInfo *threadInfo,std::vectorsetWorldLoc(coordAdapter->localToDisplay(coordAdapter->getCoordSystem()->geographicToLocal3d(label->loc))); // If there's an icon, we need to offset - Point2d iconSize = (label->iconTexture==EmptyIdentity ? Point2d(0,0) : (label->iconSize.x() == 0.0 ? Point2d(height,height) : Point2d(label->iconSize.x(),label->iconSize.y()))); + const Point2d iconSize = (label->iconTexture==EmptyIdentity ? Point2d(0,0) : (label->iconSize.x() == 0.0 ? Point2d(height,height) : Point2d(label->iconSize.x(),label->iconSize.y()))); iconOff = iconSize; // Throw a rectangle in the background RGBAColor backColor = theBackColor; double backBorder = 0.0; - if (backColor.a != 0.0) + ScreenSpaceConvexGeometry backGeom; + if (backColor.a != 0.0 || label->maskID != EmptyIdentity) { // Note: This is an arbitrary border around the text backBorder = 4.0; justifyOff.x() += backBorder; - ScreenSpaceObject::ConvexGeometry smGeom; + ScreenSpaceConvexGeometry smGeom; smGeom.progID = labelInfo->programID; - Point2d ll = Point2d(drawMbr.ll().x(),drawMbr.ll().y())+iconOff+Point2d(-backBorder,-backBorder); - Point2d ur = Point2d(drawMbr.ur().x(),drawMbr.ur().y())+iconOff+Point2d(backBorder,backBorder); + const Point2d ll = Point2d(drawMbr.ll().x(),drawMbr.ll().y())+iconOff+Point2d(-backBorder,-backBorder); + const Point2d ur = Point2d(drawMbr.ur().x(),drawMbr.ur().y())+iconOff+Point2d(backBorder,backBorder); smGeom.coords.push_back(Point2d(ur.x()+label->screenOffset.x(),ll.y()+label->screenOffset.y())+iconOff+justifyOff); - smGeom.texCoords.push_back(TexCoord(0,1)); + smGeom.texCoords.emplace_back(0,1); smGeom.coords.push_back(Point2d(ur.x()+label->screenOffset.x(),ur.y()+label->screenOffset.y())+iconOff+justifyOff); - smGeom.texCoords.push_back(TexCoord(0,0)); + smGeom.texCoords.emplace_back(0,0); smGeom.coords.push_back(Point2d(ll.x()+label->screenOffset.x(),ur.y()+label->screenOffset.y())+iconOff+justifyOff); - smGeom.texCoords.push_back(TexCoord(1,0)); + smGeom.texCoords.emplace_back(1,0); smGeom.coords.push_back(Point2d(ll.x()+label->screenOffset.x(),ll.y()+label->screenOffset.y())+iconOff+justifyOff); - smGeom.texCoords.push_back(TexCoord(1,1)); + smGeom.texCoords.emplace_back(1,1); smGeom.drawPriority = labelInfo->drawPriority; smGeom.color = backColor; + backGeom = smGeom; screenShape->addGeometry(smGeom); } + + // Handle the mask rendering if needed + if (label->maskID != EmptyIdentity && label->maskRenderTargetID != EmptyIdentity) { + // Make a copy of the geometry, but target it to the mask render target + backGeom.vertexAttrs.insert(SingleVertexAttribute(a_maskNameID, renderer->getSlotForNameID(a_maskNameID), (int)label->maskID)); + backGeom.renderTargetID = label->maskRenderTargetID; + backGeom.progID = maskProgID; + screenShape->addGeometry(backGeom); + } // If it's being passed to the layout engine, do that as well if (layoutEngine) { // Put together the layout info - // layoutObject->hint = label->text; + //layoutObject->hint = label->text; layoutObject->layoutPts.push_back(Point2d(layoutMbr.ll().x()+label->screenOffset.x()-backBorder, layoutMbr.ll().y()+label->screenOffset.y()-backBorder)+iconOff+justifyOff); layoutObject->layoutPts.push_back(Point2d(layoutMbr.ur().x()+label->screenOffset.x()+backBorder, @@ -277,27 +278,43 @@ void LabelRenderer::render(PlatformThreadInfo *threadInfo,std::vectorscreenOffset.y()+backBorder)+iconOff+justifyOff); layoutObject->selectPts = layoutObject->layoutPts; - // layoutObj->iconSize = Point2f(iconSize,iconSize); + //layoutObj->iconSize = Point2f(iconSize,iconSize); layoutObject->importance = layoutImportance; layoutObject->acceptablePlacement = layoutPlacement; layoutObject->setEnable(labelInfo->enable); + // Setup layout points if we have them + if (!label->layoutShape.empty()) { + layoutObject->layoutShape = convertGeoPtsToModelSpace(label->layoutShape); + layoutObject->layoutRepeat = labelInfo->layoutRepeat; + layoutObject->layoutOffset = labelInfo->layoutOffset; + layoutObject->layoutSpacing = labelInfo->layoutSpacing; + layoutObject->layoutWidth = height; + layoutObject->layoutDebug = labelInfo->layoutDebug; + } + // The shape starts out disabled screenShape->setEnable(labelInfo->enable); if (labelInfo->startEnable != labelInfo->endEnable) + { screenShape->setEnableTime(labelInfo->startEnable, labelInfo->endEnable); + } screenShape->setOffset(Point2d(MAXFLOAT,MAXFLOAT)); - } else { + } + else + { screenShape->setEnable(labelInfo->enable); if (labelInfo->startEnable != labelInfo->endEnable) + { screenShape->setEnableTime(labelInfo->startEnable, labelInfo->endEnable); + } } // Deal with the icon here becaue we need its geometry - ScreenSpaceObject::ConvexGeometry iconGeom; + ScreenSpaceConvexGeometry iconGeom; if (label->iconTexture != EmptyIdentity && screenShape) { - SubTexture subTex = scene->getSubTexture(label->iconTexture); + const SubTexture subTex = scene->getSubTexture(label->iconTexture); std::vector texCoord; texCoord.resize(4); texCoord[3].u() = 0.0; texCoord[3].v() = 0.0; @@ -351,30 +368,42 @@ void LabelRenderer::render(PlatformThreadInfo *threadInfo,std::vector 0) - for (unsigned int ig=0;igscreenOffset.x(),ll.y()+-label->screenOffset.y()); - select2d.pts[1] = Point2f(ll.x()+label->screenOffset.x(),ur.y()+-label->screenOffset.y()); - select2d.pts[2] = Point2f(ur.x()+label->screenOffset.x(),ur.y()+-label->screenOffset.y()); - select2d.pts[3] = Point2f(ur.x()+label->screenOffset.x(),ll.y()+-label->screenOffset.y()); + double flip = 1.0; +#ifdef __ANDROID__ + flip = -1.0; +#endif + select2d.pts[0] = Point2f(ll.x()+label->screenOffset.x(),ll.y()+-flip*label->screenOffset.y()); + select2d.pts[1] = Point2f(ll.x()+label->screenOffset.x(),ur.y()+-flip*label->screenOffset.y()); + select2d.pts[2] = Point2f(ur.x()+label->screenOffset.x(),ur.y()+-flip*label->screenOffset.y()); + select2d.pts[3] = Point2f(ur.x()+label->screenOffset.x(),ll.y()+-flip*label->screenOffset.y()); select2d.selectID = label->selectID; - select2d.minVis = labelInfo->minVis; - select2d.maxVis = labelInfo->maxVis; + select2d.minVis = (float)labelInfo->minVis; + select2d.maxVis = (float)labelInfo->maxVis; if (label->hasMotion) { MovingRectSelectable2D movingSelect2d; (RectSelectable2D &)movingSelect2d = select2d; + movingSelect2d.endCenter = screenShape->getEndWorldLoc(); movingSelect2d.startTime = screenShape->getStartTime(); movingSelect2d.endTime = screenShape->getEndTime(); movingSelectables2D.push_back(movingSelect2d); - } else + } + else + { selectables2D.push_back(select2d); + } } } @@ -396,45 +425,34 @@ void LabelRenderer::render(PlatformThreadInfo *threadInfo,std::vectormbr.ur().x()-drawStr->mbr.ll().x()))/2.0; break; - case WhirlyKitTextLeft: - // Leave it alone - break; case WhirlyKitTextRight: lineOff.x() = drawMbr.ur().x()-drawMbr.ll().x() - (drawStr->mbr.ur().x()-drawStr->mbr.ll().x()); break; + default: break; } // Turn the glyph polys into simple geometry // We do this in a weird order to stick the shadow underneath - for (int ss=((theShadowSize > 0.0) ? 0: 1);ss<2;ss++) + for (int ss=((theShadowSize > 0.0) ? 0 : 1);ss<2;ss++) { - Point2d soff; - RGBAColor color; - if (ss == 1) + const Point2d soff = (ss == 1) ? Point2d(0,0) : Point2d(theShadowSize,theShadowSize); + const RGBAColor color = (ss == 1) ? (embeddedColor ? RGBAColor::white() : theTextColor) : theShadowColor; + for (const auto &poly : drawStr->glyphPolys) { - soff = Point2d(0,0); - color = embeddedColor ? RGBAColor(255,255,255,255) : theTextColor; - } else { - soff = Point2d(theShadowSize,theShadowSize); - color = theShadowColor; - } - for (unsigned int ii=0;iiglyphPolys.size();ii++) - { - const DrawableString::Rect &poly = drawStr->glyphPolys[ii]; // Note: Ignoring the desired size in favor of the font size - ScreenSpaceObject::ConvexGeometry smGeom; + ScreenSpaceConvexGeometry smGeom; smGeom.progID = labelInfo->programID; smGeom.coords.push_back(Point2d(poly.pts[1].x()+label->screenOffset.x(),poly.pts[0].y()+label->screenOffset.y() + offsetY) + soff + iconOff + justifyOff + lineOff); - smGeom.texCoords.push_back(TexCoord(poly.texCoords[1].u(),poly.texCoords[0].v())); + smGeom.texCoords.emplace_back(poly.texCoords[1].u(),poly.texCoords[0].v()); smGeom.coords.push_back(Point2d(poly.pts[1].x()+label->screenOffset.x(),poly.pts[1].y()+label->screenOffset.y() + offsetY) + soff + iconOff + justifyOff + lineOff); - smGeom.texCoords.push_back(TexCoord(poly.texCoords[1].u(),poly.texCoords[1].v())); + smGeom.texCoords.emplace_back(poly.texCoords[1].u(),poly.texCoords[1].v()); smGeom.coords.push_back(Point2d(poly.pts[0].x()+label->screenOffset.x(),poly.pts[1].y()+label->screenOffset.y() + offsetY) + soff + iconOff + justifyOff + lineOff); - smGeom.texCoords.push_back(TexCoord(poly.texCoords[0].u(),poly.texCoords[1].y())); + smGeom.texCoords.emplace_back(poly.texCoords[0].u(),poly.texCoords[1].y()); smGeom.coords.push_back(Point2d(poly.pts[0].x()+label->screenOffset.x(),poly.pts[0].y()+label->screenOffset.y() + offsetY) + soff + iconOff + justifyOff + lineOff); - smGeom.texCoords.push_back(TexCoord(poly.texCoords[0].u(),poly.texCoords[0].v())); + smGeom.texCoords.emplace_back(poly.texCoords[0].u(),poly.texCoords[0].v()); smGeom.texIDs.push_back(poly.subTex.texId); smGeom.color = color; @@ -452,29 +470,13 @@ void LabelRenderer::render(PlatformThreadInfo *threadInfo,std::vectorsecond)); - - // Flush out the icon drawables as well - for (IconDrawables::iterator it = iconDrawables.begin(); - it != iconDrawables.end(); ++it) - { - BasicDrawable *iconDrawable = it->second; - - if (labelInfo->fadeIn > 0.0) + for (auto drawStr : drawStrs) { - TimeInterval curTime = scene->getCurrentTime(); - iconDrawable->setFade(curTime,curTime+labelInfo->fadeIn); + delete drawStr; } - changes.push_back(new AddDrawableReq(iconDrawable)); - labelRep->drawIDs.insert(iconDrawable->getId()); } } } + +#include diff --git a/common/WhirlyGlobeLib/src/LayoutManager.cpp b/common/WhirlyGlobeLib/src/LayoutManager.cpp index d7ab3bafab..9aad1f0601 100644 --- a/common/WhirlyGlobeLib/src/LayoutManager.cpp +++ b/common/WhirlyGlobeLib/src/LayoutManager.cpp @@ -1,9 +1,8 @@ -/* - * LayoutManager.h +/* LayoutManager.cpp * WhirlyGlobeLib * * Created by Steve Gifford on 7/15/13. - * Copyright 2011-2019 mousebird consulting. + * Copyright 2011-2021 mousebird consulting. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -15,12 +14,13 @@ * 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. - * */ #import "LayoutManager.h" #import "WhirlyGeometry.h" #import "GlobeMath.h" +#import "SharedAttributes.h" +#import "LinearTextBuilder.h" #import "WhirlyKitLog.h" using namespace Eigen; @@ -30,11 +30,13 @@ namespace WhirlyKit // Default constructor for layout object LayoutObject::LayoutObject() - : ScreenSpaceObject(), importance(MAXFLOAT), clusterGroup(-1), acceptablePlacement(WhirlyKitLayoutPlacementLeft | WhirlyKitLayoutPlacementRight | WhirlyKitLayoutPlacementAbove | WhirlyKitLayoutPlacementBelow) + : ScreenSpaceObject(), layoutRepeat(0), layoutOffset(0.0), layoutSpacing(20.0), layoutWidth(10.0), layoutDebug(false), + importance(MAXFLOAT), clusterGroup(-1), acceptablePlacement(WhirlyKitLayoutPlacementLeft | WhirlyKitLayoutPlacementRight | WhirlyKitLayoutPlacementAbove | WhirlyKitLayoutPlacementBelow) { } LayoutObject::LayoutObject(SimpleIdentity theId) : ScreenSpaceObject(theId), + layoutRepeat(0), layoutOffset(0.0), layoutSpacing(20.0), layoutWidth(10.0), layoutDebug(false), importance(MAXFLOAT), clusterGroup(-1), acceptablePlacement(WhirlyKitLayoutPlacementLeft | WhirlyKitLayoutPlacementRight | WhirlyKitLayoutPlacementAbove | WhirlyKitLayoutPlacementBelow) { } @@ -49,7 +51,7 @@ void LayoutObject::setLayoutSize(const Point2d &layoutSize,const Point2d &offset layoutPts.push_back(layoutSize+offset); layoutPts.push_back(Point2d(0.0,layoutSize.y())+offset); } - + void LayoutObject::setSelectSize(const Point2d &selectSize,const Point2d &offset) { if (selectSize.x() == 0.0 && selectSize.y() == 0.0) @@ -71,41 +73,44 @@ LayoutObjectEntry::LayoutObjectEntry(SimpleIdentity theId) } LayoutManager::LayoutManager() - : maxDisplayObjects(0), hasUpdates(false), clusterGen(NULL) + : maxDisplayObjects(0), hasUpdates(false), clusterGen(nullptr), vecProgID(EmptyIdentity) { } LayoutManager::~LayoutManager() { - for (LayoutEntrySet::iterator it = layoutObjects.begin(); - it != layoutObjects.end(); ++it) - delete *it; + std::lock_guard guardLock(lock); + + for (auto layoutObject : layoutObjects) + { + delete layoutObject; + } layoutObjects.clear(); } void LayoutManager::setMaxDisplayObjects(int numObjects) { - std::lock_guard guardLock(layoutLock); + std::lock_guard guardLock(lock); maxDisplayObjects = numObjects; } void LayoutManager::setOverrideUUIDs(const std::set &uuids) { - std::lock_guard guardLock(layoutLock); + std::lock_guard guardLock(lock); overrideUUIDs = uuids; } void LayoutManager::addLayoutObjects(const std::vector &newObjects) { - std::lock_guard guardLock(layoutLock); + std::lock_guard guardLock(lock); - for (unsigned int ii=0;iiobj = newObjects[ii]; + const LayoutObject &layoutObj = newObject; + auto *entry = new LayoutObjectEntry(layoutObj.getId()); + entry->obj = newObject; layoutObjects.insert(entry); } hasUpdates = true; @@ -113,13 +118,13 @@ void LayoutManager::addLayoutObjects(const std::vector &newObjects void LayoutManager::addLayoutObjects(const std::vector &newObjects) { - std::lock_guard guardLock(layoutLock); + std::lock_guard guardLock(lock); - for (unsigned int ii=0;iigetId()); - entry->obj = *(newObjects[ii]); + const LayoutObject *layoutObj = newObject; + auto *entry = new LayoutObjectEntry(layoutObj->getId()); + entry->obj = *newObject; layoutObjects.insert(entry); } hasUpdates = true; @@ -128,13 +133,12 @@ void LayoutManager::addLayoutObjects(const std::vector &newObjec /// Enable/disable layout objects void LayoutManager::enableLayoutObjects(const SimpleIDSet &theObjects,bool enable) { - std::lock_guard guardLock(layoutLock); + std::lock_guard guardLock(lock); - for (SimpleIDSet::const_iterator it = theObjects.begin(); - it != theObjects.end(); ++it) + for (const auto &theObject : theObjects) { - LayoutObjectEntry entry(*it); - LayoutEntrySet::iterator eit = layoutObjects.find(&entry); + LayoutObjectEntry key(theObject); + const auto eit = layoutObjects.find(&key); if (eit != layoutObjects.end()) { LayoutObjectEntry *entry = *eit; @@ -151,13 +155,12 @@ void LayoutManager::enableLayoutObjects(const SimpleIDSet &theObjects,bool enabl void LayoutManager::removeLayoutObjects(const SimpleIDSet &oldObjects) { - std::lock_guard guardLock(layoutLock); + std::lock_guard guardLock(lock); - for (SimpleIDSet::const_iterator it = oldObjects.begin(); - it != oldObjects.end(); ++it) + for (const auto &oldObject : oldObjects) { - LayoutObjectEntry entry(*it); - LayoutEntrySet::iterator eit = layoutObjects.find(&entry); + LayoutObjectEntry entry(oldObject); + const auto eit = layoutObjects.find(&entry); if (eit != layoutObjects.end()) { delete *eit; @@ -169,25 +172,18 @@ void LayoutManager::removeLayoutObjects(const SimpleIDSet &oldObjects) bool LayoutManager::hasChanges() { - bool ret = false; - - std::lock_guard guardLock(layoutLock); - - ret = hasUpdates; - - return ret; + std::lock_guard guardLock(lock); + return hasUpdates; } // Return the screen space objects in a form the selection manager can understand void LayoutManager::getScreenSpaceObjects(const SelectionManager::PlacementInfo &pInfo,std::vector &screenSpaceObjs) { - std::lock_guard guardLock(layoutLock); + std::lock_guard guardLock(lock); // First the regular screen space objects - for (LayoutEntrySet::iterator it = layoutObjects.begin(); - it != layoutObjects.end(); ++it) + for (const auto &entry : layoutObjects) { - LayoutObjectEntry *entry = *it; if (entry->currentEnable && entry->obj.enable) { ScreenSpaceObjectLocation ssObj; @@ -204,7 +200,7 @@ void LayoutManager::getScreenSpaceObjects(const SelectionManager::PlacementInfo } // Then the clusters - for (auto &cluster : clusters) + for (const auto &cluster : clusters) { ScreenSpaceObjectLocation ssObj; ssObj.shapeIDs = cluster.objectIDs; @@ -217,11 +213,10 @@ void LayoutManager::getScreenSpaceObjects(const SelectionManager::PlacementInfo screenSpaceObjs.push_back(ssObj); } } - -void LayoutManager::addClusterGenerator(ClusterGenerator *inClusterGen) -{ - std::lock_guard guardLock(layoutLock); +void LayoutManager::addClusterGenerator(PlatformThreadInfo *, ClusterGenerator *inClusterGen) +{ + std::lock_guard guardLock(lock); clusterGen = inClusterGen; } @@ -229,10 +224,10 @@ void LayoutManager::addClusterGenerator(ClusterGenerator *inClusterGen) class ClusteredObjects { public: - ClusteredObjects() { } - ClusteredObjects(int clusterID) : clusterID(clusterID) { } + ClusteredObjects() = default; + explicit ClusteredObjects(int clusterID) : clusterID(clusterID) { } - int clusterID; + int clusterID{}; LayoutSortingSet layoutObjects; }; @@ -255,7 +250,7 @@ static const int OverlapSampleY = 60; // Now much around the screen we'll take into account static const float ScreenBuffer = 0.1; -bool LayoutManager::calcScreenPt(Point2f &objPt,LayoutObject *layoutObj,ViewStateRef viewState,const Mbr &screenMbr,const Point2f &frameBufferSize) +bool LayoutManager::calcScreenPt(Point2f &objPt,LayoutObject *layoutObj,const ViewStateRef &viewState,const Mbr &screenMbr,const Point2f &frameBufferSize) { // Figure out where this will land bool isInside = false; @@ -273,7 +268,7 @@ bool LayoutManager::calcScreenPt(Point2f &objPt,LayoutObject *layoutObj,ViewStat return isInside; } -Matrix2d LayoutManager::calcScreenRot(float &screenRot,ViewStateRef viewState,WhirlyGlobe::GlobeViewState *globeViewState,ScreenSpaceObject *ssObj,const Point2f &objPt,const Matrix4d &modelTrans,const Matrix4d &normalMat,const Point2f &frameBufferSize) +Matrix2d LayoutManager::calcScreenRot(float &screenRot,const ViewStateRef &viewState,WhirlyGlobe::GlobeViewState *globeViewState,ScreenSpaceObject *ssObj,const Point2f &objPt,const Matrix4d &modelTrans,const Matrix4d &normalMat,const Point2f &frameBufferSize) { // Switch from counter-clockwise to clockwise double rot = 2*M_PI-ssObj->rotation; @@ -300,14 +295,15 @@ Matrix2d LayoutManager::calcScreenRot(float &screenRot,ViewStateRef viewState,Wh Vector4d projRot = normalMat * Vector4d(rotVec.x(),rotVec.y(),rotVec.z(),0.0); // Use the resulting x & y - screenRot = atan2(projRot.y(),projRot.x())-M_PI/2.0; + screenRot = (float)(atan2(projRot.y(),projRot.x())-M_PI/2.0); // Keep the labels upright - if (ssObj->keepUpright) - if (screenRot > M_PI/2 && screenRot < 3*M_PI/2) - screenRot = screenRot + M_PI; + if (ssObj->keepUpright && screenRot > M_PI/2 && screenRot < 3*M_PI/2) + { + screenRot = (float)(screenRot + M_PI); + } Matrix2d screenRotMat; screenRotMat = Eigen::Rotation2Dd(screenRot); - + return screenRotMat; } @@ -316,7 +312,7 @@ class LayoutObjectContainer { public: LayoutObjectContainer() : importance(-1.0) { } - LayoutObjectContainer(LayoutObjectEntry *entry) { + explicit LayoutObjectContainer(LayoutObjectEntry *entry) { objs.push_back(entry); importance = objs[0]->obj.importance; } @@ -337,36 +333,39 @@ typedef std::vector LayoutContainerVec; typedef std::map UniqueLayoutObjectMap; // Do the actual layout logic. We'll modify the offset and on value in place. -bool LayoutManager::runLayoutRules(ViewStateRef viewState,std::vector &clusterEntries,std::vector &clusterParams) +bool LayoutManager::runLayoutRules(PlatformThreadInfo *threadInfo, + const ViewStateRef &viewState, + std::vector &clusterEntries, + std::vector &outClusterParams, + ChangeSet &changes) { if (layoutObjects.empty()) return false; bool hadChanges = false; - + ClusteredObjectsSet clusterObjs; LayoutContainerVec layoutObjs; // Special snowflake layout objects (with unique names) UniqueLayoutObjectMap uniqueLayoutObjs; // The globe has some special requirements - WhirlyGlobe::GlobeViewState *globeViewState = dynamic_cast(viewState.get()); - Maply::MapViewState *mapViewState = dynamic_cast(viewState.get()); + auto globeViewState = dynamic_cast(viewState.get()); + auto mapViewState = dynamic_cast(viewState.get()); // View related matrix stuff Matrix4d modelTrans = viewState->fullMatrices[0]; - Matrix4f fullMatrix4f = Matrix4dToMatrix4f(viewState->fullMatrices[0]); - Matrix4f fullNormalMatrix4f = Matrix4dToMatrix4f(viewState->fullNormalMatrices[0]); + Matrix4d fullMatrix = viewState->fullMatrices[0]; + Matrix4d fullNormalMatrix = viewState->fullNormalMatrices[0]; Matrix4d normalMat = viewState->fullMatrices[0].inverse().transpose(); // Turn everything off and sort by importance - for (LayoutEntrySet::iterator it = layoutObjects.begin(); - it != layoutObjects.end(); ++it) + for (const auto &layoutObject : layoutObjects) { - LayoutObjectEntry *layoutObj = *it; + LayoutObjectEntry *layoutObj = layoutObject; if (layoutObj->obj.enable) { - LayoutObjectEntry *obj = *it; + LayoutObjectEntry *obj = layoutObject; bool use = false; if (globeViewState) { @@ -382,8 +381,11 @@ bool LayoutManager::runLayoutRules(ViewStateRef viewState,std::vectorobj.worldLoc),Vector3dToVector3f(layoutObj->obj.worldLoc.normalized()),fullMatrix4f,fullNormalMatrix4f) > 0.0; + // Layout shape following doesn't work with this check + if (obj->obj.layoutShape.empty()) { + // Make sure this one is facing toward the viewer + use = CheckPointAndNormFacing(layoutObj->obj.worldLoc,layoutObj->obj.worldLoc.normalized(),fullMatrix,fullNormalMatrix) > 0.0; + } } if (use) @@ -393,7 +395,7 @@ bool LayoutManager::runLayoutRules(ViewStateRef viewState,std::vectorobj.clusterGroup); - ClusteredObjects *thisClusterObj = NULL; + ClusteredObjects *thisClusterObj = nullptr; auto cit = clusterObjs.find(&findClusterObj); if (cit == clusterObjs.end()) { @@ -452,23 +454,20 @@ bool LayoutManager::runLayoutRules(ViewStateRef viewState,std::vectorstartLayoutObjects(); + clusterGen->startLayoutObjects(threadInfo); // Lay out the clusters in order - for (ClusteredObjectsSet::iterator it = clusterObjs.begin(); it != clusterObjs.end(); ++it) + for (const auto &cluster : clusterObjs) { - ClusteredObjects *cluster = *it; - clusterParams.resize(clusterParams.size()+1); - ClusterGenerator::ClusterClassParams ¶ms = clusterParams.back(); - clusterGen->paramsForClusterClass(cluster->clusterID,params); + outClusterParams.resize(outClusterParams.size() + 1); + ClusterGenerator::ClusterClassParams ¶ms = outClusterParams.back(); + clusterGen->paramsForClusterClass(threadInfo,cluster->clusterID,params); ClusterHelper clusterHelper(screenMbr,OverlapSampleX,OverlapSampleY,resScale,params.clusterSize); // Add all the various objects to the cluster and figure out overlaps - for (LayoutSortingSet::iterator sit = cluster->layoutObjects.begin(); sit != cluster->layoutObjects.end(); ++sit) + for (const auto &entry : cluster->layoutObjects) { - LayoutObjectEntry *entry = *sit; - // Project the point and figure out the rotation bool isActive = true; Point2f objPt; @@ -500,7 +499,6 @@ bool LayoutManager::runLayoutRules(ViewStateRef viewState,std::vectorobj.getId()); - clusterGen->makeLayoutObject(cluster->clusterID, objsForCluster, clusterEntry.layoutObj); + clusterGen->makeLayoutObject(threadInfo,cluster->clusterID, objsForCluster, clusterEntry.layoutObj); if (!params.selectable) clusterEntry.layoutObj.selectPts.clear(); } - clusterEntry.clusterParamID = (int)(clusterParams.size()-1); + clusterEntry.clusterParamID = (int)(outClusterParams.size() - 1); // Figure out if all the objects in this new cluster come from the same old cluster // and assign the new cluster ID @@ -575,11 +573,13 @@ bool LayoutManager::runLayoutRules(ViewStateRef viewState,std::vectorendLayoutObjects(); + clusterGen->endLayoutObjects(threadInfo); } // NSLog(@"----Starting Layout----"); @@ -588,7 +588,7 @@ bool LayoutManager::runLayoutRules(ViewStateRef viewState,std::vectorobj,viewState,screenMbr,frameBufferSize); - - isActive &= isInside; - - // Deal with the rotation - if (layoutObj->obj.rotation != 0.0) - screenRotMat = calcScreenRot(screenRot,viewState,globeViewState,&layoutObj->obj,objPt,modelTrans,normalMat,frameBufferSize); + // Layout along a shape + if (!layoutObj->obj.layoutShape.empty()) { + // Sometimes there are just a few instances + int numInstances = 0; - // Now for the overlap checks - if (isActive) - { - // Try the four different orientations - if (!layoutObj->obj.layoutPts.empty()) - { - bool validOrient = false; - for (unsigned int orient=0;orient<6;orient++) - { - // May only want to be placed certain ways. Fair enough. - if (!(layoutObj->obj.acceptablePlacement & (1<viewMatrices.size();oi++) { + // Set up the text builder to get a set of individual runs to follow + LinearTextBuilder textBuilder(viewState,oi,frameBufferSize,layoutObj->obj.layoutWidth/2.0,&layoutObj->obj); + textBuilder.setPoints(layoutObj->obj.layoutShape); + textBuilder.process(); + // Sort the runs by length and get rid of the ones too short + textBuilder.sortRuns(2.0*layoutObj->obj.layoutSpacing); + + // Follow the individual runs + std::vector > layoutInstances; + std::vector layoutModelInstances; + + auto runs = textBuilder.getScreenVecs(); +// unsigned int ri=0; + for (auto run: runs) { +// wkLog("Run %d",ri++); + + // We need the length of the glyphs and their center + Mbr layoutMbr(layoutObj->obj.layoutPts); + float textLen = layoutMbr.ur().x(); + float midY = layoutMbr.mid().y(); + + LinearWalker walk(run); + + // Figure out how many times we could lay this out + float textRoom = walk.getTotalLength() - 2.0*layoutObj->obj.layoutSpacing; + float textInstance = textRoom / textLen; + + for (unsigned int ini=0;iniobj.layoutSpacing, nullptr, nullptr)) + continue; + + std::vector layoutMats; + + // Center around the world point on the screen + Point2f midRun; + if (!walk.nextPoint(layoutMbr.span().x()/2.0, &midRun, nullptr, false)) + continue; + Point2f worldScreenPt = midRun; + Point3d worldPt(0.0,0.0,0.0); + if (!textBuilder.screenToWorld(midRun, worldPt)) continue; - const Point2dVector &layoutPts = layoutObj->obj.layoutPts; - Mbr layoutMbr; - for (unsigned int li=0;li overlapPts; + + // Walk through the individual glyphs + bool failed = false; + for (unsigned int ig=0;igobj.geometry.size();ig++) { + const auto &geom = layoutObj->obj.geometry[ig]; + Mbr glyphMbr(geom.coords); + Point2f span = glyphMbr.span(); + Point2f midGlyph = glyphMbr.mid(); + Affine2d transOrigin(Translation2d(-midGlyph.x(),-midY)); + + // Walk along the line to get a good center + Point2f centerPt; + Point2f norm; + if (!walk.nextPoint(span.x(),¢erPt,&norm)) { + failed = true; break; - // Below - case 5: - objOffset = Point2d(-layoutSpan.x()/2.0,layoutSpan.y()); + } + walk.nextPoint(span.x(), nullptr, nullptr); + + // Don't forget the space between glyphs + if (ig < layoutObj->obj.geometry.size()-1) { + Mbr glyphNextMbr(layoutObj->obj.geometry[ig+1].coords); + float padX = glyphNextMbr.ll().x() - glyphMbr.ur().x(); + if (!walk.nextPoint(padX, nullptr, nullptr)) { + failed = true; + break; + } + } + + // Translate the glyph into that position + Affine2d transPlace(Translation2d((centerPt.x()-worldScreenPt.x())/2.0, + -(centerPt.y()-worldScreenPt.y())/2.0)); + double ang = -1.0 * (atan2(norm.y(),norm.x()) - M_PI/2.0); + Matrix2d screenRot = Eigen::Rotation2Dd(ang).matrix(); + Matrix3d screenRotMat = Matrix3d::Identity(); + for (unsigned ix=0;ix<2;ix++) + for (unsigned iy=0;iy<2;iy++) + screenRotMat(ix, iy) = screenRot(ix, iy); + Matrix3d scaleMat = Eigen::AlignedScaling3d(resScale,resScale,1.0); + Matrix3d overlapMat = transPlace.matrix() * screenRotMat * scaleMat * transOrigin.matrix(); +// Matrix3d overlapMat = transPlace.matrix() * transOrigin.matrix(); + layoutMats.push_back(transPlace.matrix() * screenRotMat * transOrigin.matrix()); +// layoutMats.push_back(transPlace.matrix() * transOrigin.matrix()); + + // Check for overlap + Point2dVector objPts; objPts.reserve(4); + for (unsigned int oi=0;oi<4;oi++) { + Point3d pt = overlapMat * Point3d(geom.coords[oi].x(),geom.coords[oi].y(),1.0); + Point2d objPt(pt.x()+worldScreenPt.x(),pt.y()+worldScreenPt.y()); + objPts.push_back(objPt); + } + +// if (!failed) { +// wkLog(" Geometry %d",ig); +// for (unsigned int ip=0;ipobj.setRotation(textBuilder.getViewStateRotation()); + layoutModelInstances.push_back(worldPt); + layoutInstances.push_back(layoutMats); + numInstances++; + + // Add the individual glyphs to the overlap manager + for (auto &glyph: overlapPts) + overlapMan.addObject(glyph); + } - // Now try it. Objects we've pegged as essential always win - if (overlapMan.addObject(objPts) || container.importance >= MAXFLOAT) - { - validOrient = true; - pickedOne = true; + if (layoutObj->obj.layoutRepeat > 0 && numInstances >= layoutObj->obj.layoutRepeat) break; - } } - isActive = validOrient; + if (layoutObj->obj.layoutRepeat > 0 && numInstances >= layoutObj->obj.layoutRepeat) + break; + } + + if (!layoutInstances.empty()) { + isActive = true; + layoutObj->newEnable = true; + layoutObj->changed = true; + layoutObj->obj.layoutPlaces = layoutInstances; + layoutObj->obj.layoutModelPlaces = layoutModelInstances; + hadChanges |= layoutObj->changed; + layoutObj->newCluster = -1; + layoutObj->offset = Point2d(0.0,0.0); + } else { + isActive = false; + layoutObj->newEnable = false; + layoutObj->obj.layoutPlaces.clear(); + layoutObj->obj.layoutModelPlaces.clear(); + } + + if (layoutObj->currentEnable != isActive) { + layoutObj->changed = true; + } + + // Debugging visual output + ShapeSet dispShapes = textBuilder.getVisualVecs(); + if (!dispShapes.empty() && layoutObj->obj.layoutDebug) { + // Turn them back into vectors to debug + VectorInfo vecInfo; + vecInfo.color = RGBAColor::red(); + vecInfo.lineWidth = 1.0; + vecInfo.drawPriority = 10000000; + vecInfo.programID = vecProgID; + + SimpleIdentity vecId = vecManage->addVectors(&dispShapes, vecInfo, changes); + if (vecId != EmptyIdentity) + debugVecIDs.insert(vecId); } } + } else { + // Layout at a point -// wkLogLevel(Debug, " Valid (%s): %s, pos = (%f,%f), offset = (%f,%f)",(isActive ? "yes" : "no"),layoutObj->obj.hint.c_str(),objPt.x(),objPt.y(), -// layoutObj->offset.x(),layoutObj->offset.y()); + // Figure out the rotation situation + float screenRot = 0.0; + Matrix2d screenRotMat; + if (pickedOne) + isActive = false; + + if (isActive) + { + Point2f objPt; + bool isInside = calcScreenPt(objPt,&layoutObj->obj,viewState,screenMbr,frameBufferSize); + + isActive &= isInside; + + // Deal with the rotation + if (layoutObj->obj.rotation != 0.0) + screenRotMat = calcScreenRot(screenRot,viewState,globeViewState,&layoutObj->obj,objPt,modelTrans,normalMat,frameBufferSize); + + // Now for the overlap checks + if (isActive) + { + // Try the four different orientations + if (!layoutObj->obj.layoutPts.empty()) + { + bool validOrient = false; + for (unsigned int orient=0;orient<6;orient++) + { + // May only want to be placed certain ways. Fair enough. + if (!(layoutObj->obj.acceptablePlacement & (1<obj.layoutPts; + Mbr layoutMbr; + for (unsigned int li=0;li= MAXFLOAT) + { + validOrient = true; + pickedOne = true; + break; + } + } + + isActive = validOrient; + } + } + + // wkLogLevel(Debug, " Valid (%s): %s, pos = (%f,%f), offset = (%f,%f)",(isActive ? "yes" : "no"),layoutObj->obj.hint.c_str(),objPt.x(),objPt.y(), + // layoutObj->offset.x(),layoutObj->offset.y()); + } } - + if (isActive) numSoFar++; @@ -761,11 +941,28 @@ static float const NewObjectFadeIn = 0.0; //static float const OldObjectFadeOut = 0.0; // Layout all the objects we're tracking -void LayoutManager::updateLayout(ViewStateRef viewState,ChangeSet &changes) +void LayoutManager::updateLayout(PlatformThreadInfo *threadInfo,const ViewStateRef &viewState,ChangeSet &changes) { CoordSystemDisplayAdapter *coordAdapter = scene->getCoordAdapter(); - std::lock_guard guardLock(layoutLock); + if (!vecManage) { + vecManage = std::dynamic_pointer_cast(scene->getManager(kWKVectorManager)); + Program *prog = scene->findProgramByName(MaplyDefaultLineShader); + if (prog) + vecProgID = prog->getId(); + else { + Program *prog = scene->findProgramByName(MaplyNoBackfaceLineShader); + if (prog) + vecProgID = prog->getId(); + } + } + + if (!debugVecIDs.empty()) { + vecManage->removeVectors(debugVecIDs, changes); + debugVecIDs.clear(); + } + + std::lock_guard guardLock(lock); TimeInterval curTime = scene->getCurrentTime(); @@ -776,7 +973,7 @@ void LayoutManager::updateLayout(ViewStateRef viewState,ChangeSet &changes) // This will recalculate the offsets and enables // If there were any changes, we need to regenerate - bool layoutChanges = runLayoutRules(viewState,clusters,clusterParams); + bool layoutChanges = runLayoutRules(threadInfo,viewState,clusters,clusterParams,changes); // Compare old and new clusters if (!layoutChanges && clusters.size() != oldClusters.size()) @@ -823,7 +1020,7 @@ void LayoutManager::updateLayout(ViewStateRef viewState,ChangeSet &changes) } else if (!layoutObj->currentEnable && layoutObj->newEnable && layoutObj->currentCluster > -1 && layoutObj->newCluster == -1) { // Just moved out of a cluster - ClusterEntry *oldCluster = NULL; + ClusterEntry *oldCluster = nullptr; if (layoutObj->currentCluster < oldClusters.size()) oldCluster = &oldClusters[layoutObj->currentCluster]; else { @@ -841,17 +1038,26 @@ void LayoutManager::updateLayout(ViewStateRef viewState,ChangeSet &changes) //animObj.setDrawOrder(?) for (auto &geom : animObj.geometry) geom.progID = params.motionShaderID; - ssBuild.addScreenObject(animObj); + ssBuild.addScreenObject(animObj,animObj.worldLoc,animObj.geometry); // And hold off on adding it + // todo: slicing, is this ok? ScreenSpaceObject shortObj = layoutObj->obj; //shortObj.setDrawOrder(?) shortObj.setEnableTime(curTime+params.markerAnimationTime, 0.0); - ssBuild.addScreenObject(shortObj); + ssBuild.addScreenObject(shortObj,shortObj.worldLoc,shortObj.geometry); } else { // It's boring, just add it - if (layoutObj->newEnable) - ssBuild.addScreenObject(layoutObj->obj); + if (layoutObj->newEnable) { + // It's a single point placement + if (layoutObj->obj.layoutShape.empty()) + ssBuild.addScreenObject(layoutObj->obj,layoutObj->obj.worldLoc,layoutObj->obj.geometry); + else { + // One or more placements along a path + for (unsigned int ii=0;iiobj.layoutPlaces.size();ii++) + ssBuild.addScreenObject(layoutObj->obj, layoutObj->obj.layoutModelPlaces[ii], layoutObj->obj.geometry, &layoutObj->obj.layoutPlaces[ii]); + } + } } layoutObj->currentEnable = layoutObj->newEnable; @@ -868,7 +1074,7 @@ void LayoutManager::updateLayout(ViewStateRef viewState,ChangeSet &changes) // Animate from the old cluster if there is one if (cluster.childOfCluster > -1) { - ClusterEntry *oldCluster = NULL; + ClusterEntry *oldCluster = nullptr; if (cluster.childOfCluster < oldClusters.size()) oldCluster = &oldClusters[cluster.childOfCluster]; else { @@ -886,16 +1092,16 @@ void LayoutManager::updateLayout(ViewStateRef viewState,ChangeSet &changes) //animObj.setDrawOrder(?) for (auto &geom : animObj.geometry) geom.progID = params.motionShaderID; - ssBuild.addScreenObject(animObj); + ssBuild.addScreenObject(animObj, animObj.worldLoc, animObj.geometry); // Hold off on adding the new one ScreenSpaceObject shortObj = cluster.layoutObj; //shortObj.setDrawOrder(?) shortObj.setEnableTime(curTime+params.markerAnimationTime, 0.0); - ssBuild.addScreenObject(shortObj); + ssBuild.addScreenObject(shortObj, shortObj.worldLoc, shortObj.geometry); } else - ssBuild.addScreenObject(cluster.layoutObj); + ssBuild.addScreenObject(cluster.layoutObj, cluster.layoutObj.worldLoc, cluster.layoutObj.geometry); } ssBuild.flushChanges(changes, drawIDs); diff --git a/common/WhirlyGlobeLib/src/LinearTextBuilder.cpp b/common/WhirlyGlobeLib/src/LinearTextBuilder.cpp new file mode 100644 index 0000000000..cf007713ce --- /dev/null +++ b/common/WhirlyGlobeLib/src/LinearTextBuilder.cpp @@ -0,0 +1,325 @@ +/* + * LinearTextBuilder.cpp + * WhirlyGlobeLib + * + * Created by Steve Gifford on 3/3/21. + * Copyright 2011-2021 mousebird consulting + * + * 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. + * + */ + +#import "LinearTextBuilder.h" +#import "VectorOffset.h" +#import "GridClipper.h" + +namespace WhirlyKit +{ + +// https://en.wikipedia.org/wiki/Ramer%E2%80%93Douglas%E2%80%93Peucker_algorithm +Point2fVector LineGeneralization(const Point2fVector &screenPts, + float eps, + unsigned int start,unsigned int end) +{ + if (screenPts.size() < 3) + return screenPts; + + // Find the point with max distance + float dMax = 0.0; + unsigned int maxIdx = 0; + float len = 0.0; + for (unsigned int ii = start+1;ii dMax) { + maxIdx = ii; + dMax = dist; + } + } + + // If max distance is greater than the epsilon, recursively simplify + Point2fVector pts; + if (maxIdx != 0 && (dMax > eps)) { + Point2fVector pts0 = LineGeneralization(screenPts, eps, start, maxIdx); + Point2fVector pts1 = LineGeneralization(screenPts, eps, maxIdx, end); + pts.insert(pts.end(),pts0.begin(),pts0.end()); + pts.insert(pts.end(),pts1.begin(),pts1.end()); + } else { + pts.push_back(screenPts[start]); + if (start != end-1) + pts.push_back(screenPts[end-1]); + } + + return pts; +} + +LinearWalker::LinearWalker(const VectorRing &pts) +: pts(pts), ptSoFar(0), offsetDist(0.0), totalLength(0.0) +{ + for (unsigned int ii=0;ii= pts.size()-1) + return false; + + float travelSoFar = 0.0; + int newPtSoFar = ptSoFar; + while (newPtSoFar < pts.size()-1) { + Point2f dir = pts[newPtSoFar+1] - pts[newPtSoFar]; + float segLen = dir.norm(); + // We're in this segment + if ((dist-travelSoFar) < (segLen-offsetDist)) { + float newOffsetDist = offsetDist + (dist-travelSoFar); + if (retPt) + *retPt = pts[newPtSoFar] + newOffsetDist/segLen * dir; + if (norm) + *norm = Point2f(-dir.y(),dir.x()).normalized(); + if (savePos) { + offsetDist = newOffsetDist; + ptSoFar = newPtSoFar; + } + return true; + } + // Keep going + travelSoFar += segLen; + newPtSoFar++; + } + + return false; +} + +LinearTextBuilder::LinearTextBuilder(ViewStateRef viewState, + unsigned int offi, + const Point2f &frameBufferSize, + float generalEps, + LayoutObject *layoutObj) +: viewState(viewState), offi(offi), frameBufferSize(frameBufferSize), generalEps(generalEps), +layoutObj(layoutObj) +{ + screenMbr.addPoint(Point2f(0.0,0.0)); + screenMbr.addPoint(Point2f(frameBufferSize.x(),frameBufferSize.y())); + + coordAdapt = viewState->coordAdapter; + coordSys = coordAdapt->getCoordSystem(); + globeViewState = dynamic_cast(viewState.get()); + mapViewState = dynamic_cast(viewState.get()); +} + +void LinearTextBuilder::setPoints(const Point3dVector &inPts) +{ + pts = inPts; + isClosed = pts.front() == pts.back(); +} + +void LinearTextBuilder::sortRuns(double minLen) +{ + std::vector > newRuns; + + // Build up the runs with length + for (const auto &run: runs) { + double len = 0.0; + for (unsigned int ii=0;ii minLen) + newRuns.push_back(std::pair(run,len)); + } + + std::sort(newRuns.begin(), newRuns.end(), + [](const std::pair& a, const std::pair& b) -> bool + { + return a.second < b.second; + }); + + runs.clear(); + for (auto const &run: newRuns) + runs.push_back(run.first); +} + +void LinearTextBuilder::process() +{ + if (pts.size() == 1) + return; + + Eigen::Matrix4d modelTrans = viewState->fullMatrices[offi]; + + Point2fVector screenPts; + for (auto pt: pts) { + Point2f thisObjPt = viewState->pointOnScreenFromDisplay(pt,&modelTrans,frameBufferSize); + screenPts.push_back(thisObjPt); + } + + // Make sure there's at least some overlap with the screen + Mbr testMbr(screenPts); + if (!testMbr.intersect(screenMbr).valid()) { + return; + } + + // Generalize the source line + screenPts = LineGeneralization(screenPts,generalEps,0,screenPts.size()); + + if (screenPts.empty()) + return; + + // Clip to a larger screen MBR + { + std::vector newRuns; + Mbr largeScreenMbr = screenMbr; + largeScreenMbr.expandByFraction(0.1); + ClipLoopToMbr(screenPts, largeScreenMbr, true, newRuns,1e6); + runs = newRuns; + } + + // Run the offsetting + if (layoutObj->layoutOffset != 0.0) { + std::vector newRuns; + for (const auto &run: runs) { + std::vector theseRuns; + if (isClosed) + theseRuns = BufferPolygon(run, layoutObj->layoutOffset); + else + theseRuns = BufferLinear(run, layoutObj->layoutOffset); + newRuns.insert(newRuns.end(),theseRuns.begin(),theseRuns.end()); + } + runs = newRuns; + } + + // Clip to the screen + { + std::vector newRuns; + for (const auto &run: runs) { + std::vector theseRuns; + ClipLoopToMbr(run, screenMbr, false, theseRuns); + if (!theseRuns.empty()) + newRuns.insert(newRuns.end(),theseRuns.begin(),theseRuns.end()); + } + runs = newRuns; + } + + // Break up runs at unlikely angles + { + std::vector newRuns; + for (const auto &run: runs) { + std::vector theseRuns; + VectorRing thisRun; + thisRun.push_back(run.front()); + for (unsigned int ii=1;ii 135.0 / 180.0 * M_PI) + thisRun.push_back(l1); + else { + thisRun.push_back(l1); + theseRuns.push_back(thisRun); + thisRun.clear(); + thisRun.push_back(l1); + } + } + thisRun.push_back(run.back()); + if (thisRun.size() > 1) + theseRuns.push_back(thisRun); + if (!theseRuns.empty()) + newRuns.insert(newRuns.end(),theseRuns.begin(),theseRuns.end()); + } + runs = newRuns; + } + + return; +} + +std::vector LinearTextBuilder::getScreenVecs() +{ + return runs; +} + +bool LinearTextBuilder::screenToWorld(const Point2f &pt,Point3d &outPt) +{ + if (globeViewState) { + Point3d modelPt; + if (globeViewState->pointOnSphereFromScreen(pt, viewState->fullMatrices[offi], frameBufferSize, modelPt, false)) { + outPt = modelPt; + return true; + } + } else { + Point3d modelPt; + if (mapViewState->pointOnPlaneFromScreen(pt, viewState->fullMatrices[offi], frameBufferSize, modelPt, false)) { + outPt = modelPt; + return true; + } + } + + return false; +} + +Point2f LinearTextBuilder::worldToScreen(const Point3d &worldPt) +{ + return viewState->pointOnScreenFromDisplay(worldPt, &viewState->fullMatrices[offi], frameBufferSize); +} + +ShapeSet LinearTextBuilder::getVisualVecs() +{ + ShapeSet retShapes; + + for (auto &vec: runs) { + VectorLinearRef lin = VectorLinear::createLinear(); + + for (auto &pt: vec) { + if (globeViewState) { + Point3d modelPt; + if (globeViewState->pointOnSphereFromScreen(pt, viewState->fullMatrices[offi], frameBufferSize, modelPt, false)) { + GeoCoord geoPt = coordSys->localToGeographic(coordAdapt->displayToLocal(modelPt)); + lin->pts.push_back(Point2f(geoPt.x(),geoPt.y())); + } + } else { + Point3d modelPt; + if (mapViewState->pointOnPlaneFromScreen(pt, viewState->fullMatrices[offi], frameBufferSize, modelPt, false)) { + GeoCoord geoPt = coordSys->localToGeographic(coordAdapt->displayToLocal(modelPt)); + lin->pts.push_back(Point2f(geoPt.x(),geoPt.y())); + } + } + } + + lin->initGeoMbr(); + retShapes.insert(lin); + } + + return retShapes; +} + +double LinearTextBuilder::getViewStateRotation() +{ +// if (globeViewState) { +// Point3d up = globeViewState->currentUp(); +// double ang = atan2(up.) +// } + +// if (globeViewState) +// return globeViewState->currentUp(); +// else if (mapViewState) +// return mapViewState-> + return 0.0; +} + + +} diff --git a/common/WhirlyGlobeLib/src/LoftManager.cpp b/common/WhirlyGlobeLib/src/LoftManager.cpp index 942e4bac3f..7f8f842b74 100644 --- a/common/WhirlyGlobeLib/src/LoftManager.cpp +++ b/common/WhirlyGlobeLib/src/LoftManager.cpp @@ -1,9 +1,8 @@ -/* - * LoftManager.mm +/* LoftManager.cpp * WhirlyGlobeLib * * Created by Steve Gifford on 7/30/13. - * Copyright 2011-2019 mousebird consulting. + * Copyright 2011-2021 mousebird consulting. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -15,7 +14,6 @@ * 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. - * */ #import "LoftManager.h" @@ -386,6 +384,8 @@ LoftManager::LoftManager() } LoftManager::~LoftManager() { + std::lock_guard guardLock(lock); + for (LoftedPolySceneRepSet::iterator it = loftReps.begin(); it != loftReps.end(); ++it) delete *it; @@ -532,7 +532,7 @@ SimpleIdentity LoftManager::addLoftedPolys(WhirlyKit::ShapeSet *shapes,const Lof addGeometryToBuilder(sceneRep, polyInfo, shapeMbr, center, centerValid, geoCenter, *shapes, triMesh, outlines, changes); { - std::lock_guard guardLock(loftLock); + std::lock_guard guardLock(lock); loftReps.insert(sceneRep); } @@ -542,7 +542,7 @@ SimpleIdentity LoftManager::addLoftedPolys(WhirlyKit::ShapeSet *shapes,const Lof /// Enable/disable lofted polys void LoftManager::enableLoftedPolys(const SimpleIDSet &polyIDs,bool enable,ChangeSet &changes) { - std::lock_guard guardLock(loftLock); + std::lock_guard guardLock(lock); for (SimpleIDSet::iterator idIt = polyIDs.begin(); idIt != polyIDs.end(); ++idIt) { @@ -561,7 +561,7 @@ void LoftManager::enableLoftedPolys(const SimpleIDSet &polyIDs,bool enable,Chang /// Remove lofted polygons void LoftManager::removeLoftedPolys(const SimpleIDSet &polyIDs,ChangeSet &changes) { - std::lock_guard guardLock(loftLock); + std::lock_guard guardLock(lock); for (SimpleIDSet::iterator idIt = polyIDs.begin(); idIt != polyIDs.end(); ++idIt) { diff --git a/common/WhirlyGlobeLib/src/MapboxVectorStyleBackground.cpp b/common/WhirlyGlobeLib/src/MapboxVectorStyleBackground.cpp index 505cba0de4..a41bc05622 100644 --- a/common/WhirlyGlobeLib/src/MapboxVectorStyleBackground.cpp +++ b/common/WhirlyGlobeLib/src/MapboxVectorStyleBackground.cpp @@ -38,22 +38,25 @@ bool MapboxVectorBackgroundPaint::parse(PlatformThreadInfo *inst, } bool MapboxVectorLayerBackground::parse(PlatformThreadInfo *inst, - DictionaryRef styleEntry, - MapboxVectorStyleLayerRef refLayer, + const DictionaryRef &styleEntry, + const MapboxVectorStyleLayerRef &refLayer, int inDrawPriority) { - if (!MapboxVectorStyleLayer::parse(inst,styleEntry,refLayer,drawPriority)) { + if (!MapboxVectorStyleLayer::parse(inst,styleEntry,refLayer,drawPriority)) + { return false; } // styleSet->unsupportedCheck("layout","background",styleEntry); - if (!paint.parse(inst,styleSet,styleEntry->getDict("paint"))) { + if (!paint.parse(inst,styleSet,styleEntry->getDict("paint"))) + { return false; } // Mess directly with the opacity because we're using it for other purposes - if (styleEntry->hasField("alphaoverride")) { + if (styleEntry->hasField("alphaoverride")) + { paint.color->setAlphaOverride(styleEntry->getDouble("alphaoverride")); } @@ -63,19 +66,20 @@ bool MapboxVectorLayerBackground::parse(PlatformThreadInfo *inst, } void MapboxVectorLayerBackground::buildObjects(PlatformThreadInfo *inst, - std::vector &vecObjs, - VectorTileDataRef tileInfo) + const std::vector &vecObjs, + const VectorTileDataRef &tileInfo, + const Dictionary *desc) { const auto color = styleSet->backgroundColor(inst, tileInfo->ident.level); std::vector loops { VectorRing() }; - Mbr bbox = tileInfo->geoBBox; + const Mbr &bbox = tileInfo->geoBBox; bbox.asPoints(loops.back()); - - VectorTrianglesRef trisRef = VectorTriangles::createTriangles(); + + const auto trisRef = VectorTriangles::createTriangles(); TesselateLoops(loops, trisRef); //trisRef->setAttrDict(ar->getAttrDict()); - auto d = MutableDictionaryMake(); + const auto d = MutableDictionaryMake(); d->setString("layer_name", "background"); d->setInt("layer_order", 1); d->setInt("geometry_type", 3); @@ -98,18 +102,24 @@ void MapboxVectorLayerBackground::buildObjects(PlatformThreadInfo *inst, // TODO: Switch to stencils // vecInfo.drawOrder = tileInfo->tileNumber(); -// wkLogLevel(Debug, "background: tildID = %d: (%d,%d) drawOrder = %d, drawPriority = %d",tileInfo->ident.level, tileInfo->ident.x, tileInfo->ident.y, vecInfo.drawOrder,vecInfo.drawPriority); +// wkLogLevel(Debug, "background: tileID = %d: (%d,%d) drawOrder = %d, drawPriority = %d",tileInfo->ident.level, tileInfo->ident.x, tileInfo->ident.y, vecInfo.drawOrder,vecInfo.drawPriority); - if (minzoom != 0 || maxzoom < 1000) { + if (minzoom != 0 || maxzoom < 1000) + { vecInfo.minZoomVis = minzoom; vecInfo.maxZoomVis = maxzoom; } - if (const auto vecID = styleSet->vecManage->addVectors(&tessShapes, vecInfo, tileInfo->changes)) { - const auto compObj = styleSet->makeComponentObject(inst); + if (const auto vecID = styleSet->vecManage->addVectors(&tessShapes, vecInfo, tileInfo->changes)) + { + const auto compObj = styleSet->makeComponentObject(inst, desc); + + // not currently supported + //compObj->representation = representation; + compObj->vectorIDs.insert(vecID); - - styleSet->compManage->addComponentObject(compObj); + + styleSet->compManage->addComponentObject(compObj, tileInfo->changes); tileInfo->compObjs.push_back(compObj); } } diff --git a/common/WhirlyGlobeLib/src/MapboxVectorStyleCircle.cpp b/common/WhirlyGlobeLib/src/MapboxVectorStyleCircle.cpp index a0444aa35e..58889a304c 100644 --- a/common/WhirlyGlobeLib/src/MapboxVectorStyleCircle.cpp +++ b/common/WhirlyGlobeLib/src/MapboxVectorStyleCircle.cpp @@ -24,7 +24,7 @@ namespace WhirlyKit { -bool MapboxVectorCirclePaint::parse(PlatformThreadInfo *inst, +bool MapboxVectorCirclePaint::parse(PlatformThreadInfo *, MapboxVectorStyleSetImpl *styleSet, DictionaryRef styleEntry) { @@ -42,99 +42,174 @@ bool MapboxVectorCirclePaint::parse(PlatformThreadInfo *inst, } bool MapboxVectorLayerCircle::parse(PlatformThreadInfo *inst, - DictionaryRef styleEntry, - MapboxVectorStyleLayerRef refLayer, + const DictionaryRef &styleEntry, + const MapboxVectorStyleLayerRef &refLayer, int drawPriority) { - if (!styleEntry) - return false; - if (!MapboxVectorStyleLayer::parse(inst,styleEntry,refLayer,drawPriority) || !paint.parse(inst, styleSet, styleEntry->getDict("paint"))) + { return false; + } + + const double maxRadius = paint.radius->maxVal(); + const double maxStrokeWidth = paint.strokeWidth->maxVal(); + + // todo: have to evaluate these dynamically to support expressions + const auto theFillColor = paint.opacity ? + paint.fillColor->withAlpha((float)paint.opacity->valForZoom(0)) : + *paint.fillColor; + const auto theStrokeColor = paint.strokeOpacity ? + paint.strokeColor->withAlpha((float)paint.strokeOpacity->valForZoom(0)) : + *paint.strokeColor; - RGBAColor theFillColor = *paint.fillColor; - RGBAColor theStrokeColor = *paint.strokeColor; - double maxRadius = paint.radius->maxVal(); - double maxStrokeWidth = paint.strokeWidth->maxVal(); circleTexID = styleSet->makeCircleTexture(inst,maxRadius,theFillColor,theStrokeColor,maxStrokeWidth,&circleSize); // Larger circles are slightly more important importance = drawPriority/1000 + styleSet->tileStyleSettings->markerImportance + maxRadius / 100000.0; + repUUIDField = styleSet->stringValue("X-Maply-RepresentationUUIDField", styleEntry, std::string()); + uuidField = styleSet->tileStyleSettings->uuidField; - + uuidField = styleSet->stringValue("X-Maply-UUIDField", styleEntry, uuidField); + return true; } -void MapboxVectorLayerCircle::cleanup(ChangeSet &changes) +void MapboxVectorLayerCircle::cleanup(PlatformThreadInfo *inst,ChangeSet &changes) { if (circleTexID != EmptyIdentity) + { changes.push_back(new RemTextureReq(circleTexID)); + } } void MapboxVectorLayerCircle::buildObjects(PlatformThreadInfo *inst, - std::vector &vecObjs, - VectorTileDataRef tileInfo) + const std::vector &vecObjs, + const VectorTileDataRef &tileInfo, + const Dictionary *desc) { - if (!visible) + // If a representation is set, we produce results for non-visible layers + if ((!visible && (representation.empty() || repUUIDField.empty())) || circleTexID == EmptyIdentity) + { return; - - ComponentObjectRef compObj = styleSet->makeComponentObject(inst); + } + + using MarkerPtrVec = std::vector; + using VecObjRefVec = std::vector; + auto const capacity = vecObjs.size() * 5; // ? + std::unordered_map> markersByUUID(capacity); + + const double opacity = paint.opacity->valForZoom(tileInfo->ident.level); + const double radius = paint.radius->valForZoom(tileInfo->ident.level); // Default settings - MarkerInfo markerInfo(true); + MarkerInfo markerInfo(/*screenObject=*/true); markerInfo.zoomSlot = styleSet->zoomSlot; - if (minzoom != 0 || maxzoom < 1000) { + markerInfo.color = RGBAColor(255,255,255,opacity*255); + markerInfo.drawPriority = drawPriority + tileInfo->ident.level * std::max(0, styleSet->tileStyleSettings->drawPriorityPerLevel) + 1; + markerInfo.programID = styleSet->screenMarkerProgramID; + + if (minzoom != 0 || maxzoom < 1000) + { markerInfo.minZoomVis = minzoom; markerInfo.maxZoomVis = maxzoom; } - const double opacity = paint.opacity->valForZoom(tileInfo->ident.level); - markerInfo.color = RGBAColor(255,255,255,opacity*255); - markerInfo.drawPriority = drawPriority; - markerInfo.programID = styleSet->screenMarkerProgramID; - // Need to find all the points, way down deep - std::vector markers; - for (auto vecObj : vecObjs) { - if (vecObj->getVectorType() == VectorPointType) { - for (VectorShapeRef shape : vecObj->shapes) { - if (auto pts = std::dynamic_pointer_cast(shape)) { - for (auto pt : pts->pts) { - // Add a marker per point - // todo: exception safety - auto marker = new WhirlyKit::Marker(); - marker->loc = GeoCoord(pt.x(),pt.y()); - marker->texIDs.push_back(circleTexID); - const double radius = paint.radius->valForZoom(tileInfo->ident.level); - marker->width = 2*radius * styleSet->tileStyleSettings->markerScale; marker->height = 2*radius * styleSet->tileStyleSettings->markerScale; - marker->layoutWidth = marker->width; marker->layoutHeight = marker->height; - marker->layoutImportance = MAXFLOAT; -// marker->layoutImportance = importance + (101-tileInfo->ident.level)/100.0; - if (selectable) { - marker->isSelectable = true; - marker->selectID = Identifiable::genId(); - styleSet->addSelectionObject(marker->selectID, vecObj, compObj); - compObj->selectIDs.insert(marker->selectID); - compObj->isSelectable = true; - } - if (!uuidField.empty()) - marker->uniqueID = uuidField; - markers.push_back(marker); - } + std::vector> markerOwner; // automatic cleanup of local temporary allocations + const auto emptyVal = std::make_pair(MarkerPtrVec(), VecObjRefVec()); + for (const auto &vecObj : vecObjs) + { + if (vecObj->getVectorType() != VectorPointType) + { + continue; + } + + const auto attrs = vecObj->getAttributes(); + + for (const VectorShapeRef &shape : vecObj->shapes) + { + const auto pts = dynamic_cast(shape.get()); + if (!pts) + { + continue; + } + + for (const auto &pt : pts->pts) + { + // Add a marker per point + markerOwner.emplace_back(std::make_unique()); + auto marker = markerOwner.back().get(); + marker->loc = GeoCoord(pt.x(),pt.y()); + marker->texIDs.push_back(circleTexID); + marker->width = 2*radius * styleSet->tileStyleSettings->markerScale; + marker->height = 2*radius * styleSet->tileStyleSettings->markerScale; + marker->layoutWidth = marker->width; + marker->layoutHeight = marker->height; + marker->layoutImportance = MAXFLOAT; //importance + (101-tileInfo->ident.level)/100.0; + marker->uniqueID = uuidField.empty() ? std::string() : attrs->getString(uuidField); + + const std::string repUUID = repUUIDField.empty() ? std::string() : attrs->getString(repUUIDField); + + // Look up the vectors of markers/objects for this uuid (including blank), inserting empty ones if necessary + const auto result = markersByUUID.insert(std::make_pair(repUUID, emptyVal)); + + auto &markers = result.first->second.first; + auto &markerObjs = result.first->second.second; + + if (markers.empty()) + { + markers.reserve(pts->pts.size()); + markerObjs.reserve(pts->pts.size()); } + markers.push_back(marker); + markerObjs.push_back(vecObj); } } } - - // Set up the markers and get a change set - SimpleIdentity markerID = styleSet->markerManage->addMarkers(markers, markerInfo, tileInfo->changes); - for (auto marker: markers) - delete marker; - if (markerID != EmptyIdentity) - compObj->markerIDs.insert(markerID); - styleSet->compManage->addComponentObject(compObj); - tileInfo->compObjs.push_back(compObj); + + for (const auto &kvp : markersByUUID) + { + const auto &uuid = kvp.first; + const auto &markers = kvp.second.first; + const auto &vecObjs = kvp.second.second; + + // Generate one component object per unique UUID (including blank) + const auto compObj = styleSet->makeComponentObject(inst, desc); + + compObj->uuid = uuid; + compObj->representation = representation; + + // Keep the vector objects around if they need to be selectable + if (selectable) + { + assert(markers.size() == vecObjs.size()); + const auto count = std::min(markers.size(), vecObjs.size()); + for (auto i = (size_t)0; i < count; ++i) + { + auto *marker = markers[i]; + const auto &vecObj = vecObjs[i]; + + marker->isSelectable = true; + marker->selectID = Identifiable::genId(); + styleSet->addSelectionObject(marker->selectID, vecObj, compObj); + compObj->selectIDs.insert(marker->selectID); + compObj->isSelectable = true; + } + } + + if (!markers.empty()) + { + // Set up the markers and get a change set + if (const auto markerID = styleSet->markerManage->addMarkers(markers, markerInfo, tileInfo->changes)) + { + compObj->markerIDs.insert(markerID); + } + + styleSet->compManage->addComponentObject(compObj, tileInfo->changes); + tileInfo->compObjs.push_back(compObj); + } + } } } diff --git a/common/WhirlyGlobeLib/src/MapboxVectorStyleFill.cpp b/common/WhirlyGlobeLib/src/MapboxVectorStyleFill.cpp index 6f7e7c4c58..38a94e6aa0 100644 --- a/common/WhirlyGlobeLib/src/MapboxVectorStyleFill.cpp +++ b/common/WhirlyGlobeLib/src/MapboxVectorStyleFill.cpp @@ -1,9 +1,8 @@ -/* - * MapboxVectorStyleFill.mm +/* MapboxVectorStyleFill.cpp * WhirlyGlobe-MaplyComponent * * Created by Steve Gifford on 2/17/15. - * Copyright 2011-2015 mousebird consulting + * Copyright 2011-2021 mousebird consulting * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -15,7 +14,6 @@ * 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. - * */ #import "MapboxVectorStyleFill.h" @@ -28,7 +26,7 @@ namespace WhirlyKit bool MapboxVectorFillPaint::parse(PlatformThreadInfo *inst, MapboxVectorStyleSetImpl *styleSet, - DictionaryRef styleEntry) + const DictionaryRef &styleEntry) { styleSet->unsupportedCheck("fill-antialias","paint_fill",styleEntry); styleSet->unsupportedCheck("fill-translate","paint_fill",styleEntry); @@ -49,21 +47,21 @@ bool MapboxVectorFillPaint::parse(PlatformThreadInfo *inst, } bool MapboxVectorLayerFill::parse(PlatformThreadInfo *inst, - DictionaryRef styleEntry, - MapboxVectorStyleLayerRef refLayer, + const DictionaryRef &styleEntry, + const MapboxVectorStyleLayerRef &refLayer, int inDrawPriority) { - if (!styleEntry) - return false; - if (!MapboxVectorStyleLayer::parse(inst,styleEntry,refLayer,drawPriority) || !paint.parse(inst,styleSet,styleEntry->getDict("paint"))) + { return false; + } arealShaderID = styleSet->tileStyleSettings->settingsArealShaderID; // Mess directly with the opacity because we're using it for other purposes - if (styleEntry && styleEntry->hasField("alphaoverride")) { + if (styleEntry && styleEntry->hasField("alphaoverride")) + { paint.color->setAlphaOverride(styleEntry->getDouble("alphaoverride")); } @@ -77,36 +75,55 @@ void MapboxVectorLayerFill::cleanup(PlatformThreadInfo *inst,ChangeSet &changes) } void MapboxVectorLayerFill::buildObjects(PlatformThreadInfo *inst, - std::vector &vecObjs, - VectorTileDataRef tileInfo) + const std::vector &vecObjs, + const VectorTileDataRef &tileInfo, + const Dictionary *desc) { - if (!visible || !(paint.color || paint.outlineColor)) + // If a representation is set, we produce results for non-visible layers + if (!visible /*&& representation.empty()*/) + { return; - - ComponentObjectRef compObj = styleSet->makeComponentObject(inst); + } + + if (!paint.color && !paint.outlineColor) + { + return; + } + + const auto compObj = styleSet->makeComponentObject(inst, desc); + + // not currently supported + //compObj->representation = representation; // Gather all the areal features for fill and/or outline std::vector shapes; - for (auto vecObj : vecObjs) { + for (auto vecObj : vecObjs) + { if (vecObj->getVectorType() == VectorArealType) + { std::copy(vecObj->shapes.begin(),vecObj->shapes.end(),std::back_inserter(shapes)); + } } // Filled polygons - if (paint.color) { - // tesselate the area features + if (paint.color) + { + // tessellate the area features std::vector tessShapes; - for (auto const &it : shapes) { - if (auto ar = std::dynamic_pointer_cast(it)) { - VectorTrianglesRef trisRef = VectorTriangles::createTriangles(); + for (const auto &it : shapes) + { + if (const auto ar = dynamic_cast(it.get())) + { + const auto trisRef = VectorTriangles::createTriangles(); TesselateLoops(ar->loops, trisRef); trisRef->setAttrDict(ar->getAttrDict()); trisRef->initGeoMbr(); tessShapes.push_back(trisRef); } } - - if (auto color = styleSet->resolveColor(paint.color, paint.opacity, tileInfo->ident.level, MBResolveColorOpacityComposeAlpha)) { + + if (const auto color = styleSet->resolveColor(paint.color, paint.opacity, tileInfo->ident.level, MBResolveColorOpacityComposeAlpha)) + { // Set up the description for constructing vectors VectorInfo vecInfo; vecInfo.hasExp = true; @@ -125,7 +142,8 @@ void MapboxVectorLayerFill::buildObjects(PlatformThreadInfo *inst, // wkLogLevel(Debug, "fill: tildID = %d: (%d,%d) drawOrder = %d, drawPriority = %d",tileInfo->ident.level, tileInfo->ident.x, tileInfo->ident.y, vecInfo.drawOrder,vecInfo.drawPriority); - if (minzoom != 0 || maxzoom < 1000) { + if (minzoom != 0 || maxzoom < 1000) + { vecInfo.minZoomVis = minzoom; vecInfo.maxZoomVis = maxzoom; } @@ -133,10 +151,12 @@ void MapboxVectorLayerFill::buildObjects(PlatformThreadInfo *inst, //wkLogLevel(Debug, "Color: %s %d %d %d %d",ident.c_str(),(int)color->r,(int)color->g,(int)color->b,(int)color->a); const SimpleIdentity vecID = styleSet->vecManage->addVectors(&tessShapes, vecInfo, tileInfo->changes); - if (vecID != EmptyIdentity) { + if (vecID != EmptyIdentity) + { compObj->vectorIDs.insert(vecID); - if (selectable) { + if (selectable) + { compObj->isSelectable = selectable; compObj->vecObjs = vecObjs; } @@ -145,8 +165,10 @@ void MapboxVectorLayerFill::buildObjects(PlatformThreadInfo *inst, } // Outlines - if (paint.outlineColor) { - if (auto color = styleSet->resolveColor(paint.outlineColor, paint.opacity, tileInfo->ident.level, MBResolveColorOpacityComposeAlpha)) { + if (paint.outlineColor) + { + if (const auto color = styleSet->resolveColor(paint.outlineColor, paint.opacity, tileInfo->ident.level, MBResolveColorOpacityComposeAlpha)) + { // Set up the description for constructing vectors VectorInfo vecInfo; vecInfo.hasExp = true; @@ -159,21 +181,24 @@ void MapboxVectorLayerFill::buildObjects(PlatformThreadInfo *inst, vecInfo.drawPriority = drawPriority + tileInfo->ident.level * std::max(0, styleSet->tileStyleSettings->drawPriorityPerLevel) + 1; vecInfo.drawOrder = tileInfo->tileNumber(); - if (minzoom != 0 || maxzoom < 1000) { + if (minzoom != 0 || maxzoom < 1000) + { vecInfo.zoomSlot = styleSet->zoomSlot; vecInfo.minZoomVis = minzoom; vecInfo.maxZoomVis = maxzoom; } const SimpleIdentity vecID = styleSet->vecManage->addVectors(&shapes, vecInfo, tileInfo->changes); - if (vecID != EmptyIdentity) { + if (vecID != EmptyIdentity) + { compObj->vectorIDs.insert(vecID); } } } - if (!compObj->vectorIDs.empty()) { - styleSet->compManage->addComponentObject(compObj); + if (!compObj->vectorIDs.empty()) + { + styleSet->compManage->addComponentObject(compObj, tileInfo->changes); tileInfo->compObjs.push_back(compObj); } } diff --git a/common/WhirlyGlobeLib/src/MapboxVectorStyleLayer.cpp b/common/WhirlyGlobeLib/src/MapboxVectorStyleLayer.cpp index 311d5e133d..cca2fb19f1 100644 --- a/common/WhirlyGlobeLib/src/MapboxVectorStyleLayer.cpp +++ b/common/WhirlyGlobeLib/src/MapboxVectorStyleLayer.cpp @@ -34,7 +34,7 @@ namespace WhirlyKit MapboxVectorStyleLayerRef MapboxVectorStyleLayer::VectorStyleLayer(PlatformThreadInfo *inst, MapboxVectorStyleSetImpl *styleSet, - DictionaryRef layerDict, + const DictionaryRef &layerDict, int drawPriority) { MapboxVectorStyleLayerRef layer; @@ -54,49 +54,49 @@ MapboxVectorStyleLayerRef MapboxVectorStyleLayer::VectorStyleLayer(PlatformThrea if (type.empty()) { wkLogLevel(Warn, "Expecting string type for layer"); - return NULL; + return nullptr; } if (type == "fill") { - layer = MapboxVectorStyleLayerRef(new MapboxVectorLayerFill(styleSet)); + layer = std::make_shared(styleSet); } else if (type == "fill-extrusion") { wkLogLevel(Warn, "Treating fill-extrusion layer as fill"); - layer = MapboxVectorStyleLayerRef(new MapboxVectorLayerFill(styleSet)); + layer = std::make_shared(styleSet); } else if (type == "line") { - layer = MapboxVectorStyleLayerRef(new MapboxVectorLayerLine(styleSet)); + layer = std::make_shared(styleSet); } else if (type == "symbol") { - layer = MapboxVectorStyleLayerRef(new MapboxVectorLayerSymbol(styleSet)); + layer = std::make_shared(styleSet); } else if (type == "circle") { - layer = MapboxVectorStyleLayerRef(new MapboxVectorLayerCircle(styleSet)); + layer = std::make_shared(styleSet); } else if (type == "raster") { - layer = MapboxVectorStyleLayerRef(new MapboxVectorLayerRaster(styleSet)); + layer = std::make_shared(styleSet); } else if (type == "background") { - layer = MapboxVectorStyleLayerRef(new MapboxVectorLayerBackground(styleSet)); + layer = std::make_shared(styleSet); } else if (type == "fill-extrusion") { wkLogLevel(Warn,"Skipping layer type %s",type.c_str()); - return NULL; + return nullptr; } - layer->type = type; if (!layer) { wkLogLevel(Warn,"Unknown layer type %s",type.c_str()); - return NULL; + return nullptr; } + layer->type = type; if (!layer->parse(inst, layerDict, refLayer, drawPriority)) { wkLogLevel(Warn, "Failed to parse layer %s",layer->ident.c_str()); - return NULL; + return nullptr; } if (layerDict->getType("filter") == DictTypeArray) { - layer->filter = MapboxVectorFilterRef(new MapboxVectorFilter()); + layer->filter = std::make_shared(); auto filterArray = layerDict->getArray("filter"); layer->filter->parse(filterArray,styleSet); } - + layer->visible = styleSet->boolValue("visibility", layerDict->getDict("layout"), "visible", true); layer->selectable = styleSet->tileStyleSettings->selectable; - layer->metadata = layerDict->getDict("metadata"); - + layer->representation = layerDict->getString("X-Maply-Representation"); + return layer; } @@ -111,21 +111,26 @@ MapboxVectorStyleLayer::~MapboxVectorStyleLayer() } bool MapboxVectorStyleLayer::parse(PlatformThreadInfo *inst, - DictionaryRef styleEntry, - MapboxVectorStyleLayerRef refLayer, + const DictionaryRef &styleEntry, + const MapboxVectorStyleLayerRef &refLayer, int inDrawPriority) { + if (!styleEntry) + { + return false; + } + drawPriority = inDrawPriority; uuid = styleSet->generateID(); - + ident = styleEntry->getString("id"); - source = styleSet->stringValue("source", styleEntry, refLayer ? refLayer->source : ""); - sourceLayer = styleSet->stringValue("source-layer", styleEntry, refLayer ? refLayer->sourceLayer : ""); + source = styleSet->stringValue("source", styleEntry, refLayer ? refLayer->source : std::string()); + sourceLayer = styleSet->stringValue("source-layer", styleEntry, refLayer ? refLayer->sourceLayer : std::string()); minzoom = styleSet->intValue("minzoom", styleEntry, 0); maxzoom = styleSet->intValue("maxzoom", styleEntry, 1000); - category = styleSet->stringValue("wkcategory", styleEntry, ""); + category = styleSet->stringValue("wkcategory", styleEntry, std::string()); return true; } diff --git a/common/WhirlyGlobeLib/src/MapboxVectorStyleLine.cpp b/common/WhirlyGlobeLib/src/MapboxVectorStyleLine.cpp index 52ce41bc12..4560b3f24e 100644 --- a/common/WhirlyGlobeLib/src/MapboxVectorStyleLine.cpp +++ b/common/WhirlyGlobeLib/src/MapboxVectorStyleLine.cpp @@ -1,9 +1,8 @@ -/* - * MapboxVectorStyleLine.mm +/* MapboxVectorStyleLine.cpp * WhirlyGlobe-MaplyComponent * * Created by Steve Gifford on 2/17/15. - * Copyright 2011-2019 mousebird consulting + * Copyright 2011-2021 mousebird consulting * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -15,7 +14,6 @@ * 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. - * */ #import "MapboxVectorStyleLine.h" @@ -24,10 +22,10 @@ namespace WhirlyKit { -static const char * const lineCapVals[] = {"butt","round","square",NULL}; -static const char * const joinVals[] = {"bevel","round","miter",NULL}; +static const char * const lineCapVals[] = {"butt","round","square",nullptr}; +static const char * const joinVals[] = {"bevel","round","miter",nullptr}; -bool MapboxVectorLineLayout::parse(PlatformThreadInfo *inst,MapboxVectorStyleSetImpl *styleSet,DictionaryRef styleEntry) +bool MapboxVectorLineLayout::parse(PlatformThreadInfo *,MapboxVectorStyleSetImpl *styleSet,const DictionaryRef &styleEntry) { cap = styleEntry ? (MapboxVectorLineCap)styleSet->enumValue(styleEntry->getEntry("line-cap"),lineCapVals,(int)MBLineCapButt) : MBLineCapButt; join = styleEntry ? (MapboxVectorLineJoin)styleSet->enumValue(styleEntry->getEntry("line-join"),joinVals,(int)MBLineJoinMiter) : MBLineJoinMiter; @@ -37,7 +35,7 @@ bool MapboxVectorLineLayout::parse(PlatformThreadInfo *inst,MapboxVectorStyleSet return true; } -bool MapboxVectorLinePaint::parse(PlatformThreadInfo *inst,MapboxVectorStyleSetImpl *styleSet,DictionaryRef styleEntry) +bool MapboxVectorLinePaint::parse(PlatformThreadInfo *,MapboxVectorStyleSetImpl *styleSet,const DictionaryRef &styleEntry) { styleSet->unsupportedCheck("line-translate", "line-paint", styleEntry); styleSet->unsupportedCheck("line-translate-anchor", "line-paint", styleEntry); @@ -47,12 +45,13 @@ bool MapboxVectorLinePaint::parse(PlatformThreadInfo *inst,MapboxVectorStyleSetI opacity = styleSet->transDouble("line-opacity", styleEntry, 1.0); width = styleSet->transDouble("line-width", styleEntry, 1.0); + offset = styleSet->transDouble("line-offset", styleEntry, 0.0); color = styleSet->transColor("line-color", styleEntry, RGBAColor::black()); pattern = styleSet->stringValue("line-pattern", styleEntry, ""); if (styleEntry && styleEntry->getType("line-dasharray") == DictTypeArray) { auto vecArray = styleEntry->getArray("line-dasharray"); - for (auto entry : vecArray) { + for (const auto &entry : vecArray) { if (entry->getType() == DictTypeDouble || entry->getType() == DictTypeInt) { lineDashArray.push_back(entry->getDouble()); } else { @@ -65,17 +64,16 @@ bool MapboxVectorLinePaint::parse(PlatformThreadInfo *inst,MapboxVectorStyleSetI } bool MapboxVectorLayerLine::parse(PlatformThreadInfo *inst, - DictionaryRef styleEntry, - MapboxVectorStyleLayerRef refLayer, + const DictionaryRef &styleEntry, + const MapboxVectorStyleLayerRef &refLayer, int drawPriority) { - if (!styleEntry) - return false; - if (!MapboxVectorStyleLayer::parse(inst, styleEntry,refLayer,drawPriority) || !layout.parse(inst, styleSet, styleEntry->getDict("layout")) || !paint.parse(inst, styleSet, styleEntry->getDict("paint"))) + { return false; + } this->drawPriority = styleSet->intValue("drawPriority", styleEntry, drawPriority); linearClipToBounds = styleSet->boolValue("linearize-clip-to-bounds", styleEntry, "yes", false); @@ -92,13 +90,14 @@ bool MapboxVectorLayerLine::parse(PlatformThreadInfo *inst, for (double val : paint.lineDashArray) totLen += val; - int totLenRounded = NextPowOf2(totLen); + unsigned totLenRounded = NextPowOf2((unsigned)totLen); if (totLenRounded < 64) totLenRounded = 64; std::vector dashComponents; + dashComponents.reserve(paint.lineDashArray.size()); for (double val : paint.lineDashArray) { - double len = val * totLenRounded / totLen; + const double len = val * totLenRounded / totLen; dashComponents.push_back(len); } totLen *= maxWidth; @@ -107,7 +106,12 @@ bool MapboxVectorLayerLine::parse(PlatformThreadInfo *inst, } fade = styleSet->doubleValue("fade",styleEntry,0.0); + repUUIDField = styleSet->stringValue("X-Maply-RepresentationUUIDField", styleEntry, std::string()); + lineScale = styleSet->tileStyleSettings->lineScale; + + uuidField = styleSet->tileStyleSettings->uuidField; + uuidField = styleSet->stringValue("X-Maply-UUIDField", styleEntry, uuidField); return true; } @@ -117,101 +121,150 @@ void MapboxVectorLayerLine::cleanup(PlatformThreadInfo *inst,ChangeSet &changes) } void MapboxVectorLayerLine::buildObjects(PlatformThreadInfo *inst, - std::vector &inVecObjs, - VectorTileDataRef tileInfo) + const std::vector &inVecObjs, + const VectorTileDataRef &tileInfo, + const Dictionary *desc) { - if (!visible) + // If a representation is set, we produce results for non-visible layers + if (!visible && (representation.empty() || repUUIDField.empty())) + { return; - - ComponentObjectRef compObj = styleSet->makeComponentObject(inst); + } // Turn into linears (if not already) and then clip to the bounds // Slightly different, but we want to clip all the areals that are converted to linears std::vector vecObjs; vecObjs.reserve(inVecObjs.size()); - for (auto const &vecObj : inVecObjs) { + for (auto const &vecObj : inVecObjs) + { bool clip = linearClipToBounds; VectorObjectRef newVecObj = vecObj; if (dropGridLines) + { newVecObj = newVecObj->filterClippedEdges(); + } - if (newVecObj->getVectorType() == VectorArealType) { + if (newVecObj->getVectorType() == VectorArealType) + { newVecObj = newVecObj->arealsToLinears(); clip = true; } if (newVecObj && clip) - newVecObj = VectorObjectRef(newVecObj->clipToMbr(tileInfo->geoBBox.ll(), tileInfo->geoBBox.ur())); + { + newVecObj = newVecObj->clipToMbr(tileInfo->geoBBox.ll(), tileInfo->geoBBox.ur()); + } if (newVecObj) + { vecObjs.push_back(newVecObj); + } } // Subdivide long-ish lines to the globe, if set - if (subdivToGlobe > 0.0) { + if (subdivToGlobe > 0.0) + { std::vector newVecObjs; newVecObjs.reserve(3 * vecObjs.size()); - for (auto vecObj : vecObjs) { - vecObj->subdivideToGlobe(subdivToGlobe); + for (const auto &vecObj : vecObjs) + { + vecObj->subdivideToGlobe((float)subdivToGlobe); newVecObjs.push_back(vecObj); } vecObjs = newVecObjs; } // If we have a filled texture, we'll use that - const float repeatLen = totLen; + const auto repeatLen = (float)totLen; // TODO: We can also have a symbol, where we might do the same thing - // Problem is, we'll need to pass the subtexture logic through to the renderer + // Problem is, we'll need to pass the sub-texture logic through to the renderer // because right now it's expecting a single texture that can be strung along the line const RGBAColorRef color = styleSet->resolveColor(paint.color, paint.opacity, tileInfo->ident.level, MBResolveColorOpacityMultiply); const double width = paint.width->valForZoom(tileInfo->ident.level) * lineScale; + const double offset = paint.offset->valForZoom(tileInfo->ident.level) * lineScale; - if (color && width > 0.0) + if (!color || width <= 0.0) { - WideVectorInfo vecInfo; - vecInfo.hasExp = true; - vecInfo.coordType = WideVecCoordScreen; - vecInfo.programID = styleSet->wideVectorProgramID; - vecInfo.fade = fade; - vecInfo.zoomSlot = styleSet->zoomSlot; - if (minzoom != 0 || maxzoom < 1000) { - vecInfo.minZoomVis = minzoom; - vecInfo.maxZoomVis = maxzoom; - // wkLogLevel(Debug, "zoomSlot = %d, minZoom = %f, maxZoom = %f",styleSet->zoomSlot,vecInfo.minZoomVis,vecInfo.maxZoomVis); - } - if (filledLineTexID != EmptyIdentity) { - vecInfo.texID = filledLineTexID; - vecInfo.repeatSize = repeatLen; - } + return; + } - vecInfo.color = *color; - vecInfo.width = width; - vecInfo.widthExp = paint.width->expression(); - // Scale by the lineScale - if (vecInfo.widthExp) - vecInfo.widthExp->scaleBy(lineScale); - vecInfo.colorExp = paint.color->expression(); - vecInfo.opacityExp = paint.opacity->expression(); - vecInfo.drawPriority = drawPriority + tileInfo->ident.level * std::max(0, styleSet->tileStyleSettings->drawPriorityPerLevel)+2; - // TODO: Switch to stencils + WideVectorInfo vecInfo; + vecInfo.hasExp = true; + vecInfo.coordType = WideVecCoordScreen; + vecInfo.programID = styleSet->wideVectorProgramID; + vecInfo.fade = fade; + vecInfo.zoomSlot = styleSet->zoomSlot; + vecInfo.color = *color; + vecInfo.width = (float)width; + vecInfo.offset = (float)-offset; + vecInfo.widthExp = paint.width->expression(); + vecInfo.offsetExp = paint.offset->expression(); + vecInfo.colorExp = paint.color->expression(); + vecInfo.opacityExp = paint.opacity->expression(); + vecInfo.drawPriority = drawPriority + tileInfo->ident.level * std::max(0, styleSet->tileStyleSettings->drawPriorityPerLevel)+2; + // TODO: Switch to stencils // vecInfo.drawOrder = tileInfo->tileNumber(); + + if (minzoom != 0 || maxzoom < 1000) + { + vecInfo.minZoomVis = minzoom; + vecInfo.maxZoomVis = maxzoom; + } + if (filledLineTexID != EmptyIdentity) + { + vecInfo.texID = filledLineTexID; + vecInfo.repeatSize = repeatLen; + } + if (vecInfo.widthExp) + { + vecInfo.widthExp->scaleBy(lineScale); + } + if (vecInfo.offsetExp) + { + vecInfo.offsetExp->scaleBy(-lineScale); + } + + using ShapeRefVec = std::vector; + auto const capacity = inVecObjs.size() * 5; // ? + std::unordered_map shapesByUUID(capacity); - // Gather all the linear features - std::vector shapes; - for (auto vecObj : vecObjs) { - if (vecObj->getVectorType() == VectorLinearType) - std::copy(vecObj->shapes.begin(),vecObj->shapes.end(),std::back_inserter(shapes)); + // Gather all the linear features + for (const auto &vecObj : vecObjs) + { + if (vecObj->getVectorType() != VectorLinearType) + { + continue; } - - const auto wideVecID = styleSet->wideVecManage->addVectors(shapes, vecInfo, tileInfo->changes); - if (wideVecID != EmptyIdentity) - compObj->wideVectorIDs.insert(wideVecID); + + const auto attrs = vecObj->getAttributes(); + const auto uuid = repUUIDField.empty() ? std::string() : attrs->getString(repUUIDField); + + // Look up the vectors of markers/objects for this uuid (including blank), inserting empty ones if necessary + const auto result = shapesByUUID.insert(std::make_pair(std::ref(uuid), ShapeRefVec())); + auto &shapes = result.first->second; + + shapes.reserve(shapes.size() + vecObj->shapes.size()); + std::copy(vecObj->shapes.begin(),vecObj->shapes.end(),std::back_inserter(shapes)); } - - if (!compObj->wideVectorIDs.empty()) { - styleSet->compManage->addComponentObject(compObj); - tileInfo->compObjs.push_back(compObj); + + for (const auto &kvp : shapesByUUID) + { + const auto &uuid = kvp.first; + const auto &shapes = kvp.second; + + // Generate one component object per unique UUID (including blank) + const auto compObj = styleSet->makeComponentObject(inst, desc); + + compObj->uuid = uuid; + compObj->representation = representation; + + if (const auto wideVecID = styleSet->wideVecManage->addVectors(shapes, vecInfo, tileInfo->changes)) + { + compObj->wideVectorIDs.insert(wideVecID); + styleSet->compManage->addComponentObject(compObj, tileInfo->changes); + tileInfo->compObjs.push_back(compObj); + } } } diff --git a/common/WhirlyGlobeLib/src/MapboxVectorStyleRaster.cpp b/common/WhirlyGlobeLib/src/MapboxVectorStyleRaster.cpp index f480927d4a..9aa1fe970b 100644 --- a/common/WhirlyGlobeLib/src/MapboxVectorStyleRaster.cpp +++ b/common/WhirlyGlobeLib/src/MapboxVectorStyleRaster.cpp @@ -24,16 +24,21 @@ namespace WhirlyKit { bool MapboxVectorLayerRaster::parse(PlatformThreadInfo *inst, - DictionaryRef styleEntry, - MapboxVectorStyleLayerRef refLayer, + const DictionaryRef &styleEntry, + const MapboxVectorStyleLayerRef &refLayer, int drawPriority) { + if (!MapboxVectorStyleLayer::parse(inst,styleEntry,refLayer,drawPriority)) + { + return false; + } return true; } void MapboxVectorLayerRaster::buildObjects(PlatformThreadInfo *inst, - std::vector &vecObjs, - VectorTileDataRef tileInfo) + const std::vector &vecObjs, + const VectorTileDataRef &tileInfo, + const Dictionary *desc) { } diff --git a/common/WhirlyGlobeLib/src/MapboxVectorStyleSetC.cpp b/common/WhirlyGlobeLib/src/MapboxVectorStyleSetC.cpp index ee996f01dc..0a1e2490ac 100644 --- a/common/WhirlyGlobeLib/src/MapboxVectorStyleSetC.cpp +++ b/common/WhirlyGlobeLib/src/MapboxVectorStyleSetC.cpp @@ -28,62 +28,72 @@ namespace WhirlyKit { +static const std::string strUnderbar("_"); +static const std::string strBase("base"); +static const std::string strStops("stops"); +static const std::string strName("name"); +static const std::string strVersion("version"); +static const std::string strLayers("layers"); +static const std::string strBackground("background"); +static const std::regex colorSeparatorPattern("[(),]"); +static const std::regex fieldSeparatorPattern(R"([{}]+)"); +static const std::regex colonPattern(":"); + bool MapboxRegexField::parse(const std::string &textField) { // Parse out the {} groups in the text // TODO: We're missing a boatload of stuff in the spec - std::regex regex{R"([{}]+)"}; + const auto ®ex = fieldSeparatorPattern; std::sregex_token_iterator it{textField.begin(), textField.end(), regex, -1}; - std::vector regexChunks{it, {}}; bool isJustText = textField[0] != '{'; - for (auto regexChunk : regexChunks) { - if (regexChunk.empty()) + for (; it != std::sregex_token_iterator(); ++it) { + if (it->length() == 0) + { continue; + } + const auto regexChunk = std::string(*it); MapboxTextChunk textChunk; if (isJustText) { textChunk.str = regexChunk; } else { textChunk.keys.push_back(regexChunk); // For some reason name:en is sometimes name_en - std::string textVariant = regexChunk; - textVariant = std::regex_replace(textVariant, std::regex(":"), "_"); - textChunk.keys.push_back(textVariant); + textChunk.keys.push_back(std::regex_replace(regexChunk, colonPattern, strUnderbar)); } chunks.push_back(textChunk); isJustText = !isJustText; } valid = true; - + return true; } bool MapboxRegexField::parse(const std::string &fieldName, - MapboxVectorStyleSetImpl *styleSet, - DictionaryRef styleEntry) + MapboxVectorStyleSetImpl *styleSet, + const DictionaryRef &styleEntry) { - std::string textField = styleSet->stringValue(fieldName, styleEntry, ""); - if (!textField.empty()) - parse(textField); - - return true; + const std::string textField = styleSet->stringValue(fieldName, styleEntry, std::string()); + return textField.empty() || parse(textField); } -std::string MapboxRegexField::build(DictionaryRef attrs) +std::string MapboxRegexField::build(const DictionaryRef &attrs) { bool found = false; bool didLookup = false; - std::string text = ""; - - for (auto chunk : chunks) { + + std::string text; + text.reserve(chunks.size() * 20); + + for (const auto &chunk : chunks) { if (!chunk.str.empty()) text += chunk.str; else { - for (auto key : chunk.keys) { + for (const auto &key : chunk.keys) { didLookup = true; if (attrs->hasField(key)) { found = true; - std::string keyVal = attrs->getString(key); + const std::string keyVal = attrs->getString(key); if (!keyVal.empty()) { text += keyVal; break; @@ -94,8 +104,8 @@ std::string MapboxRegexField::build(DictionaryRef attrs) } if (didLookup && !found) - return ""; - + return std::string(); + if (!text.empty() && text[text.size()-1] == '\n') text.resize(text.size()-1); @@ -107,24 +117,24 @@ MaplyVectorFunctionStop::MaplyVectorFunctionStop() { } -bool MaplyVectorFunctionStops::parse(DictionaryRef entry,MapboxVectorStyleSetImpl *styleSet,bool isText) +bool MaplyVectorFunctionStops::parse(const DictionaryRef &entry,MapboxVectorStyleSetImpl *styleSet,bool isText) { - base = entry->getDouble("base",1.0); + base = entry->getDouble(strBase,1.0); - std::vector dataArray = entry->getArray("stops"); + std::vector dataArray = entry->getArray(strStops); if (dataArray.size() < 2) { wkLogLevel(Warn, "Expecting at least two arguments for function stops."); return false; } - for (auto stop : dataArray) { + for (const auto &stop : dataArray) { if (stop->getType() == DictTypeArray) { - std::vector stopEntries = stop->getArray(); + const std::vector stopEntries = stop->getArray(); if (stopEntries.size() != 2) { wkLogLevel(Warn,"Expecting two arguments in each entry for a function stop."); return false; } - + MaplyVectorFunctionStop fStop; fStop.zoom = stopEntries[0]->getDouble(); if (stopEntries[1]->getType() == DictTypeDouble || stopEntries[1]->getType() == DictTypeInt) { @@ -136,10 +146,10 @@ bool MaplyVectorFunctionStops::parse(DictionaryRef entry,MapboxVectorStyleSetImp if (isText) fStop.textField.parse(stopEntries[1]->getString()); else - fStop.color = styleSet->colorValue("", stopEntries[1], NULL, NULL, false); + fStop.color = styleSet->colorValue(std::string(), stopEntries[1], nullptr, nullptr, false); break; case DictTypeObject: - fStop.color = RGBAColorRef(new RGBAColor(stopEntries[1]->getColor())); + fStop.color = std::make_shared(stopEntries[1]->getColor()); break; default: wkLogLevel(Warn, "Expecting color compatible object in function stop."); @@ -208,7 +218,7 @@ RGBAColorRef MaplyVectorFunctionStops::colorForZoom(double zoom) float res[4]; for (unsigned int ii=0;ii<4;ii++) res[ii] = ratio * (bc[ii]-ac[ii]) + ac[ii]; - return RGBAColorRef(new RGBAColor(RGBAColor::FromUnitFloats(res))); + return std::make_shared(RGBAColor::FromUnitFloats(res)); } a = b; } @@ -237,8 +247,10 @@ double MaplyVectorFunctionStops::minValue() { double val = MAXFLOAT; - for (auto stop : stops) + for (const auto &stop : stops) + { val = std::min(val,stop.val); + } return val; } @@ -247,7 +259,8 @@ double MaplyVectorFunctionStops::maxValue() { double val = -MAXFLOAT; - for (auto stop : stops) { + for (const auto &stop : stops) + { val = std::max(val,stop.val); } @@ -262,7 +275,7 @@ MapboxTransDouble::MapboxTransDouble(double value) MapboxTransDouble::MapboxTransDouble(MaplyVectorFunctionStopsRef inStops) { val = 0.0; - stops = inStops; + stops = std::move(inStops); } double MapboxTransDouble::valForZoom(double zoom) @@ -280,7 +293,7 @@ FloatExpressionInfoRef MapboxTransDouble::expression() if (!stops) return FloatExpressionInfoRef(); - FloatExpressionInfoRef floatExp(new FloatExpressionInfo()); + auto floatExp = std::make_shared(); floatExp->type = ExpressionExponential; floatExp->base = stops->base; floatExp->stopInputs.resize(stops->stops.size()); @@ -296,27 +309,25 @@ FloatExpressionInfoRef MapboxTransDouble::expression() double MapboxTransDouble::minVal() { - if (stops) { - return stops->minValue(); - } else - return val; + return stops ? stops->minValue() : val; } double MapboxTransDouble::maxVal() { - if (stops) { - return stops->maxValue(); - } else - return val; + return stops ? stops->maxValue() : val; } -MapboxTransColor::MapboxTransColor(RGBAColorRef color) -: color(color), useAlphaOverride(false), alpha(1.0) +MapboxTransColor::MapboxTransColor(RGBAColorRef color) : + color(std::move(color)), + useAlphaOverride(false), + alpha(1.0) { } -MapboxTransColor::MapboxTransColor(MaplyVectorFunctionStopsRef stops) -: useAlphaOverride(false), alpha(1.0), stops(stops) +MapboxTransColor::MapboxTransColor(MaplyVectorFunctionStopsRef stops) : + useAlphaOverride(false), + alpha(1.0), + stops(std::move(stops)) { } @@ -342,7 +353,7 @@ ColorExpressionInfoRef MapboxTransColor::expression() if (!stops) return ColorExpressionInfoRef(); - ColorExpressionInfoRef colorExp(new ColorExpressionInfo()); + auto colorExp = std::make_shared(); colorExp->type = ExpressionExponential; colorExp->base = stops->base; colorExp->stopInputs.resize(stops->stops.size()); @@ -361,28 +372,38 @@ MapboxTransText::MapboxTransText(const std::string &inText) textField.parse(inText); } -MapboxTransText::MapboxTransText(MaplyVectorFunctionStopsRef stops) -: stops(stops) +MapboxTransText::MapboxTransText(MaplyVectorFunctionStopsRef stops) : + stops(std::move(stops)) { } MapboxRegexField MapboxTransText::textForZoom(double zoom) { - if (stops) { - return stops->textForZoom(zoom); - } - - return textField; + return stops ? stops->textForZoom(zoom) : textField; } -MapboxVectorStyleSetImpl::MapboxVectorStyleSetImpl(Scene *inScene,CoordSystem *coordSys,VectorStyleSettingsImplRef settings) -: scene(inScene), currentID(0), tileStyleSettings(settings), coordSys(coordSys), zoomSlot(-1) +static constexpr size_t TypicalLayerCount = 500; + +MapboxVectorStyleSetImpl::MapboxVectorStyleSetImpl(Scene *inScene, + CoordSystem *coordSys, + VectorStyleSettingsImplRef settings) : + scene(inScene), + version(-1), + currentID(0), + tileStyleSettings(std::move(settings)), + coordSys(coordSys), + zoomSlot(-1), + layersByName(TypicalLayerCount), + layersByUUID(TypicalLayerCount), + layersBySource(TypicalLayerCount) { - vecManage = (VectorManager *)scene->getManager(kWKVectorManager); - wideVecManage = (WideVectorManager *)scene->getManager(kWKWideVectorManager); - markerManage = (MarkerManager *)scene->getManager(kWKMarkerManager); - labelManage = (LabelManager *)scene->getManager(kWKLabelManager); - compManage = (ComponentManager *)scene->getManager(kWKComponentManager); + layers.reserve(TypicalLayerCount); + + vecManage = scene->getManager(kWKVectorManager); + wideVecManage = scene->getManager(kWKWideVectorManager); + markerManage = scene->getManager(kWKMarkerManager); + labelManage = scene->getManager(kWKLabelManager); + compManage = scene->getManager(kWKComponentManager); // We'll look for the versions that do expressions first and // then fall back to the simpler ones @@ -411,39 +432,30 @@ MapboxVectorStyleSetImpl::MapboxVectorStyleSetImpl(Scene *inScene,CoordSystem *c wideVectorProgramID = prog->getId(); } -MapboxVectorStyleSetImpl::~MapboxVectorStyleSetImpl() +bool MapboxVectorStyleSetImpl::parse(PlatformThreadInfo *inst,const DictionaryRef &styleDict) { -} + name = styleDict->getString(strName); + version = styleDict->getInt(strVersion); -bool MapboxVectorStyleSetImpl::parse(PlatformThreadInfo *inst,DictionaryRef styleDict) -{ - name = styleDict->getString("name"); - version = styleDict->getInt("version"); - // Layers are where the action is - std::vector layerStyles = styleDict->getArray("layers"); + const std::vector layerStyles = styleDict->getArray(strLayers); int which = 0; - for (auto layerStyle : layerStyles) { + for (const auto &layerStyle : layerStyles) { if (layerStyle->getType() == DictTypeDictionary) { - MapboxVectorStyleLayerRef layer(MapboxVectorStyleLayer::VectorStyleLayer(inst,this,layerStyle->getDict(),(1*which + tileStyleSettings->baseDrawPriority))); - if (!layer) { + auto layer = MapboxVectorStyleLayer::VectorStyleLayer(inst,this,layerStyle->getDict(),(1*which + tileStyleSettings->baseDrawPriority)); + if (!layer) + { continue; - } else { - // Sort into various buckets for quick lookup - layersByName[layer->ident] = layer; - layersByUUID[layer->getUuid(inst)] = layer; - if (!layer->sourceLayer.empty()) { - auto it = layersBySource.find(layer->sourceLayer); - if (it != layersBySource.end()) { - it->second.push_back(layer); - } else { - std::vector layers; - layers.push_back(layer); - layersBySource[layer->sourceLayer] = layers; - } - } - layers.push_back(layer); } + + // Sort into various buckets for quick lookup + layersByName[layer->ident] = layer; + layersByUUID[layer->getUuid(inst)] = layer; + if (!layer->sourceLayer.empty()) + { + layersBySource.insert(std::make_pair(layer->sourceLayer, layer)); + } + layers.push_back(layer); } which++; } @@ -456,20 +468,26 @@ long long MapboxVectorStyleSetImpl::generateID() return currentID++; } -int MapboxVectorStyleSetImpl::intValue(const std::string &name,DictionaryRef dict,int defVal) +int MapboxVectorStyleSetImpl::intValue(const std::string &inName,const DictionaryRef &dict,int defVal) { - DictionaryEntryRef thing = dict->getEntry(name); - if (!thing) - return defVal; - - if (thing->getType() == DictTypeDouble || thing->getType() == DictTypeInt || thing->getType() == DictTypeIdentity) - return thing->getInt(); - - wkLogLevel(Warn, "Expected integer for %s but got something else",name.c_str()); - return defVal; + const auto thing = dict->getEntry(inName); + switch (thing ? thing->getType() : DictTypeNone) + { + case DictTypeDouble: + case DictTypeInt: + case DictTypeInt64: + case DictTypeIdentity: + return thing->getInt(); + default: + if (thing) + { + wkLogLevel(Warn,"Expected integer for %s but got type %d",inName.c_str(),thing->getType()); + } + return defVal; + } } -double MapboxVectorStyleSetImpl::doubleValue(DictionaryEntryRef thing,double defVal) +double MapboxVectorStyleSetImpl::doubleValue(const DictionaryEntryRef &thing,double defVal) { if (!thing) return defVal; @@ -481,7 +499,7 @@ double MapboxVectorStyleSetImpl::doubleValue(DictionaryEntryRef thing,double def return defVal; } -double MapboxVectorStyleSetImpl::doubleValue(const std::string &name,DictionaryRef dict,double defVal) +double MapboxVectorStyleSetImpl::doubleValue(const std::string &name,const DictionaryRef &dict,double defVal) { if (!dict) return defVal; @@ -497,78 +515,76 @@ double MapboxVectorStyleSetImpl::doubleValue(const std::string &name,DictionaryR return defVal; } -bool MapboxVectorStyleSetImpl::boolValue(const std::string &name,DictionaryRef dict,const std::string &onString,bool defVal) +bool MapboxVectorStyleSetImpl::boolValue(const std::string &name,const DictionaryRef &dict,const std::string &onString,bool defVal) { if (!dict) return defVal; - - DictionaryEntryRef thing = dict->getEntry(name); - if (!thing) - return defVal; - - if (thing->getType() == DictTypeString) - return thing->getString() == onString; - else if (thing->getType() == DictTypeInt) - return thing->getInt(); - else if (thing->getType() == DictTypeDouble) - return thing->getInt(); - else - return defVal; + + const auto thing = dict->getEntry(name); + switch (thing ? thing->getType() : DictTypeNone) + { + case DictTypeString: return thing->getString() == onString; + case DictTypeInt: + case DictTypeInt64: + case DictTypeIdentity: + case DictTypeDouble: return thing->getInt() != 0; + default: return defVal; + } } -std::string MapboxVectorStyleSetImpl::stringValue(const std::string &name,DictionaryRef dict,const std::string &defVal) +std::string MapboxVectorStyleSetImpl::stringValue(const std::string &inName,const DictionaryRef &dict,const std::string &defVal) { if (!dict) return defVal; - DictionaryEntryRef thing = dict->getEntry(name); + DictionaryEntryRef thing = dict->getEntry(inName); if (!thing) return defVal; if (thing->getType() == DictTypeString) return thing->getString(); - wkLogLevel(Warn, "Expected string for %s but got something else",name.c_str()); + wkLogLevel(Warn, "Expected string for %s but got something else",inName.c_str()); return defVal; } -std::vector MapboxVectorStyleSetImpl::arrayValue(const std::string &name,DictionaryRef dict) +std::vector MapboxVectorStyleSetImpl::arrayValue(const std::string &inName,const DictionaryRef &dict) { std::vector ret; if (!dict) return ret; - DictionaryEntryRef thing = dict->getEntry(name); + DictionaryEntryRef thing = dict->getEntry(inName); if (!thing) return ret; if (thing->getType() == DictTypeArray) return thing->getArray(); - wkLogLevel(Warn, "Expected string for %s but got something else",name.c_str()); + wkLogLevel(Warn, "Expected array for %s but got something else",inName.c_str()); return ret; } -RGBAColorRef MapboxVectorStyleSetImpl::colorValue(const std::string &name,DictionaryEntryRef val,DictionaryRef dict,RGBAColorRef defVal,bool multiplyAlpha) +RGBAColorRef MapboxVectorStyleSetImpl::colorValue(const std::string &inName, const DictionaryEntryRef &val, const DictionaryRef &dict, const RGBAColorRef &defVal, bool multiplyAlpha) { DictionaryEntryRef thing; if (dict) - thing = dict->getEntry(name); + thing = dict->getEntry(inName); else thing = val; if (!thing) return defVal; if (thing->getType() != DictTypeString) { - wkLogLevel(Warn,"Expecting a string for color (%s)",name.c_str()); + wkLogLevel(Warn, "Expecting a string for color (%s)", inName.c_str()); return defVal; } std::string str = thing->getString(); if (str.empty()) { - wkLogLevel(Warn,"Expecting non-empty string for color (%s)",name.c_str()); + wkLogLevel(Warn, "Expecting non-empty string for color (%s)", inName.c_str()); return defVal; } // Hex string @@ -580,7 +596,7 @@ RGBAColorRef MapboxVectorStyleSetImpl::colorValue(const std::string &name,Dictio iVal = std::stoul(str.substr(1), nullptr, 16); } catch (...) { - wkLogLevel(Warn,"Invalid hex value (%s) in color (%s)",str.c_str(),name.c_str()); + wkLogLevel(Warn, "Invalid hex value (%s) in color (%s)", str.c_str(), inName.c_str()); return defVal; } @@ -601,110 +617,107 @@ RGBAColorRef MapboxVectorStyleSetImpl::colorValue(const std::string &name,Dictio green = (iVal >> 8) & 0xff; blue = iVal & 0xff; } - return RGBAColorRef(new RGBAColor(red,green,blue,alpha)); + return std::make_shared(red,green,blue,alpha); } else if (str.find("rgb(") == 0) { - std::regex reg("[(),]"); - std::sregex_token_iterator iter(str.begin()+4, str.end(), reg, -1); - std::sregex_token_iterator end; - std::vector toks(iter, end); + const auto ® = colorSeparatorPattern; + const std::sregex_token_iterator iter(str.begin()+4, str.end(), reg, -1); + const std::vector toks(iter, std::sregex_token_iterator()); if (toks.size() != 3) { - wkLogLevel(Warn, "Unrecognized format in color %s",name.c_str()); + wkLogLevel(Warn, "Unrecognized format in color %s", inName.c_str()); return defVal; } - int red = std::stoi(toks[0]); - int green = std::stoi(toks[1]); - int blue = std::stoi(toks[2]); + const int red = std::stoi(toks[0]); + const int green = std::stoi(toks[1]); + const int blue = std::stoi(toks[2]); - return RGBAColorRef(new RGBAColor(red,green,blue,1.0)); + return std::make_shared(red,green,blue,1.0); } else if (str.find("rgba(") == 0) { - std::regex reg("[(),]"); - std::sregex_token_iterator iter(str.begin()+5, str.end(), reg, -1); - std::sregex_token_iterator end; - std::vector toks(iter, end); + const auto ® = colorSeparatorPattern; + const std::sregex_token_iterator iter(str.begin()+5, str.end(), reg, -1); + std::vector toks(iter, std::sregex_token_iterator()); if (toks.size() != 4) { - wkLogLevel(Warn, "Unrecognized format in color %s",name.c_str()); + wkLogLevel(Warn, "Unrecognized format in color %s", inName.c_str()); return defVal; } - int red = std::stoi(toks[0]); - int green = std::stoi(toks[1]); - int blue = std::stoi(toks[2]); - double alpha = std::stod(toks[3]); + const int red = std::stoi(toks[0]); + const int green = std::stoi(toks[1]); + const int blue = std::stoi(toks[2]); + const double alpha = std::stod(toks[3]); if (multiplyAlpha) - return RGBAColorRef(new RGBAColor(red * alpha,green * alpha,blue * alpha,255.0*alpha)); + return std::make_shared(red * alpha,green * alpha,blue * alpha,255.0*alpha); else - return RGBAColorRef(new RGBAColor(red,green,blue,255.0*alpha)); + return std::make_shared(red,green,blue,255.0*alpha); } else if (str.find("hsl(") == 0) { - std::regex reg("[(),]"); - std::sregex_token_iterator iter(str.begin()+4, str.end(), reg, -1); - std::sregex_token_iterator end; - std::vector toks(iter, end); + const auto ® = colorSeparatorPattern; + const std::sregex_token_iterator iter(str.begin()+4, str.end(), reg, -1); + const std::vector toks(iter, std::sregex_token_iterator()); if (toks.size() != 3) { - wkLogLevel(Warn, "Unrecognized format in color %s",name.c_str()); + wkLogLevel(Warn, "Unrecognized format in color %s", inName.c_str()); return defVal; } - int hue = std::stoi(toks[0]); - int sat = std::stoi(toks[1]); - int light = std::stoi(toks[2]); - float newLight = light / 100.0; - float newSat = sat / 100.0; + const int hue = std::stoi(toks[0]); + const int sat = std::stoi(toks[1]); + const int light = std::stoi(toks[2]); + const float newLight = light / 100.0; + const float newSat = sat / 100.0; - return RGBAColorRef(new RGBAColor(RGBAColor::FromHSL(hue, newSat, newLight))); + return std::make_shared(RGBAColor::FromHSL(hue, newSat, newLight)); } else if (str.find("hsla(") == 0) { - std::regex reg("[(),]"); - std::sregex_token_iterator iter(str.begin()+5, str.end(), reg, -1); - std::sregex_token_iterator end; - std::vector toks(iter, end); + const auto ® = colorSeparatorPattern; + const std::sregex_token_iterator iter(str.begin()+5, str.end(), reg, -1); + const std::vector toks(iter, std::sregex_token_iterator()); if (toks.size() != 4) { - wkLogLevel(Warn, "Unrecognized format in color %s",name.c_str()); + wkLogLevel(Warn, "Unrecognized format in color %s", inName.c_str()); return defVal; } - int hue = std::stoi(toks[0]); - int sat = std::stoi(toks[1]); - int light = std::stoi(toks[2]); - float alpha = std::stod(toks[3]); - float newLight = light / 100.0; - float newSat = sat / 100.0; - - RGBAColorRef color(new RGBAColor(RGBAColor::FromHSL(hue, newSat, newLight))); - color->a = alpha * 255.0; - + const int hue = std::stoi(toks[0]); + const int sat = std::stoi(toks[1]); + const int light = std::stoi(toks[2]); + const auto alpha = (float)std::stod(toks[3]); + const auto newLight = light / 100.0f; + const auto newSat = sat / 100.0f; + + auto color = std::make_shared(RGBAColor::FromHSL(hue, newSat, newLight)); + color->a = (uint8_t)(alpha * 255.0); + return color; } - wkLogLevel(Warn,"Didn't recognize format of color (%s)",name.c_str()); + wkLogLevel(Warn, "Didn't recognize format of color (%s)", inName.c_str()); return defVal; } -RGBAColorRef MapboxVectorStyleSetImpl::colorValue(const std::string &name,DictionaryEntryRef val,DictionaryRef dict,RGBAColor defVal,bool multiplyAlpha) +RGBAColorRef MapboxVectorStyleSetImpl::colorValue(const std::string &inName,const DictionaryEntryRef &val,const DictionaryRef &dict,const RGBAColor &defVal,bool multiplyAlpha) { - RGBAColorRef color(new RGBAColor(defVal)); - - return colorValue(name, val, dict, color, multiplyAlpha); + return colorValue(inName, val, dict, std::make_shared(defVal), multiplyAlpha); } -int MapboxVectorStyleSetImpl::enumValue(DictionaryEntryRef entry,const char * const options[],int defVal) +int MapboxVectorStyleSetImpl::enumValue(const DictionaryEntryRef &entry,const char * const options[],int defVal) { if (!entry || entry->getType() != DictTypeString) return defVal; - std::string name = entry->getString(); + const std::string localName = entry->getString(); - for (int which = 0; options[which]; which++) { + for (int which = 0; options[which]; which++) + { const char * const val = options[which]; - if (!strcmp(val, name.c_str())) + if (!strcmp(val, localName.c_str())) + { return which; + } } - wkLogLevel(Warn,"Found unexpected value (%s) in enumerated type",name.c_str()); + wkLogLevel(Warn, "Found unexpected value (%s) in enumerated type", localName.c_str()); return defVal; } -MapboxTransDoubleRef MapboxVectorStyleSetImpl::transDouble(DictionaryEntryRef theEntry, double defVal) +MapboxTransDoubleRef MapboxVectorStyleSetImpl::transDouble(const DictionaryEntryRef &theEntry, double defVal) { if (!theEntry) return std::make_shared(defVal); @@ -728,20 +741,20 @@ MapboxTransDoubleRef MapboxVectorStyleSetImpl::transDouble(DictionaryEntryRef th } -MapboxTransDoubleRef MapboxVectorStyleSetImpl::transDouble(const std::string &name,DictionaryRef entry,double defVal) +MapboxTransDoubleRef MapboxVectorStyleSetImpl::transDouble(const std::string &name,const DictionaryRef &entry,double defVal) { return transDouble(entry ? entry->getEntry(name) : DictionaryEntryRef(), defVal); } -MapboxTransColorRef MapboxVectorStyleSetImpl::transColor(const std::string &name,DictionaryRef entry,const RGBAColor *defVal) +MapboxTransColorRef MapboxVectorStyleSetImpl::transColor(const std::string &name,const DictionaryRef &entry,const RGBAColor *defVal) { RGBAColorRef defValRef; if (defVal) - defValRef = RGBAColorRef(new RGBAColor(*defVal)); + defValRef = std::make_shared(*defVal); if (!entry) { if (defVal) - return MapboxTransColorRef(new MapboxTransColor(RGBAColorRef(new RGBAColor(*defVal)))); + return std::make_shared(std::make_shared(*defVal)); return MapboxTransColorRef(); } @@ -749,23 +762,22 @@ MapboxTransColorRef MapboxVectorStyleSetImpl::transColor(const std::string &name DictionaryEntryRef theEntry = entry->getEntry(name); if (!theEntry) { if (defVal) - return MapboxTransColorRef(new MapboxTransColor(RGBAColorRef(new RGBAColor(*defVal)))); + return std::make_shared(std::make_shared(*defVal)); return MapboxTransColorRef(); } // This is probably stops if (theEntry->getType() == DictTypeDictionary) { - MaplyVectorFunctionStopsRef stops(new MaplyVectorFunctionStops()); - stops->parse(theEntry->getDict(), this, false); - if (stops) { - return MapboxTransColorRef(new MapboxTransColor(stops));; + auto stops = std::make_shared(); + if (stops->parse(theEntry->getDict(), this, false)) { + return std::make_shared(stops); } else { wkLogLevel(Warn, "Expecting key word 'stops' in entry %s",name.c_str()); } } else if (theEntry->getType() == DictTypeString) { RGBAColorRef color = colorValue(name, theEntry, DictionaryRef(), defValRef, false); if (color) - return MapboxTransColorRef(new MapboxTransColor(color)); + return std::make_shared(color); else { wkLogLevel(Warn,"Unexpected type found in entry %s. Was expecting a color.",name.c_str()); } @@ -776,48 +788,42 @@ MapboxTransColorRef MapboxVectorStyleSetImpl::transColor(const std::string &name return MapboxTransColorRef(); } -MapboxTransColorRef MapboxVectorStyleSetImpl::transColor(const std::string &name,DictionaryRef entry,const RGBAColor &inColor) +MapboxTransColorRef MapboxVectorStyleSetImpl::transColor(const std::string &inName, const DictionaryRef &entry, const RGBAColor &inColor) { RGBAColor color = inColor; - return transColor(name, entry, &color); + return transColor(inName, entry, &color); } -MapboxTransTextRef MapboxVectorStyleSetImpl::transText(const std::string &name,DictionaryRef entry,const std::string &str) +MapboxTransTextRef MapboxVectorStyleSetImpl::transText(const std::string &inName, const DictionaryRef &entry, const std::string &str) { if (!entry) { - if (!str.empty()) - return MapboxTransTextRef(new MapboxTransText(str)); - return MapboxTransTextRef(); + return str.empty() ? MapboxTransTextRef() : std::make_shared(str); } // They pass in the whole dictionary and let us look the field up - DictionaryEntryRef theEntry = entry->getEntry(name); + DictionaryEntryRef theEntry = entry->getEntry(inName); if (!theEntry) { - if (!str.empty()) - return MapboxTransTextRef(new MapboxTransText(str)); - return MapboxTransTextRef(); + return str.empty() ? MapboxTransTextRef() : std::make_shared(str); } // This is probably stops if (theEntry->getType() == DictTypeDictionary) { - MaplyVectorFunctionStopsRef stops(new MaplyVectorFunctionStops()); - stops->parse(theEntry->getDict(), this, true); - if (stops) { - return MapboxTransTextRef(new MapboxTransText(stops));; + auto stops = std::make_shared(); + if (stops->parse(theEntry->getDict(), this, true)) { + return std::make_shared(stops); } else { - wkLogLevel(Warn, "Expecting key word 'stops' in entry %s",name.c_str()); + wkLogLevel(Warn, "Expecting key word 'stops' in entry %s", inName.c_str()); } } else if (theEntry->getType() == DictTypeString) { - std::string text = theEntry->getString(); - return MapboxTransTextRef(new MapboxTransText(text)); + return std::make_shared(theEntry->getString()); } else { - wkLogLevel(Warn,"Unexpected type found in entry %s. Was expecting a string.",name.c_str()); + wkLogLevel(Warn, "Unexpected type found in entry %s. Was expecting a string.", inName.c_str()); } return MapboxTransTextRef(); } -void MapboxVectorStyleSetImpl::unsupportedCheck(const char *field,const char *what,DictionaryRef styleEntry) +void MapboxVectorStyleSetImpl::unsupportedCheck(const char *field,const char *what,const DictionaryRef &styleEntry) { if (styleEntry && styleEntry->hasField(field)) { #if DEBUG @@ -826,7 +832,7 @@ void MapboxVectorStyleSetImpl::unsupportedCheck(const char *field,const char *wh } } -RGBAColorRef MapboxVectorStyleSetImpl::resolveColor(MapboxTransColorRef color,MapboxTransDoubleRef opacity,double zoom,MBResolveColorType resolveMode) +RGBAColorRef MapboxVectorStyleSetImpl::resolveColor(const MapboxTransColorRef &color,const MapboxTransDoubleRef &opacity,double zoom,MBResolveColorType resolveMode) { // No color means no color if (!color) @@ -838,7 +844,7 @@ RGBAColorRef MapboxVectorStyleSetImpl::resolveColor(MapboxTransColorRef color,Ma if (!opacity || color->hasAlphaOverride()) return std::make_shared(thisColor); - double const thisOpacity = opacity->valForZoom(zoom) * 255; + const double thisOpacity = opacity->valForZoom(zoom) * 255; float vals[4]; thisColor.asUnitFloats(vals); @@ -860,27 +866,23 @@ RGBAColor MapboxVectorStyleSetImpl::color(RGBAColor color,double opacity) { float vals[4]; color.asUnitFloats(vals); - - return RGBAColor(vals[0]*opacity*255,vals[1]*opacity*255,vals[2]*opacity*255,vals[3]*opacity*255); -} - -MapboxVectorStyleLayerRef MapboxVectorStyleSetImpl::getLayer(const std::string &name) -{ - auto it = layersByName.find(name); - if (it == layersByName.end()) - return MapboxVectorStyleLayerRef(); - - return it->second; + return { + (uint8_t)(vals[0]*opacity*255), + (uint8_t)(vals[1]*opacity*255), + (uint8_t)(vals[2]*opacity*255), + (uint8_t)(vals[3]*opacity*255) + }; } -void MapboxVectorStyleSetImpl::setZoomSlot(int inZoomSlot) +MapboxVectorStyleLayerRef MapboxVectorStyleSetImpl::getLayer(const std::string &inName) { - zoomSlot = inZoomSlot; + const auto it = layersByName.find(inName); + return (it == layersByName.end()) ? MapboxVectorStyleLayerRef() : it->second; } VectorStyleImplRef MapboxVectorStyleSetImpl::backgroundStyle(PlatformThreadInfo *inst) const { - const auto it = layersByName.find("background"); + const auto it = layersByName.find(strBackground); if (it != layersByName.end()) { if (auto backLayer = std::dynamic_pointer_cast(it->second)) { return backLayer; @@ -891,32 +893,33 @@ VectorStyleImplRef MapboxVectorStyleSetImpl::backgroundStyle(PlatformThreadInfo RGBAColorRef MapboxVectorStyleSetImpl::backgroundColor(PlatformThreadInfo *inst,double zoom) { - auto it = layersByName.find("background"); + const auto it = layersByName.find(strBackground); if (it != layersByName.end()) { - MapboxVectorLayerBackgroundRef backLayer = std::dynamic_pointer_cast(it->second); - if (backLayer) { - return RGBAColorRef(new RGBAColor(backLayer->paint.color->colorForZoom(zoom))); + if (const auto backLayer = std::dynamic_pointer_cast(it->second)) { + return std::make_shared(backLayer->paint.color->colorForZoom(zoom)); } } - return RGBAColorRef(); } - std::vector MapboxVectorStyleSetImpl::stylesForFeature(PlatformThreadInfo *inst, const Dictionary &attrs, const QuadTreeIdentifier &tileID, const std::string &layerName) { std::vector styles; - - const auto it = layersBySource.find(layerName); - if (it != layersBySource.end()) { - for (const auto &layer : it->second) { - if (!layer->filter || layer->filter->testFeature(attrs, tileID)) { - styles.reserve(it->second.size()); - styles.push_back(layer); + + const auto range = layersBySource.equal_range(layerName); + for (auto i = range.first; i != range.second; ++i) + { + auto &layer = i->second; + if (!layer->filter || layer->filter->testFeature(attrs, tileID)) + { + if (styles.empty()) + { + styles.reserve(std::distance(range.first, range.second)); } + styles.push_back(layer); } } @@ -928,8 +931,15 @@ bool MapboxVectorStyleSetImpl::layerShouldDisplay(PlatformThreadInfo *inst, const std::string &layerName, const QuadTreeNew::Node &tileID) { - const auto it = layersBySource.find(layerName); - return it != layersBySource.end(); + const auto range = layersBySource.equal_range(layerName); + for (auto i = range.first; i != range.second; ++i) + { + if (i->second->visible || !i->second->representation.empty()) + { + return true; + } + } + return false; } /// Return the style associated with the given UUID. @@ -947,7 +957,7 @@ std::vector MapboxVectorStyleSetImpl::allStyles(PlatformThre void MapboxVectorStyleSetImpl::addSprites(MapboxVectorStyleSpritesRef newSprites) { - sprites = newSprites; + sprites = std::move(newSprites); } } diff --git a/common/WhirlyGlobeLib/src/MapboxVectorStyleSymbol.cpp b/common/WhirlyGlobeLib/src/MapboxVectorStyleSymbol.cpp index 264800674e..fccd2cc591 100644 --- a/common/WhirlyGlobeLib/src/MapboxVectorStyleSymbol.cpp +++ b/common/WhirlyGlobeLib/src/MapboxVectorStyleSymbol.cpp @@ -1,9 +1,9 @@ /* - * MapboxVectorStyleSymbol.h + * MapboxVectorStyleSymbol.cpp * WhirlyGlobe-MaplyComponent * * Created by Steve Gifford on 2/17/15. - * Copyright 2011-2015 mousebird consulting + * Copyright 2011-2021 mousebird consulting * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -35,7 +35,7 @@ static const char * const justifyVals[] = {"center","left","right",NULL}; bool MapboxVectorSymbolLayout::parse(PlatformThreadInfo *inst, MapboxVectorStyleSetImpl *styleSet, - DictionaryRef styleEntry) + const DictionaryRef &styleEntry) { globalTextScale = styleSet->tileStyleSettings->textScale; placement = styleEntry ? (MapboxSymbolPlacement)styleSet->enumValue(styleEntry->getEntry("symbol-placement"), placementVals, (int)MBPlacePoint) : MBPlacePoint; @@ -48,7 +48,7 @@ bool MapboxVectorSymbolLayout::parse(PlatformThreadInfo *inst, textFontArray = styleEntry->getArray("text-font"); if (!textFontArray.empty()) { for (int ii=0;iigetString(); + const std::string &textField = textFontArray[ii]->getString(); if (!textField.empty()) textFontNames.push_back(textField); } @@ -60,7 +60,7 @@ bool MapboxVectorSymbolLayout::parse(PlatformThreadInfo *inst, textMaxWidth = styleSet->transDouble("text-max-width", styleEntry, 10.0); textSize = styleSet->transDouble("text-size", styleEntry, 24.0); - auto offsetEntries = styleSet->arrayValue("text-offset", styleEntry); + const auto offsetEntries = styleSet->arrayValue("text-offset", styleEntry); textOffsetX = (offsetEntries.size() > 0) ? styleSet->transDouble(offsetEntries[0], 0) : MapboxTransDoubleRef(); textOffsetY = (offsetEntries.size() > 1) ? styleSet->transDouble(offsetEntries[1], 0) : MapboxTransDoubleRef(); @@ -74,15 +74,15 @@ bool MapboxVectorSymbolLayout::parse(PlatformThreadInfo *inst, textJustifySet = (styleEntry && styleEntry->getEntry("text-justify")); textJustify = styleEntry ? (TextJustify)styleSet->enumValue(styleEntry->getEntry("text-justify"), justifyVals, WhirlyKitTextCenter) : WhirlyKitTextCenter; - iconImageField = styleSet->transText("icon-image", styleEntry, ""); + iconImageField = styleSet->transText("icon-image", styleEntry, std::string()); iconSize = styleSet->transDouble("icon-size", styleEntry, 1.0); - + return true; } bool MapboxVectorSymbolPaint::parse(PlatformThreadInfo *inst, MapboxVectorStyleSetImpl *styleSet, - DictionaryRef styleEntry) + const DictionaryRef &styleEntry) { textColor = styleSet->transColor("text-color", styleEntry, RGBAColor::black()); textOpacity = styleSet->transDouble("text-opacity", styleEntry, 1.0); @@ -94,21 +94,27 @@ bool MapboxVectorSymbolPaint::parse(PlatformThreadInfo *inst, } bool MapboxVectorLayerSymbol::parse(PlatformThreadInfo *inst, - DictionaryRef styleEntry, - MapboxVectorStyleLayerRef refLayer, - int inDrawPriority) + const DictionaryRef &styleEntry, + const MapboxVectorStyleLayerRef &refLayer, + int inDrawPriority) { if (!MapboxVectorStyleLayer::parse(inst,styleEntry,refLayer,drawPriority)) + { return false; - bool hasLayout = layout.parse(inst,styleSet,styleEntry->getDict("layout")); - bool hasPaint = paint.parse(inst,styleSet, styleEntry->getDict("paint")); + } + + const bool hasLayout = layout.parse(inst,styleSet,styleEntry->getDict("layout")); + const bool hasPaint = paint.parse(inst,styleSet, styleEntry->getDict("paint")); if (!hasLayout && !hasPaint) return false; uniqueLabel = styleSet->boolValue("unique-label", styleEntry, "yes", false); - + + repUUIDField = styleSet->stringValue("X-Maply-RepresentationUUIDField", styleEntry, std::string()); + uuidField = styleSet->tileStyleSettings->uuidField; - + uuidField = styleSet->stringValue("X-Maply-UUIDField", styleEntry, uuidField); + useZoomLevels = styleSet->tileStyleSettings->useZoomLevels; drawPriority = inDrawPriority; @@ -116,20 +122,26 @@ bool MapboxVectorLayerSymbol::parse(PlatformThreadInfo *inst, return true; } -std::string MapboxVectorLayerSymbol::breakUpText(PlatformThreadInfo *inst,const std::string &text,double textMaxWidth,LabelInfoRef labelInfo) +std::string MapboxVectorLayerSymbol::breakUpText(PlatformThreadInfo *inst, + const std::string &text, + double textMaxWidth, + const LabelInfoRef &labelInfo) { // If there are no spaces, let's not break it up - if (text.find(" ") == std::string::npos) + if (text.find(' ') == std::string::npos) return text; size_t start, end = 0; std::vector chunks; - while ((start = text.find_first_not_of(" ", end)) != std::string::npos) { - end = text.find(" ",start); + chunks.reserve(text.size() / 5 + 1); + while ((start = text.find_first_not_of(' ', end)) != std::string::npos) { + end = text.find(' ',start); chunks.push_back(text.substr(start, end - start)); } - - std::string soFar,retStr; + + std::string soFar,retStr,testStr; + soFar.reserve(text.size()); + retStr.reserve(text.size() + 5); for (auto chunk : chunks) { if (soFar.empty()) { soFar = chunk; @@ -137,8 +149,8 @@ std::string MapboxVectorLayerSymbol::breakUpText(PlatformThreadInfo *inst,const } // Try the string with the next chunk - std::string testStr = soFar.empty() ? chunk : soFar + " " + chunk; - double width = styleSet->calculateTextWidth(inst,labelInfo,testStr); + testStr = soFar.empty() ? chunk : soFar + " " + chunk; + const double width = styleSet->calculateTextWidth(inst,labelInfo,testStr); // Flush out what we have so far and start with this new chunk if (width > textMaxWidth) { @@ -146,7 +158,7 @@ std::string MapboxVectorLayerSymbol::breakUpText(PlatformThreadInfo *inst,const soFar = testStr; if (retStr.size() > 0) { - retStr.append("\n"); + retStr += '\n'; } retStr.append(soFar); soFar = chunk; @@ -156,9 +168,9 @@ std::string MapboxVectorLayerSymbol::breakUpText(PlatformThreadInfo *inst,const } } if (retStr.size() > 0) - retStr.append("\n"); + retStr += '\n'; retStr.append(soFar); - + return retStr; } @@ -181,9 +193,9 @@ void MapboxVectorLayerSymbol::cleanup(PlatformThreadInfo *inst,ChangeSet &change SingleLabelRef MapboxVectorLayerSymbol::setupLabel(PlatformThreadInfo *inst, const Point2f &pt, - LabelInfoRef labelInfo, - MutableDictionaryRef attrs, - VectorTileDataRef tileInfo) + const LabelInfoRef &labelInfo, + const MutableDictionaryRef &attrs, + const VectorTileDataRef &tileInfo) { // Reconstruct the string from its replacement form std::string text = layout.textField.build(attrs); @@ -288,24 +300,23 @@ SingleLabelRef MapboxVectorLayerSymbol::setupLabel(PlatformThreadInfo *inst, return label; } -Marker *MapboxVectorLayerSymbol::setupMarker(PlatformThreadInfo *inst, - const Point2f &pt, - VectorObjectRef vecObj, - MutableDictionaryRef attrs, - ComponentObjectRef compObj, - VectorTileDataRef tileInfo) +std::unique_ptr MapboxVectorLayerSymbol::setupMarker(PlatformThreadInfo *inst, + const Point2f &pt, + const MutableDictionaryRef &attrs, + const VectorTileDataRef &tileInfo) { // The symbol name might get tricky std::string symbolName = layout.iconImageField->textForZoom(tileInfo->ident.level).build(attrs); - + // Sometimes they stick an empty text string in there if (symbolName.empty()) - return NULL; - + return nullptr; + Point2d markerSize; - auto subTex = styleSet->sprites->getTexture(symbolName,markerSize); - - if (markerSize.x() == 0.0) { + const auto subTex = styleSet->sprites->getTexture(symbolName,markerSize); + + if (markerSize.x() == 0.0) + { #if DEBUG wkLogLevel(Warn, "MapboxVectorLayerSymbol: Failed to find symbol %s",symbolName.c_str()); { @@ -313,64 +324,86 @@ Marker *MapboxVectorLayerSymbol::setupMarker(PlatformThreadInfo *inst, std::string symbolName = layout.iconImageField->textForZoom(tileInfo->ident.level).build(attrs); } #endif - return NULL; + return nullptr; } - double size = layout.iconSize->valForZoom(tileInfo->ident.level); - if (!layout.iconSize->isExpression()) { + const double size = layout.iconSize->valForZoom(tileInfo->ident.level); + if (!layout.iconSize->isExpression()) + { markerSize.x() *= size; markerSize.y() *= size; } - SimpleIdentity markerTexID = subTex.getId(); - Marker *marker = new Marker(); + auto marker = std::make_unique(); marker->width = markerSize.x(); marker->height = markerSize.y(); marker->loc = GeoCoord(pt.x(),pt.y()); - if (!layout.iconAllowOverlap) - marker->layoutImportance = layout.layoutImportance; - else - marker->layoutImportance = MAXFLOAT; - if (selectable) { - marker->isSelectable = true; - marker->selectID = Identifiable::genId(); - styleSet->addSelectionObject(marker->selectID, vecObj, compObj); - compObj->selectIDs.insert(marker->selectID); - compObj->isSelectable = true; - } + marker->layoutImportance = layout.iconAllowOverlap ? MAXFLOAT : layout.layoutImportance; + + SimpleIdentity markerTexID = subTex.getId(); if (markerTexID != EmptyIdentity) + { marker->texIDs.push_back(markerTexID); + } return marker; } static const int ScreenDrawPriorityOffset = 1000000; +using MarkerPtrVec = std::vector; +using VecObjRefVec = std::vector; +using LabelRefVec = std::vector; +using MarkersByUUIDMap = std::unordered_map>; +static const auto emptyMapValue = std::make_tuple(MarkerPtrVec(),VecObjRefVec(),LabelRefVec()); + +static std::tuple Lookup(const std::string &uuid, MarkersByUUIDMap &map) +{ + // Look up the vectors of markers/objects for this uuid (including blank), inserting empty ones if necessary + const auto result = map.insert(std::make_pair(uuid,emptyMapValue)); + auto &value = result.first->second; + return std::make_tuple(&std::get<0>(value),&std::get<1>(value),&std::get<2>(value)); +} + void MapboxVectorLayerSymbol::buildObjects(PlatformThreadInfo *inst, - std::vector &vecObjs, - VectorTileDataRef tileInfo) + const std::vector &vecObjs, + const VectorTileDataRef &tileInfo, + const Dictionary *desc) { - if (!visible) + // If a representation is set, we produce results for non-visible layers + if (!visible && (representation.empty() || repUUIDField.empty())) + { return; + } const auto zoomLevel = tileInfo->ident.level; - ComponentObjectRef compObj = styleSet->makeComponentObject(inst); + + using MarkerPtrVec = std::vector; + using VecObjRefVec = std::vector; + using LabelRefVec = std::vector; + auto const capacity = vecObjs.size() * 5; // ? + std::unordered_map> markersByUUID(capacity); + const auto emptyMapValue = std::make_tuple(MarkerPtrVec(),VecObjRefVec(),LabelRefVec()); // Render at the max size and then scale dynamically double textSize = layout.textSize->maxVal() * layout.globalTextScale; - textSize = (int)(textSize + 0.5); - if (textSize <= 0.0) - textSize = 1.0; - + textSize = std::max(1.0, std::round(textSize)); + // When there's no dynamic scaling, we need to scale the text size down - if (!layout.textSize->isExpression()) { + if (!layout.textSize->isExpression()) + { textSize /= styleSet->tileStyleSettings->rendererScale; } LabelInfoRef labelInfo = styleSet->makeLabelInfo(inst,layout.textFontNames,textSize); + if (!labelInfo) { + return; + } + labelInfo->hasExp = true; labelInfo->zoomSlot = styleSet->zoomSlot; - if (minzoom != 0 || maxzoom < 1000) { + if (minzoom != 0 || maxzoom < 1000) + { labelInfo->minZoomVis = minzoom; labelInfo->maxZoomVis = maxzoom; // wkLogLevel(Debug, "zoomSlot = %d, minZoom = %f, maxZoom = %f",styleSet->zoomSlot,labelInfo->minZoomVis,labelInfo->maxZoomVis); @@ -385,22 +418,25 @@ void MapboxVectorLayerSymbol::buildObjects(PlatformThreadInfo *inst, // Note: To fix this we need to blast the text apart into pieces const auto textColor = styleSet->resolveColor(paint.textColor, NULL, zoomLevel, MBResolveColorOpacityReplaceAlpha); if (textColor) + { labelInfo->textColor = *textColor; + } // We can apply a scale, but it needs to be scaled to the current text size labelInfo->scaleExp = layout.textSize->expression(); - if (labelInfo->scaleExp) { + if (labelInfo->scaleExp) + { for (unsigned int ii=0;iiscaleExp->stopOutputs.size();ii++) + { labelInfo->scaleExp->stopOutputs[ii] /= textSize; + } } if (paint.textHaloColor && paint.textHaloWidth) { labelInfo->outlineColor = paint.textHaloColor->colorForZoom(zoomLevel); - // Note: We're not using blue right here - labelInfo->outlineSize = (paint.textHaloWidth->valForZoom(zoomLevel) - paint.textHaloBlur->valForZoom(zoomLevel)) * layout.globalTextScale; - if (labelInfo->outlineSize < 0.5) - labelInfo->outlineSize = 0.5; + // Note: We're not using blur right here + labelInfo->outlineSize = std::max(0.5, (paint.textHaloWidth->valForZoom(zoomLevel) - paint.textHaloBlur->valForZoom(zoomLevel)) * layout.globalTextScale); } // // Note: Made up value for pushing multi-line text together @@ -409,23 +445,30 @@ void MapboxVectorLayerSymbol::buildObjects(PlatformThreadInfo *inst, const bool iconInclude = layout.iconImageField && styleSet->sprites; const bool textInclude = (textColor && textSize > 0.0 && !layout.textField.chunks.empty()); if (!textInclude && !iconInclude) + { return; + } // Sort out the image for the marker if we're doing that - MarkerInfo markerInfo(true); + MarkerInfo markerInfo(/*screenObject=*/true); markerInfo.hasExp = true; markerInfo.zoomSlot = styleSet->zoomSlot; - if (minzoom != 0 || maxzoom < 1000) { + markerInfo.scaleExp = layout.iconSize->expression(); + + if (minzoom != 0 || maxzoom < 1000) + { markerInfo.minZoomVis = minzoom; markerInfo.maxZoomVis = maxzoom; } - markerInfo.scaleExp = layout.iconSize->expression(); - if (iconInclude) { + if (iconInclude) + { markerInfo.programID = styleSet->screenMarkerProgramID; markerInfo.drawPriority = labelInfo->drawPriority; } + ComponentObjectRef compObj = styleSet->makeComponentObject(inst); + // Calculate the present value of the offsets in ems. // This isn't in setupLabel because it only needs to be done once. // @@ -433,49 +476,97 @@ void MapboxVectorLayerSymbol::buildObjects(PlatformThreadInfo *inst, // `label->screenOffset` uses the same convention for Y but the opposite convention for X. const Point2d offset = Point2d(layout.textOffsetX ? (layout.textOffsetX->valForZoom(zoomLevel) * textSize) : 0.0, layout.textOffsetY ? (layout.textOffsetY->valForZoom(zoomLevel) * -textSize) : 0.0); - - std::vector labels; - std::vector markers; - for (auto vecObj : vecObjs) { - if (vecObj->getVectorType() == VectorPointType) { - for (VectorShapeRef shape : vecObj->shapes) { - if (auto pts = std::dynamic_pointer_cast(shape)) { - for (auto pt : pts->pts) { - if (textInclude) { - if (auto label = setupLabel(inst,pt,labelInfo,vecObj->getAttributes(),tileInfo)) { + + std::vector> markerOwner; + for (auto vecObj : vecObjs) + { + const auto vecType = vecObj->getVectorType(); + switch (vecType) + { + case VectorPointType: + case VectorLinearType: + case VectorArealType: break; + default: continue; + } + + const auto &attrs = vecObj->getAttributes(); + const auto uuid = repUUIDField.empty() ? std::string() : attrs->getString(repUUIDField); + + MarkerPtrVec* markers = nullptr; + VecObjRefVec* vecObjs = nullptr; + LabelRefVec* labels = nullptr; + + if (vecType == VectorPointType) + { + for (const VectorShapeRef &shape : vecObj->shapes) + { + if (auto pts = dynamic_cast(shape.get())) + { + if (!markers && !pts->pts.empty()) + { + // Find/create the map entry now that we know there's something to put in it + std::tie(markers, vecObjs, labels) = Lookup(uuid, markersByUUID); + } + + for (const auto &pt : pts->pts) + { + if (textInclude) + { + if (auto label = setupLabel(inst,pt,labelInfo,attrs,tileInfo)) + { label->screenOffset = offset; - labels.push_back(label); + labels->push_back(label); #if DEBUG - } else { + } + else + { wkLogLevel(Warn,"Failed to find text for label"); #endif } } - if (iconInclude) { - Marker *marker = setupMarker(inst, pt, vecObj, vecObj->getAttributes(), compObj, tileInfo); - if (marker) - markers.push_back(marker); + if (iconInclude) + { + if (auto marker = setupMarker(inst, pt, attrs, tileInfo)) + { + markers->push_back(marker.get()); + vecObjs->push_back(vecObj); + markerOwner.emplace_back(std::move(marker)); + } } } } } - } else if (vecObj->getVectorType() == VectorLinearType) { + } + else if (vecType == VectorLinearType) + { #if DEBUG - if (vecObj->shapes.size() > 1) { + if (vecObj->shapes.size() > 1) + { static int warned = 0; - if (!warned++) { + if (!warned++) + { wkLogLevel(Warn, "MapboxVectorLayerSymbol: Linear vector object contains %d shapes", vecObj->shapes.size()); } } #endif - for (VectorShapeRef shape : vecObj->shapes) { + for (const VectorShapeRef &shape : vecObj->shapes) + { // for each line in the shape set ... (we expect exactly one) - if (VectorLinearRef line = std::dynamic_pointer_cast(shape)) { + if (auto line = dynamic_cast(shape.get())) + { + if (!markers) + { + // Find/create the map entry now that we know there's something to put in it + std::tie(markers, vecObjs, labels) = Lookup(uuid, markersByUUID); + } + + // Place the symbol at the middle of the line // Note that if there are multiple shapes, this will be recalculated unnecessarily. Point2d middle; double rot; - if (!vecObj->linearMiddle(middle, rot, styleSet->coordSys)) { + if (!vecObj->linearMiddle(middle, rot, styleSet->coordSys)) + { #if DEBUG wkLogLevel(Warn, "MapboxVectorLayerSymbol: Failed to compute middle of linear shape"); #endif @@ -484,46 +575,66 @@ void MapboxVectorLayerSymbol::buildObjects(PlatformThreadInfo *inst, const auto pt = Point2f(middle.x(), middle.y()); - if (textInclude) { - if (auto label = setupLabel(inst,pt,labelInfo,vecObj->getAttributes(),tileInfo)) { - if (layout.placement == MBPlaceLine) { + if (textInclude) + { + if (auto label = setupLabel(inst,pt,labelInfo,attrs,tileInfo)) + { + if (layout.placement == MBPlaceLine) + { label->rotation = -1 * rot + M_PI/2.0; if (label->rotation > M_PI_2 || label->rotation < -M_PI_2) + { label->rotation += M_PI; + } label->keepUpright = true; } label->screenOffset = offset; - labels.push_back(label); + labels->push_back(label); } } - if (iconInclude) { - Marker *marker = setupMarker(inst, pt, vecObj, vecObj->getAttributes(), compObj, tileInfo); - if (marker) - markers.push_back(marker); + if (iconInclude) + { + if (auto marker = setupMarker(inst, pt, attrs, tileInfo)) + { + markers->push_back(marker.get()); + vecObjs->push_back(vecObj); + markerOwner.emplace_back(std::move(marker)); + } } } } } - else if (vecObj->getVectorType() == VectorArealType) { + else if (vecType == VectorArealType) + { #if DEBUG - if (vecObj->shapes.size() > 1) { + if (vecObj->shapes.size() > 1) + { static int warned = 0; - if (!warned++) { + if (!warned++) + { wkLogLevel(Warn, "MapboxVectorLayerSymbol: Areal vector object contains %d shapes", vecObj->shapes.size()); } } #endif - for (auto shape : vecObj->shapes) { + for (const auto &shape : vecObj->shapes) + { // each polygon in the shape set... (we expect exactly one) - if (auto aereal = std::dynamic_pointer_cast(shape)) { - + if (auto areal = dynamic_cast(shape.get())) + { + if (!markers) + { + // Find/create the map entry now that we know there's something to put in it + std::tie(markers, vecObjs, labels) = Lookup(uuid, markersByUUID); + } + // Place the marker at the middle of the polygon. // Note that if there are multiple shapes, this will be recalculated unnecessarily. Point2d middle; - if (!vecObj->center(middle)) { + if (!vecObj->center(middle)) + { #if DEBUG wkLogLevel(Warn, "MapboxVectorLayerSymbol: Failed to compute center of areal shape"); #endif @@ -531,20 +642,25 @@ void MapboxVectorLayerSymbol::buildObjects(PlatformThreadInfo *inst, } const auto pt = Point2f(middle.x(), middle.y()); - const auto& attributes = vecObj->getAttributes(); - if (textInclude) { - if (auto label = setupLabel(inst, pt, labelInfo, attributes, tileInfo)) { + if (textInclude) + { + if (auto label = setupLabel(inst, pt, labelInfo, attrs, tileInfo)) + { // layout.placement is ignored for polygons // except for offset, which we already calculated so we might as well use label->screenOffset = offset; - labels.push_back(label); + labels->push_back(label); } } - - if (iconInclude) { - if (auto marker = setupMarker(inst, pt, vecObj, attributes, compObj, tileInfo)) { - markers.push_back(marker); + + if (iconInclude) + { + if (auto marker = setupMarker(inst, pt, attrs, tileInfo)) + { + markers->push_back(marker.get()); + vecObjs->push_back(vecObj); + markerOwner.emplace_back(std::move(marker)); } } } @@ -552,23 +668,57 @@ void MapboxVectorLayerSymbol::buildObjects(PlatformThreadInfo *inst, } } - if (!labels.empty()) { - SimpleIdentity labelID = styleSet->labelManage->addLabels(inst, labels, *labelInfo, tileInfo->changes); - if (labelID != EmptyIdentity) - compObj->labelIDs.insert(labelID); - } - - if (!markers.empty()) { - SimpleIdentity markerID = styleSet->markerManage->addMarkers(markers, markerInfo, tileInfo->changes); - for (auto marker: markers) - delete marker; - if (markerID != EmptyIdentity) - compObj->markerIDs.insert(markerID); - } - - if (!compObj->labelIDs.empty() || !compObj->markerIDs.empty()) { - styleSet->compManage->addComponentObject(compObj); - tileInfo->compObjs.push_back(compObj); + for (auto &kvp : markersByUUID) + { + const auto& uuid = kvp.first; + const auto& markers = std::get<0>(kvp.second); + const auto& vecObjs = std::get<1>(kvp.second); + const auto& labels = std::get<2>(kvp.second); + + // Generate one component object per unique UUID (including blank) + const auto compObj = styleSet->makeComponentObject(inst, desc); + + compObj->uuid = uuid; + compObj->representation = representation; + + if (selectable) + { + assert(markers.size() == vecObjs.size()); + const auto count = std::min(markers.size(), vecObjs.size()); + for (auto i = (size_t)0; i < count; ++i) + { + auto *marker = markers[i]; + const auto &vecObj = vecObjs[i]; + + marker->isSelectable = true; + marker->selectID = Identifiable::genId(); + styleSet->addSelectionObject(marker->selectID, vecObj, compObj); + compObj->selectIDs.insert(marker->selectID); + compObj->isSelectable = true; + } + } + + if (!labels.empty()) + { + if (const auto labelID = styleSet->labelManage->addLabels(inst, labels, *labelInfo, tileInfo->changes)) + { + compObj->labelIDs.insert(labelID); + } + } + + if (!markers.empty()) + { + if (const auto markerID = styleSet->markerManage->addMarkers(markers, markerInfo, tileInfo->changes)) + { + compObj->markerIDs.insert(markerID); + } + } + + if (!compObj->labelIDs.empty() || !compObj->markerIDs.empty()) + { + styleSet->compManage->addComponentObject(compObj, tileInfo->changes); + tileInfo->compObjs.push_back(compObj); + } } } diff --git a/common/WhirlyGlobeLib/src/MapboxVectorTileParser.cpp b/common/WhirlyGlobeLib/src/MapboxVectorTileParser.cpp index 6e7d4e6723..960526d8f4 100644 --- a/common/WhirlyGlobeLib/src/MapboxVectorTileParser.cpp +++ b/common/WhirlyGlobeLib/src/MapboxVectorTileParser.cpp @@ -102,11 +102,22 @@ MapboxVectorTileParser::MapboxVectorTileParser(PlatformThreadInfo *inst,VectorSt MapboxVectorTileParser::~MapboxVectorTileParser() { } - -void MapboxVectorTileParser::setUUIDs(const std::string &name,const std::set &uuids) + +void MapboxVectorTileParser::setUUIDName(const std::string &name) { uuidName = name; - uuidValues = uuids; +} + +void MapboxVectorTileParser::setAttributeFilter(const std::string &name,const std::set &values) +{ + filterName = name; + filterValues = values; +} + +void MapboxVectorTileParser::setAttributeFilter(const std::string &name,std::set &&values) +{ + filterName = name; + filterValues = std::move(values); } void MapboxVectorTileParser::addCategory(const std::string &category,long long styleID) @@ -122,27 +133,33 @@ static inline double secondsSince(const std::chrono::steady_clock::time_point &t bool MapboxVectorTileParser::parse(PlatformThreadInfo *styleInst, RawData *rawData, VectorTileData *tileData, volatile bool *cancelBool) { - wkLogLevel(Verbose, "MapboxVectorTileParser: Parse [%d/%d/%d] starting", - tileData->ident.level, tileData->ident.x, tileData->ident.y); +//#if DEBUG +// wkLogLevel(Verbose, "MapboxVectorTileParser: Parse [%d/%d/%d] starting", +// tileData->ident.level, tileData->ident.x, tileData->ident.y); +//#endif const auto t0 = std::chrono::steady_clock::now(); - VectorTilePBFParser parser(tileData, &*styleDelegate, styleInst, uuidName, uuidValues, + VectorTilePBFParser parser(tileData, &*styleDelegate, styleInst, filterName, filterValues, tileData->vecObjsByStyle, localCoords, parseAll, keepVectors ? &tileData->vecObjs : nullptr, [=](){ return cancelBool && *cancelBool; }); if (!parser.parse(rawData->getRawData(), rawData->getLen())) { - if (parser.getParseCancelled()) { + if (parser.getParseCancelled()) + { const auto duration = secondsSince(t0); wkLogLevel(Verbose, "MapboxVectorTileParser: Cancelled [%d/%d/%d] - %.2f MiB - %.4f s", tileData->ident.level, tileData->ident.x, tileData->ident.y, rawData->getLen() / 1024.0 / 1024, duration); - } else { + } + else + { wkLogLevel(Warn, "MapboxVectorTileParser: Parse [%d/%d/%d] failed - '%s'", tileData->ident.level, tileData->ident.x, tileData->ident.y, parser.getErrorString("unknown").c_str()); #if DEBUG - if (parser.getTotalErrorCount() > 0) { + if (parser.getTotalErrorCount() > 0) + { wkLogLevel(Debug, "MapboxVectorTileParser: [%d/%d/%d] parse Errors: %d, Bad Attributes: %d, " "Unknown Commands: %d, Unknown Geom: %d, Unknown Value Types: %d", @@ -158,12 +175,14 @@ bool MapboxVectorTileParser::parse(PlatformThreadInfo *styleInst, RawData *rawDa return false; } +#if DEBUG const auto duration = std::max(1e-9, secondsSince(t0)); wkLogLevel(Verbose, "MapboxVectorTileParser: Finished [%d/%d/%d] - %.2f MiB - %.4f s - %.4f MiB/s - %.1f features/s", tileData->ident.level, tileData->ident.x, tileData->ident.y, rawData->getLen() / 1024.0 / 1024, duration, rawData->getLen() / duration / 1024 / 1024, parser.getFeatureCount() / duration); +#endif // TODO: Switch to stencils and get this working again // Call background @@ -176,21 +195,24 @@ bool MapboxVectorTileParser::parse(PlatformThreadInfo *styleInst, RawData *rawDa // } // Run the styles over their assembled data - for (auto it : tileData->vecObjsByStyle) { - std::vector *vecs = it.second; + for (const auto &it : tileData->vecObjsByStyle) + { + std::vector &vecs = *it.second; auto styleData = std::make_shared(*tileData); // Ask the subclass to run the style and fill in the VectorTileData - buildForStyle(styleInst,it.first,*vecs,styleData); - + buildForStyle(styleInst,it.first,vecs,styleData); + // Sort the results into categories if needed auto catIt = styleCategories.find(it.first); - if (catIt != styleCategories.end() && !styleData->compObjs.empty()) { + if (catIt != styleCategories.end() && !styleData->compObjs.empty()) + { const std::string &category = catIt->second; auto compObjs = styleData->compObjs; auto categoryIt = tileData->categories.find(category); - if (categoryIt != tileData->categories.end()) { + if (categoryIt != tileData->categories.end()) + { compObjs.insert(compObjs.end(), categoryIt->second.begin(), categoryIt->second.end()); } tileData->categories[category] = compObjs; @@ -249,12 +271,13 @@ bool MapboxVectorTileParser::parse(PlatformThreadInfo *styleInst, RawData *rawDa void MapboxVectorTileParser::buildForStyle(PlatformThreadInfo *styleInst, long long styleID, - std::vector &vecObjs, - VectorTileDataRef data) + const std::vector &vecObjs, + const VectorTileDataRef &data) { - VectorStyleImplRef style = styleDelegate->styleForUUID(styleInst,styleID); - if (style) - style->buildObjects(styleInst,vecObjs, data); + if (auto style = styleDelegate->styleForUUID(styleInst,styleID)) + { + style->buildObjects(styleInst,vecObjs,data,nullptr); + } } } diff --git a/common/WhirlyGlobeLib/src/MaplyView.cpp b/common/WhirlyGlobeLib/src/MaplyView.cpp index ba60d9cd02..f477c04de4 100644 --- a/common/WhirlyGlobeLib/src/MaplyView.cpp +++ b/common/WhirlyGlobeLib/src/MaplyView.cpp @@ -1,9 +1,8 @@ -/* - * MaplyView.h +/* MaplyView.cpp * WhirlyGlobeLib * * Created by Steve Gifford on 1/9/12. - * Copyright 2011-2019 mousebird consulting + * Copyright 2011-2021 mousebird consulting * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -15,7 +14,6 @@ * 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. - * */ #import "Platform.h" @@ -182,7 +180,7 @@ void MapView::setLoc(WhirlyKit::Point3d newLoc) setLoc(newLoc,true); } -void MapView::setLoc(WhirlyKit::Point3d &newLoc,bool runUpdates) +void MapView::setLoc(const WhirlyKit::Point3d &newLoc,bool runUpdates) { loc = newLoc; @@ -192,7 +190,7 @@ void MapView::setLoc(WhirlyKit::Point3d &newLoc,bool runUpdates) { if (loc.z() < heightInflection) { - double t = 1.0 - (heightInflection - loc.z()) / (heightInflection - absoluteMinHeight); + const double t = 1.0 - (heightInflection - loc.z()) / (heightInflection - absoluteMinHeight); nearPlane = t * (defaultNearPlane-absoluteMinNearPlane) + absoluteMinNearPlane; farPlane = loc.z()+nearPlane; } else { diff --git a/common/WhirlyGlobeLib/src/MarkerManager.cpp b/common/WhirlyGlobeLib/src/MarkerManager.cpp index 4ee27f0107..1fa51567a5 100644 --- a/common/WhirlyGlobeLib/src/MarkerManager.cpp +++ b/common/WhirlyGlobeLib/src/MarkerManager.cpp @@ -1,9 +1,8 @@ -/* - * MarkerManager.mm +/* MarkerManager.cpp * WhirlyGlobeLib * * Created by Steve Gifford on 7/16/13. - * Copyright 2011-2019 mousebird consulting. + * Copyright 2011-2021 mousebird consulting. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -15,7 +14,6 @@ * 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. - * */ #import "MarkerManager.h" @@ -58,7 +56,7 @@ MarkerSceneRep::MarkerSceneRep() { } -void MarkerSceneRep::enableContents(SelectionManager *selectManager,LayoutManager *layoutManager,bool enable,ChangeSet &changes) +void MarkerSceneRep::enableContents(SelectionManagerRef &selectManager,LayoutManagerRef &layoutManager,bool enable,ChangeSet &changes) { for (SimpleIDSet::iterator idIt = drawIDs.begin(); idIt != drawIDs.end(); ++idIt) @@ -71,7 +69,7 @@ void MarkerSceneRep::enableContents(SelectionManager *selectManager,LayoutManage layoutManager->enableLayoutObjects(screenShapeIDs, enable); } -void MarkerSceneRep::clearContents(SelectionManager *selectManager,LayoutManager *layoutManager,ChangeSet &changes,TimeInterval when) +void MarkerSceneRep::clearContents(SelectionManagerRef &selectManager,LayoutManagerRef &layoutManager,ChangeSet &changes,TimeInterval when) { // Just delete everything for (SimpleIDSet::iterator idIt = drawIDs.begin(); @@ -97,7 +95,8 @@ Marker::Marker() height(0), width(0), layoutHeight(-1.0), layoutWidth(-1.0), rotation(0), offset(0,0), period(0), - timeOffset(0), layoutImportance(MAXFLOAT), orderBy(-1) + timeOffset(0), layoutImportance(MAXFLOAT), orderBy(-1), + maskID(EmptyIdentity), maskRenderTargetID(EmptyIdentity) { } @@ -112,11 +111,14 @@ void Marker::addTexID(SimpleIdentity texID) } MarkerManager::MarkerManager() +: maskProgID(EmptyIdentity) { } MarkerManager::~MarkerManager() { + std::lock_guard guardLock(lock); + for (MarkerSceneRepSet::iterator it = markerReps.begin(); it != markerReps.end(); ++it) delete *it; @@ -127,10 +129,16 @@ typedef std::map DrawableMap; SimpleIdentity MarkerManager::addMarkers(const std::vector &markers,const MarkerInfo &markerInfo,ChangeSet &changes) { - SelectionManager *selectManager = (SelectionManager *)scene->getManager(kWKSelectionManager); - LayoutManager *layoutManager = (LayoutManager *)scene->getManager(kWKLayoutManager); - TimeInterval curTime = scene->getCurrentTime(); + auto selectManager = scene->getManager(kWKSelectionManager); + auto layoutManager = scene->getManager(kWKLayoutManager); + const TimeInterval curTime = scene->getCurrentTime(); + if (maskProgID == EmptyIdentity) { + Program *prog = scene->findProgramByName(MaplyScreenSpaceMaskShader); + if (prog) + maskProgID = prog->getId(); + } + CoordSystemDisplayAdapter *coordAdapter = scene->getCoordAdapter(); MarkerSceneRep *markerRep = new MarkerSceneRep(); markerRep->fadeOut = markerInfo.fadeOut; @@ -204,7 +212,7 @@ SimpleIdentity MarkerManager::addMarkers(const std::vector &markers,co shape->setPeriod(marker->period); - ScreenSpaceObject::ConvexGeometry smGeom; + ScreenSpaceConvexGeometry smGeom; for (unsigned int ii=0;ii &markers,co shape->addGeometry(smGeom); markerRep->screenShapeIDs.insert(shape->getId()); + // Handle the mask rendering if it's there + if (marker->maskID != EmptyIdentity && marker->maskRenderTargetID != EmptyIdentity) { + // Make a copy of the geometry, but target it to the mask render target + std::vector geom = shape->getGeometry(); + for (auto entry: geom) { + entry.vertexAttrs.insert(SingleVertexAttribute(a_maskNameID, renderer->getSlotForNameID(a_maskNameID), (int)marker->maskID)); + entry.renderTargetID = marker->maskRenderTargetID; + entry.progID = maskProgID; + shape->addGeometry(entry); + } + } + // Set up for the layout layer if (layoutImport < MAXFLOAT) { @@ -341,8 +361,8 @@ SimpleIdentity MarkerManager::addMarkers(const std::vector &markers,co TimeInterval now = scene->getCurrentTime(); std::vector texIDVec; std::copy(texIDs.begin(), texIDs.end(), std::back_inserter(texIDVec)); - BasicDrawableTexTweaker *tweak = new BasicDrawableTexTweaker(texIDVec,now,marker->period); - draw->addTweaker(DrawableTweakerRef(tweak)); + auto tweak = std::make_shared(texIDVec,now,marker->period); + draw->addTweaker(tweak); } } @@ -403,7 +423,7 @@ SimpleIdentity MarkerManager::addMarkers(const std::vector &markers,co SimpleIdentity markerID = markerRep->getId(); { - std::lock_guard guardLock(markerLock); + std::lock_guard guardLock(lock); markerReps.insert(markerRep); } @@ -412,10 +432,10 @@ SimpleIdentity MarkerManager::addMarkers(const std::vector &markers,co void MarkerManager::enableMarkers(SimpleIDSet &markerIDs,bool enable,ChangeSet &changes) { - SelectionManager *selectManager = (SelectionManager *)scene->getManager(kWKSelectionManager); - LayoutManager *layoutManager = (LayoutManager *)scene->getManager(kWKLayoutManager); + SelectionManagerRef selectManager = std::dynamic_pointer_cast(scene->getManager(kWKSelectionManager)); + LayoutManagerRef layoutManager = std::dynamic_pointer_cast(scene->getManager(kWKLayoutManager)); - std::lock_guard guardLock(markerLock); + std::lock_guard guardLock(lock); for (SimpleIDSet::iterator mit = markerIDs.begin();mit != markerIDs.end(); ++mit) { @@ -432,10 +452,10 @@ void MarkerManager::enableMarkers(SimpleIDSet &markerIDs,bool enable,ChangeSet & void MarkerManager::removeMarkers(SimpleIDSet &markerIDs,ChangeSet &changes) { - SelectionManager *selectManager = (SelectionManager *)scene->getManager(kWKSelectionManager); - LayoutManager *layoutManager = (LayoutManager *)scene->getManager(kWKLayoutManager); + SelectionManagerRef selectManager = std::dynamic_pointer_cast(scene->getManager(kWKSelectionManager)); + LayoutManagerRef layoutManager = std::dynamic_pointer_cast(scene->getManager(kWKLayoutManager)); - std::lock_guard guardLock(markerLock); + std::lock_guard guardLock(lock); TimeInterval curTime = scene->getCurrentTime(); for (SimpleIDSet::iterator mit = markerIDs.begin();mit != markerIDs.end(); ++mit) diff --git a/common/WhirlyGlobeLib/src/OverlapHelper.cpp b/common/WhirlyGlobeLib/src/OverlapHelper.cpp index ee635e6369..d54b99ce7e 100644 --- a/common/WhirlyGlobeLib/src/OverlapHelper.cpp +++ b/common/WhirlyGlobeLib/src/OverlapHelper.cpp @@ -1,9 +1,8 @@ -/* - * OverlapHelper.mm +/* OverlapHelper.cpp * WhirlyGlobeLib * * Created by Steve Gifford on 9/28/15. - * Copyright 2011-2019 mousebird consulting. + * Copyright 2011-2021 mousebird consulting. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -15,7 +14,6 @@ * 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. - * */ #import "OverlapHelper.h" @@ -36,7 +34,7 @@ OverlapHelper::OverlapHelper(const Mbr &mbr,int sizeX,int sizeY) } // Try to add an object. Might fail (kind of the whole point). -bool OverlapHelper::addObject(const Point2dVector &pts) +bool OverlapHelper::addCheckObject(const Point2dVector &pts) { Mbr objMbr; for (unsigned int ii=0;ii= sizeX) ex = sizeX-1; + int ey = ceilf((objMbr.ur().y()-mbr.ll().y())/cellSize.y()); + if (ey >= sizeY) ey = sizeY-1; + for (int ix=sx;ix<=ex;ix++) + for (int iy=sy;iy<=ey;iy++) + { + std::vector &objList = grid[iy*sizeX + ix]; + for (unsigned int ii=0;ii= sizeX) ex = sizeX-1; + int ey = ceilf((objMbr.ur().y()-mbr.ll().y())/cellSize.y()); + if (ey >= sizeY) ey = sizeY-1; + + // Okay, so it doesn't overlap. Let's add it where needed. + objects.resize(objects.size()+1); + int newId = (int)(objects.size()-1); + BoundedObject &newObj = objects[newId]; + newObj.pts = pts; + for (int ix=sx;ix<=ex;ix++) + for (int iy=sy;iy<=ey;iy++) + { + std::vector &objList = grid[iy*sizeX + ix]; + objList.push_back(newId); + } +} ClusterHelper::ObjectWithBounds::ObjectWithBounds() { @@ -154,7 +208,7 @@ void ClusterHelper::findObjectsWithin(const Mbr &mbr,std::set &objSet) } // Try to add an object. Might fail (kind of the whole point). -void ClusterHelper::addObject(LayoutObjectEntry *objEntry,const Point2dVector pts) +void ClusterHelper::addObject(LayoutObjectEntry *objEntry,const Point2dVector &pts) { // We'll add this one way or another simpleObjects.resize(simpleObjects.size()+1); diff --git a/common/WhirlyGlobeLib/src/ParticleSystemDrawableGLES.cpp b/common/WhirlyGlobeLib/src/ParticleSystemDrawableGLES.cpp index 24fd49b968..88d20ba0b3 100644 --- a/common/WhirlyGlobeLib/src/ParticleSystemDrawableGLES.cpp +++ b/common/WhirlyGlobeLib/src/ParticleSystemDrawableGLES.cpp @@ -36,7 +36,7 @@ ParticleSystemDrawableGLES::ParticleSystemDrawableGLES(const std::string &name) void ParticleSystemDrawableGLES::setupForRenderer(const RenderSetupInfo *inSetupInfo,Scene *scene) { - RenderSetupInfoGLES *setupInfo = (RenderSetupInfoGLES *)inSetupInfo; + auto setupInfo = (RenderSetupInfoGLES *)inSetupInfo; if (pointBuffer != 0) return; @@ -76,17 +76,17 @@ void ParticleSystemDrawableGLES::setupForRenderer(const RenderSetupInfo *inSetup } // If we have varyings we need buffers to hold them - for (auto varyAttr : varyAttrs) { - GLuint totalSize = varyAttr.size()*numTotalPoints; + for (const auto &varyAttr : varyAttrs) { + const GLuint totalSize = varyAttr.size()*numTotalPoints; - VaryBufferPair bufferPair; - for (unsigned int ii=0;ii<2;ii++) { - bufferPair.buffers[ii] = setupInfo->memManager->getBufferID(totalSize,GL_DYNAMIC_DRAW); + VaryBufferPair bufferPair = {0,0}; + for (auto &buffer : bufferPair.buffers) { + buffer = setupInfo->memManager->getBufferID(totalSize,GL_DYNAMIC_DRAW); // Zero out the new buffers // That's how we signal that they're new - glBindBuffer(GL_ARRAY_BUFFER, bufferPair.buffers[ii]); - void *glMem = NULL; + glBindBuffer(GL_ARRAY_BUFFER, buffer); + void *glMem = nullptr; glMem = glMapBufferRange(GL_ARRAY_BUFFER, 0, totalSize, GL_MAP_WRITE_BIT); memset(glMem, 0, totalSize); glUnmapBuffer(GL_ARRAY_BUFFER); @@ -114,7 +114,7 @@ void ParticleSystemDrawableGLES::setupForRenderer(const RenderSetupInfo *inSetup void ParticleSystemDrawableGLES::teardownForRenderer(const RenderSetupInfo *inSetupInfo,Scene *scene,RenderTeardownInfoRef teardown) { - RenderSetupInfoGLES *setupInfo = (RenderSetupInfoGLES *)inSetupInfo; + auto setupInfo = (RenderSetupInfoGLES *)inSetupInfo; if (pointBuffer) setupInfo->memManager->removeBufferID(pointBuffer); @@ -122,8 +122,8 @@ void ParticleSystemDrawableGLES::teardownForRenderer(const RenderSetupInfo *inSe if (rectBuffer) setupInfo->memManager->removeBufferID(rectBuffer); for (auto bufferPair : varyBuffers) - for (unsigned int ii=0;ii<2;ii++) - setupInfo->memManager->removeBufferID(bufferPair.buffers[ii]); + for (unsigned int buffer : bufferPair.buffers) + setupInfo->memManager->removeBufferID(buffer); varyBuffers.clear(); rectBuffer = 0; batches.clear(); @@ -139,7 +139,7 @@ void ParticleSystemDrawableGLES::addAttributeData(const RenderSetupInfo *setupIn // When the particles initialize themselves we don't have vertex data if (vertexSize > 0) { glBindBuffer(GL_ARRAY_BUFFER, pointBuffer); - unsigned char *glMem = NULL; + unsigned char *glMem = nullptr; int glMemOffset = 0; glMem = (unsigned char *)glMapBufferRange(GL_ARRAY_BUFFER, batch.batchID*vertexSize*batchSize, vertexSize*batchSize, GL_MAP_WRITE_BIT); @@ -150,7 +150,7 @@ void ParticleSystemDrawableGLES::addAttributeData(const RenderSetupInfo *setupIn const AttributeData &thisAttrData = attrData[ai]; SingleVertexAttributeInfo &attrInfo = vertAttrs[ai]; int attrSize = attrInfo.size(); - unsigned char *rawAttrData = (unsigned char *)thisAttrData.data; + auto rawAttrData = (unsigned char *)thisAttrData.data; unsigned char *ptr = glMem + attrOffset + glMemOffset; // Copy into each vertex for (unsigned int ii=0;ii glTexIDs; for (SimpleIdentity texID : texIDs) { GLuint glTexID = scene->getGLTexture(texID); - anyTextures = true; + //anyTextures = true; glTexIDs.push_back(glTexID); } @@ -252,7 +252,7 @@ void ParticleSystemDrawableGLES::drawSetupUniforms(RendererFrameInfo *frameInfo, prog->setUniform(u_frameLenID, (float)frameInfo->frameLen); } -void ParticleSystemDrawableGLES::drawBindAttrs(RendererFrameInfo *frameInfo,Scene *scene,ProgramGLES *prog,const BufferChunk &chunk,int vertexOffset,bool useInstancingHere) +void ParticleSystemDrawableGLES::drawBindAttrs(RendererFrameInfo *,Scene *,ProgramGLES *prog,const BufferChunk &chunk,int vertexOffset,bool useInstancingHere) { glBindBuffer(GL_ARRAY_BUFFER,pointBuffer); @@ -335,15 +335,14 @@ void ParticleSystemDrawableGLES::drawUnbindAttrs(ProgramGLES *prog) glVertexAttribDivisor(thisAttr->index, 0); } } - int which = 0; - for (SingleVertexAttributeInfo &varyInfo : varyAttrs) + for (const auto &name : varyNames) { - const OpenGLESAttribute *thisAttr = prog->findAttribute(varyNames[which]); - if (thisAttr) { + const OpenGLESAttribute *thisAttr = prog->findAttribute(name); + if (thisAttr) + { glDisableVertexAttribArray(thisAttr->index); glVertexAttribDivisor(thisAttr->index, 0); } - which++; } } @@ -358,7 +357,7 @@ void ParticleSystemDrawableGLES::calculate(RendererFrameInfoGLES *frameInfo,Scen if (chunks.empty()) return; - ProgramGLES *prog = (ProgramGLES *)frameInfo->program; + auto prog = (ProgramGLES *)frameInfo->program; if (!prog) return; @@ -394,7 +393,7 @@ void ParticleSystemDrawableGLES::calculate(RendererFrameInfoGLES *frameInfo,Scen glEndTransformFeedback(); CheckGLError("BasicDrawable::calculate() glEndTransformFeedback"); - for (int varyIdx = 0; varyIdx < varyAttrs.size(); varyIdx++) { + for (varyIdx = 0; varyIdx < varyAttrs.size(); varyIdx++) { glBindBufferBase(GL_TRANSFORM_FEEDBACK_BUFFER, varyIdx, 0); } @@ -441,7 +440,7 @@ void ParticleSystemDrawableGLES::draw(RendererFrameInfoGLES *frameInfo,Scene *sc if (chunks.empty()) return; - ProgramGLES *prog = (ProgramGLES *)frameInfo->program; + auto prog = (ProgramGLES *)frameInfo->program; // Sometimes the program is deleted before the drawable (oops) if (!prog) @@ -562,13 +561,13 @@ void main() } )"; -ProgramGLES *BuildParticleSystemProgramGLES(const std::string &name,SceneRenderer *renderer) +ProgramGLES *BuildParticleSystemProgramGLES(const std::string &name,SceneRenderer *) { - ProgramGLES *shader = new ProgramGLES(name,vertexShaderTri,fragmentShaderTri); + auto shader = new ProgramGLES(name,vertexShaderTri,fragmentShaderTri); if (!shader->isValid()) { delete shader; - shader = NULL; + shader = nullptr; } if (shader) diff --git a/common/WhirlyGlobeLib/src/ParticleSystemManager.cpp b/common/WhirlyGlobeLib/src/ParticleSystemManager.cpp index 466bcfc3c8..7d402a40cb 100644 --- a/common/WhirlyGlobeLib/src/ParticleSystemManager.cpp +++ b/common/WhirlyGlobeLib/src/ParticleSystemManager.cpp @@ -80,6 +80,8 @@ ParticleSystemManager::ParticleSystemManager() ParticleSystemManager::~ParticleSystemManager() { + std::lock_guard guardLock(lock); + for (auto it : sceneReps) delete it.second; sceneReps.clear(); @@ -125,7 +127,7 @@ SimpleIdentity ParticleSystemManager::addParticleSystem(const ParticleSystem &ne sceneRep->draws.insert(draw->getDrawable()); { - std::lock_guard guardLock(partSysLock); + std::lock_guard guardLock(lock); sceneReps[partSysID] = sceneRep; } @@ -134,7 +136,7 @@ SimpleIdentity ParticleSystemManager::addParticleSystem(const ParticleSystem &ne void ParticleSystemManager::enableParticleSystem(SimpleIdentity sysID,bool enable,ChangeSet &changes) { - std::lock_guard guardLock(partSysLock); + std::lock_guard guardLock(lock); auto it = sceneReps.find(sysID); if (it != sceneReps.end()) @@ -143,7 +145,7 @@ void ParticleSystemManager::enableParticleSystem(SimpleIdentity sysID,bool enabl void ParticleSystemManager::removeParticleSystem(SimpleIdentity sysID,ChangeSet &changes) { - std::lock_guard guardLock(partSysLock); + std::lock_guard guardLock(lock); auto it = sceneReps.find(sysID); if (it != sceneReps.end()) @@ -155,7 +157,7 @@ void ParticleSystemManager::removeParticleSystem(SimpleIdentity sysID,ChangeSet void ParticleSystemManager::addParticleBatch(SimpleIdentity sysID,const ParticleBatch &batch,ChangeSet &changes) { - std::lock_guard guardLock(partSysLock); + std::lock_guard guardLock(lock); // TimeInterval now = TimeGetCurrent(); @@ -195,7 +197,7 @@ void ParticleSystemManager::addParticleBatch(SimpleIdentity sysID,const Particle void ParticleSystemManager::changeRenderTarget(SimpleIdentity sysID,SimpleIdentity targetID,ChangeSet &changes) { - std::lock_guard guardLock(partSysLock); + std::lock_guard guardLock(lock); ParticleSystemSceneRep *sceneRep = NULL; auto it = sceneReps.find(sysID); @@ -215,7 +217,7 @@ void ParticleSystemManager::changeRenderTarget(SimpleIdentity sysID,SimpleIdenti void ParticleSystemManager::setUniformBlock(const SimpleIDSet &partSysIDs,const RawDataRef &uniBlock,int bufferID,ChangeSet &changes) { - std::lock_guard guardLock(partSysLock); + std::lock_guard guardLock(lock); for (auto sysID : partSysIDs) { auto it = sceneReps.find(sysID); diff --git a/common/WhirlyGlobeLib/src/PerformanceTimer.cpp b/common/WhirlyGlobeLib/src/PerformanceTimer.cpp index 3568415f0c..08e25c3649 100644 --- a/common/WhirlyGlobeLib/src/PerformanceTimer.cpp +++ b/common/WhirlyGlobeLib/src/PerformanceTimer.cpp @@ -1,9 +1,8 @@ -/* - * PerformanceTimer.mm +/* PerformanceTimer.cpp * WhirlyGlobeLib * * Created by Steve Gifford on 10/20/12. - * Copyright 2011-2019 mousebird consulting + * Copyright 2011-2021 mousebird consulting * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -15,7 +14,6 @@ * 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. - * */ #import diff --git a/common/WhirlyGlobeLib/src/QuadImageFrameLoader.cpp b/common/WhirlyGlobeLib/src/QuadImageFrameLoader.cpp index 8a9415635c..48a7a74c07 100644 --- a/common/WhirlyGlobeLib/src/QuadImageFrameLoader.cpp +++ b/common/WhirlyGlobeLib/src/QuadImageFrameLoader.cpp @@ -703,9 +703,10 @@ QuadImageFrameLoader::QuadImageFrameLoader(const SamplingParams ¶ms,Mode mod baseDrawPriority(100), drawPriorityPerLevel(1), colorChanged(false), color(RGBAColor(255,255,255,255)), - control(NULL), builder(NULL), + control(nullptr), + builder(nullptr), changesSinceLastFlush(true), - compManager(NULL), + compManager(nullptr), generation(0), targetLevel(-1), curOvlLevel(-1), loadingStatus(true) { @@ -785,6 +786,11 @@ void QuadImageFrameLoader::setLoadMode(LoadMode newMode) updatePriorityDefaults(); } +QuadImageFrameLoader::LoadMode QuadImageFrameLoader::getLoadMode() +{ + return loadMode; +} + bool QuadImageFrameLoader::getLoadingStatus() { return loadingStatus; @@ -833,7 +839,7 @@ int QuadImageFrameLoader::calcLoadPriority(const QuadTreeNew::ImportantNode &ide return restPriority; } -void QuadImageFrameLoader::setColor(RGBAColor &inColor,ChangeSet *changes) +void QuadImageFrameLoader::setColor(const RGBAColor &inColor,ChangeSet *changes) { color = inColor; @@ -904,8 +910,11 @@ void QuadImageFrameLoader::setDrawPriorityPerLevel(int newPrior) drawPriorityPerLevel = newPrior; } -void QuadImageFrameLoader::setCurFrame(int focusID,double inCurFrame) +void QuadImageFrameLoader::setCurFrame(PlatformThreadInfo *threadInfo,int focusID,double inCurFrame) { + if (curFrames[focusID] == inCurFrame) + return; + curFrames[focusID] = inCurFrame; } @@ -1015,6 +1024,23 @@ void QuadImageFrameLoader::reload(PlatformThreadInfo *threadInfo,int frameIndex, // We're not making any visual changes here, just messing with loading so no ChangeSet processBatchOps(threadInfo,batchOps.get()); } + +void QuadImageFrameLoader::updatePriorities(PlatformThreadInfo *threadInfo) +{ + // Work through the tiles and frames + for (const auto &it : tiles) { + const QIFTileAssetRef &tile = it.second; + + for (const auto &frame: tile->frames) { + if (tile->isFrameLoading(frame->getFrameInfo())) { + int newPriority = calcLoadPriority(tile->ident, frame->getFrameInfo()->frameIndex); + if (newPriority != frame->getPriority()) { + frame->updateFetching(threadInfo, this, newPriority, tile->ident.importance); + } + } + } + } +} QIFTileAssetRef QuadImageFrameLoader::addNewTile(PlatformThreadInfo *threadInfo,const QuadTreeNew::ImportantNode &ident,QIFBatchOps *batchOps,ChangeSet &changes) { @@ -1162,15 +1188,15 @@ void QuadImageFrameLoader::updateRenderState(ChangeSet &changes) } // Work through the tiles, figuring out textures and objects - for (auto tileIt : tiles) { - auto tileID = tileIt.first; - auto tile = tileIt.second; + for (const auto &tileIt : tiles) { + const auto tileID = tileIt.first; + const auto tile = tileIt.second; // Enable/disable the various visual objects - bool enable = tile->getShouldEnable() || !params.singleLevel; - auto compObjIDs = tile->getCompObjs(); + const bool enable = tile->getShouldEnable() || !params.singleLevel; + const auto compObjIDs = tile->getCompObjs(); if (!compObjIDs.empty()) - compManager->enableComponentObjects(compObjIDs, enable, changes); + compManager->enableComponentObjects(compObjIDs, enable, changes, /*resolveReps=*/true); // Only turn on the overlays if the Tile ID is one of the overlay level bool ovlEnable = enable && (tileID.level == curOvlLevel); @@ -1315,7 +1341,7 @@ void QuadImageFrameLoader::buildRenderState(ChangeSet &changes) auto theLastRunReqFlag = lastRunReqFlag; auto mergeReq = new RunBlockReq([this,newRenderState,theLastRunReqFlag](Scene *scene,SceneRenderer *renderer,View *view) { - if (theLastRunReqFlag) { + if (*theLastRunReqFlag) { if (builder) renderState = newRenderState; } @@ -1341,7 +1367,7 @@ void QuadImageFrameLoader::setBuilder(QuadTileBuilder *inBuilder,QuadDisplayCont { builder = inBuilder; control = inControl; - compManager = (ComponentManager *)control->getScene()->getManager(kWKComponentManager); + compManager = std::dynamic_pointer_cast(control->getScene()->getManager(kWKComponentManager)); } /// Before we tell the delegate to unload tiles, see if they want to keep them around @@ -1460,6 +1486,8 @@ void QuadImageFrameLoader::builderLoad(PlatformThreadInfo *threadInfo, removeTile(threadInfo,inTile, batchOps, changes); somethingChanged = true; } + + // Note: Not processing changes in importance builderLoadAdditional(threadInfo,builder,updates,changes); @@ -1543,6 +1571,9 @@ void QuadImageFrameLoader::updateForFrame(RendererFrameInfo *frameInfo) return; if (!control) return; + Scene *scene = control->getScene(); + if (!scene) + return; ChangeSet changes; @@ -1616,6 +1647,8 @@ void QuadImageFrameLoader::cleanup(PlatformThreadInfo *threadInfo,ChangeSet &cha processBatchOps(threadInfo,batchOps); delete batchOps; + + compManager.reset(); } bool QuadImageFrameLoader::isFrameLoading(const QuadTreeIdentifier &ident,QuadFrameInfoRef frame) diff --git a/common/WhirlyGlobeLib/src/QuadSamplingController.cpp b/common/WhirlyGlobeLib/src/QuadSamplingController.cpp index eed313d3de..3a9625c892 100644 --- a/common/WhirlyGlobeLib/src/QuadSamplingController.cpp +++ b/common/WhirlyGlobeLib/src/QuadSamplingController.cpp @@ -181,12 +181,15 @@ double QuadSamplingController::importanceForTile(const QuadTreeIdentifier &ident ViewStateRef viewState, const Point2f &frameSize) { + const auto coordAdapter = scene->getCoordAdapter(); // World spanning level 0 nodes sometimes have problems evaluating - if (params.minImportanceTop == 0.0 && ident.level == 0) + if (!coordAdapter || (params.minImportanceTop == 0.0 && ident.level == 0)) + { return MAXFLOAT; + } DisplaySolidRef dispSolid; - double import = ScreenImportance(viewState.get(), frameSize, viewState->eyeVec, 1, params.coordSys.get(), scene->getCoordAdapter(), mbr, ident, dispSolid); + double import = ScreenImportance(viewState.get(), frameSize, viewState->eyeVec, 1, params.coordSys.get(), coordAdapter, mbr, ident, dispSolid); return import; } diff --git a/common/WhirlyGlobeLib/src/QuadTreeNew.cpp b/common/WhirlyGlobeLib/src/QuadTreeNew.cpp index e6435a6bb2..46f4cc967d 100644 --- a/common/WhirlyGlobeLib/src/QuadTreeNew.cpp +++ b/common/WhirlyGlobeLib/src/QuadTreeNew.cpp @@ -1,9 +1,8 @@ -/* - * QuadTreeNew.mm +/* QuadTreeNew.cpp * WhirlyGlobeLib * - * Created by Steve Gifford on 3/26/18. - * Copyright 2012-2018 Saildrone Inc + * Created by Steve Gifford + * Copyright 2012-2021 mousebird consulting * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -15,11 +14,12 @@ * 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. - * */ #import "QuadTreeNew.h" +static constexpr int maxMaxLevel = 24; + namespace WhirlyKit { @@ -97,7 +97,7 @@ bool QuadTreeNew::ImportantNode::operator==(const WhirlyKit::QuadTreeNew::Import } QuadTreeNew::QuadTreeNew(const MbrD &mbr,int minLevel,int maxLevel) - : mbr(mbr), minLevel(minLevel), maxLevel(maxLevel) + : mbr(mbr), minLevel(minLevel), maxLevel(std::min(maxLevel, maxMaxLevel)) { } @@ -202,7 +202,7 @@ bool QuadTreeNew::evalNodeVisible(ImportantNode node,const std::vector & if (levelsToLoad.find(node.level) != levelsToLoad.end()) visibleSet.insert(node); - // Exceeded the number of nodes we can plausible load. Fail. + // Exceeded the number of nodes we can plausibly load. Fail. if (visibleSet.size() > maxNodes) return false; diff --git a/common/WhirlyGlobeLib/src/RawPNGImage.cpp b/common/WhirlyGlobeLib/src/RawPNGImage.cpp new file mode 100644 index 0000000000..b7a5ad7d2e --- /dev/null +++ b/common/WhirlyGlobeLib/src/RawPNGImage.cpp @@ -0,0 +1,74 @@ +/* + * RawPNGImage.cpp + * WhirlyGlobeLib + * + * Created by Steve Gifford on 12/3/20. + * Copyright 2011-2020 mousebird consulting + * + * 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. + * + */ + +#include +#include +#import "WhirlyKitLog.h" +#import "RawPNGImage.h" +#import "lodepng.h" + +namespace WhirlyKit +{ + +unsigned char *RawPNGImageLoaderInterpreter(unsigned int &width,unsigned int &height, + const unsigned char *data,size_t length, + const std::vector &valueMap, + int &byteWidth, + unsigned int &err) +{ + unsigned char *outData = NULL; + + try { + LodePNGState pngState; + lodepng_state_init(&pngState); + err = lodepng_inspect(&width, &height, &pngState, data, length); + if (pngState.info_png.color.colortype == LCT_GREY) { + byteWidth = 1; + err = lodepng_decode_memory(&outData, &width, &height, data, length, LCT_GREY, 8); + } else { + byteWidth = 4; + err = lodepng_decode_memory(&outData, &width, &height, data, length, LCT_RGBA, 8); + } + } + catch (const std::exception &ex) { + wkLogLevel(Error, "Exception in MaplyQuadImageLoader::dataForTile: %s", ex.what()); + err = -1; + } + catch (...) { + wkLogLevel(Error, "Exception in MaplyQuadImageLoader::dataForTile"); + err = -1; + } + + // Remap data values + if (byteWidth == 1 && !valueMap.empty()) { + unsigned char *data = outData; + for (unsigned int ii=0;ii= 0) + *data = newVal; + data++; + } + } + + return outData; +} + +} + diff --git a/common/WhirlyGlobeLib/src/Scene.cpp b/common/WhirlyGlobeLib/src/Scene.cpp index 2260b58ce4..2d0e461ee1 100644 --- a/common/WhirlyGlobeLib/src/Scene.cpp +++ b/common/WhirlyGlobeLib/src/Scene.cpp @@ -37,47 +37,72 @@ #import "LoftManager.h" #import "ParticleSystemManager.h" #import "BillboardManager.h" -#import "WideVectorManager.h" #import "GeometryManager.h" -#import "FontTextureManager.h" #import "ComponentManager.h" namespace WhirlyKit { - + +SceneManager::SceneManager() +: scene(nullptr), renderer(nullptr) +{ +} + +void SceneManager::setRenderer(SceneRenderer *inRenderer) +{ +// std::lock_guard guardLock(lock); // ? + renderer = inRenderer; +} + +void SceneManager::setScene(Scene *inScene) +{ +// std::lock_guard guardLock(lock); + scene = inScene; +} + +Scene *SceneManager::getScene() const +{ + return scene; +} + +SceneRenderer *SceneManager::getSceneRenderer() const +{ + return renderer; +} + Scene::Scene(CoordSystemDisplayAdapter *adapter) - : fontTextureManager(NULL), setupInfo(NULL), currentTime(0.0) + : setupInfo(nullptr) + , currentTime(0.0) + , coordAdapter(adapter) { SetupDrawableStrings(); - coordAdapter = adapter; - // Selection manager is used for object selection from any thread - addManager(kWKSelectionManager,new SelectionManager(this)); + addManager(kWKSelectionManager,std::make_shared(this)); // Intersection handling - addManager(kWKIntersectionManager, new IntersectionManager(this)); + addManager(kWKIntersectionManager, std::make_shared(this)); // Layout manager handles text and icon layout - addManager(kWKLayoutManager, new LayoutManager()); + addManager(kWKLayoutManager, std::make_shared()); // Shape manager handles circles, spheres and such - addManager(kWKShapeManager, new ShapeManager()); + addManager(kWKShapeManager, std::make_shared()); // Marker manager handles 2D and 3D markers - addManager(kWKMarkerManager, new MarkerManager()); + addManager(kWKMarkerManager, std::make_shared()); // Label manager handes 2D and 3D labels - addManager(kWKLabelManager, new LabelManager()); + addManager(kWKLabelManager, std::make_shared()); // Vector manager handes vector features - addManager(kWKVectorManager, new VectorManager()); + addManager(kWKVectorManager, std::make_shared()); // Chunk manager handles geographic chunks that cover a large chunk of the globe - addManager(kWKSphericalChunkManager, new SphericalChunkManager()); + addManager(kWKSphericalChunkManager, std::make_shared()); // Loft manager handles lofted polygon geometry - addManager(kWKLoftedPolyManager, new LoftManager()); + addManager(kWKLoftedPolyManager, std::make_shared()); // Particle system manager - addManager(kWKParticleSystemManager, new ParticleSystemManager()); + addManager(kWKParticleSystemManager, std::make_shared()); // 3D billboards - addManager(kWKBillboardManager, new BillboardManager()); + addManager(kWKBillboardManager, std::make_shared()); // Widened vectors - addManager(kWKWideVectorManager, new WideVectorManager()); + addManager(kWKWideVectorManager, std::make_shared()); // Raw Geometry - addManager(kWKGeometryManager, new GeometryManager()); + addManager(kWKGeometryManager, std::make_shared()); // Components (groups of things) addManager(kWKComponentManager, MakeComponentManager()); @@ -94,9 +119,6 @@ Scene::~Scene() textures.clear(); - for (std::map::iterator it = managers.begin(); - it != managers.end(); ++it) - delete it->second; managers.clear(); auto theChangeRequests = changeRequests; @@ -113,10 +135,10 @@ Scene::~Scene() programs.clear(); - fontTextureManager = NULL; + fontTextureManager = nullptr; } -CoordSystemDisplayAdapter *Scene::getCoordAdapter() +CoordSystemDisplayAdapter *Scene::getCoordAdapter() const { return coordAdapter; } @@ -152,22 +174,19 @@ void Scene::addChangeRequest(ChangeRequest *newChange) changeRequests.push_back(newChange); } -int Scene::getNumChangeRequests() +int Scene::getNumChangeRequests() const { std::lock_guard guardLock(changeRequestLock); return changeRequests.size(); } -DrawableRef Scene::getDrawable(SimpleIdentity drawId) +DrawableRef Scene::getDrawable(SimpleIdentity drawId) const { std::lock_guard guardLock(drawablesLock); - auto it = drawables.find(drawId); - if (it != drawables.end()) - return it->second; - - return DrawableRef(); + const auto it = drawables.find(drawId); + return (it != drawables.end()) ? it->second : DrawableRef(); } void Scene::addLocalMbr(const Mbr &localMbr) @@ -178,61 +197,58 @@ void Scene::addLocalMbr(const Mbr &localMbr) // Note: This will only get bigger, never smaller if (localMbr.ll().x() < ll.x() && localMbr.ur().x() > ll.x()) { - double dx1 = ll.x() - localMbr.ll().x(); - double dx2 = localMbr.ur().x() - ll.x(); - double dx = std::max(dx1,dx2); - overlapMargin = std::max(overlapMargin,dx); + const double dx1 = ll.x() - localMbr.ll().x(); + const double dx2 = localMbr.ur().x() - ll.x(); + overlapMargin = std::max(overlapMargin, std::max(dx1, dx2)); } } - + void Scene::setRenderer(SceneRenderer *renderer) { setupInfo = renderer->getRenderSetupInfo(); std::lock_guard guardLock(managerLock); - - for (std::map::iterator it = managers.begin(); - it != managers.end(); ++it) - it->second->setRenderer(renderer); + + for (const auto &kvp : managers) + { + kvp.second->setRenderer(renderer); + } } -SceneManager *Scene::getManager(const char *name) +SceneManagerRef Scene::getManager(const std::string &name) { std::lock_guard guardLock(managerLock); - return getManagerNoLock(name); } -SceneManager *Scene::getManagerNoLock(const char *name) +SceneManagerRef Scene::getManagerNoLock(const std::string &name) { - SceneManager *ret = NULL; - - std::map::iterator it = managers.find((std::string)name); - if (it != managers.end()) - ret = it->second; - - return ret; + const auto it = managers.find(name); + return (it != managers.end()) ? it->second : SceneManagerRef(); } -void Scene::addManager(const char *name,SceneManager *manager) +void Scene::addManager(const std::string &name,const SceneManagerRef &manager) { std::lock_guard guardLock(managerLock); // If there's one here, we'll clear it out first - std::map::iterator it = managers.find((std::string)name); - if (it != managers.end()) - managers.erase(it); - managers[(std::string)name] = manager; + const auto result = managers.insert(std::make_pair(name, manager)); + if (!result.second) + { + // Entry was already present, replace it. + // Previous manager reference is released, possibly destroying it. + result.first->second = manager; + } manager->setScene(this); } void Scene::addActiveModel(ActiveModelRef activeModel) { - activeModels.push_back(activeModel); - activeModel->startWithScene(this); + activeModels.emplace_back(std::move(activeModel)); + activeModels.back()->startWithScene(this); } - -void Scene::removeActiveModel(ActiveModelRef activeModel) + +void Scene::removeActiveModel(const ActiveModelRef &activeModel) { int which = 0; @@ -247,20 +263,16 @@ void Scene::removeActiveModel(ActiveModelRef activeModel) activeModel->teardown(); } } - -TextureBaseRef Scene::getTexture(SimpleIdentity texId) + +TextureBaseRef Scene::getTexture(SimpleIdentity texId) const { std::lock_guard guardLock(textureLock); - TextureBaseRef retTex; - auto it = textures.find(texId); - if (it != textures.end()) - retTex = it->second; - - return retTex; + const auto it = textures.find(texId); + return (it != textures.end()) ? it->second : TextureBaseRef(); } - -const std::vector Scene::getDrawables() + +const std::vector Scene::getDrawables() const { std::vector retDraws; @@ -280,23 +292,19 @@ void Scene::setCurrentTime(TimeInterval newTime) void Scene::markProgramsUnchanged() { std::lock_guard guardLock(programLock); - + for (auto it: programs) { auto prog = it.second; - if (prog->changed) - prog->changed = false; + prog->changed = false; } } -TimeInterval Scene::getCurrentTime() +TimeInterval Scene::getCurrentTime() const { - if (currentTime == 0.0) - return TimeGetCurrent(); - - return currentTime; + return (currentTime == 0.0) ? TimeGetCurrent() : currentTime; } -TimeInterval Scene::getBaseTime() +TimeInterval Scene::getBaseTime() const { return baseTime; } @@ -361,7 +369,7 @@ int Scene::processChanges(WhirlyKit::View *view,SceneRenderer *renderer,TimeInte return numChanges; } -bool Scene::hasChanges(TimeInterval now) +bool Scene::hasChanges(TimeInterval now) const { bool changes = false; if (changeRequestLock.try_lock()) @@ -423,16 +431,18 @@ void Scene::removeSubTextures(const std::vector &subTexIDs) } // Look for a sub texture by ID -SubTexture Scene::getSubTexture(SimpleIdentity subTexId) +SubTexture Scene::getSubTexture(SimpleIdentity subTexId) const { - std::lock_guard guardLock(subTexLock); SubTexture dumbTex; dumbTex.setId(subTexId); - SubTextureSet::iterator it = subTextureMap.find(dumbTex); + + std::lock_guard guardLock(subTexLock); + + const auto it = subTextureMap.find(dumbTex); if (it == subTextureMap.end()) { SubTexture passTex; - passTex.trans = passTex.trans.Identity(); + passTex.trans = decltype(passTex.trans)::Identity(); passTex.texId = subTexId; return passTex; } @@ -476,7 +486,7 @@ bool Scene::removeTexture(SimpleIdentity texID) return false; } -void Scene::dumpStats() +void Scene::dumpStats() const { wkLogLevel(Verbose,"Scene: %ld drawables",drawables.size()); wkLogLevel(Verbose,"Scene: %d active models",(int)activeModels.size()); @@ -484,7 +494,7 @@ void Scene::dumpStats() wkLogLevel(Verbose,"Scene: %ld sub textures",subTextureMap.size()); } -void Scene::setFontTextureManager(FontTextureManagerRef newManager) +void Scene::setFontTextureManager(const FontTextureManagerRef &newManager) { fontTextureManager = newManager; } @@ -529,7 +539,7 @@ void Scene::addProgram(ProgramRef prog) programs[prog->getId()] = prog; } -void Scene::removeProgram(SimpleIdentity progId,RenderTeardownInfoRef teardown) +void Scene::removeProgram(SimpleIdentity progId,const RenderTeardownInfoRef & /*teardown*/) { std::lock_guard guardLock(programLock); @@ -571,21 +581,17 @@ void Scene::setZoomSlotValue(int zoomSlot,float zoom) zoomSlots[zoomSlot] = zoom; } -float Scene::getZoomSlotValue(int zoomSlot) +float Scene::getZoomSlotValue(int zoomSlot) const { std::lock_guard guardLock(zoomSlotLock); - - if (zoomSlot < 0 || zoomSlot >= MaplyMaxZoomSlots) - return 0.0; - return zoomSlots[zoomSlot]; + return (zoomSlot < 0 || zoomSlot >= MaplyMaxZoomSlots) ? 0.0 : zoomSlots[zoomSlot]; } void Scene::copyZoomSlots(float *dest) { std::lock_guard guardLock(zoomSlotLock); - - memcpy(dest, &zoomSlots[0], sizeof(float)*MaplyMaxZoomSlots); + std::copy(&zoomSlots[0], &zoomSlots[MaplyMaxZoomSlots], dest); } void AddTextureReq::setupForRenderer(const RenderSetupInfo *setupInfo,Scene *scene) @@ -594,21 +600,21 @@ void AddTextureReq::setupForRenderer(const RenderSetupInfo *setupInfo,Scene *sce texRef->createInRenderer(setupInfo); } -TextureBase *AddTextureReq::getTex() +TextureBase *AddTextureReq::getTex() const { return texRef.get(); } AddTextureReq::~AddTextureReq() { - texRef = NULL; + texRef = nullptr; } void AddTextureReq::execute(Scene *scene,SceneRenderer *renderer,WhirlyKit::View *view) { texRef->createInRenderer(renderer->getRenderSetupInfo()); scene->addTexture(texRef); - texRef = NULL; + texRef = nullptr; } void RemTextureReq::execute(Scene *scene,SceneRenderer *renderer,WhirlyKit::View *view) @@ -617,10 +623,14 @@ void RemTextureReq::execute(Scene *scene,SceneRenderer *renderer,WhirlyKit::View if (tex) { if (renderer->teardownInfo) + { renderer->teardownInfo->destroyTexture(renderer,tex); + } scene->removeTexture(texture); } else + { wkLogLevel(Warn,"RemTextureReq: No such texture."); + } } void AddDrawableReq::setupForRenderer(const RenderSetupInfo *setupInfo,Scene *scene) @@ -635,20 +645,22 @@ void AddDrawableReq::setupForRenderer(const RenderSetupInfo *setupInfo,Scene *sc AddDrawableReq::~AddDrawableReq() { - drawRef = NULL; + drawRef = nullptr; } void AddDrawableReq::execute(Scene *scene,SceneRenderer *renderer,WhirlyKit::View *view) { // If this is an instance, deal with that madness - BasicDrawableInstance *drawInst = dynamic_cast(drawRef.get()); - if (drawInst) + if (auto drawInst = dynamic_cast(drawRef.get())) { - DrawableRef theDraw = scene->getDrawable(drawInst->getMasterID()); - BasicDrawableRef baseDraw = std::dynamic_pointer_cast(theDraw); - if (baseDraw) + const auto theDraw = scene->getDrawable(drawInst->getMasterID()); + if (const auto baseDraw = std::dynamic_pointer_cast(theDraw)) + { drawInst->setMaster(baseDraw); - else { + } + else + { + wkLogLevel(Error,"Found BasicDrawableInstance without masterID. Dropping."); return; } } @@ -659,7 +671,7 @@ void AddDrawableReq::execute(Scene *scene,SceneRenderer *renderer,WhirlyKit::Vie if (drawRef->getLocalMbr().valid()) scene->addLocalMbr(drawRef->getLocalMbr()); - drawRef = NULL; + drawRef = nullptr; } RemDrawableReq::RemDrawableReq(SimpleIdentity drawId) : drawID(drawId) @@ -679,14 +691,17 @@ void RemDrawableReq::execute(Scene *scene,SceneRenderer *renderer,WhirlyKit::Vie { renderer->removeDrawable(draw, true, renderer->teardownInfo); scene->remDrawable(draw); - } else + } + else + { wkLogLevel(Warn,"Missing drawable for RemDrawableReq: %llu", drawID); + } } void AddProgramReq::execute(Scene *scene,SceneRenderer *renderer,WhirlyKit::View *view) { scene->addProgram(program); - program = NULL; + program = nullptr; } void RemProgramReq::execute(Scene *scene,SceneRenderer *renderer,WhirlyKit::View *view) @@ -697,11 +712,7 @@ void RemProgramReq::execute(Scene *scene,SceneRenderer *renderer,WhirlyKit::View RunBlockReq::RunBlockReq(BlockFunc newFunc) : func(newFunc) { } - -RunBlockReq::~RunBlockReq() -{ -} - + void RunBlockReq::execute(Scene *scene,SceneRenderer *renderer,WhirlyKit::View *view) { func(scene,renderer,view); diff --git a/common/WhirlyGlobeLib/src/SceneGLES.cpp b/common/WhirlyGlobeLib/src/SceneGLES.cpp index 95924bcc24..c86ef8dbea 100644 --- a/common/WhirlyGlobeLib/src/SceneGLES.cpp +++ b/common/WhirlyGlobeLib/src/SceneGLES.cpp @@ -1,9 +1,8 @@ -/* - * SceneGLES.cpp +/* SceneGLES.cpp * WhirlyGlobeLib * * Created by Steve Gifford on 5/14/19. - * Copyright 2011-2019 mousebird consulting + * Copyright 2011-2021 mousebird consulting * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -15,11 +14,11 @@ * 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. - * */ #import "SceneGLES.h" #import "TextureGLES.h" +#import "FontTextureManager.h" namespace WhirlyKit { @@ -48,10 +47,10 @@ GLuint SceneGLES::getGLTexture(SimpleIdentity texIdent) return ret; } -void SceneGLES::teardown() +void SceneGLES::teardown(PlatformThreadInfo* threadInfo) { for (auto it : drawables) - it.second->teardownForRenderer(setupInfo,this, NULL); + it.second->teardownForRenderer(setupInfo,this, nullptr); drawables.clear(); for (auto it : textures) { it.second->destroyInRenderer(setupInfo,this); @@ -60,6 +59,11 @@ void SceneGLES::teardown() memManager.clearBufferIDs(); memManager.clearTextureIDs(); + + if (fontTextureManager) + { + fontTextureManager->teardown(threadInfo); + } } } diff --git a/common/WhirlyGlobeLib/src/SceneRenderer.cpp b/common/WhirlyGlobeLib/src/SceneRenderer.cpp index 5daeb36376..0a66fecb9e 100644 --- a/common/WhirlyGlobeLib/src/SceneRenderer.cpp +++ b/common/WhirlyGlobeLib/src/SceneRenderer.cpp @@ -487,6 +487,14 @@ bool SceneRenderer::hasChanges() return frameCount - frameCountLastChanged <= extraFrames; } +int SceneRenderer::getSlotForNameID(SimpleIdentity nameID) +{ + auto it = slotMap.find(nameID); + if (it == slotMap.end()) + return -1; + return it->second; +} + void SceneRenderer::shutdown() { offDrawables.clear(); diff --git a/common/WhirlyGlobeLib/src/SceneRendererGLES.cpp b/common/WhirlyGlobeLib/src/SceneRendererGLES.cpp index ccac2c152d..217a41ae4d 100644 --- a/common/WhirlyGlobeLib/src/SceneRendererGLES.cpp +++ b/common/WhirlyGlobeLib/src/SceneRendererGLES.cpp @@ -1,9 +1,8 @@ -/* - * SceneRendererGLES.cpp +/* SceneRendererGLES.cpp * WhirlyGlobeLib * * Created by Steve Gifford on 5/13/19. - * Copyright 2011-2019 mousebird consulting + * Copyright 2011-2021 mousebird consulting * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -15,7 +14,6 @@ * 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. - * */ #import "SceneRendererGLES.h" @@ -27,7 +25,6 @@ #import "ScreenSpaceDrawableBuilderGLES.h" #import "WideVectorDrawableBuilderGLES.h" #import "ParticleSystemDrawableBuilderGLES.h" -#import "RenderTargetGLES.h" #import "DynamicTextureAtlasGLES.h" #import "MaplyView.h" #import "WhirlyKitLog.h" @@ -45,12 +42,11 @@ WorkGroupGLES::WorkGroupGLES(GroupType inGroupType) switch (groupType) { case Calculation: // For calculation we don't really have a render target - renderTargetContainers.push_back(WorkGroupGLES::makeRenderTargetContainer(NULL)); + renderTargetContainers.push_back(WorkGroupGLES::makeRenderTargetContainer(nullptr)); break; + default: case Offscreen: - break; case ReduceOps: - break; case ScreenRender: break; } @@ -58,26 +54,27 @@ WorkGroupGLES::WorkGroupGLES(GroupType inGroupType) RenderTargetContainerRef WorkGroupGLES::makeRenderTargetContainer(RenderTargetRef renderTarget) { - return RenderTargetContainerRef(new RenderTargetContainerGLES(renderTarget)); + return std::make_shared(renderTarget); } RendererFrameInfoGLES::RendererFrameInfoGLES() -: glesVersion(0) + : glesVersion(0) { } -SceneRendererGLES::SceneRendererGLES() +SceneRendererGLES::SceneRendererGLES() : + extraFrameCount(0) { - init(); + init(); // NOLINT: derived virtual methods not called // Calculation shaders - workGroups.push_back(WorkGroupRef(new WorkGroupGLES(WorkGroup::Calculation))); + workGroups.emplace_back(std::make_shared(WorkGroup::Calculation)); // Offscreen target render group - workGroups.push_back(WorkGroupRef(new WorkGroupGLES(WorkGroup::Offscreen))); + workGroups.emplace_back(std::make_shared(WorkGroup::Offscreen)); // Middle one for weird stuff - workGroups.push_back(WorkGroupRef(new WorkGroupGLES(WorkGroup::ReduceOps))); + workGroups.emplace_back(std::make_shared(WorkGroup::ReduceOps)); // Last workgroup is used for on screen rendering - workGroups.push_back(WorkGroupRef(new WorkGroupGLES(WorkGroup::ScreenRender))); + workGroups.emplace_back(std::make_shared(WorkGroup::ScreenRender)); extraFrameMode = false; } @@ -101,9 +98,9 @@ bool SceneRendererGLES::setup(int apiVersion,int sizeX,int sizeY,float inScale) zBufferMode = zBufferOn; clearColor.r = 0; clearColor.g = 0; clearColor.b = 0; clearColor.a = 0; perfInterval = -1; - scene = NULL; + scene = nullptr; scale = inScale; - theView = NULL; + theView = nullptr; // All the animations should work now, except for particle systems useViewChanged = true; @@ -123,11 +120,11 @@ bool SceneRendererGLES::setup(int apiVersion,int sizeX,int sizeY,float inScale) framebufferTexGL->setHeight(framebufferHeight); framebufferTexGL->setIsEmptyTexture(true); framebufferTexGL->setFormat(TexTypeUnsignedByte); - framebufferTexGL->createInRenderer(NULL); + framebufferTexGL->createInRenderer(nullptr); framebufferTex = framebufferTexGL; } - RenderTargetGLESRef defaultTarget = RenderTargetGLESRef(new RenderTargetGLES(EmptyIdentity)); + auto defaultTarget = std::make_shared(EmptyIdentity); defaultTarget->width = sizeX; defaultTarget->height = sizeY; if (framebufferTex) { @@ -136,7 +133,7 @@ bool SceneRendererGLES::setup(int apiVersion,int sizeX,int sizeY,float inScale) defaultTarget->blendEnable = false; } else { if (sizeX > 0 && sizeY > 0) - defaultTarget->init(this,NULL,EmptyIdentity); + defaultTarget->init(this,nullptr,EmptyIdentity); defaultTarget->blendEnable = true; } defaultTarget->clearEveryFrame = true; @@ -157,7 +154,7 @@ void SceneRendererGLES::setView(View *newView) void SceneRendererGLES::setScene(Scene *newScene) { SceneRenderer::setScene(newScene); - SceneGLES *sceneGL = (SceneGLES *)newScene; + auto *sceneGL = (SceneGLES *)newScene; setupInfo.memManager = sceneGL->getMemManager(); } @@ -173,23 +170,34 @@ bool SceneRendererGLES::resize(int sizeX,int sizeY) RenderTargetRef defaultTarget = renderTargets.back(); defaultTarget->width = sizeX; defaultTarget->height = sizeY; - defaultTarget->init(this, NULL, EmptyIdentity); + defaultTarget->init(this, nullptr, EmptyIdentity); // Note: Check this return true; } -SceneRendererGLES::~SceneRendererGLES() -{ -} +SceneRendererGLES::~SceneRendererGLES() = default; // Keep track of a drawable and the MVP we're supposed to use with it class DrawableContainer { public: - DrawableContainer(DrawableGLES *draw) : drawable(draw) { mvpMat = mvpMat.Identity(); mvMat = mvMat.Identity(); mvNormalMat = mvNormalMat.Identity(); } - DrawableContainer(DrawableGLES *draw,Matrix4d mvpMat,Matrix4d mvMat,Matrix4d mvNormalMat) : drawable(draw), mvpMat(mvpMat), mvMat(mvMat), mvNormalMat(mvNormalMat) { } - + explicit DrawableContainer(DrawableGLES *draw) : + drawable(draw), + mvpMat(Eigen::Matrix4d::Identity()), + mvMat(Eigen::Matrix4d::Identity()), + mvNormalMat(Eigen::Matrix4d::Identity()) + { + } + + DrawableContainer(DrawableGLES *draw,Matrix4d mvpMat,Matrix4d mvMat,Matrix4d mvNormalMat) : + drawable(draw), + mvpMat(std::move(mvpMat)), + mvMat(std::move(mvMat)), + mvNormalMat(std::move(mvNormalMat)) + { + } + DrawableGLES *drawable; Matrix4d mvpMat,mvMat,mvNormalMat; }; @@ -199,30 +207,31 @@ class DrawableContainer class DrawListSortStruct2 { public: + DrawListSortStruct2() = delete; DrawListSortStruct2(bool useZBuffer,RendererFrameInfo *frameInfo) : useZBuffer(useZBuffer), frameInfo(frameInfo) { } - DrawListSortStruct2() { } - DrawListSortStruct2(const DrawListSortStruct2 &that) : useZBuffer(that.useZBuffer), frameInfo(that.frameInfo) + DrawListSortStruct2(const DrawListSortStruct2 &that) = default; + DrawListSortStruct2 & operator =(const DrawListSortStruct2 &that) { - } - DrawListSortStruct2 & operator = (const DrawListSortStruct2 &that) - { - useZBuffer= that.useZBuffer; - frameInfo = that.frameInfo; + if (this != &that) + { + useZBuffer= that.useZBuffer; + frameInfo = that.frameInfo; + } return *this; } - bool operator()(const DrawableContainer &conA, const DrawableContainer &conB) + bool operator()(const DrawableContainer &conA, const DrawableContainer &conB) const { - Drawable *a = conA.drawable; - Drawable *b = conB.drawable; + const Drawable *a = conA.drawable; + const Drawable *b = conB.drawable; if (a->getDrawPriority() == b->getDrawPriority()) { if (useZBuffer) { - bool bufferA = a->getRequestZBuffer(); - bool bufferB = b->getRequestZBuffer(); + const bool bufferA = a->getRequestZBuffer(); + const bool bufferB = b->getRequestZBuffer(); if (bufferA != bufferB) return !bufferA; } @@ -234,7 +243,7 @@ class DrawListSortStruct2 bool useZBuffer; RendererFrameInfo *frameInfo; }; - + void SceneRendererGLES::setExtraFrameMode(bool newMode) { extraFrameMode = newMode; @@ -274,10 +283,10 @@ void SceneRendererGLES::render(TimeInterval duration) } // See if we're dealing with a globe or map view - Maply::MapView *mapView = dynamic_cast(theView); float overlapMarginX = 0.0; - if (mapView) { - overlapMarginX = scene->getOverlapMargin(); + if (auto mapView = dynamic_cast(theView)) + { + overlapMarginX = (float)scene->getOverlapMargin(); } // Get the model and view matrices @@ -350,8 +359,7 @@ void SceneRendererGLES::render(TimeInterval duration) baseFrameInfo.viewModelNormalMat = modelAndViewNormalMat; baseFrameInfo.viewAndModelMat = modelAndViewMat; baseFrameInfo.viewAndModelMat4d = modelAndViewMat4d; - Matrix4f pvMat4f = Matrix4dToMatrix4f(pvMat); - baseFrameInfo.pvMat = pvMat4f; + baseFrameInfo.pvMat = Matrix4dToMatrix4f(pvMat); baseFrameInfo.pvMat4d = pvMat; theView->getOffsetMatrices(baseFrameInfo.offsetMatrices, frameSize, overlapMarginX); Point2d screenSize = theView->screenSizeInDisplayCoords(frameSize); @@ -370,7 +378,7 @@ void SceneRendererGLES::render(TimeInterval duration) baseFrameInfo.fullEyeVec = -fullEyeVec3; Vector4d eyeVec4d = modelTrans4d.inverse() * Vector4d(0,0,1,0.0); baseFrameInfo.heightAboveSurface = 0.0; - baseFrameInfo.heightAboveSurface = theView->heightAboveSurface(); + baseFrameInfo.heightAboveSurface = (float)theView->heightAboveSurface(); baseFrameInfo.eyePos = Vector3d(eyeVec4d.x(),eyeVec4d.y(),eyeVec4d.z()) * (1.0+baseFrameInfo.heightAboveSurface); if (perfInterval > 0) @@ -391,7 +399,7 @@ void SceneRendererGLES::render(TimeInterval duration) // Let the active models to their thing // That thing had better not take too long auto activeModels = scene->getActiveModels(); - for (auto activeModel : activeModels) { + for (const auto &activeModel : activeModels) { activeModel->updateForFrame(&baseFrameInfo); // Note: We were setting the GL context here. Do we need to? } @@ -430,7 +438,7 @@ void SceneRendererGLES::render(TimeInterval duration) bool calcPassDone = false; for (unsigned int off=0;offgetDrawables(); for (auto draw : rawDrawables) { - DrawableGLES *theDrawable = dynamic_cast(draw); + auto *theDrawable = dynamic_cast(draw); if (theDrawable->isOn(&offFrameInfo)) { const Matrix4d *localMat = theDrawable->getMatrix(); @@ -465,9 +472,9 @@ void SceneRendererGLES::render(TimeInterval duration) Eigen::Matrix4d newMvpMat = thisMvpMat * (*localMat); Eigen::Matrix4d newMvMat = modelAndViewMat4d * (*localMat); Eigen::Matrix4d newMvNormalMat = newMvMat.inverse().transpose(); - drawList.push_back(DrawableContainer(theDrawable,newMvpMat,newMvMat,newMvNormalMat)); + drawList.emplace_back(theDrawable,newMvpMat,newMvMat,newMvNormalMat); } else - drawList.push_back(DrawableContainer(theDrawable,thisMvpMat,modelAndViewMat4d,modelAndViewNormalMat4d)); + drawList.emplace_back(theDrawable,thisMvpMat,modelAndViewMat4d,modelAndViewNormalMat4d); } } } @@ -504,7 +511,7 @@ void SceneRendererGLES::render(TimeInterval duration) // Figure out the program to use for drawing if (calcProgID == EmptyIdentity) continue; - ProgramGLES *program = (ProgramGLES *)scene->getProgram(calcProgID); + auto *program = (ProgramGLES *)scene->getProgram(calcProgID); if (program) { glUseProgram(program->getProgram()); @@ -533,7 +540,7 @@ void SceneRendererGLES::render(TimeInterval duration) SimpleIdentity curProgramId = EmptyIdentity; // Iterate through rendering targets here - for (RenderTargetRef inRenderTarget : renderTargets) + for (const RenderTargetRef &inRenderTarget : renderTargets) { RenderTargetGLESRef renderTarget = std::dynamic_pointer_cast(inRenderTarget); @@ -591,13 +598,13 @@ void SceneRendererGLES::render(TimeInterval duration) if (drawProgramId != curProgramId) { curProgramId = drawProgramId; - ProgramGLES *program = (ProgramGLES *)scene->getProgram(drawProgramId); + auto program = (ProgramGLES *)scene->getProgram(drawProgramId); if (program) { // [renderStateOptimizer setUseProgram:program->getProgram()]; glUseProgram(program->getProgram()); // Assign the lights if we need to - if (program->hasLights() && (lights.size() > 0)) + if (program->hasLights() && !lights.empty()) program->setLights(lights, lightsLastUpdated, &defaultMat, currentMvpMat); // Explicitly turn the lights on program->setUniform(u_numLightsNameID, (int)lights.size()); @@ -674,10 +681,10 @@ void SceneRendererGLES::render(TimeInterval duration) // Update the frames per sec if (perfInterval > 0 && frameCount > perfInterval) { - TimeInterval now = scene->getCurrentTime(); - TimeInterval howLong = now - frameCountStart;; - framesPerSec = frameCount / howLong; - frameCountStart = now; + const TimeInterval newNow = scene->getCurrentTime(); + const TimeInterval howLong = newNow - frameCountStart; + framesPerSec = (float)(frameCount / howLong); + frameCountStart = newNow; frameCount = 0; wkLogLevel(Verbose,"---Rendering Performance---"); @@ -689,7 +696,7 @@ void SceneRendererGLES::render(TimeInterval duration) RawDataRef SceneRendererGLES::getSnapshotAt(SimpleIdentity renderTargetID, int x, int y, int width, int height) { - for (auto renderTarget: renderTargets) { + for (const auto &renderTarget: renderTargets) { if (renderTarget->getId() == renderTargetID) { if (width <= 0 || height <= 0) { return renderTarget->snapshot(); @@ -705,43 +712,45 @@ RawDataRef SceneRendererGLES::getSnapshotAt(SimpleIdentity renderTargetID, int x BasicDrawableBuilderRef SceneRendererGLES::makeBasicDrawableBuilder(const std::string &name) const { - return BasicDrawableBuilderRef(new BasicDrawableBuilderGLES(name,scene)); + return std::make_shared(name,scene); } BasicDrawableInstanceBuilderRef SceneRendererGLES::makeBasicDrawableInstanceBuilder(const std::string &name) const { - return BasicDrawableInstanceBuilderRef(new BasicDrawableInstanceBuilderGLES(name,scene)); + return std::make_shared(name,scene); } BillboardDrawableBuilderRef SceneRendererGLES::makeBillboardDrawableBuilder(const std::string &name) const { - return BillboardDrawableBuilderRef(new BillboardDrawableBuilderGLES(name,scene)); + return std::make_shared(name,scene); } ScreenSpaceDrawableBuilderRef SceneRendererGLES::makeScreenSpaceDrawableBuilder(const std::string &name) const { - return ScreenSpaceDrawableBuilderRef(new ScreenSpaceDrawableBuilderGLES(name,scene)); + return std::make_shared(name,scene); } ParticleSystemDrawableBuilderRef SceneRendererGLES::makeParticleSystemDrawableBuilder(const std::string &name) const { - return ParticleSystemDrawableBuilderRef(new ParticleSystemDrawableBuilderGLES(name,scene)); + return std::make_shared(name,scene); } WideVectorDrawableBuilderRef SceneRendererGLES::makeWideVectorDrawableBuilder(const std::string &name) const { - return WideVectorDrawableBuilderRef(new WideVectorDrawableBuilderGLES(name,scene)); + return std::make_shared(name,this,scene); } RenderTargetRef SceneRendererGLES::makeRenderTarget() const { - return RenderTargetRef(new RenderTargetGLES()); + return std::make_shared(); } DynamicTextureRef SceneRendererGLES::makeDynamicTexture(const std::string &name) const { - return DynamicTextureRef(new DynamicTextureGLES(name)); + return std::make_shared(name); } } + +#include diff --git a/common/WhirlyGlobeLib/src/ScreenImportance.cpp b/common/WhirlyGlobeLib/src/ScreenImportance.cpp index b6651207dc..cb24ce61d0 100644 --- a/common/WhirlyGlobeLib/src/ScreenImportance.cpp +++ b/common/WhirlyGlobeLib/src/ScreenImportance.cpp @@ -173,11 +173,9 @@ double PolyImportance(const Point3dVector &poly,const Point3d &norm,ViewState *v screenPts.push_back(screenPt); } - double screenArea = CalcLoopArea(screenPts); - if (std::isnan(screenArea)) - screenArea = 0.0; + const double screenArea = CalcLoopArea(screenPts); // The polygon came out backwards, so toss it - if (screenArea <= 0.0) + if (!std::isfinite(screenArea) || screenArea <= 0.0) continue; // Now project the screen points back into model space @@ -286,7 +284,7 @@ bool DisplaySolid::isOnScreenForViewState(ViewState *viewState,const Point2f &fr bool TileIsOnScreen(ViewState *viewState,const WhirlyKit::Point2f &frameSize,WhirlyKit::CoordSystem *srcSystem,WhirlyKit::CoordSystemDisplayAdapter *coordAdapter,const WhirlyKit::Mbr &nodeMbr,const WhirlyKit::QuadTreeIdentifier &nodeIdent,DisplaySolidRef &dispSolid) { if (!dispSolid) - dispSolid = DisplaySolidRef(new DisplaySolid(nodeIdent,nodeMbr,0.0,0.0,srcSystem,coordAdapter)); + dispSolid = std::make_shared(nodeIdent,nodeMbr,0.0,0.0,srcSystem,coordAdapter); // This means the tile is degenerate (as far as we're concerned) if (!dispSolid->valid) @@ -300,7 +298,7 @@ bool TileIsOnScreen(ViewState *viewState,const WhirlyKit::Point2f &frameSize,Whi double ScreenImportance(ViewState *viewState,const WhirlyKit::Point2f &frameSize,const Point3d ¬Used,int pixelsSquare,WhirlyKit::CoordSystem *srcSystem,WhirlyKit::CoordSystemDisplayAdapter *coordAdapter,const Mbr &nodeMbr,const WhirlyKit::QuadTreeIdentifier &nodeIdent,DisplaySolidRef &dispSolid) { if (!dispSolid) - dispSolid = DisplaySolidRef(new DisplaySolid(nodeIdent,nodeMbr,0.0,0.0,srcSystem,coordAdapter)); + dispSolid = std::make_shared(nodeIdent,nodeMbr,0.0,0.0,srcSystem,coordAdapter); // This means the tile is degenerate (as far as we're concerned) if (!dispSolid->valid) @@ -319,7 +317,7 @@ double ScreenImportance(ViewState *viewState,const WhirlyKit::Point2f &frameSize double ScreenImportance(ViewState *viewState,const WhirlyKit::Point2f &frameSize,int pixelsSquare,WhirlyKit::CoordSystem *srcSystem,WhirlyKit::CoordSystemDisplayAdapter *coordAdapter,const Mbr &nodeMbr,double minZ,double maxZ,const WhirlyKit::QuadTreeIdentifier &nodeIdent,DisplaySolidRef &dispSolid) { if (!dispSolid) - dispSolid = DisplaySolidRef(new DisplaySolid(nodeIdent,nodeMbr,minZ,maxZ,srcSystem,coordAdapter)); + dispSolid = std::make_shared(nodeIdent,nodeMbr,minZ,maxZ,srcSystem,coordAdapter); // This means the tile is degenerate (as far as we're concerned) if (!dispSolid->valid) diff --git a/common/WhirlyGlobeLib/src/ScreenSpaceBuilder.cpp b/common/WhirlyGlobeLib/src/ScreenSpaceBuilder.cpp index 3964f47934..bc781d8627 100644 --- a/common/WhirlyGlobeLib/src/ScreenSpaceBuilder.cpp +++ b/common/WhirlyGlobeLib/src/ScreenSpaceBuilder.cpp @@ -27,9 +27,9 @@ namespace WhirlyKit ScreenSpaceBuilder::DrawableState::DrawableState() : period(0.0), progID(EmptyIdentity), fadeUp(0.0), fadeDown(0.0), enable(true), startEnable(0.0), endEnable(0.0), - drawPriority(0), minVis(DrawVisibleInvalid), maxVis(DrawVisibleInvalid), + drawPriority(0), renderTargetID(EmptyIdentity), minVis(DrawVisibleInvalid), maxVis(DrawVisibleInvalid), zoomSlot(-1), minZoomVis(DrawVisibleInvalid), maxZoomVis(DrawVisibleInvalid), - motion(false), rotation(false), keepUpright(false) + motion(false), rotation(false), keepUpright(false), hasMask(false) { } @@ -43,6 +43,8 @@ bool ScreenSpaceBuilder::DrawableState::operator < (const DrawableState &that) c return progID < that.progID; if (drawPriority != that.drawPriority) return drawPriority < that.drawPriority; + if (renderTargetID != that.renderTargetID) + return renderTargetID < that.renderTargetID; if (minVis != that.minVis) return minVis < that.minVis; if (maxVis != that.maxVis) @@ -69,6 +71,8 @@ bool ScreenSpaceBuilder::DrawableState::operator < (const DrawableState &that) c return rotation < that.rotation; if (keepUpright != that.keepUpright) return keepUpright < that.keepUpright; + if (hasMask != that.hasMask) + return hasMask < that.hasMask; if (vertexAttrs != that.vertexAttrs) return vertexAttrs < that.vertexAttrs; SimpleIdentity opacityExp0 = opacityExp ? opacityExp->getId() : EmptyIdentity, @@ -95,7 +99,7 @@ ScreenSpaceBuilder::DrawableWrap::DrawableWrap(SceneRenderer *render,const Drawa : state(state), center(0,0,0) { locDraw = render->makeScreenSpaceDrawableBuilder("ScreenSpace Builder"); - locDraw->Init(state.motion,state.rotation); + locDraw->ScreenSpaceInit(state.motion,state.rotation,state.hasMask); locDraw->setType(Triangles); // A max of two textures per for (unsigned int ii=0;iisetProgram(state.progID); locDraw->setDrawOrder(state.drawOrder); locDraw->setDrawPriority(state.drawPriority); + if (state.renderTargetID != EmptyIdentity) + locDraw->setRenderTarget(state.renderTargetID); locDraw->setFade(state.fadeDown, state.fadeUp); locDraw->setVisibleRange(state.minVis, state.maxVis); locDraw->setZoomInfo(state.zoomSlot, state.minZoomVis, state.maxZoomVis); @@ -219,6 +225,11 @@ void ScreenSpaceBuilder::setDrawPriority(int drawPriority) curState.drawPriority = drawPriority; } +void ScreenSpaceBuilder::setRenderTarget(SimpleIdentity renderTargetID) +{ + curState.renderTargetID = renderTargetID; +} + void ScreenSpaceBuilder::setVisibility(float minVis,float maxVis) { curState.minVis = minVis; @@ -331,7 +342,7 @@ void ScreenSpaceBuilder::addScreenObjects(std::vector &screen { ScreenSpaceObject &ssObj = screenObjects[ii]; - addScreenObject(ssObj); + addScreenObject(ssObj,ssObj.worldLoc,ssObj.geometry); } } @@ -344,29 +355,41 @@ void ScreenSpaceBuilder::addScreenObjects(std::vector &scre { ScreenSpaceObject *ssObj = screenObjects[ii]; - addScreenObject(*ssObj); + addScreenObject(*ssObj, ssObj->worldLoc, ssObj->geometry); } } -void ScreenSpaceBuilder::addScreenObject(const ScreenSpaceObject &ssObj) +void ScreenSpaceBuilder::addScreenObject(const ScreenSpaceObject &ssObj, + const Point3d &worldLoc, + const std::vector &geoms, + const std::vector *places) { - for (unsigned int ii=0;ii -1) state.drawPriority = geom.drawPriority; + if (geom.renderTargetID != EmptyIdentity) + state.renderTargetID = geom.renderTargetID; state.enable = ssObj.enable; state.startEnable = ssObj.startEnable; state.endEnable = ssObj.endEnable; VertexAttributeSetConvert(geom.vertexAttrs,state.vertexAttrs); - DrawableWrapRef drawWrap = findOrAddDrawWrap(state,(int)geom.coords.size(),(int)(geom.coords.size()-2),ssObj.worldLoc); + DrawableWrapRef drawWrap = findOrAddDrawWrap(state,(int)geom.coords.size(),(int)(geom.coords.size()-2),worldLoc); // May need to adjust things based on time - Point3d startLoc3d = ssObj.worldLoc; + Point3d startLoc3d = worldLoc; Point3f dir(0,0,0); if (state.motion) { @@ -424,8 +447,8 @@ void ScreenSpaceBuilder::flushChanges(ChangeSet &changes,SimpleIDSet &drawIDs) draws.clear(); } -ScreenSpaceObject::ScreenSpaceObject::ConvexGeometry::ConvexGeometry() - : progID(EmptyIdentity), color(255,255,255,255), drawPriority(-1) +ScreenSpaceConvexGeometry::ScreenSpaceConvexGeometry() + : progID(EmptyIdentity), color(255,255,255,255), drawPriority(-1), renderTargetID(EmptyIdentity) , drawOrder(BaseInfo::DrawOrderTiles) { } @@ -526,6 +549,11 @@ void ScreenSpaceObject::setDrawPriority(int drawPriority) state.drawPriority = drawPriority; } +void ScreenSpaceObject::setRenderTarget(SimpleIdentity renderTargetID) +{ + state.renderTargetID = renderTargetID; +} + void ScreenSpaceObject::setKeepUpright(bool inKeepUpright) { keepUpright = inKeepUpright; @@ -558,11 +586,16 @@ void ScreenSpaceObject::setOrderBy(long inOrderBy) orderBy = inOrderBy; } -void ScreenSpaceObject::addGeometry(const ConvexGeometry &geom) +void ScreenSpaceObject::addGeometry(const ScreenSpaceConvexGeometry &geom) { geometry.push_back(geom); } - + +void ScreenSpaceObject::addGeometry(const std::vector &geom) +{ + geometry.insert(geometry.end(), geom.begin(), geom.end()); +} + SimpleIdentity ScreenSpaceObject::getTypicalProgramID() { for (auto geom : geometry) diff --git a/common/WhirlyGlobeLib/src/ScreenSpaceDrawableBuilder.cpp b/common/WhirlyGlobeLib/src/ScreenSpaceDrawableBuilder.cpp index a6d896de8e..f664e08570 100644 --- a/common/WhirlyGlobeLib/src/ScreenSpaceDrawableBuilder.cpp +++ b/common/WhirlyGlobeLib/src/ScreenSpaceDrawableBuilder.cpp @@ -29,7 +29,7 @@ ScreenSpaceDrawableBuilder::ScreenSpaceDrawableBuilder() { } -void ScreenSpaceDrawableBuilder::Init(bool hasMotion,bool hasRotation,bool buildAnyway) +void ScreenSpaceDrawableBuilder::ScreenSpaceInit(bool hasMotion,bool hasRotation,bool buildAnyway) { rotation = hasRotation; motion = hasMotion; @@ -89,14 +89,15 @@ void ScreenSpaceDrawableBuilder::setScaleExpression(FloatExpressionInfoRef inSca scaleExp = inScaleExp; } -void ScreenSpaceDrawableBuilder::setupTweaker(BasicDrawable *theDraw) +void ScreenSpaceDrawableBuilder::setupTweaker(const DrawableTweakerRef &inTweaker) const { - ScreenSpaceTweaker *tweak = makeTweaker(); - tweak->startTime = startTime; - tweak->keepUpright = keepUpright; - tweak->activeRot = rotation; - tweak->motion = motion; - theDraw->addTweaker(DrawableTweakerRef(tweak)); + if (auto tweak = std::dynamic_pointer_cast(inTweaker)) + { + tweak->startTime = startTime; + tweak->keepUpright = keepUpright; + tweak->activeRot = rotation; + tweak->motion = motion; + } } - + } diff --git a/common/WhirlyGlobeLib/src/ScreenSpaceDrawableBuilderGLES.cpp b/common/WhirlyGlobeLib/src/ScreenSpaceDrawableBuilderGLES.cpp index d960100dd2..0450435331 100644 --- a/common/WhirlyGlobeLib/src/ScreenSpaceDrawableBuilderGLES.cpp +++ b/common/WhirlyGlobeLib/src/ScreenSpaceDrawableBuilderGLES.cpp @@ -1,9 +1,8 @@ -/* - * ScreenSpaceDrawableBuilderGLES.cpp +/* ScreenSpaceDrawableBuilderGLES.cpp * WhirlyGlobeLib * * Created by Steve Gifford on 5/14/19. - * Copyright 2011-2019 mousebird consulting + * Copyright 2011-2021 mousebird consulting * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -15,10 +14,10 @@ * 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. - * */ #import "ScreenSpaceDrawableBuilderGLES.h" +#import "WhirlyKitLog.h" namespace WhirlyKit { @@ -48,9 +47,9 @@ int ScreenSpaceDrawableBuilderGLES::addAttribute(BDAttributeDataType dataType,St return BasicDrawableBuilderGLES::addAttribute(dataType, nameID, slot, numThings); } -ScreenSpaceTweaker *ScreenSpaceDrawableBuilderGLES::makeTweaker() +DrawableTweakerRef ScreenSpaceDrawableBuilderGLES::makeTweaker() const { - return new ScreenSpaceTweakerGLES(); + return std::make_shared(); } BasicDrawableRef ScreenSpaceDrawableBuilderGLES::getDrawable() @@ -59,7 +58,7 @@ BasicDrawableRef ScreenSpaceDrawableBuilderGLES::getDrawable() return BasicDrawableBuilderGLES::getDrawable(); auto theDraw = BasicDrawableBuilderGLES::getDrawable(); - setupTweaker(theDraw.get()); + setupTweaker(*theDraw); return theDraw; } diff --git a/common/WhirlyGlobeLib/src/SelectionManager.cpp b/common/WhirlyGlobeLib/src/SelectionManager.cpp index 435e6e9253..d57bdf7085 100644 --- a/common/WhirlyGlobeLib/src/SelectionManager.cpp +++ b/common/WhirlyGlobeLib/src/SelectionManager.cpp @@ -78,6 +78,7 @@ SelectionManager::SelectionManager(Scene *scene) SelectionManager::~SelectionManager() { + std::lock_guard guardLock(lock); } // Add a rectangle (in 3-space) available for selection @@ -95,7 +96,7 @@ void SelectionManager::addSelectableRect(SimpleIdentity selectId,Point3f *pts,bo newSelect.pts[ii] = pts[ii]; { - std::lock_guard guardLock(mutex); + std::lock_guard guardLock(lock); rect3Dselectables.insert(newSelect); } } @@ -115,7 +116,7 @@ void SelectionManager::addSelectableRect(SimpleIdentity selectId,Point3f *pts,fl newSelect.pts[ii] = pts[ii]; { - std::lock_guard guardLock(mutex); + std::lock_guard guardLock(lock); rect3Dselectables.insert(newSelect); } } @@ -136,7 +137,7 @@ void SelectionManager::addSelectableScreenRect(SimpleIdentity selectId,const Poi newSelect.pts[ii] = pts[ii]; { - std::lock_guard guardLock(mutex); + std::lock_guard guardLock(lock); rect2Dselectables.insert(newSelect); } } @@ -160,7 +161,7 @@ void SelectionManager::addSelectableMovingScreenRect(SimpleIdentity selectId,con newSelect.pts[ii] = pts[ii]; { - std::lock_guard guardLock(mutex); + std::lock_guard guardLock(lock); movingRect2Dselectables.insert(newSelect); } } @@ -197,7 +198,7 @@ void SelectionManager::addSelectableRectSolid(SimpleIdentity selectId,Point3f *p } { - std::lock_guard guardLock(mutex); + std::lock_guard guardLock(lock); polytopeSelectables.insert(newSelect); } } @@ -232,7 +233,7 @@ void SelectionManager::addSelectableRectSolid(SimpleIdentity selectId,Point3d *p } { - std::lock_guard guardLock(mutex); + std::lock_guard guardLock(lock); polytopeSelectables.insert(newSelect); } } @@ -278,7 +279,7 @@ void SelectionManager::addPolytope(SimpleIdentity selectId,const std::vector guardLock(mutex); + std::lock_guard guardLock(lock); polytopeSelectables.insert(newSelect); } } @@ -350,7 +351,7 @@ void SelectionManager::addMovingPolytope(SimpleIdentity selectId,const std::vect } { - std::lock_guard guardLock(mutex); + std::lock_guard guardLock(lock); movingPolytopeSelectables.insert(newSelect); } } @@ -412,7 +413,7 @@ void SelectionManager::addSelectableLinear(SimpleIdentity selectId,const Point3d } { - std::lock_guard guardLock(mutex); + std::lock_guard guardLock(lock); linearSelectables.insert(newSelect); } } @@ -433,14 +434,14 @@ void SelectionManager::addSelectableBillboard(SimpleIdentity selectId,const Poin newSelect.maxVis = maxVis; { - std::lock_guard guardLock(mutex); + std::lock_guard guardLock(lock); billboardSelectables.insert(newSelect); } } void SelectionManager::enableSelectable(SimpleIdentity selectID,bool enable) { - std::lock_guard guardLock(mutex); + std::lock_guard guardLock(lock); RectSelectable3DSet::iterator it = rect3Dselectables.find(RectSelectable3D(selectID)); @@ -509,7 +510,7 @@ void SelectionManager::enableSelectable(SimpleIdentity selectID,bool enable) void SelectionManager::enableSelectables(const SimpleIDSet &selectIDs,bool enable) { - std::lock_guard guardLock(mutex); + std::lock_guard guardLock(lock); for (SimpleIDSet::iterator sit = selectIDs.begin(); sit != selectIDs.end(); ++sit) { @@ -583,7 +584,7 @@ void SelectionManager::enableSelectables(const SimpleIDSet &selectIDs,bool enabl // Remove the given selectable from consideration void SelectionManager::removeSelectable(SimpleIdentity selectID) { - std::lock_guard guardLock(mutex); + std::lock_guard guardLock(lock); RectSelectable3DSet::iterator it = rect3Dselectables.find(RectSelectable3D(selectID)); @@ -617,7 +618,7 @@ void SelectionManager::removeSelectable(SimpleIdentity selectID) void SelectionManager::removeSelectables(const SimpleIDSet &selectIDs) { - std::lock_guard guardLock(mutex); + std::lock_guard guardLock(lock); //bool found = false; for (SimpleIDSet::iterator sit = selectIDs.begin(); sit != selectIDs.end(); ++sit) @@ -879,9 +880,9 @@ void SelectionManager::pickObjects(Point2f touchPt,float maxDist,ViewStateRef vi frameBufferSize.x() = renderer->framebufferWidth; frameBufferSize.y() = renderer->framebufferHeight; - LayoutManager *layoutManager = (LayoutManager *)scene->getManager(kWKLayoutManager); - - std::lock_guard guardLock(mutex); + LayoutManagerRef layoutManager = std::dynamic_pointer_cast(scene->getManager(kWKLayoutManager)); + + std::lock_guard guardLock(lock); // Figure out where the screen space objects are, both layout manager // controlled and other diff --git a/common/WhirlyGlobeLib/src/ShapeDrawableBuilder.cpp b/common/WhirlyGlobeLib/src/ShapeDrawableBuilder.cpp index 23edb71493..aeb246b591 100644 --- a/common/WhirlyGlobeLib/src/ShapeDrawableBuilder.cpp +++ b/common/WhirlyGlobeLib/src/ShapeDrawableBuilder.cpp @@ -1,9 +1,8 @@ -/* - * ShapeDrawableBuilder.mm +/* ShapeDrawableBuilder.cpp * WhirlyGlobeLib * * Created by Steve Gifford on 9/28/11. - * Copyright 2011-2019 mousebird consulting. + * Copyright 2011-2021 mousebird consulting. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -15,7 +14,6 @@ * 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. - * */ #import "GlobeMath.h" @@ -33,28 +31,33 @@ using namespace WhirlyKit; namespace WhirlyKit { + ShapeInfo::ShapeInfo() - : color(RGBAColor(255,255,255,255)), lineWidth(1.0), insideOut(false), hasCenter(false), center(0.0,0.0,0.0) + : color(RGBAColor(255,255,255,255)) + , lineWidth(1.0) + , insideOut(false) + , hasCenter(false) + , center(0.0,0.0,0.0) { zBufferRead = true; } - + ShapeInfo::ShapeInfo(const Dictionary &dict) : BaseInfo(dict) + , hasCenter(false) + , center(0,0,0) { - if (!dict.hasField(MaplyZBufferRead)) - zBufferRead = true; + zBufferRead = dict.getBool(MaplyZBufferRead, true); color = dict.getColor(MaplyColor,RGBAColor(255,255,255,255)); lineWidth = dict.getDouble(MaplyVecWidth,1.0); insideOut = dict.getBool(MaplyShapeInsideOut,false); - hasCenter = false; - center = Point3d(0.0,0.0,0.0); if (dict.hasField(MaplyShapeCenterX) || dict.hasField(MaplyShapeCenterY) || dict.hasField(MaplyShapeCenterZ)) { hasCenter = true; - center.x() = dict.getDouble(MaplyShapeCenterX, center.x()); - center.y() = dict.getDouble(MaplyShapeCenterY, center.y()); - center.z() = dict.getDouble(MaplyShapeCenterZ, center.z()); + // Snap to float + center.x() = (float)dict.getDouble(MaplyShapeCenterX, 0.0); + center.y() = (float)dict.getDouble(MaplyShapeCenterY, 0.0); + center.z() = (float)dict.getDouble(MaplyShapeCenterZ, 0.0); } } diff --git a/common/WhirlyGlobeLib/src/ShapeManager.cpp b/common/WhirlyGlobeLib/src/ShapeManager.cpp index d4fd9dd6a5..ce0b41f8aa 100644 --- a/common/WhirlyGlobeLib/src/ShapeManager.cpp +++ b/common/WhirlyGlobeLib/src/ShapeManager.cpp @@ -1,9 +1,8 @@ -/* - * ShapeManager.cpp +/* ShapeManager.cpp * WhirlyGlobeLib * * Created by jmnavarro - * Copyright 2011-2019 mousebird consulting. + * Copyright 2011-2021 mousebird consulting. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -15,7 +14,6 @@ * 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. - * */ #import "ShapeManager.h" #import "GlobeMath.h" @@ -35,7 +33,7 @@ using namespace WhirlyKit; namespace WhirlyKit { -void ShapeSceneRep::enableContents(WhirlyKit::SelectionManager *selectManager, bool enable, ChangeSet &changes) +void ShapeSceneRep::enableContents(WhirlyKit::SelectionManagerRef &selectManager, bool enable, ChangeSet &changes) { for (const SimpleIdentity idIt : drawIDs){ changes.push_back(new OnOffChangeRequest(idIt, enable)); @@ -45,7 +43,7 @@ void ShapeSceneRep::enableContents(WhirlyKit::SelectionManager *selectManager, b } } -void ShapeSceneRep::clearContents(SelectionManager *selectManager, ChangeSet &changes,TimeInterval when) +void ShapeSceneRep::clearContents(SelectionManagerRef &selectManager, ChangeSet &changes,TimeInterval when) { for (const SimpleIdentity idIt : drawIDs){ changes.push_back(new RemDrawableReq(idIt,when)); @@ -65,7 +63,7 @@ Shape::~Shape() } // Base shape doesn't make anything -void Shape::makeGeometryWithBuilder(WhirlyKit::ShapeDrawableBuilder *regBuilder, WhirlyKit::ShapeDrawableBuilderTri *triBuilder, WhirlyKit::Scene *scene, WhirlyKit::SelectionManager *selectManager, WhirlyKit::ShapeSceneRep *sceneRep) +void Shape::makeGeometryWithBuilder(WhirlyKit::ShapeDrawableBuilder *regBuilder, WhirlyKit::ShapeDrawableBuilderTri *triBuilder, WhirlyKit::Scene *scene, WhirlyKit::SelectionManagerRef &selectManager, WhirlyKit::ShapeSceneRep *sceneRep) { } @@ -75,7 +73,8 @@ Point3d Shape::displayCenter(WhirlyKit::CoordSystemDisplayAdapter *coordAdapter, } Circle::Circle() -: loc(0,0), radius(0.0), height(0.0), sampleX(10) { + : loc(0,0), radius(0.0), height(0.0), sampleX(10) +{ } Circle::~Circle() @@ -87,15 +86,19 @@ Point3d Circle::displayCenter(WhirlyKit::CoordSystemDisplayAdapter *coordAdapter if (shapeInfo.hasCenter) return shapeInfo.center; - Point3d localPt = coordAdapter->getCoordSystem()->geographicToLocal3d(loc); - Point3d dispPt = coordAdapter->localToDisplay(localPt); + const Point3d localPt = coordAdapter->getCoordSystem()->geographicToLocal3d(loc); + const Point3d dispPt = coordAdapter->localToDisplay(localPt); return dispPt; } - -static const float sqrt2 = 1.4142135623; -void Circle::makeGeometryWithBuilder(WhirlyKit::ShapeDrawableBuilder *regBuilder, WhirlyKit::ShapeDrawableBuilderTri *triBuilder, WhirlyKit::Scene *scene, WhirlyKit::SelectionManager *selectManager, WhirlyKit::ShapeSceneRep *sceneRep) +static const float sqrt2 = M_SQRT2; + +void Circle::makeGeometryWithBuilder(WhirlyKit::ShapeDrawableBuilder *regBuilder, + WhirlyKit::ShapeDrawableBuilderTri *triBuilder, + WhirlyKit::Scene *scene, + WhirlyKit::SelectionManagerRef &selectManager, + WhirlyKit::ShapeSceneRep *sceneRep) { CoordSystemDisplayAdapter *coordAdapter = scene->getCoordAdapter(); @@ -186,7 +189,7 @@ Point3d Sphere::displayCenter(WhirlyKit::CoordSystemDisplayAdapter *coordAdapter return dispPt; } -void Sphere::makeGeometryWithBuilder(WhirlyKit::ShapeDrawableBuilder *regBuilder, WhirlyKit::ShapeDrawableBuilderTri *triBuilder, WhirlyKit::Scene *scene, WhirlyKit::SelectionManager *selectManager, WhirlyKit::ShapeSceneRep *sceneRep) +void Sphere::makeGeometryWithBuilder(WhirlyKit::ShapeDrawableBuilder *regBuilder, WhirlyKit::ShapeDrawableBuilderTri *triBuilder, WhirlyKit::Scene *scene, WhirlyKit::SelectionManagerRef &selectManager, WhirlyKit::ShapeSceneRep *sceneRep) { CoordSystemDisplayAdapter *coordAdapter = scene->getCoordAdapter(); @@ -272,7 +275,8 @@ void Sphere::makeGeometryWithBuilder(WhirlyKit::ShapeDrawableBuilder *regBuilder } Cylinder::Cylinder() -: loc(0,0), baseHeight(0.0), radius(0.0), height(0.0), sampleX(10) { + : loc(0,0), baseHeight(0.0), radius(0.0), height(0.0), sampleX(10) +{ } Cylinder::~Cylinder() @@ -290,7 +294,7 @@ Point3d Cylinder::displayCenter(WhirlyKit::CoordSystemDisplayAdapter *coordAdapt return dispPt; } -void Cylinder::makeGeometryWithBuilder(WhirlyKit::ShapeDrawableBuilder *regBuilder, WhirlyKit::ShapeDrawableBuilderTri *triBuilder, WhirlyKit::Scene *scene, WhirlyKit::SelectionManager *selectManager, WhirlyKit::ShapeSceneRep *sceneRep) +void Cylinder::makeGeometryWithBuilder(WhirlyKit::ShapeDrawableBuilder *regBuilder, WhirlyKit::ShapeDrawableBuilderTri *triBuilder, WhirlyKit::Scene *scene, WhirlyKit::SelectionManagerRef &selectManager, WhirlyKit::ShapeSceneRep *sceneRep) { CoordSystemDisplayAdapter *coordAdapter = scene->getCoordAdapter(); @@ -402,7 +406,7 @@ Point3d Linear::displayCenter(WhirlyKit::CoordSystemDisplayAdapter *coordAdapter return Point3d(0,0,0); } -void Linear::makeGeometryWithBuilder(WhirlyKit::ShapeDrawableBuilder *regBuilder, WhirlyKit::ShapeDrawableBuilderTri *triBuilder, WhirlyKit::Scene *scene, WhirlyKit::SelectionManager *selectManager, WhirlyKit::ShapeSceneRep *sceneRep) +void Linear::makeGeometryWithBuilder(WhirlyKit::ShapeDrawableBuilder *regBuilder, WhirlyKit::ShapeDrawableBuilderTri *triBuilder, WhirlyKit::Scene *scene, WhirlyKit::SelectionManagerRef &selectManager, WhirlyKit::ShapeSceneRep *sceneRep) { auto theColor = useColor ? color : regBuilder->getShapeInfo()->color; @@ -436,7 +440,7 @@ Point3d Extruded::displayCenter(WhirlyKit::CoordSystemDisplayAdapter *coordAdapt return dispPt; } -void Extruded::makeGeometryWithBuilder(WhirlyKit::ShapeDrawableBuilder *regBuilder, WhirlyKit::ShapeDrawableBuilderTri *triBuilder, WhirlyKit::Scene *scene, WhirlyKit::SelectionManager *selectManager, WhirlyKit::ShapeSceneRep *sceneRep) +void Extruded::makeGeometryWithBuilder(WhirlyKit::ShapeDrawableBuilder *regBuilder, WhirlyKit::ShapeDrawableBuilderTri *triBuilder, WhirlyKit::Scene *scene, WhirlyKit::SelectionManagerRef &selectManager, WhirlyKit::ShapeSceneRep *sceneRep) { CoordSystemDisplayAdapter *coordAdapter = scene->getCoordAdapter(); @@ -571,7 +575,7 @@ Point3d Rectangle::displayCenter(CoordSystemDisplayAdapter *coordAdapter,const S } // Build the geometry for a circle in display space -void Rectangle::makeGeometryWithBuilder(ShapeDrawableBuilder *regBuilder,ShapeDrawableBuilderTri *triBuilder,Scene *scene,SelectionManager *selectManager,ShapeSceneRep *sceneRep) +void Rectangle::makeGeometryWithBuilder(ShapeDrawableBuilder *regBuilder,ShapeDrawableBuilderTri *triBuilder,Scene *scene,SelectionManagerRef &selectManager,ShapeSceneRep *sceneRep) { CoordSystemDisplayAdapter *coordAdapter = scene->getCoordAdapter(); @@ -628,6 +632,8 @@ ShapeManager::ShapeManager() ShapeManager::~ShapeManager() { + std::lock_guard guardLock(lock); + for (ShapeSceneRepSet::iterator it = shapeReps.begin(); it != shapeReps.end(); ++it) delete *it; @@ -647,7 +653,8 @@ void ShapeManager::convertShape(Shape &shape,std::vector { drawBuildTri.clipCoords = true; } - shape.makeGeometryWithBuilder(&drawBuildReg,&drawBuildTri,scene,NULL,NULL); + auto selectManage = SelectionManagerRef(); + shape.makeGeometryWithBuilder(&drawBuildReg,&drawBuildTri,scene,selectManage,NULL); // Scrape out the triangles drawBuildTri.flush(); @@ -689,15 +696,16 @@ void ShapeManager::convertShape(Shape &shape,std::vector /// Add an array of shapes. The returned ID can be used to remove or modify the group of shapes. SimpleIdentity ShapeManager::addShapes(std::vector shapes, const ShapeInfo &shapeInfo, ChangeSet &changes) { - SelectionManager *selectManager = (SelectionManager *)getScene()->getManager(kWKSelectionManager); + auto selectManager = scene->getManager(kWKSelectionManager); - ShapeSceneRep *sceneRep = new ShapeSceneRep(); + auto sceneRep = std::make_unique(); sceneRep->fade = shapeInfo.fade; // Figure out a good center Point3d center(0,0,0); int numObjects = 0; - for (auto shape : shapes) { + for (auto shape : shapes) + { center += shape->displayCenter(getScene()->getCoordAdapter(), shapeInfo); numObjects++; } @@ -708,12 +716,13 @@ SimpleIdentity ShapeManager::addShapes(std::vector shapes, const ShapeIn ShapeDrawableBuilder drawBuildReg(getScene()->getCoordAdapter(),renderer,shapeInfo,true,center); // Work through the shapes - for (auto shape : shapes) { + for (auto shape : shapes) + { if (shape->clipCoords) drawBuildTri.setClipCoords(true); else drawBuildTri.setClipCoords(false); - shape->makeGeometryWithBuilder(&drawBuildReg, &drawBuildTri, getScene(), selectManager, sceneRep); + shape->makeGeometryWithBuilder(&drawBuildReg, &drawBuildTri, getScene(), selectManager, sceneRep.get()); } // Flush out remaining geometry @@ -724,8 +733,8 @@ SimpleIdentity ShapeManager::addShapes(std::vector shapes, const ShapeIn SimpleIdentity shapeID = sceneRep->getId(); { - std::lock_guard guardLock(shapeLock); - shapeReps.insert(sceneRep); + std::lock_guard guardLock(lock); + shapeReps.insert(sceneRep.release()); // transfer ownership } return shapeID; @@ -733,9 +742,9 @@ SimpleIdentity ShapeManager::addShapes(std::vector shapes, const ShapeIn void ShapeManager::enableShapes(SimpleIDSet &shapeIDs,bool enable,ChangeSet &changes) { - SelectionManager *selectManager = (SelectionManager *)getScene()->getManager(kWKSelectionManager); + SelectionManagerRef selectManager = std::dynamic_pointer_cast(scene->getManager(kWKSelectionManager)); - std::lock_guard guardLock(shapeLock); + std::lock_guard guardLock(lock); for (auto shapeID : shapeIDs) { ShapeSceneRep dummyRep(shapeID); @@ -750,9 +759,9 @@ void ShapeManager::enableShapes(SimpleIDSet &shapeIDs,bool enable,ChangeSet &cha /// Remove a group of shapes named by the given ID void ShapeManager::removeShapes(SimpleIDSet &shapeIDs,ChangeSet &changes) { - SelectionManager *selectManager = (SelectionManager *)getScene()->getManager(kWKSelectionManager); + SelectionManagerRef selectManager = std::dynamic_pointer_cast(scene->getManager(kWKSelectionManager)); - std::lock_guard guardLock(shapeLock); + std::lock_guard guardLock(lock); TimeInterval curTime = scene->getCurrentTime(); for (auto shapeID : shapeIDs) { @@ -776,7 +785,7 @@ void ShapeManager::removeShapes(SimpleIDSet &shapeIDs,ChangeSet &changes) void ShapeManager::setUniformBlock(const SimpleIDSet &shapeIDs,const RawDataRef &uniBlock,int bufferID,ChangeSet &changes) { - std::lock_guard guardLock(shapeLock); + std::lock_guard guardLock(lock); for (auto shapeID : shapeIDs) { ShapeSceneRep dummyRep(shapeID); diff --git a/common/WhirlyGlobeLib/src/ShapeReader.cpp b/common/WhirlyGlobeLib/src/ShapeReader.cpp index 6fe27685a8..b2f3c02508 100644 --- a/common/WhirlyGlobeLib/src/ShapeReader.cpp +++ b/common/WhirlyGlobeLib/src/ShapeReader.cpp @@ -95,6 +95,7 @@ VectorShapeRef ShapeReader::getObjectByIndex(unsigned int vecIndex,const StringS case SHPT_MULTIPOINTZ: { VectorPointsRef points = VectorPoints::createPoints(); + points->pts.reserve(thisShape->nParts); theShape = points; for (unsigned int ii=0;iinParts;ii++) { @@ -107,6 +108,7 @@ VectorShapeRef ShapeReader::getObjectByIndex(unsigned int vecIndex,const StringS case SHPT_ARCZ: { VectorLinearRef linear = VectorLinear::createLinear(); + linear->pts.reserve(thisShape->nVertices); theShape = linear; for (unsigned int ii=0;iinVertices;ii++) { diff --git a/common/WhirlyGlobeLib/src/SphericalEarthChunkManager.cpp b/common/WhirlyGlobeLib/src/SphericalEarthChunkManager.cpp index 7ec5bebdbf..38409f07e5 100644 --- a/common/WhirlyGlobeLib/src/SphericalEarthChunkManager.cpp +++ b/common/WhirlyGlobeLib/src/SphericalEarthChunkManager.cpp @@ -313,6 +313,7 @@ SphericalChunkManager::SphericalChunkManager() SphericalChunkManager::~SphericalChunkManager() { + std::lock_guard guardLock(lock); } /// Add the given chunk (enabled or disabled) @@ -376,7 +377,7 @@ SimpleIdentity SphericalChunkManager::addChunks(const std::vector guardLock(repLock); + std::lock_guard guardLock(lock); chunkReps.insert(chunkRep); } @@ -389,7 +390,7 @@ bool SphericalChunkManager::modifyChunkTextures(SimpleIdentity chunkID,const std SimpleIDSet oldTexIDs; { - std::lock_guard guardLock(repLock); + std::lock_guard guardLock(lock); ChunkSceneRepRef dummyRef(new ChunkSceneRep(chunkID)); ChunkRepSet::iterator it = chunkReps.find(dummyRef); if (it != chunkReps.end()) @@ -414,7 +415,7 @@ bool SphericalChunkManager::modifyDrawPriority(SimpleIdentity chunkID,int drawPr SimpleIDSet drawIDs; { - std::lock_guard guardLock(repLock); + std::lock_guard guardLock(lock); ChunkSceneRepRef dummyRef(new ChunkSceneRep(chunkID)); ChunkRepSet::iterator it = chunkReps.find(dummyRef); if (it != chunkReps.end()) @@ -432,7 +433,7 @@ bool SphericalChunkManager::modifyDrawPriority(SimpleIdentity chunkID,int drawPr /// Enable or disable the given chunk void SphericalChunkManager::enableChunk(SimpleIdentity chunkID,bool enable,ChangeSet &changes) { - std::lock_guard guardLock(repLock); + std::lock_guard guardLock(lock); ChunkSceneRepRef dummyRef(new ChunkSceneRep(chunkID)); ChunkRepSet::iterator it = chunkReps.find(dummyRef); if (it != chunkReps.end()) { @@ -446,7 +447,7 @@ void SphericalChunkManager::enableChunk(SimpleIdentity chunkID,bool enable,Chang /// Remove the given chunks void SphericalChunkManager::removeChunks(SimpleIDSet &chunkIDs,ChangeSet &changes) { - std::lock_guard guardLock(repLock); + std::lock_guard guardLock(lock); for (auto chunkID : chunkIDs) { ChunkSceneRepRef dummyRef(new ChunkSceneRep(chunkID)); ChunkRepSet::iterator it = chunkReps.find(dummyRef); @@ -459,7 +460,7 @@ void SphericalChunkManager::removeChunks(SimpleIDSet &chunkIDs,ChangeSet &change int SphericalChunkManager::getNumChunks() { - std::lock_guard guardLock(repLock); + std::lock_guard guardLock(lock); //int numChunks = 0; //numChunks = chunkReps.size(); diff --git a/common/WhirlyGlobeLib/src/StringIndexer.cpp b/common/WhirlyGlobeLib/src/StringIndexer.cpp index 61d3a11a60..68a597bfd7 100644 --- a/common/WhirlyGlobeLib/src/StringIndexer.cpp +++ b/common/WhirlyGlobeLib/src/StringIndexer.cpp @@ -1,9 +1,8 @@ -/* - * StringIndexer.mm +/* StringIndexer.cpp * WhirlyGlobeLib * * Created by Steve Gifford on 7/30/18. - * Copyright 2011-2019 Saildrone Inc + * Copyright 2011-2021 Saildrone Inc * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -15,7 +14,6 @@ * 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. - * */ #import "StringIndexer.h" @@ -23,41 +21,38 @@ #import "Identifiable.h" namespace WhirlyKit { - -StringIndexer &StringIndexer::getInstance() + +StringIndexer StringIndexer::instance; + +StringIndexer::StringIndexer() : stringToIdent(500) { - static StringIndexer instance; - - return instance; + identToString.reserve(500); } StringIdentity StringIndexer::getStringID(const std::string &str) { StringIndexer &index = getInstance(); - + + // todo: upgradeable shared mutex would be perfect here std::lock_guard lock(index.mutex); - - auto it = index.stringToIdent.find(str); + + const auto it = index.stringToIdent.find(str); if (it != index.stringToIdent.end()) return it->second; - - int strID = index.identToString.size(); + + const int strID = index.identToString.size(); index.identToString.push_back(str); index.stringToIdent[str] = strID; - return strID; } std::string StringIndexer::getString(StringIdentity strID) { - StringIndexer &index = getInstance(); - + const StringIndexer &index = getInstance(); + std::lock_guard lock(index.mutex); - - if (strID >= index.identToString.size()) - return ""; - - return index.identToString[strID]; + + return (strID < index.identToString.size()) ? index.identToString[strID] : std::string(); } // Note: This is from OpenGL. Doesn't hold anymore on iOS @@ -102,6 +97,8 @@ StringIdentity u_uprightNameID; StringIdentity u_activerotNameID; StringIdentity a_rotNameID; StringIdentity a_dirNameID; +StringIdentity a_maskNameID; +StringIdentity a_maskNameIDs[WhirlyKitMaxMasks]; StringIdentity a_texCoordNameID; StringIdentity u_w2NameID; StringIdentity u_Realw2NameID; @@ -119,12 +116,8 @@ StringIdentity a_instanceColorNameID; StringIdentity a_modelDirNameID; // Turn the string names into IDs, but just once -static bool stringsSetup = false; -void SetupDrawableStrings() +static void SetupDrawableStringsOnce() { - if (stringsSetup) - return; - for (unsigned int ii=0;ii<8;ii++) { std::string baseMapName = "s_baseMap" + std::to_string(ii); baseMapNameIDs[ii] = StringIndexer::getStringID(baseMapName); @@ -155,7 +148,7 @@ void SetupDrawableStrings() materialDiffuseNameID = StringIndexer::getStringID("material.diffuse"); materialSpecularNameID = StringIndexer::getStringID("material.specular"); materialSpecularExponentNameID = StringIndexer::getStringID("material.specular_exponent"); - + mvpMatrixNameID = StringIndexer::getStringID("u_mvpMatrix"); mvpInvMatrixNameID = StringIndexer::getStringID("u_mvpInvMatrix"); mvMatrixNameID = StringIndexer::getStringID("u_mvMatrix"); @@ -179,6 +172,11 @@ void SetupDrawableStrings() u_activerotNameID = StringIndexer::getStringID("u_activerot"); a_rotNameID = StringIndexer::getStringID("a_rot"); a_dirNameID = StringIndexer::getStringID("a_dir"); + a_maskNameID = StringIndexer::getStringID("a_maskID"); + for (unsigned int index=0;index diff --git a/common/WhirlyGlobeLib/src/TextureAtlas.cpp b/common/WhirlyGlobeLib/src/TextureAtlas.cpp index 73ff6db843..a246233dfe 100644 --- a/common/WhirlyGlobeLib/src/TextureAtlas.cpp +++ b/common/WhirlyGlobeLib/src/TextureAtlas.cpp @@ -1,9 +1,8 @@ -/* - * TextureAtlas.mm +/* TextureAtlas.mm * WhirlyGlobeLib * * Created by Steve Gifford on 3/28/11. - * Copyright 2011-2019 mousebird consulting + * Copyright 2011-2021 mousebird consulting * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -15,7 +14,6 @@ * 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. - * */ #import "TextureAtlas.h" @@ -28,7 +26,7 @@ using namespace WhirlyKit; // Set up the texture mapping matrix from the destination texture coords void SubTexture::setFromTex(const TexCoord &texOrg,const TexCoord &texDest) { - trans = trans.Identity(); + trans = decltype(trans)::Identity(); trans.translate(texOrg); trans.scale(Point2f(texDest.x()-texOrg.x(),texDest.y()-texOrg.y())); } @@ -36,17 +34,17 @@ void SubTexture::setFromTex(const TexCoord &texOrg,const TexCoord &texDest) // Calculate a destination texture coordinate TexCoord SubTexture::processTexCoord(const TexCoord &inCoord) const { - Vector3f res = trans * Vector3f(inCoord.x(),inCoord.y(),1.0); + const Vector3f res = trans * Vector3f(inCoord.x(),inCoord.y(),1.0); return TexCoord(res.x(),res.y()); } // Calculate destination texture coords for a while group void SubTexture::processTexCoords(std::vector &coords) const { - for (unsigned int ii=0;ii 0.0 ? normalize((u_mvpMatrix * vec4(a_normal.xyz, 0.0)).xyz) : a_normal.xzy; float ndotl; -// float ndoth;\n +// float ndoth; ndotl = max(0.0, dot(adjNorm, light[ii].direction)); -// ndotl = pow(ndotl,0.5);\n -// ndoth = max(0.0, dot(adjNorm, light[ii].halfplane));\n +// ndotl = pow(ndotl,0.5); +// ndoth = max(0.0, dot(adjNorm, light[ii].halfplane)); ambient += light[ii].ambient; diffuse += ndotl * light[ii].diffuse; } @@ -251,7 +251,7 @@ void main() v_color = inColor * u_fade; } vec3 center = a_modelDir * u_time + a_modelCenter; - vec3 vertPos = (a_singleMatrix *vec4(a_position,1.0)).xyz + center; + vec3 vertPos = (a_singleMatrix * vec4(a_position,1.0)).xyz + center; gl_Position = u_mvpMatrix * vec4(vertPos,1.0); } diff --git a/common/WhirlyGlobeLib/src/VectorData.cpp b/common/WhirlyGlobeLib/src/VectorData.cpp index 9cc8c48435..64c284846f 100644 --- a/common/WhirlyGlobeLib/src/VectorData.cpp +++ b/common/WhirlyGlobeLib/src/VectorData.cpp @@ -27,108 +27,104 @@ namespace WhirlyKit { -// Calculate area of a single loop -float CalcLoopArea(const VectorRing &loop) +template +double CalcLoopArea(const std::vector> &loop) { - float area = 0.0; - for (unsigned int ii=0;ii +T CalcLoopCentroid(const std::vector> &loop, double loopArea) { - Point2f centroid(0,0); - - float area = 0.0; - for (unsigned int ii=0;ii(sumX/(3*loopArea)), + static_cast(sumY/(3*loopArea))}; } -Point2d CalcCenterOfMass(const Point2dVector &loop) +// Calculate the centroid of an arbitrary loop +template +T CalcLoopCentroid(const std::vector> &loop) { - Point2d center(0,0); + return CalcLoopCentroid(loop, CalcLoopArea(loop)); +} - if (loop.empty()) - return center; - - for (auto &pt : loop) - center += pt; - center /= loop.size(); +template +T CalcCenterOfMass(const std::vector> &loop) +{ + if (loop.empty()) { + return {0,0}; + } + + double cx = 0, cy = 0; + for (const auto &pt : loop) { + cx += pt.x(); + cy += pt.y(); + } - return center; + return {static_cast(cx/loop.size()), + static_cast(cy/loop.size())}; } +// Export specific instantiations of the templates above. +template double CalcLoopArea(const VectorRing&); +template double CalcLoopArea(const Point2dVector&); +template Point2f CalcLoopCentroid(const VectorRing&); +template Point2d CalcLoopCentroid(const Point2dVector&); +template Point2f CalcLoopCentroid(const VectorRing&, double); +template Point2d CalcLoopCentroid(const Point2dVector&, double); +template Point2f CalcCenterOfMass(const VectorRing&); +template Point2d CalcCenterOfMass(const Point2dVector&); + // Break any edge longer than the given length -// Returns true if it broke anything. If it didn't, doesn't fill in outPts void SubdivideEdges(const VectorRing &inPts,VectorRing &outPts,bool closed,float maxLen) { - float maxLen2 = maxLen*maxLen; + const float maxLen2 = maxLen*maxLen; + + outPts.reserve(inPts.size()); for (int ii=0;ii<(closed ? inPts.size() : inPts.size()-1);ii++) { @@ -136,15 +132,14 @@ void SubdivideEdges(const VectorRing &inPts,VectorRing &outPts,bool closed,float const Point2f &p1 = inPts[(ii+1)%inPts.size()]; outPts.push_back(p0); Point2f dir = p1-p0; - float dist2 = dir.squaredNorm(); + const float dist2 = dir.squaredNorm(); if (dist2 > maxLen2) { - float dist = sqrtf(dist2); + const float dist = sqrtf(dist2); dir /= dist; for (float pos=maxLen;pos std::string to_string(T value) @@ -102,10 +100,12 @@ std::string VectorInfo::toString() void VectorSceneRep::clear(ChangeSet &changes) { - for (SimpleIDSet::iterator it = drawIDs.begin(); it != drawIDs.end(); ++it) - changes.push_back(new RemDrawableReq(*it)); + for (auto it : drawIDs) + changes.push_back(new RemDrawableReq(it)); } +static const std::string vecBuilderName("Vector Layer"); + /* Drawable Builder Used to construct drawables with multiple shapes in them. Eventually, we'll move this out to be a more generic object. @@ -115,9 +115,18 @@ class VectorDrawableBuilder public: VectorDrawableBuilder(Scene *scene,SceneRenderer *sceneRender,ChangeSet &changeRequests,VectorSceneRep *sceneRep, const VectorInfo *vecInfo,bool linesOrPoints,bool doColor) - : changeRequests(changeRequests), scene(scene), sceneRender(sceneRender), sceneRep(sceneRep), vecInfo(vecInfo), drawable(NULL), centerValid(false), center(0,0,0), geoCenter(0,0), doColor(doColor) + : changeRequests(changeRequests) + , scene(scene) + , sceneRender(sceneRender) + , sceneRep(sceneRep) + , vecInfo(vecInfo) + , drawable(nullptr) + , centerValid(false) + , center(0,0,0) + , geoCenter(0,0) + , doColor(doColor) + , primType(linesOrPoints ? Lines : Points) { - primType = (linesOrPoints ? Lines : Points); } ~VectorDrawableBuilder() @@ -132,17 +141,17 @@ class VectorDrawableBuilder geoCenter = inGeoCenter; } - void addPoints(VectorRing3d &inPts,bool closed,MutableDictionaryRef attrs) + void addPoints(const VectorRing3d &inPts,bool closed, const MutableDictionaryRef &attrs) { VectorRing pts; pts.reserve(pts.size()); for (const auto &pt : inPts) - pts.push_back(Point2f(pt.x(),pt.y())); + pts.emplace_back(pt.x(),pt.y()); addPoints(pts,closed,attrs); } - void addPoints(VectorRing &pts,bool closed,MutableDictionaryRef attrs) + void addPoints(const VectorRing &pts,bool closed,const MutableDictionaryRef &attrs) { CoordSystemDisplayAdapter *coordAdapter = scene->getCoordAdapter(); RGBAColor ringColor = attrs->getColor(MaplyColor, vecInfo->color); @@ -156,7 +165,7 @@ class VectorDrawableBuilder if (drawable) flush(); - drawable = sceneRender->makeBasicDrawableBuilder("Vector Layer"); + drawable = sceneRender->makeBasicDrawableBuilder(vecBuilderName); drawMbr.reset(); drawable->setType(primType); vecInfo->setupBasicDrawable(drawable); @@ -172,13 +181,13 @@ class VectorDrawableBuilder for (unsigned int jj=0;jjgetCoordSystem()->geographicToLocal(geoCoordD); - Point3d norm3d = coordAdapter->normalForLocal(localPt); - Point3f norm(norm3d.x(),norm3d.y(),norm3d.z()); - Point3d pt3d = coordAdapter->localToDisplay(localPt) - center; - Point3f pt(pt3d.x(),pt3d.y(),pt3d.z()); + const Point2f &geoPt = pts[jj]; + const Point2d geoCoordD(geoPt.x()+geoCenter.x(),geoPt.y()+geoCenter.y()); + const Point3d localPt = coordAdapter->getCoordSystem()->geographicToLocal(geoCoordD); + const Point3d norm3d = coordAdapter->normalForLocal(localPt); + const Point3f norm(norm3d.x(),norm3d.y(),norm3d.z()); + const Point3d pt3d = coordAdapter->localToDisplay(localPt) - center; + const Point3f pt(pt3d.x(),pt3d.y(),pt3d.z()); // Add to drawable // Depending on the type, we do this differently @@ -234,19 +243,19 @@ class VectorDrawableBuilder sceneRep->drawIDs.insert(drawable->getDrawableID()); if (centerValid) { - Eigen::Affine3d trans(Eigen::Translation3d(center.x(),center.y(),center.z())); + const Eigen::Affine3d trans(Eigen::Translation3d(center.x(),center.y(),center.z())); Matrix4d transMat = trans.matrix(); drawable->setMatrix(&transMat); } if (vecInfo->fade > 0.0) { - TimeInterval curTime = scene->getCurrentTime(); + const TimeInterval curTime = scene->getCurrentTime(); drawable->setFade(curTime,curTime+vecInfo->fade); } changeRequests.push_back(new AddDrawableReq(drawable->getDrawable())); } - drawable = NULL; + drawable = nullptr; } } @@ -262,7 +271,7 @@ class VectorDrawableBuilder Point3d center; Point2d geoCenter; bool centerValid; - GeometryType primType; + const GeometryType primType; }; /* Drawable Builder (Triangle version) @@ -274,8 +283,16 @@ class VectorDrawableBuilderTri public: VectorDrawableBuilderTri(Scene *scene,SceneRenderer *sceneRender,ChangeSet &changeRequests,VectorSceneRep *sceneRep, const VectorInfo *vecInfo,bool doColor) - : changeRequests(changeRequests), scene(scene), sceneRender(sceneRender), sceneRep(sceneRep), - vecInfo(vecInfo), drawable(NULL), centerValid(false), center(0,0,0), doColor(doColor), geoCenter(0,0) + : changeRequests(changeRequests) + , scene(scene) + , sceneRender(sceneRender) + , sceneRep(sceneRep) + , vecInfo(vecInfo) + , drawable(nullptr) + , centerValid(false) + , center(0,0,0) + , doColor(doColor) + , geoCenter(0,0) { } @@ -291,8 +308,8 @@ class VectorDrawableBuilderTri geoCenter = newGeoCenter; } - // This version converts a ring into a mesh (chopping, tesselating, etc...) - void addPoints(VectorRing &ring,MutableDictionaryRef attrs) + // This version converts a ring into a mesh (chopping, tessellating, etc...) + void addPoints(const VectorRing &ring,const MutableDictionaryRef &attrs) { // Grid subdivision is done here std::vector inRings; @@ -300,6 +317,7 @@ class VectorDrawableBuilderTri ClipLoopToGrid(ring, Point2f(0.0,0.0), Point2f(vecInfo->subdivEps,vecInfo->subdivEps), inRings); else inRings.push_back(ring); + VectorTrianglesRef mesh(VectorTriangles::createTriangles()); for (unsigned int ii=0;ii inRings; @@ -321,6 +339,7 @@ class VectorDrawableBuilderTri ClipLoopToGrid(ring, Point2f(0.0,0.0), Point2f(vecInfo->subdivEps,vecInfo->subdivEps), inRings); else inRings.push_back(ring); + VectorTrianglesRef mesh(VectorTriangles::createTriangles()); for (unsigned int ii=0;ii &rings,MutableDictionaryRef attrs) + // This version converts a ring into a mesh (chopping, tessellating, etc...) + void addPoints(const std::vector &rings,const MutableDictionaryRef &attrs) { // Grid subdivision is done here std::vector inRings; @@ -338,6 +357,7 @@ class VectorDrawableBuilderTri ClipLoopToGrid(rings[ii], Point2f(0.0,0.0), Point2f(vecInfo->subdivEps,vecInfo->subdivEps), inRings); else inRings = rings; + VectorTrianglesRef mesh(VectorTriangles::createTriangles()); TesselateLoops(inRings, mesh); @@ -345,7 +365,7 @@ class VectorDrawableBuilderTri } // If it's a mesh, we're assuming it's been fully processed (triangulated, chopped, and so on) - void addPoints(VectorTrianglesRef mesh,MutableDictionaryRef attrs) + void addPoints(const VectorTrianglesRef &mesh, const MutableDictionaryRef &attrs) { RGBAColor ringColor = attrs->getColor(MaplyColor, vecInfo->color); @@ -363,8 +383,8 @@ class VectorDrawableBuilderTri mesh->getTriangle(ir, pts); // Decide if we'll appending to an existing drawable or // create a new one - int ptCount = (int)pts.size(); - int triCount = (int)(pts.size()-2); + const int ptCount = (int)pts.size(); + const int triCount = (int)(pts.size()-2); if (!drawable || (drawable->getNumPoints()+ptCount > MaxDrawablePoints) || (drawable->getNumTris()+triCount > MaxDrawableTriangles)) @@ -373,7 +393,7 @@ class VectorDrawableBuilderTri if (drawable) flush(); - drawable = sceneRender->makeBasicDrawableBuilder("Vector Layer"); + drawable = sceneRender->makeBasicDrawableBuilder(vecBuilderName); drawMbr.reset(); drawable->setType(Triangles); vecInfo->setupBasicDrawable(drawable); @@ -392,7 +412,7 @@ class VectorDrawableBuilderTri Point3d planeOrg(0,0,0),planeUp(0,0,1),planeX(1,0,0),planeY(0,1,0); if (vecInfo->texProj == TextureProjectionTanPlane) { - Point3d localPt = coordAdapter->getCoordSystem()->geographicToLocal3d(GeoCoord(centroid.x(),centroid.y())); + const Point3d localPt = coordAdapter->getCoordSystem()->geographicToLocal3d(GeoCoord(centroid.x(),centroid.y())); planeOrg = coordAdapter->localToDisplay(localPt); planeUp = coordAdapter->normalForLocal(localPt); planeX = Point3d(0,0,1).cross(planeUp); @@ -413,17 +433,17 @@ class VectorDrawableBuilderTri TexCoord minCoord(MAXFLOAT,MAXFLOAT); for (unsigned int jj=0;jjtexProj) { case TextureProjectionTanPlane: { - Point3d dispPt = coordAdapter->localToDisplay(coordAdapter->getCoordSystem()->geographicToLocal(geoCoordD))-center; - Point3d dir = dispPt - planeOrg; - Point3d comp(dir.dot(planeX),dir.dot(planeY),dir.dot(planeUp)); + const Point3d dispPt = coordAdapter->localToDisplay(coordAdapter->getCoordSystem()->geographicToLocal(geoCoordD))-center; + const Point3d dir = dispPt - planeOrg; + const Point3d comp(dir.dot(planeX),dir.dot(planeY),dir.dot(planeUp)); texCoord.x() = comp.x() * vecInfo->texScale.x(); texCoord.y() = comp.y() * vecInfo->texScale.y(); } @@ -457,13 +477,13 @@ class VectorDrawableBuilderTri for (unsigned int jj=0;jjgetCoordSystem()->geographicToLocal(geoCoordD); - Point3d norm3d = coordAdapter->normalForLocal(localPt); - Point3f norm(norm3d.x(),norm3d.y(),norm3d.z()); - Point3d pt3d = coordAdapter->localToDisplay(localPt) - center; - Point3f pt(pt3d.x(),pt3d.y(),pt3d.z()); + const Point2f &geoPt = pts[jj]; + const Point2d geoCoordD(geoPt.x()+geoCenter.x(),geoPt.y()+geoCenter.y()); + const Point3d localPt = coordAdapter->getCoordSystem()->geographicToLocal(geoCoordD); + const Point3d norm3d = coordAdapter->normalForLocal(localPt); + const Point3f norm(norm3d.x(),norm3d.y(),norm3d.z()); + const Point3d pt3d = coordAdapter->localToDisplay(localPt) - center; + const Point3f pt(pt3d.x(),pt3d.y(),pt3d.z()); drawable->addPoint(pt); if (doColor) @@ -485,37 +505,36 @@ class VectorDrawableBuilderTri void flush() { if (drawable) - { + { if (drawable->getNumPoints() > 0) { // If we're doing screen coordinates, attach the tweaker if (vecInfo->texProj == TextureProjectionScreen) { - Point2f midPt = drawMbr.mid(); - Point3d centerPt = scene->getCoordAdapter()->localToDisplay(Point3d(midPt.x(),midPt.y(),0.0)); - Point2d texScale(vecInfo->texScale.x(),vecInfo->texScale.y()); - BasicDrawableScreenTexTweaker *texTweaker = new BasicDrawableScreenTexTweaker(centerPt,texScale); - drawable->addTweaker(DrawableTweakerRef(texTweaker)); + const Point2f midPt = drawMbr.mid(); + const Point3d centerPt = scene->getCoordAdapter()->localToDisplay(Point3d(midPt.x(),midPt.y(),0.0)); + const Point2d texScale(vecInfo->texScale.x(),vecInfo->texScale.y()); + drawable->addTweaker(std::make_shared(centerPt,texScale)); } drawable->setLocalMbr(drawMbr); if (centerValid) { - Eigen::Affine3d trans(Eigen::Translation3d(center.x(),center.y(),center.z())); - Matrix4d transMat = trans.matrix(); + const Eigen::Affine3d trans(Eigen::Translation3d(center.x(),center.y(),center.z())); + const Matrix4d transMat = trans.matrix(); drawable->setMatrix(&transMat); } sceneRep->drawIDs.insert(drawable->getDrawableID()); if (vecInfo->fade > 0.0) { - TimeInterval curTime = scene->getCurrentTime(); + const TimeInterval curTime = scene->getCurrentTime(); drawable->setFade(curTime,curTime+vecInfo->fade); } changeRequests.push_back(new AddDrawableReq(drawable->getDrawable())); } - drawable = NULL; + drawable = nullptr; } } @@ -539,12 +558,15 @@ VectorManager::VectorManager() VectorManager::~VectorManager() { - for (VectorSceneRepSet::iterator it = vectorReps.begin(); - it != vectorReps.end(); ++it) - delete *it; + std::lock_guard guardLock(lock); + + for (auto it : vectorReps) + delete it; vectorReps.clear(); } +static const std::string colorStr("color"); + // TODO: Get rid of this version SimpleIdentity VectorManager::addVectors(ShapeSet *shapes, const VectorInfo &vecInfo, ChangeSet &changes) { @@ -560,9 +582,9 @@ SimpleIdentity VectorManager::addVectors(ShapeSet *shapes, const VectorInfo &vec // Look for per vector colors bool doColors = false; - for (ShapeSet::iterator it = shapes->begin();it != shapes->end(); ++it) + for (auto it : *shapes) { - if ((*it)->getAttrDict()->hasField("color")) + if (it->getAttrDict()->hasField(colorStr)) { doColors = true; break; @@ -583,7 +605,7 @@ SimpleIdentity VectorManager::addVectors(ShapeSet *shapes, const VectorInfo &vec { geoCenter.x() = vecInfo.vecCenter.x(); geoCenter.y() = vecInfo.vecCenter.y(); - Point3d dispPt = coordAdapter->localToDisplay(coordSys->geographicToLocal(geoCenter)); + const Point3d dispPt = coordAdapter->localToDisplay(coordSys->geographicToLocal(geoCenter)); center = dispPt; centerValid = true; } else { @@ -593,8 +615,8 @@ SimpleIdentity VectorManager::addVectors(ShapeSet *shapes, const VectorInfo &vec geoMbr.expand((*it)->calcGeoMbr()); if (geoMbr.valid()) { - Point3d p0 = coordAdapter->localToDisplay(coordSys->geographicToLocal3d(geoMbr.ll())); - Point3d p1 = coordAdapter->localToDisplay(coordSys->geographicToLocal3d(geoMbr.ur())); + const Point3d p0 = coordAdapter->localToDisplay(coordSys->geographicToLocal3d(geoMbr.ll())); + const Point3d p1 = coordAdapter->localToDisplay(coordSys->geographicToLocal3d(geoMbr.ur())); center = (p0+p1)/2.0; centerValid = true; } @@ -625,7 +647,7 @@ SimpleIdentity VectorManager::addVectors(ShapeSet *shapes, const VectorInfo &vec // Work through the loops for (unsigned int ri=0;riloops.size();ri++) { - VectorRing &ring = theAreal->loops[ri]; + const VectorRing &ring = theAreal->loops[ri]; // Break the edges around the globe (presumably) if (vecInfo.sample > 0.0) @@ -695,7 +717,7 @@ SimpleIdentity VectorManager::addVectors(ShapeSet *shapes, const VectorInfo &vec SimpleIdentity vecID = sceneRep->getId(); { - std::lock_guard guardLock(vectorLock); + std::lock_guard guardLock(lock); vectorReps.insert(sceneRep); } @@ -705,9 +727,11 @@ SimpleIdentity VectorManager::addVectors(ShapeSet *shapes, const VectorInfo &vec // TODO: Take a reference instead of a pointer SimpleIdentity VectorManager::addVectors(const std::vector *shapes, const VectorInfo &vecInfo, ChangeSet &changes) { - if (shapes->empty()) + if (!shapes || shapes->empty()) + { return EmptyIdentity; - + } + VectorSceneRep *sceneRep = new VectorSceneRep(); sceneRep->fade = vecInfo.fade; @@ -716,13 +740,16 @@ SimpleIdentity VectorManager::addVectors(const std::vector *shap // bool linesOrPoints = (thePoints.get() ? false : true); // Look for per vector colors - bool doColors = false; - for (auto it = shapes->begin();it != shapes->end(); ++it) + bool doColors = (vecInfo.colorExp || vecInfo.opacityExp); + if (!doColors) { - if ((*it)->getAttrDict()->hasField("color")) + for (const auto &shape : *shapes) { - doColors = true; - break; + if (shape->getAttrDict()->hasField(colorStr)) + { + doColors = true; + break; + } } } @@ -740,7 +767,7 @@ SimpleIdentity VectorManager::addVectors(const std::vector *shap { geoCenter.x() = vecInfo.vecCenter.x(); geoCenter.y() = vecInfo.vecCenter.y(); - Point3d dispPt = coordAdapter->localToDisplay(coordSys->geographicToLocal(geoCenter)); + const Point3d dispPt = coordAdapter->localToDisplay(coordSys->geographicToLocal(geoCenter)); center = dispPt; centerValid = true; } else { @@ -750,8 +777,8 @@ SimpleIdentity VectorManager::addVectors(const std::vector *shap geoMbr.expand((*it)->calcGeoMbr()); if (geoMbr.valid()) { - Point3d p0 = coordAdapter->localToDisplay(coordSys->geographicToLocal3d(geoMbr.ll())); - Point3d p1 = coordAdapter->localToDisplay(coordSys->geographicToLocal3d(geoMbr.ur())); + const Point3d p0 = coordAdapter->localToDisplay(coordSys->geographicToLocal3d(geoMbr.ll())); + const Point3d p1 = coordAdapter->localToDisplay(coordSys->geographicToLocal3d(geoMbr.ur())); center = (p0+p1)/2.0; centerValid = true; } @@ -776,13 +803,13 @@ SimpleIdentity VectorManager::addVectors(const std::vector *shap if (vecInfo.filled) { - // Trianglate outside and loops + // Triangulate outside and loops drawBuildTri.addPoints(theAreal->loops,theAreal->getAttrDict()); } else { // Work through the loops for (unsigned int ri=0;riloops.size();ri++) { - VectorRing &ring = theAreal->loops[ri]; + const VectorRing &ring = theAreal->loops[ri]; // Break the edges around the globe (presumably) if (vecInfo.sample > 0.0) @@ -852,7 +879,7 @@ SimpleIdentity VectorManager::addVectors(const std::vector *shap SimpleIdentity vecID = sceneRep->getId(); { - std::lock_guard guardLock(vectorLock); + std::lock_guard guardLock(lock); vectorReps.insert(sceneRep); } @@ -863,7 +890,7 @@ SimpleIdentity VectorManager::instanceVectors(SimpleIdentity vecID,const VectorI { SimpleIdentity newId = EmptyIdentity; - std::lock_guard guardLock(vectorLock); + std::lock_guard guardLock(lock); // Look for the representation VectorSceneRep dummyRep(vecID); @@ -909,7 +936,7 @@ SimpleIdentity VectorManager::instanceVectors(SimpleIdentity vecID,const VectorI void VectorManager::changeVectors(SimpleIdentity vecID,const VectorInfo &vecInfo,ChangeSet &changes) { - std::lock_guard guardLock(vectorLock); + std::lock_guard guardLock(lock); VectorSceneRep dummyRep(vecID); VectorSceneRepSet::iterator it = vectorReps.find(&dummyRep); @@ -927,7 +954,10 @@ void VectorManager::changeVectors(SimpleIdentity vecID,const VectorInfo &vecInfo changes.push_back(new ColorChangeRequest(*idIt, vecInfo.color)); // Changed visibility - changes.push_back(new VisibilityChangeRequest(*idIt, vecInfo.minVis, vecInfo.maxVis)); + if (vecInfo.minVis != DrawVisibleInvalid || vecInfo.maxVis != DrawVisibleInvalid) + { + changes.push_back(new VisibilityChangeRequest(*idIt, vecInfo.minVis, vecInfo.maxVis)); + } // Changed line width changes.push_back(new LineWidthChangeRequest(*idIt, vecInfo.lineWidth)); @@ -943,7 +973,7 @@ void VectorManager::changeVectors(SimpleIdentity vecID,const VectorInfo &vecInfo void VectorManager::removeVectors(SimpleIDSet &vecIDs,ChangeSet &changes) { - std::lock_guard guardLock(vectorLock); + std::lock_guard guardLock(lock); const TimeInterval curTime = scene->getCurrentTime(); for (const auto id : vecIDs) @@ -977,7 +1007,7 @@ void VectorManager::removeVectors(SimpleIDSet &vecIDs,ChangeSet &changes) void VectorManager::enableVectors(SimpleIDSet &vecIDs,bool enable,ChangeSet &changes) { - std::lock_guard guardLock(vectorLock); + std::lock_guard guardLock(lock); for (SimpleIDSet::iterator vIt = vecIDs.begin();vIt != vecIDs.end();++vIt) { diff --git a/common/WhirlyGlobeLib/src/VectorObject.cpp b/common/WhirlyGlobeLib/src/VectorObject.cpp index 67d5c9c206..8a0d07965d 100644 --- a/common/WhirlyGlobeLib/src/VectorObject.cpp +++ b/common/WhirlyGlobeLib/src/VectorObject.cpp @@ -79,10 +79,9 @@ bool VectorObject::fromShapeFile(const std::string &fileName) return false; const int numObj = shapeReader.getNumObjects(); - shapes.reserve(numObj); + shapes.reserve(shapes.size() + numObj); for (unsigned int ii=0;ii(shape); - if (areal && areal->loops.size() > 0) - { - for (unsigned int ii=0;iiloops.size();ii++) - { - const float area = std::abs(CalcLoopArea(areal->loops[ii])); - if (area > bigArea) + if (const auto areal = dynamic_cast(shape)) { + if (areal->loops.size() > 0) { + for (unsigned int ii=0;iiloops.size();ii++) { - bigLoop = &areal->loops[ii]; - bigArea = area; + const float area = CalcLoopArea(areal->loops[ii]); + if (std::abs(area) > std::abs(bigArea)) + { + bigLoop = &areal->loops[ii]; + bigArea = area; + } } } } else if (const auto linear = dynamic_cast(shape)) { @@ -170,9 +169,9 @@ bool VectorObject::centroid(Point2d ¢roid) const } } - if (bigLoop && bigArea >= 0) // TODO: >? + if (bigLoop && bigArea != 0) { - const Point2f centroid2f = CalcLoopCentroid(*bigLoop); + const Point2f centroid2f = CalcLoopCentroid(*bigLoop, bigArea); centroid.x() = centroid2f.x(); centroid.y() = centroid2f.y(); return true; diff --git a/common/WhirlyGlobeLib/src/VectorOffset.cpp b/common/WhirlyGlobeLib/src/VectorOffset.cpp new file mode 100644 index 0000000000..0a5ffd5785 --- /dev/null +++ b/common/WhirlyGlobeLib/src/VectorOffset.cpp @@ -0,0 +1,98 @@ +/* + * VectorOffset.cpp + * WhirlyGlobeLib + * + * Created by Steve Gifford on 3/4/21. + * Copyright 2011-2021 mousebird consulting + * + * 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. + * + */ + +#import "VectorOffset.h" +#import "clipper.hpp" + +using namespace ClipperLib; + +namespace WhirlyKit +{ + +static float PolyScale = 1e6; + +std::vector BufferLinear(const VectorRing &ring, float offset) +{ + // If the offset is negative, just flip the geometry + VectorRing theRing = ring; + if (offset < 0) { + offset *= -1.0; + std::reverse(theRing.begin(), theRing.end()); + } + + Mbr mbr(ring); + Point2f org = mbr.ll(); + + Path path; + for (const auto &pt: ring) + path.push_back(IntPoint((pt.x() - org.x()) * PolyScale, (pt.y() - org.y()) * PolyScale)); + + ClipperOffset co; + Paths soln; + co.AddPath(path, jtMiter, etOpenButt); + co.Execute(soln, offset * PolyScale); + + std::vector rets; + for (unsigned int ii=0;ii 2) + rets.push_back(outRing); + } + return rets; +} + +/// Offset/buffer a polygon into one or more loops +std::vector BufferPolygon(const VectorRing &ring, float offset) +{ + Point2f org = ring[0]; + + Path path; + for (const auto &pt: ring) + path.push_back(IntPoint((pt.x() - org.x()) * PolyScale, (pt.y() - org.y()) * PolyScale)); + path.push_back(IntPoint((ring[0].x() - org.x()) * PolyScale, (ring[0].y() - org.y()) * PolyScale)); + + ClipperOffset co; + Paths soln; + co.AddPath(path, jtMiter, etClosedPolygon); + co.Execute(soln, offset * PolyScale); + + std::vector rets; + for (unsigned int ii=0;ii 2) + rets.push_back(outRing); + } + return rets; +} + +} diff --git a/common/WhirlyGlobeLib/src/VectorTilePBFParser.cpp b/common/WhirlyGlobeLib/src/VectorTilePBFParser.cpp index 1f8fb3abb0..6cd61f5696 100644 --- a/common/WhirlyGlobeLib/src/VectorTilePBFParser.cpp +++ b/common/WhirlyGlobeLib/src/VectorTilePBFParser.cpp @@ -154,7 +154,7 @@ bool VectorTilePBFParser::layerDecode(pb_istream_t *stream, const pb_field_iter_ return false; } - const auto layerName = std::string(layerNameView); + auto layerName = std::string(layerNameView); // When `has_extent` is false, nanopb sets the default in `extent` _layerScale = (double)layer.extent / TileSize; @@ -168,9 +168,25 @@ bool VectorTilePBFParser::layerDecode(pb_istream_t *stream, const pb_field_iter_ return true; } - // if we dont have any styles for a layer, dont bother parsing the features - if (!_styleDelegate->layerShouldDisplay(_styleInst, layerName, _tileData->ident)) - { + // if we don't have any styles for a layer, don't bother parsing the features + bool found = false; + if (_styleDelegate->layerShouldDisplay(_styleInst, layerName, _tileData->ident)) + found = true; + else { + // Try a lowercase version + // TODO: This doesn't handle non-ASCII well + std::string lowerLayerName = layerName; + std::transform(lowerLayerName.begin(), lowerLayerName.end(), lowerLayerName.begin(), + [](unsigned char c){ return std::tolower(c); }); + + if (lowerLayerName != layerName) + if (_styleDelegate->layerShouldDisplay(_styleInst, lowerLayerName, _tileData->ident)) { + found = true; + layerName = lowerLayerName; + } + } + + if (!found) { _skippedLayerCount += 1; return true; } diff --git a/common/WhirlyGlobeLib/src/VertexAttribute.cpp b/common/WhirlyGlobeLib/src/VertexAttribute.cpp index 34d7d32cab..5f504cee3d 100644 --- a/common/WhirlyGlobeLib/src/VertexAttribute.cpp +++ b/common/WhirlyGlobeLib/src/VertexAttribute.cpp @@ -147,6 +147,17 @@ void VertexAttribute::addInt(int val) (*ints).push_back(val); } +void VertexAttribute::addInt64(int64_t val) +{ + if (dataType != BDInt64Type) + return; + + if (!data) + data = new std::vector(); + std::vector *ints = (std::vector *)data; + (*ints).push_back(val); +} + /// Reserve size in the data array void VertexAttribute::reserve(int size) { @@ -200,6 +211,14 @@ void VertexAttribute::reserve(int size) ints->reserve(size); } break; + case BDInt64Type: + { + if (!data) + data = new std::vector(); + std::vector *ints = (std::vector *)data; + ints->reserve(size); + } + break; case BDDataTypeMax: break; } @@ -247,6 +266,12 @@ int VertexAttribute::numElements() const { std::vector *ints = (std::vector *)data; return (int)ints->size(); + } + break; + case BDInt64Type: + { + std::vector *ints = (std::vector *)data; + return (int)ints->size(); } case BDDataTypeMax: return 0; @@ -277,6 +302,9 @@ int VertexAttribute::size() const case BDIntType: return 4; break; + case BDInt64Type: + return 8; + break; case BDDataTypeMax: return 0; break; @@ -315,6 +343,9 @@ int SingleVertexAttributeInfo::size() const case BDIntType: return 4; break; + case BDInt64Type: + return 8; + break; case BDDataTypeMax: return 0; break; @@ -337,6 +368,12 @@ SingleVertexAttribute::SingleVertexAttribute(StringIdentity nameID,int slot,int data.intVal = intVal; } +SingleVertexAttribute::SingleVertexAttribute(StringIdentity nameID,int slot,int64_t intVal) +: SingleVertexAttributeInfo(nameID,slot,BDInt64Type) +{ + data.int64Val = intVal; +} + SingleVertexAttribute::SingleVertexAttribute(StringIdentity nameID,int slot,unsigned char colorVal[4]) : SingleVertexAttributeInfo(nameID,slot,BDChar4Type) { @@ -411,6 +448,12 @@ void VertexAttribute::clear() delete ints; } break; + case BDInt64Type: + { + std::vector *ints = (std::vector *)data; + delete ints; + } + break; case BDDataTypeMax: break; } @@ -459,6 +502,12 @@ void *VertexAttribute::addressForElement(int which) return &(*ints)[which]; } break; + case BDInt64Type: + { + std::vector *ints = (std::vector *)data; + return &(*ints)[which]; + } + break; case BDDataTypeMax: return NULL; break; diff --git a/common/WhirlyGlobeLib/src/VertexAttributeGLES.cpp b/common/WhirlyGlobeLib/src/VertexAttributeGLES.cpp index 82a9b69621..10637dc575 100644 --- a/common/WhirlyGlobeLib/src/VertexAttributeGLES.cpp +++ b/common/WhirlyGlobeLib/src/VertexAttributeGLES.cpp @@ -58,6 +58,9 @@ GLuint VertexAttributeGLES::glEntryComponents() const case BDIntType: return 1; break; + case BDInt64Type: + return 1; + break; case BDDataTypeMax: return 0; break; @@ -94,6 +97,9 @@ GLuint SingleVertexAttributeInfoGLES::glEntryComponents() const case BDIntType: return 1; break; + case BDInt64Type: + return 1; + break; case BDDataTypeMax: break; } @@ -116,6 +122,7 @@ GLenum VertexAttributeGLES::glType() const return GL_UNSIGNED_BYTE; break; case BDIntType: + case BDInt64Type: return GL_INT; break; case BDDataTypeMax: @@ -140,6 +147,7 @@ GLenum SingleVertexAttributeInfoGLES::glType() const return GL_UNSIGNED_BYTE; break; case BDIntType: + case BDInt64Type: return GL_INT; break; case BDDataTypeMax: @@ -159,6 +167,7 @@ GLboolean VertexAttributeGLES::glNormalize() const case BDFloat2Type: case BDFloatType: case BDIntType: + case BDInt64Type: return GL_FALSE; break; case BDChar4Type: @@ -180,6 +189,7 @@ GLboolean SingleVertexAttributeInfoGLES::glNormalize() const case BDFloat2Type: case BDFloatType: case BDIntType: + case BDInt64Type: return GL_FALSE; break; case BDChar4Type: @@ -211,6 +221,7 @@ void VertexAttributeGLES::glSetDefault(int index) const glVertexAttrib4f(index, defaultData.color[0] / 255.0, defaultData.color[1] / 255.0, defaultData.color[2] / 255.0, defaultData.color[3] / 255.0); break; case BDIntType: + case BDInt64Type: glVertexAttrib1f(index, defaultData.intVal); break; case BDDataTypeMax: diff --git a/common/WhirlyGlobeLib/src/WhirlyGeometry.cpp b/common/WhirlyGlobeLib/src/WhirlyGeometry.cpp index fec66b3ab5..d50700cb5a 100644 --- a/common/WhirlyGlobeLib/src/WhirlyGeometry.cpp +++ b/common/WhirlyGlobeLib/src/WhirlyGeometry.cpp @@ -1,9 +1,8 @@ -/* - * WhirlyGeometry.mm +/* WhirlyGeometry.cpp * WhirlyGlobeLib * * Created by Steve Gifford on 1/18/11. - * Copyright 2011-2019 mousebird consulting + * Copyright 2011-2021 mousebird consulting * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -15,7 +14,6 @@ * 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. - * */ #import "WhirlyGeometry.h" diff --git a/common/WhirlyGlobeLib/src/WhirlyOctEncoding.cpp b/common/WhirlyGlobeLib/src/WhirlyOctEncoding.cpp index 85da6bbef0..a9897ca5fb 100644 --- a/common/WhirlyGlobeLib/src/WhirlyOctEncoding.cpp +++ b/common/WhirlyGlobeLib/src/WhirlyOctEncoding.cpp @@ -1,9 +1,8 @@ -/* - * WhirlyOctEncoding.mm +/* WhirlyOctEncoding.cpp * WhirlyGlobeLib * * Created by @jmnavarro on 7/2/15. - * Copyright 2011-2019 mousebird consulting + * Copyright 2011-2021 mousebird consulting * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -15,7 +14,6 @@ * 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. - * */ #import "WhirlyOctEncoding.h" diff --git a/common/WhirlyGlobeLib/src/WideVectorDrawableBuilder.cpp b/common/WhirlyGlobeLib/src/WideVectorDrawableBuilder.cpp index 10137e22f0..ab90e35423 100644 --- a/common/WhirlyGlobeLib/src/WideVectorDrawableBuilder.cpp +++ b/common/WhirlyGlobeLib/src/WideVectorDrawableBuilder.cpp @@ -1,9 +1,8 @@ -/* - * WideVectorDrawable.mm +/* WideVectorDrawable.mm * WhirlyGlobeLib * * Created by Steve Gifford on 5/29/14. - * Copyright 2011-2019 mousebird consulting + * Copyright 2011-2021 mousebird consulting * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -15,10 +14,10 @@ * 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. - * */ #import "WideVectorDrawableBuilder.h" +#import "BasicDrawableInstanceBuilder.h" #import "SceneRenderer.h" #import "FlatMath.h" @@ -27,8 +26,14 @@ using namespace Eigen; namespace WhirlyKit { -WideVectorDrawableBuilder::WideVectorDrawableBuilder() - : texRepeat(1.0), edgeSize(1.0), realWidthSet(false), globeMode(true), color(255,255,255,255) +WideVectorDrawableBuilder::WideVectorDrawableBuilder(std::string name, + const SceneRenderer *sceneRenderer, + Scene *scene) + : name(std::move(name)), renderer(sceneRenderer), scene(scene), + implType(WideVecImplBasic), basicDrawable(nullptr), instDrawable(nullptr), + lineWidth(1.0), lineOffset(0.0), globeMode(false), + texRepeat(1.0), edgeSize(1.0), + p1_index(-1), n0_index(-1), offset_index(-1), c0_index(-1), tex_index(-1) { } @@ -36,35 +41,55 @@ WideVectorDrawableBuilder::~WideVectorDrawableBuilder() { } -void WideVectorDrawableBuilder::Init(unsigned int numVert,unsigned int numTri,bool inGlobeMode) +void WideVectorDrawableBuilder::Init(unsigned int numVert, + unsigned int numTri, + unsigned int numCenterline, + WideVecImplType implType, + bool inGlobeMode, + const WideVectorInfo *vecInfo) { globeMode = inGlobeMode; + this->implType = implType; - BasicDrawableBuilder::Init(); - // Don't want standard attributes - - points.reserve(numVert); - tris.reserve(numTri); + basicDrawable = renderer->makeBasicDrawableBuilder(name); + basicDrawable->Init(); + basicDrawable->setupStandardAttributes(); + basicDrawable->setType(Triangles); + basicDrawable->points.reserve(numVert); + basicDrawable->tris.reserve(numTri); + if (implType == WideVecImplPerf) { + instDrawable = renderer->makeBasicDrawableInstanceBuilder(name); + // TODO: Reserve size for the instances + basicDrawable->setOnOff(false); + vecInfo->setupBasicDrawableInstance(instDrawable); + instDrawable->setMasterID(basicDrawable->basicDraw->getId(), BasicDrawableInstance::LocalStyle); + inst_index = addAttribute(BDIntType, StringIndexer::getStringID("a_instIndex"),numVert); + } else { + vecInfo->setupBasicDrawable(basicDrawable); + } + lineWidth = 10.0/1024.0; - if (globeMode) - basicDraw->normalEntry = addAttribute(BDFloat3Type, a_normalNameID,numVert); - basicDraw->colorEntry = addAttribute(BDChar4Type, a_colorNameID); - p1_index = addAttribute(BDFloat3Type, StringIndexer::getStringID("a_p1"),numVert); - tex_index = addAttribute(BDFloat4Type, StringIndexer::getStringID("a_texinfo"),numVert); - n0_index = addAttribute(BDFloat3Type, StringIndexer::getStringID("a_n0"),numVert); - c0_index = addAttribute(BDFloatType, StringIndexer::getStringID("a_c0"),numVert); -} - -void WideVectorDrawableBuilder::setColor(RGBAColor inColor) -{ - color = inColor; + lineOffset = 0.0; + if (implType == WideVecImplBasic) { + p1_index = addAttribute(BDFloat3Type, StringIndexer::getStringID("a_p1"),numVert); + tex_index = addAttribute(BDFloat4Type, StringIndexer::getStringID("a_texinfo"),numVert); + n0_index = addAttribute(BDFloat3Type, StringIndexer::getStringID("a_n0"),numVert); + offset_index = addAttribute(BDFloat3Type, StringIndexer::getStringID("a_offset"),numVert); + c0_index = addAttribute(BDFloatType, StringIndexer::getStringID("a_c0"),numVert); + } } void WideVectorDrawableBuilder::setLineWidth(float inWidth) { lineWidth = inWidth; } + +void WideVectorDrawableBuilder::setLineOffset(float inOffset) +{ + lineOffset = inOffset; + lineOffsetSet = true; +} void WideVectorDrawableBuilder::setTexRepeat(float inTexRepeat) { texRepeat = inTexRepeat; } @@ -72,32 +97,30 @@ void WideVectorDrawableBuilder::setTexRepeat(float inTexRepeat) void WideVectorDrawableBuilder::setEdgeSize(float inEdgeSize) { edgeSize = inEdgeSize; } -void WideVectorDrawableBuilder::setRealWorldWidth(double width) - { realWidthSet = true; realWidth = width; } - - unsigned int WideVectorDrawableBuilder::addPoint(const Point3f &pt) { #ifdef WIDEVECDEBUG locPts.push_back(pt); #endif - return BasicDrawableBuilder::addPoint(pt); + basicDrawable->addPoint(pt); + + return basicDrawable->getNumPoints()-1; +} + +void WideVectorDrawableBuilder::addInstancePoint(const Point3f &pt,int vertIndex,int polyIndex) +{ + basicDrawable->addPoint(pt); + basicDrawable->addAttributeValue(inst_index, polyIndex | (vertIndex << 16)); } void WideVectorDrawableBuilder::addNormal(const Point3f &norm) { - if (globeMode) - { - BasicDrawableBuilder::addNormal(norm); - } + basicDrawable->addNormal(norm); } void WideVectorDrawableBuilder::addNormal(const Point3d &norm) { - if (globeMode) - { - BasicDrawableBuilder::addNormal(norm); - } + basicDrawable->addNormal(norm); } void WideVectorDrawableBuilder::add_p1(const Point3f &pt) @@ -123,6 +146,11 @@ void WideVectorDrawableBuilder::add_n0(const Point3f &dir) #endif } +void WideVectorDrawableBuilder::add_offset(const Point3f &offset) +{ + addAttributeValue(offset_index, offset); +} + void WideVectorDrawableBuilder::add_c0(float val) { addAttributeValue(c0_index, val); @@ -131,21 +159,216 @@ void WideVectorDrawableBuilder::add_c0(float val) #endif } +void WideVectorDrawableBuilder::setColorExpression(ColorExpressionInfoRef colorExp) +{ + this->colorExp = std::move(colorExp); +} + +void WideVectorDrawableBuilder::setOpacityExpression(FloatExpressionInfoRef opacityExp) +{ + this->opacityExp = std::move(opacityExp); +} + void WideVectorDrawableBuilder::setWidthExpression(FloatExpressionInfoRef inWidthExp) { - widthExp = inWidthExp; + widthExp = std::move(inWidthExp); } -void WideVectorDrawableBuilder::setupTweaker(BasicDrawable *theDraw) +void WideVectorDrawableBuilder::setOffsetExpression(FloatExpressionInfoRef inOffsetExp) { - WideVectorTweaker *tweak = makeTweaker(); - tweak->realWidthSet = false; - tweak->realWidth = realWidth; - tweak->edgeSize = edgeSize; - tweak->lineWidth = lineWidth; - tweak->texRepeat = texRepeat; - tweak->color = color; - theDraw->addTweaker(DrawableTweakerRef(tweak)); -} - + offsetExp = std::move(inOffsetExp); +} + +void WideVectorDrawableBuilder::setupTweaker(BasicDrawable &theDraw) const +{ + if (auto tweaker = makeTweaker()) + { + setupTweaker(tweaker); + theDraw.addTweaker(tweaker); + } +} + +void WideVectorDrawableBuilder::setupTweaker(const DrawableTweakerRef &inTweaker) const +{ + basicDrawable->setupTweaker(inTweaker); + if (auto tweak = dynamic_cast(inTweaker.get())) + { + tweak->edgeSize = edgeSize; + tweak->lineWidth = lineWidth; + tweak->widthExp = widthExp; + tweak->texRepeat = texRepeat; + tweak->offset = lineOffset; + tweak->offsetSet = lineOffsetSet; + tweak->offsetExp = offsetExp; + tweak->color = color; + } +} + +void WideVectorDrawableBuilder::addCenterLine(const Point3d ¢erPt, + const Point3d &up, + double len, + const RGBAColor &inColor, + const std::vector &maskIDs, + int prev,int next) +{ + CenterPoint pt; + pt.center = Point3f(centerPt.x(),centerPt.y(),centerPt.z()); + pt.up = Point3f(up.x(),up.y(),up.z()); + pt.len = (float)len; + pt.color = inColor; + pt.maskIDs[0] = maskIDs.empty() ? 0 : (int)maskIDs[0]; + pt.maskIDs[1] = maskIDs.size() > 1 ? (int)maskIDs[1] : 0; + pt.prev = prev; + pt.next = next; + centerline.push_back(pt); } + +/// Number of points added so far +unsigned int WideVectorDrawableBuilder::getNumPoints() +{ + return basicDrawable->getNumPoints(); +} + +/// Numer of triangles added so far +unsigned int WideVectorDrawableBuilder::getNumTris() +{ + return basicDrawable->getNumTris(); +} + +int WideVectorDrawableBuilder::getCenterLineCount() +{ + return centerline.size(); +} + +SimpleIdentity WideVectorDrawableBuilder::getBasicDrawableID() +{ + if (!basicDrawable) + return EmptyIdentity; + return basicDrawable->getDrawableID(); +} + +BasicDrawableRef WideVectorDrawableBuilder::getBasicDrawable() +{ + if (!basicDrawable) + return nullptr; + return basicDrawable->getDrawable(); +} + +SimpleIdentity WideVectorDrawableBuilder::getInstanceDrawableID() +{ + if (!instDrawable) + return EmptyIdentity; + return instDrawable->getDrawableID(); +} + +BasicDrawableInstanceRef WideVectorDrawableBuilder::getInstanceDrawable() +{ + if (!instDrawable) + return nullptr; + return instDrawable->getDrawable(); +} + +void WideVectorDrawableBuilder::setFade(TimeInterval inFadeDown,TimeInterval inFadeUp) +{ + if (instDrawable) + instDrawable->setFade(inFadeDown, inFadeUp); + else + basicDrawable->setFade(inFadeDown, inFadeUp); +} + +void WideVectorDrawableBuilder::setColor(RGBAColor inColor) +{ + color = inColor; + if (instDrawable) + instDrawable->setColor(inColor); + else + basicDrawable->setColor(inColor); +} + + +void WideVectorDrawableBuilder::setLocalMbr(const Mbr &) +{ +// instDrawable->setLocalMbr(mbr); +} + +void WideVectorDrawableBuilder::setMatrix(const Eigen::Matrix4d *inMat) +{ + if (instDrawable) { + // TODO: Add matrix offset to instance +// instDrawable->setMatrix(inMat); + } else + basicDrawable->setMatrix(inMat); +} + +void WideVectorDrawableBuilder::addVertexAttributes(const SingleVertexAttributeSet &attrs) +{ + basicDrawable->addVertexAttributes(attrs); +} + +/// Add a 2D vector to the given attribute array +void WideVectorDrawableBuilder::addAttributeValue(int attrId,const Eigen::Vector2f &vec) +{ + basicDrawable->addAttributeValue(attrId,vec); +} + +/// Add a 3D vector to the given attribute array +void WideVectorDrawableBuilder::addAttributeValue(int attrId,const Eigen::Vector3f &vec) +{ + basicDrawable->addAttributeValue(attrId,vec); +} + +/// Add a 4D vector to the given attribute array +void WideVectorDrawableBuilder::addAttributeValue(int attrId,const Eigen::Vector4f &vec) +{ + basicDrawable->addAttributeValue(attrId,vec); +} + +/// Add a 4 component char array to the given attribute array +void WideVectorDrawableBuilder::addAttributeValue(int attrId,const RGBAColor &theColor) +{ + basicDrawable->addAttributeValue(attrId,theColor); +} + +/// Add a float to the given attribute array +void WideVectorDrawableBuilder::addAttributeValue(int attrId,float theColor) +{ + basicDrawable->addAttributeValue(attrId,theColor); +} + +/// Add an integer value to the given attribute array +void WideVectorDrawableBuilder::addAttributeValue(int attrId,int val) +{ + basicDrawable->addAttributeValue(attrId,val); +} + +/// Add an identity-type value to the given attribute array +void WideVectorDrawableBuilder::addAttributeValue(int attrId,int64_t val) +{ + basicDrawable->addAttributeValue(attrId,val); +} + +/// Add a triangle. Should point to the vertex IDs. +void WideVectorDrawableBuilder::addTriangle(BasicDrawable::Triangle tri) +{ + basicDrawable->addTriangle(tri); +} + +void WideVectorDrawableBuilder::setTexId(unsigned int which,SimpleIdentity inId) +{ + if (instDrawable) + instDrawable->setTexId(which, inId); + else + basicDrawable->setTexId(which, inId); +} + +void WideVectorDrawableBuilder::setProgram(SimpleIdentity progId) +{ + if (instDrawable) + instDrawable->setProgram(progId); + else + basicDrawable->setProgram(progId); +} + +} + +#include diff --git a/common/WhirlyGlobeLib/src/WideVectorDrawableBuilderGLES.cpp b/common/WhirlyGlobeLib/src/WideVectorDrawableBuilderGLES.cpp index c225cb680c..81a1737c45 100644 --- a/common/WhirlyGlobeLib/src/WideVectorDrawableBuilderGLES.cpp +++ b/common/WhirlyGlobeLib/src/WideVectorDrawableBuilderGLES.cpp @@ -1,9 +1,8 @@ -/* - * WideVectorDrawableBuilderGLES.cpp +/* WideVectorDrawableBuilderGLES.cpp * WhirlyGlobeLib * * Created by Steve Gifford on 5/14/19. - * Copyright 2011-2019 mousebird consulting + * Copyright 2011-2021 mousebird consulting * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -15,74 +14,97 @@ * 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. - * */ -#import "WideVectorDrawableBuilderGLES.h" +#import +#import +#import using namespace Eigen; namespace WhirlyKit { - + // OpenGL version of the tweaker void WideVectorTweakerGLES::tweakForFrame(Drawable *inDraw,RendererFrameInfo *frameInfo) { - if (frameInfo->program) + auto basicDraw = dynamic_cast(inDraw); + if (!basicDraw) { - float scale = std::max(frameInfo->sceneRenderer->framebufferWidth,frameInfo->sceneRenderer->framebufferHeight); - float screenSize = frameInfo->screenSizeInDisplayCoords.x(); - float pixDispSize = std::min(frameInfo->screenSizeInDisplayCoords.x(),frameInfo->screenSizeInDisplayCoords.y()) / scale; - float texScale = scale/(screenSize*texRepeat); - - ProgramGLES *programGLES = (ProgramGLES *)frameInfo->program; - - if (realWidthSet) - { - programGLES->setUniform(u_w2NameID, (float)(realWidth / pixDispSize)); - programGLES->setUniform(u_Realw2NameID, (float)realWidth); - programGLES->setUniform(u_EdgeNameID, edgeSize); - } else { - programGLES->setUniform(u_w2NameID, lineWidth); - programGLES->setUniform(u_Realw2NameID, pixDispSize * lineWidth); - programGLES->setUniform(u_EdgeNameID, edgeSize); - } - programGLES->setUniform(u_texScaleNameID, texScale); - programGLES->setUniform(u_colorNameID, Vector4f(color.r/255.0,color.g/255.0,color.b/255.0,color.a/255.0)); + wkLogLevel(Warn, "Invalid drawable passed to WideVectorTweakerGLES"); + return; + } + + const double scale = std::max(frameInfo->sceneRenderer->framebufferWidth,frameInfo->sceneRenderer->framebufferHeight); + const double screenSize = frameInfo->screenSizeInDisplayCoords.x(); + const double pixDispSize = std::min(frameInfo->screenSizeInDisplayCoords.x(),frameInfo->screenSizeInDisplayCoords.y()) / scale; + const double texScale = scale / (screenSize * texRepeat); + const float zoom = (opacityExp || colorExp || widthExp) ? getZoom(*inDraw,*frameInfo->scene,0.0f) : 0.0f; + + Vector4f c = colorExp ? colorExp->evaluateF(zoom,color) : color.asRGBAVecF(); + + if (opacityExp) + { + c.w() = opacityExp->evaluate(zoom, 1.0f); } + + const RGBAColor newC = RGBAColor::FromUnitFloats(&c[0]); + basicDraw->setOverrideColor(newC); + + const float width = (widthExp ? widthExp->evaluate(zoom, lineWidth) : lineWidth) + 2 * edgeSize; + basicDraw->setUniform(u_w2NameID, width); + basicDraw->setUniform(u_Realw2NameID, (float)(pixDispSize * width)); + + basicDraw->setUniform(u_EdgeNameID, edgeSize); + basicDraw->setUniform(u_texScaleNameID, (float)texScale); } -WideVectorDrawableBuilderGLES::WideVectorDrawableBuilderGLES(const std::string &name,Scene *scene) -: BasicDrawableBuilderGLES(name,scene,false) +WideVectorDrawableBuilderGLES::WideVectorDrawableBuilderGLES(const std::string &name,const SceneRenderer *sceneRenderer,Scene *scene) : + WideVectorDrawableBuilder(name,sceneRenderer,scene), + drawableGotten(false), + instanceGotten(false) { } -void WideVectorDrawableBuilderGLES::Init(unsigned int numVert,unsigned int numTri,bool globeMode) +void WideVectorDrawableBuilderGLES::Init(unsigned int numVert,unsigned int numTri,unsigned int numCenterline, + WideVecImplType implType, + bool globeMode, + const WideVectorInfo *vecInfo) { - basicDraw = std::make_shared("Wide Vector"); - WideVectorDrawableBuilder::Init(numVert,numTri,globeMode); + WideVectorDrawableBuilder::Init(numVert,numTri,0,implType,globeMode,vecInfo); } - + +// NOLINTNEXTLINE(google-default-arguments) int WideVectorDrawableBuilderGLES::addAttribute(BDAttributeDataType dataType,StringIdentity nameID,int slot,int numThings) { - return BasicDrawableBuilderGLES::addAttribute(dataType, nameID, slot, numThings); + return basicDrawable->addAttribute(dataType, nameID, slot, numThings); } - -WideVectorTweaker *WideVectorDrawableBuilderGLES::makeTweaker() + +DrawableTweakerRef WideVectorDrawableBuilderGLES::makeTweaker() const { - return new WideVectorTweakerGLES(); + return std::make_shared(); } - -BasicDrawableRef WideVectorDrawableBuilderGLES::getDrawable() + +BasicDrawableRef WideVectorDrawableBuilderGLES::getBasicDrawable() { if (drawableGotten) - return BasicDrawableBuilderGLES::getDrawable(); - - const auto theDraw = BasicDrawableBuilderGLES::getDrawable(); - setupTweaker(theDraw.get()); - + { + return basicDrawable->getDrawable(); + } + + drawableGotten = true; + // non-const to allow copy elision on return + auto theDraw = basicDrawable->getDrawable(); + + setupTweaker(*theDraw); + return theDraw; } + +BasicDrawableInstanceRef WideVectorDrawableBuilderGLES::getInstanceDrawable() +{ + return nullptr; +} static const char *vertexShaderTri = R"( precision highp float; @@ -93,29 +115,33 @@ uniform float u_fade; uniform float u_w2; uniform float u_real_w2; uniform float u_texScale; -uniform vec4 u_color; attribute vec3 a_position; attribute vec3 a_normal; attribute vec4 a_texinfo; -//attribute vec4 a_color; +attribute vec4 a_color; attribute vec3 a_p1; attribute vec3 a_n0; attribute float a_c0; +attribute vec3 a_offset; varying vec2 v_texCoord; -//"varying vec4 v_color; +varying vec4 v_color; void main() { - // v_color = a_color;\n" + v_color = a_color; // Position along the line float t0 = a_c0 * u_real_w2; - t0 = clamp(t0,0.0,1.0); - vec3 realPos = (a_p1 - a_position) * t0 + a_n0 * u_real_w2 + a_position; + t0 = clamp(t0,-4.0,5.0); + vec3 dir = normalize(a_p1 - a_position); + vec3 realPosOffset = (a_p1 - a_position) * t0 + + dir * u_real_w2 * a_offset.y + + a_n0 * u_real_w2 + + a_n0 * u_real_w2 * a_offset.x; float texPos = ((a_texinfo.z - a_texinfo.y) * t0 + a_texinfo.y + a_texinfo.w * u_real_w2) * u_texScale; v_texCoord = vec2(a_texinfo.x, texPos); - vec4 screenPos = u_mvpMatrix * vec4(realPos,1.0); + vec4 screenPos = u_mvpMatrix * vec4(a_position,1.0) + u_mvpMatrix * vec4(realPosOffset,0.0); screenPos /= screenPos.w; gl_Position = vec4(screenPos.xy,0,1.0); } @@ -131,34 +157,38 @@ uniform float u_fade; uniform float u_w2; uniform float u_real_w2; uniform float u_texScale; -uniform vec4 u_color; attribute vec3 a_position; attribute vec3 a_normal; attribute vec4 a_texinfo; -//attribute vec4 a_color; +attribute vec4 a_color; attribute vec3 a_p1; attribute vec3 a_n0; attribute float a_c0; +attribute vec3 a_offset; varying vec2 v_texCoord; -//varying vec4 v_color; +varying vec4 v_color; varying float v_dot; void main() { - // v_color = a_color; + v_color = a_color; // Position along the line float t0 = a_c0 * u_real_w2; - t0 = clamp(t0,0.0,1.0); - vec3 realPos = (a_p1 - a_position) * t0 + a_n0 * u_real_w2 + a_position; + t0 = clamp(t0,-4.0,5.0); + vec3 dir = normalize(a_p1 - a_position); + vec3 realPosOffset = (a_p1 - a_position) * t0 + + dir * u_real_w2 * a_offset.y + + a_n0 * u_real_w2 + + a_n0 * u_real_w2 * a_offset.x; vec4 pt = u_mvMatrix * vec4(a_position,1.0); pt /= pt.w; vec4 testNorm = u_mvNormalMatrix * vec4(a_normal,0.0); v_dot = dot(-pt.xyz,testNorm.xyz); float texPos = ((a_texinfo.z - a_texinfo.y) * t0 + a_texinfo.y + a_texinfo.w * u_real_w2) * u_texScale; v_texCoord = vec2(a_texinfo.x, texPos); - vec4 screenPos = u_mvpMatrix * vec4(realPos,1.0); + vec4 screenPos = u_mvpMatrix * vec4(a_position,1.0) + u_mvpMatrix * vec4(realPosOffset,0.0); screenPos /= screenPos.w; gl_Position = vec4(screenPos.xy,0,1.0); } @@ -171,10 +201,10 @@ uniform sampler2D s_baseMap0; uniform bool u_hasTexture; uniform float u_w2; uniform float u_edge; -uniform vec4 u_color; uniform float u_fade; varying vec2 v_texCoord; +varying vec4 v_color; void main() { @@ -185,7 +215,7 @@ void main() alpha = across/u_edge; if (across > u_w2-u_edge) alpha = (u_w2-across)/u_edge; - gl_FragColor = u_color * alpha * patternVal * u_fade; + gl_FragColor = v_color * alpha * patternVal * u_fade; } )"; @@ -196,12 +226,11 @@ uniform sampler2D s_baseMap0; uniform bool u_hasTexture; uniform float u_w2; uniform float u_edge; -uniform vec4 u_color; uniform float u_fade; varying vec2 v_texCoord; varying float v_dot; -//"varying vec4 v_color; +varying vec4 v_color; void main() { @@ -212,17 +241,17 @@ void main() alpha = across/u_edge; if (across > u_w2-u_edge) alpha = (u_w2-across)/u_edge; - gl_FragColor = (v_dot > 0.0 ? u_color * alpha * patternVal * u_fade : vec4(0.0,0.0,0.0,0.0)); + gl_FragColor = (v_dot > 0.0 ? v_color * alpha * patternVal * u_fade : vec4(0.0,0.0,0.0,0.0)); } )"; -ProgramGLES *BuildWideVectorProgramGLES(const std::string &name,SceneRenderer *renderer) +ProgramGLES *BuildWideVectorProgramGLES(const std::string &name, SceneRenderer *) { - ProgramGLES *shader = new ProgramGLES(name,vertexShaderTri,fragmentShaderTriAlias); + auto shader = new ProgramGLES(name,vertexShaderTri,fragmentShaderTriAlias); if (!shader->isValid()) { delete shader; - shader = NULL; + shader = nullptr; } // Set some reasonable defaults @@ -238,13 +267,13 @@ ProgramGLES *BuildWideVectorProgramGLES(const std::string &name,SceneRenderer *r return shader; } -ProgramGLES *BuildWideVectorGlobeProgramGLES(const std::string &name,SceneRenderer *renderer) +ProgramGLES *BuildWideVectorGlobeProgramGLES(const std::string &name, SceneRenderer *) { - ProgramGLES *shader = new ProgramGLES(name,vertexGlobeShaderTri,fragmentGlobeShaderTriAlias); + auto shader = new ProgramGLES(name,vertexGlobeShaderTri,fragmentGlobeShaderTriAlias); if (!shader->isValid()) { delete shader; - shader = NULL; + shader = nullptr; } // Set some reasonable defaults @@ -255,8 +284,7 @@ ProgramGLES *BuildWideVectorGlobeProgramGLES(const std::string &name,SceneRender shader->setUniform(u_lengthNameID, 10.f/1024); shader->setUniform(u_texScaleNameID, 1.f); } - - + return shader; } diff --git a/common/WhirlyGlobeLib/src/WideVectorManager.cpp b/common/WhirlyGlobeLib/src/WideVectorManager.cpp index f444ee2af6..b279cafbe3 100644 --- a/common/WhirlyGlobeLib/src/WideVectorManager.cpp +++ b/common/WhirlyGlobeLib/src/WideVectorManager.cpp @@ -1,9 +1,8 @@ -/* - * WideVectorManager.mm +/* WideVectorManager.cpp * WhirlyGlobeLib * * Created by Steve Gifford on 4/29/14. - * Copyright 2011-2019 mousebird consulting. + * Copyright 2011-2021 mousebird consulting. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -15,33 +14,40 @@ * 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. - * */ #import "WideVectorManager.h" +#import "VectorManager.h" #import "BasicDrawableInstanceBuilder.h" #import "FlatMath.h" #import "WhirlyKitLog.h" +#import "StringIndexer.h" #import "SharedAttributes.h" +#import "WideVectorDrawableBuilder.h" using namespace WhirlyKit; using namespace Eigen; namespace WhirlyKit { -WideVectorInfo::WideVectorInfo() -: color(255,255,255,255), width(2.0), -repeatSize(32.0), edgeSize(1.0), subdivEps(0.0), -coordType(WideVecCoordScreen), joinType(WideVecMiterJoin), capType(WideVecButtCap), -texID(EmptyIdentity), miterLimit(2.0) -{ +WideVectorInfo::WideVectorInfo() : + implType(WideVecImplBasic), color(RGBAColor::white()), width(2.0), + repeatSize(32.0), edgeSize(1.0), subdivEps(0.0), + coordType(WideVecCoordScreen), joinType(WideVecMiterJoin), capType(WideVecButtCap), + texID(EmptyIdentity), miterLimit(2.0), offset(0.0f) +{ } - + WideVectorInfo::WideVectorInfo(const Dictionary &dict) : BaseInfo(dict) { - color = dict.getColor(MaplyColor,RGBAColor(255,255,255,255)); + implType = WideVecImplBasic; + std::string implTypeStr = dict.getString(MaplyWideVecImpl); + if (!implTypeStr.compare(MaplyWideVecImplPerf)) + implType = WideVecImplPerf; + color = dict.getColor(MaplyColor,RGBAColor::white()); width = dict.getDouble(MaplyVecWidth,2.0); + offset = (float)-dict.getDouble(MaplyWideVecOffset,0.0); std::string coordTypeStr = dict.getString(MaplyWideVecCoordType); subdivEps = dict.getDouble(MaplySubdivEpsilon,0.0); coordType = WideVecCoordScreen; @@ -68,8 +74,23 @@ WideVectorInfo::WideVectorInfo(const Dictionary &dict) class WideVectorBuilder { public: - WideVectorBuilder(const WideVectorInfo *vecInfo,const Point3d &localCenter,const Point3d &dispCenter,const RGBAColor inColor,bool makeTurns,CoordSystemDisplayAdapter *coordAdapter) - : vecInfo(vecInfo), angleCutoff(DegToRad(30.0)), texOffset(0.0), edgePointsValid(false), coordAdapter(coordAdapter), localCenter(localCenter), dispCenter(dispCenter), makeDistinctTurn(makeTurns) + WideVectorBuilder(const WideVectorInfo *vecInfo, + Point3d localCenter, + Point3d dispCenter, + const RGBAColor inColor, + std::vector maskIDs, + bool makeTurns, + CoordSystemDisplayAdapter *coordAdapter) : + vecInfo(vecInfo), + angleCutoff(DegToRad(30.0)), + texOffset(0.0), + edgePointsValid(false), + coordAdapter(coordAdapter), + localCenter(std::move(localCenter)), + dispCenter(std::move(dispCenter)), + makeDistinctTurn(makeTurns), + maskIDs(std::move(maskIDs)), + color(RGBAColor::white()) { color = inColor; } @@ -79,7 +100,16 @@ class WideVectorBuilder class InterPoint { public: - InterPoint() : texX(0.0),texYmin(0.0),texYmax(0.0),texOffset(0.0) { } + InterPoint() : + c(0.0), + texX(0.0), + texYmin(0.0), + texYmax(0.0), + texOffset(0.0), + offset(0.0,0.0), + centerlineDir(1.0) + { } + // Construct with a single line InterPoint(const Point3d &p0,const Point3d &p1,const Point3d &n0,double inTexX,double inTexYmin,double inTexYmax,double inTexOffset) { @@ -88,46 +118,97 @@ class WideVectorBuilder n = n0; org = p0; dest = p1; + centerlineDir = 1.0; + offset = Point2d(0.0,0.0); texX = inTexX; texYmin = inTexYmin; texYmax = inTexYmax; texOffset = inTexOffset; } - - // Return a version of the point flipped around its main axis - InterPoint flipped() + + // Pass in the half width to calculate the intersection point + Point3d calcInterPt(double centerOffset,double w2) { - InterPoint iPt = *this; - iPt.n *= -1; - iPt.texX = 1.0 - texX; + double t0 = c * (centerOffset + w2); + Point3d iPt = dir * t0 + + dir * w2 * offset.y() + + n * (centerOffset + w2) + + n * offset.x() + + org; return iPt; } - // Pass in the half width to calculate the intersection point - Point3d calcInterPt(double w2) - { - double t0 = c * w2; - Point3d iPt = dir * t0 + n * w2 + org; + InterPoint flipped() { + InterPoint newPt = *this; + newPt.n *= -1; - return iPt; + return newPt; + } + + // Same point, but offset along the centerline + InterPoint nudgeAlongCenter(double nudge) { + InterPoint newPt = *this; + newPt.offset.y() += nudge; + + return newPt; + } + + // Same point, but offset along the normal + InterPoint nudgeAlongNormal(double nudge) { + InterPoint newPt = *this; + newPt.offset.x() += nudge; + + return newPt; + } + + // Set the texture X coordinate, but otherwise just copy + InterPoint withTexX(double newTexX) { + InterPoint newPt = *this; + newPt.texX = newTexX; + + return newPt; + } + + // Set the tex min/max accordingly, but otherwise just copy + InterPoint withTexY(double newMinTexY,double newMaxTexY) { + InterPoint newPt = *this; + newPt.texYmin = newMinTexY; + newPt.texYmax = newMaxTexY; + + return newPt; + } + + // Set the tex offset, but otherwise just copy + InterPoint withTexOffset(double newTexOffset) { + InterPoint newPt = *this; + newPt.texOffset = newTexOffset; + + return newPt; } double c; Point3d dir; Point3d n; Point3d org,dest; + Point2d offset; + double centerlineDir; double texX; double texYmin,texYmax,texOffset; }; // Intersect the wide lines, but return an equation to calculate the point - bool intersectWideLines(const Point3d &p0,const Point3d &p1,const Point3d &p2,const Point3d &n0,const Point3d &n1,InterPoint &iPt0,InterPoint &iPt1,double texX,double texY0,double texY1,double texY2) + static bool intersectWideLines(const Point3d &p0,const Point3d &p1,const Point3d &p2, + const Point3d &n0,const Point3d &n1, + InterPoint &iPt0,InterPoint &iPt1, + double centerlineDir0, double centerlineDir1, + double texX,double texY0,double texY1,double texY2) { { iPt0.texX = texX; iPt0.dir = p0 - p1; iPt0.n = n0; + iPt0.centerlineDir = centerlineDir0; iPt0.org = p1; iPt0.texYmin = texY1; iPt0.dest = p0; @@ -136,7 +217,7 @@ class WideVectorBuilder Point3d n01 = n0 - n1; Point3d p21 = p2 - p1; - double denom = (p21.y()*p01.x() - p01.y()*p21.x()); + const double denom = (p21.y()*p01.x() - p01.y()*p21.x()); if (denom == 0.0) return false; iPt0.c = (n01.y()*p21.x() - n01.x()*p21.y())/denom; @@ -146,6 +227,7 @@ class WideVectorBuilder iPt1.texX = texX; iPt1.dir = p2 - p1; iPt1.n = n1; + iPt1.centerlineDir = centerlineDir1; iPt1.org = p1; iPt1.texYmin = texY1; iPt1.dest = p2; @@ -153,7 +235,7 @@ class WideVectorBuilder Point3d n10 = n1 - n0; Point3d p21 = p2 - p1; Point3d p01 = p0 - p1; - double denom = p21.x()*p01.y()-p21.y()*p01.x(); + const double denom = p21.x()*p01.y()-p21.y()*p01.x(); if (denom == 0.0) return false; iPt1.c = (n10.y()*p01.x() - n10.x()*p01.y())/denom; @@ -163,19 +245,22 @@ class WideVectorBuilder } // Add a rectangle to the wide drawable - void addWideRect(WideVectorDrawableBuilderRef drawable,InterPoint *verts,const Point3d &up) + void addWideRect(const WideVectorDrawableBuilderRef &drawable,InterPoint *verts,const Point3d &up) { - int startPt = drawable->getNumPoints(); + const int startPt = drawable->getNumPoints(); for (unsigned int vi=0;vi<4;vi++) { - InterPoint &vert = verts[vi]; + const InterPoint &vert = verts[vi]; drawable->addPoint(Vector3dToVector3f(vert.org)); drawable->addNormal(up); drawable->add_p1(Vector3dToVector3f(vert.dest)); drawable->add_n0(Vector3dToVector3f(vert.n)); + drawable->add_offset(Vector3dToVector3f(Point3d(vert.offset.x(),vert.offset.y(),vert.centerlineDir))); drawable->add_c0(vert.c); drawable->add_texInfo(vert.texX,vert.texYmin,vert.texYmax,vert.texOffset); + for (unsigned int ii=0;iiaddAttributeValue(maskEntries[ii], (int) maskIDs[ii]); } drawable->addTriangle(BasicDrawable::Triangle(startPt+0,startPt+1,startPt+3)); @@ -185,17 +270,20 @@ class WideVectorBuilder // Add a triangle to the wide drawable void addWideTri(WideVectorDrawableBuilderRef drawable,InterPoint *verts,const Point3d &up) { - int startPt = drawable->getNumPoints(); + const int startPt = drawable->getNumPoints(); for (unsigned int vi=0;vi<3;vi++) { - InterPoint &vert = verts[vi]; + const InterPoint &vert = verts[vi]; drawable->addPoint(Vector3dToVector3f(vert.org)); drawable->addNormal(up); drawable->add_p1(Vector3dToVector3f(vert.dest)); drawable->add_n0(Vector3dToVector3f(vert.n)); + drawable->add_offset(Vector3dToVector3f(Point3d(vert.offset.x(),vert.offset.y(),vert.centerlineDir))); drawable->add_c0(vert.c); drawable->add_texInfo(vert.texX,vert.texYmin,vert.texYmax,vert.texOffset); + for (unsigned int ii=0;iiaddAttributeValue(maskEntries[ii], (int) maskIDs[ii]); } drawable->addTriangle(BasicDrawable::Triangle(startPt+0,startPt+1,startPt+2)); @@ -214,7 +302,7 @@ class WideVectorBuilder if (pc) { if ((*pc-*pb).norm() == 0.0) - pc = NULL; + pc = nullptr; } // We need the normal (with respect to the line), and its inverse @@ -248,8 +336,8 @@ class WideVectorBuilder // Look for valid starting points. If they're not there, make some simple ones if (!edgePointsValid) { - e0 = InterPoint(paLocal,pbLocal,revNorm0,1.0,texBase,texNext,0.0); - e1 = InterPoint(paLocal,pbLocal,norm0,0.0,texBase,texNext,0.0); + e0 = InterPoint(paLocal,pbLocal,revNorm0,1.0,texBase,texNext,1.0); + e1 = e0.nudgeAlongNormal(-2.0).withTexX(0.0); edgePointsValid = true; } @@ -271,8 +359,8 @@ class WideVectorBuilder Point3d dirB = (pcLocal-pbLocal).normalized(); dot = dirA.dot(dirB); if (dot > -0.99999998476 && dot < 0.99999998476) - if (intersectWideLines(paLocal, pbLocal, pcLocal, norm0, norm1, rPt0, rPt1, 0.0, texBase, texNext, texNext2) && - intersectWideLines(paLocal, pbLocal, pcLocal, revNorm0, revNorm1, lPt0, lPt1, 1.0, texBase, texNext, texNext2)) + if (intersectWideLines(paLocal, pbLocal, pcLocal, norm0, norm1, rPt0, rPt1, -1.0, -1.0, 0.0, texBase, texNext, texNext2) && + intersectWideLines(paLocal, pbLocal, pcLocal, revNorm0, revNorm1, lPt0, lPt1, 1.0, 1.0, 1.0, texBase, texNext, texNext2)) { iPtsValid = true; angleBetween = acos(dot); @@ -292,10 +380,10 @@ class WideVectorBuilder } // End points of the segments - InterPoint endPt0(pbLocal,paLocal,norm0,0.0,texNext,texBase,0.0); + InterPoint endPt0(pbLocal,paLocal,revNorm0,1.0,texNext,texBase,1.0); InterPoint endPt1; if (pc) - endPt1 = InterPoint(pbLocal,pcLocal,norm1,0.0,texNext,texNext2,0.0); + endPt1 = InterPoint(pbLocal,pcLocal,norm1,1.0,texNext,texNext2,1.0); // Set up the segment points if (iPtsValid) @@ -303,121 +391,141 @@ class WideVectorBuilder // Bending right if (rPt0.c > 0.0) { + corners[3] = rPt0.nudgeAlongNormal(-2.0).withTexX(1.0); corners[2] = rPt0; - corners[3] = rPt0.flipped(); - next_e0 = rPt1.flipped(); + next_e0 = rPt1.nudgeAlongNormal(-2.0).withTexX(1.0); next_e1 = rPt1; } else { // Bending left - corners[2] = lPt0.flipped(); corners[3] = lPt0; + corners[2] = lPt0.nudgeAlongNormal(-2.0).withTexX(0.0); next_e0 = lPt1; - next_e1 = lPt1.flipped(); + next_e1 = lPt1.nudgeAlongNormal(-2.0).withTexX(0.0); } } else { - corners[2] = endPt0; - corners[3] = endPt0.flipped(); - next_e0 = endPt0.flipped(); - next_e1 = endPt0; + corners[3] = endPt0; + corners[2] = endPt0.nudgeAlongNormal(-2.0).withTexX(0.0); + next_e0 = endPt0; + next_e1 = endPt0.nudgeAlongNormal(-2.0).withTexX(0.0); } + // TODO: Revisit the texture adjustment around corners + // The problem is the corners can be much large depending on offset and width + double texAdjust = 0.0; // Do the join polygons if we can - // Note: Always doing bevel case (sort of) if (iPtsValid && buildJunction) { WideVectorLineJoinType joinType = vecInfo->joinType; - // Switch to a miter join if the angle is too great for a bevel - if (joinType == WideVecMiterJoin && angleBetween < (M_PI-vecInfo->miterLimit*M_PI/180.0)) + // Switch to a bevel join if the angle is too great for a miter + double miterLimit = vecInfo->miterLimit; + if (joinType == WideVecMiterJoin && angleBetween > (M_PI-miterLimit*M_PI/180.0)) joinType = WideVecBevelJoin; - + // We don't do bevels below 30 degrees + if (joinType == WideVecBevelJoin && angleBetween > 150.0 / 180.0 * M_PI) + joinType = WideVecMiterJoin; + + // An offset that makes the texture coordinates work around corners + texAdjust = cos(angleBetween/2.0); + switch (joinType) { case WideVecBevelJoin: { - // An offset that makes the texture coordinates work - double texAdjust = cos(angleBetween/2.0); - - // Three triangles make up the bend - // Bending right if (rPt0.c > 0.0) { InterPoint triVerts[3]; - - triVerts[0] = texLen > texLen2 ? rPt0 : rPt1; - triVerts[0].texYmin = texNext; - triVerts[0].texYmax = texNext; - triVerts[1] = endPt1.flipped(); - triVerts[1].texYmin = texNext; - triVerts[1].texYmax = texNext; - triVerts[1].texOffset = texAdjust; - triVerts[2] = endPt0.flipped(); - triVerts[2].texYmin = texNext; - triVerts[2].texYmax = texNext; - triVerts[2].texOffset = -texAdjust; + + InterPoint r0 = corners[2]; + InterPoint r1 = next_e1; + InterPoint l0 = corners[3]; + InterPoint l1 = corners[3].nudgeAlongCenter(-1.0); + InterPoint l2 = next_e0.nudgeAlongCenter(-1.0); + InterPoint l3 = next_e0; + + triVerts[0] = r0.withTexY(texNext,texNext); + triVerts[1] = l1.withTexY(texNext,texNext); + triVerts[2] = l0.withTexY(texNext,texNext); + addWideTri(wideDrawable,triVerts,up); + + triVerts[0] = r0.withTexY(texNext,texNext); + triVerts[1] = l2.withTexY(texNext,texNext); + triVerts[2] = l1.withTexY(texNext,texNext); + addWideTri(wideDrawable,triVerts,up); + + triVerts[0] = r0.withTexY(texNext,texNext); + triVerts[1] = r1.withTexY(texNext,texNext); + triVerts[2] = l2.withTexY(texNext,texNext); + addWideTri(wideDrawable,triVerts,up); + + triVerts[0] = r1.withTexY(texNext,texNext); + triVerts[1] = l3.withTexY(texNext,texNext); + triVerts[2] = l2.withTexY(texNext,texNext); addWideTri(wideDrawable,triVerts,up); - - if (makeDistinctTurn) - { - // Build separate triangles for the turn - triVerts[0] = rPt0; - triVerts[1] = endPt0.flipped(); - triVerts[2] = rPt0.flipped(); - addWideTri(wideDrawable,triVerts,up); - - triVerts[0] = rPt1; - triVerts[1] = rPt1.flipped(); - triVerts[2] = endPt1.flipped(); - addWideTri(wideDrawable,triVerts,up); - } else { - // Extend the segments - corners[3] = endPt0.flipped(); - next_e0 = endPt1.flipped(); - } } else { // Bending left InterPoint triVerts[3]; - - triVerts[0] = texLen > texLen2 ? lPt0 : lPt1; - triVerts[0].texYmin = texNext; - triVerts[0].texYmax = texNext; - triVerts[1] = endPt0; - triVerts[1].texYmin = texNext; - triVerts[1].texYmax = texNext; - triVerts[1].texOffset = -texAdjust; - triVerts[2] = endPt1; - triVerts[2].texYmin = texNext; - triVerts[2].texYmax = texNext; - triVerts[2].texOffset = texAdjust; + + InterPoint l0 = corners[3]; + InterPoint l1 = next_e0; + InterPoint r0 = corners[2]; + InterPoint r1 = corners[2].nudgeAlongCenter(-1.0); + InterPoint r2 = next_e1.nudgeAlongCenter(-1.0); + InterPoint r3 = next_e1; + + triVerts[0] = l0.withTexY(texNext,texNext); + triVerts[1] = r0.withTexY(texNext,texNext); + triVerts[2] = r1.withTexY(texNext,texNext); + addWideTri(wideDrawable,triVerts,up); + + triVerts[0] = l0.withTexY(texNext,texNext); + triVerts[1] = r1.withTexY(texNext,texNext); + triVerts[2] = r2.withTexY(texNext,texNext); + addWideTri(wideDrawable,triVerts,up); + + triVerts[0] = l0.withTexY(texNext,texNext); + triVerts[1] = r2.withTexY(texNext,texNext); + triVerts[2] = l1.withTexY(texNext,texNext); + addWideTri(wideDrawable,triVerts,up); + + triVerts[0] = l1.withTexY(texNext,texNext); + triVerts[1] = r2.withTexY(texNext,texNext); + triVerts[2] = r3.withTexY(texNext,texNext); addWideTri(wideDrawable,triVerts,up); - - if (makeDistinctTurn) - { - // Build separate triangles for the turn - triVerts[0] = lPt0; - triVerts[1] = lPt0.flipped(); - triVerts[2] = endPt0; - addWideTri(wideDrawable,triVerts,up); - - triVerts[0] = lPt1; - triVerts[1] = endPt1; - triVerts[2] = lPt1.flipped(); - addWideTri(wideDrawable,triVerts,up); - } else { - // Extend the segments - corners[2] = endPt0; - next_e1 = endPt1; - } } } break; case WideVecMiterJoin: { - // Don't do anything special for miter joins - corners[2] = rPt0; - corners[3] = lPt0; - next_e0 = lPt0; - next_e1 = rPt0; + InterPoint triVerts[3]; + + // Bending right + if (rPt0.c > 0.0) { + const double texYmin = lPt0.texYmin; + const double textYmax = lPt0.texYmax; + triVerts[0] = lPt0.withTexY(texYmin,textYmax); + triVerts[1] = corners[3].withTexY(texYmin,textYmax); + triVerts[2] = corners[2].withTexY(texYmin,textYmax); + addWideTri(wideDrawable,triVerts,up); + + triVerts[0] = next_e1.withTexY(texYmin,textYmax); + triVerts[1] = next_e0.withTexY(texYmin,textYmax); + triVerts[2] = lPt1.withTexY(texYmin,textYmax); + addWideTri(wideDrawable,triVerts,up); + } else { + // Bending left + const double texYmin = rPt0.texYmin; + const double textYmax = rPt0.texYmax; + triVerts[0] = corners[3].withTexY(texYmin,textYmax); + triVerts[1] = corners[2].withTexY(texYmin,textYmax); + triVerts[2] = rPt0.withTexY(texYmin,textYmax); + addWideTri(wideDrawable,triVerts,up); + + triVerts[0] = rPt1.withTexY(texYmin,textYmax); + triVerts[1] = next_e1.withTexY(texYmin,textYmax); + triVerts[2] = next_e0.withTexY(texYmin,textYmax); + addWideTri(wideDrawable,triVerts,up); + } } break; case WideVecRoundJoin: @@ -468,13 +576,15 @@ class WideVectorBuilder { const Point3d &pa = pts[pts.size()-2]; const Point3d &pb = pts[pts.size()-1]; - buildPolys(&pa, &pb, NULL, lastUp, drawable, buildLastSegment, buildLastJunction); + buildPolys(&pa, &pb, nullptr, lastUp, drawable, buildLastSegment, buildLastJunction); } } const WideVectorInfo *vecInfo; CoordSystemDisplayAdapter *coordAdapter; RGBAColor color; + std::vector maskEntries; + std::vector maskIDs; Point3d localCenter,dispCenter; double angleCutoff; bool makeDistinctTurn; @@ -493,8 +603,8 @@ class WideVectorBuilder class WideVectorDrawableConstructor { public: - WideVectorDrawableConstructor(SceneRenderer *sceneRender,Scene *scene,const WideVectorInfo *vecInfo) - : sceneRender(sceneRender), scene(scene), vecInfo(vecInfo), drawable(NULL), centerValid(false), localCenter(0,0,0), dispCenter(0,0,0) + WideVectorDrawableConstructor(SceneRenderer *sceneRender,Scene *scene,const WideVectorInfo *vecInfo,int numMaskIDs) + : sceneRender(sceneRender), scene(scene), vecInfo(vecInfo), drawable(nullptr), centerValid(false), localCenter(0,0,0), dispCenter(0,0,0), numMaskIDs(numMaskIDs) { coordAdapter = scene->getCoordAdapter(); coordSys = coordAdapter->getCoordSystem(); @@ -509,46 +619,96 @@ class WideVectorDrawableConstructor } // Build or return a suitable drawable (depending on the mode) - WideVectorDrawableBuilderRef getDrawable(int ptCount,int triCount,int ptCountAllocate,int triCountAllocate) + WideVectorDrawableBuilderRef getDrawable(int ptCount,int triCount,int ptCountAllocate,int triCountAllocate,int clineCount) { - int ptGuess = std::min(std::max(ptCount,0),(int)MaxDrawablePoints); - int triGuess = std::min(std::max(triCount,0),(int)MaxDrawableTriangles); + if (vecInfo->implType == WideVecImplPerf) { + // Performance mode uses instancing and makes the renderer do the work + if (!drawable || + drawable->getCenterLineCount()+clineCount > drawable->maxInstances()) + { + flush(); - if (!drawable || - (drawable->getNumPoints()+ptGuess > MaxDrawablePoints) || - (drawable->getNumTris()+triGuess > MaxDrawableTriangles)) - { - flush(); - -// NSLog(@"Pts = %d, tris = %d",ptGuess,triGuess); - int ptAlloc = std::min(std::max(ptCountAllocate,0),(int)MaxDrawablePoints); - int triAlloc = std::min(std::max(triCountAllocate,0),(int)MaxDrawableTriangles); - WideVectorDrawableBuilderRef wideDrawable = sceneRender->makeWideVectorDrawableBuilder("Wide Vector"); - wideDrawable->Init(ptAlloc,triAlloc,!scene->getCoordAdapter()->isFlat()); - drawable = wideDrawable; - wideDrawable->setTexRepeat(vecInfo->repeatSize); - wideDrawable->setEdgeSize(vecInfo->edgeSize); - wideDrawable->setLineWidth(vecInfo->width); -// drawMbr.reset(); - drawable->setType(Triangles); - vecInfo->setupBasicDrawable(drawable); - if (vecInfo->coordType == WideVecCoordReal) - wideDrawable->setRealWorldWidth(vecInfo->width); - if (vecInfo->widthExp) - wideDrawable->setWidthExpression(vecInfo->widthExp); - if (vecInfo->opacityExp) - wideDrawable->setOpacityExpression(vecInfo->opacityExp); - if (vecInfo->colorExp) - wideDrawable->setColorExpression(vecInfo->colorExp); - - drawable->setColor(vecInfo->color); - if (vecInfo->texID != EmptyIdentity) - drawable->setTexId(0, vecInfo->texID); - if (centerValid) + WideVectorDrawableBuilderRef wideDrawable = sceneRender->makeWideVectorDrawableBuilder("Wide Vector"); + wideDrawable->Init(ptCountAllocate,triCountAllocate,clineCount, + vecInfo->implType, + !scene->getCoordAdapter()->isFlat(), + vecInfo); + drawable = wideDrawable; + wideDrawable->setTexRepeat(vecInfo->repeatSize); + wideDrawable->setEdgeSize(vecInfo->edgeSize); + wideDrawable->setLineWidth(vecInfo->width); + wideDrawable->setLineOffset(vecInfo->offset); + if (vecInfo->widthExp) + wideDrawable->setWidthExpression(vecInfo->widthExp); + if (vecInfo->opacityExp) + wideDrawable->setOpacityExpression(vecInfo->opacityExp); + if (vecInfo->colorExp) + wideDrawable->setColorExpression(vecInfo->colorExp); + if (vecInfo->offsetExp) + wideDrawable->setOffsetExpression(vecInfo->offsetExp); + maskEntries.resize(numMaskIDs); + for (unsigned int ii=0;iiaddAttribute(BDIntType, a_maskNameIDs[ii], sceneRender->getSlotForNameID(a_maskNameIDs[ii]), ptCount); + + drawable->setColor(vecInfo->color); + + int baseTexId = 0; + if (vecInfo->texID != EmptyIdentity) + drawable->setTexId(baseTexId++, vecInfo->texID); + if (centerValid) + { + Eigen::Affine3d trans(Eigen::Translation3d(dispCenter.x(),dispCenter.y(),dispCenter.z())); + Matrix4d transMat = trans.matrix(); + drawable->setMatrix(&transMat); + } + } + } else { + // Basic mode builds up a lot more geometry + int ptGuess = std::min(std::max(ptCount,0),(int)MaxDrawablePoints); + int triGuess = std::min(std::max(triCount,0),(int)MaxDrawableTriangles); + + if (!drawable || + (drawable->getNumPoints()+ptGuess > MaxDrawablePoints) || + (drawable->getNumTris()+triGuess > MaxDrawableTriangles)) { - Eigen::Affine3d trans(Eigen::Translation3d(dispCenter.x(),dispCenter.y(),dispCenter.z())); - Matrix4d transMat = trans.matrix(); - drawable->setMatrix(&transMat); + flush(); + + // NSLog(@"Pts = %d, tris = %d",ptGuess,triGuess); + int ptAlloc = std::min(std::max(ptCountAllocate,0),(int)MaxDrawablePoints); + int triAlloc = std::min(std::max(triCountAllocate,0),(int)MaxDrawableTriangles); + WideVectorDrawableBuilderRef wideDrawable = sceneRender->makeWideVectorDrawableBuilder("Wide Vector"); + wideDrawable->Init(ptAlloc,triAlloc,0, + vecInfo->implType, + !scene->getCoordAdapter()->isFlat(), + vecInfo); + drawable = wideDrawable; + wideDrawable->setTexRepeat(vecInfo->repeatSize); + wideDrawable->setEdgeSize(vecInfo->edgeSize); + wideDrawable->setLineWidth(vecInfo->width); + wideDrawable->setLineOffset(vecInfo->offset); + // drawMbr.reset(); + if (vecInfo->widthExp) + wideDrawable->setWidthExpression(vecInfo->widthExp); + if (vecInfo->opacityExp) + wideDrawable->setOpacityExpression(vecInfo->opacityExp); + if (vecInfo->colorExp) + wideDrawable->setColorExpression(vecInfo->colorExp); + if (vecInfo->offsetExp) + wideDrawable->setOffsetExpression(vecInfo->offsetExp); + maskEntries.resize(numMaskIDs); + for (unsigned int ii=0;iiaddAttribute(BDIntType, a_maskNameIDs[ii], sceneRender->getSlotForNameID(a_maskNameIDs[ii]), ptAlloc); + + drawable->setColor(vecInfo->color); + int baseTexId = 0; + if (vecInfo->texID != EmptyIdentity) + drawable->setTexId(baseTexId++, vecInfo->texID); + if (centerValid) + { + Eigen::Affine3d trans(Eigen::Translation3d(dispCenter.x(),dispCenter.y(),dispCenter.z())); + Matrix4d transMat = trans.matrix(); + drawable->setMatrix(&transMat); + } } } @@ -556,99 +716,194 @@ class WideVectorDrawableConstructor } // Add the points for a linear - void addLinear(const VectorRing &pts,const Point3d &up,bool closed) + void addLinear(const VectorRing &pts, + const Point3d &up, + const std::vector &maskIDs, + bool closed) { - // We'll add one on the beginning and two on the end - // if we're doing a closed loop. This gets us - // valid junctions that match up. - int startPoint = 0; - bool makeDistinctTurns = false; - if (closed) - { - // Note: We need this so we don't lose one turn - // This could be optimized - makeDistinctTurns = true; - if (pts.size() > 2) + if (vecInfo->implType == WideVecImplPerf) { + // Performance mode makes the renderer do the work + + // Clean up the points first + VectorRing newPts; + newPts.reserve(pts.size()); + for (unsigned int ii=0;ii 0 && pts[ii] == pts[ii-1]) + continue; + + // If it's a closed shape, no duplicates there either + if (closed && (ii == pts.size()-1) && (pts.front() == pts.back())) + continue; + + newPts.push_back(pts[ii]); + } + if (newPts.size() < 2) + return; + + // We're instancing, so we only need a few points and triangles + WideVectorDrawableBuilderRef thisDrawable = getDrawable(8,6,8,6,pts.size()+1); + drawable = thisDrawable; + + if (drawable->getNumTris() == 0) { + // 8 points and 6 triangles. + // Many of the points can't be shared because the end caps + // will be handled differently by the fragment shader + + // End cap: vertices [0,3], polygon 0 + drawable->addInstancePoint(Point3f(-1.0,-2.0,0.0),0,0); + drawable->addInstancePoint(Point3f(1.0,-2.0,0.0),1,0); + drawable->addInstancePoint(Point3f(-1.0,-1.0,0.0),2,0); + drawable->addInstancePoint(Point3f(1.0,-1.0,0.0),3,0); + drawable->addTriangle(BasicDrawable::Triangle(0,3,1)); + drawable->addTriangle(BasicDrawable::Triangle(0,2,3)); + + // Middle segment: vertices [4,7], polygon 1 + drawable->addInstancePoint(Point3f(-1.0,-1.0,0.0),4,1); + drawable->addInstancePoint(Point3f(1.0,-1.0,0.0),5,1); + drawable->addInstancePoint(Point3f(-1.0,1.0,0.0),6,1); + drawable->addInstancePoint(Point3f(1.0,1.0,0.0),7,1); + drawable->addTriangle(BasicDrawable::Triangle(4,7,5)); + drawable->addTriangle(BasicDrawable::Triangle(4,6,7)); + + // End cap: vertices [8,11], polygon 2 + drawable->addInstancePoint(Point3f(-1.0,1.0,0.0),8,2); + drawable->addInstancePoint(Point3f(1.0,1.0,0.0),9,2); + drawable->addInstancePoint(Point3f(-1.0,2.0,0.0),10,2); + drawable->addInstancePoint(Point3f(1.0,2.0,0.0),11,2); + drawable->addTriangle(BasicDrawable::Triangle(8,11,9)); + drawable->addTriangle(BasicDrawable::Triangle(8,10,11)); + } + + // Run through the points, adding centerline instances + double len = 0.0; + int startPt = drawable->getCenterLineCount(); + for (unsigned int ii=0;iigeographicToLocal3d(GeoCoord(pt.x(),pt.y())); + Point3d dispPa = coordAdapter->localToDisplay(localPa); + + int prev = startPt + ii - 1; + if (ii == 0) { + prev = closed ? startPt + newPts.size() - 1 : -1; + } + int next = startPt + ii + 1; + if (ii == newPts.size()-1) { + next = closed ? startPt : -1; + } + + drawable->addCenterLine(dispPa,up,len,vecInfo->color,maskIDs,prev,next); + + if (ii 2) { - startPoint = -3; - } else { - startPoint = -2; + if (pts.front() == pts.back()) + { + startPoint = -3; + } else { + startPoint = -2; + } } } - } - - RGBAColor color = vecInfo->color; - WideVectorBuilder vecBuilder(vecInfo,localCenter,dispCenter,color,makeDistinctTurns,coordAdapter); + + RGBAColor color = vecInfo->color; + WideVectorBuilder vecBuilder(vecInfo, + localCenter, + dispCenter, + color, + maskIDs, + makeDistinctTurns, + coordAdapter); - // Guess at how many points and triangles we'll need - int totalTriCount = (int)(5*pts.size()); - int totalPtCount = totalTriCount * 3; - if (totalTriCount < 0) totalTriCount = 0; - if (totalPtCount < 0) totalPtCount = 0; - - // Work through the segments - Point2f lastPt; - bool validLastPt = false; - for (int ii=startPoint;ii<(int)pts.size();ii++) - { - // Get the points in display space - Point2f geoA = pts[(ii+pts.size())%pts.size()]; - - if (validLastPt && geoA == lastPt) - continue; - - Point3d localPa = coordSys->geographicToLocal3d(GeoCoord(geoA.x(),geoA.y())); - Point3d dispPa = coordAdapter->localToDisplay(localPa); - Point3d thisUp = up; - if (!coordAdapter->isFlat()) - thisUp = coordAdapter->normalForLocal(localPa); + // Guess at how many points and triangles we'll need + int totalTriCount = (int)(5*pts.size()); + int totalPtCount = totalTriCount * 3; + if (totalTriCount < 0) totalTriCount = 0; + if (totalPtCount < 0) totalPtCount = 0; - // Get a drawable ready - int triCount = 2+3; - int ptCount = triCount*3; - WideVectorDrawableBuilderRef thisDrawable = getDrawable(ptCount,triCount,totalPtCount,totalTriCount); - totalTriCount -= triCount; - totalPtCount -= ptCount; - drawMbr.addPoint(geoA); - - bool doSegment = !closed || (ii > 0); - bool doJunction = !closed || (ii >= 0); - vecBuilder.addPoint(dispPa,thisUp,thisDrawable,closed,doSegment,doJunction); - -// NSLog(@"Pt = (%f,%f), doSegment = %d, doJunction = %d",geoA.x(),geoA.y(),(int)doSegment,(int)doJunction); - - lastPt = geoA; - validLastPt = true; - } + // Work through the segments + Point2f lastPt; + bool validLastPt = false; + for (int ii=startPoint;ii<(int)pts.size();ii++) + { + // Get the points in display space + Point2f geoA = pts[(ii+pts.size())%pts.size()]; + + if (validLastPt && geoA == lastPt) + continue; - vecBuilder.flush(drawable,!closed,true); + Point3d localPa = coordSys->geographicToLocal3d(GeoCoord(geoA.x(),geoA.y())); + Point3d dispPa = coordAdapter->localToDisplay(localPa); + Point3d thisUp = up; + if (!coordAdapter->isFlat()) + thisUp = coordAdapter->normalForLocal(localPa); + + // Get a drawable ready + int triCount = 2+3; + int ptCount = triCount*3; + WideVectorDrawableBuilderRef thisDrawable = getDrawable(ptCount,triCount,totalPtCount,totalTriCount,0); + vecBuilder.maskEntries = maskEntries; + totalTriCount -= triCount; + totalPtCount -= ptCount; + drawMbr.addPoint(geoA); + + bool doSegment = !closed || (ii > 0); + bool doJunction = !closed || (ii >= 0); + vecBuilder.addPoint(dispPa,thisUp,thisDrawable,closed,doSegment,doJunction); + + // NSLog(@"Pt = (%f,%f), doSegment = %d, doJunction = %d",geoA.x(),geoA.y(),(int)doSegment,(int)doJunction); + + lastPt = geoA; + validLastPt = true; + } + + vecBuilder.flush(drawable,!closed,true); + } } - // Debug verson of add linear + // Debug version of add linear void addLinearDebug() { const Point3d up(0,0,1); - VectorRing pts; - pts.push_back(GeoCoord(0,1)); - pts.push_back(GeoCoord(0,0)); - pts.push_back(GeoCoord(1,0)); - + const VectorRing pts = { + {0,1}, + {0,0}, + {1,0} }; + const RGBAColor color = vecInfo->color; - WideVectorBuilder vecBuilder(vecInfo,Point3d(0,0,0),Point3d(0,0,0),color,false,coordAdapter); + std::vector maskIDs; + WideVectorBuilder vecBuilder(vecInfo, + Point3d(0,0,0), + Point3d(0,0,0), + color, + maskIDs, + false, + coordAdapter); - for (unsigned int ii=0;iigetCurrentTime(); + const TimeInterval curTime = scene->getCurrentTime(); WideVectorSceneRep *sceneRep = new WideVectorSceneRep(); sceneRep->fade = vecInfo->fade; - for (unsigned int ii=0;iidrawIDs.insert(drawable->getDrawableID()); + if (auto drawID = drawable->getBasicDrawableID()) + sceneRep->drawIDs.insert(drawID); + if (auto drawID = drawable->getInstanceDrawableID()) + sceneRep->drawIDs.insert(drawID); if (vecInfo->fade > 0.0) drawable->setFade(curTime,curTime+vecInfo->fade); - changes.push_back(new AddDrawableReq(drawable->getDrawable())); + if (auto draw = drawable->getBasicDrawable()) + changes.push_back(new AddDrawableReq(draw)); + if (auto draw = drawable->getInstanceDrawable()) + changes.push_back(new AddDrawableReq(draw)); } drawables.clear(); @@ -692,10 +952,12 @@ class WideVectorDrawableConstructor drawable->setLocalMbr(drawMbr); drawables.push_back(drawable); } - drawable = NULL; + drawable = nullptr; } bool centerValid; + int numMaskIDs; + std::vector maskEntries; Point3d localCenter,dispCenter; Mbr drawMbr; SceneRenderer *sceneRender; @@ -743,25 +1005,29 @@ WideVectorManager::WideVectorManager() WideVectorManager::~WideVectorManager() { + std::lock_guard guardLock(lock); + for (auto it : sceneReps) delete it; sceneReps.clear(); } -// TODO: Get rid of this version -SimpleIdentity WideVectorManager::addVectors(const ShapeSet &shapes,const WideVectorInfo &vecInfo,ChangeSet &changes) +SimpleIdentity WideVectorManager::addVectors(const std::vector &shapes,const WideVectorInfo &vecInfo,ChangeSet &changes) { // Calculate a center for this geometry + bool hasMaskIDs = false; GeoMbr geoMbr; for (const auto &shape : shapes) { + if (shape->getAttrDict()->hasField("maskID0")) + hasMaskIDs = true; geoMbr.expand(shape->calcGeoMbr()); } // No data? if (!geoMbr.valid()) return EmptyIdentity; - WideVectorDrawableConstructor builder(renderer,scene,&vecInfo); + WideVectorDrawableConstructor builder(renderer,scene,&vecInfo,hasMaskIDs ? WhirlyKitMaxMasks : 0); const GeoCoord centerGeo = geoMbr.mid(); @@ -774,79 +1040,38 @@ SimpleIdentity WideVectorManager::addVectors(const ShapeSet &shapes,const WideVe for (const auto &shape : shapes) { - if (const auto lin = std::dynamic_pointer_cast(shape)) - { - builder.addLinear(lin->pts,centerUp,false); - } - else if (const auto ar = std::dynamic_pointer_cast(shape)) - { - for (const auto &loop : ar->loops) - { - if (loop.size() > 2 && loop.begin() != loop.end()) - { - // Just tack on another point at the end. Kind of dumb, but easy. - VectorRing newLoop = loop; - newLoop.push_back(loop[0]); - builder.addLinear(newLoop, centerUp, true); - } else - builder.addLinear(loop, centerUp, true); + // Look for mask IDs. + // Only support 2 for now + std::vector maskIDs; + if (hasMaskIDs) { + for (unsigned int ii=0;ii<2;ii++) { + std::string attrName = "maskID" + std::to_string(ii); + if (shape->getAttrDict()->hasField(attrName)) + maskIDs.push_back(shape->getAttrDict()->getInt64(attrName)); } } - } -// builder.addLinearDebug(); - - SimpleIdentity vecID = EmptyIdentity; - if (auto sceneRep = builder.flush(changes)) - { - vecID = sceneRep->getId(); - std::lock_guard guardLock(vecLock); - sceneReps.insert(sceneRep); - } - - return vecID; -} - -SimpleIdentity WideVectorManager::addVectors(const std::vector &shapes,const WideVectorInfo &vecInfo,ChangeSet &changes) -{ - // Calculate a center for this geometry - GeoMbr geoMbr; - for (const auto &shape : shapes) - { - geoMbr.expand(shape->calcGeoMbr()); - } - // No data? - if (!geoMbr.valid()) - return EmptyIdentity; - - WideVectorDrawableConstructor builder(renderer,scene,&vecInfo); - - const GeoCoord centerGeo = geoMbr.mid(); - - CoordSystemDisplayAdapter *coordAdapter = scene->getCoordAdapter(); - const Point3d localCenter = coordAdapter->getCoordSystem()->geographicToLocal3d(centerGeo); - const Point3d centerDisp = coordAdapter->localToDisplay(localCenter); - const auto centerUp = coordAdapter->isFlat() ? Point3d(0,0,1) : coordAdapter->normalForLocal(localCenter); - - builder.setCenter(localCenter,centerDisp); - - for (const auto &shape : shapes) - { + // If there's not enough masks, but there is one, then fill in the rest + if (!maskIDs.empty() && maskIDs.size() < WhirlyKitMaxMasks) { + while (maskIDs.size() < WhirlyKitMaxMasks) + maskIDs.push_back(maskIDs.front()); + } + if (const auto lin = std::dynamic_pointer_cast(shape)) { - builder.addLinear(lin->pts,centerUp,false); + builder.addLinear(lin->pts, centerUp, maskIDs, false); } - else if (const auto ar = std::dynamic_pointer_cast(shape)) + else if (const auto ar = dynamic_cast(shape.get())) { for (const auto &loop : ar->loops) { - if (loop.size() > 2 && loop.begin() != loop.end()) + if (loop.size() > 2 && (loop.begin() != loop.end() && vecInfo.implType != WideVecImplPerf)) { // Just tack on another point at the end. Kind of dumb, but easy. VectorRing newLoop = loop; newLoop.push_back(loop[0]); - builder.addLinear(newLoop, centerUp, true); + builder.addLinear(newLoop, centerUp, maskIDs, true); } else - builder.addLinear(loop, centerUp, true); + builder.addLinear(loop, centerUp, maskIDs, true); } } } @@ -856,7 +1081,7 @@ SimpleIdentity WideVectorManager::addVectors(const std::vector & if (auto sceneRep = builder.flush(changes)) { vecID = sceneRep->getId(); - std::lock_guard guardLock(vecLock); + std::lock_guard guardLock(lock); sceneReps.insert(sceneRep); } @@ -865,7 +1090,7 @@ SimpleIdentity WideVectorManager::addVectors(const std::vector & void WideVectorManager::enableVectors(SimpleIDSet &vecIDs,bool enable,ChangeSet &changes) { - std::lock_guard guardLock(vecLock); + std::lock_guard guardLock(lock); for (const auto &vit : vecIDs) { @@ -886,7 +1111,7 @@ SimpleIdentity WideVectorManager::instanceVectors(SimpleIdentity vecID,const Wid { SimpleIdentity newId = EmptyIdentity; - std::lock_guard guardLock(vecLock); + std::lock_guard guardLock(lock); // Look for the representation WideVectorSceneRep dummyRep(vecID); @@ -913,6 +1138,9 @@ SimpleIdentity WideVectorManager::instanceVectors(SimpleIdentity vecID,const Wid // Changed line width drawInst->setLineWidth(vecInfo.width); + // Changed offset +// drawInst->setLineOffset(vecInfo.offset); + // Changed draw order drawInst->setDrawOrder(vecInfo.drawOrder); @@ -930,10 +1158,47 @@ SimpleIdentity WideVectorManager::instanceVectors(SimpleIdentity vecID,const Wid return newId; } - + +void WideVectorManager::changeVectors(SimpleIdentity vecID,const WideVectorInfo &vecInfo,ChangeSet &changes) +{ + std::lock_guard guardLock(lock); + + WideVectorSceneRep dummyRep(vecID); + const auto it = sceneReps.find(&dummyRep); + if (it != sceneReps.end()) + { + const auto sceneRep = *it; + + // Make sure we change both drawables and instances + SimpleIDSet allIDs = sceneRep->drawIDs; + allIDs.insert(sceneRep->instIDs.begin(),sceneRep->instIDs.end()); + + for (auto id : allIDs) + { + // Changed color + changes.push_back(new ColorChangeRequest(id, vecInfo.color)); + + // Changed visibility + if (vecInfo.minVis != DrawVisibleInvalid || vecInfo.maxVis != DrawVisibleInvalid) + { + changes.push_back(new VisibilityChangeRequest(id, vecInfo.minVis, vecInfo.maxVis)); + } + + // Changed line width + changes.push_back(new LineWidthChangeRequest(id, vecInfo.width)); + + // Changed draw priority + changes.push_back(new DrawPriorityChangeRequest(id, vecInfo.drawPriority)); + + // Changed draw order + changes.push_back(new DrawOrderChangeRequest(id, vecInfo.drawOrder)); + } + } +} + void WideVectorManager::removeVectors(SimpleIDSet &vecIDs,ChangeSet &changes) { - std::lock_guard guardLock(vecLock); + std::lock_guard guardLock(lock); TimeInterval curTime = scene->getCurrentTime(); for (SimpleIDSet::iterator vit = vecIDs.begin();vit != vecIDs.end();++vit) diff --git a/common/local_libs/GeographicLib/include/GeographicLib/Accumulator.hpp b/common/local_libs/GeographicLib/include/GeographicLib/Accumulator.hpp new file mode 100644 index 0000000000..577b556193 --- /dev/null +++ b/common/local_libs/GeographicLib/include/GeographicLib/Accumulator.hpp @@ -0,0 +1,198 @@ +/** + * \file Accumulator.hpp + * \brief Header for GeographicLib::Accumulator class + * + * Copyright (c) Charles Karney (2010-2020) and licensed + * under the MIT/X11 License. For more information, see + * https://geographiclib.sourceforge.io/ + **********************************************************************/ + +#if !defined(GEOGRAPHICLIB_ACCUMULATOR_HPP) +#define GEOGRAPHICLIB_ACCUMULATOR_HPP 1 + +#include + +namespace GeographicLib { + + /** + * \brief An accumulator for sums + * + * This allows many numbers of floating point type \e T to be added together + * with twice the normal precision. Thus if \e T is double, the effective + * precision of the sum is 106 bits or about 32 decimal places. + * + * The implementation follows J. R. Shewchuk, + * Adaptive Precision + * Floating-Point Arithmetic and Fast Robust Geometric Predicates, + * Discrete & Computational Geometry 18(3) 305--363 (1997). + * + * Approximate timings (summing a vector) + * - double: 2ns + * - Accumulator: 23ns + * + * In the documentation of the member functions, \e sum stands for the value + * currently held in the accumulator. + * + * Example of use: + * \include example-Accumulator.cpp + **********************************************************************/ + template + class GEOGRAPHICLIB_EXPORT Accumulator { + private: + // _s + _t accumulators for the sum. + T _s, _t; + // Same as Math::sum, but requires abs(u) >= abs(v). This isn't currently + // used. + static T fastsum(T u, T v, T& t) { + GEOGRAPHICLIB_VOLATILE T s = u + v; + GEOGRAPHICLIB_VOLATILE T vp = s - u; + t = v - vp; + return s; + } + void Add(T y) { + // Here's Shewchuk's solution... + T u; // hold exact sum as [s, t, u] + // Accumulate starting at least significant end + y = Math::sum(y, _t, u); + _s = Math::sum(y, _s, _t); + // Start is _s, _t decreasing and non-adjacent. Sum is now (s + t + u) + // exactly with s, t, u non-adjacent and in decreasing order (except for + // possible zeros). The following code tries to normalize the result. + // Ideally, we want _s = round(s+t+u) and _u = round(s+t+u - _s). The + // following does an approximate job (and maintains the decreasing + // non-adjacent property). Here are two "failures" using 3-bit floats: + // + // Case 1: _s is not equal to round(s+t+u) -- off by 1 ulp + // [12, -1] - 8 -> [4, 0, -1] -> [4, -1] = 3 should be [3, 0] = 3 + // + // Case 2: _s+_t is not as close to s+t+u as it shold be + // [64, 5] + 4 -> [64, 8, 1] -> [64, 8] = 72 (off by 1) + // should be [80, -7] = 73 (exact) + // + // "Fixing" these problems is probably not worth the expense. The + // representation inevitably leads to small errors in the accumulated + // values. The additional errors illustrated here amount to 1 ulp of the + // less significant word during each addition to the Accumulator and an + // additional possible error of 1 ulp in the reported sum. + // + // Incidentally, the "ideal" representation described above is not + // canonical, because _s = round(_s + _t) may not be true. For example, + // with 3-bit floats: + // + // [128, 16] + 1 -> [160, -16] -- 160 = round(145). + // But [160, 0] - 16 -> [128, 16] -- 128 = round(144). + // + if (_s == 0) // This implies t == 0, + _s = u; // so result is u + else + _t += u; // otherwise just accumulate u to t. + } + T Sum(T y) const { + Accumulator a(*this); + a.Add(y); + return a._s; + } + public: + /** + * Construct from a \e T. This is not declared explicit, so that you can + * write Accumulator a = 5;. + * + * @param[in] y set \e sum = \e y. + **********************************************************************/ + Accumulator(T y = T(0)) : _s(y), _t(0) { + static_assert(!std::numeric_limits::is_integer, + "Accumulator type is not floating point"); + } + /** + * Set the accumulator to a number. + * + * @param[in] y set \e sum = \e y. + **********************************************************************/ + Accumulator& operator=(T y) { _s = y; _t = 0; return *this; } + /** + * Return the value held in the accumulator. + * + * @return \e sum. + **********************************************************************/ + T operator()() const { return _s; } + /** + * Return the result of adding a number to \e sum (but don't change \e + * sum). + * + * @param[in] y the number to be added to the sum. + * @return \e sum + \e y. + **********************************************************************/ + T operator()(T y) const { return Sum(y); } + /** + * Add a number to the accumulator. + * + * @param[in] y set \e sum += \e y. + **********************************************************************/ + Accumulator& operator+=(T y) { Add(y); return *this; } + /** + * Subtract a number from the accumulator. + * + * @param[in] y set \e sum -= \e y. + **********************************************************************/ + Accumulator& operator-=(T y) { Add(-y); return *this; } + /** + * Multiply accumulator by an integer. To avoid loss of accuracy, use only + * integers such that \e n × \e T is exactly representable as a \e T + * (i.e., ± powers of two). Use \e n = −1 to negate \e sum. + * + * @param[in] n set \e sum *= \e n. + **********************************************************************/ + Accumulator& operator*=(int n) { _s *= n; _t *= n; return *this; } + /** + * Multiply accumulator by a number. The fma (fused multiply and add) + * instruction is used (if available) in order to maintain accuracy. + * + * @param[in] y set \e sum *= \e y. + **********************************************************************/ + Accumulator& operator*=(T y) { + using std::fma; + T d = _s; _s *= y; + d = fma(y, d, -_s); // the error in the first multiplication + _t = fma(y, _t, d); // add error to the second term + return *this; + } + /** + * Reduce accumulator to the range [-y/2, y/2]. + * + * @param[in] y the modulus. + **********************************************************************/ + Accumulator& remainder(T y) { + using std::remainder; + _s = remainder(_s, y); + Add(0); // This renormalizes the result. + return *this; + } + /** + * Test equality of an Accumulator with a number. + **********************************************************************/ + bool operator==(T y) const { return _s == y; } + /** + * Test inequality of an Accumulator with a number. + **********************************************************************/ + bool operator!=(T y) const { return _s != y; } + /** + * Less operator on an Accumulator and a number. + **********************************************************************/ + bool operator<(T y) const { return _s < y; } + /** + * Less or equal operator on an Accumulator and a number. + **********************************************************************/ + bool operator<=(T y) const { return _s <= y; } + /** + * Greater operator on an Accumulator and a number. + **********************************************************************/ + bool operator>(T y) const { return _s > y; } + /** + * Greater or equal operator on an Accumulator and a number. + **********************************************************************/ + bool operator>=(T y) const { return _s >= y; } + }; + +} // namespace GeographicLib + +#endif // GEOGRAPHICLIB_ACCUMULATOR_HPP diff --git a/common/local_libs/GeographicLib/include/GeographicLib/AlbersEqualArea.hpp b/common/local_libs/GeographicLib/include/GeographicLib/AlbersEqualArea.hpp new file mode 100644 index 0000000000..193ed62dea --- /dev/null +++ b/common/local_libs/GeographicLib/include/GeographicLib/AlbersEqualArea.hpp @@ -0,0 +1,321 @@ +/** + * \file AlbersEqualArea.hpp + * \brief Header for GeographicLib::AlbersEqualArea class + * + * Copyright (c) Charles Karney (2010-2020) and licensed + * under the MIT/X11 License. For more information, see + * https://geographiclib.sourceforge.io/ + **********************************************************************/ + +#if !defined(GEOGRAPHICLIB_ALBERSEQUALAREA_HPP) +#define GEOGRAPHICLIB_ALBERSEQUALAREA_HPP 1 + +#include + +namespace GeographicLib { + + /** + * \brief Albers equal area conic projection + * + * Implementation taken from the report, + * - J. P. Snyder, + * Map Projections: A + * Working Manual, USGS Professional Paper 1395 (1987), + * pp. 101--102. + * + * This is a implementation of the equations in Snyder except that divided + * differences will be [have been] used to transform the expressions into + * ones which may be evaluated accurately. [In this implementation, the + * projection correctly becomes the cylindrical equal area or the azimuthal + * equal area projection when the standard latitude is the equator or a + * pole.] + * + * The ellipsoid parameters, the standard parallels, and the scale on the + * standard parallels are set in the constructor. Internally, the case with + * two standard parallels is converted into a single standard parallel, the + * latitude of minimum azimuthal scale, with an azimuthal scale specified on + * this parallel. This latitude is also used as the latitude of origin which + * is returned by AlbersEqualArea::OriginLatitude. The azimuthal scale on + * the latitude of origin is given by AlbersEqualArea::CentralScale. The + * case with two standard parallels at opposite poles is singular and is + * disallowed. The central meridian (which is a trivial shift of the + * longitude) is specified as the \e lon0 argument of the + * AlbersEqualArea::Forward and AlbersEqualArea::Reverse functions. + * AlbersEqualArea::Forward and AlbersEqualArea::Reverse also return the + * meridian convergence, γ, and azimuthal scale, \e k. A small square + * aligned with the cardinal directions is projected to a rectangle with + * dimensions \e k (in the E-W direction) and 1/\e k (in the N-S direction). + * The E-W sides of the rectangle are oriented γ degrees + * counter-clockwise from the \e x axis. There is no provision in this class + * for specifying a false easting or false northing or a different latitude + * of origin. + * + * Example of use: + * \include example-AlbersEqualArea.cpp + * + * ConicProj is a command-line utility + * providing access to the functionality of LambertConformalConic and + * AlbersEqualArea. + **********************************************************************/ + class GEOGRAPHICLIB_EXPORT AlbersEqualArea { + private: + typedef Math::real real; + real eps_, epsx_, epsx2_, tol_, tol0_; + real _a, _f, _fm, _e2, _e, _e2m, _qZ, _qx; + real _sign, _lat0, _k0; + real _n0, _m02, _nrho0, _k2, _txi0, _scxi0, _sxi0; + static const int numit_ = 5; // Newton iterations in Reverse + static const int numit0_ = 20; // Newton iterations in Init + static real hyp(real x) { + using std::hypot; + return hypot(real(1), x); + } + // atanh( e * x)/ e if f > 0 + // atan (sqrt(-e2) * x)/sqrt(-e2) if f < 0 + // x if f = 0 + real atanhee(real x) const { + using std::atan2; using std::abs; using std::atanh; + return _f > 0 ? atanh(_e * x)/_e : + // We only invoke atanhee in txif for positive latitude. Then x is + // only negative for very prolate ellipsoids (_b/_a >= sqrt(2)) and we + // still need to return a positive result in this case; hence the need + // for the call to atan2. + (_f < 0 ? (atan2(_e * abs(x), real(x < 0 ? -1 : 1))/_e) : x); + } + // return atanh(sqrt(x))/sqrt(x) - 1, accurate for small x + static real atanhxm1(real x); + + // Divided differences + // Definition: Df(x,y) = (f(x)-f(y))/(x-y) + // See: + // W. M. Kahan and R. J. Fateman, + // Symbolic computation of divided differences, + // SIGSAM Bull. 33(3), 7-28 (1999) + // https://doi.org/10.1145/334714.334716 + // http://www.cs.berkeley.edu/~fateman/papers/divdiff.pdf + // + // General rules + // h(x) = f(g(x)): Dh(x,y) = Df(g(x),g(y))*Dg(x,y) + // h(x) = f(x)*g(x): + // Dh(x,y) = Df(x,y)*g(x) + Dg(x,y)*f(y) + // = Df(x,y)*g(y) + Dg(x,y)*f(x) + // = Df(x,y)*(g(x)+g(y))/2 + Dg(x,y)*(f(x)+f(y))/2 + // + // sn(x) = x/sqrt(1+x^2): Dsn(x,y) = (x+y)/((sn(x)+sn(y))*(1+x^2)*(1+y^2)) + static real Dsn(real x, real y, real sx, real sy) { + // sx = x/hyp(x) + real t = x * y; + return t > 0 ? (x + y) * Math::sq( (sx * sy)/t ) / (sx + sy) : + (x - y != 0 ? (sx - sy) / (x - y) : 1); + } + // Datanhee(x,y) = atanhee((x-y)/(1-e^2*x*y))/(x-y) + real Datanhee(real x, real y) const { + real t = x - y, d = 1 - _e2 * x * y; + return t != 0 ? atanhee(t / d) / t : 1 / d; + } + // DDatanhee(x,y) = (Datanhee(1,y) - Datanhee(1,x))/(y-x) + real DDatanhee(real x, real y) const; + void Init(real sphi1, real cphi1, real sphi2, real cphi2, real k1); + real txif(real tphi) const; + real tphif(real txi) const; + + friend class Ellipsoid; // For access to txif, tphif, etc. + public: + + /** + * Constructor with a single standard parallel. + * + * @param[in] a equatorial radius of ellipsoid (meters). + * @param[in] f flattening of ellipsoid. Setting \e f = 0 gives a sphere. + * Negative \e f gives a prolate ellipsoid. + * @param[in] stdlat standard parallel (degrees), the circle of tangency. + * @param[in] k0 azimuthal scale on the standard parallel. + * @exception GeographicErr if \e a, (1 − \e f) \e a, or \e k0 is + * not positive. + * @exception GeographicErr if \e stdlat is not in [−90°, + * 90°]. + **********************************************************************/ + AlbersEqualArea(real a, real f, real stdlat, real k0); + + /** + * Constructor with two standard parallels. + * + * @param[in] a equatorial radius of ellipsoid (meters). + * @param[in] f flattening of ellipsoid. Setting \e f = 0 gives a sphere. + * Negative \e f gives a prolate ellipsoid. + * @param[in] stdlat1 first standard parallel (degrees). + * @param[in] stdlat2 second standard parallel (degrees). + * @param[in] k1 azimuthal scale on the standard parallels. + * @exception GeographicErr if \e a, (1 − \e f) \e a, or \e k1 is + * not positive. + * @exception GeographicErr if \e stdlat1 or \e stdlat2 is not in + * [−90°, 90°], or if \e stdlat1 and \e stdlat2 are + * opposite poles. + **********************************************************************/ + AlbersEqualArea(real a, real f, real stdlat1, real stdlat2, real k1); + + /** + * Constructor with two standard parallels specified by sines and cosines. + * + * @param[in] a equatorial radius of ellipsoid (meters). + * @param[in] f flattening of ellipsoid. Setting \e f = 0 gives a sphere. + * Negative \e f gives a prolate ellipsoid. + * @param[in] sinlat1 sine of first standard parallel. + * @param[in] coslat1 cosine of first standard parallel. + * @param[in] sinlat2 sine of second standard parallel. + * @param[in] coslat2 cosine of second standard parallel. + * @param[in] k1 azimuthal scale on the standard parallels. + * @exception GeographicErr if \e a, (1 − \e f) \e a, or \e k1 is + * not positive. + * @exception GeographicErr if \e stdlat1 or \e stdlat2 is not in + * [−90°, 90°], or if \e stdlat1 and \e stdlat2 are + * opposite poles. + * + * This allows parallels close to the poles to be specified accurately. + * This routine computes the latitude of origin and the azimuthal scale at + * this latitude. If \e dlat = abs(\e lat2 − \e lat1) ≤ 160°, + * then the error in the latitude of origin is less than 4.5 × + * 10−14d;. + **********************************************************************/ + AlbersEqualArea(real a, real f, + real sinlat1, real coslat1, + real sinlat2, real coslat2, + real k1); + + /** + * Set the azimuthal scale for the projection. + * + * @param[in] lat (degrees). + * @param[in] k azimuthal scale at latitude \e lat (default 1). + * @exception GeographicErr \e k is not positive. + * @exception GeographicErr if \e lat is not in (−90°, + * 90°). + * + * This allows a "latitude of conformality" to be specified. + **********************************************************************/ + void SetScale(real lat, real k = real(1)); + + /** + * Forward projection, from geographic to Lambert conformal conic. + * + * @param[in] lon0 central meridian longitude (degrees). + * @param[in] lat latitude of point (degrees). + * @param[in] lon longitude of point (degrees). + * @param[out] x easting of point (meters). + * @param[out] y northing of point (meters). + * @param[out] gamma meridian convergence at point (degrees). + * @param[out] k azimuthal scale of projection at point; the radial + * scale is the 1/\e k. + * + * The latitude origin is given by AlbersEqualArea::LatitudeOrigin(). No + * false easting or northing is added and \e lat should be in the range + * [−90°, 90°]. The values of \e x and \e y returned for + * points which project to infinity (i.e., one or both of the poles) will + * be large but finite. + **********************************************************************/ + void Forward(real lon0, real lat, real lon, + real& x, real& y, real& gamma, real& k) const; + + /** + * Reverse projection, from Lambert conformal conic to geographic. + * + * @param[in] lon0 central meridian longitude (degrees). + * @param[in] x easting of point (meters). + * @param[in] y northing of point (meters). + * @param[out] lat latitude of point (degrees). + * @param[out] lon longitude of point (degrees). + * @param[out] gamma meridian convergence at point (degrees). + * @param[out] k azimuthal scale of projection at point; the radial + * scale is the 1/\e k. + * + * The latitude origin is given by AlbersEqualArea::LatitudeOrigin(). No + * false easting or northing is added. The value of \e lon returned is in + * the range [−180°, 180°]. The value of \e lat returned is + * in the range [−90°, 90°]. If the input point is outside + * the legal projected space the nearest pole is returned. + **********************************************************************/ + void Reverse(real lon0, real x, real y, + real& lat, real& lon, real& gamma, real& k) const; + + /** + * AlbersEqualArea::Forward without returning the convergence and + * scale. + **********************************************************************/ + void Forward(real lon0, real lat, real lon, + real& x, real& y) const { + real gamma, k; + Forward(lon0, lat, lon, x, y, gamma, k); + } + + /** + * AlbersEqualArea::Reverse without returning the convergence and + * scale. + **********************************************************************/ + void Reverse(real lon0, real x, real y, + real& lat, real& lon) const { + real gamma, k; + Reverse(lon0, x, y, lat, lon, gamma, k); + } + + /** \name Inspector functions + **********************************************************************/ + ///@{ + /** + * @return \e a the equatorial radius of the ellipsoid (meters). This is + * the value used in the constructor. + **********************************************************************/ + Math::real EquatorialRadius() const { return _a; } + + /** + * @return \e f the flattening of the ellipsoid. This is the value used in + * the constructor. + **********************************************************************/ + Math::real Flattening() const { return _f; } + + /** + * @return latitude of the origin for the projection (degrees). + * + * This is the latitude of minimum azimuthal scale and equals the \e stdlat + * in the 1-parallel constructor and lies between \e stdlat1 and \e stdlat2 + * in the 2-parallel constructors. + **********************************************************************/ + Math::real OriginLatitude() const { return _lat0; } + + /** + * @return central scale for the projection. This is the azimuthal scale + * on the latitude of origin. + **********************************************************************/ + Math::real CentralScale() const { return _k0; } + + /** + * \deprecated An old name for EquatorialRadius(). + **********************************************************************/ + GEOGRAPHICLIB_DEPRECATED("Use EquatorialRadius()") + Math::real MajorRadius() const { return EquatorialRadius(); } + ///@} + + /** + * A global instantiation of AlbersEqualArea with the WGS84 ellipsoid, \e + * stdlat = 0, and \e k0 = 1. This degenerates to the cylindrical equal + * area projection. + **********************************************************************/ + static const AlbersEqualArea& CylindricalEqualArea(); + + /** + * A global instantiation of AlbersEqualArea with the WGS84 ellipsoid, \e + * stdlat = 90°, and \e k0 = 1. This degenerates to the + * Lambert azimuthal equal area projection. + **********************************************************************/ + static const AlbersEqualArea& AzimuthalEqualAreaNorth(); + + /** + * A global instantiation of AlbersEqualArea with the WGS84 ellipsoid, \e + * stdlat = −90°, and \e k0 = 1. This degenerates to the + * Lambert azimuthal equal area projection. + **********************************************************************/ + static const AlbersEqualArea& AzimuthalEqualAreaSouth(); + }; + +} // namespace GeographicLib + +#endif // GEOGRAPHICLIB_ALBERSEQUALAREA_HPP diff --git a/common/local_libs/GeographicLib/include/GeographicLib/AzimuthalEquidistant.hpp b/common/local_libs/GeographicLib/include/GeographicLib/AzimuthalEquidistant.hpp new file mode 100644 index 0000000000..05a6c41be9 --- /dev/null +++ b/common/local_libs/GeographicLib/include/GeographicLib/AzimuthalEquidistant.hpp @@ -0,0 +1,145 @@ +/** + * \file AzimuthalEquidistant.hpp + * \brief Header for GeographicLib::AzimuthalEquidistant class + * + * Copyright (c) Charles Karney (2009-2020) and licensed + * under the MIT/X11 License. For more information, see + * https://geographiclib.sourceforge.io/ + **********************************************************************/ + +#if !defined(GEOGRAPHICLIB_AZIMUTHALEQUIDISTANT_HPP) +#define GEOGRAPHICLIB_AZIMUTHALEQUIDISTANT_HPP 1 + +#include +#include + +namespace GeographicLib { + + /** + * \brief Azimuthal equidistant projection + * + * Azimuthal equidistant projection centered at an arbitrary position on the + * ellipsoid. For a point in projected space (\e x, \e y), the geodesic + * distance from the center position is hypot(\e x, \e y) and the azimuth of + * the geodesic from the center point is atan2(\e x, \e y). The Forward and + * Reverse methods also return the azimuth \e azi of the geodesic at (\e x, + * \e y) and reciprocal scale \e rk in the azimuthal direction which, + * together with the basic properties of the projection, serve to specify + * completely the local affine transformation between geographic and + * projected coordinates. + * + * The conversions all take place using a Geodesic object (by default + * Geodesic::WGS84()). For more information on geodesics see \ref geodesic. + * + * Example of use: + * \include example-AzimuthalEquidistant.cpp + * + * GeodesicProj is a command-line utility + * providing access to the functionality of AzimuthalEquidistant, Gnomonic, + * and CassiniSoldner. + **********************************************************************/ + + class GEOGRAPHICLIB_EXPORT AzimuthalEquidistant { + private: + typedef Math::real real; + real eps_; + Geodesic _earth; + public: + + /** + * Constructor for AzimuthalEquidistant. + * + * @param[in] earth the Geodesic object to use for geodesic calculations. + * By default this uses the WGS84 ellipsoid. + **********************************************************************/ + explicit AzimuthalEquidistant(const Geodesic& earth = Geodesic::WGS84()); + + /** + * Forward projection, from geographic to azimuthal equidistant. + * + * @param[in] lat0 latitude of center point of projection (degrees). + * @param[in] lon0 longitude of center point of projection (degrees). + * @param[in] lat latitude of point (degrees). + * @param[in] lon longitude of point (degrees). + * @param[out] x easting of point (meters). + * @param[out] y northing of point (meters). + * @param[out] azi azimuth of geodesic at point (degrees). + * @param[out] rk reciprocal of azimuthal scale at point. + * + * \e lat0 and \e lat should be in the range [−90°, 90°]. + * The scale of the projection is 1 in the "radial" direction, \e azi + * clockwise from true north, and is 1/\e rk in the direction perpendicular + * to this. A call to Forward followed by a call to Reverse will return + * the original (\e lat, \e lon) (to within roundoff). + **********************************************************************/ + void Forward(real lat0, real lon0, real lat, real lon, + real& x, real& y, real& azi, real& rk) const; + + /** + * Reverse projection, from azimuthal equidistant to geographic. + * + * @param[in] lat0 latitude of center point of projection (degrees). + * @param[in] lon0 longitude of center point of projection (degrees). + * @param[in] x easting of point (meters). + * @param[in] y northing of point (meters). + * @param[out] lat latitude of point (degrees). + * @param[out] lon longitude of point (degrees). + * @param[out] azi azimuth of geodesic at point (degrees). + * @param[out] rk reciprocal of azimuthal scale at point. + * + * \e lat0 should be in the range [−90°, 90°]. \e lat will + * be in the range [−90°, 90°] and \e lon will be in the + * range [−180°, 180°]. The scale of the projection is 1 in + * the "radial" direction, \e azi clockwise from true north, and is 1/\e rk + * in the direction perpendicular to this. A call to Reverse followed by a + * call to Forward will return the original (\e x, \e y) (to roundoff) only + * if the geodesic to (\e x, \e y) is a shortest path. + **********************************************************************/ + void Reverse(real lat0, real lon0, real x, real y, + real& lat, real& lon, real& azi, real& rk) const; + + /** + * AzimuthalEquidistant::Forward without returning the azimuth and scale. + **********************************************************************/ + void Forward(real lat0, real lon0, real lat, real lon, + real& x, real& y) const { + real azi, rk; + Forward(lat0, lon0, lat, lon, x, y, azi, rk); + } + + /** + * AzimuthalEquidistant::Reverse without returning the azimuth and scale. + **********************************************************************/ + void Reverse(real lat0, real lon0, real x, real y, + real& lat, real& lon) const { + real azi, rk; + Reverse(lat0, lon0, x, y, lat, lon, azi, rk); + } + + /** \name Inspector functions + **********************************************************************/ + ///@{ + /** + * @return \e a the equatorial radius of the ellipsoid (meters). This is + * the value inherited from the Geodesic object used in the constructor. + **********************************************************************/ + Math::real EquatorialRadius() const { return _earth.EquatorialRadius(); } + + /** + * @return \e f the flattening of the ellipsoid. This is the value + * inherited from the Geodesic object used in the constructor. + **********************************************************************/ + Math::real Flattening() const { return _earth.Flattening(); } + + /** + * \deprecated An old name for EquatorialRadius(). + **********************************************************************/ + GEOGRAPHICLIB_DEPRECATED("Use EquatorialRadius()") + Math::real MajorRadius() const { return EquatorialRadius(); } + ///@} + + }; + +} // namespace GeographicLib + +#endif // GEOGRAPHICLIB_AZIMUTHALEQUIDISTANT_HPP diff --git a/common/local_libs/GeographicLib/include/GeographicLib/CassiniSoldner.hpp b/common/local_libs/GeographicLib/include/GeographicLib/CassiniSoldner.hpp new file mode 100644 index 0000000000..fdc0957ba1 --- /dev/null +++ b/common/local_libs/GeographicLib/include/GeographicLib/CassiniSoldner.hpp @@ -0,0 +1,210 @@ +/** + * \file CassiniSoldner.hpp + * \brief Header for GeographicLib::CassiniSoldner class + * + * Copyright (c) Charles Karney (2009-2020) and licensed + * under the MIT/X11 License. For more information, see + * https://geographiclib.sourceforge.io/ + **********************************************************************/ + +#if !defined(GEOGRAPHICLIB_CASSINISOLDNER_HPP) +#define GEOGRAPHICLIB_CASSINISOLDNER_HPP 1 + +#include +#include +#include + +namespace GeographicLib { + + /** + * \brief Cassini-Soldner projection + * + * Cassini-Soldner projection centered at an arbitrary position, \e lat0, \e + * lon0, on the ellipsoid. This projection is a transverse cylindrical + * equidistant projection. The projection from (\e lat, \e lon) to easting + * and northing (\e x, \e y) is defined by geodesics as follows. Go north + * along a geodesic a distance \e y from the central point; then turn + * clockwise 90° and go a distance \e x along a geodesic. + * (Although the initial heading is north, this changes to south if the pole + * is crossed.) This procedure uniquely defines the reverse projection. The + * forward projection is constructed as follows. Find the point (\e lat1, \e + * lon1) on the meridian closest to (\e lat, \e lon). Here we consider the + * full meridian so that \e lon1 may be either \e lon0 or \e lon0 + + * 180°. \e x is the geodesic distance from (\e lat1, \e lon1) to + * (\e lat, \e lon), appropriately signed according to which side of the + * central meridian (\e lat, \e lon) lies. \e y is the shortest distance + * along the meridian from (\e lat0, \e lon0) to (\e lat1, \e lon1), again, + * appropriately signed according to the initial heading. [Note that, in the + * case of prolate ellipsoids, the shortest meridional path from (\e lat0, \e + * lon0) to (\e lat1, \e lon1) may not be the shortest path.] This procedure + * uniquely defines the forward projection except for a small class of points + * for which there may be two equally short routes for either leg of the + * path. + * + * Because of the properties of geodesics, the (\e x, \e y) grid is + * orthogonal. The scale in the easting direction is unity. The scale, \e + * k, in the northing direction is unity on the central meridian and + * increases away from the central meridian. The projection routines return + * \e azi, the true bearing of the easting direction, and \e rk = 1/\e k, the + * reciprocal of the scale in the northing direction. + * + * The conversions all take place using a Geodesic object (by default + * Geodesic::WGS84()). For more information on geodesics see \ref geodesic. + * The determination of (\e lat1, \e lon1) in the forward projection is by + * solving the inverse geodesic problem for (\e lat, \e lon) and its twin + * obtained by reflection in the meridional plane. The scale is found by + * determining where two neighboring geodesics intersecting the central + * meridian at \e lat1 and \e lat1 + \e dlat1 intersect and taking the ratio + * of the reduced lengths for the two geodesics between that point and, + * respectively, (\e lat1, \e lon1) and (\e lat, \e lon). + * + * Example of use: + * \include example-CassiniSoldner.cpp + * + * GeodesicProj is a command-line utility + * providing access to the functionality of AzimuthalEquidistant, Gnomonic, + * and CassiniSoldner. + **********************************************************************/ + + class GEOGRAPHICLIB_EXPORT CassiniSoldner { + private: + typedef Math::real real; + Geodesic _earth; + GeodesicLine _meridian; + real _sbet0, _cbet0; + static const unsigned maxit_ = 10; + + public: + + /** + * Constructor for CassiniSoldner. + * + * @param[in] earth the Geodesic object to use for geodesic calculations. + * By default this uses the WGS84 ellipsoid. + * + * This constructor makes an "uninitialized" object. Call Reset to set the + * central latitude and longitude, prior to calling Forward and Reverse. + **********************************************************************/ + explicit CassiniSoldner(const Geodesic& earth = Geodesic::WGS84()); + + /** + * Constructor for CassiniSoldner specifying a center point. + * + * @param[in] lat0 latitude of center point of projection (degrees). + * @param[in] lon0 longitude of center point of projection (degrees). + * @param[in] earth the Geodesic object to use for geodesic calculations. + * By default this uses the WGS84 ellipsoid. + * + * \e lat0 should be in the range [−90°, 90°]. + **********************************************************************/ + CassiniSoldner(real lat0, real lon0, + const Geodesic& earth = Geodesic::WGS84()); + + /** + * Set the central point of the projection + * + * @param[in] lat0 latitude of center point of projection (degrees). + * @param[in] lon0 longitude of center point of projection (degrees). + * + * \e lat0 should be in the range [−90°, 90°]. + **********************************************************************/ + void Reset(real lat0, real lon0); + + /** + * Forward projection, from geographic to Cassini-Soldner. + * + * @param[in] lat latitude of point (degrees). + * @param[in] lon longitude of point (degrees). + * @param[out] x easting of point (meters). + * @param[out] y northing of point (meters). + * @param[out] azi azimuth of easting direction at point (degrees). + * @param[out] rk reciprocal of azimuthal northing scale at point. + * + * \e lat should be in the range [−90°, 90°]. A call to + * Forward followed by a call to Reverse will return the original (\e lat, + * \e lon) (to within roundoff). The routine does nothing if the origin + * has not been set. + **********************************************************************/ + void Forward(real lat, real lon, + real& x, real& y, real& azi, real& rk) const; + + /** + * Reverse projection, from Cassini-Soldner to geographic. + * + * @param[in] x easting of point (meters). + * @param[in] y northing of point (meters). + * @param[out] lat latitude of point (degrees). + * @param[out] lon longitude of point (degrees). + * @param[out] azi azimuth of easting direction at point (degrees). + * @param[out] rk reciprocal of azimuthal northing scale at point. + * + * A call to Reverse followed by a call to Forward will return the original + * (\e x, \e y) (to within roundoff), provided that \e x and \e y are + * sufficiently small not to "wrap around" the earth. The routine does + * nothing if the origin has not been set. + **********************************************************************/ + void Reverse(real x, real y, + real& lat, real& lon, real& azi, real& rk) const; + + /** + * CassiniSoldner::Forward without returning the azimuth and scale. + **********************************************************************/ + void Forward(real lat, real lon, + real& x, real& y) const { + real azi, rk; + Forward(lat, lon, x, y, azi, rk); + } + + /** + * CassiniSoldner::Reverse without returning the azimuth and scale. + **********************************************************************/ + void Reverse(real x, real y, + real& lat, real& lon) const { + real azi, rk; + Reverse(x, y, lat, lon, azi, rk); + } + + /** \name Inspector functions + **********************************************************************/ + ///@{ + /** + * @return true if the object has been initialized. + **********************************************************************/ + bool Init() const { return _meridian.Init(); } + + /** + * @return \e lat0 the latitude of origin (degrees). + **********************************************************************/ + Math::real LatitudeOrigin() const + { return _meridian.Latitude(); } + + /** + * @return \e lon0 the longitude of origin (degrees). + **********************************************************************/ + Math::real LongitudeOrigin() const + { return _meridian.Longitude(); } + + /** + * @return \e a the equatorial radius of the ellipsoid (meters). This is + * the value inherited from the Geodesic object used in the constructor. + **********************************************************************/ + Math::real EquatorialRadius() const { return _earth.EquatorialRadius(); } + + /** + * @return \e f the flattening of the ellipsoid. This is the value + * inherited from the Geodesic object used in the constructor. + **********************************************************************/ + Math::real Flattening() const { return _earth.Flattening(); } + + /** + * \deprecated An old name for EquatorialRadius(). + **********************************************************************/ + GEOGRAPHICLIB_DEPRECATED("Use EquatorialRadius()") + Math::real MajorRadius() const { return EquatorialRadius(); } + ///@} + + }; + +} // namespace GeographicLib + +#endif // GEOGRAPHICLIB_CASSINISOLDNER_HPP diff --git a/common/local_libs/GeographicLib/include/GeographicLib/CircularEngine.hpp b/common/local_libs/GeographicLib/include/GeographicLib/CircularEngine.hpp new file mode 100644 index 0000000000..ef43a215c5 --- /dev/null +++ b/common/local_libs/GeographicLib/include/GeographicLib/CircularEngine.hpp @@ -0,0 +1,195 @@ +/** + * \file CircularEngine.hpp + * \brief Header for GeographicLib::CircularEngine class + * + * Copyright (c) Charles Karney (2011-2015) and licensed + * under the MIT/X11 License. For more information, see + * https://geographiclib.sourceforge.io/ + **********************************************************************/ + +#if !defined(GEOGRAPHICLIB_CIRCULARENGINE_HPP) +#define GEOGRAPHICLIB_CIRCULARENGINE_HPP 1 + +#include +#include +#include + +#if defined(_MSC_VER) +// Squelch warnings about dll vs vector +# pragma warning (push) +# pragma warning (disable: 4251) +#endif + +namespace GeographicLib { + + /** + * \brief Spherical harmonic sums for a circle + * + * The class is a companion to SphericalEngine. If the results of a + * spherical harmonic sum are needed for several points on a circle of + * constant latitude \e lat and height \e h, then SphericalEngine::Circle can + * compute the inner sum, which is independent of longitude \e lon, and + * produce a CircularEngine object. CircularEngine::operator()() can + * then be used to perform the outer sum for particular vales of \e lon. + * This can lead to substantial improvements in computational speed for high + * degree sum (approximately by a factor of \e N / 2 where \e N is the + * maximum degree). + * + * CircularEngine is tightly linked to the internals of SphericalEngine. For + * that reason, the constructor for this class is private. Use + * SphericalHarmonic::Circle, SphericalHarmonic1::Circle, and + * SphericalHarmonic2::Circle to create instances of this class. + * + * CircularEngine stores the coefficients needed to allow the summation over + * order to be performed in 2 or 6 vectors of length \e M + 1 (depending on + * whether gradients are to be calculated). For this reason the constructor + * may throw a std::bad_alloc exception. + * + * Example of use: + * \include example-CircularEngine.cpp + **********************************************************************/ + + class GEOGRAPHICLIB_EXPORT CircularEngine { + private: + typedef Math::real real; + enum normalization { + FULL = SphericalEngine::FULL, + SCHMIDT = SphericalEngine::SCHMIDT, + }; + int _M; + bool _gradp; + unsigned _norm; + real _a, _r, _u, _t; + std::vector _wc, _ws, _wrc, _wrs, _wtc, _wts; + real _q, _uq, _uq2; + + Math::real Value(bool gradp, real sl, real cl, + real& gradx, real& grady, real& gradz) const; + + friend class SphericalEngine; + CircularEngine(int M, bool gradp, unsigned norm, + real a, real r, real u, real t) + : _M(M) + , _gradp(gradp) + , _norm(norm) + , _a(a) + , _r(r) + , _u(u) + , _t(t) + , _wc(std::vector(_M + 1, 0)) + , _ws(std::vector(_M + 1, 0)) + , _wrc(std::vector(_gradp ? _M + 1 : 0, 0)) + , _wrs(std::vector(_gradp ? _M + 1 : 0, 0)) + , _wtc(std::vector(_gradp ? _M + 1 : 0, 0)) + , _wts(std::vector(_gradp ? _M + 1 : 0, 0)) + { + _q = _a / _r; + _uq = _u * _q; + _uq2 = Math::sq(_uq); + } + + void SetCoeff(int m, real wc, real ws) + { _wc[m] = wc; _ws[m] = ws; } + + void SetCoeff(int m, real wc, real ws, + real wrc, real wrs, real wtc, real wts) { + _wc[m] = wc; _ws[m] = ws; + if (_gradp) { + _wrc[m] = wrc; _wrs[m] = wrs; + _wtc[m] = wtc; _wts[m] = wts; + } + } + + public: + + /** + * A default constructor. CircularEngine::operator()() on the resulting + * object returns zero. The resulting object can be assigned to the result + * of SphericalHarmonic::Circle. + **********************************************************************/ + CircularEngine() + : _M(-1) + , _gradp(true) + , _u(0) + , _t(1) + {} + + /** + * Evaluate the sum for a particular longitude given in terms of its + * sine and cosine. + * + * @param[in] sinlon the sine of the longitude. + * @param[in] coslon the cosine of the longitude. + * @return \e V the value of the sum. + * + * The arguments must satisfy sinlon2 + + * coslon2 = 1. + **********************************************************************/ + Math::real operator()(real sinlon, real coslon) const { + real dummy; + return Value(false, sinlon, coslon, dummy, dummy, dummy); + } + + /** + * Evaluate the sum for a particular longitude. + * + * @param[in] lon the longitude (degrees). + * @return \e V the value of the sum. + **********************************************************************/ + Math::real operator()(real lon) const { + real sinlon, coslon; + Math::sincosd(lon, sinlon, coslon); + return (*this)(sinlon, coslon); + } + + /** + * Evaluate the sum and its gradient for a particular longitude given in + * terms of its sine and cosine. + * + * @param[in] sinlon the sine of the longitude. + * @param[in] coslon the cosine of the longitude. + * @param[out] gradx \e x component of the gradient. + * @param[out] grady \e y component of the gradient. + * @param[out] gradz \e z component of the gradient. + * @return \e V the value of the sum. + * + * The gradients will only be computed if the CircularEngine object was + * created with this capability (e.g., via \e gradp = true in + * SphericalHarmonic::Circle). If not, \e gradx, etc., will not be + * touched. The arguments must satisfy sinlon2 + + * coslon2 = 1. + **********************************************************************/ + Math::real operator()(real sinlon, real coslon, + real& gradx, real& grady, real& gradz) const { + return Value(true, sinlon, coslon, gradx, grady, gradz); + } + + /** + * Evaluate the sum and its gradient for a particular longitude. + * + * @param[in] lon the longitude (degrees). + * @param[out] gradx \e x component of the gradient. + * @param[out] grady \e y component of the gradient. + * @param[out] gradz \e z component of the gradient. + * @return \e V the value of the sum. + * + * The gradients will only be computed if the CircularEngine object was + * created with this capability (e.g., via \e gradp = true in + * SphericalHarmonic::Circle). If not, \e gradx, etc., will not be + * touched. + **********************************************************************/ + Math::real operator()(real lon, + real& gradx, real& grady, real& gradz) const { + real sinlon, coslon; + Math::sincosd(lon, sinlon, coslon); + return (*this)(sinlon, coslon, gradx, grady, gradz); + } + }; + +} // namespace GeographicLib + +#if defined(_MSC_VER) +# pragma warning (pop) +#endif + +#endif // GEOGRAPHICLIB_CIRCULARENGINE_HPP diff --git a/common/local_libs/GeographicLib/include/GeographicLib/Config-ac.h.in b/common/local_libs/GeographicLib/include/GeographicLib/Config-ac.h.in new file mode 100644 index 0000000000..ac3ba0bc54 --- /dev/null +++ b/common/local_libs/GeographicLib/include/GeographicLib/Config-ac.h.in @@ -0,0 +1,91 @@ +/* include/GeographicLib/Config-ac.h.in. Generated from configure.ac by autoheader. */ + +/* Define if building universal (internal helper macro) */ +#undef AC_APPLE_UNIVERSAL_BUILD + +/* major version number */ +#undef GEOGRAPHICLIB_VERSION_MAJOR + +/* minor version number */ +#undef GEOGRAPHICLIB_VERSION_MINOR + +/* patch number */ +#undef GEOGRAPHICLIB_VERSION_PATCH + +/* define if the compiler supports basic C++11 syntax */ +#undef HAVE_CXX11 + +/* Define to 1 if you have the header file. */ +#undef HAVE_DLFCN_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_INTTYPES_H + +/* Define to 1 if the system has the type `long double'. */ +#undef HAVE_LONG_DOUBLE + +/* Define to 1 if you have the header file. */ +#undef HAVE_MEMORY_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_STDINT_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_STDLIB_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_STRINGS_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_STRING_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_SYS_STAT_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_SYS_TYPES_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_UNISTD_H + +/* Define to the sub-directory where libtool stores uninstalled libraries. */ +#undef LT_OBJDIR + +/* Name of package */ +#undef PACKAGE + +/* Define to the address where bug reports for this package should be sent. */ +#undef PACKAGE_BUGREPORT + +/* Define to the full name of this package. */ +#undef PACKAGE_NAME + +/* Define to the full name and version of this package. */ +#undef PACKAGE_STRING + +/* Define to the one symbol short name of this package. */ +#undef PACKAGE_TARNAME + +/* Define to the home page for this package. */ +#undef PACKAGE_URL + +/* Define to the version of this package. */ +#undef PACKAGE_VERSION + +/* Define to 1 if you have the ANSI C header files. */ +#undef STDC_HEADERS + +/* Version number of package */ +#undef VERSION + +/* Define WORDS_BIGENDIAN to 1 if your processor stores words with the most + significant byte first (like Motorola and SPARC, unlike Intel). */ +#if defined AC_APPLE_UNIVERSAL_BUILD +# if defined __BIG_ENDIAN__ +# define WORDS_BIGENDIAN 1 +# endif +#else +# ifndef WORDS_BIGENDIAN +# undef WORDS_BIGENDIAN +# endif +#endif diff --git a/common/local_libs/GeographicLib/include/GeographicLib/Config.h b/common/local_libs/GeographicLib/include/GeographicLib/Config.h new file mode 100644 index 0000000000..96d39fc259 --- /dev/null +++ b/common/local_libs/GeographicLib/include/GeographicLib/Config.h @@ -0,0 +1,12 @@ +// This will be overwritten by ./configure + +#define GEOGRAPHICLIB_VERSION_STRING "1.51" +#define GEOGRAPHICLIB_VERSION_MAJOR 1 +#define GEOGRAPHICLIB_VERSION_MINOR 51 +#define GEOGRAPHICLIB_VERSION_PATCH 0 + +// Undefine HAVE_LONG_DOUBLE if this type is unknown to the compiler +#define GEOGRAPHICLIB_HAVE_LONG_DOUBLE 1 + +// Define WORDS_BIGENDIAN to be 1 if your machine is big endian +/* #undef WORDS_BIGENDIAN */ diff --git a/common/local_libs/GeographicLib/include/GeographicLib/Constants.hpp b/common/local_libs/GeographicLib/include/GeographicLib/Constants.hpp new file mode 100644 index 0000000000..f6a77325b6 --- /dev/null +++ b/common/local_libs/GeographicLib/include/GeographicLib/Constants.hpp @@ -0,0 +1,329 @@ +/** + * \file Constants.hpp + * \brief Header for GeographicLib::Constants class + * + * Copyright (c) Charles Karney (2008-2020) and licensed + * under the MIT/X11 License. For more information, see + * https://geographiclib.sourceforge.io/ + **********************************************************************/ + +#if !defined(GEOGRAPHICLIB_CONSTANTS_HPP) +#define GEOGRAPHICLIB_CONSTANTS_HPP 1 + +#include + +/** + * @relates GeographicLib::Constants + * Pack the version components into a single integer. Users should not rely on + * this particular packing of the components of the version number; see the + * documentation for GEOGRAPHICLIB_VERSION, below. + **********************************************************************/ +#define GEOGRAPHICLIB_VERSION_NUM(a,b,c) ((((a) * 10000 + (b)) * 100) + (c)) + +/** + * @relates GeographicLib::Constants + * The version of GeographicLib as a single integer, packed as MMmmmmpp where + * MM is the major version, mmmm is the minor version, and pp is the patch + * level. Users should not rely on this particular packing of the components + * of the version number. Instead they should use a test such as \code + #if GEOGRAPHICLIB_VERSION >= GEOGRAPHICLIB_VERSION_NUM(1,37,0) + ... + #endif + * \endcode + **********************************************************************/ +#define GEOGRAPHICLIB_VERSION \ + GEOGRAPHICLIB_VERSION_NUM(GEOGRAPHICLIB_VERSION_MAJOR, \ + GEOGRAPHICLIB_VERSION_MINOR, \ + GEOGRAPHICLIB_VERSION_PATCH) + +// For reference, here is a table of Visual Studio and _MSC_VER +// correspondences: +// +// _MSC_VER Visual Studio +// 1100 vc5 +// 1200 vc6 +// 1300 vc7 +// 1310 vc7.1 (2003) +// 1400 vc8 (2005) +// 1500 vc9 (2008) +// 1600 vc10 (2010) +// 1700 vc11 (2012) +// 1800 vc12 (2013) First version of VS to include enough C++11 support +// 1900 vc14 (2015) +// 191[0-9] vc15 (2017) +// 192[0-9] vc16 (2019) + +#if defined(_MSC_VER) && defined(GEOGRAPHICLIB_SHARED_LIB) && \ + GEOGRAPHICLIB_SHARED_LIB +# if GEOGRAPHICLIB_SHARED_LIB > 1 +# error GEOGRAPHICLIB_SHARED_LIB must be 0 or 1 +# elif defined(GeographicLib_SHARED_EXPORTS) +# define GEOGRAPHICLIB_EXPORT __declspec(dllexport) +# else +# define GEOGRAPHICLIB_EXPORT __declspec(dllimport) +# endif +#else +# define GEOGRAPHICLIB_EXPORT +#endif + +// Use GEOGRAPHICLIB_DEPRECATED to mark functions, types or variables as +// deprecated. Code inspired by Apache Subversion's svn_types.h file (via +// MPFR). +#if defined(__GNUC__) +# if __GNUC__ > 4 +# define GEOGRAPHICLIB_DEPRECATED(msg) __attribute__((deprecated(msg))) +# else +# define GEOGRAPHICLIB_DEPRECATED(msg) __attribute__((deprecated)) +# endif +#elif defined(_MSC_VER) && _MSC_VER >= 1300 +# define GEOGRAPHICLIB_DEPRECATED(msg) __declspec(deprecated(msg)) +#else +# define GEOGRAPHICLIB_DEPRECATED(msg) +#endif + +#include +#include +#include + +/** + * \brief Namespace for %GeographicLib + * + * All of %GeographicLib is defined within the GeographicLib namespace. In + * addition all the header files are included via %GeographicLib/Class.hpp. + * This minimizes the likelihood of conflicts with other packages. + **********************************************************************/ +namespace GeographicLib { + + /** + * \brief %Constants needed by %GeographicLib + * + * Define constants specifying the WGS84 ellipsoid, the UTM and UPS + * projections, and various unit conversions. + * + * Example of use: + * \include example-Constants.cpp + **********************************************************************/ + class GEOGRAPHICLIB_EXPORT Constants { + private: + typedef Math::real real; + Constants(); // Disable constructor + + public: + /** + * A synonym for Math::degree(). + **********************************************************************/ + static Math::real degree() { return Math::degree(); } + /** + * @return the number of radians in an arcminute. + **********************************************************************/ + static Math::real arcminute() + { return Math::degree() / 60; } + /** + * @return the number of radians in an arcsecond. + **********************************************************************/ + static Math::real arcsecond() + { return Math::degree() / 3600; } + + /** \name Ellipsoid parameters + **********************************************************************/ + ///@{ + /** + * @tparam T the type of the returned value. + * @return the equatorial radius of WGS84 ellipsoid (6378137 m). + **********************************************************************/ + template static T WGS84_a() + { return 6378137 * meter(); } + /** + * @tparam T the type of the returned value. + * @return the flattening of WGS84 ellipsoid (1/298.257223563). + **********************************************************************/ + template static T WGS84_f() { + // Evaluating this as 1000000000 / T(298257223563LL) reduces the + // round-off error by about 10%. However, expressing the flattening as + // 1/298.257223563 is well ingrained. + return 1 / ( T(298257223563LL) / 1000000000 ); + } + /** + * @tparam T the type of the returned value. + * @return the gravitational constant of the WGS84 ellipsoid, \e GM, in + * m3 s−2. + **********************************************************************/ + template static T WGS84_GM() + { return T(3986004) * 100000000 + 41800000; } + /** + * @tparam T the type of the returned value. + * @return the angular velocity of the WGS84 ellipsoid, ω, in rad + * s−1. + **********************************************************************/ + template static T WGS84_omega() + { return 7292115 / (T(1000000) * 100000); } + /** + * @tparam T the type of the returned value. + * @return the equatorial radius of GRS80 ellipsoid, \e a, in m. + **********************************************************************/ + template static T GRS80_a() + { return 6378137 * meter(); } + /** + * @tparam T the type of the returned value. + * @return the gravitational constant of the GRS80 ellipsoid, \e GM, in + * m3 s−2. + **********************************************************************/ + template static T GRS80_GM() + { return T(3986005) * 100000000; } + /** + * @tparam T the type of the returned value. + * @return the angular velocity of the GRS80 ellipsoid, ω, in rad + * s−1. + * + * This is about 2 π 366.25 / (365.25 × 24 × 3600) rad + * s−1. 365.25 is the number of days in a Julian year and + * 365.35/366.25 converts from solar days to sidereal days. Using the + * number of days in a Gregorian year (365.2425) results in a worse + * approximation (because the Gregorian year includes the precession of the + * earth's axis). + **********************************************************************/ + template static T GRS80_omega() + { return 7292115 / (T(1000000) * 100000); } + /** + * @tparam T the type of the returned value. + * @return the dynamical form factor of the GRS80 ellipsoid, + * J2. + **********************************************************************/ + template static T GRS80_J2() + { return T(108263) / 100000000; } + /** + * @tparam T the type of the returned value. + * @return the central scale factor for UTM (0.9996). + **********************************************************************/ + template static T UTM_k0() + {return T(9996) / 10000; } + /** + * @tparam T the type of the returned value. + * @return the central scale factor for UPS (0.994). + **********************************************************************/ + template static T UPS_k0() + { return T(994) / 1000; } + ///@} + + /** \name SI units + **********************************************************************/ + ///@{ + /** + * @tparam T the type of the returned value. + * @return the number of meters in a meter. + * + * This is unity, but this lets the internal system of units be changed if + * necessary. + **********************************************************************/ + template static T meter() { return T(1); } + /** + * @return the number of meters in a kilometer. + **********************************************************************/ + static Math::real kilometer() + { return 1000 * meter(); } + /** + * @return the number of meters in a nautical mile (approximately 1 arc + * minute) + **********************************************************************/ + static Math::real nauticalmile() + { return 1852 * meter(); } + + /** + * @tparam T the type of the returned value. + * @return the number of square meters in a square meter. + * + * This is unity, but this lets the internal system of units be changed if + * necessary. + **********************************************************************/ + template static T square_meter() + { return meter() * meter(); } + /** + * @return the number of square meters in a hectare. + **********************************************************************/ + static Math::real hectare() + { return 10000 * square_meter(); } + /** + * @return the number of square meters in a square kilometer. + **********************************************************************/ + static Math::real square_kilometer() + { return kilometer() * kilometer(); } + /** + * @return the number of square meters in a square nautical mile. + **********************************************************************/ + static Math::real square_nauticalmile() + { return nauticalmile() * nauticalmile(); } + ///@} + + /** \name Anachronistic British units + **********************************************************************/ + ///@{ + /** + * @return the number of meters in an international foot. + **********************************************************************/ + static Math::real foot() + { return real(254 * 12) / 10000 * meter(); } + /** + * @return the number of meters in a yard. + **********************************************************************/ + static Math::real yard() { return 3 * foot(); } + /** + * @return the number of meters in a fathom. + **********************************************************************/ + static Math::real fathom() { return 2 * yard(); } + /** + * @return the number of meters in a chain. + **********************************************************************/ + static Math::real chain() { return 22 * yard(); } + /** + * @return the number of meters in a furlong. + **********************************************************************/ + static Math::real furlong() { return 10 * chain(); } + /** + * @return the number of meters in a statute mile. + **********************************************************************/ + static Math::real mile() { return 8 * furlong(); } + /** + * @return the number of square meters in an acre. + **********************************************************************/ + static Math::real acre() { return chain() * furlong(); } + /** + * @return the number of square meters in a square statute mile. + **********************************************************************/ + static Math::real square_mile() { return mile() * mile(); } + ///@} + + /** \name Anachronistic US units + **********************************************************************/ + ///@{ + /** + * @return the number of meters in a US survey foot. + **********************************************************************/ + static Math::real surveyfoot() + { return real(1200) / 3937 * meter(); } + ///@} + }; + + /** + * \brief Exception handling for %GeographicLib + * + * A class to handle exceptions. It's derived from std::runtime_error so it + * can be caught by the usual catch clauses. + * + * Example of use: + * \include example-GeographicErr.cpp + **********************************************************************/ + class GeographicErr : public std::runtime_error { + public: + + /** + * Constructor + * + * @param[in] msg a string message, which is accessible in the catch + * clause via what(). + **********************************************************************/ + GeographicErr(const std::string& msg) : std::runtime_error(msg) {} + }; + +} // namespace GeographicLib + +#endif // GEOGRAPHICLIB_CONSTANTS_HPP diff --git a/common/local_libs/GeographicLib/include/GeographicLib/DMS.hpp b/common/local_libs/GeographicLib/include/GeographicLib/DMS.hpp new file mode 100644 index 0000000000..23f4654805 --- /dev/null +++ b/common/local_libs/GeographicLib/include/GeographicLib/DMS.hpp @@ -0,0 +1,405 @@ +/** + * \file DMS.hpp + * \brief Header for GeographicLib::DMS class + * + * Copyright (c) Charles Karney (2008-2020) and licensed + * under the MIT/X11 License. For more information, see + * https://geographiclib.sourceforge.io/ + **********************************************************************/ + +#if !defined(GEOGRAPHICLIB_DMS_HPP) +#define GEOGRAPHICLIB_DMS_HPP 1 + +#include +#include + +#if defined(_MSC_VER) +// Squelch warnings about dll vs vector and constant conditional expressions +# pragma warning (push) +# pragma warning (disable: 4251 4127) +#endif + +namespace GeographicLib { + + /** + * \brief Convert between degrees and the %DMS representation + * + * Parse a string representing degree, minutes, and seconds and return the + * angle in degrees and format an angle in degrees as degree, minutes, and + * seconds. In addition, handle NANs and infinities on input and output. + * + * Example of use: + * \include example-DMS.cpp + **********************************************************************/ + class GEOGRAPHICLIB_EXPORT DMS { + public: + + /** + * Indicator for presence of hemisphere indicator (N/S/E/W) on latitudes + * and longitudes. + **********************************************************************/ + enum flag { + /** + * No indicator present. + * @hideinitializer + **********************************************************************/ + NONE = 0, + /** + * Latitude indicator (N/S) present. + * @hideinitializer + **********************************************************************/ + LATITUDE = 1, + /** + * Longitude indicator (E/W) present. + * @hideinitializer + **********************************************************************/ + LONGITUDE = 2, + /** + * Used in Encode to indicate output of an azimuth in [000, 360) with no + * letter indicator. + * @hideinitializer + **********************************************************************/ + AZIMUTH = 3, + /** + * Used in Encode to indicate output of a plain number. + * @hideinitializer + **********************************************************************/ + NUMBER = 4, + }; + + /** + * Indicator for trailing units on an angle. + **********************************************************************/ + enum component { + /** + * Trailing unit is degrees. + * @hideinitializer + **********************************************************************/ + DEGREE = 0, + /** + * Trailing unit is arc minutes. + * @hideinitializer + **********************************************************************/ + MINUTE = 1, + /** + * Trailing unit is arc seconds. + * @hideinitializer + **********************************************************************/ + SECOND = 2, + }; + + private: + typedef Math::real real; + // Replace all occurrences of pat by c. If c is NULL remove pat. + static void replace(std::string& s, const std::string& pat, char c) { + std::string::size_type p = 0; + int count = c ? 1 : 0; + while (true) { + p = s.find(pat, p); + if (p == std::string::npos) + break; + s.replace(p, pat.length(), count, c); + } + } + static const char* const hemispheres_; + static const char* const signs_; + static const char* const digits_; + static const char* const dmsindicators_; + static const char* const components_[3]; + static Math::real NumMatch(const std::string& s); + static Math::real InternalDecode(const std::string& dmsa, flag& ind); + DMS(); // Disable constructor + + public: + + /** + * Convert a string in DMS to an angle. + * + * @param[in] dms string input. + * @param[out] ind a DMS::flag value signaling the presence of a + * hemisphere indicator. + * @exception GeographicErr if \e dms is malformed (see below). + * @return angle (degrees). + * + * Degrees, minutes, and seconds are indicated by the characters d, ' + * (single quote), " (double quote), and these components may only be + * given in this order. Any (but not all) components may be omitted and + * other symbols (e.g., the ° symbol for degrees and the unicode prime + * and double prime symbols for minutes and seconds) may be substituted; + * two single quotes can be used instead of ". The last component + * indicator may be omitted and is assumed to be the next smallest unit + * (thus 33d10 is interpreted as 33d10'). The final component may be a + * decimal fraction but the non-final components must be integers. Instead + * of using d, ', and " to indicate degrees, minutes, and seconds, : + * (colon) may be used to separate these components (numbers must + * appear before and after each colon); thus 50d30'10.3" may be + * written as 50:30:10.3, 5.5' may be written 0:5.5, and so on. The + * integer parts of the minutes and seconds components must be less + * than 60. A single leading sign is permitted. A hemisphere designator + * (N, E, W, S) may be added to the beginning or end of the string. The + * result is multiplied by the implied sign of the hemisphere designator + * (negative for S and W). In addition \e ind is set to DMS::LATITUDE if N + * or S is present, to DMS::LONGITUDE if E or W is present, and to + * DMS::NONE otherwise. Throws an error on a malformed string. No check + * is performed on the range of the result. Examples of legal and illegal + * strings are + * - LEGAL (all the entries on each line are equivalent) + * - -20.51125, 20d30'40.5"S, -20°30'40.5, -20d30.675, + * N-20d30'40.5", -20:30:40.5 + * - 4d0'9, 4d9", 4d9'', 4:0:9, 004:00:09, 4.0025, 4.0025d, 4d0.15, + * 04:.15 + * - 4:59.99999999999999, 4:60.0, 4:59:59.9999999999999, 4:59:60.0, 5 + * - ILLEGAL (the exception thrown explains the problem) + * - 4d5"4', 4::5, 4:5:, :4:5, 4d4.5'4", -N20.5, 1.8e2d, 4:60, + * 4:59:60 + * + * The decoding operation can also perform addition and subtraction + * operations. If the string includes internal signs (i.e., not at + * the beginning nor immediately after an initial hemisphere designator), + * then the string is split immediately before such signs and each piece is + * decoded according to the above rules and the results added; thus + * S3-2.5+4.1N is parsed as the sum of S3, + * -2.5, +4.1N. Any piece can include a + * hemisphere designator; however, if multiple designators are given, they + * must compatible; e.g., you cannot mix N and E. In addition, the + * designator can appear at the beginning or end of the first piece, but + * must be at the end of all subsequent pieces (a hemisphere designator is + * not allowed after the initial sign). Examples of legal and illegal + * combinations are + * - LEGAL (these are all equivalent) + * - 070:00:45, 70:01:15W+0:0.5, 70:01:15W-0:0:30W, W70:01:15+0:0:30E + * - ILLEGAL (the exception thrown explains the problem) + * - 70:01:15W+0:0:15N, W70:01:15+W0:0:15 + * + * \warning The "exponential" notation is not recognized. Thus + * 7.0E1 is illegal, while 7.0E+1 is parsed as + * (7.0E) + (+1), yielding the same result as + * 8.0E. + * + * \note At present, all the string handling in the C++ implementation of + * %GeographicLib is with 8-bit characters. The support for unicode + * symbols for degrees, minutes, and seconds is therefore via the + * UTF-8 encoding. (The + * JavaScript implementation of this class uses unicode natively, of + * course.) + * + * Here is the list of Unicode symbols supported for degrees, minutes, + * seconds, and the plus and minus signs; various symbols denoting variants + * of a space, which may separate the components of a DMS string, are + * removed: + * - degrees: + * - d, D lower and upper case letters + * - U+00b0 degree symbol (°) + * - U+00ba masculine ordinal indicator (º) + * - U+2070 superscript zero (⁰) + * - U+02da ring above (˚) + * - U+2218 compose function (∘) + * - * the GRiD symbol for degrees + * - minutes: + * - ' apostrophe + * - ` grave accent + * - U+2032 prime (′) + * - U+2035 back prime (‵) + * - U+00b4 acute accent (´) + * - U+2018 left single quote (‘) + * - U+2019 right single quote (’) + * - U+201b reversed-9 single quote (‛) + * - U+02b9 modifier letter prime (ʹ) + * - U+02ca modifier letter acute accent (ˊ) + * - U+02cb modifier letter grave accent (ˋ) + * - seconds: + * - " quotation mark + * - U+2033 double prime (″) + * - U+2036 reversed double prime (‶) + * + U+02dd double acute accent (˝) + * - U+201c left double quote (“) + * - U+201d right double quote (”) + * - U+201f reversed-9 double quote (‟) + * - U+02ba modifier letter double prime (ʺ) + * - ' ' any two consecutive symbols for minutes + * - plus sign: + * - + plus + * - U+2795 heavy plus (➕) + * - U+2064 invisible plus (|⁤|) + * - minus sign: + * - - hyphen + * - U+2010 dash (‐) + * - U+2011 non-breaking hyphen (‑) + * - U+2013 en dash (–) + * - U+2014 em dash (—) + * - U+2212 minus sign (−) + * - U+2796 heavy minus (➖) + * - ignored spaces: + * - U+00a0 non-breaking space + * - U+2007 figure space (| |) + * - U+2009 thin space (| |) + * - U+200a hair space ( | |) + * - U+200b invisible space (|​|) + * - U+202f narrow space ( | |) + * - U+2063 invisible separator (|⁣|) + * . + * The codes with a leading zero byte, e.g., U+00b0, are accepted in their + * UTF-8 coded form 0xc2 0xb0 and as a single byte 0xb0. + **********************************************************************/ + static Math::real Decode(const std::string& dms, flag& ind); + + /** + * Convert DMS to an angle. + * + * @param[in] d degrees. + * @param[in] m arc minutes. + * @param[in] s arc seconds. + * @return angle (degrees) + * + * This does not propagate the sign on \e d to the other components, + * so -3d20' would need to be represented as - DMS::Decode(3.0, 20.0) or + * DMS::Decode(-3.0, -20.0). + **********************************************************************/ + static Math::real Decode(real d, real m = 0, real s = 0) + { return d + (m + s / 60) / 60; } + + /** + * Convert a pair of strings to latitude and longitude. + * + * @param[in] dmsa first string. + * @param[in] dmsb second string. + * @param[out] lat latitude (degrees). + * @param[out] lon longitude (degrees). + * @param[in] longfirst if true assume longitude is given before latitude + * in the absence of hemisphere designators (default false). + * @exception GeographicErr if \e dmsa or \e dmsb is malformed. + * @exception GeographicErr if \e dmsa and \e dmsb are both interpreted as + * latitudes. + * @exception GeographicErr if \e dmsa and \e dmsb are both interpreted as + * longitudes. + * @exception GeographicErr if decoded latitude is not in [−90°, + * 90°]. + * + * By default, the \e lat (resp., \e lon) is assigned to the results of + * decoding \e dmsa (resp., \e dmsb). However this is overridden if either + * \e dmsa or \e dmsb contain a latitude or longitude hemisphere designator + * (N, S, E, W). If an exception is thrown, \e lat and \e lon are + * unchanged. + **********************************************************************/ + static void DecodeLatLon(const std::string& dmsa, const std::string& dmsb, + real& lat, real& lon, + bool longfirst = false); + + /** + * Convert a string to an angle in degrees. + * + * @param[in] angstr input string. + * @exception GeographicErr if \e angstr is malformed. + * @exception GeographicErr if \e angstr includes a hemisphere designator. + * @return angle (degrees) + * + * No hemisphere designator is allowed and no check is done on the range of + * the result. + **********************************************************************/ + static Math::real DecodeAngle(const std::string& angstr); + + /** + * Convert a string to an azimuth in degrees. + * + * @param[in] azistr input string. + * @exception GeographicErr if \e azistr is malformed. + * @exception GeographicErr if \e azistr includes a N/S designator. + * @return azimuth (degrees) reduced to the range [−180°, + * 180°]. + * + * A hemisphere designator E/W can be used; the result is multiplied by + * −1 if W is present. + **********************************************************************/ + static Math::real DecodeAzimuth(const std::string& azistr); + + /** + * Convert angle (in degrees) into a DMS string (using d, ', and "). + * + * @param[in] angle input angle (degrees) + * @param[in] trailing DMS::component value indicating the trailing units + * of the string (this component is given as a decimal number if + * necessary). + * @param[in] prec the number of digits after the decimal point for the + * trailing component. + * @param[in] ind DMS::flag value indicating additional formatting. + * @param[in] dmssep if non-null, use as the DMS separator character + * (instead of d, ', " delimiters). + * @exception std::bad_alloc if memory for the string can't be allocated. + * @return formatted string + * + * The interpretation of \e ind is as follows: + * - ind == DMS::NONE, signed result no leading zeros on degrees except in + * the units place, e.g., -8d03'. + * - ind == DMS::LATITUDE, trailing N or S hemisphere designator, no sign, + * pad degrees to 2 digits, e.g., 08d03'S. + * - ind == DMS::LONGITUDE, trailing E or W hemisphere designator, no + * sign, pad degrees to 3 digits, e.g., 008d03'W. + * - ind == DMS::AZIMUTH, convert to the range [0, 360°), no + * sign, pad degrees to 3 digits, e.g., 351d57'. + * . + * The integer parts of the minutes and seconds components are always given + * with 2 digits. + **********************************************************************/ + static std::string Encode(real angle, component trailing, unsigned prec, + flag ind = NONE, char dmssep = char(0)); + + /** + * Convert angle into a DMS string (using d, ', and ") selecting the + * trailing component based on the precision. + * + * @param[in] angle input angle (degrees) + * @param[in] prec the precision relative to 1 degree. + * @param[in] ind DMS::flag value indicated additional formatting. + * @param[in] dmssep if non-null, use as the DMS separator character + * (instead of d, ', " delimiters). + * @exception std::bad_alloc if memory for the string can't be allocated. + * @return formatted string + * + * \e prec indicates the precision relative to 1 degree, e.g., \e prec = 3 + * gives a result accurate to 0.1' and \e prec = 4 gives a result accurate + * to 1". \e ind is interpreted as in DMS::Encode with the additional + * facility that DMS::NUMBER represents \e angle as a number in fixed + * format with precision \e prec. + **********************************************************************/ + static std::string Encode(real angle, unsigned prec, flag ind = NONE, + char dmssep = char(0)) { + return ind == NUMBER ? Utility::str(angle, int(prec)) : + Encode(angle, + prec < 2 ? DEGREE : (prec < 4 ? MINUTE : SECOND), + prec < 2 ? prec : (prec < 4 ? prec - 2 : prec - 4), + ind, dmssep); + } + + /** + * Split angle into degrees and minutes + * + * @param[in] ang angle (degrees) + * @param[out] d degrees (an integer returned as a real) + * @param[out] m arc minutes. + **********************************************************************/ + static void Encode(real ang, real& d, real& m) { + d = int(ang); m = 60 * (ang - d); + } + + /** + * Split angle into degrees and minutes and seconds. + * + * @param[in] ang angle (degrees) + * @param[out] d degrees (an integer returned as a real) + * @param[out] m arc minutes (an integer returned as a real) + * @param[out] s arc seconds. + **********************************************************************/ + static void Encode(real ang, real& d, real& m, real& s) { + d = int(ang); ang = 60 * (ang - d); + m = int(ang); s = 60 * (ang - m); + } + + }; + +} // namespace GeographicLib + +#if defined(_MSC_VER) +# pragma warning (pop) +#endif + +#endif // GEOGRAPHICLIB_DMS_HPP diff --git a/common/local_libs/GeographicLib/include/GeographicLib/Ellipsoid.hpp b/common/local_libs/GeographicLib/include/GeographicLib/Ellipsoid.hpp new file mode 100644 index 0000000000..6a821ce88c --- /dev/null +++ b/common/local_libs/GeographicLib/include/GeographicLib/Ellipsoid.hpp @@ -0,0 +1,542 @@ +/** + * \file Ellipsoid.hpp + * \brief Header for GeographicLib::Ellipsoid class + * + * Copyright (c) Charles Karney (2012-2020) and licensed + * under the MIT/X11 License. For more information, see + * https://geographiclib.sourceforge.io/ + **********************************************************************/ + +#if !defined(GEOGRAPHICLIB_ELLIPSOID_HPP) +#define GEOGRAPHICLIB_ELLIPSOID_HPP 1 + +#include +#include +#include +#include + +namespace GeographicLib { + + /** + * \brief Properties of an ellipsoid + * + * This class returns various properties of the ellipsoid and converts + * between various types of latitudes. The latitude conversions are also + * possible using the various projections supported by %GeographicLib; but + * Ellipsoid provides more direct access (sometimes using private functions + * of the projection classes). Ellipsoid::RectifyingLatitude, + * Ellipsoid::InverseRectifyingLatitude, and Ellipsoid::MeridianDistance + * provide functionality which can be provided by the Geodesic class. + * However Geodesic uses a series approximation (valid for abs \e f < 1/150), + * whereas Ellipsoid computes these quantities using EllipticFunction which + * provides accurate results even when \e f is large. Use of this class + * should be limited to −3 < \e f < 3/4 (i.e., 1/4 < b/a < 4). + * + * Example of use: + * \include example-Ellipsoid.cpp + **********************************************************************/ + + class GEOGRAPHICLIB_EXPORT Ellipsoid { + private: + typedef Math::real real; + static const int numit_ = 10; + real stol_; + real _a, _f, _f1, _f12, _e2, _es, _e12, _n, _b; + TransverseMercator _tm; + EllipticFunction _ell; + AlbersEqualArea _au; + + // These are the alpha and beta coefficients in the Krueger series from + // TransverseMercator. Thy are used by RhumbSolve to compute + // (psi2-psi1)/(mu2-mu1). + const Math::real* ConformalToRectifyingCoeffs() const { return _tm._alp; } + const Math::real* RectifyingToConformalCoeffs() const { return _tm._bet; } + friend class Rhumb; friend class RhumbLine; + public: + /** \name Constructor + **********************************************************************/ + ///@{ + + /** + * Constructor for a ellipsoid with + * + * @param[in] a equatorial radius (meters). + * @param[in] f flattening of ellipsoid. Setting \e f = 0 gives a sphere. + * Negative \e f gives a prolate ellipsoid. + * @exception GeographicErr if \e a or (1 − \e f) \e a is not + * positive. + **********************************************************************/ + Ellipsoid(real a, real f); + ///@} + + /** \name %Ellipsoid dimensions. + **********************************************************************/ + ///@{ + + /** + * @return \e a the equatorial radius of the ellipsoid (meters). This is + * the value used in the constructor. + **********************************************************************/ + Math::real EquatorialRadius() const { return _a; } + + /** + * @return \e b the polar semi-axis (meters). + **********************************************************************/ + Math::real MinorRadius() const { return _b; } + + /** + * @return \e L the distance between the equator and a pole along a + * meridian (meters). For a sphere \e L = (π/2) \e a. The radius + * of a sphere with the same meridian length is \e L / (π/2). + **********************************************************************/ + Math::real QuarterMeridian() const; + + /** + * @return \e A the total area of the ellipsoid (meters2). For + * a sphere \e A = 4π a2. The radius of a sphere + * with the same area is sqrt(\e A / (4π)). + **********************************************************************/ + Math::real Area() const; + + /** + * @return \e V the total volume of the ellipsoid (meters3). + * For a sphere \e V = (4π / 3) a3. The radius of + * a sphere with the same volume is cbrt(\e V / (4π/3)). + **********************************************************************/ + Math::real Volume() const + { return (4 * Math::pi()) * Math::sq(_a) * _b / 3; } + + /** + * \deprecated An old name for EquatorialRadius(). + **********************************************************************/ + GEOGRAPHICLIB_DEPRECATED("Use EquatorialRadius()") + Math::real MajorRadius() const { return EquatorialRadius(); } + ///@} + + /** \name %Ellipsoid shape + **********************************************************************/ + ///@{ + + /** + * @return \e f = (\e a − \e b) / \e a, the flattening of the + * ellipsoid. This is the value used in the constructor. This is zero, + * positive, or negative for a sphere, oblate ellipsoid, or prolate + * ellipsoid. + **********************************************************************/ + Math::real Flattening() const { return _f; } + + /** + * @return \e f ' = (\e a − \e b) / \e b, the second flattening of + * the ellipsoid. This is zero, positive, or negative for a sphere, + * oblate ellipsoid, or prolate ellipsoid. + **********************************************************************/ + Math::real SecondFlattening() const { return _f / (1 - _f); } + + /** + * @return \e n = (\e a − \e b) / (\e a + \e b), the third flattening + * of the ellipsoid. This is zero, positive, or negative for a sphere, + * oblate ellipsoid, or prolate ellipsoid. + **********************************************************************/ + Math::real ThirdFlattening() const { return _n; } + + /** + * @return e2 = (a2 − + * b2) / a2, the eccentricity squared + * of the ellipsoid. This is zero, positive, or negative for a sphere, + * oblate ellipsoid, or prolate ellipsoid. + **********************************************************************/ + Math::real EccentricitySq() const { return _e2; } + + /** + * @return e' 2 = (a2 − + * b2) / b2, the second eccentricity + * squared of the ellipsoid. This is zero, positive, or negative for a + * sphere, oblate ellipsoid, or prolate ellipsoid. + **********************************************************************/ + Math::real SecondEccentricitySq() const { return _e12; } + + /** + * @return e'' 2 = (a2 − + * b2) / (a2 + b2), + * the third eccentricity squared of the ellipsoid. This is zero, + * positive, or negative for a sphere, oblate ellipsoid, or prolate + * ellipsoid. + **********************************************************************/ + Math::real ThirdEccentricitySq() const { return _e2 / (2 - _e2); } + ///@} + + /** \name Latitude conversion. + **********************************************************************/ + ///@{ + + /** + * @param[in] phi the geographic latitude (degrees). + * @return β the parametric latitude (degrees). + * + * The geographic latitude, φ, is the angle between the equatorial + * plane and a vector normal to the surface of the ellipsoid. + * + * The parametric latitude (also called the reduced latitude), β, + * allows the cartesian coordinated of a meridian to be expressed + * conveniently in parametric form as + * - \e R = \e a cos β + * - \e Z = \e b sin β + * . + * where \e a and \e b are the equatorial radius and the polar semi-axis. + * For a sphere β = φ. + * + * φ must lie in the range [−90°, 90°]; the + * result is undefined if this condition does not hold. The returned value + * β lies in [−90°, 90°]. + **********************************************************************/ + Math::real ParametricLatitude(real phi) const; + + /** + * @param[in] beta the parametric latitude (degrees). + * @return φ the geographic latitude (degrees). + * + * β must lie in the range [−90°, 90°]; the + * result is undefined if this condition does not hold. The returned value + * φ lies in [−90°, 90°]. + **********************************************************************/ + Math::real InverseParametricLatitude(real beta) const; + + /** + * @param[in] phi the geographic latitude (degrees). + * @return θ the geocentric latitude (degrees). + * + * The geocentric latitude, θ, is the angle between the equatorial + * plane and a line between the center of the ellipsoid and a point on the + * ellipsoid. For a sphere θ = φ. + * + * φ must lie in the range [−90°, 90°]; the + * result is undefined if this condition does not hold. The returned value + * θ lies in [−90°, 90°]. + **********************************************************************/ + Math::real GeocentricLatitude(real phi) const; + + /** + * @param[in] theta the geocentric latitude (degrees). + * @return φ the geographic latitude (degrees). + * + * θ must lie in the range [−90°, 90°]; the + * result is undefined if this condition does not hold. The returned value + * φ lies in [−90°, 90°]. + **********************************************************************/ + Math::real InverseGeocentricLatitude(real theta) const; + + /** + * @param[in] phi the geographic latitude (degrees). + * @return μ the rectifying latitude (degrees). + * + * The rectifying latitude, μ, has the property that the distance along + * a meridian of the ellipsoid between two points with rectifying latitudes + * μ1 and μ2 is equal to + * (μ2 - μ1) \e L / 90°, + * where \e L = QuarterMeridian(). For a sphere μ = φ. + * + * φ must lie in the range [−90°, 90°]; the + * result is undefined if this condition does not hold. The returned value + * μ lies in [−90°, 90°]. + **********************************************************************/ + Math::real RectifyingLatitude(real phi) const; + + /** + * @param[in] mu the rectifying latitude (degrees). + * @return φ the geographic latitude (degrees). + * + * μ must lie in the range [−90°, 90°]; the + * result is undefined if this condition does not hold. The returned value + * φ lies in [−90°, 90°]. + **********************************************************************/ + Math::real InverseRectifyingLatitude(real mu) const; + + /** + * @param[in] phi the geographic latitude (degrees). + * @return ξ the authalic latitude (degrees). + * + * The authalic latitude, ξ, has the property that the area of the + * ellipsoid between two circles with authalic latitudes + * ξ1 and ξ2 is equal to (sin + * ξ2 - sin ξ1) \e A / 2, where \e A + * = Area(). For a sphere ξ = φ. + * + * φ must lie in the range [−90°, 90°]; the + * result is undefined if this condition does not hold. The returned value + * ξ lies in [−90°, 90°]. + **********************************************************************/ + Math::real AuthalicLatitude(real phi) const; + + /** + * @param[in] xi the authalic latitude (degrees). + * @return φ the geographic latitude (degrees). + * + * ξ must lie in the range [−90°, 90°]; the + * result is undefined if this condition does not hold. The returned value + * φ lies in [−90°, 90°]. + **********************************************************************/ + Math::real InverseAuthalicLatitude(real xi) const; + + /** + * @param[in] phi the geographic latitude (degrees). + * @return χ the conformal latitude (degrees). + * + * The conformal latitude, χ, gives the mapping of the ellipsoid to a + * sphere which which is conformal (angles are preserved) and in which the + * equator of the ellipsoid maps to the equator of the sphere. For a + * sphere χ = φ. + * + * φ must lie in the range [−90°, 90°]; the + * result is undefined if this condition does not hold. The returned value + * χ lies in [−90°, 90°]. + **********************************************************************/ + Math::real ConformalLatitude(real phi) const; + + /** + * @param[in] chi the conformal latitude (degrees). + * @return φ the geographic latitude (degrees). + * + * χ must lie in the range [−90°, 90°]; the + * result is undefined if this condition does not hold. The returned value + * φ lies in [−90°, 90°]. + **********************************************************************/ + Math::real InverseConformalLatitude(real chi) const; + + /** + * @param[in] phi the geographic latitude (degrees). + * @return ψ the isometric latitude (degrees). + * + * The isometric latitude gives the mapping of the ellipsoid to a plane + * which which is conformal (angles are preserved) and in which the equator + * of the ellipsoid maps to a straight line of constant scale; this mapping + * defines the Mercator projection. For a sphere ψ = + * sinh−1 tan φ. + * + * φ must lie in the range [−90°, 90°]; the result is + * undefined if this condition does not hold. The value returned for φ + * = ±90° is some (positive or negative) large but finite value, + * such that InverseIsometricLatitude returns the original value of φ. + **********************************************************************/ + Math::real IsometricLatitude(real phi) const; + + /** + * @param[in] psi the isometric latitude (degrees). + * @return φ the geographic latitude (degrees). + * + * The returned value φ lies in [−90°, 90°]. For a + * sphere φ = tan−1 sinh ψ. + **********************************************************************/ + Math::real InverseIsometricLatitude(real psi) const; + ///@} + + /** \name Other quantities. + **********************************************************************/ + ///@{ + + /** + * @param[in] phi the geographic latitude (degrees). + * @return \e R = \e a cos β the radius of a circle of latitude + * φ (meters). \e R (π/180°) gives meters per degree + * longitude measured along a circle of latitude. + * + * φ must lie in the range [−90°, 90°]; the + * result is undefined if this condition does not hold. + **********************************************************************/ + Math::real CircleRadius(real phi) const; + + /** + * @param[in] phi the geographic latitude (degrees). + * @return \e Z = \e b sin β the distance of a circle of latitude + * φ from the equator measured parallel to the ellipsoid axis + * (meters). + * + * φ must lie in the range [−90°, 90°]; the + * result is undefined if this condition does not hold. + **********************************************************************/ + Math::real CircleHeight(real phi) const; + + /** + * @param[in] phi the geographic latitude (degrees). + * @return \e s the distance along a meridian + * between the equator and a point of latitude φ (meters). \e s is + * given by \e s = μ \e L / 90°, where \e L = + * QuarterMeridian()). + * + * φ must lie in the range [−90°, 90°]; the + * result is undefined if this condition does not hold. + **********************************************************************/ + Math::real MeridianDistance(real phi) const; + + /** + * @param[in] phi the geographic latitude (degrees). + * @return ρ the meridional radius of curvature of the ellipsoid at + * latitude φ (meters); this is the curvature of the meridian. \e + * rho is given by ρ = (180°/π) d\e s / dφ, + * where \e s = MeridianDistance(); thus ρ (π/180°) + * gives meters per degree latitude measured along a meridian. + * + * φ must lie in the range [−90°, 90°]; the + * result is undefined if this condition does not hold. + **********************************************************************/ + Math::real MeridionalCurvatureRadius(real phi) const; + + /** + * @param[in] phi the geographic latitude (degrees). + * @return ν the transverse radius of curvature of the ellipsoid at + * latitude φ (meters); this is the curvature of a curve on the + * ellipsoid which also lies in a plane perpendicular to the ellipsoid + * and to the meridian. ν is related to \e R = CircleRadius() by \e + * R = ν cos φ. + * + * φ must lie in the range [−90°, 90°]; the + * result is undefined if this condition does not hold. + **********************************************************************/ + Math::real TransverseCurvatureRadius(real phi) const; + + /** + * @param[in] phi the geographic latitude (degrees). + * @param[in] azi the angle between the meridian and the normal section + * (degrees). + * @return the radius of curvature of the ellipsoid in the normal + * section at latitude φ inclined at an angle \e azi to the + * meridian (meters). + * + * φ must lie in the range [−90°, 90°]; the result is + * undefined this condition does not hold. + **********************************************************************/ + Math::real NormalCurvatureRadius(real phi, real azi) const; + ///@} + + /** \name Eccentricity conversions. + **********************************************************************/ + ///@{ + + /** + * @param[in] fp = \e f ' = (\e a − \e b) / \e b, the second + * flattening. + * @return \e f = (\e a − \e b) / \e a, the flattening. + * + * \e f ' should lie in (−1, ∞). + * The returned value \e f lies in (−∞, 1). + **********************************************************************/ + static Math::real SecondFlatteningToFlattening(real fp) + { return fp / (1 + fp); } + + /** + * @param[in] f = (\e a − \e b) / \e a, the flattening. + * @return \e f ' = (\e a − \e b) / \e b, the second flattening. + * + * \e f should lie in (−∞, 1). + * The returned value \e f ' lies in (−1, ∞). + **********************************************************************/ + static Math::real FlatteningToSecondFlattening(real f) + { return f / (1 - f); } + + /** + * @param[in] n = (\e a − \e b) / (\e a + \e b), the third + * flattening. + * @return \e f = (\e a − \e b) / \e a, the flattening. + * + * \e n should lie in (−1, 1). + * The returned value \e f lies in (−∞, 1). + **********************************************************************/ + static Math::real ThirdFlatteningToFlattening(real n) + { return 2 * n / (1 + n); } + + /** + * @param[in] f = (\e a − \e b) / \e a, the flattening. + * @return \e n = (\e a − \e b) / (\e a + \e b), the third + * flattening. + * + * \e f should lie in (−∞, 1). + * The returned value \e n lies in (−1, 1). + **********************************************************************/ + static Math::real FlatteningToThirdFlattening(real f) + { return f / (2 - f); } + + /** + * @param[in] e2 = e2 = (a2 − + * b2) / a2, the eccentricity + * squared. + * @return \e f = (\e a − \e b) / \e a, the flattening. + * + * e2 should lie in (−∞, 1). + * The returned value \e f lies in (−∞, 1). + **********************************************************************/ + static Math::real EccentricitySqToFlattening(real e2) + { using std::sqrt; return e2 / (sqrt(1 - e2) + 1); } + + /** + * @param[in] f = (\e a − \e b) / \e a, the flattening. + * @return e2 = (a2 − + * b2) / a2, the eccentricity + * squared. + * + * \e f should lie in (−∞, 1). + * The returned value e2 lies in (−∞, 1). + **********************************************************************/ + static Math::real FlatteningToEccentricitySq(real f) + { return f * (2 - f); } + + /** + * @param[in] ep2 = e' 2 = (a2 − + * b2) / b2, the second eccentricity + * squared. + * @return \e f = (\e a − \e b) / \e a, the flattening. + * + * e' 2 should lie in (−1, ∞). + * The returned value \e f lies in (−∞, 1). + **********************************************************************/ + static Math::real SecondEccentricitySqToFlattening(real ep2) + { using std::sqrt; return ep2 / (sqrt(1 + ep2) + 1 + ep2); } + + /** + * @param[in] f = (\e a − \e b) / \e a, the flattening. + * @return e' 2 = (a2 − + * b2) / b2, the second eccentricity + * squared. + * + * \e f should lie in (−∞, 1). + * The returned value e' 2 lies in (−1, ∞). + **********************************************************************/ + static Math::real FlatteningToSecondEccentricitySq(real f) + { return f * (2 - f) / Math::sq(1 - f); } + + /** + * @param[in] epp2 = e'' 2 = (a2 + * − b2) / (a2 + + * b2), the third eccentricity squared. + * @return \e f = (\e a − \e b) / \e a, the flattening. + * + * e'' 2 should lie in (−1, 1). + * The returned value \e f lies in (−∞, 1). + **********************************************************************/ + static Math::real ThirdEccentricitySqToFlattening(real epp2) { + using std::sqrt; + return 2 * epp2 / (sqrt((1 - epp2) * (1 + epp2)) + 1 + epp2); + } + + /** + * @param[in] f = (\e a − \e b) / \e a, the flattening. + * @return e'' 2 = (a2 − + * b2) / (a2 + b2), + * the third eccentricity squared. + * + * \e f should lie in (−∞, 1). + * The returned value e'' 2 lies in (−1, 1). + **********************************************************************/ + static Math::real FlatteningToThirdEccentricitySq(real f) + { return f * (2 - f) / (1 + Math::sq(1 - f)); } + + ///@} + + /** + * A global instantiation of Ellipsoid with the parameters for the WGS84 + * ellipsoid. + **********************************************************************/ + static const Ellipsoid& WGS84(); + }; + +} // namespace GeographicLib + +#endif // GEOGRAPHICLIB_ELLIPSOID_HPP diff --git a/common/local_libs/GeographicLib/include/GeographicLib/EllipticFunction.hpp b/common/local_libs/GeographicLib/include/GeographicLib/EllipticFunction.hpp new file mode 100644 index 0000000000..e27392322f --- /dev/null +++ b/common/local_libs/GeographicLib/include/GeographicLib/EllipticFunction.hpp @@ -0,0 +1,701 @@ +/** + * \file EllipticFunction.hpp + * \brief Header for GeographicLib::EllipticFunction class + * + * Copyright (c) Charles Karney (2008-2019) and licensed + * under the MIT/X11 License. For more information, see + * https://geographiclib.sourceforge.io/ + **********************************************************************/ + +#if !defined(GEOGRAPHICLIB_ELLIPTICFUNCTION_HPP) +#define GEOGRAPHICLIB_ELLIPTICFUNCTION_HPP 1 + +#include + +namespace GeographicLib { + + /** + * \brief Elliptic integrals and functions + * + * This provides the elliptic functions and integrals needed for Ellipsoid, + * GeodesicExact, and TransverseMercatorExact. Two categories of function + * are provided: + * - \e static functions to compute symmetric elliptic integrals + * (https://dlmf.nist.gov/19.16.i) + * - \e member functions to compute Legrendre's elliptic + * integrals (https://dlmf.nist.gov/19.2.ii) and the + * Jacobi elliptic functions (https://dlmf.nist.gov/22.2). + * . + * In the latter case, an object is constructed giving the modulus \e k (and + * optionally the parameter α2). The modulus is always + * passed as its square k2 which allows \e k to be pure + * imaginary (k2 < 0). (Confusingly, Abramowitz and + * Stegun call \e m = k2 the "parameter" and \e n = + * α2 the "characteristic".) + * + * In geodesic applications, it is convenient to separate the incomplete + * integrals into secular and periodic components, e.g., + * \f[ + * E(\phi, k) = (2 E(k) / \pi) [ \phi + \delta E(\phi, k) ] + * \f] + * where δ\e E(φ, \e k) is an odd periodic function with period + * π. + * + * The computation of the elliptic integrals uses the algorithms given in + * - B. C. Carlson, + * Computation of real or + * complex elliptic integrals, Numerical Algorithms 10, 13--26 (1995) + * . + * with the additional optimizations given in https://dlmf.nist.gov/19.36.i. + * The computation of the Jacobi elliptic functions uses the algorithm given + * in + * - R. Bulirsch, + * Numerical Calculation of + * Elliptic Integrals and Elliptic Functions, Numericshe Mathematik 7, + * 78--90 (1965). + * . + * The notation follows https://dlmf.nist.gov/19 and https://dlmf.nist.gov/22 + * + * Example of use: + * \include example-EllipticFunction.cpp + **********************************************************************/ + class GEOGRAPHICLIB_EXPORT EllipticFunction { + private: + typedef Math::real real; + + enum { num_ = 13 }; // Max depth required for sncndn; probably 5 is enough. + real _k2, _kp2, _alpha2, _alphap2, _eps; + real _Kc, _Ec, _Dc, _Pic, _Gc, _Hc; + public: + /** \name Constructor + **********************************************************************/ + ///@{ + /** + * Constructor specifying the modulus and parameter. + * + * @param[in] k2 the square of the modulus k2. + * k2 must lie in (−∞, 1]. + * @param[in] alpha2 the parameter α2. + * α2 must lie in (−∞, 1]. + * @exception GeographicErr if \e k2 or \e alpha2 is out of its legal + * range. + * + * If only elliptic integrals of the first and second kinds are needed, + * then set α2 = 0 (the default value); in this case, we + * have Π(φ, 0, \e k) = \e F(φ, \e k), \e G(φ, 0, \e k) = \e + * E(φ, \e k), and \e H(φ, 0, \e k) = \e F(φ, \e k) - \e + * D(φ, \e k). + **********************************************************************/ + EllipticFunction(real k2 = 0, real alpha2 = 0) + { Reset(k2, alpha2); } + + /** + * Constructor specifying the modulus and parameter and their complements. + * + * @param[in] k2 the square of the modulus k2. + * k2 must lie in (−∞, 1]. + * @param[in] alpha2 the parameter α2. + * α2 must lie in (−∞, 1]. + * @param[in] kp2 the complementary modulus squared k'2 = + * 1 − k2. This must lie in [0, ∞). + * @param[in] alphap2 the complementary parameter α'2 = 1 + * − α2. This must lie in [0, ∞). + * @exception GeographicErr if \e k2, \e alpha2, \e kp2, or \e alphap2 is + * out of its legal range. + * + * The arguments must satisfy \e k2 + \e kp2 = 1 and \e alpha2 + \e alphap2 + * = 1. (No checking is done that these conditions are met.) This + * constructor is provided to enable accuracy to be maintained, e.g., when + * \e k is very close to unity. + **********************************************************************/ + EllipticFunction(real k2, real alpha2, real kp2, real alphap2) + { Reset(k2, alpha2, kp2, alphap2); } + + /** + * Reset the modulus and parameter. + * + * @param[in] k2 the new value of square of the modulus + * k2 which must lie in (−∞, ]. + * done.) + * @param[in] alpha2 the new value of parameter α2. + * α2 must lie in (−∞, 1]. + * @exception GeographicErr if \e k2 or \e alpha2 is out of its legal + * range. + **********************************************************************/ + void Reset(real k2 = 0, real alpha2 = 0) + { Reset(k2, alpha2, 1 - k2, 1 - alpha2); } + + /** + * Reset the modulus and parameter supplying also their complements. + * + * @param[in] k2 the square of the modulus k2. + * k2 must lie in (−∞, 1]. + * @param[in] alpha2 the parameter α2. + * α2 must lie in (−∞, 1]. + * @param[in] kp2 the complementary modulus squared k'2 = + * 1 − k2. This must lie in [0, ∞). + * @param[in] alphap2 the complementary parameter α'2 = 1 + * − α2. This must lie in [0, ∞). + * @exception GeographicErr if \e k2, \e alpha2, \e kp2, or \e alphap2 is + * out of its legal range. + * + * The arguments must satisfy \e k2 + \e kp2 = 1 and \e alpha2 + \e alphap2 + * = 1. (No checking is done that these conditions are met.) This + * constructor is provided to enable accuracy to be maintained, e.g., when + * is very small. + **********************************************************************/ + void Reset(real k2, real alpha2, real kp2, real alphap2); + + ///@} + + /** \name Inspector functions. + **********************************************************************/ + ///@{ + /** + * @return the square of the modulus k2. + **********************************************************************/ + Math::real k2() const { return _k2; } + + /** + * @return the square of the complementary modulus k'2 = + * 1 − k2. + **********************************************************************/ + Math::real kp2() const { return _kp2; } + + /** + * @return the parameter α2. + **********************************************************************/ + Math::real alpha2() const { return _alpha2; } + + /** + * @return the complementary parameter α'2 = 1 − + * α2. + **********************************************************************/ + Math::real alphap2() const { return _alphap2; } + ///@} + + /** \name Complete elliptic integrals. + **********************************************************************/ + ///@{ + /** + * The complete integral of the first kind. + * + * @return \e K(\e k). + * + * \e K(\e k) is defined in https://dlmf.nist.gov/19.2.E4 + * \f[ + * K(k) = \int_0^{\pi/2} \frac1{\sqrt{1-k^2\sin^2\phi}}\,d\phi. + * \f] + **********************************************************************/ + Math::real K() const { return _Kc; } + + /** + * The complete integral of the second kind. + * + * @return \e E(\e k). + * + * \e E(\e k) is defined in https://dlmf.nist.gov/19.2.E5 + * \f[ + * E(k) = \int_0^{\pi/2} \sqrt{1-k^2\sin^2\phi}\,d\phi. + * \f] + **********************************************************************/ + Math::real E() const { return _Ec; } + + /** + * Jahnke's complete integral. + * + * @return \e D(\e k). + * + * \e D(\e k) is defined in https://dlmf.nist.gov/19.2.E6 + * \f[ + * D(k) = + * \int_0^{\pi/2} \frac{\sin^2\phi}{\sqrt{1-k^2\sin^2\phi}}\,d\phi. + * \f] + **********************************************************************/ + Math::real D() const { return _Dc; } + + /** + * The difference between the complete integrals of the first and second + * kinds. + * + * @return \e K(\e k) − \e E(\e k). + **********************************************************************/ + Math::real KE() const { return _k2 * _Dc; } + + /** + * The complete integral of the third kind. + * + * @return Π(α2, \e k). + * + * Π(α2, \e k) is defined in + * https://dlmf.nist.gov/19.2.E7 + * \f[ + * \Pi(\alpha^2, k) = \int_0^{\pi/2} + * \frac1{\sqrt{1-k^2\sin^2\phi}(1 - \alpha^2\sin^2\phi)}\,d\phi. + * \f] + **********************************************************************/ + Math::real Pi() const { return _Pic; } + + /** + * Legendre's complete geodesic longitude integral. + * + * @return \e G(α2, \e k). + * + * \e G(α2, \e k) is given by + * \f[ + * G(\alpha^2, k) = \int_0^{\pi/2} + * \frac{\sqrt{1-k^2\sin^2\phi}}{1 - \alpha^2\sin^2\phi}\,d\phi. + * \f] + **********************************************************************/ + Math::real G() const { return _Gc; } + + /** + * Cayley's complete geodesic longitude difference integral. + * + * @return \e H(α2, \e k). + * + * \e H(α2, \e k) is given by + * \f[ + * H(\alpha^2, k) = \int_0^{\pi/2} + * \frac{\cos^2\phi}{(1-\alpha^2\sin^2\phi)\sqrt{1-k^2\sin^2\phi}} + * \,d\phi. + * \f] + **********************************************************************/ + Math::real H() const { return _Hc; } + ///@} + + /** \name Incomplete elliptic integrals. + **********************************************************************/ + ///@{ + /** + * The incomplete integral of the first kind. + * + * @param[in] phi + * @return \e F(φ, \e k). + * + * \e F(φ, \e k) is defined in https://dlmf.nist.gov/19.2.E4 + * \f[ + * F(\phi, k) = \int_0^\phi \frac1{\sqrt{1-k^2\sin^2\theta}}\,d\theta. + * \f] + **********************************************************************/ + Math::real F(real phi) const; + + /** + * The incomplete integral of the second kind. + * + * @param[in] phi + * @return \e E(φ, \e k). + * + * \e E(φ, \e k) is defined in https://dlmf.nist.gov/19.2.E5 + * \f[ + * E(\phi, k) = \int_0^\phi \sqrt{1-k^2\sin^2\theta}\,d\theta. + * \f] + **********************************************************************/ + Math::real E(real phi) const; + + /** + * The incomplete integral of the second kind with the argument given in + * degrees. + * + * @param[in] ang in degrees. + * @return \e E(π ang/180, \e k). + **********************************************************************/ + Math::real Ed(real ang) const; + + /** + * The inverse of the incomplete integral of the second kind. + * + * @param[in] x + * @return φ = E−1(\e x, \e k); i.e., the + * solution of such that \e E(φ, \e k) = \e x. + **********************************************************************/ + Math::real Einv(real x) const; + + /** + * The incomplete integral of the third kind. + * + * @param[in] phi + * @return Π(φ, α2, \e k). + * + * Π(φ, α2, \e k) is defined in + * https://dlmf.nist.gov/19.2.E7 + * \f[ + * \Pi(\phi, \alpha^2, k) = \int_0^\phi + * \frac1{\sqrt{1-k^2\sin^2\theta}(1 - \alpha^2\sin^2\theta)}\,d\theta. + * \f] + **********************************************************************/ + Math::real Pi(real phi) const; + + /** + * Jahnke's incomplete elliptic integral. + * + * @param[in] phi + * @return \e D(φ, \e k). + * + * \e D(φ, \e k) is defined in https://dlmf.nist.gov/19.2.E4 + * \f[ + * D(\phi, k) = \int_0^\phi + * \frac{\sin^2\theta}{\sqrt{1-k^2\sin^2\theta}}\,d\theta. + * \f] + **********************************************************************/ + Math::real D(real phi) const; + + /** + * Legendre's geodesic longitude integral. + * + * @param[in] phi + * @return \e G(φ, α2, \e k). + * + * \e G(φ, α2, \e k) is defined by + * \f[ + * \begin{align} + * G(\phi, \alpha^2, k) &= + * \frac{k^2}{\alpha^2} F(\phi, k) + + * \biggl(1 - \frac{k^2}{\alpha^2}\biggr) \Pi(\phi, \alpha^2, k) \\ + * &= \int_0^\phi + * \frac{\sqrt{1-k^2\sin^2\theta}}{1 - \alpha^2\sin^2\theta}\,d\theta. + * \end{align} + * \f] + * + * Legendre expresses the longitude of a point on the geodesic in terms of + * this combination of elliptic integrals in Exercices de Calcul + * Intégral, Vol. 1 (1811), p. 181, + * https://books.google.com/books?id=riIOAAAAQAAJ&pg=PA181. + * + * See \ref geodellip for the expression for the longitude in terms of this + * function. + **********************************************************************/ + Math::real G(real phi) const; + + /** + * Cayley's geodesic longitude difference integral. + * + * @param[in] phi + * @return \e H(φ, α2, \e k). + * + * \e H(φ, α2, \e k) is defined by + * \f[ + * \begin{align} + * H(\phi, \alpha^2, k) &= + * \frac1{\alpha^2} F(\phi, k) + + * \biggl(1 - \frac1{\alpha^2}\biggr) \Pi(\phi, \alpha^2, k) \\ + * &= \int_0^\phi + * \frac{\cos^2\theta} + * {(1-\alpha^2\sin^2\theta)\sqrt{1-k^2\sin^2\theta}} + * \,d\theta. + * \end{align} + * \f] + * + * Cayley expresses the longitude difference of a point on the geodesic in + * terms of this combination of elliptic integrals in Phil. Mag. 40 + * (1870), p. 333, https://books.google.com/books?id=Zk0wAAAAIAAJ&pg=PA333. + * + * See \ref geodellip for the expression for the longitude in terms of this + * function. + **********************************************************************/ + Math::real H(real phi) const; + ///@} + + /** \name Incomplete integrals in terms of Jacobi elliptic functions. + **********************************************************************/ + /** + * The incomplete integral of the first kind in terms of Jacobi elliptic + * functions. + * + * @param[in] sn = sinφ. + * @param[in] cn = cosφ. + * @param[in] dn = sqrt(1 − k2 + * sin2φ). + * @return \e F(φ, \e k) as though φ ∈ (−π, π]. + **********************************************************************/ + Math::real F(real sn, real cn, real dn) const; + + /** + * The incomplete integral of the second kind in terms of Jacobi elliptic + * functions. + * + * @param[in] sn = sinφ. + * @param[in] cn = cosφ. + * @param[in] dn = sqrt(1 − k2 + * sin2φ). + * @return \e E(φ, \e k) as though φ ∈ (−π, π]. + **********************************************************************/ + Math::real E(real sn, real cn, real dn) const; + + /** + * The incomplete integral of the third kind in terms of Jacobi elliptic + * functions. + * + * @param[in] sn = sinφ. + * @param[in] cn = cosφ. + * @param[in] dn = sqrt(1 − k2 + * sin2φ). + * @return Π(φ, α2, \e k) as though φ ∈ + * (−π, π]. + **********************************************************************/ + Math::real Pi(real sn, real cn, real dn) const; + + /** + * Jahnke's incomplete elliptic integral in terms of Jacobi elliptic + * functions. + * + * @param[in] sn = sinφ. + * @param[in] cn = cosφ. + * @param[in] dn = sqrt(1 − k2 + * sin2φ). + * @return \e D(φ, \e k) as though φ ∈ (−π, π]. + **********************************************************************/ + Math::real D(real sn, real cn, real dn) const; + + /** + * Legendre's geodesic longitude integral in terms of Jacobi elliptic + * functions. + * + * @param[in] sn = sinφ. + * @param[in] cn = cosφ. + * @param[in] dn = sqrt(1 − k2 + * sin2φ). + * @return \e G(φ, α2, \e k) as though φ ∈ + * (−π, π]. + **********************************************************************/ + Math::real G(real sn, real cn, real dn) const; + + /** + * Cayley's geodesic longitude difference integral in terms of Jacobi + * elliptic functions. + * + * @param[in] sn = sinφ. + * @param[in] cn = cosφ. + * @param[in] dn = sqrt(1 − k2 + * sin2φ). + * @return \e H(φ, α2, \e k) as though φ ∈ + * (−π, π]. + **********************************************************************/ + Math::real H(real sn, real cn, real dn) const; + ///@} + + /** \name Periodic versions of incomplete elliptic integrals. + **********************************************************************/ + ///@{ + /** + * The periodic incomplete integral of the first kind. + * + * @param[in] sn = sinφ. + * @param[in] cn = cosφ. + * @param[in] dn = sqrt(1 − k2 + * sin2φ). + * @return the periodic function π \e F(φ, \e k) / (2 \e K(\e k)) - + * φ. + **********************************************************************/ + Math::real deltaF(real sn, real cn, real dn) const; + + /** + * The periodic incomplete integral of the second kind. + * + * @param[in] sn = sinφ. + * @param[in] cn = cosφ. + * @param[in] dn = sqrt(1 − k2 + * sin2φ). + * @return the periodic function π \e E(φ, \e k) / (2 \e E(\e k)) - + * φ. + **********************************************************************/ + Math::real deltaE(real sn, real cn, real dn) const; + + /** + * The periodic inverse of the incomplete integral of the second kind. + * + * @param[in] stau = sinτ. + * @param[in] ctau = sinτ. + * @return the periodic function E−1(τ (2 \e + * E(\e k)/π), \e k) - τ. + **********************************************************************/ + Math::real deltaEinv(real stau, real ctau) const; + + /** + * The periodic incomplete integral of the third kind. + * + * @param[in] sn = sinφ. + * @param[in] cn = cosφ. + * @param[in] dn = sqrt(1 − k2 + * sin2φ). + * @return the periodic function π Π(φ, α2, + * \e k) / (2 Π(α2, \e k)) - φ. + **********************************************************************/ + Math::real deltaPi(real sn, real cn, real dn) const; + + /** + * The periodic Jahnke's incomplete elliptic integral. + * + * @param[in] sn = sinφ. + * @param[in] cn = cosφ. + * @param[in] dn = sqrt(1 − k2 + * sin2φ). + * @return the periodic function π \e D(φ, \e k) / (2 \e D(\e k)) - + * φ. + **********************************************************************/ + Math::real deltaD(real sn, real cn, real dn) const; + + /** + * Legendre's periodic geodesic longitude integral. + * + * @param[in] sn = sinφ. + * @param[in] cn = cosφ. + * @param[in] dn = sqrt(1 − k2 + * sin2φ). + * @return the periodic function π \e G(φ, \e k) / (2 \e G(\e k)) - + * φ. + **********************************************************************/ + Math::real deltaG(real sn, real cn, real dn) const; + + /** + * Cayley's periodic geodesic longitude difference integral. + * + * @param[in] sn = sinφ. + * @param[in] cn = cosφ. + * @param[in] dn = sqrt(1 − k2 + * sin2φ). + * @return the periodic function π \e H(φ, \e k) / (2 \e H(\e k)) - + * φ. + **********************************************************************/ + Math::real deltaH(real sn, real cn, real dn) const; + ///@} + + /** \name Elliptic functions. + **********************************************************************/ + ///@{ + /** + * The Jacobi elliptic functions. + * + * @param[in] x the argument. + * @param[out] sn sn(\e x, \e k). + * @param[out] cn cn(\e x, \e k). + * @param[out] dn dn(\e x, \e k). + **********************************************************************/ + void sncndn(real x, real& sn, real& cn, real& dn) const; + + /** + * The Δ amplitude function. + * + * @param[in] sn sinφ. + * @param[in] cn cosφ. + * @return Δ = sqrt(1 − k2 + * sin2φ). + **********************************************************************/ + Math::real Delta(real sn, real cn) const { + using std::sqrt; + return sqrt(_k2 < 0 ? 1 - _k2 * sn*sn : _kp2 + _k2 * cn*cn); + } + ///@} + + /** \name Symmetric elliptic integrals. + **********************************************************************/ + ///@{ + /** + * Symmetric integral of the first kind RF. + * + * @param[in] x + * @param[in] y + * @param[in] z + * @return RF(\e x, \e y, \e z). + * + * RF is defined in https://dlmf.nist.gov/19.16.E1 + * \f[ R_F(x, y, z) = \frac12 + * \int_0^\infty\frac1{\sqrt{(t + x) (t + y) (t + z)}}\, dt \f] + * If one of the arguments is zero, it is more efficient to call the + * two-argument version of this function with the non-zero arguments. + **********************************************************************/ + static real RF(real x, real y, real z); + + /** + * Complete symmetric integral of the first kind, + * RF with one argument zero. + * + * @param[in] x + * @param[in] y + * @return RF(\e x, \e y, 0). + **********************************************************************/ + static real RF(real x, real y); + + /** + * Degenerate symmetric integral of the first kind + * RC. + * + * @param[in] x + * @param[in] y + * @return RC(\e x, \e y) = + * RF(\e x, \e y, \e y). + * + * RC is defined in https://dlmf.nist.gov/19.2.E17 + * \f[ R_C(x, y) = \frac12 + * \int_0^\infty\frac1{\sqrt{t + x}(t + y)}\,dt \f] + **********************************************************************/ + static real RC(real x, real y); + + /** + * Symmetric integral of the second kind RG. + * + * @param[in] x + * @param[in] y + * @param[in] z + * @return RG(\e x, \e y, \e z). + * + * RG is defined in Carlson, eq 1.5 + * \f[ R_G(x, y, z) = \frac14 + * \int_0^\infty[(t + x) (t + y) (t + z)]^{-1/2} + * \biggl( + * \frac x{t + x} + \frac y{t + y} + \frac z{t + z} + * \biggr)t\,dt \f] + * See also https://dlmf.nist.gov/19.16.E3. + * If one of the arguments is zero, it is more efficient to call the + * two-argument version of this function with the non-zero arguments. + **********************************************************************/ + static real RG(real x, real y, real z); + + /** + * Complete symmetric integral of the second kind, + * RG with one argument zero. + * + * @param[in] x + * @param[in] y + * @return RG(\e x, \e y, 0). + **********************************************************************/ + static real RG(real x, real y); + + /** + * Symmetric integral of the third kind RJ. + * + * @param[in] x + * @param[in] y + * @param[in] z + * @param[in] p + * @return RJ(\e x, \e y, \e z, \e p). + * + * RJ is defined in https://dlmf.nist.gov/19.16.E2 + * \f[ R_J(x, y, z, p) = \frac32 + * \int_0^\infty + * [(t + x) (t + y) (t + z)]^{-1/2} (t + p)^{-1}\, dt \f] + **********************************************************************/ + static real RJ(real x, real y, real z, real p); + + /** + * Degenerate symmetric integral of the third kind + * RD. + * + * @param[in] x + * @param[in] y + * @param[in] z + * @return RD(\e x, \e y, \e z) = + * RJ(\e x, \e y, \e z, \e z). + * + * RD is defined in https://dlmf.nist.gov/19.16.E5 + * \f[ R_D(x, y, z) = \frac32 + * \int_0^\infty[(t + x) (t + y)]^{-1/2} (t + z)^{-3/2}\, dt \f] + **********************************************************************/ + static real RD(real x, real y, real z); + ///@} + + }; + +} // namespace GeographicLib + +#endif // GEOGRAPHICLIB_ELLIPTICFUNCTION_HPP diff --git a/common/local_libs/GeographicLib/include/GeographicLib/GARS.hpp b/common/local_libs/GeographicLib/include/GeographicLib/GARS.hpp new file mode 100644 index 0000000000..b66b3b22a1 --- /dev/null +++ b/common/local_libs/GeographicLib/include/GeographicLib/GARS.hpp @@ -0,0 +1,143 @@ +/** + * \file GARS.hpp + * \brief Header for GeographicLib::GARS class + * + * Copyright (c) Charles Karney (2015-2017) and licensed + * under the MIT/X11 License. For more information, see + * https://geographiclib.sourceforge.io/ + **********************************************************************/ + +#if !defined(GEOGRAPHICLIB_GARS_HPP) +#define GEOGRAPHICLIB_GARS_HPP 1 + +#include + +#if defined(_MSC_VER) +// Squelch warnings about dll vs string +# pragma warning (push) +# pragma warning (disable: 4251) +#endif + +namespace GeographicLib { + + /** + * \brief Conversions for the Global Area Reference System (GARS) + * + * The Global Area Reference System is described in + * - https://en.wikipedia.org/wiki/Global_Area_Reference_System + * - http://earth-info.nga.mil/GandG/coordsys/grids/gars.html + * . + * It provides a compact string representation of a geographic area + * (expressed as latitude and longitude). The classes Georef and Geohash + * implement similar compact representations. + * + * Example of use: + * \include example-GARS.cpp + **********************************************************************/ + + class GEOGRAPHICLIB_EXPORT GARS { + private: + typedef Math::real real; + static const char* const digits_; + static const char* const letters_; + enum { + lonorig_ = -180, // Origin for longitude + latorig_ = -90, // Origin for latitude + baselon_ = 10, // Base for longitude tiles + baselat_ = 24, // Base for latitude tiles + lonlen_ = 3, + latlen_ = 2, + baselen_ = lonlen_ + latlen_, + mult1_ = 2, // base precision = 1/2 degree + mult2_ = 2, // 6th char gives 2x more precision + mult3_ = 3, // 7th char gives 3x more precision + m_ = mult1_ * mult2_ * mult3_, + maxprec_ = 2, + maxlen_ = baselen_ + maxprec_, + }; + GARS(); // Disable constructor + + public: + + /** + * Convert from geographic coordinates to GARS. + * + * @param[in] lat latitude of point (degrees). + * @param[in] lon longitude of point (degrees). + * @param[in] prec the precision of the resulting GARS. + * @param[out] gars the GARS string. + * @exception GeographicErr if \e lat is not in [−90°, + * 90°]. + * @exception std::bad_alloc if memory for \e gars can't be allocated. + * + * \e prec specifies the precision of \e gars as follows: + * - \e prec = 0 (min), 30' precision, e.g., 006AG; + * - \e prec = 1, 15' precision, e.g., 006AG3; + * - \e prec = 2 (max), 5' precision, e.g., 006AG39. + * + * If \e lat or \e lon is NaN, then \e gars is set to "INVALID". + **********************************************************************/ + static void Forward(real lat, real lon, int prec, std::string& gars); + + /** + * Convert from GARS to geographic coordinates. + * + * @param[in] gars the GARS. + * @param[out] lat latitude of point (degrees). + * @param[out] lon longitude of point (degrees). + * @param[out] prec the precision of \e gars. + * @param[in] centerp if true (the default) return the center of the + * \e gars, otherwise return the south-west corner. + * @exception GeographicErr if \e gars is illegal. + * + * The case of the letters in \e gars is ignored. \e prec is in the range + * [0, 2] and gives the precision of \e gars as follows: + * - \e prec = 0 (min), 30' precision, e.g., 006AG; + * - \e prec = 1, 15' precision, e.g., 006AG3; + * - \e prec = 2 (max), 5' precision, e.g., 006AG39. + * + * If the first 3 characters of \e gars are "INV", then \e lat and \e lon + * are set to NaN and \e prec is unchanged. + **********************************************************************/ + static void Reverse(const std::string& gars, real& lat, real& lon, + int& prec, bool centerp = true); + + /** + * The angular resolution of a GARS. + * + * @param[in] prec the precision of the GARS. + * @return the latitude-longitude resolution (degrees). + * + * Internally, \e prec is first put in the range [0, 2]. + **********************************************************************/ + static Math::real Resolution(int prec) { + return 1/real(prec <= 0 ? mult1_ : (prec == 1 ? mult1_ * mult2_ : + mult1_ * mult2_ * mult3_)); + } + + /** + * The GARS precision required to meet a given geographic resolution. + * + * @param[in] res the minimum of resolution in latitude and longitude + * (degrees). + * @return GARS precision. + * + * The returned length is in the range [0, 2]. + **********************************************************************/ + static int Precision(real res) { + using std::abs; res = abs(res); + for (int prec = 0; prec < maxprec_; ++prec) + if (Resolution(prec) <= res) + return prec; + return maxprec_; + } + + }; + +} // namespace GeographicLib + +#if defined(_MSC_VER) +# pragma warning (pop) +#endif + +#endif // GEOGRAPHICLIB_GARS_HPP diff --git a/common/local_libs/GeographicLib/include/GeographicLib/GeoCoords.hpp b/common/local_libs/GeographicLib/include/GeographicLib/GeoCoords.hpp new file mode 100644 index 0000000000..95e9f2fbd1 --- /dev/null +++ b/common/local_libs/GeographicLib/include/GeographicLib/GeoCoords.hpp @@ -0,0 +1,553 @@ +/** + * \file GeoCoords.hpp + * \brief Header for GeographicLib::GeoCoords class + * + * Copyright (c) Charles Karney (2008-2020) and licensed + * under the MIT/X11 License. For more information, see + * https://geographiclib.sourceforge.io/ + **********************************************************************/ + +#if !defined(GEOGRAPHICLIB_GEOCOORDS_HPP) +#define GEOGRAPHICLIB_GEOCOORDS_HPP 1 + +#include +#include + +namespace GeographicLib { + + /** + * \brief Conversion between geographic coordinates + * + * This class stores a geographic position which may be set via the + * constructors or Reset via + * - latitude and longitude + * - UTM or UPS coordinates + * - a string representation of these or an MGRS coordinate string + * + * The state consists of the latitude and longitude and the supplied UTM or + * UPS coordinates (possibly derived from the MGRS coordinates). If latitude + * and longitude were given then the UTM/UPS coordinates follows the standard + * conventions. + * + * The mutable state consists of the UTM or UPS coordinates for a alternate + * zone. A method SetAltZone is provided to set the alternate UPS/UTM zone. + * + * Methods are provided to return the geographic coordinates, the input UTM + * or UPS coordinates (and associated meridian convergence and scale), or + * alternate UTM or UPS coordinates (and their associated meridian + * convergence and scale). + * + * Once the input string has been parsed, you can print the result out in any + * of the formats, decimal degrees, degrees minutes seconds, MGRS, UTM/UPS. + * + * Example of use: + * \include example-GeoCoords.cpp + * + * GeoConvert is a command-line utility + * providing access to the functionality of GeoCoords. + **********************************************************************/ + class GEOGRAPHICLIB_EXPORT GeoCoords { + private: + typedef Math::real real; + real _lat, _long, _easting, _northing, _gamma, _k; + bool _northp; + int _zone; // See UTMUPS::zonespec + mutable real _alt_easting, _alt_northing, _alt_gamma, _alt_k; + mutable int _alt_zone; + + void CopyToAlt() const { + _alt_easting = _easting; + _alt_northing = _northing; + _alt_gamma = _gamma; + _alt_k = _k; + _alt_zone = _zone; + } + static void UTMUPSString(int zone, bool northp, + real easting, real northing, + int prec, bool abbrev, std::string& utm); + void FixHemisphere(); + public: + + /** \name Initializing the GeoCoords object + **********************************************************************/ + ///@{ + /** + * The default constructor sets the coordinate as undefined. + **********************************************************************/ + GeoCoords() + : _lat(Math::NaN()) + , _long(Math::NaN()) + , _easting(Math::NaN()) + , _northing(Math::NaN()) + , _gamma(Math::NaN()) + , _k(Math::NaN()) + , _northp(false) + , _zone(UTMUPS::INVALID) + { CopyToAlt(); } + + /** + * Construct from a string. + * + * @param[in] s 1-element, 2-element, or 3-element string representation of + * the position. + * @param[in] centerp governs the interpretation of MGRS coordinates (see + * below). + * @param[in] longfirst governs the interpretation of geographic + * coordinates (see below). + * @exception GeographicErr if the \e s is malformed (see below). + * + * Parse as a string and interpret it as a geographic position. The input + * string is broken into space (or comma) separated pieces and Basic + * decision on which format is based on number of components + * -# MGRS + * -# "Lat Long" or "Long Lat" + * -# "Zone Easting Northing" or "Easting Northing Zone" + * + * The following inputs are approximately the same (Ar Ramadi Bridge, Iraq) + * - Latitude and Longitude + * - 33.44 43.27 + * - N33d26.4' E43d16.2' + * - 43d16'12"E 33d26'24"N + * - 43:16:12E 33:26:24 + * - MGRS + * - 38SLC30 + * - 38SLC391014 + * - 38SLC3918701405 + * - 37SHT9708 + * - UTM + * - 38n 339188 3701405 + * - 897039 3708229 37n + * + * Latitude and Longitude parsing: Latitude precedes longitude, + * unless a N, S, E, W hemisphere designator is used on one or both + * coordinates. If \e longfirst = true (default is false), then + * longitude precedes latitude in the absence of a hemisphere designator. + * Thus (with \e longfirst = false) + * - 40 -75 + * - N40 W75 + * - -75 N40 + * - 75W 40N + * - E-75 -40S + * . + * are all the same position. The coordinates may be given in + * decimal degrees, degrees and decimal minutes, degrees, minutes, + * seconds, etc. Use d, ', and " to mark off the degrees, + * minutes and seconds. Various alternative symbols for degrees, minutes, + * and seconds are allowed. Alternatively, use : to separate these + * components. A single addition or subtraction is allowed. (See + * DMS::Decode for details.) Thus + * - 40d30'30" + * - 40d30'30 + * - 40°30'30 + * - 40d30.5' + * - 40d30.5 + * - 40:30:30 + * - 40:30.5 + * - 40.508333333 + * - 40:30+0:0:30 + * - 40:31-0:0.5 + * . + * all specify the same angle. The leading sign applies to the following + * components so -1d30 is -(1+30/60) = −1.5. However, note + * that -1:30-0:0:15 is parsed as (-1:30) + (-0:0:15) = −(1+30/60) + * − (15/3600). Latitudes must be in the range [−90°, + * 90°]. Internally longitudes are reduced to the range + * [−180°, 180°]. + * + * UTM/UPS parsing: For UTM zones (−80° ≤ Lat < + * 84°), the zone designator is made up of a zone number (for 1 to 60) + * and a hemisphere letter (n or s), e.g., 38n (38north can also be used). + * The latitude band designer ([C--M] in the southern hemisphere and [N--X] + * in the northern) should NOT be used. (This is part of the MGRS + * coordinate.) The zone designator for the poles (where UPS is employed) + * is a hemisphere letter by itself, i.e., n or s (north or south can also + * be used). + * + * MGRS parsing interprets the grid references as square area at the + * specified precision (1m, 10m, 100m, etc.). If \e centerp = true (the + * default), the center of this square is then taken to be the precise + * position; thus: + * - 38SMB = 38n 450000 3650000 + * - 38SMB4484 = 38n 444500 3684500 + * - 38SMB44148470 = 38n 444145 3684705 + * . + * Otherwise, the "south-west" corner of the square is used, i.e., + * - 38SMB = 38n 400000 3600000 + * - 38SMB4484 = 38n 444000 3684000 + * - 38SMB44148470 = 38n 444140 3684700 + **********************************************************************/ + explicit GeoCoords(const std::string& s, + bool centerp = true, bool longfirst = false) + { Reset(s, centerp, longfirst); } + + /** + * Construct from geographic coordinates. + * + * @param[in] latitude (degrees). + * @param[in] longitude (degrees). + * @param[in] zone if specified, force the UTM/UPS representation to use a + * specified zone using the rules given in UTMUPS::zonespec. + * @exception GeographicErr if \e latitude is not in [−90°, + * 90°]. + * @exception GeographicErr if \e zone cannot be used for this location. + **********************************************************************/ + GeoCoords(real latitude, real longitude, int zone = UTMUPS::STANDARD) { + Reset(latitude, longitude, zone); + } + + /** + * Construct from UTM/UPS coordinates. + * + * @param[in] zone UTM zone (zero means UPS). + * @param[in] northp hemisphere (true means north, false means south). + * @param[in] easting (meters). + * @param[in] northing (meters). + * @exception GeographicErr if \e zone, \e easting, or \e northing is + * outside its allowed range. + **********************************************************************/ + GeoCoords(int zone, bool northp, real easting, real northing) { + Reset(zone, northp, easting, northing); + } + + /** + * Reset the location from a string. See + * GeoCoords(const std::string& s, bool centerp, bool longfirst). + * + * @param[in] s 1-element, 2-element, or 3-element string representation of + * the position. + * @param[in] centerp governs the interpretation of MGRS coordinates. + * @param[in] longfirst governs the interpretation of geographic + * coordinates. + * @exception GeographicErr if the \e s is malformed. + **********************************************************************/ + void Reset(const std::string& s, + bool centerp = true, bool longfirst = false); + + /** + * Reset the location in terms of geographic coordinates. See + * GeoCoords(real latitude, real longitude, int zone). + * + * @param[in] latitude (degrees). + * @param[in] longitude (degrees). + * @param[in] zone if specified, force the UTM/UPS representation to use a + * specified zone using the rules given in UTMUPS::zonespec. + * @exception GeographicErr if \e latitude is not in [−90°, + * 90°]. + * @exception GeographicErr if \e zone cannot be used for this location. + **********************************************************************/ + void Reset(real latitude, real longitude, int zone = UTMUPS::STANDARD) { + UTMUPS::Forward(latitude, longitude, + _zone, _northp, _easting, _northing, _gamma, _k, + zone); + _lat = latitude; + _long = longitude; + if (_long >= 180) _long -= 360; + else if (_long < -180) _long += 360; + CopyToAlt(); + } + + /** + * Reset the location in terms of UPS/UPS coordinates. See + * GeoCoords(int zone, bool northp, real easting, real northing). + * + * @param[in] zone UTM zone (zero means UPS). + * @param[in] northp hemisphere (true means north, false means south). + * @param[in] easting (meters). + * @param[in] northing (meters). + * @exception GeographicErr if \e zone, \e easting, or \e northing is + * outside its allowed range. + **********************************************************************/ + void Reset(int zone, bool northp, real easting, real northing) { + UTMUPS::Reverse(zone, northp, easting, northing, + _lat, _long, _gamma, _k); + _zone = zone; + _northp = northp; + _easting = easting; + _northing = northing; + FixHemisphere(); + CopyToAlt(); + } + ///@} + + /** \name Querying the GeoCoords object + **********************************************************************/ + ///@{ + /** + * @return latitude (degrees) + **********************************************************************/ + Math::real Latitude() const { return _lat; } + + /** + * @return longitude (degrees) + **********************************************************************/ + Math::real Longitude() const { return _long; } + + /** + * @return easting (meters) + **********************************************************************/ + Math::real Easting() const { return _easting; } + + /** + * @return northing (meters) + **********************************************************************/ + Math::real Northing() const { return _northing; } + + /** + * @return meridian convergence (degrees) for the UTM/UPS projection. + **********************************************************************/ + Math::real Convergence() const { return _gamma; } + + /** + * @return scale for the UTM/UPS projection. + **********************************************************************/ + Math::real Scale() const { return _k; } + + /** + * @return hemisphere (false means south, true means north). + **********************************************************************/ + bool Northp() const { return _northp; } + + /** + * @return hemisphere letter n or s. + **********************************************************************/ + char Hemisphere() const { return _northp ? 'n' : 's'; } + + /** + * @return the zone corresponding to the input (return 0 for UPS). + **********************************************************************/ + int Zone() const { return _zone; } + + ///@} + + /** \name Setting and querying the alternate zone + **********************************************************************/ + ///@{ + /** + * Specify alternate zone number. + * + * @param[in] zone zone number for the alternate representation. + * @exception GeographicErr if \e zone cannot be used for this location. + * + * See UTMUPS::zonespec for more information on the interpretation of \e + * zone. Note that \e zone == UTMUPS::STANDARD (the default) use the + * standard UPS or UTM zone, UTMUPS::MATCH does nothing retaining the + * existing alternate representation. Before this is called the alternate + * zone is the input zone. + **********************************************************************/ + void SetAltZone(int zone = UTMUPS::STANDARD) const { + if (zone == UTMUPS::MATCH) + return; + zone = UTMUPS::StandardZone(_lat, _long, zone); + if (zone == _zone) + CopyToAlt(); + else { + bool northp; + UTMUPS::Forward(_lat, _long, + _alt_zone, northp, + _alt_easting, _alt_northing, _alt_gamma, _alt_k, + zone); + } + } + + /** + * @return current alternate zone (return 0 for UPS). + **********************************************************************/ + int AltZone() const { return _alt_zone; } + + /** + * @return easting (meters) for alternate zone. + **********************************************************************/ + Math::real AltEasting() const { return _alt_easting; } + + /** + * @return northing (meters) for alternate zone. + **********************************************************************/ + Math::real AltNorthing() const { return _alt_northing; } + + /** + * @return meridian convergence (degrees) for alternate zone. + **********************************************************************/ + Math::real AltConvergence() const { return _alt_gamma; } + + /** + * @return scale for alternate zone. + **********************************************************************/ + Math::real AltScale() const { return _alt_k; } + ///@} + + /** \name String representations of the GeoCoords object + **********************************************************************/ + ///@{ + /** + * String representation with latitude and longitude as signed decimal + * degrees. + * + * @param[in] prec precision (relative to about 1m). + * @param[in] longfirst if true give longitude first (default = false) + * @exception std::bad_alloc if memory for the string can't be allocated. + * @return decimal latitude/longitude string representation. + * + * Precision specifies accuracy of representation as follows: + * - prec = −5 (min), 1° + * - prec = 0, 10−5° (about 1m) + * - prec = 3, 10−8° + * - prec = 9 (max), 10−14° + **********************************************************************/ + std::string GeoRepresentation(int prec = 0, bool longfirst = false) const; + + /** + * String representation with latitude and longitude as degrees, minutes, + * seconds, and hemisphere. + * + * @param[in] prec precision (relative to about 1m) + * @param[in] longfirst if true give longitude first (default = false) + * @param[in] dmssep if non-null, use as the DMS separator character + * (instead of d, ', " delimiters). + * @exception std::bad_alloc if memory for the string can't be allocated. + * @return DMS latitude/longitude string representation. + * + * Precision specifies accuracy of representation as follows: + * - prec = −5 (min), 1° + * - prec = −4, 0.1° + * - prec = −3, 1' + * - prec = −2, 0.1' + * - prec = −1, 1" + * - prec = 0, 0.1" (about 3m) + * - prec = 1, 0.01" + * - prec = 10 (max), 10−11" + **********************************************************************/ + std::string DMSRepresentation(int prec = 0, bool longfirst = false, + char dmssep = char(0)) + const; + + /** + * MGRS string. + * + * @param[in] prec precision (relative to about 1m). + * @exception std::bad_alloc if memory for the string can't be allocated. + * @return MGRS string. + * + * This gives the coordinates of the enclosing grid square with size given + * by the precision. Thus 38n 444180 3684790 converted to a MGRS + * coordinate at precision −2 (100m) is 38SMB441847 and not + * 38SMB442848. \e prec specifies the precision of the MGRS string as + * follows: + * - prec = −6 (min), only the grid zone is returned, e.g., 38S + * - prec = −5, 100km, e.g., 38SMB + * - prec = −4, 10km + * - prec = −3, 1km + * - prec = −2, 100m + * - prec = −1, 10m + * - prec = 0, 1m + * - prec = 1, 0.1m + * - prec = 6 (max), 1μm + **********************************************************************/ + std::string MGRSRepresentation(int prec = 0) const; + + /** + * UTM/UPS string. + * + * @param[in] prec precision (relative to about 1m) + * @param[in] abbrev if true (the default) use abbreviated (n/s) notation + * for hemisphere; otherwise spell out the hemisphere (north/south) + * @exception std::bad_alloc if memory for the string can't be allocated. + * @return UTM/UPS string representation: zone designator, easting, and + * northing. + * + * Precision specifies accuracy of representation as follows: + * - prec = −5 (min), 100km + * - prec = −3, 1km + * - prec = 0, 1m + * - prec = 3, 1mm + * - prec = 6, 1μm + * - prec = 9 (max), 1nm + **********************************************************************/ + std::string UTMUPSRepresentation(int prec = 0, bool abbrev = true) const; + + /** + * UTM/UPS string with hemisphere override. + * + * @param[in] northp hemisphere override + * @param[in] prec precision (relative to about 1m) + * @param[in] abbrev if true (the default) use abbreviated (n/s) notation + * for hemisphere; otherwise spell out the hemisphere (north/south) + * @exception GeographicErr if the hemisphere override attempts to change + * UPS N to UPS S or vice versa. + * @exception std::bad_alloc if memory for the string can't be allocated. + * @return UTM/UPS string representation: zone designator, easting, and + * northing. + **********************************************************************/ + std::string UTMUPSRepresentation(bool northp, int prec = 0, + bool abbrev = true) const; + + /** + * MGRS string for the alternate zone. See GeoCoords::MGRSRepresentation. + * + * @param[in] prec precision (relative to about 1m). + * @exception std::bad_alloc if memory for the string can't be allocated. + * @return MGRS string. + **********************************************************************/ + std::string AltMGRSRepresentation(int prec = 0) const; + + /** + * UTM/UPS string for the alternate zone. See + * GeoCoords::UTMUPSRepresentation. + * + * @param[in] prec precision (relative to about 1m) + * @param[in] abbrev if true (the default) use abbreviated (n/s) notation + * for hemisphere; otherwise spell out the hemisphere (north/south) + * @exception std::bad_alloc if memory for the string can't be allocated. + * @return UTM/UPS string representation: zone designator, easting, and + * northing. + **********************************************************************/ + std::string AltUTMUPSRepresentation(int prec = 0, bool abbrev = true) + const; + + /** + * UTM/UPS string for the alternate zone, with hemisphere override. + * + * @param[in] northp hemisphere override + * @param[in] prec precision (relative to about 1m) + * @param[in] abbrev if true (the default) use abbreviated (n/s) notation + * for hemisphere; otherwise spell out the hemisphere (north/south) + * @exception GeographicErr if the hemisphere override attempts to change + * UPS n to UPS s or vice verse. + * @exception std::bad_alloc if memory for the string can't be allocated. + * @return UTM/UPS string representation: zone designator, easting, and + * northing. + **********************************************************************/ + std::string AltUTMUPSRepresentation(bool northp, int prec = 0, + bool abbrev = true) const; + ///@} + + /** \name Inspector functions + **********************************************************************/ + ///@{ + /** + * @return \e a the equatorial radius of the WGS84 ellipsoid (meters). + * + * (The WGS84 value is returned because the UTM and UPS projections are + * based on this ellipsoid.) + **********************************************************************/ + Math::real EquatorialRadius() const { return UTMUPS::EquatorialRadius(); } + + /** + * @return \e f the flattening of the WGS84 ellipsoid. + * + * (The WGS84 value is returned because the UTM and UPS projections are + * based on this ellipsoid.) + **********************************************************************/ + Math::real Flattening() const { return UTMUPS::Flattening(); } + + /** + * \deprecated An old name for EquatorialRadius(). + **********************************************************************/ + GEOGRAPHICLIB_DEPRECATED("Use EquatorialRadius()") + Math::real MajorRadius() const { return EquatorialRadius(); } + ///@} + + }; + +} // namespace GeographicLib + +#endif // GEOGRAPHICLIB_GEOCOORDS_HPP diff --git a/common/local_libs/GeographicLib/include/GeographicLib/Geocentric.hpp b/common/local_libs/GeographicLib/include/GeographicLib/Geocentric.hpp new file mode 100644 index 0000000000..dce99ec104 --- /dev/null +++ b/common/local_libs/GeographicLib/include/GeographicLib/Geocentric.hpp @@ -0,0 +1,274 @@ +/** + * \file Geocentric.hpp + * \brief Header for GeographicLib::Geocentric class + * + * Copyright (c) Charles Karney (2008-2020) and licensed + * under the MIT/X11 License. For more information, see + * https://geographiclib.sourceforge.io/ + **********************************************************************/ + +#if !defined(GEOGRAPHICLIB_GEOCENTRIC_HPP) +#define GEOGRAPHICLIB_GEOCENTRIC_HPP 1 + +#include +#include + +namespace GeographicLib { + + /** + * \brief %Geocentric coordinates + * + * Convert between geodetic coordinates latitude = \e lat, longitude = \e + * lon, height = \e h (measured vertically from the surface of the ellipsoid) + * to geocentric coordinates (\e X, \e Y, \e Z). The origin of geocentric + * coordinates is at the center of the earth. The \e Z axis goes thru the + * north pole, \e lat = 90°. The \e X axis goes thru \e lat = 0, + * \e lon = 0. %Geocentric coordinates are also known as earth centered, + * earth fixed (ECEF) coordinates. + * + * The conversion from geographic to geocentric coordinates is + * straightforward. For the reverse transformation we use + * - H. Vermeille, + * Direct + * transformation from geocentric coordinates to geodetic coordinates, + * J. Geodesy 76, 451--454 (2002). + * . + * Several changes have been made to ensure that the method returns accurate + * results for all finite inputs (even if \e h is infinite). The changes are + * described in Appendix B of + * - C. F. F. Karney, + * Geodesics + * on an ellipsoid of revolution, + * Feb. 2011; + * preprint + * arxiv:1102.1215v1. + * . + * Vermeille similarly updated his method in + * - H. Vermeille, + * + * An analytical method to transform geocentric into + * geodetic coordinates, J. Geodesy 85, 105--117 (2011). + * . + * See \ref geocentric for more information. + * + * The errors in these routines are close to round-off. Specifically, for + * points within 5000 km of the surface of the ellipsoid (either inside or + * outside the ellipsoid), the error is bounded by 7 nm (7 nanometers) for + * the WGS84 ellipsoid. See \ref geocentric for further information on the + * errors. + * + * Example of use: + * \include example-Geocentric.cpp + * + * CartConvert is a command-line utility + * providing access to the functionality of Geocentric and LocalCartesian. + **********************************************************************/ + + class GEOGRAPHICLIB_EXPORT Geocentric { + private: + typedef Math::real real; + friend class LocalCartesian; + friend class MagneticCircle; // MagneticCircle uses Rotation + friend class MagneticModel; // MagneticModel uses IntForward + friend class GravityCircle; // GravityCircle uses Rotation + friend class GravityModel; // GravityModel uses IntForward + friend class NormalGravity; // NormalGravity uses IntForward + static const size_t dim_ = 3; + static const size_t dim2_ = dim_ * dim_; + real _a, _f, _e2, _e2m, _e2a, _e4a, _maxrad; + static void Rotation(real sphi, real cphi, real slam, real clam, + real M[dim2_]); + static void Rotate(real M[dim2_], real x, real y, real z, + real& X, real& Y, real& Z) { + // Perform [X,Y,Z]^t = M.[x,y,z]^t + // (typically local cartesian to geocentric) + X = M[0] * x + M[1] * y + M[2] * z; + Y = M[3] * x + M[4] * y + M[5] * z; + Z = M[6] * x + M[7] * y + M[8] * z; + } + static void Unrotate(real M[dim2_], real X, real Y, real Z, + real& x, real& y, real& z) { + // Perform [x,y,z]^t = M^t.[X,Y,Z]^t + // (typically geocentric to local cartesian) + x = M[0] * X + M[3] * Y + M[6] * Z; + y = M[1] * X + M[4] * Y + M[7] * Z; + z = M[2] * X + M[5] * Y + M[8] * Z; + } + void IntForward(real lat, real lon, real h, real& X, real& Y, real& Z, + real M[dim2_]) const; + void IntReverse(real X, real Y, real Z, real& lat, real& lon, real& h, + real M[dim2_]) const; + + public: + + /** + * Constructor for a ellipsoid with + * + * @param[in] a equatorial radius (meters). + * @param[in] f flattening of ellipsoid. Setting \e f = 0 gives a sphere. + * Negative \e f gives a prolate ellipsoid. + * @exception GeographicErr if \e a or (1 − \e f) \e a is not + * positive. + **********************************************************************/ + Geocentric(real a, real f); + + /** + * A default constructor (for use by NormalGravity). + **********************************************************************/ + Geocentric() : _a(-1) {} + + /** + * Convert from geodetic to geocentric coordinates. + * + * @param[in] lat latitude of point (degrees). + * @param[in] lon longitude of point (degrees). + * @param[in] h height of point above the ellipsoid (meters). + * @param[out] X geocentric coordinate (meters). + * @param[out] Y geocentric coordinate (meters). + * @param[out] Z geocentric coordinate (meters). + * + * \e lat should be in the range [−90°, 90°]. + **********************************************************************/ + void Forward(real lat, real lon, real h, real& X, real& Y, real& Z) + const { + if (Init()) + IntForward(lat, lon, h, X, Y, Z, NULL); + } + + /** + * Convert from geodetic to geocentric coordinates and return rotation + * matrix. + * + * @param[in] lat latitude of point (degrees). + * @param[in] lon longitude of point (degrees). + * @param[in] h height of point above the ellipsoid (meters). + * @param[out] X geocentric coordinate (meters). + * @param[out] Y geocentric coordinate (meters). + * @param[out] Z geocentric coordinate (meters). + * @param[out] M if the length of the vector is 9, fill with the rotation + * matrix in row-major order. + * + * Let \e v be a unit vector located at (\e lat, \e lon, \e h). We can + * express \e v as \e column vectors in one of two ways + * - in east, north, up coordinates (where the components are relative to a + * local coordinate system at (\e lat, \e lon, \e h)); call this + * representation \e v1. + * - in geocentric \e X, \e Y, \e Z coordinates; call this representation + * \e v0. + * . + * Then we have \e v0 = \e M ⋅ \e v1. + **********************************************************************/ + void Forward(real lat, real lon, real h, real& X, real& Y, real& Z, + std::vector& M) + const { + if (!Init()) + return; + if (M.end() == M.begin() + dim2_) { + real t[dim2_]; + IntForward(lat, lon, h, X, Y, Z, t); + std::copy(t, t + dim2_, M.begin()); + } else + IntForward(lat, lon, h, X, Y, Z, NULL); + } + + /** + * Convert from geocentric to geodetic to coordinates. + * + * @param[in] X geocentric coordinate (meters). + * @param[in] Y geocentric coordinate (meters). + * @param[in] Z geocentric coordinate (meters). + * @param[out] lat latitude of point (degrees). + * @param[out] lon longitude of point (degrees). + * @param[out] h height of point above the ellipsoid (meters). + * + * In general, there are multiple solutions and the result which minimizes + * |h |is returned, i.e., (lat, lon) corresponds to + * the closest point on the ellipsoid. If there are still multiple + * solutions with different latitudes (applies only if \e Z = 0), then the + * solution with \e lat > 0 is returned. If there are still multiple + * solutions with different longitudes (applies only if \e X = \e Y = 0) + * then \e lon = 0 is returned. The value of \e h returned satisfies \e h + * ≥ − \e a (1 − e2) / sqrt(1 − + * e2 sin2\e lat). The value of \e lon + * returned is in the range [−180°, 180°]. + **********************************************************************/ + void Reverse(real X, real Y, real Z, real& lat, real& lon, real& h) + const { + if (Init()) + IntReverse(X, Y, Z, lat, lon, h, NULL); + } + + /** + * Convert from geocentric to geodetic to coordinates. + * + * @param[in] X geocentric coordinate (meters). + * @param[in] Y geocentric coordinate (meters). + * @param[in] Z geocentric coordinate (meters). + * @param[out] lat latitude of point (degrees). + * @param[out] lon longitude of point (degrees). + * @param[out] h height of point above the ellipsoid (meters). + * @param[out] M if the length of the vector is 9, fill with the rotation + * matrix in row-major order. + * + * Let \e v be a unit vector located at (\e lat, \e lon, \e h). We can + * express \e v as \e column vectors in one of two ways + * - in east, north, up coordinates (where the components are relative to a + * local coordinate system at (\e lat, \e lon, \e h)); call this + * representation \e v1. + * - in geocentric \e X, \e Y, \e Z coordinates; call this representation + * \e v0. + * . + * Then we have \e v1 = MT ⋅ \e v0, where + * MT is the transpose of \e M. + **********************************************************************/ + void Reverse(real X, real Y, real Z, real& lat, real& lon, real& h, + std::vector& M) + const { + if (!Init()) + return; + if (M.end() == M.begin() + dim2_) { + real t[dim2_]; + IntReverse(X, Y, Z, lat, lon, h, t); + std::copy(t, t + dim2_, M.begin()); + } else + IntReverse(X, Y, Z, lat, lon, h, NULL); + } + + /** \name Inspector functions + **********************************************************************/ + ///@{ + /** + * @return true if the object has been initialized. + **********************************************************************/ + bool Init() const { return _a > 0; } + /** + * @return \e a the equatorial radius of the ellipsoid (meters). This is + * the value used in the constructor. + **********************************************************************/ + Math::real EquatorialRadius() const + { return Init() ? _a : Math::NaN(); } + + /** + * @return \e f the flattening of the ellipsoid. This is the + * value used in the constructor. + **********************************************************************/ + Math::real Flattening() const + { return Init() ? _f : Math::NaN(); } + + /** + * \deprecated An old name for EquatorialRadius(). + **********************************************************************/ + GEOGRAPHICLIB_DEPRECATED("Use EquatorialRadius()") + Math::real MajorRadius() const { return EquatorialRadius(); } + ///@} + + /** + * A global instantiation of Geocentric with the parameters for the WGS84 + * ellipsoid. + **********************************************************************/ + static const Geocentric& WGS84(); + }; + +} // namespace GeographicLib + +#endif // GEOGRAPHICLIB_GEOCENTRIC_HPP diff --git a/common/local_libs/GeographicLib/include/GeographicLib/Geodesic.hpp b/common/local_libs/GeographicLib/include/GeographicLib/Geodesic.hpp new file mode 100644 index 0000000000..53fec2588d --- /dev/null +++ b/common/local_libs/GeographicLib/include/GeographicLib/Geodesic.hpp @@ -0,0 +1,977 @@ +/** + * \file Geodesic.hpp + * \brief Header for GeographicLib::Geodesic class + * + * Copyright (c) Charles Karney (2009-2020) and licensed + * under the MIT/X11 License. For more information, see + * https://geographiclib.sourceforge.io/ + **********************************************************************/ + +#if !defined(GEOGRAPHICLIB_GEODESIC_HPP) +#define GEOGRAPHICLIB_GEODESIC_HPP 1 + +#include + +#if !defined(GEOGRAPHICLIB_GEODESIC_ORDER) +/** + * The order of the expansions used by Geodesic. + * GEOGRAPHICLIB_GEODESIC_ORDER can be set to any integer in [3, 8]. + **********************************************************************/ +# define GEOGRAPHICLIB_GEODESIC_ORDER \ + (GEOGRAPHICLIB_PRECISION == 2 ? 6 : \ + (GEOGRAPHICLIB_PRECISION == 1 ? 3 : \ + (GEOGRAPHICLIB_PRECISION == 3 ? 7 : 8))) +#endif + +namespace GeographicLib { + + class GeodesicLine; + + /** + * \brief %Geodesic calculations + * + * The shortest path between two points on a ellipsoid at (\e lat1, \e lon1) + * and (\e lat2, \e lon2) is called the geodesic. Its length is \e s12 and + * the geodesic from point 1 to point 2 has azimuths \e azi1 and \e azi2 at + * the two end points. (The azimuth is the heading measured clockwise from + * north. \e azi2 is the "forward" azimuth, i.e., the heading that takes you + * beyond point 2 not back to point 1.) In the figure below, latitude if + * labeled φ, longitude λ (with λ12 = + * λ2 − λ1), and azimuth α. + * + * spheroidal triangle + * + * Given \e lat1, \e lon1, \e azi1, and \e s12, we can determine \e lat2, \e + * lon2, and \e azi2. This is the \e direct geodesic problem and its + * solution is given by the function Geodesic::Direct. (If \e s12 is + * sufficiently large that the geodesic wraps more than halfway around the + * earth, there will be another geodesic between the points with a smaller \e + * s12.) + * + * Given \e lat1, \e lon1, \e lat2, and \e lon2, we can determine \e azi1, \e + * azi2, and \e s12. This is the \e inverse geodesic problem, whose solution + * is given by Geodesic::Inverse. Usually, the solution to the inverse + * problem is unique. In cases where there are multiple solutions (all with + * the same \e s12, of course), all the solutions can be easily generated + * once a particular solution is provided. + * + * The standard way of specifying the direct problem is the specify the + * distance \e s12 to the second point. However it is sometimes useful + * instead to specify the arc length \e a12 (in degrees) on the auxiliary + * sphere. This is a mathematical construct used in solving the geodesic + * problems. The solution of the direct problem in this form is provided by + * Geodesic::ArcDirect. An arc length in excess of 180° indicates that + * the geodesic is not a shortest path. In addition, the arc length between + * an equatorial crossing and the next extremum of latitude for a geodesic is + * 90°. + * + * This class can also calculate several other quantities related to + * geodesics. These are: + * - reduced length. If we fix the first point and increase \e azi1 + * by \e dazi1 (radians), the second point is displaced \e m12 \e dazi1 in + * the direction \e azi2 + 90°. The quantity \e m12 is called + * the "reduced length" and is symmetric under interchange of the two + * points. On a curved surface the reduced length obeys a symmetry + * relation, \e m12 + \e m21 = 0. On a flat surface, we have \e m12 = \e + * s12. The ratio s12/\e m12 gives the azimuthal scale for an + * azimuthal equidistant projection. + * - geodesic scale. Consider a reference geodesic and a second + * geodesic parallel to this one at point 1 and separated by a small + * distance \e dt. The separation of the two geodesics at point 2 is \e + * M12 \e dt where \e M12 is called the "geodesic scale". \e M21 is + * defined similarly (with the geodesics being parallel at point 2). On a + * flat surface, we have \e M12 = \e M21 = 1. The quantity 1/\e M12 gives + * the scale of the Cassini-Soldner projection. + * - area. The area between the geodesic from point 1 to point 2 and + * the equation is represented by \e S12; it is the area, measured + * counter-clockwise, of the geodesic quadrilateral with corners + * (lat1,lon1), (0,lon1), (0,lon2), and + * (lat2,lon2). It can be used to compute the area of any + * geodesic polygon. + * + * Overloaded versions of Geodesic::Direct, Geodesic::ArcDirect, and + * Geodesic::Inverse allow these quantities to be returned. In addition + * there are general functions Geodesic::GenDirect, and Geodesic::GenInverse + * which allow an arbitrary set of results to be computed. The quantities \e + * m12, \e M12, \e M21 which all specify the behavior of nearby geodesics + * obey addition rules. If points 1, 2, and 3 all lie on a single geodesic, + * then the following rules hold: + * - \e s13 = \e s12 + \e s23 + * - \e a13 = \e a12 + \e a23 + * - \e S13 = \e S12 + \e S23 + * - \e m13 = \e m12 \e M23 + \e m23 \e M21 + * - \e M13 = \e M12 \e M23 − (1 − \e M12 \e M21) \e m23 / \e m12 + * - \e M31 = \e M32 \e M21 − (1 − \e M23 \e M32) \e m12 / \e m23 + * + * Additional functionality is provided by the GeodesicLine class, which + * allows a sequence of points along a geodesic to be computed. + * + * The shortest distance returned by the solution of the inverse problem is + * (obviously) uniquely defined. However, in a few special cases there are + * multiple azimuths which yield the same shortest distance. Here is a + * catalog of those cases: + * - \e lat1 = −\e lat2 (with neither point at a pole). If \e azi1 = + * \e azi2, the geodesic is unique. Otherwise there are two geodesics and + * the second one is obtained by setting [\e azi1, \e azi2] → [\e + * azi2, \e azi1], [\e M12, \e M21] → [\e M21, \e M12], \e S12 → + * −\e S12. (This occurs when the longitude difference is near + * ±180° for oblate ellipsoids.) + * - \e lon2 = \e lon1 ± 180° (with neither point at a pole). If + * \e azi1 = 0° or ±180°, the geodesic is unique. Otherwise + * there are two geodesics and the second one is obtained by setting [\e + * azi1, \e azi2] → [−\e azi1, −\e azi2], \e S12 → + * −\e S12. (This occurs when \e lat2 is near −\e lat1 for + * prolate ellipsoids.) + * - Points 1 and 2 at opposite poles. There are infinitely many geodesics + * which can be generated by setting [\e azi1, \e azi2] → [\e azi1, \e + * azi2] + [\e d, −\e d], for arbitrary \e d. (For spheres, this + * prescription applies when points 1 and 2 are antipodal.) + * - \e s12 = 0 (coincident points). There are infinitely many geodesics + * which can be generated by setting [\e azi1, \e azi2] → + * [\e azi1, \e azi2] + [\e d, \e d], for arbitrary \e d. + * + * The calculations are accurate to better than 15 nm (15 nanometers) for the + * WGS84 ellipsoid. See Sec. 9 of + * arXiv:1102.1215v1 for + * details. The algorithms used by this class are based on series expansions + * using the flattening \e f as a small parameter. These are only accurate + * for |f| < 0.02; however reasonably accurate results will be + * obtained for |f| < 0.2. Here is a table of the approximate + * maximum error (expressed as a distance) for an ellipsoid with the same + * equatorial radius as the WGS84 ellipsoid and different values of the + * flattening.

+   *     |f|      error
+   *     0.01     25 nm
+   *     0.02     30 nm
+   *     0.05     10 um
+   *     0.1     1.5 mm
+   *     0.2     300 mm
+   * 
+ * For very eccentric ellipsoids, use GeodesicExact instead. + * + * The algorithms are described in + * - C. F. F. Karney, + * + * Algorithms for geodesics, + * J. Geodesy 87, 43--55 (2013); + * DOI: + * 10.1007/s00190-012-0578-z; + * addenda: + * + * geod-addenda.html. + * . + * For more information on geodesics see \ref geodesic. + * + * Example of use: + * \include example-Geodesic.cpp + * + * GeodSolve is a command-line utility + * providing access to the functionality of Geodesic and GeodesicLine. + **********************************************************************/ + + class GEOGRAPHICLIB_EXPORT Geodesic { + private: + typedef Math::real real; + friend class GeodesicLine; + static const int nA1_ = GEOGRAPHICLIB_GEODESIC_ORDER; + static const int nC1_ = GEOGRAPHICLIB_GEODESIC_ORDER; + static const int nC1p_ = GEOGRAPHICLIB_GEODESIC_ORDER; + static const int nA2_ = GEOGRAPHICLIB_GEODESIC_ORDER; + static const int nC2_ = GEOGRAPHICLIB_GEODESIC_ORDER; + static const int nA3_ = GEOGRAPHICLIB_GEODESIC_ORDER; + static const int nA3x_ = nA3_; + static const int nC3_ = GEOGRAPHICLIB_GEODESIC_ORDER; + static const int nC3x_ = (nC3_ * (nC3_ - 1)) / 2; + static const int nC4_ = GEOGRAPHICLIB_GEODESIC_ORDER; + static const int nC4x_ = (nC4_ * (nC4_ + 1)) / 2; + // Size for temporary array + // nC = max(max(nC1_, nC1p_, nC2_) + 1, max(nC3_, nC4_)) + static const int nC_ = GEOGRAPHICLIB_GEODESIC_ORDER + 1; + static const unsigned maxit1_ = 20; + unsigned maxit2_; + real tiny_, tol0_, tol1_, tol2_, tolb_, xthresh_; + + 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, + CAP_MASK = CAP_ALL, + OUT_ALL = 0x7F80U, + OUT_MASK = 0xFF80U, // Includes LONG_UNROLL + }; + + static real SinCosSeries(bool sinp, + real sinx, real cosx, const real c[], int n); + static real Astroid(real x, real y); + + real _a, _f, _f1, _e2, _ep2, _n, _b, _c2, _etol2; + real _A3x[nA3x_], _C3x[nC3x_], _C4x[nC4x_]; + + void Lengths(real eps, real sig12, + real ssig1, real csig1, real dn1, + real ssig2, real csig2, real dn2, + real cbet1, real cbet2, unsigned outmask, + real& s12s, real& m12a, real& m0, + real& M12, real& M21, real Ca[]) const; + real InverseStart(real sbet1, real cbet1, real dn1, + real sbet2, real cbet2, real dn2, + real lam12, real slam12, real clam12, + real& salp1, real& calp1, + real& salp2, real& calp2, real& dnm, + real Ca[]) const; + real Lambda12(real sbet1, real cbet1, real dn1, + real sbet2, real cbet2, real dn2, + real salp1, real calp1, real slam120, real clam120, + real& salp2, real& calp2, real& sig12, + real& ssig1, real& csig1, real& ssig2, real& csig2, + real& eps, real& domg12, + bool diffp, real& dlam12, real Ca[]) const; + real GenInverse(real lat1, real lon1, real lat2, real lon2, + unsigned outmask, real& s12, + real& salp1, real& calp1, real& salp2, real& calp2, + real& m12, real& M12, real& M21, real& S12) const; + + // These are Maxima generated functions to provide series approximations to + // the integrals for the ellipsoidal geodesic. + 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[]); + + void A3coeff(); + real A3f(real eps) const; + void C3coeff(); + void C3f(real eps, real c[]) const; + void C4coeff(); + void C4f(real k2, real c[]) const; + + public: + + /** + * Bit masks for what calculations to do. These masks do double duty. + * They signify to the GeodesicLine::GeodesicLine constructor and to + * Geodesic::Line what capabilities should be included in the GeodesicLine + * object. They also specify which results to return in the general + * routines Geodesic::GenDirect and Geodesic::GenInverse routines. + * GeodesicLine::mask is a duplication of this enum. + **********************************************************************/ + enum mask { + /** + * No capabilities, no output. + * @hideinitializer + **********************************************************************/ + NONE = 0U, + /** + * Calculate latitude \e lat2. (It's not necessary to include this as a + * capability to GeodesicLine because this is included by default.) + * @hideinitializer + **********************************************************************/ + LATITUDE = 1U<<7 | CAP_NONE, + /** + * Calculate longitude \e lon2. + * @hideinitializer + **********************************************************************/ + LONGITUDE = 1U<<8 | CAP_C3, + /** + * Calculate azimuths \e azi1 and \e azi2. (It's not necessary to + * include this as a capability to GeodesicLine because this is included + * by default.) + * @hideinitializer + **********************************************************************/ + AZIMUTH = 1U<<9 | CAP_NONE, + /** + * Calculate distance \e s12. + * @hideinitializer + **********************************************************************/ + DISTANCE = 1U<<10 | CAP_C1, + /** + * Allow distance \e s12 to be used as input in the direct geodesic + * problem. + * @hideinitializer + **********************************************************************/ + DISTANCE_IN = 1U<<11 | CAP_C1 | CAP_C1p, + /** + * Calculate reduced length \e m12. + * @hideinitializer + **********************************************************************/ + REDUCEDLENGTH = 1U<<12 | CAP_C1 | CAP_C2, + /** + * Calculate geodesic scales \e M12 and \e M21. + * @hideinitializer + **********************************************************************/ + GEODESICSCALE = 1U<<13 | CAP_C1 | CAP_C2, + /** + * Calculate area \e S12. + * @hideinitializer + **********************************************************************/ + AREA = 1U<<14 | CAP_C4, + /** + * Unroll \e lon2 in the direct calculation. + * @hideinitializer + **********************************************************************/ + LONG_UNROLL = 1U<<15, + /** + * All capabilities, calculate everything. (LONG_UNROLL is not + * included in this mask.) + * @hideinitializer + **********************************************************************/ + ALL = OUT_ALL| CAP_ALL, + }; + + /** \name Constructor + **********************************************************************/ + ///@{ + /** + * Constructor for a ellipsoid with + * + * @param[in] a equatorial radius (meters). + * @param[in] f flattening of ellipsoid. Setting \e f = 0 gives a sphere. + * Negative \e f gives a prolate ellipsoid. + * @exception GeographicErr if \e a or (1 − \e f) \e a is not + * positive. + **********************************************************************/ + Geodesic(real a, real f); + ///@} + + /** \name Direct geodesic problem specified in terms of distance. + **********************************************************************/ + ///@{ + /** + * Solve the direct geodesic problem where the length of the geodesic + * is specified in terms of distance. + * + * @param[in] lat1 latitude of point 1 (degrees). + * @param[in] lon1 longitude of point 1 (degrees). + * @param[in] azi1 azimuth at point 1 (degrees). + * @param[in] s12 distance between point 1 and point 2 (meters); it can be + * negative. + * @param[out] lat2 latitude of point 2 (degrees). + * @param[out] lon2 longitude of point 2 (degrees). + * @param[out] azi2 (forward) azimuth at point 2 (degrees). + * @param[out] m12 reduced length of geodesic (meters). + * @param[out] M12 geodesic scale of point 2 relative to point 1 + * (dimensionless). + * @param[out] M21 geodesic scale of point 1 relative to point 2 + * (dimensionless). + * @param[out] S12 area under the geodesic (meters2). + * @return \e a12 arc length of between point 1 and point 2 (degrees). + * + * \e lat1 should be in the range [−90°, 90°]. The values of + * \e lon2 and \e azi2 returned are in the range [−180°, + * 180°]. + * + * If either point is at a pole, the azimuth is defined by keeping the + * longitude fixed, writing \e lat = ±(90° − ε), + * and taking the limit ε → 0+. An arc length greater that + * 180° signifies a geodesic which is not a shortest path. (For a + * prolate ellipsoid, an additional condition is necessary for a shortest + * path: the longitudinal extent must not exceed of 180°.) + * + * The following functions are overloaded versions of Geodesic::Direct + * which omit some of the output parameters. Note, however, that the arc + * length is always computed and returned as the function value. + **********************************************************************/ + Math::real Direct(real lat1, real lon1, real azi1, real s12, + real& lat2, real& lon2, real& azi2, + real& m12, real& M12, real& M21, real& S12) + const { + real t; + return GenDirect(lat1, lon1, azi1, false, s12, + LATITUDE | LONGITUDE | AZIMUTH | + REDUCEDLENGTH | GEODESICSCALE | AREA, + lat2, lon2, azi2, t, m12, M12, M21, S12); + } + + /** + * See the documentation for Geodesic::Direct. + **********************************************************************/ + Math::real Direct(real lat1, real lon1, real azi1, real s12, + real& lat2, real& lon2) + const { + real t; + return GenDirect(lat1, lon1, azi1, false, s12, + LATITUDE | LONGITUDE, + lat2, lon2, t, t, t, t, t, t); + } + + /** + * See the documentation for Geodesic::Direct. + **********************************************************************/ + Math::real Direct(real lat1, real lon1, real azi1, real s12, + real& lat2, real& lon2, real& azi2) + const { + real t; + return GenDirect(lat1, lon1, azi1, false, s12, + LATITUDE | LONGITUDE | AZIMUTH, + lat2, lon2, azi2, t, t, t, t, t); + } + + /** + * See the documentation for Geodesic::Direct. + **********************************************************************/ + Math::real Direct(real lat1, real lon1, real azi1, real s12, + real& lat2, real& lon2, real& azi2, real& m12) + const { + real t; + return GenDirect(lat1, lon1, azi1, false, s12, + LATITUDE | LONGITUDE | AZIMUTH | REDUCEDLENGTH, + lat2, lon2, azi2, t, m12, t, t, t); + } + + /** + * See the documentation for Geodesic::Direct. + **********************************************************************/ + Math::real Direct(real lat1, real lon1, real azi1, real s12, + real& lat2, real& lon2, real& azi2, + real& M12, real& M21) + const { + real t; + return GenDirect(lat1, lon1, azi1, false, s12, + LATITUDE | LONGITUDE | AZIMUTH | GEODESICSCALE, + lat2, lon2, azi2, t, t, M12, M21, t); + } + + /** + * See the documentation for Geodesic::Direct. + **********************************************************************/ + Math::real Direct(real lat1, real lon1, real azi1, real s12, + real& lat2, real& lon2, real& azi2, + real& m12, real& M12, real& M21) + const { + real t; + return GenDirect(lat1, lon1, azi1, false, s12, + LATITUDE | LONGITUDE | AZIMUTH | + REDUCEDLENGTH | GEODESICSCALE, + lat2, lon2, azi2, t, m12, M12, M21, t); + } + ///@} + + /** \name Direct geodesic problem specified in terms of arc length. + **********************************************************************/ + ///@{ + /** + * Solve the direct geodesic problem where the length of the geodesic + * is specified in terms of arc length. + * + * @param[in] lat1 latitude of point 1 (degrees). + * @param[in] lon1 longitude of point 1 (degrees). + * @param[in] azi1 azimuth at point 1 (degrees). + * @param[in] a12 arc length between point 1 and point 2 (degrees); it can + * be negative. + * @param[out] lat2 latitude of point 2 (degrees). + * @param[out] lon2 longitude of point 2 (degrees). + * @param[out] azi2 (forward) azimuth at point 2 (degrees). + * @param[out] s12 distance between point 1 and point 2 (meters). + * @param[out] m12 reduced length of geodesic (meters). + * @param[out] M12 geodesic scale of point 2 relative to point 1 + * (dimensionless). + * @param[out] M21 geodesic scale of point 1 relative to point 2 + * (dimensionless). + * @param[out] S12 area under the geodesic (meters2). + * + * \e lat1 should be in the range [−90°, 90°]. The values of + * \e lon2 and \e azi2 returned are in the range [−180°, + * 180°]. + * + * If either point is at a pole, the azimuth is defined by keeping the + * longitude fixed, writing \e lat = ±(90° − ε), + * and taking the limit ε → 0+. An arc length greater that + * 180° signifies a geodesic which is not a shortest path. (For a + * prolate ellipsoid, an additional condition is necessary for a shortest + * path: the longitudinal extent must not exceed of 180°.) + * + * The following functions are overloaded versions of Geodesic::Direct + * which omit some of the output parameters. + **********************************************************************/ + void ArcDirect(real lat1, real lon1, real azi1, real a12, + real& lat2, real& lon2, real& azi2, real& s12, + real& m12, real& M12, real& M21, real& S12) + const { + GenDirect(lat1, lon1, azi1, true, a12, + LATITUDE | LONGITUDE | AZIMUTH | DISTANCE | + REDUCEDLENGTH | GEODESICSCALE | AREA, + lat2, lon2, azi2, s12, m12, M12, M21, S12); + } + + /** + * See the documentation for Geodesic::ArcDirect. + **********************************************************************/ + void ArcDirect(real lat1, real lon1, real azi1, real a12, + real& lat2, real& lon2) const { + real t; + GenDirect(lat1, lon1, azi1, true, a12, + LATITUDE | LONGITUDE, + lat2, lon2, t, t, t, t, t, t); + } + + /** + * See the documentation for Geodesic::ArcDirect. + **********************************************************************/ + void ArcDirect(real lat1, real lon1, real azi1, real a12, + real& lat2, real& lon2, real& azi2) const { + real t; + GenDirect(lat1, lon1, azi1, true, a12, + LATITUDE | LONGITUDE | AZIMUTH, + lat2, lon2, azi2, t, t, t, t, t); + } + + /** + * See the documentation for Geodesic::ArcDirect. + **********************************************************************/ + void ArcDirect(real lat1, real lon1, real azi1, real a12, + real& lat2, real& lon2, real& azi2, real& s12) + const { + real t; + GenDirect(lat1, lon1, azi1, true, a12, + LATITUDE | LONGITUDE | AZIMUTH | DISTANCE, + lat2, lon2, azi2, s12, t, t, t, t); + } + + /** + * See the documentation for Geodesic::ArcDirect. + **********************************************************************/ + void ArcDirect(real lat1, real lon1, real azi1, real a12, + real& lat2, real& lon2, real& azi2, + real& s12, real& m12) const { + real t; + GenDirect(lat1, lon1, azi1, true, a12, + LATITUDE | LONGITUDE | AZIMUTH | DISTANCE | + REDUCEDLENGTH, + lat2, lon2, azi2, s12, m12, t, t, t); + } + + /** + * See the documentation for Geodesic::ArcDirect. + **********************************************************************/ + void ArcDirect(real lat1, real lon1, real azi1, real a12, + real& lat2, real& lon2, real& azi2, real& s12, + real& M12, real& M21) const { + real t; + GenDirect(lat1, lon1, azi1, true, a12, + LATITUDE | LONGITUDE | AZIMUTH | DISTANCE | + GEODESICSCALE, + lat2, lon2, azi2, s12, t, M12, M21, t); + } + + /** + * See the documentation for Geodesic::ArcDirect. + **********************************************************************/ + void ArcDirect(real lat1, real lon1, real azi1, real a12, + real& lat2, real& lon2, real& azi2, real& s12, + real& m12, real& M12, real& M21) const { + real t; + GenDirect(lat1, lon1, azi1, true, a12, + LATITUDE | LONGITUDE | AZIMUTH | DISTANCE | + REDUCEDLENGTH | GEODESICSCALE, + lat2, lon2, azi2, s12, m12, M12, M21, t); + } + ///@} + + /** \name General version of the direct geodesic solution. + **********************************************************************/ + ///@{ + + /** + * The general direct geodesic problem. Geodesic::Direct and + * Geodesic::ArcDirect are defined in terms of this function. + * + * @param[in] lat1 latitude of point 1 (degrees). + * @param[in] lon1 longitude of point 1 (degrees). + * @param[in] azi1 azimuth at point 1 (degrees). + * @param[in] arcmode boolean flag determining the meaning of the \e + * s12_a12. + * @param[in] s12_a12 if \e arcmode is false, this is the distance between + * point 1 and point 2 (meters); otherwise it is the arc length between + * point 1 and point 2 (degrees); it can be negative. + * @param[in] outmask a bitor'ed combination of Geodesic::mask values + * specifying which of the following parameters should be set. + * @param[out] lat2 latitude of point 2 (degrees). + * @param[out] lon2 longitude of point 2 (degrees). + * @param[out] azi2 (forward) azimuth at point 2 (degrees). + * @param[out] s12 distance between point 1 and point 2 (meters). + * @param[out] m12 reduced length of geodesic (meters). + * @param[out] M12 geodesic scale of point 2 relative to point 1 + * (dimensionless). + * @param[out] M21 geodesic scale of point 1 relative to point 2 + * (dimensionless). + * @param[out] S12 area under the geodesic (meters2). + * @return \e a12 arc length of between point 1 and point 2 (degrees). + * + * The Geodesic::mask values possible for \e outmask are + * - \e outmask |= Geodesic::LATITUDE for the latitude \e lat2; + * - \e outmask |= Geodesic::LONGITUDE for the latitude \e lon2; + * - \e outmask |= Geodesic::AZIMUTH for the latitude \e azi2; + * - \e outmask |= Geodesic::DISTANCE for the distance \e s12; + * - \e outmask |= Geodesic::REDUCEDLENGTH for the reduced length \e + * m12; + * - \e outmask |= Geodesic::GEODESICSCALE for the geodesic scales \e + * M12 and \e M21; + * - \e outmask |= Geodesic::AREA for the area \e S12; + * - \e outmask |= Geodesic::ALL for all of the above; + * - \e outmask |= Geodesic::LONG_UNROLL to unroll \e lon2 instead of + * wrapping it into the range [−180°, 180°]. + * . + * The function value \e a12 is always computed and returned and this + * equals \e s12_a12 is \e arcmode is true. If \e outmask includes + * Geodesic::DISTANCE and \e arcmode is false, then \e s12 = \e s12_a12. + * It is not necessary to include Geodesic::DISTANCE_IN in \e outmask; this + * is automatically included is \e arcmode is false. + * + * With the Geodesic::LONG_UNROLL bit set, the quantity \e lon2 − \e + * lon1 indicates how many times and in what sense the geodesic encircles + * the ellipsoid. + **********************************************************************/ + Math::real GenDirect(real lat1, real lon1, real azi1, + bool arcmode, real s12_a12, unsigned outmask, + real& lat2, real& lon2, real& azi2, + real& s12, real& m12, real& M12, real& M21, + real& S12) const; + ///@} + + /** \name Inverse geodesic problem. + **********************************************************************/ + ///@{ + /** + * Solve the inverse geodesic problem. + * + * @param[in] lat1 latitude of point 1 (degrees). + * @param[in] lon1 longitude of point 1 (degrees). + * @param[in] lat2 latitude of point 2 (degrees). + * @param[in] lon2 longitude of point 2 (degrees). + * @param[out] s12 distance between point 1 and point 2 (meters). + * @param[out] azi1 azimuth at point 1 (degrees). + * @param[out] azi2 (forward) azimuth at point 2 (degrees). + * @param[out] m12 reduced length of geodesic (meters). + * @param[out] M12 geodesic scale of point 2 relative to point 1 + * (dimensionless). + * @param[out] M21 geodesic scale of point 1 relative to point 2 + * (dimensionless). + * @param[out] S12 area under the geodesic (meters2). + * @return \e a12 arc length of between point 1 and point 2 (degrees). + * + * \e lat1 and \e lat2 should be in the range [−90°, 90°]. + * The values of \e azi1 and \e azi2 returned are in the range + * [−180°, 180°]. + * + * If either point is at a pole, the azimuth is defined by keeping the + * longitude fixed, writing \e lat = ±(90° − ε), + * and taking the limit ε → 0+. + * + * The solution to the inverse problem is found using Newton's method. If + * this fails to converge (this is very unlikely in geodetic applications + * but does occur for very eccentric ellipsoids), then the bisection method + * is used to refine the solution. + * + * The following functions are overloaded versions of Geodesic::Inverse + * which omit some of the output parameters. Note, however, that the arc + * length is always computed and returned as the function value. + **********************************************************************/ + Math::real Inverse(real lat1, real lon1, real lat2, real lon2, + real& s12, real& azi1, real& azi2, real& m12, + real& M12, real& M21, real& S12) const { + return GenInverse(lat1, lon1, lat2, lon2, + DISTANCE | AZIMUTH | + REDUCEDLENGTH | GEODESICSCALE | AREA, + s12, azi1, azi2, m12, M12, M21, S12); + } + + /** + * See the documentation for Geodesic::Inverse. + **********************************************************************/ + Math::real Inverse(real lat1, real lon1, real lat2, real lon2, + real& s12) const { + real t; + return GenInverse(lat1, lon1, lat2, lon2, + DISTANCE, + s12, t, t, t, t, t, t); + } + + /** + * See the documentation for Geodesic::Inverse. + **********************************************************************/ + Math::real Inverse(real lat1, real lon1, real lat2, real lon2, + real& azi1, real& azi2) const { + real t; + return GenInverse(lat1, lon1, lat2, lon2, + AZIMUTH, + t, azi1, azi2, t, t, t, t); + } + + /** + * See the documentation for Geodesic::Inverse. + **********************************************************************/ + Math::real Inverse(real lat1, real lon1, real lat2, real lon2, + real& s12, real& azi1, real& azi2) + const { + real t; + return GenInverse(lat1, lon1, lat2, lon2, + DISTANCE | AZIMUTH, + s12, azi1, azi2, t, t, t, t); + } + + /** + * See the documentation for Geodesic::Inverse. + **********************************************************************/ + Math::real Inverse(real lat1, real lon1, real lat2, real lon2, + real& s12, real& azi1, real& azi2, real& m12) + const { + real t; + return GenInverse(lat1, lon1, lat2, lon2, + DISTANCE | AZIMUTH | REDUCEDLENGTH, + s12, azi1, azi2, m12, t, t, t); + } + + /** + * See the documentation for Geodesic::Inverse. + **********************************************************************/ + Math::real Inverse(real lat1, real lon1, real lat2, real lon2, + real& s12, real& azi1, real& azi2, + real& M12, real& M21) const { + real t; + return GenInverse(lat1, lon1, lat2, lon2, + DISTANCE | AZIMUTH | GEODESICSCALE, + s12, azi1, azi2, t, M12, M21, t); + } + + /** + * See the documentation for Geodesic::Inverse. + **********************************************************************/ + Math::real Inverse(real lat1, real lon1, real lat2, real lon2, + real& s12, real& azi1, real& azi2, real& m12, + real& M12, real& M21) const { + real t; + return GenInverse(lat1, lon1, lat2, lon2, + DISTANCE | AZIMUTH | + REDUCEDLENGTH | GEODESICSCALE, + s12, azi1, azi2, m12, M12, M21, t); + } + ///@} + + /** \name General version of inverse geodesic solution. + **********************************************************************/ + ///@{ + /** + * The general inverse geodesic calculation. Geodesic::Inverse is defined + * in terms of this function. + * + * @param[in] lat1 latitude of point 1 (degrees). + * @param[in] lon1 longitude of point 1 (degrees). + * @param[in] lat2 latitude of point 2 (degrees). + * @param[in] lon2 longitude of point 2 (degrees). + * @param[in] outmask a bitor'ed combination of Geodesic::mask values + * specifying which of the following parameters should be set. + * @param[out] s12 distance between point 1 and point 2 (meters). + * @param[out] azi1 azimuth at point 1 (degrees). + * @param[out] azi2 (forward) azimuth at point 2 (degrees). + * @param[out] m12 reduced length of geodesic (meters). + * @param[out] M12 geodesic scale of point 2 relative to point 1 + * (dimensionless). + * @param[out] M21 geodesic scale of point 1 relative to point 2 + * (dimensionless). + * @param[out] S12 area under the geodesic (meters2). + * @return \e a12 arc length of between point 1 and point 2 (degrees). + * + * The Geodesic::mask values possible for \e outmask are + * - \e outmask |= Geodesic::DISTANCE for the distance \e s12; + * - \e outmask |= Geodesic::AZIMUTH for the latitude \e azi2; + * - \e outmask |= Geodesic::REDUCEDLENGTH for the reduced length \e + * m12; + * - \e outmask |= Geodesic::GEODESICSCALE for the geodesic scales \e + * M12 and \e M21; + * - \e outmask |= Geodesic::AREA for the area \e S12; + * - \e outmask |= Geodesic::ALL for all of the above. + * . + * The arc length is always computed and returned as the function value. + **********************************************************************/ + Math::real GenInverse(real lat1, real lon1, real lat2, real lon2, + unsigned outmask, + real& s12, real& azi1, real& azi2, + real& m12, real& M12, real& M21, real& S12) const; + ///@} + + /** \name Interface to GeodesicLine. + **********************************************************************/ + ///@{ + + /** + * Set up to compute several points on a single geodesic. + * + * @param[in] lat1 latitude of point 1 (degrees). + * @param[in] lon1 longitude of point 1 (degrees). + * @param[in] azi1 azimuth at point 1 (degrees). + * @param[in] caps bitor'ed combination of Geodesic::mask values + * specifying the capabilities the GeodesicLine object should possess, + * i.e., which quantities can be returned in calls to + * GeodesicLine::Position. + * @return a GeodesicLine object. + * + * \e lat1 should be in the range [−90°, 90°]. + * + * The Geodesic::mask values are + * - \e caps |= Geodesic::LATITUDE for the latitude \e lat2; this is + * added automatically; + * - \e caps |= Geodesic::LONGITUDE for the latitude \e lon2; + * - \e caps |= Geodesic::AZIMUTH for the azimuth \e azi2; this is + * added automatically; + * - \e caps |= Geodesic::DISTANCE for the distance \e s12; + * - \e caps |= Geodesic::REDUCEDLENGTH for the reduced length \e m12; + * - \e caps |= Geodesic::GEODESICSCALE for the geodesic scales \e M12 + * and \e M21; + * - \e caps |= Geodesic::AREA for the area \e S12; + * - \e caps |= Geodesic::DISTANCE_IN permits the length of the + * geodesic to be given in terms of \e s12; without this capability the + * length can only be specified in terms of arc length; + * - \e caps |= Geodesic::ALL for all of the above. + * . + * The default value of \e caps is Geodesic::ALL. + * + * If the point is at a pole, the azimuth is defined by keeping \e lon1 + * fixed, writing \e lat1 = ±(90 − ε), and taking the + * limit ε → 0+. + **********************************************************************/ + GeodesicLine Line(real lat1, real lon1, real azi1, unsigned caps = ALL) + const; + + /** + * Define a GeodesicLine in terms of the inverse geodesic problem. + * + * @param[in] lat1 latitude of point 1 (degrees). + * @param[in] lon1 longitude of point 1 (degrees). + * @param[in] lat2 latitude of point 2 (degrees). + * @param[in] lon2 longitude of point 2 (degrees). + * @param[in] caps bitor'ed combination of Geodesic::mask values + * specifying the capabilities the GeodesicLine object should possess, + * i.e., which quantities can be returned in calls to + * GeodesicLine::Position. + * @return a GeodesicLine object. + * + * This function sets point 3 of the GeodesicLine to correspond to point 2 + * of the inverse geodesic problem. + * + * \e lat1 and \e lat2 should be in the range [−90°, 90°]. + **********************************************************************/ + GeodesicLine InverseLine(real lat1, real lon1, real lat2, real lon2, + unsigned caps = ALL) const; + + /** + * Define a GeodesicLine in terms of the direct geodesic problem specified + * in terms of distance. + * + * @param[in] lat1 latitude of point 1 (degrees). + * @param[in] lon1 longitude of point 1 (degrees). + * @param[in] azi1 azimuth at point 1 (degrees). + * @param[in] s12 distance between point 1 and point 2 (meters); it can be + * negative. + * @param[in] caps bitor'ed combination of Geodesic::mask values + * specifying the capabilities the GeodesicLine object should possess, + * i.e., which quantities can be returned in calls to + * GeodesicLine::Position. + * @return a GeodesicLine object. + * + * This function sets point 3 of the GeodesicLine to correspond to point 2 + * of the direct geodesic problem. + * + * \e lat1 should be in the range [−90°, 90°]. + **********************************************************************/ + GeodesicLine DirectLine(real lat1, real lon1, real azi1, real s12, + unsigned caps = ALL) const; + + /** + * Define a GeodesicLine in terms of the direct geodesic problem specified + * in terms of arc length. + * + * @param[in] lat1 latitude of point 1 (degrees). + * @param[in] lon1 longitude of point 1 (degrees). + * @param[in] azi1 azimuth at point 1 (degrees). + * @param[in] a12 arc length between point 1 and point 2 (degrees); it can + * be negative. + * @param[in] caps bitor'ed combination of Geodesic::mask values + * specifying the capabilities the GeodesicLine object should possess, + * i.e., which quantities can be returned in calls to + * GeodesicLine::Position. + * @return a GeodesicLine object. + * + * This function sets point 3 of the GeodesicLine to correspond to point 2 + * of the direct geodesic problem. + * + * \e lat1 should be in the range [−90°, 90°]. + **********************************************************************/ + GeodesicLine ArcDirectLine(real lat1, real lon1, real azi1, real a12, + unsigned caps = ALL) const; + + /** + * Define a GeodesicLine in terms of the direct geodesic problem specified + * in terms of either distance or arc length. + * + * @param[in] lat1 latitude of point 1 (degrees). + * @param[in] lon1 longitude of point 1 (degrees). + * @param[in] azi1 azimuth at point 1 (degrees). + * @param[in] arcmode boolean flag determining the meaning of the \e + * s12_a12. + * @param[in] s12_a12 if \e arcmode is false, this is the distance between + * point 1 and point 2 (meters); otherwise it is the arc length between + * point 1 and point 2 (degrees); it can be negative. + * @param[in] caps bitor'ed combination of Geodesic::mask values + * specifying the capabilities the GeodesicLine object should possess, + * i.e., which quantities can be returned in calls to + * GeodesicLine::Position. + * @return a GeodesicLine object. + * + * This function sets point 3 of the GeodesicLine to correspond to point 2 + * of the direct geodesic problem. + * + * \e lat1 should be in the range [−90°, 90°]. + **********************************************************************/ + GeodesicLine GenDirectLine(real lat1, real lon1, real azi1, + bool arcmode, real s12_a12, + unsigned caps = ALL) const; + ///@} + + /** \name Inspector functions. + **********************************************************************/ + ///@{ + + /** + * @return \e a the equatorial radius of the ellipsoid (meters). This is + * the value used in the constructor. + **********************************************************************/ + Math::real EquatorialRadius() const { return _a; } + + /** + * @return \e f the flattening of the ellipsoid. This is the + * value used in the constructor. + **********************************************************************/ + Math::real Flattening() const { return _f; } + + /** + * @return total area of ellipsoid in meters2. The area of a + * polygon encircling a pole can be found by adding + * Geodesic::EllipsoidArea()/2 to the sum of \e S12 for each side of the + * polygon. + **********************************************************************/ + Math::real EllipsoidArea() const + { return 4 * Math::pi() * _c2; } + + /** + * \deprecated An old name for EquatorialRadius(). + **********************************************************************/ + GEOGRAPHICLIB_DEPRECATED("Use EquatorialRadius()") + Math::real MajorRadius() const { return EquatorialRadius(); } + ///@} + + /** + * A global instantiation of Geodesic with the parameters for the WGS84 + * ellipsoid. + **********************************************************************/ + static const Geodesic& WGS84(); + + }; + +} // namespace GeographicLib + +#endif // GEOGRAPHICLIB_GEODESIC_HPP diff --git a/common/local_libs/GeographicLib/include/GeographicLib/GeodesicExact.hpp b/common/local_libs/GeographicLib/include/GeographicLib/GeodesicExact.hpp new file mode 100644 index 0000000000..abb8da6f31 --- /dev/null +++ b/common/local_libs/GeographicLib/include/GeographicLib/GeodesicExact.hpp @@ -0,0 +1,869 @@ +/** + * \file GeodesicExact.hpp + * \brief Header for GeographicLib::GeodesicExact class + * + * Copyright (c) Charles Karney (2012-2020) and licensed + * under the MIT/X11 License. For more information, see + * https://geographiclib.sourceforge.io/ + **********************************************************************/ + +#if !defined(GEOGRAPHICLIB_GEODESICEXACT_HPP) +#define GEOGRAPHICLIB_GEODESICEXACT_HPP 1 + +#include +#include + +#if !defined(GEOGRAPHICLIB_GEODESICEXACT_ORDER) +/** + * The order of the expansions used by GeodesicExact. + **********************************************************************/ +# define GEOGRAPHICLIB_GEODESICEXACT_ORDER 30 +#endif + +namespace GeographicLib { + + class GeodesicLineExact; + + /** + * \brief Exact geodesic calculations + * + * The equations for geodesics on an ellipsoid can be expressed in terms of + * incomplete elliptic integrals. The Geodesic class expands these integrals + * in a series in the flattening \e f and this provides an accurate solution + * for \e f ∈ [-0.01, 0.01]. The GeodesicExact class computes the + * ellitpic integrals directly and so provides a solution which is valid for + * all \e f. However, in practice, its use should be limited to about + * b/\e a ∈ [0.01, 100] or \e f ∈ [−99, 0.99]. + * + * For the WGS84 ellipsoid, these classes are 2--3 times \e slower than the + * series solution and 2--3 times \e less \e accurate (because it's less easy + * to control round-off errors with the elliptic integral formulation); i.e., + * the error is about 40 nm (40 nanometers) instead of 15 nm. However the + * error in the series solution scales as f7 while the + * error in the elliptic integral solution depends weakly on \e f. If the + * quarter meridian distance is 10000 km and the ratio b/\e a = 1 + * − \e f is varied then the approximate maximum error (expressed as a + * distance) is
+   *       1 - f  error (nm)
+   *       1/128     387
+   *       1/64      345
+   *       1/32      269
+   *       1/16      210
+   *       1/8       115
+   *       1/4        69
+   *       1/2        36
+   *         1        15
+   *         2        25
+   *         4        96
+   *         8       318
+   *        16       985
+   *        32      2352
+   *        64      6008
+   *       128     19024
+   * 
+ * + * The computation of the area in these classes is via a 30th order series. + * This gives accurate results for b/\e a ∈ [1/2, 2]; the + * accuracy is about 8 decimal digits for b/\e a ∈ [1/4, 4]. + * + * See \ref geodellip for the formulation. See the documentation on the + * Geodesic class for additional information on the geodesic problems. + * + * Example of use: + * \include example-GeodesicExact.cpp + * + * GeodSolve is a command-line utility + * providing access to the functionality of GeodesicExact and + * GeodesicLineExact (via the -E option). + **********************************************************************/ + + class GEOGRAPHICLIB_EXPORT GeodesicExact { + private: + typedef Math::real real; + friend class GeodesicLineExact; + static const int nC4_ = GEOGRAPHICLIB_GEODESICEXACT_ORDER; + static const int nC4x_ = (nC4_ * (nC4_ + 1)) / 2; + static const unsigned maxit1_ = 20; + unsigned maxit2_; + real tiny_, tol0_, tol1_, tol2_, tolb_, xthresh_; + + enum captype { + CAP_NONE = 0U, + CAP_E = 1U<<0, + // Skip 1U<<1 for compatibility with Geodesic (not required) + CAP_D = 1U<<2, + CAP_H = 1U<<3, + CAP_C4 = 1U<<4, + CAP_ALL = 0x1FU, + CAP_MASK = CAP_ALL, + OUT_ALL = 0x7F80U, + OUT_MASK = 0xFF80U, // Includes LONG_UNROLL + }; + + static real CosSeries(real sinx, real cosx, const real c[], int n); + static real Astroid(real x, real y); + + real _a, _f, _f1, _e2, _ep2, _n, _b, _c2, _etol2; + real _C4x[nC4x_]; + + void Lengths(const EllipticFunction& E, + real sig12, + real ssig1, real csig1, real dn1, + real ssig2, real csig2, real dn2, + real cbet1, real cbet2, unsigned outmask, + real& s12s, real& m12a, real& m0, + real& M12, real& M21) const; + real InverseStart(EllipticFunction& E, + real sbet1, real cbet1, real dn1, + real sbet2, real cbet2, real dn2, + real lam12, real slam12, real clam12, + real& salp1, real& calp1, + real& salp2, real& calp2, real& dnm) const; + real Lambda12(real sbet1, real cbet1, real dn1, + real sbet2, real cbet2, real dn2, + real salp1, real calp1, real slam120, real clam120, + real& salp2, real& calp2, real& sig12, + real& ssig1, real& csig1, real& ssig2, real& csig2, + EllipticFunction& E, + real& domg12, bool diffp, real& dlam12) const; + real GenInverse(real lat1, real lon1, real lat2, real lon2, + unsigned outmask, real& s12, + real& salp1, real& calp1, real& salp2, real& calp2, + real& m12, real& M12, real& M21, real& S12) const; + + // These are Maxima generated functions to provide series approximations to + // the integrals for the area. + void C4coeff(); + void C4f(real k2, real c[]) const; + // Large coefficients are split so that lo contains the low 52 bits and hi + // the rest. This choice avoids double rounding with doubles and higher + // precision types. float coefficients will suffer double rounding; + // however the accuracy is already lousy for floats. + static Math::real reale(long long hi, long long lo) { + using std::ldexp; + return ldexp(real(hi), 52) + lo; + } + + public: + + /** + * Bit masks for what calculations to do. These masks do double duty. + * They signify to the GeodesicLineExact::GeodesicLineExact constructor and + * to GeodesicExact::Line what capabilities should be included in the + * GeodesicLineExact object. They also specify which results to return in + * the general routines GeodesicExact::GenDirect and + * GeodesicExact::GenInverse routines. GeodesicLineExact::mask is a + * duplication of this enum. + **********************************************************************/ + enum mask { + /** + * No capabilities, no output. + * @hideinitializer + **********************************************************************/ + NONE = 0U, + /** + * Calculate latitude \e lat2. (It's not necessary to include this as a + * capability to GeodesicLineExact because this is included by default.) + * @hideinitializer + **********************************************************************/ + LATITUDE = 1U<<7 | CAP_NONE, + /** + * Calculate longitude \e lon2. + * @hideinitializer + **********************************************************************/ + LONGITUDE = 1U<<8 | CAP_H, + /** + * Calculate azimuths \e azi1 and \e azi2. (It's not necessary to + * include this as a capability to GeodesicLineExact because this is + * included by default.) + * @hideinitializer + **********************************************************************/ + AZIMUTH = 1U<<9 | CAP_NONE, + /** + * Calculate distance \e s12. + * @hideinitializer + **********************************************************************/ + DISTANCE = 1U<<10 | CAP_E, + /** + * Allow distance \e s12 to be used as input in the direct geodesic + * problem. + * @hideinitializer + **********************************************************************/ + DISTANCE_IN = 1U<<11 | CAP_E, + /** + * Calculate reduced length \e m12. + * @hideinitializer + **********************************************************************/ + REDUCEDLENGTH = 1U<<12 | CAP_D, + /** + * Calculate geodesic scales \e M12 and \e M21. + * @hideinitializer + **********************************************************************/ + GEODESICSCALE = 1U<<13 | CAP_D, + /** + * Calculate area \e S12. + * @hideinitializer + **********************************************************************/ + AREA = 1U<<14 | CAP_C4, + /** + * Unroll \e lon2 in the direct calculation. + * @hideinitializer + **********************************************************************/ + LONG_UNROLL = 1U<<15, + /** + * All capabilities, calculate everything. (LONG_UNROLL is not + * included in this mask.) + * @hideinitializer + **********************************************************************/ + ALL = OUT_ALL| CAP_ALL, + }; + + /** \name Constructor + **********************************************************************/ + ///@{ + /** + * Constructor for a ellipsoid with + * + * @param[in] a equatorial radius (meters). + * @param[in] f flattening of ellipsoid. Setting \e f = 0 gives a sphere. + * Negative \e f gives a prolate ellipsoid. + * @exception GeographicErr if \e a or (1 − \e f) \e a is not + * positive. + **********************************************************************/ + GeodesicExact(real a, real f); + ///@} + + /** \name Direct geodesic problem specified in terms of distance. + **********************************************************************/ + ///@{ + /** + * Perform the direct geodesic calculation where the length of the geodesic + * is specified in terms of distance. + * + * @param[in] lat1 latitude of point 1 (degrees). + * @param[in] lon1 longitude of point 1 (degrees). + * @param[in] azi1 azimuth at point 1 (degrees). + * @param[in] s12 distance between point 1 and point 2 (meters); it can be + * signed. + * @param[out] lat2 latitude of point 2 (degrees). + * @param[out] lon2 longitude of point 2 (degrees). + * @param[out] azi2 (forward) azimuth at point 2 (degrees). + * @param[out] m12 reduced length of geodesic (meters). + * @param[out] M12 geodesic scale of point 2 relative to point 1 + * (dimensionless). + * @param[out] M21 geodesic scale of point 1 relative to point 2 + * (dimensionless). + * @param[out] S12 area under the geodesic (meters2). + * @return \e a12 arc length of between point 1 and point 2 (degrees). + * + * \e lat1 should be in the range [−90°, 90°]. The values of + * \e lon2 and \e azi2 returned are in the range [−180°, + * 180°]. + * + * If either point is at a pole, the azimuth is defined by keeping the + * longitude fixed, writing \e lat = ±(90° − ε), + * and taking the limit ε → 0+. An arc length greater that + * 180° signifies a geodesic which is not a shortest path. (For a + * prolate ellipsoid, an additional condition is necessary for a shortest + * path: the longitudinal extent must not exceed of 180°.) + * + * The following functions are overloaded versions of GeodesicExact::Direct + * which omit some of the output parameters. Note, however, that the arc + * length is always computed and returned as the function value. + **********************************************************************/ + Math::real Direct(real lat1, real lon1, real azi1, real s12, + real& lat2, real& lon2, real& azi2, + real& m12, real& M12, real& M21, real& S12) + const { + real t; + return GenDirect(lat1, lon1, azi1, false, s12, + LATITUDE | LONGITUDE | AZIMUTH | + REDUCEDLENGTH | GEODESICSCALE | AREA, + lat2, lon2, azi2, t, m12, M12, M21, S12); + } + + /** + * See the documentation for GeodesicExact::Direct. + **********************************************************************/ + Math::real Direct(real lat1, real lon1, real azi1, real s12, + real& lat2, real& lon2) + const { + real t; + return GenDirect(lat1, lon1, azi1, false, s12, + LATITUDE | LONGITUDE, + lat2, lon2, t, t, t, t, t, t); + } + + /** + * See the documentation for GeodesicExact::Direct. + **********************************************************************/ + Math::real Direct(real lat1, real lon1, real azi1, real s12, + real& lat2, real& lon2, real& azi2) + const { + real t; + return GenDirect(lat1, lon1, azi1, false, s12, + LATITUDE | LONGITUDE | AZIMUTH, + lat2, lon2, azi2, t, t, t, t, t); + } + + /** + * See the documentation for GeodesicExact::Direct. + **********************************************************************/ + Math::real Direct(real lat1, real lon1, real azi1, real s12, + real& lat2, real& lon2, real& azi2, real& m12) + const { + real t; + return GenDirect(lat1, lon1, azi1, false, s12, + LATITUDE | LONGITUDE | AZIMUTH | REDUCEDLENGTH, + lat2, lon2, azi2, t, m12, t, t, t); + } + + /** + * See the documentation for GeodesicExact::Direct. + **********************************************************************/ + Math::real Direct(real lat1, real lon1, real azi1, real s12, + real& lat2, real& lon2, real& azi2, + real& M12, real& M21) + const { + real t; + return GenDirect(lat1, lon1, azi1, false, s12, + LATITUDE | LONGITUDE | AZIMUTH | GEODESICSCALE, + lat2, lon2, azi2, t, t, M12, M21, t); + } + + /** + * See the documentation for GeodesicExact::Direct. + **********************************************************************/ + Math::real Direct(real lat1, real lon1, real azi1, real s12, + real& lat2, real& lon2, real& azi2, + real& m12, real& M12, real& M21) + const { + real t; + return GenDirect(lat1, lon1, azi1, false, s12, + LATITUDE | LONGITUDE | AZIMUTH | + REDUCEDLENGTH | GEODESICSCALE, + lat2, lon2, azi2, t, m12, M12, M21, t); + } + ///@} + + /** \name Direct geodesic problem specified in terms of arc length. + **********************************************************************/ + ///@{ + /** + * Perform the direct geodesic calculation where the length of the geodesic + * is specified in terms of arc length. + * + * @param[in] lat1 latitude of point 1 (degrees). + * @param[in] lon1 longitude of point 1 (degrees). + * @param[in] azi1 azimuth at point 1 (degrees). + * @param[in] a12 arc length between point 1 and point 2 (degrees); it can + * be signed. + * @param[out] lat2 latitude of point 2 (degrees). + * @param[out] lon2 longitude of point 2 (degrees). + * @param[out] azi2 (forward) azimuth at point 2 (degrees). + * @param[out] s12 distance between point 1 and point 2 (meters). + * @param[out] m12 reduced length of geodesic (meters). + * @param[out] M12 geodesic scale of point 2 relative to point 1 + * (dimensionless). + * @param[out] M21 geodesic scale of point 1 relative to point 2 + * (dimensionless). + * @param[out] S12 area under the geodesic (meters2). + * + * \e lat1 should be in the range [−90°, 90°]. The values of + * \e lon2 and \e azi2 returned are in the range [−180°, + * 180°]. + * + * If either point is at a pole, the azimuth is defined by keeping the + * longitude fixed, writing \e lat = ±(90° − ε), + * and taking the limit ε → 0+. An arc length greater that + * 180° signifies a geodesic which is not a shortest path. (For a + * prolate ellipsoid, an additional condition is necessary for a shortest + * path: the longitudinal extent must not exceed of 180°.) + * + * The following functions are overloaded versions of GeodesicExact::Direct + * which omit some of the output parameters. + **********************************************************************/ + void ArcDirect(real lat1, real lon1, real azi1, real a12, + real& lat2, real& lon2, real& azi2, real& s12, + real& m12, real& M12, real& M21, real& S12) + const { + GenDirect(lat1, lon1, azi1, true, a12, + LATITUDE | LONGITUDE | AZIMUTH | DISTANCE | + REDUCEDLENGTH | GEODESICSCALE | AREA, + lat2, lon2, azi2, s12, m12, M12, M21, S12); + } + + /** + * See the documentation for GeodesicExact::ArcDirect. + **********************************************************************/ + void ArcDirect(real lat1, real lon1, real azi1, real a12, + real& lat2, real& lon2) const { + real t; + GenDirect(lat1, lon1, azi1, true, a12, + LATITUDE | LONGITUDE, + lat2, lon2, t, t, t, t, t, t); + } + + /** + * See the documentation for GeodesicExact::ArcDirect. + **********************************************************************/ + void ArcDirect(real lat1, real lon1, real azi1, real a12, + real& lat2, real& lon2, real& azi2) const { + real t; + GenDirect(lat1, lon1, azi1, true, a12, + LATITUDE | LONGITUDE | AZIMUTH, + lat2, lon2, azi2, t, t, t, t, t); + } + + /** + * See the documentation for GeodesicExact::ArcDirect. + **********************************************************************/ + void ArcDirect(real lat1, real lon1, real azi1, real a12, + real& lat2, real& lon2, real& azi2, real& s12) + const { + real t; + GenDirect(lat1, lon1, azi1, true, a12, + LATITUDE | LONGITUDE | AZIMUTH | DISTANCE, + lat2, lon2, azi2, s12, t, t, t, t); + } + + /** + * See the documentation for GeodesicExact::ArcDirect. + **********************************************************************/ + void ArcDirect(real lat1, real lon1, real azi1, real a12, + real& lat2, real& lon2, real& azi2, + real& s12, real& m12) const { + real t; + GenDirect(lat1, lon1, azi1, true, a12, + LATITUDE | LONGITUDE | AZIMUTH | DISTANCE | + REDUCEDLENGTH, + lat2, lon2, azi2, s12, m12, t, t, t); + } + + /** + * See the documentation for GeodesicExact::ArcDirect. + **********************************************************************/ + void ArcDirect(real lat1, real lon1, real azi1, real a12, + real& lat2, real& lon2, real& azi2, real& s12, + real& M12, real& M21) const { + real t; + GenDirect(lat1, lon1, azi1, true, a12, + LATITUDE | LONGITUDE | AZIMUTH | DISTANCE | + GEODESICSCALE, + lat2, lon2, azi2, s12, t, M12, M21, t); + } + + /** + * See the documentation for GeodesicExact::ArcDirect. + **********************************************************************/ + void ArcDirect(real lat1, real lon1, real azi1, real a12, + real& lat2, real& lon2, real& azi2, real& s12, + real& m12, real& M12, real& M21) const { + real t; + GenDirect(lat1, lon1, azi1, true, a12, + LATITUDE | LONGITUDE | AZIMUTH | DISTANCE | + REDUCEDLENGTH | GEODESICSCALE, + lat2, lon2, azi2, s12, m12, M12, M21, t); + } + ///@} + + /** \name General version of the direct geodesic solution. + **********************************************************************/ + ///@{ + + /** + * The general direct geodesic calculation. GeodesicExact::Direct and + * GeodesicExact::ArcDirect are defined in terms of this function. + * + * @param[in] lat1 latitude of point 1 (degrees). + * @param[in] lon1 longitude of point 1 (degrees). + * @param[in] azi1 azimuth at point 1 (degrees). + * @param[in] arcmode boolean flag determining the meaning of the second + * parameter. + * @param[in] s12_a12 if \e arcmode is false, this is the distance between + * point 1 and point 2 (meters); otherwise it is the arc length between + * point 1 and point 2 (degrees); it can be signed. + * @param[in] outmask a bitor'ed combination of GeodesicExact::mask values + * specifying which of the following parameters should be set. + * @param[out] lat2 latitude of point 2 (degrees). + * @param[out] lon2 longitude of point 2 (degrees). + * @param[out] azi2 (forward) azimuth at point 2 (degrees). + * @param[out] s12 distance between point 1 and point 2 (meters). + * @param[out] m12 reduced length of geodesic (meters). + * @param[out] M12 geodesic scale of point 2 relative to point 1 + * (dimensionless). + * @param[out] M21 geodesic scale of point 1 relative to point 2 + * (dimensionless). + * @param[out] S12 area under the geodesic (meters2). + * @return \e a12 arc length of between point 1 and point 2 (degrees). + * + * The GeodesicExact::mask values possible for \e outmask are + * - \e outmask |= GeodesicExact::LATITUDE for the latitude \e lat2; + * - \e outmask |= GeodesicExact::LONGITUDE for the latitude \e lon2; + * - \e outmask |= GeodesicExact::AZIMUTH for the latitude \e azi2; + * - \e outmask |= GeodesicExact::DISTANCE for the distance \e s12; + * - \e outmask |= GeodesicExact::REDUCEDLENGTH for the reduced length \e + * m12; + * - \e outmask |= GeodesicExact::GEODESICSCALE for the geodesic scales \e + * M12 and \e M21; + * - \e outmask |= GeodesicExact::AREA for the area \e S12; + * - \e outmask |= GeodesicExact::ALL for all of the above; + * - \e outmask |= GeodesicExact::LONG_UNROLL to unroll \e lon2 instead of + * wrapping it into the range [−180°, 180°]. + * . + * The function value \e a12 is always computed and returned and this + * equals \e s12_a12 is \e arcmode is true. If \e outmask includes + * GeodesicExact::DISTANCE and \e arcmode is false, then \e s12 = \e + * s12_a12. It is not necessary to include GeodesicExact::DISTANCE_IN in + * \e outmask; this is automatically included is \e arcmode is false. + * + * With the GeodesicExact::LONG_UNROLL bit set, the quantity \e lon2 + * − \e lon1 indicates how many times and in what sense the geodesic + * encircles the ellipsoid. + **********************************************************************/ + Math::real GenDirect(real lat1, real lon1, real azi1, + bool arcmode, real s12_a12, unsigned outmask, + real& lat2, real& lon2, real& azi2, + real& s12, real& m12, real& M12, real& M21, + real& S12) const; + ///@} + + /** \name Inverse geodesic problem. + **********************************************************************/ + ///@{ + /** + * Perform the inverse geodesic calculation. + * + * @param[in] lat1 latitude of point 1 (degrees). + * @param[in] lon1 longitude of point 1 (degrees). + * @param[in] lat2 latitude of point 2 (degrees). + * @param[in] lon2 longitude of point 2 (degrees). + * @param[out] s12 distance between point 1 and point 2 (meters). + * @param[out] azi1 azimuth at point 1 (degrees). + * @param[out] azi2 (forward) azimuth at point 2 (degrees). + * @param[out] m12 reduced length of geodesic (meters). + * @param[out] M12 geodesic scale of point 2 relative to point 1 + * (dimensionless). + * @param[out] M21 geodesic scale of point 1 relative to point 2 + * (dimensionless). + * @param[out] S12 area under the geodesic (meters2). + * @return \e a12 arc length of between point 1 and point 2 (degrees). + * + * \e lat1 and \e lat2 should be in the range [−90°, 90°]. + * The values of \e azi1 and \e azi2 returned are in the range + * [−180°, 180°]. + * + * If either point is at a pole, the azimuth is defined by keeping the + * longitude fixed, writing \e lat = ±(90° − ε), + * and taking the limit ε → 0+. + * + * The following functions are overloaded versions of + * GeodesicExact::Inverse which omit some of the output parameters. Note, + * however, that the arc length is always computed and returned as the + * function value. + **********************************************************************/ + Math::real Inverse(real lat1, real lon1, real lat2, real lon2, + real& s12, real& azi1, real& azi2, real& m12, + real& M12, real& M21, real& S12) const { + return GenInverse(lat1, lon1, lat2, lon2, + DISTANCE | AZIMUTH | + REDUCEDLENGTH | GEODESICSCALE | AREA, + s12, azi1, azi2, m12, M12, M21, S12); + } + + /** + * See the documentation for GeodesicExact::Inverse. + **********************************************************************/ + Math::real Inverse(real lat1, real lon1, real lat2, real lon2, + real& s12) const { + real t; + return GenInverse(lat1, lon1, lat2, lon2, + DISTANCE, + s12, t, t, t, t, t, t); + } + + /** + * See the documentation for GeodesicExact::Inverse. + **********************************************************************/ + Math::real Inverse(real lat1, real lon1, real lat2, real lon2, + real& azi1, real& azi2) const { + real t; + return GenInverse(lat1, lon1, lat2, lon2, + AZIMUTH, + t, azi1, azi2, t, t, t, t); + } + + /** + * See the documentation for GeodesicExact::Inverse. + **********************************************************************/ + Math::real Inverse(real lat1, real lon1, real lat2, real lon2, + real& s12, real& azi1, real& azi2) + const { + real t; + return GenInverse(lat1, lon1, lat2, lon2, + DISTANCE | AZIMUTH, + s12, azi1, azi2, t, t, t, t); + } + + /** + * See the documentation for GeodesicExact::Inverse. + **********************************************************************/ + Math::real Inverse(real lat1, real lon1, real lat2, real lon2, + real& s12, real& azi1, real& azi2, real& m12) + const { + real t; + return GenInverse(lat1, lon1, lat2, lon2, + DISTANCE | AZIMUTH | REDUCEDLENGTH, + s12, azi1, azi2, m12, t, t, t); + } + + /** + * See the documentation for GeodesicExact::Inverse. + **********************************************************************/ + Math::real Inverse(real lat1, real lon1, real lat2, real lon2, + real& s12, real& azi1, real& azi2, + real& M12, real& M21) const { + real t; + return GenInverse(lat1, lon1, lat2, lon2, + DISTANCE | AZIMUTH | GEODESICSCALE, + s12, azi1, azi2, t, M12, M21, t); + } + + /** + * See the documentation for GeodesicExact::Inverse. + **********************************************************************/ + Math::real Inverse(real lat1, real lon1, real lat2, real lon2, + real& s12, real& azi1, real& azi2, real& m12, + real& M12, real& M21) const { + real t; + return GenInverse(lat1, lon1, lat2, lon2, + DISTANCE | AZIMUTH | + REDUCEDLENGTH | GEODESICSCALE, + s12, azi1, azi2, m12, M12, M21, t); + } + ///@} + + /** \name General version of inverse geodesic solution. + **********************************************************************/ + ///@{ + /** + * The general inverse geodesic calculation. GeodesicExact::Inverse is + * defined in terms of this function. + * + * @param[in] lat1 latitude of point 1 (degrees). + * @param[in] lon1 longitude of point 1 (degrees). + * @param[in] lat2 latitude of point 2 (degrees). + * @param[in] lon2 longitude of point 2 (degrees). + * @param[in] outmask a bitor'ed combination of GeodesicExact::mask values + * specifying which of the following parameters should be set. + * @param[out] s12 distance between point 1 and point 2 (meters). + * @param[out] azi1 azimuth at point 1 (degrees). + * @param[out] azi2 (forward) azimuth at point 2 (degrees). + * @param[out] m12 reduced length of geodesic (meters). + * @param[out] M12 geodesic scale of point 2 relative to point 1 + * (dimensionless). + * @param[out] M21 geodesic scale of point 1 relative to point 2 + * (dimensionless). + * @param[out] S12 area under the geodesic (meters2). + * @return \e a12 arc length of between point 1 and point 2 (degrees). + * + * The GeodesicExact::mask values possible for \e outmask are + * - \e outmask |= GeodesicExact::DISTANCE for the distance \e s12; + * - \e outmask |= GeodesicExact::AZIMUTH for the latitude \e azi2; + * - \e outmask |= GeodesicExact::REDUCEDLENGTH for the reduced length \e + * m12; + * - \e outmask |= GeodesicExact::GEODESICSCALE for the geodesic scales \e + * M12 and \e M21; + * - \e outmask |= GeodesicExact::AREA for the area \e S12; + * - \e outmask |= GeodesicExact::ALL for all of the above. + * . + * The arc length is always computed and returned as the function value. + **********************************************************************/ + Math::real GenInverse(real lat1, real lon1, real lat2, real lon2, + unsigned outmask, + real& s12, real& azi1, real& azi2, + real& m12, real& M12, real& M21, real& S12) const; + ///@} + + /** \name Interface to GeodesicLineExact. + **********************************************************************/ + ///@{ + + /** + * Set up to compute several points on a single geodesic. + * + * @param[in] lat1 latitude of point 1 (degrees). + * @param[in] lon1 longitude of point 1 (degrees). + * @param[in] azi1 azimuth at point 1 (degrees). + * @param[in] caps bitor'ed combination of GeodesicExact::mask values + * specifying the capabilities the GeodesicLineExact object should + * possess, i.e., which quantities can be returned in calls to + * GeodesicLineExact::Position. + * @return a GeodesicLineExact object. + * + * \e lat1 should be in the range [−90°, 90°]. + * + * The GeodesicExact::mask values are + * - \e caps |= GeodesicExact::LATITUDE for the latitude \e lat2; this is + * added automatically; + * - \e caps |= GeodesicExact::LONGITUDE for the latitude \e lon2; + * - \e caps |= GeodesicExact::AZIMUTH for the azimuth \e azi2; this is + * added automatically; + * - \e caps |= GeodesicExact::DISTANCE for the distance \e s12; + * - \e caps |= GeodesicExact::REDUCEDLENGTH for the reduced length \e m12; + * - \e caps |= GeodesicExact::GEODESICSCALE for the geodesic scales \e M12 + * and \e M21; + * - \e caps |= GeodesicExact::AREA for the area \e S12; + * - \e caps |= GeodesicExact::DISTANCE_IN permits the length of the + * geodesic to be given in terms of \e s12; without this capability the + * length can only be specified in terms of arc length; + * - \e caps |= GeodesicExact::ALL for all of the above. + * . + * The default value of \e caps is GeodesicExact::ALL which turns on all + * the capabilities. + * + * If the point is at a pole, the azimuth is defined by keeping \e lon1 + * fixed, writing \e lat1 = ±(90 − ε), and taking the + * limit ε → 0+. + **********************************************************************/ + GeodesicLineExact Line(real lat1, real lon1, real azi1, + unsigned caps = ALL) const; + + /** + * Define a GeodesicLineExact in terms of the inverse geodesic problem. + * + * @param[in] lat1 latitude of point 1 (degrees). + * @param[in] lon1 longitude of point 1 (degrees). + * @param[in] lat2 latitude of point 2 (degrees). + * @param[in] lon2 longitude of point 2 (degrees). + * @param[in] caps bitor'ed combination of GeodesicExact::mask values + * specifying the capabilities the GeodesicLineExact object should + * possess, i.e., which quantities can be returned in calls to + * GeodesicLineExact::Position. + * @return a GeodesicLineExact object. + * + * This function sets point 3 of the GeodesicLineExact to correspond to + * point 2 of the inverse geodesic problem. + * + * \e lat1 and \e lat2 should be in the range [−90°, 90°]. + **********************************************************************/ + GeodesicLineExact InverseLine(real lat1, real lon1, real lat2, real lon2, + unsigned caps = ALL) const; + + /** + * Define a GeodesicLineExact in terms of the direct geodesic problem + * specified in terms of distance. + * + * @param[in] lat1 latitude of point 1 (degrees). + * @param[in] lon1 longitude of point 1 (degrees). + * @param[in] azi1 azimuth at point 1 (degrees). + * @param[in] s12 distance between point 1 and point 2 (meters); it can be + * negative. + * @param[in] caps bitor'ed combination of GeodesicExact::mask values + * specifying the capabilities the GeodesicLineExact object should + * possess, i.e., which quantities can be returned in calls to + * GeodesicLineExact::Position. + * @return a GeodesicLineExact object. + * + * This function sets point 3 of the GeodesicLineExact to correspond to + * point 2 of the direct geodesic problem. + * + * \e lat1 should be in the range [−90°, 90°]. + **********************************************************************/ + GeodesicLineExact DirectLine(real lat1, real lon1, real azi1, real s12, + unsigned caps = ALL) const; + + /** + * Define a GeodesicLineExact in terms of the direct geodesic problem + * specified in terms of arc length. + * + * @param[in] lat1 latitude of point 1 (degrees). + * @param[in] lon1 longitude of point 1 (degrees). + * @param[in] azi1 azimuth at point 1 (degrees). + * @param[in] a12 arc length between point 1 and point 2 (degrees); it can + * be negative. + * @param[in] caps bitor'ed combination of GeodesicExact::mask values + * specifying the capabilities the GeodesicLineExact object should + * possess, i.e., which quantities can be returned in calls to + * GeodesicLineExact::Position. + * @return a GeodesicLineExact object. + * + * This function sets point 3 of the GeodesicLineExact to correspond to + * point 2 of the direct geodesic problem. + * + * \e lat1 should be in the range [−90°, 90°]. + **********************************************************************/ + GeodesicLineExact ArcDirectLine(real lat1, real lon1, real azi1, real a12, + unsigned caps = ALL) const; + + /** + * Define a GeodesicLineExact in terms of the direct geodesic problem + * specified in terms of either distance or arc length. + * + * @param[in] lat1 latitude of point 1 (degrees). + * @param[in] lon1 longitude of point 1 (degrees). + * @param[in] azi1 azimuth at point 1 (degrees). + * @param[in] arcmode boolean flag determining the meaning of the \e + * s12_a12. + * @param[in] s12_a12 if \e arcmode is false, this is the distance between + * point 1 and point 2 (meters); otherwise it is the arc length between + * point 1 and point 2 (degrees); it can be negative. + * @param[in] caps bitor'ed combination of GeodesicExact::mask values + * specifying the capabilities the GeodesicLineExact object should + * possess, i.e., which quantities can be returned in calls to + * GeodesicLineExact::Position. + * @return a GeodesicLineExact object. + * + * This function sets point 3 of the GeodesicLineExact to correspond to + * point 2 of the direct geodesic problem. + * + * \e lat1 should be in the range [−90°, 90°]. + **********************************************************************/ + GeodesicLineExact GenDirectLine(real lat1, real lon1, real azi1, + bool arcmode, real s12_a12, + unsigned caps = ALL) const; + ///@} + + /** \name Inspector functions. + **********************************************************************/ + ///@{ + + /** + * @return \e a the equatorial radius of the ellipsoid (meters). This is + * the value used in the constructor. + **********************************************************************/ + Math::real EquatorialRadius() const { return _a; } + + /** + * @return \e f the flattening of the ellipsoid. This is the + * value used in the constructor. + **********************************************************************/ + Math::real Flattening() const { return _f; } + + /** + * @return total area of ellipsoid in meters2. The area of a + * polygon encircling a pole can be found by adding + * GeodesicExact::EllipsoidArea()/2 to the sum of \e S12 for each side of + * the polygon. + **********************************************************************/ + Math::real EllipsoidArea() const + { return 4 * Math::pi() * _c2; } + + /** + * \deprecated An old name for EquatorialRadius(). + **********************************************************************/ + GEOGRAPHICLIB_DEPRECATED("Use EquatorialRadius()") + Math::real MajorRadius() const { return EquatorialRadius(); } + ///@} + + /** + * A global instantiation of GeodesicExact with the parameters for the + * WGS84 ellipsoid. + **********************************************************************/ + static const GeodesicExact& WGS84(); + + }; + +} // namespace GeographicLib + +#endif // GEOGRAPHICLIB_GEODESICEXACT_HPP diff --git a/common/local_libs/GeographicLib/include/GeographicLib/GeodesicLine.hpp b/common/local_libs/GeographicLib/include/GeographicLib/GeodesicLine.hpp new file mode 100644 index 0000000000..f3120963e3 --- /dev/null +++ b/common/local_libs/GeographicLib/include/GeographicLib/GeodesicLine.hpp @@ -0,0 +1,708 @@ +/** + * \file GeodesicLine.hpp + * \brief Header for GeographicLib::GeodesicLine class + * + * Copyright (c) Charles Karney (2009-2020) and licensed + * under the MIT/X11 License. For more information, see + * https://geographiclib.sourceforge.io/ + **********************************************************************/ + +#if !defined(GEOGRAPHICLIB_GEODESICLINE_HPP) +#define GEOGRAPHICLIB_GEODESICLINE_HPP 1 + +#include +#include + +namespace GeographicLib { + + /** + * \brief A geodesic line + * + * GeodesicLine facilitates the determination of a series of points on a + * single geodesic. The starting point (\e lat1, \e lon1) and the azimuth \e + * azi1 are specified in the constructor; alternatively, the Geodesic::Line + * method can be used to create a GeodesicLine. GeodesicLine.Position + * returns the location of point 2 a distance \e s12 along the geodesic. In + * addition, GeodesicLine.ArcPosition gives the position of point 2 an arc + * length \e a12 along the geodesic. + * + * You can register the position of a reference point 3 a distance (arc + * length), \e s13 (\e a13) along the geodesic with the + * GeodesicLine.SetDistance (GeodesicLine.SetArc) functions. Points a + * fractional distance along the line can be found by providing, for example, + * 0.5 * Distance() as an argument to GeodesicLine.Position. The + * Geodesic::InverseLine or Geodesic::DirectLine methods return GeodesicLine + * objects with point 3 set to the point 2 of the corresponding geodesic + * problem. GeodesicLine objects created with the public constructor or with + * Geodesic::Line have \e s13 and \e a13 set to NaNs. + * + * The default copy constructor and assignment operators work with this + * class. Similarly, a vector can be used to hold GeodesicLine objects. + * + * The calculations are accurate to better than 15 nm (15 nanometers). See + * Sec. 9 of + * arXiv:1102.1215v1 for + * details. The algorithms used by this class are based on series expansions + * using the flattening \e f as a small parameter. These are only accurate + * for |f| < 0.02; however reasonably accurate results will be + * obtained for |f| < 0.2. For very eccentric ellipsoids, use + * GeodesicLineExact instead. + * + * The algorithms are described in + * - C. F. F. Karney, + * + * Algorithms for geodesics, + * J. Geodesy 87, 43--55 (2013); + * DOI: + * 10.1007/s00190-012-0578-z; + * addenda: + * + * geod-addenda.html. + * . + * For more information on geodesics see \ref geodesic. + * + * Example of use: + * \include example-GeodesicLine.cpp + * + * GeodSolve is a command-line utility + * providing access to the functionality of Geodesic and GeodesicLine. + **********************************************************************/ + + class GEOGRAPHICLIB_EXPORT GeodesicLine { + private: + typedef Math::real real; + friend class Geodesic; + static const int nC1_ = Geodesic::nC1_; + static const int nC1p_ = Geodesic::nC1p_; + static const int nC2_ = Geodesic::nC2_; + static const int nC3_ = Geodesic::nC3_; + static const int nC4_ = Geodesic::nC4_; + + real tiny_; + real _lat1, _lon1, _azi1; + real _a, _f, _b, _c2, _f1, _salp0, _calp0, _k2, + _salp1, _calp1, _ssig1, _csig1, _dn1, _stau1, _ctau1, _somg1, _comg1, + _A1m1, _A2m1, _A3c, _B11, _B21, _B31, _A4, _B41; + real _a13, _s13; + // index zero elements of _C1a, _C1pa, _C2a, _C3a are unused + real _C1a[nC1_ + 1], _C1pa[nC1p_ + 1], _C2a[nC2_ + 1], _C3a[nC3_], + _C4a[nC4_]; // all the elements of _C4a are used + unsigned _caps; + + void LineInit(const Geodesic& g, + real lat1, real lon1, + real azi1, real salp1, real calp1, + unsigned caps); + GeodesicLine(const Geodesic& g, + real lat1, real lon1, + real azi1, real salp1, real calp1, + unsigned caps, bool arcmode, real s13_a13); + + enum captype { + CAP_NONE = Geodesic::CAP_NONE, + CAP_C1 = Geodesic::CAP_C1, + CAP_C1p = Geodesic::CAP_C1p, + CAP_C2 = Geodesic::CAP_C2, + CAP_C3 = Geodesic::CAP_C3, + CAP_C4 = Geodesic::CAP_C4, + CAP_ALL = Geodesic::CAP_ALL, + CAP_MASK = Geodesic::CAP_MASK, + OUT_ALL = Geodesic::OUT_ALL, + OUT_MASK = Geodesic::OUT_MASK, + }; + public: + + /** + * Bit masks for what calculations to do. They signify to the + * GeodesicLine::GeodesicLine constructor and to Geodesic::Line what + * capabilities should be included in the GeodesicLine object. This is + * merely a duplication of Geodesic::mask. + **********************************************************************/ + enum mask { + /** + * No capabilities, no output. + * @hideinitializer + **********************************************************************/ + NONE = Geodesic::NONE, + /** + * Calculate latitude \e lat2. (It's not necessary to include this as a + * capability to GeodesicLine because this is included by default.) + * @hideinitializer + **********************************************************************/ + LATITUDE = Geodesic::LATITUDE, + /** + * Calculate longitude \e lon2. + * @hideinitializer + **********************************************************************/ + LONGITUDE = Geodesic::LONGITUDE, + /** + * Calculate azimuths \e azi1 and \e azi2. (It's not necessary to + * include this as a capability to GeodesicLine because this is included + * by default.) + * @hideinitializer + **********************************************************************/ + AZIMUTH = Geodesic::AZIMUTH, + /** + * Calculate distance \e s12. + * @hideinitializer + **********************************************************************/ + DISTANCE = Geodesic::DISTANCE, + /** + * Allow distance \e s12 to be used as input in the direct geodesic + * problem. + * @hideinitializer + **********************************************************************/ + DISTANCE_IN = Geodesic::DISTANCE_IN, + /** + * Calculate reduced length \e m12. + * @hideinitializer + **********************************************************************/ + REDUCEDLENGTH = Geodesic::REDUCEDLENGTH, + /** + * Calculate geodesic scales \e M12 and \e M21. + * @hideinitializer + **********************************************************************/ + GEODESICSCALE = Geodesic::GEODESICSCALE, + /** + * Calculate area \e S12. + * @hideinitializer + **********************************************************************/ + AREA = Geodesic::AREA, + /** + * Unroll \e lon2 in the direct calculation. + * @hideinitializer + **********************************************************************/ + LONG_UNROLL = Geodesic::LONG_UNROLL, + /** + * All capabilities, calculate everything. (LONG_UNROLL is not + * included in this mask.) + * @hideinitializer + **********************************************************************/ + ALL = Geodesic::ALL, + }; + + /** \name Constructors + **********************************************************************/ + ///@{ + + /** + * Constructor for a geodesic line staring at latitude \e lat1, longitude + * \e lon1, and azimuth \e azi1 (all in degrees). + * + * @param[in] g A Geodesic object used to compute the necessary information + * about the GeodesicLine. + * @param[in] lat1 latitude of point 1 (degrees). + * @param[in] lon1 longitude of point 1 (degrees). + * @param[in] azi1 azimuth at point 1 (degrees). + * @param[in] caps bitor'ed combination of GeodesicLine::mask values + * specifying the capabilities the GeodesicLine object should possess, + * i.e., which quantities can be returned in calls to + * GeodesicLine::Position. + * + * \e lat1 should be in the range [−90°, 90°]. + * + * The GeodesicLine::mask values are + * - \e caps |= GeodesicLine::LATITUDE for the latitude \e lat2; this is + * added automatically; + * - \e caps |= GeodesicLine::LONGITUDE for the latitude \e lon2; + * - \e caps |= GeodesicLine::AZIMUTH for the latitude \e azi2; this is + * added automatically; + * - \e caps |= GeodesicLine::DISTANCE for the distance \e s12; + * - \e caps |= GeodesicLine::REDUCEDLENGTH for the reduced length \e m12; + * - \e caps |= GeodesicLine::GEODESICSCALE for the geodesic scales \e M12 + * and \e M21; + * - \e caps |= GeodesicLine::AREA for the area \e S12; + * - \e caps |= GeodesicLine::DISTANCE_IN permits the length of the + * geodesic to be given in terms of \e s12; without this capability the + * length can only be specified in terms of arc length; + * - \e caps |= GeodesicLine::ALL for all of the above. + * . + * The default value of \e caps is GeodesicLine::ALL. + * + * If the point is at a pole, the azimuth is defined by keeping \e lon1 + * fixed, writing \e lat1 = ±(90° − ε), and taking + * the limit ε → 0+. + **********************************************************************/ + GeodesicLine(const Geodesic& g, real lat1, real lon1, real azi1, + unsigned caps = ALL); + + /** + * A default constructor. If GeodesicLine::Position is called on the + * resulting object, it returns immediately (without doing any + * calculations). The object can be set with a call to Geodesic::Line. + * Use Init() to test whether object is still in this uninitialized state. + **********************************************************************/ + GeodesicLine() : _caps(0U) {} + ///@} + + /** \name Position in terms of distance + **********************************************************************/ + ///@{ + + /** + * Compute the position of point 2 which is a distance \e s12 (meters) from + * point 1. + * + * @param[in] s12 distance from point 1 to point 2 (meters); it can be + * negative. + * @param[out] lat2 latitude of point 2 (degrees). + * @param[out] lon2 longitude of point 2 (degrees); requires that the + * GeodesicLine object was constructed with \e caps |= + * GeodesicLine::LONGITUDE. + * @param[out] azi2 (forward) azimuth at point 2 (degrees). + * @param[out] m12 reduced length of geodesic (meters); requires that the + * GeodesicLine object was constructed with \e caps |= + * GeodesicLine::REDUCEDLENGTH. + * @param[out] M12 geodesic scale of point 2 relative to point 1 + * (dimensionless); requires that the GeodesicLine object was constructed + * with \e caps |= GeodesicLine::GEODESICSCALE. + * @param[out] M21 geodesic scale of point 1 relative to point 2 + * (dimensionless); requires that the GeodesicLine object was constructed + * with \e caps |= GeodesicLine::GEODESICSCALE. + * @param[out] S12 area under the geodesic (meters2); requires + * that the GeodesicLine object was constructed with \e caps |= + * GeodesicLine::AREA. + * @return \e a12 arc length from point 1 to point 2 (degrees). + * + * The values of \e lon2 and \e azi2 returned are in the range + * [−180°, 180°]. + * + * The GeodesicLine object \e must have been constructed with \e caps |= + * GeodesicLine::DISTANCE_IN; otherwise Math::NaN() is returned and no + * parameters are set. Requesting a value which the GeodesicLine object is + * not capable of computing is not an error; the corresponding argument + * will not be altered. + * + * The following functions are overloaded versions of + * GeodesicLine::Position which omit some of the output parameters. Note, + * however, that the arc length is always computed and returned as the + * function value. + **********************************************************************/ + Math::real Position(real s12, + real& lat2, real& lon2, real& azi2, + real& m12, real& M12, real& M21, + real& S12) const { + real t; + return GenPosition(false, s12, + LATITUDE | LONGITUDE | AZIMUTH | + REDUCEDLENGTH | GEODESICSCALE | AREA, + lat2, lon2, azi2, t, m12, M12, M21, S12); + } + + /** + * See the documentation for GeodesicLine::Position. + **********************************************************************/ + Math::real Position(real s12, real& lat2, real& lon2) const { + real t; + return GenPosition(false, s12, + LATITUDE | LONGITUDE, + lat2, lon2, t, t, t, t, t, t); + } + + /** + * See the documentation for GeodesicLine::Position. + **********************************************************************/ + Math::real Position(real s12, real& lat2, real& lon2, + real& azi2) const { + real t; + return GenPosition(false, s12, + LATITUDE | LONGITUDE | AZIMUTH, + lat2, lon2, azi2, t, t, t, t, t); + } + + /** + * See the documentation for GeodesicLine::Position. + **********************************************************************/ + Math::real Position(real s12, real& lat2, real& lon2, + real& azi2, real& m12) const { + real t; + return GenPosition(false, s12, + LATITUDE | LONGITUDE | + AZIMUTH | REDUCEDLENGTH, + lat2, lon2, azi2, t, m12, t, t, t); + } + + /** + * See the documentation for GeodesicLine::Position. + **********************************************************************/ + Math::real Position(real s12, real& lat2, real& lon2, + real& azi2, real& M12, real& M21) + const { + real t; + return GenPosition(false, s12, + LATITUDE | LONGITUDE | + AZIMUTH | GEODESICSCALE, + lat2, lon2, azi2, t, t, M12, M21, t); + } + + /** + * See the documentation for GeodesicLine::Position. + **********************************************************************/ + Math::real Position(real s12, + real& lat2, real& lon2, real& azi2, + real& m12, real& M12, real& M21) + const { + real t; + return GenPosition(false, s12, + LATITUDE | LONGITUDE | AZIMUTH | + REDUCEDLENGTH | GEODESICSCALE, + lat2, lon2, azi2, t, m12, M12, M21, t); + } + ///@} + + /** \name Position in terms of arc length + **********************************************************************/ + ///@{ + + /** + * Compute the position of point 2 which is an arc length \e a12 (degrees) + * from point 1. + * + * @param[in] a12 arc length from point 1 to point 2 (degrees); it can + * be negative. + * @param[out] lat2 latitude of point 2 (degrees). + * @param[out] lon2 longitude of point 2 (degrees); requires that the + * GeodesicLine object was constructed with \e caps |= + * GeodesicLine::LONGITUDE. + * @param[out] azi2 (forward) azimuth at point 2 (degrees). + * @param[out] s12 distance from point 1 to point 2 (meters); requires + * that the GeodesicLine object was constructed with \e caps |= + * GeodesicLine::DISTANCE. + * @param[out] m12 reduced length of geodesic (meters); requires that the + * GeodesicLine object was constructed with \e caps |= + * GeodesicLine::REDUCEDLENGTH. + * @param[out] M12 geodesic scale of point 2 relative to point 1 + * (dimensionless); requires that the GeodesicLine object was constructed + * with \e caps |= GeodesicLine::GEODESICSCALE. + * @param[out] M21 geodesic scale of point 1 relative to point 2 + * (dimensionless); requires that the GeodesicLine object was constructed + * with \e caps |= GeodesicLine::GEODESICSCALE. + * @param[out] S12 area under the geodesic (meters2); requires + * that the GeodesicLine object was constructed with \e caps |= + * GeodesicLine::AREA. + * + * The values of \e lon2 and \e azi2 returned are in the range + * [−180°, 180°]. + * + * Requesting a value which the GeodesicLine object is not capable of + * computing is not an error; the corresponding argument will not be + * altered. + * + * The following functions are overloaded versions of + * GeodesicLine::ArcPosition which omit some of the output parameters. + **********************************************************************/ + void ArcPosition(real a12, real& lat2, real& lon2, real& azi2, + real& s12, real& m12, real& M12, real& M21, + real& S12) const { + GenPosition(true, a12, + LATITUDE | LONGITUDE | AZIMUTH | DISTANCE | + REDUCEDLENGTH | GEODESICSCALE | AREA, + lat2, lon2, azi2, s12, m12, M12, M21, S12); + } + + /** + * See the documentation for GeodesicLine::ArcPosition. + **********************************************************************/ + void ArcPosition(real a12, real& lat2, real& lon2) + const { + real t; + GenPosition(true, a12, + LATITUDE | LONGITUDE, + lat2, lon2, t, t, t, t, t, t); + } + + /** + * See the documentation for GeodesicLine::ArcPosition. + **********************************************************************/ + void ArcPosition(real a12, + real& lat2, real& lon2, real& azi2) + const { + real t; + GenPosition(true, a12, + LATITUDE | LONGITUDE | AZIMUTH, + lat2, lon2, azi2, t, t, t, t, t); + } + + /** + * See the documentation for GeodesicLine::ArcPosition. + **********************************************************************/ + void ArcPosition(real a12, real& lat2, real& lon2, real& azi2, + real& s12) const { + real t; + GenPosition(true, a12, + LATITUDE | LONGITUDE | AZIMUTH | DISTANCE, + lat2, lon2, azi2, s12, t, t, t, t); + } + + /** + * See the documentation for GeodesicLine::ArcPosition. + **********************************************************************/ + void ArcPosition(real a12, real& lat2, real& lon2, real& azi2, + real& s12, real& m12) const { + real t; + GenPosition(true, a12, + LATITUDE | LONGITUDE | AZIMUTH | + DISTANCE | REDUCEDLENGTH, + lat2, lon2, azi2, s12, m12, t, t, t); + } + + /** + * See the documentation for GeodesicLine::ArcPosition. + **********************************************************************/ + void ArcPosition(real a12, real& lat2, real& lon2, real& azi2, + real& s12, real& M12, real& M21) + const { + real t; + GenPosition(true, a12, + LATITUDE | LONGITUDE | AZIMUTH | + DISTANCE | GEODESICSCALE, + lat2, lon2, azi2, s12, t, M12, M21, t); + } + + /** + * See the documentation for GeodesicLine::ArcPosition. + **********************************************************************/ + void ArcPosition(real a12, real& lat2, real& lon2, real& azi2, + real& s12, real& m12, real& M12, real& M21) + const { + real t; + GenPosition(true, a12, + LATITUDE | LONGITUDE | AZIMUTH | + DISTANCE | REDUCEDLENGTH | GEODESICSCALE, + lat2, lon2, azi2, s12, m12, M12, M21, t); + } + ///@} + + /** \name The general position function. + **********************************************************************/ + ///@{ + + /** + * The general position function. GeodesicLine::Position and + * GeodesicLine::ArcPosition are defined in terms of this function. + * + * @param[in] arcmode boolean flag determining the meaning of the second + * parameter; if \e arcmode is false, then the GeodesicLine object must + * have been constructed with \e caps |= GeodesicLine::DISTANCE_IN. + * @param[in] s12_a12 if \e arcmode is false, this is the distance between + * point 1 and point 2 (meters); otherwise it is the arc length between + * point 1 and point 2 (degrees); it can be negative. + * @param[in] outmask a bitor'ed combination of GeodesicLine::mask values + * specifying which of the following parameters should be set. + * @param[out] lat2 latitude of point 2 (degrees). + * @param[out] lon2 longitude of point 2 (degrees); requires that the + * GeodesicLine object was constructed with \e caps |= + * GeodesicLine::LONGITUDE. + * @param[out] azi2 (forward) azimuth at point 2 (degrees). + * @param[out] s12 distance from point 1 to point 2 (meters); requires + * that the GeodesicLine object was constructed with \e caps |= + * GeodesicLine::DISTANCE. + * @param[out] m12 reduced length of geodesic (meters); requires that the + * GeodesicLine object was constructed with \e caps |= + * GeodesicLine::REDUCEDLENGTH. + * @param[out] M12 geodesic scale of point 2 relative to point 1 + * (dimensionless); requires that the GeodesicLine object was constructed + * with \e caps |= GeodesicLine::GEODESICSCALE. + * @param[out] M21 geodesic scale of point 1 relative to point 2 + * (dimensionless); requires that the GeodesicLine object was constructed + * with \e caps |= GeodesicLine::GEODESICSCALE. + * @param[out] S12 area under the geodesic (meters2); requires + * that the GeodesicLine object was constructed with \e caps |= + * GeodesicLine::AREA. + * @return \e a12 arc length from point 1 to point 2 (degrees). + * + * The GeodesicLine::mask values possible for \e outmask are + * - \e outmask |= GeodesicLine::LATITUDE for the latitude \e lat2; + * - \e outmask |= GeodesicLine::LONGITUDE for the latitude \e lon2; + * - \e outmask |= GeodesicLine::AZIMUTH for the latitude \e azi2; + * - \e outmask |= GeodesicLine::DISTANCE for the distance \e s12; + * - \e outmask |= GeodesicLine::REDUCEDLENGTH for the reduced length \e + * m12; + * - \e outmask |= GeodesicLine::GEODESICSCALE for the geodesic scales \e + * M12 and \e M21; + * - \e outmask |= GeodesicLine::AREA for the area \e S12; + * - \e outmask |= GeodesicLine::ALL for all of the above; + * - \e outmask |= GeodesicLine::LONG_UNROLL to unroll \e lon2 instead of + * reducing it into the range [−180°, 180°]. + * . + * Requesting a value which the GeodesicLine object is not capable of + * computing is not an error; the corresponding argument will not be + * altered. Note, however, that the arc length is always computed and + * returned as the function value. + * + * With the GeodesicLine::LONG_UNROLL bit set, the quantity \e lon2 − + * \e lon1 indicates how many times and in what sense the geodesic + * encircles the ellipsoid. + **********************************************************************/ + Math::real GenPosition(bool arcmode, real s12_a12, unsigned outmask, + real& lat2, real& lon2, real& azi2, + real& s12, real& m12, real& M12, real& M21, + real& S12) const; + ///@} + + /** \name Setting point 3 + **********************************************************************/ + ///@{ + + /** + * Specify position of point 3 in terms of distance. + * + * @param[in] s13 the distance from point 1 to point 3 (meters); it + * can be negative. + * + * This is only useful if the GeodesicLine object has been constructed + * with \e caps |= GeodesicLine::DISTANCE_IN. + **********************************************************************/ + void SetDistance(real s13); + + /** + * Specify position of point 3 in terms of arc length. + * + * @param[in] a13 the arc length from point 1 to point 3 (degrees); it + * can be negative. + * + * The distance \e s13 is only set if the GeodesicLine object has been + * constructed with \e caps |= GeodesicLine::DISTANCE. + **********************************************************************/ + void SetArc(real a13); + + /** + * Specify position of point 3 in terms of either distance or arc length. + * + * @param[in] arcmode boolean flag determining the meaning of the second + * parameter; if \e arcmode is false, then the GeodesicLine object must + * have been constructed with \e caps |= GeodesicLine::DISTANCE_IN. + * @param[in] s13_a13 if \e arcmode is false, this is the distance from + * point 1 to point 3 (meters); otherwise it is the arc length from + * point 1 to point 3 (degrees); it can be negative. + **********************************************************************/ + void GenSetDistance(bool arcmode, real s13_a13); + ///@} + + /** \name Inspector functions + **********************************************************************/ + ///@{ + + /** + * @return true if the object has been initialized. + **********************************************************************/ + bool Init() const { return _caps != 0U; } + + /** + * @return \e lat1 the latitude of point 1 (degrees). + **********************************************************************/ + Math::real Latitude() const + { return Init() ? _lat1 : Math::NaN(); } + + /** + * @return \e lon1 the longitude of point 1 (degrees). + **********************************************************************/ + Math::real Longitude() const + { return Init() ? _lon1 : Math::NaN(); } + + /** + * @return \e azi1 the azimuth (degrees) of the geodesic line at point 1. + **********************************************************************/ + Math::real Azimuth() const + { return Init() ? _azi1 : Math::NaN(); } + + /** + * The sine and cosine of \e azi1. + * + * @param[out] sazi1 the sine of \e azi1. + * @param[out] cazi1 the cosine of \e azi1. + **********************************************************************/ + void Azimuth(real& sazi1, real& cazi1) const + { if (Init()) { sazi1 = _salp1; cazi1 = _calp1; } } + + /** + * @return \e azi0 the azimuth (degrees) of the geodesic line as it crosses + * the equator in a northward direction. + * + * The result lies in [−90°, 90°]. + **********************************************************************/ + Math::real EquatorialAzimuth() const + { return Init() ? Math::atan2d(_salp0, _calp0) : Math::NaN(); } + + /** + * The sine and cosine of \e azi0. + * + * @param[out] sazi0 the sine of \e azi0. + * @param[out] cazi0 the cosine of \e azi0. + **********************************************************************/ + void EquatorialAzimuth(real& sazi0, real& cazi0) const + { if (Init()) { sazi0 = _salp0; cazi0 = _calp0; } } + + /** + * @return \e a1 the arc length (degrees) between the northward equatorial + * crossing and point 1. + * + * The result lies in (−180°, 180°]. + **********************************************************************/ + Math::real EquatorialArc() const { + return Init() ? Math::atan2d(_ssig1, _csig1) : Math::NaN(); + } + + /** + * @return \e a the equatorial radius of the ellipsoid (meters). This is + * the value inherited from the Geodesic object used in the constructor. + **********************************************************************/ + Math::real EquatorialRadius() const + { return Init() ? _a : Math::NaN(); } + + /** + * @return \e f the flattening of the ellipsoid. This is the value + * inherited from the Geodesic object used in the constructor. + **********************************************************************/ + Math::real Flattening() const + { return Init() ? _f : Math::NaN(); } + + /** + * @return \e caps the computational capabilities that this object was + * constructed with. LATITUDE and AZIMUTH are always included. + **********************************************************************/ + unsigned Capabilities() const { return _caps; } + + /** + * Test what capabilities are available. + * + * @param[in] testcaps a set of bitor'ed GeodesicLine::mask values. + * @return true if the GeodesicLine object has all these capabilities. + **********************************************************************/ + bool Capabilities(unsigned testcaps) const { + testcaps &= OUT_ALL; + return (_caps & testcaps) == testcaps; + } + + /** + * The distance or arc length to point 3. + * + * @param[in] arcmode boolean flag determining the meaning of returned + * value. + * @return \e s13 if \e arcmode is false; \e a13 if \e arcmode is true. + **********************************************************************/ + Math::real GenDistance(bool arcmode) const + { return Init() ? (arcmode ? _a13 : _s13) : Math::NaN(); } + + /** + * @return \e s13, the distance to point 3 (meters). + **********************************************************************/ + Math::real Distance() const { return GenDistance(false); } + + /** + * @return \e a13, the arc length to point 3 (degrees). + **********************************************************************/ + Math::real Arc() const { return GenDistance(true); } + + /** + * \deprecated An old name for EquatorialRadius(). + **********************************************************************/ + GEOGRAPHICLIB_DEPRECATED("Use EquatorialRadius()") + Math::real MajorRadius() const { return EquatorialRadius(); } + ///@} + + }; + +} // namespace GeographicLib + +#endif // GEOGRAPHICLIB_GEODESICLINE_HPP diff --git a/common/local_libs/GeographicLib/include/GeographicLib/GeodesicLineExact.hpp b/common/local_libs/GeographicLib/include/GeographicLib/GeodesicLineExact.hpp new file mode 100644 index 0000000000..5fdf6350e6 --- /dev/null +++ b/common/local_libs/GeographicLib/include/GeographicLib/GeodesicLineExact.hpp @@ -0,0 +1,673 @@ +/** + * \file GeodesicLineExact.hpp + * \brief Header for GeographicLib::GeodesicLineExact class + * + * Copyright (c) Charles Karney (2012-2020) and licensed + * under the MIT/X11 License. For more information, see + * https://geographiclib.sourceforge.io/ + **********************************************************************/ + +#if !defined(GEOGRAPHICLIB_GEODESICLINEEXACT_HPP) +#define GEOGRAPHICLIB_GEODESICLINEEXACT_HPP 1 + +#include +#include +#include + +namespace GeographicLib { + + /** + * \brief An exact geodesic line + * + * GeodesicLineExact facilitates the determination of a series of points on a + * single geodesic. This is a companion to the GeodesicExact class. For + * additional information on this class see the documentation on the + * GeodesicLine class. + * + * Example of use: + * \include example-GeodesicLineExact.cpp + * + * GeodSolve is a command-line utility + * providing access to the functionality of GeodesicExact and + * GeodesicLineExact (via the -E option). + **********************************************************************/ + + class GEOGRAPHICLIB_EXPORT GeodesicLineExact { + private: + typedef Math::real real; + friend class GeodesicExact; + static const int nC4_ = GeodesicExact::nC4_; + + real tiny_; + real _lat1, _lon1, _azi1; + real _a, _f, _b, _c2, _f1, _e2, _salp0, _calp0, _k2, + _salp1, _calp1, _ssig1, _csig1, _dn1, _stau1, _ctau1, + _somg1, _comg1, _cchi1, + _A4, _B41, _E0, _D0, _H0, _E1, _D1, _H1; + real _a13, _s13; + real _C4a[nC4_]; // all the elements of _C4a are used + EllipticFunction _E; + unsigned _caps; + + void LineInit(const GeodesicExact& g, + real lat1, real lon1, + real azi1, real salp1, real calp1, + unsigned caps); + GeodesicLineExact(const GeodesicExact& g, + real lat1, real lon1, + real azi1, real salp1, real calp1, + unsigned caps, bool arcmode, real s13_a13); + + enum captype { + CAP_NONE = GeodesicExact::CAP_NONE, + CAP_E = GeodesicExact::CAP_E, + CAP_D = GeodesicExact::CAP_D, + CAP_H = GeodesicExact::CAP_H, + CAP_C4 = GeodesicExact::CAP_C4, + CAP_ALL = GeodesicExact::CAP_ALL, + CAP_MASK = GeodesicExact::CAP_MASK, + OUT_ALL = GeodesicExact::OUT_ALL, + OUT_MASK = GeodesicExact::OUT_MASK, + }; + public: + + /** + * Bit masks for what calculations to do. They signify to the + * GeodesicLineExact::GeodesicLineExact constructor and to + * GeodesicExact::Line what capabilities should be included in the + * GeodesicLineExact object. This is merely a duplication of + * GeodesicExact::mask. + **********************************************************************/ + enum mask { + /** + * No capabilities, no output. + * @hideinitializer + **********************************************************************/ + NONE = GeodesicExact::NONE, + /** + * Calculate latitude \e lat2. (It's not necessary to include this as a + * capability to GeodesicLineExact because this is included by default.) + * @hideinitializer + **********************************************************************/ + LATITUDE = GeodesicExact::LATITUDE, + /** + * Calculate longitude \e lon2. + * @hideinitializer + **********************************************************************/ + LONGITUDE = GeodesicExact::LONGITUDE, + /** + * Calculate azimuths \e azi1 and \e azi2. (It's not necessary to + * include this as a capability to GeodesicLineExact because this is + * included by default.) + * @hideinitializer + **********************************************************************/ + AZIMUTH = GeodesicExact::AZIMUTH, + /** + * Calculate distance \e s12. + * @hideinitializer + **********************************************************************/ + DISTANCE = GeodesicExact::DISTANCE, + /** + * Allow distance \e s12 to be used as input in the direct geodesic + * problem. + * @hideinitializer + **********************************************************************/ + DISTANCE_IN = GeodesicExact::DISTANCE_IN, + /** + * Calculate reduced length \e m12. + * @hideinitializer + **********************************************************************/ + REDUCEDLENGTH = GeodesicExact::REDUCEDLENGTH, + /** + * Calculate geodesic scales \e M12 and \e M21. + * @hideinitializer + **********************************************************************/ + GEODESICSCALE = GeodesicExact::GEODESICSCALE, + /** + * Calculate area \e S12. + * @hideinitializer + **********************************************************************/ + AREA = GeodesicExact::AREA, + /** + * Unroll \e lon2 in the direct calculation. + * @hideinitializer + **********************************************************************/ + LONG_UNROLL = GeodesicExact::LONG_UNROLL, + /** + * All capabilities, calculate everything. (LONG_UNROLL is not + * included in this mask.) + * @hideinitializer + **********************************************************************/ + ALL = GeodesicExact::ALL, + }; + + /** \name Constructors + **********************************************************************/ + ///@{ + + /** + * Constructor for a geodesic line staring at latitude \e lat1, longitude + * \e lon1, and azimuth \e azi1 (all in degrees). + * + * @param[in] g A GeodesicExact object used to compute the necessary + * information about the GeodesicLineExact. + * @param[in] lat1 latitude of point 1 (degrees). + * @param[in] lon1 longitude of point 1 (degrees). + * @param[in] azi1 azimuth at point 1 (degrees). + * @param[in] caps bitor'ed combination of GeodesicLineExact::mask values + * specifying the capabilities the GeodesicLineExact object should + * possess, i.e., which quantities can be returned in calls to + * GeodesicLine::Position. + * + * \e lat1 should be in the range [−90°, 90°]. + * + * The GeodesicLineExact::mask values are + * - \e caps |= GeodesicLineExact::LATITUDE for the latitude \e lat2; this + * is added automatically; + * - \e caps |= GeodesicLineExact::LONGITUDE for the latitude \e lon2; + * - \e caps |= GeodesicLineExact::AZIMUTH for the latitude \e azi2; this + * is added automatically; + * - \e caps |= GeodesicLineExact::DISTANCE for the distance \e s12; + * - \e caps |= GeodesicLineExact::REDUCEDLENGTH for the reduced length \e + m12; + * - \e caps |= GeodesicLineExact::GEODESICSCALE for the geodesic scales \e + * M12 and \e M21; + * - \e caps |= GeodesicLineExact::AREA for the area \e S12; + * - \e caps |= GeodesicLineExact::DISTANCE_IN permits the length of the + * geodesic to be given in terms of \e s12; without this capability the + * length can only be specified in terms of arc length; + * - \e caps |= GeodesicLineExact::ALL for all of the above. + * . + * The default value of \e caps is GeodesicLineExact::ALL. + * + * If the point is at a pole, the azimuth is defined by keeping \e lon1 + * fixed, writing \e lat1 = ±(90° − ε), and taking + * the limit ε → 0+. + **********************************************************************/ + GeodesicLineExact(const GeodesicExact& g, real lat1, real lon1, real azi1, + unsigned caps = ALL); + + /** + * A default constructor. If GeodesicLineExact::Position is called on the + * resulting object, it returns immediately (without doing any + * calculations). The object can be set with a call to + * GeodesicExact::Line. Use Init() to test whether object is still in this + * uninitialized state. + **********************************************************************/ + GeodesicLineExact() : _caps(0U) {} + ///@} + + /** \name Position in terms of distance + **********************************************************************/ + ///@{ + + /** + * Compute the position of point 2 which is a distance \e s12 (meters) + * from point 1. + * + * @param[in] s12 distance from point 1 to point 2 (meters); it can be + * signed. + * @param[out] lat2 latitude of point 2 (degrees). + * @param[out] lon2 longitude of point 2 (degrees); requires that the + * GeodesicLineExact object was constructed with \e caps |= + * GeodesicLineExact::LONGITUDE. + * @param[out] azi2 (forward) azimuth at point 2 (degrees). + * @param[out] m12 reduced length of geodesic (meters); requires that the + * GeodesicLineExact object was constructed with \e caps |= + * GeodesicLineExact::REDUCEDLENGTH. + * @param[out] M12 geodesic scale of point 2 relative to point 1 + * (dimensionless); requires that the GeodesicLineExact object was + * constructed with \e caps |= GeodesicLineExact::GEODESICSCALE. + * @param[out] M21 geodesic scale of point 1 relative to point 2 + * (dimensionless); requires that the GeodesicLineExact object was + * constructed with \e caps |= GeodesicLineExact::GEODESICSCALE. + * @param[out] S12 area under the geodesic (meters2); requires + * that the GeodesicLineExact object was constructed with \e caps |= + * GeodesicLineExact::AREA. + * @return \e a12 arc length from point 1 to point 2 (degrees). + * + * The values of \e lon2 and \e azi2 returned are in the range + * [−180°, 180°]. + * + * The GeodesicLineExact object \e must have been constructed with \e caps + * |= GeodesicLineExact::DISTANCE_IN; otherwise Math::NaN() is returned and + * no parameters are set. Requesting a value which the GeodesicLineExact + * object is not capable of computing is not an error; the corresponding + * argument will not be altered. + * + * The following functions are overloaded versions of + * GeodesicLineExact::Position which omit some of the output parameters. + * Note, however, that the arc length is always computed and returned as + * the function value. + **********************************************************************/ + Math::real Position(real s12, + real& lat2, real& lon2, real& azi2, + real& m12, real& M12, real& M21, + real& S12) const { + real t; + return GenPosition(false, s12, + LATITUDE | LONGITUDE | AZIMUTH | + REDUCEDLENGTH | GEODESICSCALE | AREA, + lat2, lon2, azi2, t, m12, M12, M21, S12); + } + + /** + * See the documentation for GeodesicLineExact::Position. + **********************************************************************/ + Math::real Position(real s12, real& lat2, real& lon2) const { + real t; + return GenPosition(false, s12, + LATITUDE | LONGITUDE, + lat2, lon2, t, t, t, t, t, t); + } + + /** + * See the documentation for GeodesicLineExact::Position. + **********************************************************************/ + Math::real Position(real s12, real& lat2, real& lon2, + real& azi2) const { + real t; + return GenPosition(false, s12, + LATITUDE | LONGITUDE | AZIMUTH, + lat2, lon2, azi2, t, t, t, t, t); + } + + /** + * See the documentation for GeodesicLineExact::Position. + **********************************************************************/ + Math::real Position(real s12, real& lat2, real& lon2, + real& azi2, real& m12) const { + real t; + return GenPosition(false, s12, + LATITUDE | LONGITUDE | + AZIMUTH | REDUCEDLENGTH, + lat2, lon2, azi2, t, m12, t, t, t); + } + + /** + * See the documentation for GeodesicLineExact::Position. + **********************************************************************/ + Math::real Position(real s12, real& lat2, real& lon2, + real& azi2, real& M12, real& M21) + const { + real t; + return GenPosition(false, s12, + LATITUDE | LONGITUDE | + AZIMUTH | GEODESICSCALE, + lat2, lon2, azi2, t, t, M12, M21, t); + } + + /** + * See the documentation for GeodesicLineExact::Position. + **********************************************************************/ + Math::real Position(real s12, + real& lat2, real& lon2, real& azi2, + real& m12, real& M12, real& M21) + const { + real t; + return GenPosition(false, s12, + LATITUDE | LONGITUDE | AZIMUTH | + REDUCEDLENGTH | GEODESICSCALE, + lat2, lon2, azi2, t, m12, M12, M21, t); + } + ///@} + + /** \name Position in terms of arc length + **********************************************************************/ + ///@{ + + /** + * Compute the position of point 2 which is an arc length \e a12 (degrees) + * from point 1. + * + * @param[in] a12 arc length from point 1 to point 2 (degrees); it can + * be signed. + * @param[out] lat2 latitude of point 2 (degrees). + * @param[out] lon2 longitude of point 2 (degrees); requires that the + * GeodesicLineExact object was constructed with \e caps |= + * GeodesicLineExact::LONGITUDE. + * @param[out] azi2 (forward) azimuth at point 2 (degrees). + * @param[out] s12 distance from point 1 to point 2 (meters); requires + * that the GeodesicLineExact object was constructed with \e caps |= + * GeodesicLineExact::DISTANCE. + * @param[out] m12 reduced length of geodesic (meters); requires that the + * GeodesicLineExact object was constructed with \e caps |= + * GeodesicLineExact::REDUCEDLENGTH. + * @param[out] M12 geodesic scale of point 2 relative to point 1 + * (dimensionless); requires that the GeodesicLineExact object was + * constructed with \e caps |= GeodesicLineExact::GEODESICSCALE. + * @param[out] M21 geodesic scale of point 1 relative to point 2 + * (dimensionless); requires that the GeodesicLineExact object was + * constructed with \e caps |= GeodesicLineExact::GEODESICSCALE. + * @param[out] S12 area under the geodesic (meters2); requires + * that the GeodesicLineExact object was constructed with \e caps |= + * GeodesicLineExact::AREA. + * + * The values of \e lon2 and \e azi2 returned are in the range + * [−180°, 180°]. + * + * Requesting a value which the GeodesicLineExact object is not capable of + * computing is not an error; the corresponding argument will not be + * altered. + * + * The following functions are overloaded versions of + * GeodesicLineExact::ArcPosition which omit some of the output parameters. + **********************************************************************/ + void ArcPosition(real a12, real& lat2, real& lon2, real& azi2, + real& s12, real& m12, real& M12, real& M21, + real& S12) const { + GenPosition(true, a12, + LATITUDE | LONGITUDE | AZIMUTH | DISTANCE | + REDUCEDLENGTH | GEODESICSCALE | AREA, + lat2, lon2, azi2, s12, m12, M12, M21, S12); + } + + /** + * See the documentation for GeodesicLineExact::ArcPosition. + **********************************************************************/ + void ArcPosition(real a12, real& lat2, real& lon2) + const { + real t; + GenPosition(true, a12, + LATITUDE | LONGITUDE, + lat2, lon2, t, t, t, t, t, t); + } + + /** + * See the documentation for GeodesicLineExact::ArcPosition. + **********************************************************************/ + void ArcPosition(real a12, + real& lat2, real& lon2, real& azi2) + const { + real t; + GenPosition(true, a12, + LATITUDE | LONGITUDE | AZIMUTH, + lat2, lon2, azi2, t, t, t, t, t); + } + + /** + * See the documentation for GeodesicLineExact::ArcPosition. + **********************************************************************/ + void ArcPosition(real a12, real& lat2, real& lon2, real& azi2, + real& s12) const { + real t; + GenPosition(true, a12, + LATITUDE | LONGITUDE | AZIMUTH | DISTANCE, + lat2, lon2, azi2, s12, t, t, t, t); + } + + /** + * See the documentation for GeodesicLineExact::ArcPosition. + **********************************************************************/ + void ArcPosition(real a12, real& lat2, real& lon2, real& azi2, + real& s12, real& m12) const { + real t; + GenPosition(true, a12, + LATITUDE | LONGITUDE | AZIMUTH | + DISTANCE | REDUCEDLENGTH, + lat2, lon2, azi2, s12, m12, t, t, t); + } + + /** + * See the documentation for GeodesicLineExact::ArcPosition. + **********************************************************************/ + void ArcPosition(real a12, real& lat2, real& lon2, real& azi2, + real& s12, real& M12, real& M21) + const { + real t; + GenPosition(true, a12, + LATITUDE | LONGITUDE | AZIMUTH | + DISTANCE | GEODESICSCALE, + lat2, lon2, azi2, s12, t, M12, M21, t); + } + + /** + * See the documentation for GeodesicLineExact::ArcPosition. + **********************************************************************/ + void ArcPosition(real a12, real& lat2, real& lon2, real& azi2, + real& s12, real& m12, real& M12, real& M21) + const { + real t; + GenPosition(true, a12, + LATITUDE | LONGITUDE | AZIMUTH | + DISTANCE | REDUCEDLENGTH | GEODESICSCALE, + lat2, lon2, azi2, s12, m12, M12, M21, t); + } + ///@} + + /** \name The general position function. + **********************************************************************/ + ///@{ + + /** + * The general position function. GeodesicLineExact::Position and + * GeodesicLineExact::ArcPosition are defined in terms of this function. + * + * @param[in] arcmode boolean flag determining the meaning of the second + * parameter; if \e arcmode is false, then the GeodesicLineExact object + * must have been constructed with \e caps |= + * GeodesicLineExact::DISTANCE_IN. + * @param[in] s12_a12 if \e arcmode is false, this is the distance between + * point 1 and point 2 (meters); otherwise it is the arc length between + * point 1 and point 2 (degrees); it can be signed. + * @param[in] outmask a bitor'ed combination of GeodesicLineExact::mask + * values specifying which of the following parameters should be set. + * @param[out] lat2 latitude of point 2 (degrees). + * @param[out] lon2 longitude of point 2 (degrees); requires that the + * GeodesicLineExact object was constructed with \e caps |= + * GeodesicLineExact::LONGITUDE. + * @param[out] azi2 (forward) azimuth at point 2 (degrees). + * @param[out] s12 distance from point 1 to point 2 (meters); requires + * that the GeodesicLineExact object was constructed with \e caps |= + * GeodesicLineExact::DISTANCE. + * @param[out] m12 reduced length of geodesic (meters); requires that the + * GeodesicLineExact object was constructed with \e caps |= + * GeodesicLineExact::REDUCEDLENGTH. + * @param[out] M12 geodesic scale of point 2 relative to point 1 + * (dimensionless); requires that the GeodesicLineExact object was + * constructed with \e caps |= GeodesicLineExact::GEODESICSCALE. + * @param[out] M21 geodesic scale of point 1 relative to point 2 + * (dimensionless); requires that the GeodesicLineExact object was + * constructed with \e caps |= GeodesicLineExact::GEODESICSCALE. + * @param[out] S12 area under the geodesic (meters2); requires + * that the GeodesicLineExact object was constructed with \e caps |= + * GeodesicLineExact::AREA. + * @return \e a12 arc length from point 1 to point 2 (degrees). + * + * The GeodesicLineExact::mask values possible for \e outmask are + * - \e outmask |= GeodesicLineExact::LATITUDE for the latitude \e lat2; + * - \e outmask |= GeodesicLineExact::LONGITUDE for the latitude \e lon2; + * - \e outmask |= GeodesicLineExact::AZIMUTH for the latitude \e azi2; + * - \e outmask |= GeodesicLineExact::DISTANCE for the distance \e s12; + * - \e outmask |= GeodesicLineExact::REDUCEDLENGTH for the reduced length + * \e m12; + * - \e outmask |= GeodesicLineExact::GEODESICSCALE for the geodesic scales + * \e M12 and \e M21; + * - \e outmask |= GeodesicLineExact::AREA for the area \e S12; + * - \e outmask |= GeodesicLineExact::ALL for all of the above; + * - \e outmask |= GeodesicLineExact::LONG_UNROLL to unroll \e lon2 instead + * of wrapping it into the range [−180°, 180°]. + * . + * Requesting a value which the GeodesicLineExact object is not capable of + * computing is not an error; the corresponding argument will not be + * altered. Note, however, that the arc length is always computed and + * returned as the function value. + * + * With the GeodesicLineExact::LONG_UNROLL bit set, the quantity \e lon2 + * − \e lon1 indicates how many times and in what sense the geodesic + * encircles the ellipsoid. + **********************************************************************/ + Math::real GenPosition(bool arcmode, real s12_a12, unsigned outmask, + real& lat2, real& lon2, real& azi2, + real& s12, real& m12, real& M12, real& M21, + real& S12) const; + ///@} + + /** \name Setting point 3 + **********************************************************************/ + ///@{ + + /** + * Specify position of point 3 in terms of distance. + * + * @param[in] s13 the distance from point 1 to point 3 (meters); it + * can be negative. + * + * This is only useful if the GeodesicLineExact object has been constructed + * with \e caps |= GeodesicLineExact::DISTANCE_IN. + **********************************************************************/ + void SetDistance(real s13); + + /** + * Specify position of point 3 in terms of arc length. + * + * @param[in] a13 the arc length from point 1 to point 3 (degrees); it + * can be negative. + * + * The distance \e s13 is only set if the GeodesicLineExact object has been + * constructed with \e caps |= GeodesicLineExact::DISTANCE. + **********************************************************************/ + void SetArc(real a13); + + /** + * Specify position of point 3 in terms of either distance or arc length. + * + * @param[in] arcmode boolean flag determining the meaning of the second + * parameter; if \e arcmode is false, then the GeodesicLineExact object + * must have been constructed with \e caps |= + * GeodesicLineExact::DISTANCE_IN. + * @param[in] s13_a13 if \e arcmode is false, this is the distance from + * point 1 to point 3 (meters); otherwise it is the arc length from + * point 1 to point 3 (degrees); it can be negative. + **********************************************************************/ + void GenSetDistance(bool arcmode, real s13_a13); + + /** \name Inspector functions + **********************************************************************/ + ///@{ + + /** + * @return true if the object has been initialized. + **********************************************************************/ + bool Init() const { return _caps != 0U; } + + /** + * @return \e lat1 the latitude of point 1 (degrees). + **********************************************************************/ + Math::real Latitude() const + { return Init() ? _lat1 : Math::NaN(); } + + /** + * @return \e lon1 the longitude of point 1 (degrees). + **********************************************************************/ + Math::real Longitude() const + { return Init() ? _lon1 : Math::NaN(); } + + /** + * @return \e azi1 the azimuth (degrees) of the geodesic line at point 1. + **********************************************************************/ + Math::real Azimuth() const + { return Init() ? _azi1 : Math::NaN(); } + + /** + * The sine and cosine of \e azi1. + * + * @param[out] sazi1 the sine of \e azi1. + * @param[out] cazi1 the cosine of \e azi1. + **********************************************************************/ + void Azimuth(real& sazi1, real& cazi1) const + { if (Init()) { sazi1 = _salp1; cazi1 = _calp1; } } + + /** + * @return \e azi0 the azimuth (degrees) of the geodesic line as it crosses + * the equator in a northward direction. + * + * The result lies in [−90°, 90°]. + **********************************************************************/ + Math::real EquatorialAzimuth() const + { return Init() ? Math::atan2d(_salp0, _calp0) : Math::NaN(); } + + /** + * The sine and cosine of \e azi0. + * + * @param[out] sazi0 the sine of \e azi0. + * @param[out] cazi0 the cosine of \e azi0. + **********************************************************************/ + void EquatorialAzimuth(real& sazi0, real& cazi0) const + { if (Init()) { sazi0 = _salp0; cazi0 = _calp0; } } + + /** + * @return \e a1 the arc length (degrees) between the northward equatorial + * crossing and point 1. + * + * The result lies in (−180°, 180°]. + **********************************************************************/ + Math::real EquatorialArc() const { + using std::atan2; + return Init() ? atan2(_ssig1, _csig1) / Math::degree() : Math::NaN(); + } + + /** + * @return \e a the equatorial radius of the ellipsoid (meters). This is + * the value inherited from the GeodesicExact object used in the + * constructor. + **********************************************************************/ + Math::real EquatorialRadius() const + { return Init() ? _a : Math::NaN(); } + + /** + * @return \e f the flattening of the ellipsoid. This is the value + * inherited from the GeodesicExact object used in the constructor. + **********************************************************************/ + Math::real Flattening() const + { return Init() ? _f : Math::NaN(); } + + /** + * @return \e caps the computational capabilities that this object was + * constructed with. LATITUDE and AZIMUTH are always included. + **********************************************************************/ + unsigned Capabilities() const { return _caps; } + + /** + * Test what capabilities are available. + * + * @param[in] testcaps a set of bitor'ed GeodesicLineExact::mask values. + * @return true if the GeodesicLineExact object has all these capabilities. + **********************************************************************/ + bool Capabilities(unsigned testcaps) const { + testcaps &= OUT_ALL; + return (_caps & testcaps) == testcaps; + } + + /** + * The distance or arc length to point 3. + * + * @param[in] arcmode boolean flag determining the meaning of returned + * value. + * @return \e s13 if \e arcmode is false; \e a13 if \e arcmode is true. + **********************************************************************/ + Math::real GenDistance(bool arcmode) const + { return Init() ? (arcmode ? _a13 : _s13) : Math::NaN(); } + + /** + * @return \e s13, the distance to point 3 (meters). + **********************************************************************/ + Math::real Distance() const { return GenDistance(false); } + + /** + * @return \e a13, the arc length to point 3 (degrees). + **********************************************************************/ + Math::real Arc() const { return GenDistance(true); } + + /** + * \deprecated An old name for EquatorialRadius(). + **********************************************************************/ + GEOGRAPHICLIB_DEPRECATED("Use EquatorialRadius()") + Math::real MajorRadius() const { return EquatorialRadius(); } + ///@} + + }; + +} // namespace GeographicLib + +#endif // GEOGRAPHICLIB_GEODESICLINEEXACT_HPP diff --git a/common/local_libs/GeographicLib/include/GeographicLib/Geohash.hpp b/common/local_libs/GeographicLib/include/GeographicLib/Geohash.hpp new file mode 100644 index 0000000000..d7d1d8acfc --- /dev/null +++ b/common/local_libs/GeographicLib/include/GeographicLib/Geohash.hpp @@ -0,0 +1,180 @@ +/** + * \file Geohash.hpp + * \brief Header for GeographicLib::Geohash class + * + * Copyright (c) Charles Karney (2012-2017) and licensed + * under the MIT/X11 License. For more information, see + * https://geographiclib.sourceforge.io/ + **********************************************************************/ + +#if !defined(GEOGRAPHICLIB_GEOHASH_HPP) +#define GEOGRAPHICLIB_GEOHASH_HPP 1 + +#include + +#if defined(_MSC_VER) +// Squelch warnings about dll vs string +# pragma warning (push) +# pragma warning (disable: 4251) +#endif + +namespace GeographicLib { + + /** + * \brief Conversions for geohashes + * + * Geohashes are described in + * - https://en.wikipedia.org/wiki/Geohash + * - http://geohash.org/ + * . + * They provide a compact string representation of a particular geographic + * location (expressed as latitude and longitude), with the property that if + * trailing characters are dropped from the string the geographic location + * remains nearby. The classes Georef and GARS implement similar compact + * representations. + * + * Example of use: + * \include example-Geohash.cpp + **********************************************************************/ + + class GEOGRAPHICLIB_EXPORT Geohash { + private: + typedef Math::real real; + static const int maxlen_ = 18; + static const unsigned long long mask_ = 1ULL << 45; + static const char* const lcdigits_; + static const char* const ucdigits_; + Geohash(); // Disable constructor + + public: + + /** + * Convert from geographic coordinates to a geohash. + * + * @param[in] lat latitude of point (degrees). + * @param[in] lon longitude of point (degrees). + * @param[in] len the length of the resulting geohash. + * @param[out] geohash the geohash. + * @exception GeographicErr if \e lat is not in [−90°, + * 90°]. + * @exception std::bad_alloc if memory for \e geohash can't be allocated. + * + * Internally, \e len is first put in the range [0, 18]. (\e len = 18 + * provides approximately 1μm precision.) + * + * If \e lat or \e lon is NaN, the returned geohash is "invalid". + **********************************************************************/ + static void Forward(real lat, real lon, int len, std::string& geohash); + + /** + * Convert from a geohash to geographic coordinates. + * + * @param[in] geohash the geohash. + * @param[out] lat latitude of point (degrees). + * @param[out] lon longitude of point (degrees). + * @param[out] len the length of the geohash. + * @param[in] centerp if true (the default) return the center of the + * geohash location, otherwise return the south-west corner. + * @exception GeographicErr if \e geohash contains illegal characters. + * + * Only the first 18 characters for \e geohash are considered. (18 + * characters provides approximately 1μm precision.) The case of the + * letters in \e geohash is ignored. + * + * If the first 3 characters of \e geohash are "inv", then \e lat and \e + * lon are set to NaN and \e len is unchanged. ("nan" is treated + * similarly.) + **********************************************************************/ + static void Reverse(const std::string& geohash, real& lat, real& lon, + int& len, bool centerp = true); + + /** + * The latitude resolution of a geohash. + * + * @param[in] len the length of the geohash. + * @return the latitude resolution (degrees). + * + * Internally, \e len is first put in the range [0, 18]. + **********************************************************************/ + static Math::real LatitudeResolution(int len) { + using std::ldexp; + len = (std::max)(0, (std::min)(int(maxlen_), len)); + return ldexp(real(180), -(5 * len / 2)); + } + + /** + * The longitude resolution of a geohash. + * + * @param[in] len the length of the geohash. + * @return the longitude resolution (degrees). + * + * Internally, \e len is first put in the range [0, 18]. + **********************************************************************/ + static Math::real LongitudeResolution(int len) { + using std::ldexp; + len = (std::max)(0, (std::min)(int(maxlen_), len)); + return ldexp(real(360), -(5 * len - 5 * len / 2)); + } + + /** + * The geohash length required to meet a given geographic resolution. + * + * @param[in] res the minimum of resolution in latitude and longitude + * (degrees). + * @return geohash length. + * + * The returned length is in the range [0, 18]. + **********************************************************************/ + static int GeohashLength(real res) { + using std::abs; res = abs(res); + for (int len = 0; len < maxlen_; ++len) + if (LongitudeResolution(len) <= res) + return len; + return maxlen_; + } + + /** + * The geohash length required to meet a given geographic resolution. + * + * @param[in] latres the resolution in latitude (degrees). + * @param[in] lonres the resolution in longitude (degrees). + * @return geohash length. + * + * The returned length is in the range [0, 18]. + **********************************************************************/ + static int GeohashLength(real latres, real lonres) { + using std::abs; + latres = abs(latres); + lonres = abs(lonres); + for (int len = 0; len < maxlen_; ++len) + if (LatitudeResolution(len) <= latres && + LongitudeResolution(len) <= lonres) + return len; + return maxlen_; + } + + /** + * The decimal geographic precision required to match a given geohash + * length. This is the number of digits needed after decimal point in a + * decimal degrees representation. + * + * @param[in] len the length of the geohash. + * @return the decimal precision (may be negative). + * + * Internally, \e len is first put in the range [0, 18]. The returned + * decimal precision is in the range [−2, 12]. + **********************************************************************/ + static int DecimalPrecision(int len) { + using std::floor; using std::log; + return -int(floor(log(LatitudeResolution(len))/log(Math::real(10)))); + } + + }; + +} // namespace GeographicLib + +#if defined(_MSC_VER) +# pragma warning (pop) +#endif + +#endif // GEOGRAPHICLIB_GEOHASH_HPP diff --git a/common/local_libs/GeographicLib/include/GeographicLib/Geoid.hpp b/common/local_libs/GeographicLib/include/GeographicLib/Geoid.hpp new file mode 100644 index 0000000000..b7a17317d6 --- /dev/null +++ b/common/local_libs/GeographicLib/include/GeographicLib/Geoid.hpp @@ -0,0 +1,478 @@ +/** + * \file Geoid.hpp + * \brief Header for GeographicLib::Geoid class + * + * Copyright (c) Charles Karney (2009-2020) and licensed + * under the MIT/X11 License. For more information, see + * https://geographiclib.sourceforge.io/ + **********************************************************************/ + +#if !defined(GEOGRAPHICLIB_GEOID_HPP) +#define GEOGRAPHICLIB_GEOID_HPP 1 + +#include +#include +#include + +#if defined(_MSC_VER) +// Squelch warnings about dll vs vector and constant conditional expressions +# pragma warning (push) +# pragma warning (disable: 4251 4127) +#endif + +#if !defined(GEOGRAPHICLIB_GEOID_PGM_PIXEL_WIDTH) +/** + * The size of the pixel data in the pgm data files for the geoids. 2 is the + * standard size corresponding to a maxval 216−1. Setting it + * to 4 uses a maxval of 232−1 and changes the extension for + * the data files from .pgm to .pgm4. Note that the format of these pgm4 files + * is a non-standard extension of the pgm format. + **********************************************************************/ +# define GEOGRAPHICLIB_GEOID_PGM_PIXEL_WIDTH 2 +#endif + +namespace GeographicLib { + + /** + * \brief Looking up the height of the geoid above the ellipsoid + * + * This class evaluates the height of one of the standard geoids, EGM84, + * EGM96, or EGM2008 by bilinear or cubic interpolation into a rectangular + * grid of data. These geoid models are documented in + * - EGM84: + * http://earth-info.nga.mil/GandG/wgs84/gravitymod/wgs84_180/wgs84_180.html + * - EGM96: + * http://earth-info.nga.mil/GandG/wgs84/gravitymod/egm96/egm96.html + * - EGM2008: + * http://earth-info.nga.mil/GandG/wgs84/gravitymod/egm2008 + * + * The geoids are defined in terms of spherical harmonics. However in order + * to provide a quick and flexible method of evaluating the geoid heights, + * this class evaluates the height by interpolation into a grid of + * precomputed values. + * + * The height of the geoid above the ellipsoid, \e N, is sometimes called the + * geoid undulation. It can be used to convert a height above the ellipsoid, + * \e h, to the corresponding height above the geoid (the orthometric height, + * roughly the height above mean sea level), \e H, using the relations + * + *    \e h = \e N + \e H; + *   \e H = −\e N + \e h. + * + * See \ref geoid for details of how to install the data sets, the data + * format, estimates of the interpolation errors, and how to use caching. + * + * This class is typically \e not thread safe in that a single instantiation + * cannot be safely used by multiple threads because of the way the object + * reads the data set and because it maintains a single-cell cache. If + * multiple threads need to calculate geoid heights they should all construct + * thread-local instantiations. Alternatively, set the optional \e + * threadsafe parameter to true in the constructor. This causes the + * constructor to read all the data into memory and to turn off the + * single-cell caching which results in a Geoid object which \e is thread + * safe. + * + * Example of use: + * \include example-Geoid.cpp + * + * GeoidEval is a command-line utility + * providing access to the functionality of Geoid. + **********************************************************************/ + + class GEOGRAPHICLIB_EXPORT Geoid { + private: + typedef Math::real real; +#if GEOGRAPHICLIB_GEOID_PGM_PIXEL_WIDTH != 4 + typedef unsigned short pixel_t; + static const unsigned pixel_size_ = 2; + static const unsigned pixel_max_ = 0xffffu; +#else + typedef unsigned pixel_t; + static const unsigned pixel_size_ = 4; + static const unsigned pixel_max_ = 0xffffffffu; +#endif + static const unsigned stencilsize_ = 12; + static const unsigned nterms_ = ((3 + 1) * (3 + 2))/2; // for a cubic fit + static const int c0_; + static const int c0n_; + static const int c0s_; + static const int c3_[stencilsize_ * nterms_]; + static const int c3n_[stencilsize_ * nterms_]; + static const int c3s_[stencilsize_ * nterms_]; + + std::string _name, _dir, _filename; + const bool _cubic; + const real _a, _e2, _degree, _eps; + mutable std::ifstream _file; + real _rlonres, _rlatres; + std::string _description, _datetime; + real _offset, _scale, _maxerror, _rmserror; + int _width, _height; + unsigned long long _datastart, _swidth; + bool _threadsafe; + // Area cache + mutable std::vector< std::vector > _data; + mutable bool _cache; + // NE corner and extent of cache + mutable int _xoffset, _yoffset, _xsize, _ysize; + // Cell cache + mutable int _ix, _iy; + mutable real _v00, _v01, _v10, _v11; + mutable real _t[nterms_]; + void filepos(int ix, int iy) const { + _file.seekg(std::streamoff + (_datastart + + pixel_size_ * (unsigned(iy)*_swidth + unsigned(ix)))); + } + real rawval(int ix, int iy) const { + if (ix < 0) + ix += _width; + else if (ix >= _width) + ix -= _width; + if (_cache && iy >= _yoffset && iy < _yoffset + _ysize && + ((ix >= _xoffset && ix < _xoffset + _xsize) || + (ix + _width >= _xoffset && ix + _width < _xoffset + _xsize))) { + return real(_data[iy - _yoffset] + [ix >= _xoffset ? ix - _xoffset : ix + _width - _xoffset]); + } else { + if (iy < 0 || iy >= _height) { + iy = iy < 0 ? -iy : 2 * (_height - 1) - iy; + ix += (ix < _width/2 ? 1 : -1) * _width/2; + } + try { + filepos(ix, iy); + // initial values to suppress warnings in case get fails + char a = 0, b = 0; + _file.get(a); + _file.get(b); + unsigned r = ((unsigned char)(a) << 8) | (unsigned char)(b); + if (pixel_size_ == 4) { + _file.get(a); + _file.get(b); + r = (r << 16) | ((unsigned char)(a) << 8) | (unsigned char)(b); + } + return real(r); + } + catch (const std::exception& e) { + // throw GeographicErr("Error reading " + _filename + ": " + // + e.what()); + // triggers complaints about the "binary '+'" under Visual Studio. + // So use '+=' instead. + std::string err("Error reading "); + err += _filename; + err += ": "; + err += e.what(); + throw GeographicErr(err); + } + } + } + real height(real lat, real lon) const; + Geoid(const Geoid&); // copy constructor not allowed + Geoid& operator=(const Geoid&); // copy assignment not allowed + public: + + /** + * Flags indicating conversions between heights above the geoid and heights + * above the ellipsoid. + **********************************************************************/ + enum convertflag { + /** + * The multiplier for converting from heights above the geoid to heights + * above the ellipsoid. + **********************************************************************/ + ELLIPSOIDTOGEOID = -1, + /** + * No conversion. + **********************************************************************/ + NONE = 0, + /** + * The multiplier for converting from heights above the ellipsoid to + * heights above the geoid. + **********************************************************************/ + GEOIDTOELLIPSOID = 1, + }; + + /** \name Setting up the geoid + **********************************************************************/ + ///@{ + /** + * Construct a geoid. + * + * @param[in] name the name of the geoid. + * @param[in] path (optional) directory for data file. + * @param[in] cubic (optional) interpolation method; false means bilinear, + * true (the default) means cubic. + * @param[in] threadsafe (optional), if true, construct a thread safe + * object. The default is false + * @exception GeographicErr if the data file cannot be found, is + * unreadable, or is corrupt. + * @exception GeographicErr if \e threadsafe is true but the memory + * necessary for caching the data can't be allocated. + * + * The data file is formed by appending ".pgm" to the name. If \e path is + * specified (and is non-empty), then the file is loaded from directory, \e + * path. Otherwise the path is given by DefaultGeoidPath(). If the \e + * threadsafe parameter is true, the data set is read into memory, the data + * file is closed, and single-cell caching is turned off; this results in a + * Geoid object which \e is thread safe. + **********************************************************************/ + explicit Geoid(const std::string& name, const std::string& path = "", + bool cubic = true, bool threadsafe = false); + + /** + * Set up a cache. + * + * @param[in] south latitude (degrees) of the south edge of the cached + * area. + * @param[in] west longitude (degrees) of the west edge of the cached area. + * @param[in] north latitude (degrees) of the north edge of the cached + * area. + * @param[in] east longitude (degrees) of the east edge of the cached area. + * @exception GeographicErr if the memory necessary for caching the data + * can't be allocated (in this case, you will have no cache and can try + * again with a smaller area). + * @exception GeographicErr if there's a problem reading the data. + * @exception GeographicErr if this is called on a threadsafe Geoid. + * + * Cache the data for the specified "rectangular" area bounded by the + * parallels \e south and \e north and the meridians \e west and \e east. + * \e east is always interpreted as being east of \e west, if necessary by + * adding 360° to its value. \e south and \e north should be in + * the range [−90°, 90°]. + **********************************************************************/ + void CacheArea(real south, real west, real north, real east) const; + + /** + * Cache all the data. + * + * @exception GeographicErr if the memory necessary for caching the data + * can't be allocated (in this case, you will have no cache and can try + * again with a smaller area). + * @exception GeographicErr if there's a problem reading the data. + * @exception GeographicErr if this is called on a threadsafe Geoid. + * + * On most computers, this is fast for data sets with grid resolution of 5' + * or coarser. For a 1' grid, the required RAM is 450MB; a 2.5' grid needs + * 72MB; and a 5' grid needs 18MB. + **********************************************************************/ + void CacheAll() const { CacheArea(real(-90), real(0), + real(90), real(360)); } + + /** + * Clear the cache. This never throws an error. (This does nothing with a + * thread safe Geoid.) + **********************************************************************/ + void CacheClear() const; + + ///@} + + /** \name Compute geoid heights + **********************************************************************/ + ///@{ + /** + * Compute the geoid height at a point + * + * @param[in] lat latitude of the point (degrees). + * @param[in] lon longitude of the point (degrees). + * @exception GeographicErr if there's a problem reading the data; this + * never happens if (\e lat, \e lon) is within a successfully cached + * area. + * @return the height of the geoid above the ellipsoid (meters). + * + * The latitude should be in [−90°, 90°]. + **********************************************************************/ + Math::real operator()(real lat, real lon) const { + return height(lat, lon); + } + + /** + * Convert a height above the geoid to a height above the ellipsoid and + * vice versa. + * + * @param[in] lat latitude of the point (degrees). + * @param[in] lon longitude of the point (degrees). + * @param[in] h height of the point (degrees). + * @param[in] d a Geoid::convertflag specifying the direction of the + * conversion; Geoid::GEOIDTOELLIPSOID means convert a height above the + * geoid to a height above the ellipsoid; Geoid::ELLIPSOIDTOGEOID means + * convert a height above the ellipsoid to a height above the geoid. + * @exception GeographicErr if there's a problem reading the data; this + * never happens if (\e lat, \e lon) is within a successfully cached + * area. + * @return converted height (meters). + **********************************************************************/ + Math::real ConvertHeight(real lat, real lon, real h, + convertflag d) const { + return h + real(d) * height(lat, lon); + } + + ///@} + + /** \name Inspector functions + **********************************************************************/ + ///@{ + /** + * @return geoid description, if available, in the data file; if + * absent, return "NONE". + **********************************************************************/ + const std::string& Description() const { return _description; } + + /** + * @return date of the data file; if absent, return "UNKNOWN". + **********************************************************************/ + const std::string& DateTime() const { return _datetime; } + + /** + * @return full file name used to load the geoid data. + **********************************************************************/ + const std::string& GeoidFile() const { return _filename; } + + /** + * @return "name" used to load the geoid data (from the first argument of + * the constructor). + **********************************************************************/ + const std::string& GeoidName() const { return _name; } + + /** + * @return directory used to load the geoid data. + **********************************************************************/ + const std::string& GeoidDirectory() const { return _dir; } + + /** + * @return interpolation method ("cubic" or "bilinear"). + **********************************************************************/ + const std::string Interpolation() const + { return std::string(_cubic ? "cubic" : "bilinear"); } + + /** + * @return estimate of the maximum interpolation and quantization error + * (meters). + * + * This relies on the value being stored in the data file. If the value is + * absent, return −1. + **********************************************************************/ + Math::real MaxError() const { return _maxerror; } + + /** + * @return estimate of the RMS interpolation and quantization error + * (meters). + * + * This relies on the value being stored in the data file. If the value is + * absent, return −1. + **********************************************************************/ + Math::real RMSError() const { return _rmserror; } + + /** + * @return offset (meters). + * + * This in used in converting from the pixel values in the data file to + * geoid heights. + **********************************************************************/ + Math::real Offset() const { return _offset; } + + /** + * @return scale (meters). + * + * This in used in converting from the pixel values in the data file to + * geoid heights. + **********************************************************************/ + Math::real Scale() const { return _scale; } + + /** + * @return true if the object is constructed to be thread safe. + **********************************************************************/ + bool ThreadSafe() const { return _threadsafe; } + + /** + * @return true if a data cache is active. + **********************************************************************/ + bool Cache() const { return _cache; } + + /** + * @return west edge of the cached area; the cache includes this edge. + **********************************************************************/ + Math::real CacheWest() const { + return _cache ? ((_xoffset + (_xsize == _width ? 0 : _cubic) + + _width/2) % _width - _width/2) / _rlonres : + 0; + } + + /** + * @return east edge of the cached area; the cache excludes this edge. + **********************************************************************/ + Math::real CacheEast() const { + return _cache ? + CacheWest() + + (_xsize - (_xsize == _width ? 0 : 1 + 2 * _cubic)) / _rlonres : + 0; + } + + /** + * @return north edge of the cached area; the cache includes this edge. + **********************************************************************/ + Math::real CacheNorth() const { + return _cache ? 90 - (_yoffset + _cubic) / _rlatres : 0; + } + + /** + * @return south edge of the cached area; the cache excludes this edge + * unless it's the south pole. + **********************************************************************/ + Math::real CacheSouth() const { + return _cache ? 90 - ( _yoffset + _ysize - 1 - _cubic) / _rlatres : 0; + } + + /** + * @return \e a the equatorial radius of the WGS84 ellipsoid (meters). + * + * (The WGS84 value is returned because the supported geoid models are all + * based on this ellipsoid.) + **********************************************************************/ + Math::real EquatorialRadius() const + { return Constants::WGS84_a(); } + + /** + * @return \e f the flattening of the WGS84 ellipsoid. + * + * (The WGS84 value is returned because the supported geoid models are all + * based on this ellipsoid.) + **********************************************************************/ + Math::real Flattening() const { return Constants::WGS84_f(); } + + /** + * \deprecated An old name for EquatorialRadius(). + **********************************************************************/ + GEOGRAPHICLIB_DEPRECATED("Use EquatorialRadius()") + Math::real MajorRadius() const { return EquatorialRadius(); } + ///@} + + /** + * @return the default path for geoid data files. + * + * This is the value of the environment variable GEOGRAPHICLIB_GEOID_PATH, + * if set; otherwise, it is $GEOGRAPHICLIB_DATA/geoids if the environment + * variable GEOGRAPHICLIB_DATA is set; otherwise, it is a compile-time + * default (/usr/local/share/GeographicLib/geoids on non-Windows systems + * and C:/ProgramData/GeographicLib/geoids on Windows systems). + **********************************************************************/ + static std::string DefaultGeoidPath(); + + /** + * @return the default name for the geoid. + * + * This is the value of the environment variable GEOGRAPHICLIB_GEOID_NAME, + * if set; otherwise, it is "egm96-5". The Geoid class does not use this + * function; it is just provided as a convenience for a calling program + * when constructing a Geoid object. + **********************************************************************/ + static std::string DefaultGeoidName(); + + }; + +} // namespace GeographicLib + +#if defined(_MSC_VER) +# pragma warning (pop) +#endif + +#endif // GEOGRAPHICLIB_GEOID_HPP diff --git a/common/local_libs/GeographicLib/include/GeographicLib/Georef.hpp b/common/local_libs/GeographicLib/include/GeographicLib/Georef.hpp new file mode 100644 index 0000000000..726b5d5a53 --- /dev/null +++ b/common/local_libs/GeographicLib/include/GeographicLib/Georef.hpp @@ -0,0 +1,161 @@ +/** + * \file Georef.hpp + * \brief Header for GeographicLib::Georef class + * + * Copyright (c) Charles Karney (2015-2017) and licensed + * under the MIT/X11 License. For more information, see + * https://geographiclib.sourceforge.io/ + **********************************************************************/ + +#if !defined(GEOGRAPHICLIB_GEOREF_HPP) +#define GEOGRAPHICLIB_GEOREF_HPP 1 + +#include + +#if defined(_MSC_VER) +// Squelch warnings about dll vs string +# pragma warning (push) +# pragma warning (disable: 4251) +#endif + +namespace GeographicLib { + + /** + * \brief Conversions for the World Geographic Reference System (georef) + * + * The World Geographic Reference System is described in + * - https://en.wikipedia.org/wiki/Georef + * - http://earth-info.nga.mil/GandG/coordsys/grids/georef.pdf + * . + * It provides a compact string representation of a geographic area + * (expressed as latitude and longitude). The classes GARS and Geohash + * implement similar compact representations. + * + * Example of use: + * \include example-Georef.cpp + **********************************************************************/ + + class GEOGRAPHICLIB_EXPORT Georef { + private: + typedef Math::real real; + static const char* const digits_; + static const char* const lontile_; + static const char* const lattile_; + static const char* const degrees_; + enum { + tile_ = 15, // The size of tile in degrees + lonorig_ = -180, // Origin for longitude + latorig_ = -90, // Origin for latitude + base_ = 10, // Base for minutes + baselen_ = 4, + maxprec_ = 11, // approximately equivalent to MGRS class + maxlen_ = baselen_ + 2 * maxprec_, + }; + Georef(); // Disable constructor + + public: + + /** + * Convert from geographic coordinates to georef. + * + * @param[in] lat latitude of point (degrees). + * @param[in] lon longitude of point (degrees). + * @param[in] prec the precision of the resulting georef. + * @param[out] georef the georef string. + * @exception GeographicErr if \e lat is not in [−90°, + * 90°]. + * @exception std::bad_alloc if memory for \e georef can't be allocated. + * + * \e prec specifies the precision of \e georef as follows: + * - \e prec = −1 (min), 15° + * - \e prec = 0, 1° + * - \e prec = 1, converted to \e prec = 2 + * - \e prec = 2, 1' + * - \e prec = 3, 0.1' + * - \e prec = 4, 0.01' + * - \e prec = 5, 0.001' + * - … + * - \e prec = 11 (max), 10−9' + * + * If \e lat or \e lon is NaN, then \e georef is set to "INVALID". + **********************************************************************/ + static void Forward(real lat, real lon, int prec, std::string& georef); + + /** + * Convert from Georef to geographic coordinates. + * + * @param[in] georef the Georef. + * @param[out] lat latitude of point (degrees). + * @param[out] lon longitude of point (degrees). + * @param[out] prec the precision of \e georef. + * @param[in] centerp if true (the default) return the center + * \e georef, otherwise return the south-west corner. + * @exception GeographicErr if \e georef is illegal. + * + * The case of the letters in \e georef is ignored. \e prec is in the + * range [−1, 11] and gives the precision of \e georef as follows: + * - \e prec = −1 (min), 15° + * - \e prec = 0, 1° + * - \e prec = 1, not returned + * - \e prec = 2, 1' + * - \e prec = 3, 0.1' + * - \e prec = 4, 0.01' + * - \e prec = 5, 0.001' + * - … + * - \e prec = 11 (max), 10−9' + * + * If the first 3 characters of \e georef are "INV", then \e lat and \e lon + * are set to NaN and \e prec is unchanged. + **********************************************************************/ + static void Reverse(const std::string& georef, real& lat, real& lon, + int& prec, bool centerp = true); + + /** + * The angular resolution of a Georef. + * + * @param[in] prec the precision of the Georef. + * @return the latitude-longitude resolution (degrees). + * + * Internally, \e prec is first put in the range [−1, 11]. + **********************************************************************/ + static Math::real Resolution(int prec) { + if (prec < 1) + return real(prec < 0 ? 15 : 1); + else { + using std::pow; + // Treat prec = 1 as 2. + prec = (std::max)(2, (std::min)(int(maxprec_), prec)); + // Need extra real because, since C++11, pow(float, int) returns double + return 1/(60 * real(pow(real(base_), prec - 2))); + } + } + + /** + * The Georef precision required to meet a given geographic resolution. + * + * @param[in] res the minimum of resolution in latitude and longitude + * (degrees). + * @return Georef precision. + * + * The returned length is in the range [0, 11]. + **********************************************************************/ + static int Precision(real res) { + using std::abs; res = abs(res); + for (int prec = 0; prec < maxprec_; ++prec) { + if (prec == 1) + continue; + if (Resolution(prec) <= res) + return prec; + } + return maxprec_; + } + + }; + +} // namespace GeographicLib + +#if defined(_MSC_VER) +# pragma warning (pop) +#endif + +#endif // GEOGRAPHICLIB_GEOREF_HPP diff --git a/common/local_libs/GeographicLib/include/GeographicLib/Gnomonic.hpp b/common/local_libs/GeographicLib/include/GeographicLib/Gnomonic.hpp new file mode 100644 index 0000000000..1448fb01c1 --- /dev/null +++ b/common/local_libs/GeographicLib/include/GeographicLib/Gnomonic.hpp @@ -0,0 +1,221 @@ +/** + * \file Gnomonic.hpp + * \brief Header for GeographicLib::Gnomonic class + * + * Copyright (c) Charles Karney (2010-2020) and licensed + * under the MIT/X11 License. For more information, see + * https://geographiclib.sourceforge.io/ + **********************************************************************/ + +#if !defined(GEOGRAPHICLIB_GNOMONIC_HPP) +#define GEOGRAPHICLIB_GNOMONIC_HPP 1 + +#include +#include +#include + +namespace GeographicLib { + + /** + * \brief %Gnomonic projection + * + * %Gnomonic projection centered at an arbitrary position \e C on the + * ellipsoid. This projection is derived in Section 8 of + * - C. F. F. Karney, + * + * Algorithms for geodesics, + * J. Geodesy 87, 43--55 (2013); + * DOI: + * 10.1007/s00190-012-0578-z; + * addenda: + * + * geod-addenda.html. + * . + * The projection of \e P is defined as follows: compute the geodesic line + * from \e C to \e P; compute the reduced length \e m12, geodesic scale \e + * M12, and ρ = m12/\e M12; finally \e x = ρ sin \e azi1; \e + * y = ρ cos \e azi1, where \e azi1 is the azimuth of the geodesic at \e + * C. The Gnomonic::Forward and Gnomonic::Reverse methods also return the + * azimuth \e azi of the geodesic at \e P and reciprocal scale \e rk in the + * azimuthal direction. The scale in the radial direction if + * 1/rk2. + * + * For a sphere, ρ is reduces to \e a tan(s12/a), where \e + * s12 is the length of the geodesic from \e C to \e P, and the gnomonic + * projection has the property that all geodesics appear as straight lines. + * For an ellipsoid, this property holds only for geodesics interesting the + * centers. However geodesic segments close to the center are approximately + * straight. + * + * Consider a geodesic segment of length \e l. Let \e T be the point on the + * geodesic (extended if necessary) closest to \e C the center of the + * projection and \e t be the distance \e CT. To lowest order, the maximum + * deviation (as a true distance) of the corresponding gnomonic line segment + * (i.e., with the same end points) from the geodesic is
+ *
+ * (K(T) - K(C)) + * l2 \e t / 32.
+ *
+ * where \e K is the Gaussian curvature. + * + * This result applies for any surface. For an ellipsoid of revolution, + * consider all geodesics whose end points are within a distance \e r of \e + * C. For a given \e r, the deviation is maximum when the latitude of \e C + * is 45°, when endpoints are a distance \e r away, and when their + * azimuths from the center are ± 45° or ± 135°. + * To lowest order in \e r and the flattening \e f, the deviation is \e f + * (r/2a)3 \e r. + * + * The conversions all take place using a Geodesic object (by default + * Geodesic::WGS84()). For more information on geodesics see \ref geodesic. + * + * \warning The definition of this projection for a sphere is + * standard. However, there is no standard for how it should be extended to + * an ellipsoid. The choices are: + * - Declare that the projection is undefined for an ellipsoid. + * - Project to a tangent plane from the center of the ellipsoid. This + * causes great ellipses to appear as straight lines in the projection; + * i.e., it generalizes the spherical great circle to a great ellipse. + * This was proposed by independently by Bowring and Williams in 1997. + * - Project to the conformal sphere with the constant of integration chosen + * so that the values of the latitude match for the center point and + * perform a central projection onto the plane tangent to the conformal + * sphere at the center point. This causes normal sections through the + * center point to appear as straight lines in the projection; i.e., it + * generalizes the spherical great circle to a normal section. This was + * proposed by I. G. Letoval'tsev, Generalization of the gnomonic + * projection for a spheroid and the principal geodetic problems involved + * in the alignment of surface routes, Geodesy and Aerophotography (5), + * 271--274 (1963). + * - The projection given here. This causes geodesics close to the center + * point to appear as straight lines in the projection; i.e., it + * generalizes the spherical great circle to a geodesic. + * + * Example of use: + * \include example-Gnomonic.cpp + * + * GeodesicProj is a command-line utility + * providing access to the functionality of AzimuthalEquidistant, Gnomonic, + * and CassiniSoldner. + **********************************************************************/ + + class GEOGRAPHICLIB_EXPORT Gnomonic { + private: + typedef Math::real real; + real eps0_, eps_; + Geodesic _earth; + real _a, _f; + // numit_ increased from 10 to 20 to fix convergence failure with high + // precision (e.g., GEOGRAPHICLIB_DIGITS=2000) calculations. Reverse uses + // Newton's method which converges quadratically and so numit_ = 10 would + // normally be big enough. However, since the Geodesic class is based on a + // series it is of limited accuracy; in particular, the derivative rules + // used by Reverse only hold approximately. Consequently, after a few + // iterations, the convergence in the Reverse falls back to improvements in + // each step by a constant (albeit small) factor. + static const int numit_ = 20; + public: + + /** + * Constructor for Gnomonic. + * + * @param[in] earth the Geodesic object to use for geodesic calculations. + * By default this uses the WGS84 ellipsoid. + **********************************************************************/ + explicit Gnomonic(const Geodesic& earth = Geodesic::WGS84()); + + /** + * Forward projection, from geographic to gnomonic. + * + * @param[in] lat0 latitude of center point of projection (degrees). + * @param[in] lon0 longitude of center point of projection (degrees). + * @param[in] lat latitude of point (degrees). + * @param[in] lon longitude of point (degrees). + * @param[out] x easting of point (meters). + * @param[out] y northing of point (meters). + * @param[out] azi azimuth of geodesic at point (degrees). + * @param[out] rk reciprocal of azimuthal scale at point. + * + * \e lat0 and \e lat should be in the range [−90°, 90°]. + * The scale of the projection is 1/rk2 in the "radial" + * direction, \e azi clockwise from true north, and is 1/\e rk in the + * direction perpendicular to this. If the point lies "over the horizon", + * i.e., if \e rk ≤ 0, then NaNs are returned for \e x and \e y (the + * correct values are returned for \e azi and \e rk). A call to Forward + * followed by a call to Reverse will return the original (\e lat, \e lon) + * (to within roundoff) provided the point in not over the horizon. + **********************************************************************/ + void Forward(real lat0, real lon0, real lat, real lon, + real& x, real& y, real& azi, real& rk) const; + + /** + * Reverse projection, from gnomonic to geographic. + * + * @param[in] lat0 latitude of center point of projection (degrees). + * @param[in] lon0 longitude of center point of projection (degrees). + * @param[in] x easting of point (meters). + * @param[in] y northing of point (meters). + * @param[out] lat latitude of point (degrees). + * @param[out] lon longitude of point (degrees). + * @param[out] azi azimuth of geodesic at point (degrees). + * @param[out] rk reciprocal of azimuthal scale at point. + * + * \e lat0 should be in the range [−90°, 90°]. \e lat will + * be in the range [−90°, 90°] and \e lon will be in the + * range [−180°, 180°]. The scale of the projection is + * 1/rk2 in the "radial" direction, \e azi clockwise from + * true north, and is 1/\e rk in the direction perpendicular to this. Even + * though all inputs should return a valid \e lat and \e lon, it's possible + * that the procedure fails to converge for very large \e x or \e y; in + * this case NaNs are returned for all the output arguments. A call to + * Reverse followed by a call to Forward will return the original (\e x, \e + * y) (to roundoff). + **********************************************************************/ + void Reverse(real lat0, real lon0, real x, real y, + real& lat, real& lon, real& azi, real& rk) const; + + /** + * Gnomonic::Forward without returning the azimuth and scale. + **********************************************************************/ + void Forward(real lat0, real lon0, real lat, real lon, + real& x, real& y) const { + real azi, rk; + Forward(lat0, lon0, lat, lon, x, y, azi, rk); + } + + /** + * Gnomonic::Reverse without returning the azimuth and scale. + **********************************************************************/ + void Reverse(real lat0, real lon0, real x, real y, + real& lat, real& lon) const { + real azi, rk; + Reverse(lat0, lon0, x, y, lat, lon, azi, rk); + } + + /** \name Inspector functions + **********************************************************************/ + ///@{ + /** + * @return \e a the equatorial radius of the ellipsoid (meters). This is + * the value inherited from the Geodesic object used in the constructor. + **********************************************************************/ + Math::real EquatorialRadius() const { return _earth.EquatorialRadius(); } + + /** + * @return \e f the flattening of the ellipsoid. This is the value + * inherited from the Geodesic object used in the constructor. + **********************************************************************/ + Math::real Flattening() const { return _earth.Flattening(); } + + /** + * \deprecated An old name for EquatorialRadius(). + **********************************************************************/ + GEOGRAPHICLIB_DEPRECATED("Use EquatorialRadius()") + Math::real MajorRadius() const { return EquatorialRadius(); } + ///@} + + }; + +} // namespace GeographicLib + +#endif // GEOGRAPHICLIB_GNOMONIC_HPP diff --git a/common/local_libs/GeographicLib/include/GeographicLib/GravityCircle.hpp b/common/local_libs/GeographicLib/include/GeographicLib/GravityCircle.hpp new file mode 100644 index 0000000000..4828907e76 --- /dev/null +++ b/common/local_libs/GeographicLib/include/GeographicLib/GravityCircle.hpp @@ -0,0 +1,287 @@ +/** + * \file GravityCircle.hpp + * \brief Header for GeographicLib::GravityCircle class + * + * Copyright (c) Charles Karney (2011-2020) and licensed + * under the MIT/X11 License. For more information, see + * https://geographiclib.sourceforge.io/ + **********************************************************************/ + +#if !defined(GEOGRAPHICLIB_GRAVITYCIRCLE_HPP) +#define GEOGRAPHICLIB_GRAVITYCIRCLE_HPP 1 + +#include +#include +#include +#include + +namespace GeographicLib { + + /** + * \brief Gravity on a circle of latitude + * + * Evaluate the earth's gravity field on a circle of constant height and + * latitude. This uses a CircularEngine to pre-evaluate the inner sum of the + * spherical harmonic sum, allowing the values of the field at several + * different longitudes to be evaluated rapidly. + * + * Use GravityModel::Circle to create a GravityCircle object. (The + * constructor for this class is private.) + * + * See \ref gravityparallel for an example of using GravityCircle (together + * with OpenMP) to speed up the computation of geoid heights. + * + * Example of use: + * \include example-GravityCircle.cpp + * + * Gravity is a command-line utility providing + * access to the functionality of GravityModel and GravityCircle. + **********************************************************************/ + + class GEOGRAPHICLIB_EXPORT GravityCircle { + private: + typedef Math::real real; + enum mask { + NONE = GravityModel::NONE, + GRAVITY = GravityModel::GRAVITY, + DISTURBANCE = GravityModel::DISTURBANCE, + DISTURBING_POTENTIAL = GravityModel::DISTURBING_POTENTIAL, + GEOID_HEIGHT = GravityModel::GEOID_HEIGHT, + SPHERICAL_ANOMALY = GravityModel::SPHERICAL_ANOMALY, + ALL = GravityModel::ALL, + }; + + unsigned _caps; + real _a, _f, _lat, _h, _Z, _Px, _invR, _cpsi, _spsi, + _cphi, _sphi, _amodel, _GMmodel, _dzonal0, + _corrmult, _gamma0, _gamma, _frot; + CircularEngine _gravitational, _disturbing, _correction; + + GravityCircle(mask caps, real a, real f, real lat, real h, + real Z, real P, real cphi, real sphi, + real amodel, real GMmodel, + real dzonal0, real corrmult, + real gamma0, real gamma, real frot, + const CircularEngine& gravitational, + const CircularEngine& disturbing, + const CircularEngine& correction); + + friend class GravityModel; // GravityModel calls the private constructor + Math::real W(real slam, real clam, + real& gX, real& gY, real& gZ) const; + Math::real V(real slam, real clam, + real& gX, real& gY, real& gZ) const; + Math::real InternalT(real slam, real clam, + real& deltaX, real& deltaY, real& deltaZ, + bool gradp, bool correct) const; + public: + /** + * A default constructor for the normal gravity. This sets up an + * uninitialized object which can be later replaced by the + * GravityModel::Circle. + **********************************************************************/ + GravityCircle() : _a(-1) {} + + /** \name Compute the gravitational field + **********************************************************************/ + ///@{ + /** + * Evaluate the gravity. + * + * @param[in] lon the geographic longitude (degrees). + * @param[out] gx the easterly component of the acceleration + * (m s−2). + * @param[out] gy the northerly component of the acceleration + * (m s−2). + * @param[out] gz the upward component of the acceleration + * (m s−2); this is usually negative. + * @return \e W the sum of the gravitational and centrifugal potentials + * (m2 s−2). + * + * The function includes the effects of the earth's rotation. + **********************************************************************/ + Math::real Gravity(real lon, real& gx, real& gy, real& gz) const; + + /** + * Evaluate the gravity disturbance vector. + * + * @param[in] lon the geographic longitude (degrees). + * @param[out] deltax the easterly component of the disturbance vector + * (m s−2). + * @param[out] deltay the northerly component of the disturbance vector + * (m s−2). + * @param[out] deltaz the upward component of the disturbance vector + * (m s−2). + * @return \e T the corresponding disturbing potential + * (m2 s−2). + **********************************************************************/ + Math::real Disturbance(real lon, real& deltax, real& deltay, real& deltaz) + const; + + /** + * Evaluate the geoid height. + * + * @param[in] lon the geographic longitude (degrees). + * @return \e N the height of the geoid above the reference ellipsoid + * (meters). + * + * Some approximations are made in computing the geoid height so that the + * results of the NGA codes are reproduced accurately. Details are given + * in \ref gravitygeoid. + **********************************************************************/ + Math::real GeoidHeight(real lon) const; + + /** + * Evaluate the components of the gravity anomaly vector using the + * spherical approximation. + * + * @param[in] lon the geographic longitude (degrees). + * @param[out] Dg01 the gravity anomaly (m s−2). + * @param[out] xi the northerly component of the deflection of the vertical + * (degrees). + * @param[out] eta the easterly component of the deflection of the vertical + * (degrees). + * + * The spherical approximation (see Heiskanen and Moritz, Sec 2-14) is used + * so that the results of the NGA codes are reproduced accurately. + * approximations used here. Details are given in \ref gravitygeoid. + **********************************************************************/ + void SphericalAnomaly(real lon, real& Dg01, real& xi, real& eta) + const; + + /** + * Evaluate the components of the acceleration due to gravity and the + * centrifugal acceleration in geocentric coordinates. + * + * @param[in] lon the geographic longitude (degrees). + * @param[out] gX the \e X component of the acceleration + * (m s−2). + * @param[out] gY the \e Y component of the acceleration + * (m s−2). + * @param[out] gZ the \e Z component of the acceleration + * (m s−2). + * @return \e W = \e V + Φ the sum of the gravitational and + * centrifugal potentials (m2 s−2). + **********************************************************************/ + Math::real W(real lon, real& gX, real& gY, real& gZ) const { + real slam, clam; + Math::sincosd(lon, slam, clam); + return W(slam, clam, gX, gY, gZ); + } + + /** + * Evaluate the components of the acceleration due to gravity in geocentric + * coordinates. + * + * @param[in] lon the geographic longitude (degrees). + * @param[out] GX the \e X component of the acceleration + * (m s−2). + * @param[out] GY the \e Y component of the acceleration + * (m s−2). + * @param[out] GZ the \e Z component of the acceleration + * (m s−2). + * @return \e V = \e W - Φ the gravitational potential + * (m2 s−2). + **********************************************************************/ + Math::real V(real lon, real& GX, real& GY, real& GZ) const { + real slam, clam; + Math::sincosd(lon, slam, clam); + return V(slam, clam, GX, GY, GZ); + } + + /** + * Evaluate the components of the gravity disturbance in geocentric + * coordinates. + * + * @param[in] lon the geographic longitude (degrees). + * @param[out] deltaX the \e X component of the gravity disturbance + * (m s−2). + * @param[out] deltaY the \e Y component of the gravity disturbance + * (m s−2). + * @param[out] deltaZ the \e Z component of the gravity disturbance + * (m s−2). + * @return \e T = \e W - \e U the disturbing potential (also called the + * anomalous potential) (m2 s−2). + **********************************************************************/ + Math::real T(real lon, real& deltaX, real& deltaY, real& deltaZ) + const { + real slam, clam; + Math::sincosd(lon, slam, clam); + return InternalT(slam, clam, deltaX, deltaY, deltaZ, true, true); + } + + /** + * Evaluate disturbing potential in geocentric coordinates. + * + * @param[in] lon the geographic longitude (degrees). + * @return \e T = \e W - \e U the disturbing potential (also called the + * anomalous potential) (m2 s−2). + **********************************************************************/ + Math::real T(real lon) const { + real slam, clam, dummy; + Math::sincosd(lon, slam, clam); + return InternalT(slam, clam, dummy, dummy, dummy, false, true); + } + + ///@} + + /** \name Inspector functions + **********************************************************************/ + ///@{ + /** + * @return true if the object has been initialized. + **********************************************************************/ + bool Init() const { return _a > 0; } + + /** + * @return \e a the equatorial radius of the ellipsoid (meters). This is + * the value inherited from the GravityModel object used in the + * constructor. + **********************************************************************/ + Math::real EquatorialRadius() const + { return Init() ? _a : Math::NaN(); } + + /** + * @return \e f the flattening of the ellipsoid. This is the value + * inherited from the GravityModel object used in the constructor. + **********************************************************************/ + Math::real Flattening() const + { return Init() ? _f : Math::NaN(); } + + /** + * @return the latitude of the circle (degrees). + **********************************************************************/ + Math::real Latitude() const + { return Init() ? _lat : Math::NaN(); } + + /** + * @return the height of the circle (meters). + **********************************************************************/ + Math::real Height() const + { return Init() ? _h : Math::NaN(); } + + /** + * @return \e caps the computational capabilities that this object was + * constructed with. + **********************************************************************/ + unsigned Capabilities() const { return _caps; } + + /** + * @param[in] testcaps a set of bitor'ed GravityModel::mask values. + * @return true if the GravityCircle object has all these capabilities. + **********************************************************************/ + bool Capabilities(unsigned testcaps) const { + return (_caps & testcaps) == testcaps; + } + + /** + * \deprecated An old name for EquatorialRadius(). + **********************************************************************/ + GEOGRAPHICLIB_DEPRECATED("Use EquatorialRadius()") + Math::real MajorRadius() const { return EquatorialRadius(); } + ///@} + }; + +} // namespace GeographicLib + +#endif // GEOGRAPHICLIB_GRAVITYCIRCLE_HPP diff --git a/common/local_libs/GeographicLib/include/GeographicLib/GravityModel.hpp b/common/local_libs/GeographicLib/include/GeographicLib/GravityModel.hpp new file mode 100644 index 0000000000..17797d5722 --- /dev/null +++ b/common/local_libs/GeographicLib/include/GeographicLib/GravityModel.hpp @@ -0,0 +1,548 @@ +/** + * \file GravityModel.hpp + * \brief Header for GeographicLib::GravityModel class + * + * Copyright (c) Charles Karney (2011-2020) and licensed + * under the MIT/X11 License. For more information, see + * https://geographiclib.sourceforge.io/ + **********************************************************************/ + +#if !defined(GEOGRAPHICLIB_GRAVITYMODEL_HPP) +#define GEOGRAPHICLIB_GRAVITYMODEL_HPP 1 + +#include +#include +#include +#include + +#if defined(_MSC_VER) +// Squelch warnings about dll vs vector +# pragma warning (push) +# pragma warning (disable: 4251) +#endif + +namespace GeographicLib { + + class GravityCircle; + + /** + * \brief Model of the earth's gravity field + * + * Evaluate the earth's gravity field according to a model. The supported + * models treat only the gravitational field exterior to the mass of the + * earth. When computing the field at points near (but above) the surface of + * the earth a small correction can be applied to account for the mass of the + * atmosphere above the point in question; see \ref gravityatmos. + * Determining the height of the geoid above the ellipsoid entails correcting + * for the mass of the earth above the geoid. The egm96 and egm2008 include + * separate correction terms to account for this mass. + * + * Definitions and terminology (from Heiskanen and Moritz, Sec 2-13): + * - \e V = gravitational potential; + * - Φ = rotational potential; + * - \e W = \e V + Φ = \e T + \e U = total potential; + * - V0 = normal gravitation potential; + * - \e U = V0 + Φ = total normal potential; + * - \e T = \e W − \e U = \e V − V0 = anomalous + * or disturbing potential; + * - g = ∇\e W = γ + δ; + * - f = ∇Φ; + * - Γ = ∇V0; + * - γ = ∇\e U; + * - δ = ∇\e T = gravity disturbance vector + * = gPγP; + * - δ\e g = gravity disturbance = gP − + * γP; + * - Δg = gravity anomaly vector = gP + * − γQ; here the line \e PQ is + * perpendicular to ellipsoid and the potential at \e P equals the normal + * potential at \e Q; + * - Δ\e g = gravity anomaly = gP − + * γQ; + * - (ξ, η) deflection of the vertical, the difference in + * directions of gP and + * γQ, ξ = NS, η = EW. + * - \e X, \e Y, \e Z, geocentric coordinates; + * - \e x, \e y, \e z, local cartesian coordinates used to denote the east, + * north and up directions. + * + * See \ref gravity for details of how to install the gravity models and the + * data format. + * + * References: + * - W. A. Heiskanen and H. Moritz, Physical Geodesy (Freeman, San + * Francisco, 1967). + * + * Example of use: + * \include example-GravityModel.cpp + * + * Gravity is a command-line utility providing + * access to the functionality of GravityModel and GravityCircle. + **********************************************************************/ + + class GEOGRAPHICLIB_EXPORT GravityModel { + private: + typedef Math::real real; + friend class GravityCircle; + static const int idlength_ = 8; + std::string _name, _dir, _description, _date, _filename, _id; + real _amodel, _GMmodel, _zeta0, _corrmult; + int _nmx, _mmx; + SphericalHarmonic::normalization _norm; + NormalGravity _earth; + std::vector _Cx, _Sx, _CC, _CS, _zonal; + real _dzonal0; // A left over contribution to _zonal. + SphericalHarmonic _gravitational; + SphericalHarmonic1 _disturbing; + SphericalHarmonic _correction; + void ReadMetadata(const std::string& name); + Math::real InternalT(real X, real Y, real Z, + real& deltaX, real& deltaY, real& deltaZ, + bool gradp, bool correct) const; + GravityModel(const GravityModel&); // copy constructor not allowed + GravityModel& operator=(const GravityModel&); // nor copy assignment + + enum captype { + CAP_NONE = 0U, + CAP_G = 1U<<0, // implies potentials W and V + CAP_T = 1U<<1, + CAP_DELTA = 1U<<2 | CAP_T, // delta implies T? + CAP_C = 1U<<3, + CAP_GAMMA0 = 1U<<4, + CAP_GAMMA = 1U<<5, + CAP_ALL = 0x3FU, + }; + + public: + + /** + * Bit masks for the capabilities to be given to the GravityCircle object + * produced by Circle. + **********************************************************************/ + enum mask { + /** + * No capabilities. + * @hideinitializer + **********************************************************************/ + NONE = 0U, + /** + * Allow calls to GravityCircle::Gravity, GravityCircle::W, and + * GravityCircle::V. + * @hideinitializer + **********************************************************************/ + GRAVITY = CAP_G, + /** + * Allow calls to GravityCircle::Disturbance and GravityCircle::T. + * @hideinitializer + **********************************************************************/ + DISTURBANCE = CAP_DELTA, + /** + * Allow calls to GravityCircle::T(real lon) (i.e., computing the + * disturbing potential and not the gravity disturbance vector). + * @hideinitializer + **********************************************************************/ + DISTURBING_POTENTIAL = CAP_T, + /** + * Allow calls to GravityCircle::SphericalAnomaly. + * @hideinitializer + **********************************************************************/ + SPHERICAL_ANOMALY = CAP_DELTA | CAP_GAMMA, + /** + * Allow calls to GravityCircle::GeoidHeight. + * @hideinitializer + **********************************************************************/ + GEOID_HEIGHT = CAP_T | CAP_C | CAP_GAMMA0, + /** + * All capabilities. + * @hideinitializer + **********************************************************************/ + ALL = CAP_ALL, + }; + /** \name Setting up the gravity model + **********************************************************************/ + ///@{ + /** + * Construct a gravity model. + * + * @param[in] name the name of the model. + * @param[in] path (optional) directory for data file. + * @param[in] Nmax (optional) if non-negative, truncate the degree of the + * model this value. + * @param[in] Mmax (optional) if non-negative, truncate the order of the + * model this value. + * @exception GeographicErr if the data file cannot be found, is + * unreadable, or is corrupt, or if \e Mmax > \e Nmax. + * @exception std::bad_alloc if the memory necessary for storing the model + * can't be allocated. + * + * A filename is formed by appending ".egm" (World Gravity Model) to the + * name. If \e path is specified (and is non-empty), then the file is + * loaded from directory, \e path. Otherwise the path is given by + * DefaultGravityPath(). + * + * This file contains the metadata which specifies the properties of the + * model. The coefficients for the spherical harmonic sums are obtained + * from a file obtained by appending ".cof" to metadata file (so the + * filename ends in ".egm.cof"). + * + * If \e Nmax ≥ 0 and \e Mmax < 0, then \e Mmax is set to \e Nmax. + * After the model is loaded, the maximum degree and order of the model can + * be found by the Degree() and Order() methods. + **********************************************************************/ + explicit GravityModel(const std::string& name, + const std::string& path = "", + int Nmax = -1, int Mmax = -1); + ///@} + + /** \name Compute gravity in geodetic coordinates + **********************************************************************/ + ///@{ + /** + * Evaluate the gravity at an arbitrary point above (or below) the + * ellipsoid. + * + * @param[in] lat the geographic latitude (degrees). + * @param[in] lon the geographic longitude (degrees). + * @param[in] h the height above the ellipsoid (meters). + * @param[out] gx the easterly component of the acceleration + * (m s−2). + * @param[out] gy the northerly component of the acceleration + * (m s−2). + * @param[out] gz the upward component of the acceleration + * (m s−2); this is usually negative. + * @return \e W the sum of the gravitational and centrifugal potentials + * (m2 s−2). + * + * The function includes the effects of the earth's rotation. + **********************************************************************/ + Math::real Gravity(real lat, real lon, real h, + real& gx, real& gy, real& gz) const; + + /** + * Evaluate the gravity disturbance vector at an arbitrary point above (or + * below) the ellipsoid. + * + * @param[in] lat the geographic latitude (degrees). + * @param[in] lon the geographic longitude (degrees). + * @param[in] h the height above the ellipsoid (meters). + * @param[out] deltax the easterly component of the disturbance vector + * (m s−2). + * @param[out] deltay the northerly component of the disturbance vector + * (m s−2). + * @param[out] deltaz the upward component of the disturbance vector + * (m s−2). + * @return \e T the corresponding disturbing potential + * (m2 s−2). + **********************************************************************/ + Math::real Disturbance(real lat, real lon, real h, + real& deltax, real& deltay, real& deltaz) + const; + + /** + * Evaluate the geoid height. + * + * @param[in] lat the geographic latitude (degrees). + * @param[in] lon the geographic longitude (degrees). + * @return \e N the height of the geoid above the ReferenceEllipsoid() + * (meters). + * + * This calls NormalGravity::U for ReferenceEllipsoid(). Some + * approximations are made in computing the geoid height so that the + * results of the NGA codes are reproduced accurately. Details are given + * in \ref gravitygeoid. + **********************************************************************/ + Math::real GeoidHeight(real lat, real lon) const; + + /** + * Evaluate the components of the gravity anomaly vector using the + * spherical approximation. + * + * @param[in] lat the geographic latitude (degrees). + * @param[in] lon the geographic longitude (degrees). + * @param[in] h the height above the ellipsoid (meters). + * @param[out] Dg01 the gravity anomaly (m s−2). + * @param[out] xi the northerly component of the deflection of the vertical + * (degrees). + * @param[out] eta the easterly component of the deflection of the vertical + * (degrees). + * + * The spherical approximation (see Heiskanen and Moritz, Sec 2-14) is used + * so that the results of the NGA codes are reproduced accurately. + * approximations used here. Details are given in \ref gravitygeoid. + **********************************************************************/ + void SphericalAnomaly(real lat, real lon, real h, + real& Dg01, real& xi, real& eta) const; + ///@} + + /** \name Compute gravity in geocentric coordinates + **********************************************************************/ + ///@{ + /** + * Evaluate the components of the acceleration due to gravity and the + * centrifugal acceleration in geocentric coordinates. + * + * @param[in] X geocentric coordinate of point (meters). + * @param[in] Y geocentric coordinate of point (meters). + * @param[in] Z geocentric coordinate of point (meters). + * @param[out] gX the \e X component of the acceleration + * (m s−2). + * @param[out] gY the \e Y component of the acceleration + * (m s−2). + * @param[out] gZ the \e Z component of the acceleration + * (m s−2). + * @return \e W = \e V + Φ the sum of the gravitational and + * centrifugal potentials (m2 s−2). + * + * This calls NormalGravity::U for ReferenceEllipsoid(). + **********************************************************************/ + Math::real W(real X, real Y, real Z, + real& gX, real& gY, real& gZ) const; + + /** + * Evaluate the components of the acceleration due to gravity in geocentric + * coordinates. + * + * @param[in] X geocentric coordinate of point (meters). + * @param[in] Y geocentric coordinate of point (meters). + * @param[in] Z geocentric coordinate of point (meters). + * @param[out] GX the \e X component of the acceleration + * (m s−2). + * @param[out] GY the \e Y component of the acceleration + * (m s−2). + * @param[out] GZ the \e Z component of the acceleration + * (m s−2). + * @return \e V = \e W - Φ the gravitational potential + * (m2 s−2). + **********************************************************************/ + Math::real V(real X, real Y, real Z, + real& GX, real& GY, real& GZ) const; + + /** + * Evaluate the components of the gravity disturbance in geocentric + * coordinates. + * + * @param[in] X geocentric coordinate of point (meters). + * @param[in] Y geocentric coordinate of point (meters). + * @param[in] Z geocentric coordinate of point (meters). + * @param[out] deltaX the \e X component of the gravity disturbance + * (m s−2). + * @param[out] deltaY the \e Y component of the gravity disturbance + * (m s−2). + * @param[out] deltaZ the \e Z component of the gravity disturbance + * (m s−2). + * @return \e T = \e W - \e U the disturbing potential (also called the + * anomalous potential) (m2 s−2). + **********************************************************************/ + Math::real T(real X, real Y, real Z, + real& deltaX, real& deltaY, real& deltaZ) const + { return InternalT(X, Y, Z, deltaX, deltaY, deltaZ, true, true); } + + /** + * Evaluate disturbing potential in geocentric coordinates. + * + * @param[in] X geocentric coordinate of point (meters). + * @param[in] Y geocentric coordinate of point (meters). + * @param[in] Z geocentric coordinate of point (meters). + * @return \e T = \e W - \e U the disturbing potential (also called the + * anomalous potential) (m2 s−2). + **********************************************************************/ + Math::real T(real X, real Y, real Z) const { + real dummy; + return InternalT(X, Y, Z, dummy, dummy, dummy, false, true); + } + + /** + * Evaluate the components of the acceleration due to normal gravity and + * the centrifugal acceleration in geocentric coordinates. + * + * @param[in] X geocentric coordinate of point (meters). + * @param[in] Y geocentric coordinate of point (meters). + * @param[in] Z geocentric coordinate of point (meters). + * @param[out] gammaX the \e X component of the normal acceleration + * (m s−2). + * @param[out] gammaY the \e Y component of the normal acceleration + * (m s−2). + * @param[out] gammaZ the \e Z component of the normal acceleration + * (m s−2). + * @return \e U = V0 + Φ the sum of the + * normal gravitational and centrifugal potentials + * (m2 s−2). + * + * This calls NormalGravity::U for ReferenceEllipsoid(). + **********************************************************************/ + Math::real U(real X, real Y, real Z, + real& gammaX, real& gammaY, real& gammaZ) const + { return _earth.U(X, Y, Z, gammaX, gammaY, gammaZ); } + + /** + * Evaluate the centrifugal acceleration in geocentric coordinates. + * + * @param[in] X geocentric coordinate of point (meters). + * @param[in] Y geocentric coordinate of point (meters). + * @param[out] fX the \e X component of the centrifugal acceleration + * (m s−2). + * @param[out] fY the \e Y component of the centrifugal acceleration + * (m s−2). + * @return Φ the centrifugal potential (m2 + * s−2). + * + * This calls NormalGravity::Phi for ReferenceEllipsoid(). + **********************************************************************/ + Math::real Phi(real X, real Y, real& fX, real& fY) const + { return _earth.Phi(X, Y, fX, fY); } + ///@} + + /** \name Compute gravity on a circle of constant latitude + **********************************************************************/ + ///@{ + /** + * Create a GravityCircle object to allow the gravity field at many points + * with constant \e lat and \e h and varying \e lon to be computed + * efficiently. + * + * @param[in] lat latitude of the point (degrees). + * @param[in] h the height of the point above the ellipsoid (meters). + * @param[in] caps bitor'ed combination of GravityModel::mask values + * specifying the capabilities of the resulting GravityCircle object. + * @exception std::bad_alloc if the memory necessary for creating a + * GravityCircle can't be allocated. + * @return a GravityCircle object whose member functions computes the + * gravitational field at a particular values of \e lon. + * + * The GravityModel::mask values are + * - \e caps |= GravityModel::GRAVITY + * - \e caps |= GravityModel::DISTURBANCE + * - \e caps |= GravityModel::DISTURBING_POTENTIAL + * - \e caps |= GravityModel::SPHERICAL_ANOMALY + * - \e caps |= GravityModel::GEOID_HEIGHT + * . + * The default value of \e caps is GravityModel::ALL which turns on all the + * capabilities. If an unsupported function is invoked, it will return + * NaNs. Note that GravityModel::GEOID_HEIGHT will only be honored if \e h + * = 0. + * + * If the field at several points on a circle of latitude need to be + * calculated then creating a GravityCircle object and using its member + * functions will be substantially faster, especially for high-degree + * models. See \ref gravityparallel for an example of using GravityCircle + * (together with OpenMP) to speed up the computation of geoid heights. + **********************************************************************/ + GravityCircle Circle(real lat, real h, unsigned caps = ALL) const; + ///@} + + /** \name Inspector functions + **********************************************************************/ + ///@{ + + /** + * @return the NormalGravity object for the reference ellipsoid. + **********************************************************************/ + const NormalGravity& ReferenceEllipsoid() const { return _earth; } + + /** + * @return the description of the gravity model, if available, in the data + * file; if absent, return "NONE". + **********************************************************************/ + const std::string& Description() const { return _description; } + + /** + * @return date of the model; if absent, return "UNKNOWN". + **********************************************************************/ + const std::string& DateTime() const { return _date; } + + /** + * @return full file name used to load the gravity model. + **********************************************************************/ + const std::string& GravityFile() const { return _filename; } + + /** + * @return "name" used to load the gravity model (from the first argument + * of the constructor, but this may be overridden by the model file). + **********************************************************************/ + const std::string& GravityModelName() const { return _name; } + + /** + * @return directory used to load the gravity model. + **********************************************************************/ + const std::string& GravityModelDirectory() const { return _dir; } + + /** + * @return \e a the equatorial radius of the ellipsoid (meters). + **********************************************************************/ + Math::real EquatorialRadius() const { return _earth.EquatorialRadius(); } + + /** + * @return \e GM the mass constant of the model (m3 + * s−2); this is the product of \e G the gravitational + * constant and \e M the mass of the earth (usually including the mass of + * the earth's atmosphere). + **********************************************************************/ + Math::real MassConstant() const { return _GMmodel; } + + /** + * @return \e GM the mass constant of the ReferenceEllipsoid() + * (m3 s−2). + **********************************************************************/ + Math::real ReferenceMassConstant() const + { return _earth.MassConstant(); } + + /** + * @return ω the angular velocity of the model and the + * ReferenceEllipsoid() (rad s−1). + **********************************************************************/ + Math::real AngularVelocity() const + { return _earth.AngularVelocity(); } + + /** + * @return \e f the flattening of the ellipsoid. + **********************************************************************/ + Math::real Flattening() const { return _earth.Flattening(); } + + /** + * @return \e Nmax the maximum degree of the components of the model. + **********************************************************************/ + int Degree() const { return _nmx; } + + /** + * @return \e Mmax the maximum order of the components of the model. + **********************************************************************/ + int Order() const { return _mmx; } + + /** + * \deprecated An old name for EquatorialRadius(). + **********************************************************************/ + GEOGRAPHICLIB_DEPRECATED("Use EquatorialRadius()") + Math::real MajorRadius() const { return EquatorialRadius(); } + ///@} + + /** + * @return the default path for gravity model data files. + * + * This is the value of the environment variable + * GEOGRAPHICLIB_GRAVITY_PATH, if set; otherwise, it is + * $GEOGRAPHICLIB_DATA/gravity if the environment variable + * GEOGRAPHICLIB_DATA is set; otherwise, it is a compile-time default + * (/usr/local/share/GeographicLib/gravity on non-Windows systems and + * C:/ProgramData/GeographicLib/gravity on Windows systems). + **********************************************************************/ + static std::string DefaultGravityPath(); + + /** + * @return the default name for the gravity model. + * + * This is the value of the environment variable + * GEOGRAPHICLIB_GRAVITY_NAME, if set; otherwise, it is "egm96". The + * GravityModel class does not use this function; it is just provided as a + * convenience for a calling program when constructing a GravityModel + * object. + **********************************************************************/ + static std::string DefaultGravityName(); + }; + +} // namespace GeographicLib + +#if defined(_MSC_VER) +# pragma warning (pop) +#endif + +#endif // GEOGRAPHICLIB_GRAVITYMODEL_HPP diff --git a/common/local_libs/GeographicLib/include/GeographicLib/LambertConformalConic.hpp b/common/local_libs/GeographicLib/include/GeographicLib/LambertConformalConic.hpp new file mode 100644 index 0000000000..da968e8c08 --- /dev/null +++ b/common/local_libs/GeographicLib/include/GeographicLib/LambertConformalConic.hpp @@ -0,0 +1,330 @@ +/** + * \file LambertConformalConic.hpp + * \brief Header for GeographicLib::LambertConformalConic class + * + * Copyright (c) Charles Karney (2010-2020) and licensed + * under the MIT/X11 License. For more information, see + * https://geographiclib.sourceforge.io/ + **********************************************************************/ + +#if !defined(GEOGRAPHICLIB_LAMBERTCONFORMALCONIC_HPP) +#define GEOGRAPHICLIB_LAMBERTCONFORMALCONIC_HPP 1 + +#include + +namespace GeographicLib { + + /** + * \brief Lambert conformal conic projection + * + * Implementation taken from the report, + * - J. P. Snyder, + * Map Projections: A + * Working Manual, USGS Professional Paper 1395 (1987), + * pp. 107--109. + * + * This is a implementation of the equations in Snyder except that divided + * differences have been used to transform the expressions into ones which + * may be evaluated accurately and that Newton's method is used to invert the + * projection. In this implementation, the projection correctly becomes the + * Mercator projection or the polar stereographic projection when the + * standard latitude is the equator or a pole. The accuracy of the + * projections is about 10 nm (10 nanometers). + * + * The ellipsoid parameters, the standard parallels, and the scale on the + * standard parallels are set in the constructor. Internally, the case with + * two standard parallels is converted into a single standard parallel, the + * latitude of tangency (also the latitude of minimum scale), with a scale + * specified on this parallel. This latitude is also used as the latitude of + * origin which is returned by LambertConformalConic::OriginLatitude. The + * scale on the latitude of origin is given by + * LambertConformalConic::CentralScale. The case with two distinct standard + * parallels where one is a pole is singular and is disallowed. The central + * meridian (which is a trivial shift of the longitude) is specified as the + * \e lon0 argument of the LambertConformalConic::Forward and + * LambertConformalConic::Reverse functions. + * + * This class also returns the meridian convergence \e gamma and scale \e k. + * The meridian convergence is the bearing of grid north (the \e y axis) + * measured clockwise from true north. + * + * There is no provision in this + * class for specifying a false easting or false northing or a different + * latitude of origin. However these are can be simply included by the + * calling function. For example the Pennsylvania South state coordinate + * system ( + * EPSG:3364) is obtained by: + * \include example-LambertConformalConic.cpp + * + * ConicProj is a command-line utility + * providing access to the functionality of LambertConformalConic and + * AlbersEqualArea. + **********************************************************************/ + class GEOGRAPHICLIB_EXPORT LambertConformalConic { + private: + typedef Math::real real; + real eps_, epsx_, ahypover_; + real _a, _f, _fm, _e2, _es; + real _sign, _n, _nc, _t0nm1, _scale, _lat0, _k0; + real _scbet0, _tchi0, _scchi0, _psi0, _nrho0, _drhomax; + static const int numit_ = 5; + static real hyp(real x) { + using std::hypot; + return hypot(real(1), x); + } + // Divided differences + // Definition: Df(x,y) = (f(x)-f(y))/(x-y) + // See: + // W. M. Kahan and R. J. Fateman, + // Symbolic computation of divided differences, + // SIGSAM Bull. 33(3), 7-28 (1999) + // https://doi.org/10.1145/334714.334716 + // http://www.cs.berkeley.edu/~fateman/papers/divdiff.pdf + // + // General rules + // h(x) = f(g(x)): Dh(x,y) = Df(g(x),g(y))*Dg(x,y) + // h(x) = f(x)*g(x): + // Dh(x,y) = Df(x,y)*g(x) + Dg(x,y)*f(y) + // = Df(x,y)*g(y) + Dg(x,y)*f(x) + // = Df(x,y)*(g(x)+g(y))/2 + Dg(x,y)*(f(x)+f(y))/2 + // + // hyp(x) = sqrt(1+x^2): Dhyp(x,y) = (x+y)/(hyp(x)+hyp(y)) + static real Dhyp(real x, real y, real hx, real hy) + // hx = hyp(x) + { return (x + y) / (hx + hy); } + // sn(x) = x/sqrt(1+x^2): Dsn(x,y) = (x+y)/((sn(x)+sn(y))*(1+x^2)*(1+y^2)) + static real Dsn(real x, real y, real sx, real sy) { + // sx = x/hyp(x) + real t = x * y; + return t > 0 ? (x + y) * Math::sq( (sx * sy)/t ) / (sx + sy) : + (x - y != 0 ? (sx - sy) / (x - y) : 1); + } + // Dlog1p(x,y) = log1p((x-y)/(1+y))/(x-y) + static real Dlog1p(real x, real y) { + using std::log1p; + real t = x - y; if (t < 0) { t = -t; y = x; } + return t != 0 ? log1p(t / (1 + y)) / t : 1 / (1 + x); + } + // Dexp(x,y) = exp((x+y)/2) * 2*sinh((x-y)/2)/(x-y) + static real Dexp(real x, real y) { + using std::sinh; using std::exp; + real t = (x - y)/2; + return (t != 0 ? sinh(t)/t : 1) * exp((x + y)/2); + } + // Dsinh(x,y) = 2*sinh((x-y)/2)/(x-y) * cosh((x+y)/2) + // cosh((x+y)/2) = (c+sinh(x)*sinh(y)/c)/2 + // c=sqrt((1+cosh(x))*(1+cosh(y))) + // cosh((x+y)/2) = sqrt( (sinh(x)*sinh(y) + cosh(x)*cosh(y) + 1)/2 ) + static real Dsinh(real x, real y, real sx, real sy, real cx, real cy) + // sx = sinh(x), cx = cosh(x) + { + // real t = (x - y)/2, c = sqrt((1 + cx) * (1 + cy)); + // return (t ? sinh(t)/t : real(1)) * (c + sx * sy / c) /2; + using std::sinh; using std::sqrt; + real t = (x - y)/2; + return (t != 0 ? sinh(t)/t : 1) * sqrt((sx * sy + cx * cy + 1) /2); + } + // Dasinh(x,y) = asinh((x-y)*(x+y)/(x*sqrt(1+y^2)+y*sqrt(1+x^2)))/(x-y) + // = asinh((x*sqrt(1+y^2)-y*sqrt(1+x^2)))/(x-y) + static real Dasinh(real x, real y, real hx, real hy) { + // hx = hyp(x) + using std::asinh; + real t = x - y; + return t != 0 ? + asinh(x*y > 0 ? t * (x + y) / (x*hy + y*hx) : x*hy - y*hx) / t : + 1 / hx; + } + // Deatanhe(x,y) = eatanhe((x-y)/(1-e^2*x*y))/(x-y) + real Deatanhe(real x, real y) const { + real t = x - y, d = 1 - _e2 * x * y; + return t != 0 ? Math::eatanhe(t / d, _es) / t : _e2 / d; + } + void Init(real sphi1, real cphi1, real sphi2, real cphi2, real k1); + public: + + /** + * Constructor with a single standard parallel. + * + * @param[in] a equatorial radius of ellipsoid (meters). + * @param[in] f flattening of ellipsoid. Setting \e f = 0 gives a sphere. + * Negative \e f gives a prolate ellipsoid. + * @param[in] stdlat standard parallel (degrees), the circle of tangency. + * @param[in] k0 scale on the standard parallel. + * @exception GeographicErr if \e a, (1 − \e f) \e a, or \e k0 is + * not positive. + * @exception GeographicErr if \e stdlat is not in [−90°, + * 90°]. + **********************************************************************/ + LambertConformalConic(real a, real f, real stdlat, real k0); + + /** + * Constructor with two standard parallels. + * + * @param[in] a equatorial radius of ellipsoid (meters). + * @param[in] f flattening of ellipsoid. Setting \e f = 0 gives a sphere. + * Negative \e f gives a prolate ellipsoid. + * @param[in] stdlat1 first standard parallel (degrees). + * @param[in] stdlat2 second standard parallel (degrees). + * @param[in] k1 scale on the standard parallels. + * @exception GeographicErr if \e a, (1 − \e f) \e a, or \e k1 is + * not positive. + * @exception GeographicErr if \e stdlat1 or \e stdlat2 is not in + * [−90°, 90°], or if either \e stdlat1 or \e + * stdlat2 is a pole and \e stdlat1 is not equal \e stdlat2. + **********************************************************************/ + LambertConformalConic(real a, real f, real stdlat1, real stdlat2, real k1); + + /** + * Constructor with two standard parallels specified by sines and cosines. + * + * @param[in] a equatorial radius of ellipsoid (meters). + * @param[in] f flattening of ellipsoid. Setting \e f = 0 gives a sphere. + * Negative \e f gives a prolate ellipsoid. + * @param[in] sinlat1 sine of first standard parallel. + * @param[in] coslat1 cosine of first standard parallel. + * @param[in] sinlat2 sine of second standard parallel. + * @param[in] coslat2 cosine of second standard parallel. + * @param[in] k1 scale on the standard parallels. + * @exception GeographicErr if \e a, (1 − \e f) \e a, or \e k1 is + * not positive. + * @exception GeographicErr if \e stdlat1 or \e stdlat2 is not in + * [−90°, 90°], or if either \e stdlat1 or \e + * stdlat2 is a pole and \e stdlat1 is not equal \e stdlat2. + * + * This allows parallels close to the poles to be specified accurately. + * This routine computes the latitude of origin and the scale at this + * latitude. In the case where \e lat1 and \e lat2 are different, the + * errors in this routines are as follows: if \e dlat = abs(\e lat2 − + * \e lat1) ≤ 160° and max(abs(\e lat1), abs(\e lat2)) ≤ 90 + * − min(0.0002, 2.2 × 10−6(180 − \e + * dlat), 6 × 10−8 dlat2) (in + * degrees), then the error in the latitude of origin is less than 4.5 + * × 10−14d and the relative error in the scale is + * less than 7 × 10−15. + **********************************************************************/ + LambertConformalConic(real a, real f, + real sinlat1, real coslat1, + real sinlat2, real coslat2, + real k1); + + /** + * Set the scale for the projection. + * + * @param[in] lat (degrees). + * @param[in] k scale at latitude \e lat (default 1). + * @exception GeographicErr \e k is not positive. + * @exception GeographicErr if \e lat is not in [−90°, + * 90°]. + **********************************************************************/ + void SetScale(real lat, real k = real(1)); + + /** + * Forward projection, from geographic to Lambert conformal conic. + * + * @param[in] lon0 central meridian longitude (degrees). + * @param[in] lat latitude of point (degrees). + * @param[in] lon longitude of point (degrees). + * @param[out] x easting of point (meters). + * @param[out] y northing of point (meters). + * @param[out] gamma meridian convergence at point (degrees). + * @param[out] k scale of projection at point. + * + * The latitude origin is given by LambertConformalConic::LatitudeOrigin(). + * No false easting or northing is added and \e lat should be in the range + * [−90°, 90°]. The error in the projection is less than + * about 10 nm (10 nanometers), true distance, and the errors in the + * meridian convergence and scale are consistent with this. The values of + * \e x and \e y returned for points which project to infinity (i.e., one + * or both of the poles) will be large but finite. + **********************************************************************/ + void Forward(real lon0, real lat, real lon, + real& x, real& y, real& gamma, real& k) const; + + /** + * Reverse projection, from Lambert conformal conic to geographic. + * + * @param[in] lon0 central meridian longitude (degrees). + * @param[in] x easting of point (meters). + * @param[in] y northing of point (meters). + * @param[out] lat latitude of point (degrees). + * @param[out] lon longitude of point (degrees). + * @param[out] gamma meridian convergence at point (degrees). + * @param[out] k scale of projection at point. + * + * The latitude origin is given by LambertConformalConic::LatitudeOrigin(). + * No false easting or northing is added. The value of \e lon returned is + * in the range [−180°, 180°]. The error in the projection + * is less than about 10 nm (10 nanometers), true distance, and the errors + * in the meridian convergence and scale are consistent with this. + **********************************************************************/ + void Reverse(real lon0, real x, real y, + real& lat, real& lon, real& gamma, real& k) const; + + /** + * LambertConformalConic::Forward without returning the convergence and + * scale. + **********************************************************************/ + void Forward(real lon0, real lat, real lon, + real& x, real& y) const { + real gamma, k; + Forward(lon0, lat, lon, x, y, gamma, k); + } + + /** + * LambertConformalConic::Reverse without returning the convergence and + * scale. + **********************************************************************/ + void Reverse(real lon0, real x, real y, + real& lat, real& lon) const { + real gamma, k; + Reverse(lon0, x, y, lat, lon, gamma, k); + } + + /** \name Inspector functions + **********************************************************************/ + ///@{ + /** + * @return \e a the equatorial radius of the ellipsoid (meters). This is + * the value used in the constructor. + **********************************************************************/ + Math::real EquatorialRadius() const { return _a; } + + /** + * @return \e f the flattening of the ellipsoid. This is the + * value used in the constructor. + **********************************************************************/ + Math::real Flattening() const { return _f; } + + /** + * @return latitude of the origin for the projection (degrees). + * + * This is the latitude of minimum scale and equals the \e stdlat in the + * 1-parallel constructor and lies between \e stdlat1 and \e stdlat2 in the + * 2-parallel constructors. + **********************************************************************/ + Math::real OriginLatitude() const { return _lat0; } + + /** + * @return central scale for the projection. This is the scale on the + * latitude of origin. + **********************************************************************/ + Math::real CentralScale() const { return _k0; } + + /** + * \deprecated An old name for EquatorialRadius(). + **********************************************************************/ + GEOGRAPHICLIB_DEPRECATED("Use EquatorialRadius()") + Math::real MajorRadius() const { return EquatorialRadius(); } + ///@} + + /** + * A global instantiation of LambertConformalConic with the WGS84 + * ellipsoid, \e stdlat = 0, and \e k0 = 1. This degenerates to the + * Mercator projection. + **********************************************************************/ + static const LambertConformalConic& Mercator(); + }; + +} // namespace GeographicLib + +#endif // GEOGRAPHICLIB_LAMBERTCONFORMALCONIC_HPP diff --git a/common/local_libs/GeographicLib/include/GeographicLib/LocalCartesian.hpp b/common/local_libs/GeographicLib/include/GeographicLib/LocalCartesian.hpp new file mode 100644 index 0000000000..abb4f11d91 --- /dev/null +++ b/common/local_libs/GeographicLib/include/GeographicLib/LocalCartesian.hpp @@ -0,0 +1,244 @@ +/** + * \file LocalCartesian.hpp + * \brief Header for GeographicLib::LocalCartesian class + * + * Copyright (c) Charles Karney (2008-2020) and licensed + * under the MIT/X11 License. For more information, see + * https://geographiclib.sourceforge.io/ + **********************************************************************/ + +#if !defined(GEOGRAPHICLIB_LOCALCARTESIAN_HPP) +#define GEOGRAPHICLIB_LOCALCARTESIAN_HPP 1 + +#include +#include + +namespace GeographicLib { + + /** + * \brief Local cartesian coordinates + * + * Convert between geodetic coordinates latitude = \e lat, longitude = \e + * lon, height = \e h (measured vertically from the surface of the ellipsoid) + * to local cartesian coordinates (\e x, \e y, \e z). The origin of local + * cartesian coordinate system is at \e lat = \e lat0, \e lon = \e lon0, \e h + * = \e h0. The \e z axis is normal to the ellipsoid; the \e y axis points + * due north. The plane \e z = - \e h0 is tangent to the ellipsoid. + * + * The conversions all take place via geocentric coordinates using a + * Geocentric object (by default Geocentric::WGS84()). + * + * Example of use: + * \include example-LocalCartesian.cpp + * + * CartConvert is a command-line utility + * providing access to the functionality of Geocentric and LocalCartesian. + **********************************************************************/ + + class GEOGRAPHICLIB_EXPORT LocalCartesian { + private: + typedef Math::real real; + static const size_t dim_ = 3; + static const size_t dim2_ = dim_ * dim_; + Geocentric _earth; + real _lat0, _lon0, _h0; + real _x0, _y0, _z0, _r[dim2_]; + void IntForward(real lat, real lon, real h, real& x, real& y, real& z, + real M[dim2_]) const; + void IntReverse(real x, real y, real z, real& lat, real& lon, real& h, + real M[dim2_]) const; + void MatrixMultiply(real M[dim2_]) const; + public: + + /** + * Constructor setting the origin. + * + * @param[in] lat0 latitude at origin (degrees). + * @param[in] lon0 longitude at origin (degrees). + * @param[in] h0 height above ellipsoid at origin (meters); default 0. + * @param[in] earth Geocentric object for the transformation; default + * Geocentric::WGS84(). + * + * \e lat0 should be in the range [−90°, 90°]. + **********************************************************************/ + LocalCartesian(real lat0, real lon0, real h0 = 0, + const Geocentric& earth = Geocentric::WGS84()) + : _earth(earth) + { Reset(lat0, lon0, h0); } + + /** + * Default constructor. + * + * @param[in] earth Geocentric object for the transformation; default + * Geocentric::WGS84(). + * + * Sets \e lat0 = 0, \e lon0 = 0, \e h0 = 0. + **********************************************************************/ + explicit LocalCartesian(const Geocentric& earth = Geocentric::WGS84()) + : _earth(earth) + { Reset(real(0), real(0), real(0)); } + + /** + * Reset the origin. + * + * @param[in] lat0 latitude at origin (degrees). + * @param[in] lon0 longitude at origin (degrees). + * @param[in] h0 height above ellipsoid at origin (meters); default 0. + * + * \e lat0 should be in the range [−90°, 90°]. + **********************************************************************/ + void Reset(real lat0, real lon0, real h0 = 0); + + /** + * Convert from geodetic to local cartesian coordinates. + * + * @param[in] lat latitude of point (degrees). + * @param[in] lon longitude of point (degrees). + * @param[in] h height of point above the ellipsoid (meters). + * @param[out] x local cartesian coordinate (meters). + * @param[out] y local cartesian coordinate (meters). + * @param[out] z local cartesian coordinate (meters). + * + * \e lat should be in the range [−90°, 90°]. + **********************************************************************/ + void Forward(real lat, real lon, real h, real& x, real& y, real& z) + const { + IntForward(lat, lon, h, x, y, z, NULL); + } + + /** + * Convert from geodetic to local cartesian coordinates and return rotation + * matrix. + * + * @param[in] lat latitude of point (degrees). + * @param[in] lon longitude of point (degrees). + * @param[in] h height of point above the ellipsoid (meters). + * @param[out] x local cartesian coordinate (meters). + * @param[out] y local cartesian coordinate (meters). + * @param[out] z local cartesian coordinate (meters). + * @param[out] M if the length of the vector is 9, fill with the rotation + * matrix in row-major order. + * + * \e lat should be in the range [−90°, 90°]. + * + * Let \e v be a unit vector located at (\e lat, \e lon, \e h). We can + * express \e v as \e column vectors in one of two ways + * - in east, north, up coordinates (where the components are relative to a + * local coordinate system at (\e lat, \e lon, \e h)); call this + * representation \e v1. + * - in \e x, \e y, \e z coordinates (where the components are relative to + * the local coordinate system at (\e lat0, \e lon0, \e h0)); call this + * representation \e v0. + * . + * Then we have \e v0 = \e M ⋅ \e v1. + **********************************************************************/ + void Forward(real lat, real lon, real h, real& x, real& y, real& z, + std::vector& M) + const { + if (M.end() == M.begin() + dim2_) { + real t[dim2_]; + IntForward(lat, lon, h, x, y, z, t); + std::copy(t, t + dim2_, M.begin()); + } else + IntForward(lat, lon, h, x, y, z, NULL); + } + + /** + * Convert from local cartesian to geodetic coordinates. + * + * @param[in] x local cartesian coordinate (meters). + * @param[in] y local cartesian coordinate (meters). + * @param[in] z local cartesian coordinate (meters). + * @param[out] lat latitude of point (degrees). + * @param[out] lon longitude of point (degrees). + * @param[out] h height of point above the ellipsoid (meters). + * + * In general, there are multiple solutions and the result which minimizes + * |h |is returned, i.e., (lat, lon) corresponds to + * the closest point on the ellipsoid. The value of \e lon returned is in + * the range [−180°, 180°]. + **********************************************************************/ + void Reverse(real x, real y, real z, real& lat, real& lon, real& h) + const { + IntReverse(x, y, z, lat, lon, h, NULL); + } + + /** + * Convert from local cartesian to geodetic coordinates and return rotation + * matrix. + * + * @param[in] x local cartesian coordinate (meters). + * @param[in] y local cartesian coordinate (meters). + * @param[in] z local cartesian coordinate (meters). + * @param[out] lat latitude of point (degrees). + * @param[out] lon longitude of point (degrees). + * @param[out] h height of point above the ellipsoid (meters). + * @param[out] M if the length of the vector is 9, fill with the rotation + * matrix in row-major order. + * + * Let \e v be a unit vector located at (\e lat, \e lon, \e h). We can + * express \e v as \e column vectors in one of two ways + * - in east, north, up coordinates (where the components are relative to a + * local coordinate system at (\e lat, \e lon, \e h)); call this + * representation \e v1. + * - in \e x, \e y, \e z coordinates (where the components are relative to + * the local coordinate system at (\e lat0, \e lon0, \e h0)); call this + * representation \e v0. + * . + * Then we have \e v1 = MT ⋅ \e v0, where + * MT is the transpose of \e M. + **********************************************************************/ + void Reverse(real x, real y, real z, real& lat, real& lon, real& h, + std::vector& M) + const { + if (M.end() == M.begin() + dim2_) { + real t[dim2_]; + IntReverse(x, y, z, lat, lon, h, t); + std::copy(t, t + dim2_, M.begin()); + } else + IntReverse(x, y, z, lat, lon, h, NULL); + } + + /** \name Inspector functions + **********************************************************************/ + ///@{ + /** + * @return latitude of the origin (degrees). + **********************************************************************/ + Math::real LatitudeOrigin() const { return _lat0; } + + /** + * @return longitude of the origin (degrees). + **********************************************************************/ + Math::real LongitudeOrigin() const { return _lon0; } + + /** + * @return height of the origin (meters). + **********************************************************************/ + Math::real HeightOrigin() const { return _h0; } + + /** + * @return \e a the equatorial radius of the ellipsoid (meters). This is + * the value of \e a inherited from the Geocentric object used in the + * constructor. + **********************************************************************/ + Math::real EquatorialRadius() const { return _earth.EquatorialRadius(); } + + /** + * @return \e f the flattening of the ellipsoid. This is the value + * inherited from the Geocentric object used in the constructor. + **********************************************************************/ + Math::real Flattening() const { return _earth.Flattening(); } + + /** + * \deprecated An old name for EquatorialRadius(). + **********************************************************************/ + GEOGRAPHICLIB_DEPRECATED("Use EquatorialRadius()") + Math::real MajorRadius() const { return EquatorialRadius(); } + ///@} + + }; + +} // namespace GeographicLib + +#endif // GEOGRAPHICLIB_LOCALCARTESIAN_HPP diff --git a/common/local_libs/GeographicLib/include/GeographicLib/MGRS.hpp b/common/local_libs/GeographicLib/include/GeographicLib/MGRS.hpp new file mode 100644 index 0000000000..b6a6f6888a --- /dev/null +++ b/common/local_libs/GeographicLib/include/GeographicLib/MGRS.hpp @@ -0,0 +1,361 @@ +/** + * \file MGRS.hpp + * \brief Header for GeographicLib::MGRS class + * + * Copyright (c) Charles Karney (2008-2020) and licensed + * under the MIT/X11 License. For more information, see + * https://geographiclib.sourceforge.io/ + **********************************************************************/ + +#if !defined(GEOGRAPHICLIB_MGRS_HPP) +#define GEOGRAPHICLIB_MGRS_HPP 1 + +#include +#include + +#if defined(_MSC_VER) +// Squelch warnings about dll vs string +# pragma warning (push) +# pragma warning (disable: 4251) +#endif + +namespace GeographicLib { + + /** + * \brief Convert between UTM/UPS and %MGRS + * + * MGRS is defined in Chapter 3 of + * - J. W. Hager, L. L. Fry, S. S. Jacks, D. R. Hill, + * + * Datums, Ellipsoids, Grids, and Grid Reference Systems, + * Defense Mapping Agency, Technical Manual TM8358.1 (1990). + * . + * This document has been updated by the two NGA documents + * - + * Universal Grids and Grid Reference Systems, + * NGA.STND.0037_2.0.0_GRIDS (2014). + * - + * The Universal Grids and the Transverse Mercator and Polar Stereographic + * Map Projections, NGA.SIG.0012_2.0.0_UTMUPS (2014). + * + * This implementation has the following properties: + * - The conversions are closed, i.e., output from Forward is legal input for + * Reverse and vice versa. Conversion in both directions preserve the + * UTM/UPS selection and the UTM zone. + * - Forward followed by Reverse and vice versa is approximately the + * identity. (This is affected in predictable ways by errors in + * determining the latitude band and by loss of precision in the MGRS + * coordinates.) + * - The trailing digits produced by Forward are consistent as the precision + * is varied. Specifically, the digits are obtained by operating on the + * easting with ⌊106 x⌋ and extracting the + * required digits from the resulting number (and similarly for the + * northing). + * - All MGRS coordinates truncate to legal 100 km blocks. All MGRS + * coordinates with a legal 100 km block prefix are legal (even though the + * latitude band letter may now belong to a neighboring band). + * - The range of UTM/UPS coordinates allowed for conversion to MGRS + * coordinates is the maximum consistent with staying within the letter + * ranges of the MGRS scheme. + * - All the transformations are implemented as static methods in the MGRS + * class. + * + * The NGA software package + * geotrans + * also provides conversions to and from MGRS. Version 3.0 (and earlier) + * suffers from some drawbacks: + * - Inconsistent rules are used to determine the whether a particular MGRS + * coordinate is legal. A more systematic approach is taken here. + * - The underlying projections are not very accurately implemented. + * + * Example of use: + * \include example-MGRS.cpp + **********************************************************************/ + class GEOGRAPHICLIB_EXPORT MGRS { + private: + typedef Math::real real; + static const char* const hemispheres_; + static const char* const utmcols_[3]; + static const char* const utmrow_; + static const char* const upscols_[4]; + static const char* const upsrows_[2]; + static const char* const latband_; + static const char* const upsband_; + static const char* const digits_; + + static const int mineasting_[4]; + static const int maxeasting_[4]; + static const int minnorthing_[4]; + static const int maxnorthing_[4]; + enum { + base_ = 10, + // Top-level tiles are 10^5 m = 100 km on a side + tilelevel_ = 5, + // Period of UTM row letters + utmrowperiod_ = 20, + // Row letters are shifted by 5 for even zones + utmevenrowshift_ = 5, + // Maximum precision is um + maxprec_ = 5 + 6, + // For generating digits at maxprec + mult_ = 1000000, + }; + static void CheckCoords(bool utmp, bool& northp, real& x, real& y); + static int UTMRow(int iband, int icol, int irow); + + friend class UTMUPS; // UTMUPS::StandardZone calls LatitudeBand + // Return latitude band number [-10, 10) for the given latitude (degrees). + // The bands are reckoned in include their southern edges. + static int LatitudeBand(real lat) { + using std::floor; + int ilat = int(floor(lat)); + return (std::max)(-10, (std::min)(9, (ilat + 80)/8 - 10)); + } + // Return approximate latitude band number [-10, 10) for the given northing + // (meters). With this rule, each 100km tile would have a unique band + // letter corresponding to the latitude at the center of the tile. This + // function isn't currently used. + static int ApproxLatitudeBand(real y) { + // northing at tile center in units of tile = 100km + using std::floor; using std::abs; + real ya = floor( (std::min)(real(88), abs(y/tile_)) ) + + real(0.5); + // convert to lat (mult by 90/100) and then to band (divide by 8) + // the +1 fine tunes the boundary between bands 3 and 4 + int b = int(floor( ((ya * 9 + 1) / 10) / 8 )); + // For the northern hemisphere we have + // band rows num + // N 0 0:8 9 + // P 1 9:17 9 + // Q 2 18:26 9 + // R 3 27:34 8 + // S 4 35:43 9 + // T 5 44:52 9 + // U 6 53:61 9 + // V 7 62:70 9 + // W 8 71:79 9 + // X 9 80:94 15 + return y >= 0 ? b : -(b + 1); + } + // UTMUPS access these enums + enum { + tile_ = 100000, // Size MGRS blocks + minutmcol_ = 1, + maxutmcol_ = 9, + minutmSrow_ = 10, + maxutmSrow_ = 100, // Also used for UTM S false northing + minutmNrow_ = 0, // Also used for UTM N false northing + maxutmNrow_ = 95, + minupsSind_ = 8, // These 4 ind's apply to easting and northing + maxupsSind_ = 32, + minupsNind_ = 13, + maxupsNind_ = 27, + upseasting_ = 20, // Also used for UPS false northing + utmeasting_ = 5, // UTM false easting + // Difference between S hemisphere northing and N hemisphere northing + utmNshift_ = (maxutmSrow_ - minutmNrow_) * tile_ + }; + MGRS(); // Disable constructor + + public: + + /** + * Convert UTM or UPS coordinate to an MGRS coordinate. + * + * @param[in] zone UTM zone (zero means UPS). + * @param[in] northp hemisphere (true means north, false means south). + * @param[in] x easting of point (meters). + * @param[in] y northing of point (meters). + * @param[in] prec precision relative to 100 km. + * @param[out] mgrs MGRS string. + * @exception GeographicErr if \e zone, \e x, or \e y is outside its + * allowed range. + * @exception GeographicErr if the memory for the MGRS string can't be + * allocated. + * + * \e prec specifies the precision of the MGRS string as follows: + * - \e prec = −1 (min), only the grid zone is returned + * - \e prec = 0, 100 km + * - \e prec = 1, 10 km + * - \e prec = 2, 1 km + * - \e prec = 3, 100 m + * - \e prec = 4, 10 m + * - \e prec = 5, 1 m + * - \e prec = 6, 0.1 m + * - … + * - \e prec = 11 (max), 1 μm + * + * UTM eastings are allowed to be in the range [100 km, 900 km], northings + * are allowed to be in in [0 km, 9500 km] for the northern hemisphere and + * in [1000 km, 10000 km] for the southern hemisphere. (However UTM + * northings can be continued across the equator. So the actual limits on + * the northings are [−9000 km, 9500 km] for the "northern" + * hemisphere and [1000 km, 19500 km] for the "southern" hemisphere.) + * + * UPS eastings/northings are allowed to be in the range [1300 km, 2700 km] + * in the northern hemisphere and in [800 km, 3200 km] in the southern + * hemisphere. + * + * The ranges are 100 km more restrictive than for the conversion between + * geographic coordinates and UTM and UPS given by UTMUPS. These + * restrictions are dictated by the allowed letters in MGRS coordinates. + * The choice of 9500 km for the maximum northing for northern hemisphere + * and of 1000 km as the minimum northing for southern hemisphere provide + * at least 0.5 degree extension into standard UPS zones. The upper ends + * of the ranges for the UPS coordinates is dictated by requiring symmetry + * about the meridians 0E and 90E. + * + * All allowed UTM and UPS coordinates may now be converted to legal MGRS + * coordinates with the proviso that eastings and northings on the upper + * boundaries are silently reduced by about 4 nm (4 nanometers) to place + * them \e within the allowed range. (This includes reducing a southern + * hemisphere northing of 10000 km by 4 nm so that it is placed in latitude + * band M.) The UTM or UPS coordinates are truncated to requested + * precision to determine the MGRS coordinate. Thus in UTM zone 38n, the + * square area with easting in [444 km, 445 km) and northing in [3688 km, + * 3689 km) maps to MGRS coordinate 38SMB4488 (at \e prec = 2, 1 km), + * Khulani Sq., Baghdad. + * + * The UTM/UPS selection and the UTM zone is preserved in the conversion to + * MGRS coordinate. Thus for \e zone > 0, the MGRS coordinate begins with + * the zone number followed by one of [C--M] for the southern + * hemisphere and [N--X] for the northern hemisphere. For \e zone = + * 0, the MGRS coordinates begins with one of [AB] for the southern + * hemisphere and [XY] for the northern hemisphere. + * + * The conversion to the MGRS is exact for prec in [0, 5] except that a + * neighboring latitude band letter may be given if the point is within 5nm + * of a band boundary. For prec in [6, 11], the conversion is accurate to + * roundoff. + * + * If \e prec = −1, then the "grid zone designation", e.g., 18T, is + * returned. This consists of the UTM zone number (absent for UPS) and the + * first letter of the MGRS string which labels the latitude band for UTM + * and the hemisphere for UPS. + * + * If \e x or \e y is NaN or if \e zone is UTMUPS::INVALID, the returned + * MGRS string is "INVALID". + * + * Return the result via a reference argument to avoid the overhead of + * allocating a potentially large number of small strings. If an error is + * thrown, then \e mgrs is unchanged. + **********************************************************************/ + static void Forward(int zone, bool northp, real x, real y, + int prec, std::string& mgrs); + + /** + * Convert UTM or UPS coordinate to an MGRS coordinate when the latitude is + * known. + * + * @param[in] zone UTM zone (zero means UPS). + * @param[in] northp hemisphere (true means north, false means south). + * @param[in] x easting of point (meters). + * @param[in] y northing of point (meters). + * @param[in] lat latitude (degrees). + * @param[in] prec precision relative to 100 km. + * @param[out] mgrs MGRS string. + * @exception GeographicErr if \e zone, \e x, or \e y is outside its + * allowed range. + * @exception GeographicErr if \e lat is inconsistent with the given UTM + * coordinates. + * @exception std::bad_alloc if the memory for \e mgrs can't be allocated. + * + * The latitude is ignored for \e zone = 0 (UPS); otherwise the latitude is + * used to determine the latitude band and this is checked for consistency + * using the same tests as Reverse. + **********************************************************************/ + static void Forward(int zone, bool northp, real x, real y, real lat, + int prec, std::string& mgrs); + + /** + * Convert a MGRS coordinate to UTM or UPS coordinates. + * + * @param[in] mgrs MGRS string. + * @param[out] zone UTM zone (zero means UPS). + * @param[out] northp hemisphere (true means north, false means south). + * @param[out] x easting of point (meters). + * @param[out] y northing of point (meters). + * @param[out] prec precision relative to 100 km. + * @param[in] centerp if true (default), return center of the MGRS square, + * else return SW (lower left) corner. + * @exception GeographicErr if \e mgrs is illegal. + * + * All conversions from MGRS to UTM/UPS are permitted provided the MGRS + * coordinate is a possible result of a conversion in the other direction. + * (The leading 0 may be dropped from an input MGRS coordinate for UTM + * zones 1--9.) In addition, MGRS coordinates with a neighboring + * latitude band letter are permitted provided that some portion of the + * 100 km block is within the given latitude band. Thus + * - 38VLS and 38WLS are allowed (latitude 64N intersects the square + * 38[VW]LS); but 38VMS is not permitted (all of 38WMS is north of 64N) + * - 38MPE and 38NPF are permitted (they straddle the equator); but 38NPE + * and 38MPF are not permitted (the equator does not intersect either + * block). + * - Similarly ZAB and YZB are permitted (they straddle the prime + * meridian); but YAB and ZZB are not (the prime meridian does not + * intersect either block). + * + * The UTM/UPS selection and the UTM zone is preserved in the conversion + * from MGRS coordinate. The conversion is exact for prec in [0, 5]. With + * \e centerp = true, the conversion from MGRS to geographic and back is + * stable. This is not assured if \e centerp = false. + * + * If a "grid zone designation" (for example, 18T or A) is given, then some + * suitable (but essentially arbitrary) point within that grid zone is + * returned. The main utility of the conversion is to allow \e zone and \e + * northp to be determined. In this case, the \e centerp parameter is + * ignored and \e prec is set to −1. + * + * If the first 3 characters of \e mgrs are "INV", then \e x and \e y are + * set to NaN, \e zone is set to UTMUPS::INVALID, and \e prec is set to + * −2. + * + * If an exception is thrown, then the arguments are unchanged. + **********************************************************************/ + static void Reverse(const std::string& mgrs, + int& zone, bool& northp, real& x, real& y, + int& prec, bool centerp = true); + + /** \name Inspector functions + **********************************************************************/ + ///@{ + /** + * @return \e a the equatorial radius of the WGS84 ellipsoid (meters). + * + * (The WGS84 value is returned because the UTM and UPS projections are + * based on this ellipsoid.) + **********************************************************************/ + static Math::real EquatorialRadius() { return UTMUPS::EquatorialRadius(); } + + /** + * @return \e f the flattening of the WGS84 ellipsoid. + * + * (The WGS84 value is returned because the UTM and UPS projections are + * based on this ellipsoid.) + **********************************************************************/ + static Math::real Flattening() { return UTMUPS::Flattening(); } + + /** + * \deprecated An old name for EquatorialRadius(). + **********************************************************************/ + GEOGRAPHICLIB_DEPRECATED("Use EquatorialRadius()") + static Math::real MajorRadius() { return EquatorialRadius(); } + ///@} + + /** + * Perform some checks on the UTMUPS coordinates on this ellipsoid. Throw + * an error if any of the assumptions made in the MGRS class is not true. + * This check needs to be carried out if the ellipsoid parameters (or the + * UTM/UPS scales) are ever changed. + **********************************************************************/ + static void Check(); + + }; + +} // namespace GeographicLib + +#if defined(_MSC_VER) +# pragma warning (pop) +#endif + +#endif // GEOGRAPHICLIB_MGRS_HPP diff --git a/common/local_libs/GeographicLib/include/GeographicLib/MagneticCircle.hpp b/common/local_libs/GeographicLib/include/GeographicLib/MagneticCircle.hpp new file mode 100644 index 0000000000..e5d54087f3 --- /dev/null +++ b/common/local_libs/GeographicLib/include/GeographicLib/MagneticCircle.hpp @@ -0,0 +1,185 @@ +/** + * \file MagneticCircle.hpp + * \brief Header for GeographicLib::MagneticCircle class + * + * Copyright (c) Charles Karney (2011-2020) and licensed + * under the MIT/X11 License. For more information, see + * https://geographiclib.sourceforge.io/ + **********************************************************************/ + +#if !defined(GEOGRAPHICLIB_MAGNETICCIRCLE_HPP) +#define GEOGRAPHICLIB_MAGNETICCIRCLE_HPP 1 + +#include +#include +#include + +namespace GeographicLib { + + /** + * \brief Geomagnetic field on a circle of latitude + * + * Evaluate the earth's magnetic field on a circle of constant height and + * latitude. This uses a CircularEngine to pre-evaluate the inner sum of the + * spherical harmonic sum, allowing the values of the field at several + * different longitudes to be evaluated rapidly. + * + * Use MagneticModel::Circle to create a MagneticCircle object. (The + * constructor for this class is private.) + * + * Example of use: + * \include example-MagneticCircle.cpp + * + * MagneticField is a command-line utility + * providing access to the functionality of MagneticModel and MagneticCircle. + **********************************************************************/ + + class GEOGRAPHICLIB_EXPORT MagneticCircle { + private: + typedef Math::real real; + + real _a, _f, _lat, _h, _t, _cphi, _sphi, _t1, _dt0; + bool _interpolate, _constterm; + CircularEngine _circ0, _circ1, _circ2; + + MagneticCircle(real a, real f, real lat, real h, real t, + real cphi, real sphi, real t1, real dt0, + bool interpolate, + const CircularEngine& circ0, const CircularEngine& circ1) + : _a(a) + , _f(f) + , _lat(Math::LatFix(lat)) + , _h(h) + , _t(t) + , _cphi(cphi) + , _sphi(sphi) + , _t1(t1) + , _dt0(dt0) + , _interpolate(interpolate) + , _constterm(false) + , _circ0(circ0) + , _circ1(circ1) + {} + + MagneticCircle(real a, real f, real lat, real h, real t, + real cphi, real sphi, real t1, real dt0, + bool interpolate, + const CircularEngine& circ0, const CircularEngine& circ1, + const CircularEngine& circ2) + : _a(a) + , _f(f) + , _lat(lat) + , _h(h) + , _t(t) + , _cphi(cphi) + , _sphi(sphi) + , _t1(t1) + , _dt0(dt0) + , _interpolate(interpolate) + , _constterm(true) + , _circ0(circ0) + , _circ1(circ1) + , _circ2(circ2) + {} + + void Field(real lon, bool diffp, + real& Bx, real& By, real& Bz, + real& Bxt, real& Byt, real& Bzt) const; + + friend class MagneticModel; // MagneticModel calls the private constructor + + public: + + /** + * A default constructor for the normal gravity. This sets up an + * uninitialized object which can be later replaced by the + * MagneticModel::Circle. + **********************************************************************/ + MagneticCircle() : _a(-1) {} + + /** \name Compute the magnetic field + **********************************************************************/ + ///@{ + /** + * Evaluate the components of the geomagnetic field at a particular + * longitude. + * + * @param[in] lon longitude of the point (degrees). + * @param[out] Bx the easterly component of the magnetic field (nanotesla). + * @param[out] By the northerly component of the magnetic field + * (nanotesla). + * @param[out] Bz the vertical (up) component of the magnetic field + * (nanotesla). + **********************************************************************/ + void operator()(real lon, real& Bx, real& By, real& Bz) const { + real dummy; + Field(lon, false, Bx, By, Bz, dummy, dummy, dummy); + } + + /** + * Evaluate the components of the geomagnetic field and their time + * derivatives at a particular longitude. + * + * @param[in] lon longitude of the point (degrees). + * @param[out] Bx the easterly component of the magnetic field (nanotesla). + * @param[out] By the northerly component of the magnetic field + * (nanotesla). + * @param[out] Bz the vertical (up) component of the magnetic field + * (nanotesla). + * @param[out] Bxt the rate of change of \e Bx (nT/yr). + * @param[out] Byt the rate of change of \e By (nT/yr). + * @param[out] Bzt the rate of change of \e Bz (nT/yr). + **********************************************************************/ + void operator()(real lon, real& Bx, real& By, real& Bz, + real& Bxt, real& Byt, real& Bzt) const { + Field(lon, true, Bx, By, Bz, Bxt, Byt, Bzt); + } + ///@} + + /** \name Inspector functions + **********************************************************************/ + ///@{ + /** + * @return true if the object has been initialized. + **********************************************************************/ + bool Init() const { return _a > 0; } + /** + * @return \e a the equatorial radius of the ellipsoid (meters). This is + * the value inherited from the MagneticModel object used in the + * constructor. + **********************************************************************/ + Math::real EquatorialRadius() const + { return Init() ? _a : Math::NaN(); } + /** + * @return \e f the flattening of the ellipsoid. This is the value + * inherited from the MagneticModel object used in the constructor. + **********************************************************************/ + Math::real Flattening() const + { return Init() ? _f : Math::NaN(); } + /** + * @return the latitude of the circle (degrees). + **********************************************************************/ + Math::real Latitude() const + { return Init() ? _lat : Math::NaN(); } + /** + * @return the height of the circle (meters). + **********************************************************************/ + Math::real Height() const + { return Init() ? _h : Math::NaN(); } + /** + * @return the time (fractional years). + **********************************************************************/ + Math::real Time() const + { return Init() ? _t : Math::NaN(); } + + /** + * \deprecated An old name for EquatorialRadius(). + **********************************************************************/ + GEOGRAPHICLIB_DEPRECATED("Use EquatorialRadius()") + Math::real MajorRadius() const { return EquatorialRadius(); } + ///@} + }; + +} // namespace GeographicLib + +#endif // GEOGRAPHICLIB_MAGNETICCIRCLE_HPP diff --git a/common/local_libs/GeographicLib/include/GeographicLib/MagneticModel.hpp b/common/local_libs/GeographicLib/include/GeographicLib/MagneticModel.hpp new file mode 100644 index 0000000000..99fb4a1649 --- /dev/null +++ b/common/local_libs/GeographicLib/include/GeographicLib/MagneticModel.hpp @@ -0,0 +1,386 @@ +/** + * \file MagneticModel.hpp + * \brief Header for GeographicLib::MagneticModel class + * + * Copyright (c) Charles Karney (2011-2020) and licensed + * under the MIT/X11 License. For more information, see + * https://geographiclib.sourceforge.io/ + **********************************************************************/ + +#if !defined(GEOGRAPHICLIB_MAGNETICMODEL_HPP) +#define GEOGRAPHICLIB_MAGNETICMODEL_HPP 1 + +#include +#include +#include + +#if defined(_MSC_VER) +// Squelch warnings about dll vs vector +# pragma warning (push) +# pragma warning (disable: 4251) +#endif + +namespace GeographicLib { + + class MagneticCircle; + + /** + * \brief Model of the earth's magnetic field + * + * Evaluate the earth's magnetic field according to a model. At present only + * internal magnetic fields are handled. These are due to the earth's code + * and crust; these vary slowly (over many years). Excluded are the effects + * of currents in the ionosphere and magnetosphere which have daily and + * annual variations. + * + * See \ref magnetic for details of how to install the magnetic models and + * the data format. + * + * See + * - General information: + * - http://geomag.org/models/index.html + * - WMM2010: + * - https://ngdc.noaa.gov/geomag/WMM/DoDWMM.shtml + * - https://ngdc.noaa.gov/geomag/WMM/data/WMM2010/WMM2010COF.zip + * - WMM2015 (deprecated): + * - https://ngdc.noaa.gov/geomag/WMM/DoDWMM.shtml + * - https://ngdc.noaa.gov/geomag/WMM/data/WMM2015/WMM2015COF.zip + * - WMM2015V2: + * - https://ngdc.noaa.gov/geomag/WMM/DoDWMM.shtml + * - https://ngdc.noaa.gov/geomag/WMM/data/WMM2015/WMM2015v2COF.zip + * - WMM2020: + * - https://ngdc.noaa.gov/geomag/WMM/DoDWMM.shtml + * - https://ngdc.noaa.gov/geomag/WMM/data/WMM2020/WMM2020COF.zip + * - IGRF11: + * - https://ngdc.noaa.gov/IAGA/vmod/igrf.html + * - https://ngdc.noaa.gov/IAGA/vmod/igrf11coeffs.txt + * - https://ngdc.noaa.gov/IAGA/vmod/geomag70_linux.tar.gz + * - EMM2010: + * - https://ngdc.noaa.gov/geomag/EMM/index.html + * - https://ngdc.noaa.gov/geomag/EMM/data/geomag/EMM2010_Sph_Windows_Linux.zip + * - EMM2015: + * - https://ngdc.noaa.gov/geomag/EMM/index.html + * - https://www.ngdc.noaa.gov/geomag/EMM/data/geomag/EMM2015_Sph_Linux.zip + * - EMM2017: + * - https://ngdc.noaa.gov/geomag/EMM/index.html + * - https://www.ngdc.noaa.gov/geomag/EMM/data/geomag/EMM2017_Sph_Linux.zip + * + * Example of use: + * \include example-MagneticModel.cpp + * + * MagneticField is a command-line utility + * providing access to the functionality of MagneticModel and MagneticCircle. + **********************************************************************/ + + class GEOGRAPHICLIB_EXPORT MagneticModel { + private: + typedef Math::real real; + static const int idlength_ = 8; + std::string _name, _dir, _description, _date, _filename, _id; + real _t0, _dt0, _tmin, _tmax, _a, _hmin, _hmax; + int _Nmodels, _Nconstants, _nmx, _mmx; + SphericalHarmonic::normalization _norm; + Geocentric _earth; + std::vector< std::vector > _G; + std::vector< std::vector > _H; + std::vector _harm; + void Field(real t, real lat, real lon, real h, bool diffp, + real& Bx, real& By, real& Bz, + real& Bxt, real& Byt, real& Bzt) const; + void ReadMetadata(const std::string& name); + MagneticModel(const MagneticModel&); // copy constructor not allowed + MagneticModel& operator=(const MagneticModel&); // nor copy assignment + public: + + /** \name Setting up the magnetic model + **********************************************************************/ + ///@{ + /** + * Construct a magnetic model. + * + * @param[in] name the name of the model. + * @param[in] path (optional) directory for data file. + * @param[in] earth (optional) Geocentric object for converting + * coordinates; default Geocentric::WGS84(). + * @param[in] Nmax (optional) if non-negative, truncate the degree of the + * model this value. + * @param[in] Mmax (optional) if non-negative, truncate the order of the + * model this value. + * @exception GeographicErr if the data file cannot be found, is + * unreadable, or is corrupt, or if \e Mmax > \e Nmax. + * @exception std::bad_alloc if the memory necessary for storing the model + * can't be allocated. + * + * A filename is formed by appending ".wmm" (World Magnetic Model) to the + * name. If \e path is specified (and is non-empty), then the file is + * loaded from directory, \e path. Otherwise the path is given by the + * DefaultMagneticPath(). + * + * This file contains the metadata which specifies the properties of the + * model. The coefficients for the spherical harmonic sums are obtained + * from a file obtained by appending ".cof" to metadata file (so the + * filename ends in ".wwm.cof"). + * + * The model is not tied to a particular ellipsoidal model of the earth. + * The final earth argument to the constructor specifies an ellipsoid to + * allow geodetic coordinates to the transformed into the spherical + * coordinates used in the spherical harmonic sum. + * + * If \e Nmax ≥ 0 and \e Mmax < 0, then \e Mmax is set to \e Nmax. + * After the model is loaded, the maximum degree and order of the model can + * be found by the Degree() and Order() methods. + **********************************************************************/ + explicit MagneticModel(const std::string& name, + const std::string& path = "", + const Geocentric& earth = Geocentric::WGS84(), + int Nmax = -1, int Mmax = -1); + ///@} + + /** \name Compute the magnetic field + **********************************************************************/ + ///@{ + /** + * Evaluate the components of the geomagnetic field. + * + * @param[in] t the time (years). + * @param[in] lat latitude of the point (degrees). + * @param[in] lon longitude of the point (degrees). + * @param[in] h the height of the point above the ellipsoid (meters). + * @param[out] Bx the easterly component of the magnetic field (nanotesla). + * @param[out] By the northerly component of the magnetic field + * (nanotesla). + * @param[out] Bz the vertical (up) component of the magnetic field + * (nanotesla). + **********************************************************************/ + void operator()(real t, real lat, real lon, real h, + real& Bx, real& By, real& Bz) const { + real dummy; + Field(t, lat, lon, h, false, Bx, By, Bz, dummy, dummy, dummy); + } + + /** + * Evaluate the components of the geomagnetic field and their time + * derivatives + * + * @param[in] t the time (years). + * @param[in] lat latitude of the point (degrees). + * @param[in] lon longitude of the point (degrees). + * @param[in] h the height of the point above the ellipsoid (meters). + * @param[out] Bx the easterly component of the magnetic field (nanotesla). + * @param[out] By the northerly component of the magnetic field + * (nanotesla). + * @param[out] Bz the vertical (up) component of the magnetic field + * (nanotesla). + * @param[out] Bxt the rate of change of \e Bx (nT/yr). + * @param[out] Byt the rate of change of \e By (nT/yr). + * @param[out] Bzt the rate of change of \e Bz (nT/yr). + **********************************************************************/ + void operator()(real t, real lat, real lon, real h, + real& Bx, real& By, real& Bz, + real& Bxt, real& Byt, real& Bzt) const { + Field(t, lat, lon, h, true, Bx, By, Bz, Bxt, Byt, Bzt); + } + + /** + * Create a MagneticCircle object to allow the geomagnetic field at many + * points with constant \e lat, \e h, and \e t and varying \e lon to be + * computed efficiently. + * + * @param[in] t the time (years). + * @param[in] lat latitude of the point (degrees). + * @param[in] h the height of the point above the ellipsoid (meters). + * @exception std::bad_alloc if the memory necessary for creating a + * MagneticCircle can't be allocated. + * @return a MagneticCircle object whose MagneticCircle::operator()(real + * lon) member function computes the field at particular values of \e + * lon. + * + * If the field at several points on a circle of latitude need to be + * calculated then creating a MagneticCircle and using its member functions + * will be substantially faster, especially for high-degree models. + **********************************************************************/ + MagneticCircle Circle(real t, real lat, real h) const; + + /** + * Compute various quantities dependent on the magnetic field. + * + * @param[in] Bx the \e x (easterly) component of the magnetic field (nT). + * @param[in] By the \e y (northerly) component of the magnetic field (nT). + * @param[in] Bz the \e z (vertical, up positive) component of the magnetic + * field (nT). + * @param[out] H the horizontal magnetic field (nT). + * @param[out] F the total magnetic field (nT). + * @param[out] D the declination of the field (degrees east of north). + * @param[out] I the inclination of the field (degrees down from + * horizontal). + **********************************************************************/ + static void FieldComponents(real Bx, real By, real Bz, + real& H, real& F, real& D, real& I) { + real Ht, Ft, Dt, It; + FieldComponents(Bx, By, Bz, real(0), real(1), real(0), + H, F, D, I, Ht, Ft, Dt, It); + } + + /** + * Compute various quantities dependent on the magnetic field and its rate + * of change. + * + * @param[in] Bx the \e x (easterly) component of the magnetic field (nT). + * @param[in] By the \e y (northerly) component of the magnetic field (nT). + * @param[in] Bz the \e z (vertical, up positive) component of the magnetic + * field (nT). + * @param[in] Bxt the rate of change of \e Bx (nT/yr). + * @param[in] Byt the rate of change of \e By (nT/yr). + * @param[in] Bzt the rate of change of \e Bz (nT/yr). + * @param[out] H the horizontal magnetic field (nT). + * @param[out] F the total magnetic field (nT). + * @param[out] D the declination of the field (degrees east of north). + * @param[out] I the inclination of the field (degrees down from + * horizontal). + * @param[out] Ht the rate of change of \e H (nT/yr). + * @param[out] Ft the rate of change of \e F (nT/yr). + * @param[out] Dt the rate of change of \e D (degrees/yr). + * @param[out] It the rate of change of \e I (degrees/yr). + **********************************************************************/ + static void FieldComponents(real Bx, real By, real Bz, + real Bxt, real Byt, real Bzt, + real& H, real& F, real& D, real& I, + real& Ht, real& Ft, real& Dt, real& It); + ///@} + + /** \name Inspector functions + **********************************************************************/ + ///@{ + /** + * @return the description of the magnetic model, if available, from the + * Description file in the data file; if absent, return "NONE". + **********************************************************************/ + const std::string& Description() const { return _description; } + + /** + * @return date of the model, if available, from the ReleaseDate field in + * the data file; if absent, return "UNKNOWN". + **********************************************************************/ + const std::string& DateTime() const { return _date; } + + /** + * @return full file name used to load the magnetic model. + **********************************************************************/ + const std::string& MagneticFile() const { return _filename; } + + /** + * @return "name" used to load the magnetic model (from the first argument + * of the constructor, but this may be overridden by the model file). + **********************************************************************/ + const std::string& MagneticModelName() const { return _name; } + + /** + * @return directory used to load the magnetic model. + **********************************************************************/ + const std::string& MagneticModelDirectory() const { return _dir; } + + /** + * @return the minimum height above the ellipsoid (in meters) for which + * this MagneticModel should be used. + * + * Because the model will typically provide useful results + * slightly outside the range of allowed heights, no check of \e t + * argument is made by MagneticModel::operator()() or + * MagneticModel::Circle. + **********************************************************************/ + Math::real MinHeight() const { return _hmin; } + + /** + * @return the maximum height above the ellipsoid (in meters) for which + * this MagneticModel should be used. + * + * Because the model will typically provide useful results + * slightly outside the range of allowed heights, no check of \e t + * argument is made by MagneticModel::operator()() or + * MagneticModel::Circle. + **********************************************************************/ + Math::real MaxHeight() const { return _hmax; } + + /** + * @return the minimum time (in years) for which this MagneticModel should + * be used. + * + * Because the model will typically provide useful results + * slightly outside the range of allowed times, no check of \e t + * argument is made by MagneticModel::operator()() or + * MagneticModel::Circle. + **********************************************************************/ + Math::real MinTime() const { return _tmin; } + + /** + * @return the maximum time (in years) for which this MagneticModel should + * be used. + * + * Because the model will typically provide useful results + * slightly outside the range of allowed times, no check of \e t + * argument is made by MagneticModel::operator()() or + * MagneticModel::Circle. + **********************************************************************/ + Math::real MaxTime() const { return _tmax; } + + /** + * @return \e a the equatorial radius of the ellipsoid (meters). This is + * the value of \e a inherited from the Geocentric object used in the + * constructor. + **********************************************************************/ + Math::real EquatorialRadius() const { return _earth.EquatorialRadius(); } + + /** + * @return \e f the flattening of the ellipsoid. This is the value + * inherited from the Geocentric object used in the constructor. + **********************************************************************/ + Math::real Flattening() const { return _earth.Flattening(); } + + /** + * @return \e Nmax the maximum degree of the components of the model. + **********************************************************************/ + int Degree() const { return _nmx; } + + /** + * @return \e Mmax the maximum order of the components of the model. + **********************************************************************/ + int Order() const { return _mmx; } + + /** + * \deprecated An old name for EquatorialRadius(). + **********************************************************************/ + GEOGRAPHICLIB_DEPRECATED("Use EquatorialRadius()") + Math::real MajorRadius() const { return EquatorialRadius(); } + ///@} + + /** + * @return the default path for magnetic model data files. + * + * This is the value of the environment variable + * GEOGRAPHICLIB_MAGNETIC_PATH, if set; otherwise, it is + * $GEOGRAPHICLIB_DATA/magnetic if the environment variable + * GEOGRAPHICLIB_DATA is set; otherwise, it is a compile-time default + * (/usr/local/share/GeographicLib/magnetic on non-Windows systems and + * C:/ProgramData/GeographicLib/magnetic on Windows systems). + **********************************************************************/ + static std::string DefaultMagneticPath(); + + /** + * @return the default name for the magnetic model. + * + * This is the value of the environment variable + * GEOGRAPHICLIB_MAGNETIC_NAME, if set; otherwise, it is "wmm2020". The + * MagneticModel class does not use this function; it is just provided as a + * convenience for a calling program when constructing a MagneticModel + * object. + **********************************************************************/ + static std::string DefaultMagneticName(); + }; + +} // namespace GeographicLib + +#if defined(_MSC_VER) +# pragma warning (pop) +#endif + +#endif // GEOGRAPHICLIB_MAGNETICMODEL_HPP diff --git a/common/local_libs/GeographicLib/include/GeographicLib/Math.hpp b/common/local_libs/GeographicLib/include/GeographicLib/Math.hpp new file mode 100644 index 0000000000..a76941d380 --- /dev/null +++ b/common/local_libs/GeographicLib/include/GeographicLib/Math.hpp @@ -0,0 +1,669 @@ +/** + * \file Math.hpp + * \brief Header for GeographicLib::Math class + * + * Copyright (c) Charles Karney (2008-2020) and licensed + * under the MIT/X11 License. For more information, see + * https://geographiclib.sourceforge.io/ + **********************************************************************/ + +// Constants.hpp includes Math.hpp. Place this include outside Math.hpp's +// include guard to enforce this ordering. +#include + +#if !defined(GEOGRAPHICLIB_MATH_HPP) +#define GEOGRAPHICLIB_MATH_HPP 1 + +#if !defined(GEOGRAPHICLIB_WORDS_BIGENDIAN) +# define GEOGRAPHICLIB_WORDS_BIGENDIAN 0 +#endif + +#if !defined(GEOGRAPHICLIB_HAVE_LONG_DOUBLE) +# define GEOGRAPHICLIB_HAVE_LONG_DOUBLE 0 +#endif + +#if !defined(GEOGRAPHICLIB_PRECISION) +/** + * The precision of floating point numbers used in %GeographicLib. 1 means + * float (single precision); 2 (the default) means double; 3 means long double; + * 4 is reserved for quadruple precision. Nearly all the testing has been + * carried out with doubles and that's the recommended configuration. In order + * for long double to be used, GEOGRAPHICLIB_HAVE_LONG_DOUBLE needs to be + * defined. Note that with Microsoft Visual Studio, long double is the same as + * double. + **********************************************************************/ +# define GEOGRAPHICLIB_PRECISION 2 +#endif + +#include +#include +#include + +#if GEOGRAPHICLIB_PRECISION == 4 +#include +#include +#include +#elif GEOGRAPHICLIB_PRECISION == 5 +#include +#endif + +#if GEOGRAPHICLIB_PRECISION > 3 +// volatile keyword makes no sense for multiprec types +#define GEOGRAPHICLIB_VOLATILE +// Signal a convergence failure with multiprec types by throwing an exception +// at loop exit. +#define GEOGRAPHICLIB_PANIC \ + (throw GeographicLib::GeographicErr("Convergence failure"), false) +#else +#define GEOGRAPHICLIB_VOLATILE volatile +// Ignore convergence failures with standard floating points types by allowing +// loop to exit cleanly. +#define GEOGRAPHICLIB_PANIC false +#endif + +namespace GeographicLib { + + /** + * \brief Mathematical functions needed by %GeographicLib + * + * Define mathematical functions in order to localize system dependencies and + * to provide generic versions of the functions. In addition define a real + * type to be used by %GeographicLib. + * + * Example of use: + * \include example-Math.cpp + **********************************************************************/ + class GEOGRAPHICLIB_EXPORT Math { + private: + void dummy(); // Static check for GEOGRAPHICLIB_PRECISION + Math(); // Disable constructor + public: + +#if GEOGRAPHICLIB_HAVE_LONG_DOUBLE + /** + * The extended precision type for real numbers, used for some testing. + * This is long double on computers with this type; otherwise it is double. + **********************************************************************/ + typedef long double extended; +#else + typedef double extended; +#endif + +#if GEOGRAPHICLIB_PRECISION == 2 + /** + * The real type for %GeographicLib. Nearly all the testing has been done + * with \e real = double. However, the algorithms should also work with + * float and long double (where available). (CAUTION: reasonable + * accuracy typically cannot be obtained using floats.) + **********************************************************************/ + typedef double real; +#elif GEOGRAPHICLIB_PRECISION == 1 + typedef float real; +#elif GEOGRAPHICLIB_PRECISION == 3 + typedef extended real; +#elif GEOGRAPHICLIB_PRECISION == 4 + typedef boost::multiprecision::float128 real; +#elif GEOGRAPHICLIB_PRECISION == 5 + typedef mpfr::mpreal real; +#else + typedef double real; +#endif + + /** + * @return the number of bits of precision in a real number. + **********************************************************************/ + static int digits(); + + /** + * Set the binary precision of a real number. + * + * @param[in] ndigits the number of bits of precision. + * @return the resulting number of bits of precision. + * + * This only has an effect when GEOGRAPHICLIB_PRECISION = 5. See also + * Utility::set_digits for caveats about when this routine should be + * called. + **********************************************************************/ + static int set_digits(int ndigits); + + /** + * @return the number of decimal digits of precision in a real number. + **********************************************************************/ + static int digits10(); + + /** + * Number of additional decimal digits of precision for real relative to + * double (0 for float). + **********************************************************************/ + static int extra_digits(); + + /** + * true if the machine is big-endian. + **********************************************************************/ + static const bool bigendian = GEOGRAPHICLIB_WORDS_BIGENDIAN; + + /** + * @tparam T the type of the returned value. + * @return π. + **********************************************************************/ + template static T pi() { + using std::atan2; + static const T pi = atan2(T(0), T(-1)); + return pi; + } + + /** + * @tparam T the type of the returned value. + * @return the number of radians in a degree. + **********************************************************************/ + template static T degree() { + static const T degree = pi() / 180; + return degree; + } + + /** + * Square a number. + * + * @tparam T the type of the argument and the returned value. + * @param[in] x + * @return x2. + **********************************************************************/ + template static T sq(T x) + { return x * x; } + + /** + * The hypotenuse function avoiding underflow and overflow. + * + * @tparam T the type of the arguments and the returned value. + * @param[in] x + * @param[in] y + * @return sqrt(x2 + y2). + * + * \deprecated Use std::hypot(x, y). + **********************************************************************/ + template + GEOGRAPHICLIB_DEPRECATED("Use std::hypot(x, y)") + static T hypot(T x, T y); + + /** + * exp(\e x) − 1 accurate near \e x = 0. + * + * @tparam T the type of the argument and the returned value. + * @param[in] x + * @return exp(\e x) − 1. + * + * \deprecated Use std::expm1(x). + **********************************************************************/ + template + GEOGRAPHICLIB_DEPRECATED("Use std::expm1(x)") + static T expm1(T x); + + /** + * log(1 + \e x) accurate near \e x = 0. + * + * @tparam T the type of the argument and the returned value. + * @param[in] x + * @return log(1 + \e x). + * + * \deprecated Use std::log1p(x). + **********************************************************************/ + template + GEOGRAPHICLIB_DEPRECATED("Use std::log1p(x)") + static T log1p(T x); + + /** + * The inverse hyperbolic sine function. + * + * @tparam T the type of the argument and the returned value. + * @param[in] x + * @return asinh(\e x). + * + * \deprecated Use std::asinh(x). + **********************************************************************/ + template + GEOGRAPHICLIB_DEPRECATED("Use std::asinh(x)") + static T asinh(T x); + + /** + * The inverse hyperbolic tangent function. + * + * @tparam T the type of the argument and the returned value. + * @param[in] x + * @return atanh(\e x). + * + * \deprecated Use std::atanh(x). + **********************************************************************/ + template + GEOGRAPHICLIB_DEPRECATED("Use std::atanh(x)") + static T atanh(T x); + + /** + * Copy the sign. + * + * @tparam T the type of the argument. + * @param[in] x gives the magitude of the result. + * @param[in] y gives the sign of the result. + * @return value with the magnitude of \e x and with the sign of \e y. + * + * This routine correctly handles the case \e y = −0, returning + * &minus|x|. + * + * \deprecated Use std::copysign(x, y). + **********************************************************************/ + template + GEOGRAPHICLIB_DEPRECATED("Use std::copysign(x, y)") + static T copysign(T x, T y); + + /** + * The cube root function. + * + * @tparam T the type of the argument and the returned value. + * @param[in] x + * @return the real cube root of \e x. + * + * \deprecated Use std::cbrt(x). + **********************************************************************/ + template + GEOGRAPHICLIB_DEPRECATED("Use std::cbrt(x)") + static T cbrt(T x); + + /** + * The remainder function. + * + * @tparam T the type of the arguments and the returned value. + * @param[in] x + * @param[in] y + * @return the remainder of \e x/\e y in the range [−\e y/2, \e y/2]. + * + * \deprecated Use std::remainder(x). + **********************************************************************/ + template + GEOGRAPHICLIB_DEPRECATED("Use std::remainder(x)") + static T remainder(T x, T y); + + /** + * The remquo function. + * + * @tparam T the type of the arguments and the returned value. + * @param[in] x + * @param[in] y + * @param[out] n the low 3 bits of the quotient + * @return the remainder of \e x/\e y in the range [−\e y/2, \e y/2]. + * + * \deprecated Use std::remquo(x, y, n). + **********************************************************************/ + template + GEOGRAPHICLIB_DEPRECATED("Use std::remquo(x, y, n)") + static T remquo(T x, T y, int* n); + + /** + * The round function. + * + * @tparam T the type of the argument and the returned value. + * @param[in] x + * @return \e x round to the nearest integer (ties round away from 0). + * + * \deprecated Use std::round(x). + **********************************************************************/ + template + GEOGRAPHICLIB_DEPRECATED("Use std::round(x)") + static T round(T x); + + /** + * The lround function. + * + * @tparam T the type of the argument. + * @param[in] x + * @return \e x round to the nearest integer as a long int (ties round away + * from 0). + * + * If the result does not fit in a long int, the return value is undefined. + * + * \deprecated Use std::lround(x). + **********************************************************************/ + template + GEOGRAPHICLIB_DEPRECATED("Use std::lround(x)") + static long lround(T x); + + /** + * Fused multiply and add. + * + * @tparam T the type of the arguments and the returned value. + * @param[in] x + * @param[in] y + * @param[in] z + * @return xy + z, correctly rounded (on those platforms with + * support for the fma instruction). + * + * On platforms without the fma instruction, no attempt is + * made to improve on the result of a rounded multiplication followed by a + * rounded addition. + * + * \deprecated Use std::fma(x, y, z). + **********************************************************************/ + template + GEOGRAPHICLIB_DEPRECATED("Use std::fma(x, y, z)") + static T fma(T x, T y, T z); + + /** + * Normalize a two-vector. + * + * @tparam T the type of the argument and the returned value. + * @param[in,out] x on output set to x/hypot(x, y). + * @param[in,out] y on output set to y/hypot(x, y). + **********************************************************************/ + template static void norm(T& x, T& y) { + using std::hypot; T h = hypot(x, y); x /= h; y /= h; + } + + /** + * The error-free sum of two numbers. + * + * @tparam T the type of the argument and the returned value. + * @param[in] u + * @param[in] v + * @param[out] t the exact error given by (\e u + \e v) - \e s. + * @return \e s = round(\e u + \e v). + * + * See D. E. Knuth, TAOCP, Vol 2, 4.2.2, Theorem B. (Note that \e t can be + * the same as one of the first two arguments.) + **********************************************************************/ + template static T sum(T u, T v, T& t); + + /** + * Evaluate a polynomial. + * + * @tparam T the type of the arguments and returned value. + * @param[in] N the order of the polynomial. + * @param[in] p the coefficient array (of size \e N + 1). + * @param[in] x the variable. + * @return the value of the polynomial. + * + * Evaluate y = ∑n=0..N + * pn xNn. + * Return 0 if \e N < 0. Return p0, if \e N = 0 (even + * if \e x is infinite or a nan). The evaluation uses Horner's method. + **********************************************************************/ + template static T polyval(int N, const T p[], T x) { + // This used to employ Math::fma; but that's too slow and it seemed not to + // improve the accuracy noticeably. This might change when there's direct + // hardware support for fma. + T y = N < 0 ? 0 : *p++; + while (--N >= 0) y = y * x + *p++; + return y; + } + + /** + * Normalize an angle. + * + * @tparam T the type of the argument and returned value. + * @param[in] x the angle in degrees. + * @return the angle reduced to the range (−180°, 180°]. + * + * The range of \e x is unrestricted. + **********************************************************************/ + template static T AngNormalize(T x) { + using std::remainder; + x = remainder(x, T(360)); return x != -180 ? x : 180; + } + + /** + * Normalize a latitude. + * + * @tparam T the type of the argument and returned value. + * @param[in] x the angle in degrees. + * @return x if it is in the range [−90°, 90°], otherwise + * return NaN. + **********************************************************************/ + template static T LatFix(T x) + { using std::abs; return abs(x) > 90 ? NaN() : x; } + + /** + * The exact difference of two angles reduced to + * (−180°, 180°]. + * + * @tparam T the type of the arguments and returned value. + * @param[in] x the first angle in degrees. + * @param[in] y the second angle in degrees. + * @param[out] e the error term in degrees. + * @return \e d, the truncated value of \e y − \e x. + * + * This computes \e z = \e y − \e x exactly, reduced to + * (−180°, 180°]; and then sets \e z = \e d + \e e where \e d + * is the nearest representable number to \e z and \e e is the truncation + * error. If \e d = −180, then \e e > 0; If \e d = 180, then \e e + * ≤ 0. + **********************************************************************/ + template static T AngDiff(T x, T y, T& e) { + using std::remainder; + T t, d = AngNormalize(sum(remainder(-x, T(360)), + remainder( y, T(360)), 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 sum(d == 180 && t > 0 ? -180 : d, t, e); + } + + /** + * Difference of two angles reduced to [−180°, 180°] + * + * @tparam T the type of the arguments and returned value. + * @param[in] x the first angle in degrees. + * @param[in] y the second angle in degrees. + * @return \e y − \e x, reduced to the range [−180°, + * 180°]. + * + * The result is equivalent to computing the difference exactly, reducing + * it to (−180°, 180°] and rounding the result. Note that + * this prescription allows −180° to be returned (e.g., if \e x + * is tiny and negative and \e y = 180°). + **********************************************************************/ + template static T AngDiff(T x, T y) + { T e; return AngDiff(x, y, e); } + + /** + * Coarsen a value close to zero. + * + * @tparam T the type of the argument and returned value. + * @param[in] x + * @return the coarsened value. + * + * The makes the smallest gap in \e x = 1/16 − nextafter(1/16, 0) = + * 1/257 for reals = 0.7 pm on the earth if \e x is an angle in + * degrees. (This is about 1000 times more resolution than we get with + * angles around 90°.) We use this to avoid having to deal with near + * singular cases when \e x is non-zero but tiny (e.g., + * 10−200). This converts −0 to +0; however tiny + * negative numbers get converted to −0. + **********************************************************************/ + template static T AngRound(T x); + + /** + * Evaluate the sine and cosine function with the argument in degrees + * + * @tparam T the type of the arguments. + * @param[in] x in degrees. + * @param[out] sinx sin(x). + * @param[out] cosx cos(x). + * + * The results obey exactly the elementary properties of the trigonometric + * functions, e.g., sin 9° = cos 81° = − sin 123456789°. + * If x = −0, then \e sinx = −0; this is the only case where + * −0 is returned. + **********************************************************************/ + template static void sincosd(T x, T& sinx, T& cosx); + + /** + * Evaluate the sine function with the argument in degrees + * + * @tparam T the type of the argument and the returned value. + * @param[in] x in degrees. + * @return sin(x). + **********************************************************************/ + template static T sind(T x); + + /** + * Evaluate the cosine function with the argument in degrees + * + * @tparam T the type of the argument and the returned value. + * @param[in] x in degrees. + * @return cos(x). + **********************************************************************/ + template static T cosd(T x); + + /** + * Evaluate the tangent function with the argument in degrees + * + * @tparam T the type of the argument and the returned value. + * @param[in] x in degrees. + * @return tan(x). + * + * If \e x = ±90°, then a suitably large (but finite) value is + * returned. + **********************************************************************/ + template static T tand(T x); + + /** + * Evaluate the atan2 function with the result in degrees + * + * @tparam T the type of the arguments and the returned value. + * @param[in] y + * @param[in] x + * @return atan2(y, x) in degrees. + * + * The result is in the range (−180° 180°]. N.B., + * atan2d(±0, −1) = +180°; atan2d(−ε, + * −1) = −180°, for ε positive and tiny; + * atan2d(±0, +1) = ±0°. + **********************************************************************/ + template static T atan2d(T y, T x); + + /** + * Evaluate the atan function with the result in degrees + * + * @tparam T the type of the argument and the returned value. + * @param[in] x + * @return atan(x) in degrees. + **********************************************************************/ + template static T atand(T x); + + /** + * Evaluate e atanh(e x) + * + * @tparam T the type of the argument and the returned value. + * @param[in] x + * @param[in] es the signed eccentricity = sign(e2) + * sqrt(|e2|) + * @return e atanh(e x) + * + * If e2 is negative (e is imaginary), the + * expression is evaluated in terms of atan. + **********************************************************************/ + template static T eatanhe(T x, T es); + + /** + * tanχ in terms of tanφ + * + * @tparam T the type of the argument and the returned value. + * @param[in] tau τ = tanφ + * @param[in] es the signed eccentricity = sign(e2) + * sqrt(|e2|) + * @return τ′ = tanχ + * + * See Eqs. (7--9) of + * C. F. F. Karney, + * + * Transverse Mercator with an accuracy of a few nanometers, + * J. Geodesy 85(8), 475--485 (Aug. 2011) + * (preprint + * arXiv:1002.1417). + **********************************************************************/ + template static T taupf(T tau, T es); + + /** + * tanφ in terms of tanχ + * + * @tparam T the type of the argument and the returned value. + * @param[in] taup τ′ = tanχ + * @param[in] es the signed eccentricity = sign(e2) + * sqrt(|e2|) + * @return τ = tanφ + * + * See Eqs. (19--21) of + * C. F. F. Karney, + * + * Transverse Mercator with an accuracy of a few nanometers, + * J. Geodesy 85(8), 475--485 (Aug. 2011) + * (preprint + * arXiv:1002.1417). + **********************************************************************/ + template static T tauf(T taup, T es); + + /** + * Test for finiteness. + * + * @tparam T the type of the argument. + * @param[in] x + * @return true if number is finite, false if NaN or infinite. + * + * \deprecated Use std::isfinite(x). + **********************************************************************/ + template + GEOGRAPHICLIB_DEPRECATED("Use std::isfinite(x)") + static bool isfinite(T x); + + /** + * The NaN (not a number) + * + * @tparam T the type of the returned value. + * @return NaN if available, otherwise return the max real of type T. + **********************************************************************/ + template static T NaN(); + + /** + * Test for NaN. + * + * @tparam T the type of the argument. + * @param[in] x + * @return true if argument is a NaN. + * + * \deprecated Use std::isnan(x). + **********************************************************************/ + template + GEOGRAPHICLIB_DEPRECATED("Use std::isnan(x)") + static bool isnan(T x); + + /** + * Infinity + * + * @tparam T the type of the returned value. + * @return infinity if available, otherwise return the max real. + **********************************************************************/ + template static T infinity(); + + /** + * Swap the bytes of a quantity + * + * @tparam T the type of the argument and the returned value. + * @param[in] x + * @return x with its bytes swapped. + **********************************************************************/ + template static T swab(T x) { + union { + T r; + unsigned char c[sizeof(T)]; + } b; + b.r = x; + for (int i = sizeof(T)/2; i--; ) + std::swap(b.c[i], b.c[sizeof(T) - 1 - i]); + return b.r; + } + + }; + +} // namespace GeographicLib + +#endif // GEOGRAPHICLIB_MATH_HPP diff --git a/common/local_libs/GeographicLib/include/GeographicLib/NearestNeighbor.hpp b/common/local_libs/GeographicLib/include/GeographicLib/NearestNeighbor.hpp new file mode 100644 index 0000000000..56edba2538 --- /dev/null +++ b/common/local_libs/GeographicLib/include/GeographicLib/NearestNeighbor.hpp @@ -0,0 +1,837 @@ +/** + * \file NearestNeighbor.hpp + * \brief Header for GeographicLib::NearestNeighbor class + * + * Copyright (c) Charles Karney (2016-2020) and licensed + * under the MIT/X11 License. For more information, see + * https://geographiclib.sourceforge.io/ + **********************************************************************/ + +#if !defined(GEOGRAPHICLIB_NEARESTNEIGHBOR_HPP) +#define GEOGRAPHICLIB_NEARESTNEIGHBOR_HPP 1 + +#include // for nth_element, max_element, etc. +#include +#include // for priority_queue +#include // for swap + pair +#include +#include +#include +#include +#include +// Only for GeographicLib::GeographicErr +#include + +#if defined(GEOGRAPHICLIB_HAVE_BOOST_SERIALIZATION) && \ + GEOGRAPHICLIB_HAVE_BOOST_SERIALIZATION +#include +#include +#include +#include +#endif + +#if defined(_MSC_VER) +// Squelch warnings about constant conditional expressions +# pragma warning (push) +# pragma warning (disable: 4127) +#endif + +namespace GeographicLib { + + /** + * \brief Nearest-neighbor calculations + * + * This class solves the nearest-neighbor problm using a vantage-point tree + * as described in \ref nearest. + * + * This class is templated so that it can handle arbitrary metric spaces as + * follows: + * + * @tparam dist_t the type used for measuring distances; it can be a real or + * signed integer type; in typical geodetic applications, \e dist_t might + * be double. + * @tparam pos_t the type for specifying the positions of points; geodetic + * application might bundled the latitude and longitude into a + * std::pair. + * @tparam distfun_t the type of a function object which takes takes two + * positions (of type \e pos_t) and returns the distance (of type \e + * dist_t); in geodetic applications, this might be a class which is + * constructed with a Geodesic object and which implements a member + * function with a signature dist_t operator() (const pos_t&, const + * pos_t&) const, which returns the geodesic distance between two + * points. + * + * \note The distance measure must satisfy the triangle inequality, \f$ + * d(a,c) \le d(a,b) + d(b,c) \f$ for all points \e a, \e b, \e c. The + * geodesic distance (given by Geodesic::Inverse) does, while the great + * ellipse distance and the rhumb line distance do not. If you use + * the ordinary Euclidean distance, i.e., \f$ \sqrt{(x_a-x_b)^2 + + * (y_a-y_b)^2} \f$ for two dimensions, don't be tempted to leave out the + * square root in the interests of "efficiency"; the squared distance does + * not satisfy the triangle inequality! + * + * \note This is a "header-only" implementation and, as such, depends in a + * minimal way on the rest of GeographicLib (the only dependency is through + * the use of GeographicLib::GeographicErr for handling compile-time and + * run-time exceptions). Therefore, it is easy to extract this class from + * the rest of GeographicLib and use it as a stand-alone facility. + * + * The \e dist_t type must support numeric_limits queries (specifically: + * is_signed, is_integer, max(), digits). + * + * The NearestNeighbor object is constructed with a vector of points (type \e + * pos_t) and a distance function (type \e distfun_t). However the object + * does \e not store the points. When querying the object with Search(), + * it's necessary to supply the same vector of points and the same distance + * function. + * + * There's no capability in this implementation to add or remove points from + * the set. Instead Initialize() should be called to re-initialize the + * object with the modified vector of points. + * + * Because of the overhead in constructing a NearestNeighbor object for a + * large set of points, functions Save() and Load() are provided to save the + * object to an external file. operator<<(), operator>>() and Boost + * serialization can also be used to save and restore a NearestNeighbor + * object. This is illustrated in the example. + * + * Example of use: + * \include example-NearestNeighbor.cpp + **********************************************************************/ + template + class NearestNeighbor { + // For tracking changes to the I/O format + static const int version = 1; + // This is what we get "free"; but if sizeof(dist_t) = 1 (unlikely), allow + // 4 slots (and this accommodates the default value bucket = 4). + static const int maxbucket = + (2 + ((4 * sizeof(dist_t)) / sizeof(int) >= 2 ? + (4 * sizeof(dist_t)) / sizeof(int) : 2)); + public: + + /** + * Default constructor for NearestNeighbor. + * + * This is equivalent to specifying an empty set of points. + **********************************************************************/ + NearestNeighbor() : _numpoints(0), _bucket(0), _cost(0) {} + + /** + * Constructor for NearestNeighbor. + * + * @param[in] pts a vector of points to include in the set. + * @param[in] dist the distance function object. + * @param[in] bucket the size of the buckets at the leaf nodes; this must + * lie in [0, 2 + 4*sizeof(dist_t)/sizeof(int)] (default 4). + * @exception GeographicErr if the value of \e bucket is out of bounds or + * the size of \e pts is too big for an int. + * @exception std::bad_alloc if memory for the tree can't be allocated. + * + * \e pts may contain coincident points (i.e., the distance between them + * vanishes); these are treated as distinct. + * + * The choice of \e bucket is a tradeoff between space and efficiency. A + * larger \e bucket decreases the size of the NearestNeighbor object which + * scales as pts.size() / max(1, bucket) and reduces the number of distance + * calculations to construct the object by log2(bucket) * pts.size(). + * However each search then requires about bucket additional distance + * calculations. + * + * \warning The distances computed by \e dist must satisfy the standard + * metric conditions. If not, the results are undefined. Neither the data + * in \e pts nor the query points should contain NaNs or infinities because + * such data violates the metric conditions. + * + * \warning The same arguments \e pts and \e dist must be provided + * to the Search() function. + **********************************************************************/ + NearestNeighbor(const std::vector& pts, const distfun_t& dist, + int bucket = 4) { + Initialize(pts, dist, bucket); + } + + /** + * Initialize or re-initialize NearestNeighbor. + * + * @param[in] pts a vector of points to include in the tree. + * @param[in] dist the distance function object. + * @param[in] bucket the size of the buckets at the leaf nodes; this must + * lie in [0, 2 + 4*sizeof(dist_t)/sizeof(int)] (default 4). + * @exception GeographicErr if the value of \e bucket is out of bounds or + * the size of \e pts is too big for an int. + * @exception std::bad_alloc if memory for the tree can't be allocated. + * + * See also the documentation on the constructor. + * + * If an exception is thrown, the state of the NearestNeighbor is + * unchanged. + **********************************************************************/ + void Initialize(const std::vector& pts, const distfun_t& dist, + int bucket = 4) { + static_assert(std::numeric_limits::is_signed, + "dist_t must be a signed type"); + if (!( 0 <= bucket && bucket <= maxbucket )) + throw GeographicLib::GeographicErr + ("bucket must lie in [0, 2 + 4*sizeof(dist_t)/sizeof(int)]"); + if (pts.size() > size_t(std::numeric_limits::max())) + throw GeographicLib::GeographicErr("pts array too big"); + // the pair contains distance+id + std::vector ids(pts.size()); + for (int k = int(ids.size()); k--;) + ids[k] = std::make_pair(dist_t(0), k); + int cost = 0; + std::vector tree; + init(pts, dist, bucket, tree, ids, cost, + 0, int(ids.size()), int(ids.size()/2)); + _tree.swap(tree); + _numpoints = int(pts.size()); + _bucket = bucket; + _mc = _sc = 0; + _cost = cost; _c1 = _k = _cmax = 0; + _cmin = std::numeric_limits::max(); + } + + /** + * Search the NearestNeighbor. + * + * @param[in] pts the vector of points used for initialization. + * @param[in] dist the distance function object used for initialization. + * @param[in] query the query point. + * @param[out] ind a vector of indices to the closest points found. + * @param[in] k the number of points to search for (default = 1). + * @param[in] maxdist only return points with distances of \e maxdist or + * less from \e query (default is the maximum \e dist_t). + * @param[in] mindist only return points with distances of more than + * \e mindist from \e query (default = −1). + * @param[in] exhaustive whether to do an exhaustive search (default true). + * @param[in] tol the tolerance on the results (default 0). + * @return the distance to the closest point found (−1 if no points + * are found). + * @exception GeographicErr if \e pts has a different size from that used + * to construct the object. + * + * The indices returned in \e ind are sorted by distance from \e query + * (closest first). + * + * The simplest invocation is with just the 4 non-optional arguments. This + * returns the closest distance and the index to the closest point in + * ind0. If there are several points equally close, then + * ind0 gives the index of an arbirary one of them. If + * there's no closest point (because the set of points is empty), then \e + * ind is empty and −1 is returned. + * + * With \e exhaustive = true and \e tol = 0 (their default values), this + * finds the indices of \e k closest neighbors to \e query whose distances + * to \e query are in (\e mindist, \e maxdist]. If \e mindist and \e + * maxdist have their default values, then these bounds have no effect. If + * \e query is one of the points in the tree, then set \e mindist = 0 to + * prevent this point (and other coincident points) from being returned. + * + * If \e exhaustive = false, exit as soon as \e k results satisfying the + * distance criteria are found. If less than \e k results are returned + * then the search was exhaustive even if \e exhaustive = false. + * + * If \e tol is positive, do an approximate search; in this case the + * results are to be interpreted as follows: if the k'th distance is + * \e dk, then all results with distances less than or equal \e dk − + * \e tol are correct; all others are suspect — there may be other + * closer results with distances greater or equal to \e dk − \e tol. + * If less than \e k results are found, then the search is exact. + * + * \e mindist should be used to exclude a "small" neighborhood of the query + * point (relative to the average spacing of the data). If \e mindist is + * large, the efficiency of the search deteriorates. + * + * \note Only the shortest distance is returned (as as the function value). + * The distances to other points (indexed by indj + * for \e j > 0) can be found by invoking \e dist again. + * + * \warning The arguments \e pts and \e dist must be identical to those + * used to initialize the NearestNeighbor; if not, this function will + * return some meaningless result (however, if the size of \e pts is wrong, + * this function throw an exception). + * + * \warning The query point cannot be a NaN or infinite because then the + * metric conditions are violated. + **********************************************************************/ + dist_t Search(const std::vector& pts, const distfun_t& dist, + const pos_t& query, + std::vector& ind, + int k = 1, + dist_t maxdist = std::numeric_limits::max(), + dist_t mindist = -1, + bool exhaustive = true, + dist_t tol = 0) const { + if (_numpoints != int(pts.size())) + throw GeographicLib::GeographicErr("pts array has wrong size"); + std::priority_queue results; + if (_numpoints > 0 && k > 0 && maxdist > mindist) { + // distance to the kth closest point so far + dist_t tau = maxdist; + // first is negative of how far query is outside boundary of node + // +1 if on boundary or inside + // second is node index + std::priority_queue todo; + todo.push(std::make_pair(dist_t(1), int(_tree.size()) - 1)); + int c = 0; + while (!todo.empty()) { + int n = todo.top().second; + dist_t d = -todo.top().first; + todo.pop(); + dist_t tau1 = tau - tol; + // compare tau and d again since tau may have become smaller. + if (!( n >= 0 && tau1 >= d )) continue; + const Node& current = _tree[n]; + dist_t dst = 0; // to suppress warning about uninitialized variable + bool exitflag = false, leaf = current.index < 0; + for (int i = 0; i < (leaf ? _bucket : 1); ++i) { + int index = leaf ? current.leaves[i] : current.index; + if (index < 0) break; + dst = dist(pts[index], query); + ++c; + + if (dst > mindist && dst <= tau) { + if (int(results.size()) == k) results.pop(); + results.push(std::make_pair(dst, index)); + if (int(results.size()) == k) { + if (exhaustive) + tau = results.top().first; + else { + exitflag = true; + break; + } + if (tau <= tol) { + exitflag = true; + break; + } + } + } + } + if (exitflag) break; + + if (current.index < 0) continue; + tau1 = tau - tol; + for (int l = 0; l < 2; ++l) { + if (current.data.child[l] >= 0 && + dst + current.data.upper[l] >= mindist) { + if (dst < current.data.lower[l]) { + d = current.data.lower[l] - dst; + if (tau1 >= d) + todo.push(std::make_pair(-d, current.data.child[l])); + } else if (dst > current.data.upper[l]) { + d = dst - current.data.upper[l]; + if (tau1 >= d) + todo.push(std::make_pair(-d, current.data.child[l])); + } else + todo.push(std::make_pair(dist_t(1), current.data.child[l])); + } + } + } + ++_k; + _c1 += c; + double omc = _mc; + _mc += (c - omc) / _k; + _sc += (c - omc) * (c - _mc); + if (c > _cmax) _cmax = c; + if (c < _cmin) _cmin = c; + } + + dist_t d = -1; + ind.resize(results.size()); + + for (int i = int(ind.size()); i--;) { + ind[i] = int(results.top().second); + if (i == 0) d = results.top().first; + results.pop(); + } + return d; + + } + + /** + * @return the total number of points in the set. + **********************************************************************/ + int NumPoints() const { return _numpoints; } + + /** + * Write the object to an I/O stream. + * + * @param[in,out] os the stream to write to. + * @param[in] bin if true (the default) save in binary mode. + * @exception std::bad_alloc if memory for the string representation of the + * object can't be allocated. + * + * The counters tracking the statistics of searches are not saved; however + * the initializtion cost is saved. The format of the binary saves is \e + * not portable. + * + * \note + * Boost serialization can also be used to save and restore a + * NearestNeighbor object. This requires that the + * GEOGRAPHICLIB_HAVE_BOOST_SERIALIZATION macro be defined. + **********************************************************************/ + void Save(std::ostream& os, bool bin = true) const { + int realspec = std::numeric_limits::digits * + (std::numeric_limits::is_integer ? -1 : 1); + if (bin) { + char id[] = "NearestNeighbor_"; + os.write(id, 16); + int buf[6]; + buf[0] = version; + buf[1] = realspec; + buf[2] = _bucket; + buf[3] = _numpoints; + buf[4] = int(_tree.size()); + buf[5] = _cost; + os.write(reinterpret_cast(buf), 6 * sizeof(int)); + for (int i = 0; i < int(_tree.size()); ++i) { + const Node& node = _tree[i]; + os.write(reinterpret_cast(&node.index), sizeof(int)); + if (node.index >= 0) { + os.write(reinterpret_cast(node.data.lower), + 2 * sizeof(dist_t)); + os.write(reinterpret_cast(node.data.upper), + 2 * sizeof(dist_t)); + os.write(reinterpret_cast(node.data.child), + 2 * sizeof(int)); + } else { + os.write(reinterpret_cast(node.leaves), + _bucket * sizeof(int)); + } + } + } else { + std::stringstream ostring; + // Ensure enough precision for type dist_t. With C++11, max_digits10 + // can be used instead. + if (!std::numeric_limits::is_integer) { + static const int prec + = int(std::ceil(std::numeric_limits::digits * + std::log10(2.0) + 1)); + ostring.precision(prec); + } + ostring << version << " " << realspec << " " << _bucket << " " + << _numpoints << " " << _tree.size() << " " << _cost; + for (int i = 0; i < int(_tree.size()); ++i) { + const Node& node = _tree[i]; + ostring << "\n" << node.index; + if (node.index >= 0) { + for (int l = 0; l < 2; ++l) + ostring << " " << node.data.lower[l] << " " << node.data.upper[l] + << " " << node.data.child[l]; + } else { + for (int l = 0; l < _bucket; ++l) + ostring << " " << node.leaves[l]; + } + } + os << ostring.str(); + } + } + + /** + * Read the object from an I/O stream. + * + * @param[in,out] is the stream to read from + * @param[in] bin if true (the default) load in binary mode. + * @exception GeographicErr if the state read from \e is is illegal. + * @exception std::bad_alloc if memory for the tree can't be allocated. + * + * The counters tracking the statistics of searches are reset by this + * operation. Binary data must have been saved on a machine with the same + * architecture. If an exception is thrown, the state of the + * NearestNeighbor is unchanged. + * + * \note + * Boost serialization can also be used to save and restore a + * NearestNeighbor object. This requires that the + * GEOGRAPHICLIB_HAVE_BOOST_SERIALIZATION macro be defined. + * + * \warning The same arguments \e pts and \e dist used for + * initialization must be provided to the Search() function. + **********************************************************************/ + void Load(std::istream& is, bool bin = true) { + int version1, realspec, bucket, numpoints, treesize, cost; + if (bin) { + char id[17]; + is.read(id, 16); + id[16] = '\0'; + if (!(std::strcmp(id, "NearestNeighbor_") == 0)) + throw GeographicLib::GeographicErr("Bad ID"); + is.read(reinterpret_cast(&version1), sizeof(int)); + is.read(reinterpret_cast(&realspec), sizeof(int)); + is.read(reinterpret_cast(&bucket), sizeof(int)); + is.read(reinterpret_cast(&numpoints), sizeof(int)); + is.read(reinterpret_cast(&treesize), sizeof(int)); + is.read(reinterpret_cast(&cost), sizeof(int)); + } else { + if (!( is >> version1 >> realspec >> bucket >> numpoints >> treesize + >> cost )) + throw GeographicLib::GeographicErr("Bad header"); + } + if (!( version1 == version )) + throw GeographicLib::GeographicErr("Incompatible version"); + if (!( realspec == std::numeric_limits::digits * + (std::numeric_limits::is_integer ? -1 : 1) )) + throw GeographicLib::GeographicErr("Different dist_t types"); + if (!( 0 <= bucket && bucket <= maxbucket )) + throw GeographicLib::GeographicErr("Bad bucket size"); + if (!( 0 <= treesize && treesize <= numpoints )) + throw + GeographicLib::GeographicErr("Bad number of points or tree size"); + if (!( 0 <= cost )) + throw GeographicLib::GeographicErr("Bad value for cost"); + std::vector tree; + tree.reserve(treesize); + for (int i = 0; i < treesize; ++i) { + Node node; + if (bin) { + is.read(reinterpret_cast(&node.index), sizeof(int)); + if (node.index >= 0) { + is.read(reinterpret_cast(node.data.lower), + 2 * sizeof(dist_t)); + is.read(reinterpret_cast(node.data.upper), + 2 * sizeof(dist_t)); + is.read(reinterpret_cast(node.data.child), + 2 * sizeof(int)); + } else { + is.read(reinterpret_cast(node.leaves), + bucket * sizeof(int)); + for (int l = bucket; l < maxbucket; ++l) + node.leaves[l] = 0; + } + } else { + if (!( is >> node.index )) + throw GeographicLib::GeographicErr("Bad index"); + if (node.index >= 0) { + for (int l = 0; l < 2; ++l) { + if (!( is >> node.data.lower[l] >> node.data.upper[l] + >> node.data.child[l] )) + throw GeographicLib::GeographicErr("Bad node data"); + } + } else { + // Must be at least one valid leaf followed by a sequence end + // markers (-1). + for (int l = 0; l < bucket; ++l) { + if (!( is >> node.leaves[l] )) + throw GeographicLib::GeographicErr("Bad leaf data"); + } + for (int l = bucket; l < maxbucket; ++l) + node.leaves[l] = 0; + } + } + node.Check(numpoints, treesize, bucket); + tree.push_back(node); + } + _tree.swap(tree); + _numpoints = numpoints; + _bucket = bucket; + _mc = _sc = 0; + _cost = cost; _c1 = _k = _cmax = 0; + _cmin = std::numeric_limits::max(); + } + + /** + * Write the object to stream \e os as text. + * + * @param[in,out] os the output stream. + * @param[in] t the NearestNeighbor object to be saved. + * @exception std::bad_alloc if memory for the string representation of the + * object can't be allocated. + **********************************************************************/ + friend std::ostream& operator<<(std::ostream& os, const NearestNeighbor& t) + { t.Save(os, false); return os; } + + /** + * Read the object from stream \e is as text. + * + * @param[in,out] is the input stream. + * @param[out] t the NearestNeighbor object to be loaded. + * @exception GeographicErr if the state read from \e is is illegal. + * @exception std::bad_alloc if memory for the tree can't be allocated. + **********************************************************************/ + friend std::istream& operator>>(std::istream& is, NearestNeighbor& t) + { t.Load(is, false); return is; } + + /** + * Swap with another NearestNeighbor object. + * + * @param[in,out] t the NearestNeighbor object to swap with. + **********************************************************************/ + void swap(NearestNeighbor& t) { + std::swap(_numpoints, t._numpoints); + std::swap(_bucket, t._bucket); + std::swap(_cost, t._cost); + _tree.swap(t._tree); + std::swap(_mc, t._mc); + std::swap(_sc, t._sc); + std::swap(_c1, t._c1); + std::swap(_k, t._k); + std::swap(_cmin, t._cmin); + std::swap(_cmax, t._cmax); + } + + /** + * The accumulated statistics on the searches so far. + * + * @param[out] setupcost the cost of initializing the NearestNeighbor. + * @param[out] numsearches the number of calls to Search(). + * @param[out] searchcost the total cost of the calls to Search(). + * @param[out] mincost the minimum cost of a Search(). + * @param[out] maxcost the maximum cost of a Search(). + * @param[out] mean the mean cost of a Search(). + * @param[out] sd the standard deviation in the cost of a Search(). + * + * Here "cost" measures the number of distance calculations needed. Note + * that the accumulation of statistics is \e not thread safe. + **********************************************************************/ + void Statistics(int& setupcost, int& numsearches, int& searchcost, + int& mincost, int& maxcost, + double& mean, double& sd) const { + setupcost = _cost; numsearches = _k; searchcost = _c1; + mincost = _cmin; maxcost = _cmax; + mean = _mc; sd = std::sqrt(_sc / (_k - 1)); + } + + /** + * Reset the counters for the accumulated statistics on the searches so + * far. + **********************************************************************/ + void ResetStatistics() const { + _mc = _sc = 0; + _c1 = _k = _cmax = 0; + _cmin = std::numeric_limits::max(); + } + + private: + // Package up a dist_t and an int. We will want to sort on the dist_t so + // put it first. + typedef std::pair item; + // \cond SKIP + class Node { + public: + struct bounds { + dist_t lower[2], upper[2]; // bounds on inner/outer distances + int child[2]; + }; + union { + bounds data; + int leaves[maxbucket]; + }; + int index; + + Node() + : index(-1) + { + for (int i = 0; i < 2; ++i) { + data.lower[i] = data.upper[i] = 0; + data.child[i] = -1; + } + } + + // Sanity check on a Node + void Check(int numpoints, int treesize, int bucket) const { + if (!( -1 <= index && index < numpoints )) + throw GeographicLib::GeographicErr("Bad index"); + if (index >= 0) { + if (!( -1 <= data.child[0] && data.child[0] < treesize && + -1 <= data.child[1] && data.child[1] < treesize )) + throw GeographicLib::GeographicErr("Bad child pointers"); + if (!( 0 <= data.lower[0] && data.lower[0] <= data.upper[0] && + data.upper[0] <= data.lower[1] && + data.lower[1] <= data.upper[1] )) + throw GeographicLib::GeographicErr("Bad bounds"); + } else { + // Must be at least one valid leaf followed by a sequence end markers + // (-1). + bool start = true; + for (int l = 0; l < bucket; ++l) { + if (!( (start ? + ((l == 0 ? 0 : -1) <= leaves[l] && leaves[l] < numpoints) : + leaves[l] == -1) )) + throw GeographicLib::GeographicErr("Bad leaf data"); + start = leaves[l] >= 0; + } + for (int l = bucket; l < maxbucket; ++l) { + if (leaves[l] != 0) + throw GeographicLib::GeographicErr("Bad leaf data"); + } + } + } + +#if defined(GEOGRAPHICLIB_HAVE_BOOST_SERIALIZATION) && \ + GEOGRAPHICLIB_HAVE_BOOST_SERIALIZATION + friend class boost::serialization::access; + template + void save(Archive& ar, const unsigned int) const { + ar & boost::serialization::make_nvp("index", index); + if (index < 0) + ar & boost::serialization::make_nvp("leaves", leaves); + else + ar & boost::serialization::make_nvp("lower", data.lower) + & boost::serialization::make_nvp("upper", data.upper) + & boost::serialization::make_nvp("child", data.child); + } + template + void load(Archive& ar, const unsigned int) { + ar & boost::serialization::make_nvp("index", index); + if (index < 0) + ar & boost::serialization::make_nvp("leaves", leaves); + else + ar & boost::serialization::make_nvp("lower", data.lower) + & boost::serialization::make_nvp("upper", data.upper) + & boost::serialization::make_nvp("child", data.child); + } + template + void serialize(Archive& ar, const unsigned int file_version) + { boost::serialization::split_member(ar, *this, file_version); } +#endif + }; + // \endcond +#if defined(GEOGRAPHICLIB_HAVE_BOOST_SERIALIZATION) && \ + GEOGRAPHICLIB_HAVE_BOOST_SERIALIZATION + friend class boost::serialization::access; + template void save(Archive& ar, const unsigned) const { + int realspec = std::numeric_limits::digits * + (std::numeric_limits::is_integer ? -1 : 1); + // Need to use version1, otherwise load error in debug mode on Linux: + // undefined reference to GeographicLib::NearestNeighbor<...>::version. + int version1 = version; + ar & boost::serialization::make_nvp("version", version1) + & boost::serialization::make_nvp("realspec", realspec) + & boost::serialization::make_nvp("bucket", _bucket) + & boost::serialization::make_nvp("numpoints", _numpoints) + & boost::serialization::make_nvp("cost", _cost) + & boost::serialization::make_nvp("tree", _tree); + } + template void load(Archive& ar, const unsigned) { + int version1, realspec, bucket, numpoints, cost; + ar & boost::serialization::make_nvp("version", version1); + if (version1 != version) + throw GeographicLib::GeographicErr("Incompatible version"); + std::vector tree; + ar & boost::serialization::make_nvp("realspec", realspec); + if (!( realspec == std::numeric_limits::digits * + (std::numeric_limits::is_integer ? -1 : 1) )) + throw GeographicLib::GeographicErr("Different dist_t types"); + ar & boost::serialization::make_nvp("bucket", bucket); + if (!( 0 <= bucket && bucket <= maxbucket )) + throw GeographicLib::GeographicErr("Bad bucket size"); + ar & boost::serialization::make_nvp("numpoints", numpoints) + & boost::serialization::make_nvp("cost", cost) + & boost::serialization::make_nvp("tree", tree); + if (!( 0 <= int(tree.size()) && int(tree.size()) <= numpoints )) + throw + GeographicLib::GeographicErr("Bad number of points or tree size"); + for (int i = 0; i < int(tree.size()); ++i) + tree[i].Check(numpoints, int(tree.size()), bucket); + _tree.swap(tree); + _numpoints = numpoints; + _bucket = bucket; + _mc = _sc = 0; + _cost = cost; _c1 = _k = _cmax = 0; + _cmin = std::numeric_limits::max(); + } + template + void serialize(Archive& ar, const unsigned int file_version) + { boost::serialization::split_member(ar, *this, file_version); } +#endif + + int _numpoints, _bucket, _cost; + std::vector _tree; + // Counters to track stastistics on the cost of searches + mutable double _mc, _sc; + mutable int _c1, _k, _cmin, _cmax; + + int init(const std::vector& pts, const distfun_t& dist, int bucket, + std::vector& tree, std::vector& ids, int& cost, + int l, int u, int vp) { + + if (u == l) + return -1; + Node node; + + if (u - l > (bucket == 0 ? 1 : bucket)) { + + // choose a vantage point and move it to the start + int i = vp; + std::swap(ids[l], ids[i]); + + int m = (u + l + 1) / 2; + + for (int k = l + 1; k < u; ++k) { + ids[k].first = dist(pts[ids[l].second], pts[ids[k].second]); + ++cost; + } + // partition around the median distance + std::nth_element(ids.begin() + l + 1, + ids.begin() + m, + ids.begin() + u); + node.index = ids[l].second; + if (m > l + 1) { // node.child[0] is possibly empty + typename std::vector::iterator + t = std::min_element(ids.begin() + l + 1, ids.begin() + m); + node.data.lower[0] = t->first; + t = std::max_element(ids.begin() + l + 1, ids.begin() + m); + node.data.upper[0] = t->first; + // Use point with max distance as vantage point; this point act as a + // "corner" point and leads to a good partition. + node.data.child[0] = init(pts, dist, bucket, tree, ids, cost, + l + 1, m, int(t - ids.begin())); + } + typename std::vector::iterator + t = std::max_element(ids.begin() + m, ids.begin() + u); + node.data.lower[1] = ids[m].first; + node.data.upper[1] = t->first; + // Use point with max distance as vantage point here too + node.data.child[1] = init(pts, dist, bucket, tree, ids, cost, + m, u, int(t - ids.begin())); + } else { + if (bucket == 0) + node.index = ids[l].second; + else { + node.index = -1; + // Sort the bucket entries so that the tree is independent of the + // implementation of nth_element. + std::sort(ids.begin() + l, ids.begin() + u); + for (int i = l; i < u; ++i) + node.leaves[i-l] = ids[i].second; + for (int i = u - l; i < bucket; ++i) + node.leaves[i] = -1; + for (int i = bucket; i < maxbucket; ++i) + node.leaves[i] = 0; + } + } + + tree.push_back(node); + return int(tree.size()) - 1; + } + + }; + +} // namespace GeographicLib + +namespace std { + + /** + * Swap two GeographicLib::NearestNeighbor objects. + * + * @tparam dist_t the type used for measuring distances. + * @tparam pos_t the type for specifying the positions of points. + * @tparam distfun_t the type for a function object which calculates + * distances between points. + * @param[in,out] a the first GeographicLib::NearestNeighbor to swap. + * @param[in,out] b the second GeographicLib::NearestNeighbor to swap. + **********************************************************************/ + template + void swap(GeographicLib::NearestNeighbor& a, + GeographicLib::NearestNeighbor& b) { + a.swap(b); + } + +} // namespace std + +#if defined(_MSC_VER) +# pragma warning (pop) +#endif + +#endif // GEOGRAPHICLIB_NEARESTNEIGHBOR_HPP diff --git a/common/local_libs/GeographicLib/include/GeographicLib/NormalGravity.hpp b/common/local_libs/GeographicLib/include/GeographicLib/NormalGravity.hpp new file mode 100644 index 0000000000..07aa88e34c --- /dev/null +++ b/common/local_libs/GeographicLib/include/GeographicLib/NormalGravity.hpp @@ -0,0 +1,400 @@ +/** + * \file NormalGravity.hpp + * \brief Header for GeographicLib::NormalGravity class + * + * Copyright (c) Charles Karney (2011-2020) and licensed + * under the MIT/X11 License. For more information, see + * https://geographiclib.sourceforge.io/ + **********************************************************************/ + +#if !defined(GEOGRAPHICLIB_NORMALGRAVITY_HPP) +#define GEOGRAPHICLIB_NORMALGRAVITY_HPP 1 + +#include +#include + +namespace GeographicLib { + + /** + * \brief The normal gravity of the earth + * + * "Normal" gravity refers to an idealization of the earth which is modeled + * as an rotating ellipsoid. The eccentricity of the ellipsoid, the rotation + * speed, and the distribution of mass within the ellipsoid are such that the + * ellipsoid is a "level ellipoid", a surface of constant potential + * (gravitational plus centrifugal). The acceleration due to gravity is + * therefore perpendicular to the surface of the ellipsoid. + * + * Because the distribution of mass within the ellipsoid is unspecified, only + * the potential exterior to the ellipsoid is well defined. In this class, + * the mass is assumed to be to concentrated on a "focal disc" of radius, + * (a2b2)1/2, where + * \e a is the equatorial radius of the ellipsoid and \e b is its polar + * semi-axis. In the case of an oblate ellipsoid, the mass is concentrated + * on a "focal rod" of length 2(b2 − + * a2)1/2. As a result the potential is well + * defined everywhere. + * + * There is a closed solution to this problem which is implemented here. + * Series "approximations" are only used to evaluate certain combinations of + * elementary functions where use of the closed expression results in a loss + * of accuracy for small arguments due to cancellation of the leading terms. + * However these series include sufficient terms to give full machine + * precision. + * + * Although the formulation used in this class applies to ellipsoids with + * arbitrary flattening, in practice, its use should be limited to about + * b/\e a ∈ [0.01, 100] or \e f ∈ [−99, 0.99]. + * + * Definitions: + * - V0, the gravitational contribution to the normal + * potential; + * - Φ, the rotational contribution to the normal potential; + * - \e U = V0 + Φ, the total potential; + * - Γ = ∇V0, the acceleration due to + * mass of the earth; + * - f = ∇Φ, the centrifugal acceleration; + * - γ = ∇\e U = Γ + f, the normal + * acceleration; + * - \e X, \e Y, \e Z, geocentric coordinates; + * - \e x, \e y, \e z, local cartesian coordinates used to denote the east, + * north and up directions. + * + * References: + * - C. Somigliana, Teoria generale del campo gravitazionale dell'ellissoide + * di rotazione, Mem. Soc. Astron. Ital, 4, 541--599 (1929). + * - W. A. Heiskanen and H. Moritz, Physical Geodesy (Freeman, San + * Francisco, 1967), Secs. 1-19, 2-7, 2-8 (2-9, 2-10), 6-2 (6-3). + * - B. Hofmann-Wellenhof, H. Moritz, Physical Geodesy (Second edition, + * Springer, 2006) https://doi.org/10.1007/978-3-211-33545-1 + * - H. Moritz, Geodetic Reference System 1980, J. Geodesy 54(3), 395-405 + * (1980) https://doi.org/10.1007/BF02521480 + * + * For more information on normal gravity see \ref normalgravity. + * + * Example of use: + * \include example-NormalGravity.cpp + **********************************************************************/ + + class GEOGRAPHICLIB_EXPORT NormalGravity { + private: + static const int maxit_ = 20; + typedef Math::real real; + friend class GravityModel; + real _a, _GM, _omega, _f, _J2, _omega2, _aomega2; + real _e2, _ep2, _b, _E, _U0, _gammae, _gammap, _Q0, _k, _fstar; + Geocentric _earth; + static real atanzz(real x, bool alt) { + // This routine obeys the identity + // atanzz(x, alt) = atanzz(-x/(1+x), !alt) + // + // Require x >= -1. Best to call with alt, s.t. x >= 0; this results in + // a call to atan, instead of asin, or to asinh, instead of atanh. + using std::sqrt; using std::abs; using std::atan; using std::asin; + using std::asinh; using std::atanh; + real z = sqrt(abs(x)); + return x == 0 ? 1 : + (alt ? + (!(x < 0) ? asinh(z) : asin(z)) / sqrt(abs(x) / (1 + x)) : + (!(x < 0) ? atan(z) : atanh(z)) / z); + } + static real atan7series(real x); + static real atan5series(real x); + static real Qf(real x, bool alt); + static real Hf(real x, bool alt); + static real QH3f(real x, bool alt); + real Jn(int n) const; + void Initialize(real a, real GM, real omega, real f_J2, bool geometricp); + public: + + /** \name Setting up the normal gravity + **********************************************************************/ + ///@{ + /** + * Constructor for the normal gravity. + * + * @param[in] a equatorial radius (meters). + * @param[in] GM mass constant of the ellipsoid + * (meters3/seconds2); this is the product of \e G + * the gravitational constant and \e M the mass of the earth (usually + * including the mass of the earth's atmosphere). + * @param[in] omega the angular velocity (rad s−1). + * @param[in] f_J2 either the flattening of the ellipsoid \e f or the + * the dynamical form factor \e J2. + * @param[out] geometricp if true (the default), then \e f_J2 denotes the + * flattening, else it denotes the dynamical form factor \e J2. + * @exception if \e a is not positive or if the other parameters do not + * obey the restrictions given below. + * + * The shape of the ellipsoid can be given in one of two ways: + * - geometrically (\e geomtricp = true), the ellipsoid is defined by the + * flattening \e f = (\e a − \e b) / \e a, where \e a and \e b are + * the equatorial radius and the polar semi-axis. The parameters should + * obey \e a > 0, \e f < 1. There are no restrictions on \e GM or + * \e omega, in particular, \e GM need not be positive. + * - physically (\e geometricp = false), the ellipsoid is defined by the + * dynamical form factor J2 = (\e C − \e A) / + * Ma2, where \e A and \e C are the equatorial and + * polar moments of inertia and \e M is the mass of the earth. The + * parameters should obey \e a > 0, \e GM > 0 and \e J2 < 1/3 + * − (omega2a3/GM) + * 8/(45π). There is no restriction on \e omega. + **********************************************************************/ + NormalGravity(real a, real GM, real omega, real f_J2, + bool geometricp = true); + + /** + * A default constructor for the normal gravity. This sets up an + * uninitialized object and is used by GravityModel which constructs this + * object before it has read in the parameters for the reference ellipsoid. + **********************************************************************/ + NormalGravity() : _a(-1) {} + ///@} + + /** \name Compute the gravity + **********************************************************************/ + ///@{ + /** + * Evaluate the gravity on the surface of the ellipsoid. + * + * @param[in] lat the geographic latitude (degrees). + * @return γ the acceleration due to gravity, positive downwards + * (m s−2). + * + * Due to the axial symmetry of the ellipsoid, the result is independent of + * the value of the longitude. This acceleration is perpendicular to the + * surface of the ellipsoid. It includes the effects of the earth's + * rotation. + **********************************************************************/ + Math::real SurfaceGravity(real lat) const; + + /** + * Evaluate the gravity at an arbitrary point above (or below) the + * ellipsoid. + * + * @param[in] lat the geographic latitude (degrees). + * @param[in] h the height above the ellipsoid (meters). + * @param[out] gammay the northerly component of the acceleration + * (m s−2). + * @param[out] gammaz the upward component of the acceleration + * (m s−2); this is usually negative. + * @return \e U the corresponding normal potential + * (m2 s−2). + * + * Due to the axial symmetry of the ellipsoid, the result is independent of + * the value of the longitude and the easterly component of the + * acceleration vanishes, \e gammax = 0. The function includes the effects + * of the earth's rotation. When \e h = 0, this function gives \e gammay = + * 0 and the returned value matches that of NormalGravity::SurfaceGravity. + **********************************************************************/ + Math::real Gravity(real lat, real h, real& gammay, real& gammaz) + const; + + /** + * Evaluate the components of the acceleration due to gravity and the + * centrifugal acceleration in geocentric coordinates. + * + * @param[in] X geocentric coordinate of point (meters). + * @param[in] Y geocentric coordinate of point (meters). + * @param[in] Z geocentric coordinate of point (meters). + * @param[out] gammaX the \e X component of the acceleration + * (m s−2). + * @param[out] gammaY the \e Y component of the acceleration + * (m s−2). + * @param[out] gammaZ the \e Z component of the acceleration + * (m s−2). + * @return \e U = V0 + Φ the sum of the + * gravitational and centrifugal potentials + * (m2 s−2). + * + * The acceleration given by γ = ∇\e U = + * ∇V0 + ∇Φ = Γ + f. + **********************************************************************/ + Math::real U(real X, real Y, real Z, + real& gammaX, real& gammaY, real& gammaZ) const; + + /** + * Evaluate the components of the acceleration due to the gravitational + * force in geocentric coordinates. + * + * @param[in] X geocentric coordinate of point (meters). + * @param[in] Y geocentric coordinate of point (meters). + * @param[in] Z geocentric coordinate of point (meters). + * @param[out] GammaX the \e X component of the acceleration due to the + * gravitational force (m s−2). + * @param[out] GammaY the \e Y component of the acceleration due to the + * @param[out] GammaZ the \e Z component of the acceleration due to the + * gravitational force (m s−2). + * @return V0 the gravitational potential + * (m2 s−2). + * + * This function excludes the centrifugal acceleration and is appropriate + * to use for space applications. In terrestrial applications, the + * function NormalGravity::U (which includes this effect) should usually be + * used. + **********************************************************************/ + Math::real V0(real X, real Y, real Z, + real& GammaX, real& GammaY, real& GammaZ) const; + + /** + * Evaluate the centrifugal acceleration in geocentric coordinates. + * + * @param[in] X geocentric coordinate of point (meters). + * @param[in] Y geocentric coordinate of point (meters). + * @param[out] fX the \e X component of the centrifugal acceleration + * (m s−2). + * @param[out] fY the \e Y component of the centrifugal acceleration + * (m s−2). + * @return Φ the centrifugal potential (m2 + * s−2). + * + * Φ is independent of \e Z, thus \e fZ = 0. This function + * NormalGravity::U sums the results of NormalGravity::V0 and + * NormalGravity::Phi. + **********************************************************************/ + Math::real Phi(real X, real Y, real& fX, real& fY) const; + ///@} + + /** \name Inspector functions + **********************************************************************/ + ///@{ + /** + * @return true if the object has been initialized. + **********************************************************************/ + bool Init() const { return _a > 0; } + + /** + * @return \e a the equatorial radius of the ellipsoid (meters). This is + * the value used in the constructor. + **********************************************************************/ + Math::real EquatorialRadius() const + { return Init() ? _a : Math::NaN(); } + + /** + * @return \e GM the mass constant of the ellipsoid + * (m3 s−2). This is the value used in the + * constructor. + **********************************************************************/ + Math::real MassConstant() const + { return Init() ? _GM : Math::NaN(); } + + /** + * @return Jn the dynamical form factors of the + * ellipsoid. + * + * If \e n = 2 (the default), this is the value of J2 + * used in the constructor. Otherwise it is the zonal coefficient of the + * Legendre harmonic sum of the normal gravitational potential. Note that + * Jn = 0 if \e n is odd. In most gravity + * applications, fully normalized Legendre functions are used and the + * corresponding coefficient is Cn0 = + * −Jn / sqrt(2 \e n + 1). + **********************************************************************/ + Math::real DynamicalFormFactor(int n = 2) const + { return Init() ? ( n == 2 ? _J2 : Jn(n)) : Math::NaN(); } + + /** + * @return ω the angular velocity of the ellipsoid (rad + * s−1). This is the value used in the constructor. + **********************************************************************/ + Math::real AngularVelocity() const + { return Init() ? _omega : Math::NaN(); } + + /** + * @return f the flattening of the ellipsoid (\e a − \e b)/\e + * a. + **********************************************************************/ + Math::real Flattening() const + { return Init() ? _f : Math::NaN(); } + + /** + * @return γe the normal gravity at equator (m + * s−2). + **********************************************************************/ + Math::real EquatorialGravity() const + { return Init() ? _gammae : Math::NaN(); } + + /** + * @return γp the normal gravity at poles (m + * s−2). + **********************************************************************/ + Math::real PolarGravity() const + { return Init() ? _gammap : Math::NaN(); } + + /** + * @return f* the gravity flattening (γp − + * γe) / γe. + **********************************************************************/ + Math::real GravityFlattening() const + { return Init() ? _fstar : Math::NaN(); } + + /** + * @return U0 the constant normal potential for the + * surface of the ellipsoid (m2 s−2). + **********************************************************************/ + Math::real SurfacePotential() const + { return Init() ? _U0 : Math::NaN(); } + + /** + * @return the Geocentric object used by this instance. + **********************************************************************/ + const Geocentric& Earth() const { return _earth; } + + /** + * \deprecated An old name for EquatorialRadius(). + **********************************************************************/ + GEOGRAPHICLIB_DEPRECATED("Use EquatorialRadius()") + Math::real MajorRadius() const { return EquatorialRadius(); } + ///@} + + /** + * A global instantiation of NormalGravity for the WGS84 ellipsoid. + **********************************************************************/ + static const NormalGravity& WGS84(); + + /** + * A global instantiation of NormalGravity for the GRS80 ellipsoid. + **********************************************************************/ + static const NormalGravity& GRS80(); + + /** + * Compute the flattening from the dynamical form factor. + * + * @param[in] a equatorial radius (meters). + * @param[in] GM mass constant of the ellipsoid + * (meters3/seconds2); this is the product of \e G + * the gravitational constant and \e M the mass of the earth (usually + * including the mass of the earth's atmosphere). + * @param[in] omega the angular velocity (rad s−1). + * @param[in] J2 the dynamical form factor. + * @return \e f the flattening of the ellipsoid. + * + * This routine requires \e a > 0, \e GM > 0, \e J2 < 1/3 − + * omega2a3/GM 8/(45π). A + * NaN is returned if these conditions do not hold. The restriction to + * positive \e GM is made because for negative \e GM two solutions are + * possible. + **********************************************************************/ + static Math::real J2ToFlattening(real a, real GM, real omega, real J2); + + /** + * Compute the dynamical form factor from the flattening. + * + * @param[in] a equatorial radius (meters). + * @param[in] GM mass constant of the ellipsoid + * (meters3/seconds2); this is the product of \e G + * the gravitational constant and \e M the mass of the earth (usually + * including the mass of the earth's atmosphere). + * @param[in] omega the angular velocity (rad s−1). + * @param[in] f the flattening of the ellipsoid. + * @return \e J2 the dynamical form factor. + * + * This routine requires \e a > 0, \e GM ≠ 0, \e f < 1. The + * values of these parameters are not checked. + **********************************************************************/ + static Math::real FlatteningToJ2(real a, real GM, real omega, real f); + }; + +} // namespace GeographicLib + +#endif // GEOGRAPHICLIB_NORMALGRAVITY_HPP diff --git a/common/local_libs/GeographicLib/include/GeographicLib/OSGB.hpp b/common/local_libs/GeographicLib/include/GeographicLib/OSGB.hpp new file mode 100644 index 0000000000..c2e7797a77 --- /dev/null +++ b/common/local_libs/GeographicLib/include/GeographicLib/OSGB.hpp @@ -0,0 +1,255 @@ +/** + * \file OSGB.hpp + * \brief Header for GeographicLib::OSGB class + * + * Copyright (c) Charles Karney (2010-2020) and licensed + * under the MIT/X11 License. For more information, see + * https://geographiclib.sourceforge.io/ + **********************************************************************/ + +#if !defined(GEOGRAPHICLIB_OSGB_HPP) +#define GEOGRAPHICLIB_OSGB_HPP 1 + +#include +#include + +#if defined(_MSC_VER) +// Squelch warnings about dll vs string +# pragma warning (push) +# pragma warning (disable: 4251) +#endif + +namespace GeographicLib { + + /** + * \brief Ordnance Survey grid system for Great Britain + * + * The class implements the coordinate system used by the Ordnance Survey for + * maps of Great Britain and conversions to the grid reference system. + * + * See + * - + * A guide to coordinate systems in Great Britain + * - + * Guide to the National Grid + * + * \warning the latitudes and longitudes for the Ordnance Survey grid + * system do not use the WGS84 datum. Do not use the values returned by this + * class in the UTMUPS, MGRS, or Geoid classes without first converting the + * datum (and vice versa). + * + * Example of use: + * \include example-OSGB.cpp + **********************************************************************/ + class GEOGRAPHICLIB_EXPORT OSGB { + private: + typedef Math::real real; + static const char* const letters_; + static const char* const digits_; + static const TransverseMercator& OSGBTM(); + enum { + base_ = 10, + tile_ = 100000, + tilelevel_ = 5, + tilegrid_ = 5, + tileoffx_ = 2 * tilegrid_, + tileoffy_ = 1 * tilegrid_, + minx_ = - tileoffx_ * tile_, + miny_ = - tileoffy_ * tile_, + maxx_ = (tilegrid_*tilegrid_ - tileoffx_) * tile_, + maxy_ = (tilegrid_*tilegrid_ - tileoffy_) * tile_, + // Maximum precision is um + maxprec_ = 5 + 6, + }; + static real computenorthoffset(); + static void CheckCoords(real x, real y); + OSGB(); // Disable constructor + public: + + /** + * Forward projection, from geographic to OSGB coordinates. + * + * @param[in] lat latitude of point (degrees). + * @param[in] lon longitude of point (degrees). + * @param[out] x easting of point (meters). + * @param[out] y northing of point (meters). + * @param[out] gamma meridian convergence at point (degrees). + * @param[out] k scale of projection at point. + * + * \e lat should be in the range [−90°, 90°]. + **********************************************************************/ + static void Forward(real lat, real lon, + real& x, real& y, real& gamma, real& k) { + OSGBTM().Forward(OriginLongitude(), lat, lon, x, y, gamma, k); + x += FalseEasting(); + y += computenorthoffset(); + } + + /** + * Reverse projection, from OSGB coordinates to geographic. + * + * @param[in] x easting of point (meters). + * @param[in] y northing of point (meters). + * @param[out] lat latitude of point (degrees). + * @param[out] lon longitude of point (degrees). + * @param[out] gamma meridian convergence at point (degrees). + * @param[out] k scale of projection at point. + * + * The value of \e lon returned is in the range [−180°, + * 180°]. + **********************************************************************/ + + static void Reverse(real x, real y, + real& lat, real& lon, real& gamma, real& k) { + x -= FalseEasting(); + y -= computenorthoffset(); + OSGBTM().Reverse(OriginLongitude(), x, y, lat, lon, gamma, k); + } + + /** + * OSGB::Forward without returning the convergence and scale. + **********************************************************************/ + static void Forward(real lat, real lon, real& x, real& y) { + real gamma, k; + Forward(lat, lon, x, y, gamma, k); + } + + /** + * OSGB::Reverse without returning the convergence and scale. + **********************************************************************/ + static void Reverse(real x, real y, real& lat, real& lon) { + real gamma, k; + Reverse(x, y, lat, lon, gamma, k); + } + + /** + * Convert OSGB coordinates to a grid reference. + * + * @param[in] x easting of point (meters). + * @param[in] y northing of point (meters). + * @param[in] prec precision relative to 100 km. + * @param[out] gridref National Grid reference. + * @exception GeographicErr if \e prec, \e x, or \e y is outside its + * allowed range. + * @exception std::bad_alloc if the memory for \e gridref can't be + * allocatied. + * + * \e prec specifies the precision of the grid reference string as follows: + * - prec = 0 (min), 100km + * - prec = 1, 10km + * - prec = 2, 1km + * - prec = 3, 100m + * - prec = 4, 10m + * - prec = 5, 1m + * - prec = 6, 0.1m + * - prec = 11 (max), 1μm + * + * The easting must be in the range [−1000 km, 1500 km) and the + * northing must be in the range [−500 km, 2000 km). These bounds + * are consistent with rules for the letter designations for the grid + * system. + * + * If \e x or \e y is NaN, the returned grid reference is "INVALID". + **********************************************************************/ + static void GridReference(real x, real y, int prec, std::string& gridref); + + /** + * Convert OSGB coordinates to a grid reference. + * + * @param[in] gridref National Grid reference. + * @param[out] x easting of point (meters). + * @param[out] y northing of point (meters). + * @param[out] prec precision relative to 100 km. + * @param[in] centerp if true (default), return center of the grid square, + * else return SW (lower left) corner. + * @exception GeographicErr if \e gridref is illegal. + * + * The grid reference must be of the form: two letters (not including I) + * followed by an even number of digits (up to 22). + * + * If the first 2 characters of \e gridref are "IN", then \e x and \e y are + * set to NaN and \e prec is set to −2. + **********************************************************************/ + static void GridReference(const std::string& gridref, + real& x, real& y, int& prec, + bool centerp = true); + + /** \name Inspector functions + **********************************************************************/ + ///@{ + /** + * @return \e a the equatorial radius of the Airy 1830 ellipsoid (meters). + * + * This is 20923713 ft converted to meters using the rule 1 ft = + * 109.48401603−10 m. The Airy 1830 value is returned + * because the OSGB projection is based on this ellipsoid. The conversion + * factor from feet to meters is the one used for the 1936 retriangulation + * of Britain; see Section A.1 (p. 37) of A guide to coordinate systems + * in Great Britain, v2.2 (Dec. 2013). + **********************************************************************/ + static Math::real EquatorialRadius() { + // result is about 6377563.3960320664406 m + using std::pow; + return pow(real(10), real(48401603 - 100000000) / 100000000) + * real(20923713); + } + + /** + * @return \e f the inverse flattening of the Airy 1830 ellipsoid. + * + * For the Airy 1830 ellipsoid, \e a = 20923713 ft and \e b = 20853810 ft; + * thus the flattening = (20923713 − 20853810)/20923713 = + * 7767/2324857 = 1/299.32496459... (The Airy 1830 value is returned + * because the OSGB projection is based on this ellipsoid.) + **********************************************************************/ + static Math::real Flattening() + { return real(20923713 - 20853810) / real(20923713); } + + /** + * @return \e k0 central scale for the OSGB projection (0.9996012717...). + * + * C. J. Mugnier, Grids & Datums, PE&RS, Oct. 2003, states that + * this is defined as 109.9998268−10. + **********************************************************************/ + static Math::real CentralScale() { + using std::pow; + return pow(real(10), real(9998268 - 10000000) / 10000000); + } + + /** + * @return latitude of the origin for the OSGB projection (49 degrees). + **********************************************************************/ + static Math::real OriginLatitude() { return real(49); } + + /** + * @return longitude of the origin for the OSGB projection (−2 + * degrees). + **********************************************************************/ + static Math::real OriginLongitude() { return real(-2); } + + /** + * @return false northing the OSGB projection (−100000 meters). + **********************************************************************/ + static Math::real FalseNorthing() { return real(-100000); } + + /** + * @return false easting the OSGB projection (400000 meters). + **********************************************************************/ + static Math::real FalseEasting() { return real(400000); } + + /** + * \deprecated An old name for EquatorialRadius(). + **********************************************************************/ + GEOGRAPHICLIB_DEPRECATED("Use EquatorialRadius()") + static Math::real MajorRadius() { return EquatorialRadius(); } + ///@} + + }; + +} // namespace GeographicLib + +#if defined(_MSC_VER) +# pragma warning (pop) +#endif + +#endif // GEOGRAPHICLIB_OSGB_HPP diff --git a/common/local_libs/GeographicLib/include/GeographicLib/PolarStereographic.hpp b/common/local_libs/GeographicLib/include/GeographicLib/PolarStereographic.hpp new file mode 100644 index 0000000000..e82870672d --- /dev/null +++ b/common/local_libs/GeographicLib/include/GeographicLib/PolarStereographic.hpp @@ -0,0 +1,160 @@ +/** + * \file PolarStereographic.hpp + * \brief Header for GeographicLib::PolarStereographic class + * + * Copyright (c) Charles Karney (2008-2020) and licensed + * under the MIT/X11 License. For more information, see + * https://geographiclib.sourceforge.io/ + **********************************************************************/ + +#if !defined(GEOGRAPHICLIB_POLARSTEREOGRAPHIC_HPP) +#define GEOGRAPHICLIB_POLARSTEREOGRAPHIC_HPP 1 + +#include + +namespace GeographicLib { + + /** + * \brief Polar stereographic projection + * + * Implementation taken from the report, + * - J. P. Snyder, + * Map Projections: A + * Working Manual, USGS Professional Paper 1395 (1987), + * pp. 160--163. + * + * This is a straightforward implementation of the equations in Snyder except + * that Newton's method is used to invert the projection. + * + * This class also returns the meridian convergence \e gamma and scale \e k. + * The meridian convergence is the bearing of grid north (the \e y axis) + * measured clockwise from true north. + * + * Example of use: + * \include example-PolarStereographic.cpp + **********************************************************************/ + class GEOGRAPHICLIB_EXPORT PolarStereographic { + private: + typedef Math::real real; + real _a, _f, _e2, _es, _e2m, _c; + real _k0; + public: + + /** + * Constructor for a ellipsoid with + * + * @param[in] a equatorial radius (meters). + * @param[in] f flattening of ellipsoid. Setting \e f = 0 gives a sphere. + * Negative \e f gives a prolate ellipsoid. + * @param[in] k0 central scale factor. + * @exception GeographicErr if \e a, (1 − \e f) \e a, or \e k0 is + * not positive. + **********************************************************************/ + PolarStereographic(real a, real f, real k0); + + /** + * Set the scale for the projection. + * + * @param[in] lat (degrees) assuming \e northp = true. + * @param[in] k scale at latitude \e lat (default 1). + * @exception GeographicErr \e k is not positive. + * @exception GeographicErr if \e lat is not in (−90°, + * 90°]. + **********************************************************************/ + void SetScale(real lat, real k = real(1)); + + /** + * Forward projection, from geographic to polar stereographic. + * + * @param[in] northp the pole which is the center of projection (true means + * north, false means south). + * @param[in] lat latitude of point (degrees). + * @param[in] lon longitude of point (degrees). + * @param[out] x easting of point (meters). + * @param[out] y northing of point (meters). + * @param[out] gamma meridian convergence at point (degrees). + * @param[out] k scale of projection at point. + * + * No false easting or northing is added. \e lat should be in the range + * (−90°, 90°] for \e northp = true and in the range + * [−90°, 90°) for \e northp = false. + **********************************************************************/ + void Forward(bool northp, real lat, real lon, + real& x, real& y, real& gamma, real& k) const; + + /** + * Reverse projection, from polar stereographic to geographic. + * + * @param[in] northp the pole which is the center of projection (true means + * north, false means south). + * @param[in] x easting of point (meters). + * @param[in] y northing of point (meters). + * @param[out] lat latitude of point (degrees). + * @param[out] lon longitude of point (degrees). + * @param[out] gamma meridian convergence at point (degrees). + * @param[out] k scale of projection at point. + * + * No false easting or northing is added. The value of \e lon returned is + * in the range [−180°, 180°]. + **********************************************************************/ + void Reverse(bool northp, real x, real y, + real& lat, real& lon, real& gamma, real& k) const; + + /** + * PolarStereographic::Forward without returning the convergence and scale. + **********************************************************************/ + void Forward(bool northp, real lat, real lon, + real& x, real& y) const { + real gamma, k; + Forward(northp, lat, lon, x, y, gamma, k); + } + + /** + * PolarStereographic::Reverse without returning the convergence and scale. + **********************************************************************/ + void Reverse(bool northp, real x, real y, + real& lat, real& lon) const { + real gamma, k; + Reverse(northp, x, y, lat, lon, gamma, k); + } + + /** \name Inspector functions + **********************************************************************/ + ///@{ + /** + * @return \e a the equatorial radius of the ellipsoid (meters). This is + * the value used in the constructor. + **********************************************************************/ + Math::real EquatorialRadius() const { return _a; } + + /** + * @return \e f the flattening of the ellipsoid. This is the value used in + * the constructor. + **********************************************************************/ + Math::real Flattening() const { return _f; } + + /** + * The central scale for the projection. This is the value of \e k0 used + * in the constructor and is the scale at the pole unless overridden by + * PolarStereographic::SetScale. + **********************************************************************/ + Math::real CentralScale() const { return _k0; } + + /** + * \deprecated An old name for EquatorialRadius(). + **********************************************************************/ + GEOGRAPHICLIB_DEPRECATED("Use EquatorialRadius()") + Math::real MajorRadius() const { return EquatorialRadius(); } + ///@} + + /** + * A global instantiation of PolarStereographic with the WGS84 ellipsoid + * and the UPS scale factor. However, unlike UPS, no false easting or + * northing is added. + **********************************************************************/ + static const PolarStereographic& UPS(); + }; + +} // namespace GeographicLib + +#endif // GEOGRAPHICLIB_POLARSTEREOGRAPHIC_HPP diff --git a/common/local_libs/GeographicLib/include/GeographicLib/PolygonArea.hpp b/common/local_libs/GeographicLib/include/GeographicLib/PolygonArea.hpp new file mode 100644 index 0000000000..c88009a959 --- /dev/null +++ b/common/local_libs/GeographicLib/include/GeographicLib/PolygonArea.hpp @@ -0,0 +1,297 @@ +/** + * \file PolygonArea.hpp + * \brief Header for GeographicLib::PolygonAreaT class + * + * Copyright (c) Charles Karney (2010-2020) and licensed + * under the MIT/X11 License. For more information, see + * https://geographiclib.sourceforge.io/ + **********************************************************************/ + +#if !defined(GEOGRAPHICLIB_POLYGONAREA_HPP) +#define GEOGRAPHICLIB_POLYGONAREA_HPP 1 + +#include +#include +#include +#include + +namespace GeographicLib { + + /** + * \brief Polygon areas + * + * This computes the area of a polygon whose edges are geodesics using the + * method given in Section 6 of + * - C. F. F. Karney, + * + * Algorithms for geodesics, + * J. Geodesy 87, 43--55 (2013); + * DOI: + * 10.1007/s00190-012-0578-z; + * addenda: + * + * geod-addenda.html. + * + * Arbitrarily complex polygons are allowed. In the case self-intersecting + * of polygons the area is accumulated "algebraically", e.g., the areas of + * the 2 loops in a figure-8 polygon will partially cancel. + * + * This class lets you add vertices and edges one at a time to the polygon. + * The sequence must start with a vertex and thereafter vertices and edges + * can be added in any order. Any vertex after the first creates a new edge + * which is the \e shortest geodesic from the previous vertex. In some + * cases there may be two or many such shortest geodesics and the area is + * then not uniquely defined. In this case, either add an intermediate + * vertex or add the edge \e as an edge (by defining its direction and + * length). + * + * The area and perimeter are accumulated at two times the standard floating + * point precision to guard against the loss of accuracy with many-sided + * polygons. At any point you can ask for the perimeter and area so far. + * There's an option to treat the points as defining a polyline instead of a + * polygon; in that case, only the perimeter is computed. + * + * This is a templated class to allow it to be used with Geodesic, + * GeodesicExact, and Rhumb. GeographicLib::PolygonArea, + * GeographicLib::PolygonAreaExact, and GeographicLib::PolygonAreaRhumb are + * typedefs for these cases. + * + * @tparam GeodType the geodesic class to use. + * + * Example of use: + * \include example-PolygonArea.cpp + * + * Planimeter is a command-line utility + * providing access to the functionality of PolygonAreaT. + **********************************************************************/ + + template + class PolygonAreaT { + private: + typedef Math::real real; + GeodType _earth; + real _area0; // Full ellipsoid area + bool _polyline; // Assume polyline (don't close and skip area) + unsigned _mask; + unsigned _num; + int _crossings; + Accumulator<> _areasum, _perimetersum; + real _lat0, _lon0, _lat1, _lon1; + static int transit(real lon1, real lon2) { + // 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 = Math::AngNormalize(lon1); + lon2 = Math::AngNormalize(lon2); + real lon12 = Math::AngDiff(lon1, lon2); + // Treat 0 as negative in these tests. This balances +/- 180 being + // treated as positive, i.e., +180. + int cross = + lon1 <= 0 && lon2 > 0 && lon12 > 0 ? 1 : + (lon2 <= 0 && lon1 > 0 && lon12 < 0 ? -1 : 0); + return cross; + } + // an alternate version of transit to deal with longitudes in the direct + // problem. + static int transitdirect(real lon1, real lon2) { + // Compute exactly the parity of + // int(ceil(lon2 / 360)) - int(ceil(lon1 / 360)) + using std::remainder; + lon1 = remainder(lon1, real(720)); + lon2 = remainder(lon2, real(720)); + return ( (lon2 <= 0 && lon2 > -360 ? 1 : 0) - + (lon1 <= 0 && lon1 > -360 ? 1 : 0) ); + } + void Remainder(Accumulator<>& a) const { a.remainder(_area0); } + void Remainder(real& a) const { + using std::remainder; + a = remainder(a, _area0); + } + template + void AreaReduce(T& area, int crossings, bool reverse, bool sign) const; + public: + + /** + * Constructor for PolygonAreaT. + * + * @param[in] earth the Geodesic object to use for geodesic calculations. + * @param[in] polyline if true that treat the points as defining a polyline + * instead of a polygon (default = false). + **********************************************************************/ + PolygonAreaT(const GeodType& earth, bool polyline = false) + : _earth(earth) + , _area0(_earth.EllipsoidArea()) + , _polyline(polyline) + , _mask(GeodType::LATITUDE | GeodType::LONGITUDE | GeodType::DISTANCE | + (_polyline ? GeodType::NONE : + GeodType::AREA | GeodType::LONG_UNROLL)) + { Clear(); } + + /** + * Clear PolygonAreaT, allowing a new polygon to be started. + **********************************************************************/ + void Clear() { + _num = 0; + _crossings = 0; + _areasum = 0; + _perimetersum = 0; + _lat0 = _lon0 = _lat1 = _lon1 = Math::NaN(); + } + + /** + * Add a point to the polygon or polyline. + * + * @param[in] lat the latitude of the point (degrees). + * @param[in] lon the longitude of the point (degrees). + * + * \e lat should be in the range [−90°, 90°]. + **********************************************************************/ + void AddPoint(real lat, real lon); + + /** + * Add an edge to the polygon or polyline. + * + * @param[in] azi azimuth at current point (degrees). + * @param[in] s distance from current point to next point (meters). + * + * This does nothing if no points have been added yet. Use + * PolygonAreaT::CurrentPoint to determine the position of the new vertex. + **********************************************************************/ + void AddEdge(real azi, real s); + + /** + * Return the results so far. + * + * @param[in] reverse if true then clockwise (instead of counter-clockwise) + * traversal counts as a positive area. + * @param[in] sign if true then return a signed result for the area if + * the polygon is traversed in the "wrong" direction instead of returning + * the area for the rest of the earth. + * @param[out] perimeter the perimeter of the polygon or length of the + * polyline (meters). + * @param[out] area the area of the polygon (meters2); only set + * if \e polyline is false in the constructor. + * @return the number of points. + * + * More points can be added to the polygon after this call. + **********************************************************************/ + unsigned Compute(bool reverse, bool sign, + real& perimeter, real& area) const; + + /** + * Return the results assuming a tentative final test point is added; + * however, the data for the test point is not saved. This lets you report + * a running result for the perimeter and area as the user moves the mouse + * cursor. Ordinary floating point arithmetic is used to accumulate the + * data for the test point; thus the area and perimeter returned are less + * accurate than if PolygonAreaT::AddPoint and PolygonAreaT::Compute are + * used. + * + * @param[in] lat the latitude of the test point (degrees). + * @param[in] lon the longitude of the test point (degrees). + * @param[in] reverse if true then clockwise (instead of counter-clockwise) + * traversal counts as a positive area. + * @param[in] sign if true then return a signed result for the area if + * the polygon is traversed in the "wrong" direction instead of returning + * the area for the rest of the earth. + * @param[out] perimeter the approximate perimeter of the polygon or length + * of the polyline (meters). + * @param[out] area the approximate area of the polygon + * (meters2); only set if polyline is false in the + * constructor. + * @return the number of points. + * + * \e lat should be in the range [−90°, 90°]. + **********************************************************************/ + unsigned TestPoint(real lat, real lon, bool reverse, bool sign, + real& perimeter, real& area) const; + + /** + * Return the results assuming a tentative final test point is added via an + * azimuth and distance; however, the data for the test point is not saved. + * This lets you report a running result for the perimeter and area as the + * user moves the mouse cursor. Ordinary floating point arithmetic is used + * to accumulate the data for the test point; thus the area and perimeter + * returned are less accurate than if PolygonAreaT::AddEdge and + * PolygonAreaT::Compute are used. + * + * @param[in] azi azimuth at current point (degrees). + * @param[in] s distance from current point to final test point (meters). + * @param[in] reverse if true then clockwise (instead of counter-clockwise) + * traversal counts as a positive area. + * @param[in] sign if true then return a signed result for the area if + * the polygon is traversed in the "wrong" direction instead of returning + * the area for the rest of the earth. + * @param[out] perimeter the approximate perimeter of the polygon or length + * of the polyline (meters). + * @param[out] area the approximate area of the polygon + * (meters2); only set if polyline is false in the + * constructor. + * @return the number of points. + **********************************************************************/ + unsigned TestEdge(real azi, real s, bool reverse, bool sign, + real& perimeter, real& area) const; + + /** \name Inspector functions + **********************************************************************/ + ///@{ + /** + * @return \e a the equatorial radius of the ellipsoid (meters). This is + * the value inherited from the Geodesic object used in the constructor. + **********************************************************************/ + + Math::real EquatorialRadius() const { return _earth.EquatorialRadius(); } + + /** + * @return \e f the flattening of the ellipsoid. This is the value + * inherited from the Geodesic object used in the constructor. + **********************************************************************/ + Math::real Flattening() const { return _earth.Flattening(); } + + /** + * Report the previous vertex added to the polygon or polyline. + * + * @param[out] lat the latitude of the point (degrees). + * @param[out] lon the longitude of the point (degrees). + * + * If no points have been added, then NaNs are returned. Otherwise, \e lon + * will be in the range [−180°, 180°]. + **********************************************************************/ + void CurrentPoint(real& lat, real& lon) const + { lat = _lat1; lon = _lon1; } + + /** + * \deprecated An old name for EquatorialRadius(). + **********************************************************************/ + GEOGRAPHICLIB_DEPRECATED("Use EquatorialRadius()") + Math::real MajorRadius() const { return EquatorialRadius(); } + ///@} + }; + + /** + * @relates PolygonAreaT + * + * Polygon areas using Geodesic. This should be used if the flattening is + * small. + **********************************************************************/ + typedef PolygonAreaT PolygonArea; + + /** + * @relates PolygonAreaT + * + * Polygon areas using GeodesicExact. (But note that the implementation of + * areas in GeodesicExact uses a high order series and this is only accurate + * for modest flattenings.) + **********************************************************************/ + typedef PolygonAreaT PolygonAreaExact; + + /** + * @relates PolygonAreaT + * + * Polygon areas using Rhumb. + **********************************************************************/ + typedef PolygonAreaT PolygonAreaRhumb; + +} // namespace GeographicLib + +#endif // GEOGRAPHICLIB_POLYGONAREA_HPP diff --git a/common/local_libs/GeographicLib/include/GeographicLib/Rhumb.hpp b/common/local_libs/GeographicLib/include/GeographicLib/Rhumb.hpp new file mode 100644 index 0000000000..7fe0028e7d --- /dev/null +++ b/common/local_libs/GeographicLib/include/GeographicLib/Rhumb.hpp @@ -0,0 +1,617 @@ +/** + * \file Rhumb.hpp + * \brief Header for GeographicLib::Rhumb and GeographicLib::RhumbLine classes + * + * Copyright (c) Charles Karney (2014-2020) and licensed + * under the MIT/X11 License. For more information, see + * https://geographiclib.sourceforge.io/ + **********************************************************************/ + +#if !defined(GEOGRAPHICLIB_RHUMB_HPP) +#define GEOGRAPHICLIB_RHUMB_HPP 1 + +#include +#include + +#if !defined(GEOGRAPHICLIB_RHUMBAREA_ORDER) +/** + * The order of the series approximation used in rhumb area calculations. + * GEOGRAPHICLIB_RHUMBAREA_ORDER can be set to any integer in [4, 8]. + **********************************************************************/ +# define GEOGRAPHICLIB_RHUMBAREA_ORDER \ + (GEOGRAPHICLIB_PRECISION == 2 ? 6 : \ + (GEOGRAPHICLIB_PRECISION == 1 ? 4 : 8)) +#endif + +namespace GeographicLib { + + class RhumbLine; + template class PolygonAreaT; + + /** + * \brief Solve of the direct and inverse rhumb problems. + * + * The path of constant azimuth between two points on a ellipsoid at (\e + * lat1, \e lon1) and (\e lat2, \e lon2) is called the rhumb line (also + * called the loxodrome). Its length is \e s12 and its azimuth is \e azi12. + * (The azimuth is the heading measured clockwise from north.) + * + * Given \e lat1, \e lon1, \e azi12, and \e s12, we can determine \e lat2, + * and \e lon2. This is the \e direct rhumb problem and its solution is + * given by the function Rhumb::Direct. + * + * Given \e lat1, \e lon1, \e lat2, and \e lon2, we can determine \e azi12 + * and \e s12. This is the \e inverse rhumb problem, whose solution is given + * by Rhumb::Inverse. This finds the shortest such rhumb line, i.e., the one + * that wraps no more than half way around the earth. If the end points are + * on opposite meridians, there are two shortest rhumb lines and the + * east-going one is chosen. + * + * These routines also optionally calculate the area under the rhumb line, \e + * S12. This is the area, measured counter-clockwise, of the rhumb line + * quadrilateral with corners (lat1,lon1), (0,lon1), + * (0,lon2), and (lat2,lon2). + * + * Note that rhumb lines may be appreciably longer (up to 50%) than the + * corresponding Geodesic. For example the distance between London Heathrow + * and Tokyo Narita via the rhumb line is 11400 km which is 18% longer than + * the geodesic distance 9600 km. + * + * For more information on rhumb lines see \ref rhumb. + * + * Example of use: + * \include example-Rhumb.cpp + **********************************************************************/ + + class GEOGRAPHICLIB_EXPORT Rhumb { + private: + typedef Math::real real; + friend class RhumbLine; + template friend class PolygonAreaT; + Ellipsoid _ell; + bool _exact; + real _c2; + static const int tm_maxord = GEOGRAPHICLIB_TRANSVERSEMERCATOR_ORDER; + static const int maxpow_ = GEOGRAPHICLIB_RHUMBAREA_ORDER; + // _R[0] unused + real _R[maxpow_ + 1]; + static real gd(real x) + { using std::atan; using std::sinh; return atan(sinh(x)); } + + // Use divided differences to determine (mu2 - mu1) / (psi2 - psi1) + // accurately + // + // Definition: Df(x,y,d) = (f(x) - f(y)) / (x - y) + // See: + // W. M. Kahan and R. J. Fateman, + // Symbolic computation of divided differences, + // SIGSAM Bull. 33(3), 7-28 (1999) + // https://doi.org/10.1145/334714.334716 + // http://www.cs.berkeley.edu/~fateman/papers/divdiff.pdf + + static real Dlog(real x, real y) { + using std::sqrt; using std::asinh; + real t = x - y; + // Change + // + // atanh(t / (x + y)) + // + // to + // + // asinh(t / (2 * sqrt(x*y))) + // + // to avoid taking atanh(1) when x is large and y is 1. N.B., this + // routine is invoked with positive x and y, so no need to guard against + // taking the sqrt of a negative quantity. This fixes bogus results for + // the area being returning when an endpoint is at a pole. + return t != 0 ? 2 * asinh(t / (2 * sqrt(x*y))) / t : 1 / x; + } + // N.B., x and y are in degrees + static real Dtan(real x, real y) { + real d = x - y, tx = Math::tand(x), ty = Math::tand(y), txy = tx * ty; + return d != 0 ? + (2 * txy > -1 ? (1 + txy) * Math::tand(d) : tx - ty) / + (d * Math::degree()) : + 1 + txy; + } + static real Datan(real x, real y) { + using std::atan; + real d = x - y, xy = x * y; + return d != 0 ? + (2 * xy > -1 ? atan( d / (1 + xy) ) : atan(x) - atan(y)) / d : + 1 / (1 + xy); + } + static real Dsin(real x, real y) { + using std::sin; using std::cos; + real d = (x - y) / 2; + return cos((x + y)/2) * (d != 0 ? sin(d) / d : 1); + } + static real Dsinh(real x, real y) { + using std::sinh; using std::cosh; + real d = (x - y) / 2; + return cosh((x + y) / 2) * (d != 0 ? sinh(d) / d : 1); + } + static real Dcosh(real x, real y) { + using std::sinh; + real d = (x - y) / 2; + return sinh((x + y) / 2) * (d != 0 ? sinh(d) / d : 1); + } + static real Dasinh(real x, real y) { + using std::asinh; using std::hypot; + real d = x - y, + hx = hypot(real(1), x), hy = hypot(real(1), y); + return d != 0 ? + asinh(x*y > 0 ? d * (x + y) / (x*hy + y*hx) : x*hy - y*hx) / d : + 1 / hx; + } + static real Dgd(real x, real y) { + using std::sinh; + return Datan(sinh(x), sinh(y)) * Dsinh(x, y); + } + // N.B., x and y are the tangents of the angles + static real Dgdinv(real x, real y) + { return Dasinh(x, y) / Datan(x, y); } + // Copied from LambertConformalConic... + // Deatanhe(x,y) = eatanhe((x-y)/(1-e^2*x*y))/(x-y) + real Deatanhe(real x, real y) const { + real t = x - y, d = 1 - _ell._e2 * x * y; + return t != 0 ? Math::eatanhe(t / d, _ell._es) / t : _ell._e2 / d; + } + // (E(x) - E(y)) / (x - y) -- E = incomplete elliptic integral of 2nd kind + real DE(real x, real y) const; + // (mux - muy) / (phix - phiy) using elliptic integrals + real DRectifying(real latx, real laty) const; + // (psix - psiy) / (phix - phiy) + real DIsometric(real latx, real laty) const; + + // (sum(c[j]*sin(2*j*x),j=1..n) - sum(c[j]*sin(2*j*x),j=1..n)) / (x - y) + static real SinCosSeries(bool sinp, + real x, real y, const real c[], int n); + // (mux - muy) / (chix - chiy) using Krueger's series + real DConformalToRectifying(real chix, real chiy) const; + // (chix - chiy) / (mux - muy) using Krueger's series + real DRectifyingToConformal(real mux, real muy) const; + + // (mux - muy) / (psix - psiy) + // N.B., psix and psiy are in degrees + real DIsometricToRectifying(real psix, real psiy) const; + // (psix - psiy) / (mux - muy) + real DRectifyingToIsometric(real mux, real muy) const; + + real MeanSinXi(real psi1, real psi2) const; + + // The following two functions (with lots of ignored arguments) mimic the + // interface to the corresponding Geodesic function. These are needed by + // PolygonAreaT. + void GenDirect(real lat1, real lon1, real azi12, + bool, real s12, unsigned outmask, + real& lat2, real& lon2, real&, real&, real&, real&, real&, + real& S12) const { + GenDirect(lat1, lon1, azi12, s12, outmask, lat2, lon2, S12); + } + void GenInverse(real lat1, real lon1, real lat2, real lon2, + unsigned outmask, real& s12, real& azi12, + real&, real& , real& , real& , real& S12) const { + GenInverse(lat1, lon1, lat2, lon2, outmask, s12, azi12, S12); + } + public: + + /** + * Bit masks for what calculations to do. They specify which results to + * return in the general routines Rhumb::GenDirect and Rhumb::GenInverse + * routines. RhumbLine::mask is a duplication of this enum. + **********************************************************************/ + enum mask { + /** + * No output. + * @hideinitializer + **********************************************************************/ + NONE = 0U, + /** + * Calculate latitude \e lat2. + * @hideinitializer + **********************************************************************/ + LATITUDE = 1U<<7, + /** + * Calculate longitude \e lon2. + * @hideinitializer + **********************************************************************/ + LONGITUDE = 1U<<8, + /** + * Calculate azimuth \e azi12. + * @hideinitializer + **********************************************************************/ + AZIMUTH = 1U<<9, + /** + * Calculate distance \e s12. + * @hideinitializer + **********************************************************************/ + DISTANCE = 1U<<10, + /** + * Calculate area \e S12. + * @hideinitializer + **********************************************************************/ + AREA = 1U<<14, + /** + * Unroll \e lon2 in the direct calculation. + * @hideinitializer + **********************************************************************/ + LONG_UNROLL = 1U<<15, + /** + * Calculate everything. (LONG_UNROLL is not included in this mask.) + * @hideinitializer + **********************************************************************/ + ALL = 0x7F80U, + }; + + /** + * Constructor for a ellipsoid with + * + * @param[in] a equatorial radius (meters). + * @param[in] f flattening of ellipsoid. Setting \e f = 0 gives a sphere. + * Negative \e f gives a prolate ellipsoid. + * @param[in] exact if true (the default) use an addition theorem for + * elliptic integrals to compute divided differences; otherwise use + * series expansion (accurate for |f| < 0.01). + * @exception GeographicErr if \e a or (1 − \e f) \e a is not + * positive. + * + * See \ref rhumb, for a detailed description of the \e exact parameter. + **********************************************************************/ + Rhumb(real a, real f, bool exact = true); + + /** + * Solve the direct rhumb problem returning also the area. + * + * @param[in] lat1 latitude of point 1 (degrees). + * @param[in] lon1 longitude of point 1 (degrees). + * @param[in] azi12 azimuth of the rhumb line (degrees). + * @param[in] s12 distance between point 1 and point 2 (meters); it can be + * negative. + * @param[out] lat2 latitude of point 2 (degrees). + * @param[out] lon2 longitude of point 2 (degrees). + * @param[out] S12 area under the rhumb line (meters2). + * + * \e lat1 should be in the range [−90°, 90°]. The value of + * \e lon2 returned is in the range [−180°, 180°]. + * + * If point 1 is a pole, the cosine of its latitude is taken to be + * 1/ε2 (where ε is 2-52). This + * position, which is extremely close to the actual pole, allows the + * calculation to be carried out in finite terms. If \e s12 is large + * enough that the rhumb line crosses a pole, the longitude of point 2 + * is indeterminate (a NaN is returned for \e lon2 and \e S12). + **********************************************************************/ + void Direct(real lat1, real lon1, real azi12, real s12, + real& lat2, real& lon2, real& S12) const { + GenDirect(lat1, lon1, azi12, s12, + LATITUDE | LONGITUDE | AREA, lat2, lon2, S12); + } + + /** + * Solve the direct rhumb problem without the area. + **********************************************************************/ + void Direct(real lat1, real lon1, real azi12, real s12, + real& lat2, real& lon2) const { + real t; + GenDirect(lat1, lon1, azi12, s12, LATITUDE | LONGITUDE, lat2, lon2, t); + } + + /** + * The general direct rhumb problem. Rhumb::Direct is defined in terms + * of this function. + * + * @param[in] lat1 latitude of point 1 (degrees). + * @param[in] lon1 longitude of point 1 (degrees). + * @param[in] azi12 azimuth of the rhumb line (degrees). + * @param[in] s12 distance between point 1 and point 2 (meters); it can be + * negative. + * @param[in] outmask a bitor'ed combination of Rhumb::mask values + * specifying which of the following parameters should be set. + * @param[out] lat2 latitude of point 2 (degrees). + * @param[out] lon2 longitude of point 2 (degrees). + * @param[out] S12 area under the rhumb line (meters2). + * + * The Rhumb::mask values possible for \e outmask are + * - \e outmask |= Rhumb::LATITUDE for the latitude \e lat2; + * - \e outmask |= Rhumb::LONGITUDE for the latitude \e lon2; + * - \e outmask |= Rhumb::AREA for the area \e S12; + * - \e outmask |= Rhumb::ALL for all of the above; + * - \e outmask |= Rhumb::LONG_UNROLL to unroll \e lon2 instead of wrapping + * it into the range [−180°, 180°]. + * . + * With the Rhumb::LONG_UNROLL bit set, the quantity \e lon2 − + * \e lon1 indicates how many times and in what sense the rhumb line + * encircles the ellipsoid. + **********************************************************************/ + void GenDirect(real lat1, real lon1, real azi12, real s12, + unsigned outmask, real& lat2, real& lon2, real& S12) const; + + /** + * Solve the inverse rhumb problem returning also the area. + * + * @param[in] lat1 latitude of point 1 (degrees). + * @param[in] lon1 longitude of point 1 (degrees). + * @param[in] lat2 latitude of point 2 (degrees). + * @param[in] lon2 longitude of point 2 (degrees). + * @param[out] s12 rhumb distance between point 1 and point 2 (meters). + * @param[out] azi12 azimuth of the rhumb line (degrees). + * @param[out] S12 area under the rhumb line (meters2). + * + * The shortest rhumb line is found. If the end points are on opposite + * meridians, there are two shortest rhumb lines and the east-going one is + * chosen. \e lat1 and \e lat2 should be in the range [−90°, + * 90°]. The value of \e azi12 returned is in the range + * [−180°, 180°]. + * + * If either point is a pole, the cosine of its latitude is taken to be + * 1/ε2 (where ε is 2-52). This + * position, which is extremely close to the actual pole, allows the + * calculation to be carried out in finite terms. + **********************************************************************/ + void Inverse(real lat1, real lon1, real lat2, real lon2, + real& s12, real& azi12, real& S12) const { + GenInverse(lat1, lon1, lat2, lon2, + DISTANCE | AZIMUTH | AREA, s12, azi12, S12); + } + + /** + * Solve the inverse rhumb problem without the area. + **********************************************************************/ + void Inverse(real lat1, real lon1, real lat2, real lon2, + real& s12, real& azi12) const { + real t; + GenInverse(lat1, lon1, lat2, lon2, DISTANCE | AZIMUTH, s12, azi12, t); + } + + /** + * The general inverse rhumb problem. Rhumb::Inverse is defined in terms + * of this function. + * + * @param[in] lat1 latitude of point 1 (degrees). + * @param[in] lon1 longitude of point 1 (degrees). + * @param[in] lat2 latitude of point 2 (degrees). + * @param[in] lon2 longitude of point 2 (degrees). + * @param[in] outmask a bitor'ed combination of Rhumb::mask values + * specifying which of the following parameters should be set. + * @param[out] s12 rhumb distance between point 1 and point 2 (meters). + * @param[out] azi12 azimuth of the rhumb line (degrees). + * @param[out] S12 area under the rhumb line (meters2). + * + * The Rhumb::mask values possible for \e outmask are + * - \e outmask |= Rhumb::DISTANCE for the latitude \e s12; + * - \e outmask |= Rhumb::AZIMUTH for the latitude \e azi12; + * - \e outmask |= Rhumb::AREA for the area \e S12; + * - \e outmask |= Rhumb::ALL for all of the above; + **********************************************************************/ + void GenInverse(real lat1, real lon1, real lat2, real lon2, + unsigned outmask, + real& s12, real& azi12, real& S12) const; + + /** + * Set up to compute several points on a single rhumb line. + * + * @param[in] lat1 latitude of point 1 (degrees). + * @param[in] lon1 longitude of point 1 (degrees). + * @param[in] azi12 azimuth of the rhumb line (degrees). + * @return a RhumbLine object. + * + * \e lat1 should be in the range [−90°, 90°]. + * + * If point 1 is a pole, the cosine of its latitude is taken to be + * 1/ε2 (where ε is 2-52). This + * position, which is extremely close to the actual pole, allows the + * calculation to be carried out in finite terms. + **********************************************************************/ + RhumbLine Line(real lat1, real lon1, real azi12) const; + + /** \name Inspector functions. + **********************************************************************/ + ///@{ + + /** + * @return \e a the equatorial radius of the ellipsoid (meters). This is + * the value used in the constructor. + **********************************************************************/ + Math::real EquatorialRadius() const { return _ell.EquatorialRadius(); } + + /** + * @return \e f the flattening of the ellipsoid. This is the + * value used in the constructor. + **********************************************************************/ + Math::real Flattening() const { return _ell.Flattening(); } + + /** + * @return total area of ellipsoid in meters2. The area of a + * polygon encircling a pole can be found by adding + * Geodesic::EllipsoidArea()/2 to the sum of \e S12 for each side of the + * polygon. + **********************************************************************/ + Math::real EllipsoidArea() const { return _ell.Area(); } + + /** + * \deprecated An old name for EquatorialRadius(). + **********************************************************************/ + GEOGRAPHICLIB_DEPRECATED("Use EquatorialRadius()") + Math::real MajorRadius() const { return EquatorialRadius(); } + ///@} + + /** + * A global instantiation of Rhumb with the parameters for the WGS84 + * ellipsoid. + **********************************************************************/ + static const Rhumb& WGS84(); + }; + + /** + * \brief Find a sequence of points on a single rhumb line. + * + * RhumbLine facilitates the determination of a series of points on a single + * rhumb line. The starting point (\e lat1, \e lon1) and the azimuth \e + * azi12 are specified in the call to Rhumb::Line which returns a RhumbLine + * object. RhumbLine.Position returns the location of point 2 (and, + * optionally, the corresponding area, \e S12) a distance \e s12 along the + * rhumb line. + * + * There is no public constructor for this class. (Use Rhumb::Line to create + * an instance.) The Rhumb object used to create a RhumbLine must stay in + * scope as long as the RhumbLine. + * + * Example of use: + * \include example-RhumbLine.cpp + **********************************************************************/ + + class GEOGRAPHICLIB_EXPORT RhumbLine { + private: + typedef Math::real real; + friend class Rhumb; + const Rhumb& _rh; + bool _exact; + real _lat1, _lon1, _azi12, _salp, _calp, _mu1, _psi1, _r1; + RhumbLine& operator=(const RhumbLine&); // copy assignment not allowed + RhumbLine(const Rhumb& rh, real lat1, real lon1, real azi12, + bool exact); + public: + + /** + * This is a duplication of Rhumb::mask. + **********************************************************************/ + enum mask { + /** + * No output. + * @hideinitializer + **********************************************************************/ + NONE = Rhumb::NONE, + /** + * Calculate latitude \e lat2. + * @hideinitializer + **********************************************************************/ + LATITUDE = Rhumb::LATITUDE, + /** + * Calculate longitude \e lon2. + * @hideinitializer + **********************************************************************/ + LONGITUDE = Rhumb::LONGITUDE, + /** + * Calculate azimuth \e azi12. + * @hideinitializer + **********************************************************************/ + AZIMUTH = Rhumb::AZIMUTH, + /** + * Calculate distance \e s12. + * @hideinitializer + **********************************************************************/ + DISTANCE = Rhumb::DISTANCE, + /** + * Calculate area \e S12. + * @hideinitializer + **********************************************************************/ + AREA = Rhumb::AREA, + /** + * Unroll \e lon2 in the direct calculation. + * @hideinitializer + **********************************************************************/ + LONG_UNROLL = Rhumb::LONG_UNROLL, + /** + * Calculate everything. (LONG_UNROLL is not included in this mask.) + * @hideinitializer + **********************************************************************/ + ALL = Rhumb::ALL, + }; + + /** + * Compute the position of point 2 which is a distance \e s12 (meters) from + * point 1. The area is also computed. + * + * @param[in] s12 distance between point 1 and point 2 (meters); it can be + * negative. + * @param[out] lat2 latitude of point 2 (degrees). + * @param[out] lon2 longitude of point 2 (degrees). + * @param[out] S12 area under the rhumb line (meters2). + * + * The value of \e lon2 returned is in the range [−180°, + * 180°]. + * + * If \e s12 is large enough that the rhumb line crosses a pole, the + * longitude of point 2 is indeterminate (a NaN is returned for \e lon2 and + * \e S12). + **********************************************************************/ + void Position(real s12, real& lat2, real& lon2, real& S12) const { + GenPosition(s12, LATITUDE | LONGITUDE | AREA, lat2, lon2, S12); + } + + /** + * Compute the position of point 2 which is a distance \e s12 (meters) from + * point 1. The area is not computed. + **********************************************************************/ + void Position(real s12, real& lat2, real& lon2) const { + real t; + GenPosition(s12, LATITUDE | LONGITUDE, lat2, lon2, t); + } + + /** + * The general position routine. RhumbLine::Position is defined in term so + * this function. + * + * @param[in] s12 distance between point 1 and point 2 (meters); it can be + * negative. + * @param[in] outmask a bitor'ed combination of RhumbLine::mask values + * specifying which of the following parameters should be set. + * @param[out] lat2 latitude of point 2 (degrees). + * @param[out] lon2 longitude of point 2 (degrees). + * @param[out] S12 area under the rhumb line (meters2). + * + * The RhumbLine::mask values possible for \e outmask are + * - \e outmask |= RhumbLine::LATITUDE for the latitude \e lat2; + * - \e outmask |= RhumbLine::LONGITUDE for the latitude \e lon2; + * - \e outmask |= RhumbLine::AREA for the area \e S12; + * - \e outmask |= RhumbLine::ALL for all of the above; + * - \e outmask |= RhumbLine::LONG_UNROLL to unroll \e lon2 instead of + * wrapping it into the range [−180°, 180°]. + * . + * With the RhumbLine::LONG_UNROLL bit set, the quantity \e lon2 − \e + * lon1 indicates how many times and in what sense the rhumb line encircles + * the ellipsoid. + * + * If \e s12 is large enough that the rhumb line crosses a pole, the + * longitude of point 2 is indeterminate (a NaN is returned for \e lon2 and + * \e S12). + **********************************************************************/ + void GenPosition(real s12, unsigned outmask, + real& lat2, real& lon2, real& S12) const; + + /** \name Inspector functions + **********************************************************************/ + ///@{ + + /** + * @return \e lat1 the latitude of point 1 (degrees). + **********************************************************************/ + Math::real Latitude() const { return _lat1; } + + /** + * @return \e lon1 the longitude of point 1 (degrees). + **********************************************************************/ + Math::real Longitude() const { return _lon1; } + + /** + * @return \e azi12 the azimuth of the rhumb line (degrees). + **********************************************************************/ + Math::real Azimuth() const { return _azi12; } + + /** + * @return \e a the equatorial radius of the ellipsoid (meters). This is + * the value inherited from the Rhumb object used in the constructor. + **********************************************************************/ + Math::real EquatorialRadius() const { return _rh.EquatorialRadius(); } + + /** + * @return \e f the flattening of the ellipsoid. This is the value + * inherited from the Rhumb object used in the constructor. + **********************************************************************/ + Math::real Flattening() const { return _rh.Flattening(); } + }; + +} // namespace GeographicLib + +#endif // GEOGRAPHICLIB_RHUMB_HPP diff --git a/common/local_libs/GeographicLib/include/GeographicLib/SphericalEngine.hpp b/common/local_libs/GeographicLib/include/GeographicLib/SphericalEngine.hpp new file mode 100644 index 0000000000..8b8ad38681 --- /dev/null +++ b/common/local_libs/GeographicLib/include/GeographicLib/SphericalEngine.hpp @@ -0,0 +1,384 @@ +/** + * \file SphericalEngine.hpp + * \brief Header for GeographicLib::SphericalEngine class + * + * Copyright (c) Charles Karney (2011-2019) and licensed + * under the MIT/X11 License. For more information, see + * https://geographiclib.sourceforge.io/ + **********************************************************************/ + +#if !defined(GEOGRAPHICLIB_SPHERICALENGINE_HPP) +#define GEOGRAPHICLIB_SPHERICALENGINE_HPP 1 + +#include +#include +#include + +#if defined(_MSC_VER) +// Squelch warnings about dll vs vector +# pragma warning (push) +# pragma warning (disable: 4251) +#endif + +namespace GeographicLib { + + class CircularEngine; + + /** + * \brief The evaluation engine for SphericalHarmonic + * + * This serves as the backend to SphericalHarmonic, SphericalHarmonic1, and + * SphericalHarmonic2. Typically end-users will not have to access this + * class directly. + * + * See SphericalEngine.cpp for more information on the implementation. + * + * Example of use: + * \include example-SphericalEngine.cpp + **********************************************************************/ + + class GEOGRAPHICLIB_EXPORT SphericalEngine { + private: + typedef Math::real real; + // CircularEngine needs access to sqrttable, scale + friend class CircularEngine; + // Return the table of the square roots of integers + static std::vector& sqrttable(); + // An internal scaling of the coefficients to avoid overflow in + // intermediate calculations. + static real scale() { + using std::pow; + static const real + // Need extra real because, since C++11, pow(float, int) returns double + s = real(pow(real(std::numeric_limits::radix), + -3 * (std::numeric_limits::max_exponent < (1<<14) ? + std::numeric_limits::max_exponent : (1<<14)) + / 5)); + return s; + } + // Move latitudes near the pole off the axis by this amount. + static real eps() { + using std::sqrt; + return std::numeric_limits::epsilon() * + sqrt(std::numeric_limits::epsilon()); + } + SphericalEngine(); // Disable constructor + public: + /** + * Supported normalizations for associated Legendre polynomials. + **********************************************************************/ + enum normalization { + /** + * Fully normalized associated Legendre polynomials. See + * SphericalHarmonic::FULL for documentation. + * + * @hideinitializer + **********************************************************************/ + FULL = 0, + /** + * Schmidt semi-normalized associated Legendre polynomials. See + * SphericalHarmonic::SCHMIDT for documentation. + * + * @hideinitializer + **********************************************************************/ + SCHMIDT = 1, + }; + + /** + * \brief Package up coefficients for SphericalEngine + * + * This packages up the \e C, \e S coefficients and information about how + * the coefficients are stored into a single structure. This allows a + * vector of type SphericalEngine::coeff to be passed to + * SphericalEngine::Value. This class also includes functions to aid + * indexing into \e C and \e S. + * + * The storage layout of the coefficients is documented in + * SphericalHarmonic and SphericalHarmonic::SphericalHarmonic. + **********************************************************************/ + class GEOGRAPHICLIB_EXPORT coeff { + private: + int _Nx, _nmx, _mmx; + std::vector::const_iterator _Cnm; + std::vector::const_iterator _Snm; + public: + /** + * A default constructor + **********************************************************************/ + coeff() : _Nx(-1) , _nmx(-1) , _mmx(-1) {} + /** + * The general constructor. + * + * @param[in] C a vector of coefficients for the cosine terms. + * @param[in] S a vector of coefficients for the sine terms. + * @param[in] N the degree giving storage layout for \e C and \e S. + * @param[in] nmx the maximum degree to be used. + * @param[in] mmx the maximum order to be used. + * @exception GeographicErr if \e N, \e nmx, and \e mmx do not satisfy + * \e N ≥ \e nmx ≥ \e mmx ≥ −1. + * @exception GeographicErr if \e C or \e S is not big enough to hold the + * coefficients. + * @exception std::bad_alloc if the memory for the square root table + * can't be allocated. + **********************************************************************/ + coeff(const std::vector& C, + const std::vector& S, + int N, int nmx, int mmx) + : _Nx(N) + , _nmx(nmx) + , _mmx(mmx) + , _Cnm(C.begin()) + , _Snm(S.begin()) + { + if (!((_Nx >= _nmx && _nmx >= _mmx && _mmx >= 0) || + // If mmx = -1 then the sums are empty so require nmx = -1 also. + (_nmx == -1 && _mmx == -1))) + throw GeographicErr("Bad indices for coeff"); + if (!(index(_nmx, _mmx) < int(C.size()) && + index(_nmx, _mmx) < int(S.size()) + (_Nx + 1))) + throw GeographicErr("Arrays too small in coeff"); + SphericalEngine::RootTable(_nmx); + } + /** + * The constructor for full coefficient vectors. + * + * @param[in] C a vector of coefficients for the cosine terms. + * @param[in] S a vector of coefficients for the sine terms. + * @param[in] N the maximum degree and order. + * @exception GeographicErr if \e N does not satisfy \e N ≥ −1. + * @exception GeographicErr if \e C or \e S is not big enough to hold the + * coefficients. + * @exception std::bad_alloc if the memory for the square root table + * can't be allocated. + **********************************************************************/ + coeff(const std::vector& C, + const std::vector& S, + int N) + : _Nx(N) + , _nmx(N) + , _mmx(N) + , _Cnm(C.begin()) + , _Snm(S.begin()) + { + if (!(_Nx >= -1)) + throw GeographicErr("Bad indices for coeff"); + if (!(index(_nmx, _mmx) < int(C.size()) && + index(_nmx, _mmx) < int(S.size()) + (_Nx + 1))) + throw GeographicErr("Arrays too small in coeff"); + SphericalEngine::RootTable(_nmx); + } + /** + * @return \e N the degree giving storage layout for \e C and \e S. + **********************************************************************/ + int N() const { return _Nx; } + /** + * @return \e nmx the maximum degree to be used. + **********************************************************************/ + int nmx() const { return _nmx; } + /** + * @return \e mmx the maximum order to be used. + **********************************************************************/ + int mmx() const { return _mmx; } + /** + * The one-dimensional index into \e C and \e S. + * + * @param[in] n the degree. + * @param[in] m the order. + * @return the one-dimensional index. + **********************************************************************/ + int index(int n, int m) const + { return m * _Nx - m * (m - 1) / 2 + n; } + /** + * An element of \e C. + * + * @param[in] k the one-dimensional index. + * @return the value of the \e C coefficient. + **********************************************************************/ + Math::real Cv(int k) const { return *(_Cnm + k); } + /** + * An element of \e S. + * + * @param[in] k the one-dimensional index. + * @return the value of the \e S coefficient. + **********************************************************************/ + Math::real Sv(int k) const { return *(_Snm + (k - (_Nx + 1))); } + /** + * An element of \e C with checking. + * + * @param[in] k the one-dimensional index. + * @param[in] n the requested degree. + * @param[in] m the requested order. + * @param[in] f a multiplier. + * @return the value of the \e C coefficient multiplied by \e f in \e n + * and \e m are in range else 0. + **********************************************************************/ + Math::real Cv(int k, int n, int m, real f) const + { return m > _mmx || n > _nmx ? 0 : *(_Cnm + k) * f; } + /** + * An element of \e S with checking. + * + * @param[in] k the one-dimensional index. + * @param[in] n the requested degree. + * @param[in] m the requested order. + * @param[in] f a multiplier. + * @return the value of the \e S coefficient multiplied by \e f in \e n + * and \e m are in range else 0. + **********************************************************************/ + Math::real Sv(int k, int n, int m, real f) const + { return m > _mmx || n > _nmx ? 0 : *(_Snm + (k - (_Nx + 1))) * f; } + + /** + * The size of the coefficient vector for the cosine terms. + * + * @param[in] N the maximum degree. + * @param[in] M the maximum order. + * @return the size of the vector of cosine terms as stored in column + * major order. + **********************************************************************/ + static int Csize(int N, int M) + { return (M + 1) * (2 * N - M + 2) / 2; } + + /** + * The size of the coefficient vector for the sine terms. + * + * @param[in] N the maximum degree. + * @param[in] M the maximum order. + * @return the size of the vector of cosine terms as stored in column + * major order. + **********************************************************************/ + static int Ssize(int N, int M) + { return Csize(N, M) - (N + 1); } + + /** + * Load coefficients from a binary stream. + * + * @param[in] stream the input stream. + * @param[in,out] N The maximum degree of the coefficients. + * @param[in,out] M The maximum order of the coefficients. + * @param[out] C The vector of cosine coefficients. + * @param[out] S The vector of sine coefficients. + * @param[in] truncate if false (the default) then \e N and \e M are + * determined by the values in the binary stream; otherwise, the input + * values of \e N and \e M are used to truncate the coefficients read + * from the stream at the given degree and order. + * @exception GeographicErr if \e N and \e M do not satisfy \e N ≥ + * \e M ≥ −1. + * @exception GeographicErr if there's an error reading the data. + * @exception std::bad_alloc if the memory for \e C or \e S can't be + * allocated. + * + * \e N and \e M are read as 4-byte ints. \e C and \e S are resized to + * accommodate all the coefficients (with the \e m = 0 coefficients for + * \e S excluded) and the data for these coefficients read as 8-byte + * doubles. The coefficients are stored in column major order. The + * bytes in the stream should use little-endian ordering. IEEE floating + * point is assumed for the coefficients. + **********************************************************************/ + static void readcoeffs(std::istream& stream, int& N, int& M, + std::vector& C, std::vector& S, + bool truncate = false); + }; + + /** + * Evaluate a spherical harmonic sum and its gradient. + * + * @tparam gradp should the gradient be calculated. + * @tparam norm the normalization for the associated Legendre polynomials. + * @tparam L the number of terms in the coefficients. + * @param[in] c an array of coeff objects. + * @param[in] f array of coefficient multipliers. f[0] should be 1. + * @param[in] x the \e x component of the cartesian position. + * @param[in] y the \e y component of the cartesian position. + * @param[in] z the \e z component of the cartesian position. + * @param[in] a the normalizing radius. + * @param[out] gradx the \e x component of the gradient. + * @param[out] grady the \e y component of the gradient. + * @param[out] gradz the \e z component of the gradient. + * @result the spherical harmonic sum. + * + * See the SphericalHarmonic class for the definition of the sum. The + * coefficients used by this function are, for example, c[0].Cv + f[1] * + * c[1].Cv + ... + f[L−1] * c[L−1].Cv. (Note that f[0] is \e + * not used.) The upper limits on the sum are determined by c[0].nmx() and + * c[0].mmx(); these limits apply to \e all the components of the + * coefficients. The parameters \e gradp, \e norm, and \e L are template + * parameters, to allow more optimization to be done at compile time. + * + * Clenshaw summation is used which permits the evaluation of the sum + * without the need to allocate temporary arrays. Thus this function never + * throws an exception. + **********************************************************************/ + template + static Math::real Value(const coeff c[], const real f[], + real x, real y, real z, real a, + real& gradx, real& grady, real& gradz); + + /** + * Create a CircularEngine object + * + * @tparam gradp should the gradient be calculated. + * @tparam norm the normalization for the associated Legendre polynomials. + * @tparam L the number of terms in the coefficients. + * @param[in] c an array of coeff objects. + * @param[in] f array of coefficient multipliers. f[0] should be 1. + * @param[in] p the radius of the circle = sqrt(x2 + + * y2). + * @param[in] z the height of the circle. + * @param[in] a the normalizing radius. + * @exception std::bad_alloc if the memory for the CircularEngine can't be + * allocated. + * @result the CircularEngine object. + * + * If you need to evaluate the spherical harmonic sum for several points + * with constant \e f, \e p = sqrt(x2 + + * y2), \e z, and \e a, it is more efficient to construct + * call SphericalEngine::Circle to give a CircularEngine object and then + * call CircularEngine::operator()() with arguments x/\e p and + * y/\e p. + **********************************************************************/ + template + static CircularEngine Circle(const coeff c[], const real f[], + real p, real z, real a); + /** + * Check that the static table of square roots is big enough and enlarge it + * if necessary. + * + * @param[in] N the maximum degree to be used in SphericalEngine. + * @exception std::bad_alloc if the memory for the square root table can't + * be allocated. + * + * Typically, there's no need for an end-user to call this routine, because + * the constructors for SphericalEngine::coeff do so. However, since this + * updates a static table, there's a possible race condition in a + * multi-threaded environment. Because this routine does nothing if the + * table is already large enough, one way to avoid race conditions is to + * call this routine at program start up (when it's still single threaded), + * supplying the largest degree that your program will use. E.g., \code + GeographicLib::SphericalEngine::RootTable(2190); + \endcode + * suffices to accommodate extant magnetic and gravity models. + **********************************************************************/ + static void RootTable(int N); + + /** + * Clear the static table of square roots and release the memory. Call + * this only when you are sure you no longer will be using SphericalEngine. + * Your program will crash if you call SphericalEngine after calling this + * routine. + * + * \warning It's safest not to call this routine at all. (The space used + * by the table is modest.) + **********************************************************************/ + static void ClearRootTable() { + std::vector temp(0); + sqrttable().swap(temp); + } + }; + +} // namespace GeographicLib + +#if defined(_MSC_VER) +# pragma warning (pop) +#endif + +#endif // GEOGRAPHICLIB_SPHERICALENGINE_HPP diff --git a/common/local_libs/GeographicLib/include/GeographicLib/SphericalHarmonic.hpp b/common/local_libs/GeographicLib/include/GeographicLib/SphericalHarmonic.hpp new file mode 100644 index 0000000000..e645af3fc4 --- /dev/null +++ b/common/local_libs/GeographicLib/include/GeographicLib/SphericalHarmonic.hpp @@ -0,0 +1,354 @@ +/** + * \file SphericalHarmonic.hpp + * \brief Header for GeographicLib::SphericalHarmonic class + * + * Copyright (c) Charles Karney (2011-2019) and licensed + * under the MIT/X11 License. For more information, see + * https://geographiclib.sourceforge.io/ + **********************************************************************/ + +#if !defined(GEOGRAPHICLIB_SPHERICALHARMONIC_HPP) +#define GEOGRAPHICLIB_SPHERICALHARMONIC_HPP 1 + +#include +#include +#include +#include + +namespace GeographicLib { + + /** + * \brief Spherical harmonic series + * + * This class evaluates the spherical harmonic sum \verbatim + V(x, y, z) = sum(n = 0..N)[ q^(n+1) * sum(m = 0..n)[ + (C[n,m] * cos(m*lambda) + S[n,m] * sin(m*lambda)) * + P[n,m](cos(theta)) ] ] + \endverbatim + * where + * - p2 = x2 + y2, + * - r2 = p2 + z2, + * - \e q = a/r, + * - θ = atan2(\e p, \e z) = the spherical \e colatitude, + * - λ = atan2(\e y, \e x) = the longitude. + * - Pnm(\e t) is the associated Legendre polynomial of + * degree \e n and order \e m. + * + * Two normalizations are supported for Pnm + * - fully normalized denoted by SphericalHarmonic::FULL. + * - Schmidt semi-normalized denoted by SphericalHarmonic::SCHMIDT. + * + * Clenshaw summation is used for the sums over both \e n and \e m. This + * allows the computation to be carried out without the need for any + * temporary arrays. See SphericalEngine.cpp for more information on the + * implementation. + * + * References: + * - C. W. Clenshaw, + * + * A note on the summation of Chebyshev series, + * %Math. Tables Aids Comput. 9(51), 118--120 (1955). + * - R. E. Deakin, Derivatives of the earth's potentials, Geomatics + * Research Australasia 68, 31--60, (June 1998). + * - W. A. Heiskanen and H. Moritz, Physical Geodesy, (Freeman, San + * Francisco, 1967). (See Sec. 1-14, for a definition of Pbar.) + * - S. A. Holmes and W. E. Featherstone, + * + * A unified approach to the Clenshaw summation and the recursive + * computation of very high degree and order normalised associated Legendre + * functions, J. Geodesy 76(5), 279--299 (2002). + * - C. C. Tscherning and K. Poder, + * + * Some geodetic applications of Clenshaw summation, + * Boll. Geod. Sci. Aff. 41(4), 349--375 (1982). + * + * Example of use: + * \include example-SphericalHarmonic.cpp + **********************************************************************/ + + class GEOGRAPHICLIB_EXPORT SphericalHarmonic { + public: + /** + * Supported normalizations for the associated Legendre polynomials. + **********************************************************************/ + enum normalization { + /** + * Fully normalized associated Legendre polynomials. + * + * These are defined by + * Pnmfull(\e z) + * = (−1)m + * sqrt(\e k (2\e n + 1) (\e n − \e m)! / (\e n + \e m)!) + * Pnm(\e z), where + * Pnm(\e z) is Ferrers + * function (also known as the Legendre function on the cut or the + * associated Legendre polynomial) https://dlmf.nist.gov/14.7.E10 and + * \e k = 1 for \e m = 0 and \e k = 2 otherwise. + * + * The mean squared value of + * Pnmfull(cosθ) + * cos(mλ) and + * Pnmfull(cosθ) + * sin(mλ) over the sphere is 1. + * + * @hideinitializer + **********************************************************************/ + FULL = SphericalEngine::FULL, + /** + * Schmidt semi-normalized associated Legendre polynomials. + * + * These are defined by + * Pnmschmidt(\e z) + * = (−1)m + * sqrt(\e k (\e n − \e m)! / (\e n + \e m)!) + * Pnm(\e z), where + * Pnm(\e z) is Ferrers + * function (also known as the Legendre function on the cut or the + * associated Legendre polynomial) https://dlmf.nist.gov/14.7.E10 and + * \e k = 1 for \e m = 0 and \e k = 2 otherwise. + * + * The mean squared value of + * Pnmschmidt(cosθ) + * cos(mλ) and + * Pnmschmidt(cosθ) + * sin(mλ) over the sphere is 1/(2\e n + 1). + * + * @hideinitializer + **********************************************************************/ + SCHMIDT = SphericalEngine::SCHMIDT, + }; + + private: + typedef Math::real real; + SphericalEngine::coeff _c[1]; + real _a; + unsigned _norm; + + public: + /** + * Constructor with a full set of coefficients specified. + * + * @param[in] C the coefficients Cnm. + * @param[in] S the coefficients Snm. + * @param[in] N the maximum degree and order of the sum + * @param[in] a the reference radius appearing in the definition of the + * sum. + * @param[in] norm the normalization for the associated Legendre + * polynomials, either SphericalHarmonic::FULL (the default) or + * SphericalHarmonic::SCHMIDT. + * @exception GeographicErr if \e N does not satisfy \e N ≥ −1. + * @exception GeographicErr if \e C or \e S is not big enough to hold the + * coefficients. + * + * The coefficients Cnm and + * Snm are stored in the one-dimensional vectors + * \e C and \e S which must contain (\e N + 1)(\e N + 2)/2 and \e N (\e N + + * 1)/2 elements, respectively, stored in "column-major" order. Thus for + * \e N = 3, the order would be: + * C00, + * C10, + * C20, + * C30, + * C11, + * C21, + * C31, + * C22, + * C32, + * C33. + * In general the (\e n,\e m) element is at index \e m \e N − \e m + * (\e m − 1)/2 + \e n. The layout of \e S is the same except that + * the first column is omitted (since the \e m = 0 terms never contribute + * to the sum) and the 0th element is S11 + * + * The class stores pointers to the first elements of \e C and \e S. + * These arrays should not be altered or destroyed during the lifetime of a + * SphericalHarmonic object. + **********************************************************************/ + SphericalHarmonic(const std::vector& C, + const std::vector& S, + int N, real a, unsigned norm = FULL) + : _a(a) + , _norm(norm) + { _c[0] = SphericalEngine::coeff(C, S, N); } + + /** + * Constructor with a subset of coefficients specified. + * + * @param[in] C the coefficients Cnm. + * @param[in] S the coefficients Snm. + * @param[in] N the degree used to determine the layout of \e C and \e S. + * @param[in] nmx the maximum degree used in the sum. The sum over \e n is + * from 0 thru \e nmx. + * @param[in] mmx the maximum order used in the sum. The sum over \e m is + * from 0 thru min(\e n, \e mmx). + * @param[in] a the reference radius appearing in the definition of the + * sum. + * @param[in] norm the normalization for the associated Legendre + * polynomials, either SphericalHarmonic::FULL (the default) or + * SphericalHarmonic::SCHMIDT. + * @exception GeographicErr if \e N, \e nmx, and \e mmx do not satisfy + * \e N ≥ \e nmx ≥ \e mmx ≥ −1. + * @exception GeographicErr if \e C or \e S is not big enough to hold the + * coefficients. + * + * The class stores pointers to the first elements of \e C and \e S. + * These arrays should not be altered or destroyed during the lifetime of a + * SphericalHarmonic object. + **********************************************************************/ + SphericalHarmonic(const std::vector& C, + const std::vector& S, + int N, int nmx, int mmx, + real a, unsigned norm = FULL) + : _a(a) + , _norm(norm) + { _c[0] = SphericalEngine::coeff(C, S, N, nmx, mmx); } + + /** + * A default constructor so that the object can be created when the + * constructor for another object is initialized. This default object can + * then be reset with the default copy assignment operator. + **********************************************************************/ + SphericalHarmonic() {} + + /** + * Compute the spherical harmonic sum. + * + * @param[in] x cartesian coordinate. + * @param[in] y cartesian coordinate. + * @param[in] z cartesian coordinate. + * @return \e V the spherical harmonic sum. + * + * This routine requires constant memory and thus never throws an + * exception. + **********************************************************************/ + Math::real operator()(real x, real y, real z) const { + real f[] = {1}; + real v = 0; + real dummy; + switch (_norm) { + case FULL: + v = SphericalEngine::Value + (_c, f, x, y, z, _a, dummy, dummy, dummy); + break; + case SCHMIDT: + v = SphericalEngine::Value + (_c, f, x, y, z, _a, dummy, dummy, dummy); + break; + } + return v; + } + + /** + * Compute a spherical harmonic sum and its gradient. + * + * @param[in] x cartesian coordinate. + * @param[in] y cartesian coordinate. + * @param[in] z cartesian coordinate. + * @param[out] gradx \e x component of the gradient + * @param[out] grady \e y component of the gradient + * @param[out] gradz \e z component of the gradient + * @return \e V the spherical harmonic sum. + * + * This is the same as the previous function, except that the components of + * the gradients of the sum in the \e x, \e y, and \e z directions are + * computed. This routine requires constant memory and thus never throws + * an exception. + **********************************************************************/ + Math::real operator()(real x, real y, real z, + real& gradx, real& grady, real& gradz) const { + real f[] = {1}; + real v = 0; + switch (_norm) { + case FULL: + v = SphericalEngine::Value + (_c, f, x, y, z, _a, gradx, grady, gradz); + break; + case SCHMIDT: + v = SphericalEngine::Value + (_c, f, x, y, z, _a, gradx, grady, gradz); + break; + } + return v; + } + + /** + * Create a CircularEngine to allow the efficient evaluation of several + * points on a circle of latitude. + * + * @param[in] p the radius of the circle. + * @param[in] z the height of the circle above the equatorial plane. + * @param[in] gradp if true the returned object will be able to compute the + * gradient of the sum. + * @exception std::bad_alloc if the memory for the CircularEngine can't be + * allocated. + * @return the CircularEngine object. + * + * SphericalHarmonic::operator()() exchanges the order of the sums in the + * definition, i.e., ∑n = 0..N + * ∑m = 0..n becomes ∑m = + * 0..Nn = m..N. + * SphericalHarmonic::Circle performs the inner sum over degree \e n (which + * entails about N2 operations). Calling + * CircularEngine::operator()() on the returned object performs the outer + * sum over the order \e m (about \e N operations). + * + * Here's an example of computing the spherical sum at a sequence of + * longitudes without using a CircularEngine object \code + SphericalHarmonic h(...); // Create the SphericalHarmonic object + double r = 2, lat = 33, lon0 = 44, dlon = 0.01; + double + phi = lat * Math::degree(), + z = r * sin(phi), p = r * cos(phi); + for (int i = 0; i <= 100; ++i) { + real + lon = lon0 + i * dlon, + lam = lon * Math::degree(); + std::cout << lon << " " << h(p * cos(lam), p * sin(lam), z) << "\n"; + } + \endcode + * Here is the same calculation done using a CircularEngine object. This + * will be about N/2 times faster. \code + SphericalHarmonic h(...); // Create the SphericalHarmonic object + double r = 2, lat = 33, lon0 = 44, dlon = 0.01; + double + phi = lat * Math::degree(), + z = r * sin(phi), p = r * cos(phi); + CircularEngine c(h(p, z, false)); // Create the CircularEngine object + for (int i = 0; i <= 100; ++i) { + real + lon = lon0 + i * dlon; + std::cout << lon << " " << c(lon) << "\n"; + } + \endcode + **********************************************************************/ + CircularEngine Circle(real p, real z, bool gradp) const { + real f[] = {1}; + switch (_norm) { + case FULL: + return gradp ? + SphericalEngine::Circle + (_c, f, p, z, _a) : + SphericalEngine::Circle + (_c, f, p, z, _a); + break; + case SCHMIDT: + default: // To avoid compiler warnings + return gradp ? + SphericalEngine::Circle + (_c, f, p, z, _a) : + SphericalEngine::Circle + (_c, f, p, z, _a); + break; + } + } + + /** + * @return the zeroth SphericalEngine::coeff object. + **********************************************************************/ + const SphericalEngine::coeff& Coefficients() const + { return _c[0]; } + }; + +} // namespace GeographicLib + +#endif // GEOGRAPHICLIB_SPHERICALHARMONIC_HPP diff --git a/common/local_libs/GeographicLib/include/GeographicLib/SphericalHarmonic1.hpp b/common/local_libs/GeographicLib/include/GeographicLib/SphericalHarmonic1.hpp new file mode 100644 index 0000000000..543686eee3 --- /dev/null +++ b/common/local_libs/GeographicLib/include/GeographicLib/SphericalHarmonic1.hpp @@ -0,0 +1,281 @@ +/** + * \file SphericalHarmonic1.hpp + * \brief Header for GeographicLib::SphericalHarmonic1 class + * + * Copyright (c) Charles Karney (2011) and licensed under + * the MIT/X11 License. For more information, see + * https://geographiclib.sourceforge.io/ + **********************************************************************/ + +#if !defined(GEOGRAPHICLIB_SPHERICALHARMONIC1_HPP) +#define GEOGRAPHICLIB_SPHERICALHARMONIC1_HPP 1 + +#include +#include +#include +#include + +namespace GeographicLib { + + /** + * \brief Spherical harmonic series with a correction to the coefficients + * + * This classes is similar to SphericalHarmonic, except that the coefficients + * Cnm are replaced by + * Cnm + \e tau C'nm (and + * similarly for Snm). + * + * Example of use: + * \include example-SphericalHarmonic1.cpp + **********************************************************************/ + + class GEOGRAPHICLIB_EXPORT SphericalHarmonic1 { + public: + /** + * Supported normalizations for associate Legendre polynomials. + **********************************************************************/ + enum normalization { + /** + * Fully normalized associated Legendre polynomials. See + * SphericalHarmonic::FULL for documentation. + * + * @hideinitializer + **********************************************************************/ + FULL = SphericalEngine::FULL, + /** + * Schmidt semi-normalized associated Legendre polynomials. See + * SphericalHarmonic::SCHMIDT for documentation. + * + * @hideinitializer + **********************************************************************/ + SCHMIDT = SphericalEngine::SCHMIDT, + }; + + private: + typedef Math::real real; + SphericalEngine::coeff _c[2]; + real _a; + unsigned _norm; + + public: + /** + * Constructor with a full set of coefficients specified. + * + * @param[in] C the coefficients Cnm. + * @param[in] S the coefficients Snm. + * @param[in] N the maximum degree and order of the sum + * @param[in] C1 the coefficients C'nm. + * @param[in] S1 the coefficients S'nm. + * @param[in] N1 the maximum degree and order of the correction + * coefficients C'nm and + * S'nm. + * @param[in] a the reference radius appearing in the definition of the + * sum. + * @param[in] norm the normalization for the associated Legendre + * polynomials, either SphericalHarmonic1::FULL (the default) or + * SphericalHarmonic1::SCHMIDT. + * @exception GeographicErr if \e N and \e N1 do not satisfy \e N ≥ + * \e N1 ≥ −1. + * @exception GeographicErr if any of the vectors of coefficients is not + * large enough. + * + * See SphericalHarmonic for the way the coefficients should be stored. + * + * The class stores pointers to the first elements of \e C, \e S, \e + * C', and \e S'. These arrays should not be altered or destroyed during + * the lifetime of a SphericalHarmonic object. + **********************************************************************/ + SphericalHarmonic1(const std::vector& C, + const std::vector& S, + int N, + const std::vector& C1, + const std::vector& S1, + int N1, + real a, unsigned norm = FULL) + : _a(a) + , _norm(norm) { + if (!(N1 <= N)) + throw GeographicErr("N1 cannot be larger that N"); + _c[0] = SphericalEngine::coeff(C, S, N); + _c[1] = SphericalEngine::coeff(C1, S1, N1); + } + + /** + * Constructor with a subset of coefficients specified. + * + * @param[in] C the coefficients Cnm. + * @param[in] S the coefficients Snm. + * @param[in] N the degree used to determine the layout of \e C and \e S. + * @param[in] nmx the maximum degree used in the sum. The sum over \e n is + * from 0 thru \e nmx. + * @param[in] mmx the maximum order used in the sum. The sum over \e m is + * from 0 thru min(\e n, \e mmx). + * @param[in] C1 the coefficients C'nm. + * @param[in] S1 the coefficients S'nm. + * @param[in] N1 the degree used to determine the layout of \e C' and \e + * S'. + * @param[in] nmx1 the maximum degree used for \e C' and \e S'. + * @param[in] mmx1 the maximum order used for \e C' and \e S'. + * @param[in] a the reference radius appearing in the definition of the + * sum. + * @param[in] norm the normalization for the associated Legendre + * polynomials, either SphericalHarmonic1::FULL (the default) or + * SphericalHarmonic1::SCHMIDT. + * @exception GeographicErr if the parameters do not satisfy \e N ≥ \e + * nmx ≥ \e mmx ≥ −1; \e N1 ≥ \e nmx1 ≥ \e mmx1 ≥ + * −1; \e N ≥ \e N1; \e nmx ≥ \e nmx1; \e mmx ≥ \e mmx1. + * @exception GeographicErr if any of the vectors of coefficients is not + * large enough. + * + * The class stores pointers to the first elements of \e C, \e S, \e + * C', and \e S'. These arrays should not be altered or destroyed during + * the lifetime of a SphericalHarmonic object. + **********************************************************************/ + SphericalHarmonic1(const std::vector& C, + const std::vector& S, + int N, int nmx, int mmx, + const std::vector& C1, + const std::vector& S1, + int N1, int nmx1, int mmx1, + real a, unsigned norm = FULL) + : _a(a) + , _norm(norm) { + if (!(nmx1 <= nmx)) + throw GeographicErr("nmx1 cannot be larger that nmx"); + if (!(mmx1 <= mmx)) + throw GeographicErr("mmx1 cannot be larger that mmx"); + _c[0] = SphericalEngine::coeff(C, S, N, nmx, mmx); + _c[1] = SphericalEngine::coeff(C1, S1, N1, nmx1, mmx1); + } + + /** + * A default constructor so that the object can be created when the + * constructor for another object is initialized. This default object can + * then be reset with the default copy assignment operator. + **********************************************************************/ + SphericalHarmonic1() {} + + /** + * Compute a spherical harmonic sum with a correction term. + * + * @param[in] tau multiplier for correction coefficients \e C' and \e S'. + * @param[in] x cartesian coordinate. + * @param[in] y cartesian coordinate. + * @param[in] z cartesian coordinate. + * @return \e V the spherical harmonic sum. + * + * This routine requires constant memory and thus never throws + * an exception. + **********************************************************************/ + Math::real operator()(real tau, real x, real y, real z) const { + real f[] = {1, tau}; + real v = 0; + real dummy; + switch (_norm) { + case FULL: + v = SphericalEngine::Value + (_c, f, x, y, z, _a, dummy, dummy, dummy); + break; + case SCHMIDT: + v = SphericalEngine::Value + (_c, f, x, y, z, _a, dummy, dummy, dummy); + break; + } + return v; + } + + /** + * Compute a spherical harmonic sum with a correction term and its + * gradient. + * + * @param[in] tau multiplier for correction coefficients \e C' and \e S'. + * @param[in] x cartesian coordinate. + * @param[in] y cartesian coordinate. + * @param[in] z cartesian coordinate. + * @param[out] gradx \e x component of the gradient + * @param[out] grady \e y component of the gradient + * @param[out] gradz \e z component of the gradient + * @return \e V the spherical harmonic sum. + * + * This is the same as the previous function, except that the components of + * the gradients of the sum in the \e x, \e y, and \e z directions are + * computed. This routine requires constant memory and thus never throws + * an exception. + **********************************************************************/ + Math::real operator()(real tau, real x, real y, real z, + real& gradx, real& grady, real& gradz) const { + real f[] = {1, tau}; + real v = 0; + switch (_norm) { + case FULL: + v = SphericalEngine::Value + (_c, f, x, y, z, _a, gradx, grady, gradz); + break; + case SCHMIDT: + v = SphericalEngine::Value + (_c, f, x, y, z, _a, gradx, grady, gradz); + break; + } + return v; + } + + /** + * Create a CircularEngine to allow the efficient evaluation of several + * points on a circle of latitude at a fixed value of \e tau. + * + * @param[in] tau the multiplier for the correction coefficients. + * @param[in] p the radius of the circle. + * @param[in] z the height of the circle above the equatorial plane. + * @param[in] gradp if true the returned object will be able to compute the + * gradient of the sum. + * @exception std::bad_alloc if the memory for the CircularEngine can't be + * allocated. + * @return the CircularEngine object. + * + * SphericalHarmonic1::operator()() exchanges the order of the sums in the + * definition, i.e., ∑n = 0..N + * ∑m = 0..n becomes ∑m = + * 0..Nn = m..N. + * SphericalHarmonic1::Circle performs the inner sum over degree \e n + * (which entails about N2 operations). Calling + * CircularEngine::operator()() on the returned object performs the outer + * sum over the order \e m (about \e N operations). + * + * See SphericalHarmonic::Circle for an example of its use. + **********************************************************************/ + CircularEngine Circle(real tau, real p, real z, bool gradp) const { + real f[] = {1, tau}; + switch (_norm) { + case FULL: + return gradp ? + SphericalEngine::Circle + (_c, f, p, z, _a) : + SphericalEngine::Circle + (_c, f, p, z, _a); + break; + case SCHMIDT: + default: // To avoid compiler warnings + return gradp ? + SphericalEngine::Circle + (_c, f, p, z, _a) : + SphericalEngine::Circle + (_c, f, p, z, _a); + break; + } + } + + /** + * @return the zeroth SphericalEngine::coeff object. + **********************************************************************/ + const SphericalEngine::coeff& Coefficients() const + { return _c[0]; } + /** + * @return the first SphericalEngine::coeff object. + **********************************************************************/ + const SphericalEngine::coeff& Coefficients1() const + { return _c[1]; } + }; + +} // namespace GeographicLib + +#endif // GEOGRAPHICLIB_SPHERICALHARMONIC1_HPP diff --git a/common/local_libs/GeographicLib/include/GeographicLib/SphericalHarmonic2.hpp b/common/local_libs/GeographicLib/include/GeographicLib/SphericalHarmonic2.hpp new file mode 100644 index 0000000000..9477dc63d2 --- /dev/null +++ b/common/local_libs/GeographicLib/include/GeographicLib/SphericalHarmonic2.hpp @@ -0,0 +1,318 @@ +/** + * \file SphericalHarmonic2.hpp + * \brief Header for GeographicLib::SphericalHarmonic2 class + * + * Copyright (c) Charles Karney (2011-2012) and licensed + * under the MIT/X11 License. For more information, see + * https://geographiclib.sourceforge.io/ + **********************************************************************/ + +#if !defined(GEOGRAPHICLIB_SPHERICALHARMONIC2_HPP) +#define GEOGRAPHICLIB_SPHERICALHARMONIC2_HPP 1 + +#include +#include +#include +#include + +namespace GeographicLib { + + /** + * \brief Spherical harmonic series with two corrections to the coefficients + * + * This classes is similar to SphericalHarmonic, except that the coefficients + * Cnm are replaced by + * Cnm + \e tau' C'nm + \e + * tau'' C''nm (and similarly for + * Snm). + * + * Example of use: + * \include example-SphericalHarmonic2.cpp + **********************************************************************/ + + // Don't include the GEOGRPAHIC_EXPORT because this header-only class isn't + // used by any other classes in the library. + class /*GEOGRAPHICLIB_EXPORT*/ SphericalHarmonic2 { + public: + /** + * Supported normalizations for associate Legendre polynomials. + **********************************************************************/ + enum normalization { + /** + * Fully normalized associated Legendre polynomials. See + * SphericalHarmonic::FULL for documentation. + * + * @hideinitializer + **********************************************************************/ + FULL = SphericalEngine::FULL, + /** + * Schmidt semi-normalized associated Legendre polynomials. See + * SphericalHarmonic::SCHMIDT for documentation. + * + * @hideinitializer + **********************************************************************/ + SCHMIDT = SphericalEngine::SCHMIDT, + }; + + private: + typedef Math::real real; + SphericalEngine::coeff _c[3]; + real _a; + unsigned _norm; + + public: + /** + * Constructor with a full set of coefficients specified. + * + * @param[in] C the coefficients Cnm. + * @param[in] S the coefficients Snm. + * @param[in] N the maximum degree and order of the sum + * @param[in] C1 the coefficients C'nm. + * @param[in] S1 the coefficients S'nm. + * @param[in] N1 the maximum degree and order of the first correction + * coefficients C'nm and + * S'nm. + * @param[in] C2 the coefficients C''nm. + * @param[in] S2 the coefficients S''nm. + * @param[in] N2 the maximum degree and order of the second correction + * coefficients C'nm and + * S'nm. + * @param[in] a the reference radius appearing in the definition of the + * sum. + * @param[in] norm the normalization for the associated Legendre + * polynomials, either SphericalHarmonic2::FULL (the default) or + * SphericalHarmonic2::SCHMIDT. + * @exception GeographicErr if \e N and \e N1 do not satisfy \e N ≥ + * \e N1 ≥ −1, and similarly for \e N2. + * @exception GeographicErr if any of the vectors of coefficients is not + * large enough. + * + * See SphericalHarmonic for the way the coefficients should be stored. \e + * N1 and \e N2 should satisfy \e N1 ≤ \e N and \e N2 ≤ \e N. + * + * The class stores pointers to the first elements of \e C, \e S, \e + * C', \e S', \e C'', and \e S''. These arrays should not be altered or + * destroyed during the lifetime of a SphericalHarmonic object. + **********************************************************************/ + SphericalHarmonic2(const std::vector& C, + const std::vector& S, + int N, + const std::vector& C1, + const std::vector& S1, + int N1, + const std::vector& C2, + const std::vector& S2, + int N2, + real a, unsigned norm = FULL) + : _a(a) + , _norm(norm) { + if (!(N1 <= N && N2 <= N)) + throw GeographicErr("N1 and N2 cannot be larger that N"); + _c[0] = SphericalEngine::coeff(C, S, N); + _c[1] = SphericalEngine::coeff(C1, S1, N1); + _c[2] = SphericalEngine::coeff(C2, S2, N2); + } + + /** + * Constructor with a subset of coefficients specified. + * + * @param[in] C the coefficients Cnm. + * @param[in] S the coefficients Snm. + * @param[in] N the degree used to determine the layout of \e C and \e S. + * @param[in] nmx the maximum degree used in the sum. The sum over \e n is + * from 0 thru \e nmx. + * @param[in] mmx the maximum order used in the sum. The sum over \e m is + * from 0 thru min(\e n, \e mmx). + * @param[in] C1 the coefficients C'nm. + * @param[in] S1 the coefficients S'nm. + * @param[in] N1 the degree used to determine the layout of \e C' and \e + * S'. + * @param[in] nmx1 the maximum degree used for \e C' and \e S'. + * @param[in] mmx1 the maximum order used for \e C' and \e S'. + * @param[in] C2 the coefficients C''nm. + * @param[in] S2 the coefficients S''nm. + * @param[in] N2 the degree used to determine the layout of \e C'' and \e + * S''. + * @param[in] nmx2 the maximum degree used for \e C'' and \e S''. + * @param[in] mmx2 the maximum order used for \e C'' and \e S''. + * @param[in] a the reference radius appearing in the definition of the + * sum. + * @param[in] norm the normalization for the associated Legendre + * polynomials, either SphericalHarmonic2::FULL (the default) or + * SphericalHarmonic2::SCHMIDT. + * @exception GeographicErr if the parameters do not satisfy \e N ≥ \e + * nmx ≥ \e mmx ≥ −1; \e N1 ≥ \e nmx1 ≥ \e mmx1 ≥ + * −1; \e N ≥ \e N1; \e nmx ≥ \e nmx1; \e mmx ≥ \e mmx1; + * and similarly for \e N2, \e nmx2, and \e mmx2. + * @exception GeographicErr if any of the vectors of coefficients is not + * large enough. + * + * The class stores pointers to the first elements of \e C, \e S, \e + * C', \e S', \e C'', and \e S''. These arrays should not be altered or + * destroyed during the lifetime of a SphericalHarmonic object. + **********************************************************************/ + SphericalHarmonic2(const std::vector& C, + const std::vector& S, + int N, int nmx, int mmx, + const std::vector& C1, + const std::vector& S1, + int N1, int nmx1, int mmx1, + const std::vector& C2, + const std::vector& S2, + int N2, int nmx2, int mmx2, + real a, unsigned norm = FULL) + : _a(a) + , _norm(norm) { + if (!(nmx1 <= nmx && nmx2 <= nmx)) + throw GeographicErr("nmx1 and nmx2 cannot be larger that nmx"); + if (!(mmx1 <= mmx && mmx2 <= mmx)) + throw GeographicErr("mmx1 and mmx2 cannot be larger that mmx"); + _c[0] = SphericalEngine::coeff(C, S, N, nmx, mmx); + _c[1] = SphericalEngine::coeff(C1, S1, N1, nmx1, mmx1); + _c[2] = SphericalEngine::coeff(C2, S2, N2, nmx2, mmx2); + } + + /** + * A default constructor so that the object can be created when the + * constructor for another object is initialized. This default object can + * then be reset with the default copy assignment operator. + **********************************************************************/ + SphericalHarmonic2() {} + + /** + * Compute a spherical harmonic sum with two correction terms. + * + * @param[in] tau1 multiplier for correction coefficients \e C' and \e S'. + * @param[in] tau2 multiplier for correction coefficients \e C'' and \e + * S''. + * @param[in] x cartesian coordinate. + * @param[in] y cartesian coordinate. + * @param[in] z cartesian coordinate. + * @return \e V the spherical harmonic sum. + * + * This routine requires constant memory and thus never throws an + * exception. + **********************************************************************/ + Math::real operator()(real tau1, real tau2, real x, real y, real z) + const { + real f[] = {1, tau1, tau2}; + real v = 0; + real dummy; + switch (_norm) { + case FULL: + v = SphericalEngine::Value + (_c, f, x, y, z, _a, dummy, dummy, dummy); + break; + case SCHMIDT: + v = SphericalEngine::Value + (_c, f, x, y, z, _a, dummy, dummy, dummy); + break; + } + return v; + } + + /** + * Compute a spherical harmonic sum with two correction terms and its + * gradient. + * + * @param[in] tau1 multiplier for correction coefficients \e C' and \e S'. + * @param[in] tau2 multiplier for correction coefficients \e C'' and \e + * S''. + * @param[in] x cartesian coordinate. + * @param[in] y cartesian coordinate. + * @param[in] z cartesian coordinate. + * @param[out] gradx \e x component of the gradient + * @param[out] grady \e y component of the gradient + * @param[out] gradz \e z component of the gradient + * @return \e V the spherical harmonic sum. + * + * This is the same as the previous function, except that the components of + * the gradients of the sum in the \e x, \e y, and \e z directions are + * computed. This routine requires constant memory and thus never throws + * an exception. + **********************************************************************/ + Math::real operator()(real tau1, real tau2, real x, real y, real z, + real& gradx, real& grady, real& gradz) const { + real f[] = {1, tau1, tau2}; + real v = 0; + switch (_norm) { + case FULL: + v = SphericalEngine::Value + (_c, f, x, y, z, _a, gradx, grady, gradz); + break; + case SCHMIDT: + v = SphericalEngine::Value + (_c, f, x, y, z, _a, gradx, grady, gradz); + break; + } + return v; + } + + /** + * Create a CircularEngine to allow the efficient evaluation of several + * points on a circle of latitude at fixed values of \e tau1 and \e tau2. + * + * @param[in] tau1 multiplier for correction coefficients \e C' and \e S'. + * @param[in] tau2 multiplier for correction coefficients \e C'' and \e + * S''. + * @param[in] p the radius of the circle. + * @param[in] z the height of the circle above the equatorial plane. + * @param[in] gradp if true the returned object will be able to compute the + * gradient of the sum. + * @exception std::bad_alloc if the memory for the CircularEngine can't be + * allocated. + * @return the CircularEngine object. + * + * SphericalHarmonic2::operator()() exchanges the order of the sums in the + * definition, i.e., ∑n = 0..N + * ∑m = 0..n becomes ∑m = + * 0..Nn = m..N.. + * SphericalHarmonic2::Circle performs the inner sum over degree \e n + * (which entails about N2 operations). Calling + * CircularEngine::operator()() on the returned object performs the outer + * sum over the order \e m (about \e N operations). + * + * See SphericalHarmonic::Circle for an example of its use. + **********************************************************************/ + CircularEngine Circle(real tau1, real tau2, real p, real z, bool gradp) + const { + real f[] = {1, tau1, tau2}; + switch (_norm) { + case FULL: + return gradp ? + SphericalEngine::Circle + (_c, f, p, z, _a) : + SphericalEngine::Circle + (_c, f, p, z, _a); + break; + case SCHMIDT: + default: // To avoid compiler warnings + return gradp ? + SphericalEngine::Circle + (_c, f, p, z, _a) : + SphericalEngine::Circle + (_c, f, p, z, _a); + break; + } + } + + /** + * @return the zeroth SphericalEngine::coeff object. + **********************************************************************/ + const SphericalEngine::coeff& Coefficients() const + { return _c[0]; } + /** + * @return the first SphericalEngine::coeff object. + **********************************************************************/ + const SphericalEngine::coeff& Coefficients1() const + { return _c[1]; } + /** + * @return the second SphericalEngine::coeff object. + **********************************************************************/ + const SphericalEngine::coeff& Coefficients2() const + { return _c[2]; } + }; + +} // namespace GeographicLib + +#endif // GEOGRAPHICLIB_SPHERICALHARMONIC2_HPP diff --git a/common/local_libs/GeographicLib/include/GeographicLib/TransverseMercator.hpp b/common/local_libs/GeographicLib/include/GeographicLib/TransverseMercator.hpp new file mode 100644 index 0000000000..2eed3dc402 --- /dev/null +++ b/common/local_libs/GeographicLib/include/GeographicLib/TransverseMercator.hpp @@ -0,0 +1,206 @@ +/** + * \file TransverseMercator.hpp + * \brief Header for GeographicLib::TransverseMercator class + * + * Copyright (c) Charles Karney (2008-2020) and licensed + * under the MIT/X11 License. For more information, see + * https://geographiclib.sourceforge.io/ + **********************************************************************/ + +#if !defined(GEOGRAPHICLIB_TRANSVERSEMERCATOR_HPP) +#define GEOGRAPHICLIB_TRANSVERSEMERCATOR_HPP 1 + +#include + +#if !defined(GEOGRAPHICLIB_TRANSVERSEMERCATOR_ORDER) +/** + * The order of the series approximation used in TransverseMercator. + * GEOGRAPHICLIB_TRANSVERSEMERCATOR_ORDER can be set to any integer in [4, 8]. + **********************************************************************/ +# define GEOGRAPHICLIB_TRANSVERSEMERCATOR_ORDER \ + (GEOGRAPHICLIB_PRECISION == 2 ? 6 : \ + (GEOGRAPHICLIB_PRECISION == 1 ? 4 : 8)) +#endif + +namespace GeographicLib { + + /** + * \brief Transverse Mercator projection + * + * This uses Krüger's method which evaluates the projection and its + * inverse in terms of a series. See + * - L. Krüger, + * Konforme + * Abbildung des Erdellipsoids in der Ebene (Conformal mapping of the + * ellipsoidal earth to the plane), Royal Prussian Geodetic Institute, New + * Series 52, 172 pp. (1912). + * - C. F. F. Karney, + * + * Transverse Mercator with an accuracy of a few nanometers, + * J. Geodesy 85(8), 475--485 (Aug. 2011); + * preprint + * arXiv:1002.1417. + * + * Krüger's method has been extended from 4th to 6th order. The maximum + * error is 5 nm (5 nanometers), ground distance, for all positions within 35 + * degrees of the central meridian. The error in the convergence is 2 + * × 10−15" and the relative error in the scale + * is 6 × 10−12%%. See Sec. 4 of + * arXiv:1002.1417 for details. + * The speed penalty in going to 6th order is only about 1%. + * + * There's a singularity in the projection at φ = 0°, λ + * − λ0 = ±(1 − \e e)90° (≈ + * ±82.6° for the WGS84 ellipsoid), where \e e is the + * eccentricity. Beyond this point, the series ceases to converge and the + * results from this method will be garbage. To be on the safe side, don't + * use this method if the angular distance from the central meridian exceeds + * (1 − 2e)90° (≈ 75° for the WGS84 ellipsoid) + * + * TransverseMercatorExact is an alternative implementation of the projection + * using exact formulas which yield accurate (to 8 nm) results over the + * entire ellipsoid. + * + * The ellipsoid parameters and the central scale are set in the constructor. + * The central meridian (which is a trivial shift of the longitude) is + * specified as the \e lon0 argument of the TransverseMercator::Forward and + * TransverseMercator::Reverse functions. The latitude of origin is taken to + * be the equator. There is no provision in this class for specifying a + * false easting or false northing or a different latitude of origin. + * However these are can be simply included by the calling function. For + * example, the UTMUPS class applies the false easting and false northing for + * the UTM projections. A more complicated example is the British National + * Grid ( + * EPSG:7405) which requires the use of a latitude of origin. This is + * implemented by the GeographicLib::OSGB class. + * + * This class also returns the meridian convergence \e gamma and scale \e k. + * The meridian convergence is the bearing of grid north (the \e y axis) + * measured clockwise from true north. + * + * See TransverseMercator.cpp for more information on the implementation. + * + * See \ref transversemercator for a discussion of this projection. + * + * Example of use: + * \include example-TransverseMercator.cpp + * + * TransverseMercatorProj is a + * command-line utility providing access to the functionality of + * TransverseMercator and TransverseMercatorExact. + **********************************************************************/ + + class GEOGRAPHICLIB_EXPORT TransverseMercator { + private: + typedef Math::real real; + static const int maxpow_ = GEOGRAPHICLIB_TRANSVERSEMERCATOR_ORDER; + static const int numit_ = 5; + real _a, _f, _k0, _e2, _es, _e2m, _c, _n; + // _alp[0] and _bet[0] unused + real _a1, _b1, _alp[maxpow_ + 1], _bet[maxpow_ + 1]; + friend class Ellipsoid; // For access to taupf, tauf. + public: + + /** + * Constructor for a ellipsoid with + * + * @param[in] a equatorial radius (meters). + * @param[in] f flattening of ellipsoid. Setting \e f = 0 gives a sphere. + * Negative \e f gives a prolate ellipsoid. + * @param[in] k0 central scale factor. + * @exception GeographicErr if \e a, (1 − \e f) \e a, or \e k0 is + * not positive. + **********************************************************************/ + TransverseMercator(real a, real f, real k0); + + /** + * Forward projection, from geographic to transverse Mercator. + * + * @param[in] lon0 central meridian of the projection (degrees). + * @param[in] lat latitude of point (degrees). + * @param[in] lon longitude of point (degrees). + * @param[out] x easting of point (meters). + * @param[out] y northing of point (meters). + * @param[out] gamma meridian convergence at point (degrees). + * @param[out] k scale of projection at point. + * + * No false easting or northing is added. \e lat should be in the range + * [−90°, 90°]. + **********************************************************************/ + void Forward(real lon0, real lat, real lon, + real& x, real& y, real& gamma, real& k) const; + + /** + * Reverse projection, from transverse Mercator to geographic. + * + * @param[in] lon0 central meridian of the projection (degrees). + * @param[in] x easting of point (meters). + * @param[in] y northing of point (meters). + * @param[out] lat latitude of point (degrees). + * @param[out] lon longitude of point (degrees). + * @param[out] gamma meridian convergence at point (degrees). + * @param[out] k scale of projection at point. + * + * No false easting or northing is added. The value of \e lon returned is + * in the range [−180°, 180°]. + **********************************************************************/ + void Reverse(real lon0, real x, real y, + real& lat, real& lon, real& gamma, real& k) const; + + /** + * TransverseMercator::Forward without returning the convergence and scale. + **********************************************************************/ + void Forward(real lon0, real lat, real lon, + real& x, real& y) const { + real gamma, k; + Forward(lon0, lat, lon, x, y, gamma, k); + } + + /** + * TransverseMercator::Reverse without returning the convergence and scale. + **********************************************************************/ + void Reverse(real lon0, real x, real y, + real& lat, real& lon) const { + real gamma, k; + Reverse(lon0, x, y, lat, lon, gamma, k); + } + + /** \name Inspector functions + **********************************************************************/ + ///@{ + /** + * @return \e a the equatorial radius of the ellipsoid (meters). This is + * the value used in the constructor. + **********************************************************************/ + Math::real EquatorialRadius() const { return _a; } + + /** + * @return \e f the flattening of the ellipsoid. This is the value used in + * the constructor. + **********************************************************************/ + Math::real Flattening() const { return _f; } + + /** + * @return \e k0 central scale for the projection. This is the value of \e + * k0 used in the constructor and is the scale on the central meridian. + **********************************************************************/ + Math::real CentralScale() const { return _k0; } + + /** + * \deprecated An old name for EquatorialRadius(). + **********************************************************************/ + GEOGRAPHICLIB_DEPRECATED("Use EquatorialRadius()") + Math::real MajorRadius() const { return EquatorialRadius(); } + ///@} + + /** + * A global instantiation of TransverseMercator with the WGS84 ellipsoid + * and the UTM scale factor. However, unlike UTM, no false easting or + * northing is added. + **********************************************************************/ + static const TransverseMercator& UTM(); + }; + +} // namespace GeographicLib + +#endif // GEOGRAPHICLIB_TRANSVERSEMERCATOR_HPP diff --git a/common/local_libs/GeographicLib/include/GeographicLib/TransverseMercatorExact.hpp b/common/local_libs/GeographicLib/include/GeographicLib/TransverseMercatorExact.hpp new file mode 100644 index 0000000000..d81f0c8f3f --- /dev/null +++ b/common/local_libs/GeographicLib/include/GeographicLib/TransverseMercatorExact.hpp @@ -0,0 +1,264 @@ +/** + * \file TransverseMercatorExact.hpp + * \brief Header for GeographicLib::TransverseMercatorExact class + * + * Copyright (c) Charles Karney (2008-2020) and licensed + * under the MIT/X11 License. For more information, see + * https://geographiclib.sourceforge.io/ + **********************************************************************/ + +#if !defined(GEOGRAPHICLIB_TRANSVERSEMERCATOREXACT_HPP) +#define GEOGRAPHICLIB_TRANSVERSEMERCATOREXACT_HPP 1 + +#include +#include + +namespace GeographicLib { + + /** + * \brief An exact implementation of the transverse Mercator projection + * + * Implementation of the Transverse Mercator Projection given in + * - L. P. Lee, + * Conformal + * Projections Based On Jacobian Elliptic Functions, Part V of + * Conformal Projections Based on Elliptic Functions, + * (B. V. Gutsell, Toronto, 1976), 128pp., + * ISBN: 0919870163 + * (also appeared as: + * Monograph 16, Suppl. No. 1 to Canadian Cartographer, Vol 13). + * - C. F. F. Karney, + * + * Transverse Mercator with an accuracy of a few nanometers, + * J. Geodesy 85(8), 475--485 (Aug. 2011); + * preprint + * arXiv:1002.1417. + * + * Lee gives the correct results for forward and reverse transformations + * subject to the branch cut rules (see the description of the \e extendp + * argument to the constructor). The maximum error is about 8 nm (8 + * nanometers), ground distance, for the forward and reverse transformations. + * The error in the convergence is 2 × 10−15", + * the relative error in the scale is 7 × 10−12%%. + * See Sec. 3 of + * arXiv:1002.1417 for details. + * The method is "exact" in the sense that the errors are close to the + * round-off limit and that no changes are needed in the algorithms for them + * to be used with reals of a higher precision. Thus the errors using long + * double (with a 64-bit fraction) are about 2000 times smaller than using + * double (with a 53-bit fraction). + * + * This algorithm is about 4.5 times slower than the 6th-order Krüger + * method, TransverseMercator, taking about 11 us for a combined forward and + * reverse projection on a 2.66 GHz Intel machine (g++, version 4.3.0, -O3). + * + * The ellipsoid parameters and the central scale are set in the constructor. + * The central meridian (which is a trivial shift of the longitude) is + * specified as the \e lon0 argument of the TransverseMercatorExact::Forward + * and TransverseMercatorExact::Reverse functions. The latitude of origin is + * taken to be the equator. See the documentation on TransverseMercator for + * how to include a false easting, false northing, or a latitude of origin. + * + * See tm-grid.kmz, for an + * illustration of the transverse Mercator grid in Google Earth. + * + * This class also returns the meridian convergence \e gamma and scale \e k. + * The meridian convergence is the bearing of grid north (the \e y axis) + * measured clockwise from true north. + * + * See TransverseMercatorExact.cpp for more information on the + * implementation. + * + * See \ref transversemercator for a discussion of this projection. + * + * Example of use: + * \include example-TransverseMercatorExact.cpp + * + * TransverseMercatorProj is a + * command-line utility providing access to the functionality of + * TransverseMercator and TransverseMercatorExact. + **********************************************************************/ + + class GEOGRAPHICLIB_EXPORT TransverseMercatorExact { + private: + typedef Math::real real; + static const int numit_ = 10; + real tol_, tol2_, taytol_; + real _a, _f, _k0, _mu, _mv, _e; + bool _extendp; + EllipticFunction _Eu, _Ev; + + void zeta(real u, real snu, real cnu, real dnu, + real v, real snv, real cnv, real dnv, + real& taup, real& lam) const; + + void dwdzeta(real u, real snu, real cnu, real dnu, + real v, real snv, real cnv, real dnv, + real& du, real& dv) const; + + bool zetainv0(real psi, real lam, real& u, real& v) const; + void zetainv(real taup, real lam, real& u, real& v) const; + + void sigma(real u, real snu, real cnu, real dnu, + real v, real snv, real cnv, real dnv, + real& xi, real& eta) const; + + void dwdsigma(real u, real snu, real cnu, real dnu, + real v, real snv, real cnv, real dnv, + real& du, real& dv) const; + + bool sigmainv0(real xi, real eta, real& u, real& v) const; + void sigmainv(real xi, real eta, real& u, real& v) const; + + void Scale(real tau, real lam, + real snu, real cnu, real dnu, + real snv, real cnv, real dnv, + real& gamma, real& k) const; + + public: + + /** + * Constructor for a ellipsoid with + * + * @param[in] a equatorial radius (meters). + * @param[in] f flattening of ellipsoid. + * @param[in] k0 central scale factor. + * @param[in] extendp use extended domain. + * @exception GeographicErr if \e a, \e f, or \e k0 is not positive. + * + * The transverse Mercator projection has a branch point singularity at \e + * lat = 0 and \e lon − \e lon0 = 90 (1 − \e e) or (for + * TransverseMercatorExact::UTM) x = 18381 km, y = 0m. The \e extendp + * argument governs where the branch cut is placed. With \e extendp = + * false, the "standard" convention is followed, namely the cut is placed + * along \e x > 18381 km, \e y = 0m. Forward can be called with any \e lat + * and \e lon then produces the transformation shown in Lee, Fig 46. + * Reverse analytically continues this in the ± \e x direction. As + * a consequence, Reverse may map multiple points to the same geographic + * location; for example, for TransverseMercatorExact::UTM, \e x = + * 22051449.037349 m, \e y = −7131237.022729 m and \e x = + * 29735142.378357 m, \e y = 4235043.607933 m both map to \e lat = + * −2°, \e lon = 88°. + * + * With \e extendp = true, the branch cut is moved to the lower left + * quadrant. The various symmetries of the transverse Mercator projection + * can be used to explore the projection on any sheet. In this mode the + * domains of \e lat, \e lon, \e x, and \e y are restricted to + * - the union of + * - \e lat in [0, 90] and \e lon − \e lon0 in [0, 90] + * - \e lat in (-90, 0] and \e lon − \e lon0 in [90 (1 − \e + e), 90] + * - the union of + * - x/(\e k0 \e a) in [0, ∞) and + * y/(\e k0 \e a) in [0, E(e2)] + * - x/(\e k0 \e a) in [K(1 − e2) − + * E(1 − e2), ∞) and y/(\e k0 \e + * a) in (−∞, 0] + * . + * See Sec. 5 of + * arXiv:1002.1417 for a full + * discussion of the treatment of the branch cut. + * + * The method will work for all ellipsoids used in terrestrial geodesy. + * The method cannot be applied directly to the case of a sphere (\e f = 0) + * because some the constants characterizing this method diverge in that + * limit, and in practice, \e f should be larger than about + * numeric_limits::epsilon(). However, TransverseMercator treats the + * sphere exactly. + **********************************************************************/ + TransverseMercatorExact(real a, real f, real k0, bool extendp = false); + + /** + * Forward projection, from geographic to transverse Mercator. + * + * @param[in] lon0 central meridian of the projection (degrees). + * @param[in] lat latitude of point (degrees). + * @param[in] lon longitude of point (degrees). + * @param[out] x easting of point (meters). + * @param[out] y northing of point (meters). + * @param[out] gamma meridian convergence at point (degrees). + * @param[out] k scale of projection at point. + * + * No false easting or northing is added. \e lat should be in the range + * [−90°, 90°]. + **********************************************************************/ + void Forward(real lon0, real lat, real lon, + real& x, real& y, real& gamma, real& k) const; + + /** + * Reverse projection, from transverse Mercator to geographic. + * + * @param[in] lon0 central meridian of the projection (degrees). + * @param[in] x easting of point (meters). + * @param[in] y northing of point (meters). + * @param[out] lat latitude of point (degrees). + * @param[out] lon longitude of point (degrees). + * @param[out] gamma meridian convergence at point (degrees). + * @param[out] k scale of projection at point. + * + * No false easting or northing is added. The value of \e lon returned is + * in the range [−180°, 180°]. + **********************************************************************/ + void Reverse(real lon0, real x, real y, + real& lat, real& lon, real& gamma, real& k) const; + + /** + * TransverseMercatorExact::Forward without returning the convergence and + * scale. + **********************************************************************/ + void Forward(real lon0, real lat, real lon, + real& x, real& y) const { + real gamma, k; + Forward(lon0, lat, lon, x, y, gamma, k); + } + + /** + * TransverseMercatorExact::Reverse without returning the convergence and + * scale. + **********************************************************************/ + void Reverse(real lon0, real x, real y, + real& lat, real& lon) const { + real gamma, k; + Reverse(lon0, x, y, lat, lon, gamma, k); + } + + /** \name Inspector functions + **********************************************************************/ + ///@{ + /** + * @return \e a the equatorial radius of the ellipsoid (meters). This is + * the value used in the constructor. + **********************************************************************/ + Math::real EquatorialRadius() const { return _a; } + + /** + * @return \e f the flattening of the ellipsoid. This is the value used in + * the constructor. + **********************************************************************/ + Math::real Flattening() const { return _f; } + + /** + * @return \e k0 central scale for the projection. This is the value of \e + * k0 used in the constructor and is the scale on the central meridian. + **********************************************************************/ + Math::real CentralScale() const { return _k0; } + + /** + * \deprecated An old name for EquatorialRadius(). + **********************************************************************/ + GEOGRAPHICLIB_DEPRECATED("Use EquatorialRadius()") + Math::real MajorRadius() const { return EquatorialRadius(); } + ///@} + + /** + * A global instantiation of TransverseMercatorExact with the WGS84 + * ellipsoid and the UTM scale factor. However, unlike UTM, no false + * easting or northing is added. + **********************************************************************/ + static const TransverseMercatorExact& UTM(); + }; + +} // namespace GeographicLib + +#endif // GEOGRAPHICLIB_TRANSVERSEMERCATOREXACT_HPP diff --git a/common/local_libs/GeographicLib/include/GeographicLib/UTMUPS.hpp b/common/local_libs/GeographicLib/include/GeographicLib/UTMUPS.hpp new file mode 100644 index 0000000000..e863c9bc19 --- /dev/null +++ b/common/local_libs/GeographicLib/include/GeographicLib/UTMUPS.hpp @@ -0,0 +1,428 @@ +/** + * \file UTMUPS.hpp + * \brief Header for GeographicLib::UTMUPS class + * + * Copyright (c) Charles Karney (2008-2020) and licensed + * under the MIT/X11 License. For more information, see + * https://geographiclib.sourceforge.io/ + **********************************************************************/ + +#if !defined(GEOGRAPHICLIB_UTMUPS_HPP) +#define GEOGRAPHICLIB_UTMUPS_HPP 1 + +#include + +namespace GeographicLib { + + /** + * \brief Convert between geographic coordinates and UTM/UPS + * + * UTM and UPS are defined + * - J. W. Hager, J. F. Behensky, and B. W. Drew, + * + * The Universal Grids: Universal Transverse Mercator (UTM) and Universal + * Polar Stereographic (UPS), Defense Mapping Agency, Technical Manual + * TM8358.2 (1989). + * . + * Section 2-3 defines UTM and section 3-2.4 defines UPS. This document also + * includes approximate algorithms for the computation of the underlying + * transverse Mercator and polar stereographic projections. Here we + * substitute much more accurate algorithms given by + * GeographicLib:TransverseMercator and GeographicLib:PolarStereographic. + * These are the algorithms recommended by the NGA document + * - + * The Universal Grids and the Transverse Mercator and Polar Stereographic + * Map Projections, NGA.SIG.0012_2.0.0_UTMUPS (2014). + * + * In this implementation, the conversions are closed, i.e., output from + * Forward is legal input for Reverse and vice versa. The error is about 5nm + * in each direction. However, the conversion from legal UTM/UPS coordinates + * to geographic coordinates and back might throw an error if the initial + * point is within 5nm of the edge of the allowed range for the UTM/UPS + * coordinates. + * + * The simplest way to guarantee the closed property is to define allowed + * ranges for the eastings and northings for UTM and UPS coordinates. The + * UTM boundaries are the same for all zones. (The only place the + * exceptional nature of the zone boundaries is evident is when converting to + * UTM/UPS coordinates requesting the standard zone.) The MGRS lettering + * scheme imposes natural limits on UTM/UPS coordinates which may be + * converted into MGRS coordinates. For the conversion to/from geographic + * coordinates these ranges have been extended by 100km in order to provide a + * generous overlap between UTM and UPS and between UTM zones. + * + * The NGA software package + * geotrans + * also provides conversions to and from UTM and UPS. Version 2.4.2 (and + * earlier) suffers from some drawbacks: + * - Inconsistent rules are used to determine the whether a particular UTM or + * UPS coordinate is legal. A more systematic approach is taken here. + * - The underlying projections are not very accurately implemented. + * + * The GeographicLib::UTMUPS::EncodeZone encodes the UTM zone and hemisphere + * to allow UTM/UPS coordinated to be displayed as, for example, "38N 444500 + * 3688500". According to NGA.SIG.0012_2.0.0_UTMUPS the use of "N" to denote + * "north" in the context is not allowed (since a upper case letter in this + * context denotes the MGRS latitude band). Consequently, as of version + * 1.36, EncodeZone uses the lower case letters "n" and "s" to denote the + * hemisphere. In addition EncodeZone accepts an optional final argument \e + * abbrev, which, if false, results in the hemisphere being spelled out as in + * "38north". + * + * Example of use: + * \include example-UTMUPS.cpp + **********************************************************************/ + class GEOGRAPHICLIB_EXPORT UTMUPS { + private: + typedef Math::real real; + static const int falseeasting_[4]; + static const int falsenorthing_[4]; + static const int mineasting_[4]; + static const int maxeasting_[4]; + static const int minnorthing_[4]; + static const int maxnorthing_[4]; + static const int epsg01N = 32601; // EPSG code for UTM 01N + static const int epsg60N = 32660; // EPSG code for UTM 60N + static const int epsgN = 32661; // EPSG code for UPS N + static const int epsg01S = 32701; // EPSG code for UTM 01S + static const int epsg60S = 32760; // EPSG code for UTM 60S + static const int epsgS = 32761; // EPSG code for UPS S + static real CentralMeridian(int zone) + { return real(6 * zone - 183); } + // Throw an error if easting or northing are outside standard ranges. If + // throwp = false, return bool instead. + static bool CheckCoords(bool utmp, bool northp, real x, real y, + bool msgrlimits = false, bool throwp = true); + UTMUPS(); // Disable constructor + + public: + + /** + * In this class we bring together the UTM and UPS coordinates systems. + * The UTM divides the earth between latitudes −80° and 84° + * into 60 zones numbered 1 thru 60. Zone assign zone number 0 to the UPS + * regions, covering the two poles. Within UTMUPS, non-negative zone + * numbers refer to one of the "physical" zones, 0 for UPS and [1, 60] for + * UTM. Negative "pseudo-zone" numbers are used to select one of the + * physical zones. + **********************************************************************/ + enum zonespec { + /** + * The smallest pseudo-zone number. + **********************************************************************/ + MINPSEUDOZONE = -4, + /** + * A marker for an undefined or invalid zone. Equivalent to NaN. + **********************************************************************/ + INVALID = -4, + /** + * If a coordinate already include zone information (e.g., it is an MGRS + * coordinate), use that, otherwise apply the UTMUPS::STANDARD rules. + **********************************************************************/ + MATCH = -3, + /** + * Apply the standard rules for UTM zone assigment extending the UTM zone + * to each pole to give a zone number in [1, 60]. For example, use UTM + * zone 38 for longitude in [42°, 48°). The rules include the + * Norway and Svalbard exceptions. + **********************************************************************/ + UTM = -2, + /** + * Apply the standard rules for zone assignment to give a zone number in + * [0, 60]. If the latitude is not in [−80°, 84°), then + * use UTMUPS::UPS = 0, otherwise apply the rules for UTMUPS::UTM. The + * tests on latitudes and longitudes are all closed on the lower end open + * on the upper. Thus for UTM zone 38, latitude is in [−80°, + * 84°) and longitude is in [42°, 48°). + **********************************************************************/ + STANDARD = -1, + /** + * The largest pseudo-zone number. + **********************************************************************/ + MAXPSEUDOZONE = -1, + /** + * The smallest physical zone number. + **********************************************************************/ + MINZONE = 0, + /** + * The zone number used for UPS + **********************************************************************/ + UPS = 0, + /** + * The smallest UTM zone number. + **********************************************************************/ + MINUTMZONE = 1, + /** + * The largest UTM zone number. + **********************************************************************/ + MAXUTMZONE = 60, + /** + * The largest physical zone number. + **********************************************************************/ + MAXZONE = 60, + }; + + /** + * The standard zone. + * + * @param[in] lat latitude (degrees). + * @param[in] lon longitude (degrees). + * @param[in] setzone zone override (optional). If omitted, use the + * standard rules for picking the zone. If \e setzone is given then use + * that zone if it is non-negative, otherwise apply the rules given in + * UTMUPS::zonespec. + * @exception GeographicErr if \e setzone is outside the range + * [UTMUPS::MINPSEUDOZONE, UTMUPS::MAXZONE] = [−4, 60]. + * + * This is exact. + **********************************************************************/ + static int StandardZone(real lat, real lon, int setzone = STANDARD); + + /** + * Forward projection, from geographic to UTM/UPS. + * + * @param[in] lat latitude of point (degrees). + * @param[in] lon longitude of point (degrees). + * @param[out] zone the UTM zone (zero means UPS). + * @param[out] northp hemisphere (true means north, false means south). + * @param[out] x easting of point (meters). + * @param[out] y northing of point (meters). + * @param[out] gamma meridian convergence at point (degrees). + * @param[out] k scale of projection at point. + * @param[in] setzone zone override (optional). + * @param[in] mgrslimits if true enforce the stricter MGRS limits on the + * coordinates (default = false). + * @exception GeographicErr if \e lat is not in [−90°, + * 90°]. + * @exception GeographicErr if the resulting \e x or \e y is out of allowed + * range (see Reverse); in this case, these arguments are unchanged. + * + * If \e setzone is omitted, use the standard rules for picking the zone. + * If \e setzone is given then use that zone if it is non-negative, + * otherwise apply the rules given in UTMUPS::zonespec. The accuracy of + * the conversion is about 5nm. + * + * The northing \e y jumps by UTMUPS::UTMShift() when crossing the equator + * in the southerly direction. Sometimes it is useful to remove this + * discontinuity in \e y by extending the "northern" hemisphere using + * UTMUPS::Transfer: + * \code + double lat = -1, lon = 123; + int zone; + bool northp; + double x, y, gamma, k; + GeographicLib::UTMUPS::Forward(lat, lon, zone, northp, x, y, gamma, k); + GeographicLib::UTMUPS::Transfer(zone, northp, x, y, + zone, true, x, y, zone); + northp = true; + \endcode + **********************************************************************/ + static void Forward(real lat, real lon, + int& zone, bool& northp, real& x, real& y, + real& gamma, real& k, + int setzone = STANDARD, bool mgrslimits = false); + + /** + * Reverse projection, from UTM/UPS to geographic. + * + * @param[in] zone the UTM zone (zero means UPS). + * @param[in] northp hemisphere (true means north, false means south). + * @param[in] x easting of point (meters). + * @param[in] y northing of point (meters). + * @param[out] lat latitude of point (degrees). + * @param[out] lon longitude of point (degrees). + * @param[out] gamma meridian convergence at point (degrees). + * @param[out] k scale of projection at point. + * @param[in] mgrslimits if true enforce the stricter MGRS limits on the + * coordinates (default = false). + * @exception GeographicErr if \e zone, \e x, or \e y is out of allowed + * range; this this case the arguments are unchanged. + * + * The accuracy of the conversion is about 5nm. + * + * UTM eastings are allowed to be in the range [0km, 1000km], northings are + * allowed to be in in [0km, 9600km] for the northern hemisphere and in + * [900km, 10000km] for the southern hemisphere. However UTM northings + * can be continued across the equator. So the actual limits on the + * northings are [-9100km, 9600km] for the "northern" hemisphere and + * [900km, 19600km] for the "southern" hemisphere. + * + * UPS eastings and northings are allowed to be in the range [1200km, + * 2800km] in the northern hemisphere and in [700km, 3300km] in the + * southern hemisphere. + * + * These ranges are 100km larger than allowed for the conversions to MGRS. + * (100km is the maximum extra padding consistent with eastings remaining + * non-negative.) This allows generous overlaps between zones and UTM and + * UPS. If \e mgrslimits = true, then all the ranges are shrunk by 100km + * so that they agree with the stricter MGRS ranges. No checks are + * performed besides these (e.g., to limit the distance outside the + * standard zone boundaries). + **********************************************************************/ + static void Reverse(int zone, bool northp, real x, real y, + real& lat, real& lon, real& gamma, real& k, + bool mgrslimits = false); + + /** + * UTMUPS::Forward without returning convergence and scale. + **********************************************************************/ + static void Forward(real lat, real lon, + int& zone, bool& northp, real& x, real& y, + int setzone = STANDARD, bool mgrslimits = false) { + real gamma, k; + Forward(lat, lon, zone, northp, x, y, gamma, k, setzone, mgrslimits); + } + + /** + * UTMUPS::Reverse without returning convergence and scale. + **********************************************************************/ + static void Reverse(int zone, bool northp, real x, real y, + real& lat, real& lon, bool mgrslimits = false) { + real gamma, k; + Reverse(zone, northp, x, y, lat, lon, gamma, k, mgrslimits); + } + + /** + * Transfer UTM/UPS coordinated from one zone to another. + * + * @param[in] zonein the UTM zone for \e xin and \e yin (or zero for UPS). + * @param[in] northpin hemisphere for \e xin and \e yin (true means north, + * false means south). + * @param[in] xin easting of point (meters) in \e zonein. + * @param[in] yin northing of point (meters) in \e zonein. + * @param[in] zoneout the requested UTM zone for \e xout and \e yout (or + * zero for UPS). + * @param[in] northpout hemisphere for \e xout output and \e yout. + * @param[out] xout easting of point (meters) in \e zoneout. + * @param[out] yout northing of point (meters) in \e zoneout. + * @param[out] zone the actual UTM zone for \e xout and \e yout (or zero + * for UPS); this equals \e zoneout if \e zoneout ≥ 0. + * @exception GeographicErr if \e zonein is out of range (see below). + * @exception GeographicErr if \e zoneout is out of range (see below). + * @exception GeographicErr if \e xin or \e yin fall outside their allowed + * ranges (see UTMUPS::Reverse). + * @exception GeographicErr if \e xout or \e yout fall outside their + * allowed ranges (see UTMUPS::Reverse). + * + * \e zonein must be in the range [UTMUPS::MINZONE, UTMUPS::MAXZONE] = [0, + * 60] with \e zonein = UTMUPS::UPS, 0, indicating UPS. \e zonein may + * also be UTMUPS::INVALID. + * + * \e zoneout must be in the range [UTMUPS::MINPSEUDOZONE, UTMUPS::MAXZONE] + * = [-4, 60]. If \e zoneout < UTMUPS::MINZONE then the rules give in + * the documentation of UTMUPS::zonespec are applied, and \e zone is set to + * the actual zone used for output. + * + * (\e xout, \e yout) can overlap with (\e xin, \e yin). + **********************************************************************/ + static void Transfer(int zonein, bool northpin, real xin, real yin, + int zoneout, bool northpout, real& xout, real& yout, + int& zone); + + /** + * Decode a UTM/UPS zone string. + * + * @param[in] zonestr string representation of zone and hemisphere. + * @param[out] zone the UTM zone (zero means UPS). + * @param[out] northp hemisphere (true means north, false means south). + * @exception GeographicErr if \e zonestr is malformed. + * + * For UTM, \e zonestr has the form of a zone number in the range + * [UTMUPS::MINUTMZONE, UTMUPS::MAXUTMZONE] = [1, 60] followed by a + * hemisphere letter, n or s (or "north" or "south" spelled out). For UPS, + * it consists just of the hemisphere letter (or the spelled out + * hemisphere). The returned value of \e zone is UTMUPS::UPS = 0 for UPS. + * Note well that "38s" indicates the southern hemisphere of zone 38 and + * not latitude band S, 32° ≤ \e lat < 40°. n, 01s, 2n, 38s, + * south, 3north are legal. 0n, 001s, +3n, 61n, 38P are illegal. INV is a + * special value for which the returned value of \e is UTMUPS::INVALID. + **********************************************************************/ + static void DecodeZone(const std::string& zonestr, + int& zone, bool& northp); + + /** + * Encode a UTM/UPS zone string. + * + * @param[in] zone the UTM zone (zero means UPS). + * @param[in] northp hemisphere (true means north, false means south). + * @param[in] abbrev if true (the default) use abbreviated (n/s) notation + * for hemisphere; otherwise spell out the hemisphere (north/south) + * @exception GeographicErr if \e zone is out of range (see below). + * @exception std::bad_alloc if memoy for the string can't be allocated. + * @return string representation of zone and hemisphere. + * + * \e zone must be in the range [UTMUPS::MINZONE, UTMUPS::MAXZONE] = [0, + * 60] with \e zone = UTMUPS::UPS, 0, indicating UPS (but the resulting + * string does not contain "0"). \e zone may also be UTMUPS::INVALID, in + * which case the returned string is "inv". This reverses + * UTMUPS::DecodeZone. + **********************************************************************/ + static std::string EncodeZone(int zone, bool northp, bool abbrev = true); + + /** + * Decode EPSG. + * + * @param[in] epsg the EPSG code. + * @param[out] zone the UTM zone (zero means UPS). + * @param[out] northp hemisphere (true means north, false means south). + * + * EPSG (European Petroleum Survery Group) codes are a way to refer to many + * different projections. DecodeEPSG decodes those referring to UTM or UPS + * projections for the WGS84 ellipsoid. If the code does not refer to one + * of these projections, \e zone is set to UTMUPS::INVALID. See + * https://www.spatialreference.org/ref/epsg/ + **********************************************************************/ + static void DecodeEPSG(int epsg, int& zone, bool& northp); + + /** + * Encode zone as EPSG. + * + * @param[in] zone the UTM zone (zero means UPS). + * @param[in] northp hemisphere (true means north, false means south). + * @return EPSG code (or -1 if \e zone is not in the range + * [UTMUPS::MINZONE, UTMUPS::MAXZONE] = [0, 60]) + * + * Convert \e zone and \e northp to the corresponding EPSG (European + * Petroleum Survery Group) codes + **********************************************************************/ + static int EncodeEPSG(int zone, bool northp); + + /** + * @return shift (meters) necessary to align north and south halves of a + * UTM zone (107). + **********************************************************************/ + static Math::real UTMShift(); + + /** \name Inspector functions + **********************************************************************/ + ///@{ + /** + * @return \e a the equatorial radius of the WGS84 ellipsoid (meters). + * + * (The WGS84 value is returned because the UTM and UPS projections are + * based on this ellipsoid.) + **********************************************************************/ + static Math::real EquatorialRadius() + { return Constants::WGS84_a(); } + + /** + * @return \e f the flattening of the WGS84 ellipsoid. + * + * (The WGS84 value is returned because the UTM and UPS projections are + * based on this ellipsoid.) + **********************************************************************/ + static Math::real Flattening() + { return Constants::WGS84_f(); } + + /** + * \deprecated An old name for EquatorialRadius(). + **********************************************************************/ + GEOGRAPHICLIB_DEPRECATED("Use EquatorialRadius()") + static Math::real MajorRadius() { return EquatorialRadius(); } + ///@} + + }; + +} // namespace GeographicLib + +#endif // GEOGRAPHICLIB_UTMUPS_HPP diff --git a/common/local_libs/GeographicLib/include/GeographicLib/Utility.hpp b/common/local_libs/GeographicLib/include/GeographicLib/Utility.hpp new file mode 100644 index 0000000000..f24ce93f9c --- /dev/null +++ b/common/local_libs/GeographicLib/include/GeographicLib/Utility.hpp @@ -0,0 +1,731 @@ +/** + * \file Utility.hpp + * \brief Header for GeographicLib::Utility class + * + * Copyright (c) Charles Karney (2011-2020) and licensed + * under the MIT/X11 License. For more information, see + * https://geographiclib.sourceforge.io/ + **********************************************************************/ + +#if !defined(GEOGRAPHICLIB_UTILITY_HPP) +#define GEOGRAPHICLIB_UTILITY_HPP 1 + +#include +#include +#include +#include +#include +#include +#include + +#if defined(_MSC_VER) +// Squelch warnings about constant conditional expressions and unsafe gmtime +# pragma warning (push) +# pragma warning (disable: 4127 4996) +#endif + +namespace GeographicLib { + + /** + * \brief Some utility routines for %GeographicLib + * + * Example of use: + * \include example-Utility.cpp + **********************************************************************/ + class GEOGRAPHICLIB_EXPORT Utility { + private: + static bool gregorian(int y, int m, int d) { + // The original cut over to the Gregorian calendar in Pope Gregory XIII's + // time had 1582-10-04 followed by 1582-10-15. Here we implement the + // switch over used by the English-speaking world where 1752-09-02 was + // followed by 1752-09-14. We also assume that the year always begins + // with January 1, whereas in reality it often was reckoned to begin in + // March. + return 100 * (100 * y + m) + d >= 17520914; // or 15821015 + } + static bool gregorian(int s) { + return s >= 639799; // 1752-09-14 + } + public: + + /** + * Convert a date to the day numbering sequentially starting with + * 0001-01-01 as day 1. + * + * @param[in] y the year (must be positive). + * @param[in] m the month, Jan = 1, etc. (must be positive). Default = 1. + * @param[in] d the day of the month (must be positive). Default = 1. + * @return the sequential day number. + **********************************************************************/ + static int day(int y, int m = 1, int d = 1) { + // Convert from date to sequential day and vice versa + // + // Here is some code to convert a date to sequential day and vice + // versa. The sequential day is numbered so that January 1, 1 AD is day 1 + // (a Saturday). So this is offset from the "Julian" day which starts the + // numbering with 4713 BC. + // + // This is inspired by a talk by John Conway at the John von Neumann + // National Supercomputer Center when he described his Doomsday algorithm + // for figuring the day of the week. The code avoids explicitly doing ifs + // (except for the decision of whether to use the Julian or Gregorian + // calendar). Instead the equivalent result is achieved using integer + // arithmetic. I got this idea from the routine for the day of the week + // in MACLisp (I believe that that routine was written by Guy Steele). + // + // There are three issues to take care of + // + // 1. the rules for leap years, + // 2. the inconvenient placement of leap days at the end of February, + // 3. the irregular pattern of month lengths. + // + // We deal with these as follows: + // + // 1. Leap years are given by simple rules which are straightforward to + // accommodate. + // + // 2. We simplify the calculations by moving January and February to the + // previous year. Here we internally number the months March–December, + // January, February as 0–9, 10, 11. + // + // 3. The pattern of month lengths from March through January is regular + // with a 5-month period—31, 30, 31, 30, 31, 31, 30, 31, 30, 31, 31. The + // 5-month period is 153 days long. Since February is now at the end of + // the year, we don't need to include its length in this part of the + // calculation. + bool greg = gregorian(y, m, d); + y += (m + 9) / 12 - 1; // Move Jan and Feb to previous year, + m = (m + 9) % 12; // making March month 0. + return + (1461 * y) / 4 // Julian years converted to days. Julian year is 365 + + // 1/4 = 1461/4 days. + // Gregorian leap year corrections. The 2 offset with respect to the + // Julian calendar synchronizes the vernal equinox with that at the + // time of the Council of Nicea (325 AD). + + (greg ? (y / 100) / 4 - (y / 100) + 2 : 0) + + (153 * m + 2) / 5 // The zero-based start of the m'th month + + d - 1 // The zero-based day + - 305; // The number of days between March 1 and December 31. + // This makes 0001-01-01 day 1 + } + + /** + * Convert a date to the day numbering sequentially starting with + * 0001-01-01 as day 1. + * + * @param[in] y the year (must be positive). + * @param[in] m the month, Jan = 1, etc. (must be positive). Default = 1. + * @param[in] d the day of the month (must be positive). Default = 1. + * @param[in] check whether to check the date. + * @exception GeographicErr if the date is invalid and \e check is true. + * @return the sequential day number. + **********************************************************************/ + static int day(int y, int m, int d, bool check) { + int s = day(y, m, d); + if (!check) + return s; + int y1, m1, d1; + date(s, y1, m1, d1); + if (!(s > 0 && y == y1 && m == m1 && d == d1)) + throw GeographicErr("Invalid date " + + str(y) + "-" + str(m) + "-" + str(d) + + (s > 0 ? "; use " + + str(y1) + "-" + str(m1) + "-" + str(d1) : + " before 0001-01-01")); + return s; + } + + /** + * Given a day (counting from 0001-01-01 as day 1), return the date. + * + * @param[in] s the sequential day number (must be positive) + * @param[out] y the year. + * @param[out] m the month, Jan = 1, etc. + * @param[out] d the day of the month. + **********************************************************************/ + static void date(int s, int& y, int& m, int& d) { + int c = 0; + bool greg = gregorian(s); + s += 305; // s = 0 on March 1, 1BC + if (greg) { + s -= 2; // The 2 day Gregorian offset + // Determine century with the Gregorian rules for leap years. The + // Gregorian year is 365 + 1/4 - 1/100 + 1/400 = 146097/400 days. + c = (4 * s + 3) / 146097; + s -= (c * 146097) / 4; // s = 0 at beginning of century + } + y = (4 * s + 3) / 1461; // Determine the year using Julian rules. + s -= (1461 * y) / 4; // s = 0 at start of year, i.e., March 1 + y += c * 100; // Assemble full year + m = (5 * s + 2) / 153; // Determine the month + s -= (153 * m + 2) / 5; // s = 0 at beginning of month + d = s + 1; // Determine day of month + y += (m + 2) / 12; // Move Jan and Feb back to original year + m = (m + 2) % 12 + 1; // Renumber the months so January = 1 + } + + /** + * Given a date as a string in the format yyyy, yyyy-mm, or yyyy-mm-dd, + * return the numeric values for the year, month, and day. No checking is + * done on these values. The string "now" is interpreted as the present + * date (in UTC). + * + * @param[in] s the date in string format. + * @param[out] y the year. + * @param[out] m the month, Jan = 1, etc. + * @param[out] d the day of the month. + * @exception GeographicErr is \e s is malformed. + **********************************************************************/ + static void date(const std::string& s, int& y, int& m, int& d) { + if (s == "now") { + std::time_t t = std::time(0); + struct tm* now = gmtime(&t); + y = now->tm_year + 1900; + m = now->tm_mon + 1; + d = now->tm_mday; + return; + } + int y1, m1 = 1, d1 = 1; + const char* digits = "0123456789"; + std::string::size_type p1 = s.find_first_not_of(digits); + if (p1 == std::string::npos) + y1 = val(s); + else if (s[p1] != '-') + throw GeographicErr("Delimiter not hyphen in date " + s); + else if (p1 == 0) + throw GeographicErr("Empty year field in date " + s); + else { + y1 = val(s.substr(0, p1)); + if (++p1 == s.size()) + throw GeographicErr("Empty month field in date " + s); + std::string::size_type p2 = s.find_first_not_of(digits, p1); + if (p2 == std::string::npos) + m1 = val(s.substr(p1)); + else if (s[p2] != '-') + throw GeographicErr("Delimiter not hyphen in date " + s); + else if (p2 == p1) + throw GeographicErr("Empty month field in date " + s); + else { + m1 = val(s.substr(p1, p2 - p1)); + if (++p2 == s.size()) + throw GeographicErr("Empty day field in date " + s); + d1 = val(s.substr(p2)); + } + } + y = y1; m = m1; d = d1; + } + + /** + * Given the date, return the day of the week. + * + * @param[in] y the year (must be positive). + * @param[in] m the month, Jan = 1, etc. (must be positive). + * @param[in] d the day of the month (must be positive). + * @return the day of the week with Sunday, Monday--Saturday = 0, + * 1--6. + **********************************************************************/ + static int dow(int y, int m, int d) { return dow(day(y, m, d)); } + + /** + * Given the sequential day, return the day of the week. + * + * @param[in] s the sequential day (must be positive). + * @return the day of the week with Sunday, Monday--Saturday = 0, + * 1--6. + **********************************************************************/ + static int dow(int s) { + return (s + 5) % 7; // The 5 offset makes day 1 (0001-01-01) a Saturday. + } + + /** + * Convert a string representing a date to a fractional year. + * + * @tparam T the type of the argument. + * @param[in] s the string to be converted. + * @exception GeographicErr if \e s can't be interpreted as a date. + * @return the fractional year. + * + * The string is first read as an ordinary number (e.g., 2010 or 2012.5); + * if this is successful, the value is returned. Otherwise the string + * should be of the form yyyy-mm or yyyy-mm-dd and this is converted to a + * number with 2010-01-01 giving 2010.0 and 2012-07-03 giving 2012.5. + **********************************************************************/ + template static T fractionalyear(const std::string& s) { + try { + return val(s); + } + catch (const std::exception&) {} + int y, m, d; + date(s, y, m, d); + int t = day(y, m, d, true); + return T(y) + T(t - day(y)) / T(day(y + 1) - day(y)); + } + + /** + * Convert a object of type T to a string. + * + * @tparam T the type of the argument. + * @param[in] x the value to be converted. + * @param[in] p the precision used (default −1). + * @exception std::bad_alloc if memory for the string can't be allocated. + * @return the string representation. + * + * If \e p ≥ 0, then the number fixed format is used with p bits of + * precision. With p < 0, there is no manipulation of the format. + **********************************************************************/ + template static std::string str(T x, int p = -1) { + std::ostringstream s; + if (p >= 0) s << std::fixed << std::setprecision(p); + s << x; return s.str(); + } + + /** + * Convert a Math::real object to a string. + * + * @param[in] x the value to be converted. + * @param[in] p the precision used (default −1). + * @exception std::bad_alloc if memory for the string can't be allocated. + * @return the string representation. + * + * If \e p ≥ 0, then the number fixed format is used with p bits of + * precision. With p < 0, there is no manipulation of the format. This is + * an overload of str which deals with inf and nan. + **********************************************************************/ + static std::string str(Math::real x, int p = -1) { + using std::isfinite; + if (!isfinite(x)) + return x < 0 ? std::string("-inf") : + (x > 0 ? std::string("inf") : std::string("nan")); + std::ostringstream s; +#if GEOGRAPHICLIB_PRECISION == 4 + // boost-quadmath treats precision == 0 as "use as many digits as + // necessary" (see https://svn.boost.org/trac/boost/ticket/10103), so... + using std::floor; using std::fmod; + if (p == 0) { + x += Math::real(0.5); + Math::real ix = floor(x); + // Implement the "round ties to even" rule + x = (ix == x && fmod(ix, Math::real(2)) == 1) ? ix - 1 : ix; + s << std::fixed << std::setprecision(1) << x; + std::string r(s.str()); + // strip off trailing ".0" + return r.substr(0, (std::max)(int(r.size()) - 2, 0)); + } +#endif + if (p >= 0) s << std::fixed << std::setprecision(p); + s << x; return s.str(); + } + + /** + * Trim the white space from the beginning and end of a string. + * + * @param[in] s the string to be trimmed + * @return the trimmed string + **********************************************************************/ + static std::string trim(const std::string& s) { + unsigned + beg = 0, + end = unsigned(s.size()); + while (beg < end && isspace(s[beg])) + ++beg; + while (beg < end && isspace(s[end - 1])) + --end; + return std::string(s, beg, end-beg); + } + + /** + * Convert a string to type T. + * + * @tparam T the type of the return value. + * @param[in] s the string to be converted. + * @exception GeographicErr is \e s is not readable as a T. + * @return object of type T. + * + * White space at the beginning and end of \e s is ignored. + * + * Special handling is provided for some types. + * + * If T is a floating point type, then inf and nan are recognized. + * + * If T is bool, then \e s should either be string a representing 0 (false) + * or 1 (true) or one of the strings + * - "false", "f", "nil", "no", "n", "off", or "" meaning false, + * - "true", "t", "yes", "y", or "on" meaning true; + * . + * case is ignored. + * + * If T is std::string, then \e s is returned (with the white space at the + * beginning and end removed). + **********************************************************************/ + template static T val(const std::string& s) { + // If T is bool, then the specialization val() defined below is + // used. + T x; + std::string errmsg, t(trim(s)); + do { // Executed once (provides the ability to break) + std::istringstream is(t); + if (!(is >> x)) { + errmsg = "Cannot decode " + t; + break; + } + int pos = int(is.tellg()); // Returns -1 at end of string? + if (!(pos < 0 || pos == int(t.size()))) { + errmsg = "Extra text " + t.substr(pos) + " at end of " + t; + break; + } + return x; + } while (false); + x = std::numeric_limits::is_integer ? 0 : nummatch(t); + if (x == 0) + throw GeographicErr(errmsg); + return x; + } + /** + * \deprecated An old name for val(s). + **********************************************************************/ + template + GEOGRAPHICLIB_DEPRECATED("Use Utility::val(s)") + static T num(const std::string& s) { + return val(s); + } + + /** + * Match "nan" and "inf" (and variants thereof) in a string. + * + * @tparam T the type of the return value (this should be a floating point + * type). + * @param[in] s the string to be matched. + * @return appropriate special value (±∞, nan) or 0 if none is + * found. + * + * White space is not allowed at the beginning or end of \e s. + **********************************************************************/ + template static T nummatch(const std::string& s) { + if (s.length() < 3) + return 0; + std::string t(s); + for (std::string::iterator p = t.begin(); p != t.end(); ++p) + *p = char(std::toupper(*p)); + for (size_t i = s.length(); i--;) + t[i] = char(std::toupper(s[i])); + int sign = t[0] == '-' ? -1 : 1; + std::string::size_type p0 = t[0] == '-' || t[0] == '+' ? 1 : 0; + std::string::size_type p1 = t.find_last_not_of('0'); + if (p1 == std::string::npos || p1 + 1 < p0 + 3) + return 0; + // Strip off sign and trailing 0s + t = t.substr(p0, p1 + 1 - p0); // Length at least 3 + if (t == "NAN" || t == "1.#QNAN" || t == "1.#SNAN" || t == "1.#IND" || + t == "1.#R") + return Math::NaN(); + else if (t == "INF" || t == "1.#INF") + return sign * Math::infinity(); + return 0; + } + + /** + * Read a simple fraction, e.g., 3/4, from a string to an object of type T. + * + * @tparam T the type of the return value. + * @param[in] s the string to be converted. + * @exception GeographicErr is \e s is not readable as a fraction of type + * T. + * @return object of type T + * + * \note The msys shell under Windows converts arguments which look like + * pathnames into their Windows equivalents. As a result the argument + * "-1/300" gets mangled into something unrecognizable. A workaround is to + * use a floating point number in the numerator, i.e., "-1.0/300". (Recent + * versions of the msys shell appear \e not to have this problem.) + **********************************************************************/ + template static T fract(const std::string& s) { + std::string::size_type delim = s.find('/'); + return + !(delim != std::string::npos && delim >= 1 && delim + 2 <= s.size()) ? + val(s) : + // delim in [1, size() - 2] + val(s.substr(0, delim)) / val(s.substr(delim + 1)); + } + + /** + * Lookup up a character in a string. + * + * @param[in] s the string to be searched. + * @param[in] c the character to look for. + * @return the index of the first occurrence character in the string or + * −1 is the character is not present. + * + * \e c is converted to upper case before search \e s. Therefore, it is + * intended that \e s should not contain any lower case letters. + **********************************************************************/ + static int lookup(const std::string& s, char c) { + std::string::size_type r = s.find(char(std::toupper(c))); + return r == std::string::npos ? -1 : int(r); + } + + /** + * Lookup up a character in a char*. + * + * @param[in] s the char* string to be searched. + * @param[in] c the character to look for. + * @return the index of the first occurrence character in the string or + * −1 is the character is not present. + * + * \e c is converted to upper case before search \e s. Therefore, it is + * intended that \e s should not contain any lower case letters. + **********************************************************************/ + static int lookup(const char* s, char c) { + const char* p = std::strchr(s, std::toupper(c)); + return p != NULL ? int(p - s) : -1; + } + + /** + * Read data of type ExtT from a binary stream to an array of type IntT. + * The data in the file is in (bigendp ? big : little)-endian format. + * + * @tparam ExtT the type of the objects in the binary stream (external). + * @tparam IntT the type of the objects in the array (internal). + * @tparam bigendp true if the external storage format is big-endian. + * @param[in] str the input stream containing the data of type ExtT + * (external). + * @param[out] array the output array of type IntT (internal). + * @param[in] num the size of the array. + * @exception GeographicErr if the data cannot be read. + **********************************************************************/ + template + static void readarray(std::istream& str, IntT array[], size_t num) { +#if GEOGRAPHICLIB_PRECISION < 4 + if (sizeof(IntT) == sizeof(ExtT) && + std::numeric_limits::is_integer == + std::numeric_limits::is_integer) + { + // Data is compatible (aside from the issue of endian-ness). + str.read(reinterpret_cast(array), num * sizeof(ExtT)); + if (!str.good()) + throw GeographicErr("Failure reading data"); + if (bigendp != Math::bigendian) { // endian mismatch -> swap bytes + for (size_t i = num; i--;) + array[i] = Math::swab(array[i]); + } + } + else +#endif + { + const int bufsize = 1024; // read this many values at a time + ExtT buffer[bufsize]; // temporary buffer + int k = int(num); // data values left to read + int i = 0; // index into output array + while (k) { + int n = (std::min)(k, bufsize); + str.read(reinterpret_cast(buffer), n * sizeof(ExtT)); + if (!str.good()) + throw GeographicErr("Failure reading data"); + for (int j = 0; j < n; ++j) + // fix endian-ness and cast to IntT + array[i++] = IntT(bigendp == Math::bigendian ? buffer[j] : + Math::swab(buffer[j])); + k -= n; + } + } + return; + } + + /** + * Read data of type ExtT from a binary stream to a vector array of type + * IntT. The data in the file is in (bigendp ? big : little)-endian + * format. + * + * @tparam ExtT the type of the objects in the binary stream (external). + * @tparam IntT the type of the objects in the array (internal). + * @tparam bigendp true if the external storage format is big-endian. + * @param[in] str the input stream containing the data of type ExtT + * (external). + * @param[out] array the output vector of type IntT (internal). + * @exception GeographicErr if the data cannot be read. + **********************************************************************/ + template + static void readarray(std::istream& str, std::vector& array) { + if (array.size() > 0) + readarray(str, &array[0], array.size()); + } + + /** + * Write data in an array of type IntT as type ExtT to a binary stream. + * The data in the file is in (bigendp ? big : little)-endian format. + * + * @tparam ExtT the type of the objects in the binary stream (external). + * @tparam IntT the type of the objects in the array (internal). + * @tparam bigendp true if the external storage format is big-endian. + * @param[out] str the output stream for the data of type ExtT (external). + * @param[in] array the input array of type IntT (internal). + * @param[in] num the size of the array. + * @exception GeographicErr if the data cannot be written. + **********************************************************************/ + template + static void writearray(std::ostream& str, const IntT array[], size_t num) + { +#if GEOGRAPHICLIB_PRECISION < 4 + if (sizeof(IntT) == sizeof(ExtT) && + std::numeric_limits::is_integer == + std::numeric_limits::is_integer && + bigendp == Math::bigendian) + { + // Data is compatible (including endian-ness). + str.write(reinterpret_cast(array), num * sizeof(ExtT)); + if (!str.good()) + throw GeographicErr("Failure writing data"); + } + else +#endif + { + const int bufsize = 1024; // write this many values at a time + ExtT buffer[bufsize]; // temporary buffer + int k = int(num); // data values left to write + int i = 0; // index into output array + while (k) { + int n = (std::min)(k, bufsize); + for (int j = 0; j < n; ++j) + // cast to ExtT and fix endian-ness + buffer[j] = bigendp == Math::bigendian ? ExtT(array[i++]) : + Math::swab(ExtT(array[i++])); + str.write(reinterpret_cast(buffer), n * sizeof(ExtT)); + if (!str.good()) + throw GeographicErr("Failure writing data"); + k -= n; + } + } + return; + } + + /** + * Write data in an array of type IntT as type ExtT to a binary stream. + * The data in the file is in (bigendp ? big : little)-endian format. + * + * @tparam ExtT the type of the objects in the binary stream (external). + * @tparam IntT the type of the objects in the array (internal). + * @tparam bigendp true if the external storage format is big-endian. + * @param[out] str the output stream for the data of type ExtT (external). + * @param[in] array the input vector of type IntT (internal). + * @exception GeographicErr if the data cannot be written. + **********************************************************************/ + template + static void writearray(std::ostream& str, std::vector& array) { + if (array.size() > 0) + writearray(str, &array[0], array.size()); + } + + /** + * Parse a KEY [=] VALUE line. + * + * @param[in] line the input line. + * @param[out] key the KEY. + * @param[out] value the VALUE. + * @param[in] delim delimiter to separate KEY and VALUE, if NULL use first + * space character. + * @exception std::bad_alloc if memory for the internal strings can't be + * allocated. + * @return whether a key was found. + * + * A "#" character and everything after it are discarded and the result + * trimmed of leading and trailing white space. Use the delimiter + * character (or, if it is NULL, the first white space) to separate \e key + * and \e value. \e key and \e value are trimmed of leading and trailing + * white space. If \e key is empty, then \e value is set to "" and false + * is returned. + **********************************************************************/ + static bool ParseLine(const std::string& line, + std::string& key, std::string& value, + char delim); + + /** + * Parse a KEY VALUE line. + * + * @param[in] line the input line. + * @param[out] key the KEY. + * @param[out] value the VALUE. + * @exception std::bad_alloc if memory for the internal strings can't be + * allocated. + * @return whether a key was found. + * + * \note This is a transition routine. At some point \e delim will be made + * an optional argument in the previous version of ParseLine and this + * version will be removed. + **********************************************************************/ + + static bool ParseLine(const std::string& line, + std::string& key, std::string& value); + + /** + * Set the binary precision of a real number. + * + * @param[in] ndigits the number of bits of precision. If ndigits is 0 + * (the default), then determine the precision from the environment + * variable GEOGRAPHICLIB_DIGITS. If this is undefined, use ndigits = + * 256 (i.e., about 77 decimal digits). + * @return the resulting number of bits of precision. + * + * This only has an effect when GEOGRAPHICLIB_PRECISION = 5. The + * precision should only be set once and before calls to any other + * GeographicLib functions. (Several functions, for example Math::pi(), + * cache the return value in a static local variable. The precision needs + * to be set before a call to any such functions.) In multi-threaded + * applications, it is necessary also to set the precision in each thread + * (see the example GeoidToGTX.cpp). + **********************************************************************/ + static int set_digits(int ndigits = 0); + + }; + + /** + * The specialization of Utility::val() for strings. + **********************************************************************/ + template<> inline std::string Utility::val(const std::string& s) + { return trim(s); } + + /** + * The specialization of Utility::val() for bools. + **********************************************************************/ + template<> inline bool Utility::val(const std::string& s) { + std::string t(trim(s)); + if (t.empty()) return false; + bool x; + { + std::istringstream is(t); + if (is >> x) { + int pos = int(is.tellg()); // Returns -1 at end of string? + if (!(pos < 0 || pos == int(t.size()))) + throw GeographicErr("Extra text " + t.substr(pos) + + " at end of " + t); + return x; + } + } + for (std::string::iterator p = t.begin(); p != t.end(); ++p) + *p = char(std::tolower(*p)); + switch (t[0]) { // already checked that t isn't empty + case 'f': + if (t == "f" || t == "false") return false; + break; + case 'n': + if (t == "n" || t == "nil" || t == "no") return false; + break; + case 'o': + if (t == "off") return false; + else if (t == "on") return true; + break; + case 't': + if (t == "t" || t == "true") return true; + break; + case 'y': + if (t == "y" || t == "yes") return true; + break; + } + throw GeographicErr("Cannot decode " + t + " as a bool"); + } + +} // namespace GeographicLib + +#if defined(_MSC_VER) +# pragma warning (pop) +#endif + +#endif // GEOGRAPHICLIB_UTILITY_HPP diff --git a/common/local_libs/GeographicLib/src/Accumulator.cpp b/common/local_libs/GeographicLib/src/Accumulator.cpp new file mode 100644 index 0000000000..d2b00b38ac --- /dev/null +++ b/common/local_libs/GeographicLib/src/Accumulator.cpp @@ -0,0 +1,23 @@ +/** + * \file Accumulator.cpp + * \brief Implementation for GeographicLib::Accumulator class + * + * Copyright (c) Charles Karney (2013) and licensed under + * the MIT/X11 License. For more information, see + * https://geographiclib.sourceforge.io/ + **********************************************************************/ + +#include + +namespace GeographicLib { + + /// \cond SKIP + + // Need to instantiate Accumulator to get the code into the shared library + // (without this, NETGeographic complains about not finding the == and != + // operators). + template class GEOGRAPHICLIB_EXPORT Accumulator; + + /// \endcond + +} // namespace GeographicLib diff --git a/common/local_libs/GeographicLib/src/AlbersEqualArea.cpp b/common/local_libs/GeographicLib/src/AlbersEqualArea.cpp new file mode 100644 index 0000000000..404d1908c2 --- /dev/null +++ b/common/local_libs/GeographicLib/src/AlbersEqualArea.cpp @@ -0,0 +1,445 @@ +/** + * \file AlbersEqualArea.cpp + * \brief Implementation for GeographicLib::AlbersEqualArea class + * + * Copyright (c) Charles Karney (2010-2020) and licensed + * under the MIT/X11 License. For more information, see + * https://geographiclib.sourceforge.io/ + **********************************************************************/ + +#include + +#if defined(_MSC_VER) +// Squelch warnings about constant conditional expressions +# pragma warning (disable: 4127) +#endif + +namespace GeographicLib { + + using namespace std; + + AlbersEqualArea::AlbersEqualArea(real a, real f, real stdlat, real k0) + : eps_(numeric_limits::epsilon()) + , epsx_(Math::sq(eps_)) + , epsx2_(Math::sq(epsx_)) + , tol_(sqrt(eps_)) + , tol0_(tol_ * sqrt(sqrt(eps_))) + , _a(a) + , _f(f) + , _fm(1 - _f) + , _e2(_f * (2 - _f)) + , _e(sqrt(abs(_e2))) + , _e2m(1 - _e2) + , _qZ(1 + _e2m * atanhee(real(1))) + , _qx(_qZ / ( 2 * _e2m )) + { + if (!(isfinite(_a) && _a > 0)) + throw GeographicErr("Equatorial radius is not positive"); + if (!(isfinite(_f) && _f < 1)) + throw GeographicErr("Polar semi-axis is not positive"); + if (!(isfinite(k0) && k0 > 0)) + throw GeographicErr("Scale is not positive"); + if (!(abs(stdlat) <= 90)) + throw GeographicErr("Standard latitude not in [-90d, 90d]"); + real sphi, cphi; + Math::sincosd(stdlat, sphi, cphi); + Init(sphi, cphi, sphi, cphi, k0); + } + + AlbersEqualArea::AlbersEqualArea(real a, real f, real stdlat1, real stdlat2, + real k1) + : eps_(numeric_limits::epsilon()) + , epsx_(Math::sq(eps_)) + , epsx2_(Math::sq(epsx_)) + , tol_(sqrt(eps_)) + , tol0_(tol_ * sqrt(sqrt(eps_))) + , _a(a) + , _f(f) + , _fm(1 - _f) + , _e2(_f * (2 - _f)) + , _e(sqrt(abs(_e2))) + , _e2m(1 - _e2) + , _qZ(1 + _e2m * atanhee(real(1))) + , _qx(_qZ / ( 2 * _e2m )) + { + if (!(isfinite(_a) && _a > 0)) + throw GeographicErr("Equatorial radius is not positive"); + if (!(isfinite(_f) && _f < 1)) + throw GeographicErr("Polar semi-axis is not positive"); + if (!(isfinite(k1) && k1 > 0)) + throw GeographicErr("Scale is not positive"); + if (!(abs(stdlat1) <= 90)) + throw GeographicErr("Standard latitude 1 not in [-90d, 90d]"); + if (!(abs(stdlat2) <= 90)) + throw GeographicErr("Standard latitude 2 not in [-90d, 90d]"); + real sphi1, cphi1, sphi2, cphi2; + Math::sincosd(stdlat1, sphi1, cphi1); + Math::sincosd(stdlat2, sphi2, cphi2); + Init(sphi1, cphi1, sphi2, cphi2, k1); + } + + AlbersEqualArea::AlbersEqualArea(real a, real f, + real sinlat1, real coslat1, + real sinlat2, real coslat2, + real k1) + : eps_(numeric_limits::epsilon()) + , epsx_(Math::sq(eps_)) + , epsx2_(Math::sq(epsx_)) + , tol_(sqrt(eps_)) + , tol0_(tol_ * sqrt(sqrt(eps_))) + , _a(a) + , _f(f) + , _fm(1 - _f) + , _e2(_f * (2 - _f)) + , _e(sqrt(abs(_e2))) + , _e2m(1 - _e2) + , _qZ(1 + _e2m * atanhee(real(1))) + , _qx(_qZ / ( 2 * _e2m )) + { + if (!(isfinite(_a) && _a > 0)) + throw GeographicErr("Equatorial radius is not positive"); + if (!(isfinite(_f) && _f < 1)) + throw GeographicErr("Polar semi-axis is not positive"); + if (!(isfinite(k1) && k1 > 0)) + throw GeographicErr("Scale is not positive"); + if (!(coslat1 >= 0)) + throw GeographicErr("Standard latitude 1 not in [-90d, 90d]"); + if (!(coslat2 >= 0)) + throw GeographicErr("Standard latitude 2 not in [-90d, 90d]"); + if (!(abs(sinlat1) <= 1 && coslat1 <= 1) || (coslat1 == 0 && sinlat1 == 0)) + throw GeographicErr("Bad sine/cosine of standard latitude 1"); + if (!(abs(sinlat2) <= 1 && coslat2 <= 1) || (coslat2 == 0 && sinlat2 == 0)) + throw GeographicErr("Bad sine/cosine of standard latitude 2"); + if (coslat1 == 0 && coslat2 == 0 && sinlat1 * sinlat2 <= 0) + throw GeographicErr + ("Standard latitudes cannot be opposite poles"); + Init(sinlat1, coslat1, sinlat2, coslat2, k1); + } + + void AlbersEqualArea::Init(real sphi1, real cphi1, + real sphi2, real cphi2, real k1) { + { + real r; + r = hypot(sphi1, cphi1); + sphi1 /= r; cphi1 /= r; + r = hypot(sphi2, cphi2); + sphi2 /= r; cphi2 /= r; + } + bool polar = (cphi1 == 0); + cphi1 = max(epsx_, cphi1); // Avoid singularities at poles + cphi2 = max(epsx_, cphi2); + // Determine hemisphere of tangent latitude + _sign = sphi1 + sphi2 >= 0 ? 1 : -1; + // Internally work with tangent latitude positive + sphi1 *= _sign; sphi2 *= _sign; + if (sphi1 > sphi2) { + swap(sphi1, sphi2); swap(cphi1, cphi2); // Make phi1 < phi2 + } + real + tphi1 = sphi1/cphi1, tphi2 = sphi2/cphi2; + + // q = (1-e^2)*(sphi/(1-e^2*sphi^2) - atanhee(sphi)) + // qZ = q(pi/2) = (1 + (1-e^2)*atanhee(1)) + // atanhee(x) = atanh(e*x)/e + // q = sxi * qZ + // dq/dphi = 2*(1-e^2)*cphi/(1-e^2*sphi^2)^2 + // + // n = (m1^2-m2^2)/(q2-q1) -> sin(phi0) for phi1, phi2 -> phi0 + // C = m1^2 + n*q1 = (m1^2*q2-m2^2*q1)/(q2-q1) + // let + // rho(pi/2)/rho(-pi/2) = (1-s)/(1+s) + // s = n*qZ/C + // = qZ * (m1^2-m2^2)/(m1^2*q2-m2^2*q1) + // = qZ * (scbet2^2 - scbet1^2)/(scbet2^2*q2 - scbet1^2*q1) + // = (scbet2^2 - scbet1^2)/(scbet2^2*sxi2 - scbet1^2*sxi1) + // = (tbet2^2 - tbet1^2)/(scbet2^2*sxi2 - scbet1^2*sxi1) + // 1-s = -((1-sxi2)*scbet2^2 - (1-sxi1)*scbet1^2)/ + // (scbet2^2*sxi2 - scbet1^2*sxi1) + // + // Define phi0 to give same value of s, i.e., + // s = sphi0 * qZ / (m0^2 + sphi0*q0) + // = sphi0 * scbet0^2 / (1/qZ + sphi0 * scbet0^2 * sxi0) + + real tphi0, C; + if (polar || tphi1 == tphi2) { + tphi0 = tphi2; + C = 1; // ignored + } else { + real + tbet1 = _fm * tphi1, scbet12 = 1 + Math::sq(tbet1), + tbet2 = _fm * tphi2, scbet22 = 1 + Math::sq(tbet2), + txi1 = txif(tphi1), cxi1 = 1/hyp(txi1), sxi1 = txi1 * cxi1, + txi2 = txif(tphi2), cxi2 = 1/hyp(txi2), sxi2 = txi2 * cxi2, + dtbet2 = _fm * (tbet1 + tbet2), + es1 = 1 - _e2 * Math::sq(sphi1), es2 = 1 - _e2 * Math::sq(sphi2), + /* + dsxi = ( (_e2 * sq(sphi2 + sphi1) + es2 + es1) / (2 * es2 * es1) + + Datanhee(sphi2, sphi1) ) * Dsn(tphi2, tphi1, sphi2, sphi1) / + ( 2 * _qx ), + */ + dsxi = ( (1 + _e2 * sphi1 * sphi2) / (es2 * es1) + + Datanhee(sphi2, sphi1) ) * Dsn(tphi2, tphi1, sphi2, sphi1) / + ( 2 * _qx ), + den = (sxi2 + sxi1) * dtbet2 + (scbet22 + scbet12) * dsxi, + // s = (sq(tbet2) - sq(tbet1)) / (scbet22*sxi2 - scbet12*sxi1) + s = 2 * dtbet2 / den, + // 1-s = -(sq(scbet2)*(1-sxi2) - sq(scbet1)*(1-sxi1)) / + // (scbet22*sxi2 - scbet12*sxi1) + // Write + // sq(scbet)*(1-sxi) = sq(scbet)*(1-sphi) * (1-sxi)/(1-sphi) + sm1 = -Dsn(tphi2, tphi1, sphi2, sphi1) * + ( -( ((sphi2 <= 0 ? (1 - sxi2) / (1 - sphi2) : + Math::sq(cxi2/cphi2) * (1 + sphi2) / (1 + sxi2)) + + (sphi1 <= 0 ? (1 - sxi1) / (1 - sphi1) : + Math::sq(cxi1/cphi1) * (1 + sphi1) / (1 + sxi1))) ) * + (1 + _e2 * (sphi1 + sphi2 + sphi1 * sphi2)) / + (1 + (sphi1 + sphi2 + sphi1 * sphi2)) + + (scbet22 * (sphi2 <= 0 ? 1 - sphi2 : + Math::sq(cphi2) / ( 1 + sphi2)) + + scbet12 * (sphi1 <= 0 ? 1 - sphi1 : Math::sq(cphi1) / ( 1 + sphi1))) + * (_e2 * (1 + sphi1 + sphi2 + _e2 * sphi1 * sphi2)/(es1 * es2) + +_e2m * DDatanhee(sphi1, sphi2) ) / _qZ ) / den; + // C = (scbet22*sxi2 - scbet12*sxi1) / (scbet22 * scbet12 * (sx2 - sx1)) + C = den / (2 * scbet12 * scbet22 * dsxi); + tphi0 = (tphi2 + tphi1)/2; + real stol = tol0_ * max(real(1), abs(tphi0)); + for (int i = 0; i < 2*numit0_ || GEOGRAPHICLIB_PANIC; ++i) { + // Solve (scbet0^2 * sphi0) / (1/qZ + scbet0^2 * sphi0 * sxi0) = s + // for tphi0 by Newton's method on + // v(tphi0) = (scbet0^2 * sphi0) - s * (1/qZ + scbet0^2 * sphi0 * sxi0) + // = 0 + // Alt: + // (scbet0^2 * sphi0) / (1/qZ - scbet0^2 * sphi0 * (1-sxi0)) + // = s / (1-s) + // w(tphi0) = (1-s) * (scbet0^2 * sphi0) + // - s * (1/qZ - scbet0^2 * sphi0 * (1-sxi0)) + // = (1-s) * (scbet0^2 * sphi0) + // - S/qZ * (1 - scbet0^2 * sphi0 * (qZ-q0)) + // Now + // qZ-q0 = (1+e2*sphi0)*(1-sphi0)/(1-e2*sphi0^2) + + // (1-e2)*atanhee((1-sphi0)/(1-e2*sphi0)) + // In limit sphi0 -> 1, qZ-q0 -> 2*(1-sphi0)/(1-e2), so wrte + // qZ-q0 = 2*(1-sphi0)/(1-e2) + A + B + // A = (1-sphi0)*( (1+e2*sphi0)/(1-e2*sphi0^2) - (1+e2)/(1-e2) ) + // = -e2 *(1-sphi0)^2 * (2+(1+e2)*sphi0) / ((1-e2)*(1-e2*sphi0^2)) + // B = (1-e2)*atanhee((1-sphi0)/(1-e2*sphi0)) - (1-sphi0) + // = (1-sphi0)*(1-e2)/(1-e2*sphi0)* + // ((atanhee(x)/x-1) - e2*(1-sphi0)/(1-e2)) + // x = (1-sphi0)/(1-e2*sphi0), atanhee(x)/x = atanh(e*x)/(e*x) + // + // 1 - scbet0^2 * sphi0 * (qZ-q0) + // = 1 - scbet0^2 * sphi0 * (2*(1-sphi0)/(1-e2) + A + B) + // = D - scbet0^2 * sphi0 * (A + B) + // D = 1 - scbet0^2 * sphi0 * 2*(1-sphi0)/(1-e2) + // = (1-sphi0)*(1-e2*(1+2*sphi0*(1+sphi0)))/((1-e2)*(1+sphi0)) + // dD/dsphi0 = -2*(1-e2*sphi0^2*(2*sphi0+3))/((1-e2)*(1+sphi0)^2) + // d(A+B)/dsphi0 = 2*(1-sphi0^2)*e2*(2-e2*(1+sphi0^2))/ + // ((1-e2)*(1-e2*sphi0^2)^2) + + real + scphi02 = 1 + Math::sq(tphi0), scphi0 = sqrt(scphi02), + // sphi0m = 1-sin(phi0) = 1/( sec(phi0) * (tan(phi0) + sec(phi0)) ) + sphi0 = tphi0 / scphi0, sphi0m = 1/(scphi0 * (tphi0 + scphi0)), + // scbet0^2 * sphi0 + g = (1 + Math::sq( _fm * tphi0 )) * sphi0, + // dg/dsphi0 = dg/dtphi0 * scphi0^3 + dg = _e2m * scphi02 * (1 + 2 * Math::sq(tphi0)) + _e2, + D = sphi0m * (1 - _e2*(1 + 2*sphi0*(1+sphi0))) / (_e2m * (1+sphi0)), + // dD/dsphi0 + dD = -2 * (1 - _e2*Math::sq(sphi0) * (2*sphi0+3)) / + (_e2m * Math::sq(1+sphi0)), + A = -_e2 * Math::sq(sphi0m) * (2+(1+_e2)*sphi0) / + (_e2m*(1-_e2*Math::sq(sphi0))), + B = (sphi0m * _e2m / (1 - _e2*sphi0) * + (atanhxm1(_e2 * + Math::sq(sphi0m / (1-_e2*sphi0))) - _e2*sphi0m/_e2m)), + // d(A+B)/dsphi0 + dAB = (2 * _e2 * (2 - _e2 * (1 + Math::sq(sphi0))) / + (_e2m * Math::sq(1 - _e2*Math::sq(sphi0)) * scphi02)), + u = sm1 * g - s/_qZ * ( D - g * (A + B) ), + // du/dsphi0 + du = sm1 * dg - s/_qZ * (dD - dg * (A + B) - g * dAB), + dtu = -u/du * (scphi0 * scphi02); + tphi0 += dtu; + if (!(abs(dtu) >= stol)) + break; + } + } + _txi0 = txif(tphi0); _scxi0 = hyp(_txi0); _sxi0 = _txi0 / _scxi0; + _n0 = tphi0/hyp(tphi0); + _m02 = 1 / (1 + Math::sq(_fm * tphi0)); + _nrho0 = polar ? 0 : _a * sqrt(_m02); + _k0 = sqrt(tphi1 == tphi2 ? 1 : C / (_m02 + _n0 * _qZ * _sxi0)) * k1; + _k2 = Math::sq(_k0); + _lat0 = _sign * atan(tphi0)/Math::degree(); + } + + const AlbersEqualArea& AlbersEqualArea::CylindricalEqualArea() { + static const AlbersEqualArea + cylindricalequalarea(Constants::WGS84_a(), Constants::WGS84_f(), + real(0), real(1), real(0), real(1), real(1)); + return cylindricalequalarea; + } + + const AlbersEqualArea& AlbersEqualArea::AzimuthalEqualAreaNorth() { + static const AlbersEqualArea + azimuthalequalareanorth(Constants::WGS84_a(), Constants::WGS84_f(), + real(1), real(0), real(1), real(0), real(1)); + return azimuthalequalareanorth; + } + + const AlbersEqualArea& AlbersEqualArea::AzimuthalEqualAreaSouth() { + static const AlbersEqualArea + azimuthalequalareasouth(Constants::WGS84_a(), Constants::WGS84_f(), + real(-1), real(0), real(-1), real(0), real(1)); + return azimuthalequalareasouth; + } + + Math::real AlbersEqualArea::txif(real tphi) const { + // sxi = ( sphi/(1-e2*sphi^2) + atanhee(sphi) ) / + // ( 1/(1-e2) + atanhee(1) ) + // + // txi = ( sphi/(1-e2*sphi^2) + atanhee(sphi) ) / + // sqrt( ( (1+e2*sphi)*(1-sphi)/( (1-e2*sphi^2) * (1-e2) ) + + // atanhee((1-sphi)/(1-e2*sphi)) ) * + // ( (1-e2*sphi)*(1+sphi)/( (1-e2*sphi^2) * (1-e2) ) + + // atanhee((1+sphi)/(1+e2*sphi)) ) ) + // + // subst 1-sphi = cphi^2/(1+sphi) + int s = tphi < 0 ? -1 : 1; // Enforce odd parity + tphi *= s; + real + cphi2 = 1 / (1 + Math::sq(tphi)), + sphi = tphi * sqrt(cphi2), + es1 = _e2 * sphi, + es2m1 = 1 - es1 * sphi, + sp1 = 1 + sphi, + es1m1 = (1 - es1) * sp1, + es2m1a = _e2m * es2m1, + es1p1 = sp1 / (1 + es1); + return s * ( sphi / es2m1 + atanhee(sphi) ) / + sqrt( ( cphi2 / (es1p1 * es2m1a) + atanhee(cphi2 / es1m1) ) * + ( es1m1 / es2m1a + atanhee(es1p1) ) ); + } + + Math::real AlbersEqualArea::tphif(real txi) const { + real + tphi = txi, + stol = tol_ * max(real(1), abs(txi)); + // CHECK: min iterations = 1, max iterations = 2; mean = 1.99 + for (int i = 0; i < numit_ || GEOGRAPHICLIB_PANIC; ++i) { + // dtxi/dtphi = (scxi/scphi)^3 * 2*(1-e^2)/(qZ*(1-e^2*sphi^2)^2) + real + txia = txif(tphi), + tphi2 = Math::sq(tphi), + scphi2 = 1 + tphi2, + scterm = scphi2/(1 + Math::sq(txia)), + dtphi = (txi - txia) * scterm * sqrt(scterm) * + _qx * Math::sq(1 - _e2 * tphi2 / scphi2); + tphi += dtphi; + if (!(abs(dtphi) >= stol)) + break; + } + return tphi; + } + + // return atanh(sqrt(x))/sqrt(x) - 1 = y/3 + y^2/5 + y^3/7 + ... + // typical x < e^2 = 2*f + Math::real AlbersEqualArea::atanhxm1(real x) { + real s = 0; + if (abs(x) < real(0.5)) { + real os = -1, y = 1, k = 1; + while (os != s) { + os = s; + y *= x; // y = x^n + k += 2; // k = 2*n + 1 + s += y/k; // sum( x^n/(2*n + 1) ) + } + } else { + real xs = sqrt(abs(x)); + s = (x > 0 ? atanh(xs) : atan(xs)) / xs - 1; + } + return s; + } + + // return (Datanhee(1,y) - Datanhee(1,x))/(y-x) + Math::real AlbersEqualArea::DDatanhee(real x, real y) const { + real s = 0; + if (_e2 * (abs(x) + abs(y)) < real(0.5)) { + real os = -1, z = 1, k = 1, t = 0, c = 0, en = 1; + while (os != s) { + os = s; + t = y * t + z; c += t; z *= x; + t = y * t + z; c += t; z *= x; + k += 2; en *= _e2; + // Here en[l] = e2^l, k[l] = 2*l + 1, + // c[l] = sum( x^i * y^j; i >= 0, j >= 0, i+j < 2*l) + s += en * c / k; + } + // Taylor expansion is + // s = sum( c[l] * e2^l / (2*l + 1), l, 1, N) + } else + s = (Datanhee(1, y) - Datanhee(x, y))/(1 - x); + return s; + } + + void AlbersEqualArea::Forward(real lon0, real lat, real lon, + real& x, real& y, real& gamma, real& k) const { + lon = Math::AngDiff(lon0, lon); + lat *= _sign; + real sphi, cphi; + Math::sincosd(Math::LatFix(lat) * _sign, sphi, cphi); + cphi = max(epsx_, cphi); + real + lam = lon * Math::degree(), + tphi = sphi/cphi, txi = txif(tphi), sxi = txi/hyp(txi), + dq = _qZ * Dsn(txi, _txi0, sxi, _sxi0) * (txi - _txi0), + drho = - _a * dq / (sqrt(_m02 - _n0 * dq) + _nrho0 / _a), + theta = _k2 * _n0 * lam, stheta = sin(theta), ctheta = cos(theta), + t = _nrho0 + _n0 * drho; + x = t * (_n0 != 0 ? stheta / _n0 : _k2 * lam) / _k0; + y = (_nrho0 * + (_n0 != 0 ? + (ctheta < 0 ? 1 - ctheta : Math::sq(stheta)/(1 + ctheta)) / _n0 : + 0) + - drho * ctheta) / _k0; + k = _k0 * (t != 0 ? t * hyp(_fm * tphi) / _a : 1); + y *= _sign; + gamma = _sign * theta / Math::degree(); + } + + void AlbersEqualArea::Reverse(real lon0, real x, real y, + real& lat, real& lon, + real& gamma, real& k) const { + y *= _sign; + real + nx = _k0 * _n0 * x, ny = _k0 * _n0 * y, y1 = _nrho0 - ny, + den = hypot(nx, y1) + _nrho0, // 0 implies origin with polar aspect + drho = den != 0 ? (_k0*x*nx - 2*_k0*y*_nrho0 + _k0*y*ny) / den : 0, + // dsxia = scxi0 * dsxi + dsxia = - _scxi0 * (2 * _nrho0 + _n0 * drho) * drho / + (Math::sq(_a) * _qZ), + txi = (_txi0 + dsxia) / sqrt(max(1 - dsxia * (2*_txi0 + dsxia), epsx2_)), + tphi = tphif(txi), + theta = atan2(nx, y1), + lam = _n0 != 0 ? theta / (_k2 * _n0) : x / (y1 * _k0); + gamma = _sign * theta / Math::degree(); + lat = Math::atand(_sign * tphi); + lon = lam / Math::degree(); + lon = Math::AngNormalize(lon + Math::AngNormalize(lon0)); + k = _k0 * (den != 0 ? (_nrho0 + _n0 * drho) * hyp(_fm * tphi) / _a : 1); + } + + void AlbersEqualArea::SetScale(real lat, real k) { + if (!(isfinite(k) && k > 0)) + throw GeographicErr("Scale is not positive"); + if (!(abs(lat) < 90)) + throw GeographicErr("Latitude for SetScale not in (-90d, 90d)"); + real x, y, gamma, kold; + Forward(0, lat, 0, x, y, gamma, kold); + k /= kold; + _k0 *= k; + _k2 = Math::sq(_k0); + } + +} // namespace GeographicLib diff --git a/common/local_libs/GeographicLib/src/AzimuthalEquidistant.cpp b/common/local_libs/GeographicLib/src/AzimuthalEquidistant.cpp new file mode 100644 index 0000000000..3eba8e7be2 --- /dev/null +++ b/common/local_libs/GeographicLib/src/AzimuthalEquidistant.cpp @@ -0,0 +1,41 @@ +/** + * \file AzimuthalEquidistant.cpp + * \brief Implementation for GeographicLib::AzimuthalEquidistant class + * + * Copyright (c) Charles Karney (2009-2020) and licensed + * under the MIT/X11 License. For more information, see + * https://geographiclib.sourceforge.io/ + **********************************************************************/ + +#include + +namespace GeographicLib { + + using namespace std; + + AzimuthalEquidistant::AzimuthalEquidistant(const Geodesic& earth) + : eps_(real(0.01) * sqrt(numeric_limits::min())) + , _earth(earth) {} + + void AzimuthalEquidistant::Forward(real lat0, real lon0, real lat, real lon, + real& x, real& y, + real& azi, real& rk) const { + real sig, s, azi0, m; + sig = _earth.Inverse(lat0, lon0, lat, lon, s, azi0, azi, m); + Math::sincosd(azi0, x, y); + x *= s; y *= s; + rk = !(sig <= eps_) ? m / s : 1; + } + + void AzimuthalEquidistant::Reverse(real lat0, real lon0, real x, real y, + real& lat, real& lon, + real& azi, real& rk) const { + real + azi0 = Math::atan2d(x, y), + s = hypot(x, y); + real sig, m; + sig = _earth.Direct(lat0, lon0, azi0, s, lat, lon, azi, m); + rk = !(sig <= eps_) ? m / s : 1; + } + +} // namespace GeographicLib diff --git a/common/local_libs/GeographicLib/src/CassiniSoldner.cpp b/common/local_libs/GeographicLib/src/CassiniSoldner.cpp new file mode 100644 index 0000000000..e53b5d23d0 --- /dev/null +++ b/common/local_libs/GeographicLib/src/CassiniSoldner.cpp @@ -0,0 +1,89 @@ +/** + * \file CassiniSoldner.cpp + * \brief Implementation for GeographicLib::CassiniSoldner class + * + * Copyright (c) Charles Karney (2009-2015) and licensed + * under the MIT/X11 License. For more information, see + * https://geographiclib.sourceforge.io/ + **********************************************************************/ + +#include + +namespace GeographicLib { + + using namespace std; + + CassiniSoldner::CassiniSoldner(const Geodesic& earth) + : _earth(earth) {} + + CassiniSoldner::CassiniSoldner(real lat0, real lon0, const Geodesic& earth) + : _earth(earth) + { Reset(lat0, lon0); } + + void CassiniSoldner::Reset(real lat0, real lon0) { + _meridian = _earth.Line(lat0, lon0, real(0), + Geodesic::LATITUDE | Geodesic::LONGITUDE | + Geodesic::DISTANCE | Geodesic::DISTANCE_IN | + Geodesic::AZIMUTH); + real f = _earth.Flattening(); + Math::sincosd(LatitudeOrigin(), _sbet0, _cbet0); + _sbet0 *= (1 - f); + Math::norm(_sbet0, _cbet0); + } + + void CassiniSoldner::Forward(real lat, real lon, real& x, real& y, + real& azi, real& rk) const { + if (!Init()) + return; + real dlon = Math::AngDiff(LongitudeOrigin(), lon); + real sig12, s12, azi1, azi2; + sig12 = _earth.Inverse(lat, -abs(dlon), lat, abs(dlon), s12, azi1, azi2); + sig12 *= real(0.5); + s12 *= real(0.5); + if (s12 == 0) { + real da = Math::AngDiff(azi1, azi2)/2; + if (abs(dlon) <= 90) { + azi1 = 90 - da; + azi2 = 90 + da; + } else { + azi1 = -90 - da; + azi2 = -90 + da; + } + } + if (dlon < 0) { + azi2 = azi1; + s12 = -s12; + sig12 = -sig12; + } + x = s12; + azi = Math::AngNormalize(azi2); + GeodesicLine perp(_earth.Line(lat, dlon, azi, Geodesic::GEODESICSCALE)); + real t; + perp.GenPosition(true, -sig12, + Geodesic::GEODESICSCALE, + t, t, t, t, t, t, rk, t); + + real salp0, calp0; + Math::sincosd(perp.EquatorialAzimuth(), salp0, calp0); + real + sbet1 = lat >=0 ? calp0 : -calp0, + cbet1 = abs(dlon) <= 90 ? abs(salp0) : -abs(salp0), + sbet01 = sbet1 * _cbet0 - cbet1 * _sbet0, + cbet01 = cbet1 * _cbet0 + sbet1 * _sbet0, + sig01 = atan2(sbet01, cbet01) / Math::degree(); + _meridian.GenPosition(true, sig01, + Geodesic::DISTANCE, + t, t, t, y, t, t, t, t); + } + + void CassiniSoldner::Reverse(real x, real y, real& lat, real& lon, + real& azi, real& rk) const { + if (!Init()) + return; + real lat1, lon1; + real azi0, t; + _meridian.Position(y, lat1, lon1, azi0); + _earth.Direct(lat1, lon1, azi0 + 90, x, lat, lon, azi, rk, t); + } + +} // namespace GeographicLib diff --git a/common/local_libs/GeographicLib/src/CircularEngine.cpp b/common/local_libs/GeographicLib/src/CircularEngine.cpp new file mode 100644 index 0000000000..811b650bf2 --- /dev/null +++ b/common/local_libs/GeographicLib/src/CircularEngine.cpp @@ -0,0 +1,96 @@ +/** + * \file CircularEngine.cpp + * \brief Implementation for GeographicLib::CircularEngine class + * + * Copyright (c) Charles Karney (2011) and licensed under + * the MIT/X11 License. For more information, see + * https://geographiclib.sourceforge.io/ + **********************************************************************/ + +#include + +namespace GeographicLib { + + using namespace std; + + Math::real CircularEngine::Value(bool gradp, real sl, real cl, + real& gradx, real& grady, real& gradz) const + { + gradp = _gradp && gradp; + const vector& root( SphericalEngine::sqrttable() ); + + // Initialize outer sum + real vc = 0, vc2 = 0, vs = 0, vs2 = 0; // v [N + 1], v [N + 2] + // vr, vt, vl and similar w variable accumulate the sums for the + // derivatives wrt r, theta, and lambda, respectively. + real vrc = 0, vrc2 = 0, vrs = 0, vrs2 = 0; // vr[N + 1], vr[N + 2] + real vtc = 0, vtc2 = 0, vts = 0, vts2 = 0; // vt[N + 1], vt[N + 2] + real vlc = 0, vlc2 = 0, vls = 0, vls2 = 0; // vl[N + 1], vl[N + 2] + for (int m = _M; m >= 0; --m) { // m = M .. 0 + // Now Sc[m] = wc, Ss[m] = ws + // Sc'[m] = wtc, Ss'[m] = wtc + if (m) { + real v, A, B; // alpha[m], beta[m + 1] + switch (_norm) { + case FULL: + v = root[2] * root[2 * m + 3] / root[m + 1]; + A = cl * v * _uq; + B = - v * root[2 * m + 5] / (root[8] * root[m + 2]) * _uq2; + break; + case SCHMIDT: + v = root[2] * root[2 * m + 1] / root[m + 1]; + A = cl * v * _uq; + B = - v * root[2 * m + 3] / (root[8] * root[m + 2]) * _uq2; + break; + default: + A = B = 0; + } + v = A * vc + B * vc2 + _wc[m] ; vc2 = vc ; vc = v; + v = A * vs + B * vs2 + _ws[m] ; vs2 = vs ; vs = v; + if (gradp) { + v = A * vrc + B * vrc2 + _wrc[m]; vrc2 = vrc; vrc = v; + v = A * vrs + B * vrs2 + _wrs[m]; vrs2 = vrs; vrs = v; + v = A * vtc + B * vtc2 + _wtc[m]; vtc2 = vtc; vtc = v; + v = A * vts + B * vts2 + _wts[m]; vts2 = vts; vts = v; + v = A * vlc + B * vlc2 + m*_ws[m]; vlc2 = vlc; vlc = v; + v = A * vls + B * vls2 - m*_wc[m]; vls2 = vls; vls = v; + } + } else { + real A, B, qs; + switch (_norm) { + case FULL: + A = root[3] * _uq; // F[1]/(q*cl) or F[1]/(q*sl) + B = - root[15]/2 * _uq2; // beta[1]/q + break; + case SCHMIDT: + A = _uq; + B = - root[3]/2 * _uq2; + break; + default: + A = B = 0; + } + qs = _q / SphericalEngine::scale(); + vc = qs * (_wc[m] + A * (cl * vc + sl * vs ) + B * vc2); + if (gradp) { + qs /= _r; + // The components of the gradient in circular coordinates are + // r: dV/dr + // theta: 1/r * dV/dtheta + // lambda: 1/(r*u) * dV/dlambda + vrc = - qs * (_wrc[m] + A * (cl * vrc + sl * vrs) + B * vrc2); + vtc = qs * (_wtc[m] + A * (cl * vtc + sl * vts) + B * vtc2); + vlc = qs / _u * ( A * (cl * vlc + sl * vls) + B * vlc2); + } + } + } + + if (gradp) { + // Rotate into cartesian (geocentric) coordinates + gradx = cl * (_u * vrc + _t * vtc) - sl * vlc; + grady = sl * (_u * vrc + _t * vtc) + cl * vlc; + gradz = _t * vrc - _u * vtc ; + } + return vc; + } + +} // namespace GeographicLib diff --git a/common/local_libs/GeographicLib/src/DMS.cpp b/common/local_libs/GeographicLib/src/DMS.cpp new file mode 100644 index 0000000000..93792e8f9e --- /dev/null +++ b/common/local_libs/GeographicLib/src/DMS.cpp @@ -0,0 +1,487 @@ +/** + * \file DMS.cpp + * \brief Implementation for GeographicLib::DMS class + * + * Copyright (c) Charles Karney (2008-2020) and licensed + * under the MIT/X11 License. For more information, see + * https://geographiclib.sourceforge.io/ + **********************************************************************/ + +#include +#include + +#if defined(_MSC_VER) +// Squelch warnings about constant conditional expressions +# pragma warning (disable: 4127) +#endif + +namespace GeographicLib { + + using namespace std; + + const char* const DMS::hemispheres_ = "SNWE"; + const char* const DMS::signs_ = "-+"; + const char* const DMS::digits_ = "0123456789"; + const char* const DMS::dmsindicators_ = "D'\":"; + const char* const DMS::components_[] = {"degrees", "minutes", "seconds"}; + + Math::real DMS::Decode(const std::string& dms, flag& ind) { + // Here's a table of the allowed characters + + // S unicode dec UTF-8 descripton + + // DEGREE + // d U+0064 100 64 d + // D U+0044 68 44 D + // ° U+00b0 176 c2 b0 degree symbol + // º U+00ba 186 c2 ba alt symbol + // ⁰ U+2070 8304 e2 81 b0 sup zero + // ˚ U+02da 730 cb 9a ring above + // ∘ U+2218 8728 e2 88 98 compose function + // * U+002a 42 2a GRiD symbol for degrees + + // MINUTES + // ' U+0027 39 27 apostrophe + // ` U+0060 96 60 grave accent + // ′ U+2032 8242 e2 80 b2 prime + // ‵ U+2035 8245 e2 80 b5 back prime + // ´ U+00b4 180 c2 b4 acute accent + // ‘ U+2018 8216 e2 80 98 left single quote (also ext ASCII 0x91) + // ’ U+2019 8217 e2 80 99 right single quote (also ext ASCII 0x92) + // ‛ U+201b 8219 e2 80 9b reversed-9 single quote + // ʹ U+02b9 697 ca b9 modifier letter prime + // ˊ U+02ca 714 cb 8a modifier letter acute accent + // ˋ U+02cb 715 cb 8b modifier letter grave accent + + // SECONDS + // " U+0022 34 22 quotation mark + // ″ U+2033 8243 e2 80 b3 double prime + // ‶ U+2036 8246 e2 80 b6 reversed double prime + // ˝ U+02dd 733 cb 9d double acute accent + // “ U+201c 8220 e2 80 9c left double quote (also ext ASCII 0x93) + // ” U+201d 8221 e2 80 9d right double quote (also ext ASCII 0x94) + // ‟ U+201f 8223 e2 80 9f reversed-9 double quote + // ʺ U+02ba 698 ca ba modifier letter double prime + + // PLUS + // + U+002b 43 2b plus sign + // ➕ U+2795 10133 e2 9e 95 heavy plus + // U+2064 8292 e2 81 a4 invisible plus |⁤| + + // MINUS + // - U+002d 45 2d hyphen + // ‐ U+2010 8208 e2 80 90 dash + // ‑ U+2011 8209 e2 80 91 non-breaking hyphen + // – U+2013 8211 e2 80 93 en dash (also ext ASCII 0x96) + // — U+2014 8212 e2 80 94 em dash (also ext ASCII 0x97) + // − U+2212 8722 e2 88 92 minus sign + // ➖ U+2796 10134 e2 9e 96 heavy minus + + // IGNORED + //   U+00a0 160 c2 a0 non-breaking space + // U+2007 8199 e2 80 87 figure space | | + // U+2009 8201 e2 80 89 thin space | | + // U+200a 8202 e2 80 8a hair space | | + // U+200b 8203 e2 80 8b invisible space |​| + //   U+202f 8239 e2 80 af narrow space | | + // U+2063 8291 e2 81 a3 invisible separator |⁣| + // « U+00ab 171 c2 ab left guillemot (for cgi-bin) + // » U+00bb 187 c2 bb right guillemot (for cgi-bin) + + string dmsa = dms; + replace(dmsa, "\xc2\xb0", 'd' ); // U+00b0 degree symbol + replace(dmsa, "\xc2\xba", 'd' ); // U+00ba alt symbol + replace(dmsa, "\xe2\x81\xb0", 'd' ); // U+2070 sup zero + replace(dmsa, "\xcb\x9a", 'd' ); // U+02da ring above + replace(dmsa, "\xe2\x88\x98", 'd' ); // U+2218 compose function + + replace(dmsa, "\xe2\x80\xb2", '\''); // U+2032 prime + replace(dmsa, "\xe2\x80\xb5", '\''); // U+2035 back prime + replace(dmsa, "\xc2\xb4", '\''); // U+00b4 acute accent + replace(dmsa, "\xe2\x80\x98", '\''); // U+2018 left single quote + replace(dmsa, "\xe2\x80\x99", '\''); // U+2019 right single quote + replace(dmsa, "\xe2\x80\x9b", '\''); // U+201b reversed-9 single quote + replace(dmsa, "\xca\xb9", '\''); // U+02b9 modifier letter prime + replace(dmsa, "\xcb\x8a", '\''); // U+02ca modifier letter acute accent + replace(dmsa, "\xcb\x8b", '\''); // U+02cb modifier letter grave accent + + replace(dmsa, "\xe2\x80\xb3", '"' ); // U+2033 double prime + replace(dmsa, "\xe2\x80\xb6", '"' ); // U+2036 reversed double prime + replace(dmsa, "\xcb\x9d", '"' ); // U+02dd double acute accent + replace(dmsa, "\xe2\x80\x9c", '"' ); // U+201c left double quote + replace(dmsa, "\xe2\x80\x9d", '"' ); // U+201d right double quote + replace(dmsa, "\xe2\x80\x9f", '"' ); // U+201f reversed-9 double quote + replace(dmsa, "\xca\xba", '"' ); // U+02ba modifier letter double prime + + replace(dmsa, "\xe2\x9e\x95", '+' ); // U+2795 heavy plus + replace(dmsa, "\xe2\x81\xa4", '+' ); // U+2064 invisible plus + + replace(dmsa, "\xe2\x80\x90", '-' ); // U+2010 dash + replace(dmsa, "\xe2\x80\x91", '-' ); // U+2011 non-breaking hyphen + replace(dmsa, "\xe2\x80\x93", '-' ); // U+2013 en dash + replace(dmsa, "\xe2\x80\x94", '-' ); // U+2014 em dash + replace(dmsa, "\xe2\x88\x92", '-' ); // U+2212 minus sign + replace(dmsa, "\xe2\x9e\x96", '-' ); // U+2796 heavy minus + + replace(dmsa, "\xc2\xa0", '\0'); // U+00a0 non-breaking space + replace(dmsa, "\xe2\x80\x87", '\0'); // U+2007 figure space + replace(dmsa, "\xe2\x80\x89", '\0'); // U+2007 thin space + replace(dmsa, "\xe2\x80\x8a", '\0'); // U+200a hair space + replace(dmsa, "\xe2\x80\x8b", '\0'); // U+200b invisible space + replace(dmsa, "\xe2\x80\xaf", '\0'); // U+202f narrow space + replace(dmsa, "\xe2\x81\xa3", '\0'); // U+2063 invisible separator + + replace(dmsa, "\xb0", 'd' ); // 0xb0 bare degree symbol + replace(dmsa, "\xba", 'd' ); // 0xba bare alt symbol + replace(dmsa, "*", 'd' ); // GRiD symbol for degree + replace(dmsa, "`", '\''); // grave accent + replace(dmsa, "\xb4", '\''); // 0xb4 bare acute accent + // Don't implement these alternatives; they are only relevant for cgi-bin + // replace(dmsa, "\x91", '\''); // 0x91 ext ASCII left single quote + // replace(dmsa, "\x92", '\''); // 0x92 ext ASCII right single quote + // replace(dmsa, "\x93", '"' ); // 0x93 ext ASCII left double quote + // replace(dmsa, "\x94", '"' ); // 0x94 ext ASCII right double quote + // replace(dmsa, "\x96", '-' ); // 0x96 ext ASCII en dash + // replace(dmsa, "\x97", '-' ); // 0x97 ext ASCII em dash + replace(dmsa, "\xa0", '\0'); // 0xa0 bare non-breaking space + replace(dmsa, "''", '"' ); // '' -> " + string::size_type + beg = 0, + end = unsigned(dmsa.size()); + while (beg < end && isspace(dmsa[beg])) + ++beg; + while (beg < end && isspace(dmsa[end - 1])) + --end; + // The trimmed string in [beg, end) + real v = 0; + int i = 0; + flag ind1 = NONE; + // p is pointer to the next piece that needs decoding + for (string::size_type p = beg, pb; p < end; p = pb, ++i) { + string::size_type pa = p; + // Skip over initial hemisphere letter (for i == 0) + if (i == 0 && Utility::lookup(hemispheres_, dmsa[pa]) >= 0) + ++pa; + // Skip over initial sign (checking for it if i == 0) + if (i > 0 || (pa < end && Utility::lookup(signs_, dmsa[pa]) >= 0)) + ++pa; + // Find next sign + pb = min(dmsa.find_first_of(signs_, pa), end); + flag ind2 = NONE; + v += InternalDecode(dmsa.substr(p, pb - p), ind2); + if (ind1 == NONE) + ind1 = ind2; + else if (!(ind2 == NONE || ind1 == ind2)) + throw GeographicErr("Incompatible hemisphere specifier in " + + dmsa.substr(beg, pb - beg)); + } + if (i == 0) + throw GeographicErr("Empty or incomplete DMS string " + + dmsa.substr(beg, end - beg)); + ind = ind1; + return v; + } + + Math::real DMS::InternalDecode(const string& dmsa, flag& ind) { + string errormsg; + do { // Executed once (provides the ability to break) + int sign = 1; + unsigned + beg = 0, + end = unsigned(dmsa.size()); + flag ind1 = NONE; + int k = -1; + if (end > beg && (k = Utility::lookup(hemispheres_, dmsa[beg])) >= 0) { + ind1 = (k / 2) ? LONGITUDE : LATITUDE; + sign = k % 2 ? 1 : -1; + ++beg; + } + if (end > beg && (k = Utility::lookup(hemispheres_, dmsa[end-1])) >= 0) { + if (k >= 0) { + if (ind1 != NONE) { + if (toupper(dmsa[beg - 1]) == toupper(dmsa[end - 1])) + errormsg = "Repeated hemisphere indicators " + + Utility::str(dmsa[beg - 1]) + + " in " + dmsa.substr(beg - 1, end - beg + 1); + else + errormsg = "Contradictory hemisphere indicators " + + Utility::str(dmsa[beg - 1]) + " and " + + Utility::str(dmsa[end - 1]) + " in " + + dmsa.substr(beg - 1, end - beg + 1); + break; + } + ind1 = (k / 2) ? LONGITUDE : LATITUDE; + sign = k % 2 ? 1 : -1; + --end; + } + } + if (end > beg && (k = Utility::lookup(signs_, dmsa[beg])) >= 0) { + if (k >= 0) { + sign *= k ? 1 : -1; + ++beg; + } + } + if (end == beg) { + errormsg = "Empty or incomplete DMS string " + dmsa; + break; + } + real ipieces[] = {0, 0, 0}; + real fpieces[] = {0, 0, 0}; + unsigned npiece = 0; + real icurrent = 0; + real fcurrent = 0; + unsigned ncurrent = 0, p = beg; + bool pointseen = false; + unsigned digcount = 0, intcount = 0; + while (p < end) { + char x = dmsa[p++]; + if ((k = Utility::lookup(digits_, x)) >= 0) { + ++ncurrent; + if (digcount > 0) + ++digcount; // Count of decimal digits + else { + icurrent = 10 * icurrent + k; + ++intcount; + } + } else if (x == '.') { + if (pointseen) { + errormsg = "Multiple decimal points in " + + dmsa.substr(beg, end - beg); + break; + } + pointseen = true; + digcount = 1; + } else if ((k = Utility::lookup(dmsindicators_, x)) >= 0) { + if (k >= 3) { + if (p == end) { + errormsg = "Illegal for : to appear at the end of " + + dmsa.substr(beg, end - beg); + break; + } + k = npiece; + } + if (unsigned(k) == npiece - 1) { + errormsg = "Repeated " + string(components_[k]) + + " component in " + dmsa.substr(beg, end - beg); + break; + } else if (unsigned(k) < npiece) { + errormsg = string(components_[k]) + " component follows " + + string(components_[npiece - 1]) + " component in " + + dmsa.substr(beg, end - beg); + break; + } + if (ncurrent == 0) { + errormsg = "Missing numbers in " + string(components_[k]) + + " component of " + dmsa.substr(beg, end - beg); + break; + } + if (digcount > 0) { + istringstream s(dmsa.substr(p - intcount - digcount - 1, + intcount + digcount)); + s >> fcurrent; + icurrent = 0; + } + ipieces[k] = icurrent; + fpieces[k] = icurrent + fcurrent; + if (p < end) { + npiece = k + 1; + icurrent = fcurrent = 0; + ncurrent = digcount = intcount = 0; + } + } else if (Utility::lookup(signs_, x) >= 0) { + errormsg = "Internal sign in DMS string " + + dmsa.substr(beg, end - beg); + break; + } else { + errormsg = "Illegal character " + Utility::str(x) + " in DMS string " + + dmsa.substr(beg, end - beg); + break; + } + } + if (!errormsg.empty()) + break; + if (Utility::lookup(dmsindicators_, dmsa[p - 1]) < 0) { + if (npiece >= 3) { + errormsg = "Extra text following seconds in DMS string " + + dmsa.substr(beg, end - beg); + break; + } + if (ncurrent == 0) { + errormsg = "Missing numbers in trailing component of " + + dmsa.substr(beg, end - beg); + break; + } + if (digcount > 0) { + istringstream s(dmsa.substr(p - intcount - digcount, + intcount + digcount)); + s >> fcurrent; + icurrent = 0; + } + ipieces[npiece] = icurrent; + fpieces[npiece] = icurrent + fcurrent; + } + if (pointseen && digcount == 0) { + errormsg = "Decimal point in non-terminal component of " + + dmsa.substr(beg, end - beg); + break; + } + // Note that we accept 59.999999... even though it rounds to 60. + if (ipieces[1] >= 60 || fpieces[1] > 60 ) { + errormsg = "Minutes " + Utility::str(fpieces[1]) + + " not in range [0, 60)"; + break; + } + if (ipieces[2] >= 60 || fpieces[2] > 60) { + errormsg = "Seconds " + Utility::str(fpieces[2]) + + " not in range [0, 60)"; + break; + } + ind = ind1; + // Assume check on range of result is made by calling routine (which + // might be able to offer a better diagnostic). + return real(sign) * + ( fpieces[2] != 0 ? + (60*(60*fpieces[0] + fpieces[1]) + fpieces[2]) / 3600 : + ( fpieces[1] != 0 ? + (60*fpieces[0] + fpieces[1]) / 60 : fpieces[0] ) ); + } while (false); + real val = Utility::nummatch(dmsa); + if (val == 0) + throw GeographicErr(errormsg); + else + ind = NONE; + return val; + } + + void DMS::DecodeLatLon(const string& stra, const string& strb, + real& lat, real& lon, + bool longfirst) { + real a, b; + flag ia, ib; + a = Decode(stra, ia); + b = Decode(strb, ib); + if (ia == NONE && ib == NONE) { + // Default to lat, long unless longfirst + ia = longfirst ? LONGITUDE : LATITUDE; + ib = longfirst ? LATITUDE : LONGITUDE; + } else if (ia == NONE) + ia = flag(LATITUDE + LONGITUDE - ib); + else if (ib == NONE) + ib = flag(LATITUDE + LONGITUDE - ia); + if (ia == ib) + throw GeographicErr("Both " + stra + " and " + + strb + " interpreted as " + + (ia == LATITUDE ? "latitudes" : "longitudes")); + real + lat1 = ia == LATITUDE ? a : b, + lon1 = ia == LATITUDE ? b : a; + if (abs(lat1) > 90) + throw GeographicErr("Latitude " + Utility::str(lat1) + + "d not in [-90d, 90d]"); + lat = lat1; + lon = lon1; + } + + Math::real DMS::DecodeAngle(const string& angstr) { + flag ind; + real ang = Decode(angstr, ind); + if (ind != NONE) + throw GeographicErr("Arc angle " + angstr + + " includes a hemisphere, N/E/W/S"); + return ang; + } + + Math::real DMS::DecodeAzimuth(const string& azistr) { + flag ind; + real azi = Decode(azistr, ind); + if (ind == LATITUDE) + throw GeographicErr("Azimuth " + azistr + + " has a latitude hemisphere, N/S"); + return Math::AngNormalize(azi); + } + + string DMS::Encode(real angle, component trailing, unsigned prec, flag ind, + char dmssep) { + // Assume check on range of input angle has been made by calling + // routine (which might be able to offer a better diagnostic). + if (!isfinite(angle)) + return angle < 0 ? string("-inf") : + (angle > 0 ? string("inf") : string("nan")); + + // 15 - 2 * trailing = ceiling(log10(2^53/90/60^trailing)). + // This suffices to give full real precision for numbers in [-90,90] + prec = min(15 + Math::extra_digits() - 2 * unsigned(trailing), prec); + real scale = 1; + for (unsigned i = 0; i < unsigned(trailing); ++i) + scale *= 60; + for (unsigned i = 0; i < prec; ++i) + scale *= 10; + if (ind == AZIMUTH) + angle -= floor(angle/360) * 360; + int sign = angle < 0 ? -1 : 1; + angle *= sign; + + // Break off integer part to preserve precision in manipulation of + // fractional part. + real + idegree = floor(angle), + fdegree = (angle - idegree) * scale + real(0.5); + { + // Implement the "round ties to even" rule + real f = floor(fdegree); + fdegree = (f == fdegree && fmod(f, real(2)) == 1) ? f - 1 : f; + } + fdegree /= scale; + if (fdegree >= 1) { + idegree += 1; + fdegree -= 1; + } + real pieces[3] = {fdegree, 0, 0}; + for (unsigned i = 1; i <= unsigned(trailing); ++i) { + real + ip = floor(pieces[i - 1]), + fp = pieces[i - 1] - ip; + pieces[i] = fp * 60; + pieces[i - 1] = ip; + } + pieces[0] += idegree; + ostringstream s; + s << fixed << setfill('0'); + if (ind == NONE && sign < 0) + s << '-'; + switch (trailing) { + case DEGREE: + if (ind != NONE) + s << setw(1 + min(int(ind), 2) + prec + (prec ? 1 : 0)); + s << Utility::str(pieces[0], prec); + // Don't include degree designator (d) if it is the trailing component. + break; + default: + if (ind != NONE) + s << setw(1 + min(int(ind), 2)); + s << int(pieces[0]) + << (dmssep ? dmssep : char(tolower(dmsindicators_[0]))); + switch (trailing) { + case MINUTE: + s << setw(2 + prec + (prec ? 1 : 0)) << Utility::str(pieces[1], prec); + if (!dmssep) + s << char(tolower(dmsindicators_[1])); + break; + case SECOND: + s << setw(2) + << int(pieces[1]) + << (dmssep ? dmssep : char(tolower(dmsindicators_[1]))) + << setw(2 + prec + (prec ? 1 : 0)) << Utility::str(pieces[2], prec); + if (!dmssep) + s << char(tolower(dmsindicators_[2])); + break; + default: + break; + } + } + if (ind != NONE && ind != AZIMUTH) + s << hemispheres_[(ind == LATITUDE ? 0 : 2) + (sign < 0 ? 0 : 1)]; + return s.str(); + } + +} // namespace GeographicLib diff --git a/common/local_libs/GeographicLib/src/Ellipsoid.cpp b/common/local_libs/GeographicLib/src/Ellipsoid.cpp new file mode 100644 index 0000000000..d0d9835b18 --- /dev/null +++ b/common/local_libs/GeographicLib/src/Ellipsoid.cpp @@ -0,0 +1,124 @@ +/** + * \file Ellipsoid.cpp + * \brief Implementation for GeographicLib::Ellipsoid class + * + * Copyright (c) Charles Karney (2012-2020) and licensed + * under the MIT/X11 License. For more information, see + * https://geographiclib.sourceforge.io/ + **********************************************************************/ + +#include + +namespace GeographicLib { + + using namespace std; + + Ellipsoid::Ellipsoid(real a, real f) + : stol_(real(0.01) * sqrt(numeric_limits::epsilon())) + , _a(a) + , _f(f) + , _f1(1 - _f) + , _f12(Math::sq(_f1)) + , _e2(_f * (2 - _f)) + , _es((_f < 0 ? -1 : 1) * sqrt(abs(_e2))) + , _e12(_e2 / (1 - _e2)) + , _n(_f / (2 - _f)) + , _b(_a * _f1) + , _tm(_a, _f, real(1)) + , _ell(-_e12) + , _au(_a, _f, real(0), real(1), real(0), real(1), real(1)) + {} + + const Ellipsoid& Ellipsoid::WGS84() { + static const Ellipsoid wgs84(Constants::WGS84_a(), Constants::WGS84_f()); + return wgs84; + } + + Math::real Ellipsoid::QuarterMeridian() const + { return _b * _ell.E(); } + + Math::real Ellipsoid::Area() const { + return 4 * Math::pi() * + ((Math::sq(_a) + Math::sq(_b) * + (_e2 == 0 ? 1 : + (_e2 > 0 ? atanh(sqrt(_e2)) : atan(sqrt(-_e2))) / + sqrt(abs(_e2))))/2); + } + + Math::real Ellipsoid::ParametricLatitude(real phi) const + { return Math::atand(_f1 * Math::tand(Math::LatFix(phi))); } + + Math::real Ellipsoid::InverseParametricLatitude(real beta) const + { return Math::atand(Math::tand(Math::LatFix(beta)) / _f1); } + + Math::real Ellipsoid::GeocentricLatitude(real phi) const + { return Math::atand(_f12 * Math::tand(Math::LatFix(phi))); } + + Math::real Ellipsoid::InverseGeocentricLatitude(real theta) const + { return Math::atand(Math::tand(Math::LatFix(theta)) / _f12); } + + Math::real Ellipsoid::RectifyingLatitude(real phi) const { + return abs(phi) == 90 ? phi: + 90 * MeridianDistance(phi) / QuarterMeridian(); + } + + Math::real Ellipsoid::InverseRectifyingLatitude(real mu) const { + if (abs(mu) == 90) + return mu; + return InverseParametricLatitude(_ell.Einv(mu * _ell.E() / 90) / + Math::degree()); + } + + Math::real Ellipsoid::AuthalicLatitude(real phi) const + { return Math::atand(_au.txif(Math::tand(Math::LatFix(phi)))); } + + Math::real Ellipsoid::InverseAuthalicLatitude(real xi) const + { return Math::atand(_au.tphif(Math::tand(Math::LatFix(xi)))); } + + Math::real Ellipsoid::ConformalLatitude(real phi) const + { return Math::atand(Math::taupf(Math::tand(Math::LatFix(phi)), _es)); } + + Math::real Ellipsoid::InverseConformalLatitude(real chi) const + { return Math::atand(Math::tauf(Math::tand(Math::LatFix(chi)), _es)); } + + Math::real Ellipsoid::IsometricLatitude(real phi) const + { return asinh(Math::taupf(Math::tand(Math::LatFix(phi)), _es)) / + Math::degree(); } + + Math::real Ellipsoid::InverseIsometricLatitude(real psi) const + { return Math::atand(Math::tauf(sinh(psi * Math::degree()), _es)); } + + Math::real Ellipsoid::CircleRadius(real phi) const { + return abs(phi) == 90 ? 0 : + // a * cos(beta) + _a / hypot(real(1), _f1 * Math::tand(Math::LatFix(phi))); + } + + Math::real Ellipsoid::CircleHeight(real phi) const { + real tbeta = _f1 * Math::tand(phi); + // b * sin(beta) + return _b * tbeta / hypot(real(1), + _f1 * Math::tand(Math::LatFix(phi))); + } + + Math::real Ellipsoid::MeridianDistance(real phi) const + { return _b * _ell.Ed( ParametricLatitude(phi) ); } + + Math::real Ellipsoid::MeridionalCurvatureRadius(real phi) const { + real v = 1 - _e2 * Math::sq(Math::sind(Math::LatFix(phi))); + return _a * (1 - _e2) / (v * sqrt(v)); + } + + Math::real Ellipsoid::TransverseCurvatureRadius(real phi) const { + real v = 1 - _e2 * Math::sq(Math::sind(Math::LatFix(phi))); + return _a / sqrt(v); + } + + Math::real Ellipsoid::NormalCurvatureRadius(real phi, real azi) const { + real calp, salp, + v = 1 - _e2 * Math::sq(Math::sind(Math::LatFix(phi))); + Math::sincosd(azi, salp, calp); + return _a / (sqrt(v) * (Math::sq(calp) * v / (1 - _e2) + Math::sq(salp))); + } + +} // namespace GeographicLib diff --git a/common/local_libs/GeographicLib/src/EllipticFunction.cpp b/common/local_libs/GeographicLib/src/EllipticFunction.cpp new file mode 100644 index 0000000000..c3e27b7918 --- /dev/null +++ b/common/local_libs/GeographicLib/src/EllipticFunction.cpp @@ -0,0 +1,570 @@ +/** + * \file EllipticFunction.cpp + * \brief Implementation for GeographicLib::EllipticFunction class + * + * Copyright (c) Charles Karney (2008-2020) and licensed + * under the MIT/X11 License. For more information, see + * https://geographiclib.sourceforge.io/ + **********************************************************************/ + +#include + +#if defined(_MSC_VER) +// Squelch warnings about constant conditional expressions +# pragma warning (disable: 4127) +#endif + +namespace GeographicLib { + + using namespace std; + + /* + * Implementation of methods given in + * + * B. C. Carlson + * Computation of elliptic integrals + * Numerical Algorithms 10, 13-26 (1995) + */ + + Math::real EllipticFunction::RF(real x, real y, real z) { + // Carlson, eqs 2.2 - 2.7 + static const real tolRF = + pow(3 * numeric_limits::epsilon() * real(0.01), 1/real(8)); + real + A0 = (x + y + z)/3, + An = A0, + Q = max(max(abs(A0-x), abs(A0-y)), abs(A0-z)) / tolRF, + x0 = x, + y0 = y, + z0 = z, + mul = 1; + while (Q >= mul * abs(An)) { + // Max 6 trips + real lam = sqrt(x0)*sqrt(y0) + sqrt(y0)*sqrt(z0) + sqrt(z0)*sqrt(x0); + An = (An + lam)/4; + x0 = (x0 + lam)/4; + y0 = (y0 + lam)/4; + z0 = (z0 + lam)/4; + mul *= 4; + } + real + X = (A0 - x) / (mul * An), + Y = (A0 - y) / (mul * An), + Z = - (X + Y), + E2 = X*Y - Z*Z, + E3 = X*Y*Z; + // https://dlmf.nist.gov/19.36.E1 + // Polynomial is + // (1 - E2/10 + E3/14 + E2^2/24 - 3*E2*E3/44 + // - 5*E2^3/208 + 3*E3^2/104 + E2^2*E3/16) + // convert to Horner form... + return (E3 * (6930 * E3 + E2 * (15015 * E2 - 16380) + 17160) + + E2 * ((10010 - 5775 * E2) * E2 - 24024) + 240240) / + (240240 * sqrt(An)); + } + + Math::real EllipticFunction::RF(real x, real y) { + // Carlson, eqs 2.36 - 2.38 + static const real tolRG0 = + real(2.7) * sqrt((numeric_limits::epsilon() * real(0.01))); + real xn = sqrt(x), yn = sqrt(y); + if (xn < yn) swap(xn, yn); + while (abs(xn-yn) > tolRG0 * xn) { + // Max 4 trips + real t = (xn + yn) /2; + yn = sqrt(xn * yn); + xn = t; + } + return Math::pi() / (xn + yn); + } + + Math::real EllipticFunction::RC(real x, real y) { + // Defined only for y != 0 and x >= 0. + return ( !(x >= y) ? // x < y and catch nans + // https://dlmf.nist.gov/19.2.E18 + atan(sqrt((y - x) / x)) / sqrt(y - x) : + ( x == y ? 1 / sqrt(y) : + asinh( y > 0 ? + // https://dlmf.nist.gov/19.2.E19 + // atanh(sqrt((x - y) / x)) + sqrt((x - y) / y) : + // https://dlmf.nist.gov/19.2.E20 + // atanh(sqrt(x / (x - y))) + sqrt(-x / y) ) / sqrt(x - y) ) ); + } + + Math::real EllipticFunction::RG(real x, real y, real z) { + if (z == 0) + swap(y, z); + // Carlson, eq 1.7 + return (z * RF(x, y, z) - (x-z) * (y-z) * RD(x, y, z) / 3 + + sqrt(x * y / z)) / 2; + } + + Math::real EllipticFunction::RG(real x, real y) { + // Carlson, eqs 2.36 - 2.39 + static const real tolRG0 = + real(2.7) * sqrt((numeric_limits::epsilon() * real(0.01))); + real + x0 = sqrt(max(x, y)), + y0 = sqrt(min(x, y)), + xn = x0, + yn = y0, + s = 0, + mul = real(0.25); + while (abs(xn-yn) > tolRG0 * xn) { + // Max 4 trips + real t = (xn + yn) /2; + yn = sqrt(xn * yn); + xn = t; + mul *= 2; + t = xn - yn; + s += mul * t * t; + } + return (Math::sq( (x0 + y0)/2 ) - s) * Math::pi() / (2 * (xn + yn)); + } + + Math::real EllipticFunction::RJ(real x, real y, real z, real p) { + // Carlson, eqs 2.17 - 2.25 + static const real + tolRD = pow(real(0.2) * (numeric_limits::epsilon() * real(0.01)), + 1/real(8)); + real + A0 = (x + y + z + 2*p)/5, + An = A0, + delta = (p-x) * (p-y) * (p-z), + Q = max(max(abs(A0-x), abs(A0-y)), max(abs(A0-z), abs(A0-p))) / tolRD, + x0 = x, + y0 = y, + z0 = z, + p0 = p, + mul = 1, + mul3 = 1, + s = 0; + while (Q >= mul * abs(An)) { + // Max 7 trips + real + lam = sqrt(x0)*sqrt(y0) + sqrt(y0)*sqrt(z0) + sqrt(z0)*sqrt(x0), + d0 = (sqrt(p0)+sqrt(x0)) * (sqrt(p0)+sqrt(y0)) * (sqrt(p0)+sqrt(z0)), + e0 = delta/(mul3 * Math::sq(d0)); + s += RC(1, 1 + e0)/(mul * d0); + An = (An + lam)/4; + x0 = (x0 + lam)/4; + y0 = (y0 + lam)/4; + z0 = (z0 + lam)/4; + p0 = (p0 + lam)/4; + mul *= 4; + mul3 *= 64; + } + real + X = (A0 - x) / (mul * An), + Y = (A0 - y) / (mul * An), + Z = (A0 - z) / (mul * An), + P = -(X + Y + Z) / 2, + E2 = X*Y + X*Z + Y*Z - 3*P*P, + E3 = X*Y*Z + 2*P * (E2 + 2*P*P), + E4 = (2*X*Y*Z + P * (E2 + 3*P*P)) * P, + E5 = X*Y*Z*P*P; + // https://dlmf.nist.gov/19.36.E2 + // Polynomial is + // (1 - 3*E2/14 + E3/6 + 9*E2^2/88 - 3*E4/22 - 9*E2*E3/52 + 3*E5/26 + // - E2^3/16 + 3*E3^2/40 + 3*E2*E4/20 + 45*E2^2*E3/272 + // - 9*(E3*E4+E2*E5)/68) + return ((471240 - 540540 * E2) * E5 + + (612612 * E2 - 540540 * E3 - 556920) * E4 + + E3 * (306306 * E3 + E2 * (675675 * E2 - 706860) + 680680) + + E2 * ((417690 - 255255 * E2) * E2 - 875160) + 4084080) / + (4084080 * mul * An * sqrt(An)) + 6 * s; + } + + Math::real EllipticFunction::RD(real x, real y, real z) { + // Carlson, eqs 2.28 - 2.34 + static const real + tolRD = pow(real(0.2) * (numeric_limits::epsilon() * real(0.01)), + 1/real(8)); + real + A0 = (x + y + 3*z)/5, + An = A0, + Q = max(max(abs(A0-x), abs(A0-y)), abs(A0-z)) / tolRD, + x0 = x, + y0 = y, + z0 = z, + mul = 1, + s = 0; + while (Q >= mul * abs(An)) { + // Max 7 trips + real lam = sqrt(x0)*sqrt(y0) + sqrt(y0)*sqrt(z0) + sqrt(z0)*sqrt(x0); + s += 1/(mul * sqrt(z0) * (z0 + lam)); + An = (An + lam)/4; + x0 = (x0 + lam)/4; + y0 = (y0 + lam)/4; + z0 = (z0 + lam)/4; + mul *= 4; + } + real + X = (A0 - x) / (mul * An), + Y = (A0 - y) / (mul * An), + Z = -(X + Y) / 3, + E2 = X*Y - 6*Z*Z, + E3 = (3*X*Y - 8*Z*Z)*Z, + E4 = 3 * (X*Y - Z*Z) * Z*Z, + E5 = X*Y*Z*Z*Z; + // https://dlmf.nist.gov/19.36.E2 + // Polynomial is + // (1 - 3*E2/14 + E3/6 + 9*E2^2/88 - 3*E4/22 - 9*E2*E3/52 + 3*E5/26 + // - E2^3/16 + 3*E3^2/40 + 3*E2*E4/20 + 45*E2^2*E3/272 + // - 9*(E3*E4+E2*E5)/68) + return ((471240 - 540540 * E2) * E5 + + (612612 * E2 - 540540 * E3 - 556920) * E4 + + E3 * (306306 * E3 + E2 * (675675 * E2 - 706860) + 680680) + + E2 * ((417690 - 255255 * E2) * E2 - 875160) + 4084080) / + (4084080 * mul * An * sqrt(An)) + 3 * s; + } + + void EllipticFunction::Reset(real k2, real alpha2, + real kp2, real alphap2) { + // Accept nans here (needed for GeodesicExact) + if (k2 > 1) + throw GeographicErr("Parameter k2 is not in (-inf, 1]"); + if (alpha2 > 1) + throw GeographicErr("Parameter alpha2 is not in (-inf, 1]"); + if (kp2 < 0) + throw GeographicErr("Parameter kp2 is not in [0, inf)"); + if (alphap2 < 0) + throw GeographicErr("Parameter alphap2 is not in [0, inf)"); + _k2 = k2; + _kp2 = kp2; + _alpha2 = alpha2; + _alphap2 = alphap2; + _eps = _k2/Math::sq(sqrt(_kp2) + 1); + // Values of complete elliptic integrals for k = 0,1 and alpha = 0,1 + // K E D + // k = 0: pi/2 pi/2 pi/4 + // k = 1: inf 1 inf + // Pi G H + // k = 0, alpha = 0: pi/2 pi/2 pi/4 + // k = 1, alpha = 0: inf 1 1 + // k = 0, alpha = 1: inf inf pi/2 + // k = 1, alpha = 1: inf inf inf + // + // Pi(0, k) = K(k) + // G(0, k) = E(k) + // H(0, k) = K(k) - D(k) + // Pi(0, k) = K(k) + // G(0, k) = E(k) + // H(0, k) = K(k) - D(k) + // Pi(alpha2, 0) = pi/(2*sqrt(1-alpha2)) + // G(alpha2, 0) = pi/(2*sqrt(1-alpha2)) + // H(alpha2, 0) = pi/(2*(1 + sqrt(1-alpha2))) + // Pi(alpha2, 1) = inf + // H(1, k) = K(k) + // G(alpha2, 1) = H(alpha2, 1) = RC(1, alphap2) + if (_k2 != 0) { + // Complete elliptic integral K(k), Carlson eq. 4.1 + // https://dlmf.nist.gov/19.25.E1 + _Kc = _kp2 != 0 ? RF(_kp2, 1) : Math::infinity(); + // Complete elliptic integral E(k), Carlson eq. 4.2 + // https://dlmf.nist.gov/19.25.E1 + _Ec = _kp2 != 0 ? 2 * RG(_kp2, 1) : 1; + // D(k) = (K(k) - E(k))/k^2, Carlson eq.4.3 + // https://dlmf.nist.gov/19.25.E1 + _Dc = _kp2 != 0 ? RD(0, _kp2, 1) / 3 : Math::infinity(); + } else { + _Kc = _Ec = Math::pi()/2; _Dc = _Kc/2; + } + if (_alpha2 != 0) { + // https://dlmf.nist.gov/19.25.E2 + real rj = (_kp2 != 0 && _alphap2 != 0) ? RJ(0, _kp2, 1, _alphap2) : + Math::infinity(), + // Only use rc if _kp2 = 0. + rc = _kp2 != 0 ? 0 : + (_alphap2 != 0 ? RC(1, _alphap2) : Math::infinity()); + // Pi(alpha^2, k) + _Pic = _kp2 != 0 ? _Kc + _alpha2 * rj / 3 : Math::infinity(); + // G(alpha^2, k) + _Gc = _kp2 != 0 ? _Kc + (_alpha2 - _k2) * rj / 3 : rc; + // H(alpha^2, k) + _Hc = _kp2 != 0 ? _Kc - (_alphap2 != 0 ? _alphap2 * rj : 0) / 3 : rc; + } else { + _Pic = _Kc; _Gc = _Ec; + // Hc = Kc - Dc but this involves large cancellations if k2 is close to + // 1. So write (for alpha2 = 0) + // Hc = int(cos(phi)^2/sqrt(1-k2*sin(phi)^2),phi,0,pi/2) + // = 1/sqrt(1-k2) * int(sin(phi)^2/sqrt(1-k2/kp2*sin(phi)^2,...) + // = 1/kp * D(i*k/kp) + // and use D(k) = RD(0, kp2, 1) / 3 + // so Hc = 1/kp * RD(0, 1/kp2, 1) / 3 + // = kp2 * RD(0, 1, kp2) / 3 + // using https://dlmf.nist.gov/19.20.E18 + // Equivalently + // RF(x, 1) - RD(0, x, 1)/3 = x * RD(0, 1, x)/3 for x > 0 + // For k2 = 1 and alpha2 = 0, we have + // Hc = int(cos(phi),...) = 1 + _Hc = _kp2 != 0 ? _kp2 * RD(0, 1, _kp2) / 3 : 1; + } + } + + /* + * Implementation of methods given in + * + * R. Bulirsch + * Numerical Calculation of Elliptic Integrals and Elliptic Functions + * Numericshe Mathematik 7, 78-90 (1965) + */ + + void EllipticFunction::sncndn(real x, real& sn, real& cn, real& dn) const { + // Bulirsch's sncndn routine, p 89. + static const real tolJAC = + sqrt(numeric_limits::epsilon() * real(0.01)); + if (_kp2 != 0) { + real mc = _kp2, d = 0; + if (_kp2 < 0) { + d = 1 - mc; + mc /= -d; + d = sqrt(d); + x *= d; + } + real c = 0; // To suppress warning about uninitialized variable + real m[num_], n[num_]; + unsigned l = 0; + for (real a = 1; l < num_ || GEOGRAPHICLIB_PANIC; ++l) { + // This converges quadratically. Max 5 trips + m[l] = a; + n[l] = mc = sqrt(mc); + c = (a + mc) / 2; + if (!(abs(a - mc) > tolJAC * a)) { + ++l; + break; + } + mc *= a; + a = c; + } + x *= c; + sn = sin(x); + cn = cos(x); + dn = 1; + if (sn != 0) { + real a = cn / sn; + c *= a; + while (l--) { + real b = m[l]; + a *= c; + c *= dn; + dn = (n[l] + a) / (b + a); + a = c / b; + } + a = 1 / sqrt(c*c + 1); + sn = sn < 0 ? -a : a; + cn = c * sn; + if (_kp2 < 0) { + swap(cn, dn); + sn /= d; + } + } + } else { + sn = tanh(x); + dn = cn = 1 / cosh(x); + } + } + + Math::real EllipticFunction::F(real sn, real cn, real dn) const { + // Carlson, eq. 4.5 and + // https://dlmf.nist.gov/19.25.E5 + real cn2 = cn*cn, dn2 = dn*dn, + fi = cn2 != 0 ? abs(sn) * RF(cn2, dn2, 1) : K(); + // Enforce usual trig-like symmetries + if (cn < 0) + fi = 2 * K() - fi; + return copysign(fi, sn); + } + + Math::real EllipticFunction::E(real sn, real cn, real dn) const { + real + cn2 = cn*cn, dn2 = dn*dn, sn2 = sn*sn, + ei = cn2 != 0 ? + abs(sn) * ( _k2 <= 0 ? + // Carlson, eq. 4.6 and + // https://dlmf.nist.gov/19.25.E9 + RF(cn2, dn2, 1) - _k2 * sn2 * RD(cn2, dn2, 1) / 3 : + ( _kp2 >= 0 ? + // https://dlmf.nist.gov/19.25.E10 + _kp2 * RF(cn2, dn2, 1) + + _k2 * _kp2 * sn2 * RD(cn2, 1, dn2) / 3 + + _k2 * abs(cn) / dn : + // https://dlmf.nist.gov/19.25.E11 + - _kp2 * sn2 * RD(dn2, 1, cn2) / 3 + + dn / abs(cn) ) ) : + E(); + // Enforce usual trig-like symmetries + if (cn < 0) + ei = 2 * E() - ei; + return copysign(ei, sn); + } + + Math::real EllipticFunction::D(real sn, real cn, real dn) const { + // Carlson, eq. 4.8 and + // https://dlmf.nist.gov/19.25.E13 + real + cn2 = cn*cn, dn2 = dn*dn, sn2 = sn*sn, + di = cn2 != 0 ? abs(sn) * sn2 * RD(cn2, dn2, 1) / 3 : D(); + // Enforce usual trig-like symmetries + if (cn < 0) + di = 2 * D() - di; + return copysign(di, sn); + } + + Math::real EllipticFunction::Pi(real sn, real cn, real dn) const { + // Carlson, eq. 4.7 and + // https://dlmf.nist.gov/19.25.E14 + real + cn2 = cn*cn, dn2 = dn*dn, sn2 = sn*sn, + pii = cn2 != 0 ? abs(sn) * (RF(cn2, dn2, 1) + + _alpha2 * sn2 * + RJ(cn2, dn2, 1, cn2 + _alphap2 * sn2) / 3) : + Pi(); + // Enforce usual trig-like symmetries + if (cn < 0) + pii = 2 * Pi() - pii; + return copysign(pii, sn); + } + + Math::real EllipticFunction::G(real sn, real cn, real dn) const { + real + cn2 = cn*cn, dn2 = dn*dn, sn2 = sn*sn, + gi = cn2 != 0 ? abs(sn) * (RF(cn2, dn2, 1) + + (_alpha2 - _k2) * sn2 * + RJ(cn2, dn2, 1, cn2 + _alphap2 * sn2) / 3) : + G(); + // Enforce usual trig-like symmetries + if (cn < 0) + gi = 2 * G() - gi; + return copysign(gi, sn); + } + + Math::real EllipticFunction::H(real sn, real cn, real dn) const { + real + cn2 = cn*cn, dn2 = dn*dn, sn2 = sn*sn, + // WARNING: large cancellation if k2 = 1, alpha2 = 0, and phi near pi/2 + hi = cn2 != 0 ? abs(sn) * (RF(cn2, dn2, 1) - + _alphap2 * sn2 * + RJ(cn2, dn2, 1, cn2 + _alphap2 * sn2) / 3) : + H(); + // Enforce usual trig-like symmetries + if (cn < 0) + hi = 2 * H() - hi; + return copysign(hi, sn); + } + + Math::real EllipticFunction::deltaF(real sn, real cn, real dn) const { + // Function is periodic with period pi + if (cn < 0) { cn = -cn; sn = -sn; } + return F(sn, cn, dn) * (Math::pi()/2) / K() - atan2(sn, cn); + } + + Math::real EllipticFunction::deltaE(real sn, real cn, real dn) const { + // Function is periodic with period pi + if (cn < 0) { cn = -cn; sn = -sn; } + return E(sn, cn, dn) * (Math::pi()/2) / E() - atan2(sn, cn); + } + + Math::real EllipticFunction::deltaPi(real sn, real cn, real dn) const { + // Function is periodic with period pi + if (cn < 0) { cn = -cn; sn = -sn; } + return Pi(sn, cn, dn) * (Math::pi()/2) / Pi() - atan2(sn, cn); + } + + Math::real EllipticFunction::deltaD(real sn, real cn, real dn) const { + // Function is periodic with period pi + if (cn < 0) { cn = -cn; sn = -sn; } + return D(sn, cn, dn) * (Math::pi()/2) / D() - atan2(sn, cn); + } + + Math::real EllipticFunction::deltaG(real sn, real cn, real dn) const { + // Function is periodic with period pi + if (cn < 0) { cn = -cn; sn = -sn; } + return G(sn, cn, dn) * (Math::pi()/2) / G() - atan2(sn, cn); + } + + Math::real EllipticFunction::deltaH(real sn, real cn, real dn) const { + // Function is periodic with period pi + if (cn < 0) { cn = -cn; sn = -sn; } + return H(sn, cn, dn) * (Math::pi()/2) / H() - atan2(sn, cn); + } + + Math::real EllipticFunction::F(real phi) const { + real sn = sin(phi), cn = cos(phi), dn = Delta(sn, cn); + return abs(phi) < Math::pi() ? F(sn, cn, dn) : + (deltaF(sn, cn, dn) + phi) * K() / (Math::pi()/2); + } + + Math::real EllipticFunction::E(real phi) const { + real sn = sin(phi), cn = cos(phi), dn = Delta(sn, cn); + return abs(phi) < Math::pi() ? E(sn, cn, dn) : + (deltaE(sn, cn, dn) + phi) * E() / (Math::pi()/2); + } + + Math::real EllipticFunction::Ed(real ang) const { + real n = ceil(ang/360 - real(0.5)); + ang -= 360 * n; + real sn, cn; + Math::sincosd(ang, sn, cn); + return E(sn, cn, Delta(sn, cn)) + 4 * E() * n; + } + + Math::real EllipticFunction::Pi(real phi) const { + real sn = sin(phi), cn = cos(phi), dn = Delta(sn, cn); + return abs(phi) < Math::pi() ? Pi(sn, cn, dn) : + (deltaPi(sn, cn, dn) + phi) * Pi() / (Math::pi()/2); + } + + Math::real EllipticFunction::D(real phi) const { + real sn = sin(phi), cn = cos(phi), dn = Delta(sn, cn); + return abs(phi) < Math::pi() ? D(sn, cn, dn) : + (deltaD(sn, cn, dn) + phi) * D() / (Math::pi()/2); + } + + Math::real EllipticFunction::G(real phi) const { + real sn = sin(phi), cn = cos(phi), dn = Delta(sn, cn); + return abs(phi) < Math::pi() ? G(sn, cn, dn) : + (deltaG(sn, cn, dn) + phi) * G() / (Math::pi()/2); + } + + Math::real EllipticFunction::H(real phi) const { + real sn = sin(phi), cn = cos(phi), dn = Delta(sn, cn); + return abs(phi) < Math::pi() ? H(sn, cn, dn) : + (deltaH(sn, cn, dn) + phi) * H() / (Math::pi()/2); + } + + Math::real EllipticFunction::Einv(real x) const { + static const real tolJAC = + sqrt(numeric_limits::epsilon() * real(0.01)); + real n = floor(x / (2 * _Ec) + real(0.5)); + x -= 2 * _Ec * n; // x now in [-ec, ec) + // Linear approximation + real phi = Math::pi() * x / (2 * _Ec); // phi in [-pi/2, pi/2) + // First order correction + phi -= _eps * sin(2 * phi) / 2; + // For kp2 close to zero use asin(x/_Ec) or + // J. P. Boyd, Applied Math. and Computation 218, 7005-7013 (2012) + // https://doi.org/10.1016/j.amc.2011.12.021 + for (int i = 0; i < num_ || GEOGRAPHICLIB_PANIC; ++i) { + real + sn = sin(phi), + cn = cos(phi), + dn = Delta(sn, cn), + err = (E(sn, cn, dn) - x)/dn; + phi -= err; + if (!(abs(err) > tolJAC)) + break; + } + return n * Math::pi() + phi; + } + + Math::real EllipticFunction::deltaEinv(real stau, real ctau) const { + // Function is periodic with period pi + if (ctau < 0) { ctau = -ctau; stau = -stau; } + real tau = atan2(stau, ctau); + return Einv( tau * E() / (Math::pi()/2) ) - tau; + } + +} // namespace GeographicLib diff --git a/common/local_libs/GeographicLib/src/GARS.cpp b/common/local_libs/GeographicLib/src/GARS.cpp new file mode 100644 index 0000000000..4efaeab5f4 --- /dev/null +++ b/common/local_libs/GeographicLib/src/GARS.cpp @@ -0,0 +1,124 @@ +/** + * \file GARS.cpp + * \brief Implementation for GeographicLib::GARS class + * + * Copyright (c) Charles Karney (2015-2020) and licensed + * under the MIT/X11 License. For more information, see + * https://geographiclib.sourceforge.io/ + **********************************************************************/ + +#include +#include + +namespace GeographicLib { + + using namespace std; + + const char* const GARS::digits_ = "0123456789"; + const char* const GARS::letters_ = "ABCDEFGHJKLMNPQRSTUVWXYZ"; + + void GARS::Forward(real lat, real lon, int prec, string& gars) { + using std::isnan; // Needed for Centos 7, ubuntu 14 + if (abs(lat) > 90) + throw GeographicErr("Latitude " + Utility::str(lat) + + "d not in [-90d, 90d]"); + if (isnan(lat) || isnan(lon)) { + gars = "INVALID"; + return; + } + lon = Math::AngNormalize(lon); + if (lon == 180) lon = -180; // lon now in [-180,180) + if (lat == 90) lat *= (1 - numeric_limits::epsilon() / 2); + prec = max(0, min(int(maxprec_), prec)); + int + x = int(floor(lon * m_)) - lonorig_ * m_, + y = int(floor(lat * m_)) - latorig_ * m_, + ilon = x * mult1_ / m_, + ilat = y * mult1_ / m_; + x -= ilon * m_ / mult1_; y -= ilat * m_ / mult1_; + char gars1[maxlen_]; + ++ilon; + for (int c = lonlen_; c--;) { + gars1[c] = digits_[ ilon % baselon_]; ilon /= baselon_; + } + for (int c = latlen_; c--;) { + gars1[lonlen_ + c] = letters_[ilat % baselat_]; ilat /= baselat_; + } + if (prec > 0) { + ilon = x / mult3_; ilat = y / mult3_; + gars1[baselen_] = digits_[mult2_ * (mult2_ - 1 - ilat) + ilon + 1]; + if (prec > 1) { + ilon = x % mult3_; ilat = y % mult3_; + gars1[baselen_ + 1] = digits_[mult3_ * (mult3_ - 1 - ilat) + ilon + 1]; + } + } + gars.resize(baselen_ + prec); + copy(gars1, gars1 + baselen_ + prec, gars.begin()); + } + + void GARS::Reverse(const string& gars, real& lat, real& lon, + int& prec, bool centerp) { + int len = int(gars.length()); + if (len >= 3 && + toupper(gars[0]) == 'I' && + toupper(gars[1]) == 'N' && + toupper(gars[2]) == 'V') { + lat = lon = Math::NaN(); + return; + } + if (len < baselen_) + throw GeographicErr("GARS must have at least 5 characters " + gars); + if (len > maxlen_) + throw GeographicErr("GARS can have at most 7 characters " + gars); + int prec1 = len - baselen_; + int ilon = 0; + for (int c = 0; c < lonlen_; ++c) { + int k = Utility::lookup(digits_, gars[c]); + if (k < 0) + throw GeographicErr("GARS must start with 3 digits " + gars); + ilon = ilon * baselon_ + k; + } + if (!(ilon >= 1 && ilon <= 720)) + throw GeographicErr("Initial digits in GARS must lie in [1, 720] " + + gars); + --ilon; + int ilat = 0; + for (int c = 0; c < latlen_; ++c) { + int k = Utility::lookup(letters_, gars[lonlen_ + c]); + if (k < 0) + throw GeographicErr("Illegal letters in GARS " + gars.substr(3,2)); + ilat = ilat * baselat_ + k; + } + if (!(ilat < 360)) + throw GeographicErr("GARS letters must lie in [AA, QZ] " + gars); + real + unit = mult1_, + lat1 = ilat + latorig_ * unit, + lon1 = ilon + lonorig_ * unit; + if (prec1 > 0) { + int k = Utility::lookup(digits_, gars[baselen_]); + if (!(k >= 1 && k <= mult2_ * mult2_)) + throw GeographicErr("6th character in GARS must [1, 4] " + gars); + --k; + unit *= mult2_; + lat1 = mult2_ * lat1 + (mult2_ - 1 - k / mult2_); + lon1 = mult2_ * lon1 + (k % mult2_); + if (prec1 > 1) { + k = Utility::lookup(digits_, gars[baselen_ + 1]); + if (!(k >= 1 /* && k <= mult3_ * mult3_ */)) + throw GeographicErr("7th character in GARS must [1, 9] " + gars); + --k; + unit *= mult3_; + lat1 = mult3_ * lat1 + (mult3_ - 1 - k / mult3_); + lon1 = mult3_ * lon1 + (k % mult3_); + } + } + if (centerp) { + unit *= 2; lat1 = 2 * lat1 + 1; lon1 = 2 * lon1 + 1; + } + lat = lat1 / unit; + lon = lon1 / unit; + prec = prec1; + } + +} // namespace GeographicLib diff --git a/common/local_libs/GeographicLib/src/GeoCoords.cpp b/common/local_libs/GeographicLib/src/GeoCoords.cpp new file mode 100644 index 0000000000..ecb513ea16 --- /dev/null +++ b/common/local_libs/GeographicLib/src/GeoCoords.cpp @@ -0,0 +1,178 @@ +/** + * \file GeoCoords.cpp + * \brief Implementation for GeographicLib::GeoCoords class + * + * Copyright (c) Charles Karney (2008-2020) and licensed + * under the MIT/X11 License. For more information, see + * https://geographiclib.sourceforge.io/ + **********************************************************************/ + +#include +#include +#include +#include + +namespace GeographicLib { + + using namespace std; + + void GeoCoords::Reset(const std::string& s, bool centerp, bool longfirst) { + vector sa; + const char* spaces = " \t\n\v\f\r,"; // Include comma as a space + for (string::size_type pos0 = 0, pos1; pos0 != string::npos;) { + pos1 = s.find_first_not_of(spaces, pos0); + if (pos1 == string::npos) + break; + pos0 = s.find_first_of(spaces, pos1); + sa.push_back(s.substr(pos1, pos0 == string::npos ? pos0 : pos0 - pos1)); + } + if (sa.size() == 1) { + int prec; + MGRS::Reverse(sa[0], _zone, _northp, _easting, _northing, prec, centerp); + UTMUPS::Reverse(_zone, _northp, _easting, _northing, + _lat, _long, _gamma, _k); + } else if (sa.size() == 2) { + DMS::DecodeLatLon(sa[0], sa[1], _lat, _long, longfirst); + _long = Math::AngNormalize(_long); + UTMUPS::Forward( _lat, _long, + _zone, _northp, _easting, _northing, _gamma, _k); + } else if (sa.size() == 3) { + unsigned zoneind, coordind; + if (sa[0].size() > 0 && isalpha(sa[0][sa[0].size() - 1])) { + zoneind = 0; + coordind = 1; + } else if (sa[2].size() > 0 && isalpha(sa[2][sa[2].size() - 1])) { + zoneind = 2; + coordind = 0; + } else + throw GeographicErr("Neither " + sa[0] + " nor " + sa[2] + + " of the form UTM/UPS Zone + Hemisphere" + + " (ex: 38n, 09s, n)"); + UTMUPS::DecodeZone(sa[zoneind], _zone, _northp); + for (unsigned i = 0; i < 2; ++i) + (i ? _northing : _easting) = Utility::val(sa[coordind + i]); + UTMUPS::Reverse(_zone, _northp, _easting, _northing, + _lat, _long, _gamma, _k); + FixHemisphere(); + } else + throw GeographicErr("Coordinate requires 1, 2, or 3 elements"); + CopyToAlt(); + } + + string GeoCoords::GeoRepresentation(int prec, bool longfirst) const { + using std::isnan; // Needed for Centos 7, ubuntu 14 + prec = max(0, min(9 + Math::extra_digits(), prec) + 5); + ostringstream os; + os << fixed << setprecision(prec); + real a = longfirst ? _long : _lat; + real b = longfirst ? _lat : _long; + if (!isnan(a)) + os << a; + else + os << "nan"; + os << " "; + if (!isnan(b)) + os << b; + else + os << "nan"; + return os.str(); + } + + string GeoCoords::DMSRepresentation(int prec, bool longfirst, + char dmssep) const { + prec = max(0, min(10 + Math::extra_digits(), prec) + 5); + return DMS::Encode(longfirst ? _long : _lat, unsigned(prec), + longfirst ? DMS::LONGITUDE : DMS::LATITUDE, dmssep) + + " " + DMS::Encode(longfirst ? _lat : _long, unsigned(prec), + longfirst ? DMS::LATITUDE : DMS::LONGITUDE, dmssep); + } + + string GeoCoords::MGRSRepresentation(int prec) const { + // Max precision is um + prec = max(-1, min(6, prec) + 5); + string mgrs; + MGRS::Forward(_zone, _northp, _easting, _northing, _lat, prec, mgrs); + return mgrs; + } + + string GeoCoords::AltMGRSRepresentation(int prec) const { + // Max precision is um + prec = max(-1, min(6, prec) + 5); + string mgrs; + MGRS::Forward(_alt_zone, _northp, _alt_easting, _alt_northing, _lat, prec, + mgrs); + return mgrs; + } + + void GeoCoords::UTMUPSString(int zone, bool northp, + real easting, real northing, int prec, + bool abbrev, string& utm) { + ostringstream os; + prec = max(-5, min(9 + Math::extra_digits(), prec)); + // Need extra real because, since C++11, pow(float, int) returns double + real scale = prec < 0 ? real(pow(real(10), -prec)) : real(1); + os << UTMUPS::EncodeZone(zone, northp, abbrev) << fixed << setfill('0'); + if (isfinite(easting)) { + os << " " << Utility::str(easting / scale, max(0, prec)); + if (prec < 0 && abs(easting / scale) > real(0.5)) + os << setw(-prec) << 0; + } else + os << " nan"; + if (isfinite(northing)) { + os << " " << Utility::str(northing / scale, max(0, prec)); + if (prec < 0 && abs(northing / scale) > real(0.5)) + os << setw(-prec) << 0; + } else + os << " nan"; + utm = os.str(); + } + + string GeoCoords::UTMUPSRepresentation(int prec, bool abbrev) const { + string utm; + UTMUPSString(_zone, _northp, _easting, _northing, prec, abbrev, utm); + return utm; + } + + string GeoCoords::UTMUPSRepresentation(bool northp, int prec, + bool abbrev) const { + real e, n; + int z; + UTMUPS::Transfer(_zone, _northp, _easting, _northing, + _zone, northp, e, n, z); + string utm; + UTMUPSString(_zone, northp, e, n, prec, abbrev, utm); + return utm; + } + + string GeoCoords::AltUTMUPSRepresentation(int prec, bool abbrev) const { + string utm; + UTMUPSString(_alt_zone, _northp, _alt_easting, _alt_northing, prec, + abbrev, utm); + return utm; + } + + string GeoCoords::AltUTMUPSRepresentation(bool northp, int prec, + bool abbrev) const { + real e, n; + int z; + UTMUPS::Transfer(_alt_zone, _northp, _alt_easting, _alt_northing, + _alt_zone, northp, e, n, z); + string utm; + UTMUPSString(_alt_zone, northp, e, n, prec, abbrev, utm); + return utm; + } + + void GeoCoords::FixHemisphere() { + using std::isnan; // Needed for Centos 7, ubuntu 14 + if (_lat == 0 || (_northp && _lat >= 0) || (!_northp && _lat < 0) || + isnan(_lat)) + // Allow either hemisphere for equator + return; + if (_zone != UTMUPS::UPS) { + _northing += (_northp ? 1 : -1) * UTMUPS::UTMShift(); + _northp = !_northp; + } else + throw GeographicErr("Hemisphere mixup"); + } + +} // namespace GeographicLib diff --git a/common/local_libs/GeographicLib/src/Geocentric.cpp b/common/local_libs/GeographicLib/src/Geocentric.cpp new file mode 100644 index 0000000000..956b4f5a47 --- /dev/null +++ b/common/local_libs/GeographicLib/src/Geocentric.cpp @@ -0,0 +1,172 @@ +/** + * \file Geocentric.cpp + * \brief Implementation for GeographicLib::Geocentric class + * + * Copyright (c) Charles Karney (2008-2020) and licensed + * under the MIT/X11 License. For more information, see + * https://geographiclib.sourceforge.io/ + **********************************************************************/ + +#include + +namespace GeographicLib { + + using namespace std; + + Geocentric::Geocentric(real a, real f) + : _a(a) + , _f(f) + , _e2(_f * (2 - _f)) + , _e2m(Math::sq(1 - _f)) // 1 - _e2 + , _e2a(abs(_e2)) + , _e4a(Math::sq(_e2)) + , _maxrad(2 * _a / numeric_limits::epsilon()) + { + if (!(isfinite(_a) && _a > 0)) + throw GeographicErr("Equatorial radius is not positive"); + if (!(isfinite(_f) && _f < 1)) + throw GeographicErr("Polar semi-axis is not positive"); + } + + const Geocentric& Geocentric::WGS84() { + static const Geocentric wgs84(Constants::WGS84_a(), Constants::WGS84_f()); + return wgs84; + } + + void Geocentric::IntForward(real lat, real lon, real h, + real& X, real& Y, real& Z, + real M[dim2_]) const { + real sphi, cphi, slam, clam; + Math::sincosd(Math::LatFix(lat), sphi, cphi); + Math::sincosd(lon, slam, clam); + real n = _a/sqrt(1 - _e2 * Math::sq(sphi)); + Z = (_e2m * n + h) * sphi; + X = (n + h) * cphi; + Y = X * slam; + X *= clam; + if (M) + Rotation(sphi, cphi, slam, clam, M); + } + + void Geocentric::IntReverse(real X, real Y, real Z, + real& lat, real& lon, real& h, + real M[dim2_]) const { + real + R = hypot(X, Y), + slam = R != 0 ? Y / R : 0, + clam = R != 0 ? X / R : 1; + h = hypot(R, Z); // Distance to center of earth + real sphi, cphi; + if (h > _maxrad) { + // We really far away (> 12 million light years); treat the earth as a + // point and h, above, is an acceptable approximation to the height. + // This avoids overflow, e.g., in the computation of disc below. It's + // possible that h has overflowed to inf; but that's OK. + // + // Treat the case X, Y finite, but R overflows to +inf by scaling by 2. + R = hypot(X/2, Y/2); + slam = R != 0 ? (Y/2) / R : 0; + clam = R != 0 ? (X/2) / R : 1; + real H = hypot(Z/2, R); + sphi = (Z/2) / H; + cphi = R / H; + } else if (_e4a == 0) { + // Treat the spherical case. Dealing with underflow in the general case + // with _e2 = 0 is difficult. Origin maps to N pole same as with + // ellipsoid. + real H = hypot(h == 0 ? 1 : Z, R); + sphi = (h == 0 ? 1 : Z) / H; + cphi = R / H; + h -= _a; + } else { + // Treat prolate spheroids by swapping R and Z here and by switching + // the arguments to phi = atan2(...) at the end. + real + p = Math::sq(R / _a), + q = _e2m * Math::sq(Z / _a), + r = (p + q - _e4a) / 6; + if (_f < 0) swap(p, q); + if ( !(_e4a * 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 = _e4a * p * q / 4, // S = r^3 * s + r2 = Math::sq(r), + r3 = r * r2, + disc = S * (2 * r3 + S); + real u = r; + if (disc >= 0) { + real T3 = S + r3; + // 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. cbrt always returns the real root. cbrt(-8) = -2. + real T = cbrt(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); + } + real + v = sqrt(Math::sq(u) + _e4a * q), // guaranteed positive + // Avoid loss of accuracy when u < 0. Underflow doesn't occur in + // e4 * q / (v - u) because u ~ e^4 when q is small and u < 0. + uv = u < 0 ? _e4a * q / (v - u) : u + v, // u+v, guaranteed positive + // Need to guard against w going negative due to roundoff in uv - q. + w = max(real(0), _e2a * (uv - q) / (2 * v)), + // 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 + Math::sq(w)) + w), + k1 = _f >= 0 ? k : k - _e2, + k2 = _f >= 0 ? k + _e2 : k, + d = k1 * R / k2, + H = hypot(Z/k1, R/k2); + sphi = (Z/k1) / H; + cphi = (R/k2) / H; + h = (1 - _e2m/k1) * hypot(d, Z); + } else { // e4 * q == 0 && r <= 0 + // This leads to k = 0 (oblate, equatorial plane) and k + e^2 = 0 + // (prolate, rotation axis) and the generation of 0/0 in the general + // formulas for phi and h. using the general formula and division by 0 + // in formula for h. So handle this case by taking the limits: + // f > 0: z -> 0, k -> e2 * sqrt(q)/sqrt(e4 - p) + // f < 0: R -> 0, k + e2 -> - e2 * sqrt(q)/sqrt(e4 - p) + real + zz = sqrt((_f >= 0 ? _e4a - p : p) / _e2m), + xx = sqrt( _f < 0 ? _e4a - p : p ), + H = hypot(zz, xx); + sphi = zz / H; + cphi = xx / H; + if (Z < 0) sphi = -sphi; // for tiny negative Z (not for prolate) + h = - _a * (_f >= 0 ? _e2m : 1) * H / _e2a; + } + } + lat = Math::atan2d(sphi, cphi); + lon = Math::atan2d(slam, clam); + if (M) + Rotation(sphi, cphi, slam, clam, M); + } + + void Geocentric::Rotation(real sphi, real cphi, real slam, real clam, + real M[dim2_]) { + // This rotation matrix is given by the following quaternion operations + // qrot(lam, [0,0,1]) * qrot(phi, [0,-1,0]) * [1,1,1,1]/2 + // or + // qrot(pi/2 + lam, [0,0,1]) * qrot(-pi/2 + phi , [-1,0,0]) + // where + // qrot(t,v) = [cos(t/2), sin(t/2)*v[1], sin(t/2)*v[2], sin(t/2)*v[3]] + + // Local X axis (east) in geocentric coords + M[0] = -slam; M[3] = clam; M[6] = 0; + // Local Y axis (north) in geocentric coords + M[1] = -clam * sphi; M[4] = -slam * sphi; M[7] = cphi; + // Local Z axis (up) in geocentric coords + M[2] = clam * cphi; M[5] = slam * cphi; M[8] = sphi; + } + +} // namespace GeographicLib diff --git a/common/local_libs/GeographicLib/src/Geodesic.cpp b/common/local_libs/GeographicLib/src/Geodesic.cpp new file mode 100644 index 0000000000..5b59fab169 --- /dev/null +++ b/common/local_libs/GeographicLib/src/Geodesic.cpp @@ -0,0 +1,1900 @@ +/** + * \file Geodesic.cpp + * \brief Implementation for GeographicLib::Geodesic class + * + * Copyright (c) Charles Karney (2009-2020) and licensed + * under the MIT/X11 License. For more information, see + * https://geographiclib.sourceforge.io/ + * + * This is a reformulation of the geodesic problem. The notation is as + * follows: + * - at a general point (no suffix or 1 or 2 as suffix) + * - phi = latitude + * - beta = latitude on auxiliary sphere + * - omega = longitude on auxiliary sphere + * - lambda = longitude + * - alpha = azimuth of great circle + * - sigma = arc length along great circle + * - s = distance + * - tau = scaled distance (= sigma at multiples of pi/2) + * - at northwards equator crossing + * - beta = phi = 0 + * - omega = lambda = 0 + * - alpha = alpha0 + * - sigma = s = 0 + * - a 12 suffix means a difference, e.g., s12 = s2 - s1. + * - s and c prefixes mean sin and cos + **********************************************************************/ + +#include +#include + +#if defined(_MSC_VER) +// Squelch warnings about potentially uninitialized local variables and +// constant conditional expressions +# pragma warning (disable: 4701 4127) +#endif + +namespace GeographicLib { + + using namespace std; + + Geodesic::Geodesic(real a, real f) + : maxit2_(maxit1_ + Math::digits() + 10) + // Underflow guard. We require + // tiny_ * epsilon() > 0 + // tiny_ + epsilon() == epsilon() + , tiny_(sqrt(numeric_limits::min())) + , tol0_(numeric_limits::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_)) + , tolb_(tol0_ * tol2_) // Check on bisection interval + , xthresh_(1000 * tol2_) + , _a(a) + , _f(f) + , _f1(1 - _f) + , _e2(_f * (2 - _f)) + , _ep2(_e2 / Math::sq(_f1)) // e2 / (1 - e2) + , _n(_f / ( 2 - _f)) + , _b(_a * _f1) + , _c2((Math::sq(_a) + Math::sq(_b) * + (_e2 == 0 ? 1 : + Math::eatanhe(real(1), (_f < 0 ? -1 : 1) * sqrt(abs(_e2))) / _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. + , _etol2(real(0.1) * tol2_ / + sqrt( max(real(0.001), abs(_f)) * min(real(1), 1 - _f/2) / 2 )) + { + if (!(isfinite(_a) && _a > 0)) + throw GeographicErr("Equatorial radius is not positive"); + if (!(isfinite(_b) && _b > 0)) + throw GeographicErr("Polar semi-axis is not positive"); + A3coeff(); + C3coeff(); + C4coeff(); + } + + const Geodesic& Geodesic::WGS84() { + static const Geodesic wgs84(Constants::WGS84_a(), Constants::WGS84_f()); + return wgs84; + } + + Math::real Geodesic::SinCosSeries(bool 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 + c += (n + sinp); // Point to one beyond last element + real + 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) + } + + GeodesicLine Geodesic::Line(real lat1, real lon1, real azi1, + unsigned caps) const { + return GeodesicLine(*this, lat1, lon1, azi1, caps); + } + + Math::real Geodesic::GenDirect(real lat1, real lon1, real azi1, + bool arcmode, real s12_a12, unsigned outmask, + real& lat2, real& lon2, real& azi2, + real& s12, real& m12, real& M12, real& M21, + real& S12) const { + // Automatically supply DISTANCE_IN if necessary + if (!arcmode) outmask |= DISTANCE_IN; + return GeodesicLine(*this, lat1, lon1, azi1, outmask) + . // Note the dot! + GenPosition(arcmode, s12_a12, outmask, + lat2, lon2, azi2, s12, m12, M12, M21, S12); + } + + GeodesicLine Geodesic::GenDirectLine(real lat1, real lon1, real azi1, + bool arcmode, real s12_a12, + unsigned caps) const { + azi1 = Math::AngNormalize(azi1); + real salp1, calp1; + // Guard against underflow in salp0. Also -0 is converted to +0. + Math::sincosd(Math::AngRound(azi1), salp1, calp1); + // Automatically supply DISTANCE_IN if necessary + if (!arcmode) caps |= DISTANCE_IN; + return GeodesicLine(*this, lat1, lon1, azi1, salp1, calp1, + caps, arcmode, s12_a12); + } + + GeodesicLine Geodesic::DirectLine(real lat1, real lon1, real azi1, real s12, + unsigned caps) const { + return GenDirectLine(lat1, lon1, azi1, false, s12, caps); + } + + GeodesicLine Geodesic::ArcDirectLine(real lat1, real lon1, real azi1, + real a12, unsigned caps) const { + return GenDirectLine(lat1, lon1, azi1, true, a12, caps); + } + + Math::real Geodesic::GenInverse(real lat1, real lon1, real lat2, real lon2, + unsigned outmask, real& s12, + real& salp1, real& calp1, + real& salp2, real& calp2, + real& m12, real& M12, real& M21, + real& S12) const { + // 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. + real lon12s, lon12 = Math::AngDiff(lon1, lon2, lon12s); + // Make longitude difference positive. + int lonsign = lon12 >= 0 ? 1 : -1; + // If very close to being on the same half-meridian, then make it so. + lon12 = lonsign * Math::AngRound(lon12); + lon12s = Math::AngRound((180 - lon12) - lonsign * lon12s); + real + lam12 = lon12 * Math::degree(), + slam12, clam12; + if (lon12 > 90) { + Math::sincosd(lon12s, slam12, clam12); + clam12 = -clam12; + } else + Math::sincosd(lon12, slam12, clam12); + + // If really close to the equator, treat as on equator. + lat1 = Math::AngRound(Math::LatFix(lat1)); + lat2 = Math::AngRound(Math::LatFix(lat2)); + // Swap points so that point with higher (abs) latitude is point 1. + // If one latitude is a nan, then it becomes lat1. + int swapp = abs(lat1) < abs(lat2) ? -1 : 1; + if (swapp < 0) { + lonsign *= -1; + swap(lat1, lat2); + } + // Make lat1 <= 0 + int 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. + + real sbet1, cbet1, sbet2, cbet2, s12x=0, m12x=0; + + Math::sincosd(lat1, sbet1, cbet1); sbet1 *= _f1; + // Ensure cbet1 = +epsilon at poles; doing the fix on beta means that sig12 + // will be <= 2*tiny for two points at the same pole. + Math::norm(sbet1, cbet1); cbet1 = max(tiny_, cbet1); + + Math::sincosd(lat2, sbet2, cbet2); sbet2 *= _f1; + // Ensure cbet2 = +epsilon at poles + Math::norm(sbet2, cbet2); cbet2 = max(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 (abs(sbet2) == -sbet1) + cbet2 = cbet1; + } + + real + dn1 = sqrt(1 + _ep2 * Math::sq(sbet1)), + dn2 = sqrt(1 + _ep2 * Math::sq(sbet2)); + + real a12=0, sig12; + // index zero element of this array is unused + real Ca[nC_]; + + bool meridian = lat1 == -90 || slam12 == 0; + + if (meridian) { + + // Endpoints are on a single full meridian, so the geodesic might lie on + // a meridian. + + calp1 = clam12; salp1 = slam12; // Head to the target longitude + calp2 = 1; salp2 = 0; // At the target we're heading north + + real + // tan(bet) = tan(sig) * cos(alp) + ssig1 = sbet1, csig1 = calp1 * cbet1, + ssig2 = sbet2, csig2 = calp2 * cbet2; + + // sig12 = sig2 - sig1 + sig12 = atan2(max(real(0), csig1 * ssig2 - ssig1 * csig2), + csig1 * csig2 + ssig1 * ssig2); + { + real dummy; + Lengths(_n, sig12, ssig1, csig1, dn1, ssig2, csig2, dn2, cbet1, cbet2, + outmask | DISTANCE | REDUCEDLENGTH, + s12x, m12x, dummy, M12, M21, 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 *= _b; + s12x *= _b; + a12 = sig12 / Math::degree(); + } else + // m12 < 0, i.e., prolate and too close to anti-podal + meridian = false; + } + + // somg12 > 1 marks that it needs to be calculated + real omg12 = 0, somg12 = 2, comg12 = 0; + if (!meridian && + sbet1 == 0 && // and sbet2 == 0 + (_f <= 0 || lon12s >= _f * 180)) { + + // Geodesic runs along equator + calp1 = calp2 = 0; salp1 = salp2 = 1; + s12x = _a * lam12; + sig12 = omg12 = lam12 / _f1; + m12x = _b * sin(sig12); + if (outmask & GEODESICSCALE) + M12 = M21 = cos(sig12); + a12 = lon12 / _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; + sig12 = InverseStart(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 * _b * dnm; + m12x = Math::sq(dnm) * _b * sin(sig12 / dnm); + if (outmask & GEODESICSCALE) + M12 = M21 = cos(sig12 / dnm); + a12 = sig12 / Math::degree(); + omg12 = lam12 / (_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. + // + // initial values to suppress warnings (if loop is executed 0 times) + 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; + for (bool tripn = false, tripb = false; + numit < maxit2_ || GEOGRAPHICLIB_PANIC; + ++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; + real v = Lambda12(sbet1, cbet1, dn1, sbet2, cbet2, dn2, salp1, calp1, + slam12, clam12, + salp2, calp2, sig12, ssig1, csig1, ssig2, csig2, + eps, domg12, numit < maxit1_, dv, Ca); + // Reversed test to allow escape with NaNs + if (tripb || !(abs(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 && abs(dalp1) < Math::pi()) { + calp1 = calp1 * cdalp1 - salp1 * sdalp1; + salp1 = nsalp1; + Math::norm(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 = abs(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; + Math::norm(salp1, calp1); + tripn = false; + tripb = (abs(salp1a - salp1) + (calp1a - calp1) < tolb_ || + abs(salp1 - salp1b) + (calp1 - calp1b) < tolb_); + } + { + real dummy; + // Ensure that the reduced length and geodesic scale are computed in + // a "canonical" way, with the I2 integral. + unsigned lengthmask = outmask | + (outmask & (REDUCEDLENGTH | GEODESICSCALE) ? DISTANCE : NONE); + Lengths(eps, sig12, ssig1, csig1, dn1, ssig2, csig2, dn2, + cbet1, cbet2, lengthmask, s12x, m12x, dummy, M12, M21, Ca); + } + m12x *= _b; + s12x *= _b; + a12 = sig12 / Math::degree(); + if (outmask & AREA) { + // omg12 = lam12 - domg12 + real sdomg12 = sin(domg12), cdomg12 = cos(domg12); + somg12 = slam12 * cdomg12 - clam12 * sdomg12; + comg12 = clam12 * cdomg12 + slam12 * sdomg12; + } + } + } + + if (outmask & DISTANCE) + s12 = 0 + s12x; // Convert -0 to 0 + + if (outmask & REDUCEDLENGTH) + m12 = 0 + m12x; // Convert -0 to 0 + + if (outmask & AREA) { + real + // From Lambda12: sin(alp1) * cos(bet1) = sin(alp0) + salp0 = salp1 * cbet1, + calp0 = hypot(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 = Math::sq(calp0) * _ep2, + eps = k2 / (2 * (1 + sqrt(1 + k2)) + k2), + // Multiplier = a^2 * e^2 * cos(alpha0) * sin(alpha0). + A4 = Math::sq(_a) * calp0 * salp0 * _e2; + Math::norm(ssig1, csig1); + Math::norm(ssig2, csig2); + C4f(eps, Ca); + real + 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 += _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) { + swap(salp1, salp2); + swap(calp1, calp2); + if (outmask & GEODESICSCALE) + swap(M12, M21); + } + + salp1 *= swapp * lonsign; calp1 *= swapp * latsign; + salp2 *= swapp * lonsign; calp2 *= swapp * latsign; + + // Returned value in [0, 180] + return a12; + } + + Math::real Geodesic::GenInverse(real lat1, real lon1, real lat2, real lon2, + unsigned outmask, + real& s12, real& azi1, real& azi2, + real& m12, real& M12, real& M21, + real& S12) const { + outmask &= OUT_MASK; + real salp1, calp1, salp2, calp2, + a12 = GenInverse(lat1, lon1, lat2, lon2, + outmask, s12, salp1, calp1, salp2, calp2, + m12, M12, M21, S12); + if (outmask & AZIMUTH) { + azi1 = Math::atan2d(salp1, calp1); + azi2 = Math::atan2d(salp2, calp2); + } + return a12; + } + + GeodesicLine Geodesic::InverseLine(real lat1, real lon1, + real lat2, real lon2, + unsigned caps) const { + real t, salp1, calp1, salp2, calp2, + a12 = GenInverse(lat1, lon1, lat2, lon2, + // No need to specify AZIMUTH here + 0u, t, salp1, calp1, salp2, calp2, + t, t, t, t), + azi1 = Math::atan2d(salp1, calp1); + // Ensure that a12 can be converted to a distance + if (caps & (OUT_MASK & DISTANCE_IN)) caps |= DISTANCE; + return + GeodesicLine(*this, lat1, lon1, azi1, salp1, calp1, caps, true, a12); + } + + void Geodesic::Lengths(real eps, real sig12, + real ssig1, real csig1, real dn1, + real ssig2, real csig2, real dn2, + real cbet1, real cbet2, unsigned outmask, + real& s12b, real& m12b, real& m0, + real& M12, real& M21, + // Scratch area of the right size + real Ca[]) const { + // Return m12b = (reduced length)/_b; also calculate s12b = distance/_b, + // and m0 = coefficient of secular term in expression for reduced length. + + outmask &= OUT_MASK; + // outmask & DISTANCE: set s12b + // outmask & REDUCEDLENGTH: set m12b & m0 + // outmask & GEODESICSCALE: set M12 & M21 + + real m0x = 0, J12 = 0, A1 = 0, A2 = 0; + real Cb[nC2_ + 1]; + if (outmask & (DISTANCE | REDUCEDLENGTH | GEODESICSCALE)) { + A1 = A1m1f(eps); + C1f(eps, Ca); + if (outmask & (REDUCEDLENGTH | GEODESICSCALE)) { + A2 = A2m1f(eps); + C2f(eps, Cb); + m0x = A1 - A2; + A2 = 1 + A2; + } + A1 = 1 + A1; + } + if (outmask & DISTANCE) { + real B1 = SinCosSeries(true, ssig2, csig2, Ca, nC1_) - + SinCosSeries(true, ssig1, csig1, Ca, nC1_); + // Missing a factor of _b + s12b = A1 * (sig12 + B1); + if (outmask & (REDUCEDLENGTH | GEODESICSCALE)) { + real B2 = SinCosSeries(true, ssig2, csig2, Cb, nC2_) - + SinCosSeries(true, ssig1, csig1, Cb, nC2_); + J12 = m0x * sig12 + (A1 * B1 - A2 * B2); + } + } else if (outmask & (REDUCEDLENGTH | GEODESICSCALE)) { + // Assume here that nC1_ >= nC2_ + for (int l = 1; l <= nC2_; ++l) + Cb[l] = A1 * Ca[l] - A2 * Cb[l]; + J12 = m0x * sig12 + (SinCosSeries(true, ssig2, csig2, Cb, nC2_) - + SinCosSeries(true, ssig1, csig1, Cb, nC2_)); + } + if (outmask & REDUCEDLENGTH) { + m0 = m0x; + // Missing a factor of _b. + // Add parens around (csig1 * ssig2) and (ssig1 * csig2) to ensure + // accurate cancellation in the case of coincident points. + m12b = dn2 * (csig1 * ssig2) - dn1 * (ssig1 * csig2) - + csig1 * csig2 * J12; + } + if (outmask & GEODESICSCALE) { + real csig12 = csig1 * csig2 + ssig1 * ssig2; + real t = _ep2 * (cbet1 - cbet2) * (cbet1 + cbet2) / (dn1 + dn2); + M12 = csig12 + (t * ssig2 - csig2 * J12) * ssig1 / dn1; + M21 = csig12 - (t * ssig1 - csig1 * J12) * ssig2 / dn2; + } + } + + Math::real Geodesic::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 = Math::sq(x), + q = Math::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 = Math::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; + if (disc >= 0) { + real T3 = S + r3; + // 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. cbrt always returns the real root. cbrt(-8) = -2. + real T = cbrt(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); + } + real + v = sqrt(Math::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 + Math::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; + } + + Math::real Geodesic::InverseStart(real sbet1, real cbet1, real dn1, + real sbet2, real cbet2, real dn2, + real lam12, real slam12, real clam12, + real& salp1, real& calp1, + // Only updated if return val >= 0 + real& salp2, real& calp2, + // Only updated for short lines + real& dnm, + // Scratch area of the right size + real Ca[]) const { + // 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 = sbet2 * cbet1 + cbet2 * sbet1; + bool shortline = cbet12 >= 0 && sbet12 < real(0.5) && + cbet2 * lam12 < real(0.5); + real somg12, comg12; + if (shortline) { + real sbetm2 = Math::sq(sbet1 + sbet2); + // sin((bet1+bet2)/2)^2 + // = (sbet1 + sbet2)^2 / ((sbet1 + sbet2)^2 + (cbet1 + cbet2)^2) + sbetm2 /= sbetm2 + Math::sq(cbet1 + cbet2); + dnm = sqrt(1 + _ep2 * sbetm2); + real omg12 = lam12 / (_f1 * dnm); + somg12 = sin(omg12); comg12 = cos(omg12); + } else { + somg12 = slam12; comg12 = clam12; + } + + salp1 = cbet2 * somg12; + calp1 = comg12 >= 0 ? + sbet12 + cbet2 * sbet1 * Math::sq(somg12) / (1 + comg12) : + sbet12a - cbet2 * sbet1 * Math::sq(somg12) / (1 - comg12); + + real + ssig12 = hypot(salp1, calp1), + csig12 = sbet1 * sbet2 + cbet1 * cbet2 * comg12; + + if (shortline && ssig12 < _etol2) { + // really short lines + salp2 = cbet1 * somg12; + calp2 = sbet12 - cbet1 * sbet2 * + (comg12 >= 0 ? Math::sq(somg12) / (1 + comg12) : 1 - comg12); + Math::norm(salp2, calp2); + // Set return value + sig12 = atan2(ssig12, csig12); + } else if (abs(_n) > real(0.1) || // Skip astroid calc if too eccentric + csig12 >= 0 || + ssig12 >= 6 * abs(_n) * Math::pi() * Math::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 + GEOGRAPHICLIB_VOLATILE real x; + real lam12x = atan2(-slam12, -clam12); // lam12 - pi + if (_f >= 0) { // In fact f == 0 does not get here + // x = dlong, y = dlat + { + real + k2 = Math::sq(sbet1) * _ep2, + eps = k2 / (2 * (1 + sqrt(1 + k2)) + k2); + lamscale = _f * cbet1 * A3f(eps) * Math::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, dummy; + // In the case of lon12 = 180, this repeats a calculation made in + // Inverse. + Lengths(_n, Math::pi() + bet12a, + sbet1, -cbet1, dn1, sbet2, cbet2, dn2, + cbet1, cbet2, + REDUCEDLENGTH, dummy, m12b, m0, dummy, dummy, Ca); + x = -1 + m12b / (cbet1 * cbet2 * m0 * Math::pi()); + betscale = x < -real(0.01) ? sbet12a / x : + -_f * Math::sq(cbet1) * Math::pi(); + lamscale = betscale / cbet1; + y = lam12x / lamscale; + } + + if (y > -tol1_ && x > -1 - xthresh_) { + // strip near cut + // Need real(x) here to cast away the volatility of x for min/max + if (_f >= 0) { + salp1 = min(real(1), -real(x)); calp1 = - sqrt(1 - Math::sq(salp1)); + } else { + calp1 = max(real(x > -tol1_ ? 0 : -1), real(x)); + salp1 = sqrt(1 - Math::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 * ( _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 * Math::sq(somg12) / (1 - comg12); + } + } + // Sanity check on starting guess. Backwards check allows NaN through. + if (!(salp1 <= 0)) + Math::norm(salp1, calp1); + else { + salp1 = 1; calp1 = 0; + } + return sig12; + } + + Math::real Geodesic::Lambda12(real sbet1, real cbet1, real dn1, + real sbet2, real cbet2, real dn2, + real salp1, real calp1, + real slam120, real clam120, + real& salp2, real& calp2, + real& sig12, + real& ssig1, real& csig1, + real& ssig2, real& csig2, + real& eps, real& domg12, + bool diffp, real& dlam12, + // Scratch area of the right size + real Ca[]) const { + + if (sbet1 == 0 && calp1 == 0) + // Break degeneracy of equatorial line. This case has already been + // handled. + calp1 = -tiny_; + + real + // sin(alp1) * cos(bet1) = sin(alp0) + salp0 = salp1 * cbet1, + calp0 = hypot(calp1, salp1 * sbet1); // calp0 > 0 + + real somg1, comg1, somg2, comg2, somg12, comg12, lam12; + // 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; + Math::norm(ssig1, csig1); + // Math::norm(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 || abs(sbet2) != -sbet1 ? + sqrt(Math::sq(calp1 * cbet1) + + (cbet1 < -sbet1 ? + (cbet2 - cbet1) * (cbet1 + cbet2) : + (sbet1 - sbet2) * (sbet1 + sbet2))) / cbet2 : + abs(calp1); + // tan(bet2) = tan(sig2) * cos(alp2) + // tan(omg2) = sin(alp0) * tan(sig2). + ssig2 = sbet2; somg2 = salp0 * sbet2; + csig2 = comg2 = calp2 * cbet2; + Math::norm(ssig2, csig2); + // Math::norm(somg2, comg2); -- don't need to normalize! + + // sig12 = sig2 - sig1, limit to [0, pi] + sig12 = atan2(max(real(0), csig1 * ssig2 - ssig1 * csig2), + csig1 * csig2 + ssig1 * ssig2); + + // omg12 = omg2 - omg1, limit to [0, pi] + somg12 = max(real(0), comg1 * somg2 - somg1 * comg2); + comg12 = comg1 * comg2 + somg1 * somg2; + // eta = omg12 - lam120 + real eta = atan2(somg12 * clam120 - comg12 * slam120, + comg12 * clam120 + somg12 * slam120); + real B312; + real k2 = Math::sq(calp0) * _ep2; + eps = k2 / (2 * (1 + sqrt(1 + k2)) + k2); + C3f(eps, Ca); + B312 = (SinCosSeries(true, ssig2, csig2, Ca, nC3_-1) - + SinCosSeries(true, ssig1, csig1, Ca, nC3_-1)); + domg12 = -_f * A3f(eps) * salp0 * (sig12 + B312); + lam12 = eta + domg12; + + if (diffp) { + if (calp2 == 0) + dlam12 = - 2 * _f1 * dn1 / sbet1; + else { + real dummy; + Lengths(eps, sig12, ssig1, csig1, dn1, ssig2, csig2, dn2, + cbet1, cbet2, REDUCEDLENGTH, + dummy, dlam12, dummy, dummy, dummy, Ca); + dlam12 *= _f1 / (calp2 * cbet2); + } + } + + return lam12; + } + + Math::real Geodesic::A3f(real eps) const { + // Evaluate A3 + return Math::polyval(nA3_ - 1, _A3x, eps); + } + + void Geodesic::C3f(real eps, real c[]) const { + // Evaluate C3 coeffs + // Elements c[1] thru c[nC3_ - 1] are set + real mult = 1; + int o = 0; + for (int 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 * Math::polyval(m, _C3x + o, eps); + o += m + 1; + } + // Post condition: o == nC3x_ + } + + void Geodesic::C4f(real eps, real c[]) const { + // Evaluate C4 coeffs + // Elements c[0] thru c[nC4_ - 1] are set + real mult = 1; + int o = 0; + for (int l = 0; l < nC4_; ++l) { // l is index of C4[l] + int m = nC4_ - l - 1; // order of polynomial in eps + c[l] = mult * Math::polyval(m, _C4x + o, eps); + o += m + 1; + mult *= eps; + } + // Post condition: o == nC4x_ + } + + // The static const coefficient arrays in the following functions are + // generated by Maxima and give the coefficients of the Taylor expansions for + // the geodesics. The convention on the order of these coefficients is as + // follows: + // + // ascending order in the trigonometric expansion, + // then powers of eps in descending order, + // finally powers of n in descending order. + // + // (For some expansions, only a subset of levels occur.) For each polynomial + // of order n at the lowest level, the (n+1) coefficients of the polynomial + // are followed by a divisor which is applied to the whole polynomial. In + // this way, the coefficients are expressible with no round off error. The + // sizes of the coefficient arrays are: + // + // A1m1f, A2m1f = floor(N/2) + 2 + // C1f, C1pf, C2f, A3coeff = (N^2 + 7*N - 2*floor(N/2)) / 4 + // C3coeff = (N - 1) * (N^2 + 7*N - 2*floor(N/2)) / 8 + // C4coeff = N * (N + 1) * (N + 5) / 6 + // + // where N = GEOGRAPHICLIB_GEODESIC_ORDER + // = nA1 = nA2 = nC1 = nC1p = nA3 = nC4 + + // The scale factor A1-1 = mean value of (d/dsigma)I1 - 1 + Math::real Geodesic::A1m1f(real eps) { + // Generated by Maxima on 2015-05-05 18:08:12-04:00 +#if GEOGRAPHICLIB_GEODESIC_ORDER/2 == 1 + static const real coeff[] = { + // (1-eps)*A1-1, polynomial in eps2 of order 1 + 1, 0, 4, + }; +#elif GEOGRAPHICLIB_GEODESIC_ORDER/2 == 2 + static const real coeff[] = { + // (1-eps)*A1-1, polynomial in eps2 of order 2 + 1, 16, 0, 64, + }; +#elif GEOGRAPHICLIB_GEODESIC_ORDER/2 == 3 + static const real coeff[] = { + // (1-eps)*A1-1, polynomial in eps2 of order 3 + 1, 4, 64, 0, 256, + }; +#elif GEOGRAPHICLIB_GEODESIC_ORDER/2 == 4 + static const real coeff[] = { + // (1-eps)*A1-1, polynomial in eps2 of order 4 + 25, 64, 256, 4096, 0, 16384, + }; +#else +#error "Bad value for GEOGRAPHICLIB_GEODESIC_ORDER" +#endif + static_assert(sizeof(coeff) / sizeof(real) == nA1_/2 + 2, + "Coefficient array size mismatch in A1m1f"); + int m = nA1_/2; + real t = Math::polyval(m, coeff, Math::sq(eps)) / coeff[m + 1]; + return (t + eps) / (1 - eps); + } + + // The coefficients C1[l] in the Fourier expansion of B1 + void Geodesic::C1f(real eps, real c[]) { + // Generated by Maxima on 2015-05-05 18:08:12-04:00 +#if GEOGRAPHICLIB_GEODESIC_ORDER == 3 + static const real coeff[] = { + // C1[1]/eps^1, polynomial in eps2 of order 1 + 3, -8, 16, + // C1[2]/eps^2, polynomial in eps2 of order 0 + -1, 16, + // C1[3]/eps^3, polynomial in eps2 of order 0 + -1, 48, + }; +#elif GEOGRAPHICLIB_GEODESIC_ORDER == 4 + static const real coeff[] = { + // C1[1]/eps^1, polynomial in eps2 of order 1 + 3, -8, 16, + // C1[2]/eps^2, polynomial in eps2 of order 1 + 1, -2, 32, + // C1[3]/eps^3, polynomial in eps2 of order 0 + -1, 48, + // C1[4]/eps^4, polynomial in eps2 of order 0 + -5, 512, + }; +#elif GEOGRAPHICLIB_GEODESIC_ORDER == 5 + 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 1 + 1, -2, 32, + // C1[3]/eps^3, polynomial in eps2 of order 1 + 9, -16, 768, + // C1[4]/eps^4, polynomial in eps2 of order 0 + -5, 512, + // C1[5]/eps^5, polynomial in eps2 of order 0 + -7, 1280, + }; +#elif GEOGRAPHICLIB_GEODESIC_ORDER == 6 + 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, + }; +#elif GEOGRAPHICLIB_GEODESIC_ORDER == 7 + static const real coeff[] = { + // C1[1]/eps^1, polynomial in eps2 of order 3 + 19, -64, 384, -1024, 2048, + // C1[2]/eps^2, polynomial in eps2 of order 2 + -9, 64, -128, 2048, + // C1[3]/eps^3, polynomial in eps2 of order 2 + -9, 72, -128, 6144, + // C1[4]/eps^4, polynomial in eps2 of order 1 + 3, -5, 512, + // C1[5]/eps^5, polynomial in eps2 of order 1 + 35, -56, 10240, + // C1[6]/eps^6, polynomial in eps2 of order 0 + -7, 2048, + // C1[7]/eps^7, polynomial in eps2 of order 0 + -33, 14336, + }; +#elif GEOGRAPHICLIB_GEODESIC_ORDER == 8 + static const real coeff[] = { + // C1[1]/eps^1, polynomial in eps2 of order 3 + 19, -64, 384, -1024, 2048, + // C1[2]/eps^2, polynomial in eps2 of order 3 + 7, -18, 128, -256, 4096, + // C1[3]/eps^3, polynomial in eps2 of order 2 + -9, 72, -128, 6144, + // C1[4]/eps^4, polynomial in eps2 of order 2 + -11, 96, -160, 16384, + // C1[5]/eps^5, polynomial in eps2 of order 1 + 35, -56, 10240, + // C1[6]/eps^6, polynomial in eps2 of order 1 + 9, -14, 4096, + // C1[7]/eps^7, polynomial in eps2 of order 0 + -33, 14336, + // C1[8]/eps^8, polynomial in eps2 of order 0 + -429, 262144, + }; +#else +#error "Bad value for GEOGRAPHICLIB_GEODESIC_ORDER" +#endif + static_assert(sizeof(coeff) / sizeof(real) == + (nC1_*nC1_ + 7*nC1_ - 2*(nC1_/2)) / 4, + "Coefficient array size mismatch in C1f"); + real + eps2 = Math::sq(eps), + d = eps; + int o = 0; + for (int 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 * Math::polyval(m, coeff + o, eps2) / coeff[o + m + 1]; + o += m + 2; + d *= eps; + } + // Post condition: o == sizeof(coeff) / sizeof(real) + } + + // The coefficients C1p[l] in the Fourier expansion of B1p + void Geodesic::C1pf(real eps, real c[]) { + // Generated by Maxima on 2015-05-05 18:08:12-04:00 +#if GEOGRAPHICLIB_GEODESIC_ORDER == 3 + static const real coeff[] = { + // C1p[1]/eps^1, polynomial in eps2 of order 1 + -9, 16, 32, + // C1p[2]/eps^2, polynomial in eps2 of order 0 + 5, 16, + // C1p[3]/eps^3, polynomial in eps2 of order 0 + 29, 96, + }; +#elif GEOGRAPHICLIB_GEODESIC_ORDER == 4 + static const real coeff[] = { + // C1p[1]/eps^1, polynomial in eps2 of order 1 + -9, 16, 32, + // C1p[2]/eps^2, polynomial in eps2 of order 1 + -37, 30, 96, + // C1p[3]/eps^3, polynomial in eps2 of order 0 + 29, 96, + // C1p[4]/eps^4, polynomial in eps2 of order 0 + 539, 1536, + }; +#elif GEOGRAPHICLIB_GEODESIC_ORDER == 5 + 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 1 + -37, 30, 96, + // C1p[3]/eps^3, polynomial in eps2 of order 1 + -225, 116, 384, + // C1p[4]/eps^4, polynomial in eps2 of order 0 + 539, 1536, + // C1p[5]/eps^5, polynomial in eps2 of order 0 + 3467, 7680, + }; +#elif GEOGRAPHICLIB_GEODESIC_ORDER == 6 + 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, + }; +#elif GEOGRAPHICLIB_GEODESIC_ORDER == 7 + static const real coeff[] = { + // C1p[1]/eps^1, polynomial in eps2 of order 3 + -4879, 9840, -20736, 36864, 73728, + // C1p[2]/eps^2, polynomial in eps2 of order 2 + 4005, -4736, 3840, 12288, + // C1p[3]/eps^3, polynomial in eps2 of order 2 + 8703, -7200, 3712, 12288, + // C1p[4]/eps^4, polynomial in eps2 of order 1 + -7173, 2695, 7680, + // C1p[5]/eps^5, polynomial in eps2 of order 1 + -141115, 41604, 92160, + // C1p[6]/eps^6, polynomial in eps2 of order 0 + 38081, 61440, + // C1p[7]/eps^7, polynomial in eps2 of order 0 + 459485, 516096, + }; +#elif GEOGRAPHICLIB_GEODESIC_ORDER == 8 + static const real coeff[] = { + // C1p[1]/eps^1, polynomial in eps2 of order 3 + -4879, 9840, -20736, 36864, 73728, + // C1p[2]/eps^2, polynomial in eps2 of order 3 + -86171, 120150, -142080, 115200, 368640, + // C1p[3]/eps^3, polynomial in eps2 of order 2 + 8703, -7200, 3712, 12288, + // C1p[4]/eps^4, polynomial in eps2 of order 2 + 1082857, -688608, 258720, 737280, + // C1p[5]/eps^5, polynomial in eps2 of order 1 + -141115, 41604, 92160, + // C1p[6]/eps^6, polynomial in eps2 of order 1 + -2200311, 533134, 860160, + // C1p[7]/eps^7, polynomial in eps2 of order 0 + 459485, 516096, + // C1p[8]/eps^8, polynomial in eps2 of order 0 + 109167851, 82575360, + }; +#else +#error "Bad value for GEOGRAPHICLIB_GEODESIC_ORDER" +#endif + static_assert(sizeof(coeff) / sizeof(real) == + (nC1p_*nC1p_ + 7*nC1p_ - 2*(nC1p_/2)) / 4, + "Coefficient array size mismatch in C1pf"); + real + eps2 = Math::sq(eps), + d = eps; + int o = 0; + for (int 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 * Math::polyval(m, coeff + o, eps2) / coeff[o + m + 1]; + o += m + 2; + d *= eps; + } + // Post condition: o == sizeof(coeff) / sizeof(real) + } + + // The scale factor A2-1 = mean value of (d/dsigma)I2 - 1 + Math::real Geodesic::A2m1f(real eps) { + // Generated by Maxima on 2015-05-29 08:09:47-04:00 +#if GEOGRAPHICLIB_GEODESIC_ORDER/2 == 1 + static const real coeff[] = { + // (eps+1)*A2-1, polynomial in eps2 of order 1 + -3, 0, 4, + }; // count = 3 +#elif GEOGRAPHICLIB_GEODESIC_ORDER/2 == 2 + static const real coeff[] = { + // (eps+1)*A2-1, polynomial in eps2 of order 2 + -7, -48, 0, 64, + }; // count = 4 +#elif GEOGRAPHICLIB_GEODESIC_ORDER/2 == 3 + static const real coeff[] = { + // (eps+1)*A2-1, polynomial in eps2 of order 3 + -11, -28, -192, 0, 256, + }; // count = 5 +#elif GEOGRAPHICLIB_GEODESIC_ORDER/2 == 4 + static const real coeff[] = { + // (eps+1)*A2-1, polynomial in eps2 of order 4 + -375, -704, -1792, -12288, 0, 16384, + }; // count = 6 +#else +#error "Bad value for GEOGRAPHICLIB_GEODESIC_ORDER" +#endif + static_assert(sizeof(coeff) / sizeof(real) == nA2_/2 + 2, + "Coefficient array size mismatch in A2m1f"); + int m = nA2_/2; + real t = Math::polyval(m, coeff, Math::sq(eps)) / coeff[m + 1]; + return (t - eps) / (1 + eps); + } + + // The coefficients C2[l] in the Fourier expansion of B2 + void Geodesic::C2f(real eps, real c[]) { + // Generated by Maxima on 2015-05-05 18:08:12-04:00 +#if GEOGRAPHICLIB_GEODESIC_ORDER == 3 + static const real coeff[] = { + // C2[1]/eps^1, polynomial in eps2 of order 1 + 1, 8, 16, + // C2[2]/eps^2, polynomial in eps2 of order 0 + 3, 16, + // C2[3]/eps^3, polynomial in eps2 of order 0 + 5, 48, + }; +#elif GEOGRAPHICLIB_GEODESIC_ORDER == 4 + static const real coeff[] = { + // C2[1]/eps^1, polynomial in eps2 of order 1 + 1, 8, 16, + // C2[2]/eps^2, polynomial in eps2 of order 1 + 1, 6, 32, + // C2[3]/eps^3, polynomial in eps2 of order 0 + 5, 48, + // C2[4]/eps^4, polynomial in eps2 of order 0 + 35, 512, + }; +#elif GEOGRAPHICLIB_GEODESIC_ORDER == 5 + 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 1 + 1, 6, 32, + // C2[3]/eps^3, polynomial in eps2 of order 1 + 15, 80, 768, + // C2[4]/eps^4, polynomial in eps2 of order 0 + 35, 512, + // C2[5]/eps^5, polynomial in eps2 of order 0 + 63, 1280, + }; +#elif GEOGRAPHICLIB_GEODESIC_ORDER == 6 + 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, + }; +#elif GEOGRAPHICLIB_GEODESIC_ORDER == 7 + static const real coeff[] = { + // C2[1]/eps^1, polynomial in eps2 of order 3 + 41, 64, 128, 1024, 2048, + // C2[2]/eps^2, polynomial in eps2 of order 2 + 35, 64, 384, 2048, + // C2[3]/eps^3, polynomial in eps2 of order 2 + 69, 120, 640, 6144, + // C2[4]/eps^4, polynomial in eps2 of order 1 + 7, 35, 512, + // C2[5]/eps^5, polynomial in eps2 of order 1 + 105, 504, 10240, + // C2[6]/eps^6, polynomial in eps2 of order 0 + 77, 2048, + // C2[7]/eps^7, polynomial in eps2 of order 0 + 429, 14336, + }; +#elif GEOGRAPHICLIB_GEODESIC_ORDER == 8 + static const real coeff[] = { + // C2[1]/eps^1, polynomial in eps2 of order 3 + 41, 64, 128, 1024, 2048, + // C2[2]/eps^2, polynomial in eps2 of order 3 + 47, 70, 128, 768, 4096, + // C2[3]/eps^3, polynomial in eps2 of order 2 + 69, 120, 640, 6144, + // C2[4]/eps^4, polynomial in eps2 of order 2 + 133, 224, 1120, 16384, + // C2[5]/eps^5, polynomial in eps2 of order 1 + 105, 504, 10240, + // C2[6]/eps^6, polynomial in eps2 of order 1 + 33, 154, 4096, + // C2[7]/eps^7, polynomial in eps2 of order 0 + 429, 14336, + // C2[8]/eps^8, polynomial in eps2 of order 0 + 6435, 262144, + }; +#else +#error "Bad value for GEOGRAPHICLIB_GEODESIC_ORDER" +#endif + static_assert(sizeof(coeff) / sizeof(real) == + (nC2_*nC2_ + 7*nC2_ - 2*(nC2_/2)) / 4, + "Coefficient array size mismatch in C2f"); + real + eps2 = Math::sq(eps), + d = eps; + int o = 0; + for (int 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 * Math::polyval(m, coeff + o, eps2) / coeff[o + m + 1]; + o += m + 2; + d *= eps; + } + // Post condition: o == sizeof(coeff) / sizeof(real) + } + + // The scale factor A3 = mean value of (d/dsigma)I3 + void Geodesic::A3coeff() { + // Generated by Maxima on 2015-05-05 18:08:13-04:00 +#if GEOGRAPHICLIB_GEODESIC_ORDER == 3 + static const real coeff[] = { + // A3, coeff of eps^2, polynomial in n of order 0 + -1, 4, + // 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, + }; +#elif GEOGRAPHICLIB_GEODESIC_ORDER == 4 + static const real coeff[] = { + // A3, coeff of eps^3, polynomial in n of order 0 + -1, 16, + // A3, coeff of eps^2, polynomial in n of order 1 + -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, + }; +#elif GEOGRAPHICLIB_GEODESIC_ORDER == 5 + static const real coeff[] = { + // A3, coeff of eps^4, polynomial in n of order 0 + -3, 64, + // A3, coeff of eps^3, polynomial in n of order 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, + }; +#elif GEOGRAPHICLIB_GEODESIC_ORDER == 6 + 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, + }; +#elif GEOGRAPHICLIB_GEODESIC_ORDER == 7 + static const real coeff[] = { + // A3, coeff of eps^6, polynomial in n of order 0 + -5, 256, + // A3, coeff of eps^5, polynomial in n of order 1 + -5, -3, 128, + // A3, coeff of eps^4, polynomial in n of order 2 + -10, -2, -3, 64, + // A3, coeff of eps^3, polynomial in n of order 3 + 5, -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, + }; +#elif GEOGRAPHICLIB_GEODESIC_ORDER == 8 + static const real coeff[] = { + // A3, coeff of eps^7, polynomial in n of order 0 + -25, 2048, + // A3, coeff of eps^6, polynomial in n of order 1 + -15, -20, 1024, + // A3, coeff of eps^5, polynomial in n of order 2 + -5, -10, -6, 256, + // A3, coeff of eps^4, polynomial in n of order 3 + -5, -20, -4, -6, 128, + // A3, coeff of eps^3, polynomial in n of order 3 + 5, -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, + }; +#else +#error "Bad value for GEOGRAPHICLIB_GEODESIC_ORDER" +#endif + static_assert(sizeof(coeff) / sizeof(real) == + (nA3_*nA3_ + 7*nA3_ - 2*(nA3_/2)) / 4, + "Coefficient array size mismatch in A3f"); + int o = 0, k = 0; + for (int j = nA3_ - 1; j >= 0; --j) { // coeff of eps^j + int m = min(nA3_ - j - 1, j); // order of polynomial in n + _A3x[k++] = Math::polyval(m, coeff + o, _n) / coeff[o + m + 1]; + o += m + 2; + } + // Post condition: o == sizeof(coeff) / sizeof(real) && k == nA3x_ + } + + // The coefficients C3[l] in the Fourier expansion of B3 + void Geodesic::C3coeff() { + // Generated by Maxima on 2015-05-05 18:08:13-04:00 +#if GEOGRAPHICLIB_GEODESIC_ORDER == 3 + static const real coeff[] = { + // C3[1], coeff of eps^2, polynomial in n of order 0 + 1, 8, + // C3[1], coeff of eps^1, polynomial in n of order 1 + -1, 1, 4, + // C3[2], coeff of eps^2, polynomial in n of order 0 + 1, 16, + }; +#elif GEOGRAPHICLIB_GEODESIC_ORDER == 4 + static const real coeff[] = { + // C3[1], coeff of eps^3, polynomial in n of order 0 + 3, 64, + // C3[1], coeff of eps^2, polynomial in n of order 1 + // This is a case where a leading 0 term has been inserted to maintain the + // pattern in the orders of the polynomials. + 0, 1, 8, + // C3[1], coeff of eps^1, polynomial in n of order 1 + -1, 1, 4, + // C3[2], coeff of eps^3, polynomial in n of order 0 + 3, 64, + // C3[2], coeff of eps^2, polynomial in n of order 1 + -3, 2, 32, + // C3[3], coeff of eps^3, polynomial in n of order 0 + 5, 192, + }; +#elif GEOGRAPHICLIB_GEODESIC_ORDER == 5 + static const real coeff[] = { + // C3[1], coeff of eps^4, polynomial in n of order 0 + 5, 128, + // C3[1], coeff of eps^3, polynomial in n of order 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^4, polynomial in n of order 0 + 3, 128, + // C3[2], coeff of eps^3, polynomial in n of order 1 + -2, 3, 64, + // C3[2], coeff of eps^2, polynomial in n of order 2 + 1, -3, 2, 32, + // C3[3], coeff of eps^4, polynomial in n of order 0 + 3, 128, + // C3[3], coeff of eps^3, polynomial in n of order 1 + -9, 5, 192, + // C3[4], coeff of eps^4, polynomial in n of order 0 + 7, 512, + }; +#elif GEOGRAPHICLIB_GEODESIC_ORDER == 6 + 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, + }; +#elif GEOGRAPHICLIB_GEODESIC_ORDER == 7 + static const real coeff[] = { + // C3[1], coeff of eps^6, polynomial in n of order 0 + 21, 1024, + // C3[1], coeff of eps^5, polynomial in n of order 1 + 11, 12, 512, + // C3[1], coeff of eps^4, polynomial in n of order 2 + 2, 2, 5, 128, + // C3[1], coeff of eps^3, polynomial in n of order 3 + -5, -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^6, polynomial in n of order 0 + 27, 2048, + // C3[2], coeff of eps^5, polynomial in n of order 1 + 1, 5, 256, + // C3[2], coeff of eps^4, polynomial in n of order 2 + -9, 2, 6, 256, + // C3[2], coeff of eps^3, polynomial in n of order 3 + 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^6, polynomial in n of order 0 + 3, 256, + // C3[3], coeff of eps^5, polynomial in n of order 1 + -4, 21, 1536, + // C3[3], coeff of eps^4, polynomial in n of order 2 + -6, -10, 9, 384, + // C3[3], coeff of eps^3, polynomial in n of order 3 + -1, 5, -9, 5, 192, + // C3[4], coeff of eps^6, polynomial in n of order 0 + 9, 1024, + // C3[4], coeff of eps^5, polynomial in n of order 1 + -10, 7, 512, + // C3[4], coeff of eps^4, polynomial in n of order 2 + 10, -14, 7, 512, + // C3[5], coeff of eps^6, polynomial in n of order 0 + 9, 1024, + // C3[5], coeff of eps^5, polynomial in n of order 1 + -45, 21, 2560, + // C3[6], coeff of eps^6, polynomial in n of order 0 + 11, 2048, + }; +#elif GEOGRAPHICLIB_GEODESIC_ORDER == 8 + static const real coeff[] = { + // C3[1], coeff of eps^7, polynomial in n of order 0 + 243, 16384, + // C3[1], coeff of eps^6, polynomial in n of order 1 + 10, 21, 1024, + // C3[1], coeff of eps^5, polynomial in n of order 2 + 3, 11, 12, 512, + // C3[1], coeff of eps^4, polynomial in n of order 3 + -2, 2, 2, 5, 128, + // C3[1], coeff of eps^3, polynomial in n of order 3 + -5, -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^7, polynomial in n of order 0 + 187, 16384, + // C3[2], coeff of eps^6, polynomial in n of order 1 + 69, 108, 8192, + // C3[2], coeff of eps^5, polynomial in n of order 2 + -2, 1, 5, 256, + // C3[2], coeff of eps^4, polynomial in n of order 3 + -6, -9, 2, 6, 256, + // C3[2], coeff of eps^3, polynomial in n of order 3 + 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^7, polynomial in n of order 0 + 139, 16384, + // C3[3], coeff of eps^6, polynomial in n of order 1 + -1, 12, 1024, + // C3[3], coeff of eps^5, polynomial in n of order 2 + -77, -8, 42, 3072, + // C3[3], coeff of eps^4, polynomial in n of order 3 + 10, -6, -10, 9, 384, + // C3[3], coeff of eps^3, polynomial in n of order 3 + -1, 5, -9, 5, 192, + // C3[4], coeff of eps^7, polynomial in n of order 0 + 127, 16384, + // C3[4], coeff of eps^6, polynomial in n of order 1 + -43, 72, 8192, + // C3[4], coeff of eps^5, polynomial in n of order 2 + -7, -40, 28, 2048, + // C3[4], coeff of eps^4, polynomial in n of order 3 + -7, 20, -28, 14, 1024, + // C3[5], coeff of eps^7, polynomial in n of order 0 + 99, 16384, + // C3[5], coeff of eps^6, polynomial in n of order 1 + -15, 9, 1024, + // C3[5], coeff of eps^5, polynomial in n of order 2 + 75, -90, 42, 5120, + // C3[6], coeff of eps^7, polynomial in n of order 0 + 99, 16384, + // C3[6], coeff of eps^6, polynomial in n of order 1 + -99, 44, 8192, + // C3[7], coeff of eps^7, polynomial in n of order 0 + 429, 114688, + }; +#else +#error "Bad value for GEOGRAPHICLIB_GEODESIC_ORDER" +#endif + static_assert(sizeof(coeff) / sizeof(real) == + ((nC3_-1)*(nC3_*nC3_ + 7*nC3_ - 2*(nC3_/2)))/8, + "Coefficient array size mismatch in C3coeff"); + int o = 0, k = 0; + for (int l = 1; l < nC3_; ++l) { // l is index of C3[l] + for (int j = nC3_ - 1; j >= l; --j) { // coeff of eps^j + int m = min(nC3_ - j - 1, j); // order of polynomial in n + _C3x[k++] = Math::polyval(m, coeff + o, _n) / coeff[o + m + 1]; + o += m + 2; + } + } + // Post condition: o == sizeof(coeff) / sizeof(real) && k == nC3x_ + } + + void Geodesic::C4coeff() { + // Generated by Maxima on 2015-05-05 18:08:13-04:00 +#if GEOGRAPHICLIB_GEODESIC_ORDER == 3 + static const real coeff[] = { + // C4[0], coeff of eps^2, polynomial in n of order 0 + -2, 105, + // C4[0], coeff of eps^1, polynomial in n of order 1 + 16, -7, 35, + // C4[0], coeff of eps^0, polynomial in n of order 2 + 8, -28, 70, 105, + // C4[1], coeff of eps^2, polynomial in n of order 0 + -2, 105, + // C4[1], coeff of eps^1, polynomial in n of order 1 + -16, 7, 315, + // C4[2], coeff of eps^2, polynomial in n of order 0 + 4, 525, + }; +#elif GEOGRAPHICLIB_GEODESIC_ORDER == 4 + static const real coeff[] = { + // C4[0], coeff of eps^3, polynomial in n of order 0 + 11, 315, + // C4[0], coeff of eps^2, polynomial in n of order 1 + -32, -6, 315, + // C4[0], coeff of eps^1, polynomial in n of order 2 + -32, 48, -21, 105, + // C4[0], coeff of eps^0, polynomial in n of order 3 + 4, 24, -84, 210, 315, + // C4[1], coeff of eps^3, polynomial in n of order 0 + -1, 105, + // C4[1], coeff of eps^2, polynomial in n of order 1 + 64, -18, 945, + // C4[1], coeff of eps^1, polynomial in n of order 2 + 32, -48, 21, 945, + // C4[2], coeff of eps^3, polynomial in n of order 0 + -8, 1575, + // C4[2], coeff of eps^2, polynomial in n of order 1 + -32, 12, 1575, + // C4[3], coeff of eps^3, polynomial in n of order 0 + 8, 2205, + }; +#elif GEOGRAPHICLIB_GEODESIC_ORDER == 5 + static const real coeff[] = { + // C4[0], coeff of eps^4, polynomial in n of order 0 + 4, 1155, + // C4[0], coeff of eps^3, polynomial in n of order 1 + -368, 121, 3465, + // C4[0], coeff of eps^2, polynomial in n of order 2 + 1088, -352, -66, 3465, + // C4[0], coeff of eps^1, polynomial in n of order 3 + 48, -352, 528, -231, 1155, + // C4[0], coeff of eps^0, polynomial in n of order 4 + 16, 44, 264, -924, 2310, 3465, + // C4[1], coeff of eps^4, polynomial in n of order 0 + 4, 1155, + // C4[1], coeff of eps^3, polynomial in n of order 1 + 80, -99, 10395, + // C4[1], coeff of eps^2, polynomial in n of order 2 + -896, 704, -198, 10395, + // C4[1], coeff of eps^1, polynomial in n of order 3 + -48, 352, -528, 231, 10395, + // C4[2], coeff of eps^4, polynomial in n of order 0 + -8, 1925, + // C4[2], coeff of eps^3, polynomial in n of order 1 + 384, -88, 17325, + // C4[2], coeff of eps^2, polynomial in n of order 2 + 320, -352, 132, 17325, + // C4[3], coeff of eps^4, polynomial in n of order 0 + -16, 8085, + // C4[3], coeff of eps^3, polynomial in n of order 1 + -256, 88, 24255, + // C4[4], coeff of eps^4, polynomial in n of order 0 + 64, 31185, + }; +#elif GEOGRAPHICLIB_GEODESIC_ORDER == 6 + 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, + }; +#elif GEOGRAPHICLIB_GEODESIC_ORDER == 7 + static const real coeff[] = { + // C4[0], coeff of eps^6, polynomial in n of order 0 + 10, 9009, + // C4[0], coeff of eps^5, polynomial in n of order 1 + -464, 291, 45045, + // C4[0], coeff of eps^4, polynomial in n of order 2 + -4480, 1088, 156, 45045, + // C4[0], coeff of eps^3, polynomial in n of order 3 + 10736, -224, -4784, 1573, 45045, + // C4[0], coeff of eps^2, polynomial in n of order 4 + 1664, -10656, 14144, -4576, -858, 45045, + // C4[0], coeff of eps^1, polynomial in n of order 5 + 16, 64, 624, -4576, 6864, -3003, 15015, + // C4[0], coeff of eps^0, polynomial in n of order 6 + 56, 100, 208, 572, 3432, -12012, 30030, 45045, + // C4[1], coeff of eps^6, polynomial in n of order 0 + 10, 9009, + // C4[1], coeff of eps^5, polynomial in n of order 1 + 112, 15, 135135, + // C4[1], coeff of eps^4, polynomial in n of order 2 + 3840, -2944, 468, 135135, + // C4[1], coeff of eps^3, polynomial in n of order 3 + -10704, 5792, 1040, -1287, 135135, + // C4[1], coeff of eps^2, polynomial in n of order 4 + -768, 5952, -11648, 9152, -2574, 135135, + // C4[1], coeff of eps^1, polynomial in n of order 5 + -16, -64, -624, 4576, -6864, 3003, 135135, + // C4[2], coeff of eps^6, polynomial in n of order 0 + -4, 25025, + // C4[2], coeff of eps^5, polynomial in n of order 1 + -1664, 168, 225225, + // C4[2], coeff of eps^4, polynomial in n of order 2 + 1664, 1856, -936, 225225, + // C4[2], coeff of eps^3, polynomial in n of order 3 + 6784, -8448, 4992, -1144, 225225, + // C4[2], coeff of eps^2, polynomial in n of order 4 + 128, -1440, 4160, -4576, 1716, 225225, + // C4[3], coeff of eps^6, polynomial in n of order 0 + 64, 315315, + // C4[3], coeff of eps^5, polynomial in n of order 1 + 1792, -680, 315315, + // C4[3], coeff of eps^4, polynomial in n of order 2 + -2048, 1024, -208, 105105, + // C4[3], coeff of eps^3, polynomial in n of order 3 + -1792, 3584, -3328, 1144, 315315, + // C4[4], coeff of eps^6, polynomial in n of order 0 + -512, 405405, + // C4[4], coeff of eps^5, polynomial in n of order 1 + 2048, -384, 405405, + // C4[4], coeff of eps^4, polynomial in n of order 2 + 3072, -2560, 832, 405405, + // C4[5], coeff of eps^6, polynomial in n of order 0 + -256, 495495, + // C4[5], coeff of eps^5, polynomial in n of order 1 + -2048, 640, 495495, + // C4[6], coeff of eps^6, polynomial in n of order 0 + 512, 585585, + }; +#elif GEOGRAPHICLIB_GEODESIC_ORDER == 8 + static const real coeff[] = { + // C4[0], coeff of eps^7, polynomial in n of order 0 + 193, 85085, + // C4[0], coeff of eps^6, polynomial in n of order 1 + 4192, 850, 765765, + // C4[0], coeff of eps^5, polynomial in n of order 2 + 20960, -7888, 4947, 765765, + // C4[0], coeff of eps^4, polynomial in n of order 3 + 12480, -76160, 18496, 2652, 765765, + // C4[0], coeff of eps^3, polynomial in n of order 4 + -154048, 182512, -3808, -81328, 26741, 765765, + // C4[0], coeff of eps^2, polynomial in n of order 5 + 3232, 28288, -181152, 240448, -77792, -14586, 765765, + // C4[0], coeff of eps^1, polynomial in n of order 6 + 96, 272, 1088, 10608, -77792, 116688, -51051, 255255, + // C4[0], coeff of eps^0, polynomial in n of order 7 + 588, 952, 1700, 3536, 9724, 58344, -204204, 510510, 765765, + // C4[1], coeff of eps^7, polynomial in n of order 0 + 349, 2297295, + // C4[1], coeff of eps^6, polynomial in n of order 1 + -1472, 510, 459459, + // C4[1], coeff of eps^5, polynomial in n of order 2 + -39840, 1904, 255, 2297295, + // C4[1], coeff of eps^4, polynomial in n of order 3 + 52608, 65280, -50048, 7956, 2297295, + // C4[1], coeff of eps^3, polynomial in n of order 4 + 103744, -181968, 98464, 17680, -21879, 2297295, + // C4[1], coeff of eps^2, polynomial in n of order 5 + -1344, -13056, 101184, -198016, 155584, -43758, 2297295, + // C4[1], coeff of eps^1, polynomial in n of order 6 + -96, -272, -1088, -10608, 77792, -116688, 51051, 2297295, + // C4[2], coeff of eps^7, polynomial in n of order 0 + 464, 1276275, + // C4[2], coeff of eps^6, polynomial in n of order 1 + -928, -612, 3828825, + // C4[2], coeff of eps^5, polynomial in n of order 2 + 64256, -28288, 2856, 3828825, + // C4[2], coeff of eps^4, polynomial in n of order 3 + -126528, 28288, 31552, -15912, 3828825, + // C4[2], coeff of eps^3, polynomial in n of order 4 + -41472, 115328, -143616, 84864, -19448, 3828825, + // C4[2], coeff of eps^2, polynomial in n of order 5 + 160, 2176, -24480, 70720, -77792, 29172, 3828825, + // C4[3], coeff of eps^7, polynomial in n of order 0 + -16, 97461, + // C4[3], coeff of eps^6, polynomial in n of order 1 + -16384, 1088, 5360355, + // C4[3], coeff of eps^5, polynomial in n of order 2 + -2560, 30464, -11560, 5360355, + // C4[3], coeff of eps^4, polynomial in n of order 3 + 35840, -34816, 17408, -3536, 1786785, + // C4[3], coeff of eps^3, polynomial in n of order 4 + 7168, -30464, 60928, -56576, 19448, 5360355, + // C4[4], coeff of eps^7, polynomial in n of order 0 + 128, 2297295, + // C4[4], coeff of eps^6, polynomial in n of order 1 + 26624, -8704, 6891885, + // C4[4], coeff of eps^5, polynomial in n of order 2 + -77824, 34816, -6528, 6891885, + // C4[4], coeff of eps^4, polynomial in n of order 3 + -32256, 52224, -43520, 14144, 6891885, + // C4[5], coeff of eps^7, polynomial in n of order 0 + -6784, 8423415, + // C4[5], coeff of eps^6, polynomial in n of order 1 + 24576, -4352, 8423415, + // C4[5], coeff of eps^5, polynomial in n of order 2 + 45056, -34816, 10880, 8423415, + // C4[6], coeff of eps^7, polynomial in n of order 0 + -1024, 3318315, + // C4[6], coeff of eps^6, polynomial in n of order 1 + -28672, 8704, 9954945, + // C4[7], coeff of eps^7, polynomial in n of order 0 + 1024, 1640925, + }; +#else +#error "Bad value for GEOGRAPHICLIB_GEODESIC_ORDER" +#endif + static_assert(sizeof(coeff) / sizeof(real) == + (nC4_ * (nC4_ + 1) * (nC4_ + 5)) / 6, + "Coefficient array size mismatch in C4coeff"); + int o = 0, k = 0; + for (int l = 0; l < nC4_; ++l) { // l is index of C4[l] + for (int j = nC4_ - 1; j >= l; --j) { // coeff of eps^j + int m = nC4_ - j - 1; // order of polynomial in n + _C4x[k++] = Math::polyval(m, coeff + o, _n) / coeff[o + m + 1]; + o += m + 2; + } + } + // Post condition: o == sizeof(coeff) / sizeof(real) && k == nC4x_ + } + +} // namespace GeographicLib diff --git a/common/local_libs/GeographicLib/src/GeodesicExact.cpp b/common/local_libs/GeographicLib/src/GeodesicExact.cpp new file mode 100644 index 0000000000..7a8d4374a4 --- /dev/null +++ b/common/local_libs/GeographicLib/src/GeodesicExact.cpp @@ -0,0 +1,915 @@ +/** + * \file GeodesicExact.cpp + * \brief Implementation for GeographicLib::GeodesicExact class + * + * Copyright (c) Charles Karney (2012-2020) and licensed + * under the MIT/X11 License. For more information, see + * https://geographiclib.sourceforge.io/ + * + * This is a reformulation of the geodesic problem. The notation is as + * follows: + * - at a general point (no suffix or 1 or 2 as suffix) + * - phi = latitude + * - beta = latitude on auxiliary sphere + * - omega = longitude on auxiliary sphere + * - lambda = longitude + * - alpha = azimuth of great circle + * - sigma = arc length along great circle + * - s = distance + * - tau = scaled distance (= sigma at multiples of pi/2) + * - at northwards equator crossing + * - beta = phi = 0 + * - omega = lambda = 0 + * - alpha = alpha0 + * - sigma = s = 0 + * - a 12 suffix means a difference, e.g., s12 = s2 - s1. + * - s and c prefixes mean sin and cos + **********************************************************************/ + +#include +#include + +#if defined(_MSC_VER) +// Squelch warnings about potentially uninitialized local variables and +// constant conditional expressions +# pragma warning (disable: 4701 4127) +#endif + +namespace GeographicLib { + + using namespace std; + + GeodesicExact::GeodesicExact(real a, real f) + : maxit2_(maxit1_ + Math::digits() + 10) + // Underflow guard. We require + // tiny_ * epsilon() > 0 + // tiny_ + epsilon() == epsilon() + , tiny_(sqrt(numeric_limits::min())) + , tol0_(numeric_limits::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_)) + , tolb_(tol0_ * tol2_) // Check on bisection interval + , xthresh_(1000 * tol2_) + , _a(a) + , _f(f) + , _f1(1 - _f) + , _e2(_f * (2 - _f)) + , _ep2(_e2 / Math::sq(_f1)) // e2 / (1 - e2) + , _n(_f / ( 2 - _f)) + , _b(_a * _f1) + // The Geodesic class substitutes atanh(sqrt(e2)) for asinh(sqrt(ep2)) in + // the definition of _c2. The latter is more accurate for very oblate + // ellipsoids (which the Geodesic class does not attempt to handle). Of + // course, the area calculation in GeodesicExact is still based on a + // series and so only holds for moderately oblate (or prolate) + // ellipsoids. + , _c2((Math::sq(_a) + Math::sq(_b) * + (_f == 0 ? 1 : + (_f > 0 ? asinh(sqrt(_ep2)) : atan(sqrt(-_e2))) / + sqrt(abs(_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. + , _etol2(real(0.1) * tol2_ / + sqrt( max(real(0.001), abs(_f)) * min(real(1), 1 - _f/2) / 2 )) + { + if (!(isfinite(_a) && _a > 0)) + throw GeographicErr("Equatorial radius is not positive"); + if (!(isfinite(_b) && _b > 0)) + throw GeographicErr("Polar semi-axis is not positive"); + C4coeff(); + } + + const GeodesicExact& GeodesicExact::WGS84() { + static const GeodesicExact wgs84(Constants::WGS84_a(), + Constants::WGS84_f()); + return wgs84; + } + + Math::real GeodesicExact::CosSeries(real sinx, real cosx, + const real c[], int n) { + // Evaluate + // y = sum(c[i] * cos((2*i+1) * x), i, 0, n-1) + // using Clenshaw summation. + // Approx operation count = (n + 5) mult and (2 * n + 2) add + c += n ; // Point to one beyond last element + real + 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 cosx * (y0 - y1); // cos(x) * (y0 - y1) + } + + GeodesicLineExact GeodesicExact::Line(real lat1, real lon1, real azi1, + unsigned caps) const { + return GeodesicLineExact(*this, lat1, lon1, azi1, caps); + } + + Math::real GeodesicExact::GenDirect(real lat1, real lon1, real azi1, + bool arcmode, real s12_a12, + unsigned outmask, + real& lat2, real& lon2, real& azi2, + real& s12, real& m12, + real& M12, real& M21, + real& S12) const { + // Automatically supply DISTANCE_IN if necessary + if (!arcmode) outmask |= DISTANCE_IN; + return GeodesicLineExact(*this, lat1, lon1, azi1, outmask) + . // Note the dot! + GenPosition(arcmode, s12_a12, outmask, + lat2, lon2, azi2, s12, m12, M12, M21, S12); + } + + GeodesicLineExact GeodesicExact::GenDirectLine(real lat1, real lon1, + real azi1, + bool arcmode, real s12_a12, + unsigned caps) const { + azi1 = Math::AngNormalize(azi1); + real salp1, calp1; + // Guard against underflow in salp0. Also -0 is converted to +0. + Math::sincosd(Math::AngRound(azi1), salp1, calp1); + // Automatically supply DISTANCE_IN if necessary + if (!arcmode) caps |= DISTANCE_IN; + return GeodesicLineExact(*this, lat1, lon1, azi1, salp1, calp1, + caps, arcmode, s12_a12); + } + + GeodesicLineExact GeodesicExact::DirectLine(real lat1, real lon1, + real azi1, real s12, + unsigned caps) const { + return GenDirectLine(lat1, lon1, azi1, false, s12, caps); + } + + GeodesicLineExact GeodesicExact::ArcDirectLine(real lat1, real lon1, + real azi1, real a12, + unsigned caps) const { + return GenDirectLine(lat1, lon1, azi1, true, a12, caps); + } + + Math::real GeodesicExact::GenInverse(real lat1, real lon1, + real lat2, real lon2, + unsigned outmask, real& s12, + real& salp1, real& calp1, + real& salp2, real& calp2, + real& m12, real& M12, real& M21, + real& S12) const { + // 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. + real lon12s, lon12 = Math::AngDiff(lon1, lon2, lon12s); + // Make longitude difference positive. + int lonsign = lon12 >= 0 ? 1 : -1; + // If very close to being on the same half-meridian, then make it so. + lon12 = lonsign * Math::AngRound(lon12); + lon12s = Math::AngRound((180 - lon12) - lonsign * lon12s); + real + lam12 = lon12 * Math::degree(), + slam12, clam12; + if (lon12 > 90) { + Math::sincosd(lon12s, slam12, clam12); + clam12 = -clam12; + } else + Math::sincosd(lon12, slam12, clam12); + + // If really close to the equator, treat as on equator. + lat1 = Math::AngRound(Math::LatFix(lat1)); + lat2 = Math::AngRound(Math::LatFix(lat2)); + // Swap points so that point with higher (abs) latitude is point 1 + // If one latitude is a nan, then it becomes lat1. + int swapp = abs(lat1) < abs(lat2) ? -1 : 1; + if (swapp < 0) { + lonsign *= -1; + swap(lat1, lat2); + } + // Make lat1 <= 0 + int 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. + + real sbet1, cbet1, sbet2, cbet2, s12x=0, m12x=0; + // Initialize for the meridian. No longitude calculation is done in this + // case to let the parameter default to 0. + EllipticFunction E(-_ep2); + + Math::sincosd(lat1, sbet1, cbet1); sbet1 *= _f1; + // Ensure cbet1 = +epsilon at poles; doing the fix on beta means that sig12 + // will be <= 2*tiny for two points at the same pole. + Math::norm(sbet1, cbet1); cbet1 = max(tiny_, cbet1); + + Math::sincosd(lat2, sbet2, cbet2); sbet2 *= _f1; + // Ensure cbet2 = +epsilon at poles + Math::norm(sbet2, cbet2); cbet2 = max(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 (abs(sbet2) == -sbet1) + cbet2 = cbet1; + } + + real + dn1 = (_f >= 0 ? sqrt(1 + _ep2 * Math::sq(sbet1)) : + sqrt(1 - _e2 * Math::sq(cbet1)) / _f1), + dn2 = (_f >= 0 ? sqrt(1 + _ep2 * Math::sq(sbet2)) : + sqrt(1 - _e2 * Math::sq(cbet2)) / _f1); + + real a12=0, sig12; + + bool meridian = lat1 == -90 || slam12 == 0; + + if (meridian) { + + // Endpoints are on a single full meridian, so the geodesic might lie on + // a meridian. + + calp1 = clam12; salp1 = slam12; // Head to the target longitude + calp2 = 1; salp2 = 0; // At the target we're heading north + + real + // tan(bet) = tan(sig) * cos(alp) + ssig1 = sbet1, csig1 = calp1 * cbet1, + ssig2 = sbet2, csig2 = calp2 * cbet2; + + // sig12 = sig2 - sig1 + sig12 = atan2(max(real(0), csig1 * ssig2 - ssig1 * csig2), + csig1 * csig2 + ssig1 * ssig2); + { + real dummy; + Lengths(E, sig12, ssig1, csig1, dn1, ssig2, csig2, dn2, + cbet1, cbet2, outmask | REDUCEDLENGTH, + s12x, m12x, dummy, M12, M21); + } + // 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 *= _b; + s12x *= _b; + a12 = sig12 / Math::degree(); + } else + // m12 < 0, i.e., prolate and too close to anti-podal + meridian = false; + } + + // somg12 > 1 marks that it needs to be calculated + real omg12 = 0, somg12 = 2, comg12 = 0; + if (!meridian && + sbet1 == 0 && // and sbet2 == 0 + (_f <= 0 || lon12s >= _f * 180)) { + + // Geodesic runs along equator + calp1 = calp2 = 0; salp1 = salp2 = 1; + s12x = _a * lam12; + sig12 = omg12 = lam12 / _f1; + m12x = _b * sin(sig12); + if (outmask & GEODESICSCALE) + M12 = M21 = cos(sig12); + a12 = lon12 / _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; + sig12 = InverseStart(E, sbet1, cbet1, dn1, sbet2, cbet2, dn2, + lam12, slam12, clam12, + salp1, calp1, salp2, calp2, dnm); + + if (sig12 >= 0) { + // Short lines (InverseStart sets salp2, calp2, dnm) + s12x = sig12 * _b * dnm; + m12x = Math::sq(dnm) * _b * sin(sig12 / dnm); + if (outmask & GEODESICSCALE) + M12 = M21 = cos(sig12 / dnm); + a12 = sig12 / Math::degree(); + omg12 = lam12 / (_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. + // + // initial values to suppress warnings (if loop is executed 0 times) + real ssig1 = 0, csig1 = 0, ssig2 = 0, csig2 = 0, domg12 = 0; + unsigned numit = 0; + // Bracketing range + real salp1a = tiny_, calp1a = 1, salp1b = tiny_, calp1b = -1; + for (bool tripn = false, tripb = false; + numit < maxit2_ || GEOGRAPHICLIB_PANIC; + ++numit) { + // 1/4 meridian = 10e6 m and random input. max err is estimated max + // error in nm (checking solution of inverse problem by direct + // solution). iter is mean and sd of number of iterations + // + // max iter + // log2(b/a) err mean sd + // -7 387 5.33 3.68 + // -6 345 5.19 3.43 + // -5 269 5.00 3.05 + // -4 210 4.76 2.44 + // -3 115 4.55 1.87 + // -2 69 4.35 1.38 + // -1 36 4.05 1.03 + // 0 15 0.01 0.13 + // 1 25 5.10 1.53 + // 2 96 5.61 2.09 + // 3 318 6.02 2.74 + // 4 985 6.24 3.22 + // 5 2352 6.32 3.44 + // 6 6008 6.30 3.45 + // 7 19024 6.19 3.30 + real dv; + real v = Lambda12(sbet1, cbet1, dn1, sbet2, cbet2, dn2, salp1, calp1, + slam12, clam12, + salp2, calp2, sig12, ssig1, csig1, ssig2, csig2, + E, domg12, numit < maxit1_, dv); + // Reversed test to allow escape with NaNs + if (tripb || !(abs(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 && abs(dalp1) < Math::pi()) { + calp1 = calp1 * cdalp1 - salp1 * sdalp1; + salp1 = nsalp1; + Math::norm(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 = abs(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; + Math::norm(salp1, calp1); + tripn = false; + tripb = (abs(salp1a - salp1) + (calp1a - calp1) < tolb_ || + abs(salp1 - salp1b) + (calp1 - calp1b) < tolb_); + } + { + real dummy; + Lengths(E, sig12, ssig1, csig1, dn1, ssig2, csig2, dn2, + cbet1, cbet2, outmask, s12x, m12x, dummy, M12, M21); + } + m12x *= _b; + s12x *= _b; + a12 = sig12 / Math::degree(); + if (outmask & AREA) { + // omg12 = lam12 - domg12 + real sdomg12 = sin(domg12), cdomg12 = cos(domg12); + somg12 = slam12 * cdomg12 - clam12 * sdomg12; + comg12 = clam12 * cdomg12 + slam12 * sdomg12; + } + } + } + + if (outmask & DISTANCE) + s12 = 0 + s12x; // Convert -0 to 0 + + if (outmask & REDUCEDLENGTH) + m12 = 0 + m12x; // Convert -0 to 0 + + if (outmask & AREA) { + real + // From Lambda12: sin(alp1) * cos(bet1) = sin(alp0) + salp0 = salp1 * cbet1, + calp0 = hypot(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 = Math::sq(calp0) * _ep2, + eps = k2 / (2 * (1 + sqrt(1 + k2)) + k2), + // Multiplier = a^2 * e^2 * cos(alpha0) * sin(alpha0). + A4 = Math::sq(_a) * calp0 * salp0 * _e2; + Math::norm(ssig1, csig1); + Math::norm(ssig2, csig2); + real C4a[nC4_]; + C4f(eps, C4a); + real + B41 = CosSeries(ssig1, csig1, C4a, nC4_), + B42 = CosSeries(ssig2, csig2, C4a, nC4_); + S12 = A4 * (B42 - B41); + } else + // Avoid problems with indeterminate sig1, sig2 on equator + S12 = 0; + + if (!meridian) { + if (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 += _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) { + swap(salp1, salp2); + swap(calp1, calp2); + if (outmask & GEODESICSCALE) + swap(M12, M21); + } + + salp1 *= swapp * lonsign; calp1 *= swapp * latsign; + salp2 *= swapp * lonsign; calp2 *= swapp * latsign; + + // Returned value in [0, 180] + return a12; + } + + Math::real GeodesicExact::GenInverse(real lat1, real lon1, + real lat2, real lon2, + unsigned outmask, + real& s12, real& azi1, real& azi2, + real& m12, real& M12, real& M21, + real& S12) const { + outmask &= OUT_MASK; + real salp1, calp1, salp2, calp2, + a12 = GenInverse(lat1, lon1, lat2, lon2, + outmask, s12, salp1, calp1, salp2, calp2, + m12, M12, M21, S12); + if (outmask & AZIMUTH) { + azi1 = Math::atan2d(salp1, calp1); + azi2 = Math::atan2d(salp2, calp2); + } + return a12; + } + + GeodesicLineExact GeodesicExact::InverseLine(real lat1, real lon1, + real lat2, real lon2, + unsigned caps) const { + real t, salp1, calp1, salp2, calp2, + a12 = GenInverse(lat1, lon1, lat2, lon2, + // No need to specify AZIMUTH here + 0u, t, salp1, calp1, salp2, calp2, + t, t, t, t), + azi1 = Math::atan2d(salp1, calp1); + // Ensure that a12 can be converted to a distance + if (caps & (OUT_MASK & DISTANCE_IN)) caps |= DISTANCE; + return GeodesicLineExact(*this, lat1, lon1, azi1, salp1, calp1, caps, + true, a12); + } + + void GeodesicExact::Lengths(const EllipticFunction& E, + real sig12, + real ssig1, real csig1, real dn1, + real ssig2, real csig2, real dn2, + real cbet1, real cbet2, unsigned outmask, + real& s12b, real& m12b, real& m0, + real& M12, real& M21) const { + // Return m12b = (reduced length)/_b; also calculate s12b = distance/_b, + // and m0 = coefficient of secular term in expression for reduced length. + + outmask &= OUT_ALL; + // outmask & DISTANCE: set s12b + // outmask & REDUCEDLENGTH: set m12b & m0 + // outmask & GEODESICSCALE: set M12 & M21 + + // It's OK to have repeated dummy arguments, + // e.g., s12b = m0 = M12 = M21 = dummy + + if (outmask & DISTANCE) + // Missing a factor of _b + s12b = E.E() / (Math::pi() / 2) * + (sig12 + (E.deltaE(ssig2, csig2, dn2) - E.deltaE(ssig1, csig1, dn1))); + if (outmask & (REDUCEDLENGTH | GEODESICSCALE)) { + real + m0x = - E.k2() * E.D() / (Math::pi() / 2), + J12 = m0x * + (sig12 + (E.deltaD(ssig2, csig2, dn2) - E.deltaD(ssig1, csig1, dn1))); + if (outmask & REDUCEDLENGTH) { + m0 = m0x; + // Missing a factor of _b. Add parens around (csig1 * ssig2) and + // (ssig1 * csig2) to ensure accurate cancellation in the case of + // coincident points. + m12b = dn2 * (csig1 * ssig2) - dn1 * (ssig1 * csig2) - + csig1 * csig2 * J12; + } + if (outmask & GEODESICSCALE) { + real csig12 = csig1 * csig2 + ssig1 * ssig2; + real t = _ep2 * (cbet1 - cbet2) * (cbet1 + cbet2) / (dn1 + dn2); + M12 = csig12 + (t * ssig2 - csig2 * J12) * ssig1 / dn1; + M21 = csig12 - (t * ssig1 - csig1 * J12) * ssig2 / dn2; + } + } + } + + Math::real GeodesicExact::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 = Math::sq(x), + q = Math::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 = Math::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; + if (disc >= 0) { + real T3 = S + r3; + // 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. cbrt always returns the real root. cbrt(-8) = -2. + real T = cbrt(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); + } + real + v = sqrt(Math::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 + Math::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; + } + + Math::real GeodesicExact::InverseStart(EllipticFunction& E, + real sbet1, real cbet1, real dn1, + real sbet2, real cbet2, real dn2, + real lam12, real slam12, real clam12, + real& salp1, real& calp1, + // Only updated if return val >= 0 + real& salp2, real& calp2, + // Only updated for short lines + real& dnm) const { + // 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 = sbet2 * cbet1 + cbet2 * sbet1; + bool shortline = cbet12 >= 0 && sbet12 < real(0.5) && + cbet2 * lam12 < real(0.5); + real somg12, comg12; + if (shortline) { + real sbetm2 = Math::sq(sbet1 + sbet2); + // sin((bet1+bet2)/2)^2 + // = (sbet1 + sbet2)^2 / ((sbet1 + sbet2)^2 + (cbet1 + cbet2)^2) + sbetm2 /= sbetm2 + Math::sq(cbet1 + cbet2); + dnm = sqrt(1 + _ep2 * sbetm2); + real omg12 = lam12 / (_f1 * dnm); + somg12 = sin(omg12); comg12 = cos(omg12); + } else { + somg12 = slam12; comg12 = clam12; + } + + salp1 = cbet2 * somg12; + calp1 = comg12 >= 0 ? + sbet12 + cbet2 * sbet1 * Math::sq(somg12) / (1 + comg12) : + sbet12a - cbet2 * sbet1 * Math::sq(somg12) / (1 - comg12); + + real + ssig12 = hypot(salp1, calp1), + csig12 = sbet1 * sbet2 + cbet1 * cbet2 * comg12; + + if (shortline && ssig12 < _etol2) { + // really short lines + salp2 = cbet1 * somg12; + calp2 = sbet12 - cbet1 * sbet2 * + (comg12 >= 0 ? Math::sq(somg12) / (1 + comg12) : 1 - comg12); + Math::norm(salp2, calp2); + // Set return value + sig12 = atan2(ssig12, csig12); + } else if (abs(_n) > real(0.1) || // Skip astroid calc if too eccentric + csig12 >= 0 || + ssig12 >= 6 * abs(_n) * Math::pi() * Math::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 + GEOGRAPHICLIB_VOLATILE real x; + real lam12x = atan2(-slam12, -clam12); // lam12 - pi + if (_f >= 0) { // In fact f == 0 does not get here + // x = dlong, y = dlat + { + real k2 = Math::sq(sbet1) * _ep2; + E.Reset(-k2, -_ep2, 1 + k2, 1 + _ep2); + lamscale = _e2/_f1 * cbet1 * 2 * E.H(); + } + 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, dummy; + // In the case of lon12 = 180, this repeats a calculation made in + // Inverse. + Lengths(E, Math::pi() + bet12a, + sbet1, -cbet1, dn1, sbet2, cbet2, dn2, + cbet1, cbet2, REDUCEDLENGTH, dummy, m12b, m0, dummy, dummy); + x = -1 + m12b / (cbet1 * cbet2 * m0 * Math::pi()); + betscale = x < -real(0.01) ? sbet12a / x : + -_f * Math::sq(cbet1) * Math::pi(); + lamscale = betscale / cbet1; + y = lam12x / lamscale; + } + + if (y > -tol1_ && x > -1 - xthresh_) { + // strip near cut + // Need real(x) here to cast away the volatility of x for min/max + if (_f >= 0) { + salp1 = min(real(1), -real(x)); calp1 = - sqrt(1 - Math::sq(salp1)); + } else { + calp1 = max(real(x > -tol1_ ? 0 : -1), real(x)); + salp1 = sqrt(1 - Math::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 * ( _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 * Math::sq(somg12) / (1 - comg12); + } + } + // Sanity check on starting guess. Backwards check allows NaN through. + if (!(salp1 <= 0)) + Math::norm(salp1, calp1); + else { + salp1 = 1; calp1 = 0; + } + return sig12; + } + + Math::real GeodesicExact::Lambda12(real sbet1, real cbet1, real dn1, + real sbet2, real cbet2, real dn2, + real salp1, real calp1, + real slam120, real clam120, + real& salp2, real& calp2, + real& sig12, + real& ssig1, real& csig1, + real& ssig2, real& csig2, + EllipticFunction& E, + real& domg12, + bool diffp, real& dlam12) const + { + + if (sbet1 == 0 && calp1 == 0) + // Break degeneracy of equatorial line. This case has already been + // handled. + calp1 = -tiny_; + + real + // sin(alp1) * cos(bet1) = sin(alp0) + salp0 = salp1 * cbet1, + calp0 = hypot(calp1, salp1 * sbet1); // calp0 > 0 + + real somg1, comg1, somg2, comg2, somg12, comg12, cchi1, cchi2, lam12; + // 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; + // Without normalization we have schi1 = somg1. + cchi1 = _f1 * dn1 * comg1; + Math::norm(ssig1, csig1); + // Math::norm(somg1, comg1); -- don't need to normalize! + // Math::norm(schi1, cchi1); -- 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 || abs(sbet2) != -sbet1 ? + sqrt(Math::sq(calp1 * cbet1) + + (cbet1 < -sbet1 ? + (cbet2 - cbet1) * (cbet1 + cbet2) : + (sbet1 - sbet2) * (sbet1 + sbet2))) / cbet2 : + abs(calp1); + // tan(bet2) = tan(sig2) * cos(alp2) + // tan(omg2) = sin(alp0) * tan(sig2). + ssig2 = sbet2; somg2 = salp0 * sbet2; + csig2 = comg2 = calp2 * cbet2; + // Without normalization we have schi2 = somg2. + cchi2 = _f1 * dn2 * comg2; + Math::norm(ssig2, csig2); + // Math::norm(somg2, comg2); -- don't need to normalize! + // Math::norm(schi2, cchi2); -- don't need to normalize! + + // sig12 = sig2 - sig1, limit to [0, pi] + sig12 = atan2(max(real(0), csig1 * ssig2 - ssig1 * csig2), + csig1 * csig2 + ssig1 * ssig2); + + // omg12 = omg2 - omg1, limit to [0, pi] + somg12 = max(real(0), comg1 * somg2 - somg1 * comg2); + comg12 = comg1 * comg2 + somg1 * somg2; + real k2 = Math::sq(calp0) * _ep2; + E.Reset(-k2, -_ep2, 1 + k2, 1 + _ep2); + // chi12 = chi2 - chi1, limit to [0, pi] + real + schi12 = max(real(0), cchi1 * somg2 - somg1 * cchi2), + cchi12 = cchi1 * cchi2 + somg1 * somg2; + // eta = chi12 - lam120 + real eta = atan2(schi12 * clam120 - cchi12 * slam120, + cchi12 * clam120 + schi12 * slam120); + real deta12 = -_e2/_f1 * salp0 * E.H() / (Math::pi() / 2) * + (sig12 + (E.deltaH(ssig2, csig2, dn2) - E.deltaH(ssig1, csig1, dn1))); + lam12 = eta + deta12; + // domg12 = deta12 + chi12 - omg12 + domg12 = deta12 + atan2(schi12 * comg12 - cchi12 * somg12, + cchi12 * comg12 + schi12 * somg12); + if (diffp) { + if (calp2 == 0) + dlam12 = - 2 * _f1 * dn1 / sbet1; + else { + real dummy; + Lengths(E, sig12, ssig1, csig1, dn1, ssig2, csig2, dn2, + cbet1, cbet2, REDUCEDLENGTH, + dummy, dlam12, dummy, dummy, dummy); + dlam12 *= _f1 / (calp2 * cbet2); + } + } + + return lam12; + } + + void GeodesicExact::C4f(real eps, real c[]) const { + // Evaluate C4 coeffs + // Elements c[0] thru c[nC4_ - 1] are set + real mult = 1; + int o = 0; + for (int l = 0; l < nC4_; ++l) { // l is index of C4[l] + int m = nC4_ - l - 1; // order of polynomial in eps + c[l] = mult * Math::polyval(m, _C4x + o, eps); + o += m + 1; + mult *= eps; + } + // Post condition: o == nC4x_ + if (!(o == nC4x_)) + throw GeographicErr("C4 misalignment"); + } + +} // namespace GeographicLib diff --git a/common/local_libs/GeographicLib/src/GeodesicExactC4.cpp b/common/local_libs/GeographicLib/src/GeodesicExactC4.cpp new file mode 100644 index 0000000000..e683db791f --- /dev/null +++ b/common/local_libs/GeographicLib/src/GeodesicExactC4.cpp @@ -0,0 +1,7306 @@ +/** + * \file GeodesicExactC4.cpp + * \brief Implementation for GeographicLib::GeodesicExact::rawC4coeff + * + * Copyright (c) Charles Karney (2014-2020) and licensed + * under the MIT/X11 License. For more information, see + * https://geographiclib.sourceforge.io/ + * + * This function is split from the rest of the implementation of + * GeographicLib::GeodesicExact in order to work around a problem with the + * Visual Studio 12 compiler reported on 2014-07-14 + * http://connect.microsoft.com/VisualStudio/feedback/details/920594 + **********************************************************************/ + +#include + +namespace GeographicLib { + + using namespace std; + + // If the coefficient is greater or equal to 2^63, express it as a pair [a, + // b] which is combined with a*2^52 + b. The largest coefficient is + // 831281402884796906843926125 = 0x2af9eaf25d149c52a73ee6d + // = 184581550685 * 2^52 + 0x149c52a73ee6d which is less than 2^90. Both a + // and b are less that 2^52 and so are exactly representable by doubles; then + // the computation of the full double coefficient involves only a single + // rounding operation. (Actually integers up to and including 2^53 can be + // represented exactly as doubles. Limiting b to 52 bits allows it to be + // represented in 13 digits in hex.) + + // If the coefficient is less than 2^63, cast it to real if it isn't exactly + // representable as a float. Thus 121722048 = 1901907*2^6 and 1901907 < 2^24 + // so the cast is not needed; 21708121824 = 678378807*2^5 and 678378807 >= + // 2^24 so the cast is needed. + + void GeodesicExact::C4coeff() { + // Generated by Maxima on 2017-05-27 10:17:57-04:00 +#if GEOGRAPHICLIB_GEODESICEXACT_ORDER == 24 + static const real coeff[] = { + // C4[0], coeff of eps^23, polynomial in n of order 0 + 2113,real(34165005), + // C4[0], coeff of eps^22, polynomial in n of order 1 + 5189536,1279278,real(54629842995LL), + // C4[0], coeff of eps^21, polynomial in n of order 2 + real(19420000),-9609488,7145551,real(87882790905LL), + // C4[0], coeff of eps^20, polynomial in n of order 3 + real(223285780800LL),-real(146003016320LL),real(72167144896LL), + real(17737080900LL),real(0x205dc0bcbd6d7LL), + // C4[0], coeff of eps^19, polynomial in n of order 4 + real(0x4114538e4c0LL),-real(0x2f55bac3db0LL),real(0x1ee26e63c60LL), + -real(0xf3f108c690LL),real(777582423783LL),real(0x19244124e56e27LL), + // C4[0], coeff of eps^18, polynomial in n of order 5 + real(0x303f35e1bc93a0LL),-real(0x24e1f056b1d580LL), + real(0x1ab9fe0d1d4d60LL),-real(0x1164c583e996c0LL), + real(0x892da1e80cb20LL),real(0x2194519fdb596LL), + reale(3071,0xfdd7cc41833d5LL), + // C4[0], coeff of eps^17, polynomial in n of order 6 + real(0x4aad22c875ed20LL),-real(0x3a4801a1c6bad0LL), + real(0x2c487fb318d4c0LL),-real(0x1ff24d7cfd75b0LL), + real(0x14ba39245f1460LL),-real(0xa32e190328e90LL), + real(0x78c93074dfcffLL),reale(3071,0xfdd7cc41833d5LL), + // C4[0], coeff of eps^16, polynomial in n of order 7 + real(0x33d84b92096e100LL),-real(0x286d35d824ffe00LL), + real(0x1f3d33e2e951300LL),-real(0x178f58435181400LL), + real(0x10e7992a3756500LL),-real(0xaed7fa8609aa00LL), + real(0x55d8ac87b09700LL),real(0x14e51e43945a10LL), + reale(21503,0xf0e695ca96ad3LL), + // C4[0], coeff of eps^15, polynomial in n of order 8 + real(0x577cdb6aaee0d80LL),-real(0x4283c1e96325470LL), + real(0x32feef20b794020LL),-real(0x26ea2e388de1a50LL), + real(0x1d13f6131e5d6c0LL),-real(0x14b9aa66e270230LL), + real(0xd5657196ac0560LL),-real(0x6880b0118a9810LL), + real(0x4d0f1755168ee7LL),reale(21503,0xf0e695ca96ad3LL), + // C4[0], coeff of eps^14, polynomial in n of order 9 + real(0xa82410caed14920LL),-real(0x774e0539d2de300LL), + real(0x57ddc01c62bc8e0LL),-real(0x41de50dfff43e40LL), + real(0x31742450a1bdca0LL),-real(0x248524531975180LL), + real(0x19d013c6e35ec60LL),-real(0x1084c003a0434c0LL), + real(0x8103758ad86020LL),real(0x1f2409edf5e286LL), + reale(21503,0xf0e695ca96ad3LL), + // C4[0], coeff of eps^13, polynomial in n of order 10 + real(0x1c6d2d6120015ca0LL),-real(0x104cedef383403b0LL), + real(0xab9dd58c3e3d880LL),-real(0x78a4e83e5604750LL), + real(0x57aa7cf5406e460LL),-real(0x4067a93ceeb2cf0LL), + real(0x2ed62190d975c40LL),-real(0x20c076adcb21890LL), + real(0x14cfa9cb9e01c20LL),-real(0xa1e25734956e30LL), + real(0x76afbfe4ae6c4dLL),reale(21503,0xf0e695ca96ad3LL), + // C4[0], coeff of eps^12, polynomial in n of order 11 + real(0x500e39e18e75c40LL),-real(0xb866fe4aaa63680LL), + real(0x4337db32e526ac0LL),-real(0x264cce8c21af200LL), + real(0x18fb7ba247a4140LL),-real(0x115709558576d80LL), + real(0xc5be96cd3dcfc0LL),-real(0x8cdca1395db900LL), + real(0x611fe1a7e00640LL),-real(0x3d26e46827e480LL), + real(0x1d93970a8fd4c0LL),real(0x70bf87cc17354LL), + reale(3071,0xfdd7cc41833d5LL), + // C4[0], coeff of eps^11, polynomial in n of order 12 + -real(0x158a522ca96a9f40LL),real(0x14d4e49882e048f0LL), + real(0x51a6258bc6026a0LL),-real(0xc07af3677bdc6b0LL), + real(0x45ac09bc3b66080LL),-real(0x275e4ef59a8b450LL), + real(0x195f928e5402a60LL),-real(0x114aa7eeb31a3f0LL), + real(0xbf706c784da040LL),-real(0x817ec7d97ab990LL), + real(0x508b8ca80cde20LL),-real(0x26b120ea091930LL), + real(0x1c1ab3faf18ecdLL),reale(3071,0xfdd7cc41833d5LL), + // C4[0], coeff of eps^10, polynomial in n of order 13 + real(0x85cd94c7a43620LL),real(0x41534458719f180LL), + -real(0x1688b497e3eabf20LL),real(0x15fa3ad6bcd8bd40LL), + real(0x531c27984875fa0LL),-real(0xc9b33381ee39f00LL), + real(0x485a2b8a7ad1a60LL),-real(0x286be979df41b40LL), + real(0x199b6e19072f920LL),-real(0x10f769bc7a1af80LL), + real(0xb2b30e0b2b83e0LL),-real(0x6d4c30bc0953c0LL), + real(0x3405b9397b42a0LL),real(0xc1ffd0ada51beLL), + reale(3071,0xfdd7cc41833d5LL), + // C4[0], coeff of eps^9, polynomial in n of order 14 + real(0x77c3b2fb788360LL),real(0x12370e8b6ebba50LL), + real(0x3ce89570a2d35c0LL),real(0x1ddd463aa5801f30LL), + -reale(2652,0xb61760f09fe0LL),reale(2613,0x24df88b461210LL), + real(0x24dea39341926e80LL),-real(0x5ce704fae2f44110LL), + real(0x20ecef343dc3cce0LL),-real(0x121947a4ab4bae30LL), + real(0xb2a76f84c78e740LL),-real(0x70dd3a5c9a20950LL), + real(0x43604f2667d29a0LL),-real(0x1fa7f2abdd82670LL), + real(0x169d55eb03244c1LL),reale(21503,0xf0e695ca96ad3LL), + // C4[0], coeff of eps^8, polynomial in n of order 15 + real(0x21331eec152c80LL),real(0x3c94fa87392d00LL), + real(0x7bff534019c580LL),real(0x12eee208e5fe200LL), + real(0x3f965ae4945ee80LL),real(0x1f56cb06e4e85700LL), + -reale(2802,0x46e8e19f880LL),reale(2796,0xadb20bd4ec00LL), + real(0x251d0efe774e7080LL),-real(0x625b74d58e27ff00LL), + real(0x224674d7e8ab8980LL),-real(0x1260f3bdc69c0a00LL), + real(0xad7256a98d1b280LL),-real(0x63bd65ce944d500LL), + real(0x2df89c0cd0d4b80LL),real(0xa46618fc50ff08LL), + reale(21503,0xf0e695ca96ad3LL), + // C4[0], coeff of eps^7, polynomial in n of order 16 + real(0xcb641c2517300LL),real(0x1435342f6c1790LL), + real(0x2223c168d902a0LL),real(0x3e90a70fac72b0LL), + real(0x80a310c4f84640LL),real(0x13bcb7c20d40bd0LL), + real(0x42a5540b0e391e0LL),real(0x210e40977bd376f0LL), + -reale(2980,0x94d9def1cc680LL),reale(3022,0x503caf61c4810LL), + real(0x24d397da2b859120LL),-real(0x68d822cc2f04ecd0LL), + real(0x23a043b28810ecc0LL),-real(0x125159fafe6e93b0LL), + real(0x9e1bc8a31f5a060LL),-real(0x46aed7b45d01890LL), + real(0x30c71f0f146542fLL),reale(21503,0xf0e695ca96ad3LL), + // C4[0], coeff of eps^6, polynomial in n of order 17 + real(0x5c9c64c833ea0LL),real(0x87cba49bc6200LL),real(0xcee016a8ff560LL), + real(0x14a860e941a1c0LL),real(0x231567934bf020LL), + real(0x40a648fc642980LL),real(0x85b2123b2c36e0LL), + real(0x14a4159e5b98140LL),real(0x462d226dee7d1a0LL), + real(0x2316888f6f2f3100LL),-reale(3198,0x3491a799c37a0LL), + reale(3311,0xbf8f265e6c0c0LL),real(0x2372de10575f2320LL), + -real(0x70af5543c56e4780LL),real(0x24bbd6e6395ee9e0LL), + -real(0x116009bab4325fc0LL),real(0x75b7dfa9c5a24a0LL), + real(0x17de90e4beab49eLL),reale(21503,0xf0e695ca96ad3LL), + // C4[0], coeff of eps^5, polynomial in n of order 18 + real(0x6a525328e6e0LL),real(0x93f17033fb30LL),real(0xd36a04706f00LL), + real(0x137db4aaadad0LL),real(0x1de17febed720LL),real(0x300ece09a4c70LL), + real(0x5230537724340LL),real(0x98911a7bab410LL),real(0x13df6f0042d760LL), + real(0x317f809c6f75b0LL),real(0xa9d28ba9acb780LL), + real(0x55d121ad9d8f550LL),-real(0x1efee1555125f860LL), + real(0x21073529064696f0LL),real(0x486394f46ccebc0LL), + -real(0x11777145e6374170LL),real(0x54159fc268987e0LL), + -real(0x1fa4dd5835d2fd0LL),real(0x13d87fc86cca643LL), + reale(3071,0xfdd7cc41833d5LL), + // C4[0], coeff of eps^4, polynomial in n of order 19 + real(0x3804d31f10c0LL),real(0x4b2ec20ad280LL),real(0x66f0ea418040LL), + real(0x903f2204b400LL),real(0xcfad72d447c0LL),real(0x134cb9fa41580LL), + real(0x1dd70e331b740LL),real(0x306dd8a084700LL),real(0x53a0a0b201ec0LL), + real(0x9cd7c33c89880LL),real(0x14a7b599a9ce40LL), + real(0x340e256f2c5a00LL),real(0xb4e7d2cf7515c0LL), + real(0x5cc8e678862db80LL),-real(0x22304c48df63bac0LL), + real(0x25f7d3a888bb6d00LL),real(0x3210c8a6905acc0LL), + -real(0x131873ea3222a180LL),real(0x4a33217f63b9c40LL), + real(0xaa39109cb79b1cLL),reale(3071,0xfdd7cc41833d5LL), + // C4[0], coeff of eps^3, polynomial in n of order 20 + real(0x1d8a60744340LL),real(0x26a12f47d0f0LL),real(0x3353c9ffe420LL), + real(0x4570fd193850LL),real(0x5fe8194aa900LL),real(0x87a7057de1b0LL), + real(0xc54ab4558de0LL),real(0x12897a64b8910LL),real(0x1d013b7f18ec0LL), + real(0x2fb033b96ea70LL),real(0x5384f3e45a7a0LL),real(0x9f10eb531c1d0LL), + real(0x154d17c994d480LL),real(0x36ab828088cb30LL), + real(0xc1d47f99841160LL),real(0x65b5717bb21c290LL), + -real(0x269fd1ef6edfa5c0LL),real(0x2dc2d3f3f9f963f0LL), + -real(0xf46c321c1b54e0LL),-real(0x14642b52c5fe94b0LL), + real(0x6b46a122c3b5c05LL),reale(3071,0xfdd7cc41833d5LL), + // C4[0], coeff of eps^2, polynomial in n of order 21 + real(0x65e46db33460LL),real(0x82b39a7b3380LL),real(0xa9e8c6cf36a0LL), + real(0xe0317d0fa0c0LL),real(0x12cd0399df4e0LL),real(0x19b576ed17600LL), + real(0x23ecb07d1c720LL),real(0x33785d3e48b40LL),real(0x4bedad56b0560LL), + real(0x73f4d1eccb880LL),real(0xb8a5a1bdc07a0LL),real(0x1359aad161d5c0LL), + real(0x22a518d96d25e0LL),real(0x43a50f3643bb00LL), + real(0x95133a4d60b820LL),real(0x18b02de0f4e4040LL), + real(0x5ac287501571660LL),real(0x31a5fa2db58d3d80LL), + -reale(5087,0xbd2e8f8d6760LL),reale(6752,0x2ce8487308ac0LL), + -reale(2184,0x86ffdb3446920LL),-real(0x199994ff919cd3b6LL), + reale(21503,0xf0e695ca96ad3LL), + // C4[0], coeff of eps^1, polynomial in n of order 22 + real(0xd0da1980ba0LL),real(0x10803fb20d70LL),real(0x151a70ced0c0LL), + real(0x1b569dc61a10LL),real(0x23ecd2ce6de0LL),real(0x2ff80cba60b0LL), + real(0x413672596700LL),real(0x5a7b8b75a550LL),real(0x8082f2984020LL), + real(0xbb859b75abf0LL),real(0x11a6bf1637d40LL),real(0x1b9a143813890LL), + real(0x2d2aacb8da260LL),real(0x4e2c5253a0f30LL),real(0x914a9e2ed3380LL), + real(0x128a302f4ef3d0LL),real(0x2b2226f5e6b4a0LL), + real(0x7a36190e0daa70LL),real(0x1e8d8643836a9c0LL), + real(0x129e3dd12414f710LL),-reale(2184,0x86ffdb3446920LL), + reale(3276,0xca7fc8ce69db0LL),-real(0x5999897e7da4e4fdLL), + reale(7167,0xfaf78743878f1LL), + // C4[0], coeff of eps^0, polynomial in n of order 23 + real(0x71a68037fdf14LL),real(0x81ebac5d53b48LL),real(0x957440e8ac5fcLL), + real(0xad1ce56088670LL),real(0xca0c260c189e4LL),real(0xedd10e292f598LL), + real(0x11a912af9e18ccLL),real(0x1534f4af92bec0LL), + real(0x19c5b078ed00b4LL),real(0x1fc05a701dd7e8LL), + real(0x27bd1031afaf9cLL),real(0x32a7dc61183710LL), + real(0x41fc58560eb384LL),real(0x583759590a1238LL), + real(0x79bd058a3bfa6cLL),real(0xaecdc650561f60LL), + real(0x108312ea2251254LL),real(0x1abbd57b12fd488LL), + real(0x2fbd21c97d5693cLL),real(0x634bf45b6b1a7b0LL), + real(0x11110dffb6688d24LL),real(0x666653fe46734ed8LL), + -reale(5734,0x625f9f69393f4LL),reale(14335,0xf5ef0e870f1e2LL), + reale(21503,0xf0e695ca96ad3LL), + // C4[1], coeff of eps^23, polynomial in n of order 0 + 3401,real(512475075), + // C4[1], coeff of eps^22, polynomial in n of order 1 + -5479232,3837834,real(163889528985LL), + // C4[1], coeff of eps^21, polynomial in n of order 2 + -real(1286021216),real(571443856),real(142575393),real(0xef8343fb2e1LL), + // C4[1], coeff of eps^20, polynomial in n of order 3 + -real(237999188352LL),real(138477414656LL),-real(77042430080LL), + real(53211242700LL),real(0x6119423638485LL), + // C4[1], coeff of eps^19, polynomial in n of order 4 + -real(0x2066cb6031fc0LL),real(0x14c85e7394470LL),-real(0xf6b8f35571e0LL), + real(0x6ad3f08040d0LL),real(0x1aa3b2832565LL),real(0x230f8ed873f29c63LL), + // C4[1], coeff of eps^18, polynomial in n of order 5 + -real(0x33e9644cad5b40LL),real(0x22b6849ca6a500LL), + -real(0x1ce364ad2a4ec0LL),real(0x104aaed8cf4680LL), + -real(0x949f0f8a89e40LL),real(0x64bcf4df920c2LL), + reale(9215,0xf98764c489b7fLL), + // C4[1], coeff of eps^17, polynomial in n of order 6 + -real(0x50a85b2e2e4060LL),real(0x36bb9aa442c6f0LL), + -real(0x3029aafbbe0440LL),real(0x1dc29c0bd6ce90LL), + -real(0x16a422844d9020LL),real(0x9763b8d8ca030LL), + real(0x25b8d7edff7ebLL),reale(9215,0xf98764c489b7fLL), + // C4[1], coeff of eps^16, polynomial in n of order 7 + -real(0x3822c174e5c7e00LL),real(0x25fbaf973d78c00LL), + -real(0x222a860fbdb7a00LL),real(0x15dabd7a0984800LL), + -real(0x129f00215535600LL),real(0xa0e9e0ae9b8400LL), + -real(0x5ee97a6d2d5200LL),real(0x3eaf5acabd0e30LL), + reale(64511,0xd2b3c15fc4079LL), + // C4[1], coeff of eps^15, polynomial in n of order 8 + -real(0x5ec1dcd7666b480LL),real(0x3ed4935a3fd8cd0LL), + -real(0x38014f5e5d79960LL),real(0x240af6a53256570LL), + -real(0x2049d0fb0404a40LL),real(0x12efbc065d3f410LL), + -real(0xee9d804d5d8320LL),real(0x5ed209adebbcb0LL), + real(0x1798ea7fdd6773LL),reale(64511,0xd2b3c15fc4079LL), + // C4[1], coeff of eps^14, polynomial in n of order 9 + -real(0x19f69929deb8bc0LL),real(0x1054723730b1600LL), + -real(0xdce6aeb616e040LL),real(0x8c0069813d6480LL), + -real(0x7e59f70027c8c0LL),real(0x4bea01551feb00LL), + -real(0x42bb28790cad40LL),real(0x21dd61f97d4180LL), + -real(0x14f93d4343f5c0LL),real(0xd58968a8df35eLL), + reale(9215,0xf98764c489b7fLL), + // C4[1], coeff of eps^13, polynomial in n of order 10 + -real(0x1ecd4a3794400de0LL),real(0x101df33ec1bb0110LL), + -real(0xbc64ec7794b2980LL),real(0x71d5f4e2a637ff0LL), + -real(0x625888ecafc7520LL),real(0x3aa6879742ff4d0LL), + -real(0x3585f7f60d164c0LL),real(0x1d18174ef21abb0LL), + -real(0x18117eb39416c60LL),real(0x8df7a42ab2f090LL), + real(0x23413de9276581LL),reale(64511,0xd2b3c15fc4079LL), + // C4[1], coeff of eps^12, polynomial in n of order 11 + -real(0x113775cb09582880LL),real(0x5790112bb17c4700LL), + -real(0x204e01ed2b929d80LL),real(0x1063af9e8d99cc00LL), + -real(0xc3ef805036ada80LL),real(0x701a56aa2d31100LL), + -real(0x63910631abdcf80LL),real(0x368e0c562512600LL), + -real(0x31ed34307286c80LL),real(0x170e89cb9dd1b00LL), + -real(0xf5f0efdd07a180LL),real(0x93fb623bde75e4LL), + reale(64511,0xd2b3c15fc4079LL), + // C4[1], coeff of eps^11, polynomial in n of order 12 + real(0x13635f7860ae69c0LL),-real(0x169d904d9d4691d0LL), + -real(0x2254277308cd9e0LL),real(0xd20446e8d8a9710LL), + -real(0x4df2aedeefd1980LL),real(0x25e2aff2baec9f0LL), + -real(0x1d3856fa2b08920LL),real(0xf7cadc640f92d0LL), + -real(0xe3d2f6c9ad5cc0LL),real(0x6e412eaf297db0LL), + -real(0x62000ef613c860LL),real(0x201266fb021690LL), + real(0x7ee4c480c21e1LL),reale(9215,0xf98764c489b7fLL), + // C4[1], coeff of eps^10, polynomial in n of order 13 + -real(0x5fe482817c4c40LL),-real(0x3373730b4b79d00LL), + real(0x140f919171472640LL),-real(0x17f10e5417ef9980LL), + -real(0x1b454cf244cf340LL),real(0xdd42319af5c0200LL), + -real(0x530205145e450c0LL),real(0x25eec00584a7d80LL), + -real(0x1e9e562555aaa40LL),real(0xe85806d73b2100LL), + -real(0xde44387c5bb7c0LL),real(0x581f06023d3480LL), + -real(0x421ccd71c33140LL),real(0x245ff7208ef53aLL), + reale(9215,0xf98764c489b7fLL), + // C4[1], coeff of eps^9, polynomial in n of order 14 + -real(0x47f3709eaa4320LL),-real(0xbb640bc2e1ae70LL), + -real(0x2a7854a3ead7b40LL),-real(0x1701de8d91314210LL), + reale(2329,0x5f8472b9624a0LL),-reale(2855,0xe7c1182872fb0LL), + -real(0x785bf95be998780LL),real(0x66690260b30024b0LL), + -real(0x272595745774a3a0LL),real(0x104f772bee315710LL), + -real(0xe11ad02f34b53c0LL),real(0x5a192e055800370LL), + -real(0x58d8bfb781fbbe0LL),real(0x17a156426e4c5d0LL), + real(0x5c88907e67c575LL),reale(64511,0xd2b3c15fc4079LL), + // C4[1], coeff of eps^8, polynomial in n of order 15 + -real(0x1138d3e7324700LL),-real(0x210a1008a4f200LL), + -real(0x47b7d2285e8500LL),-real(0xbbe3dba17a1400LL), + -real(0x2aeb63e9e4cb300LL),-real(0x1781d8a9c80b7600LL), + reale(2419,0xe4212c9be8f00LL),-reale(3063,0xd7c230ad9b800LL), + -real(0x116171a56015f00LL),real(0x6cc31b4079da8600LL), + -real(0x2af22cc657d11d00LL),real(0xf75e4ec12d0a400LL), + -real(0xeb60cc0dd754b00LL),real(0x472a49a74880200LL), + -real(0x4174f343c328900LL),real(0x1ed324af4f2fd18LL), + reale(64511,0xd2b3c15fc4079LL), + // C4[1], coeff of eps^7, polynomial in n of order 16 + -real(0xd56426d4f700LL),-real(0x15fa65017d450LL), + -real(0x26ba18ad11e20LL),-real(0x4a9605f1a58f0LL), + -real(0xa2b494aee2940LL),-real(0x1ad07f38fd2390LL), + -real(0x62deb836d71c60LL),-real(0x36d68c47bf27830LL), + real(0x167d3fa4abc50480LL),-real(0x1d9b2fd161b99ad0LL), + real(0x13a59aea9293560LL),real(0x10886ca52ccf3090LL), + -real(0x6e8a4c27dbf8dc0LL),real(0x1f02cd8f1f8a5f0LL), + -real(0x2216230a1ac48e0LL),real(0x5f13c815b08150LL), + real(0x1666b06ca8f56dLL),reale(9215,0xf98764c489b7fLL), + // C4[1], coeff of eps^6, polynomial in n of order 17 + -real(0x2678d0ed9f140LL),-real(0x39d0dbe263c00LL), + -real(0x5aa623a5216c0LL),-real(0x95d2f30c44880LL), + -real(0x108ea4db631840LL),-real(0x2005d27e0acd00LL), + -real(0x463ad5e0e22dc0LL),-real(0xba80ab02c40180LL), + -real(0x2b67c47d5d48f40LL),-real(0x186d6a49f7da1e00LL), + reale(2625,0x9832921f08b40LL),-reale(3627,0xa72ee4675a80LL), + real(0x17be252bac67e9c0LL),real(0x7a8f5366d9ba1100LL), + -real(0x38a15d77b043abc0LL),real(0x9cd4e0bf35fec80LL), + -real(0xceae5004f176d40LL),real(0x479bb2ae3c01ddaLL), + reale(64511,0xd2b3c15fc4079LL), + // C4[1], coeff of eps^5, polynomial in n of order 18 + -real(0x11dc9e54dea60LL),-real(0x193ec5647cdf0LL), + -real(0x24bda460ceb00LL),-real(0x3760182d9a010LL), + -real(0x5717ea0e54ba0LL),-real(0x907095ecddc30LL), + -real(0x10063188dee040LL),-real(0x1f228e862f9650LL), + -real(0x44adcde9a37ce0LL),-real(0xb7cbf8f2d0e270LL), + -real(0x2b3f803c770f580LL),-real(0x18c05d008644d490LL), + reale(2737,0x3ce4b1d74e1e0LL),-reale(4017,0xdf79eceb980b0LL), + real(0x30ac41edd5123540LL),real(0x7e3ade121a8e0530LL), + -real(0x45ec5d28a0fecf60LL),real(0x3577aaf625fa910LL), + real(0x7292b77d2ccfc9LL),reale(64511,0xd2b3c15fc4079LL), + // C4[1], coeff of eps^4, polynomial in n of order 19 + -real(0x14469ef39280LL),-real(0x1b74a6d65900LL),-real(0x25fc6724f380LL), + -real(0x35e25bf6c800LL),-real(0x4eb76c6a3c80LL),-real(0x771a92ddb700LL), + -real(0xbc1644489d80LL),-real(0x13946cde25600LL), + -real(0x22eaf36054680LL),-real(0x44349dbbbd500LL), + -real(0x976a625a56780LL),-real(0x1989ef99e16400LL), + -real(0x6150e2c16e3080LL),-real(0x38c68feccea3300LL), + real(0x1963a1a8e71b2e80LL),-real(0x2849f713f5ed7200LL), + real(0xd30bac57bb18580LL),real(0x105e1a36741daf00LL), + -real(0xc8c696e03b05b80LL),real(0x1feab31d626d154LL), + reale(9215,0xf98764c489b7fLL), + // C4[1], coeff of eps^3, polynomial in n of order 20 + -real(0xa4172dfa1c0LL),-real(0xd77fb109ed0LL),-real(0x11fc3eda7860LL), + -real(0x1879b9235cf0LL),-real(0x2209eb95db00LL),-real(0x308bcfa5f110LL), + -real(0x47510fa29da0LL),-real(0x6c88ffcf6f30LL),-real(0xac6dd3019440LL), + -real(0x120fcca63eb50LL),-real(0x206b8121592e0LL), + -real(0x3fc3a9ace7970LL),-real(0x8ea4f3b556d80LL), + -real(0x18488ccc5b2d90LL),-real(0x5db9d9787df820LL), + -real(0x37d6c7544511bb0LL),real(0x1a02f9f8abfbf940LL), + -real(0x2d9fe91163ac57d0LL),real(0x18b01234447992a0LL), + real(0x46ed1c414c80a10LL),-real(0x57c56c90ceabfa7LL), + reale(9215,0xf98764c489b7fLL), + // C4[1], coeff of eps^2, polynomial in n of order 21 + -real(0x2271f7278cc0LL),-real(0x2c3f5c6ec900LL),-real(0x399dc5a18140LL), + -real(0x4c2bebb96280LL),-real(0x6670101499c0LL),-real(0x8c75450f5400LL), + -real(0xc4e9f8733e40LL),-real(0x11b3ff75a0580LL), + -real(0x1a3e7cf3fd6c0LL),-real(0x2853a9e02df00LL), + -real(0x40b8bca6ccb40LL),-real(0x6da2a9d234880LL), + -real(0xc6fc7477c83c0LL),-real(0x18bdddb834aa00LL), + -real(0x37ff6cf7616840LL),-real(0x9a5f4811c06b80LL), + -real(0x25bde21729de0c0LL),-real(0x16ea24b2a28ff500LL), + reale(2841,0x69c686bdbaac0LL),-reale(5560,0x9d73ff6dcae80LL), + reale(4369,0xdffb6688d240LL),-real(0x4cccbefeb4d67b22LL), + reale(64511,0xd2b3c15fc4079LL), + // C4[1], coeff of eps^1, polynomial in n of order 22 + -real(0xd0da1980ba0LL),-real(0x10803fb20d70LL),-real(0x151a70ced0c0LL), + -real(0x1b569dc61a10LL),-real(0x23ecd2ce6de0LL),-real(0x2ff80cba60b0LL), + -real(0x413672596700LL),-real(0x5a7b8b75a550LL),-real(0x8082f2984020LL), + -real(0xbb859b75abf0LL),-real(0x11a6bf1637d40LL), + -real(0x1b9a143813890LL),-real(0x2d2aacb8da260LL), + -real(0x4e2c5253a0f30LL),-real(0x914a9e2ed3380LL), + -real(0x128a302f4ef3d0LL),-real(0x2b2226f5e6b4a0LL), + -real(0x7a36190e0daa70LL),-real(0x1e8d8643836a9c0LL), + -real(0x129e3dd12414f710LL),reale(2184,0x86ffdb3446920LL), + -reale(3276,0xca7fc8ce69db0LL),real(0x5999897e7da4e4fdLL), + reale(64511,0xd2b3c15fc4079LL), + // C4[2], coeff of eps^23, polynomial in n of order 0 + 10384,real(854125125), + // C4[2], coeff of eps^22, polynomial in n of order 1 + real(61416608),15713412,real(0x35f1be97217LL), + // C4[2], coeff of eps^21, polynomial in n of order 2 + real(1053643008),-real(709188480),real(436906360),real(0x18f301bf7f77LL), + // C4[2], coeff of eps^20, polynomial in n of order 3 + real(0x45823cb069c0LL),-real(0x3dc56cd10180LL),real(0x15b4532d4340LL), + real(0x5946b207ad8LL),real(0xf72bf6e15a9abe5LL), + // C4[2], coeff of eps^19, polynomial in n of order 4 + real(0x1b1b08a8c6e00LL),-real(0x1a1dea5249180LL),real(0xc1b857255700LL), + -real(0x8a94db95d080LL),real(0x5209b9749ec8LL), + real(0x3a6f4368c13f04a5LL), + // C4[2], coeff of eps^18, polynomial in n of order 5 + real(0x13c972f90d64d60LL),-real(0x12d8369dbbbb080LL), + real(0xa013fa80d7c1a0LL),-real(0x95d1a2bb4de840LL), + real(0x30a495fb9aa5e0LL),real(0xc95efc891d64cLL), + reale(107519,0xb480ecf4f161fLL), + // C4[2], coeff of eps^17, polynomial in n of order 6 + real(0x4b31e4eff4bc00LL),-real(0x4190c8b5d5de00LL), + real(0x27770ac0842800LL),-real(0x270a0d33995200LL), + real(0x10c9f01b859400LL),-real(0xd056352974600LL), + real(0x74f9dc1f6f260LL),reale(15359,0xf536fd4790329LL), + // C4[2], coeff of eps^16, polynomial in n of order 7 + real(0x39908ef33285d00LL),-real(0x2a7d467835cbe00LL), + real(0x1e0505551ade700LL),-real(0x1bf3204cf26d400LL), + real(0xe195527d96f100LL),-real(0xe0af5ccd52ea00LL), + real(0x41681113e87b00LL),real(0x1112b429bab2a0LL), + reale(107519,0xb480ecf4f161fLL), + // C4[2], coeff of eps^15, polynomial in n of order 8 + real(0xf8fa0142055000LL),-real(0x8f8aa7832e8a00LL), + real(0x7d6f3ddfb47c00LL),-real(0x62d1e182b7be00LL), + real(0x3bb149eddea800LL),-real(0x3be3b3e26a7200LL), + real(0x175d0d17dad400LL),-real(0x14371cfc4fa600LL), + real(0xa8f8f5855a060LL),reale(15359,0xf536fd4790329LL), + // C4[2], coeff of eps^14, polynomial in n of order 9 + real(0x21490cd145715e0LL),-real(0xe087822f191900LL), + real(0xf91f2bb3d29820LL),-real(0x949428c90dc2c0LL), + real(0x7371ad50b34a60LL),-real(0x63c52e9a850c80LL), + real(0x301579a22c8ca0LL),-real(0x33552a69ca1640LL), + real(0xcc2c8c733bee0LL),real(0x35f5f30acfbecLL), + reale(15359,0xf536fd4790329LL), + // C4[2], coeff of eps^13, polynomial in n of order 10 + real(0x29bb6acaa073ef00LL),-real(0xc930d526d728e80LL), + real(0xf55c2b3103d0c00LL),-real(0x63b9281a5449980LL), + real(0x6acdfd5dbb92900LL),-real(0x441c8fce3be0480LL), + real(0x2be797a45cb8600LL),-real(0x2aec3395f438f80LL), + real(0xec70ff5d376300LL),-real(0xedc27143c9fa80LL), + real(0x7039bcd0124e68LL),reale(107519,0xb480ecf4f161fLL), + // C4[2], coeff of eps^12, polynomial in n of order 11 + -real(0x17ce935fc610ad40LL),-real(0x5d5bbde81a902580LL), + real(0x2dcc12fb45c89240LL),-real(0xc1c61e98a479e00LL), + real(0x10183633a5ddf1c0LL),-real(0x672de318faa1680LL), + real(0x64ee85310393140LL),-real(0x481cf983db0cf00LL), + real(0x2299f24f52810c0LL),-real(0x271fc56086d0780LL), + real(0x79dac155045040LL),real(0x20c44d35dada38LL), + reale(107519,0xb480ecf4f161fLL), + // C4[2], coeff of eps^11, polynomial in n of order 12 + -real(0x6b8bdbaa2666e600LL),reale(2706,0x6d4e4332c7e80LL), + -real(0x201eb2939ffc7500LL),-real(0x605f6d97c740b880LL), + real(0x32fb1ca66ccebc00LL),-real(0xb85f2dd585e0f80LL), + real(0x10b7dbe9dec0ed00LL),-real(0x6e454f6a0fd4680LL), + real(0x594f6f139205e00LL),-real(0x4c204810d601d80LL), + real(0x16a875347934f00LL),-real(0x1be72589c185480LL), + real(0xb5a396e2ccd788LL),reale(107519,0xb480ecf4f161fLL), + // C4[2], coeff of eps^10, polynomial in n of order 13 + real(0x332d666e095e20LL),real(0x205e97ebfb32780LL), + -real(0xf80bf36cd359f20LL),real(0x19615ff8d71e0640LL), + -real(0x61aef235a414c60LL),-real(0xe1fda0393083b00LL), + real(0x83e2ad192fc7660LL),-real(0x18ece140ef0fc40LL), + real(0x26bbb213037c920LL),-real(0x11a4c9418dd9d80LL), + real(0x9ec708de66cbe0LL),-real(0xaee5994e9b7ec0LL), + real(0x1626e135e59ea0LL),real(0x610ef2b6b35c4LL), + reale(15359,0xf536fd4790329LL), + // C4[2], coeff of eps^9, polynomial in n of order 14 + real(0x1b709db1871200LL),real(0x51a2a024c26b00LL), + real(0x157c554050bb400LL),real(0xddb41f944653d00LL), + -real(0x6d182f563006aa00LL),reale(2991,0xf7eb0ae304f00LL), + -real(0x387b65599c618800LL),-real(0x64242336a83ddf00LL), + real(0x4282c6eaa3899a00LL),-real(0xa8fc3afb1e6cd00LL), + real(0x1040dddbf0493c00LL),-real(0x9184bc07b2bfb00LL), + real(0x281ea22622bde00LL),-real(0x3dc59bc648ee900LL), + real(0x13fb78815b4ca90LL),reale(107519,0xb480ecf4f161fLL), + // C4[2], coeff of eps^8, polynomial in n of order 15 + real(0xacc0646b5180LL),real(0x1753663f74b00LL),real(0x3994d0061e480LL), + real(0xadc1fbdd72e00LL),real(0x2e87a44adab780LL), + real(0x1eaeb3451821100LL),-real(0xf937e414930b580LL), + real(0x1c27d8b21df37400LL),-real(0xaa5908f76fee280LL), + -real(0xe1c8d327ee92900LL),real(0xb2675f22d49b080LL), + -real(0x19e66cd66684600LL),real(0x1f3a47aa5ea8380LL), + -real(0x18da246c74e6300LL),real(0x10dd3b80dd1680LL), + real(0x3f21f272d2a30LL),reale(15359,0xf536fd4790329LL), + // C4[2], coeff of eps^7, polynomial in n of order 16 + real(0x2957d7da1000LL),real(0x4c28ba8a3700LL),real(0x9714a6610e00LL), + real(0x14a5ff52a4500LL),real(0x33af2f78d8c00LL),real(0x9e87298409300LL), + real(0x2b4e15dbd10a00LL),real(0x1d4c6da210ea100LL), + -real(0xf6c4a6847e2f800LL),real(0x1da98c51a6b5ef00LL), + -real(0xe1270d810dcfa00LL),-real(0xd23a021f3080300LL), + real(0xd3b280b26948400LL),-real(0x22fd890d309b500LL), + real(0x119ef453c630200LL),-real(0x1959af9980da700LL), + real(0x5959078fa70870LL),reale(15359,0xf536fd4790329LL), + // C4[2], coeff of eps^6, polynomial in n of order 17 + real(0x511612baa2a0LL),real(0x87a79de92a00LL),real(0xee2dd20af160LL), + real(0x1bbcfaf32f4c0LL),real(0x37ba524fb5020LL),real(0x7b9b8f2a45f80LL), + real(0x13a76fcf6fdee0LL),real(0x3d717a0fbe0a40LL), + real(0x112dc752f02bda0LL),real(0xbfa002cc4689500LL), + -real(0x694405622017f3a0LL),reale(3484,0x979f3cbb89fc0LL), + -reale(2088,0x4fe2045ae14e0LL),-real(0x49f87439584d3580LL), + real(0x6c3e90c1455479e0LL),-real(0x1afff07538f04ac0LL), + -real(0x1a0f4cdf3b62760LL),-real(0x112f9b85f9ebf7cLL), + reale(107519,0xb480ecf4f161fLL), + // C4[2], coeff of eps^5, polynomial in n of order 18 + real(0x181437e05500LL),real(0x25c7b1fe6a80LL),real(0x3d5ebd606800LL), + real(0x67dd27f0e580LL),real(0xb8ac7d2a7b00LL),real(0x15ce71e5cc080LL), + real(0x2c7c6a3654e00LL),real(0x6460c05d0bb80LL),real(0x1046637cd7a100LL), + real(0x340d46956b9680LL),real(0xef5f1bde883400LL), + real(0xacec6aed73c1180LL),-real(0x63ea680d7ea23900LL), + reale(3605,0xecc3861a0ec80LL),-reale(2759,0xc804a6c40e600LL), + -real(0x212a787bd0571880LL),real(0x70c6a0884332ed00LL), + -real(0x31a5fa2db58d3d80LL),real(0x5033807138f7d98LL), + reale(107519,0xb480ecf4f161fLL), + // C4[2], coeff of eps^4, polynomial in n of order 19 + real(0x6f3f0983c40LL),real(0xa6cf9192980LL),real(0x100e50e166c0LL), + real(0x197f658cec00LL),real(0x29f706a6f140LL),real(0x480b7a0eae80LL), + real(0x821ecd9c1bc0LL),real(0xfa1d1da0b100LL),real(0x2081a78802640LL), + real(0x4aefd4add3380LL),real(0xc730805b650c0LL),real(0x28f491e04e7600LL), + real(0xc2d07512dddb40LL),real(0x92e539684c6b880LL), + -real(0x5a2096cfc695fa40LL),reale(3598,0x9cd1e91b83b00LL), + -reale(3553,0x1d49601c5efc0LL),real(0x31a5fa2db58d3d80LL), + real(0x3760835a5e313ac0LL),-real(0x1bed5cb9b61f7298LL), + reale(107519,0xb480ecf4f161fLL), + // C4[2], coeff of eps^3, polynomial in n of order 20 + real(273006835200LL),real(395945493120LL),real(586817304320LL), + real(891220401024LL),real(0x1440886f800LL),real(0x20a73015480LL), + real(0x36a4a027900LL),real(0x5f8b4acad80LL),real(0xb01798c3a00LL), + real(0x15a2eb8a6680LL),real(0x2e235b147b00LL),real(0x6d6a30f2bf80LL), + real(0x12c54474b7c00LL),real(0x40129870df880LL),real(0x13e41ecc817d00LL), + real(0xfcf67c8cf45180LL),-real(0xa65f288fe794200LL), + real(0x1cea83a477ce0a80LL),-real(0x240239aaff748100LL), + real(0x1547221396f36380LL),-real(0x4e04d247d427178LL), + reale(15359,0xf536fd4790329LL), + // C4[2], coeff of eps^2, polynomial in n of order 21 + real(317370445920LL),real(448806691200LL),real(646426411680LL), + real(950282020800LL),real(0x14ccaecc4e0LL),real(0x201acdf4e00LL), + real(0x33093819720LL),real(0x53ed06eb440LL),real(0x8f8eb441960LL), + real(0x1013bf0bfa80LL),real(0x1e750d7baba0LL),real(0x3dc4346800c0LL), + real(0x88729901ade0LL),real(0x150e863aba700LL),real(0x3c89c1e8d8020LL), + real(0xd9efed463cd40LL),real(0x47e39644808260LL), + real(0x3d1b0c8706d5380LL),-real(0x2af704cef0cdeb60LL), + real(0x7c1ef17245e119c0LL),-reale(2184,0x86ffdb3446920LL), + real(0x333329ff2339a76cLL),reale(107519,0xb480ecf4f161fLL), + // C4[3], coeff of eps^23, polynomial in n of order 0 + 70576,real(29211079275LL), + // C4[3], coeff of eps^22, polynomial in n of order 1 + -real(31178752),real(16812224),real(0x192c8c2464fLL), + // C4[3], coeff of eps^21, polynomial in n of order 2 + -real(135977211392LL),real(37023086848LL),real(9903771944LL), + real(0xb98f5d0044051LL), + // C4[3], coeff of eps^20, polynomial in n of order 3 + -real(0x30f8b0f5c00LL),real(0x12d79f66800LL),-real(0x115c7023400LL), + real(606224480400LL),real(0xa7c6f527b4f7c7LL), + // C4[3], coeff of eps^19, polynomial in n of order 4 + -real(0x3317d68847dc00LL),real(0x19fc69dd236700LL), + -real(0x1c6d14df7ace00LL),real(0x6cfe4fac52d00LL), + real(0x1d99f24357808LL),reale(30105,0x847604e86c8c1LL), + // C4[3], coeff of eps^18, polynomial in n of order 5 + -real(0x15b0eba45ef8000LL),real(0xf79bdd24a10000LL), + -real(0xf32a8559288000LL),real(0x563281b24a8000LL), + -real(0x5920796c2f8000LL),real(0x29f7b73471c480LL), + reale(150527,0x964e188a1ebc5LL), + // C4[3], coeff of eps^17, polynomial in n of order 6 + -real(0x1c02d0336ef1800LL),real(0x1d91ba24525dc00LL), + -real(0x163d203e4811000LL),real(0xb8e8b252aa8400LL), + -real(0xd2485de6110800LL),real(0x2a40e341b4ac00LL), + real(0xbb70f2cbcf360LL),reale(150527,0x964e188a1ebc5LL), + // C4[3], coeff of eps^16, polynomial in n of order 7 + -real(0x58b4aa16ae3000LL),real(0x7fa0a14380e000LL), + -real(0x429ab6e3829000LL),real(0x383428ed0d4000LL), + -real(0x32e93ebd99f000LL),real(0x108fe88bbda000LL), + -real(0x13ba86ffa65000LL),real(0x868b4ab8e3340LL), + reale(21503,0xf0e695ca96ad3LL), + // C4[3], coeff of eps^15, polynomial in n of order 8 + -real(0xaedfc7febee000LL),real(0xe403ca9386ec00LL), + -real(0x5568aa53f7a800LL),real(0x76f3d9af940400LL), + -real(0x475f28b7bb7000LL),real(0x29018461d69c00LL), + -real(0x2ed89591f13800LL),real(0x74380445fb400LL), + real(0x21274712bcba0LL),reale(21503,0xf0e695ca96ad3LL), + // C4[3], coeff of eps^14, polynomial in n of order 9 + -real(0x231ca125e5c8000LL),real(753027184687LL<<17), + -real(0x97f88531f38000LL),real(0xee839ade908000LL), + -real(0x572a9cdd748000LL),real(0x65a05d4f5f0000LL), + -real(0x4ce11756538000LL),real(0x177f524c958000LL), + -real(0x20e57338048000LL),real(0xc4518e260f380LL), + reale(21503,0xf0e695ca96ad3LL), + // C4[3], coeff of eps^13, polynomial in n of order 10 + -real(0x44ebd4477ad4f200LL),real(0x9a6a6024b320f00LL), + -real(0xe915ce102d6a800LL),real(0xb28d5273bcee100LL), + -real(0x37fa968ec235e00LL),real(0x68974b850671300LL), + -real(0x2a735b9bf505400LL),real(0x20513dd7a7f6500LL), + -real(0x220360a9be2ca00LL),real(0x36d1c1a3f49700LL), + real(0x10369a2227fd98LL),reale(150527,0x964e188a1ebc5LL), + // C4[3], coeff of eps^12, polynomial in n of order 11 + real(0x52462bb828351400LL),real(0x4a4d1c14e6172800LL), + -real(0x4ced32c430d22400LL),real(0xb52b1b0c2492000LL), + -real(0xd058359466b1c00LL),real(0xd07709dd3bd1800LL), + -real(0x30072e56aae5400LL),real(0x605c027d5629000LL), + -real(0x32e58b8ebb44c00LL),real(0x108221f23a90800LL), + -real(0x1a7ac7295958400LL),real(0x836be4086f28d0LL), + reale(150527,0x964e188a1ebc5LL), + // C4[3], coeff of eps^11, polynomial in n of order 12 + real(0x48f7bc8748dd3400LL),-reale(2561,0x7f9f9673a4700LL), + real(0x601d0ed1c7f2b600LL),real(0x449204e4f86d4300LL), + -real(0x56194f80f81a8800LL),real(0xea108cfa6f6ed00LL), + -real(0xa7ad46bd016c600LL),real(0xef32c344e507700LL), + -real(0x30a1762ff0e4400LL),real(0x4a78ea25c4fa100LL), + -real(0x3c3cca9d1bd4200LL),real(0x22cbd76a022b00LL), + real(0x9df3abb037278LL),reale(150527,0x964e188a1ebc5LL), + // C4[3], coeff of eps^10, polynomial in n of order 13 + -real(0x9607df2a17c000LL),-real(0x739371b7f3d8000LL), + real(0x4688c366039fc000LL),-reale(2611,0x8a66cbfc04000LL), + real(0x7056fbc7b1c24000LL),real(0x3af7506941670000LL), + -real(0x601cadbaecf24000LL),real(0x14affbea17164000LL), + -real(0x6daccbfd0bfc000LL),real(0x1036680bb42b8000LL), + -real(0x42f04a7d6e84000LL),real(0x246d9b6ab84c000LL), + -real(0x37cce3b53adc000LL),real(0xd43660c7def0c0LL), + reale(150527,0x964e188a1ebc5LL), + // C4[3], coeff of eps^9, polynomial in n of order 14 + -real(0x115a7e31ff400LL),-real(0x3c90c47c29600LL), + -real(0x1311ab10640800LL),-real(0xf2246746703a00LL), + real(0x99b5e8c5c68e400LL),-real(0x179a6d9c8ead9e00LL), + real(0x12bd250608495000LL),real(0x63777cc9563be00LL), + -real(0xf1ef7972c204400LL),real(0x47367775d725a00LL), + -real(0x63378c7bb15800LL),real(0x22d63078c5cb600LL), + -real(0xf8707c83e76c00LL),-real(0xb0e06786eae00LL), + -real(0x5e4438ea922f0LL),reale(21503,0xf0e695ca96ad3LL), + // C4[3], coeff of eps^8, polynomial in n of order 15 + -real(0x1fe011d85800LL),-real(0x4f422fb05000LL),-real(0xe40060fc8800LL), + -real(0x32e664e9c2000LL),-real(0x1078ec0ef63800LL), + -real(0xd864902b71f000LL),real(0x8fab71292d19800LL), + -real(0x179bbec0170ac000LL),real(0x15c925f1e4f1e800LL), + real(0x2c36e0d96c07000LL),-real(0x100d07856dfe4800LL), + real(0x6d9c3efea16a000LL),-real(0x13ac4a3567f800LL), + real(0x15b22a4de1ed000LL),-real(0x1452d18e2b42800LL), + real(0x32eab893d697a0LL),reale(21503,0xf0e695ca96ad3LL), + // C4[3], coeff of eps^7, polynomial in n of order 16 + -real(0x5003ad66000LL),-real(0xa79ae296200LL),-real(0x17d9e9f5d400LL), + -real(0x3c8762ad2600LL),-real(0xb232a56ac800LL),-real(0x28dbf6ee52a00LL), + -real(0xda6199e36bc00LL),-real(0xba74c6aa46ee00LL), + real(0x825959cb764d000LL),-real(0x17232e4c4e57f200LL), + real(0x190bf0598fc65c00LL),-real(0x27c51cb844db600LL), + -real(0xf8735fc98339800LL),real(0xa28217eef524600LL), + -real(0xfc87c9cb4a8c00LL),-real(0x3228ffc0ed7e00LL), + -real(0x387bf611406670LL),reale(21503,0xf0e695ca96ad3LL), + // C4[3], coeff of eps^6, polynomial in n of order 17 + -real(0x62d694dc000LL),-real(97716157LL<<17),-real(0x173b38f24000LL), + -real(0x319b0ca1c000LL),-real(0x7361a893c000LL),-real(0x12be5bef38000LL), + -real(0x38b3402cc4000LL),-real(0xd6a4403694000LL), + -real(0x4a69cc1535c000LL),-real(0x42816c266fd0000LL), + real(0x315cb6a39d95c000LL),-reale(2449,0xcf91c36a8c000LL), + reale(3143,0x2391393fc4000LL),-real(0x466890d45f668000LL), + -real(0x50368754849c4000LL),real(0x594b313771cfc000LL), + -real(0x1cc16f4e99cdc000LL),real(0x1e8d8643836a9c0LL), + reale(150527,0x964e188a1ebc5LL), + // C4[3], coeff of eps^5, polynomial in n of order 18 + -real(0x1136c8f5600LL),-real(0x1e3b013df00LL),-real(0x37550c23000LL), + -real(0x6a508e10100LL),-real(0xd872daf0a00LL),-real(0x1d8dd6618300LL), + -real(0x468422b6a400LL),-real(0xbc9d06f02500LL),-real(0x24d784d09be00LL), + -real(0x90d122dffa700LL),-real(0x347ca809f91800LL), + -real(0x31861ec3b2ac900LL),real(0x276d051382ba8e00LL), + -reale(2163,0x55347fa444b00LL),reale(3319,0x8d7da907400LL), + -reale(2191,0xdbae56666ed00LL),-real(0x47e396448082600LL), + real(0x3577aaf625fa9100LL),-real(0x1449fb28d544cb98LL), + reale(150527,0x964e188a1ebc5LL), + // C4[3], coeff of eps^4, polynomial in n of order 19 + -real(58538142720LL),-real(97662466048LL),-real(168340530176LL), + -real(301206585344LL),-real(562729180160LL),-real(0x1017e988800LL), + -real(0x21987b95400LL),-real(0x4b78a99d000LL),-real(0xb9ccd9f8c00LL), + -real(0x202de3701800LL),-real(0x68b6655d0400LL),-real(0x1af3df037e000LL), + -real(0xa515b5f563c00LL),-real(0xa65924698da800LL), + real(0x8fc72c890104c00LL),-real(0x226e597c6e0df000LL), + real(0x3ee7237bf0721400LL),-real(0x3d1b0c8706d53800LL), + real(0x1e8d8643836a9c00LL),-real(0x634bf45b6b1a7b0LL), + reale(50175,0xdcc4b2d8b4e97LL), + // C4[3], coeff of eps^3, polynomial in n of order 20 + -real(16545868800LL),-real(26558972160LL),-real(43799006720LL), + -real(74458311424LL),-real(131016159232LL),-real(239806362880LL), + -real(459418505728LL),-real(928488660736LL),-real(0x1d19ea9f400LL), + -real(0x43b761f2900LL),-real(0xad7cf6b5600LL),-real(0x1f71d9841300LL), + -real(0x6bcf7c0df800LL),-real(0x1d7abbebd1d00LL), + -real(0xc1b8d2e919a00LL),-real(0xd3e226aef40700LL), + real(0xc94a0b2634a0400LL),-real(0x3577aaf625fa9100LL), + real(0x6aef55ec4bf52200LL),-real(0x634bf45b6b1a7b00LL), + real(0x22221bff6cd11a48LL),reale(150527,0x964e188a1ebc5LL), + // C4[4], coeff of eps^23, polynomial in n of order 0 + 567424,real(87633237825LL), + // C4[4], coeff of eps^22, polynomial in n of order 1 + real(2135226368),real(598833664),real(0x1358168b64fd9LL), + // C4[4], coeff of eps^21, polynomial in n of order 2 + real(23101878272LL),-real(26986989568LL),real(11760203136LL), + real(0x4f869592664b5LL), + // C4[4], coeff of eps^20, polynomial in n of order 3 + real(0xa4d4b674a00LL),-real(0xbdc38ed8400LL),real(0x20274dfee00LL), + real(635330794560LL),real(0x436914c918b5d6dLL), + // C4[4], coeff of eps^19, polynomial in n of order 4 + real(0x481bf9079c000LL),-real(0x3c015f7917000LL),real(0x133447522e000LL), + -real(0x195b19983d000LL),real(0xa0f15f7a8700LL), + reale(3518,0xd3a367a37a66dLL), + // C4[4], coeff of eps^18, polynomial in n of order 5 + real(0x1e9f26efa689000LL),-real(0x100c94382c2c000LL), + real(0xabead3c2e1f000LL),-real(0xc04c79a6f96000LL), + real(0x18fb8548735000LL),real(0x76d40a3ef6c00LL), + reale(193535,0x781b441f4c16bLL), + // C4[4], coeff of eps^17, polynomial in n of order 6 + real(0x780536a0606000LL),-real(0x28779739e97000LL), + real(0x3a9fdf130c4000LL),-real(0x2860390cb81000LL), + real(0xcce73d3902000LL),-real(0x1322aa5844b000LL), + real(0x6bd0a3ad69900LL),reale(27647,0xec962e4d9d27dLL), + // C4[4], coeff of eps^16, polynomial in n of order 7 + real(0x45af61c2ad1f800LL),-real(0x1b140a5252fd000LL), + real(0x348e789bd7f6800LL),-real(0x137ac7aed3be000LL), + real(0x11da35dc2ded800LL),-real(0x12097ef153ff000LL), + real(0x186b19645c4800LL),real(0x7935fe20ccb00LL), + reale(193535,0x781b441f4c16bLL), + // C4[4], coeff of eps^15, polynomial in n of order 8 + real(0x788485be348000LL),-real(0xbf417480965000LL), + real(0xbdad05e3bd6000LL),-real(0x306dcc448df000LL), + real(0x6c08266aea4000LL),-real(0x364dbd52879000LL), + real(0x13468d692f2000LL),-real(0x1f6575294f3000LL), + real(0x97982d7211100LL),reale(27647,0xec962e4d9d27dLL), + // C4[4], coeff of eps^14, polynomial in n of order 9 + real(0x99754be5293000LL),-real(0x273b2ae73028000LL), + real(0xa610233e31d000LL),-real(0x8ee7336f99e000LL), + real(0xd7a1a110827000LL),-real(0x2f0d74b9c14000LL), + real(0x4f375451ab1000LL),-real(0x4002b6db48a000LL), + real(0x20d804cbbb000LL),real(0xa41d3b221400LL), + reale(27647,0xec962e4d9d27dLL), + // C4[4], coeff of eps^13, polynomial in n of order 10 + real(0x6016f6408271a000LL),-real(0x1e7546e7a0d1b000LL), + real(0x18e4e98f72c8000LL),-real(0x113f96068e695000LL), + real(0x6af41cd57176000LL),-real(0x2590480c1d6f000LL), + real(0x61253410a664000LL),-real(0x1c92661c6269000LL), + real(0xfa686d5b4d2000LL),-real(0x188238347643000LL), + real(0x60544135abb900LL),reale(193535,0x781b441f4c16bLL), + // C4[4], coeff of eps^12, polynomial in n of order 11 + -reale(2096,0xf9dac0e4d8600LL),-real(0xa96847f4d191400LL), + real(0x644f115411ee9e00LL),-real(0x2912ee32dfa61000LL), + -real(0x81eeabcb01be00LL),-real(0xfba8345c9670c00LL), + real(0x9bbda8340726600LL),-real(0x11537009b3f0800LL), + real(0x51c2ea8aa8c0a00LL),-real(0x2bb89caf7310400LL), + -real(0x162bd9b163d200LL),-real(0xac0895744a3c0LL), + reale(193535,0x781b441f4c16bLL), + // C4[4], coeff of eps^11, polynomial in n of order 12 + -real(0x296aa6e320b86000LL),real(0x7d9f9f72af514800LL), + -reale(2284,0xfefdd7e855000LL),real(0x8d22edc50949800LL), + real(0x6581767b41ffc000LL),-real(0x371ad32683bb1800LL), + -real(0x915b5d6cd33000LL),-real(0xbce7db3a027c800LL), + real(0xd0ebaf65b57e000LL),-real(0x1274db255bb7800LL), + real(0x2970a5137d6f000LL),-real(0x30b8535f9002800LL), + real(0x8fa21d365c3780LL),reale(193535,0x781b441f4c16bLL), + // C4[4], coeff of eps^10, polynomial in n of order 13 + real(0x73aaee373e800LL),real(0x6d942f05126000LL), + -real(0x55d059f7fa72800LL),real(0x114ee97e0f335000LL), + -real(0x16053fa9ce763800LL),real(0x4d23952dbcc4000LL), + real(0xdda0de6f17eb800LL),-real(0xa56bf33e63ad000LL), + real(0x90dadc83efa800LL),-real(0xbf52dd8df9e000LL), + real(0x2172ab2d7549800LL),-real(0x85ae20f708f000LL), + -real(0x10c904999a7800LL),-real(0xae78582fbfa00LL), + reale(27647,0xec962e4d9d27dLL), + // C4[4], coeff of eps^9, polynomial in n of order 14 + real(0x19fde85a2f000LL),real(0x6b4aa2bef4800LL),real(0x28c46a7eab6000LL), + real(0x2827ed076a87800LL),-real(0x210a7394d5283000LL), + real(0x72396f4bbfb2a800LL),-reale(2620,0x4dc0771ddc000LL), + real(0x40dce91ee367d800LL),real(0x52592d2deb84b000LL), + -real(0x5a9bf1fdd05df800LL),real(0x10e48562d1f92000LL), + real(0x1d4b91258bb3800LL),real(0xaa81c5529799000LL), + -real(0x6eadf18b1729800LL),real(0xd0db43634fa080LL), + reale(193535,0x781b441f4c16bLL), + // C4[4], coeff of eps^8, polynomial in n of order 15 + real(0x45bda664400LL),real(0xc8c97088800LL),real(0x2a5a46b84c00LL), + real(0xb467fe915000LL),real(0x471c8a3c15400LL),real(0x49361b74ae1800LL), + -real(0x3fb304ab7e4a400LL),real(0xedcc81cc3d0e000LL), + -real(0x1834aac92fbf9c00LL),real(0xe864613c6aba800LL), + real(0x759492ec34a6c00LL),-real(0xea1e49c1b0f9000LL), + real(0x5db63d617b37400LL),real(0x31083890113800LL), + -real(0xa60c227ea8400LL),-real(0x3b3da9a3dab180LL), + reale(27647,0xec962e4d9d27dLL), + // C4[4], coeff of eps^7, polynomial in n of order 16 + real(469241266176LL),real(0x10545cac800LL),real(0x2adf04bd000LL), + real(0x7eec6985800LL),real(0x1ba16d402000LL),real(0x7a072d7ae800LL), + real(0x322ca20e07000LL),real(0x3657aa17207800LL), + -real(0x3263434d5c54000LL),real(0xcd0703e8db70800LL), + -real(0x17ea571d4aa2f000LL),real(0x141161dbf7ec9800LL), + -real(0x57d62fedaaa000LL),-real(0xce7cd449810d800LL), + real(0x99132fccc31b000LL),-real(0x27598ad75934800LL), + real(0x18a5cd1eccf980LL),reale(27647,0xec962e4d9d27dLL), + // C4[4], coeff of eps^6, polynomial in n of order 17 + real(341540329472LL),real(727668064256LL),real(0x180da872800LL), + real(0x3b0b3acd000LL),real(0x9f94c3e7800LL),real(0x1e8177ec2000LL), + real(0x6e3ee471c800LL),real(0x1fbe99a5b7000LL),real(0xdb641b5c91800LL), + real(0xfc08a38932c000LL),-real(0xfb6a7929bd39800LL), + real(0x466e762d282a1000LL),-reale(2430,0x8d7c552bc4800LL), + reale(2721,0xe81cb8f96000LL),-real(0x4dc0eea70f08f800LL), + -real(0x1b9eda123c275000LL),real(0x2eba54dfb9ee5800LL), + -real(0xf46c321c1b54e00LL),reale(193535,0x781b441f4c16bLL), + // C4[4], coeff of eps^5, polynomial in n of order 18 + real(31160807424LL),real(61322082304LL),real(3864763LL<<15), + real(276675840000LL),real(646157094912LL),real(0x17cd936d800LL), + real(0x429614e2000LL),real(0xd3b41886800LL),real(0x31f7c0917000LL), + real(0xf21fb6ecf800LL),real(0x6ee892beec000LL),real(0x889688d5b28800LL), + -real(0x944ac482b6bf000LL),real(0x2e4469f00aa71800LL), + -real(0x73c7760d5050a000LL),reale(2642,0x7d1cf3a18a800LL), + -reale(2185,0x6d0b55a915000LL),real(0x3d1b0c8706d53800LL), + -real(0xb7512595147fa80LL),reale(193535,0x781b441f4c16bLL), + // C4[4], coeff of eps^4, polynomial in n of order 19 + real(1806732800),real(3354817536LL),real(6474635776LL), + real(13058088960LL),real(27705484800LL),real(62364503040LL), + real(150565728768LL),real(395569133568LL),real(0x10ca075be00LL), + real(0x37f6c332400LL),real(0xdf0e61c4a00LL),real(0x47dfa8095000LL), + real(0x236014b495600LL),real(0x2f60ae04237c00LL), + -real(0x38c125ca4a81e00LL),real(0x13dd33a066e0a800LL), + -real(0x389cd322becd1200LL),real(0x5ba892ca8a3fd400LL), + -real(0x4c61cfa8c88a8600LL),real(0x18d2fd16dac69ec0LL), + reale(193535,0x781b441f4c16bLL), + // C4[5], coeff of eps^23, polynomial in n of order 0 + 14777984,real(0xd190230980fLL), + // C4[5], coeff of eps^22, polynomial in n of order 1 + -real(104833024),real(39440128),real(0x62c2748ec71LL), + // C4[5], coeff of eps^21, polynomial in n of order 2 + -real(45133008896LL),real(5079242752LL),real(1557031040), + real(0x4f869592664b5LL), + // C4[5], coeff of eps^20, polynomial in n of order 3 + -real(0xecd417f0000LL),real(40869997LL<<17),-real(0x78cb3050000LL), + real(0x28d58610800LL),real(0x5263fcf5c8de3f7LL), + // C4[5], coeff of eps^19, polynomial in n of order 4 + -real(0xf4977948ac000LL),real(0xfebd5b2ac3000LL), + -real(0xf90c852576000LL),real(0x1257a8b1e1000LL),real(0x5e1a6b95fb00LL), + reale(21503,0xf0e695ca96ad3LL), + // C4[5], coeff of eps^18, polynomial in n of order 5 + -real(0x25dd48c154000LL),real(0x596953f850000LL), + -real(0x2b40cdd44c000LL),real(8741106765LL<<15),-real(0x1ab27f0a04000LL), + real(0x7e701f145600LL),reale(3071,0xfdd7cc41833d5LL), + // C4[5], coeff of eps^17, polynomial in n of order 6 + -real(0x4776cd8c606000LL),real(0x6d8a47bfe9f000LL), + -real(0x187da0ea944000LL),real(0x2b758d37739000LL), + -real(0x22fd5e6d302000LL),real(0x107133def3000LL),real(0x56ef801cd100LL), + reale(33791,0xe845c6d0a3a27LL), + // C4[5], coeff of eps^16, polynomial in n of order 7 + -real(0x6b41dfbb0208000LL),real(0x3281e67a9bd0000LL), + -real(0x11e76a3ab618000LL),real(0x2fa8791e0ae0000LL), + -real(0xef00faafea8000LL),real(0x82642584ff0000LL), + -real(0xce6c8b206b8000LL),real(0x33a2c6e1f0cc00LL), + reale(236543,0x59e86fb479711LL), + // C4[5], coeff of eps^15, polynomial in n of order 8 + -real(0xd8a9f7e5e7f8000LL),real(0x75ff062faeb000LL), + -real(0x57d41a79bb5a000LL),real(0x470a22b15ed1000LL), + -real(0x941305430fc000LL),real(0x2571b5b524d7000LL), + -real(0x15ee8622281e000LL),-real(0x810fd11a43000LL), + -real(0x3b143f8fcc100LL),reale(236543,0x59e86fb479711LL), + // C4[5], coeff of eps^14, polynomial in n of order 9 + -real(0x11e2c065bec000LL),real(597104820847LL<<17), + -real(0x2505ead2add4000LL),real(0x375d7cf9da8000LL), + -real(0x7d85d31b2fc000LL),real(0xc6e2597bcf0000LL), + -real(0x1c3d1fca5e4000LL),real(0x26eff911138000LL), + -real(0x32d040ac10c000LL),real(0xa3358a5620200LL), + reale(33791,0xe845c6d0a3a27LL), + // C4[5], coeff of eps^13, polynomial in n of order 10 + -real(0x4e0fa2600780a000LL),real(0x4e911c6aabd6b000LL), + -real(0x693532675088000LL),real(0x218ccc46e845000LL), + -real(0x117da33185e06000LL),real(0x4517905378bf000LL), + -real(0x10ba1c1d3344000LL),real(0x5399b73b0419000LL), + -real(0x1d57ddd62302000LL),-real(0x2b67cba006d000LL), + -real(0x17851f6bed3f00LL),reale(236543,0x59e86fb479711LL), + // C4[5], coeff of eps^12, polynomial in n of order 11 + reale(2256,0x5da9961330000LL),-real(0x4ad304d1312a0000LL), + -real(0x4061e93f2b8f0000LL),real(0xb6157e3bfe7LL<<19), + -real(0x11e106d1afa10000LL),-real(0x36aeeaeb6e60000LL), + -real(0xfcdce3949630000LL),real(0x8af39fd661c0000LL), + real(0x3d8b99e8cb0000LL),real(0x2f252d98fde0000LL), + -real(0x29a890537770000LL),real(0x62af9738c95800LL), + reale(236543,0x59e86fb479711LL), + // C4[5], coeff of eps^11, polynomial in n of order 12 + real(0x2c14f5cef5da000LL),-real(0xb44f7f3a7637800LL), + real(0x144dd8529649b000LL),-real(0xdf6b3f6a9dda800LL), + -real(0x611b67a2b3c4000LL),real(0xe4e2f0fafbb2800LL), + -real(0x51c03e2adea3000LL),-real(0xd7c7b9cb0f0800LL), + -real(0x16096a592762000LL),real(0x1c9393e7a4dc800LL), + -real(0x381de14f961000LL),-real(0xdc6f16ca46800LL), + -real(0xd4311572ebf80LL),reale(33791,0xe845c6d0a3a27LL), + // C4[5], coeff of eps^10, polynomial in n of order 13 + -real(0x1f7df788da000LL),-real(0x249f1260a08000LL), + real(0x2485dbf6336a000LL),-real(0x9fd55d1961bc000LL), + real(0x13ee6db114d4e000LL),-real(0x114ab28a688b0000LL), + -real(0x1759d6f434ee000LL),real(0xe5435dae775c000LL), + -real(0x883ae4654d0a000LL),real(0x6d085594a8000LL), + -real(0x3b594ff4c6000LL),real(0x18b250a1c574000LL), + -real(0xc2af3f725e2000LL),real(0x11b5d0e5824b00LL), + reale(33791,0xe845c6d0a3a27LL), + // C4[5], coeff of eps^9, polynomial in n of order 14 + -real(0x45be4df1f000LL),-real(0x154928d5d8800LL), + -real(0x9c093f54d6000LL),-real(0xbe1dac855c3800LL), + real(0xc8c35d9371b3000LL),-real(0x3b27b3be7f71e800LL), + reale(2105,0xa27ce5e51c000LL),-reale(2266,0x2251e75549800LL), + real(0x215c4ca42d605000LL),real(0x52b0fbc40a45b800LL), + -real(0x52abb6acf6af2000LL),real(0x14cab8bdb5a70800LL), + real(0x422bb90412d7000LL),real(0xaa8f3f42195800LL), + -real(0x18c864fb5207380LL),reale(236543,0x59e86fb479711LL), + // C4[5], coeff of eps^8, polynomial in n of order 15 + -real(0x323b5354000LL),-real(0xa77c1e58000LL),-real(0x297150a3c000LL), + -real(0xd25b36ef0000LL),-real(0x64c6f9d464000LL), + -real(0x816d981c288000LL),real(0x91bbe6aceeb4000LL), + -real(0x2ea0d03ef98a0000LL),real(0x748c356a9df8c000LL), + -reale(2463,0x44f7c770b8000LL),real(0x55038197b9ea4000LL), + real(0x24c2f502435b0000LL),-real(0x557a28e333384000LL), + real(0x319d6c472db18000LL),-real(0xa981b88bf66c000LL), + real(0x2452a78bb4ce00LL),reale(236543,0x59e86fb479711LL), + // C4[5], coeff of eps^7, polynomial in n of order 16 + -real(864347LL<<15),-real(77318326272LL),-real(233990443008LL), + -real(807704598528LL),-real(0x306255a2000LL),-real(0x100b9fcf2800LL), + -real(0x8171cf3d7000LL),-real(0xb08a440213800LL), + real(0xd5be3a4ba94000LL),-real(0x4af12ff99ea4800LL), + real(0xd4237986197f000LL),-real(0x15530c89262c5800LL), + real(0x12c48ba350cca000LL),-real(0x590f07b7ee96800LL), + -real(0x53e376c2a7ab000LL),real(0x5b3d559eedc8800LL), + -real(0x1b37127cacfe280LL),reale(33791,0xe845c6d0a3a27LL), + // C4[5], coeff of eps^6, polynomial in n of order 17 + -real(10859667456LL),-real(199353LL<<17),-real(67565166592LL), + -real(190510645248LL),-real(597656199168LL),-real(65543051LL<<15), + -real(0x869fe272000LL),-real(0x2f027b014000LL),-real(0x19275e39a6000LL), + -real(0x24c57351390000LL),real(0x305c8c1f55c6000LL), + -real(0x12c56d86cea0c000LL),real(0x3c958c9a69892000LL), + -real(0x75427b7d716c8000LL),reale(2264,0x2021045b7e000LL), + -real(0x686da1b1a7d04000LL),real(0x2b2226f5e6b4a000LL), + -real(0x7a36190e0daa700LL),reale(236543,0x59e86fb479711LL), + // C4[5], coeff of eps^5, polynomial in n of order 18 + -real(392933376),-real(865908736),-real(61523<<15),-real(5002905600LL), + -real(13385551872LL),-real(39200544768LL),-real(128292691968LL), + -real(483473385472LL),-real(0x1ffab8af000LL),-real(0xbdf5200f800LL), + -real(0x6d0cb854c000LL),-real(0xacf22c5668800LL), + real(0xfa276dd8697000LL),-real(0x6c92e41ed151800LL), + real(0x18f8d3300c4da000LL),-real(0x382fdb2c1baea800LL), + real(0x4f13f21826f5d000LL),-real(0x3d1b0c8706d53800LL), + real(0x131873ea3222a180LL),reale(236543,0x59e86fb479711LL), + // C4[6], coeff of eps^23, polynomial in n of order 0 + real(20016128),real(0x45dab658805LL), + // C4[6], coeff of eps^22, polynomial in n of order 1 + real(12387831808LL),real(4069857792LL),real(0x1b45118f2c973bLL), + // C4[6], coeff of eps^21, polynomial in n of order 2 + real(828267LL<<17),-real(2724645LL<<16),real(52104335360LL), + real(0x22cae1700cc0f3LL), + // C4[6], coeff of eps^20, polynomial in n of order 3 + real(0x94a2566a8000LL),-real(0x7736ce990000LL),real(0x345f5a38000LL), + real(0x11f45dc9000LL),real(0x36c560e36413be89LL), + // C4[6], coeff of eps^19, polynomial in n of order 4 + real(6043548407LL<<18),-real(7867012491LL<<16),real(0xfe56696e0000LL), + -real(6798211929LL<<16),real(0x66855efe5000LL), + reale(3630,0x89164e7bf8313LL), + // C4[6], coeff of eps^18, polynomial in n of order 5 + real(0x588efe4c176000LL),-real(0xcc317e9b08000LL), + real(0x2e65271667a000LL),-real(0x1cb46908f84000LL), + -real(0x7bc8d2682000LL),-real(0x36524dd3a400LL), + reale(39935,0xe3f55f53aa1d1LL), + // C4[6], coeff of eps^17, polynomial in n of order 6 + real(0x2dbd6ef2050000LL),-real(0x356ee7ee5e8000LL), + real(0x65e2c9482e0000LL),-real(0x1247a684858000LL), + real(84899613015LL<<16),-real(0x1b548eba6c8000LL), + real(0x5c900466be800LL),reale(39935,0xe3f55f53aa1d1LL), + // C4[6], coeff of eps^16, polynomial in n of order 7 + -real(0x3fff5b5aa54000LL),-real(0x6a2cbaeaf348000LL), + real(0x2b55e8782dc4000LL),-real(0x69f22faba30000LL), + real(0x26e11f54b9dc000LL),-real(0x105d41b83118000LL), + -real(0x12eb1ab4e0c000LL),-real(0x9530f9646a800LL), + reale(279551,0x3bb59b49a6cb7LL), + // C4[6], coeff of eps^15, polynomial in n of order 8 + real(0xf488f4012440000LL),-real(0xb16a4f02dfc8000LL), + -real(0x103bba4a90d0000LL),-real(0x4da08c72a3d8000LL), + real(0x45a11acaf220000LL),-real(0x25f21bc63e8000LL), + real(0x12fccd9d4510000LL),-real(0x13e0eb3687f8000LL), + real(0x356c2e9517d800LL),reale(279551,0x3bb59b49a6cb7LL), + // C4[6], coeff of eps^14, polynomial in n of order 9 + real(0x28c5c3199aad2000LL),real(0x80d5fb17a810000LL), + real(0x9c623a70694e000LL),-real(0xf23c0600f3f4000LL), + real(0x6928769f1ca000LL),-real(0x1e8f96869bf8000LL), + real(0x4f9253e0b846000LL),-real(0x11e4e806cbfc000LL), + -real(0x2dad19c0f3e000LL),-real(0x1f2fac1e88dc00LL), + reale(279551,0x3bb59b49a6cb7LL), + // C4[6], coeff of eps^13, polynomial in n of order 10 + -real(0xdb139b99ca0000LL),-real(0x5dbaf74a92790000LL), + real(0x76a096067dfLL<<19),real(0x39f346109690000LL), + real(964470918621LL<<17),-real(0x10aa5a9917350000LL), + real(0x49bc5039b7c0000LL),real(0x92ae304aad0000LL), + real(0x32f3e8ddd3e0000LL),-real(0x233311e51f10000LL), + real(0x4483a6a16dd000LL),reale(279551,0x3bb59b49a6cb7LL), + // C4[6], coeff of eps^12, polynomial in n of order 11 + -real(0xfbf5c5edd078000LL),real(0x1202fde81d5f0000LL), + -real(0x454a07e84fa8000LL),-real(0xbd470dafdb40000LL), + real(0xb3ba7d182928000LL),-real(0x155dacd6cc70000LL), + -real(0xdc21a82d608000LL),-real(0xe96f98256dLL<<17), + real(0x167a9a9742c8000LL),-real(0x7d81f52ed0000LL), + -real(0x7ffde3fc68000LL),-real(0xe287c62fa3000LL), + reale(39935,0xe3f55f53aa1d1LL), + // C4[6], coeff of eps^11, polynomial in n of order 12 + -real(283480971297LL<<18),real(0x5885fb25bf70000LL), + -real(0xe5dec7019ee0000LL),real(0x13305b31e4ed0000LL), + -real(0x9278e6008580000LL),-real(0x855a0cffe9d0000LL), + real(0xd3d848f453e0000LL),-real(0x4a9f485fda70000LL), + -real(0xfb7b0fc02c0000LL),-real(0x691c2e87310000LL), + real(806997945397LL<<17),-real(0x9585db4a3b0000LL), + real(0xa77dc54c8f000LL),reale(39935,0xe3f55f53aa1d1LL), + // C4[6], coeff of eps^10, polynomial in n of order 13 + real(0x6d0001099000LL),real(0x9a74d7ec5c000LL),-real(0xc18676170e1000LL), + real(0x45ad31c7f8a2000LL),-real(0xc7369375e55b000LL), + real(0x1364b97f822e8000LL),-real(0xe19539447ad5000LL), + -real(0x26bf9b041ad2000LL),real(0xce71cc8200b1000LL), + -real(0x8c822446468c000LL),real(0x12e554ec5f37000LL), + real(0xa6c4f3e59ba000LL),real(0x30bb36a52bd000LL), + -real(0x34440d2d335600LL),reale(39935,0xe3f55f53aa1d1LL), + // C4[6], coeff of eps^9, polynomial in n of order 14 + real(0x8fcb3bf8000LL),real(0x33bb5d994000LL),real(7630295323LL<<16), + real(0x2a77da91fcc000LL),-real(0x38ac5a4a0098000LL), + real(0x160f7571fbc04000LL),-real(0x45e92df7f7ee0000LL), + real(0x7f01d3c372a3c000LL),-real(0x7edcf27daed28000LL), + real(0x27dfe4585e674000LL),real(0x38a548f303090000LL), + -real(0x4b87231069354000LL),real(0x24d2adef05648000LL), + -real(0x6a5625dbc71c000LL),-real(0x18371a5d233400LL), + reale(279551,0x3bb59b49a6cb7LL), + // C4[6], coeff of eps^8, polynomial in n of order 15 + real(257397153792LL),real(991547604992LL),real(0x42cbc6ea000LL), + real(843451707LL<<15),real(0xe8a206ec6000LL),real(0x170dd449e34000LL), + -real(0x2102346c3b5e000LL),real(0xe0052eca6690000LL), + -real(0x318a0eacb0b82000LL),real(0x690a1407d3eec000LL), + -reale(2182,0xb601e615a6000LL),real(0x61bf435eea348000LL), + -real(0xe133a8622dca000LL),-real(0x2748b26bf705c000LL), + real(0x220d7d12f9812000LL),-real(0x98dbd66bee38400LL), + reale(279551,0x3bb59b49a6cb7LL), + // C4[6], coeff of eps^7, polynomial in n of order 16 + real(9867LL<<18),real(8045019136LL),real(854413LL<<15), + real(6856031LL<<14),real(8304289LL<<16),real(0x3232f0a4000LL), + real(0x1ec960fb8000LL),real(0x3439f07dcc000LL),-real(0x50f0148aea0000LL), + real(0x25bf6de530f4000LL),-real(0x9635a567bcf8000LL), + real(0x1735ee17e1e1c000LL),-real(0x25a38fef60750000LL), + real(0x2834884b55944000LL),-real(0x1b3dfda8c79a8000LL), + real(0xa981b88bf66c000LL),-real(0x1cc16f4e99cdc00LL), + reale(93183,0xbe91de6de243dLL), + // C4[6], coeff of eps^6, polynomial in n of order 17 + real(169275392),real(7007<<16),real(1348931584),real(4358086656LL), + real(15819288576LL),real(66522136576LL),real(339738054656LL), + real(0x214230b6000LL),real(0x15d36ff77000LL),real(0x2803a29af8000LL), + -real(0x43d629aab87000LL),real(0x232131018d3a000LL), + -real(0x9e155c86fb85000LL),real(0x1c3aabf38857c000LL), + -real(0x361b1ee81aa83000LL),real(0x44dcb2f8dc1be000LL), + -real(0x325282c98d281000LL),real(0xf46c321c1b54e00LL), + reale(279551,0x3bb59b49a6cb7LL), + // C4[7], coeff of eps^23, polynomial in n of order 0 + real(383798272),real(0x7ee24536c1115LL), + // C4[7], coeff of eps^22, polynomial in n of order 1 + -real(127523LL<<20),real(34096398336LL),real(0x1f771442bd4c09LL), + // C4[7], coeff of eps^21, polynomial in n of order 2 + -real(197998999LL<<19),-real(4877411LL<<18),-real(541336621056LL), + real(0x3b1ebd1165abdce9LL), + // C4[7], coeff of eps^20, polynomial in n of order 3 + -real(72076029LL<<20),real(33625235LL<<21),-real(96370351LL<<20), + real(0x142b356fa000LL),real(0x3f32837c872a7963LL), + // C4[7], coeff of eps^19, polynomial in n of order 4 + -real(2249063181LL<<20),real(51883720989LL<<18),-real(12233087197LL<<19), + -real(1430728833LL<<18),-real(0x9e5c3c48b000LL), + reale(46079,0xdfa4f7d6b097bLL), + // C4[7], coeff of eps^18, polynomial in n of order 5 + -real(19747083035LL<<20),real(5938781185LL<<22),-real(1899464157LL<<20), + real(2895955713LL<<21),-real(6730130079LL<<20),real(0x490d94cd2c000LL), + reale(46079,0xdfa4f7d6b097bLL), + // C4[7], coeff of eps^17, polynomial in n of order 6 + -real(0xf7ed31ddbc0000LL),real(90436020675LL<<17), + -real(11671406741LL<<19),real(0x58222c9a6a0000LL), + -real(28407954085LL<<18),-real(6936211449LL<<17), + -real(0x1e088e877c800LL),reale(46079,0xdfa4f7d6b097bLL), + // C4[7], coeff of eps^16, polynomial in n of order 7 + -real(688523975841LL<<19),-real(83606333811LL<<20), + -real(805224840035LL<<19),real(106897379463LL<<21), + real(22163836107LL<<19),real(88997602799LL<<20), + -real(151227539575LL<<19),real(0x28435aa5d4b000LL), + reale(322559,0x1d82c6ded425dLL), + // C4[7], coeff of eps^15, polynomial in n of order 8 + real(557482450381LL<<20),real(0xfbb72a664ee0000LL), + -real(0xa9b81eb4ea40000LL),-real(914196917515LL<<17), + -real(409568792563LL<<19),real(0x4780d431da60000LL), + -real(0x94b9eca98c0000LL),-real(82946761135LL<<17), + -real(0x238b221440f800LL),reale(322559,0x1d82c6ded425dLL), + // C4[7], coeff of eps^14, polynomial in n of order 9 + -real(0x59ec90b7ba5LL<<20),real(233491821731LL<<23), + real(762388756437LL<<20),real(284558585577LL<<21), + -real(0xf0573a4eb1LL<<20),real(25275836579LL<<22), + real(22761999561LL<<20),real(112734627747LL<<21), + -real(126941809085LL<<20),real(0x2fd680f7c84000LL), + reale(322559,0x1d82c6ded425dLL), + // C4[7], coeff of eps^13, polynomial in n of order 10 + real(0xaca84931355LL<<19),real(0x66fb36095adLL<<18), + -real(0x2e7424117bfLL<<21),real(0xcac2488dd23LL<<18), + real(762738574899LL<<19),-real(579380269895LL<<18), + -real(968587667327LL<<20),real(0x73cbed27abc0000LL), + real(75006191505LL<<19),-real(0xdb0f0aaec0000LL), + -real(0x63c3eeba719000LL),reale(322559,0x1d82c6ded425dLL), + // C4[7], coeff of eps^12, polynomial in n of order 11 + real(626455667783LL<<20),-real(567623567285LL<<21), + real(0xf5d2e8872dLL<<20),-real(13896712169LL<<23), + -real(798923144989LL<<20),real(364556664237LL<<21), + -real(129034049335LL<<20),-real(20826366601LL<<22), + -real(51607570881LL<<20),real(46156477135LL<<21), + -real(30888509275LL<<20),real(0x6042659ec2000LL), + reale(46079,0xdfa4f7d6b097bLL), + // C4[7], coeff of eps^11, polynomial in n of order 12 + real(20777559885LL<<20),-real(569775860071LL<<18), + real(0xe9ac41f6dbLL<<19),-real(0xef8ba34c8740000LL), + real(598911876783LL<<21),-real(0x7cf99a74ecc0000LL), + -real(957375911139LL<<19),real(0xc30e342965c0000LL), + -real(423483761553LL<<20),real(35714168193LL<<18), + real(79169625311LL<<19),real(68905136075LL<<18), + -real(0x2f872ef9963000LL),reale(46079,0xdfa4f7d6b097bLL), + // C4[7], coeff of eps^10, polynomial in n of order 13 + -real(18988489LL<<20),-real(129894471LL<<22),real(12886996881LL<<20), + -real(47548938145LL<<21),real(367560238059LL<<20), + -real(106884143981LL<<23),real(0x11c056e4d45LL<<20), + -real(470740881351LL<<21),real(64061082015LL<<20), + real(158992278163LL<<22),-real(634972709127LL<<20), + real(135054066707LL<<21),-real(41343081645LL<<20), + -real(0x7382e0581c000LL),reale(46079,0xdfa4f7d6b097bLL), + // C4[7], coeff of eps^9, polynomial in n of order 14 + -real(7074089LL<<17),-real(95481295LL<<16),-real(249804765LL<<18), + -real(0x6befb7d790000LL),real(0xb301172bea0000LL), + -real(0x5978c2137030000LL),real(0x2fbc3e73e21LL<<19), + -real(0x3f35c80b0f2d0000LL),real(0x6ce3ff0d91260000LL), + -real(0x7761d1ce42b70000LL),real(0x468057c8ed840000LL), + real(0x1bcb7dfb99f0000LL),-real(0x26d98474089e0000LL), + real(0x1d375a3e49150000LL),-real(0x7d9dd8c3269dc00LL), + reale(322559,0x1d82c6ded425dLL), + // C4[7], coeff of eps^8, polynomial in n of order 15 + -real(47805LL<<18),-real(105987LL<<19),-real(1141959LL<<18), + -real(2026311LL<<20),-real(89791009LL<<18),-real(1389164665LL<<19), + real(79467759189LL<<18),-real(86766818957LL<<21), + real(0xbfc5c91f6ec0000LL),-real(0x487b27f822fLL<<19), + real(0x4a699e0854c40000LL),-real(0x69d85e75b6dLL<<20), + real(0x66f7a9fb575c0000LL),-real(0x828d4038ea5LL<<19), + real(0x60dc69748cdLL<<18),-real(0x3f90a5347c68800LL), + reale(322559,0x1d82c6ded425dLL), + // C4[7], coeff of eps^7, polynomial in n of order 16 + -real(143<<20),-real(8085<<16),-real(16121<<17),-real(9810411520LL), + -real(212205LL<<18),-real(6380297LL<<16),-real(37701755LL<<17), + -real(0x95a9db330000LL),real(9764754545LL<<19),-real(0xaf0fe765fd0000LL), + real(0x3a2548493060000LL),-real(0xc8bdaa520270000LL), + real(0x7871cc979b1LL<<18),-real(0x3353672f26710000LL), + real(0x3c89c1e8d8020000LL),-real(0x2a606e22fd9b0000LL), + real(0xc94a0b2634a0400LL),reale(322559,0x1d82c6ded425dLL), + // C4[8], coeff of eps^23, polynomial in n of order 0 + real(7579<<15),real(0x4f56c0c24f87LL), + // C4[8], coeff of eps^22, polynomial in n of order 1 + -real(1660549LL<<21),-real(23648625LL<<16),real(0x38232f25bccb5275LL), + // C4[8], coeff of eps^21, polynomial in n of order 2 + real(9646043LL<<20),-real(24019457LL<<19),real(74048359LL<<15), + real(0x99262e0aeeff091LL), + // C4[8], coeff of eps^20, polynomial in n of order 3 + real(183351957435LL<<19),-real(32827160863LL<<20), + -real(6509093591LL<<19),-real(0x6677b4e9b0000LL), + reale(365566,0xff4ff27401803LL), + // C4[8], coeff of eps^19, polynomial in n of order 4 + real(67207908275LL<<21),-real(201042891LL<<19),real(44011096899LL<<20), + -real(85786308153LL<<19),real(0x195ba7c1ef8000LL), + reale(365566,0xff4ff27401803LL), + // C4[8], coeff of eps^18, polynomial in n of order 5 + -real(13677739LL<<21),-real(1155605701LL<<23),real(11263093395LL<<21), + -real(1170886701LL<<22),-real(422863935LL<<21),-real(9609473031LL<<16), + reale(52223,0xdb549059b7125LL), + // C4[8], coeff of eps^17, polynomial in n of order 6 + -real(105328611LL<<20),-real(0xe3d4e1d7080000LL),real(9484526351LL<<21), + real(4879307961LL<<19),real(13462873311LL<<20),-real(19014362253LL<<19), + real(0x45bace6718000LL),reale(52223,0xdb549059b7125LL), + // C4[8], coeff of eps^16, polynomial in n of order 7 + real(0x4802f7e045bLL<<18),-real(787109524929LL<<19), + -real(616781829503LL<<18),-real(267630157067LL<<20), + real(0xf57f439a67LL<<18),-real(26811748075LL<<19), + -real(29646920051LL<<18),-real(0x25c0cef2988000LL), + reale(365566,0xff4ff27401803LL), + // C4[8], coeff of eps^15, polynomial in n of order 8 + real(61397460605LL<<22),real(0x9d011c37ef80000LL), + real(907553463943LL<<20),-real(0xc0a473ee4980000LL), + -real(21778698179LL<<21),-real(22179652453LL<<19), + real(224024408237LL<<20),-real(212571195095LL<<19), + real(0x216a7bfadc8000LL),reale(365566,0xff4ff27401803LL), + // C4[8], coeff of eps^14, polynomial in n of order 9 + real(304663697949LL<<21),-real(51558232553LL<<24), + real(126037118963LL<<21),real(28559389965LL<<22),real(12939195833LL<<21), + -real(17167224841LL<<23),real(24466781775LL<<21),real(2302458607LL<<22), + real(456812693LL<<21),-real(0xde9c5a4230000LL), + reale(52223,0xdb549059b7125LL), + // C4[8], coeff of eps^13, polynomial in n of order 10 + -real(0x71eca5b57e5LL<<20),real(0x8d98ab5c54bLL<<19), + real(497026592783LL<<22),-real(0xacc7c9e1d9bLL<<19), + real(0x35a7c7b51ddLL<<20),-real(81233361377LL<<19), + -real(253988603057LL<<21),-real(954606696519LL<<19), + real(577751554079LL<<20),-real(333997527437LL<<19), + real(0x1689b847558000LL),reale(365566,0xff4ff27401803LL), + // C4[8], coeff of eps^12, polynomial in n of order 11 + -real(0x367f7beda59LL<<19),real(0x45996b8ba21LL<<20), + -real(0xdceb5493fc3LL<<19),real(0x18843cb160dLL<<22), + -real(0x21789a51fedLL<<19),-real(0x41cde5aa8b9LL<<20), + real(0x95638f58ea9LL<<19),-real(984566251123LL<<21), + -real(435207598721LL<<19),real(219309948781LL<<20), + real(274765170197LL<<19),-real(0x12cf88fa6ff0000LL), + reale(365566,0xff4ff27401803LL), + // C4[8], coeff of eps^11, polynomial in n of order 12 + -real(2296713447LL<<21),real(78660216877LL<<19), + -real(180155131441LL<<20),real(0xeee01825bfLL<<19), + -real(237440161933LL<<22),real(0x2042cbdcd31LL<<19), + -real(652079196855LL<<20),-real(325903664957LL<<19), + real(324695717299LL<<21),-real(0xf97e21ed4bLL<<19), + real(203483994947LL<<20),-real(52367903417LL<<19), + -real(0x8a9d0d3688000LL),reale(52223,0xdb549059b7125LL), + // C4[8], coeff of eps^10, polynomial in n of order 13 + real(1140139LL<<21),real(9315711LL<<23),-real(1126319139LL<<21), + real(5199009105LL<<22),-real(52132384161LL<<21),real(20770352565LL<<24), + -real(357583911087LL<<21),real(262213551639LL<<22), + -real(498523677485LL<<21),real(60302341333LL<<23), + real(57310064901LL<<21),-real(90954779619LL<<22), + real(124029244935LL<<21),-real(0xf0a5fe0ce50000LL), + reale(52223,0xdb549059b7125LL), + // C4[8], coeff of eps^9, polynomial in n of order 14 + real(54009LL<<20),real(849303LL<<19),real(2623117LL<<21), + real(364892913LL<<19),-real(5919882885LL<<20),real(0xdd0128d3580000LL), + -real(81910832913LL<<22),real(0x2229f5f9745LL<<19), + -real(0x2a9587ee883LL<<20),real(0x982f47b44bfLL<<19), + -real(0x30e1739ffd1LL<<21),real(0xb09887dee19LL<<19), + -real(0x35101f0ee01LL<<20),real(0x25e6f19ce93LL<<19), + -real(0x306e34ba4668000LL),reale(365566,0xff4ff27401803LL), + // C4[8], coeff of eps^8, polynomial in n of order 15 + real(2295<<17),real(5831<<18),real(72709LL<<17),real(151011LL<<19), + real(7936467LL<<17),real(147906885LL<<18),-real(0x4d5c1f23e0000LL), + real(14228642337LL<<20),-real(697203474513LL<<17), + real(0x51fe4e56b0c0000LL),-real(0xeb59f3d2e860000LL), + real(0x3e0c14100a1LL<<19),-real(0x305340db42ea0000LL), + real(0xd6c75923d41LL<<18),-real(0x2452a78bb4ce0000LL), + real(0xa981b88bf66c000LL),reale(365566,0xff4ff27401803LL), + // C4[9], coeff of eps^23, polynomial in n of order 0 + -real(45613<<15),real(0xa0b835899f381LL), + // C4[9], coeff of eps^22, polynomial in n of order 1 + -real(4663637LL<<21),real(25498473LL<<16),real(0x8f68f0ea15ed989LL), + // C4[9], coeff of eps^21, polynomial in n of order 2 + -real(313787291LL<<20),-real(89546863LL<<19),-real(880826107LL<<15), + reale(5306,0x2ad1d52b570cdLL), + // C4[9], coeff of eps^20, polynomial in n of order 3 + real(1691751267LL<<22),real(5868457511LL<<23),-real(9710518895LL<<22), + real(43389881073LL<<17),reale(408574,0xe11d1e092eda9LL), + // C4[9], coeff of eps^19, polynomial in n of order 4 + -real(45668361181LL<<21),real(290185772373LL<<19), + -real(19310638221LL<<20),-real(10267037529LL<<19), + -real(0x11435a10568000LL),reale(408574,0xe11d1e092eda9LL), + // C4[9], coeff of eps^18, polynomial in n of order 5 + -real(206915608111LL<<21),real(8005795847LL<<23),real(6676372983LL<<21), + real(24266221119LL<<22),-real(29173391667LL<<21),real(99595856143LL<<16), + reale(408574,0xe11d1e092eda9LL), + // C4[9], coeff of eps^17, polynomial in n of order 6 + -real(15515879355LL<<20),-real(36184750873LL<<19), + -real(22177807609LL<<21),real(62194714929LL<<19),real(693176727LL<<20), + -real(1189966821LL<<19),-real(0x5829503048000LL), + reale(58367,0xd70428dcbd8cfLL), + // C4[9], coeff of eps^16, polynomial in n of order 7 + real(38512528273LL<<23),real(67772681235LL<<24),-real(74410968653LL<<23), + -real(3984568679LL<<25),-real(6152374683LL<<23),real(13551170801LL<<24), + -real(11115057401LL<<23),real(24916219839LL<<18), + reale(408574,0xe11d1e092eda9LL), + // C4[9], coeff of eps^15, polynomial in n of order 8 + -real(162298412813LL<<22),real(0xff4317f5080000LL), + real(119179074953LL<<20),real(0xf6d36e74980000LL), + -real(63634032589LL<<21),real(61952932453LL<<19),real(10785104899LL<<20), + real(4191026519LL<<19),-real(0xd59ae9d0e8000LL), + reale(58367,0xd70428dcbd8cfLL), + // C4[9], coeff of eps^14, polynomial in n of order 9 + real(162971496591LL<<21),real(33816350309LL<<24), + -real(394783736543LL<<21),real(85862751303LL<<22), + real(32462900611LL<<21),-real(6369607931LL<<23),-real(39152071083LL<<21), + real(18189729581LL<<22),-real(9249690569LL<<21),real(6171570141LL<<16), + reale(58367,0xd70428dcbd8cfLL), + // C4[9], coeff of eps^13, polynomial in n of order 10 + real(0x52d38896f8bLL<<20),-real(0xd3acdf03195LL<<19), + real(0x1195b2a1cffLL<<22),real(0xca9586e4a280000LL), + -real(0x486f0b6e413LL<<20),real(0x7ca2ce8a83fLL<<19), + -real(610236546241LL<<21),-real(717677267559LL<<19), + real(159176229583LL<<20),real(291633515411LL<<19), + -real(0x110150274e88000LL),reale(408574,0xe11d1e092eda9LL), + // C4[9], coeff of eps^12, polynomial in n of order 11 + real(143956869023LL<<22),-real(243108013001LL<<23), + real(0x101d5eb1615LL<<22),-real(213537904349LL<<25), + real(0x183f300cffbLL<<22),-real(350529456991LL<<23), + -real(545724783247LL<<22),real(274121340227LL<<24), + -real(785966166377LL<<22),real(135225754699LL<<23), + -real(28607511667LL<<22),-real(0x3ee3b308260000LL), + reale(408574,0xe11d1e092eda9LL), + // C4[9], coeff of eps^11, polynomial in n of order 12 + real(2520290511LL<<21),-real(0xc4ddd05ba80000LL), + real(304931349961LL<<20),-real(0x21230116cd7LL<<19), + real(735928623493LL<<22),-real(0x9d254a11d99LL<<19), + real(0x6510e717cdfLL<<20),-real(0xa95d67804fbLL<<19), + real(0x1055dd17e45LL<<21),real(0x239bcd685c3LL<<19), + -real(0x22ba072788bLL<<20),real(0x2c142a0db61LL<<19), + -real(0x59b3a2379f58000LL),reale(408574,0xe11d1e092eda9LL), + // C4[9], coeff of eps^10, polynomial in n of order 13 + -real(29393LL<<21),-real(283917LL<<23),real(41246777LL<<21), + -real(233407875LL<<22),real(2943398547LL<<21),-real(1525553871LL<<24), + real(35837133917LL<<21),-real(38620600629LL<<22), + real(123783976375LL<<21),-real(36640057007LL<<23), + real(124599494337LL<<21),-real(35830670759LL<<22), + real(24805848987LL<<21),-real(0x1ce0b816070000LL), + reale(19455,0xf256b84994845LL), + // C4[9], coeff of eps^9, polynomial in n of order 14 + -real(1615<<20),-real(29393LL<<19),-real(106267LL<<21), + -real(17534055LL<<19),real(342711075LL<<20),-real(8430692445LL<<19), + real(7306600119LL<<22),-real(270344204403LL<<19), + real(450573674005LL<<20),-real(0x20c896b3e69LL<<19), + real(0xfa29e850f7LL<<21),-real(0x5aaf3103bffLL<<19), + real(0x3002653e387LL<<20),-real(0x3f2b92b02f5LL<<19), + real(0x914a9e2ed338000LL),reale(408574,0xe11d1e092eda9LL), + // C4[10], coeff of eps^23, polynomial in n of order 0 + real(137<<21),real(0x8757c14b789bLL), + // C4[10], coeff of eps^22, polynomial in n of order 1 + -real(1152691LL<<20),-real(6743919LL<<17),real(0x9e817610332f06fLL), + // C4[10], coeff of eps^21, polynomial in n of order 2 + real(79722199LL<<23),-real(113766289LL<<22),real(225212673LL<<18), + reale(5864,0xb6105765cc00bLL), + // C4[10], coeff of eps^20, polynomial in n of order 3 + real(64857768639LL<<21),-real(2220489243LL<<22),-real(2012833515LL<<21), + -real(19551629405LL<<18),reale(451582,0xc2ea499e5c34fLL), + // C4[10], coeff of eps^19, polynomial in n of order 4 + real(656353407LL<<24),real(1031809317LL<<22),real(12215335391LL<<23), + -real(12759999497LL<<22),real(18944346729LL<<18), + reale(451582,0xc2ea499e5c34fLL), + // C4[10], coeff of eps^18, polynomial in n of order 5 + -real(62867132873LL<<20),-real(83127481829LL<<22), + real(173460262689LL<<20),real(8415873627LL<<21),-real(1024568181LL<<20), + -real(82657907689LL<<17),reale(451582,0xc2ea499e5c34fLL), + // C4[10], coeff of eps^17, polynomial in n of order 6 + real(69839518785LL<<24),-real(46975322289LL<<23),-real(5175253237LL<<25), + -real(10608265143LL<<23),real(12870275691LL<<24),-real(9303053053LL<<23), + real(8528136981LL<<19),reale(451582,0xc2ea499e5c34fLL), + // C4[10], coeff of eps^16, polynomial in n of order 7 + -real(12671764325LL<<22),real(11821938135LL<<23),real(23903917953LL<<22), + -real(7023725731LL<<24),real(4254825447LL<<22),real(1372261021LL<<23), + real(755775181LL<<22),-real(6809268397LL<<19), + reale(64511,0xd2b3c15fc4079LL), + // C4[10], coeff of eps^15, polynomial in n of order 8 + real(10583074157LL<<26),-real(84530118029LL<<23),real(12150058407LL<<24), + real(12380362825LL<<23),-real(838454291LL<<25),-real(10410407457LL<<23), + real(3974759309LL<<24),-real(1799658059LL<<23),real(156358707LL<<19), + reale(64511,0xd2b3c15fc4079LL), + // C4[10], coeff of eps^14, polynomial in n of order 9 + -real(922119298407LL<<20),real(52944024001LL<<23), + real(329638564983LL<<20),-real(354979062141LL<<21), + real(493120994773LL<<20),-real(24099541823LL<<22), + -real(59503561293LL<<20),real(7459230081LL<<21),real(21243323153LL<<20), + -real(75576440907LL<<17),reale(64511,0xd2b3c15fc4079LL), + // C4[10], coeff of eps^13, polynomial in n of order 10 + -real(328595996641LL<<23),real(0x1245cb281e3LL<<22), + -real(207527442829LL<<25),real(0x13d84cf39cdLL<<22), + -real(169653271431LL<<23),-real(705690429577LL<<22), + real(256163704307LL<<24),-real(657414782367LL<<22), + real(103463476179LL<<23),-real(17233182197LL<<22), + -real(65863805931LL<<18),reale(451582,0xc2ea499e5c34fLL), + // C4[10], coeff of eps^12, polynomial in n of order 11 + -real(60530460661LL<<21),real(129708905557LL<<22), + -real(783916037751LL<<21),real(215690023633LL<<24), + -real(0x287cc397f79LL<<21),real(0x174d319d033LL<<22), + -real(0x22bf2de15fbLL<<21),real(172524970961LL<<23), + real(736992166659LL<<21),-real(554058611183LL<<22), + real(665956259969LL<<21),-real(0x4d7d212a0a40000LL), + reale(451582,0xc2ea499e5c34fLL), + // C4[10], coeff of eps^11, polynomial in n of order 12 + -real(31220211LL<<24),real(1576100141LL<<22),-real(5588687797LL<<23), + real(52675808031LL<<22),-real(22267080913LL<<25), + real(449824279121LL<<22),-real(432213499347LL<<23), + real(0x1275ac4a843LL<<22),-real(351080482641LL<<24), + real(0x10853170e75LL<<22),-real(314682628337LL<<23), + real(212227819111LL<<22),-real(520922828727LL<<18), + reale(451582,0xc2ea499e5c34fLL), + // C4[10], coeff of eps^10, polynomial in n of order 13 + real(46189LL<<20),real(522291LL<<22),-real(90008149LL<<20), + real(613691925LL<<21),-real(9499950999LL<<20),real(6182507793LL<<23), + -real(187536069721LL<<20),real(270344204403LL<<21), + -real(0x11a7161219bLL<<20),real(533756506129LL<<22), + -real(0x2a7db4d305dLL<<20),real(0x159e458acd1LL<<21), + -real(0x1bcb7dfb99fLL<<20),real(0x7e5725605ea0000LL), + reale(451582,0xc2ea499e5c34fLL), + // C4[11], coeff of eps^23, polynomial in n of order 0 + -real(7309LL<<21),real(0x2c95e8ad321065LL), + // C4[11], coeff of eps^22, polynomial in n of order 1 + -real(118877LL<<30),real(1675947LL<<23),real(0x7759dcb5574d50a7LL), + // C4[11], coeff of eps^21, polynomial in n of order 2 + -real(9105745LL<<24),-real(49846181LL<<23),-real(2866583251LL<<18), + reale(70655,0xce6359e2ca823LL), + // C4[11], coeff of eps^20, polynomial in n of order 3 + -real(239228553LL<<25),real(1509768547LL<<26),-real(1393694995LL<<25), + real(7195205325LL<<19),reale(494590,0xa4b77533898f5LL), + // C4[11], coeff of eps^19, polynomial in n of order 4 + -real(10520646403LL<<25),real(16651704531LL<<23),real(1510969677LL<<24), + real(227849937LL<<23),-real(40629886913LL<<18), + reale(494590,0xa4b77533898f5LL), + // C4[11], coeff of eps^18, polynomial in n of order 5 + -real(737236949LL<<28),-real(83959015LL<<31),-real(449296547LL<<28), + real(188420603LL<<30),-real(243597193LL<<28),real(1420486123LL<<21), + reale(494590,0xa4b77533898f5LL), + // C4[11], coeff of eps^17, polynomial in n of order 6 + real(1797306345LL<<25),real(7110272827LL<<24),-real(1494242189LL<<26), + real(407981949LL<<24),real(324085539LL<<25),real(232922271LL<<24), + -real(6431919403LL<<19),reale(70655,0xce6359e2ca823LL), + // C4[11], coeff of eps^16, polynomial in n of order 7 + -real(59422002475LL<<26),real(4462082415LL<<27),real(11958968063LL<<26), + -real(116564371LL<<28),-real(9243946887LL<<26),real(3024840805LL<<27), + -real(1229077213LL<<26),-real(836978961LL<<20), + reale(494590,0xa4b77533898f5LL), + // C4[11], coeff of eps^15, polynomial in n of order 8 + real(1450234755LL<<27),real(28955596425LL<<24),-real(20916501415LL<<25), + real(24148276875LL<<24),-real(639979965LL<<26),-real(3796939603LL<<24), + real(257117683LL<<25),real(1321384367LL<<24),-real(17153469915LL<<19), + reale(70655,0xce6359e2ca823LL), + // C4[11], coeff of eps^14, polynomial in n of order 9 + real(2991071409LL<<28),-real(215656441LL<<32),real(2375561279LL<<28), + -real(29715609LL<<30),-real(1772722171LL<<28),real(262089343LL<<31), + -real(1227751437LL<<28),real(88909853LL<<30),-real(21460999LL<<28), + -real(1112906091LL<<21),reale(70655,0xce6359e2ca823LL), + // C4[11], coeff of eps^13, polynomial in n of order 10 + real(48251719021LL<<24),-real(247802667483LL<<23), + real(59903451769LL<<26),-real(693923403733LL<<23), + real(362458490331LL<<24),-real(482970502063LL<<23), + real(22585671353LL<<25),real(201583163607LL<<23), + -real(128100703031LL<<24),real(147544368125LL<<23), + -real(0x43bae67ca340000LL),reale(494590,0xa4b77533898f5LL), + // C4[11], coeff of eps^12, polynomial in n of order 11 + real(488107587LL<<25),-real(1288790349LL<<26),real(9866997217LL<<25), + -real(3570890001LL<<28),real(64004720367LL<<25),-real(56017267579LL<<26), + real(152843494797LL<<25),-real(39981841137LL<<27), + real(123894347227LL<<25),-real(33286009449LL<<26), + real(21954601977LL<<25),-real(212227819111LL<<19), + reale(494590,0xa4b77533898f5LL), + // C4[11], coeff of eps^11, polynomial in n of order 12 + real(735471LL<<25),-real(44046541LL<<23),real(188198857LL<<24), + -real(2177729631LL<<23),real(1156078693LL<<26),-real(30163144081LL<<23), + real(38781185247LL<<24),-real(159433761571LL<<23), + real(65649195941LL<<25),-real(342066863061LL<<23), + real(168318615157LL<<24),-real(212227819111LL<<23), + real(0x6f2df7ee67c0000LL),reale(494590,0xa4b77533898f5LL), + // C4[12], coeff of eps^23, polynomial in n of order 0 + real(173LL<<24),real(0x88d5e64011771LL), + // C4[12], coeff of eps^22, polynomial in n of order 1 + -real(163369LL<<28),-real(266903LL<<29),reale(14529,0xb09bccfe817bfLL), + // C4[12], coeff of eps^21, polynomial in n of order 2 + real(26283479LL<<29),-real(21738605LL<<28),real(24285135LL<<24), + reale(76799,0xca12f265d0fcdLL), + // C4[12], coeff of eps^20, polynomial in n of order 3 + real(6122492151LL<<24),real(880448149LL<<25),real(269123645LL<<24), + -real(4943792525LL<<21),reale(537598,0x8684a0c8b6e9bLL), + // C4[12], coeff of eps^19, polynomial in n of order 4 + -real(616982441LL<<28),-real(2168310039LL<<26),real(1398586567LL<<27), + -real(817632445LL<<26),real(450511215LL<<22), + reale(537598,0x8684a0c8b6e9bLL), + // C4[12], coeff of eps^18, polynomial in n of order 5 + real(1912616275LL<<26),-real(308159801LL<<28),-real(17594779LL<<26), + real(72918855LL<<27),real(66311031LL<<26),-real(47313631LL<<26), + reale(76799,0xca12f265d0fcdLL), + // C4[12], coeff of eps^17, polynomial in n of order 6 + real(9134109LL<<27),real(1642561735LL<<26),real(58767343LL<<28), + -real(1299624495LL<<26),real(374812639LL<<27),-real(137300677LL<<26), + -real(61400001LL<<22),reale(76799,0xca12f265d0fcdLL), + // C4[12], coeff of eps^16, polynomial in n of order 7 + real(118127909265LL<<25),-real(66457563795LL<<26), + real(64469127555LL<<25),-real(134108625LL<<27),-real(12700511691LL<<25), + real(295233743LL<<26),real(4531750951LL<<25),-real(13670656363LL<<22), + reale(537598,0x8684a0c8b6e9bLL), + // C4[12], coeff of eps^15, polynomial in n of order 8 + -real(10859744975LL<<29),real(49132517315LL<<26),real(5188275715LL<<27), + -real(52074703975LL<<26),real(13295845745LL<<28), + -real(28808201009LL<<26),real(3853119361LL<<27),-real(278992987LL<<26), + -real(3626908831LL<<22),reale(537598,0x8684a0c8b6e9bLL), + // C4[12], coeff of eps^14, polynomial in n of order 9 + -real(5262740745LL<<26),real(1142543055LL<<29),-real(12070462215LL<<26), + real(5779723245LL<<27),-real(6878321925LL<<26),real(125534415LL<<28), + real(3745400061LL<<26),-real(2112375473LL<<27),real(2351512319LL<<26), + -real(573315259LL<<26),reale(76799,0xca12f265d0fcdLL), + // C4[12], coeff of eps^13, polynomial in n of order 10 + -real(345262775LL<<27),real(2254590065LL<<26),-real(721021595LL<<29), + real(11719656095LL<<26),-real(9489736865LL<<27),real(24346633325LL<<26), + -real(6069982555LL<<28),real(18134544155LL<<26),-real(4742880779LL<<27), + real(3068922857LL<<26),-real(7318200659LL<<22), + reale(179199,0x822c35983cf89LL), + // C4[12], coeff of eps^12, polynomial in n of order 11 + -real(58429085LL<<24),real(185910725LL<<25),-real(1747560815LL<<24), + real(794345825LL<<27),-real(18392161025LL<<24),real(21545102915LL<<25), + -real(82378334675LL<<24),real(32084193505LL<<26), + -real(160420967525LL<<24),real(76723071425LL<<25), + -real(95136608567LL<<24),real(212227819111LL<<21), + reale(537598,0x8684a0c8b6e9bLL), + // C4[13], coeff of eps^23, polynomial in n of order 0 + -real(34717LL<<24),real(0x4013d857859e5adLL), + // C4[13], coeff of eps^22, polynomial in n of order 1 + -real(52837LL<<30),real(101283LL<<25),real(0x39b1009e5dec691dLL), + // C4[13], coeff of eps^21, polynomial in n of order 2 + real(58223275LL<<29),real(25058159LL<<28),-real(597584743LL<<24), + reale(580606,0x6851cc5de4441LL), + // C4[13], coeff of eps^20, polynomial in n of order 3 + -real(38160201LL<<32),real(20133099LL<<33),-real(10736915LL<<32), + real(8118075LL<<27),reale(580606,0x6851cc5de4441LL), + // C4[13], coeff of eps^19, polynomial in n of order 4 + -real(246943573LL<<28),-real(102114339LL<<26),real(63266747LL<<27), + real(72037887LL<<26),-real(711672919LL<<22), + reale(82943,0xc5c28ae8d7777LL), + // C4[13], coeff of eps^18, polynomial in n of order 5 + real(362438863LL<<28),real(29917105LL<<30),-real(313139991LL<<28), + real(81176473LL<<29),-real(26857069LL<<28),-real(40519029LL<<23), + reale(82943,0xc5c28ae8d7777LL), + // C4[13], coeff of eps^17, polynomial in n of order 6 + -real(4194208665LL<<27),real(3411193933LL<<26),real(92059229LL<<28), + -real(832792389LL<<26),-real(13821619LL<<27),real(313960329LL<<26), + -real(1784908801LL<<22),reale(82943,0xc5c28ae8d7777LL), + // C4[13], coeff of eps^16, polynomial in n of order 7 + real(4206195495LL<<29),real(1286394165LL<<30),-real(6553065099LL<<29), + real(1494451903LL<<31),-real(3024727629LL<<29),real(374117415LL<<30), + -real(7540351LL<<29),-real(836978961LL<<24), + reale(580606,0x6851cc5de4441LL), + // C4[13], coeff of eps^15, polynomial in n of order 8 + real(8293864515LL<<29),-real(80835230175LL<<26),real(35736027705LL<<27), + -real(37780361325LL<<26),-real(587595645LL<<28),real(26485772901LL<<26), + -real(13655575661LL<<27),real(14786628311LL<<26), + -real(57193562335LL<<22),reale(580606,0x6851cc5de4441LL), + // C4[13], coeff of eps^14, polynomial in n of order 9 + real(2173316805LL<<28),-real(627936225LL<<31),real(9404910795LL<<28), + -real(7129362555LL<<29),real(17350941825LL<<28),-real(4150093185LL<<30), + real(12011779143LL<<28),-real(3068922857LL<<29),real(1952950909LL<<28), + -real(9206768571LL<<23),reale(580606,0x6851cc5de4441LL), + // C4[13], coeff of eps^13, polynomial in n of order 10 + real(79676025LL<<27),-real(638856855LL<<26),real(256634805LL<<29), + -real(5389330905LL<<26),real(5842215855LL<<27),-real(21011478075LL<<26), + real(7804263285LL<<28),-real(37664053245LL<<26),real(17576558181LL<<27), + -real(21482459999LL<<26),real(95136608567LL<<22), + reale(580606,0x6851cc5de4441LL), + // C4[14], coeff of eps^23, polynomial in n of order 0 + real(433LL<<27),real(0x16f0fb486be35c9LL), + // C4[14], coeff of eps^22, polynomial in n of order 1 + real(938669LL<<29),-real(8460179LL<<26),reale(36683,0x318959e11f277LL), + // C4[14], coeff of eps^21, polynomial in n of order 2 + real(1085551LL<<33),-real(531601LL<<32),real(109557LL<<28), + reale(36683,0x318959e11f277LL), + // C4[14], coeff of eps^20, polynomial in n of order 3 + -real(34899909LL<<31),real(11630633LL<<32),real(16602985LL<<31), + -real(73138345LL<<28),reale(623614,0x4a1ef7f3119e7LL), + // C4[14], coeff of eps^19, polynomial in n of order 4 + real(2603869LL<<34),-real(18588201LL<<32),real(4394077LL<<33), + -real(1312099LL<<32),-real(1449057LL<<28),reale(89087,0xc172236bddf21LL), + // C4[14], coeff of eps^18, polynomial in n of order 5 + real(1218191717LL<<27),real(79106081LL<<29),-real(371875421LL<<27), + -real(20795103LL<<28),real(151229409LL<<27),-real(409250479LL<<24), + reale(89087,0xc172236bddf21LL), + // C4[14], coeff of eps^17, polynomial in n of order 6 + real(249532965LL<<30),-real(917899213LL<<29),real(191097911LL<<31), + -real(363925371LL<<29),real(41606327LL<<30),real(1574359LL<<29), + -real(54936843LL<<25),reale(89087,0xc172236bddf21LL), + // C4[14], coeff of eps^16, polynomial in n of order 7 + -real(19067218845LL<<28),real(7820446095LL<<29),-real(7262714151LL<<28), + -real(421931643LL<<30),real(6566089551LL<<28),-real(3155926907LL<<29), + real(3340375493LL<<28),-real(6416838701LL<<25), + reale(623614,0x4a1ef7f3119e7LL), + // C4[14], coeff of eps^15, polynomial in n of order 8 + -real(353006415LL<<32),real(4931374455LL<<29),-real(3531935085LL<<30), + real(8211223125LL<<29),-real(1894184271LL<<31),real(5332188211LL<<29), + -real(1334642127LL<<30),real(836978961LL<<29),-real(1952950909LL<<25), + reale(623614,0x4a1ef7f3119e7LL), + // C4[14], coeff of eps^14, polynomial in n of order 9 + -real(436268025LL<<27),real(158349135LL<<30),-real(3064521495LL<<27), + real(3110604525LL<<28),-real(10615555125LL<<27),real(3784676175LL<<29), + -real(17712284499LL<<27),real(8090796623LL<<28),-real(9764754545LL<<27), + real(21482459999LL<<24),reale(623614,0x4a1ef7f3119e7LL), + // C4[15], coeff of eps^23, polynomial in n of order 0 + -real(11003LL<<27),real(0x6a44bb11ad2310dLL), + // C4[15], coeff of eps^22, polynomial in n of order 1 + -real(28003LL<<36),real(3549LL<<30),reale(39213,0x11a47a8f8b3bdLL), + // C4[15], coeff of eps^21, polynomial in n of order 2 + real(1243LL<<38),real(2249LL<<37),-real(577583LL<<28), + reale(5601,0xddf2ecefef51bLL), + // C4[15], coeff of eps^20, polynomial in n of order 3 + -real(28101LL<<40),real(24493LL<<39),-real(1645LL<<40), + -real(318801LL<<29),reale(39213,0x11a47a8f8b3bdLL), + // C4[15], coeff of eps^19, polynomial in n of order 4 + real(1359187LL<<38),-real(4447191LL<<36),-real(433293LL<<37), + real(1982883LL<<36),-real(164770109LL<<28), + reale(666622,0x2bec23883ef8dLL), + // C4[15], coeff of eps^18, polynomial in n of order 5 + -real(6907451LL<<36),real(1332757LL<<38),-real(2401277LL<<36), + real(253189LL<<37),real(26273LL<<36),-real(1574359LL<<30), + reale(95231,0xbd21bbeee46cbLL), + // C4[15], coeff of eps^17, polynomial in n of order 6 + real(60642045LL<<33),-real(48519929LL<<32),-real(5596337LL<<34), + real(57431697LL<<32),-real(26089089LL<<33),real(27095547LL<<32), + -real(828361417LL<<25),reale(95231,0xbd21bbeee46cbLL), + // C4[15], coeff of eps^16, polynomial in n of order 7 + real(53036505LL<<34),-real(36153285LL<<35),real(80745483LL<<34), + -real(18042031LL<<36),real(49556941LL<<34),-real(12180567LL<<35), + real(7540351LL<<34),-real(278992987LL<<26), + reale(222207,0x63f9612d6a52fLL), + // C4[15], coeff of eps^15, polynomial in n of order 8 + real(5892945LL<<35),-real(106383165LL<<32),real(102040995LL<<33), + -real(332742375LL<<32),real(114463377LL<<34),-real(521444273LL<<32), + real(233750881LL<<33),-real(278992987LL<<32),real(9764754545LL<<25), + reale(666622,0x2bec23883ef8dLL), + // C4[16], coeff of eps^23, polynomial in n of order 0 + -real(1LL<<31),real(0x5f43434b6401e1LL), + // C4[16], coeff of eps^22, polynomial in n of order 1 + real(4571LL<<36),-real(33945LL<<32),reale(5963,0x471b5f51fec25LL), + // C4[16], coeff of eps^21, polynomial in n of order 2 + real(24269LL<<36),-real(5831LL<<35),-real(11703LL<<31), + reale(5963,0x471b5f51fec25LL), + // C4[16], coeff of eps^20, polynomial in n of order 3 + -real(224895LL<<36),-real(32277LL<<37),real(111531LL<<36), + -real(139825LL<<34),reale(41742,0xf1bf9b3df7503LL), + // C4[16], coeff of eps^19, polynomial in n of order 4 + real(978405LL<<37),-real(1674813LL<<35),real(162197LL<<36), + real(29281LL<<35),-real(297087LL<<31),reale(41742,0xf1bf9b3df7503LL), + // C4[16], coeff of eps^18, polynomial in n of order 5 + -real(15263501LL<<36),-real(3038189LL<<38),real(24413445LL<<36), + -real(10587549LL<<37),real(10822455LL<<36),-real(41181917LL<<32), + reale(709630,0xdb94f1d6c533LL), + // C4[16], coeff of eps^17, polynomial in n of order 6 + -real(7565085LL<<36),real(16306961LL<<35),-real(3541967LL<<37), + real(9518487LL<<35),-real(2301919LL<<36),real(1408637LL<<35), + -real(3231579LL<<31),reale(101375,0xb8d15471eae75LL), + // C4[16], coeff of eps^16, polynomial in n of order 7 + -real(57998985LL<<33),real(52955595LL<<34),-real(165927531LL<<33), + real(55309177LL<<35),-real(246030477LL<<33),real(108465049LL<<34), + -real(128185967LL<<33),real(278992987LL<<30), + reale(709630,0xdb94f1d6c533LL), + // C4[17], coeff of eps^23, polynomial in n of order 0 + -real(1121LL<<31),real(0x6ef59e61feaaea7LL), + // C4[17], coeff of eps^22, polynomial in n of order 1 + -real(59LL<<37),-real(309LL<<32),real(0x14ce0db25fc00bf5LL), + // C4[17], coeff of eps^21, polynomial in n of order 2 + -real(10703LL<<36),real(30413LL<<35),-real(148003LL<<31), + reale(6324,0xb043d1b40e32fLL), + // C4[17], coeff of eps^20, polynomial in n of order 3 + -real(177777LL<<38),real(15715LL<<39),real(4277LL<<38), + -real(68103LL<<33),reale(44272,0xd1dabbec63649LL), + // C4[17], coeff of eps^19, polynomial in n of order 4 + -real(407783LL<<37),real(2775087LL<<35),-real(1157751LL<<36), + real(1167621LL<<35),-real(4428011LL<<31),reale(44272,0xd1dabbec63649LL), + // C4[17], coeff of eps^18, polynomial in n of order 5 + real(1580535LL<<37),-real(334719LL<<39),real(882049LL<<37), + -real(210231LL<<38),real(127323LL<<37),-real(580027LL<<32), + reale(44272,0xd1dabbec63649LL), + // C4[17], coeff of eps^17, polynomial in n of order 6 + real(801009LL<<36),-real(2422805LL<<35),real(785323LL<<37), + -real(3419955LL<<35),real(1485435LL<<36),-real(1740081LL<<35), + real(7540351LL<<31),reale(44272,0xd1dabbec63649LL), + // C4[18], coeff of eps^23, polynomial in n of order 0 + -real(89LL<<35),real(0x3351994085c8a607LL), + // C4[18], coeff of eps^22, polynomial in n of order 1 + real(763LL<<36),-real(1809LL<<33),real(0x15fe66403955fe03LL), + // C4[18], coeff of eps^21, polynomial in n of order 2 + real(91LL<<39),real(35LL<<38),-real(235LL<<34), + real(0x15fe66403955fe03LL), + // C4[18], coeff of eps^20, polynomial in n of order 3 + real(667755LL<<37),-real(269591LL<<38),real(268793LL<<37), + -real(508305LL<<34),reale(46802,0xb1f5dc9acf78fLL), + // C4[18], coeff of eps^19, polynomial in n of order 4 + -real(51319LL<<40),real(132867LL<<38),-real(31255LL<<39), + real(18753LL<<38),-real(42441LL<<34),reale(15600,0xe5fc9ede45285LL), + // C4[18], coeff of eps^18, polynomial in n of order 5 + -real(1198615LL<<36),real(378917LL<<38),-real(1619009LL<<36), + real(693861LL<<37),-real(806379LL<<36),real(1740081LL<<33), + reale(46802,0xb1f5dc9acf78fLL), + // C4[19], coeff of eps^23, polynomial in n of order 0 + -real(983LL<<35),real(0x3617bd362c26857dLL), + // C4[19], coeff of eps^22, polynomial in n of order 1 + real(1LL<<46),-real(189LL<<37),reale(2596,0x737a284739077LL), + // C4[19], coeff of eps^21, polynomial in n of order 2 + -real(473LL<<40),real(467LL<<39),-real(3525LL<<34), + real(0x172ebece12ebf011LL), + // C4[19], coeff of eps^20, polynomial in n of order 3 + real(2379LL<<41),-real(553LL<<42),real(329LL<<41),-real(2961LL<<35), + reale(2596,0x737a284739077LL), + // C4[19], coeff of eps^19, polynomial in n of order 4 + real(2405LL<<41),-real(10101LL<<39),real(4277LL<<40),-real(4935LL<<39), + real(42441LL<<34),reale(2596,0x737a284739077LL), + // C4[20], coeff of eps^23, polynomial in n of order 0 + -real(1LL<<38),real(0x1f5feefdb1f0c4fLL), + // C4[20], coeff of eps^22, polynomial in n of order 1 + real(379LL<<42),-real(357LL<<40),reale(2729,0x9a383778d2ed9LL), + // C4[20], coeff of eps^21, polynomial in n of order 2 + -real(249LL<<43),real(147LL<<42),-real(329LL<<38), + reale(2729,0x9a383778d2ed9LL), + // C4[20], coeff of eps^20, polynomial in n of order 3 + -real(4797LL<<40),real(2009LL<<41),-real(2303LL<<40),real(4935LL<<37), + reale(2729,0x9a383778d2ed9LL), + // C4[21], coeff of eps^23, polynomial in n of order 0 + -real(1327LL<<38),reale(2862,0xc0f646aa6cd3bLL), + // C4[21], coeff of eps^22, polynomial in n of order 1 + real(11LL<<44),-real(49LL<<39),real(0x3ba4052178e24469LL), + // C4[21], coeff of eps^21, polynomial in n of order 2 + real(473LL<<43),-real(539LL<<42),real(2303LL<<38), + reale(2862,0xc0f646aa6cd3bLL), + // C4[22], coeff of eps^23, polynomial in n of order 0 + -real(1LL<<41),real(0x5ac8f5f3162ebfdLL), + // C4[22], coeff of eps^22, polynomial in n of order 1 + -real(23LL<<43),real(49LL<<40),real(0x1105ae1d9428c3f7LL), + // C4[23], coeff of eps^23, polynomial in n of order 0 + real(1LL<<41),real(0xc5e28ed2c935abLL), + }; // count = 2900 +#elif GEOGRAPHICLIB_GEODESICEXACT_ORDER == 27 + static const real coeff[] = { + // C4[0], coeff of eps^26, polynomial in n of order 0 + 4654,real(327806325), + // C4[0], coeff of eps^25, polynomial in n of order 1 + -331600,247203,real(5135632425LL), + // C4[0], coeff of eps^24, polynomial in n of order 2 + -real(30660788480LL),real(15209307520LL),real(3757742824LL), + real(0xbd65c2e6062dLL), + // C4[0], coeff of eps^23, polynomial in n of order 3 + -real(0x4a56872d110LL),real(0x30d818a0d20LL),-real(0x183639ebbb0LL), + real(0x1207973318dLL),real(0x472c0a3d3d1ee9LL), + // C4[0], coeff of eps^22, polynomial in n of order 4 + -real(0x743607eea80LL),real(0x5536ade42a0LL),-real(0x37e9933c940LL), + real(0x1bb15f964e0LL),real(469120197546LL),real(0x472c0a3d3d1ee9LL), + // C4[0], coeff of eps^21, polynomial in n of order 5 + -real(0x1a80e82073690LL),real(0x1485d9e7af5c0LL),-real(0xf039fc9e8ff0LL), + real(0x9d5f26153ce0LL),-real(0x4ddf0f750f50LL),real(0x39e793daa6ebLL), + real(0xadde5e94360277dLL), + // C4[0], coeff of eps^20, polynomial in n of order 6 + -real(0xe72f9d31220580LL),real(0xb817a196612bc0LL), + -real(0x8e0a680913c900LL),real(0x67a3067b290a40LL), + -real(0x43c43707776c80LL),real(0x217ef7b84400c0LL), + real(0x83b895ad56e94LL),reale(16517,0x8519000aea763LL), + // C4[0], coeff of eps^19, polynomial in n of order 7 + -real(0x5be35cb0a188d670LL),real(0x49fb9f6e0e1fa420LL), + -real(0x3a970b1601b36050LL),real(0x2d0406e3051baec0LL), + -real(0x20bde41e80026c30LL),real(0x155cea808b65d160LL), + -real(0xa8bc4b2c853c610LL),real(0x7d3acd77deac86fLL), + reale(1139708,0xdfbd02f131dafLL), + // C4[0], coeff of eps^18, polynomial in n of order 8 + -reale(2219,0x955c84d349100LL),real(0x6f523368eabed3a0LL), + -real(0x58df9f4050ea48c0LL),real(0x45eb9b162449f0e0LL), + -real(0x35736f4da3b86880LL),real(0x26bb8b2d01772220LL), + -real(0x19350a3e2b857840LL),real(0xc6cd21a34a65f60LL), + real(0x30a9f24aaae2862LL),reale(1139708,0xdfbd02f131dafLL), + // C4[0], coeff of eps^17, polynomial in n of order 9 + -reale(3520,0x86c418e66b430LL),reale(2768,0x78979286ec480LL), + -reale(2191,0xabc9bb4d59ed0LL),real(0x6c38e96882e6a560LL), + -real(0x54765a5d7300bb70LL),real(0x402d11108cfc5240LL), + -real(0x2e4c264c23518e10LL),real(0x1e09e0cfb5ca8720LL), + -real(0xec7bce3f9449ab0LL),real(0xaf0b9139605a58dLL), + reale(1139708,0xdfbd02f131dafLL), + // C4[0], coeff of eps^16, polynomial in n of order 10 + -reale(6136,0x52223aecbfa00LL),reale(4597,0xf56d1171d1b00LL), + -reale(3531,0xe10107f964800LL),reale(2747,0xc7a53bf3c9500LL), + -reale(2142,0x9c25bfa8f9600LL),real(0x677abbdfa4dcef00LL), + -real(0x4e0ad45efdfc2400LL),real(0x37ff2b5bd74de900LL), + -real(0x2432b6ddc0003200LL),real(0x11c5dbb8178f4300LL), + real(0x4536f43fdb6a550LL),reale(1139708,0xdfbd02f131dafLL), + // C4[0], coeff of eps^15, polynomial in n of order 11 + -reale(13102,0xf96f6011eba70LL),reale(8724,0xbd02d5fc04060LL), + -reale(6234,0x68dfd557291d0LL),reale(4636,0xd96d16348cb80LL), + -reale(3525,0x47255186b7b30LL),reale(2702,0xc781c601a46a0LL), + -reale(2062,0x7b91b55fb7290LL),real(0x60521f1f549575c0LL), + -real(0x44a70474ce1373f0LL),real(0x2c2e0084319d1ce0LL), + -real(0x15a2a473a1b17b50LL),real(0xff41fd49dab95d3LL), + reale(1139708,0xdfbd02f131dafLL), + // C4[0], coeff of eps^14, polynomial in n of order 12 + -reale(63391,0x70a4897dc9e80LL),reale(23343,0xc5a3f9fbbcce0LL), + -reale(13453,0x278d24cdf3ac0LL),reale(8911,0x777a0315423a0LL), + -reale(6323,0x2714f8a7fff00LL),reale(4656,0xe8c5e07109660LL), + -reale(3491,0x6be5fd90e340LL),reale(2621,0xb84b17c4ad20LL), + -real(0x78f908534453df80LL),real(0x55814182d129efe0LL), + -real(0x36b7bc0c02deebc0LL),real(0x1ab5b755becbe6a0LL), + real(0x672760e43e7e5beLL),reale(1139708,0xdfbd02f131dafLL), + // C4[0], coeff of eps^13, polynomial in n of order 13 + reale(112706,0xdfd869d806ed0LL),reale(29093,0xf8d3fc140cbc0LL), + -reale(65760,0x7b52c14019950LL),reale(24105,0xa651ba0482d20LL), + -reale(13822,0xd4286a2c4c370LL),reale(9095,0xad3608e2bd280LL), + -reale(6394,0x2414e7ceec390LL),reale(4646,0x4bdec656d47e0LL), + -reale(3413,0x76099d6b04db0LL),reale(2482,0x54f2fd0561940LL), + -real(0x6c7d891fb0df15d0LL),real(0x44efe2727b65d2a0LL), + -real(0x2183dc0de2efcff0LL),real(0x189262ba581c6bf1LL), + reale(1139708,0xdfbd02f131dafLL), + // C4[0], coeff of eps^12, polynomial in n of order 14 + reale(22421,0x80a7495217980LL),-reale(122681,0x25b6cd6074ac0LL), + reale(117806,0x7498b0aecaf00LL),reale(29700,0x9de1e174ab0c0LL), + -reale(68413,0x428634ee0fb80LL),reale(24937,0xf2aac2170b440LL), + -reale(14209,0x4f5514d0cb600LL),reale(9268,0x742c2dd2c8fc0LL), + -reale(6433,0x2286f06b3b080LL),reale(4585,0x3348b70941340LL), + -reale(3266,0x3bda622d31b00LL),reale(2252,0x1340649a90ec0LL), + -real(0x589f5d02f1d02580LL),real(0x2adce3e44e715240LL), + real(0xa36591ccc5a22bcLL),reale(1139708,0xdfbd02f131dafLL), + // C4[0], coeff of eps^11, polynomial in n of order 15 + real(0x3845a63e874b7f90LL),reale(2990,0x790a9d44cfaa0LL), + reale(23275,0xc0709755ecab0LL),-reale(127863,0x516b98584c9c0LL), + reale(123656,0x74905ab09b3d0LL),reale(30291,0xc8698ff57f9e0LL), + -reale(71410,0x2ebef8806f110LL),reale(25848,0x521bca14dd980LL), + -reale(14605,0xac6deef7d4ff0LL),reale(9413,0x816443bfd6920LL), + -reale(6415,0x315eed8f094d0LL),reale(4438,0xfed32587f3cc0LL), + -reale(3002,0xabba02cdaebb0LL),real(0x74ba3cd78aa5e860LL), + -real(0x3812b2b32b2f8090LL),real(0x28bab2d4ac11f317LL), + reale(1139708,0xdfbd02f131dafLL), + // C4[0], coeff of eps^10, polynomial in n of order 16 + real(0xbcd4fd6df5b2600LL),real(0x17fed2a1d906c020LL), + real(0x3a338f7e05a82540LL),reale(3102,0x8ee9d52fa7060LL), + reale(24235,0xac0c2ca98fc80LL),-reale(133761,0xdb81f4d32fb60LL), + reale(130458,0x34533ae1a43c0LL),reale(30833,0xcd61b102f94e0LL), + -reale(74830,0xb3a54c3df6d00LL),reale(26842,0xad19affdd3920LL), + -reale(14996,0x635b9e8c37dc0LL),reale(9500,0x408e4569f0960LL), + -reale(6294,0x8e3c24f515680LL),reale(4143,0x97d5a30101da0LL), + -reale(2534,0x56aa081845f40LL),real(0x4b644b6e4da18de0LL), + real(0x11925bb6ba64765aLL),reale(1139708,0xdfbd02f131dafLL), + // C4[0], coeff of eps^9, polynomial in n of order 17 + real(0x3fcae6c51cf8fd0LL),real(0x6afa1c71c2ac100LL), + real(0xc2892977602fa30LL),real(0x18cb840e0ff332e0LL), + real(0x3c56602ddecd9290LL),reale(3228,0x26f051b5c20c0LL), + reale(25324,0xf8a24438674f0LL),-reale(140558,0x5b2d711d11960LL), + reale(138496,0xa2474d581bd50LL),reale(31265,0x7dd7c9350e080LL), + -reale(78781,0x407f0fc917850LL),reale(27920,0xd85d0c9896a60LL), + -reale(15347,0xbd51776ab0ff0LL),reale(9468,0xaa167d507e040LL), + -reale(5981,0xcd152be8bed90LL),reale(3570,0xf062f37e99e20LL), + -real(0x68dc53d94dbff530LL),real(0x4ae92c9a7a683bf5LL), + reale(1139708,0xdfbd02f131dafLL), + // C4[0], coeff of eps^8, polynomial in n of order 18 + real(0x1b54ebcbbde1f00LL),real(0x2947b9527677980LL), + real(0x415d003e7b1b800LL),real(0x6df9566e0623680LL), + real(0xc8ad7ddfed65100LL),real(0x19abdc3c4555e380LL), + real(0x3eb74cbd79d9ca00LL),reale(3370,0x20d152b7a6080LL), + reale(26575,0x8086d641a0300LL),-reale(148506,0xeae36b607280LL), + reale(148190,0x3f5dc7314dc00LL),reale(31472,0x41aaeb33d4a80LL), + -reale(83406,0xf30366e47cb00LL),reale(29065,0x630b32b837780LL), + -reale(15585,0x2764a1e4e1200LL),reale(9192,0xabf11a369f480LL), + -reale(5286,0x3613c4b401900LL),reale(2436,0x784ea73c0a180LL), + real(0x2209232c3cc4cca8LL),reale(1139708,0xdfbd02f131dafLL), + // C4[0], coeff of eps^7, polynomial in n of order 19 + real(0xd73a52d8bd1790LL),real(0x13078939da8f2e0LL), + real(0x1bc62bcb4923530LL),real(0x2a1bb9d3adccf00LL), + real(0x42f03cdd160e0d0LL),real(0x711670ab4ed8b20LL), + real(0xcf3f2963eb3be70LL),real(0x1aa1c278c7668b40LL), + real(0x416120b2cbe67210LL),reale(3532,0x3a6649f1d3360LL), + reale(28031,0x35f5ca2c79fb0LL),-reale(157970,0xd11b280f51880LL), + reale(160182,0x9c904f3daeb50LL),reale(31228,0xe702b02a70ba0LL), + -reale(88907,0xf3445bc050710LL),reale(30210,0xe03f62b8103c0LL), + -reale(15533,0x7a0f6ace49370LL),reale(8379,0xc089c57da33e0LL), + -reale(3746,0x32a85741515d0LL),reale(2585,0x396e1f38f6dbbLL), + reale(1139708,0xdfbd02f131dafLL), + // C4[0], coeff of eps^6, polynomial in n of order 20 + real(0x73457ae9fefc80LL),real(0x9bfefa36a68d60LL), + real(0xd7e57b2fb0d740LL),real(0x132c60dd72bf720LL), + real(0x1c1d29144004a00LL),real(0x2ad464b0fcdcce0LL), + real(0x446dc104a967cc0LL),real(0x7436e717eb8b6a0LL), + real(0xd626d1c40bc9780LL),real(0x1badddc640275c60LL), + real(0x445f879c8f67c240LL),reale(3719,0x5820c25fe6620LL), + reale(29754,0xa45b204c52500LL),-reale(169504,0xe227b2d578420LL), + reale(175522,0xa8a2f18c5e7c0LL),reale(30060,0x7f96216b245a0LL), + -reale(95556,0xca707dfd4cd80LL),reale(31150,0x37da9e0a66b60LL), + -reale(14734,0x203a74e6dd2c0LL),reale(6239,0x114e25ea99520LL), + real(0x4f113ff5b79764b6LL),reale(1139708,0xdfbd02f131dafLL), + // C4[0], coeff of eps^5, polynomial in n of order 21 + real(0x40c53da188eed0LL),real(0x54ed187b34c440LL), + real(0x7146df082c9bb0LL),real(0x9a154e844696a0LL), + real(0xd666e59b550690LL),real(0x13262a46ef0dd00LL), + real(0x1c3f2cd359b1b70LL),real(0x2b4dcc62e91c360LL), + real(0x45a57497f9cc650LL),real(0x771c08f5a9775c0LL), + real(0xdd1a4961392f330LL),real(0x1ccccddd60de2020LL), + real(0x47bbc762b5878e10LL),reale(3937,0xc2066e54dee80LL), + reale(31838,0x13ce9b56b82f0LL),-reale(183990,0x8ea49a06f320LL), + reale(196055,0x20a74184cbdd0LL),reale(26856,0x50de39af9a740LL), + -reale(103681,0x9284ca213d550LL),reale(31195,0x5686bd94fe9a0LL), + -reale(11739,0xecc6d600c4a70LL),reale(7362,0xc12f75a94f319LL), + reale(1139708,0xdfbd02f131dafLL), + // C4[0], coeff of eps^4, polynomial in n of order 22 + real(0x25018b34093680LL),real(0x2f66db340747c0LL), + real(0x3d8eaf55c4d300LL),real(0x512efdf6054640LL), + real(0x6cf4c335af0f80LL),real(0x952f237cecdcc0LL), + real(0xd10b7e4cd0dc00LL),real(0x12cf85d69a3fb40LL), + real(0x1bf83185acb2880LL),real(0x2b3ea99410c91c0LL), + real(0x462f30f09fee500LL),real(0x7931c8e1f8c9040LL), + real(0xe34caff0bb50180LL),real(0x1def0c2db115e6c0LL), + real(0x4b7080401d466e00LL),reale(4194,0xbf682a6ae8540LL), + reale(34423,0x2600aa7441a80LL),-reale(202943,0xe8d9bbd87a440LL), + reale(225378,0x7bd3e279ef700LL),reale(18574,0x52c9633395a40LL), + -reale(113350,0xffc66a8300c80LL),reale(27528,0x198b9d86370c0LL), + reale(3947,0xb3131e15c994LL),reale(1139708,0xdfbd02f131dafLL), + // C4[0], coeff of eps^3, polynomial in n of order 23 + real(0x14ba9dec234d90LL),real(0x1a15f878f54920LL), + real(0x2134b5fb572db0LL),real(0x2acf89c87d75c0LL), + real(0x37fb978513cbd0LL),real(0x4a626dbdd79a60LL), + real(0x64a2becb8c9bf0LL),real(0x8afd5ca732eb00LL), + real(0xc4970cf56e1210LL),real(0x11deb4357fc9ba0LL), + real(0x1add3c5ff77a230LL),real(0x2a08c939311e040LL), + real(0x451c5af5bb5c050LL),real(0x7909ad73ef1ece0LL), + real(0xe685850971be070LL),real(0x1edeb97922aff580LL), + real(0x4f3a8e20463e7690LL),reale(4494,0x6f4eb7a652e20LL), + reale(37733,0xf376431ecf6b0LL),-reale(229273,0xd3dfdae1d3540LL), + reale(271637,0x92a93446bd4d0LL),-reale(5667,0x8cc9ebb9c00a0LL), + -reale(121042,0xac8f4eff17b10LL),reale(39799,0x5b8561a065b3fLL), + reale(1139708,0xdfbd02f131dafLL), + // C4[0], coeff of eps^2, polynomial in n of order 24 + real(0xab22c89592500LL),real(0xd46ccddd414a0LL),real(0x10a4eb8f1ddb40LL), + real(0x15184ab619d7e0LL),real(0x1b0f2efb81a980LL), + real(0x232d3128e64f20LL),real(0x2e6a3ee43c47c0LL), + real(0x3e471bedb3b260LL),real(0x552919f15d6e00LL), + real(0x7700089e6e39a0LL),real(0xaa7eb4de50d440LL), + real(0xfb834e2f281ce0LL),real(0x1801af760623280LL), + real(0x263a4a7c48d9420LL),real(0x401905d594140c0LL), + real(0x72c2e250398d760LL),real(0xe012c263c05b700LL), + real(0x1edcfb1205061ea0LL),real(0x51c797f92b334d40LL), + reale(4810,0x460394707a1e0LL),reale(42101,0xccb76963dbb80LL), + -reale(269613,0x72aa3b84666e0LL),reale(357865,0x4c16ffd0cb9c0LL), + -reale(115779,0xf2f861d29c3a0LL),-reale(21708,0xbd8e92577d4aeLL), + reale(1139708,0xdfbd02f131dafLL), + // C4[0], coeff of eps^1, polynomial in n of order 25 + real(0x16b98c18c43f0LL),real(0x1be76827efc80LL),real(0x2291674649910LL), + real(0x2b3d2747a6820LL),real(0x36a8d2fdcc830LL),real(0x45e795ad137c0LL), + real(0x5a8eeaa036550LL),real(0x77007a4bcbf60LL),real(0x9ee5aa2960470LL), + real(0xd8045ac825300LL),real(0x12bb93df5b3990LL), + real(0x1a9b1c398546a0LL),real(0x26d2a92f5c98b0LL), + real(0x3a7858f998ee40LL),real(0x5b6e62f9c0b5d0LL), + real(0x959d5c24529de0LL),real(0x102f2d0b50524f0LL), + real(0x1e1472bfb1ba980LL),real(0x3d69bf9cb587a10LL), + real(0x8ee1210e8c36520LL),real(0x194d332fe8d44930LL), + real(0x6534ccbfa35124c0LL),reale(15788,0x2cc4c78572650LL), + -reale(115779,0xf2f861d29c3a0LL),reale(173669,0xec7492bbea570LL), + -reale(75980,0x9773003236861LL),reale(379902,0xf53f00fb109e5LL), + // C4[0], coeff of eps^0, polynomial in n of order 26 + real(0x104574695550b58LL),real(0x124efd1ef41bc1cLL), + real(0x14b36c04f5f7ca0LL),real(0x1787788b9792f24LL), + real(0x1ae5caaf52545e8LL),real(0x1ef111702bafd2cLL), + real(0x23d6fb7cfc3d530LL),real(0x29d483e08118c34LL), + real(0x313c47ee86cd878LL),real(0x3a800de5bbb223cLL), + real(0x463f6a859617dc0LL),real(0x555ed8909112544LL), + real(0x692d2b9362db308LL),real(0x83a245a495f5b4cLL), + real(0xa7cc0a01a036650LL),real(0xda93e49d10b2a54LL), + real(0x1243757f6f15c598LL),real(0x193422259e6ad85cLL), + real(0x24309a0ea1d47ee0LL),real(0x36b22ea791accb64LL), + real(0x588e3327aee70028LL),reale(2530,0x27feb6f2ec96cLL), + reale(5262,0xb996ed2c7b770LL),reale(14472,0x7e5f0c3a53874LL), + reale(86834,0xf63a495df52b8LL),-reale(303922,0x5dcc00c8da184LL), + reale(759805,0xea7e01f6213caLL),reale(1139708,0xdfbd02f131dafLL), + // C4[1], coeff of eps^26, polynomial in n of order 0 + 4654,real(327806325), + // C4[1], coeff of eps^25, polynomial in n of order 1 + real(22113584),5520955,real(0xf784431927LL), + // C4[1], coeff of eps^24, polynomial in n of order 2 + real(29556996608LL),-real(15922652416LL),real(11273228472LL), + real(0x2383148b21287LL), + // C4[1], coeff of eps^23, polynomial in n of order 3 + real(0x165661ad6b70LL),-real(0x1009b31cabe0LL),real(0x7444963bdd0LL), + real(0x1d0511c64f5LL),real(0x42b94999694cfa7LL), + // C4[1], coeff of eps^22, polynomial in n of order 4 + real(696434041088LL),-real(561462728640LL),real(334369174656LL), + -real(182661157184LL),real(127941872058LL),real(0x13691a10b39411LL), + // C4[1], coeff of eps^21, polynomial in n of order 5 + real(0x2b50c847e5bec70LL),-real(0x25172ad2adc8640LL), + real(0x187490c86e06510LL),-real(0x11cf5b364679120LL), + real(0x7e9f37da26e7b0LL),real(0x1f979b01bfd5e3LL), + reale(227941,0xc6590096a3923LL), + // C4[1], coeff of eps^20, polynomial in n of order 6 + real(0x84a641c077c100LL),-real(0x75601a6b667780LL), + real(0x51157a29d94600LL),-real(0x4247925ad10480LL), + real(0x269068d8c2ab00LL),-real(0x15748d5a64a980LL), + real(0xed190d6b360a4LL),reale(29731,0x892d0013a607fLL), + // C4[1], coeff of eps^19, polynomial in n of order 7 + real(0x57e3d5e3e8a64d50LL),-real(0x4ee151925712ac60LL), + real(0x379f60f9d8160ef0LL),-real(0x3036f6417460ec40LL), + real(0x1eece80c1c746690LL),-real(0x16f21d696f523420LL), + real(0x9ef6bfafd871830LL),real(0x27a3f6720674fabLL), + reale(3419126,0x9f3708d39590dLL), + // C4[1], coeff of eps^18, polynomial in n of order 8 + reale(2128,0x469250df87e00LL),-real(0x76ff6f2ca68ee740LL), + real(0x544ea56af984a280LL),-real(0x4b3b3c5b1f3b3dc0LL), + real(0x324e822f05811f00LL),-real(0x29dd8ae6f4502040LL), + real(0x179c3b6434632b80LL),-real(0xd7628385c5d56c0LL), + real(0x91fdd6e000a7926LL),reale(3419126,0x9f3708d39590dLL), + // C4[1], coeff of eps^17, polynomial in n of order 9 + reale(3396,0xc29d3f547be10LL),-reale(2963,0x6657b77d7b180LL), + reale(2082,0xa3af2d55cd2f0LL),-real(0x74e3fc23ed074b20LL), + real(0x4f51e11c0cc64dd0LL),-real(0x45cc62cad46028c0LL), + real(0x2b210825284d5ab0LL),-real(0x20cfde05bc67de60LL), + real(0xdb6584e22cc2590LL),real(0x36aae0ede944991LL), + reale(3419126,0x9f3708d39590dLL), + // C4[1], coeff of eps^16, polynomial in n of order 10 + reale(5994,0xfab7bd428a400LL),-reale(4919,0xd8955c3980a00LL), + reale(3376,0x641d9d71fd000LL),-reale(2975,0x320d339261600LL), + real(0x7dd1b5a4fb9ffc00LL),-real(0x712cdc1424704200LL), + real(0x486493a43f86e800LL),-real(0x3daeb06e6a40ce00LL), + real(0x21506b8426325400LL),-real(0x13a656589a61fa00LL), + real(0xcfa4dcbf923eff0LL),reale(3419126,0x9f3708d39590dLL), + // C4[1], coeff of eps^15, polynomial in n of order 11 + reale(13117,0x6cbddabc52ed0LL),-reale(9318,0xa8f3ea9b44c20LL), + reale(6040,0x7b2fdab4ba7f0LL),-reale(5022,0x22b8983435e80LL), + reale(3330,0x281af37e2710LL),-reale(2968,0x456e895a2c0e0LL), + real(0x7764510336be0030LL),-real(0x6af4843f7d4f5f40LL), + real(0x3eba1ed514e18750LL),-real(0x31669b90045c25a0LL), + real(0x13a17c0101ce1070LL),real(0x4e2a88c78d66acfLL), + reale(3419126,0x9f3708d39590dLL), + // C4[1], coeff of eps^14, polynomial in n of order 12 + reale(68147,0x8cb1a33fbb300LL),-reale(25030,0x19a83b314d5c0LL), + reale(13399,0xd5b954b9ffe80LL),-reale(9632,0x5ff7adc5b8740LL), + reale(6058,0x6185fb910e200LL),-reale(5122,0x24f31e326fcc0LL), + reale(3246,0x498e64bf8a580LL),-reale(2929,0xc60f539a7ee40LL), + real(0x6e041fee5d419100LL),-real(0x60b53ba76d5f13c0LL), + real(0x3113d4fc9085ec80LL),-real(0x1e6533c87b7d2540LL), + real(0x1357622acbb7b13aLL),reale(3419126,0x9f3708d39590dLL), + // C4[1], coeff of eps^13, polynomial in n of order 13 + -reale(121532,0xe4514e2bd7670LL),-reale(15940,0x17553143d1340LL), + reale(71019,0xc50f40d0125f0LL),-reale(26120,0x5d81b142df60LL), + reale(13667,0x35bfe1bb73850LL),-reale(9984,0xe4f4c1c8f9780LL), + reale(6033,0x4bb2ec6997cb0LL),-reale(5212,0x5459006443fa0LL), + reale(3108,0x7a1250dedaf10LL),-reale(2836,0xbc55f0b59dbc0LL), + real(0x605fcd3581f88b70LL),-real(0x4fb9f3b2da8b6fe0LL), + real(0x1d6444fcd70bcdd0LL),real(0x74c81d1452803b5LL), + reale(3419126,0x9f3708d39590dLL), + // C4[1], coeff of eps^12, polynomial in n of order 14 + -reale(18279,0x4105927635f00LL),reale(111436,0xf9c78acad1e80LL), + -reale(127455,0xb83d096a36600LL),-reale(14599,0xb6308ef406280LL), + reale(74253,0x38e0bbebab300LL),-reale(27394,0x6661a055a9b80LL), + reale(13898,0x35bd350d73c00LL),-reale(10384,0x95909b51f3c80LL), + reale(5941,0x73f13b5b28500LL),-reale(5277,0x6484894bf580LL), + reale(2891,0x688dd5accde00LL),-reale(2646,0x1bce07b5e7680LL), + real(0x4c6028727ac69700LL),-real(0x32eae1a8c2946f80LL), + real(0x1ea30b56650e6834LL),reale(3419126,0x9f3708d39590dLL), + // C4[1], coeff of eps^11, polynomial in n of order 15 + -real(0x26534490cad1dfb0LL),-reale(2194,0x14a85ebaf95e0LL), + -reale(18676,0x98f19d91af310LL),reale(115088,0x35b741cc34140LL), + -reale(134245,0x8207aed455070LL),-reale(12735,0xf52bb5c1fbfa0LL), + reale(77916,0x32c371fd8ec30LL),-reale(28918,0xb36d158cbf480LL), + reale(14055,0x84fcc4e4ea6d0LL),-reale(10840,0xa60c8c5d6b960LL), + reale(5745,0xafd650291c370LL),-reale(5282,0xabba6463d6a40LL), + reale(2556,0x876a7d9212610LL),-reale(2272,0x615ae9eab6320LL), + real(0x2e7aab3dc406b2b0LL),real(0xb7e588c69951913LL), + reale(3419126,0x9f3708d39590dLL), + // C4[1], coeff of eps^10, polynomial in n of order 16 + -real(0x6ec9ec72fa83400LL),-real(0xee6121f9ed5ac40LL), + -real(0x2698258da225a980LL),-reale(2223,0x82921a72280c0LL), + -reale(19088,0x4fb95e6188700LL),reale(119080,0xff5c72a1c6ec0LL), + -reale(142117,0x7c3deb03b7480LL),-reale(10117,0x6e8319b8485c0LL), + reale(82086,0xede392256e600LL),-reale(30795,0xed5c849e10640LL), + reale(14073,0x47ff3f3e080LL),-reale(11359,0x76d81b264bac0LL), + reale(5387,0x791e9eab0d300LL),-reale(5153,0xcdddc38eb4b40LL), + real(0x7fb4f5b53eb31580LL),-real(0x5fcfbdbbdde05fc0LL), + real(0x34b713242f2d630eLL),reale(3419126,0x9f3708d39590dLL), + // C4[1], coeff of eps^9, polynomial in n of order 17 + -real(0x20f38bbaca812f0LL),-real(0x39b499036d51b00LL), + -real(0x6e4d3364d687b10LL),-real(0xee56650d93fe5a0LL), + -real(0x26cbb66f58b91d30LL),-reale(2250,0xe985ef9ea8440LL), + -reale(19510,0x3134f0f32ad50LL),reale(123456,0xc66bc06159520LL), + -reale(151362,0xfafa005fcdf70LL),-reale(6379,0xaa0075c90d80LL), + reale(86843,0xd7e050f079870LL),-reale(33196,0x7f1161b25e020LL), + reale(13831,0x3ac1850370650LL),-reale(11930,0x8d19c5e9856c0LL), + reale(4775,0x36871b380b630LL),-reale(4708,0xdfb0fde91e560LL), + real(0x4e466dbc0d5cf410LL),real(0x132845ea2b7be139LL), + reale(3419126,0x9f3708d39590dLL), + // C4[1], coeff of eps^8, polynomial in n of order 18 + -real(0xcaab4ddd8d4600LL),-real(0x13c31d1cbb16d00LL), + -real(0x207a98d99de3000LL),-real(0x390c3dedd68b300LL), + -real(0x6d71551ca261a00LL),-real(0xed90e825b918900LL), + -real(0x26e62c786e462400LL),-reale(2274,0xbbaf6c5e10f00LL), + -reale(19934,0x1db266a5f6e00LL),reale(128254,0x3ade3c4739b00LL), + -reale(162383,0xab3413f131800LL),-real(0x3992c873ce48ab00LL), + reale(92230,0x4a4593a3dbe00LL),-reale(36418,0x345102e4b0100LL), + reale(13110,0x864dfe531f400LL),-reale(12475,0xa3edd9488700LL), + reale(3771,0xc13fa20286a00LL),-reale(3469,0x365d076765d00LL), + real(0x661b6984b64e65f8LL),reale(3419126,0x9f3708d39590dLL), + // C4[1], coeff of eps^7, polynomial in n of order 19 + -real(0x5b1678b2b96e30LL),-real(0x83e7d604d6e1a0LL), + -real(0xc5c1bd21f06210LL),-real(0x135402446a1f500LL), + -real(0x1fd9e061288aff0LL),-real(0x381fb1c2d0ea860LL), + -real(0x6c176a9d32ee3d0LL),-real(0xebcbb379725c7c0LL), + -real(0x26dc285f96da89b0LL),-reale(2292,0x8c4f779be1f20LL), + -reale(20344,0xed4bfa0642d90LL),reale(133496,0x33ba4ee858580LL), + -reale(175742,0x64c709ffb5b70LL),reale(7288,0xff81f26b85a20LL), + reale(98139,0x5735ff04360b0LL),-reale(41010,0x6c5dc3c9a6d40LL), + reale(11505,0xfe66ab587ad0LL),-reale(12646,0x14c7a4cad9ca0LL), + reale(2204,0x9aaf76ecb66f0LL),real(0x2076d1ad78dbacf7LL), + reale(3419126,0x9f3708d39590dLL), + // C4[1], coeff of eps^6, polynomial in n of order 20 + -real(0x2d4d049c656700LL),-real(0x3e4af5e8d022c0LL), + -real(0x57ced7fe851580LL),-real(0x7f7034131ef240LL), + -real(0xbf83d85dea6c00LL),-real(0x12c465612feb5c0LL), + -real(0x1f04ac518a30280LL),-real(0x36d88216b840540LL), + -real(0x6a13494183c7100LL),-real(0xe8a2e478ed378c0LL), + -real(0x269ca36792944f80LL),-reale(2300,0x7badf4501a840LL), + -reale(20714,0x7015050283600LL),reale(139156,0x8278406ccd440LL), + -reale(192233,0x29cb54965bc80LL),reale(20133,0xdb20ab18364c0LL), + reale(103930,0xc444b13858500LL),-reale(48022,0x859c77e028ec0LL), + reale(8312,0x1287962dbf680LL),-reale(10954,0x169105fd99e40LL), + reale(3795,0x3bfe126c62e22LL),reale(3419126,0x9f3708d39590dLL), + // C4[1], coeff of eps^5, polynomial in n of order 21 + -real(0x1802918882e770LL),-real(0x1fcd949a6860c0LL), + -real(0x2aeab9b7d2f010LL),-real(0x3b2acc792185e0LL), + -real(0x539feddcdda2b0LL),-real(0x79b43080aca700LL), + -real(0xb76e50170e2350LL),-real(0x1207f374f78a820LL), + -real(0x1de74f0a09e95f0LL),-real(0x351484156246d40LL), + -real(0x6722781c7da1e90LL),-real(0xe37fba15ed8da60LL), + -real(0x260d3a8a453ee130LL),-reale(2292,0x258c84a62d380LL), + -reale(20989,0x3411bcc4001d0LL),reale(145073,0x9b58d1932c360LL), + -reale(212947,0x443e0cc67a470LL),reale(41274,0x9a63d1cc50640LL), + reale(107042,0xff9bf7f6712f0LL),-reale(59294,0xf496954c0eee0LL), + reale(2833,0xc664f5dce0050LL),real(0x17b85ffcea47049dLL), + reale(3419126,0x9f3708d39590dLL), + // C4[1], coeff of eps^4, polynomial in n of order 22 + -real(0xd20723e198100LL),-real(0x10e999b2026480LL), + -real(0x161c2993f30e00LL),-real(0x1d62585afd4f80LL), + -real(0x27ca0dc8a2fb00LL),-real(0x370cc97a8ce280LL), + -real(0x4e170b46a3d800LL),-real(0x7213d21df5ad80LL), + -real(0xac9b82d7503500LL),-real(0x1109444f53c4080LL), + -real(0x1c6019c5f02a200LL),-real(0x329a7eb49a52b80LL), + -real(0x62d84097135af00LL),-real(0xdb6f2c88eb4fe80LL), + -real(0x2502e63c01a3ec00LL),-reale(2256,0x8389e52b04980LL), + -reale(21063,0xc2942f767e900LL),reale(150710,0x347c6ec646380LL), + -reale(239155,0x111ed671c3600LL),reale(78297,0xeac3242447880LL), + reale(97157,0xffcea47049d00LL),-reale(74487,0xcca6f58949a80LL), + reale(11841,0x219395a415cbcLL),reale(3419126,0x9f3708d39590dLL), + // C4[1], coeff of eps^3, polynomial in n of order 23 + -real(0x7207334f38cb0LL),-real(0x8fe6a0f540760LL), + -real(0xb7c4f4df6c510LL),-real(0xedcd97a176940LL), + -real(0x1384e0d9162770LL),-real(0x1a108f169c7320LL), + -real(0x2378674e3fafd0LL),-real(0x3154606a2c6100LL), + -real(0x465a9ded7c5a30LL),-real(0x675a79a8aa6ee0LL), + -real(0x9d4a8ab99e2290LL),-real(0xf9e328cb49d8c0LL), + -real(0x1a2ce594ece04f0LL),-real(0x2efbcc23543daa0LL), + -real(0x5c688ee5939fd50LL),-real(0xceb90d2fccdb080LL), + -real(0x2331240c282307b0LL),-reale(2173,0x456299e8e9660LL), + -reale(20716,0x42df2018b2010LL),reale(154405,0x43613e2a37c0LL), + -reale(270827,0xec43372c34270LL),reale(146546,0xa61bf3c2f7de0LL), + reale(26313,0x9ff2a1de69530LL),-reale(32563,0x1c55db833bf05LL), + reale(3419126,0x9f3708d39590dLL), + // C4[1], coeff of eps^2, polynomial in n of order 24 + -real(0x39a9fc22d9600LL),-real(0x47a4ffa857140LL), + -real(0x59ea353148580LL),-real(0x721982b3023c0LL), + -real(0x9291e22ef9d00LL),-real(0xbeda9ea6fc240LL), + -real(0xfc517cd616480LL),-real(0x1535335443d4c0LL), + -real(0x1d14474c2c6400LL),-real(0x28c4706fdbe340LL), + -real(0x3aa43e35a32380LL),-real(0x56eefde83775c0LL), + -real(0x859522b6982b00LL),-real(0xd663f0e8861440LL), + -real(0x16b2ad2884e0280LL),-real(0x2932441ccc746c0LL), + -real(0x51f4ee722e73200LL),-real(0xb97e18f372a9540LL), + -real(0x1ff5b9ebacd64180LL),-real(0x7d04fcecbaaf87c0LL), + -reale(19431,0x998fba7cdb900LL),reale(150594,0xe619e547a59c0LL), + -reale(294712,0x9903e1bb02080LL),reale(231559,0xe5f0c3a538740LL), + -reale(65126,0x38abb70677e0aLL),reale(3419126,0x9f3708d39590dLL), + // C4[1], coeff of eps^1, polynomial in n of order 25 + -real(0x16b98c18c43f0LL),-real(0x1be76827efc80LL), + -real(0x2291674649910LL),-real(0x2b3d2747a6820LL), + -real(0x36a8d2fdcc830LL),-real(0x45e795ad137c0LL), + -real(0x5a8eeaa036550LL),-real(0x77007a4bcbf60LL), + -real(0x9ee5aa2960470LL),-real(0xd8045ac825300LL), + -real(0x12bb93df5b3990LL),-real(0x1a9b1c398546a0LL), + -real(0x26d2a92f5c98b0LL),-real(0x3a7858f998ee40LL), + -real(0x5b6e62f9c0b5d0LL),-real(0x959d5c24529de0LL), + -real(0x102f2d0b50524f0LL),-real(0x1e1472bfb1ba980LL), + -real(0x3d69bf9cb587a10LL),-real(0x8ee1210e8c36520LL), + -real(0x194d332fe8d44930LL),-real(0x6534ccbfa35124c0LL), + -reale(15788,0x2cc4c78572650LL),reale(115779,0xf2f861d29c3a0LL), + -reale(173669,0xec7492bbea570LL),reale(75980,0x9773003236861LL), + reale(3419126,0x9f3708d39590dLL), + // C4[2], coeff of eps^26, polynomial in n of order 0 + 2894476,real(0xfe89d46f33LL), + // C4[2], coeff of eps^25, polynomial in n of order 1 + -8609536,5603312,real(590597728875LL), + // C4[2], coeff of eps^24, polynomial in n of order 2 + -real(104352359168LL),real(40707880576LL),real(10376961584LL), + real(0xb18f66b7a5ca3LL), + // C4[2], coeff of eps^23, polynomial in n of order 3 + -real(0x265f8c17d00LL),real(0x13bddd35200LL),-real(871294451456LL), + real(553528081392LL),real(0xa1c12e8b2dd1e3LL), + // C4[2], coeff of eps^22, polynomial in n of order 4 + -real(0x46e25cf59280LL),real(0x290af5269020LL),-real(0x22f7c7b01940LL), + real(0xd08f4d0d560LL),real(0x355c24081bcLL),real(0xc015674546693d9LL), + // C4[2], coeff of eps^21, polynomial in n of order 5 + -real(0x326f6045f923c80LL),real(0x1fb1615f9d3a600LL), + -real(0x1db1797638c1780LL),real(0xe9780531c07300LL), + -real(0x9d24cc38e5d280LL),real(0x60cf9034bf3868LL), + reale(379902,0xf53f00fb109e5LL), + // C4[2], coeff of eps^20, polynomial in n of order 6 + -real(0x4837c78c0550480LL),real(0x313ba08613af040LL), + -real(0x2ee33229a4bc300LL),real(0x1a152ee5f2ae9c0LL), + -real(0x172de5252da0180LL),real(0x824fa762c0c340LL), + real(0x2180172e018ad8LL),reale(379902,0xf53f00fb109e5LL), + // C4[2], coeff of eps^19, polynomial in n of order 7 + -real(0x5fc4bec46509e480LL),real(0x48096a7e75900b00LL), + -real(0x41caf1fb886dd580LL),real(0x28558a32a56ef200LL), + -real(0x26dce3ddd1a42680LL),real(0x120433e2d2025900LL), + -real(0xce36e1803df1780LL),real(0x7a135866f905bb8LL), + reale(5698544,0x5eb10eb5f946bLL), + // C4[2], coeff of eps^18, polynomial in n of order 8 + -reale(2176,0xe1585afea1500LL),real(0x73bced2a00a143a0LL), + -real(0x5fca97395e84bfc0LL),real(0x418b4cd8fc5e04e0LL), + -real(0x3e6c34ea7ddb8a80LL),real(0x212422dcacab1620LL), + -real(0x1f0466b0c7211540LL),real(0xa12130d17045760LL), + real(0x29b0aa486315dbcLL),reale(5698544,0x5eb10eb5f946bLL), + // C4[2], coeff of eps^17, polynomial in n of order 9 + -reale(3194,0x3409f96190200LL),reale(3129,0x198ba10e3f000LL), + -reale(2211,0xeca78927c1e00LL),real(0x6cf94ec7bfac7400LL), + -real(0x5f04d2df84f0ba00LL),real(0x39318494ff85f800LL), + -real(0x38939121c731d600LL),real(0x1854a6f7e2957c00LL), + -real(0x12decef0b13a7200LL),real(0xa9861a018e14120LL), + reale(5698544,0x5eb10eb5f946bLL), + // C4[2], coeff of eps^16, polynomial in n of order 10 + -reale(5172,0xb8c4b33583a00LL),reale(5700,0x1d26bd0962f00LL), + -reale(3248,0x8acf908fbc800LL),reale(3050,0xed985975b4100LL), + -reale(2251,0xef96e32335600LL),real(0x6370a1a9e900d300LL), + -real(0x5c955afee309e400LL),real(0x2eb3ea14003fe500LL), + -real(0x2e844e36822a7200LL),real(0xd8a8b891f217700LL), + real(0x388df4ca3a6fb20LL),reale(5698544,0x5eb10eb5f946bLL), + // C4[2], coeff of eps^15, polynomial in n of order 11 + -reale(11115,0xb2ff91ec6c600LL),reale(11728,0x761e1ef822c00LL), + -reale(5178,0x9a27d63f52200LL),reale(5773,0x24fd2adb2f000LL), + -reale(3328,0x5f0c31c71fe00LL),reale(2908,0x836ab328fb400LL), + -reale(2291,0x629d070485a00LL),real(0x5681ee23b9ad7800LL), + -real(0x56cafdb120433600LL),real(0x21dbd9f992213c00LL), + -real(0x1d4bdf01a76d9200LL),real(0xf4e0cbd04176b20LL), + reale(5698544,0x5eb10eb5f946bLL), + // C4[2], coeff of eps^14, polynomial in n of order 12 + -reale(73826,0x9e48c9be75880LL),reale(32637,0x887aa6de960e0LL), + -reale(10940,0x9647b1447b9c0LL),reale(12348,0xdd9347a34b3a0LL), + -reale(5206,0x461aa415f3b00LL),reale(5776,0x82c559a327660LL), + -reale(3445,0x2b71b5ef13c40LL),reale(2676,0xdbe2bf3d4c920LL), + -reale(2313,0x6c289eed11d80LL),real(0x45af1f46068fcbe0LL), + -real(0x4a646c774fde3ec0LL),real(0x127e48f8affd9ea0LL), + real(0x4e336f38ab11704LL),reale(5698544,0x5eb10eb5f946bLL), + // C4[2], coeff of eps^13, polynomial in n of order 13 + reale(130976,0x1a84c1eb6d80LL),-reale(14597,0x4f1d8a91fc600LL), + -reale(76483,0x6c58cf65980LL),reale(35388,0xd1bf338007b00LL), + -reale(10663,0x1c210a8b78080LL),reale(13004,0x14f125ca37c00LL), + -reale(5285,0x554d73733c780LL),reale(5660,0xa57467d557d00LL), + -reale(3609,0xe9c5b2656ee80LL),reale(2326,0xf26507322be00LL), + -reale(2274,0xe6ae0b8fcb580LL),real(0x30f364de4c777f00LL), + -real(0x3139417308d0dc80LL),real(0x173bf41713ca3b88LL), + reale(5698544,0x5eb10eb5f946bLL), + // C4[2], coeff of eps^12, polynomial in n of order 14 + reale(12302,0xe52cc8d8c2180LL),-reale(90162,0x247de245423c0LL), + reale(136898,0x7ace803b76f00LL),-reale(20188,0x482d40173de40LL), + -reale(79167,0xe510d7fd7c380LL),reale(38835,0xfee0572864740LL), + -reale(10270,0x4559a0d3b600LL),reale(13648,0x338b156f30cc0LL), + -reale(5468,0x80042be36a880LL),reale(5349,0x619325bd73240LL), + -reale(3821,0xffa84c59adb00LL),real(0x729df2a6c14b77c0LL), + -reale(2073,0x93dcfbe928d80LL),real(0x193a4a0699e49d40LL), + real(0x6c8a3fc264f2d98LL),reale(5698544,0x5eb10eb5f946bLL), + // C4[2], coeff of eps^11, polynomial in n of order 15 + real(0x12b65c49560e1680LL),real(0x4c91348dd4c57d00LL), + reale(12186,0xb870c2ef8b380LL),-reale(91199,0x47a39f34d9e00LL), + reale(143440,0xa133e98363080LL),-reale(27237,0xaf8901f443900LL), + -reale(81724,0x1b06c40663280LL),reale(43231,0xcee7486ccec00LL), + -reale(9771,0xb47d34b793580LL),reale(14177,0x876b1df11100LL), + -reale(5844,0x5970f546f9880LL),reale(4733,0x71ff0d3b37600LL), + -reale(4034,0xaeeb7c4e61b80LL),real(0x4b0e043dd17f5b00LL), + -real(0x5c6dac5851097e80LL),real(0x259ade3cf4689f28LL), + reale(5698544,0x5eb10eb5f946bLL), + // C4[2], coeff of eps^10, polynomial in n of order 16 + real(0x285b74a086cfe00LL),real(0x61629f583f6fc20LL), + real(0x11e1f0840e822e40LL),real(0x4a2acb7177936860LL), + reale(12009,0x162afd0a23e80LL),-reale(92025,0x51c6b64b59b60LL), + reale(150657,0xe159fc0830ec0LL),-reale(36240,0x8903bcca1af20LL), + -reale(83842,0x8f32e14ed8100LL),reale(48929,0x80db803df8d20LL), + -reale(9247,0x4a711a73d90c0LL),reale(14370,0x3118e0d87960LL), + -reale(6545,0xcfaa0092b4080LL),reale(3681,0xa71da4ef975a0LL), + -reale(4055,0x6bd2ceb58b040LL),real(0x201a58611bc4e1e0LL), + real(0x8ca8a9bec5eeb0cLL),reale(5698544,0x5eb10eb5f946bLL), + // C4[2], coeff of eps^9, polynomial in n of order 17 + real(0x8f791b0d72f300LL),real(0x116eee5fb7db000LL), + real(0x2544a69b0af6d00LL),real(0x5ae50a5c0f6ba00LL), + real(0x10e6ab279c402700LL),real(0x472bda650b6c4400LL), + reale(11750,0x4a89b28f5a100LL),-reale(92512,0x1ccd7f1613200LL), + reale(158574,0x53a9410005b00LL),-reale(47896,0xbfb8d60312800LL), + -reale(84919,0xb4a50d4cf2b00LL),reale(56401,0x32e93db7ce200LL), + -reale(8956,0x3835fd4c87100LL),reale(13782,0xdee88bf296c00LL), + -reale(7712,0x7aed9801af700LL),reale(2126,0x5791e5314f600LL), + -reale(3273,0xe9400d1963d00LL),real(0x4230ff2c7e6defd0LL), + reale(5698544,0x5eb10eb5f946bLL), + // C4[2], coeff of eps^8, polynomial in n of order 18 + real(0x289b91a48ebf00LL),real(0x45ee5b14465380LL), + real(0x7f92734c023800LL),real(0xfa5ad187871c80LL), + real(0x21cddd2df61b100LL),real(0x5372a978dde2580LL), + real(0xfbd02001ed7aa00LL),real(0x436e93187af7ee80LL), + reale(11383,0x2dcd21f7ea300LL),-reale(92459,0xff89d11970880LL), + reale(167131,0xf0a2167d11c00LL),-reale(63199,0x7fe973623f80LL), + -reale(83766,0xa02debe66b00LL),reale(66187,0xcedf7a1cac980LL), + -reale(9608,0xefbab691d7200LL),reale(11585,0x75dbe72dc9280LL), + -reale(9220,0x22c92d6997900LL),real(0x18709d3bc0679b80LL), + real(0x5b7e325c6742390LL),reale(5698544,0x5eb10eb5f946bLL), + // C4[2], coeff of eps^7, polynomial in n of order 19 + real(0xd108e5f6f6100LL),real(0x14cfb44a7f1600LL), + real(0x227bc5972bab00LL),real(0x3bea4dd1053000LL), + real(0x6e5f06564db500LL),real(0xdaf2ed1ea74a00LL), + real(0x1dec9104c41ff00LL),real(0x4ae6e1cc221e400LL), + real(0xe5bde12a5950900LL),real(0x3ec229ad8ff17e00LL), + reale(10869,0xc2e1de8335300LL),-reale(91550,0xfd5202ded6800LL), + reale(176075,0x65a5499a95d00LL),-reale(83531,0x98920703e4e00LL), + -reale(77994,0x11133349c5900LL),reale(78539,0xb0828e93b4c00LL), + -reale(12981,0x6d9e1d7114f00LL),reale(6537,0x5c156837be600LL), + -reale(9404,0xf97b75bc90500LL),reale(2071,0xc05f52f113a50LL), + reale(5698544,0x5eb10eb5f946bLL), + // C4[2], coeff of eps^6, polynomial in n of order 20 + real(0x4748ad3ff9e80LL),real(0x6b926f7e60d60LL),real(0xa71fa4085b840LL), + real(0x10c991e0a3ab20LL),real(0x1c15b3b145b200LL), + real(0x314f7c7c43f8e0LL),real(0x5be1ff458cabc0LL), + real(0xb89930a80796a0LL),real(0x199734a3c07c580LL), + real(0x411aa25f2292460LL),real(0xcb87e4542581f40LL), + real(0x38e7a442bb914220LL),reale(10156,0x20944a9a6d900LL), + -reale(89265,0x51d50a4f57020LL),reale(184683,0x63f792d3912c0LL), + -reale(110680,0x89cae6d0a5260LL),-reale(62727,0xfdf47fc1380LL), + reale(91791,0x3f8035a7d3b60LL),-reale(22895,0xcc844c9bf79c0LL), + -real(0x5652aea374b626e0LL),-real(0x38edb32bcbdda4acLL), + reale(5698544,0x5eb10eb5f946bLL), + // C4[2], coeff of eps^5, polynomial in n of order 21 + real(0x185346b40be80LL),real(0x234a30239ea00LL),real(0x345f5bcfbb580LL), + real(0x4fc2f91719900LL),real(0x7d257d9ac0c80LL),real(0xcb49d34f58800LL), + real(0x1580c944df8380LL),real(0x263bb5e9cb7700LL), + real(0x483bd94933da80LL),real(0x935c1fd3f92600LL), + real(0x14c807d3436d180LL),real(0x35e9298d8a45500LL), + real(0xac6bf9cef462880LL),real(0x318eb0c51232c400LL), + reale(9164,0xf22328f6f9f80LL),-reale(84728,0x78acb3795cd00LL), + reale(191114,0x47ac3650f680LL),-reale(146268,0x68f68696f9e00LL), + -reale(28124,0xaf1a222081280LL),reale(95633,0xf3c35e98b1100LL), + -reale(42101,0xccb76963dbb80LL),reale(4250,0xa99770cb50078LL), + reale(5698544,0x5eb10eb5f946bLL), + // C4[2], coeff of eps^4, polynomial in n of order 22 + real(0x7c86a4240e80LL),real(0xaf5db2064cc0LL),real(0xfb958bed1300LL), + real(0x17080cf847940LL),real(0x2288f92359780LL),real(0x352f6beaa45c0LL), + real(0x54760062cdc00LL),real(0x8b024608ff240LL),real(0xeea60450a2080LL), + real(0x1af0609151bec0LL),real(0x33c8072244a500LL), + real(0x6bad7af287eb40LL),real(0xf83a707fcba980LL), + real(0x293d0a92ebeb7c0LL),real(0x87aa233703e6e00LL), + real(0x2855283ce7ee6440LL),reale(7785,0x74e297d243280LL), + -reale(76427,0xf39041d0ccf40LL),reale(190726,0x777542b243700LL), + -reale(188315,0x1030e5dfaa2c0LL),reale(42101,0xccb76963dbb80LL), + reale(46959,0xb31b5803129c0LL),-reale(23682,0x43272b482b978LL), + reale(5698544,0x5eb10eb5f946bLL), + // C4[2], coeff of eps^3, polynomial in n of order 23 + real(0x21a7e921c980LL),real(0x2e51be6e8f00LL),real(0x40c19fbec480LL), + real(0x5c1e6062c200LL),real(0x8599d6a9df80LL),real(0xc60160b77500LL), + real(0x12cb7c4c7da80LL),real(0x1d5985b996800LL),real(0x2f524aaed7580LL), + real(0x4f30941955b00LL),real(0x8a76dd63f7080LL),real(0xff32326380e00LL), + real(0x1f5b1b59928b80LL),real(0x42dd3cfeae4100LL), + real(0x9e90e4efcb8680LL),real(0x1b33e235264b400LL), + real(0x5cdaf2eb93f2180LL),real(0x1cd398a25fa82700LL), + reale(5865,0x9368046121c80LL),-reale(61723,0xe7c88c9baa600LL), + reale(171645,0xcc7599f993780LL),-reale(213747,0x992d035d6f300LL), + reale(126305,0x66263c2b93280LL),-reale(28944,0xfcbe1874a70e8LL), + reale(5698544,0x5eb10eb5f946bLL), + // C4[2], coeff of eps^2, polynomial in n of order 24 + real(0x5f08c3cb900LL),real(0x807038c0ca0LL),real(0xaffaed32440LL), + real(0xf4c5be483e0LL),real(0x15a2490f6f80LL),real(0x1f28eae1cb20LL), + real(0x2dce80c7fac0LL),real(0x44e60304c260LL),real(0x6a58ca3b2600LL), + real(0xa90e89d449a0LL),real(0x1160126eb5140LL),real(0x1db88b51940e0LL), + real(0x354168d7adc80LL),real(0x64e3bca9a8820LL),real(0xcc99ed98827c0LL), + real(0x1c3fb9ad58ff60LL),real(0x45c01ca2899300LL), + real(0xc88852534b86a0LL),real(0x2d1eac1f8a97e40LL), + real(0xee21e1c2e9afde0LL),reale(3238,0x9997f46a24980LL), + -reale(36434,0x3fed7daa1bae0LL),reale(105254,0x7fca8779a54c0LL), + -reale(115779,0xf2f861d29c3a0LL),reale(43417,0x7b1d24aefa95cLL), + reale(5698544,0x5eb10eb5f946bLL), + // C4[3], coeff of eps^26, polynomial in n of order 0 + 433472,real(72882272925LL), + // C4[3], coeff of eps^25, polynomial in n of order 1 + real(76231168),real(19985680),real(0x958a9334879LL), + // C4[3], coeff of eps^24, polynomial in n of order 2 + real(969805824),-real(756467712),real(427576864),real(0x33a763b318f5LL), + // C4[3], coeff of eps^23, polynomial in n of order 3 + real(0xe7cfd39aa00LL),-real(0xe6239d55400LL),real(0x44ffe5cce00LL), + real(0x123fa804df0LL),real(0x73400ac32a3f24fLL), + // C4[3], coeff of eps^22, polynomial in n of order 4 + real(633551529LL<<15),-real(0x130f2c71c000LL),real(0x7e08a8b4000LL), + -real(0x69e0a004000LL),real(0x39175efa340LL),real(0x59a39697cb86721LL), + // C4[3], coeff of eps^21, polynomial in n of order 5 + real(0xe1a59555817c700LL),-real(0xce92ef160470400LL), + real(0x6a50b28bc94d100LL),-real(0x6ec5ce0328fa200LL), + real(0x1e2919432b73b00LL),real(0x81169f96b647f8LL), + reale(2659320,0xb4b906dd74543LL), + // C4[3], coeff of eps^20, polynomial in n of order 6 + real(0x4a951ec0f743800LL),-real(0x39128060ba74400LL), + real(0x258d1de3ebd5000LL),-real(0x25e6a8ece22dc00LL), + real(0xe953314d336800LL),-real(0xd6fbba5b80b400LL), + real(0x6d3d6d3e79ea90LL),reale(531864,0x2425015f7daa7LL), + // C4[3], coeff of eps^19, polynomial in n of order 7 + real(0x7366685d2da15300LL),-real(0x46390dd9eadeba00LL), + real(0x3de3739917104900LL),-real(0x34e3ad131262bc00LL), + real(0x1ae64995e9a59f00LL),-real(0x1d6cea9b561f3e00LL), + real(0x70d3407961b9500LL),real(0x1ea45bc7b594048LL), + reale(7977962,0x1e2b14985cfc9LL), + // C4[3], coeff of eps^18, polynomial in n of order 8 + reale(2991,8707772229LL<<17),-real(0x5c0b6a6cd5328000LL), + real(0x6cf3b04ea6358000LL),-real(0x47da0c907a958000LL), + real(0x334344c895550000LL),-real(0x3257cd9b75628000LL), + real(0x11d874d9e96c8000LL),-real(0x1273b92365d58000LL), + real(0x8b048eddb8dae80LL),reale(7977962,0x1e2b14985cfc9LL), + // C4[3], coeff of eps^17, polynomial in n of order 9 + reale(4599,0x20675bc677c00LL),-reale(2190,0x6a6db0c48a000LL), + reale(3019,0xad2c946b04400LL),-real(0x5cc951aa5f7ff800LL), + real(0x61f2b89850d68c00LL),-real(0x49aa7ace4eb85000LL), + real(0x26482ceb1d4d5400LL),-real(0x2b88fb70a186a800LL), + real(0x8bf6f0c9a679c00LL),real(0x26ce624431e62e0LL), + reale(7977962,0x1e2b14985cfc9LL), + // C4[3], coeff of eps^16, polynomial in n of order 10 + real(0x383bee2531d2a000LL),-real(0x2821094d061d1000LL), + real(0x2c347b321d4c8000LL),-real(0x125d6736b20ff000LL), + real(0x1a6c4162f9ae6000LL),-real(0xdca07dd1a07d000LL), + real(0xba2cc7913be4000LL),-real(0xa8a49fd40deb000LL), + real(0x36dcb24ee422000LL),-real(0x4159df2ed6e9000LL), + real(0x1bdad6784709c40LL),reale(1139708,0xdfbd02f131dafLL), + // C4[3], coeff of eps^15, polynomial in n of order 11 + reale(7381,0x14c34c0c1f400LL),-reale(13257,0xf5b9dadc0c800LL), + reale(7086,0x404eb1053bc00LL),-reale(4054,0xe4ed62e9ea000LL), + reale(5287,0x17e93cc880400LL),-real(0x7bc6aed7afe87800LL), + reale(2758,0x364797381cc00LL),-real(0x676ee80244a35000LL), + real(0x3b6d32d9ca041400LL),-real(0x43e3e0c280942800LL), + real(0xa86d2e316b1dc00LL),real(0x300bec0027818e0LL), + reale(7977962,0x1e2b14985cfc9LL), + // C4[3], coeff of eps^14, polynomial in n of order 12 + reale(66948,0x4f30b3f870000LL),-reale(52646,0x686a3833a8000LL), + reale(7561,0xd0b8bda7a8000LL),-reale(13026,0x7d89ec00d8000LL), + reale(8130,0xd3b0b583a0000LL),-reale(3523,0xd290763e28000LL), + reale(5530,0x8b9708b698000LL),-real(0x7e52c154efd58000LL), + reale(2356,0x7673a06ad0000LL),-real(0x6f6a34d21b028000LL), + real(0x220d8444fca88000LL),-real(0x2fac85fa2e858000LL), + real(0x11c823101280e280LL),reale(7977962,0x1e2b14985cfc9LL), + // C4[3], coeff of eps^13, polynomial in n of order 13 + -reale(129173,0x58489bc283900LL),reale(59789,0xf9dc41e63d400LL), + reale(65695,0x9083acc5cc100LL),-reale(58445,0x2f2cc6e161a00LL), + reale(8184,0x5e79915d1b00LL),-reale(12353,0x83a959670c800LL), + reale(9463,0x4211f61d49500LL),-reale(2966,0xe12b8e3527600LL), + reale(5543,0x52a28a556ef00LL),-reale(2249,0xe1f749ba16400LL), + real(0x6b0d1cda5c5fe900LL),-real(0x70ab303245f3d200LL), + real(0xb596d16f1a34300LL),real(0x35b4de912478078LL), + reale(7977962,0x1e2b14985cfc9LL), + // C4[3], coeff of eps^12, polynomial in n of order 14 + -reale(6933,0xfc2bb7bd6800LL),reale(63382,0x668969a617c00LL), + -reale(132589,0xf0bdf2e789000LL),reale(69768,0x70d2052fd2400LL), + reale(63007,0x6d053a2cb4800LL),-reale(65233,0xb829e1b817400LL), + reale(9601,0xec9983923a000LL),-reale(11042,0x4317b942ccc00LL), + reale(11048,0xa50acd625f800LL),-reale(2545,0x7c97f16176400LL), + reale(5107,0xc83f2d67d000LL),-reale(2697,0x85e48cc53bc00LL), + real(0x36af107261fea800LL),-real(0x57b6b3b8f7f45400LL), + real(0x1b355635bf037310LL),reale(7977962,0x1e2b14985cfc9LL), + // C4[3], coeff of eps^11, polynomial in n of order 15 + -real(0x718d19ce618f700LL),-real(0x22292bb4d2a0a600LL), + -reale(6561,0x7bb8e05b06500LL),reale(61876,0xa080215cbc400LL), + -reale(135759,0x6c0a25f10b300LL),reale(81504,0x4116e653fae00LL), + reale(58147,0xb03676e9edf00LL),-reale(73011,0xd75b35d7e2800LL), + reale(12405,0x6d2fd911f1100LL),-reale(8886,0xdfa5214b6fe00LL), + reale(12677,0x826d436a8a300LL),-reale(2577,0x6d77ecdf41400LL), + reale(3947,0x879d1c7c5500LL),-reale(3192,0x95f286c2eaa00LL), + real(0x7343398f272e700LL),real(0x20b3728b7b6b2d8LL), + reale(7977962,0x1e2b14985cfc9LL), + // C4[3], coeff of eps^10, polynomial in n of order 16 + -real(0xaaaed768da0000LL),-real(0x1d8d58546174000LL), + -real(0x650ff776c6dc000LL),-real(0x1f0fa133b6eac000LL), + -reale(6125,0x868b157bb8000LL),reale(59813,0x741ec012c000LL), + -reale(138411,0xa7483b2cd4000LL),reale(95264,0x22057cd374000LL), + reale(50003,0x3a5ca8a530000LL),-reale(81502,0xff7b30e274000LL), + reale(17542,0xf2776c79b4000LL),-reale(5812,0xc63b637b2c000LL), + reale(13748,0x38a6c4d018000LL),-reale(3547,0xbf6bf7e154000LL), + real(0x78ab12d1827bc000LL),-reale(2957,0x6b24852f8c000LL), + real(0x2bef42096127d7c0LL),reale(7977962,0x1e2b14985cfc9LL), + // C4[3], coeff of eps^9, polynomial in n of order 17 + -real(0x3cadc0edd6600LL),-real(0x8587ee4c4e000LL), + -real(0x14633459f95a00LL),-real(0x397bc2059d8400LL), + -real(0xc89f8adb490e00LL),-real(0x3f2a86a64b5a800LL), + -real(0x32218961953c0200LL),reale(8146,0xa930f21b73400LL), + -reale(20015,0x8b16989f1b600LL),reale(15890,0x8aa3fb72d9000LL), + reale(5271,0xbcd5aeda65600LL),-reale(12822,0x9424c22ae1400LL), + reale(3774,0x46bb658aca200LL),-real(0x148a80159bb73800LL), + real(0x736580900f31ae00LL),-real(0x336f49c74ee95c00LL), + -real(0x249e756eeea0600LL),-real(0x13841fc89043bb0LL), + reale(1139708,0xdfbd02f131dafLL), + // C4[3], coeff of eps^8, polynomial in n of order 18 + -real(0x5318540751000LL),-real(0xa0702ad537800LL), + -real(0x14a9549a688000LL),-real(0x2e31b9dc878800LL), + -real(0x72dceb1c83f000LL),-real(0x14a6c8c8df91800LL), + -real(0x49c3e43ec426000LL),-real(0x17df3e19aed32800LL), + -reale(5017,0x9bceef61ed000LL),reale(53301,0x74feac5bf4800LL), + -reale(140139,0x5706164944000LL),reale(129320,0x1fd8eca933800LL), + reale(16403,0x87db178e25000LL),-reale(95278,0x1e65e67825800LL), + reale(40665,0x6f4b03ec9e000LL),-real(0x1c82af8b65ac6800LL), + reale(8049,0x334ede6a77000LL),-reale(7540,0x5b108b15f800LL), + real(0x49ca297e3ffdbce0LL),reale(7977962,0x1e2b14985cfc9LL), + // C4[3], coeff of eps^7, polynomial in n of order 19 + -real(0x11fa490472e00LL),-real(0x1fe0e98340400LL), + -real(0x3b2a552443a00LL),-real(0x73f5544ad2000LL), + -real(0xf2e5765f90600LL),-real(0x2290ce0f423c00LL), + -real(0x57b83400ee1200LL),-real(0x1023f65b9bfd800LL), + -real(0x3b36c6db61bde00LL),-real(0x13c7b72049527400LL), + -reale(4323,0x73be8c4caea00LL),reale(48359,0x7d21dc7197000LL), + -reale(137343,0xc18958973b600LL),reale(148676,0xd51cb5c775400LL), + -reale(14754,0xa89f0bc9ec200LL),-reale(92175,0x33d1092c54800LL), + reale(60290,0x88af4d43b7200LL),-reale(5855,0x8c9719d08e400LL), + -real(0x48b16aa4982d9a00LL),-real(0x51dba59b00547450LL), + reale(7977962,0x1e2b14985cfc9LL), + // C4[3], coeff of eps^6, polynomial in n of order 20 + -real(0x3f0527da8000LL),-real(0x69410a894000LL),-real(0xb5f68cf74000LL), + -real(0x14766cd18c000LL),-real(5178956321LL<<17), + -real(0x4cf42ca274000LL),-real(0xa45199d7cc000LL), + -real(0x17e337e696c000LL),-real(0x3e169088698000LL), + -real(0xbbd1c494494000LL),-real(0x2c70014b4ca4000LL), + -real(0xf67e7406420c000LL),-reale(3524,0xcb63f52610000LL), + reale(41859,0x1cfdfa000c000LL),-reale(129839,0xf92d750efc000LL), + reale(166586,0x5d10da3394000LL),-reale(59706,0x5fbf7c0388000LL), + -reale(68020,0xa047f74594000LL),reale(75721,0x1307a9002c000LL), + -reale(24384,0xc0b45d798c000LL),real(0x6534ccbfa35124c0LL), + reale(7977962,0x1e2b14985cfc9LL), + // C4[3], coeff of eps^5, polynomial in n of order 21 + -real(0xcd30266b700LL),-real(0x147d4e1fec00LL),-real(0x21a6b4a64100LL), + -real(0x390579acce00LL),-real(0x6423741d2b00LL),-real(0xb749b833f000LL), + -real(0x1602ad6953500LL),-real(0x2ccfc753d1200LL), + -real(0x61e5d62301f00LL),-real(0xe995b2fcff400LL), + -real(0x270c826fb7a900LL),-real(0x7a09e7f3045600LL), + -real(0x1dfb4c385ed9300LL),-real(0xaddceca1091f800LL), + -reale(2624,0xc45e83fdb9d00LL),reale(33433,0x20d0a109f6600LL), + -reale(114656,0xa3de6d0238700LL),reale(175907,0x1d4b03fe80400LL), + -reale(116168,0x7b17e334f1100LL),-reale(3810,0x1e1c2e9afde00LL), + reale(45340,0x664f5dce00500LL),-reale(17205,0xff74273e2678LL), + reale(7977962,0x1e2b14985cfc9LL), + // C4[3], coeff of eps^4, polynomial in n of order 22 + -real(784468838400LL),-real(0x11a0a388400LL),-real(0x1bda05d7000LL), + -real(0x2d25cb21c00LL),-real(0x4b5283d5800LL),-real(0x81d5381f400LL), + -real(0xe84e582c000LL),-real(0x1b2017768c00LL),-real(0x354f35942800LL), + -real(0x6f49195e6400LL),-real(0xf9ffb1d81000LL),-real(0x267769207fc00LL), + -real(0x6a9801634f800LL),-real(0x15adc2fc41d400LL), + -real(0x5947d2bb916000LL),-real(0x222d7eabcda6c00LL), + -real(0x22707489da53c800LL),reale(7620,0x3c385d35fbc00LL), + -reale(29197,0x886c2c8e2b000LL),reale(53341,0xa58a8c79e2400LL), + -reale(51817,0x997f46a249800LL),reale(25908,0xccbfa35124c00LL), + -reale(5262,0xb996ed2c7b770LL),reale(2659320,0xb4b906dd74543LL), + // C4[3], coeff of eps^3, polynomial in n of order 23 + -real(242883621120LL),-real(365079728640LL),-real(559688344320LL), + -real(876931046400LL),-real(0x147bd04f500LL),-real(0x21c7b15a600LL), + -real(0x396d13e6700LL),-real(0x650be18b000LL),-real(0xb8f375f7900LL), + -real(0x16253c45ba00LL),-real(0x2cc1928ceb00LL),-real(0x6065d92f8400LL), + -real(0xe04f74737d00LL),-real(0x23eadf138ce00LL), + -real(0x682920857ef00LL),-real(0x1651f4aee45800LL), + -real(0x61a68e7d270100LL),-real(0x281b43aa424e200LL), + -real(0x2bddd20238857300LL),reale(10668,0x544ee8e52d400LL), + -reale(45340,0x664f5dce00500LL),reale(90680,0xcc9ebb9c00a00LL), + -reale(84203,0x996ed2c7b7700LL),reale(28944,0xfcbe1874a70e8LL), + reale(7977962,0x1e2b14985cfc9LL), + // C4[4], coeff of eps^26, polynomial in n of order 0 + real(74207744),real(0x377b3e1aa351LL), + // C4[4], coeff of eps^25, polynomial in n of order 1 + -real(85649408),real(42776448),real(0x7a5a1b59863LL), + // C4[4], coeff of eps^24, polynomial in n of order 2 + -real(0x5d090f66800LL),real(0x15cb8432c00LL),real(412184096896LL), + real(0x3e897844a5071ebLL), + // C4[4], coeff of eps^23, polynomial in n of order 3 + -real(0xbff3f70d800LL),real(0x44c7b31b000LL),-real(0x48108b34800LL), + real(0x21db9c9a980LL),real(0x4fc9e010f5dcf23LL), + // C4[4], coeff of eps^22, polynomial in n of order 4 + -real(0xd6b769b7e000LL),real(0x72b1142e1800LL),-real(0x82aa7be7f000LL), + real(0x1aa8532e0800LL),real(0x779e97cc600LL),real(0x40d4060dc7c384c7LL), + // C4[4], coeff of eps^21, polynomial in n of order 5 + -real(0x474af3a87693800LL),real(0x3c389a0df442000LL), + -real(0x37e1a3d92db8800LL),real(0x12d1db00bd71000LL), + -real(0x15fc16a85bcd800LL),real(0x99491c279c9880LL), + reale(1139708,0xdfbd02f131dafLL), + // C4[4], coeff of eps^20, polynomial in n of order 6 + -real(0x303d69b47fe22400LL),real(0x3f4d2c93a259b200LL), + -real(0x29be542895db1800LL),real(0x17eb54d9d2a59e00LL), + -real(0x1b89924120220c00LL),real(0x4aa7a22c8d50a00LL), + real(0x157745851f3d4c0LL),reale(10257379,0xdda51a7ac0b27LL), + // C4[4], coeff of eps^19, polynomial in n of order 7 + -real(0x44c3305a70de1000LL),real(0x6d1c9adfcac5e000LL), + -real(0x312f88327b293000LL),real(0x3351684a1a554000LL), + -real(0x2ab43a21fd0e5000LL),real(0xdaac481cc1ca000LL), + -real(0x120b854707e97000LL),real(0x7289c72302f3500LL), + reale(10257379,0xdda51a7ac0b27LL), + // C4[4], coeff of eps^18, polynomial in n of order 8 + -reale(2256,0x7b501df238000LL),reale(2620,0x5abb698ccf000LL), + -real(0x3cfd86157c22a000LL),real(0x656f30f9d7a5d000LL), + -real(0x3529aafa1251c000LL),real(0x23979dd758c6b000LL), + -real(0x27cfd52f91a0e000LL),real(0x52c1297ffdf9000LL), + real(0x1899e61f0915c00LL),reale(10257379,0xdda51a7ac0b27LL), + // C4[4], coeff of eps^17, polynomial in n of order 9 + -reale(5647,0x92962c0679000LL),reale(3064,0xd620df9a18000LL), + -real(0x73b5708edb717000LL),reale(2782,0xf8e2a6bab2000LL), + -real(0x3aa55028ed4d5000LL),real(0x54f5b0489ac0c000LL), + -real(0x3a8372ad6ebf3000LL),real(0x128f31db99de6000LL), + -real(0x1bbb3cddeb8b1000LL),real(0x9c3f5d344ffbb00LL), + reale(10257379,0xdda51a7ac0b27LL), + // C4[4], coeff of eps^16, polynomial in n of order 10 + -reale(12546,0xd0659481f7000LL),reale(2321,0x6f75c5bce2800LL), + -reale(5209,0xc9bfbad2ac000LL),reale(3693,0x4f3d4dd785800LL), + -real(0x59b26230b2e61000LL),reale(2785,0x7ef843b608800LL), + -real(0x4086b5731d656000LL),real(0x3b22d2695822b800LL), + -real(0x3bbf747f663cb000LL),real(0x50e2c41c71ae800LL), + real(0x19182d9cca60700LL),reale(10257379,0xdda51a7ac0b27LL), + // C4[4], coeff of eps^15, polynomial in n of order 11 + -reale(14655,0xa7ccf7b3e3000LL),reale(5703,0xb41e60048e000LL), + -reale(13723,0x6fa2143b1000LL),reale(2794,0x80dd2a6158000LL), + -reale(4434,0xbdbd659d5f000LL),reale(4398,0x1bf890b722000LL), + -real(0x462f1f0759b2d000LL),reale(2504,0xfcfacf17ac000LL), + -real(0x4eb2a95e9a75b000LL),real(0x1bef3eef6f4b6000LL), + -real(0x2d8008caddc29000LL),real(0xdbb189dc4eba300LL), + reale(10257379,0xdda51a7ac0b27LL), + // C4[4], coeff of eps^14, polynomial in n of order 12 + -reale(31110,0xd0a51132f4000LL),reale(76716,0x887753c58b000LL), + -reale(19285,0xcfd85f57f6000LL),reale(3558,0x4fcfd1ab09000LL), + -reale(14554,0xbf2d0ac9f8000LL),reale(3850,0x9631322307000LL), + -reale(3313,0x90f8abbffa000LL),reale(4999,0xf3c6aed085000LL), + -real(0x44308029330fc000LL),real(0x72cd2f325ae83000LL), + -real(0x5cc3eeffca3fe000LL),real(0x2f990ef34001000LL), + real(0xedd65cb262fc00LL),reale(10257379,0xdda51a7ac0b27LL), + // C4[4], coeff of eps^13, polynomial in n of order 13 + reale(109832,0xfe67f2664d000LL),-reale(101414,0x365d952fe4000LL), + -reale(21578,0x2c7dffdd75000LL),reale(81484,0xfb5b01862000LL), + -reale(25828,0x7adf44b697000LL),real(0x527645ab2c368000LL), + -reale(14626,0xa0f5b7bcd9000LL),reale(5668,0x89f8307d6e000LL), + -real(0x7c6deea8217fb000LL),reale(5148,0xb3c77272b4000LL), + -real(0x5ea4f23e05fbd000LL),real(0x33d79ea3e6f7a000LL), + -real(0x512f5a2dc7bdf000LL),real(0x13f171801c8d4d00LL), + reale(10257379,0xdda51a7ac0b27LL), + // C4[4], coeff of eps^12, polynomial in n of order 14 + reale(3290,0xf070eb97f3400LL),-reale(37925,0x14cc0872bb200LL), + reale(108756,0x262a302ba0800LL),-reale(111139,0xba49ef60cbe00LL), + -reale(8978,0x96e5af6312400LL),reale(85061,0xe9667b666b600LL), + -reale(34830,0xb50884d615000LL),-real(0x1ae66991075c5600LL), + -reale(13337,0xd2d72b2557c00LL),reale(8254,0x43d2c57af1e00LL), + -real(0x39646320240ca800LL),reale(4333,0x5a8eb4efe1200LL), + -reale(2317,0x387052d25d400LL),-real(0x4971411b9aa7a00LL), + -real(0x239dc6f1135e6c0LL),reale(10257379,0xdda51a7ac0b27LL), + // C4[4], coeff of eps^11, polynomial in n of order 15 + real(0x22fb18f3d6fc800LL),real(0xc812a63656dd000LL), + reale(2929,0x54e6120875800LL),-reale(35121,0x48d05c62be000LL), + reale(106528,0xc02be4bd3e800LL),-reale(121104,0xca8db31999000LL), + reale(7480,0x3b39caec37800LL),reale(86076,0xd8784a9f2c000LL), + -reale(46728,0xdb6f945bbf800LL),-real(0x1e17ea5787b8f000LL), + -reale(10012,0x630283c6800LL),reale(11072,0xcb500e9316000LL), + -real(0x3d2315ebbfcfd800LL),reale(2196,0x522d08f7fb000LL), + -reale(2582,0x2942c8d084800LL),real(0x1dbc900c41177d80LL), + reale(10257379,0xdda51a7ac0b27LL), + // C4[4], coeff of eps^10, polynomial in n of order 16 + real(0x2367980c018000LL),real(0x717a5d0aad6800LL), + real(0x1c7a6b9a7155000LL),real(0xa7a0b73a0f93800LL), + reale(2540,0xdc02459a12000LL),-reale(31836,0xf2625ff3ef800LL), + reale(102741,0xc61b0075cf000LL),-reale(130713,0xb431635532800LL), + reale(28618,0x913148900c000LL),reale(82224,0x225affaa4a800LL), + -reale(61371,0x71836a73b7000LL),reale(3358,0xd2d9334507800LL), + -reale(4436,0x51714c11fa000LL),reale(12409,0x2e12e0f984800LL), + -reale(3099,0xb59c601f3d000LL),-real(0x185351aa9adbe800LL), + -real(0xfcd867cd32b4e00LL),reale(10257379,0xdda51a7ac0b27LL), + // C4[4], coeff of eps^9, polynomial in n of order 17 + real(0x3b98569230800LL),real(0x954e9f9ae8000LL),real(0x1a387f0ed5f800LL), + real(0x561911aabbb000LL),real(0x163673b1889e800LL), + real(0x870aa0c397ae000LL),reale(2128,0x4412890e0d800LL), + -reale(28018,0x9edd02151f000LL),reale(96862,0x40aaeaffcc800LL), + -reale(138876,0x18d8a92e8c000LL),reale(55003,0xc4365147fb800LL), + reale(69831,0x65a81c2787000LL),-reale(76836,0x9198c23745800LL), + reale(14324,0xf9d757893a000LL),real(0x610a50cc5ec29800LL), + reale(9036,0xddda1962ad000LL),-reale(5866,0x301cbcb97800LL), + real(0x2b3d64f38f7c3a80LL),reale(10257379,0xdda51a7ac0b27LL), + // C4[4], coeff of eps^8, polynomial in n of order 18 + real(0x7c44a1c56800LL),real(0x10e1a40b9f400LL),real(0x2778995e94000LL), + real(0x6511d82348c00LL),real(0x122fbee15d1800LL), + real(0x3d60d47d162400LL),real(0x10572b5ec96f000LL), + real(0x670e5c5512cbc00LL),real(0x6a1969ca184cc800LL), + -reale(23632,0x6fc488059ac00LL),reale(88223,0x601afc7b4a000LL), + -reale(143685,0x3819032af1400LL),reale(86217,0x78ea8eac47800LL), + reale(43622,0x50ec504da8400LL),-reale(86857,0xe4e3b378db000LL), + reale(34767,0x1af4459111c00LL),real(0x470ee9f8c8f42800LL), + -real(0xf0a395fd8dd4c00LL),-real(0x55da5cd875ef3c80LL), + reale(10257379,0xdda51a7ac0b27LL), + // C4[4], coeff of eps^7, polynomial in n of order 19 + real(0x114b06357800LL),real(0x2239f3629000LL),real(0x475e8ebd2800LL), + real(0x9e5523c88000LL),real(0x17aa424dfd800LL),real(0x3e2133dde7000LL), + real(0xb7f09cec78800LL),real(0x280af153ee6000LL), + real(0xb0d866e91e3800LL),real(0x48b6aeda5425000LL), + real(0x4ec10b7f840de800LL),-reale(18693,0xda891ccdbc000LL), + reale(76065,0x2aaa760409800LL),-reale(141961,0xc3f732a21d000LL), + reale(119123,0xd1c84be04800LL),-real(0x7f4b67756e45e000LL), + -reale(76606,0xe7a6860690800LL),reale(56790,0xce45bec021000LL), + -reale(14598,0xc436164715800LL),real(0x23b84843a30d9480LL), + reale(10257379,0xdda51a7ac0b27LL), + // C4[4], coeff of eps^6, polynomial in n of order 20 + real(0x2492f246000LL),real(0x43b68382800LL),real(0x827fc7ff000LL), + real(0x10769dabb800LL),real(0x231371038000LL),real(0x4fad3dfb4800LL), + real(0xc39532c71000LL),real(0x2109cc8eed800LL),real(0x650cdd3e2a000LL), + real(0x16d3054b8e6800LL),real(0x69275cf4ee3000LL), + real(0x2d6bb9aa2a1f800LL),real(0x342dc9db6781c000LL), + -reale(13325,0xb15a42ce7800LL),reale(59725,0xe775950b55000LL), + -reale(128819,0x4abda20fae800LL),reale(144216,0xdf24ba0e000LL), + -reale(65935,0x168961cdb5800LL),-reale(23422,0x325c674239000LL), + reale(39625,0x392517e583800LL),-reale(12954,0x665fd1a892600LL), + reale(10257379,0xdda51a7ac0b27LL), + // C4[4], coeff of eps^5, polynomial in n of order 21 + real(273177999360LL),real(481049600000LL),real(875104847872LL), + real(0x180866df000LL),real(0x2f4b74a1800LL),real(0x61abf5b8000LL), + real(0xd562fc0e800LL),real(0x1f2598191000LL),real(0x4ed8f85ab800LL), + real(0xdc91252ca000LL),real(0x2bd44913d8800LL),real(0xa584ade1c3000LL), + real(0x322090df0f5800LL),real(0x16f6266186dc000LL), + real(0x1c472a543df62800LL),-reale(7859,0x7aaf0fd58b000LL), + reale(39234,0x9eeb23497f800LL),-reale(98180,0xb70c1a0b12000LL), + reale(140051,0xe6fe7071ac800LL),-reale(115827,0x9358bc0159000LL), + reale(51817,0x997f46a249800LL),-reale(9715,0xccc7dd3e6dc80LL), + reale(10257379,0xdda51a7ac0b27LL), + // C4[4], coeff of eps^4, polynomial in n of order 22 + real(18103127040LL),real(30658521600LL),real(53362944000LL), + real(95756838400LL),real(177805329408LL),real(343155696128LL), + real(692078714880LL),real(0x155e2e7de00LL),real(0x30194583c00LL), + real(0x741fc16da00LL),real(0x131155285800LL),real(0x379d38605600LL), + real(0xb96166967400LL),real(0x2e2dfa3db5200LL),real(0xee14dc9ed9000LL), + real(0x752e44962ece00LL),real(0x9cf0406db58ac00LL), + -reale(3007,0xfcd2e16ce3600LL),reale(16844,0xbb0354c82c800LL), + -reale(48007,0x7b6318074ba00LL),reale(77726,0x663ee9f36e400LL), + -reale(64771,0xffdf184adbe00LL),reale(21050,0xe65bb4b1eddc0LL), + reale(10257379,0xdda51a7ac0b27LL), + // C4[5], coeff of eps^26, polynomial in n of order 0 + 356096,real(98232628725LL), + // C4[5], coeff of eps^25, polynomial in n of order 1 + real(19006687232LL),real(5473719680LL),real(0x1580fd4afdbe65LL), + // C4[5], coeff of eps^24, polynomial in n of order 2 + real(91538057LL<<15),-real(0x378568c4000LL),real(0x16cc31e2a00LL), + real(0x4c6f2137745e091LL), + // C4[5], coeff of eps^23, polynomial in n of order 3 + real(0xef2f223e3800LL),-real(0x110fb2e7bf000LL),real(0x282bb4606800LL), + real(0xbe30d7a6780LL),reale(2828,0xfcd03d1974f5LL), + // C4[5], coeff of eps^22, polynomial in n of order 4 + real(0x5e4a1598000LL),-real(0x48b6e92a000LL),real(97904939LL<<14), + -real(0x20e8326e000LL),real(850763001088LL),real(0x2081a7235aaf593LL), + // C4[5], coeff of eps^21, polynomial in n of order 5 + real(0x40db2f49b455f800LL),-real(0x1e99bb32c4c22000LL), + real(0x173ba0294630c800LL),-real(0x194707e3169c1000LL), + real(0x2d83efe695c9800LL),real(0xdf3e0617af3080LL), + reale(12536797,0x9d1f205d24685LL), + // C4[5], coeff of eps^20, polynomial in n of order 6 + real(0x216feaa994ce0000LL),-real(0xab5f967e8690000LL), + real(0x47922226ed5LL<<18),-real(0xb74a91dab5f0000LL), + real(0x3c54ceff81a0000LL),-real(0x5d7cb98f1a50000LL), + real(0x1f9a69370b20800LL),reale(4178932,0x89b50ac9b6cd7LL), + // C4[5], coeff of eps^19, polynomial in n of order 7 + real(0x737c719d74a11000LL),-real(0x33cb00709b02e000LL), + real(0x64aa4f647e063000LL),-real(0x22d04f5347fb4000LL), + real(0x244213a9e6215000LL),-real(0x2372b83384fba000LL), + real(0x29c5a12d1767000LL),real(0xd64e2b028e9d00LL), + reale(12536797,0x9d1f205d24685LL), + // C4[5], coeff of eps^18, polynomial in n of order 8 + real(0x4d6c482dac2a0000LL),-reale(2329,0xb1fe2723dc000LL), + reale(2244,0xda129de1b8000LL),-real(0x25b9c94d1ec14000LL), + real(0x5915813997350000LL),-real(0x2b18411354f8c000LL), + real(0x1038d20e1fbe8000LL),-real(0x1a9977b2ea9c4000LL), + real(0x7df995f732ef600LL),reale(12536797,0x9d1f205d24685LL), + // C4[5], coeff of eps^17, polynomial in n of order 9 + real(0x514388ef27d31000LL),-reale(6020,0x2be450c918000LL), + real(0x6fa66bdc836df000LL),-real(0x67912be26fab2000LL), + reale(2539,0xf65fb2006d000LL),-real(0x237e1033f4d8c000LL), + real(0x3efb5ba75c79b000LL),-real(0x32b52fd83cbe6000LL), + real(0x17d40e2c1a29000LL),real(0x7dfd16a9c2e300LL), + reale(12536797,0x9d1f205d24685LL), + // C4[5], coeff of eps^16, polynomial in n of order 10 + reale(12470,0xf777d5cb70000LL),-reale(8994,0x34ff96fbd8000LL), + real(0x8b5e07446e3LL<<18),-reale(5684,0xa351b76ba8000LL), + reale(2676,0xe4b7624210000LL),-real(0x3b4e8fe27b2f8000LL), + reale(2525,0xe113384060000LL),-real(0x317b33e66b8c8000LL), + real(0x1afebbc488cb0000LL),-real(0x2abc78cdb6418000LL), + real(0xab0b32cc6da3c00LL),reale(12536797,0x9d1f205d24685LL), + // C4[5], coeff of eps^15, polynomial in n of order 11 + reale(45753,0x27312c684b000LL),real(0x6b25908081df2000LL), + reale(10080,0x3e3c4e94e9000LL),-reale(11483,0x3052990658000LL), + real(0x186dcc47df2a7000LL),-reale(4654,0xe97b33c9a2000LL), + reale(3765,0x192eb8a145000LL),-real(0x1ea7f016e242c000LL), + real(0x7c08a9e80a083000LL),-real(0x48a61c5124e36000LL), + -real(0x1ab8464a6fdf000LL),-real(0xc3b3128c53f500LL), + reale(12536797,0x9d1f205d24685LL), + // C4[5], coeff of eps^14, polynomial in n of order 12 + -reale(29853,0xf97fbea090000LL),-reale(72661,0xb2e53c820c000LL), + reale(55735,0xd505afdac8000LL),-real(0x19eb9cd373704000LL), + reale(6447,8655275741LL<<17),-reale(13735,0x934f51ea3c000LL), + real(0x503c7c1e17a78000LL),-reale(2910,0x8f0f066334000LL), + reale(4611,0xa07ae6cfd0000LL),-real(0x28ec95124696c000LL), + real(0x386dc5f3bf428000LL),-real(0x49a3cdb95c464000LL), + real(0xec86977ad08e600LL),reale(12536797,0x9d1f205d24685LL), + // C4[5], coeff of eps^13, polynomial in n of order 13 + -reale(77964,0x27205a1bd000LL),reale(116550,0x911cc360c4000LL), + -reale(45605,0xab8dec641b000LL),-reale(66195,0xc9de18da12000LL), + reale(66624,0xae21593727000LL),-reale(5576,0x36f63ac28000LL), + real(0x6f2264aae1649000LL),-reale(14832,0x2c940b773e000LL), + reale(3661,0xe0e147ff8b000LL),-real(0x37687d20b9d14000LL), + reale(4430,0xd2ef37d92d000LL),-real(0x61330ed553f6a000LL), + -real(0x8fc7d2821691000LL),-real(0x4de8f81581e0b00LL), + reale(12536797,0x9d1f205d24685LL), + // C4[5], coeff of eps^12, polynomial in n of order 14 + -real(0x520b481798460000LL),reale(18997,713316873LL<<16), + -reale(73060,0xebcc7589c0000LL),reale(119587,0x641c11f8f0000LL), + -reale(63450,0xfff4f2db20000LL),-reale(54596,0x54a14049b0000LL), + reale(77203,5136366291LL<<19),-reale(15161,0x669695c550000LL), + -reale(2898,7333080783LL<<17),-reale(13401,0xbb1dc317f0000LL), + reale(7364,7522322675LL<<18),real(0xcbde6dd32070000LL), + reale(2498,0xb270ac8f60000LL),-reale(2207,0xe5e147ba30000LL), + real(0x146e5a4ec1af3800LL),reale(12536797,0x9d1f205d24685LL), + // C4[5], coeff of eps^11, polynomial in n of order 15 + -real(0x8e2d12e55cc800LL),-real(0x3c744345ee05000LL), + -real(0x436e3347c2885800LL),reale(16354,0x603aee4aee000LL), + -reale(66895,0x3561b9526e800LL),reale(120525,0x7fafccca1000LL), + -reale(82888,0x6ce782c3a7800LL),-reale(36026,0xb730ca850c000LL), + reale(84916,0xe33bbac3af800LL),-reale(30329,0x9a1820a639000LL), + -reale(5003,0x6724146c89800LL),-reale(8175,0xa51f341306000LL), + reale(10601,0xdf58b3eb8d800LL),-real(0x51534d8656793000LL), + -real(0x13f74fe07242b800LL),-real(0x1338322158bf8680LL), + reale(12536797,0x9d1f205d24685LL), + // C4[5], coeff of eps^10, polynomial in n of order 16 + -real(0x5e9d97de20000LL),-real(0x15f51b48a5a000LL), + -real(0x679f3a6a83c000LL),-real(0x2da38dbb53ee000LL), + -real(0x351287a208998000LL),reale(13549,0xfdc5cc829e000LL), + -reale(59298,0x35ebc8a374000LL),reale(118312,0x8f7a13080a000LL), + -reale(102644,0xbe9581710000LL),-reale(8663,0x3283e8b4ea000LL), + reale(85056,0xa0c3d6fa54000LL),-reale(50541,0x58fecea57e000LL), + real(0x9e0314066f78000LL),-real(0x56026edfbaf2000LL), + reale(9162,0x6ada71271c000LL),-reale(4514,0x3f8f2be686000LL), + real(0x19aa7dbc9bd2b100LL),reale(12536797,0x9d1f205d24685LL), + // C4[5], coeff of eps^9, polynomial in n of order 17 + -real(0x689b7f794800LL),-real(0x12aa316a68000LL), + -real(0x3c5fe03b7b800LL),-real(0xe70662316b000LL), + -real(0x468257445d2800LL),-real(0x204dea1c904e000LL), + -real(0x275c24b79c179800LL),reale(10640,0x725f868a0f000LL), + -reale(50163,0x8367062950800LL),reale(111598,0xa3db986ecc000LL), + -reale(120105,0x1af4e4a837800LL),reale(28289,0xbddfd64f09000LL), + reale(70122,0x41f96206f1800LL),-reale(70104,0xcd1cf1241a000LL), + reale(17631,0x83f469b94a800LL),reale(3507,0xd4dd7e683000LL), + real(0x234fa818af3f3800LL),-real(0x5217ce807fb7e980LL), + reale(12536797,0x9d1f205d24685LL), + // C4[5], coeff of eps^8, polynomial in n of order 18 + -real(0x8baa3048000LL),-real(0x155e3991c000LL),-real(237891401LL<<18), + -real(0xa66484064000LL),-real(0x22acb24838000LL), + -real(0x89475b1e6c000LL),-real(0x2b8ce25f7b0000LL), + -real(0x14dd31b8f8b4000LL),-real(0x1acbb07dd4628000LL), + reale(7723,0xe6c1cd6b44000LL),-reale(39540,0xb1d09a9920000LL), + reale(98832,0x70f12b47fc000LL),-reale(130553,0x474c4a5618000LL), + reale(72091,0x9d4697d7f4000LL),reale(31173,0xcb977f1d70000LL), + -reale(72484,0xa77099aa54000LL),reale(42073,0x76abc75bf8000LL), + -reale(8983,0xdb34fa045c000LL),real(0x7851cafec6ea600LL), + reale(12536797,0x9d1f205d24685LL), + // C4[5], coeff of eps^7, polynomial in n of order 19 + -real(808445556736LL),-real(0x19fd8659000LL),-real(0x3ce45316800LL), + -real(0x98e89f08000LL),-real(0x1a16c5239800LL),-real(0x4ef4224b7000LL), + -real(0x11089a8d8c800LL),-real(0x461e8219c6000LL), + -real(0x1740d89936f800LL),-real(0xbb97ef56095000LL), + -real(0xffd8608f0242800LL),reale(4956,0x2ae7ba647c000LL), + -reale(27803,0x8886c0e865800LL),reale(78703,0x691d56f30d000LL), + -reale(126581,0xb2ac252438800LL),reale(111405,0x65dae188be000LL), + -reale(33040,0xc82f8ec41b800LL),-reale(31122,0xa51c18fcd1000LL), + reale(33849,0xe315529991800LL),-reale(10096,0xcfcaaeb453f80LL), + reale(12536797,0x9d1f205d24685LL), + // C4[5], coeff of eps^6, polynomial in n of order 20 + -real(57693732864LL),-real(118378242048LL),-real(254261280768LL), + -real(575562375168LL),-real(10565709LL<<17),-real(0x341c17b2000LL), + -real(0x92ee7ecc000LL),-real(0x1ccf17876000LL),-real(0x6786d9e38000LL), + -real(0x1bdf19e19a000LL),-real(0x9bb8377424000LL), + -real(0x5352681ef5e000LL),-real(0x79ce0dfd0cd0000LL), + reale(2563,0x29027cc1fe000LL),-reale(15917,0xface8c747c000LL), + reale(51375,0x61bf7d963a000LL),-reale(99436,0x390f87b768000LL), + reale(119998,0xa6d5e6f116000LL),-reale(88555,0x279c7be1d4000LL), + reale(36577,0x210e8c3652000LL),-reale(6477,0x332fe8d449300LL), + reale(12536797,0x9d1f205d24685LL), + // C4[5], coeff of eps^5, polynomial in n of order 21 + -real(2537256960LL),-real(4922368000LL),-real(9913649152LL), + -real(20825468928LL),-real(45893163008LL),-real(3260719LL<<15), + -real(265153996800LL),-real(709434249216LL),-real(0x1e3bc54b800LL), + -real(0x62f2289a000LL),-real(0x174e12bf8800LL),-real(0x69ee83c3b000LL), + -real(0x2753bfa335800LL),-real(0x1693a2298bc000LL), + -real(0x23ce232de3a2800LL),real(0x33ca29bdcdd43000LL), + -reale(5754,0x693a6155df800LL),reale(21176,0x3b8f28c122000LL), + -reale(47646,0x86021bb28c800LL),reale(67058,0x11f0010e41000LL), + -reale(51817,0x997f46a249800LL),reale(16192,0xfff7c612b6f80LL), + reale(12536797,0x9d1f205d24685LL), + // C4[6], coeff of eps^26, polynomial in n of order 0 + real(71266816),real(0x75209f8d91abLL), + // C4[6], coeff of eps^25, polynomial in n of order 1 + -real(61697<<14),real(365122560),real(0x64173937d043LL), + // C4[6], coeff of eps^24, polynomial in n of order 2 + -real(0x10389da9c000LL),real(0x19e75ef2000LL),real(558875851776LL), + real(0xd767bab38dc330dLL), + // C4[6], coeff of eps^23, polynomial in n of order 3 + -real(0x142d81502c000LL),real(0x6dee9f4b8000LL),-real(0xae181cf64000LL), + real(0x39153b46b400LL),reale(3342,0x41381bc9272f3LL), + // C4[6], coeff of eps^22, polynomial in n of order 4 + -real(0x13480fca8c000LL),real(0x16106a2c37000LL), + -real(0x1502d2e846000LL),real(0x16180c1bd000LL),real(0x74238242a00LL), + reale(3342,0x41381bc9272f3LL), + // C4[6], coeff of eps^21, polynomial in n of order 5 + -real(0x1c0b06f2aed0000LL),real(0x44926ab731c0000LL), + -real(0x2031c71e85b0000LL),real(0xca25cdaf0e0000LL), + -real(0x14c7d62b6490000LL),real(0x61052e04125000LL), + reale(1139708,0xdfbd02f131dafLL), + // C4[6], coeff of eps^20, polynomial in n of order 6 + -real(0x3c147e5183b90000LL),real(0x5c8a793ab7a08000LL), + -real(0xa71b84c4013LL<<17),real(0x26583d412b938000LL), + -real(0x1ec1409e52930000LL),real(0xd82d55b5068000LL), + real(0x4a1c5add9a3000LL),reale(14816215,0x5c99263f881e3LL), + // C4[6], coeff of eps^19, polynomial in n of order 7 + -reale(2884,0x97776797f0000LL),real(0x5dcb94a5bbaa0000LL), + -real(0x2147754a866d0000LL),real(0x59b9e153ee1c0000LL), + -real(0x1d3317b06cdb0000LL),real(0xfd67f86b28e0000LL), + -real(0x193b89a255c90000LL),real(0x662541f54195000LL), + reale(14816215,0x5c99263f881e3LL), + // C4[6], coeff of eps^18, polynomial in n of order 8 + -reale(5404,0x5e66e1f930000LL),real(0x194c5bcfa9f36000LL), + -reale(2201,0x4f230944e4000LL),reale(2053,0x73a8845e02000LL), + -real(0x127ebba7aac98000LL),real(0x433c97a5782ce000LL), + -real(0x29997437ffc4c000LL),-real(0xb36408ece66000LL), + -real(0x4eb946c9b6ac00LL),reale(14816215,0x5c99263f881e3LL), + // C4[6], coeff of eps^17, polynomial in n of order 9 + -reale(2829,0x5744c85a98000LL),real(0x53fda6bff9540000LL), + -reale(5946,0xc179df32e8000LL),real(0x424987c8bd3f0000LL), + -real(0x4d6fba1e72f38000LL),reale(2362,0x7a9b39aaa0000LL), + -real(0x1a7dd6520d788000LL),real(0x1ca5a49549150000LL), + -real(0x279b8ad82b3d8000LL),real(0x8624b660e613800LL), + reale(14816215,0x5c99263f881e3LL), + // C4[6], coeff of eps^16, polynomial in n of order 10 + reale(3052,0x1cc54fce28000LL),reale(15175,0x33b0e2aba4000LL), + -reale(5744,0xc5440d7e0000LL),-real(0xd3fdde9c4364000LL), + -reale(5627,0x42b2a45de8000LL),reale(2296,0xc920e17994000LL), + -real(0x15ef23de88bf0000LL),reale(2060,0x9b7c8a7a8c000LL), + -real(0x3634e9b2229f8000LL),-real(0x3eaac877287c000LL), + -real(0x1ee323a1ca0c800LL),reale(14816215,0x5c99263f881e3LL), + // C4[6], coeff of eps^15, polynomial in n of order 11 + -reale(77304,0xeb4d9089c8000LL),reale(21636,0x8867f71d90000LL), + reale(6061,8670344157LL<<15),reale(12960,6074462725LL<<18), + -reale(9403,0x25b985468000LL),-real(0x35c5d916ffb10000LL), + -reale(4114,0x3d13bbebb8000LL),reale(3690,0x5a8c0420a0000LL), + -real(0x7db1fc00af08000LL),real(0x3ee56918f4c50000LL), + -real(0x41d90b24a2658000LL),real(0xb0f65a4ddefb800LL), + reale(14816215,0x5c99263f881e3LL), + // C4[6], coeff of eps^14, polynomial in n of order 12 + reale(84445,0xef949ea0f8000LL),reale(19627,0xf0e541fbce000LL), + -reale(80833,0x5f741237dc000LL),reale(34575,0x1644d05d7a000LL), + reale(6828,0x4cfbe5cb50000LL),reale(8288,0x561945cd26000LL), + -reale(12838,0x6d3e328184000LL),real(0x15c5608ef0ed2000LL), + -real(0x653ba29de4a58000LL),reale(4217,0x4b5d86267e000LL), + -real(0x3b46409683b2c000LL),-real(0x974d654f27d6000LL), + -real(0x674dea252558c00LL),reale(14816215,0x5c99263f881e3LL), + // C4[6], coeff of eps^13, polynomial in n of order 13 + reale(45373,0x376f121df0000LL),-reale(98871,0xe30dbcdfc0000LL), + reale(96522,0x4174515a90000LL),-real(0x2d5b0f36d6d20000LL), + -reale(79483,0x53270530d0000LL),reale(50297,8337588523LL<<19), + reale(3071,0x5d816f2bd0000LL),real(0x5cfb30543d820000LL), + -reale(14132,0x4c1b1cdf90000LL),reale(3907,0xfc9bf30ac0000LL), + real(0x1e5e0fff75d10000LL),reale(2700,0x7f35ecdd60000LL), + -real(0x74992b46f6e50000LL),real(0xe2f417f6bbc1000LL), + reale(14816215,0x5c99263f881e3LL), + // C4[6], coeff of eps^12, polynomial in n of order 14 + real(0x1b3ddeae39bf0000LL),-reale(7839,0x62697a1358000LL), + reale(39400,0x7dae3b2360000LL),-reale(93477,0x2dd7a51de8000LL), + reale(106917,0x5f76290ad0000LL),-reale(25706,0x4975ab7078000LL), + -reale(70221,0xf8d5dabdc0000LL),reale(66679,0x434a03a4f8000LL), + -reale(7926,0xc17b4a4650000LL),-reale(5104,0x4c6b9c2d98000LL), + -reale(10825,0x972fc79ee0000LL),reale(8339,0xae0935c7d8000LL), + -real(0xb5e35652d770000LL),-real(0xb97cf166cab8000LL), + -real(0x1484ac4370939000LL),reale(14816215,0x5c99263f881e3LL), + // C4[6], coeff of eps^11, polynomial in n of order 15 + real(0x1da928c9710000LL),real(0xef3463c3520000LL), + real(0x1433e03669f30000LL),-reale(6121,0xc895edf4c0000LL), + reale(32842,0x2af7b46f50000LL),-reale(85281,0xda67593ea0000LL), + reale(113905,0x4294ec3770000LL),-reale(54341,1789231857LL<<19), + -reale(49473,0x80d6dfd870000LL),reale(78594,0x71ba158da0000LL), + -reale(27684,0xd5e2e99050000LL),-reale(5831,3589595121LL<<18), + -reale(2437,0x3d76dec030000LL),reale(8713,0x93ccba19e0000LL), + -reale(3467,0xfccc93810000LL),real(0xf2bb44edf33d000LL), + reale(14816215,0x5c99263f881e3LL), + // C4[6], coeff of eps^10, polynomial in n of order 16 + real(0xcab3dac70000LL),real(0x3665759289000LL),real(0x12ce11eabe2000LL), + real(0x9df70180dbb000LL),real(0xdfd754eb8954000LL), + -reale(4487,0x5dd2369613000LL),reale(25849,0xff24cd52c6000LL), + -reale(73908,0x17b3db62e1000LL),reale(115119,0x8d3c9a9638000LL), + -reale(83691,0x41fe3e02af000LL),-reale(14375,0xada6f2de56000LL), + reale(76590,0xeb60670083000LL),-reale(52128,0x9a91d83ce4000LL), + reale(7010,0x5a128dfcb5000LL),reale(3866,0xf6d75c088e000LL), + real(0x469f50315e7e7000LL),-real(0x4bbe9f188165a200LL), + reale(14816215,0x5c99263f881e3LL), + // C4[6], coeff of eps^9, polynomial in n of order 17 + real(0x8ddfb274000LL),real(120826333LL<<18),real(0x6b145a40c000LL), + real(0x1dc5136a58000LL),real(0xab5ca60ba4000LL),real(0x5e28748a970000LL), + real(0x8cad0403953c000LL),-reale(3003,0xaeb1521f78000LL), + reale(18707,0x350991ecd4000LL),-reale(59284,0x6845654460000LL), + reale(107702,0xd776bbe6c000LL),-reale(107579,0xe340531948000LL), + reale(33813,0xa464b8b604000LL),reale(48035,0x81a4fa0dd0000LL), + -reale(64047,0xa4265c8064000LL),reale(31225,0xe027c1dce8000LL), + -reale(5635,0xd5d68038cc000LL),-real(0x50368754849c400LL), + reale(14816215,0x5c99263f881e3LL), + // C4[6], coeff of eps^8, polynomial in n of order 18 + real(490704814080LL),real(0x13aa0f5a000LL),real(31022013LL<<17), + real(0xc68497e6000LL),real(0x2fcbb8aac000LL),real(0xdd4302e72000LL), + real(0x534405e9b8000LL),real(0x30298b6eefe000LL), + real(0x4c5dcf34c0c4000LL),-real(0x6d574da684a76000LL), + reale(11873,5016286141LL<<16),-reale(42009,0x509c0961ea000LL), + reale(89073,0x6259ee06dc000LL),-reale(115683,0xae64a27b5e000LL), + reale(82889,0x8f2a67cde8000LL),-reale(11935,0xb1dc537ad2000LL), + -reale(33312,0xcf05a2430c000LL),reale(28876,0xae4eda7bba000LL), + -reale(8101,0x83645851a5400LL),reale(14816215,0x5c99263f881e3LL), + // C4[6], coeff of eps^7, polynomial in n of order 19 + real(7458340864LL),real(560703LL<<15),real(48303816704LL), + real(522951LL<<18),real(426386014208LL),real(45283889LL<<15), + real(0x56a252ac000LL),real(440127317LL<<16),real(0xa648bd1f4000LL), + real(0x65fb114118000LL),real(0xacffeca0b3c000LL), + -real(0x860da206139LL<<17),real(0x7d0a1c0732284000LL), + -reale(7961,0x1b3e7a1f58000LL),reale(19682,0xa4af1c3bcc000LL), + -reale(31917,0xccc8ef8390000LL),reale(34094,0x3798b7b14000LL), + -reale(23101,0x583f152fc8000LL),reale(8983,0xdb34fa045c000LL), + -real(0x5f40c0b45d798c00LL),reale(4938738,0x74330cbfd80a1LL), + // C4[6], coeff of eps^6, polynomial in n of order 20 + real(651542528),real(1480134656),real(3538968576LL),real(8971595776LL), + real(371371LL<<16),real(71493373952LL),real(230978592768LL), + real(838422294528LL),real(0x334e2804000LL),real(0x106060339000LL), + real(0x6e2b415ae000LL),real(0x484c62e3a3000LL),real(0x848c0aa1558000LL), + -real(0xe0b56a0582f3000LL),real(0x745df25523d02000LL), + -reale(8378,0x6c27f21289000LL),reale(23938,0x5996b3a2ac000LL), + -reale(45881,0xd660d84d1f000LL),reale(58395,0x10d8591c56000LL), + -reale(42673,0x513ba394b5000LL),reale(12954,0x665fd1a892600LL), + reale(14816215,0x5c99263f881e3LL), + // C4[7], coeff of eps^26, polynomial in n of order 0 + real(9763<<15),real(0x75209f8d91abLL), + // C4[7], coeff of eps^25, polynomial in n of order 1 + real(239317LL<<16),real(5250319360LL),real(0x4082f7e0f93b2fLL), + // C4[7], coeff of eps^24, polynomial in n of order 2 + real(179518703LL<<19),-real(591371495LL<<18),real(0x28b139bd9800LL), + reale(3231,0x13f0854e6fdc3LL), + // C4[7], coeff of eps^23, polynomial in n of order 3 + real(0x2cef3d4baf0000LL),-real(77130417375LL<<17),real(0xef66e7c50000LL), + real(0x5431e6572400LL),reale(119549,0xe1c344562ad2fLL), + // C4[7], coeff of eps^22, polynomial in n of order 4 + real(217227301LL<<22),-real(289844049LL<<20),real(78161061LL<<21), + -real(250072603LL<<20),real(0x3ccfc393c000LL), + reale(3856,0x72a333c0b70f1LL), + // C4[7], coeff of eps^21, polynomial in n of order 5 + real(0x4e0ae513ee240000LL),-real(827903427791LL<<20), + real(0xa247f543e5fLL<<18),-real(0x3412b66b53fLL<<19), + -real(88149449003LL<<18),-real(0x22c21c78f4d000LL), + reale(17095633,0x1c132c21ebd41LL), + // C4[7], coeff of eps^20, polynomial in n of order 6 + real(0x17d653fb3b3LL<<21),-real(0x28623ac8329LL<<20), + real(0x157258d15a9LL<<22),-real(0x11bb996f2dfLL<<20), + real(568501848145LL<<21),-real(0x17b5bd88f85LL<<20), + real(0x53401a2130be000LL),reale(17095633,0x1c132c21ebd41LL), + // C4[7], coeff of eps^19, polynomial in n of order 7 + -real(0x83a0cdc49940000LL),-reale(2692,4590415189LL<<19), + real(0x5a9e6c539a840000LL),-real(834402440151LL<<20), + real(0x4606e5f7741c0000LL),-real(0x420b2360847LL<<19), + -real(530800397043LL<<18),-real(0xe57fab5d571000LL), + reale(17095633,0x1c132c21ebd41LL), + // C4[7], coeff of eps^18, polynomial in n of order 8 + reale(3472,126556531LL<<23),-reale(5076,3517313787LL<<20), + -real(76794078375LL<<21),-real(0x6a9c1a13021LL<<20), + reale(2051,1043338611LL<<22),-real(704701202247LL<<20), + real(0xfa27346673LL<<21),-real(0x245598aac6dLL<<20), + real(0x69deaea556c4000LL),reale(17095633,0x1c132c21ebd41LL), + // C4[7], coeff of eps^17, polynomial in n of order 9 + reale(15000,0xe6601a91a0000LL),-real(0x261369ca72fLL<<20), + real(0x42e9870754860000LL),-reale(5748,0xcbf4457740000LL), + real(0x3d07c1e90b320000LL),-real(0x3f02d96efefLL<<19), + real(0x7fb986a3c79e0000LL),-real(0x995e2453d1fLL<<18), + -real(0x4ae4d5f0bb60000LL),-real(0x2b86668e596d800LL), + reale(17095633,0x1c132c21ebd41LL), + // C4[7], coeff of eps^16, polynomial in n of order 10 + -real(0x73f9d78b0d9LL<<20),real(0x9cf538ea065LL<<19), + reale(15740,149203411LL<<22),-reale(4248,1728572757LL<<19), + -real(0x407b444d4cfLL<<20),-reale(4968,2121468799LL<<19), + reale(2638,499248115LL<<21),real(0x88c04a730380000LL), + real(0x44a3b895a7bLL<<20),-real(0x74a26c7b8a3LL<<19), + real(0x855f1c455087000LL),reale(17095633,0x1c132c21ebd41LL), + // C4[7], coeff of eps^15, polynomial in n of order 11 + reale(61154,0xdd701642e0000LL),-reale(66911,9396541691LL<<18), + reale(7800,0xcd3506c5a0000LL),reale(6879,1489841009LL<<20), + reale(13340,0xebc72e5460000LL),-reale(8995,2037240317LL<<18), + -real(0x58226c8c268e0000LL),-reale(2527,381291855LL<<19), + reale(3789,0xabee5235e0000LL),-real(0x7b29f7fc67fLL<<18), + -real(0x7ff214bf2760000LL),-real(0x75bce0e31735800LL), + reale(17095633,0x1c132c21ebd41LL), + // C4[7], coeff of eps^14, polynomial in n of order 12 + -reale(101656,596927171LL<<22),reale(53043,574431381LL<<20), + reale(45405,240861115LL<<21),-reale(76255,2673908009LL<<20), + reale(23050,192030143LL<<23),reale(9407,3846737689LL<<20), + reale(7022,1974859325LL<<21),-reale(12738,252856997LL<<20), + real(0x137e788e9bfLL<<22),real(0x118e235259dLL<<20), + reale(2782,635761855LL<<21),-real(0x61e77094421LL<<20), + real(0x9e768b34c754000LL),reale(17095633,0x1c132c21ebd41LL), + // C4[7], coeff of eps^13, polynomial in n of order 13 + -reale(21345,0xc6c0a8cac0000LL),reale(63537,3528773151LL<<20), + -reale(101990,1331648317LL<<18),reale(73206,6215106713LL<<19), + reale(21832,587136209LL<<18),-reale(78785,930736779LL<<21), + reale(42984,0xe415720fc0000LL),reale(4706,912279695LL<<19), + -real(0x6fb64418f6cc0000LL),-reale(11952,1697246539LL<<20), + reale(6137,3764705851LL<<18),real(0x39d9405b105LL<<19), + -real(779141568695LL<<18),-real(0x14a7906c9982d000LL), + reale(17095633,0x1c132c21ebd41LL), + // C4[7], coeff of eps^12, polynomial in n of order 14 + -real(254469508501LL<<21),reale(2615,141135587LL<<20), + -reale(16754,480949921LL<<22),reale(54113,1487459045LL<<20), + -reale(98062,1801972559LL<<21),reale(91200,2801526327LL<<20), + -reale(9603,108846763LL<<23),-reale(69011,498726663LL<<20), + reale(62980,2002280887LL<<21),-reale(11145,4221789365LL<<20), + -reale(7195,1009585291LL<<22),-reale(4457,3739558579LL<<20), + reale(7974,18407933LL<<21),-reale(2668,664195297LL<<20), + real(0x8b8039451326000LL),reale(17095633,0x1c132c21ebd41LL), + // C4[7], coeff of eps^11, polynomial in n of order 15 + -real(5353180065LL<<18),-real(25442595013LL<<19), + -real(0x4cec268118c0000LL),real(0x702c4e5b497LL<<20), + -reale(12304,5733646405LL<<18),reale(43346,2744696673LL<<19), + -reale(88871,6285139975LL<<18),reale(103468,468195229LL<<21), + -reale(46365,0xbad7731a40000LL),-reale(41349,1257587961LL<<19), + reale(72365,0x9597fe7540000LL),-reale(36580,2571848483LL<<20), + real(0xc0cfef1c9f3LL<<18),reale(3419,2944620333LL<<19), + real(0x5d00262e0cc40000LL),-real(0x44e0e913b4a79000LL), + reale(17095633,0x1c132c21ebd41LL), + // C4[7], coeff of eps^10, polynomial in n of order 16 + -real(1386231LL<<24),-real(109742265LL<<20),-real(354075457LL<<21), + -real(7044729419LL<<20),-real(48190848741LL<<22), + real(0x4592e53c723LL<<20),-reale(8214,1225367123LL<<21), + reale(31749,3931639185LL<<20),-reale(73861,194985719LL<<23), + reale(105371,3738827519LL<<20),-reale(81325,759307621LL<<21), + reale(5533,2607378797LL<<20),reale(54935,128097033LL<<22), + -reale(54849,213867813LL<<20),reale(23331,2117756809LL<<21), + -reale(3571,955076279LL<<20),-real(0xa766ab1fb094000LL), + reale(17095633,0x1c132c21ebd41LL), + // C4[7], coeff of eps^9, polynomial in n of order 17 + -real(9271959LL<<16),-real(2137131LL<<20),-real(0x8adb5490000LL), + -real(374926717LL<<17),-real(5060508635LL<<16),-real(0xc549443040000LL), + -real(0x1658a10fa0d0000LL),real(0x250f39cc17720000LL), + -reale(4742,48259999LL<<16),reale(20239,6692003029LL<<19), + -reale(53602,0x26a4a24510000LL),reale(92339,8168900207LL<<17), + -reale(101236,0x6fb3cfe30000LL),reale(59785,2334542613LL<<18), + real(0x5c1211516deb0000LL),-reale(32944,0x86c05c8b60000LL), + reale(24775,0x5aee521590000LL),-reale(6657,0xade066fea8c00LL), + reale(17095633,0x1c132c21ebd41LL), + // C4[7], coeff of eps^8, polynomial in n of order 18 + -real(31473LL<<19),-real(194623LL<<18),-real(41393LL<<22), + -real(2533665LL<<18),-real(5617311LL<<19),-real(60523827LL<<18), + -real(107394483LL<<20),-real(4758923477LL<<18),-real(73625727245LL<<19), + real(0xf5289483e640000LL),-reale(2141,878914353LL<<21), + reale(10163,0xf2a381edc0000LL),-reale(30731,8395289531LL<<19), + reale(63101,0xdb7b98c940000LL),-reale(89756,3102076305LL<<20), + reale(87316,6648120707LL<<18),-reale(55353,8132528169LL<<19), + reale(20534,9081852529LL<<18),-reale(3368,0xf233ddc1a2800LL), + reale(17095633,0x1c132c21ebd41LL), + // C4[7], coeff of eps^7, polynomial in n of order 19 + -real(4693<<16),-real(6435<<17),-real(37895LL<<16),-real(7579LL<<20), + -real(428505LL<<16),-real(854413LL<<17),-real(7933835LL<<16), + -real(11246865LL<<18),-real(338155741LL<<16),-real(0xee3402ee0000LL), + -real(0x1efc2a618f0000LL),real(517531990885LL<<19), + -real(0x243e4ae81d610000LL),reale(3081,0xb7f72703e0000LL), + -reale(10639,0x4442fa8130000LL),reale(25534,4122358181LL<<18), + -reale(43524,0x45cc2f5650000LL),reale(51336,0x52534b86a0000LL), + -reale(35935,0x6cd3e81170000LL),reale(10668,0x544ee8e52d400LL), + reale(17095633,0x1c132c21ebd41LL), + // C4[8], coeff of eps^26, polynomial in n of order 0 + real(1703<<17),real(0x7c72a9866ac5bLL), + // C4[8], coeff of eps^25, polynomial in n of order 1 + -real(177229LL<<20),real(727155LL<<16),real(0x491cf6cbc520f1LL), + // C4[8], coeff of eps^24, polynomial in n of order 2 + -real(9929683361LL<<18),-real(175790329LL<<17),-real(0x88fc23ec000LL), + reale(40280,0xc561288d94a7fLL), + // C4[8], coeff of eps^23, polynomial in n of order 3 + -real(11862711753LL<<19),real(5010641713LL<<20),-real(14709027619LL<<19), + real(0x62bf29e3e8000LL),reale(135489,0xddbb2b5096ef1LL), + // C4[8], coeff of eps^22, polynomial in n of order 4 + -real(6145646087LL<<23),real(131879372361LL<<21), + -real(33613471903LL<<22),-real(3256336589LL<<21), + -real(0xacc29a2990000LL),reale(1761368,0x42813317aa23dLL), + // C4[8], coeff of eps^21, polynomial in n of order 5 + -real(0x6a942373c4bLL<<19),real(0x26ec3bfe245LL<<21), + -real(0x8f791d3a3680000LL),real(0x11c215e6335LL<<20), + -real(0x2c38227cc2fLL<<19),real(0x4429220c0f48000LL), + reale(19375050,0xdb8d32044f89fLL), + // C4[8], coeff of eps^20, polynomial in n of order 6 + -reale(2934,444315969LL<<20),real(0x6a3b64139b1LL<<19), + -real(467101336651LL<<21),real(0x8d6914ca9b7LL<<19), + -real(0x1951684536bLL<<20),-real(344981960323LL<<19), + -real(0x1536c8746170000LL),reale(19375050,0xdb8d32044f89fLL), + // C4[8], coeff of eps^19, polynomial in n of order 7 + -reale(3511,3705843547LL<<19),-real(0x204aea957e3LL<<20), + -reale(2145,1225061153LL<<19),real(0x33d58e2ac0fLL<<21), + -real(10655273223LL<<19),real(0x21f191654dfLL<<20), + -real(0x4229ae891cdLL<<19),real(0x53ff9bb26958000LL), + reale(19375050,0xdb8d32044f89fLL), + // C4[8], coeff of eps^18, polynomial in n of order 8 + reale(2327,223378273LL<<24),reale(3002,681494021LL<<21), + -reale(5098,180818405LL<<22),-real(5074441169LL<<21), + -real(428729715071LL<<23),real(0x3cce86cb309LL<<21), + -real(434398966071LL<<22),-real(156882519885LL<<21), + -real(0x33e11620e250000LL),reale(19375050,0xdb8d32044f89fLL), + // C4[8], coeff of eps^17, polynomial in n of order 9 + -reale(6703,1474120015LL<<19),reale(14458,426935549LL<<22), + -real(511886207649LL<<19),-real(39076914681LL<<20), + -reale(5282,7254660115LL<<19),real(0x33346658ebdLL<<21), + real(0xd2bcdb640d80000LL),real(0x48aecde6f2dLL<<20), + -real(0x66a76bcf857LL<<19),real(0x650db91f67c8000LL), + reale(19375050,0xdb8d32044f89fLL), + // C4[8], coeff of eps^16, polynomial in n of order 10 + -reale(41674,2212282947LL<<19),-reale(7593,6666692295LL<<18), + real(0x22d5b967639LL<<21),reale(15266,7870015191LL<<18), + -reale(4856,4082442485LL<<19),-real(0x76ec691ccd2c0000LL), + -reale(3302,2416313159LL<<20),reale(3252,0xd63fbdd4c0000LL), + -real(0xa56dc66b5380000LL),-real(0x5b75ff5133c0000LL), + -real(0x7d0ead839928000LL),reale(19375050,0xdb8d32044f89fLL), + // C4[8], coeff of eps^15, polynomial in n of order 11 + reale(6774,8529353663LL<<19),reale(68916,757502869LL<<20), + -reale(58358,4821135835LL<<19),reale(3030,627685345LL<<22), + reale(8321,1974413611LL<<19),reale(11199,994841075LL<<20), + -reale(10210,402696815LL<<19),-real(0x10cbfe9c35fLL<<21), + -real(0x88d945e9f480000LL),reale(2764,2004030417LL<<20), + -real(0xa3f22386a83LL<<19),real(0x6eb0baaefa68000LL), + reale(19375050,0xdb8d32044f89fLL), + // C4[8], coeff of eps^14, polynomial in n of order 12 + reale(80789,157273055LL<<23),-reale(92413,883895019LL<<21), + reale(33037,752031121LL<<22),reale(52633,1725093895LL<<21), + -reale(71257,198988971LL<<24),reale(21774,462183721LL<<21), + reale(9867,923099607LL<<22),reale(2235,815700763LL<<21), + -reale(11863,140786955LL<<23),reale(4226,1910142077LL<<21), + real(854212143197LL<<22),real(169477509103LL<<21), + -real(0x1429c96cdeb90000LL),reale(19375050,0xdb8d32044f89fLL), + // C4[8], coeff of eps^13, polynomial in n of order 13 + reale(7986,2577537059LL<<19),-reale(31049,1388317679LL<<21), + reale(71398,6484881669LL<<19),-reale(96607,3840488041LL<<20), + reale(60036,7375804551LL<<19),reale(24533,301249307LL<<22), + -reale(73258,7729848599LL<<19),reale(45499,3314021057LL<<20), + -real(0x3ea6bf07b95LL<<19),-reale(6268,968456357LL<<21), + -reale(5889,8030103219LL<<19),reale(7129,2010513003LL<<20), + -reale(2060,6603694641LL<<19),real(0x4aa8326c4b38000LL), + reale(19375050,0xdb8d32044f89fLL), + // C4[8], coeff of eps^12, polynomial in n of order 14 + real(110457315575LL<<20),-real(0x55e7441aebbLL<<19), + reale(5489,1587292819LL<<21),-reale(23107,1250112621LL<<19), + reale(59020,876513493LL<<20),-reale(93669,6579434335LL<<19), + reale(83160,151752881LL<<22),-reale(14191,6428793873LL<<19), + -reale(55802,147123789LL<<20),reale(63340,7698024701LL<<19), + -reale(24299,306146767LL<<21),-reale(2685,2028352693LL<<19), + reale(2706,1245782417LL<<20),real(0xd3e9bdc0259LL<<19), + -real(0x3e4f75bd92cb0000LL),reale(19375050,0xdb8d32044f89fLL), + // C4[8], coeff of eps^11, polynomial in n of order 15 + real(349722603LL<<19),real(1945948591LL<<20),real(0xe000999c080000LL), + -real(852080688837LL<<21),reale(3397,2932652343LL<<19), + -reale(15561,3567671555LL<<20),reale(44311,3271472077LL<<19), + -reale(82040,520836183LL<<22),reale(95750,3608174083LL<<19), + -reale(56326,3054118709LL<<20),-reale(14075,6930316647LL<<19), + reale(56094,1163367017LL<<21),-reale(46280,7703552945LL<<19), + reale(17576,4216930841LL<<20),-reale(2261,6650055195LL<<19), + -real(0xc8e19a260718000LL),reale(19375050,0xdb8d32044f89fLL), + // C4[8], coeff of eps^10, polynomial in n of order 16 + real(53199LL<<25),real(4832235LL<<21),real(18086833LL<<22), + real(422991569LL<<21),real(3456128781LL<<23),-real(417864400569LL<<21), + real(0x1c1175e6463LL<<22),-reale(9006,876789843LL<<21), + reale(28706,92601679LL<<24),-reale(61776,681174429LL<<21), + reale(90600,218403669LL<<22),-reale(86125,255162935LL<<21), + reale(41671,220860591LL<<23),reale(9900,1945963071LL<<21), + -reale(31426,812677625LL<<22),reale(21427,717745189LL<<21), + -reale(5580,0x8f2cafdf0000LL),reale(19375050,0xdb8d32044f89fLL), + // C4[8], coeff of eps^9, polynomial in n of order 17 + real(47583LL<<19),real(12411LL<<23),real(964865LL<<19), + real(2862477LL<<20),real(45013059LL<<19),real(139025201LL<<21), + real(19339324389LL<<19),-real(313753792905LL<<20), + real(0x5b827ae7827LL<<19),-reale(4043,135949957LL<<22), + reale(14485,4274671945LL<<19),-reale(36111,1380328223LL<<20), + reale(64526,2642754443LL<<19),-reale(82901,1325528645LL<<21), + reale(74876,5403462445LL<<19),-reale(44997,1726039605LL<<20), + reale(16070,4300719215LL<<19),-reale(2566,0xd0ea909388000LL), + reale(19375050,0xdb8d32044f89fLL), + // C4[8], coeff of eps^8, polynomial in n of order 18 + real(1053<<18),real(7293<<17),real(1749LL<<21),real(121635LL<<17), + real(309043LL<<18),real(3853577LL<<17),real(8003583LL<<19), + real(420632751LL<<17),real(7839064905LL<<18),-real(550302356331LL<<17), + real(754118043861LL<<20),-real(0x433703efa18a0000LL), + reale(4345,0xa637f297c0000LL),-reale(12473,0x9f7aaa1be0000LL), + reale(26308,41230677LL<<19),-reale(40979,0xc6d64da720000LL), + reale(45533,1464249973LL<<18),-reale(30801,0xcafec6ea60000LL), + reale(8983,0xdb34fa045c000LL),reale(19375050,0xdb8d32044f89fLL), + // C4[9], coeff of eps^26, polynomial in n of order 0 + real(3679<<17),real(0xf744df0e6c69LL), + // C4[9], coeff of eps^25, polynomial in n of order 1 + -real(48841LL<<20),-real(336765LL<<16),real(0x19892cc90d5217fLL), + // C4[9], coeff of eps^24, polynomial in n of order 2 + real(24659297LL<<26),-real(64440233LL<<25),real(414215087LL<<20), + reale(45019,0xaf6c96bc5ad9dLL), + // C4[9], coeff of eps^23, polynomial in n of order 3 + real(0x55f7a92f661LL<<19),-real(0x115bb8ed6d9LL<<20), + -real(198450589909LL<<19),-real(0xb7278b5afc8000LL), + reale(21654468,0x9b0737e6b33fdLL), + // C4[9], coeff of eps^22, polynomial in n of order 4 + real(52440485279LL<<23),-real(8663417169LL<<21),real(29836121623LL<<22), + -real(64017745099LL<<21),real(0x517eabcb370000LL), + reale(1968588,0xe17edcf27917LL), + // C4[9], coeff of eps^21, polynomial in n of order 5 + real(0x29ddd14eea5LL<<19),-real(683397694747LL<<21), + real(0x8a9d0ded323LL<<19),-real(0x12a27ad79ebLL<<20), + -real(365440747903LL<<19),-real(0x1a278f54ba58000LL), + reale(21654468,0x9b0737e6b33fdLL), + // C4[9], coeff of eps^20, polynomial in n of order 6 + -real(258517517319LL<<23),-reale(2449,779805879LL<<22), + real(333316352075LL<<24),real(89662817151LL<<22), + real(311028248083LL<<23),-real(514657501435LL<<22), + real(0x42edd4687ca0000LL),reale(21654468,0x9b0737e6b33fdLL), + // C4[9], coeff of eps^19, polynomial in n of order 7 + reale(4708,5969586757LL<<19),-reale(3967,769306643LL<<20), + -real(0x4aa5ebcacc1LL<<19),-real(0x2338c762cc1LL<<21), + real(0xdfce640f299LL<<19),-real(0xee4b32a131LL<<20), + -real(544152989037LL<<19),-real(0x392f1a561e88000LL), + reale(21654468,0x9b0737e6b33fdLL), + // C4[9], coeff of eps^18, polynomial in n of order 8 + reale(10651,141986579LL<<24),reale(2483,579021431LL<<21), + real(0x171656e9461LL<<22),-reale(5106,1475723195LL<<21), + real(424307179891LL<<23),real(353847768099LL<<21), + real(0x12b721ceb0bLL<<22),-real(0x16800175f8fLL<<21), + real(0x4cd03e8801b0000LL),reale(21654468,0x9b0737e6b33fdLL), + // C4[9], coeff of eps^17, polynomial in n of order 9 + -reale(12598,568269079LL<<19),-reale(5705,584195995LL<<22), + reale(14434,7986153127LL<<19),-real(0x53c43a7b401LL<<20), + -real(0xc35a517653bLL<<19),-reale(3831,956767451LL<<21), + reale(2686,1674924547LL<<19),real(257168565717LL<<20), + -real(441477690591LL<<19),-real(0x7fc3df35f858000LL), + reale(21654468,0x9b0737e6b33fdLL), + // C4[9], coeff of eps^16, polynomial in n of order 10 + reale(73082,82142393LL<<24),-reale(36372,269994261LL<<23), + -reale(8446,14363443LL<<26),reale(3801,517661957LL<<23), + reale(13381,17268719LL<<24),-reale(7345,464489969LL<<23), + -real(211182139987LL<<25),-real(326075858199LL<<23), + reale(2675,147207653LL<<24),-real(589098042253LL<<23), + real(0x4cdddf4aa2c0000LL),reale(21654468,0x9b0737e6b33fdLL), + // C4[9], coeff of eps^15, polynomial in n of order 11 + -reale(70228,3204573753LL<<19),-reale(3665,997072835LL<<20), + reale(67162,8124243837LL<<19),-reale(56077,490889175LL<<22), + reale(5918,7641432915LL<<19),reale(10294,3043462539LL<<20), + reale(5723,2367840009LL<<19),-reale(10993,938348055LL<<21), + reale(2675,6462906463LL<<19),real(0x3a39e82b059LL<<20), + real(0xb502c3128a80000LL),-real(0x1358f80d9c038000LL), + reale(21654468,0x9b0737e6b33fdLL), + // C4[9], coeff of eps^14, polynomial in n of order 12 + -reale(45939,459571779LL<<23),reale(81202,1438384695LL<<21), + -reale(84011,799287213LL<<22),reale(28155,23125821LL<<21), + reale(46736,266493023LL<<24),-reale(68202,2086496557LL<<21), + reale(29667,382040805LL<<22),reale(5608,647828697LL<<21), + -reale(4401,355658689LL<<23),-reale(6763,1986460369LL<<21), + reale(6284,996052535LL<<22),-real(0x31efd65ac4bLL<<21), + real(0x21519ecdd470000LL),reale(21654468,0x9b0737e6b33fdLL), + // C4[9], coeff of eps^13, polynomial in n of order 13 + -reale(2321,7142809405LL<<19),reale(11407,565078561LL<<21), + -reale(34996,6437021595LL<<19),reale(70236,3027507143LL<<20), + -reale(89750,2730116057LL<<19),reale(59647,532152523LL<<22), + reale(10736,8218389321LL<<19),-reale(61423,3588044783LL<<20), + reale(52845,5572842763LL<<19),-reale(15060,1433211893LL<<21), + -reale(4428,664807251LL<<19),real(0x7ac3d0f14dbLL<<20), + real(0xe0ec3bda56fLL<<19),-real(0x3854598234228000LL), + reale(21654468,0x9b0737e6b33fdLL), + // C4[9], coeff of eps^12, polynomial in n of order 14 + -real(2301546703LL<<23),real(147057720589LL<<22), + -real(359161692259LL<<24),reale(7105,778398699LL<<22), + -reale(23999,359671965LL<<23),reale(54661,162239065LL<<22), + -reale(84322,1670081LL<<25),reale(82245,254480119LL<<22), + -reale(34604,180181675LL<<23),-reale(26937,29999003LL<<22), + reale(54122,167282399LL<<24),-reale(38795,392755901LL<<22), + reale(13349,275194759LL<<23),-real(0x161047343cfLL<<22), + -real(0xd052410afde0000LL),reale(21654468,0x9b0737e6b33fdLL), + // C4[9], coeff of eps^11, polynomial in n of order 15 + -real(33392709LL<<19),-real(215980657LL<<20),-real(15729792143LL<<19), + real(133575397083LL<<21),-real(0x5183d845f39LL<<19), + reale(3762,3694580381LL<<20),-reale(14049,8382023811LL<<19), + reale(36325,545288329LL<<22),-reale(66629,6532309165LL<<19), + reale(85703,4252949035LL<<20),-reale(71810,6020466679LL<<19), + reale(27704,1818174537LL<<21),reale(15098,409579871LL<<19), + -reale(29448,934474951LL<<20),reale(18689,3410848533LL<<19), + -reale(4754,0x309583fd38000LL),reale(21654468,0x9b0737e6b33fdLL), + // C4[9], coeff of eps^10, polynomial in n of order 16 + -real(893LL<<25),-real(92625LL<<21),-real(399779LL<<22), + -real(10904803LL<<21),-real(105333207LL<<23),real(15302554267LL<<21), + -real(86594321625LL<<22),real(0xfe4052cb09LL<<21), + -reale(2108,118544893LL<<24),reale(6191,505418439LL<<21), + -reale(13344,231933903LL<<22),reale(21384,2064906293LL<<21), + -reale(25319,426528669LL<<23),reale(21525,1826875827LL<<21), + -reale(12380,255070469LL<<22),reale(4285,1002542497LL<<21), + -real(0x29d9aac7ec250000LL),reale(7218156,0x33ad12a23bbffLL), + // C4[9], coeff of eps^9, polynomial in n of order 17 + -real(969<<19),-real(285LL<<23),-real(25175LL<<19),-real(85595LL<<20), + -real(1557829LL<<19),-real(5632151LL<<21),-real(929304915LL<<19), + real(18163686975LL<<20),-real(446826699585LL<<19), + real(387249806307LL<<22),-real(0xd080dd307cfLL<<19), + reale(5560,386556505LL<<20),-reale(13900,1932782525LL<<19), + reale(26517,756597539LL<<21),-reale(38450,1381788619LL<<19), + reale(40711,4015921907LL<<20),-reale(26784,1441242297LL<<19), + reale(7700,0x72bfb1ba98000LL),reale(21654468,0x9b0737e6b33fdLL), + // C4[10], coeff of eps^26, polynomial in n of order 0 + -real(5057<<18),real(0x10edb70f760db7LL), + // C4[10], coeff of eps^25, polynomial in n of order 1 + -real(4901LL<<25),real(14157LL<<21),real(0x4082f7e0f93b2fLL), + // C4[10], coeff of eps^24, polynomial in n of order 2 + -real(8688787LL<<25),-real(2064227LL<<24),-real(9250461LL<<21), + reale(7108,0x5f112546294adLL), + // C4[10], coeff of eps^23, polynomial in n of order 3 + real(363248763LL<<25),real(3123548769LL<<26),-real(5801671447LL<<25), + real(14176223919LL<<21),reale(3419126,0x9f3708d39590dLL), + // C4[10], coeff of eps^22, polynomial in n of order 4 + -real(490568702783LL<<22),real(0x422ec2346b3LL<<20), + -real(446296001151LL<<21),-real(174052882927LL<<20), + -real(0xed1818f25bLL<<17),reale(23933886,0x5a813dc916f5bLL), + // C4[10], coeff of eps^21, polynomial in n of order 5 + -reale(2585,173491781LL<<22),real(226504425479LL<<24), + real(118144668093LL<<22),real(325346294119LL<<23), + -real(464280225409LL<<22),real(919092918513LL<<18), + reale(23933886,0x5a813dc916f5bLL), + // C4[10], coeff of eps^20, polynomial in n of order 6 + -reale(2656,138725573LL<<22),-real(0x1a9c614c5c3LL<<21), + -real(765139808215LL<<23),real(0x32058af918bLL<<21), + -real(117685929879LL<<22),-real(106680176295LL<<21), + -real(0xf144800341LL<<18),reale(23933886,0x5a813dc916f5bLL), + // C4[10], coeff of eps^19, polynomial in n of order 7 + reale(3432,329072245LL<<22),reale(2930,283183745LL<<23), + -reale(4577,1044295185LL<<22),real(34786730571LL<<24), + real(54685893801LL<<22),real(647412775723LL<<23), + -real(676279973341LL<<22),real(0xe9c610e7bdLL<<18), + reale(23933886,0x5a813dc916f5bLL), + // C4[10], coeff of eps^18, polynomial in n of order 8 + -reale(11084,235058537LL<<23),reale(11858,1143524977LL<<20), + real(0x2545fd77485LL<<21),-real(0x307c82cee9dLL<<20), + -reale(4103,193833065LL<<22),reale(2140,2163909077LL<<20), + real(446041302231LL<<21),-real(54302113593LL<<20), + -real(0x7f8004b3e7a0000LL),reale(23933886,0x5a813dc916f5bLL), + // C4[10], coeff of eps^17, polynomial in n of order 9 + -reale(16525,309616105LL<<23),-reale(12873,31213113LL<<26), + -real(811482588455LL<<23),reale(13789,37992821LL<<24), + -reale(4637,221662373LL<<23),-real(274288421561LL<<25), + -real(562238052579LL<<23),reale(2541,30117927LL<<24), + -real(493061811809LL<<23),real(451991259993LL<<19), + reale(23933886,0x5a813dc916f5bLL), + // C4[10], coeff of eps^16, polynomial in n of order 10 + -reale(4541,277413243LL<<23),reale(9880,364937465LL<<22), + -reale(5569,118974143LL<<25),-real(671603509225LL<<22), + real(626562721155LL<<23),real(0x126f9949db5LL<<22), + -real(372257463743LL<<24),real(225505748691LL<<22), + real(72729834113LL<<23),real(40056084593LL<<22), + -real(360891225041LL<<19),reale(3419126,0x9f3708d39590dLL), + // C4[10], coeff of eps^15, polynomial in n of order 11 + reale(82722,490551845LL<<23),-reale(64701,63547469LL<<24), + real(116234844999LL<<23),reale(58506,49315063LL<<26), + -reale(58413,433206103LL<<23),reale(16792,103491845LL<<24), + reale(8555,183955915LL<<23),-reale(2317,84066185LL<<25), + -reale(7194,11825619LL<<23),reale(5493,119743831LL<<24), + -real(667673139889LL<<23),real(58009080297LL<<19), + reale(23933886,0x5a813dc916f5bLL), + // C4[10], coeff of eps^14, polynomial in n of order 12 + reale(18981,396462873LL<<22),-reale(46078,2701457279LL<<20), + reale(76015,1126083519LL<<21),-reale(79652,3524648005LL<<20), + reale(36586,273717939LL<<23),reale(28474,1008822389LL<<20), + -reale(61326,649857063LL<<21),reale(42595,3757087663LL<<20), + -reale(8326,955589709LL<<22),-reale(5139,3984305559LL<<20), + real(0x284545d9df3LL<<21),real(0x72b007891a3LL<<20), + -real(0x33009c87a9620000LL),reale(23933886,0x5a813dc916f5bLL), + // C4[10], coeff of eps^13, polynomial in n of order 13 + real(543312976219LL<<22),-reale(3062,112501267LL<<24), + reale(11988,59347917LL<<22),-reale(32439,32307605LL<<23), + reale(61980,821355519LL<<22),-reale(81948,80095793LL<<25), + reale(67313,1055324017LL<<22),-reale(16748,109351667LL<<23), + -reale(34832,1017554013LL<<22),reale(50577,16270159LL<<24), + -reale(32450,61276651LL<<22),reale(10213,501613231LL<<23), + -real(913358656441LL<<22),-real(0xcb30b375e9c0000LL), + reale(23933886,0x5a813dc916f5bLL), + // C4[10], coeff of eps^12, polynomial in n of order 14 + real(553451171LL<<22),-real(41782403663LL<<21),real(122732484303LL<<23), + -real(0x2eaf28525b9LL<<21),reale(6402,476837273LL<<22), + -reale(19347,183862947LL<<21),reale(42585,247358789LL<<24), + -reale(68666,1206346765LL<<21),reale(79038,878189199LL<<22), + -reale(58930,1207602423LL<<21),reale(17031,374958661LL<<23), + reale(18189,4759455LL<<21),-reale(27348,414989947LL<<22), + reale(16435,1788023477LL<<21),-reale(4106,0xe7ddb41f40000LL), + reale(23933886,0x5a813dc916f5bLL), + // C4[10], coeff of eps^11, polynomial in n of order 15 + real(259293LL<<22),real(1935549LL<<23),real(164593143LL<<22), + -real(1654671183LL<<24),real(83533307473LL<<22), + -real(296200453241LL<<23),reale(2600,89083243LL<<22), + -reale(8792,113023813LL<<25),reale(22203,397075141LL<<22), + -reale(42668,107392175LL<<23),reale(62615,179754463LL<<22), + -reale(69317,125076421LL<<24),reale(56036,868613689LL<<22), + -reale(31065,284420581LL<<23),reale(10475,628806483LL<<22), + -real(0x6470cd13038c0000LL),reale(23933886,0x5a813dc916f5bLL), + // C4[10], coeff of eps^10, polynomial in n of order 16 + real(133LL<<24),real(15675LL<<20),real(77539LL<<21),real(2448017LL<<20), + real(27681423LL<<22),-real(4770431897LL<<20),real(32525672025LL<<21), + -real(503497402947LL<<20),real(327672913029LL<<23), + -reale(2314,857372269LL<<20),reale(6672,231933903LL<<21), + -reale(14969,2031875351LL<<20),reale(26346,292729733LL<<22), + -reale(36032,1727726401LL<<20),reale(36664,1180419909LL<<21), + -reale(23570,290549227LL<<20),reale(6696,0xabcf39720000LL), + reale(23933886,0x5a813dc916f5bLL), + // C4[11], coeff of eps^26, polynomial in n of order 0 + real(611LL<<23),real(0xe6baee73ea363LL), + // C4[11], coeff of eps^25, polynomial in n of order 1 + -real(76597LL<<26),-real(1573935LL<<21),real(0x477bca00497fe9bfLL), + // C4[11], coeff of eps^24, polynomial in n of order 2 + real(5977365LL<<29),-real(9705069LL<<28),real(85309807LL<<22), + reale(54497,0x83837319e73d9LL), + // C4[11], coeff of eps^23, polynomial in n of order 3 + real(66340583679LL<<26),-real(4467880351LL<<27),-real(2404066379LL<<26), + -real(68755156353LL<<21),reale(26213304,0x19fb43ab7aab9LL), + // C4[11], coeff of eps^22, polynomial in n of order 4 + real(257415529LL<<33),real(402685503LL<<30),real(652792679LL<<32), + -real(1631824579LL<<30),real(23005724469LL<<23), + reale(26213304,0x19fb43ab7aab9LL), + // C4[11], coeff of eps^21, polynomial in n of order 5 + -real(453253333179LL<<23),-real(222902987187LL<<25), + real(749255628291LL<<23),-real(3378231395LL<<24), + -real(18492933151LL<<23),-real(0xf79dae93c9LL<<18), + reale(26213304,0x19fb43ab7aab9LL), + // C4[11], coeff of eps^20, polynomial in n of order 6 + reale(4082,27256381LL<<26),-reale(3843,110832251LL<<25), + -real(11608614857LL<<27),-real(12679113309LL<<25), + real(80017732991LL<<26),-real(73865834735LL<<25), + real(381345882225LL<<19),reale(26213304,0x19fb43ab7aab9LL), + // C4[11], coeff of eps^19, polynomial in n of order 7 + reale(8515,117356323LL<<23),reale(2727,54002259LL<<24), + real(95356337593LL<<23),-reale(4154,53817247LL<<25), + real(882540340143LL<<23),real(80081392881LL<<24),real(12076046661LL<<23), + -real(0x7d57ec14bd40000LL),reale(26213304,0x19fb43ab7aab9LL), + // C4[11], coeff of eps^18, polynomial in n of order 8 + -reale(12505,951035LL<<32),-reale(6194,13758139LL<<28), + reale(12920,711829LL<<30),-reale(2328,16199449LL<<28), + -reale(2121,1768403LL<<31),-real(23812716991LL<<28), + reale(2380,3848439LL<<30),-real(12910651229LL<<28), + real(75285764519LL<<21),reale(26213304,0x19fb43ab7aab9LL), + // C4[11], coeff of eps^17, polynomial in n of order 9 + reale(63004,136371221LL<<24),-reale(23253,27876759LL<<27), + -reale(10033,153577157LL<<24),reale(4968,6981291LL<<25), + reale(9826,264428161LL<<24),-reale(8260,44635479LL<<26), + real(151361303079LL<<24),real(120235734969LL<<25), + real(86414162541LL<<24),-real(0x22b971cd551LL<<19), + reale(26213304,0x19fb43ab7aab9LL), + // C4[11], coeff of eps^16, polynomial in n of order 10 + -reale(42751,388403LL<<27),-reale(21692,36263017LL<<26), + reale(62310,8348465LL<<29),-reale(46929,14252519LL<<26), + reale(7047,32285691LL<<27),reale(9444,49195723LL<<26), + -real(6177911663LL<<28),-reale(7300,34477811LL<<26), + reale(4777,27041001LL<<27),-real(65141092289LL<<26), + -real(44359884933LL<<20),reale(26213304,0x19fb43ab7aab9LL), + // C4[11], coeff of eps^15, polynomial in n of order 11 + -reale(54975,7679265LL<<24),reale(76586,39901517LL<<25), + -reale(65943,41351227LL<<24),reale(16034,25331417LL<<27), + reale(40019,7760011LL<<24),-reale(57816,89862917LL<<25), + reale(33374,245812081LL<<24),-reale(3538,1406183LL<<26), + -reale(5247,183755081LL<<24),real(95390660393LL<<25), + real(490233600157LL<<24),-real(0x5c9b8397461LL<<19), + reale(26213304,0x19fb43ab7aab9LL), + // C4[11], coeff of eps^14, polynomial in n of order 12 + -reale(5607,843033LL<<31),reale(17587,15869457LL<<28), + -reale(40024,1555693LL<<30),reale(66142,8872067LL<<28), + -reale(76302,93659LL<<32),reale(52531,9300813LL<<28), + -reale(2628,1860027LL<<30),-reale(39200,13058241LL<<28), + reale(46365,693773LL<<31),-reale(27149,11145943LL<<28), + reale(7864,1548807LL<<30),-real(7962030629LL<<28), + -real(412888159761LL<<21),reale(26213304,0x19fb43ab7aab9LL), + // C4[11], coeff of eps^13, polynomial in n of order 13 + -real(41790907379LL<<23),real(76324858599LL<<25), + -reale(2753,104130613LL<<23),reale(9526,224954257LL<<24), + -reale(24463,68256343LL<<23),reale(47309,29696781LL<<26), + -reale(68504,135442201LL<<23),reale(71563,253449815LL<<24), + -reale(47678,505267003LL<<23),reale(8918,86883405LL<<25), + reale(19900,176522371LL<<23),-reale(25292,67707491LL<<24), + reale(14565,326677345LL<<23),-reale(3589,0xb1b7cdcc40000LL), + reale(26213304,0x19fb43ab7aab9LL), + // C4[11], coeff of eps^12, polynomial in n of order 14 + -real(2670507LL<<26),real(235653561LL<<25),-real(820617391LL<<27), + real(25869702111LL<<25),-real(68305888497LL<<26), + reale(3896,38584213LL<<25),-reale(11280,10173573LL<<28), + reale(25274,31321979LL<<25),-reale(44240,19038327LL<<26), + reale(60354,128468529LL<<25),-reale(63152,8090597LL<<27), + reale(48923,66496087LL<<25),-reale(26288,683965LL<<26), + reale(8669,60420749LL<<25),-real(0xa3ae57ad353LL<<19), + reale(26213304,0x19fb43ab7aab9LL), + // C4[11], coeff of eps^11, polynomial in n of order 15 + -real(3933LL<<23),-real(33649LL<<24),-real(3312023LL<<23), + real(38979963LL<<25),-real(2334466673LL<<23),real(9974539421LL<<24), + -real(115419670443LL<<23),real(61272170729LL<<26), + -reale(2977,381931269LL<<23),reale(7656,260966955LL<<24), + -reale(15739,178079295LL<<23),reale(25923,81221929LL<<25), + -reale(33768,486785817LL<<23),reale(33232,239529529LL<<24), + -reale(20951,91935571LL<<23),reale(5892,8880483819LL<<18), + reale(26213304,0x19fb43ab7aab9LL), + // C4[12], coeff of eps^26, polynomial in n of order 0 + -real(1LL<<33),real(0x2f0618f20f09a7LL), + // C4[12], coeff of eps^25, polynomial in n of order 1 + -real(62273LL<<28),real(123651LL<<24),real(0x19e65bbd524850fbLL), + // C4[12], coeff of eps^24, polynomial in n of order 2 + -real(93684917LL<<28),-real(76423549LL<<27),-real(693037063LL<<24), + reale(2191747,0xd5a68f81111b3LL), + // C4[12], coeff of eps^23, polynomial in n of order 3 + real(311968535LL<<28),real(1760740793LL<<29),-real(1954369859LL<<28), + real(3073971433LL<<24),reale(9497573,0xf32718849f75dLL), + // C4[12], coeff of eps^22, polynomial in n of order 4 + -real(7646768769LL<<30),real(19951096269LL<<28),real(491010815LL<<29), + -real(320366609LL<<28),-real(523396783LL<<29), + reale(28492721,0xd975498dde617LL), + // C4[12], coeff of eps^21, polynomial in n of order 5 + -reale(3026,1811699LL<<28),-real(2760169147LL<<30), + -real(4156137093LL<<28),real(9751170709LL<<29),-real(8065022455LL<<28), + real(9009785085LL<<24),reale(28492721,0xd975498dde617LL), + // C4[12], coeff of eps^20, polynomial in n of order 6 + reale(3415,105419659LL<<25),real(300203870565LL<<24), + -reale(4036,50457863LL<<26),real(324492084003LL<<24), + real(46663751897LL<<25),real(14263553185LL<<24), + -real(262021003825LL<<21),reale(28492721,0xd975498dde617LL), + // C4[12], coeff of eps^19, polynomial in n of order 7 + -reale(9657,23913255LL<<26),reale(11274,14202393LL<<27), + -real(33749559685LL<<26),-real(32700069373LL<<28), + -real(114920432067LL<<26),reale(2209,3347763LL<<27), + -real(43334519585LL<<26),real(23877094395LL<<22), + reale(28492721,0xd975498dde617LL), + // C4[12], coeff of eps^18, polynomial in n of order 8 + -reale(10357,4316059LL<<29),-reale(12252,51976653LL<<26), + real(53111513007LL<<27),reale(10573,38618953LL<<26), + -reale(6814,7336347LL<<28),-real(6527663009LL<<26), + real(27052895205LL<<27),real(24601392501LL<<26),-real(17553357101LL<<26), + reale(28492721,0xd975498dde617LL), + // C4[12], coeff of eps^17, polynomial in n of order 9 + -reale(37197,16059447LL<<26),reale(60620,6214685LL<<29), + -reale(35564,62593849LL<<26),real(3388754439LL<<27), + reale(9080,41918565LL<<26),real(21802684253LL<<28), + -reale(7184,50608669LL<<26),reale(4144,5922861LL<<27), + -real(50938551167LL<<26),-real(22779400371LL<<22), + reale(28492721,0xd975498dde617LL), + // C4[12], coeff of eps^16, polynomial in n of order 10 + reale(72847,10250567LL<<26),-reale(50730,68954325LL<<25), + -real(18441684165LL<<28),reale(46646,59050757LL<<25), + -reale(52485,42154095LL<<26),reale(25457,83058719LL<<25), + -real(7107757125LL<<27),-reale(5015,25213703LL<<25), + real(15647388379LL<<26),real(240182800403LL<<25), + -real(724544787239LL<<22),reale(28492721,0xd975498dde617LL), + // C4[12], coeff of eps^15, polynomial in n of order 11 + reale(23399,60546659LL<<26),-reale(46215,22779895LL<<27), + reale(67437,13577137LL<<26),-reale(68612,7311579LL<<29), + reale(38802,65276767LL<<26),reale(8195,42655LL<<27), + -reale(41126,40169811LL<<26),reale(42002,3198053LL<<28), + -reale(22751,40888613LL<<26),reale(6086,3052981LL<<27), + -real(14786628311LL<<26),-real(192226168043LL<<22), + reale(28492721,0xd975498dde617LL), + // C4[12], coeff of eps^14, polynomial in n of order 12 + real(18933494775LL<<28),-reale(4401,33536241LL<<26), + reale(12915,15192945LL<<27),-reale(29094,11527179LL<<26), + reale(50530,7111165LL<<29),-reale(66729,34095909LL<<26), + reale(63904,14901367LL<<27),-reale(38025,42880575LL<<26), + reale(2775,16493565LL<<28),reale(20705,54393511LL<<26), + -reale(23355,27541123LL<<27),reale(12999,62947213LL<<26), + -reale(3169,31971073LL<<26),reale(28492721,0xd975498dde617LL), + // C4[12], coeff of eps^13, polynomial in n of order 13 + real(168754105LL<<26),-real(365884805LL<<28),real(8561579455LL<<26), + -real(18298927075LL<<27),real(119493273445LL<<26), + -reale(4555,4035095LL<<29),reale(9255,49236715LL<<26), + -reale(14989,8672597LL<<27),reale(19228,2329233LL<<26), + -reale(19175,5958615LL<<28),reale(14321,64798871LL<<26), + -reale(7491,16431175LL<<27),reale(2423,48133949LL<<26), + -real(387864634927LL<<22),reale(9497573,0xf32718849f75dLL), + // C4[12], coeff of eps^12, polynomial in n of order 14 + real(198835LL<<25),-real(20309575LL<<24),real(82800575LL<<26), + -real(3096741505LL<<24),real(9853268425LL<<25),-real(92620723195LL<<24), + real(42100328725LL<<27),-reale(3631,95393589LL<<24), + reale(8507,100242399LL<<25),-reale(16264,217481391LL<<24), + reale(25338,57859733LL<<26),-reale(31673,155080937LL<<24), + reale(30296,62498037LL<<25),-reale(18783,217084003LL<<24), + reale(5237,1702548307LL<<21),reale(28492721,0xd975498dde617LL), + // C4[13], coeff of eps^26, polynomial in n of order 0 + real(83LL<<25),real(0xb952c68e4fbe9LL), + // C4[13], coeff of eps^25, polynomial in n of order 1 + -real(71903LL<<28),-real(1749945LL<<24),reale(5818,0x23b391cd899edLL), + // C4[13], coeff of eps^24, polynomial in n of order 2 + real(16903565LL<<32),-real(16862357LL<<31),real(47373573LL<<26), + reale(789029,0x386f296be7703LL), + // C4[13], coeff of eps^23, polynomial in n of order 3 + real(16624462311LL<<28),real(913717761LL<<29),-real(74700691LL<<28), + -real(16672249061LL<<24),reale(30772139,0x98ef4f7042175LL), + // C4[13], coeff of eps^22, polynomial in n of order 4 + -real(876500127LL<<32),-real(1654287687LL<<30),real(2350551113LL<<31), + -real(1761427069LL<<30),real(3376471371LL<<25), + reale(30772139,0x98ef4f7042175LL), + // C4[13], coeff of eps^21, polynomial in n of order 5 + real(32655563463LL<<28),-reale(3801,39657LL<<30),real(14063722833LL<<28), + real(3085833575LL<<29),real(1328082427LL<<28),-real(31671991379LL<<24), + reale(30772139,0x98ef4f7042175LL), + // C4[13], coeff of eps^20, polynomial in n of order 6 + reale(9254,348341LL<<33),real(891770565LL<<32),-real(427379969LL<<34), + -real(2022490653LL<<32),real(1067054247LL<<33),-real(569056495LL<<32), + real(430257975LL<<27),reale(30772139,0x98ef4f7042175LL), + // C4[13], coeff of eps^19, polynomial in n of order 7 + -reale(12155,12992869LL<<26),-real(50480950653LL<<27), + reale(10690,33612001LL<<26),-reale(5460,12466223LL<<28), + -real(37884419769LL<<26),real(23471963137LL<<27),real(26726056077LL<<26), + -real(264030652949LL<<22),reale(30772139,0x98ef4f7042175LL), + // C4[13], coeff of eps^18, polynomial in n of order 8 + reale(55507,1702051LL<<31),-reale(25284,15301089LL<<28), + -reale(4552,6848911LL<<29),reale(8014,12209149LL<<28), + reale(2646,1117571LL<<30),-reale(6924,9493077LL<<28), + reale(3590,1368763LL<<29),-real(9963972599LL<<28), + -real(15032559759LL<<23),reale(30772139,0x98ef4f7042175LL), + // C4[13], coeff of eps^17, polynomial in n of order 9 + -reale(35540,37090525LL<<26),-reale(14632,1678969LL<<29), + reale(49583,4861453LL<<26),-reale(46373,31739579LL<<27), + reale(18858,13991831LL<<26),real(34153973959LL<<28), + -reale(4603,63875327LL<<26),-real(5127820649LL<<27), + real(116479282059LL<<26),-real(662201165171LL<<22), + reale(30772139,0x98ef4f7042175LL), + // C4[13], coeff of eps^16, polynomial in n of order 10 + -reale(50761,3025121LL<<30),reale(66355,4425085LL<<29), + -reale(59858,324757LL<<32),reale(26575,1103635LL<<29), + reale(16255,479225LL<<30),-reale(41402,7301831LL<<29), + reale(37768,714123LL<<31),-reale(19110,4265457LL<<29), + reale(4727,1747987LL<<30),-real(399638603LL<<29), + -real(44359884933LL<<24),reale(30772139,0x98ef4f7042175LL), + // C4[13], coeff of eps^15, polynomial in n of order 11 + -reale(6366,57157311LL<<26),reale(16360,10192555LL<<27), + -reale(33060,46450725LL<<26),reale(52401,3371487LL<<29), + -reale(63840,37321515LL<<26),reale(56445,29554125LL<<27), + -reale(29837,31975057LL<<26),-real(31142569185LL<<28), + reale(20917,29855465LL<<26),-reale(21569,9966225LL<<27), + reale(11677,61095555LL<<26),-reale(2823,85634603LL<<22), + reale(30772139,0x98ef4f7042175LL), + // C4[13], coeff of eps^14, polynomial in n of order 12 + -real(583637535LL<<30),real(11022475035LL<<28), + -reale(2387,6604529LL<<29),reale(6865,10202825LL<<28), + -reale(15869,914837LL<<31),reale(29710,9184775LL<<28), + -reale(45043,8145271LL<<29),reale(54812,7153333LL<<28), + -reale(52441,1442741LL<<30),reale(37945,12833459LL<<28), + -reale(19389,6190909LL<<29),reale(6169,7752673LL<<28), + -real(487958734263LL<<23),reale(30772139,0x98ef4f7042175LL), + // C4[13], coeff of eps^13, polynomial in n of order 13 + -real(23263695LL<<26),real(59053995LL<<28),-real(1639451385LL<<26), + real(4222829325LL<<27),-real(33859413315LL<<26),real(13601644665LL<<29), + -reale(4256,19212781LL<<26),reale(9227,30696251LL<<27), + -reale(16594,3848759LL<<26),reale(24654,470841LL<<28), + -reale(29745,41662305LL<<26),reale(27762,19442409LL<<27), + -reale(16966,1393323LL<<26),reale(4695,1022390371LL<<22), + reale(30772139,0x98ef4f7042175LL), + // C4[14], coeff of eps^26, polynomial in n of order 0 + -real(6781LL<<26),real(0x5fa345ccc643905LL), + // C4[14], coeff of eps^25, polynomial in n of order 1 + -real(5869LL<<31),real(7353LL<<27),real(0x148e6926290dbdd9LL), + // C4[14], coeff of eps^24, polynomial in n of order 2 + real(299903009LL<<31),real(37927009LL<<30),-real(2056312073LL<<27), + reale(33051557,0x58695552a5cd3LL), + // C4[14], coeff of eps^23, polynomial in n of order 3 + -real(368055047LL<<31),real(374500339LL<<32),-real(256592557LL<<31), + real(207940889LL<<27),reale(11017185,0xc8231c70e1ef1LL), + // C4[14], coeff of eps^22, polynomial in n of order 4 + -reale(3490,1015519LL<<31),real(4441335459LL<<29),real(1543166497LL<<30), + real(845740769LL<<29),-real(7622621279LL<<26), + reale(33051557,0x58695552a5cd3LL), + // C4[14], coeff of eps^21, polynomial in n of order 5 + real(1872610391LL<<32),-real(324912469LL<<34),-reale(2076,886527LL<<32), + real(978081451LL<<33),-real(478972501LL<<32),real(98710857LL<<28), + reale(33051557,0x58695552a5cd3LL), + // C4[14], coeff of eps^20, polynomial in n of order 6 + -reale(4075,185993LL<<32),reale(10359,933297LL<<31), + -reale(4245,239235LL<<33),-real(1849695177LL<<31),real(616423549LL<<32), + real(879958205LL<<31),-real(3876332285LL<<28), + reale(33051557,0x58695552a5cd3LL), + // C4[14], coeff of eps^19, polynomial in n of order 7 + -reale(16507,1038671LL<<32),-reale(7426,284587LL<<33), + reale(6609,461219LL<<32),reale(3685,34759LL<<34), + -reale(6576,786795LL<<32),reale(3109,191175LL<<33), + -real(486788729LL<<32),-real(537600147LL<<28), + reale(33051557,0x58695552a5cd3LL), + // C4[14], coeff of eps^18, polynomial in n of order 8 + -reale(24788,2034733LL<<30),reale(49876,16016773LL<<27), + -reale(40130,10305415LL<<28),reale(13469,4482399LL<<27), + reale(3498,5005267LL<<29),-reale(4111,23511239LL<<27), + -real(7714983213LL<<28),real(56106110739LL<<27), + -real(151831927709LL<<24),reale(33051557,0x58695552a5cd3LL), + // C4[14], coeff of eps^17, polynomial in n of order 9 + reale(63443,3600701LL<<29),-reale(50757,496203LL<<32), + reale(16003,4273171LL<<29),reale(22072,52127LL<<30), + -reale(40595,5066263LL<<29),reale(33806,1004469LL<<31), + -reale(16095,1666881LL<<29),reale(3680,908597LL<<30), + real(584087189LL<<29),-real(20381568753LL<<25), + reale(33051557,0x58695552a5cd3LL), + // C4[14], coeff of eps^16, polynomial in n of order 10 + reale(19689,3969453LL<<29),-reale(36282,12968943LL<<28), + reale(53123,80009LL<<31),-reale(60234,3770241LL<<28), + reale(49410,2521755LL<<29),-reale(22943,4183315LL<<28), + -reale(5331,2542455LL<<30),reale(20742,9731931LL<<28), + -reale(19939,3671159LL<<29),reale(10552,6717897LL<<28), + -reale(2533,118946129LL<<25),reale(33051557,0x58695552a5cd3LL), + // C4[14], coeff of eps^15, polynomial in n of order 11 + real(8533174455LL<<29),-reale(3246,843591LL<<30), + reale(8406,531117LL<<29),-reale(17842,647003LL<<32), + reale(31156,7375267LL<<29),-reale(44630,771985LL<<30), + reale(51879,2231193LL<<29),-reale(47870,1100123LL<<31), + reale(33689,2160271LL<<29),-reale(16864,3290075LL<<30), + reale(5288,925829LL<<29),-real(103506398177LL<<25), + reale(33051557,0x58695552a5cd3LL), + // C4[14], coeff of eps^14, polynomial in n of order 12 + real(66723345LL<<29),-real(1495097175LL<<27),real(3274386375LL<<28), + -real(23122205325LL<<27),real(8392504155LL<<30), + -reale(4840,16188355LL<<27),reale(9826,9115409LL<<28), + -reale(16767,17260281LL<<27),reale(23911,7831387LL<<29), + -reale(27976,32288815LL<<27),reale(25559,3357275LL<<28), + -reale(15423,21986149LL<<27),reale(4241,135611051LL<<24), + reale(33051557,0x58695552a5cd3LL), + // C4[15], coeff of eps^26, polynomial in n of order 0 + real(71LL<<30),real(0x2213ecbbb96785dLL), + // C4[15], coeff of eps^25, polynomial in n of order 1 + real(6799LL<<34),-real(2467695LL<<27),reale(43244,0xc47e8e0e2a501LL), + // C4[15], coeff of eps^24, polynomial in n of order 2 + real(1754601LL<<37),-real(1107369LL<<36),real(11866753LL<<28), + reale(1859525,0x141dc611b72bLL), + // C4[15], coeff of eps^23, polynomial in n of order 3 + real(72562737LL<<34),real(46462031LL<<35),real(31074907LL<<34), + -real(3658156407LL<<27),reale(35330975,0x17e35b3509831LL), + // C4[15], coeff of eps^22, polynomial in n of order 4 + -real(13531387LL<<38),-reale(2167,14381LL<<36),real(55828981LL<<37), + -real(25230703LL<<36),real(3197649LL<<30), + reale(35330975,0x17e35b3509831LL), + // C4[15], coeff of eps^21, polynomial in n of order 5 + reale(9732,8631LL<<37),-reale(3185,3623LL<<39),-real(35580543LL<<37), + real(7839601LL<<38),real(14184443LL<<37),-real(3642815981LL<<28), + reale(35330975,0x17e35b3509831LL), + // C4[15], coeff of eps^20, polynomial in n of order 6 + -reale(8973,2493LL<<39),reale(5093,1677LL<<40),reale(4452,53LL<<40), + -reale(6181,1625LL<<40),reale(2693,7137LL<<39),-real(1482145LL<<40), + -real(287239701LL<<29),reale(35330975,0x17e35b3509831LL), + // C4[15], coeff of eps^19, polynomial in n of order 7 + reale(48363,42681LL<<36),-reale(34138,1171LL<<37), + reale(9135,63227LL<<36),reale(4396,12847LL<<38),-reale(3596,33667LL<<36), + -real(22964529LL<<37),real(105092799LL<<36),-real(8732815777LL<<28), + reale(35330975,0x17e35b3509831LL), + // C4[15], coeff of eps^18, polynomial in n of order 8 + -reale(41806,3793LL<<39),reale(7070,18565LL<<36), + reale(26107,27709LL<<37),-reale(39103,10113LL<<36), + reale(30179,111LL<<38),-reale(13593,42919LL<<36),reale(2866,20031LL<<37), + real(9747283LL<<36),-real(584087189LL<<30), + reale(35330975,0x17e35b3509831LL), + // C4[15], coeff of eps^17, polynomial in n of order 9 + -reale(38751,968439LL<<32),reale(52907,129341LL<<35), + -reale(56213,262777LL<<32),reale(42911,476327LL<<33), + -reale(17166,1038043LL<<32),-reale(7920,60547LL<<34), + reale(20320,95267LL<<32),-reale(18461,171251LL<<33), + reale(9586,798401LL<<32),-reale(2289,97706315LL<<25), + reale(35330975,0x17e35b3509831LL), + // C4[15], coeff of eps^16, polynomial in n of order 10 + -real(182681295LL<<35),reale(3304,139139LL<<34),-reale(6521,16667LL<<37), + reale(10722,226797LL<<34),-reale(14618,113609LL<<35), + reale(16325,9799LL<<34),-reale(14590,57403LL<<36), + reale(10019,97137LL<<34),-reale(4925,40451LL<<35),real(399638603LL<<34), + -real(14786628311LL<<26),reale(11776991,0xb2a11e67032bbLL), + // C4[15], coeff of eps^15, polynomial in n of order 11 + -real(76608285LL<<32),real(147323625LL<<33),-real(936978255LL<<32), + reale(2382,112581LL<<35),-reale(5377,114593LL<<32), + reale(10315,142015LL<<33),-reale(16818,394707LL<<32), + reale(23142,22533LL<<34),-reale(26356,277413LL<<32), + reale(23629,395541LL<<33),-reale(14101,658135LL<<32), + reale(3855,122649445LL<<25),reale(35330975,0x17e35b3509831LL), + // C4[16], coeff of eps^26, polynomial in n of order 0 + -real(22951LL<<32),reale(14038,0xf79362a6f2da9LL), + // C4[16], coeff of eps^25, polynomial in n of order 1 + -real(9017LL<<35),real(4815LL<<31),reale(9206,0xf354c01a236f3LL), + // C4[16], coeff of eps^24, polynomial in n of order 2 + real(1146319LL<<36),real(916151LL<<35),-real(5763591LL<<32), + reale(1979494,0x5c2d55f3c2615LL), + // C4[16], coeff of eps^23, polynomial in n of order 3 + -real(15250071LL<<35),real(5353311LL<<36),-real(2240893LL<<35), + -real(332469LL<<31),reale(1979494,0x5c2d55f3c2615LL), + // C4[16], coeff of eps^22, polynomial in n of order 4 + -reale(2280,6539LL<<38),-real(78951693LL<<36),real(12301989LL<<37), + real(28829297LL<<36),-real(214091115LL<<32), + reale(37610392,0xd75d61176d38fLL), + // C4[16], coeff of eps^21, polynomial in n of order 5 + reale(3604,40151LL<<35),reale(4989,25591LL<<37),-reale(5766,5439LL<<35), + reale(2335,38023LL<<36),-real(36776117LL<<35),-real(73810821LL<<31), + reale(37610392,0xd75d61176d38fLL), + // C4[16], coeff of eps^20, polynomial in n of order 6 + -reale(28603,8635LL<<37),reale(5695,63155LL<<36),reale(4892,14039LL<<38), + -reale(3091,58619LL<<36),-real(29081577LL<<37),real(100489431LL<<36), + -real(125982325LL<<34),reale(37610392,0xd75d61176d38fLL), + // C4[16], coeff of eps^19, polynomial in n of order 7 + -real(44186749LL<<35),reale(28752,63675LL<<36),-reale(37202,72039LL<<35), + reale(26902,18169LL<<37),-reale(11512,105649LL<<35), + reale(2229,59753LL<<36),real(26382181LL<<35),-real(267675387LL<<31), + reale(37610392,0xd75d61176d38fLL), + // C4[16], coeff of eps^18, polynomial in n of order 8 + reale(51956,3503LL<<39),-reale(52000,65291LL<<36), + reale(36995,30461LL<<37),-reale(12343,54705LL<<36), + -reale(9828,2065LL<<38),reale(19743,35337LL<<36), + -reale(17124,20865LL<<37),reale(8752,19043LL<<36), + -reale(2081,554945LL<<32),reale(37610392,0xd75d61176d38fLL), + // C4[16], coeff of eps^17, polynomial in n of order 9 + reale(11351,15263LL<<35),-reale(21031,10301LL<<38), + reale(32809,38737LL<<35),-reale(42826,1799LL<<36), + reale(46156,123299LL<<35),-reale(40102,7421LL<<37), + reale(26942,16853LL<<35),-reale(13031,12333LL<<36), + reale(3987,20263LL<<35),-real(1198915809LL<<31), + reale(37610392,0xd75d61176d38fLL), + // C4[16], coeff of eps^16, polynomial in n of order 10 + real(100180065LL<<34),-real(583401555LL<<33),reale(2759,6541LL<<36), + -reale(5863,45661LL<<33),reale(10706,132871LL<<34), + -reale(16773,276519LL<<33),reale(22364,92173LL<<35), + -reale(24871,48433LL<<33),reale(21929,91821LL<<34), + -reale(12958,132347LL<<33),reale(3525,1706711LL<<30), + reale(37610392,0xd75d61176d38fLL), + // C4[17], coeff of eps^26, polynomial in n of order 0 + real(1LL<<32),real(0x62a61c3e4dd975LL), + // C4[17], coeff of eps^25, polynomial in n of order 1 + real(4057LL<<35),-real(45015LL<<31),reale(8569,0x3d59f665e75a3LL), + // C4[17], coeff of eps^24, polynomial in n of order 2 + real(43463LL<<40),-real(16895LL<<39),-real(11395LL<<34), + reale(299923,0x634cafeea1549LL), + // C4[17], coeff of eps^23, polynomial in n of order 3 + -real(1242717LL<<35),real(138325LL<<36),real(435713LL<<35), + -real(3030063LL<<31),reale(299923,0x634cafeea1549LL), + // C4[17], coeff of eps^22, polynomial in n of order 4 + real(2302621LL<<39),-real(9225219LL<<37),real(1747781LL<<38), + -real(372113LL<<37),-real(1948863LL<<32), + reale(2099463,0xb718cf86694ffLL), + // C4[17], coeff of eps^21, polynomial in n of order 5 + reale(2996,69763LL<<35),reale(5105,30307LL<<37),-reale(2616,23051LL<<35), + -real(67503821LL<<36),real(191814791LL<<35),-real(933454921LL<<31), + reale(39889810,0x96d766f9d0eedLL), + // C4[17], coeff of eps^20, polynomial in n of order 6 + reale(30327,1293LL<<39),-reale(35084,3299LL<<38),reale(23968,2311LL<<40), + -reale(9776,7093LL<<38),real(14159215LL<<39),real(3853577LL<<38), + -real(61360803LL<<33),reale(39889810,0x96d766f9d0eedLL), + // C4[17], coeff of eps^19, polynomial in n of order 7 + -reale(47757,47889LL<<35),reale(31664,61447LL<<36), + -reale(8326,73443LL<<35),-reale(11212,17667LL<<37), + reale(19076,23915LL<<35),-reale(15916,62675LL<<36), + reale(8026,42649LL<<35),-real(3989637911LL<<31), + reale(39889810,0x96d766f9d0eedLL), + // C4[17], coeff of eps^18, polynomial in n of order 8 + -reale(22252,923LL<<40),reale(33139,2449LL<<37),-reale(41618,6905LL<<38), + reale(43458,30291LL<<37),-reale(36814,1531LL<<39), + reale(24253,3845LL<<37),-reale(11561,2707LL<<38),reale(3500,30023LL<<37), + -real(522604327LL<<32),reale(39889810,0x96d766f9d0eedLL), + // C4[17], coeff of eps^17, polynomial in n of order 9 + -real(175857885LL<<35),reale(3123,8343LL<<38),-reale(6297,123891LL<<35), + reale(11012,26677LL<<36),-reale(16654,74217LL<<35), + reale(21593,16599LL<<37),-reale(23509,7807LL<<35),reale(20422,743LL<<36), + -reale(11961,60789LL<<35),reale(3239,1180923LL<<31), + reale(39889810,0x96d766f9d0eedLL), + // C4[18], coeff of eps^26, polynomial in n of order 0 + -real(56087LL<<33),reale(47221,0xfaefc0318df67LL), + // C4[18], coeff of eps^25, polynomial in n of order 1 + -real(19981LL<<39),-real(10755LL<<35),reale(443886,0x9d340e9e9cd95LL), + // C4[18], coeff of eps^24, polynomial in n of order 2 + real(84155LL<<39),real(380011LL<<38),-real(1249051LL<<35), + reale(2219433,0x12044919103e9LL), + // C4[18], coeff of eps^23, polynomial in n of order 3 + -real(2130987LL<<39),real(379583LL<<40),-real(70649LL<<39), + -real(240567LL<<35),reale(2219433,0x12044919103e9LL), + // C4[18], coeff of eps^22, polynomial in n of order 4 + real(4417441LL<<38),-real(7513869LL<<36),-real(1960735LL<<37), + real(4812241LL<<36),-real(11409363LL<<33), + reale(2219433,0x12044919103e9LL), + // C4[18], coeff of eps^21, polynomial in n of order 5 + -real(28350547LL<<38),real(4603793LL<<40),-real(7176677LL<<38), + real(573937LL<<39),real(220745LL<<38),-real(1482145LL<<34), + reale(2219433,0x12044919103e9LL), + // C4[18], coeff of eps^20, polynomial in n of order 6 + reale(26896,5159LL<<38),-reale(4987,8879LL<<37),-reale(12193,6323LL<<39), + reale(18360,26775LL<<37),-reale(14825,8691LL<<38), + reale(7390,26973LL<<37),-real(457982805LL<<34), + reale(42169228,0x56516cdc34a4bLL), + // C4[18], coeff of eps^19, polynomial in n of order 7 + reale(11070,12259LL<<38),-reale(13431,6105LL<<39), + reale(13633,4633LL<<38),-reale(11288,2771LL<<40),reale(7306,11663LL<<38), + -reale(3437,4851LL<<39),real(16896453LL<<38),-real(38239341LL<<34), + reale(14056409,0x721b244966e19LL), + // C4[18], coeff of eps^18, polynomial in n of order 8 + reale(3471,5433LL<<39),-reale(6682,62369LL<<36),reale(11244,10859LL<<37), + -reale(16478,49907LL<<36),reale(20837,10809LL<<38), + -reale(22258,26821LL<<36),reale(19078,20857LL<<37), + -reale(11086,15383LL<<36),reale(2990,191861LL<<33), + reale(42169228,0x56516cdc34a4bLL), + // C4[19], coeff of eps^26, polynomial in n of order 0 + -real(113LL<<37),reale(16591,0x81ae2ec54d8dfLL), + // C4[19], coeff of eps^25, polynomial in n of order 1 + real(94099LL<<40),-real(1178305LL<<35),reale(2339402,0x6cefc2abb72d3LL), + // C4[19], coeff of eps^24, polynomial in n of order 2 + real(41263LL<<43),-real(6583LL<<42),-real(117501LL<<36), + reale(2339402,0x6cefc2abb72d3LL), + // C4[19], coeff of eps^23, polynomial in n of order 3 + -real(384159LL<<40),-real(130977LL<<41),real(286571LL<<40), + -real(2657049LL<<35),reale(2339402,0x6cefc2abb72d3LL), + // C4[19], coeff of eps^22, polynomial in n of order 4 + real(64121LL<<46),-real(23919LL<<46),real(6837LL<<45),real(901LL<<46), + -real(170289LL<<37),reale(2339402,0x6cefc2abb72d3LL), + // C4[19], coeff of eps^21, polynomial in n of order 5 + -real(955747LL<<39),-real(1386619LL<<41),real(7599723LL<<39), + -real(2983211LL<<40),real(2945369LL<<39),-real(22232175LL<<34), + reale(2339402,0x6cefc2abb72d3LL), + // C4[19], coeff of eps^20, polynomial in n of order 6 + -real(2096679LL<<42),real(4148625LL<<41),-real(841269LL<<43), + real(2143479LL<<41),-real(498253LL<<42),real(296429LL<<41), + -real(2667861LL<<35),reale(2339402,0x6cefc2abb72d3LL), + // C4[19], coeff of eps^19, polynomial in n of order 7 + -real(3026933LL<<39),real(2460315LL<<40),-real(7010575LL<<39), + real(2166905LL<<41),-real(9101001LL<<39),real(3853577LL<<40), + -real(4446435LL<<39),real(38239341LL<<34), + reale(2339402,0x6cefc2abb72d3LL), + // C4[20], coeff of eps^26, polynomial in n of order 0 + -real(34781LL<<40),reale(2459371,0xc7db3c3e5e1bdLL), + // C4[20], coeff of eps^25, polynomial in n of order 1 + -real(4771LL<<42),-real(28479LL<<38),reale(2459371,0xc7db3c3e5e1bdLL), + // C4[20], coeff of eps^24, polynomial in n of order 2 + -real(68467LL<<42),real(136501LL<<41),-real(310209LL<<38), + reale(2459371,0xc7db3c3e5e1bdLL), + // C4[20], coeff of eps^23, polynomial in n of order 3 + -real(327189LL<<42),real(20533LL<<43),real(14681LL<<42), + -real(78387LL<<38),reale(2459371,0xc7db3c3e5e1bdLL), + // C4[20], coeff of eps^22, polynomial in n of order 4 + -real(179129LL<<44),real(910389LL<<42),-real(348793LL<<43), + real(341479LL<<42),-real(321657LL<<40),reale(2459371,0xc7db3c3e5e1bdLL), + // C4[20], coeff of eps^21, polynomial in n of order 5 + real(1952379LL<<42),-real(388557LL<<44),real(975677LL<<42), + -real(224349LL<<43),real(132447LL<<42),-real(296429LL<<38), + reale(2459371,0xc7db3c3e5e1bdLL), + // C4[20], coeff of eps^20, polynomial in n of order 6 + real(1242423LL<<41),-real(3451175LL<<40),real(1045213LL<<42), + -real(4322097LL<<40),real(1810109LL<<41),-real(2075003LL<<40), + real(4446435LL<<37),reale(2459371,0xc7db3c3e5e1bdLL), + // C4[21], coeff of eps^26, polynomial in n of order 0 + -real(199LL<<39),reale(37381,0xc16e795c129fbLL), + // C4[21], coeff of eps^25, polynomial in n of order 1 + real(65027LL<<42),-real(290455LL<<38),reale(2579341,0x22c6b5d1050a7LL), + // C4[21], coeff of eps^24, polynomial in n of order 2 + real(1883LL<<46),real(1837LL<<45),-real(18073LL<<40), + reale(2579341,0x22c6b5d1050a7LL), + // C4[21], coeff of eps^23, polynomial in n of order 3 + real(871509LL<<42),-real(326909LL<<43),real(317735LL<<42), + -real(1195627LL<<38),reale(2579341,0x22c6b5d1050a7LL), + // C4[21], coeff of eps^22, polynomial in n of order 4 + -real(29971LL<<46),real(74261LL<<44),-real(16907LL<<45),real(9911LL<<44), + -real(44149LL<<39),reale(859780,0x60ece745ac58dLL), + // C4[21], coeff of eps^21, polynomial in n of order 5 + -real(848003LL<<42),real(252109LL<<44),-real(1027829LL<<42), + real(426173LL<<43),-real(485639LL<<42),real(2075003LL<<38), + reale(2579341,0x22c6b5d1050a7LL), + // C4[22], coeff of eps^26, polynomial in n of order 0 + -real(2963LL<<40),reale(117361,0x5360ca6881e97LL), + // C4[22], coeff of eps^25, polynomial in n of order 1 + real(79LL<<45),-real(363LL<<41),reale(117361,0x5360ca6881e97LL), + // C4[22], coeff of eps^24, polynomial in n of order 2 + -real(76751LL<<45),real(74129LL<<44),-real(139337LL<<41), + reale(2699310,0x7db22f63abf91LL), + // C4[22], coeff of eps^23, polynomial in n of order 3 + real(102051LL<<45),-real(23023LL<<46),real(13409LL<<45), + -real(29733LL<<41),reale(2699310,0x7db22f63abf91LL), + // C4[22], coeff of eps^22, polynomial in n of order 4 + real(121647LL<<45),-real(489555LL<<43),real(201135LL<<44), + -real(227953LL<<43),real(485639LL<<40),reale(2699310,0x7db22f63abf91LL), + // C4[23], coeff of eps^26, polynomial in n of order 0 + -real(1LL<<45),reale(5837,0x4b04b152e489LL), + // C4[23], coeff of eps^25, polynomial in n of order 1 + real(377LL<<47),-real(5665LL<<41),reale(122577,0x627628bccbf3dLL), + // C4[23], coeff of eps^24, polynomial in n of order 2 + -real(57LL<<50),real(33LL<<49),-real(583LL<<42), + reale(122577,0x627628bccbf3dLL), + // C4[23], coeff of eps^23, polynomial in n of order 3 + -real(1269LL<<47),real(517LL<<48),-real(583LL<<47),real(9911LL<<41), + reale(122577,0x627628bccbf3dLL), + // C4[24], coeff of eps^26, polynomial in n of order 0 + -real(83LL<<47),reale(127793,0x718b871115fe3LL), + // C4[24], coeff of eps^25, polynomial in n of order 1 + real(5LL<<50),-real(11LL<<46),reale(42597,0xd083d7b05caa1LL), + // C4[24], coeff of eps^24, polynomial in n of order 2 + real(245LL<<49),-real(275LL<<48),real(583LL<<45), + reale(127793,0x718b871115fe3LL), + // C4[25], coeff of eps^26, polynomial in n of order 0 + -real(1LL<<47),reale(8867,0x4cd786c27dde7LL), + // C4[25], coeff of eps^25, polynomial in n of order 1 + -real(13LL<<50),real(55LL<<46),reale(26601,0xe6869447799b5LL), + // C4[26], coeff of eps^26, polynomial in n of order 0 + real(1LL<<48),reale(2126,0x8c0e9e949456fLL), + }; // count = 4032 +#elif GEOGRAPHICLIB_GEODESICEXACT_ORDER == 30 + static const real coeff[] = { + // C4[0], coeff of eps^29, polynomial in n of order 0 + 3361,real(109067695), + // C4[0], coeff of eps^28, polynomial in n of order 1 + real(121722048),real(30168404),real(0x269c465a0c9LL), + // C4[0], coeff of eps^27, polynomial in n of order 2 + real(21708121824LL),-real(10786479696LL),real(8048130587LL), + real(0xbfa33c13e963LL), + // C4[0], coeff of eps^26, polynomial in n of order 3 + real(0x738319564e0LL),-real(0x4c2475635c0LL),real(0x25d0be52da0LL), + real(643173496654LL),real(0xa0f21774b90225LL), + // C4[0], coeff of eps^25, polynomial in n of order 4 + real(0x7a99ea0a52f40LL),-real(0x5a5f53e2c3b50LL),real(0x3b83d2c0c8da0LL), + -real(0x1d8a81cb5cc70LL),real(0x1605bd50459c1LL), + real(0x6fb2ae4757107d03LL), + // C4[0], coeff of eps^24, polynomial in n of order 5 + real(0x2507d929b7f89580LL),-real(0x1ce7bf02c3715a00LL), + real(0x15463c23456c8680LL),-real(0xdfecff0050dfd00LL), + real(0x6f141ba97196780LL),real(0x1b71ab9c78b8b48LL), + reale(1520879,0x957266bcf90f9LL), + // C4[0], coeff of eps^23, polynomial in n of order 6 + reale(5214,0xb54b8c26f5620LL),-reale(4202,0x4ae5f5bcbf950LL), + reale(3272,0xab988a50dfac0LL),-reale(2404,0x84ae60c9e7b30LL), + real(0x62be65b26227b760LL),-real(0x30f2645200be8b10LL), + real(0x2472ebc3f09ad327LL),reale(9429453,0x6b5ee3606e93bLL), + // C4[0], coeff of eps^22, polynomial in n of order 7 + reale(213221,0x21fe88963f0e0LL),-reale(174746,0x12fe03af82e40LL), + reale(140344,0xd3dfad978d4a0LL),-reale(109009,0x13ee03d15f180LL), + reale(79932,0x9fff01479b460LL),-reale(52447,0x53ea945b584c0LL), + reale(25976,0xa5a6ee990f820LL),reale(6403,0x87dc4a069efc6LL), + reale(273454149,0x29bfc1ec86bafLL), + // C4[0], coeff of eps^21, polynomial in n of order 8 + reale(1513769,0x9572babb99080LL),-reale(1247902,0x66609b16e1250LL), + reale(1017692,0x228016ac84e60LL),-reale(814136,0x86ec313455df0LL), + reale(630421,0xa88f591713840LL),-reale(461205,0x487f023b60f90LL), + reale(302134,0x36942691aea20LL),-reale(149503,0x5a1d9af94cb30LL), + reale(111169,0xb14ab93d4ba6dLL),reale(1367270745,0xd0bec99ea1a6bLL), + // C4[0], coeff of eps^20, polynomial in n of order 9 + reale(2196138,0xe1b60fe1808c0LL),-reale(1802572,0x3b4b1c2a34200LL), + reale(1475191,0x47b8ccbe8340LL),-reale(1196055,0x2e2a401c46980LL), + reale(952413,0x117e9e1fb75c0LL),-reale(734856,0x2e19f1e7be100LL), + reale(536171,0x8daa599335040LL),-reale(350594,0xa58d466a3880LL), + reale(173293,0x7b19cdc9682c0LL),reale(42591,0xb005bdeb82d74LL), + reale(1367270745,0xd0bec99ea1a6bLL), + // C4[0], coeff of eps^19, polynomial in n of order 10 + reale(9954363,0x5ecc5371ca720LL),-reale(8035921,0x7cc90565e0670LL), + reale(6522783,0x32e1ec30d1a80LL),-reale(5291286,0x4172ef2beb090LL), + reale(4260231,0x65c388ed45de0LL),-reale(3373847,0x4da61e8c704b0LL), + reale(2592185,0xcd194d02dbd40LL),-reale(1885401,0xa08c9a20ef6d0LL), + reale(1230164,0x4c527bc6a84a0LL),-reale(607279,0x24d6e51bd7af0LL), + reale(450701,0xae98337b7d081LL),reale(4101812237LL,0x723c5cdbe4f41LL), + // C4[0], coeff of eps^18, polynomial in n of order 11 + reale(16160603,0x85a3ec5761ce0LL),-reale(12587219,0x97b7f7c505ac0LL), + reale(9979192,0xa0e43863a93a0LL),-reale(7988280,0xcfaf566027f00LL), + reale(6410314,0xbffc30c12660LL),-reale(5117692,0xfd9318db4c340LL), + reale(4026292,0x94c482b815d20LL),-reale(3077917,0x9c480ad851f80LL), + reale(2230377,0x99db799d8bfe0LL),-reale(1451530,0xb0005d9658bc0LL), + reale(715485,0xdbe6a2ef6d6a0LL),reale(175141,0x3547b8669b9beLL), + reale(4101812237LL,0x723c5cdbe4f41LL), + // C4[0], coeff of eps^17, polynomial in n of order 12 + reale(30091817,0x8745c27487540LL),-reale(21716256,0x7a4bb1495e170LL), + reale(16366670,0xd4e8bc19a0660LL),-reale(12670374,0x9eda0f5df2ed0LL), + reale(9963727,0x5ae4f6d3c8380LL),-reale(7887824,0x191034733ae30LL), + reale(6231873,0x96448488ef0a0LL),-reale(4863678,0x67c3c74b1b90LL), + reale(3695513,0x2e7ae0f4851c0LL),-reale(2665992,0xe6864878c32f0LL), + reale(1729741,0xf881cba41aae0LL),-reale(851104,0x888fd5b7ab050LL), + reale(629987,0x9ea5a19626943LL),reale(4101812237LL,0x723c5cdbe4f41LL), + // C4[0], coeff of eps^16, polynomial in n of order 13 + reale(79181861,0x46beef62ca900LL),-reale(45969492,0x85a19d8425400LL), + reale(30736937,0x10d9a95bb4f00LL),-reale(22084618,0xaf3a6659fa600LL), + reale(16548053,0x58583f22e9500LL),-reale(12711232,0x3d7f1b1be3800LL), + reale(9889259,0xbbf5d84b2bb00LL),-reale(7711253,0x36b17889dca00LL), + reale(5958759,0x73d1ebe040100LL),-reale(4493987,0xfa374abbe1c00LL), + reale(3224517,0x29027e04ea700LL),-reale(2084431,0x8d77e42beee00LL), + reale(1023433,0xbf113370eed00LL),reale(249103,0x93cdbdabe0fb0LL), + reale(4101812237LL,0x723c5cdbe4f41LL), + // C4[0], coeff of eps^15, polynomial in n of order 14 + reale(100415733,0x1c7e0d98777e0LL),-reale(220472579,0x196c2a7ff77f0LL), + reale(81497972,0xcf48e14d7b2c0LL),-reale(47157604,0xb4c79beff0c90LL), + reale(31400333,0x3ade51fc905a0LL),-reale(22437640,0x62c8445afeb30LL), + reale(16688020,0xb49b2cc64ec80LL),-reale(12687475,0x35a524f08d7d0LL), + reale(9727302,0xc96eb1166e360LL),-reale(7422875,0x3574dc9ff9670LL), + reale(5546536,0x3897621326640LL),-reale(3953280,0x7a61d237aeb10LL), + reale(2544043,0x942757fc8f120LL),-reale(1245848,0x5f59e2e2499b0LL), + reale(918672,0xb7e149f3f515dLL),reale(4101812237LL,0x723c5cdbe4f41LL), + // C4[0], coeff of eps^14, polynomial in n of order 15 + -reale(410150575,0x33edeefdadd60LL),reale(389451478,0x4a8eb37cf8e40LL), + reale(102537774,0xdf54e754057e0LL),-reale(228145792,0x9928ef6984980LL), + reale(84014235,0x8c476a1354120LL),-reale(48417903,0x9486b64af140LL), + reale(32072368,0xac5157de0d660LL),-reale(22757026,0x6fd3c1d71f100LL), + reale(16760216,0x75de552320fa0LL),-reale(12564203,0xce657c7ead0c0LL), + reale(9433140,0xee7b325fde4e0LL),-reale(6966096,0xc0a9d97231880LL), + reale(4923714,0x7fe1a8c934e20LL),-reale(3150864,0xcacdc5bf45040LL), + reale(1538058,0xc6e75548f4360LL),reale(371250,0x9b28ca926da22LL), + reale(4101812237LL,0x723c5cdbe4f41LL), + // C4[0], coeff of eps^13, polynomial in n of order 16 + reale(10071346,0xbead2787bab00LL),reale(77935892,0xc8037e807a610LL), + -reale(424974584,0x95c58aa2abc60LL),reale(405632040,0xf37804095de30LL), + reale(104709205,0x2c34dddf07040LL),-reale(236671973,0xc06ad427a5bb0LL), + reale(86756233,0x36f6256b264e0LL),-reale(49748360,0xa42ca4c379390LL), + reale(32735340,0x1aa6eba145580LL),-reale(23012513,0x41e6e60af5570LL), + reale(16722020,0xa0e65eb557620LL),-reale(12285046,0x712c138942d50LL), + reale(8933912,0x44131ea6cfac0LL),-reale(6247309,0xac4879043a730LL), + reale(3969671,0x8774cc7c1760LL),-reale(1929932,0x2a739696c4f10LL), + reale(1414943,0x9f9bcb791811fLL),reale(4101812237LL,0x723c5cdbe4f41LL), + // C4[0], coeff of eps^12, polynomial in n of order 17 + reale(1301009,0x7885767b34dc0LL),reale(3139452,0x6299dbe8eac00LL), + reale(10399899,0xe9c2f692aa40LL),reale(80694987,0xafcfc919b1e80LL), + -reale(441529449,0x34f14f083e140LL),reale(423985433,0x2e9be95704100LL), + reale(106892519,0x9a909730adb40LL),-reale(246219322,0x3cc21ecefbc80LL), + reale(89751674,0x8e9ea1f760fc0LL),-reale(51139306,0x4d1fa35b2aa00LL), + reale(33357165,0x391836578ec40LL),-reale(23152852,0x670df382e5780LL), + reale(16502135,0xfb453b1baa0c0LL),-reale(11755175,0x732a395d89500LL), + reale(8105218,0xa64658fb65d40LL),-reale(5103238,0xc9c658d3f3280LL), + reale(2468214,0x7d6aacb2351c0LL),reale(588064,0xecbdce72e5104LL), + reale(4101812237LL,0x723c5cdbe4f41LL), + // C4[0], coeff of eps^11, polynomial in n of order 18 + reale(365173,0x141eb92882aa0LL),reale(660579,0x721db1cc80890LL), + reale(1339643,0x6f3cff39e7d00LL),reale(3240370,0xc29100e665970LL), + reale(10762711,0xac38fa6376f60LL),reale(83769430,0x6edf90fa38050LL), + -reale(460180081,0xa7a2c15d05240LL),reale(445039582,0xb96af8d66e930LL), + reale(109020126,0x840edc5d1e420LL),-reale(257005247,0x2ec795996fff0LL), + reale(93028106,0x54adfb574be80LL),-reale(52565819,0x1d828e2b6cf10LL), + reale(33879206,0x109475f98e8e0LL),-reale(23088279,0x158dbde3c1830LL), + reale(15975944,0x7a6ca24c70f40LL),-reale(10806612,0x3c0d699b76f50LL), + reale(6721635,0xd5a36326ddda0LL),-reale(3228909,0xe44dc20d06870LL), + reale(2345355,0x81bdf10588059LL),reale(4101812237LL,0x723c5cdbe4f41LL), + // C4[0], coeff of eps^10, polynomial in n of order 19 + reale(142358,0x43f28ef2bce60LL),reale(224104,0xc49bf70fb8540LL), + reale(374789,0x29edb81ed2220LL),reale(679606,0x56dce126b3a00LL), + reale(1381751,0x3315a15e701e0LL),reale(3351469,0xe4cb186e3aec0LL), + reale(11166107,0x295c18ed1d5a0LL),reale(87224183,0xbf27e3cc5cb80LL), + -reale(481408924,0xf800e4fbbfaa0LL),reale(469519077,0x9e18ca33e7840LL), + reale(110970854,0x606788cedf920LL),-reale(269315695,0x90dadb20d6300LL), + reale(96606791,0x8c213171618e0LL),-reale(53972000,0xd509f5454de40LL), + reale(34191407,0x9021dc5d4cca0LL),-reale(22654105,0x9f8b9187f1180LL), + reale(14912791,0x946e9b2907c60LL),-reale(9121084,0x6067cd3f714c0LL), + reale(4341360,0x73b562399020LL),reale(1011849,0x75de66a5bdb46LL), + reale(4101812237LL,0x723c5cdbe4f41LL), + // C4[0], coeff of eps^9, polynomial in n of order 20 + reale(66631,0x784cbdfb1b2c0LL),reale(96606,0x3419bb8e05f90LL), + reale(145459,0xb79bffbfb42e0LL),reale(229589,0x824d22506cd30LL), + reale(385010,0x35e34fd0f4f00LL),reale(700134,0x4df5413db48d0LL), + reale(1427794,0x581b23c083b20LL),reale(3474469,0x224df4c0f7670LL), + reale(11618119,0x6c8cba4306b40LL),reale(91144571,0x713d14f45fa10LL), + -reale(505869523,0xd3d937aa3bca0LL),reale(498449385,0x686859af477b0LL), + reale(112524504,0x2ca5b0e042780LL),-reale(283533725,0xba4eec11a6cb0LL), + reale(100487121,0xc424152de7ba0LL),-reale(55236514,0x8c4dd4ee50f10LL), + reale(34077723,0x322bbe9b9a3c0LL),-reale(21528502,0x2ca44d130cb70LL), + reale(12851809,0x7f1d30d5603e0LL),-reale(6038295,0xecbfc0da7fdd0LL), + reale(4313665,0xa0fbedf62e95bLL),reale(4101812237LL,0x723c5cdbe4f41LL), + // C4[0], coeff of eps^8, polynomial in n of order 21 + reale(34939,0x4781a8598a880LL),reale(47986,0x870a153a0ba00LL), + reale(67643,0xf93c5a3d5fb80LL),reale(98366,0xdef5527b5d100LL), + reale(148567,0x565e4f7b51e80LL),reale(235242,0x766e64b79c800LL), + reale(395796,0x5614c84bc3180LL),reale(722239,0xc9f1a6fcbf00LL), + reale(1478257,0xd3352c2795480LL),reale(3611438,0xfdbc40cced600LL), + reale(12129091,0x5ec9e3d72a780LL),reale(95645231,0xe79e249b02d00LL), + -reale(534473300,0x6333290e9b580LL),reale(533336700,0xd7635e240e400LL), + reale(113268651,0x31e09daaa5d80LL),-reale(300181610,0x6cd38634ee500LL), + reale(104606327,0x6a6e0bd3d0080LL),-reale(56090968,0xcfc000b8f0e00LL), + reale(33084425,0x428f85e945380LL),-reale(19025074,0x3fea5ea1f7700LL), + reale(8768855,0x59c11511e7680LL),reale(1959911,0x57aea52b92dd8LL), + reale(4101812237LL,0x723c5cdbe4f41LL), + // C4[0], coeff of eps^7, polynomial in n of order 22 + reale(19712,0xac93bc6991f60LL),reale(26064,0x47e63bb6f7b10LL), + reale(35129,0x85349dd791940LL),reale(48412,0xcf2b50a5e4170LL), + reale(68486,0xf23457a2e7b20LL),reale(99959,0x1aee9379bdd0LL), + reale(151547,0xc976e86422100LL),reale(240911,0x67a8290f88c30LL), + reale(407002,0x79f859786e6e0LL),reale(745880,0xf6e3b80f24890LL), + reale(1533569,0xcfffb4a9fa8c0LL),reale(3764807,0xab1a08cbd8ef0LL), + reale(12712489,0x4098eb8542a0LL),reale(100884327,0x9a754746dfb50LL), + -reale(568536969,0xbcc82f5b36f80LL),reale(576497219,0x10ca042b229b0LL), + reale(112392819,0xaecaa4a6c6e60LL),-reale(319979712,0xfe05e4aae49f0LL), + reale(108728942,0x9b1cd9ac3b840LL),-reale(55904982,0xfebe8a174c390LL), + reale(30158727,0xd0df7149f4a20LL),-reale(13482566,0x2ca2af46da730LL), + reale(9304222,0x6328f1d67a7f5LL),reale(4101812237LL,0x723c5cdbe4f41LL), + // C4[0], coeff of eps^6, polynomial in n of order 23 + reale(11639,0x4298ebe4bc020LL),reale(14966,0xe9089607c0a40LL), + reale(19534,0x1996a62965260LL),reale(25928,0xdcaffa7bfcb80LL), + reale(35089,0x59fa64f7d88a0LL),reale(48563,0x32ed377221cc0LL), + reale(69004,0xe5c9403173ae0LL),reale(101181,0xf483b00105600LL), + reale(154143,0xf39432e434120LL),reale(246274,0xfc90899a3cf40LL), + reale(418255,0xdad9486cf7360LL),reale(770731,0xbf0321b55e080LL), + reale(1593877,0xd61fe95ba9a0LL),reale(3937200,0x3820413b3e1c0LL), + reale(13385919,0xf48ca237dbbe0LL),reale(107086956,0x9d1b10f932b00LL), + -reale(610048075,0x6c1b2715a7de0LL),reale(631706048,0xcac1d46451440LL), + reale(108187733,0xaf9fd1440d460LL),-reale(343908890,0x37b3c0b50a80LL), + reale(112109635,0x3a73d439f8aa0LL),-reale(53028119,0x15d1799f5d940LL), + reale(22454404,0x49a70d2177ce0LL),reale(4553016,0x22f700960daaaLL), + reale(4101812237LL,0x723c5cdbe4f41LL), + // C4[0], coeff of eps^5, polynomial in n of order 24 + reale(7030,0x634f92bbfec80LL),reale(8852,0x183ea9c784b10LL), + reale(11280,0x864427e0ea420LL),reale(14569,0x4ed71f4155e30LL), + reale(19103,0x13b2c1ad2ffc0LL),reale(25480,0x35983eb20bf50LL), + reale(34659,0x18ad59c5f9360LL),reale(48227,0x95f2c0574270LL), + reale(68917,0x8c5b3ac32f300LL),reale(101660,0x272f49f96bb90LL), + reale(155850,0xbc628b339b2a0LL),reale(250657,0x122490d07feb0LL), + reale(428675,0x21f5a97506640LL),reale(795748,0x8d9dd2ee8dfd0LL), + reale(1658420,0x22b44d2c5a1e0LL),reale(4130702,0x814b60cb632f0LL), + reale(14171990,0xb8691b29bf980LL),reale(114585240,0x7599d8275cc10LL), + -reale(662180135,0x55c1167b3fee0LL),reale(705602404,0xf6219ee07f30LL), + reale(96655880,0xe42cfbbc64cc0LL),-reale(373149978,0xd8d5a94d3dfb0LL), + reale(112272021,0x704341a757060LL),-reale(42251989,0xbf5a94cca7c90LL), + reale(26498553,0xea37274059c77LL),reale(4101812237LL,0x723c5cdbe4f41LL), + // C4[0], coeff of eps^4, polynomial in n of order 25 + reale(4244,0x3972351df5940LL),reale(5257,0xaa8f87b5d5600LL), + reale(6578,0xed6cb3b3fa2c0LL),reale(8324,0xb4008d853180LL), + reale(10662,0x703b07259b440LL),reale(13846,0x8f2f6ca125d00LL), + reale(18261,0x3a455b4269dc0LL),reale(24508,0x5045fb81ae880LL), + reale(33557,0x1b3e945f36f40LL),reale(47022,0x9499ec44e400LL), + reale(67699,0x7a940285938c0LL),reale(100662,0x403646e1e5f80LL), + reale(155637,0xf20897fb50a40LL),reale(252593,0x7106d86756b00LL), + reale(436178,0xe720d891ff3c0LL),reale(818051,0x1d79595b01680LL), + reale(1723706,0xc365c92e70540LL),reale(4344105,0xb055b91247200LL), + reale(15096896,0xe96c54f834ec0LL),reale(123888911,0x435c586708d80LL), + -reale(730395130,0x8d07d85ee1fc0LL),reale(811137162,0xd7ccf03d27900LL), + reale(66848989,0xdd39a234bc9c0LL),-reale(407950245,0xd67367b7fbb80LL), + reale(99073631,0x21cb91dfe1b40LL),reale(14205410,0x589c3f44ce7acLL), + reale(4101812237LL,0x723c5cdbe4f41LL), + // C4[0], coeff of eps^3, polynomial in n of order 26 + reale(2481,0x8d2c27b46b620LL),reale(3034,0xe44720f3fdf90LL), + reale(3743,0xf82fc54a92780LL),reale(4662,0xb922ac44f6b70LL), + reale(5867,0xae02c805f08e0LL),reale(7469,0x40a687e9b4d50LL), + reale(9629,0xbb2099bca6640LL),reale(12592,0xa0727e14e5130LL), + reale(16731,0xdc4cfea134ba0LL),reale(22636,0xbf84f9dc44310LL), + reale(31263,0xfe99294d5c500LL),reale(44220,0x78f2e666feef0LL), + reale(64313,0xe77c1f84fde60LL),reale(96684,0x43c9282e120d0LL), + reale(151281,0x84eb0984fa3c0LL),reale(248729,0xa2c4a502aa4b0LL), + reale(435615,0xd80deb212120LL),reale(829647,0x194fc60e84690LL), + reale(1777619,0x17dfea7bc6280LL),reale(4562307,0x417bb8824d270LL), + reale(16175470,0xd3a7db47373e0LL),reale(135804489,0xbb999e2601450LL), + -reale(825156505,0xa8162cc9f9ec0LL),reale(977623624,0xd8c5ee7f4d830LL), + -reale(20397512,0x4ab8f862cc960LL),-reale(435632583,0xf2b7943e115f0LL), + reale(143237887,0xa8277df5ccab1LL),reale(4101812237LL,0x723c5cdbe4f41LL), + // C4[0], coeff of eps^2, polynomial in n of order 27 + real(0x52cac993243497e0LL),real(0x6437dfaee57b9d40LL), + real(0x7a3f9cad4d2f48a0LL),reale(2405,0xee01eec3f2b00LL), + reale(2986,0x65a22988df560LL),reale(3743,0xe8ba104bd58c0LL), + reale(4745,0x82561551e620LL),reale(6086,0xa7581d3ddee80LL), + reale(7912,0x8561dfdd262e0LL),reale(10440,0x7aa2aab74b440LL), + reale(14008,0x9b1a2c148b3a0LL),reale(19155,0xcd3b8407d7200LL), + reale(26767,0x9792b4f9c2060LL),reale(38350,0xb50c17257efc0LL), + reale(56574,0xaf828f4edf120LL),reale(86399,0xb1bc40483f580LL), + reale(137581,0x7d29442656de0LL),reale(230687,0xc9059cc5d4b40LL), + reale(413025,0xcba5d91bbdea0LL),reale(806439,0xbad85d457b900LL), + reale(1777226,0xdb254a1088b60LL),reale(4709200,0x187f6563b06c0LL), + reale(17312174,0x4c53d944cbc20LL),reale(151524377,0x682a2ddefc80LL), + -reale(970338799,0x73aba5c04720LL),reale(1287957204,0xb756685e76240LL), + -reale(416692036,0xd1e73fe253660LL),-reale(78129756,0xe75b5bfa6fa32LL), + reale(4101812237LL,0x723c5cdbe4f41LL), + // C4[0], coeff of eps^1, polynomial in n of order 28 + real(0xb4c355cd41c92c0LL),real(0xd8fea3a41cc7830LL), + real(0x1064f0c6b9a6ad20LL),real(0x13f7a88902ef1b10LL), + real(0x1884a414973fcb80LL),real(0x1e5fa2ae5243d7f0LL), + real(0x25fe0bb384ddd9e0LL),real(0x3006f6e3e0e25ad0LL), + real(0x3d6c2c13c34ec440LL),real(0x4f91f34825bd4fb0LL), + real(0x688ffb74f98676a0LL),reale(2233,0xdec33bb086290LL), + reale(3036,0xe53843c2cdd00LL),reale(4213,0xb13e1137e3f70LL), + reale(5984,0xaa1cca8abe360LL),reale(8732,0xb9880d6c69250LL), + reale(13152,0x1eadcfcfd75c0LL),reale(20566,0x4e1752c3c0730LL), + reale(33653,0xf4262a5798020LL),reale(58247,0x3a420e3524a10LL), + reale(108257,0x7934f39e3ee80LL),reale(221025,0xaccc1c0dc06f0LL), + reale(514222,0xffbb852faace0LL),reale(1456965,0x29e8a4070e9d0LL), + reale(5827860,0xa7a2901c3a740LL),reale(56821641,0x6270fd1339eb0LL), + -reale(416692036,0xd1e73fe253660LL),reale(625038055,0x3adadfd37d190LL), + -reale(273454149,0x29bfc1ec86bafLL),reale(1367270745,0xd0bec99ea1a6bLL), + // C4[0], coeff of eps^0, polynomial in n of order 29 + reale(42171,0xbca3d5a569b4LL),reale(46862,0xd0a41cdef9cf0LL), + reale(52277,0xa2d5316ac1b2cLL),reale(58560,0x6f94d669a7a28LL), + reale(65892,0x788629d238da4LL),reale(74502,0x6b99bdf690d60LL), + reale(84681,0x87b277eadbb1cLL),reale(96804,0x8c76c6701c898LL), + reale(111359,0x1427f62cd3d94LL),reale(128987,0x59921e2221dd0LL), + reale(150546,0xaa0136eb20f0cLL),reale(177198,0x7742592373f08LL), + reale(210542,0x4360b9bd64984LL),reale(252821,0x8a8c09196de40LL), + reale(307248,0x66986780ae6fcLL),reale(378530,0x79d0ac77ed78LL), + reale(473750,0x5114d83948174LL),reale(603901,0x80acdb5cb5eb0LL), + reale(786661,0x2afc1dbf812ecLL),reale(1051686,0xda8ab314e3e8LL), + reale(1451326,0xc0ede2017b564LL),reale(2083956,0x5d3b51a63af20LL), + reale(3149615,0xde5c8fc3f62dcLL),reale(5099378,0x12ae3e18b3258LL), + reale(9106032,0x45ee012c1b554LL),reale(18940547,0x20d0545bbdf90LL), + reale(52086504,0x9a3ce7fc4a6ccLL),reale(312519027,0x9d6d6fe9be8c8LL), + -reale(1093816596,0xa6ff07b21aebcLL), + reale(2734541491LL,0xa17d933d434d6LL), + reale(4101812237LL,0x723c5cdbe4f41LL), + // C4[1], coeff of eps^29, polynomial in n of order 0 + 917561,real(273868982145LL), + // C4[1], coeff of eps^28, polynomial in n of order 1 + -real(125915776),real(90505212),real(0x73d4d30e25bLL), + // C4[1], coeff of eps^27, polynomial in n of order 2 + -real(0x2f7e4f2fca0LL),real(0x161b06db8f0LL),real(379339642199LL), + real(0x145a25f15d59339LL), + // C4[1], coeff of eps^26, polynomial in n of order 3 + -real(0x780f9f651c0LL),real(0x49cd6538080LL),-real(0x275396e6f40LL), + real(0x1c1406225eaLL),real(0x1e2d6465e2b066fLL), + // C4[1], coeff of eps^25, polynomial in n of order 4 + -real(0x226e68a74f6c2c0LL),real(0x178fbd94c6e4130LL), + -real(0x10bafa7048ffb60LL),real(0x7b204e43552d10LL), + real(0x1ebd785c76c649LL),reale(369943,0xaebaf6655156dLL), + // C4[1], coeff of eps^24, polynomial in n of order 5 + -real(0x26adfa4c2bcf8500LL),real(0x1be7e116f09bc400LL), + -real(0x1641521374362300LL),real(0xd7dd4a2b1831200LL), + -real(0x7449d087ac65100LL),real(0x525502d56a2a1d8LL), + reale(4562638,0xc0573436eb2ebLL), + // C4[1], coeff of eps^23, polynomial in n of order 6 + -reale(27299,0x1e7fae46f2ae0LL),reale(20250,0xb050f61211530LL), + -reale(17170,0x1ccacfb407b40LL),reale(11560,0x5557506ac7a50LL), + -reale(8300,0x1ee1dfec0f3a0LL),reale(3760,0xc5da39149a170LL), + real(0x3aaaad07e2dbe15fLL),reale(141441801,0x4a8f52a67aa75LL), + // C4[1], coeff of eps^22, polynomial in n of order 7 + -reale(223720,0xada70de871dc0LL),reale(168212,0x95f7a36b8e780LL), + -reale(147708,0x4639d71413140LL),reale(104570,0x398040c96dd00LL), + -reale(84304,0x27ca2fe2f28c0LL),reale(50205,0xd862a9f308280LL), + -reale(27426,0xbe7e08935dc40LL),reale(19210,0x9794de13dcf52LL), + reale(820362447,0x7d3f45c59430dLL), + // C4[1], coeff of eps^21, polynomial in n of order 8 + -reale(1591044,0x45108afb80980LL),reale(1200725,0xfaaefe8d2aff0LL), + -reale(1074110,0x244b18cc1fd20LL),reale(779463,0x6e55e2794e4d0LL), + -reale(667443,0x7f273db50d4c0LL),reale(440073,0xbd38cdf5ffbb0LL), + -reale(320490,0xb0902bc064460LL),reale(142410,0x1eb038cc00090LL), + reale(35531,0x5cce3f7afbb81LL),reale(4101812237LL,0x723c5cdbe4f41LL), + // C4[1], coeff of eps^20, polynomial in n of order 9 + -reale(6932123,0xff59c6bb56f80LL),reale(5207764,0x9d4c81592dc00LL), + -reale(4682178,0xdef9cf054a880LL),reale(3431350,0xdcd7f0ab97d00LL), + -reale(3036244,0xeb9781cfe3980LL),reale(2097463,0x35c6f48ae00LL), + -reale(1714507,0xab45478b85280LL),reale(997568,0xe75b4df283f00LL), + -reale(555001,0x356f72a492380LL),reale(383325,0x3033ad4799914LL), + reale(12305436712LL,0x56b51693aedc3LL), + // C4[1], coeff of eps^19, polynomial in n of order 10 + -reale(10475274,0x80e3f984eb560LL),reale(7761418,0x6cb2d37d31d50LL), + -reale(6912729,0x2574b8548f80LL),reale(5061056,0xbff13b9f8e7b0LL), + -reale(4542234,0x9c8561f8559a0LL),reale(3202970,0x45874de1c0010LL), + -reale(2776395,0x2331e9957c0LL),reale(1780809,0x24244086de270LL), + -reale(1321308,0xb7d4404aacde0LL),reale(572110,0xf0d923e3d0ad0LL), + reale(142666,0x15ad08c690505LL),reale(12305436712LL,0x56b51693aedc3LL), + // C4[1], coeff of eps^18, polynomial in n of order 11 + -reale(16991539,0x3bfa3a952a5c0LL),reale(12232630,0xc216625651e80LL), + -reale(10582386,0xca84c044c7740LL),reale(7659664,0x22fef68736200LL), + -reale(6852368,0xbf4b993050cc0LL),reale(4854746,0x78ae9dfa88580LL), + -reale(4332124,0x5850c11d91e40LL),reale(2896859,0x8330e6242d100LL), + -reale(2410777,0x3c4e4b27563c0LL),reale(1359574,0x6f5bc7e308c80LL), + -reale(775169,0xf705a84369540LL),reale(525423,0x9fd72933d2d3aLL), + reale(12305436712LL,0x56b51693aedc3LL), + // C4[1], coeff of eps^17, polynomial in n of order 12 + -reale(31605635,0x9b2a6245129c0LL),reale(21349095,0xec111ef51efd0LL), + -reale(17343382,0xc6b59d854f620LL),reale(12224940,0xad54b9902f0LL), + -reale(10665275,0xcb2c9d1586680LL),reale(7495419,0x2bbe593f97c10LL), + -reale(6731026,0x5bd11498926e0LL),reale(4567553,0xbb95797dfef30LL), + -reale(4019270,0xe17fb3dce340LL),reale(2483542,0x18261977df050LL), + -reale(1889445,0x252a3b83f47a0LL),reale(789608,0x3727b34041370LL), + reale(196748,0x5030b26b63d7fLL),reale(12305436712LL,0x56b51693aedc3LL), + // C4[1], coeff of eps^16, polynomial in n of order 13 + -reale(83651327,0x7df35b769ce00LL),reale(46183264,0x6a662d0fec800LL), + -reale(32523895,0xbf44a3e60200LL),reale(21575930,0xbd1dba7599c00LL), + -reale(17706525,0xdbcb8c6749600LL),reale(12151631,0x7c587583d3000LL), + -reale(10707728,0xa79806e6f4a00LL),reale(7245171,0x8aa6d7e27c400LL), + -reale(6517082,0x9ff2c462fde00LL),reale(4168671,0x7a21919979800LL), + -reale(3551918,0x26047c5101200LL),reale(1918361,0x786d4fd8aec00LL), + -reale(1131511,0x7e7a26769a600LL),reale(747310,0xbb693903a2f10LL), + reale(12305436712LL,0x56b51693aedc3LL), + // C4[1], coeff of eps^15, polynomial in n of order 14 + -reale(63372442,0x2cb5338504ea0LL),reale(236021120,0xed659df2db350LL), + -reale(86667901,0x5273be9be40LL),reale(47209611,0xc1161d91d1e30LL), + -reale(33537857,0x3d1f3cdba35e0LL),reale(21739691,0xd5c3b2c9df710LL), + -reale(18074666,0x2123c601d8980LL),reale(11984705,0x3d2e52a8729f0LL), + -reale(10682808,0x1cfcfab158d20LL),reale(6875060,0xeec2e9924a2d0LL), + -reale(6158904,0xf3892aedc14c0LL),reale(3612073,0x775a08e9d4db0LL), + -reale(2844696,0x4fdad4b74f460LL),reale(1130419,0xe52285ff91690LL), + reale(281319,0xf8ed6ce679421LL),reale(12305436712LL,0x56b51693aedc3LL), + // C4[1], coeff of eps^14, polynomial in n of order 15 + reale(377918798,0xab0ca9f0672c0LL),-reale(418618018,0x8099eba53f80LL), + -reale(60854873,0x3eafa33f453c0LL),reale(245263030,0xf5560cf897d00LL), + -reale(90083330,0xb4182a1e90640LL),reale(48226005,0xa87e22e4ae980LL), + -reale(34666917,0x2b03feac26cc0LL),reale(21804113,0xa9bac4593e00LL), + -reale(18434597,0x75e58711b4f40LL),reale(11683388,0x18da60c9eb280LL), + -reale(10544255,0x717858fde75c0LL),reale(6335167,0xce8110cc57f00LL), + -reale(5568830,0x1a6ca9ba6a840LL),reale(2826076,0xf4ab3cac7db80LL), + -reale(1750284,0x2ff80145eaec0LL),reale(1113751,0xd17a5fb748e66LL), + reale(12305436712LL,0x56b51693aedc3LL), + // C4[1], coeff of eps^13, polynomial in n of order 16 + -reale(7676111,0x5b2a6c5f6c100LL),-reale(64415807,0x4cf1fd08a9430LL), + reale(389009273,0x614b445047d20LL),-reale(437396877,0xd309fa5941090LL), + -reale(57368388,0x6af986a1a0c0LL),reale(255600151,0x61702d3245910LL), + -reale(94005962,0x2924b0b2256a0LL),reale(49188288,0xa4967a4d0acb0LL), + -reale(35935634,0xccf0586b2e080LL),reale(21713831,0x3869a07cfee50LL), + -reale(18759173,0xcf3c8197a7a60LL),reale(11187408,0x277eed08021f0LL), + -reale(10209411,0xbc33094486040LL),reale(5549613,0x5f33e35304b90LL), + -reale(4590963,0x90f6e6e49ce20LL),reale(1692490,0x5de933ef26f30LL), + reale(420297,0x50d0b3d8c1d9bLL),reale(12305436712LL,0x56b51693aedc3LL), + // C4[1], coeff of eps^12, polynomial in n of order 17 + -reale(852919,0x6a82cfa963080LL),-reale(2188759,0x20ca5d762f800LL), + -reale(7786929,0x3421dcca91f80LL),-reale(65787035,0x1d560be049100LL), + reale(401061675,0x8c48395cfc980LL),-reale(458713135,0x22175c326fa00LL), + -reale(52544362,0x54a9b8a28c580LL),reale(267237346,0x9f71e62ba7d00LL), + -reale(98592445,0x567d144d01c80LL),reale(50019657,0x7efcd81e48400LL), + -reale(37374118,0xabf7952238b80LL),reale(21383288,0xfc61768bbcb00LL), + -reale(18992011,0x5234632e06280LL),reale(10406178,0xe1fef86250200LL), + -reale(9523344,0xe57e66503f180LL),reale(4398013,0x8a16c0de4d900LL), + -reale(2932033,0xa738784cb8880LL),reale(1764194,0xc6396b58af30cLL), + reale(12305436712LL,0x56b51693aedc3LL), + // C4[1], coeff of eps^11, polynomial in n of order 18 + -reale(210362,0x76b369d3025e0LL),-reale(399459,0x1eaf9acef0ab0LL), + -reale(856141,0xe229f972ba700LL),-reale(2206922,0xef935c87bb50LL), + -reale(7896496,0x6b0bc697c0820LL),-reale(67217074,0x2cc6331df1df0LL), + reale(414202467,0x2b5605d0252c0LL),-reale(483149583,0xa02db175d690LL), + -reale(45836711,0xc18042256fa60LL),reale(280420397,0xa9af8baa076d0LL), + -reale(104078404,0x7a91f5b525380LL),reale(50585814,0x9d940e3bb2630LL), + -reale(39015494,0x6a69555b81ca0LL),reale(20678727,0x5f0f1f3a9390LL), + -reale(19012332,0x416957968b9c0LL),reale(9200947,0xc21b589061af0LL), + -reale(8178296,0xad1e8ab768ee0LL),reale(2676456,0xd6956da2a1850LL), + reale(661843,0xede00571b821dLL),reale(12305436712LL,0x56b51693aedc3LL), + // C4[1], coeff of eps^10, polynomial in n of order 19 + -reale(73282,0x88acf774cdcc0LL),-reale(119856,0xfafc4232d6980LL), + -reale(209310,0xc95dad3d9d040LL),-reale(398728,0xc3246fdb30c00LL), + -reale(857927,0x8ca89fdf097c0LL),-reale(2222415,0x7f22a8f79ee80LL), + -reale(8002412,0xa401cae100b40LL),-reale(68698832,0xcf05dd2d1e900LL), + reale(428572510,0x4af905b8fd40LL),-reale(511480829,0xaa7af93dad380LL), + -reale(36412636,0xa51695c145640LL),reale(295430858,0x62539c3ab7a00LL), + -reale(110834541,0xf7ac6a286ddc0LL),reale(50648730,0xf42d6a1912780LL), + -reale(40882711,0xc825af61d7140LL),reale(19389515,0xc578a6be65d00LL), + -reale(18548541,0x30b0433e6e8c0LL),reale(7353872,0xa4f0c77ab4280LL), + -reale(5517208,0xc642445621c40LL),reale(3035548,0x619b33f1391d2LL), + reale(12305436712LL,0x56b51693aedc3LL), + // C4[1], coeff of eps^9, polynomial in n of order 20 + -reale(31116,0x5ced59f2a6a40LL),-reale(46466,0x39ef1648a3c30LL), + -reale(72339,0x13bec712995a0LL),-reale(118591,0xe96704ee23c10LL), + -reale(207681,0xf3272ddf69500LL),-reale(396975,0x5586a3fda15f0LL), + -reale(857776,0x96a9e394d3460LL),-reale(2234014,0x9c760527155d0LL), + -reale(8101033,0x1f3b77f93fc0LL),-reale(70217181,0xc7476a97287b0LL), + reale(444320933,0x84d59896b7ce0LL),-reale(544755366,0x60ab42e093790LL), + -reale(22958170,0x5fc77e584ca80LL),reale(312550991,0xea91e4bc80e90LL), + -reale(119474190,0x655c7a979e1e0LL),reale(49778595,0x69cfb591beb0LL), + -reale(42938053,0xad555dfab9540LL),reale(17185991,0x9567a8e814cd0LL), + -reale(16947236,0xc941a0517b0a0LL),reale(4507394,0xb6bfddcb2cf0LL), + reale(1103154,0xee71952935057LL),reale(12305436712LL,0x56b51693aedc3LL), + // C4[1], coeff of eps^8, polynomial in n of order 21 + -reale(15013,0x669ca85dbff00LL),-reale(21081,0x7f4d799198400LL), + -reale(30470,0xbdb587d74d900LL),-reale(45587,0xe4badb51b1a00LL), + -reale(71124,0x646ea35b6300LL),-reale(116891,0x8adb62aa4d000LL), + -reale(205315,0x1aa2ab2ec7d00LL),-reale(393884,0x4b8d8eda78600LL), + -reale(855000,0x2faa553050700LL),-reale(2239966,0xb31164c141c00LL), + -reale(8186764,0x97347e701e100LL),-reale(71742883,0x7f111739b7200LL), + reale(461586973,0x9a516d5401500LL),-reale(584418823,0xe1245bd6e6800LL), + -reale(3315305,0x14110f9c0500LL),reale(331936814,0x28269ca022200LL), + -reale(131069117,0x7ee7ad0730f00LL),reale(47184778,0x227a729454c00LL), + -reale(44897669,0x9cd1b2a1e900LL),reale(13574545,0xcd96a182a3600LL), + -reale(12485695,0x45db16a057300LL),reale(5879734,0x70bef82b8988LL), + reale(12305436712LL,0x56b51693aedc3LL), + // C4[1], coeff of eps^7, polynomial in n of order 22 + -reale(7900,0x638c66d8a8320LL),-reale(10613,0xf2ac3092c9cb0LL), + -reale(14565,0xe107ae27501c0LL),-reale(20489,0xead89ce414d0LL), + -reale(29670,0x849ce08edf860LL),-reale(44482,0xeb1f022729ef0LL), + -reale(69562,0xbdfcfee35b00LL),-reale(114632,0x975e8fa16f10LL), + -reale(201989,0x9411d71111da0LL),-reale(389021,0x33d7ff034b930LL), + -reale(848628,0xc0285ec233440LL),-reale(2237713,0xb97d9ca55b150LL), + -reale(8250880,0x9132887d792e0LL),-reale(73221392,0xf1ffe05c8b70LL), + reale(480452831,0x383b5471fd280LL),-reale(632496874,0xca3591eba7b90LL), + reale(26233104,0x13df159bb07e0LL),reale(353203487,0x101c2c33c4a50LL), + -reale(147596513,0x7a337ff05e6c0LL),reale(41406718,0x88562e0e69230LL), + -reale(45513246,0x22b5bfcbced60LL),reale(7934370,0xa8c8e9d8c2810LL), + reale(1869414,0xdc5c61854a479LL),reale(12305436712LL,0x56b51693aedc3LL), + // C4[1], coeff of eps^6, polynomial in n of order 23 + -reale(4406,0xf939ae5c97c40LL),-reale(5729,0xf863eba5bf80LL), + -reale(7570,0xa927e082c4c0LL),-reale(10189,0xdc3d2b5930900LL), + -reale(14011,0xfd72406188940LL),-reale(19751,0x4ee9330f94280LL), + -reale(28665,0xa6c18d00fb1c0LL),-reale(43078,0xe8ed052a45400LL), + -reale(67543,0xd4150add2640LL),-reale(111634,0xb28e55bb02580LL), + -reale(197389,0xccdd68505cec0LL),-reale(381765,0x22e00b9b89f00LL), + -reale(837258,0xa000eefe9340LL),-reale(2223425,0xd3d15b309a880LL), + -reale(8279438,0xc28db224c5bc0LL),-reale(74551261,0xb7816e54f2a00LL), + reale(500824278,0x3891b999befc0LL),-reale(691847154,0x918a2dd450b80LL), + reale(72461747,0xa045596356740LL),reale(374046829,0x41b777218cb00LL), + -reale(172833056,0x62b9485f4dd40LL),reale(29915148,0x80284d25e7180LL), + -reale(39423763,0x40d338467c5c0LL),reale(13659048,0x68e501c228ffeLL), + reale(12305436712LL,0x56b51693aedc3LL), + // C4[1], coeff of eps^5, polynomial in n of order 24 + -reale(2545,0x1363104362d80LL),-reale(3226,0xe67b1424a4830LL), + -reale(4144,0x8c711302fa660LL),-reale(5400,0xc1bfe2853af90LL), + -reale(7153,0xb2c26c1682b40LL),-reale(9653,0x9e8ef4e7cf0f0LL), + -reale(13308,0xeb09aee491820LL),-reale(18810,0x561040fe22850LL), + -reale(27375,0xc35e0fb3fc900LL),-reale(41260,0x7d7f41fc271b0LL), + -reale(64893,0xc7a96414399e0LL),-reale(107622,0xe02e2157de910LL), + -reale(191035,0x6ce8a0a1be6c0LL),-reale(371181,0x96988a373aa70LL), + -reale(818768,0xa91a46aa60ba0LL),-reale(2191167,0x9fde37effd1d0LL), + -reale(8249435,0xe27cdc35b6480LL),-reale(75540143,0x55cc77d97b30LL), + reale(522119910,0xf5aa540a8b2a0LL),-reale(766397212,0x64559a510c290LL), + reale(148547296,0x8152775e2ddc0LL),reale(385247751,0x81b301a133c10LL), + -reale(213402544,0x90fce845e3f20LL),reale(10198756,0x255c7c31664b0LL), + reale(1365904,0xd74a19c69db33LL),reale(12305436712LL,0x56b51693aedc3LL), + // C4[1], coeff of eps^4, polynomial in n of order 25 + -real(0x5cd20bbc3c672180LL),-real(0x73720b2d98187c00LL), + -reale(2321,0xc4eb857568680LL),-reale(2952,0xb2617088c8f00LL), + -reale(3804,0x417bd8fa2e380LL),-reale(4973,0x5ec86f601d200LL), + -reale(6609,0x998272f30a880LL),-reale(8950,0x197c7ab46b500LL), + -reale(12382,0xcc481e2a44580LL),-reale(17565,0x5f7861969a800LL), + -reale(25660,0x4a6f330e22a80LL),-reale(38825,0xe447100991b00LL), + -reale(61313,0x47573aa0ec780LL),-reale(102123,0xa55bb6037e00LL), + -reale(182121,0xfb4d0590e8c80LL),-reale(355742,0x340be91b74100LL), + -reale(789743,0xf318e4285e980LL),-reale(2131260,0x2c59b0f82d400LL), + -reale(8121193,0x3f9cc7c594e80LL),-reale(75808472,0x814742dd4a700LL), + reale(542406027,0xe15955752d480LL),-reale(860719085,0xb088c959b2a00LL), + reale(281794203,0x6d691a09a0f80LL),reale(349671639,0x4a19c69db3300LL), + -reale(268081590,0x1f35e51280d80LL),reale(42616231,0x9d4bdce6b704LL), + reale(12305436712LL,0x56b51693aedc3LL), + // C4[1], coeff of eps^3, polynomial in n of order 26 + -real(0x34f88b61ee2c2e60LL),-real(0x40e8b73250ad02b0LL), + -real(0x50402824a1190680LL),-real(0x643133a56bf6de50LL), + -real(0x7e70b50d7e53aea0LL),-reale(2583,0x89ee9103c6bf0LL), + -reale(3343,0x2d56b6f20aac0LL),-reale(4390,0x9150bee746f90LL), + -reale(5862,0xecb9ee1767ee0LL),-reale(7978,0x9b4551158ad30LL), + -reale(11096,0x13774a5e7af00LL),-reale(15825,0x3f23db737e8d0LL), + -reale(23248,0xf45a340cbf20LL),-reale(35380,0xaf4478627e670LL), + -reale(56209,0x8a81f32e3340LL),-reale(94205,0x2f98ae2576a10LL), + -reale(169093,0xeae4ad4ee8f60LL),-reale(332577,0xf0ed8664037b0LL), + -reale(743995,0x906300fb45780LL),-reale(2026493,0x9c6e844791350LL), + -reale(7821602,0x7531c16940fa0LL),-reale(74557824,0x1ed43b2e7c0f0LL), + reale(555703654,0x34418f385c440LL),-reale(974709694,0x84f4a67130490LL), + reale(527421389,0x42f7f1faaa020LL),reale(94702735,0xa411a5cab5dd0LL), + -reale(117194635,0x5b0909f7a774bLL), + reale(12305436712LL,0x56b51693aedc3LL), + // C4[1], coeff of eps^2, polynomial in n of order 27 + -real(0x1bd57a8f504dd3c0LL),-real(0x21b6ff10b9172180LL), + -real(0x292825cda3a88940LL),-real(0x32aacbfadedfca00LL), + -real(0x3ef38a62fa0322c0LL),-real(0x4f013a1cfd80d280LL), + -real(0x64414a4729c69840LL),-reale(2060,0x90ead26a03300LL), + -reale(2683,0x237c6d92be1c0LL),-reale(3547,0x3d9a05c33e380LL), + -reale(4770,0x6ec9da59bf740LL),-reale(6541,0x1657e411dc00LL), + -reale(9170,0x1a8b4944fd0c0LL),-reale(13190,0xb069410801480LL), + -reale(19554,0x9e393a3b06640LL),-reale(30047,0xba30505448500LL), + -reale(48224,0x707d4f4f6afc0LL),-reale(81689,0xf05ca40b52580LL), + -reale(148265,0xab90de58ba540LL),-reale(294962,0x64373b047ee00LL), + -reale(667587,0xc0c688fa83ec0LL),-reale(1840377,0xc842d822d680LL), + -reale(7199121,0xfc41489b57440LL),-reale(69934327,0xdb9ec152bd700LL), + reale(541991040,0xe60e5a413c240LL),-reale(1060670639,0x2d9274118e780LL), + reale(833384073,0xa3ce7fc4a6cc0LL),-reale(234389270,0xb61213ef4ee96LL), + reale(12305436712LL,0x56b51693aedc3LL), + // C4[1], coeff of eps^1, polynomial in n of order 28 + -real(0xb4c355cd41c92c0LL),-real(0xd8fea3a41cc7830LL), + -real(0x1064f0c6b9a6ad20LL),-real(0x13f7a88902ef1b10LL), + -real(0x1884a414973fcb80LL),-real(0x1e5fa2ae5243d7f0LL), + -real(0x25fe0bb384ddd9e0LL),-real(0x3006f6e3e0e25ad0LL), + -real(0x3d6c2c13c34ec440LL),-real(0x4f91f34825bd4fb0LL), + -real(0x688ffb74f98676a0LL),-reale(2233,0xdec33bb086290LL), + -reale(3036,0xe53843c2cdd00LL),-reale(4213,0xb13e1137e3f70LL), + -reale(5984,0xaa1cca8abe360LL),-reale(8732,0xb9880d6c69250LL), + -reale(13152,0x1eadcfcfd75c0LL),-reale(20566,0x4e1752c3c0730LL), + -reale(33653,0xf4262a5798020LL),-reale(58247,0x3a420e3524a10LL), + -reale(108257,0x7934f39e3ee80LL),-reale(221025,0xaccc1c0dc06f0LL), + -reale(514222,0xffbb852faace0LL),-reale(1456965,0x29e8a4070e9d0LL), + -reale(5827860,0xa7a2901c3a740LL),-reale(56821641,0x6270fd1339eb0LL), + reale(416692036,0xd1e73fe253660LL),-reale(625038055,0x3adadfd37d190LL), + reale(273454149,0x29bfc1ec86bafLL), + reale(12305436712LL,0x56b51693aedc3LL), + // C4[2], coeff of eps^29, polynomial in n of order 0 + 185528,real(30429886905LL), + // C4[2], coeff of eps^28, polynomial in n of order 1 + real(17366491968LL),real(4404238552LL),real(0x74e318fa9c07fLL), + // C4[2], coeff of eps^27, polynomial in n of order 2 + real(412763643136LL),-real(248137794944LL),real(164642704408LL), + real(0x4d882f0532d9e9LL), + // C4[2], coeff of eps^26, polynomial in n of order 3 + real(0x11462b92d913a0LL),-real(0xdd4620ebadc40LL), + real(0x5974730e46be0LL),real(0x16bcec57851ccLL), + reale(33547,0x1cf91962af003LL), + // C4[2], coeff of eps^25, polynomial in n of order 4 + real(0xc83679b433c00LL),-real(0xb29b6d58dfb00LL),real(0x5f4e3bdd4de00LL), + -real(0x3affd9960e900LL),real(0x2665fb625f490LL), + reale(15809,0x8f200ee7e2a7dLL), + // C4[2], coeff of eps^24, polynomial in n of order 5 + real(0x67b92a8524a18e80LL),-real(0x609d7d3ca356ae00LL), + real(0x39db180d1b52d580LL),-real(0x2fa1e9183dec9700LL), + real(0x1294d8f2627edc80LL),real(0x4bc94ddbc9bad70LL), + reale(22813193,0xc1b4051297e97LL), + // C4[2], coeff of eps^23, polynomial in n of order 6 + reale(24830,0x3d0fb879bb600LL),-reale(23212,0xa100635ccdb00LL), + reale(14957,0x147cd156ba400LL),-reale(13653,0x51ea4b9c89d00LL), + reale(7024,0x2535370909200LL),-reale(4511,0x3af63b60c9f00LL), + reale(2865,0xf50f5adcce1f0LL),reale(235736335,0x7c44346acc6c3LL), + // C4[2], coeff of eps^22, polynomial in n of order 7 + reale(1046092,0x25a6222f26060LL),-reale(949436,0x14a3a722f1840LL), + reale(652845,0xb96689ab42720LL),-reale(615919,0x6f1345ab50580LL), + reale(356624,0x982d38f2a9de0LL),-reale(303839,0x22c37d5c832c0LL), + reale(113262,0x286189b57e4a0LL),reale(28978,0x12ae8b059bc84LL), + reale(6836353729LL,0x13b9f01928417LL), + // C4[2], coeff of eps^21, polynomial in n of order 8 + reale(4643688,0x71b79cbf7cc00LL),-reale(3959056,0x83e38a4f9d180LL), + reale(2926140,0x6f81ce5fc3900LL),-reale(2722736,0xdd03df5282c80LL), + reale(1710940,0xc70403130e600LL),-reale(1602990,0x9ebb76967a780LL), + reale(787738,0x6bf60987b1300LL),-reale(530212,0xcde2a88ab0280LL), + reale(326645,0xab9033855e368LL),reale(20509061187LL,0x3b2dd04b78c45LL), + // C4[2], coeff of eps^20, polynomial in n of order 9 + reale(2366152,0x4fc26559c91c0LL),-reale(1830925,0x4d73259824200LL), + reale(1477489,0x62c9a90a52a40LL),-reale(1299560,0xe7bf798235180LL), + reale(885946,0x5cb0a99f5e2c0LL),-reale(843740,0x47153eb842100LL), + reale(469359,0x79db9d7cfb40LL),-reale(417111,0x1a4c5e2477080LL), + reale(146559,0x51b0aa3dcb3c0LL),reale(37677,0x6dd5ee66abd48LL), + reale(6836353729LL,0x13b9f01928417LL), + // C4[2], coeff of eps^19, polynomial in n of order 10 + reale(11390177,0xa8f910291300LL),-reale(7729638,0x6f23cf47c2480LL), + reale(6929266,0x5fb765e065c00LL),-reale(5514735,0x5eb0876136380LL), + reale(4148166,0x27d6c40aa500LL),-reale(3788609,0xfef33001c8280LL), + reale(2322601,0x1de03c2bc2e00LL),-reale(2237878,0x77b7642b94180LL), + reale(1037457,0x571c66f013700LL),-reale(742165,0x8c39e6d5b6080LL), + reale(439349,0xf7cfa6e796fc8LL),reale(20509061187LL,0x3b2dd04b78c45LL), + // C4[2], coeff of eps^18, polynomial in n of order 11 + reale(19643005,0x3eb0d373a0e0LL),-reale(11359402,0x98e8f09139c0LL), + reale(11381255,0xacc1b03fd73a0LL),-reale(7834592,0x92741bdd3b00LL), + reale(6664656,0xa317edb25b660LL),-reale(5516050,0x3ff87cc43bc40LL), + reale(3774293,0xd5e83edc68920LL),-reale(3594547,0xbec9f61701d80LL), + reale(1908400,0x61c5f793c0be0LL),-reale(1786093,0xfaf3f7a19bec0LL), + reale(579905,0x9d50696085ea0LL),reale(150042,0xa9efa9004c604LL), + reale(20509061187LL,0x3b2dd04b78c45LL), + // C4[2], coeff of eps^17, polynomial in n of order 12 + reale(38321815,0x1e48683dc9800LL),-reale(18616913,0x727791f8dfa00LL), + reale(20113440,0xb841223d75400LL),-reale(11495937,0x9838f29931e00LL), + reale(11261630,0x21fd3747b1000LL),-reale(7960716,0x75135ee9c200LL), + reale(6275150,0xa8a2fa972cc00LL),-reale(5471565,0x945df446e600LL), + reale(3293426,0x6eab44c698800LL),-reale(3257897,0x559df659f8a00LL), + reale(1401057,0x756ea738a4400LL),-reale(1086629,0xf49cb94a8ae00LL), + reale(610116,0x479bdc6c290e0LL),reale(20509061187LL,0x3b2dd04b78c45LL), + // C4[2], coeff of eps^16, polynomial in n of order 13 + reale(102781113,0x98fe5a9192500LL),-reale(40336104,0xccc089a851400LL), + reale(40165652,0x6e617f3b73300LL),-reale(18616625,0x95536d5576600LL), + reale(20514709,0xd39b96f5ec100LL),-reale(11691503,0x7c1154bb0b800LL), + reale(10980290,0x40d1adbe6cf00LL),-reale(8104717,0x4a433bfb60a00LL), + reale(5726151,0xc3b2b2965d00LL),-reale(5331323,0xa4559d80c5c00LL), + reale(2689333,0x7cf2f82446b00LL),-reale(2678624,0x7904ff2b8ae00LL), + reale(779755,0xfacbca777f900LL),reale(203539,0xb4670b88476e0LL), + reale(20509061187LL,0x3b2dd04b78c45LL), + // C4[2], coeff of eps^15, polynomial in n of order 14 + -reale(23295494,0x8be82e34e6400LL),-reale(256522224,0x1264f586eb600LL), + reale(109420782,0x9692235ce1800LL),-reale(40005401,0x76f47ac799a00LL), + reale(42210732,0x9175627089400LL),-reale(18637789,0x360d04338fe00LL), + reale(20777547,0x32d7f69c1000LL),-reale(11978808,0x3c6fce691e200LL), + reale(10467739,0x890cbd2438c00LL),-reale(8246695,0x5d95a89294600LL), + reale(4981450,0x2e83f5dba0800LL),-reale(4997884,0x48d2490e42a00LL), + reale(1949724,0xd6b9d613a8400LL),-reale(1687002,0x42840cd678e00LL), + reale(881316,0x5154c853b06e0LL),reale(20509061187LL,0x3b2dd04b78c45LL), + // C4[2], coeff of eps^14, polynomial in n of order 15 + -reale(315852553,0x127aa1fb9560LL),reale(452067016,0x32f06289dc340LL), + -reale(36389203,0xc905d2dd0bc20LL),-reale(265701999,0x414c3c9652f80LL), + reale(117462481,0xb44ff33f8ed20LL),-reale(39375172,0xb9e521c5c6240LL), + reale(44443567,0x98c20ae94660LL),-reale(18737379,0x9088d09ce7500LL), + reale(20789662,0x74772cb6e2fa0LL),-reale(12399165,0xc39cbc16e07c0LL), + reale(9634015,0x48be8ec7788e0LL),-reale(8326007,0x8f1246dddba80LL), + reale(4012687,0x8a9763f933220LL),-reale(4283805,0xe15bd5742d40LL), + reale(1064918,0x3e0322e890b60LL),reale(281445,0x189dacfa2913cLL), + reale(20509061187LL,0x3b2dd04b78c45LL), + // C4[2], coeff of eps^13, polynomial in n of order 16 + reale(4607575,0xc9d7900c88800LL),reale(44527228,0x61b96ac1eb380LL), + -reale(320302478,0xa276d3450e900LL),reale(471382647,0x4d0623cc86a80LL), + -reale(52535715,0x404f1a5b09a00LL),-reale(275262322,0xf3348bb543e80LL), + reale(127364360,0xbf0504ec13500LL),-reale(38376532,0x74833ebc78780LL), + reale(46801690,0x6a3245e5c4400LL),-reale(19021914,0x3bda110f1b080LL), + reale(20372666,0xf7fc04d85300LL),-reale(12992077,0x825700022f980LL), + reale(8374681,0xba502a56d2200LL),-reale(8187369,0x8d48a8bba280LL), + reale(2818780,0x7113503f27100LL),-reale(2834494,0xf2038f04beb80LL), + reale(1337917,0xc906f381aecf8LL),reale(20509061187LL,0x3b2dd04b78c45LL), + // C4[2], coeff of eps^12, polynomial in n of order 17 + reale(388658,0x19c7c6f8ea2c0LL),reale(1117971,0xaadcbdb38ac00LL), + reale(4519560,0xaee28ee393540LL),reale(44278119,0xe09b9f50af680LL), + -reale(324493551,0x5c00bae29840LL),reale(492697628,0x7d1cc3fd18100LL), + -reale(72657626,0xb42806bf185c0LL),-reale(284925253,0x57cc84a557480LL), + reale(139770748,0x33e950dc3acc0LL),-reale(36961790,0xef70c005baa00LL), + reale(49119876,0xa052562f03f40LL),-reale(19681131,0xbaa50226adf80LL), + reale(19252422,0xc3af9265b71c0LL),-reale(13755373,0x2f0960c0cd500LL), + reale(6600104,0x6565773f88440LL),-reale(7462805,0xbfb982e534a80LL), + reale(1452711,0x6b2cd84feb6c0LL),reale(390635,0x965de9321fbe8LL), + reale(20509061187LL,0x3b2dd04b78c45LL), + // C4[2], coeff of eps^11, polynomial in n of order 18 + reale(73868,0xf53613318fd00LL),reale(155158,0x6bea1fc037e80LL), + reale(370865,0xe686995a3a800LL),reale(1077531,0xb6b00d00e5180LL), + reale(4409046,0x1d5f244685300LL),reale(43860006,0xf94485a638480LL), + -reale(328226208,0x254b380304200LL),reale(516242826,0x48cfde1d3d780LL), + -reale(98028430,0xc7227901d5700LL),-reale(294125055,0xf41dd5cbff580LL), + reale(155591277,0xc58331ae9d400LL),-reale(35168366,0x6c3820d072280LL), + reale(51023141,0xfcae9f00dff00LL),-reale(21033813,0x6b0840ce0ef80LL), + reale(17035669,0xa0ab037f7ea00LL),-reale(14520825,0x209891efc9c80LL), + reale(4321952,0xda1143d705500LL),-reale(5322397,0x9ed9b44796980LL), + reale(2165443,0xa5af00ad58358LL),reale(20509061187LL,0x3b2dd04b78c45LL), + // C4[2], coeff of eps^10, polynomial in n of order 19 + reale(19809,0x63304b335a660LL),reale(35566,0xcb4164f348e40LL), + reale(68577,0xe86c972757e20LL),reale(145245,0xbc9cc7446e200LL), + reale(350489,0x7e29a3d4285e0LL),reale(1029750,0x45087f82835c0LL), + reale(4270842,0x2203011585da0LL),reale(43220702,0xa65b618eca980LL), + -reale(331199124,0xa89ccd5235aa0LL),reale(542217711,0x200e3727c5d40LL), + -reale(130429686,0x3b8b1d50d02e0LL),-reale(301749371,0x2c4d836f88f00LL), + reale(176097282,0x8ddfe73d104e0LL),-reale(33280999,0x8c12e2a85fb40LL), + reale(51717673,0x23cc103525ca0LL),-reale(23558374,0x76fe0e70fc780LL), + reale(13250268,0x69c1c450ca460LL),-reale(14595460,0xd8a80a3d5d3c0LL), + reale(1848614,0x7d3564e37c20LL),reale(506231,0x2a6100a6a6db4LL), + reale(20509061187LL,0x3b2dd04b78c45LL), + // C4[2], coeff of eps^9, polynomial in n of order 20 + reale(6397,0xfcd62c9faa400LL),reale(10440,0x3fc8ff8e75700LL), + reale(17841,0xb7bede1dba00LL),reale(32272,0x7935213063d00LL), + reale(62742,0x8933a9bfd5000LL),reale(134128,0x223daf23d6300LL), + reale(327129,0xfca43cca0e600LL),reale(973230,0x31dda9e44900LL), + reale(4098328,0x3528b970ffc00LL),reale(42289297,0xe5d54d5326f00LL), + -reale(332951092,0xecfda756dee00LL),reale(570709002,0x2878cf4ff5500LL), + -reale(172380399,0x5788b53115800LL),-reale(305626020,0x9c65fcc7d8500LL), + reale(202987914,0xbd0aab0ad3e00LL),-reale(32233434,0x3f0406dec9f00LL), + reale(49604551,0xc747777555400LL),-reale(27757216,0x323bffb167900LL), + reale(7652705,0x1c15203ae6a00LL),-reale(11782806,0x2b7827f239300LL), + reale(3811565,0x362856b8e6d30LL),reale(20509061187LL,0x3b2dd04b78c45LL), + // C4[2], coeff of eps^8, polynomial in n of order 21 + reale(2297,0xe5959dcaf9680LL),reale(3515,0xaf44e93439a00LL), + reale(5557,0xf844363205d80LL),reale(9134,0x3148872cf3100LL), + reale(15730,0x1f27208afe480LL),reale(28695,0xbe2e993314800LL), + reale(56314,0x2c7b05479ab80LL),reale(121661,0x287926e675f00LL), + reale(300328,0xfc8a376113280LL),reale(906274,0xf1fb199eef600LL), + reale(3883000,0x5f528c391f980LL),reale(40968060,0xe6e08c5558d00LL), + -reale(332763533,0x8282a4a507f80LL),reale(601507851,0xf6ba284c8a400LL), + -reale(227453313,0x642fd223ab880LL),-reale(301473974,0xbe5976c5a4500LL), + reale(238209921,0x57c5b91e6ce80LL),-reale(34582562,0x41ecac4f5ae00LL), + reale(41696071,0xee870caef9580LL),-reale(33183269,0xa456f79c1700LL), + reale(1407347,0x27b05f0931c80LL),reale(329283,0x26010fabff570LL), + reale(20509061187LL,0x3b2dd04b78c45LL), + // C4[2], coeff of eps^7, polynomial in n of order 22 + real(0x367dbe5da7953e00LL),real(0x4f9a921ac6fb1900LL), + real(0x773454548df74400LL),reale(2938,0xbc18faed4af00LL), + reale(4681,0x407a350a64a00LL),reale(7756,0xa0ed83ee90500LL), + reale(13477,0x2fbfd87edd000LL),reale(24826,0x9ea174e739b00LL), + reale(49249,0xd3391f1d95600LL),reale(107696,0xcac2013cff100LL), + reale(269571,0xe064d3a745c00LL),reale(826840,0x70825da398700LL), + reale(3613882,0x7ef0aa40a6200LL),reale(39120270,0xc5673698bdd00LL), + -reale(329492011,0x53f65ac991800LL),reale(633695353,0xfeb5c44027300LL), + -reale(300630213,0xecf09fbea9200LL),-reale(280700646,0xcee0a2073700LL), + reale(282664342,0x7b726e8a17400LL),-reale(46720160,0x11dfe8c55a100LL), + reale(23527957,0x90f427ad67a00LL),-reale(33848503,0x5eac35f0d4b00LL), + reale(7456233,0x7c1f0b332cab0LL),reale(20509061187LL,0x3b2dd04b78c45LL), + // C4[2], coeff of eps^6, polynomial in n of order 23 + real(0x14f52a063dc5fc20LL),real(0x1d93a1e9ceb48740LL), + real(0x2a911c303b723a60LL),real(0x3ea26bba66a54980LL), + real(0x5e84fad71b3608a0LL),reale(2349,0x85d3117e94bc0LL), + reale(3776,0x1c9d51cf2c6e0LL),reale(6317,0x5193932d16e00LL), + reale(11091,0xc7716ff97d520LL),reale(20667,0xe33c2c4a29040LL), + reale(41523,0x1a30a42ae9360LL),reale(92100,0xbd0a1f1419280LL), + reale(234309,0x70b77706661a0LL),reale(732507,0x72fafb4df54c0LL), + reale(3276808,0xe462aef209fe0LL),reale(36551902,0x4c4d10a4b700LL), + -reale(321265885,0x720bf168351e0LL),reale(664675522,0x65892c55e9940LL), + -reale(398339257,0x2b82ef41c13a0LL),-reale(225754486,0xf240500d62480LL), + reale(330356701,0xbb7252695baa0LL),-reale(82401980,0x37f104ae0a240LL), + -reale(4970822,0x52bf5cccc8720LL),-reale(3278171,0x9e4b710fe0e14LL), + reale(20509061187LL,0x3b2dd04b78c45LL), + // C4[2], coeff of eps^5, polynomial in n of order 24 + real(0x7d5242068d47400LL),real(0xac3832c9e621080LL), + real(0xf0840d5e59cf500LL),real(0x155fabefd3362980LL), + real(0x1f01ffac4c30b600LL),real(0x2e0489bbd6aca280LL), + real(0x461560bdbc05f700LL),real(0x6df6210d29c3bb80LL), + reale(2857,0xf2e1b87d2f800LL),reale(4836,0xd8d8f4249b480LL), + reale(8600,0x17271d36df900LL),reale(16248,0x163bc1ffccd80LL), + reale(33146,0xc23750bad3a00LL),reale(74792,0x260310eab4680LL), + reale(194024,0xef2cdae46fb00LL),reale(620545,0xfcf47db535f80LL), + reale(2853712,0x7228ad7b17c00LL),reale(32984640,0x1c4ce82435880LL), + -reale(304937768,0x83ef272fd0300LL),reale(687819348,0xf9e0f9c397180LL), + -reale(526420007,0xa1ce2482e4200LL),-reale(101220737,0xb065c6f7c1580LL), + reale(344186593,0xf79ee4a13ff00LL),-reale(151524377,0x682a2ddefc80LL), + reale(15298134,0x380aba4a19708LL),reale(20509061187LL,0x3b2dd04b78c45LL), + // C4[2], coeff of eps^4, polynomial in n of order 25 + real(0x2b077c634ede840LL),real(0x39e80232e455600LL), + real(0x4f004399e9803c0LL),real(0x6d6a8dd96e7d980LL), + real(0x9a16639c690ff40LL),real(0xdd0eb6a29ee1d00LL), + real(0x143ca2e567649ac0LL),real(0x1e583a687f6ce080LL), + real(0x2ebb5ae27bca9640LL),real(0x4a366ef6d0a8e400LL), + real(0x7a244f6987aeb1c0LL),reale(3355,0xff6a995ee780LL), + reale(6059,0x95d9afc38ad40LL),reale(11647,0x91c4ac30bab00LL), + reale(24220,0xbe377a4d448c0LL),reale(55835,0xd9394a033ee80LL), + reale(148417,0x27a782b394440LL),reale(488256,0xe5126fdac7200LL), + reale(2322515,0xb040a0735fc0LL),reale(28019858,0x3d9464fe1f580LL), + -reale(275064197,0x290d46715a4c0LL),reale(686424553,0x6984a82213900LL), + -reale(677745912,0x9f6fb36960940LL),reale(151524377,0x682a2ddefc80LL), + reale(169007958,0xfd6a53329f240LL),-reale(85232462,0x13a97b9cd6e08LL), + reale(20509061187LL,0x3b2dd04b78c45LL), + // C4[2], coeff of eps^3, polynomial in n of order 26 + real(0xc4c78b5f73e700LL),real(0x1046756e5efb980LL), + real(0x15cbc98d9fba400LL),real(0x1d9279681ffce80LL), + real(0x28b2f34344c6100LL),real(0x38e6214caec8380LL), + real(0x50f0f0d0c655e00LL),real(0x7563dc0de2d1880LL), + real(0xadfad5eb325db00LL),real(0x1083ab8775a8cd80LL), + real(0x19c9d8efc1ad1800LL),real(0x29945e7f0056e280LL), + real(0x4594bf2102ba5500LL),real(0x79a9d12705de9780LL), + reale(3587,0xb2b264e0cd200LL),reale(7053,0x1d58043372c80LL), + reale(15040,0x44c8073c3cf00LL),reale(35667,0x702872e47e180LL), + reale(97902,0x6929355be8c00LL),reale(334186,0x1d1de4e87f680LL), + reale(1659947,0xed2beccfc4900LL),reale(21110207,0x53559189eab80LL), + -reale(222144335,0x8c70c0703ba00LL),reale(617753229,0x694fabb034080LL), + -reale(769277606,0x6fd24e8e23d00LL),reale(454573131,0x1387e899cf580LL), + -reale(104173009,0x3479cff894d98LL), + reale(20509061187LL,0x3b2dd04b78c45LL), + // C4[2], coeff of eps^2, polynomial in n of order 27 + real(0x24546bc28a93e0LL),real(0x2f6c4d745b8e40LL), + real(0x3e90f252c210a0LL),real(0x5380c389acd700LL), + real(0x70da9adde57d60LL),real(0x9aa08aca5a9fc0LL), + real(0xd7127fe199fa20LL),real(0x130248120008880LL), + real(0x1b6103e1c56a6e0LL),real(0x283fa247b6e3140LL), + real(0x3c89da46fe8a3a0LL),real(0x5d71643158b3a00LL), + real(0x948b363af771060LL),real(0xf445a32263b42c0LL), + real(0x1a1d56e9fe070d20LL),real(0x2ecb290f0241eb80LL), + real(0x58a5da95527fb9e0LL),reale(2876,0x680343126d440LL), + reale(6354,0x3e35c062e36a0LL),reale(15689,0x7d2910c199d00LL), + reale(45107,0x47d6102c9a360LL),reale(162386,0x35cf6d6d5e5c0LL), + reale(857038,0x54e3334f72020LL),reale(11655721,0x4f45203874e80LL), + -reale(131126864,0xbbc9aa7b23320LL),reale(378810942,0x9046972ad7740LL), + -reale(416692036,0xd1e73fe253660LL),reale(156259513,0xceb6b7f4df464LL), + reale(20509061187LL,0x3b2dd04b78c45LL), + // C4[3], coeff of eps^29, polynomial in n of order 0 + 594728,real(456448303575LL), + // C4[3], coeff of eps^28, polynomial in n of order 1 + -real(3245452288LL),real(1965206256),real(0x17609e98859b3LL), + // C4[3], coeff of eps^27, polynomial in n of order 2 + -real(0x15f49b7dd3600LL),real(0x7876e24c6900LL),real(0x1f5dd75c0b28LL), + reale(4837,0x68f14547adebLL), + // C4[3], coeff of eps^26, polynomial in n of order 3 + -real(0x33418e8004000LL),real(0x17b00d59dc000LL), + -real(0x11669ade1c000LL),real(0xa37322475bc0LL), + reale(6709,0x6c31d1e089667LL), + // C4[3], coeff of eps^25, polynomial in n of order 4 + -real(0xc3e38d2fc36800LL),real(0x6a604d6faf7a00LL), + -real(0x650b3de948f400LL),real(0x20a6596010be00LL), + real(0x88f534a1fae70LL),reale(275086,0x53fa9cf60167fLL), + // C4[3], coeff of eps^24, polynomial in n of order 5 + -real(0xdd5f9d233a5800LL),real(0x8b724926c9e000LL), + -real(0x8af41510346800LL),real(0x3d05686ce77000LL), + -real(0x2f9901c72df800LL),real(0x1ae74f29ea4ce0LL), + reale(223345,0xf3eec944ed143LL), + // C4[3], coeff of eps^23, polynomial in n of order 6 + -reale(81630,0xcf55ff9c68c00LL),reale(60811,0x59dd5ef6a6e00LL), + -reale(57592,0x6457f059a8800LL),reale(30387,0x2572e53b9c200LL), + -reale(30167,0xe11b4690d8400LL),reale(9044,0xd72699d03d600LL), + reale(2392,0x21f43a8f7f830LL),reale(990092609,0x9eb428d5a933LL), + // C4[3], coeff of eps^22, polynomial in n of order 7 + -reale(3070961,0xf14af9164000LL),reale(2767073,0x4d2d51bbc4000LL), + -reale(2322170,0xf623e90f3c000LL),reale(1476552,0x4ed8bf53f8000LL), + -reale(1490469,0x7e13eaba44000LL),reale(616004,0x8b84c9ea6c000LL), + -reale(517487,0xf3178ed39c000LL),reale(279040,0x23dc4dd774ec0LL), + reale(28712685662LL,0x1fa68a0342ac7LL), + // C4[3], coeff of eps^21, polynomial in n of order 8 + -reale(3998482,0x374a7520d6800LL),reale(4351696,0x89a9dbf785900LL), + -reale(3077852,0x4b8dc9fbd6e00LL),reale(2436308,0x9b47462d3fb00LL), + -reale(2230379,0xda399323b400LL),reale(1147885,0x7a5199072bd00LL), + -reale(1196012,0x91bb473d37a00LL),reale(325643,0x5e75ef9e35f00LL), + reale(87110,0x728c765d95698LL),reale(28712685662LL,0x1fa68a0342ac7LL), + // C4[3], coeff of eps^20, polynomial in n of order 9 + -reale(5536106,0x41a6dc97e5400LL),reale(6819318,0x7020ae33aa000LL), + -reale(3996497,0x7d04a5d65ec00LL),reale(4026336,0x4a526eb153800LL), + -reale(3081046,0x922df73cac400LL),reale(2027203,0x8c3cc70035000LL), + -reale(2046086,0x4cc9bc51b5c00LL),reale(787253,0x8fa9057e6800LL), + -reale(725367,0x21dd9ffc63400LL),reale(368582,0x69a43eb914890LL), + reale(28712685662LL,0x1fa68a0342ac7LL), + // C4[3], coeff of eps^19, polynomial in n of order 10 + -reale(8942538,0x3b8622ae62a00LL),reale(10481872,0x1e7c948175300LL), + -reale(5381394,0x830498d800800LL),reale(6645195,0x535f47efddd00LL), + -reale(4043713,0x9ba9cf138e600LL),reale(3563786,0x6253b3df24700LL), + -reale(3045580,0xe2f1f7a110400LL),reale(1548984,0x4828fbf665100LL), + -reale(1694435,0x63dcfc138a200LL),reale(406057,0xe76a74dc3bb00LL), + reale(110280,0xa64ca1bbeb438LL),reale(28712685662LL,0x1fa68a0342ac7LL), + // C4[3], coeff of eps^18, polynomial in n of order 11 + -reale(18204995,0x3f490d6ed8000LL),reale(15367333,0xa666c37198000LL), + -reale(8424707,0xb9613a5da8000LL),reale(10765521,3190860555LL<<17), + -reale(5300295,0xd300940f58000LL),reale(6273886,0xba1b2aa228000LL), + -reale(4137511,0x6a32b5bc28000LL),reale(2951915,0x3ffeb65fb0000LL), + -reale(2898950,0x38c8743c58000LL),reale(1027617,0x2c3889c5b8000LL), + -reale(1062542,0x7c8a4a4828000LL),reale(500325,0x147f19cd83980LL), + reale(28712685662LL,0x1fa68a0342ac7LL), + // C4[3], coeff of eps^17, polynomial in n of order 12 + -reale(46659673,0x7940546261000LL),reale(20576887,0xb72d09f420c00LL), + -reale(17371112,0xc460beb873800LL),reale(16552256,0x8d133b2d84400LL), + -reale(7883306,0x3c181b1016000LL),reale(10867815,0x95ba8c80bfc00LL), + -reale(5343012,0x31a34980f8800LL),reale(5640245,0x12558783a3400LL), + -reale(4241979,0x47a64b12cb000LL),reale(2204426,0xf7d60f21fec00LL), + -reale(2506924,0x6e46ed413d800LL),reale(503732,0xa322eb69a2400LL), + reale(139663,0x777cb98300b20LL),reale(28712685662LL,0x1fa68a0342ac7LL), + // C4[3], coeff of eps^16, polynomial in n of order 13 + -reale(156865464,0x9b4a437ced000LL),reale(26751997,0x84cabd1d8c000LL), + -reale(47510066,0xf418e3e50b000LL),reale(22667291,0xeea5410a3a000LL), + -reale(16175537,0xc4ceea20b9000LL),reale(17818506,0xfb6c54d608000LL), + -reale(7402653,0x2459922697000LL),reale(10650742,0xeb52d29456000LL), + -reale(5558253,0xfdda6aad45000LL),reale(4690304,0xc3737ed884000LL), + -reale(4248624,0xb4bb4dab63000LL),reale(1382140,0xc755b095f2000LL), + -reale(1646389,0x4c787b5791000LL),reale(701746,0xdc0286e009640LL), + reale(28712685662LL,0x1fa68a0342ac7LL), + // C4[3], coeff of eps^15, polynomial in n of order 14 + reale(158569992,0x763cf17d39800LL),reale(242045827,0xf358b9d531400LL), + -reale(171801710,0xfbdaa54751000LL),reale(26564510,0xe59a1e6b54c00LL), + -reale(47715397,0x8fdbdb93bb800LL),reale(25503418,0x124aa89300400LL), + -reale(14593564,0x65519680b6000LL),reale(19028249,0x27fd86c303c00LL), + -reale(7127523,0x40a42052f0800LL),reale(9926805,0x1876eddc2f400LL), + -reale(5956098,0xfb7e2f3f1b000LL),reale(3422018,0xde3cf0f552c00LL), + -reale(3909386,0x4ce6da2de5800LL),reale(606166,0xec68c0e73e400LL), + reale(172919,0x9ad62b665b520LL),reale(28712685662LL,0x1fa68a0342ac7LL), + // C4[3], coeff of eps^14, polynomial in n of order 15 + reale(234628808,0x48818da828000LL),-reale(452308383,0x26baa88038000LL), + reale(184630907,0xde7b734758000LL),reale(240946965,0x4db221ae90000LL), + -reale(189474421,0xed4c1e36d8000LL),reale(27214973,0x55324802d8000LL), + -reale(46882338,0xe5fcdfdca8000LL),reale(29262846,2319362995LL<<17), + -reale(12682237,0x3cee53d458000LL),reale(19904432,0x70537f02e8000LL), + -reale(7274198,0xbf917ba828000LL),reale(8480909,0x438c3da230000LL), + -reale(6415713,0xc95c9b8258000LL),reale(1960896,0x685dc04df8000LL), + -reale(2745254,0xf883406d28000LL),reale(1023946,0x4eef421f04580LL), + reale(28712685662LL,0x1fa68a0342ac7LL), + // C4[3], coeff of eps^13, polynomial in n of order 16 + -reale(2272755,0x57fd708a77000LL),-reale(26091168,0x1366cec7d9d00LL), + reale(231976719,0xafe6927fcde00LL),-reale(464894868,0x24c5c39795700LL), + reale(215184123,0xaf8273d716c00LL),reale(236438336,0xab29f0bfd4f00LL), + -reale(210344218,0x367ffa8b78600LL),reale(29454299,0x2f129bee9500LL), + -reale(44460297,0xf9cfdfb8bb800LL),reale(34058265,0xda8305b9abb00LL), + -reale(10677799,0x93543d448ea00LL),reale(19950418,0xbb16c712a0100LL), + -reale(8097327,0xc3857f1ecdc00LL),reale(6164437,0x8a1d8a85ca700LL), + -reale(6487914,0xa92c56ec54e00LL),reale(653539,0x4a58f163aed00LL), + reale(193289,0xc4fa7fb371708LL),reale(28712685662LL,0x1fa68a0342ac7LL), + // C4[3], coeff of eps^12, polynomial in n of order 17 + -reale(136365,0x73a1fcfe6ac00LL),-reale(450638,0xd074750f34000LL), + -reale(2128024,0x54e7feac4d400LL),-reale(24952088,0x92a9c1fc91800LL), + reale(228113259,0x85d44607e4400LL),-reale(477191195,0x7e69e50f07000LL), + reale(251096618,0x1896eb4cd1c00LL),reale(226763725,0xac7cda7d93800LL), + -reale(234776156,0x14cc4b0edcc00LL),reale(34557325,0x4230b4bd66000LL), + -reale(39741101,0x3a85821c7f400LL),reale(39764072,0x42dd69fc98800LL), + -reale(9161206,0x9c1a792d6dc00LL),reale(18380268,0xf302f56753000LL), + -reale(9708385,0x581708d300400LL),reale(3148914,0x8380fab1bd800LL), + -reale(5050904,0x8a565e3e8ec00LL),reale(1566765,0x6fd98617e9df0LL), + reale(28712685662LL,0x1fa68a0342ac7LL), + // C4[3], coeff of eps^11, polynomial in n of order 18 + -reale(18810,0x4977f6cdda600LL),-reale(44617,0xf507aa2256700LL), + -reale(121680,0x26c8d0378b000LL),-reale(408670,0xadcc6d8f87900LL), + -reale(1967116,0xd731d207dba00LL),-reale(23614778,0x5c1a1fadbeb00LL), + reale(222693980,0x695506ba87c00LL),-reale(488598159,0xe2ab67bc47d00LL), + reale(293333811,0x10f016a3f3200LL),reale(209273530,0x4db1c2b811100LL), + -reale(262769616,0x9b49f60945800LL),reale(44647130,0x3acb33bfff00LL), + -reale(31983858,0x227f1389ce200LL),reale(45626356,0x9e16c6ccb8d00LL), + -reale(9276161,0xf8fb16a652c00LL),reale(14205372,0x289c377eefb00LL), + -reale(11490116,0xc948e407f600LL),reale(414830,0x163387d5d8900LL), + reale(117690,0xc756ec17c4aa8LL),reale(28712685662LL,0x1fa68a0342ac7LL), + // C4[3], coeff of eps^10, polynomial in n of order 19 + -reale(3667,0x8ba48fb7ec000LL),-reale(7355,0xde5d961edc000LL), + -reale(15963,0x138d280434000LL),-reale(38393,53315683LL<<17), + -reale(106358,0x1cca460dcc000LL),-reale(363723,0x77fed5aee4000LL), + -reale(1788619,0xb46088e414000LL),-reale(22045766,0x7d53064fc8000LL), + reale(215267089,0x7c4e47994000LL),-reale(498143540,0xc077eb386c000LL), + reale(342855614,0x4b25e0bbcc000LL),reale(179961617,0x7ca6ea4dd0000LL), + -reale(293329289,0xb4e43f9ccc000LL),reale(63137066,0xbcee02f98c000LL), + -reale(20920174,0xdceb909f94000LL),reale(49479848,0x7088e98168000LL), + -reale(12768344,0x1ee1d8cbec000LL),reale(6948560,0xd8f6969c04000LL), + -reale(10643749,0x466c677134000LL),reale(2529930,0x161dcdf222440LL), + reale(28712685662LL,0x1fa68a0342ac7LL), + // C4[3], coeff of eps^9, polynomial in n of order 20 + -real(0x354d49acec3dd800LL),-real(0x606a7d34c50a0200LL), + -reale(2939,0xdc47a7c209c00LL),-reale(5971,0x671f2d9dad600LL), + -reale(13140,0xcdf9f327fe000LL),-reale(32101,0x6baea5bb9ea00LL), + -reale(90511,0x408ba9a232400LL),-reale(315893,0xc97e5e852be00LL), + -reale(1591343,0xfce30d8d1e800LL),-reale(20207205,0x8b4272e60d200LL), + reale(205238828,0x21c1cf60c5400LL),-reale(504251582,0xb2b181bcfa600LL), + reale(400330413,0xa384192d01000LL),reale(132810886,0x4094526254600LL), + -reale(323039224,0xd5680dd0e3400LL),reale(95085342,0xbfbbc74d27200LL), + -reale(8279837,0x6ce790195f800LL),reale(46514941,0x8e0e73ffc5e00LL), + -reale(20732718,0x38ef4b2eebc00LL),-reale(922541,0xf2a1d94487600LL), + -reale(491669,0x5bd07d195db30LL),reale(28712685662LL,0x1fa68a0342ac7LL), + // C4[3], coeff of eps^8, polynomial in n of order 21 + -real(0xd828cefda55a800LL),-real(0x16c6eac98e7b6000LL), + -real(0x27e1e798049c9800LL),-real(0x490330552dbbf000LL), + -reale(2255,0x88ea2b8740800LL),-reale(4647,0x88c66c31f8000LL), + -reale(10390,0xd13f35560f800LL),-reale(25836,0xfcd55e2db1000LL), + -reale(74324,0xc0bfff0e86800LL),-reale(265480,0xf5ce67923a000LL), + -reale(1374647,0xa0b10ca8f5800LL),-reale(18058373,0x723761b2e3000LL), + reale(191831943,0xc85920c253800LL),-reale(504361484,0x6e935002fc000LL), + reale(465423127,0xbaa71ebb04800LL),reale(59036306,0xf120275a2b000LL), + -reale(342905949,0x5a93131732800LL),reale(146354899,0x9f9c2b8142000LL), + -reale(1641748,0x1e8ba62ca1800LL),reale(28969072,0x51c8dabef9000LL), + -reale(27136540,0x3d9359d98800LL),reale(4249105,0xd55e5a0325120LL), + reale(28712685662LL,0x1fa68a0342ac7LL), + // C4[3], coeff of eps^7, polynomial in n of order 22 + -real(0x38123cee860f400LL),-real(0x59d375c04e8be00LL), + -real(0x942bf86bd4c1800LL),-real(0xfcbda8858afb200LL), + -real(0x1c02af2dc3443c00LL),-real(0x33fc822f8d2b6600LL), + -real(0x65e35fc07de4e000LL),-reale(3414,0xc7eb297eb5a00LL), + -reale(7775,0x1c0e884298400LL),-reale(19731,0x6a31912ef0e00LL), + -reale(58089,0x9471e600da800LL),-reale(213111,0x15a6331c60200LL), + -reale(1139019,0x77ee6ce2ccc00LL),-reale(15560104,0x33d66a0afb600LL), + reale(174045800,0x2f0a20e9d9000LL),-reale(494300177,0xd9e4761bbaa00LL), + reale(535087920,0xe9f8f195ec00LL),-reale(53102016,0x93f6bbbe95e00LL), + -reale(331738553,0x77bff637f3800LL),reale(216985631,0x987f3afb7ae00LL), + -reale(21074121,0x8043eaffd5c00LL),-reale(4185955,0xa3ff769180600LL), + -reale(4713710,0xd2e19a34f30b0LL),reale(28712685662LL,0x1fa68a0342ac7LL), + // C4[3], coeff of eps^6, polynomial in n of order 23 + -real(0xe0ca252d14c000LL),-real(0x15a70af15f24000LL), + -real(0x222b3f817554000LL),-real(0x375f97b48cd8000LL), + -real(0x5c7b9631f8ac000LL),-real(0x9fe2527c7fcc000LL), + -real(0x11face3d5ef34000LL),-real(0x21e77d8dabde0000LL), + -real(0x439dcbf7fdccc000LL),-reale(2310,0x1731d0ccf4000LL), + -reale(5373,0x35ee2c1554000LL),-reale(13965,0xf39edc32e8000LL), + -reale(42247,0xa0aa0b1cac000LL),-reale(159930,0xa2319a759c000LL), + -reale(887131,0xc123fa86b4000LL),-reale(12685735,0x6243721af0000LL), + reale(150650948,0x968da6a8b4000LL),-reale(467294064,0x1610ada8c4000LL), + reale(599544322,0x5feb9b1dac000LL),-reale(214883240,0x150075a4f8000LL), + -reale(244806233,0x53bd4b2bac000LL),reale(272520146,0x88b0e96a94000LL), + -reale(87760725,0x27ae1fc734000LL),reale(5827860,0xa7a2901c3a740LL), + reale(28712685662LL,0x1fa68a0342ac7LL), + // C4[3], coeff of eps^5, polynomial in n of order 24 + -real(0x32b69e04189800LL),-real(0x4bd39320660300LL), + -real(0x73a508e7ef1600LL),-real(0xb44a7ec206b900LL), + -real(0x1200d9d52c6d400LL),-real(0x1d916a5ad4bcf00LL), + -real(0x321a3f994641200LL),-real(0x57fce6d660f8500LL), + -real(0xa10c564a22b1000LL),-real(0x1356fa3ebba41b00LL), + -real(0x275fd13435900e00LL),-real(0x5604e2d76283d100LL), + -reale(3283,0xdf8f52c874c00LL),-reale(8783,0x8ddc09700e700LL), + -reale(27451,0x143e179f50a00LL),-reale(107903,0xe48c7d6f59d00LL), + -reale(625732,0xe2abef41d8800LL),-reale(9446536,0xacc19c0743300LL), + reale(120325828,0x5507fb0eafa00LL),-reale(412649247,0xc3fe82376e900LL), + reale(633089704,0xd19d26ed03c00LL),-reale(418090362,0x84d33548fff00LL), + -reale(13712613,0x4e3334f720200LL),reale(163180098,0x55c7c31664b00LL), + -reale(61921019,0x751f3b2bed108LL), + reale(28712685662LL,0x1fa68a0342ac7LL), + // C4[3], coeff of eps^4, polynomial in n of order 25 + -real(0x30fab48eb2c00LL),-real(0x4779db0cde000LL), + -real(0x6a1a5308c1400LL),-real(0xa07c7893bf800LL), + -real(0xf7d15b087bc00LL),-real(0x1878e181999000LL), + -real(0x27ab652bf7a400LL),-real(0x422ed0b6682800LL), + -real(0x721448fff54c00LL),-real(0xcc1e5699294000LL), + -real(0x17d5829db9a3400LL),-real(0x2ed74923dde5800LL), + -real(0x61c84aba5ffdc00LL),-real(0xdbaa1b53c88f000LL), + -real(0x21cc8beefe3fc400LL),-real(0x5da8efb832aa8800LL), + -reale(4876,0x5d83861736c00LL),-reale(20082,0x8bb9af0c4a000LL), + -reale(123005,0x97d1502b45400LL),-reale(1983151,0x65e045fd8b800LL), + reale(27425226,0x9c6669ee40400LL),-reale(105081920,0xe8c662ae85000LL), + reale(191976586,0x46cce583c1c00LL),-reale(186491540,0xf45203874e800LL), + reale(93245770,0x7a2901c3a7400LL),-reale(18940547,0x20d0545bbdf90LL), + reale(9570895220LL,0xb53783566b8edLL), + // C4[3], coeff of eps^3, polynomial in n of order 26 + -real(0x10330cb256200LL),-real(0x172cb16211100LL), + -real(0x21a8187537800LL),-real(0x31b06260f1f00LL), + -real(0x4ab014ab28e00LL),-real(0x7280309c9cd00LL), + -real(0xb366eef7be400LL),-real(0x11ff8a58b05b00LL), + -real(0x1dae666558ba00LL),-real(0x327547ac4a0900LL), + -real(0x58c9207d125000LL),-real(0xa2826b77361700LL), + -real(0x137557a5841e600LL),-real(0x275355b4b1bc500LL), + -real(0x54b37d85300bc00LL),-real(0xc517d06239a5300LL), + -real(0x1f8f2f623d981200LL),-real(0x5b85a3034c390100LL), + -reale(5020,0xa2ee6bc312800LL),-reale(21965,0x48d3177570f00LL), + -reale(144343,0x4c469a2853e00LL),-reale(2526007,0xb6d389c1bbd00LL), + reale(38395317,0x415c2de726c00LL),-reale(163180098,0x55c7c31664b00LL), + reale(326360196,0xab8f862cc9600LL),-reale(303048754,0xd0545bbdf900LL), + reale(104173009,0x3479cff894d98LL), + reale(28712685662LL,0x1fa68a0342ac7LL), + // C4[4], coeff of eps^29, polynomial in n of order 0 + 4519424,real(0x13ed3512585LL), + // C4[4], coeff of eps^28, polynomial in n of order 1 + real(322327509504LL),real(86419033792LL),real(0x12e7203d54087bdLL), + // C4[4], coeff of eps^27, polynomial in n of order 2 + real(0xdf868e997000LL),-real(0xc54488fde800LL),real(0x67996a8dfb80LL), + reale(6219,0x86ed0fee71e5LL), + // C4[4], coeff of eps^26, polynomial in n of order 3 + real(0x1e30d5f17398800LL),-real(0x20335f44c005000LL), + real(0x8656a9da59d800LL),real(0x246f3281df3200LL), + reale(1871928,0xea4bbbb5bea41LL), + // C4[4], coeff of eps^25, polynomial in n of order 4 + real(0x640278dc982000LL),-real(0x64de2b5e388800LL), + real(0x266cf1cb211000LL),-real(0x24af02897bd800LL), + real(0x125236c4932c80LL),reale(225070,0xa1cd0c0f186c5LL), + // C4[4], coeff of eps^24, polynomial in n of order 5 + real(0x183393315f62f400LL),-real(0x147c8a635ba4f000LL), + real(0xaadb07a361e2c00LL),-real(0xbd0a07cdca37800LL), + real(0x2c490db64a86400LL),real(0xc3000bbe3e2580LL), + reale(8327613,0x62a2be2e87a79LL), + // C4[4], coeff of eps^23, polynomial in n of order 6 + reale(7399,0xe4703b1ceb000LL),-reale(4925,0x718bf750ef800LL), + reale(3656,0xc01290e152000LL),-reale(3594,0x9ae0aefbbc800LL), + real(0x5080258211e79000LL),-real(0x5458466826cf9800LL), + real(0x27a09e95cf36b080LL),reale(97921247,0xc3bd6c206251LL), + // C4[4], coeff of eps^22, polynomial in n of order 7 + reale(4319137,0xe5044c1364800LL),-reale(2259378,0xc043aee633000LL), + reale(2431286,0xcceb783bf5800LL),-reale(1865690,0x884902c9a2000LL), + reale(996566,0x94ae3b7946800LL),-reale(1135368,0x2cb1c30811000LL), + reale(231629,0x92b25177d7800LL),reale(64961,0x89605803fda00LL), + reale(36916310137LL,0x41f43bb0c949LL), + // C4[4], coeff of eps^21, polynomial in n of order 8 + reale(6174501,0x53f34a829c000LL),-reale(2885765,0xddf01a0f35800LL), + reale(4089976,0x588848e445000LL),-reale(2309244,0x73683320c8800LL), + reale(1950621,0xac1b944ace000LL),-reale(1810054,0xa24c07eb4b800LL), + reale(609590,0x74daa18497000LL),-reale(712107,0x16cff78e5e800LL), + reale(310317,0x16957f6a36b80LL),reale(36916310137LL,0x41f43bb0c949LL), + // C4[4], coeff of eps^20, polynomial in n of order 9 + reale(7763095,0xd98a0c3214600LL),-reale(4551997,0xf65d38a54d000LL), + reale(6348004,0x7dcc619ba1a00LL),-reale(2777846,0x11091dc381c00LL), + reale(3645151,0x5af876afd6e00LL),-reale(2403756,0x12692c3266800LL), + reale(1377366,0xde24866584200LL),-reale(1585712,0xf2192bea6b400LL), + reale(268682,0xb0f056b079600LL),reale(77255,0xca5a822ebf740LL), + reale(36916310137LL,0x41f43bb0c949LL), + // C4[4], coeff of eps^19, polynomial in n of order 10 + reale(8073134,0x8bff962f2e000LL),-reale(9331256,0xe8e10405e1000LL), + reale(8608510,0x42ad0321d8000LL),-reale(3959617,0x4c778c1e2f000LL), + reale(6283090,0x55033b3d82000LL),-reale(2832307,0xbbdb17809d000LL), + reale(2955095,0x929c8347ec000LL),-reale(2459067,0xd43d49c36b000LL), + reale(787004,0x9cc4866d6000LL),-reale(1039103,0x6b1983acd9000LL), + reale(412222,0xf695367aa1b00LL),reale(36916310137LL,0x41f43bb0c949LL), + // C4[4], coeff of eps^18, polynomial in n of order 11 + reale(8586281,0xffd2991fd000LL),-reale(20926106,0xdd733d721a000LL), + reale(9282973,0x193483c94f000LL),-reale(8121077,0x9b55004148000LL), + reale(9430655,0x90c0e29221000LL),-reale(3512067,0x80c2ac76000LL), + reale(5840995,0x1886eb4173000LL),-reale(3061324,0xab1a78b4a4000LL), + reale(2049544,0x4067911445000LL),-reale(2292525,0x617c054ad2000LL), + reale(297833,0x966e637f97000LL),reale(88539,0x9a2e50b8c6400LL), + reale(36916310137LL,0x41f43bb0c949LL), + // C4[4], coeff of eps^17, polynomial in n of order 12 + reale(32196457,0xd679f8ae1c000LL),-reale(40594018,0x37167c5ef5000LL), + reale(8052650,0x2eda271162000LL),-reale(20325613,0xcd34eeff17000LL), + reale(11030346,0x5827875768000LL),-reale(6662972,0x9685f0fc59000LL), + reale(10015916,0xfa65faac6e000LL),-reale(3377057,0x1ef6021e7b000LL), + reale(4892320,0x94cb79bcb4000LL),-reale(3369439,0x93437f1d3d000LL), + reale(1068721,0xdee482d47a000LL),-reale(1596884,0xcb3e26805f000LL), + reale(562334,0xcf5270735f500LL),reale(36916310137LL,0x41f43bb0c949LL), + // C4[4], coeff of eps^16, polynomial in n of order 13 + reale(239019678,0x7928c61a8b800LL),-reale(41200119,0x147c0b11e000LL), + reale(27063572,0xac3757be98800LL),-reale(45155983,0xc412cf1f79000LL), + reale(8354845,0xf8b6ea7445800LL),-reale(18750027,0x4e7377c014000LL), + reale(13292220,0xfed958edd2800LL),-reale(5165101,0x26aa3105af000LL), + reale(10025000,0x43fec217f800LL),-reale(3715677,0xed5a4430a000LL), + reale(3405288,0xc16fe1018c800LL),-reale(3440521,0x6cb0e4f2e5000LL), + reale(291108,0x30be23439800LL),reale(90314,0xe93f4121c6900LL), + reale(36916310137LL,0x41f43bb0c949LL), + // C4[4], coeff of eps^15, polynomial in n of order 14 + -reale(301344600,0x1f7a69f35a000LL),-reale(137666269,0x81776c9d9b000LL), + reale(257500426,0xa27a71193c000LL),-reale(52745704,0xa8e59f44d000LL), + reale(20527629,0x3707e00852000LL),-reale(49389175,0x1679a6a55f000LL), + reale(10057417,0xa546ce8428000LL),-reale(15960633,0x79a78f6a91000LL), + reale(15828795,0x3b7a7e96fe000LL),-reale(4041479,0x5385608da3000LL), + reale(9015452,0x8a056dcb14000LL),-reale(4531739,0xb18fd7c855000LL), + reale(1608583,0x5c81da4aaa000LL),-reale(2620079,0xb9c03a2467000LL), + reale(790676,0xf12036cb88d00LL),reale(36916310137LL,0x41f43bb0c949LL), + // C4[4], coeff of eps^14, polynomial in n of order 15 + -reale(152316078,0x9ee9710b1f000LL),reale(396132268,0xf6300698d2000LL), + -reale(331944543,0x2a26efc8bd000LL),-reale(111967823,0x409ccb544c000LL), + reale(276102802,0x8592b62d25000LL),-reale(69409637,0x2e4659b6a000LL), + reale(12806364,0xaa4a38387000LL),-reale(52382533,0xaa3aad6588000LL), + reale(13858261,0x7d9fda6f69000LL),-reale(11925525,0x17f68feba6000LL), + reale(17994828,0x2633a57dcb000LL),-reale(3926621,0x9c334da6c4000LL), + reale(6610729,0xa84ec063ad000LL),-reale(5341800,0xcfe0c57fe2000LL), + reale(171304,0xc92dc0ce0f000LL),reale(53498,0x8a12fdd94c400LL), + reale(36916310137LL,0x41f43bb0c949LL), + // C4[4], coeff of eps^13, polynomial in n of order 16 + reale(945329,0x3e694a5630000LL),reale(13046260,0xd11553dc81000LL), + -reale(145063327,0x6c5bbd04f6000LL),reale(395288944,0x9758cc3483000LL), + -reale(364989750,0x4da45c465c000LL),-reale(77659847,0x7f601a5fdb000LL), + reale(293261136,0xdb46a6c9be000LL),-reale(92956699,0x68d702f4d9000LL), + reale(4748491,0xd717292318000LL),-reale(52641236,0xde7217eeb7000LL), + reale(20401071,0xa831b35d72000LL),-reale(7165143,0xe2daef21b5000LL), + reale(18530179,0x70f1fa908c000LL),-reale(5449998,0x995f61f213000LL), + reale(2985284,0xf423c13426000LL),-reale(4674955,0x4c99b17411000LL), + reale(1148405,0xaa811667d8300LL),reale(36916310137LL,0x41f43bb0c949LL), + // C4[4], coeff of eps^12, polynomial in n of order 17 + reale(39064,0xc457745427a00LL),reale(149707,0xe179ab818a000LL), + reale(834482,0xb3de3faf4c600LL),reale(11844090,0x43801d34c0c00LL), + -reale(136492367,0x606ac4f4b6e00LL),reale(391413380,0x8b1b355567800LL), + -reale(399991879,0xf56c51d232200LL),-reale(32313943,0x670cb1cd91c00LL), + reale(306137820,0x47c0d4df8aa00LL),-reale(125355715,0x12c37db13b000LL), + -reale(1549012,0x61de67b1d0a00LL),-reale(48002827,0x1ef791fca4400LL), + reale(29707099,0x80264b6e6c200LL),-reale(3304868,0xd90dacdedd800LL), + reale(15595740,0x1c41b85df0e00LL),-reale(8339676,0x731c5b6cf6c00LL), + -reale(264319,0x3253133a92600LL),-reale(128183,0x1fd72f4c70540LL), + reale(36916310137LL,0x41f43bb0c949LL), + // C4[4], coeff of eps^11, polynomial in n of order 18 + reale(3796,0xb8b80a685d000LL),reale(10243,0xe5415b1644800LL), + reale(32134,0x75fe9c2f28000LL),reale(125896,0x13cc0b67cb800LL), + reale(720062,0x2eb5ef2cf3000LL),reale(10542664,0x8e7784ebe2800LL), + -reale(126401502,0xa942d02d22000LL),reale(383396973,0xa914c081a9800LL), + -reale(435856143,0x9e18e4ddf7000LL),reale(26921352,0xa17bcee040800LL), + reale(309790567,0x432113bb94000LL),-reale(168177156,0xf5a6b5d938800LL), + -reale(1732899,0x7848d10f61000LL),-reale(36033193,0x6ff05a93a1800LL), + reale(39850986,0x4a7ce5d24a000LL),-reale(3520516,0x12d4d9afda800LL), + reale(7904559,0x47211641b5000LL),-reale(9293198,0x11e52b76c3800LL), + reale(1712350,0xd1c47193d5a80LL),reale(36916310137LL,0x41f43bb0c949LL), + // C4[4], coeff of eps^10, polynomial in n of order 19 + real(0x20b0c3dbe662b800LL),real(0x49a4ee6b654d5000LL), + reale(2895,0xbb9a481b3e800LL),reale(7963,0xd6290c9168000LL), + reale(25525,0x742091bd91800LL),reale(102493,0xec03f49fb000LL), + reale(603292,0x6fe940faa4800LL),reale(9144553,0x3f081030e000LL), + -reale(114581171,0x9502f66408800LL),reale(369767644,0x159b783921000LL), + -reale(470438620,0x42537ac0f5800LL),reale(102998223,0x33db2118b4000LL), + reale(295924658,0xfd504b0d5d800LL),-reale(220875824,0xd68590c9b9000LL), + reale(12088406,0x3b87c77470800LL),-reale(15966308,0xf7cc70b9a6000LL), + reale(44660638,0xbb68d3ddc3800LL),-reale(11155854,0x316b572a93000LL), + -reale(1400757,0x91d7719929800LL),-reale(909990,0x5b4dcbdcd9200LL), + reale(36916310137LL,0x41f43bb0c949LL), + // C4[4], coeff of eps^9, polynomial in n of order 20 + real(0x55091490e3fe000LL),real(0xab3101736f26800LL), + real(0x16d77945c4e3b000LL),real(0x345d2a91137d7800LL), + reale(2099,0xc55d2c398000LL),reale(5898,0x424192198800LL), + reale(19366,0xa6f5f449f5000LL),reale(79943,0x847cdfac49800LL), + reale(486014,0x6a1dc16732000LL),reale(7659629,0x94cc8fca800LL), + -reale(100839015,0x651046eed1000LL),reale(348607247,0x22ddc22bfb800LL), + -reale(499815073,0x4df2756234000LL),reale(197958555,0x77a0b2f8bc800LL), + reale(251323198,0x2663cfb2e9000LL),-reale(276534810,0xe292670a12800LL), + reale(51555588,0x6a67a23666000LL),reale(5587968,0x5e92831b6e800LL), + reale(32523682,0xed2ae23e23000LL),-reale(21111776,0x46401336e0800LL), + reale(2489921,0xe3c1e337a6d80LL),reale(36916310137LL,0x41f43bb0c949LL), + // C4[4], coeff of eps^8, polynomial in n of order 21 + real(0xeb8379f6b27c00LL),real(0x1b6c4de1f1d7000LL), + real(0x355a1dadc956400LL),real(0x6d308de46411800LL), + real(0xed54313f63d4c00LL),real(0x22ae87428a2ac000LL), + real(0x58ce5dd980bc3400LL),reale(4090,0xd3c824bc46800LL), + reale(13806,0x44b4a8a441c00LL),reale(58809,0x7ab991df81000LL), + reale(370898,0xe410033e70400LL),reale(6109620,0x6402b9f6fb800LL), + -reale(85053139,0x4bf446ca91400LL),reale(317515928,0x1b63894556000LL), + -reale(517123103,0xa7a388b5a2c00LL),reale(310296682,0xe98bc80130800LL), + reale(156996715,0xaa3cf3c05bc00LL),-reale(312601560,0xdd28200ed5000LL), + reale(125126811,0xf01e02788a400LL),reale(4091818,0xb5091207e5800LL), + -reale(866059,0xc9a79cf1f7400LL),-reale(4943757,0xf4721fe538b80LL), + reale(36916310137LL,0x41f43bb0c949LL), + // C4[4], coeff of eps^7, polynomial in n of order 22 + real(0x2814d49c0c5000LL),real(0x468b0d3a3db800LL), + real(0x80724d98876000LL),real(0xf31dbc49b20800LL), + real(0x1e12cb4a6a67000LL),real(0x3eb5a58b5455800LL), + real(0x8b1eef20fbf8000LL),real(0x14cb29a266eda800LL), + real(0x36974c82ca289000LL),reale(2585,0xefae20720f800LL), + reale(9007,0x1d6baf437a000LL),reale(39779,0x24ec74fd54800LL), + reale(261696,0x442f64f42b000LL),reale(4534975,0xa5b17f809800LL), + -reale(67279179,0x4d9bf05604000LL),reale(273758534,0xd27122c18e800LL), + -reale(510920394,0x40d515b3000LL),reale(428723861,0x53ee2b6143800LL), + -reale(7330129,0x37be948582000LL),-reale(275708250,0xae16364977800LL), + reale(204390109,0xe684af0fef000LL),-reale(52540960,0x7463315742800LL), + reale(2056891,0xfeee14beab380LL),reale(36916310137LL,0x41f43bb0c949LL), + // C4[4], coeff of eps^6, polynomial in n of order 23 + real(0x628e4f4bb7800LL),real(0xa60e374943000LL),real(0x11fae77940e800LL), + real(0x2022ddc061a000LL),real(0x3b7f2e2d7a5800LL), + real(0x72aa26ca9f1000LL),real(0xe77392a11fc800LL), + real(0x1ed1e51d0348000LL),real(0x460248a5fa93800LL), + real(0xabd9e84dc89f000LL),real(0x1d078c2cd5cea800LL), + real(0x58c9fda5cf076000LL),reale(5134,0xa77137081800LL), + reale(23653,0x63d76094d000LL),reale(163469,0x772f4630d8800LL), + reale(3004667,0x8d384291a4000LL),-reale(47956830,0xd53f134a90800LL), + reale(214953528,0xfe0a5a4ffb000LL),-reale(463620631,0xbff95a7639800LL), + reale(519033396,0x411553aad2000LL),-reale(237300381,0xd565fafaa2800LL), + -reale(84296486,0x10fabff57000LL),reale(142611178,0x607af3a3b4800LL), + -reale(46622885,0x3d1480e1d3a00LL),reale(36916310137LL,0x41f43bb0c949LL), + // C4[4], coeff of eps^5, polynomial in n of order 24 + real(0xc0b5b2cac000LL),real(0x139ac5d2ed800LL),real(0x20abe97223000LL), + real(0x37e2f8cba0800LL),real(0x6269b1d1ba000LL),real(0xb3074a8a43800LL), + real(0x151de1e3911000LL),real(0x298e5ccaa76800LL), + real(0x55d208375c8000LL),real(0xbb7ea958fd9800LL), + real(0x1b5e1854857f000LL),real(0x4547c4b8360c800LL), + real(0xc1cdc899e5d6000LL),real(0x2682d6f5e00af800LL), + reale(2326,0xf44888e46d000LL),reale(11275,0x7d4afe8b62800LL), + reale(82638,0x859516eee4000LL),reale(1628359,0xc1653179c5800LL), + -reale(28286265,0xc31f9b1d25000LL),reale(141205400,0x2bb5164778800LL), + -reale(353352393,0x632221a20e000LL),reale(504046796,0x730ece181b800LL), + -reale(416863444,0x7c7b16f237000LL),reale(186491540,0xf45203874e800LL), + -reale(34967163,0xedcf60a95eb80LL),reale(36916310137LL,0x41f43bb0c949LL), + // C4[4], coeff of eps^4, polynomial in n of order 25 + real(0xe07098dae00LL),real(0x16338b625000LL),real(0x23dda179f200LL), + real(0x3b41a69cf400LL),real(0x645a89a6b600LL),real(0xaeabe0e09800LL), + real(0x1397028dcfa00LL),real(0x246014e923c00LL),real(0x4633de275be00LL), + real(0x8d95c8a56e000LL),real(0x12c670f9ba0200LL), + real(0x2a433484738400LL),real(0x6608a70542c600LL), + real(0x10c10ac322d2800LL),real(0x30ddb4b92590a00LL), + real(0xa2e30513d28cc00LL),real(0x289386109855ce00LL), + reale(3347,0x17499d2cb7000LL),reale(26358,0x5763b5c021200LL), + reale(564821,0x99c65b39a1400LL),-reale(10825747,0x58af29d092a00LL), + reale(60624185,0x23d4ea299b800LL),-reale(172778927,0xa61ece902e600LL), + reale(279737311,0x6e7b054af5c00LL),-reale(233114426,0x3166846922200LL), + reale(75762188,0x8341516ef7e40LL),reale(36916310137LL,0x41f43bb0c949LL), + // C4[5], coeff of eps^29, polynomial in n of order 0 + 3108352,real(0x4338129a0b3LL), + // C4[5], coeff of eps^28, polynomial in n of order 1 + -real(4961047LL<<17),real(304969986048LL),real(0x171a7cbcbc0a5e7LL), + // C4[5], coeff of eps^27, polynomial in n of order 2 + -real(0xb7a8cf8589000LL),real(0x25cdf8a9f5800LL),real(0xaa8ee05df480LL), + reale(53207,0x4825dfa147919LL), + // C4[5], coeff of eps^26, polynomial in n of order 3 + -real(0x4519d2e6066000LL),real(0x17b1d503134000LL), + -real(0x1b53dc2d3c2000LL),real(0xc104a529c3b00LL), + reale(207992,0x1a086a30a3679LL), + // C4[5], coeff of eps^25, polynomial in n of order 4 + -real(0xe48436400f9e000LL),real(0x825cbe3b5113800LL), + -real(0x9657faac8f9f000LL),real(0x1ac735d19d16800LL), + real(0x7b639e59c13780LL),reale(8527676,0x2b5901ca2b961LL), + // C4[5], coeff of eps^24, polynomial in n of order 5 + -real(0x13b86e0d5c5dc000LL),real(0x135f9b0385fb0000LL), + -real(0x10df1064c3304000LL),real(0x58b0ae17a818000LL), + -real(0x70d05036b8ec000LL),real(0x2e5299a0b610e00LL), + reale(10178194,0x2338af8e3405bLL), + // C4[5], coeff of eps^23, polynomial in n of order 6 + -reale(126383,0x5f6b81564f000LL),reale(192332,0x2215a4d90d800LL), + -reale(113392,0x893928fcaa000LL),reale(71665,0x3fb557978e800LL), + -reale(81791,0xa6f9503f45000LL),reale(12036,0x1a6fad5adf800LL), + reale(3561,0x9aef6f2cefa80LL),reale(3470764200LL,0xea81d86b4b937LL), + // C4[5], coeff of eps^22, polynomial in n of order 7 + -reale(191647,0x188f775ada000LL),reale(308186,0x45ee8f2434000LL), + -reale(124928,0xd21a49314e000LL),reale(153616,0xaed0e35eb8000LL), + -reale(118466,0xc4b6a2a9a2000LL),reale(38029,0x77ad4b77bc000LL), + -reale(53612,0x41f60b8316000LL),reale(20169,0xecfa5f7fa8900LL), + reale(3470764200LL,0xea81d86b4b937LL), + // C4[5], coeff of eps^21, polynomial in n of order 8 + -reale(5169843,0xc81db86efc000LL),reale(5341939,0xe957aa505800LL), + -reale(2049228,0x2e9753666d000LL),reale(3734678,0xdcd2e44998800LL), + -reale(1762099,0xebebc251fe000LL),reale(1337844,0xa441c7cbb800LL), + -reale(1455577,0x7e18adc04f000LL),reale(163809,0xd9aab3cbce800LL), + reale(50215,0x8f7a6f7ead780LL),reale(45119934611LL,0xe897fd72d67cbLL), + // C4[5], coeff of eps^20, polynomial in n of order 9 + -reale(11201228,0x9af12fea90000LL),reale(5330620,7096189457LL<<19), + -reale(4084126,0xa473ecba70000LL),reale(5776338,0xc1238f4360000LL), + -reale(1850318,0x7e36514750000LL),reale(3091001,2788978033LL<<18), + -reale(1978996,0x9854b5b30000LL),reale(651396,0xde4e2e0920000LL), + -reale(1009381,0x5e1878c010000LL),reale(341219,0x67868049b6800LL), + reale(45119934611LL,0xe897fd72d67cbLL), + // C4[5], coeff of eps^19, polynomial in n of order 10 + -reale(19364139,0xf3aad6c27e000LL),reale(3661269,0x231a8ee911000LL), + -reale(10171658,0x9bc1444518000LL),reale(6650152,0x1449aa44ff000LL), + -reale(2982446,0xb2f133d6b2000LL),reale(5796709,0x225c7b8fcd000LL), + -reale(2004712,0xb33d0f538c000LL),reale(2087887,0x2718a4e53b000LL), + -reale(2041244,0xb9c4a8d7e6000LL),reale(150337,0x64e8ec0109000LL), + reale(48205,0x4eea8f2f13300LL),reale(45119934611LL,0xe897fd72d67cbLL), + // C4[5], coeff of eps^18, polynomial in n of order 11 + -reale(17821498,0x43ce2fe394000LL),reale(8113989,0x34042cf6f8000LL), + -reale(21055211,0x1d823792dc000LL),reale(4458324,0xaba1762760000LL), + -reale(8384573,0x54084121e4000LL),reale(8079221,0xcbb99849c8000LL), + -reale(2172398,0x503335ed2c000LL),reale(5129813,0x3b8a4c21b0000LL), + -reale(2481567,0xadec795134000LL),reale(934125,9279934035LL<<15), + -reale(1531704,0x9cc504aa7c000LL),reale(453383,0xd34e451346a00LL), + reale(45119934611LL,0xe897fd72d67cbLL), + // C4[5], coeff of eps^17, polynomial in n of order 12 + reale(4095301,0x789aeb9e64000LL),reale(49542396,0x46ab457e8d000LL), + -reale(24303219,0x1ccf0dd62000LL),reale(4679495,0x21a30e03df000LL), + -reale(21666597,0xecbbb1868000LL),reale(6429258,0x6611bb6911000LL), + -reale(5963806,0x7f45fe6c6e000LL),reale(9141324,0xab5773fc63000LL), + -reale(2043796,0x5ca6f33334000LL),reale(3626747,0xd85dd12c15000LL), + -reale(2919955,0xba0fdf867a000LL),reale(85758,0x333e03c667000LL), + reale(28339,0x9119c9ad54d00LL),reale(45119934611LL,0xe897fd72d67cbLL), + // C4[5], coeff of eps^16, polynomial in n of order 13 + -reale(273240474,0x43c43c74c8000LL),reale(133674826,0x952bfc30e0000LL), + reale(7048142,0x68e4684408000LL),reale(44883009,0xdb6a70b90000LL), + -reale(32370151,0x153b9e91a8000LL),reale(2006331,0xa0ac245340000LL), + -reale(20459012,0x9d1a27ed8000LL),reale(9634139,0x6e1e5ebef0000LL), + -reale(3415127,0x8d101d0c88000LL),reale(9090639,8214448173LL<<17), + -reale(2849328,0xea461fc3b8000LL),reale(1554483,7516134885LL<<16), + -reale(2460922,0x6540542d68000LL),reale(615586,0x6f27f96118400LL), + reale(45119934611LL,0xe897fd72d67cbLL), + // C4[5], coeff of eps^15, polynomial in n of order 14 + reale(385255297,0xc522d651da000LL),-reale(58599463,0x810289e63d000LL), + -reale(271784816,0x96bdc01bbc000LL),reale(164665597,0xfc4f4e3665000LL), + reale(6169937,0xa7ea1cfd2e000LL),reale(36278794,0xf1d4bf77a7000LL), + -reale(41327996,0x5935502f28000LL),reale(1406713,0xae66a659c9000LL), + -reale(16753028,0x6b0d0fac7e000LL),reale(13550589,0x7d5a3390b000LL), + -reale(1765295,0x851b6e8694000LL),reale(7142364,0xca525091ad000LL), + -reale(4183412,0x818c59892a000LL),-reale(96164,0xa4307ac011000LL), + -reale(44020,0x281c2d0515b00LL),reale(45119934611LL,0xe897fd72d67cbLL), + // C4[5], coeff of eps^14, polynomial in n of order 15 + reale(85300002,0xc7e70a9f1c000LL),-reale(294351273,0xafb8edef98000LL), + reale(403760509,0xda2cbc2e94000LL),-reale(107444454,0x9ae8f34870000LL), + -reale(261509454,0x4bda846b4000LL),reale(200593259,0xcaf344c1b8000LL), + -reale(1492598,0x1c0b3e713c000LL),reale(23203659,0x98196f9e60000LL), + -reale(49434335,0xf8209c0184000LL),reale(4620325,0x4eb0e8bd08000LL), + -reale(10475101,0x343acca80c000LL),reale(16597245,8542632147LL<<16), + -reale(2356576,0x3bbee61554000LL),reale(3249396,0x1edbdd7e58000LL), + -reale(4240477,0x930e83f9dc000LL),reale(851256,0x2b979a0197a00LL), + reale(45119934611LL,0xe897fd72d67cbLL), + // C4[5], coeff of eps^13, polynomial in n of order 16 + -reale(334885,0xc6bdc7fcb0000LL),-reale(5563880,0xa3a405a9f1000LL), + reale(77196254,0x955c2ca786000LL),-reale(280592470,0x60fd2cd013000LL), + reale(419465490,0x135ebd637c000LL),-reale(164134806,0xd03e535795000LL), + -reale(238238642,0xf95f61c30e000LL),reale(239782224,0x6d53e5d49000LL), + -reale(20068072,0x4afa414658000LL),reale(6399560,0x53e56b4c47000LL), + -reale(53380994,0xb54d3160a2000LL),reale(13179100,0x7f23319325000LL), + -reale(3190623,0x71f1454c2c000LL),reale(15946535,0x7112262fa3000LL), + -reale(5597132,0xd891768336000LL),-reale(517466,0x3872db407f000LL), + -reale(280398,0x37b65ce5ca500LL),reale(45119934611LL,0xe897fd72d67cbLL), + // C4[5], coeff of eps^12, polynomial in n of order 17 + -reale(9362,0x69735ac9d0000LL),-reale(41698,3327447843LL<<20), + -reale(274851,0x56e2bdf830000LL),-reale(4724425,0xa83b5c01a0000LL), + reale(68370240,0x5baadc4870000LL),-reale(262946254,0xff686b9240000LL), + reale(430395020,0x66a0aab610000LL),-reale(228360148,0x64a23696e0000LL), + -reale(196492193,0xc6f6cbf150000LL),reale(277855749,243039325LL<<19), + -reale(54565881,0x3f0390efb0000LL),-reale(10430670,3478671393LL<<17), + -reale(48232829,0x9769bd8710000LL),reale(26504611,0xd8be140f40000LL), + reale(733724,0x9fb250690000LL),reale(8992810,0x9e09f3a6a0000LL), + -reale(7946224,0xca1f6288d0000LL),reale(1176502,0x79934ee544800LL), + reale(45119934611LL,0xe897fd72d67cbLL), + // C4[5], coeff of eps^11, polynomial in n of order 18 + -real(0x274a66713f785000LL),-real(0x78cbe0a9df914800LL), + -reale(6986,0x5cd0ed6f68000LL),-reale(31980,0xbaca6835fb800LL), + -reale(217574,0x7dc41d384b000LL),-reale(3882916,0x2edd7dacd2800LL), + reale(58859398,0xdc7c0f67f2000LL),-reale(240755855,0x78dc5ddf79800LL), + reale(433769587,0x318800cb6f000LL),-reale(298315443,0xab75c9fd0800LL), + -reale(129660149,0x66ef2473b4000LL),reale(305615878,0x94b6a51048800LL), + -reale(109156237,0x593300db57000LL),-reale(18007247,0x43b21e10e800LL), + -reale(29424146,0x61ad17715a000LL),reale(38156138,0xf0096c8a4a800LL), + -reale(4683041,0xee399b1b9d000LL),-reale(1149725,0xbf46657f8c800LL), + -reale(1106736,0x8c2ceac93e180LL),reale(45119934611LL,0xe897fd72d67cbLL), + // C4[5], coeff of eps^10, polynomial in n of order 19 + -real(0x3bd4906e474e000LL),-real(0x97941b80ce3c000LL), + -real(0x1a66716bc5afa000LL),-real(0x532298a0bc3e0000LL), + -reale(4939,0xda9250746000LL),-reale(23308,0x7863f72384000LL), + -reale(164254,0x558c90eef2000LL),-reale(3056120,0xcef6e5fe8000LL), + reale(48766418,0xafc6204b42000LL),-reale(213414260,0xdc9b1ebcc000LL), + reale(425806905,0x15318e0496000LL),-reale(369415923,0x757d6c39f0000LL), + -reale(31178847,0x2c748765b6000LL),reale(306118804,0x213b4942ec000LL), + -reale(181898310,0x263b289662000LL),reale(568685,0x4686791808000LL), + -reale(309548,0x34bb55302e000LL),reale(32975540,0x34fcc4d2a4000LL), + -reale(16246779,0x8dca2dd5da000LL),reale(1477949,0xdae92a7065f00LL), + reale(45119934611LL,0xe897fd72d67cbLL), + // C4[5], coeff of eps^9, polynomial in n of order 20 + -real(0x69d018a3b9e000LL),-real(0xed437c3919a800LL), + -real(0x237e48279feb000LL),-real(0x5bea2151a0b3800LL), + -real(0x10666acb6ec18000LL),-real(0x350c7e1643d3c800LL), + -reale(3247,0xe2be74bf45000LL),-reale(15860,0x268da19a55800LL), + -reale(116263,0x5e4790b892000LL),-reale(2266502,0x8314b6fb1e800LL), + reale(38294967,0xecf46ee8e1000LL),-reale(180538484,0x555f9ed2b7800LL), + reale(401643505,0x9c33fda5f4000LL),-reale(432258273,0xf8da98e440800LL), + reale(101814780,0x5dd5e11f87000LL),reale(252370005,0x80f91f9d26800LL), + -reale(252307179,0x99e21a8986000LL),reale(63455824,0x191a53ee5d800LL), + reale(12621880,0x95e41abad000LL),reale(2033357,0xc3307b9c44800LL), + -reale(4727243,0x20838a8bae80LL),reale(45119934611LL,0xe897fd72d67cbLL), + // C4[5], coeff of eps^8, polynomial in n of order 21 + -real(0xc09a6adbf4000LL),-real(0x18cab6e3030000LL), + -real(0x359d0ace62c000LL),-real(0x7ab7d9cc438000LL), + -real(0x12c67ab580a4000LL),-real(856171152199LL<<18), + -real(0x9233f1c13ddc000LL),-real(0x1e779de654b48000LL), + -real(0x789f22a00b054000LL),-reale(9796,7021023797LL<<16), + -reale(75089,0xae07706a8c000LL),-reale(1543001,0x638fcd4c58000LL), + reale(27798321,0x1e96e700fc000LL),-reale(142306959,0xd3ad6eb8e0000LL), + reale(355697955,0xce7f78ffc4000LL),-reale(469861249,0x5989105b68000LL), + reale(259457720,0x1370b4ff4c000LL),reale(112194489,0x36d40ed990000LL), + -reale(260872269,0xf8005192ec000LL),reale(151422395,0x58f7b5f388000LL), + -reale(32332898,0xbdc6e34964000LL),reale(433029,0xe4d3ce78fba00LL), + reale(45119934611LL,0xe897fd72d67cbLL), + // C4[5], coeff of eps^7, polynomial in n of order 22 + -real(0x1441fa2f35000LL),-real(0x272c726527800LL), + -real(0x4ebdd7b856000LL),-real(0xa564301b74800LL), + -real(0x16d6333bd37000LL),-real(0x3580dec1951800LL), + -real(0x865ae53c178000LL),-real(0x16ec61d7f65e800LL), + -real(0x455fa2e228b9000LL),-real(0xef77f4cbfa3b800LL), + -real(0x3d9c6e708569a000LL),-reale(5230,0x8a511fbc88800LL), + -reale(42196,0xcfdba8cebb000LL),-reale(920786,0xf57a80c4e5800LL), + reale(17837247,0x2fc56aab44000LL),-reale(100064916,0x5e72032af2800LL), + reale(283253574,0xc37962f3c3000LL),-reale(455567530,0xe21e28364f800LL), + reale(400948026,0xf028b16722000LL),-reale(118913774,0x549816fe9c800LL), + -reale(112010399,0x36034a3e3f000LL),reale(121825743,0x78c43cf486800LL), + -reale(36338425,0x426e19287b880LL), + reale(45119934611LL,0xe897fd72d67cbLL), + // C4[5], coeff of eps^6, polynomial in n of order 23 + -real(0x1b5badebe000LL),-real(0x326332ca4000LL),-real(0x5fd1bd93a000LL), + -real(0xbcd8e5378000LL),-real(0x1837bef256000LL), + -real(0x3404424ccc000LL),-real(0x75bf8cd1d2000LL), + -real(38025986691LL<<17),-real(0x2dc96f11f6e000LL), + -real(0x811a6e895f4000LL),-real(0x195036bc82ea000LL), + -real(0x5af70d135548000LL),-real(0x187d57cdaa406000LL), + -reale(2189,0x32d399c61c000LL),-reale(18742,0x385cb42a82000LL), + -reale(438375,0xd6a8872030000LL),reale(9224813,0x89f7eb41e2000LL), + -reale(57288808,0xfdc8999b44000LL),reale(184899999,0x331692f966000LL), + -reale(357870966,0x3154fb6f18000LL),reale(431875147,0x7929b7544a000LL), + -reale(318710001,0xe0f19bd36c000LL),reale(131641087,0xbb852faace000LL), + -reale(23311442,0x9e8a4070e9d00LL), + reale(45119934611LL,0xe897fd72d67cbLL), + // C4[5], coeff of eps^5, polynomial in n of order 24 + -real(92116035LL<<14),-real(0x26e7bc2d800LL),-real(0x46d3779b000LL), + -real(0x84e1d0c0800LL),-real(0x101cbc30a000LL),-real(0x2073376e3800LL), + -real(0x442adb8b9000LL),-real(0x963884ff6800LL),-real(0x15dbd71e08000LL), + -real(0x363ebc6d59800LL),-real(0x9122bbd857000LL), + -real(0x1a90a4ab06c800LL),-real(0x56f0a68cd06000LL), + -real(0x147a29992a8f800LL),-real(0x5d1402e6c175000LL), + -real(0x228e263277d22800LL),-reale(5078,0x584c613b04000LL), + -reale(128863,0x92233985800LL),reale(2982258,0xd360aa0ed000LL), + -reale(20710125,0x5bbe664118800LL),reale(76213261,0x519df32cfe000LL), + -reale(171479837,0xf7a363253b800LL),reale(241341994,0x2d1ed763cf000LL), + -reale(186491540,0xf45203874e800LL),reale(58278606,0x8c59a11a48880LL), + reale(45119934611LL,0xe897fd72d67cbLL), + // C4[6], coeff of eps^29, polynomial in n of order 0 + 139264,real(63626127165LL), + // C4[6], coeff of eps^28, polynomial in n of order 1 + real(247833LL<<16),real(4782743552LL),real(0x219ae3fb400f15LL), + // C4[6], coeff of eps^27, polynomial in n of order 2 + real(420150473LL<<18),-real(0x876551ce0000LL),real(0x350bfa156000LL), + reale(4837,0x68f14547adebLL), + // C4[6], coeff of eps^26, polynomial in n of order 3 + real(0x297e6b0e9e1000LL),-real(0x2e90de909aa000LL), + real(0x6148b0a84b000LL),real(0x1d77336bca600LL), + reale(207992,0x1a086a30a3679LL), + // C4[6], coeff of eps^25, polynomial in n of order 4 + real(0x10bc6a9e4ee30000LL),-real(0xc179e3d40c9c000LL), + real(0x3edf483df118000LL),-real(0x5c91fff78634000LL), + real(0x216fdab58654400LL),reale(10078162,0xbedd8dc0620e7LL), + // C4[6], coeff of eps^24, polynomial in n of order 5 + reale(17715,0xdb1cfba26000LL),-reale(7689,0x9976d7f948000LL), + reale(6474,0xb1047d5d4a000LL),-reale(6855,0xa6eeabbaa4000LL), + real(0x2ac3e335ea26e000LL),real(0xd6d2e7c22e28400LL), + reale(372892021,0x96057cce2c163LL), + // C4[6], coeff of eps^23, polynomial in n of order 6 + reale(279883,0xa92c150938000LL),-reale(86797,0xd10c69f53c000LL), + reale(160072,0xfd9d58a4d0000LL),-reale(96731,0xc2b3d16724000LL), + reale(32938,0x46d62be868000LL),-reale(52162,0xc27e2d9b0c000LL), + reale(17103,0x67a9fde667c00LL),reale(4101812237LL,0x723c5cdbe4f41LL), + // C4[6], coeff of eps^22, polynomial in n of order 7 + reale(293467,0x7db7c77729000LL),-reale(146628,0x46fd92fe6000LL), + reale(282074,0xcdca0f3f8b000LL),-reale(92435,0x174eb2c344000LL), + reale(105774,0xf5edeb18ed000LL),-reale(100726,0x78839052a2000LL), + reale(6619,0xde4489894f000LL),reale(2174,0xdeb0a21cf2e00LL), + reale(4101812237LL,0x723c5cdbe4f41LL), + // C4[6], coeff of eps^21, polynomial in n of order 8 + reale(183603,8337878185LL<<19),-reale(387951,0x8934978f10000LL), + reale(363243,0x9b8677d760000LL),-reale(100927,0x6adc79e30000LL), + reale(246790,7131746729LL<<18),-reale(115867,0xce56197550000LL), + reale(45470,0x976a005d20000LL),-reale(74789,0x6bec0ac470000LL), + reale(21823,0x7d1eb3d72b000LL),reale(4101812237LL,0x723c5cdbe4f41LL), + // C4[6], coeff of eps^20, polynomial in n of order 9 + reale(2390210,0x71ea4526d8000LL),-reale(11473167,6397281565LL<<18), + reale(3566140,0xe9fdb6daa8000LL),-reale(3459649,0xbdbfad5d70000LL), + reale(5328875,0xe507b89678000LL),-reale(1202839,0xbeff1963a0000LL), + reale(2208040,0x527339ea48000LL),-reale(1770989,0xb71cae09d0000LL), + reale(48626,0x557ebf6618000LL),reale(16670,0x4a1716aa8d000LL), + reale(53323559086LL,0xcd10b72aa064dLL), + // C4[6], coeff of eps^19, polynomial in n of order 10 + reale(16170911,0xf66942f9a0000LL),-reale(15946100,0x87937e1ff0000LL), + reale(1191966,5683381737LL<<19),-reale(10381645,0x67a9610710000LL), + reale(5401104,0xec5f94af60000LL),-reale(1916345,0x9f2b7d6630000LL), + reale(5166787,7293640425LL<<18),-reale(1681428,0xa094a5ad50000LL), + reale(912008,0xad6a83a520000LL),-reale(1452992,0x3f13404c70000LL), + reale(367621,0xca46f4fdbb000LL),reale(53323559086LL,0xcd10b72aa064dLL), + // C4[6], coeff of eps^18, polynomial in n of order 11 + reale(51879505,0x1c6021da42000LL),-reale(3388727,0x452f2e2244000LL), + reale(10993546,0x58785d1036000LL),-reale(19450323,0x2862de39d0000LL), + reale(1456775,0xebc764482a000LL),-reale(7922511,0x8d8f4f815c000LL), + reale(7390372,0xfe1ce59e1e000LL),-reale(1065019,0x2a2a06ce8000LL), + reale(3871757,0x7ef447ee12000LL),-reale(2395461,0x8df44bf074000LL), + -reale(40351,0xb597a7abfa000LL),-reale(17707,0xeba2dcf1c1400LL), + reale(53323559086LL,0xcd10b72aa064dLL), + // C4[6], coeff of eps^17, polynomial in n of order 12 + reale(18941665,0xd940803e20000LL),-reale(2462456,0xc647b5b638000LL), + reale(55543449,0x9a9f25d270000LL),-reale(10182797,0xdffcb19ee8000LL), + reale(4836527,0xb44e233ec0000LL),-reale(21402374,0x58dcab98000LL), + reale(3817083,0xbef1c88b10000LL),-reale(4459099,0x992120d448000LL), + reale(8502561,0xac3fb5bf60000LL),-reale(1525489,0x80b8b610f8000LL), + reale(1649611,0x4cebe6e3b0000LL),-reale(2280763,0x4f507e59a8000LL), + reale(482782,0x1ffc428c24800LL),reale(53323559086LL,0xcd10b72aa064dLL), + // C4[6], coeff of eps^16, polynomial in n of order 13 + reale(169672066,0xfc4e53058c000LL),-reale(255936417,0xcd4166f930000LL), + reale(43044311,0x58bada2414000LL),reale(10984552,0x79ecf34458000LL), + reale(54615551,0xb3c2ab069c000LL),-reale(20672829,0x547b9ae620000LL), + -reale(762958,0xc96d76adc000LL),-reale(20252510,0xad74c43098000LL), + reale(8266131,0x9541dc37ac000LL),-reale(1263055,0x9458475310000LL), + reale(7416125,0xebded0d634000LL),-reale(3121438,0x16f54c0588000LL), + -reale(225538,0xf843322744000LL),-reale(111163,0x41ef8785bb800LL), + reale(53323559086LL,0xcd10b72aa064dLL), + // C4[6], coeff of eps^15, polynomial in n of order 14 + -reale(371272727,0xe93844d330000LL),reale(258600199,0x3ab9b44ef8000LL), + reale(127447726,0xd7dad2fc20000LL),-reale(278220404,0x7730102b8000LL), + reale(77869881,0xad9b189b70000LL),reale(21813766,0xb09d2ff98000LL), + reale(46644312,9197745227LL<<18),-reale(33841430,0x25b28aa218000LL), + -reale(3096455,0x6fa54a95f0000LL),-reale(14807144,0xa86ee6dfc8000LL), + reale(13281582,0xf66e06a960000LL),-reale(452377,0x35cd9cb178000LL), + reale(3621811,0x85d91d8b0000LL),-reale(3791781,0x3a80710f28000LL), + reale(636887,0x5f8cc1d1bc800LL),reale(53323559086LL,0xcd10b72aa064dLL), + // C4[6], coeff of eps^14, polynomial in n of order 15 + -reale(40751652,0x879256f716000LL),reale(182461023,0x62c00442f4000LL), + -reale(366891419,0xe235688602000LL),reale(303920923,0x2a6218fe88000LL), + reale(70640959,0xa70aa30512000LL),-reale(290919308,0xf0cc1f4de4000LL), + reale(124435738,0x116d522626000LL),reale(24575054,0x49539549b0000LL), + reale(29829722,0x6d4c4f193a000LL),-reale(46205497,0xcd680acebc000LL), + reale(1253661,0x8798d15a4e000LL),-reale(5829398,0x329c172b28000LL), + reale(15178042,0x87d0f72562000LL),-reale(3413258,0x604057df94000LL), + -reale(544537,0x1343d1098a000LL),-reale(371792,0x5ec0380ab3400LL), + reale(53323559086LL,0xcd10b72aa064dLL), + // C4[6], coeff of eps^13, polynomial in n of order 16 + reale(100946,21976965LL<<20),reale(2010862,0x3c46708bb0000LL), + -reale(34502092,0x6e09dbf3a0000LL),reale(163298206,0x527fb2e110000LL), + -reale(355839921,948516465LL<<18),reale(347383598,0x3243b82e70000LL), + -reale(2611762,0xae3f6124e0000LL),-reale(286060486,421499843LL<<16), + reale(181022396,2339564421LL<<19),reale(11053843,0x8ea9e8f130000LL), + reale(5354229,0xc704cb69e0000LL),-reale(50862137,0xf12aeaf970000LL), + reale(14064844,5665935493LL<<18),reale(1748678,0x2e869553f0000LL), + reale(9719088,0x671cfc38a0000LL),-reale(6714197,0x76aa8fd6b0000LL), + reale(816805,0x9ce5b98e4f000LL),reale(53323559086LL,0xcd10b72aa064dLL), + // C4[6], coeff of eps^12, polynomial in n of order 17 + real(0x75cff722d22b8000LL),reale(9742,5260669319LL<<19), + reale(75734,0x79163f0448000LL),reale(1568684,0xd935dd4310000LL), + -reale(28213944,0x88db35f228000LL),reale(141802366,0xe4716652a0000LL), + -reale(336424367,0x7aaa4f7098000LL),reale(384795625,0xe2aff0230000LL), + -reale(92516926,0xbd45322708000LL),-reale(252728877,4730701433LL<<18), + reale(239978666,0xfd893c3a88000LL),-reale(28528394,5445461995LL<<16), + -reale(18370370,0x5cd8a4fbe8000LL),-reale(38961300,0x78b7628f20000LL), + reale(30014507,0xb37b1485a8000LL),-reale(654615,0xa96a2bf90000LL), + -reale(667571,0x85c41bf0c8000LL),-reale(1181523,0x1c81baa857000LL), + reale(53323559086LL,0xcd10b72aa064dLL), + // C4[6], coeff of eps^11, polynomial in n of order 18 + real(0x55d873de6520000LL),real(0x12c7cfeef6810000LL), + real(0x4e200e3f1e1LL<<20),reale(6671,0xd2467fb9f0000LL), + reale(53806,3275978471LL<<17),reale(1163348,0xd1cfb7f3d0000LL), + -reale(22032298,0xf3cc53d740000LL),reale(118198962,4397370971LL<<16), + -reale(306929389,0x72efa76b60000LL),reale(409945031,0xba4df5f90000LL), + -reale(195574008,5584443935LL<<19),-reale(178055138,0x4cd4f3ce90000LL), + reale(282861404,0xd715020c60000LL),-reale(99637722,0xf11193d4b0000LL), + -reale(20986520,0xfb661347c0000LL),-reale(8771627,7018708525LL<<16), + reale(31360164,0xdb2c51c420000LL),-reale(12477955,8590832271LL<<16), + reale(873590,0xbe0d3e9693000LL),reale(53323559086LL,0xcd10b72aa064dLL), + // C4[6], coeff of eps^10, polynomial in n of order 19 + real(0x5808512b12b000LL),real(0xfaa729276e2000LL), + real(0x3175560e4519000LL),real(0xb21b680b3a90000LL), + real(0x2fcbc5fe71407000LL),reale(4229,0xf0de326e3e000LL), + reale(35532,0x38e22907f5000LL),reale(805604,0x42db4fa3ec000LL), + -reale(16150031,0xfe4d67d51d000LL),reale(93034137,0xf6628ead9a000LL), + -reale(265995225,0x398943192f000LL),reale(414315266,0x970145dd48000LL), + -reale(301204836,0xc549c7ba41000LL),-reale(51738066,0x4e1063bb0a000LL), + reale(275650719,0x10481031ad000LL),-reale(187610845,0x85f00095c000LL), + reale(25230256,0x4ada23b49b000LL),reale(13917204,0x3da6dc4452000LL), + reale(4066715,0x8660f73889000LL),-reale(4361677,0xea98323d07e00LL), + reale(53323559086LL,0xcd10b72aa064dLL), + // C4[6], coeff of eps^9, polynomial in n of order 20 + real(0x65fa8c6bf0000LL),real(0xfe88642ae4000LL),real(0x2aa82304e58000LL), + real(0x7ca8bddcccc000LL),real(434853972467LL<<18), + real(0x5e16320d44b4000LL),real(0x1a2859bf40b28000LL), + reale(2409,0x1b825da69c000LL),reale(21179,0xabe6860d90000LL), + reale(506292,0x5b6e5f0684000LL),-reale(10810252,0xeee1886808000LL), + reale(67327238,0xa18a80786c000LL),-reale(213364581,0xe79aac41a0000LL), + reale(387619687,0x51e3ba1054000LL),-reale(387180015,0xd550406b38000LL), + reale(121695298,0x2400c6e23c000LL),reale(172879787,0x9e57682f30000LL), + -reale(230507460,0xb74e70fddc000LL),reale(112381926,0x4eee70a198000LL), + -reale(20283371,0x42949e7bf4000LL),-reale(288686,0x988d3450a7c00LL), + reale(53323559086LL,0xcd10b72aa064dLL), + // C4[6], coeff of eps^8, polynomial in n of order 21 + real(0x72e86a7de000LL),real(8772831327LL<<15),real(0x273ffc1812000LL), + real(0x64635c5cac000LL),real(0x11473cdd246000LL), + real(0x33fd816c260000LL),real(0xae6e2137a7a000LL), + real(0x29ff10928814000LL),real(0xc26a115cf4ae000LL), + real(0x492994f20c1c8000LL),reale(10833,0x80f3c9e4e2000LL), + reale(274842,0xd406a2037c000LL),-reale(6296293,0xca802ed0ea000LL), + reale(42731189,0xb6f3d1e130000LL),-reale(151191524,0x41a7e788b6000LL), + reale(320575109,0xae49526ee4000LL),-reale(416345568,0xb8c8445e82000LL), + reale(298319523,0xb52957c098000LL),-reale(42956565,0x78799bae4e000LL), + -reale(119892798,0x70342c95b4000LL),reale(103927174,0x8691916be6000LL), + -reale(29157346,0x2fb5a3d22ec00LL), + reale(53323559086LL,0xcd10b72aa064dLL), + // C4[6], coeff of eps^7, polynomial in n of order 22 + real(74709635LL<<15),real(0x4ba47734000LL),real(0xa7b994d0000LL), + real(0x1869c5c6c000LL),real(0x3c23e3d88000LL),real(0x9e1c8b7a4000LL), + real(1882100649LL<<18),real(0x573ad5a4dc000LL),real(0x12f915ab6f8000LL), + real(0x4c1f4084014000LL),real(0x170ced7cbfb0000LL), + real(0x921b89aca54c000LL),real(0x599b4a7922068000LL), + reale(38914,0x1efa73f084000LL),-reale(964915,0x51a6da0ae0000LL), + reale(7200274,0x92a23dbc000LL),-reale(28652022,0x356dea628000LL), + reale(70837833,0x39cdeca8f4000LL),-reale(114872161,0xfcdf3a9570000LL), + reale(122704354,0xd9bfe74e2c000LL),-reale(83141739,0x9edadabcb8000LL), + reale(32332898,0xbdc6e34964000LL),-reale(5485045,0x527ae1fc73400LL), + reale(17774519695LL,0x99b03d0e3576fLL), + // C4[6], coeff of eps^6, polynomial in n of order 23 + real(257316433920LL),real(517719121920LL),real(0xfb6e649000LL), + real(0x221f7064000LL),real(0x4d84a37f000LL),real(0xb958155a000LL), + real(0x1d5dd0db5000LL),real(0x4faa5a050000LL),real(0xea04686eb000LL), + real(0x2f40e3db46000LL),real(0xab8623d121000LL),real(0x2d147c4903c000LL), + real(0xe63ae874e57000LL),real(0x60cd21bcc932000LL), + real(0x3f869e23e408d000LL),reale(29814,0xcc97221028000LL), + -reale(808726,0x6d837bf63d000LL),reale(6700876,0x1daf27af1e000LL), + -reale(30153942,0x8594329407000LL),reale(86154121,0x7da76bf014000LL), + -reale(165128732,0xdb80e436d1000LL),reale(210163841,0xd18cc55d0a000LL), + -reale(153581269,0x570b79c9b000LL),reale(46622885,0x3d1480e1d3a00LL), + reale(53323559086LL,0xcd10b72aa064dLL), + // C4[7], coeff of eps^29, polynomial in n of order 0 + real(13087612928LL),real(0x90e6983c364f3dLL), + // C4[7], coeff of eps^28, polynomial in n of order 1 + -real(161707LL<<21),real(7239297LL<<14),real(0xcf8f801ee602cdLL), + // C4[7], coeff of eps^27, polynomial in n of order 2 + -real(3500022825LL<<20),real(630513507LL<<19),real(0x6038c37fa000LL), + reale(72555,0x626230f3330c5LL), + // C4[7], coeff of eps^26, polynomial in n of order 3 + -real(92252949633LL<<21),real(16187170389LL<<22), + -real(51975912235LL<<21),real(0x7c00d0f2b78000LL), + reale(3119881,0x867e38d993117LL), + // C4[7], coeff of eps^25, polynomial in n of order 4 + -real(0x64d0a86bae7c0000LL),real(0x7c07ce24c65f0000LL), + -real(0x739ece76489e0000LL),real(0x6e7bce15f550000LL), + real(0x24fc420030b8400LL),reale(127915142,0x8a371ad88dcafLL), + // C4[7], coeff of eps^24, polynomial in n of order 5 + -reale(5990,0xbd2326cc40000LL),reale(14992,4018200301LL<<20), + -reale(6873,8929851351LL<<18),reale(2782,8051012645LL<<19), + -reale(4583,0xc89924b340000LL),real(0x52aed30dcf988800LL), + reale(430260024,0xe82db7640b7c1LL), + // C4[7], coeff of eps^23, polynomial in n of order 6 + -reale(169326,4206873009LL<<17),reale(261065,0x25b4e353d0000LL), + -reale(59142,0xf0c50992c0000LL),reale(111182,4597550539LL<<16), + -reale(88869,504433083LL<<17),reale(2313,0xe34bfe3f90000LL), + real(0x32dc48b9e1d23400LL),reale(4732860273LL,0xf9f6e14c7e54bLL), + // C4[7], coeff of eps^22, polynomial in n of order 7 + -reale(467157,1100000847LL<<20),reale(258178,755278933LL<<21), + -reale(91474,559664221LL<<20),reale(248285,171426119LL<<22), + -reale(82821,231309675LL<<20),reale(44668,65972935LL<<21), + -reale(71456,2669582201LL<<20),reale(18220,0x9846e079d4000LL), + reale(4732860273LL,0xf9f6e14c7e54bLL), + // C4[7], coeff of eps^21, polynomial in n of order 8 + -reale(10858183,1145150433LL<<21),reale(1155453,0xa514064740000LL), + -reale(4408275,1110140307LL<<19),reale(4494002,0xa8330ec1c0000LL), + -reale(693747,3759921697LL<<20),reale(2336198,8880970129LL<<18), + -reale(1499288,4981657777LL<<19),-reale(18466,6402610053LL<<18), + -reale(7818,0x6ee4879b83000LL),reale(61527183561LL,0xb18970e26a4cfLL), + // C4[7], coeff of eps^20, polynomial in n of order 9 + -reale(7907170,4058896835LL<<20),reale(1601483,335338375LL<<23), + -reale(11238504,3427529005LL<<20),reale(2745284,1787777405LL<<21), + -reale(2325455,2252860775LL<<20),reale(4939939,712213223LL<<22), + -reale(1021126,555773201LL<<20),reale(952760,1631005375LL<<21), + -reale(1365312,965324491LL<<20),reale(299618,0x2f589c3f22000LL), + reale(61527183561LL,0xb18970e26a4cfLL), + // C4[7], coeff of eps^19, polynomial in n of order 10 + reale(5811147,7891888051LL<<19),reale(19155879,6260648859LL<<18), + -reale(13234724,832589145LL<<21),-reale(473729,0xaccee67ac0000LL), + -reale(9690431,2460044795LL<<19),reale(5218195,5282091375LL<<18), + -reale(699193,3313511321LL<<20),reale(4032431,0xb01d955a40000LL), + -reale(1901524,5999844905LL<<19),-reale(111197,715304509LL<<18), + -reale(51622,0xdda253af9f000LL),reale(61527183561LL,0xb18970e26a4cfLL), + // C4[7], coeff of eps^18, polynomial in n of order 11 + -reale(34477536,1085877825LL<<20),reale(44845230,817114545LL<<21), + reale(4606432,1572161669LL<<20),reale(12496576,210421693LL<<23), + -reale(18271471,1543698101LL<<20),-reale(128700,742574025LL<<21), + -reale(6139017,689151983LL<<20),reale(7385046,100502461LL<<22), + -reale(590509,2783893289LL<<20),reale(1800602,699157181LL<<21), + -reale(2092277,3566080099LL<<20),reale(381025,0x99466ecd7c000LL), + reale(61527183561LL,0xb18970e26a4cfLL), + // C4[7], coeff of eps^17, polynomial in n of order 12 + -reale(152644671,981125379LL<<19),-reale(24136152,0xd3514f38e0000LL), + -reale(16909786,8097141141LL<<18),reale(53988238,0xc115854860000LL), + -reale(2192558,3293732289LL<<20),reale(3853073,2819007469LL<<17), + -reale(20689919,5309095411LL<<18),reale(3514368,0xf1b4463ee0000LL), + -reale(1814216,3975618817LL<<19),reale(7354899,0xbd88356420000LL), + -reale(2207882,191252177LL<<18),-reale(269543,3717910997LL<<17), + -reale(156646,0x7bcb3b3a6a800LL),reale(61527183561LL,0xb18970e26a4cfLL), + // C4[7], coeff of eps^16, polynomial in n of order 13 + reale(52565396,753292423LL<<19),reale(252855342,568744119LL<<21), + -reale(197183211,7281644191LL<<19),-reale(6678358,3552459447LL<<20), + reale(4519131,7283469291LL<<19),reale(56648760,112164189LL<<22), + -reale(15289276,2020707835LL<<19),-reale(3713103,1403767329LL<<20), + -reale(17880720,7304289905LL<<19),reale(9494998,1497636157LL<<21), + reale(492167,2907561065LL<<19),reale(3952538,4294903605LL<<20), + -reale(3358139,5130468237LL<<19),reale(480004,0x1e727719e9000LL), + reale(61527183561LL,0xb18970e26a4cfLL), + // C4[7], coeff of eps^15, polynomial in n of order 14 + reale(279617399,0xd9972cba40000LL),-reale(353187715,0x687b832220000LL), + reale(118965967,4456434973LL<<19),reale(220096359,3595022681LL<<17), + -reale(240814657,8170991797LL<<18),reale(28075084,0xec7a345460000LL), + reale(24758769,1818605983LL<<20),reale(48013974,0xb5345431a0000LL), + -reale(32373431,0xc7bac8f4c0000LL),-reale(5075135,8642954025LL<<17), + -reale(9094832,6469786017LL<<19),reale(13639028,3685620545LL<<17), + -reale(1773068,1431802737LL<<18),-reale(460476,0x51ab5a8ea0000LL), + -reale(423738,0x5d98934922800LL),reale(61527183561LL,0xb18970e26a4cfLL), + // C4[7], coeff of eps^14, polynomial in n of order 15 + reale(16417106,2408387839LL<<20),-reale(93245803,1562234793LL<<21), + reale(256985456,250552029LL<<20),-reale(365861944,857240429LL<<22), + reale(190902238,1499270843LL<<20),reale(163412998,1423242741LL<<21), + -reale(274443985,2668181351LL<<20),reale(82958237,163620913LL<<23), + reale(33859016,1729347703LL<<20),reale(25275487,1495319443LL<<21), + -reale(45844273,3794232747LL<<20),reale(4490176,231613489LL<<22), + reale(1010900,690735667LL<<20),reale(10013483,1036831025LL<<21), + -reale(5637707,2068106223LL<<20),reale(570308,0x8f0afe45ec000LL), + reale(61527183561LL,0xb18970e26a4cfLL), + // C4[7], coeff of eps^13, polynomial in n of order 16 + -reale(25657,393048869LL<<22),-reale(608651,0xacbc40d5c0000LL), + reale(12764052,2144856077LL<<19),-reale(76823449,3121867141LL<<18), + reale(228672619,4131243473LL<<20),-reale(367062288,0xf756dca4c0000LL), + reale(263470997,8569317111LL<<19),reale(78573490,0xffb10f8fc0000LL), + -reale(283548774,1794660389LL<<21),reale(154702622,9227087281LL<<18), + reale(16937276,1939608161LL<<19),-reale(6432822,7897704317LL<<18), + -reale(43016670,946798949LL<<20),reale(22087851,0xaa7600dd40000LL), + reale(1665577,8523064651LL<<19),-reale(163221,0xe0acad2e40000LL), + -reale(1189371,0x766c2260a3000LL),reale(61527183561LL,0xb18970e26a4cfLL), + // C4[7], coeff of eps^12, polynomial in n of order 17 + -real(0x13bc5107d5fLL<<20),-real(506650109317LL<<24), + -reale(17217,2185571073LL<<20),-reale(426469,557216187LL<<21), + reale(9411503,1140836685LL<<20),-reale(60299258,66945391LL<<22), + reale(194753933,1835852139LL<<20),-reale(352928157,2046106529LL<<21), + reale(328231147,2405007161LL<<20),-reale(34561926,360605189LL<<23), + -reale(248371006,3915897705LL<<20),reale(226668375,1401273273LL<<21), + -reale(40114392,2920598683LL<<20),-reale(25898188,1028871717LL<<22), + -reale(16043876,2538787453LL<<20),reale(28698456,1825641427LL<<21), + -reale(9602688,2437057327LL<<20),reale(502063,0xa52218333a000LL), + reale(61527183561LL,0xb18970e26a4cfLL), + // C4[7], coeff of eps^11, polynomial in n of order 18 + -real(81880241733LL<<19),-real(651169421489LL<<18), + -real(194261131981LL<<22),-real(0x4616f301f1bc0000LL), + -reale(10659,7786635659LL<<19),-reale(276843,0xf150eaf340000LL), + reale(6459374,425055961LL<<20),-reale(44283297,2370521611LL<<18), + reale(156003403,8328479919LL<<19),-reale(319848045,0xab86b09a40000LL), + reale(372382116,1407449139LL<<21),-reale(166870261,0xbaeb2e09c0000LL), + -reale(148815577,7753476247LL<<19),reale(260443738,1330203003LL<<18), + -reale(131653575,428167437LL<<20),reale(2775725,691412797LL<<18), + reale(12306214,6299226531LL<<19),reale(5355345,9401097695LL<<18), + -reale(3966302,0xcbc08bfb17000LL),reale(61527183561LL,0xb18970e26a4cfLL), + // C4[7], coeff of eps^10, polynomial in n of order 19 + -real(1704454843LL<<20),-real(2722537665LL<<21),-real(19434970697LL<<20), + -real(4989045369LL<<24),-real(394962411735LL<<20), + -real(0x128b33efecfLL<<21),-reale(5903,789230693LL<<20), + -reale(161527,569013611LL<<22),reale(4006338,1271698701LL<<20), + -reale(29564239,1312346333LL<<21),reale(114267945,2347153791LL<<20), + -reale(265827046,63320697LL<<23),reale(379233361,4202669809LL<<20), + -reale(292689947,1148927723LL<<21),reale(19915451,3747715939LL<<20), + reale(197711494,385979271LL<<22),-reale(197401730,911113003LL<<20), + reale(83971818,387288839LL<<21),-reale(12852829,1345691321LL<<20), + -reale(602476,0x5fc28370ac000LL),reale(61527183561LL,0xb18970e26a4cfLL), + // C4[7], coeff of eps^9, polynomial in n of order 20 + -real(304621785LL<<18),-real(0xc9814e4b0000LL),-real(5069418237LL<<17), + -real(0x7c4fe70d90000LL),-real(7691534469LL<<20), + -real(0x7a02179d470000LL),-real(0x274586580a60000LL), + -real(0x10907db87bd50000LL),-reale(2773,9732262223LL<<18), + -reale(80424,78339267LL<<16),reale(2134032,0xd8c3d9bae0000LL), + -reale(17066460,0x8709888510000LL),reale(72842964,6932239995LL<<19), + -reale(192914141,0x448548ebf0000LL),reale(332328916,0xa61d5e5020000LL), + -reale(364348462,0x260e7984d0000LL),reale(215166704,0xfd6630ec0000LL), + reale(5301792,6304582341LL<<16),-reale(118567350,0x6a550b6aa0000LL), + reale(89166503,0x5c73fd2370000LL),-reale(23960987,0x75c7f62663400LL), + reale(61527183561LL,0xb18970e26a4cfLL), + // C4[7], coeff of eps^8, polynomial in n of order 21 + -real(11869221LL<<18),-real(7450235LL<<20),-real(79397539LL<<18), + -real(113271327LL<<19),-real(700448177LL<<18),-real(148973407LL<<22), + -real(9118660335LL<<18),-real(20216702289LL<<19), + -real(0xcadd965ff40000LL),-real(386512744317LL<<20), + -real(0xf93c68aca7bLL<<18),-reale(30847,5279995331LL<<19), + reale(882325,8584251383LL<<18),-reale(7706931,2116826591LL<<21), + reale(36580048,2730390969LL<<18),-reale(110604386,3847062005LL<<19), + reale(227103584,0x9e98f54ac0000LL),-reale(323034443,1752619391LL<<20), + reale(314251676,0xb5ebcf2b40000LL),-reale(199218854,3061725287LL<<19), + reale(73903768,9476063903LL<<18),-reale(12124837,0x72a953b85800LL), + reale(61527183561LL,0xb18970e26a4cfLL), + // C4[7], coeff of eps^7, polynomial in n of order 22 + -real(575575LL<<17),-real(2681133LL<<16),-real(1637545LL<<18), + -real(16890107LL<<16),-real(23159565LL<<17),-real(0x8210e690000LL), + -real(27276821LL<<20),-real(0x5bebf1b70000LL),-real(3075032387LL<<17), + -real(0x6a5f183250000LL),-real(40477467135LL<<18), + -real(0x11b5c31caf30000LL),-real(0xd14cd352ff20000LL), + -reale(6969,0xb17d189610000LL),reale(216834,7757873387LL<<19), + -reale(2087035,0xf153506af0000LL),reale(11091105,0x4b9d7f7a20000LL), + -reale(38290720,0xa99fbe31d0000LL),reale(91897729,0x9718fbaac0000LL), + -reale(156643857,0x418d7e6eb0000LL),reale(184759421,0x6102c9a360000LL), + -reale(129331594,0xf71b8d2590000LL),reale(38395317,0x415c2de726c00LL), + reale(61527183561LL,0xb18970e26a4cfLL), + // C4[8], coeff of eps^29, polynomial in n of order 0 + real(7241<<16),real(0x112c657acf71bLL), + // C4[8], coeff of eps^28, polynomial in n of order 1 + real(1165359LL<<20),real(3168035LL<<17),real(0x21ffb4a731cf423fLL), + // C4[8], coeff of eps^27, polynomial in n of order 2 + real(41827383LL<<21),-real(137865429LL<<20),real(631109843LL<<16), + reale(4837,0x68f14547adebLL), + // C4[8], coeff of eps^26, polynomial in n of order 3 + real(54350489115LL<<22),-real(21656377197LL<<23),real(1080358617LL<<22), + real(0x5c4a2579a0000LL),reale(3535865,0xba8f0d3ad9e09LL), + // C4[8], coeff of eps^25, polynomial in n of order 4 + reale(4480,63845967LL<<22),-real(0x5f0bc8cec07LL<<20), + real(0x198015cca1fLL<<21),-real(0x51d1e6f78cdLL<<20), + real(0x14fb331d33f30000LL),reale(144970494,0xe0e91e6ce4f71LL), + // C4[8], coeff of eps^24, polynomial in n of order 5 + reale(226427,7535956641LL<<17),-reale(36730,6647829291LL<<19), + reale(116830,5936429895LL<<17),-reale(76966,613785099LL<<18), + -real(0x2a948e8d73a60000LL),-real(0x116572b5168a4000LL), + reale(5363908310LL,0x81b165bd17b55LL), + // C4[8], coeff of eps^23, polynomial in n of order 6 + reale(151394,3866446399LL<<20),-reale(105723,1435687723LL<<19), + reale(240417,2090106533LL<<21),-reale(54672,3991575693LL<<19), + reale(46185,3230210197LL<<20),-reale(67790,4028416911LL<<19), + reale(15270,0xa469197488000LL),reale(5363908310LL,0x81b165bd17b55LL), + // C4[8], coeff of eps^22, polynomial in n of order 7 + -reale(105618,1394014919LL<<21),-reale(5351753,377020849LL<<22), + reale(3446650,1690522763LL<<21),-reale(453181,286167171LL<<23), + reale(2431204,1637447437LL<<21),-reale(1239333,63204475LL<<22), + -reale(60030,1665832481LL<<21),-reale(26716,0x6a2a5d69d0000LL), + reale(69730808036LL,0x96022a9a34351LL), + // C4[8], coeff of eps^21, polynomial in n of order 8 + reale(4362900,465328075LL<<22),-reale(10560212,802403079LL<<19), + reale(656010,2976408017LL<<20),-reale(3068612,8162681445LL<<19), + reale(4482659,1990068235LL<<21),-reale(516359,5969201251LL<<19), + reale(1022585,502576667LL<<20),-reale(1273161,3447687361LL<<19), + reale(245310,0x45a78ad538000LL),reale(69730808036LL,0x96022a9a34351LL), + // C4[8], coeff of eps^20, polynomial in n of order 9 + reale(23624906,1629010283LL<<19),-reale(5851601,324958949LL<<22), + reale(255419,6850290885LL<<19),-reale(10559838,1365338319LL<<20), + reale(3058631,5351542623LL<<19),-reale(782822,266312293LL<<21), + reale(4071490,3032871865LL<<19),-reale(1457911,2387656005LL<<20), + -reale(144540,929274797LL<<19),-reale(76349,0x2c1c25d590000LL), + reale(69730808036LL,0x96022a9a34351LL), + // C4[8], coeff of eps^19, polynomial in n of order 10 + reale(23056909,2395766741LL<<20),reale(8427619,3212023717LL<<19), + reale(19522568,367619617LL<<22),-reale(12637641,5752438869LL<<19), + -reale(1859539,2126155853LL<<20),-reale(7720368,2358643951LL<<19), + reale(5969641,447801057LL<<21),-reale(4464,2860310889LL<<19), + reale(1954609,2968726289LL<<20),-reale(1904959,7710818563LL<<19), + reale(302310,0x7de136fc28000LL),reale(69730808036LL,0x96022a9a34351LL), + // C4[8], coeff of eps^18, polynomial in n of order 11 + -reale(34760584,1377594673LL<<21),-reale(45089279,700199389LL<<22), + reale(38964787,642296389LL<<21),reale(8377867,242649263LL<<24), + reale(10805340,270655563LL<<21),-reale(18348308,77894251LL<<22), + -reale(8504,712824639LL<<21),-reale(2874058,104939633LL<<23), + reale(7002991,271121287LL<<21),-reale(1456031,497148985LL<<22), + -reale(262921,1640850307LL<<21),-reale(186713,0x66184da2b0000LL), + reale(69730808036LL,0x96022a9a34351LL), + // C4[8], coeff of eps^17, polynomial in n of order 12 + reale(266733950,1060079417LL<<21),-reale(92315127,311764467LL<<19), + -reale(39784767,2743383633LL<<20),-reale(24124714,5368290721LL<<19), + reale(52035773,16490707LL<<22),-reale(214469,3779317103LL<<19), + -reale(32744,3406796695LL<<20),-reale(19012957,4710528797LL<<19), + reale(5897141,767669011LL<<21),reale(758445,547828629LL<<19), + reale(4185368,186448291LL<<20),-reale(2955613,5547446041LL<<19), + reale(363691,0xed908404b8000LL),reale(69730808036LL,0x96022a9a34351LL), + // C4[8], coeff of eps^16, polynomial in n of order 13 + -reale(254507630,0xe2b8b6bb40000LL),-reale(58148124,1471923579LL<<20), + reale(270522720,3187458133LL<<18),-reale(149985652,7726894061LL<<19), + -reale(27328603,0x99e6e9ea40000LL),reale(4011861,407374679LL<<21), + reale(54943982,0xaf3dd22640000LL),-reale(17478454,3922351195LL<<19), + -reale(6848089,0x9bbe86d940000LL),-reale(11885922,3297252137LL<<20), + reale(11706960,678889437LL<<18),-reale(595378,2432546249LL<<19), + -reale(329167,0xe066968840000LL),-reale(450081,0x595d162958000LL), + reale(69730808036LL,0x96022a9a34351LL), + // C4[8], coeff of eps^15, polynomial in n of order 14 + -reale(166710239,3480741959LL<<20),reale(313421255,2329933911LL<<19), + -reale(299209385,1287661491LL<<21),reale(24383199,5307535921LL<<19), + reale(248029318,3243559867LL<<20),-reale(210032461,8189928917LL<<19), + reale(10907073,960500783LL<<22),reale(29948106,2038678405LL<<19), + reale(40306034,2725271357LL<<20),-reale(36745958,6196825729LL<<19), + -reale(1934460,123839633LL<<21),-reale(492518,4761069735LL<<19), + reale(9949315,1255380799LL<<20),-reale(4720329,388065197LL<<19), + reale(398374,0x9081f25c18000LL),reale(69730808036LL,0x96022a9a34351LL), + // C4[8], coeff of eps^14, polynomial in n of order 15 + -reale(5499415,942753073LL<<21),reale(38811064,349279653LL<<22), + -reale(140017234,1709558915LL<<21),reale(290760665,163783697LL<<23), + -reale(332595868,714890693LL<<21),reale(118902683,730607999LL<<22), + reale(189429058,237701737LL<<21),-reale(256456610,243945477LL<<24), + reale(78365400,1246868327LL<<21),reale(35514427,78282137LL<<22), + reale(8045132,96899221LL<<21),-reale(42695880,422981029LL<<23), + reale(15212575,506177875LL<<21),reale(2863173,903918451LL<<22), + reale(284029,1922203905LL<<21),-reale(1161079,0x6c18f2ad70000LL), + reale(69730808036LL,0x96022a9a34351LL), + // C4[8], coeff of eps^13, polynomial in n of order 16 + reale(5407,439728533LL<<23),reale(151556,693836399LL<<19), + -reale(3836797,3870271773LL<<20),reale(28742693,8016450573LL<<19), + -reale(111747677,1508361473LL<<21),reale(256964119,236840267LL<<19), + -reale(347691811,711701031LL<<20),reale(216072654,2622689769LL<<19), + reale(88295276,790755477LL<<22),-reale(263658780,5516898905LL<<19), + reale(163753678,37603151LL<<20),-reale(1803857,6339257275LL<<19), + -reale(22560155,108468139LL<<21),-reale(21197875,3801517693LL<<19), + reale(25658955,3111371333LL<<20),-reale(7416706,6937931487LL<<19), + reale(268690,0x9ce0757848000LL),reale(69730808036LL,0x96022a9a34351LL), + // C4[8], coeff of eps^12, polynomial in n of order 17 + real(369814360487LL<<19),real(159053188703LL<<23), + reale(3152,1136779065LL<<19),reale(92558,2295771257LL<<20), + -reale(2473330,1734833909LL<<19),reale(19757571,360351901LL<<21), + -reale(83162616,6619531363LL<<19),reale(212413714,2066066043LL<<20), + -reale(337117487,5524436113LL<<19),reale(299293348,697772127LL<<22), + -reale(51076102,4535292671LL<<19),-reale(200831521,1217539203LL<<20), + reale(227963885,2651839699LL<<19),-reale(87452614,163103009LL<<21), + -reale(9664164,7186873499LL<<19),reale(9739937,3920029055LL<<20), + reale(6101400,4999938359LL<<19),-reale(3588081,0x84422b3e50000LL), + reale(69730808036LL,0x96022a9a34351LL), + // C4[8], coeff of eps^11, polynomial in n of order 18 + real(3576016431LL<<20),real(32208729499LL<<19),real(10983028711LL<<23), + real(0x9286be006280000LL),real(0x65e9f47db41LL<<20), + reale(50386,4528870031LL<<19),-reale(1428014,1685009291LL<<21), + reale(12227031,6176103481LL<<19),-reale(56007028,2392678701LL<<20), + reale(159476659,5817614083LL<<19),-reale(295263705,809939737LL<<22), + reale(344605761,6427356205LL<<19),-reale(202719833,951923227LL<<20), + -reale(50658828,5629491977LL<<19),reale(201884255,1512264231LL<<21), + -reale(166564947,5368120671LL<<19),reale(63259557,2614639991LL<<20), + -reale(8140125,1990873493LL<<19),-reale(722971,0xa61c9dba68000LL), + reale(69730808036LL,0x96022a9a34351LL), + // C4[8], coeff of eps^10, polynomial in n of order 19 + real(45596577LL<<21),real(81531441LL<<22),real(656187675LL<<21), + real(191463201LL<<25),real(17391213765LL<<21),real(65094511967LL<<22), + real(0x16272ee843fLL<<21),reale(23168,382193603LL<<23), + -reale(700305,441535191LL<<21),reale(6465118,134564813LL<<22), + -reale(32414063,913166045LL<<21),reale(103314135,145041825LL<<24), + -reale(222332965,1267927603LL<<21),reale(326070132,55789563LL<<22), + -reale(309964302,1355885369LL<<21),reale(149975409,308317249LL<<23), + reale(35633361,576916401LL<<21),-reale(113104897,1027785623LL<<22), + reale(77116975,1889590315LL<<21),-reale(20082545,0xcd53c80110000LL), + reale(69730808036LL,0x96022a9a34351LL), + // C4[8], coeff of eps^9, polynomial in n of order 20 + real(1123785LL<<21),real(13838643LL<<19),real(23159565LL<<20), + real(171251217LL<<19),real(44667189LL<<23),real(3472549135LL<<19), + real(10302054723LL<<20),real(162001999341LL<<19), + real(500351698399LL<<21),reale(8102,6578411627LL<<19), + -reale(262912,1458939143LL<<20),reale(2634746,8016047177LL<<19), + -reale(14551212,731365323LL<<22),reale(52133305,8561410375LL<<19), + -reale(129964645,2819080401LL<<20),reale(232230181,2215647013LL<<19), + -reale(298362920,1016411147LL<<21),reale(269480987,8039357859LL<<19), + -reale(161945649,1493828379LL<<20),reale(57837731,7816254593LL<<19), + -reale(9237971,9476063903LL<<15),reale(69730808036LL,0x96022a9a34351LL), + // C4[8], coeff of eps^8, polynomial in n of order 21 + real(292383LL<<17),real(202215LL<<19),real(2386137LL<<17), + real(3789747LL<<18),real(26247507LL<<17),real(6294651LL<<21), + real(437764365LL<<17),real(1112245757LL<<18),real(0x67551030e0000LL), + real(28804895217LL<<19),real(0x2c0f1d988820000LL), + real(0x66a336663d1c0000LL),-reale(57641,8501165381LL<<17), + reale(631918,3696102011LL<<20),-reale(3870503,720372107LL<<17), + reale(15639991,0xcc8b836440000LL),-reale(44892569,0xd7d7de220000LL), + reale(94682509,2360318459LL<<19),-reale(147486216,0x5ecdb08ae0000LL), + reale(163873573,0xbeaba7b6c0000LL),-reale(110855652,0xd3ce78fba0000LL), + reale(32332898,0xbdc6e34964000LL),reale(69730808036LL,0x96022a9a34351LL), + // C4[9], coeff of eps^29, polynomial in n of order 0 + real(16847<<16),real(0x3d2e2985830503LL), + // C4[9], coeff of eps^28, polynomial in n of order 1 + -real(207753LL<<23),real(1712087LL<<18),real(0x438da32e1600335LL), + // C4[9], coeff of eps^27, polynomial in n of order 2 + -real(3127493161LL<<21),-real(38277317LL<<20),-real(0xe4960490000LL), + reale(161925,0x30e683ffe0741LL), + // C4[9], coeff of eps^26, polynomial in n of order 3 + -real(9299582409LL<<22),real(3656674463LL<<23),-real(10918261107LL<<22), + real(80278491423LL<<17),reale(1317283,0x4f8aa089603a9LL), + // C4[9], coeff of eps^25, polynomial in n of order 4 + -real(711479186953LL<<22),reale(3279,1361598081LL<<20), + -real(0x3749d192179LL<<21),-real(309897952117LL<<20), + -real(0x1f18264b9990000LL),reale(162025847,0x379b22013c233LL), + // C4[9], coeff of eps^24, polynomial in n of order 5 + -reale(133856,15001023LL<<25),reale(223946,23087107LL<<27), + -reale(32028,12079289LL<<25),reale(48931,2142027LL<<26), + -reale(63933,112742755LL<<25),reale(12842,2153614949LL<<20), + reale(5994956347LL,0x96bea2db115fLL), + // C4[9], coeff of eps^23, polynomial in n of order 6 + -reale(5988742,4056322469LL<<20),reale(2349145,7648181561LL<<19), + -reale(426462,344885543LL<<21),reale(2475174,940948911LL<<19), + -reale(999559,3441325239LL<<20),-reale(83146,4971496059LL<<19), + -reale(41198,0x4f02423cb8000LL),reale(77934432511LL,0x7a7ae451fe1d3LL), + // C4[9], coeff of eps^22, polynomial in n of order 7 + -reale(8631189,1052889985LL<<21),-reale(629492,634245703LL<<22), + -reale(3874477,505696163LL<<21),reale(3866974,513650043LL<<23), + -reale(159710,1408881461LL<<21),reale(1100061,714281683LL<<22), + -reale(1180171,586380503LL<<21),reale(201643,0x9fcf910730000LL), + reale(77934432511LL,0x7a7ae451fe1d3LL), + // C4[9], coeff of eps^21, polynomial in n of order 8 + reale(341632,721850923LL<<22),reale(2597220,4632100393LL<<19), + -reale(10372056,1528523471LL<<20),reale(1205419,4719051179LL<<19), + -reale(1145316,921601685LL<<21),reale(3990959,6117017869LL<<19), + -reale(1073059,3486842565LL<<20),-reale(153111,7776387185LL<<19), + -reale(94130,0x280827bb28000LL),reale(77934432511LL,0x7a7ae451fe1d3LL), + // C4[9], coeff of eps^20, polynomial in n of order 9 + reale(3783713,134627971LL<<22),reale(23115315,66415493LL<<25), + -reale(6392067,518305043LL<<22),-reale(1733013,275013225LL<<23), + -reale(8816564,833972409LL<<22),reale(4468878,247379557LL<<24), + reale(300534,553592433LL<<22),reale(2085027,317816093LL<<23), + -reale(1725044,456624309LL<<22),reale(240877,0x8d28f00d60000LL), + reale(77934432511LL,0x7a7ae451fe1d3LL), + // C4[9], coeff of eps^19, polynomial in n of order 10 + -reale(58776429,1067354331LL<<20),reale(18829393,7579267909LL<<19), + reale(10681211,592856305LL<<22),reale(16946593,1116323851LL<<19), + -reale(14277877,2775669533LL<<20),-reale(2149268,8027942223LL<<19), + -reale(4056423,828321103LL<<21),reale(6443828,4480734455LL<<19), + -reale(857619,751312863LL<<20),-reale(227988,4599783267LL<<19), + -reale(205805,0x3340b739f8000LL),reale(77934432511LL,0x7a7ae451fe1d3LL), + // C4[9], coeff of eps^18, polynomial in n of order 11 + -reale(8326980,196156635LL<<21),-reale(34821146,552451591LL<<22), + -reale(46791029,557069513LL<<21),reale(38334852,177025053LL<<24), + reale(8937287,838991609LL<<21),reale(5317827,1033371567LL<<22), + -reale(18378967,400717301LL<<21),reale(2844411,12754877LL<<23), + reale(593018,1659418637LL<<21),reale(4310821,76308389LL<<22), + -reale(2591282,1217948513LL<<21),reale(276451,0x9f1a0fb950000LL), + reale(77934432511LL,0x7a7ae451fe1d3LL), + // C4[9], coeff of eps^17, polynomial in n of order 12 + -reale(182057178,279202431LL<<21),reale(246730983,7282837989LL<<19), + -reale(61609386,3656132889LL<<20),-reale(45340440,795982425LL<<19), + -reale(20534253,134894613LL<<22),reale(51951312,243959241LL<<19), + -reale(4823611,581671439LL<<20),-reale(5624597,8387045493LL<<19), + -reale(13789372,989768405LL<<21),reale(9667615,6509295661LL<<19), + reale(215496,1395596667LL<<20),-reale(184969,6596889361LL<<19), + -reale(459826,0xaf07be5d28000LL),reale(77934432511LL,0x7a7ae451fe1d3LL), + // C4[9], coeff of eps^16, polynomial in n of order 13 + reale(316814308,524172905LL<<23),-reale(186563878,63588247LL<<25), + -reale(110274143,526845649LL<<23),reale(263023219,83035351LL<<24), + -reale(130904637,509865531LL<<23),-reale(30397924,20206077LL<<26), + reale(13683269,123318603LL<<23),reale(48158450,141529345LL<<24), + -reale(26437768,420249375LL<<23),-reale(5662772,129791197LL<<25), + -reale(2185901,350246489LL<<23),reale(9629298,177188459LL<<24), + -reale(3949112,493038403LL<<23),reale(276643,3634960421LL<<18), + reale(77934432511LL,0x7a7ae451fe1d3LL), + // C4[9], coeff of eps^15, polynomial in n of order 14 + reale(78761274,365004673LL<<20),-reale(201515576,8484307809LL<<19), + reale(313194006,1602645493LL<<21),-reale(252751914,5568714583LL<<19), + -reale(13191170,2167441005LL<<20),reale(241719441,7606152787LL<<19), + -reale(201822768,404840345LL<<22),reale(21302083,5136432093LL<<19), + reale(37050656,1255073061LL<<20),reale(20598069,641077127LL<<19), + -reale(39565379,1270355289LL<<21),reale(9630032,7047419793LL<<19), + reale(3352897,1867330359LL<<20),reale(651457,7128437307LL<<19), + -reale(1114108,0x7475455348000LL),reale(77934432511LL,0x7a7ae451fe1d3LL), + // C4[9], coeff of eps^14, polynomial in n of order 15 + reale(1503684,1762694997LL<<21),-reale(12945810,457623793LL<<22), + reale(59109631,1997027375LL<<21),-reale(165337541,436423661LL<<23), + reale(292248408,1310925625LL<<21),-reale(302358268,80333091LL<<22), + reale(101329883,1625451155LL<<21),reale(168206436,256940945LL<<24), + -reale(245462494,1698275235LL<<21),reale(106772813,575322475LL<<22), + reale(20184277,1515722423LL<<21),-reale(15841583,115367503LL<<23), + -reale(24343366,297803839LL<<21),reale(22619454,642864953LL<<22), + -reale(5751128,1751200357LL<<21),reale(119914,0x778fad9290000LL), + reale(77934432511LL,0x7a7ae451fe1d3LL), + // C4[9], coeff of eps^13, polynomial in n of order 16 + -real(494538685723LL<<23),-reale(30244,7532247025LL<<19), + reale(913230,2357276371LL<<20),-reale(8356271,5886749331LL<<19), + reale(41054740,50726383LL<<21),-reale(125953300,8377060373LL<<19), + reale(252781900,3961145001LL<<20),-reale(323011393,7392450487LL<<19), + reale(214671336,735258085LL<<22),reale(38642307,2838366023LL<<19), + -reale(221064383,2701482241LL<<20),reale(190191489,7753766309LL<<19), + -reale(54203341,2021364059LL<<21),-reale(15936650,4639479773LL<<19), + reale(7069294,1728459477LL<<20),reale(6475976,7904740225LL<<19), + -reale(3243677,0x65d7af1058000LL),reale(77934432511LL,0x7a7ae451fe1d3LL), + // C4[9], coeff of eps^12, polynomial in n of order 17 + -real(4941153649LL<<22),-real(2434362319LL<<26), + -real(480183190319LL<<22),-reale(15428,422153761LL<<23), + reale(492912,506448323LL<<22),-reale(4815395,177795021LL<<24), + reale(25573504,64498885LL<<22),-reale(86374812,63633203LL<<23), + reale(196725482,856584503LL<<22),-reale(303474922,105041487LL<<25), + reale(296000607,1045914233LL<<22),-reale(124541003,470657541LL<<23), + -reale(96946363,592229397LL<<22),reale(194787320,217061649LL<<24), + -reale(139624521,484247315LL<<22),reale(48044895,435975913LL<<23), + -reale(5082038,276187937LL<<22),-reale(749748,0x6069872020000LL), + reale(77934432511LL,0x7a7ae451fe1d3LL), + // C4[9], coeff of eps^11, polynomial in n of order 18 + -real(231323121LL<<20),-real(2351460757LL<<19),-real(912558841LL<<23), + -real(0xdfda7610580000LL),-real(777314384543LL<<20), + -reale(6590,3852961377LL<<19),reale(223861,17176789LL<<21), + -reale(2346980,3623268951LL<<19),reale(13542533,3871010099LL<<20), + -reale(50565862,7643343277LL<<19),reale(130735502,766383623LL<<22), + -reale(239800507,7719641123LL<<19),reale(308448660,3395101317LL<<20), + -reale(258446712,3844536697LL<<19),reale(99709743,227483207LL<<21), + reale(54337873,5199140497LL<<19),-reale(105984135,215955881LL<<20), + reale(67263140,627338299LL<<19),-reale(17110329,0x5fa94e648000LL), + reale(77934432511LL,0x7a7ae451fe1d3LL), + // C4[9], coeff of eps^10, polynomial in n of order 19 + -real(538707LL<<21),-real(1075491LL<<22),-real(9728097LL<<21), + -real(3213907LL<<25),-real(333357375LL<<21),-real(1438804621LL<<22), + -real(39246385997LL<<21),-real(379094211993LL<<23), + reale(25645,1674653973LL<<21),-reale(290249,472854199LL<<22), + reale(1830100,1274307463LL<<21),-reale(7588281,99130323LL<<24), + reale(22282256,82312105LL<<21),-reale(48025833,432719649LL<<22), + reale(76964476,1304326427LL<<21),-reale(91125940,162742323LL<<23), + reale(77471536,1478654845LL<<21),-reale(44556474,1023100235LL<<22), + reale(15423395,377918063LL<<21),-reale(2409905,0x7f0a0dc2b0000LL), + reale(25978144170LL,0x7e28f6c5ff5f1LL), + // C4[9], coeff of eps^9, polynomial in n of order 20 + -real(16575LL<<21),-real(226005LL<<19),-real(421083LL<<20), + -real(3487431LL<<19),-real(1025715LL<<23),-real(90604825LL<<19), + -real(308056405LL<<20),-real(5606626571LL<<19),-real(20270111449LL<<21), + -real(0x30ab7cf8dddLL<<19),reale(15220,1707177905LL<<20), + -reale(187210,7636838095LL<<19),reale(1297995,534056013LL<<22), + -reale(6003229,1506461473LL<<19),reale(20010763,3942424887LL<<20), + -reale(50026909,6827222547LL<<19),reale(95435950,2132760845LL<<21), + -reale(138382128,8075045605LL<<19),reale(146522254,737992253LL<<20), + -reale(96396219,7300467927LL<<19),reale(27713913,0x34f39e3ee8000LL), + reale(77934432511LL,0x7a7ae451fe1d3LL), + // C4[10], coeff of eps^29, polynomial in n of order 0 + real(14059LL<<19),real(0x168a4531304537LL), + // C4[10], coeff of eps^28, polynomial in n of order 1 + -real(1004279LL<<22),-real(3373361LL<<19),reale(3807,0xdf0925caacfb9LL), + // C4[10], coeff of eps^27, polynomial in n of order 2 + real(78580619LL<<24),-real(212705597LL<<23),real(705875469LL<<19), + reale(59656,0xa639fabc960fdLL), + // C4[10], coeff of eps^26, polynomial in n of order 3 + real(927832218729LL<<21),-real(204500125453LL<<22), + -real(29157611613LL<<21),-real(0x66c4e2e4040000LL), + reale(23087123,0x49a60b16d9e77LL), + // C4[10], coeff of eps^25, polynomial in n of order 4 + real(26024288967LL<<27),-real(7678900515LL<<25),real(13514191015LL<<26), + -real(31097026337LL<<25),real(89826688809LL<<21), + reale(25583028,0x820b055e82c23LL), + // C4[10], coeff of eps^24, polynomial in n of order 5 + reale(1328855,126349401LL<<24),-reale(550962,13774891LL<<26), + reale(2464835,125518543LL<<24),-reale(784466,25625323LL<<25), + -reale(93184,68528187LL<<24),-reale(52198,1190112709LL<<21), + reale(86138056986LL,0x5ef39e09c8055LL), + // C4[10], coeff of eps^23, polynomial in n of order 6 + -reale(1114607,27405733LL<<26),-reale(4563722,53821803LL<<25), + reale(3169393,348585LL<<27),reale(68182,92955763LL<<25), + reale(1172595,45755337LL<<26),-reale(1088988,13585007LL<<25), + reale(166307,46143431LL<<21),reale(86138056986LL,0x5ef39e09c8055LL), + // C4[10], coeff of eps^22, polynomial in n of order 7 + reale(5480278,504127481LL<<20),-reale(9293162,155326547LL<<21), + -reale(197247,3072475525LL<<20),-reale(1644302,932629169LL<<22), + reale(3811061,3287215741LL<<20),-reale(747954,323686257LL<<21), + -reale(145848,3935467265LL<<20),-reale(106662,0xb8d6e5aaa0000LL), + reale(86138056986LL,0x5ef39e09c8055LL), + // C4[10], coeff of eps^21, polynomial in n of order 8 + reale(22796753,23076841LL<<25),-reale(927290,180149865LL<<22), + -reale(369606,74581989LL<<23),-reale(9303996,552920075LL<<22), + reale(3036817,71115369LL<<24),reale(396000,898162707LL<<22), + reale(2181010,484753161LL<<23),-reale(1556188,389640079LL<<22), + reale(192540,3401040927LL<<18),reale(86138056986LL,0x5ef39e09c8055LL), + // C4[10], coeff of eps^20, polynomial in n of order 9 + reale(862955,1266777839LL<<21),reale(7027949,96012647LL<<24), + reale(20762590,1932890753LL<<21),-reale(9559408,1057130891LL<<22), + -reale(3064719,1040728173LL<<21),-reale(5129237,23711641LL<<23), + reale(5760893,1279205669LL<<21),-reale(394463,240514009LL<<22), + -reale(178786,1942994377LL<<21),-reale(217080,8651652815LL<<18), + reale(86138056986LL,0x5ef39e09c8055LL), + // C4[10], coeff of eps^19, polynomial in n of order 10 + -reale(10913096,468931943LL<<23),-reale(57356320,139563275LL<<22), + reale(21632622,17971173LL<<25),reale(12352870,1067519707LL<<22), + reale(10546968,197307279LL<<23),-reale(16476123,321986815LL<<22), + reale(466396,220388453LL<<24),reale(183297,876676071LL<<22), + reale(4340035,31265157LL<<23),-reale(2266775,500956659LL<<22), + reale(210337,0xe1ea7a84c0000LL),reale(86138056986LL,0x5ef39e09c8055LL), + // C4[10], coeff of eps^18, polynomial in n of order 11 + reale(183220667,2590575043LL<<20),reale(3573393,1902991101LL<<21), + -reale(38433982,657724943LL<<20),-reale(39892891,403988263LL<<23), + reale(42677900,967722655LL<<20),reale(4292702,1711217099LL<<21), + -reale(2792039,799969587LL<<20),-reale(14767346,746757159LL<<22), + reale(7703673,1133060475LL<<20),reale(747527,637790873LL<<21), + -reale(45502,3704918615LL<<20),-reale(458872,0xc21d355260000LL), + reale(86138056986LL,0x5ef39e09c8055LL), + // C4[10], coeff of eps^17, polynomial in n of order 12 + -reale(61780842,49135749LL<<25),-reale(196091506,391376453LL<<23), + reale(232013693,187926637LL<<24),-reale(59475550,301219495LL<<23), + -reale(46331600,62864215LL<<26),-reale(5439903,151048009LL<<23), + reale(49627120,102515675LL<<24),-reale(16690048,509576107LL<<23), + -reale(7354945,21733079LL<<25),-reale(3769052,366616397LL<<23), + reale(9145462,214930505LL<<24),-reale(3305318,371590575LL<<23), + reale(189374,6271289399LL<<19),reale(86138056986LL,0x5ef39e09c8055LL), + // C4[10], coeff of eps^16, polynomial in n of order 13 + -reale(246951312,552772347LL<<22),reale(295555190,29721595LL<<24), + -reale(151972143,664869293LL<<22),-reale(114414430,423169395LL<<23), + reale(248915402,492058657LL<<22),-reale(140322148,99500631LL<<25), + -reale(15757705,299151505LL<<22),reale(29401843,368167099LL<<23), + reale(29725213,39057725LL<<22),-reale(34936824,2445655LL<<24), + reale(5290998,483472011LL<<22),reale(3412892,270211305LL<<23), + reale(939828,308185177LL<<22),-reale(1058440,2262901433LL<<19), + reale(86138056986LL,0x5ef39e09c8055LL), + // C4[10], coeff of eps^15, polynomial in n of order 14 + -reale(29237793,21929809LL<<24),reale(96597143,85827693LL<<23), + -reale(210653294,75090197LL<<25),reale(297719766,264531499LL<<23), + -reale(232859751,332419LL<<24),reale(779198,466262825LL<<23), + reale(210565738,49075321LL<<26),-reale(210231291,35636249LL<<23), + reale(60435795,147172683LL<<24),reale(30790678,95503589LL<<23), + -reale(8341137,27440903LL<<25),-reale(25891285,147600733LL<<23), + reale(19770912,119140889LL<<24),-reale(4475853,348372575LL<<23), + reale(24304,4909664935LL<<19),reale(86138056986LL,0x5ef39e09c8055LL), + // C4[10], coeff of eps^14, polynomial in n of order 15 + -reale(326980,1465789373LL<<20),reale(3379554,1566468779LL<<21), + -reale(19029758,226591575LL<<20),reale(68313947,940737655LL<<22), + -reale(165836985,3033756273LL<<20),reale(273579872,472941105LL<<21), + -reale(286670501,2169744907LL<<20),reale(131674848,489609853LL<<23), + reale(102478771,1504412891LL<<20),-reale(220713363,225877065LL<<21), + reale(153302553,1201451329LL<<20),-reale(29968476,1046042243LL<<22), + -reale(18498599,2914872793LL<<20),reale(4637884,270502717LL<<21), + reale(6604171,2668065421LL<<20),-reale(2936921,0x8973648be0000LL), + reale(86138056986LL,0x5ef39e09c8055LL), + // C4[10], coeff of eps^13, polynomial in n of order 16 + real(8181919521LL<<26),reale(4651,463423847LL<<22), + -reale(165627,528682553LL<<23),reale(1821092,755660373LL<<22), + -reale(11021646,91392285LL<<24),reale(43145010,992272131LL<<22), + -reale(116748177,310953403LL<<23),reale(223068773,47271409LL<<22), + -reale(294932999,99296991LL<<25),reale(242263024,286305695LL<<22), + -reale(60276785,30271037LL<<23),-reale(125363778,717272947LL<<22), + reale(182026841,37372833LL<<24),-reale(116787755,417593029LL<<22), + reale(36759949,346012225LL<<23),-reale(3061422,962217431LL<<22), + -reale(731281,0xaaf6b13240000LL),reale(86138056986LL,0x5ef39e09c8055LL), + // C4[10], coeff of eps^12, polynomial in n of order 17 + real(777809483LL<<21),real(436668683LL<<25),real(99139014933LL<<21), + real(0x1cfc4bfd58dLL<<22),-reale(70023,1623299233LL<<21), + reale(822756,446933025LL<<23),-reale(5376526,2111656919LL<<21), + reale(23042396,297910775LL<<22),-reale(69630161,297782669LL<<21), + reale(153266731,112309515LL<<24),-reale(247130955,1577554627LL<<21), + reale(284460705,580739169LL<<22),-reale(212091093,1801700473LL<<21), + reale(61297082,319619083LL<<23),reale(65462218,2096893009LL<<21), + -reale(98426842,1047683893LL<<22),reale(59152561,1235484315LL<<21), + -reale(14780753,0xb5d74354c0000LL), + reale(86138056986LL,0x5ef39e09c8055LL), + // C4[10], coeff of eps^11, polynomial in n of order 18 + real(1233981LL<<23),real(14104237LL<<22),real(6201077LL<<26), + real(933195507LL<<22),real(6966040851LL<<23),real(592370721657LL<<22), + -reale(22184,189431713LL<<24),reale(279989,474035391LL<<22), + -reale(1985627,52832535LL<<23),reale(9357698,635528005LL<<22), + -reale(31645438,92987147LL<<25),reale(79909927,996806539LL<<22), + -reale(153562851,494252097LL<<23),reale(225351987,543734289LL<<22), + -reale(249473559,252214923LL<<24),reale(201676475,478217047LL<<22), + -reale(111804841,353712747LL<<23),reale(37701632,700509149LL<<22), + -reale(5783773,3281237837LL<<18),reale(86138056986LL,0x5ef39e09c8055LL), + // C4[10], coeff of eps^10, polynomial in n of order 19 + real(57057LL<<20),real(126819LL<<21),real(1284843LL<<20), + real(478667LL<<24),real(56414325LL<<20),real(279062861LL<<21), + real(8810413183LL<<20),real(99625441377LL<<22), + -reale(3997,1800115191LL<<20),reale(54510,559965495LL<<21), + -reale(421909,1796318189LL<<20),reale(2196607,410595787LL<<23), + -reale(8328804,1896277603LL<<20),reale(24012916,1506461473LL<<21), + -reale(53875133,2685050457LL<<20),reale(94820235,193579723LL<<22), + -reale(129680615,3269639887LL<<20),reale(131955714,608596747LL<<21), + -reale(84828673,2009615045LL<<20),reale(24099054,0xf664899ae0000LL), + reale(86138056986LL,0x5ef39e09c8055LL), + // C4[11], coeff of eps^29, polynomial in n of order 0 + -real(255169LL<<19),real(0xbdc79d6e266b55fLL), + // C4[11], coeff of eps^28, polynomial in n of order 1 + -real(535829LL<<26),real(6461547LL<<20),real(0x56e2cdab4666fea1LL), + // C4[11], coeff of eps^27, polynomial in n of order 2 + -real(54075943LL<<25),-real(11012147LL<<24),-real(184884229LL<<19), + reale(65338,0x3c271ece8bf8fLL), + // C4[11], coeff of eps^26, polynomial in n of order 3 + -real(29189823LL<<30),real(157366885LL<<32),-real(637753597LL<<30), + real(13332470307LL<<23),reale(19666808,0xb9ff38da93b23LL), + // C4[11], coeff of eps^25, polynomial in n of order 4 + -reale(768828,16543417LL<<28),reale(2405043,41201001LL<<26), + -reale(595679,17511625LL<<27),-reale(94147,42169149LL<<26), + -reale(60455,661597895LL<<21),reale(94341681461LL,0x436c57c191ed7LL), + // C4[11], coeff of eps^24, polynomial in n of order 5 + -reale(5042070,2793567LL<<28),reale(2454743,3154771LL<<30), + reale(191058,8757223LL<<28),reale(1233521,5992667LL<<29), + -reale(1001395,9125891LL<<28),reale(137539,51052897LL<<22), + reale(94341681461LL,0x436c57c191ed7LL), + // C4[11], coeff of eps^23, polynomial in n of order 6 + -reale(7620321,6390387LL<<27),-reale(1118882,49853273LL<<26), + -reale(2175696,13938625LL<<28),reale(3557797,45648113LL<<26), + -reale(479218,13589073LL<<27),-reale(128928,23280229LL<<26), + -reale(115227,1709406351LL<<21),reale(94341681461LL,0x436c57c191ed7LL), + // C4[11], coeff of eps^22, polynomial in n of order 7 + reale(3030693,3978133LL<<30),reale(1618004,874507LL<<32), + -reale(9217736,134985LL<<30),reale(1767041,97063LL<<33), + reale(345531,3069873LL<<30),reale(2240563,263433LL<<32), + -reale(1400217,895853LL<<30),reale(154222,296573467LL<<23), + reale(94341681461LL,0x436c57c191ed7LL), + // C4[11], coeff of eps^21, polynomial in n of order 8 + reale(304504,49998275LL<<26),reale(21950325,110791689LL<<23), + -reale(5005332,65785063LL<<24),-reale(3038456,102319349LL<<23), + -reale(5977063,34913149LL<<25),reale(5022754,485487661LL<<23), + -reale(45293,7681997LL<<24),-reale(123970,179449809LL<<23), + -reale(222792,7672407751LL<<18),reale(94341681461LL,0x436c57c191ed7LL), + // C4[11], coeff of eps^20, polynomial in n of order 9 + -reale(56432361,120691497LL<<25),reale(6246807,9955417LL<<28), + reale(11410351,83254233LL<<25),reale(14692579,49664915LL<<26), + -reale(13833928,124401461LL<<25),-reale(1245123,9835207LL<<27), + -reale(339985,114545011LL<<25),reale(4291293,22713457LL<<26), + -reale(1980685,98627585LL<<25),reale(159775,7030690975LL<<19), + reale(94341681461LL,0x436c57c191ed7LL), + // C4[11], coeff of eps^19, polynomial in n of order 10 + reale(40826627,234278683LL<<24),-reale(19124677,161584861LL<<23), + -reale(51024100,50981393LL<<26),reale(30646271,384869645LL<<23), + reale(9815197,6859997LL<<24),reale(639236,244693975LL<<23), + -reale(14951689,12090449LL<<25),reale(5916250,151054657LL<<23), + reale(1073676,226322463LL<<24),reale(80953,380993803LL<<23), + -reale(451111,0xff79096c0000LL),reale(94341681461LL,0x436c57c191ed7LL), + // C4[11], coeff of eps^18, polynomial in n of order 11 + -reale(233788807,3535193LL<<28),reale(177892093,3762413LL<<30), + -reale(5486729,8981851LL<<28),-reale(45008759,222901LL<<32), + -reale(22295157,5977845LL<<28),reale(46499690,3347131LL<<30), + -reale(8381947,991351LL<<28),-reale(7636513,1723229LL<<31), + -reale(5108235,6476849LL<<28),reale(8568922,940153LL<<30), + -reale(2769555,11314291LL<<28),reale(126172,1159668425LL<<21), + reale(94341681461LL,0x436c57c191ed7LL), + // C4[11], coeff of eps^17, polynomial in n of order 12 + reale(246666787,23370677LL<<26),-reale(50685638,204720607LL<<24), + -reale(179803952,51059789LL<<25),reale(226753224,100010811LL<<24), + -reale(83690537,703961LL<<27),-reale(36110826,15584139LL<<24), + reale(17880019,26951173LL<<25),reale(35367319,73259919LL<<24), + -reale(29730133,51577369LL<<26),reale(2029349,105583177LL<<24), + reale(3224077,120316375LL<<25),reale(1158582,83501667LL<<24), + -reale(999784,6146420159LL<<19),reale(94341681461LL,0x436c57c191ed7LL), + // C4[11], coeff of eps^16, polynomial in n of order 13 + reale(135472555,2919565LL<<26),-reale(240891001,9823619LL<<28), + reale(277187915,48314475LL<<26),-reale(153860890,22130685LL<<27), + -reale(78071452,50966567LL<<26),reale(224257271,6520287LL<<29), + -reale(168898235,23643785LL<<26),reale(25365615,30758325LL<<27), + reale(33991594,22223845LL<<26),-reale(1325267,13358465LL<<28), + -reale(26274549,1352253LL<<26),reale(17195323,12709799LL<<27), + -reale(3493469,55138895LL<<26),-reale(37171,2996514251LL<<20), + reale(94341681461LL,0x436c57c191ed7LL), + // C4[11], coeff of eps^15, polynomial in n of order 14 + reale(8369149,65829073LL<<25),-reale(34468304,77612041LL<<24), + reale(98238486,50466661LL<<26),-reale(197855127,257258223LL<<24), + reale(275634083,126808451LL<<25),-reale(237329411,109823349LL<<24), + reale(57709083,378039LL<<27),reale(144028485,10992165LL<<24), + -reale(208082193,86131531LL<<25),reale(120116321,182851999LL<<24), + -reale(12733337,27687817LL<<26),-reale(18886416,178008391LL<<24), + reale(2557866,23705959LL<<25),reale(6572718,173475635LL<<24), + -reale(2666354,4022017967LL<<19),reale(94341681461LL,0x436c57c191ed7LL), + // C4[11], coeff of eps^14, polynomial in n of order 15 + reale(54399,8224347LL<<28),-reale(665621,1410497LL<<30), + reale(4527846,10561865LL<<28),-reale(20181039,1593975LL<<31), + reale(63299017,4532479LL<<28),-reale(144047710,3737571LL<<30), + reale(238046961,3527085LL<<28),-reale(274611219,485845LL<<32), + reale(189061064,3080067LL<<28),-reale(9459768,127989LL<<30), + -reale(141083601,3627343LL<<28),reale(166868825,1278147LL<<31), + -reale(97711641,16702617LL<<28),reale(28303864,4120681LL<<30), + -reale(1707991,14300715LL<<28),-reale(691965,964491519LL<<21), + reale(94341681461LL,0x436c57c191ed7LL), + // C4[11], coeff of eps^13, polynomial in n of order 16 + -real(394848061LL<<27),-real(277855615551LL<<23), + reale(21507,221049445LL<<24),-reale(280152,15918397LL<<23), + reale(2046623,76965257LL<<25),-reale(9908745,30179611LL<<23), + reale(34287090,9035647LL<<24),-reale(88042794,304571673LL<<23), + reale(170266683,41403331LL<<26),-reale(246546803,514564215LL<<23), + reale(257558635,22204697LL<<24),-reale(171596509,74164853LL<<23), + reale(32098211,100286083LL<<25),reale(71621283,185724333LL<<23), + -reale(91026815,208301517LL<<24),reale(52421624,501338287LL<<23), + -reale(12919309,7987587551LL<<18),reale(94341681461LL,0x436c57c191ed7LL), + // C4[11], coeff of eps^12, polynomial in n of order 17 + -real(2506701LL<<25),-real(1595211LL<<29),-real(414133331LL<<25), + -real(9611154693LL<<26),reale(6318,129560535LL<<25), + -reale(88018,7994433LL<<27),reale(693686,99032081LL<<25), + -reale(3663195,37640223LL<<26),reale(14022738,83451835LL<<25), + -reale(40598902,6803915LL<<28),reale(90961965,119128629LL<<25), + -reale(159220781,788729LL<<26),reale(217217490,112380639LL<<25), + -reale(227284915,26366059LL<<27),reale(176075660,9208089LL<<25), + -reale(94610548,45670931LL<<26),reale(31201351,21556291LL<<25), + -reale(4712704,700509149LL<<19),reale(94341681461LL,0x436c57c191ed7LL), + // C4[11], coeff of eps^11, polynomial in n of order 18 + -real(13041LL<<24),-real(166957LL<<23),-real(82777LL<<27), + -real(14154867LL<<23),-real(121102751LL<<24),-real(11919970777LL<<23), + real(140288886837LL<<25),-reale(15649,252654239LL<<23), + reale(133731,225409843LL<<24),-reale(773734,115698949LL<<23), + reale(3285982,23309223LL<<26),-reale(10716783,181102411LL<<23), + reale(27557442,232845957LL<<24),-reale(56645854,420384689LL<<23), + reale(93299054,125728615LL<<25),-reale(121534295,132369527LL<<23), + reale(119605179,120525655LL<<24),-reale(75403265,163638237LL<<23), + reale(21207168,6304582341LL<<18),reale(94341681461LL,0x436c57c191ed7LL), + // C4[12], coeff of eps^29, polynomial in n of order 0 + real(2113LL<<23),real(0x495846bc80a035LL), + // C4[12], coeff of eps^28, polynomial in n of order 1 + -real(5059597LL<<25),-real(23775299LL<<22), + reale(61953,0x75e619a89ce07LL), + // C4[12], coeff of eps^27, polynomial in n of order 2 + real(30823201LL<<29),-real(55301563LL<<28),real(131431881LL<<24), + reale(497138,0xbe8dd4238d2e7LL), + // C4[12], coeff of eps^26, polynomial in n of order 3 + real(8059635627LL<<28),-real(757042391LL<<29),-real(311216327LL<<28), + -real(7273579LL<<33),reale(21376966,0x1d2a1f8b6ccdLL), + // C4[12], coeff of eps^25, polynomial in n of order 4 + reale(590308,751003LL<<30),reale(77521,16047653LL<<28), + reale(426657,125003LL<<29),-reale(306166,5244457LL<<28), + reale(37995,207060411LL<<24),reale(34181768645LL,0x62a1b07dc9473LL), + // C4[12], coeff of eps^24, polynomial in n of order 5 + -reale(1599658,2394579LL<<27),-reale(2671318,5123391LL<<29), + reale(3256460,16377243LL<<27),-reale(261261,3982303LL<<28), + -reale(106562,1204279LL<<27),-reale(120793,1029973LL<<24), + reale(102545305936LL,0x27e511795bd59LL), + // C4[12], coeff of eps^23, polynomial in n of order 6 + reale(1248773,4542469LL<<29),-reale(2889741,9813249LL<<28), + reale(234885,3135591LL<<30),reale(66922,9908313LL<<28), + reale(755418,635863LL<<29),-reale(419245,13200621LL<<28), + reale(41213,192739239LL<<24),reale(34181768645LL,0x62a1b07dc9473LL), + // C4[12], coeff of eps^22, polynomial in n of order 7 + reale(20885911,4938503LL<<28),-reale(1107830,1234733LL<<29), + -reale(2370377,3771643LL<<28),-reale(6561451,624527LL<<30), + reale(4279851,10797315LL<<28),reale(210660,3761905LL<<29), + -reale(68724,2033407LL<<28),-reale(224555,1152577LL<<29), + reale(102545305936LL,0x27e511795bd59LL), + // C4[12], coeff of eps^21, polynomial in n of order 8 + -reale(5562062,38325LL<<31),reale(7741765,4470897LL<<28), + reale(17399328,6149297LL<<29),-reale(10890962,10744893LL<<28), + -reale(2368414,446197LL<<30),-reale(891562,9146315LL<<28), + reale(4183586,393403LL<<29),-reale(1730085,6072185LL<<28), + reale(120797,18742483LL<<24),reale(102545305936LL,0x27e511795bd59LL), + // C4[12], coeff of eps^20, polynomial in n of order 9 + reale(3332722,179104103LL<<24),-reale(54245156,21887465LL<<27), + reale(18428910,34326153LL<<24),reale(12293411,106053413LL<<25), + reale(4024929,78680811LL<<24),-reale(14528270,1262953LL<<26), + reale(4350569,36952333LL<<24),reale(1251271,92345015LL<<25), + reale(191236,5049199LL<<24),-reale(439124,1983321823LL<<21), + reale(102545305936LL,0x27e511795bd59LL), + // C4[12], coeff of eps^19, polynomial in n of order 10 + reale(117817828,9756529LL<<27),reale(29304818,21859033LL<<26), + -reale(33857646,3348243LL<<29),-reale(34756825,30241097LL<<26), + reale(40576649,11012471LL<<27),-reale(1809964,37385419LL<<26), + -reale(7014724,9945043LL<<28),-reale(6163099,62399597LL<<26), + reale(7950550,2557949LL<<27),-reale(2323999,3159279LL<<26), + reale(80031,1030811061LL<<22),reale(102545305936LL,0x27e511795bd59LL), + // C4[12], coeff of eps^18, polynomial in n of order 11 + reale(37677439,43610729LL<<26),-reale(212417438,31724009LL<<27), + reale(188774384,60946099LL<<26),-reale(37276694,6182933LL<<29), + -reale(44097735,31570179LL<<26),reale(5696664,10497345LL<<27), + reale(38054298,7154503LL<<26),-reale(24525159,12952085LL<<28), + -reale(350073,57822319LL<<26),reale(2901654,18012267LL<<27), + reale(1319354,63457243LL<<26),-reale(941373,59576227LL<<26), + reale(102545305936LL,0x27e511795bd59LL), + // C4[12], coeff of eps^17, polynomial in n of order 12 + -reale(253394431,1462439LL<<28),reale(237669216,409221LL<<26), + -reale(76296320,28335185LL<<27),-reale(133872864,17217849LL<<26), + reale(218174046,2622387LL<<29),-reale(127998192,57914967LL<<26), + reale(363472,30718057LL<<27),reale(32681168,4189163LL<<26), + reale(4677048,16088179LL<<28),-reale(25857930,7142835LL<<26), + reale(14914891,9312419LL<<27),-reale(2731797,52301425LL<<26), + -reale(76352,726189181LL<<22),reale(102545305936LL,0x27e511795bd59LL), + // C4[12], coeff of eps^16, polynomial in n of order 13 + -reale(53643409,97684423LL<<25),reale(127408278,3174879LL<<27), + -reale(219370358,126672641LL<<25),reale(262176902,49024297LL<<26), + -reale(182579118,132254331LL<<25),-reale(3956056,15289739LL<<28), + reale(167880537,57011019LL<<25),-reale(188895775,46555265LL<<26), + reale(91621970,25449425LL<<25),-reale(762367,26232331LL<<27), + -reale(18049661,12932969LL<<25),reale(839158,10679509LL<<26), + reale(6440415,29973277LL<<25),-reale(2428550,982597961LL<<22), + reale(102545305936LL,0x27e511795bd59LL), + // C4[12], coeff of eps^15, polynomial in n of order 14 + -reale(1786573,13634499LL<<27),reale(8939902,21088027LL<<26), + -reale(31907799,6174271LL<<28),reale(84216248,4944333LL<<26), + -reale(166330228,11364729LL<<27),reale(242706491,8863071LL<<26), + -reale(246937724,7698133LL<<29),reale(139651898,50060433LL<<26), + reale(29493809,19297617LL<<27),-reale(148014628,18656733LL<<26), + reale(151165884,622571LL<<28),-reale(81883041,55488299LL<<26), + reale(21903841,15379355LL<<27),-reale(792996,14574745LL<<26), + -reale(644309,457907141LL<<22),reale(102545305936LL,0x27e511795bd59LL), + // C4[12], coeff of eps^14, polynomial in n of order 15 + -reale(6504,28793619LL<<26),reale(93075,33271365LL<<27), + -reale(752118,6462873LL<<26),reale(4061558,11832697LL<<28), + -reale(15840997,35193887LL<<26),reale(46482714,19239327LL<<27), + -reale(104709924,13039269LL<<26),reale(181860520,7828435LL<<29), + -reale(240159499,36173099LL<<26),reale(229992094,10037497LL<<27), + -reale(136854274,43911089LL<<26),reale(9990763,2550227LL<<28), + reale(74520212,5689801LL<<26),-reale(84057599,709549LL<<27), + reale(46786776,54603587LL<<26),-reale(11406945,39298831LL<<26), + reale(102545305936LL,0x27e511795bd59LL), + // C4[12], coeff of eps^13, polynomial in n of order 16 + real(1030055LL<<30),real(829418525LL<<26),-real(19924010015LL<<27), + reale(9050,10804695LL<<26),-reale(78488,9283787LL<<28), + reale(459151,22444081LL<<26),-reale(1962716,17985613LL<<27), + reale(6408338,7820523LL<<26),-reale(16395176,1626457LL<<29), + reale(33311385,35536325LL<<26),-reale(53946341,7054843LL<<27), + reale(69201696,61410431LL<<26),-reale(69012103,3773337LL<<28), + reale(51544754,7834329LL<<26),-reale(26961871,12889641LL<<27), + reale(8722958,26104467LL<<26),-reale(1300056,320360129LL<<22), + reale(34181768645LL,0x62a1b07dc9473LL), + // C4[12], coeff of eps^12, polynomial in n of order 17 + real(127075LL<<24),real(91195LL<<28),real(26902525LL<<24), + real(715607165LL<<25),-real(73094160425LL<<24), + reale(4440,35913265LL<<26),-reale(41519,978831LL<<24), + reale(264211,112928967LL<<25),-reale(1241795,175695285LL<<24), + reale(4515620,18853435LL<<27),-reale(13069247,261014043LL<<24), + reale(30619380,129358865LL<<25),-reale(58537051,226171969LL<<24), + reale(91194564,65482939LL<<26),-reale(113993206,58979239LL<<24), + reale(109036979,115740763LL<<25),-reale(67602927,138149837LL<<24), + reale(18850816,700509149LL<<21),reale(102545305936LL,0x27e511795bd59LL), + // C4[13], coeff of eps^29, polynomial in n of order 0 + -real(634219LL<<23),reale(3193,0x402148867236bLL), + // C4[13], coeff of eps^28, polynomial in n of order 1 + -real(400561LL<<32),real(1739049LL<<27),reale(66909,0xbcc54ee94d445LL), + // C4[13], coeff of eps^27, polynomial in n of order 2 + -real(6387996953LL<<29),-real(3461245957LL<<28),-real(49206438547LL<<24), + reale(286172946,0xcc6f5fc7e64c9LL), + // C4[13], coeff of eps^26, polynomial in n of order 3 + real(7296571113LL<<30),reale(10661,1488313LL<<31), + -reale(6836,2507629LL<<30),real(103233906747LL<<25), + reale(900397808,0x384bb07b32421LL), + // C4[13], coeff of eps^25, polynomial in n of order 4 + -reale(1030602,1434287LL<<30),reale(976249,6303335LL<<28), + -reale(29214,7243007LL<<29),-reale(27193,4360723LL<<28), + -reale(41363,170006437LL<<24),reale(36916310137LL,0x41f43bb0c949LL), + // C4[13], coeff of eps^24, polynomial in n of order 5 + -reale(2597630,109963LL<<31),-reale(46366,88065LL<<33), + real(835763379LL<<31),reale(754229,667751LL<<32), + -reale(376195,1000319LL<<31),reale(33027,62908623LL<<26), + reale(36916310137LL,0x41f43bb0c949LL), + // C4[13], coeff of eps^23, polynomial in n of order 6 + reale(1907767,7512493LL<<29),-reale(1322539,10099505LL<<28), + -reale(6889396,2784065LL<<30),reale(3566231,12064393LL<<28), + reale(392016,1668111LL<<29),-reale(16024,9677725LL<<28), + -reale(223530,46890859LL<<24),reale(110748930411LL,0xc5dcb3125bdbLL), + // C4[13], coeff of eps^22, polynomial in n of order 7 + reale(2768819,1533979LL<<30),reale(18682895,1051157LL<<31), + -reale(7962826,2061455LL<<30),-reale(3008388,501585LL<<32), + -reale(1419492,411945LL<<30),reale(4033867,1208903LL<<31), + -reale(1511425,98131LL<<30),reale(90538,115806565LL<<25), + reale(110748930411LL,0xc5dcb3125bdbLL), + // C4[13], coeff of eps^21, polynomial in n of order 8 + -reale(51332124,214119LL<<31),reale(7579765,3153587LL<<28), + reale(12475441,1655707LL<<29),reale(7005177,5256105LL<<28), + -reale(13679833,119207LL<<30),reale(3016909,4530623LL<<28), + reale(1323928,2024201LL<<29),reale(284896,6925237LL<<28), + -reale(424636,138679005LL<<24),reale(110748930411LL,0xc5dcb3125bdbLL), + // C4[13], coeff of eps^20, polynomial in n of order 9 + reale(47250711,14679LL<<32),-reale(18472981,62575LL<<35), + -reale(42459575,893863LL<<32),reale(33307537,106651LL<<33), + reale(3060800,842635LL<<32),-reale(5867540,102671LL<<34), + -reale(6941741,849331LL<<32),reale(7324844,423881LL<<33), + -reale(1953157,771073LL<<32),reale(46148,28524089LL<<27), + reale(110748930411LL,0xc5dcb3125bdbLL), + // C4[13], coeff of eps^19, polynomial in n of order 10 + -reale(218954922,27801141LL<<27),reale(144947317,36456347LL<<26), + -reale(2491117,129025LL<<29),-reale(43746541,53566187LL<<26), + -reale(5414513,33128531LL<<27),reale(38475112,39418671LL<<26), + -reale(19653214,3660993LL<<28),-reale(2031714,8235735LL<<26), + reale(2517568,31068687LL<<27),reale(1433299,8158787LL<<26), + -reale(884985,911850811LL<<22),reale(110748930411LL,0xc5dcb3125bdbLL), + // C4[13], coeff of eps^18, polynomial in n of order 11 + reale(186784429,10521821LL<<28),-reale(7066121,6171767LL<<29), + -reale(168709085,159265LL<<28),reale(199772613,1997709LL<<31), + -reale(91000398,5796399LL<<28),-reale(16385586,3500385LL<<29), + reale(28845005,1198547LL<<28),reale(9523912,3994797LL<<30), + -reale(24921512,7172347LL<<28),reale(12920997,2065141LL<<29), + -reale(2137442,11262329LL<<28),-reale(100773,90157665LL<<23), + reale(110748930411LL,0xc5dcb3125bdbLL), + // C4[13], coeff of eps^17, polynomial in n of order 12 + reale(153014747,10291963LL<<28),-reale(229746959,27043849LL<<26), + reale(237324258,14238909LL<<27),-reale(127910449,9268979LL<<26), + -reale(52661288,2811671LL<<29),reale(178449477,48064707LL<<26), + -reale(166899831,11458293LL<<27),reale(67870692,24951769LL<<26), + reale(7326612,206249LL<<28),-reale(16569622,39442673LL<<26), + -reale(550002,21806887LL<<27),reale(6246699,62490405LL<<26), + -reale(2219585,747027389LL<<22),reale(110748930411LL,0xc5dcb3125bdbLL), + // C4[13], coeff of eps^16, polynomial in n of order 13 + reale(15145062,3114639LL<<29),-reale(45473383,71569LL<<31), + reale(104280194,4043033LL<<29),-reale(182691434,3191599LL<<30), + reale(238813543,4302931LL<<29),-reale(215430056,686779LL<<32), + reale(95643898,4170781LL<<29),reale(58502156,871831LL<<30), + -reale(149008930,6169513LL<<29),reale(135928257,1117477LL<<31), + -reale(68778720,227103LL<<29),reale(17013972,3743517LL<<30), + -reale(171458,5381733LL<<29),-reale(594747,43724235LL<<24), + reale(110748930411LL,0xc5dcb3125bdbLL), + // C4[13], coeff of eps^15, polynomial in n of order 14 + reale(268265,12727175LL<<27),-reale(1599130,17232215LL<<26), + reale(6942204,4883571LL<<28),-reale(22914299,20494129LL<<26), + reale(58880733,8011269LL<<27),-reale(118985431,7979051LL<<26), + reale(188592645,4054545LL<<29),-reale(229762161,35295621LL<<26), + reale(203148724,31300867LL<<27),-reale(107385077,53637247LL<<26), + -reale(6680614,2406191LL<<28),reale(75281884,8527271LL<<26), + -reale(77627899,32310399LL<<27),reale(42028799,34263981LL<<26), + -reale(10160264,35032709LL<<22),reale(110748930411LL,0xc5dcb3125bdbLL), + // C4[13], coeff of eps^14, polynomial in n of order 15 + real(8350913025LL<<28),-reale(8241,2495877LL<<29), + reale(78008,13111987LL<<28),-reale(500800,4045265LL<<30), + reale(2364509,9424021LL<<28),-reale(8593646,4773407LL<<29), + reale(24709323,11418567LL<<28),-reale(57114100,2066875LL<<31), + reale(106928260,4889705LL<<28),-reale(162113251,5033977LL<<29), + reale(197269922,8596123LL<<28),-reale(188736396,4070811LL<<30), + reale(136566807,16720509LL<<28),-reale(69783667,938643LL<<29), + reale(22203894,1359919LL<<28),-reale(3271109,212531129LL<<23), + reale(110748930411LL,0xc5dcb3125bdbLL), + // C4[13], coeff of eps^13, polynomial in n of order 16 + -real(94185LL<<30),-real(86179275LL<<26),real(2372802705LL<<27), + -real(83726038305LL<<26),reale(12668,1555717LL<<28), + -reale(87922,39994007LL<<26),reale(452934,19637187LL<<27), + -reale(1815855,62281965LL<<26),reale(5835571,1574167LL<<29), + -reale(15318374,24668899LL<<26),reale(33211265,14617205LL<<27), + -reale(59722012,27257657LL<<26),reale(88729847,57943LL<<28), + -reale(107054489,21433519LL<<26),reale(99917523,12239271LL<<27), + -reale(61060708,48513541LL<<26),reale(16900731,943456205LL<<22), + reale(110748930411LL,0xc5dcb3125bdbLL), + // C4[14], coeff of eps^29, polynomial in n of order 0 + real(41LL<<28),real(0x3fbc634a12a6b1LL), + // C4[14], coeff of eps^28, polynomial in n of order 1 + -real(6907093LL<<31),-real(59887787LL<<28), + reale(5739014,0x909af11944e4bLL), + // C4[14], coeff of eps^27, polynomial in n of order 2 + reale(3432,499601LL<<33),-real(2083199471LL<<32),real(3406572267LL<<28), + reale(307370942,0xdb94118adae9fLL), + // C4[14], coeff of eps^26, polynomial in n of order 3 + reale(287986,4314073LL<<29),reale(5344,3636147LL<<30), + -reale(6205,2906637LL<<29),-reale(13964,12467885LL<<26), + reale(13216950542LL,0xe1def252c54b5LL), + // C4[14], coeff of eps^25, polynomial in n of order 4 + -reale(258061,515595LL<<33),-reale(74790,1657665LL<<31), + reale(745027,493173LL<<32),-reale(337382,84843LL<<31), + reale(26418,5099583LL<<27),reale(39650851628LL,0xa59cd6f84fe1fLL), + // C4[14], coeff of eps^24, polynomial in n of order 5 + -reale(100052,3082133LL<<30),-reale(6991386,428305LL<<32), + reale(2902871,3549453LL<<30),reale(514674,1320943LL<<31), + reale(32543,4070319LL<<30),-reale(220557,2292103LL<<27), + reale(118952554885LL,0xf0d684e8efa5dLL), + // C4[14], coeff of eps^23, polynomial in n of order 6 + reale(6249633,975799LL<<32),-reale(1750517,1286063LL<<31), + -reale(1090661,209219LL<<33),-reale(631632,1802089LL<<31), + reale(1285387,761149LL<<32),-reale(440347,2020899LL<<31), + reale(22303,14762615LL<<27),reale(39650851628LL,0xa59cd6f84fe1fLL), + // C4[14], coeff of eps^22, polynomial in n of order 7 + -reale(1155507,7367607LL<<29),reale(11090657,3295693LL<<30), + reale(9416360,3921899LL<<29),-reale(12562252,1614097LL<<31), + reale(1905484,7990669LL<<29),reale(1324142,2135535LL<<30), + reale(362851,6226223LL<<29),-reale(408795,45924241LL<<26), + reale(118952554885LL,0xf0d684e8efa5dLL), + // C4[14], coeff of eps^21, polynomial in n of order 8 + -reale(2556392,83451LL<<35),-reale(45924405,628445LL<<32), + reale(25722566,76303LL<<33),reale(6427311,738073LL<<32), + -reale(4460754,79355LL<<34),-reale(7474566,842481LL<<32), + reale(6714086,421381LL<<33),-reale(1643964,835835LL<<32), + reale(21175,2825543LL<<28),reale(118952554885LL,0xf0d684e8efa5dLL), + // C4[14], coeff of eps^20, polynomial in n of order 9 + reale(101794762,1213867LL<<31),reale(21384252,53331LL<<34), + -reale(38294895,814203LL<<31),-reale(14666563,397319LL<<32), + reale(37283642,1395551LL<<31),-reale(15279397,125869LL<<33), + -reale(3174330,433863LL<<31),reale(2115734,458067LL<<32), + reale(1510128,1624339LL<<31),-reale(831539,10478291LL<<28), + reale(118952554885LL,0xf0d684e8efa5dLL), + // C4[14], coeff of eps^19, polynomial in n of order 10 + reale(50295468,469581LL<<33),-reale(186185623,531407LL<<32), + reale(174713425,41641LL<<35),-reale(59412258,3489LL<<32), + -reale(26728127,294149LL<<33),reale(23787374,31373LL<<32), + reale(13262792,54953LL<<34),-reale(23669724,520005LL<<32), + reale(11190603,172969LL<<33),-reale(1670792,243479LL<<32), + -reale(115324,7271069LL<<28),reale(118952554885LL,0xf0d684e8efa5dLL), + // C4[14], coeff of eps^18, polynomial in n of order 11 + -reale(229751836,26450113LL<<27),reale(205122059,12799441LL<<28), + -reale(76881886,7573243LL<<27),-reale(89213757,3943587LL<<30), + reale(179505441,31406283LL<<27),-reale(144430080,11541225LL<<28), + reale(48475411,26026641LL<<27),reale(12591449,3614557LL<<29), + -reale(14798010,26226089LL<<27),-reale(1654995,15989667LL<<28), + reale(6017860,18394141LL<<27),-reale(2035659,55899187LL<<24), + reale(118952554885LL,0xf0d684e8efa5dLL), + // C4[14], coeff of eps^17, polynomial in n of order 12 + -reale(60003627,419631LL<<31),reale(122260158,890121LL<<29), + -reale(193015194,2586489LL<<30),reale(228332901,6912147LL<<29), + -reale(182676146,109669LL<<32),reale(57596630,2823965LL<<29), + reale(79437172,3055697LL<<30),-reale(146103578,5035353LL<<29), + reale(121669517,1691035LL<<31),-reale(57926620,1249999LL<<29), + reale(13245099,2677787LL<<30),reale(250593,3348667LL<<29), + -reale(546524,56364575LL<<25),reale(118952554885LL,0xf0d684e8efa5dLL), + // C4[14], coeff of eps^16, polynomial in n of order 13 + -reale(2910026,15317989LL<<28),reale(10671028,169493LL<<30), + -reale(30788418,3245427LL<<28),reale(70862414,261923LL<<29), + -reale(130581700,1010945LL<<28),reale(191189814,642567LL<<31), + -reale(216782974,13106831LL<<28),reale(177827671,7710997LL<<29), + -reale(82572754,6587933LL<<28),-reale(19188450,2518521LL<<30), + reale(74652545,11169877LL<<28),-reale(71762036,443641LL<<29), + reale(37978089,1743047LL<<28),-reale(9119456,66783679LL<<25), + reale(118952554885LL,0xf0d684e8efa5dLL), + // C4[14], coeff of eps^15, polynomial in n of order 14 + -reale(25313,471763LL<<30),reale(176943,4508751LL<<29), + -reale(914488,1483519LL<<31),reale(3661023,8037561LL<<29), + -reale(11683077,3602217LL<<30),reale(30253421,7276067LL<<29), + -reale(64215578,725077LL<<32),reale(112133608,2030221LL<<29), + -reale(160624032,1744767LL<<30),reale(186713478,2165751LL<<29), + -reale(172286017,2016853LL<<31),reale(121247637,6964321LL<<29), + -reale(60696359,459733LL<<30),reale(19031909,1781195LL<<29), + -reale(2775486,102023215LL<<25),reale(118952554885LL,0xf0d684e8efa5dLL), + // C4[14], coeff of eps^14, polynomial in n of order 15 + -real(614557125LL<<27),real(5831464275LL<<28), + -reale(3808,17097199LL<<27),reale(28626,5026047LL<<29), + -reale(160361,32462873LL<<27),reale(702411,15495849LL<<28), + -reale(2480054,13665347LL<<27),reale(7201343,703573LL<<30), + -reale(17420896,11395693LL<<27),reale(35365729,6899711LL<<28), + -reale(60346284,10497687LL<<27),reale(86059048,7827541LL<<29), + -reale(100689087,8447169LL<<27),reale(91987561,3237205LL<<28), + -reale(55509735,6799595LL<<27),reale(15265177,48513541LL<<24), + reale(118952554885LL,0xf0d684e8efa5dLL), + // C4[15], coeff of eps^29, polynomial in n of order 0 + -real(204761LL<<28),reale(20426,0xaa7b82b97d24fLL), + // C4[15], coeff of eps^28, polynomial in n of order 1 + -real(34699LL<<42),real(26415501LL<<29),reale(6134808,0xac3bb24726559LL), + // C4[15], coeff of eps^27, polynomial in n of order 2 + reale(16894,439LL<<40),-reale(3396,5539LL<<38), + -reale(13997,7293149LL<<28),reale(14128464373LL,0x6d08ce11dbba7LL), + // C4[15], coeff of eps^26, polynomial in n of order 3 + -reale(50643,63489LL<<36),reale(243167,8553LL<<37), + -reale(100839,3467LL<<36),reale(7018,548741LL<<30), + reale(14128464373LL,0x6d08ce11dbba7LL), + // C4[15], coeff of eps^25, polynomial in n of order 4 + -reale(6907413,21379LL<<36),reale(2301071,198931LL<<34), + reale(591806,32973LL<<35),reale(76262,38289LL<<34), + -reale(216244,23833777LL<<27),reale(127156179360LL,0xd54f3ea0b98dfLL), + // C4[15], coeff of eps^24, polynomial in n of order 5 + -reale(2869395,52521LL<<36),-reale(3255913,8819LL<<38), + -reale(2304160,33823LL<<36),reale(3661540,28261LL<<37), + -reale(1155441,18213LL<<36),reale(48366,13607837LL<<28), + reale(127156179360LL,0xd54f3ea0b98dfLL), + // C4[15], coeff of eps^23, polynomial in n of order 6 + reale(8754539,110435LL<<35),reale(11218727,249609LL<<34), + -reale(11298141,31087LL<<36),reale(996220,194783LL<<34), + reale(1275763,41633LL<<35),reale(426630,95573LL<<34), + -reale(392368,19533817LL<<27),reale(127156179360LL,0xd54f3ea0b98dfLL), + // C4[15], coeff of eps^22, polynomial in n of order 7 + -reale(46020147,1607LL<<36),reale(18483914,31121LL<<37), + reale(8546239,40923LL<<36),-reale(2972379,4277LL<<38), + -reale(7799822,49315LL<<36),reale(6131851,9051LL<<37), + -reale(1385578,60289LL<<36),reale(2743,3362879LL<<30), + reale(127156179360LL,0xd54f3ea0b98dfLL), + // C4[15], coeff of eps^21, polynomial in n of order 8 + reale(36025526,1303LL<<40),-reale(30131090,26093LL<<37), + -reale(21835156,15459LL<<38),reale(35026415,31673LL<<37), + -reale(11464406,5705LL<<39),-reale(3907909,12145LL<<37), + reale(1722090,1439LL<<38),reale(1557916,18869LL<<37), + -reale(781446,6381283LL<<28),reale(127156179360LL,0xd54f3ea0b98dfLL), + // C4[15], coeff of eps^20, polynomial in n of order 9 + -reale(190262221,2387LL<<40),reale(146996287,1227LL<<41), + -reale(33573688,3541LL<<40),-reale(32294922,2067LL<<39), + reale(18331180,2115LL<<40),reale(16022794,2331LL<<40), + -reale(22246846,3383LL<<40),reale(9695242,4143LL<<39), + -reale(1302304,2671LL<<40),-reale(123235,5577019LL<<29), + reale(127156179360LL,0xd54f3ea0b98dfLL), + // C4[15], coeff of eps^19, polynomial in n of order 10 + reale(169057636,9637LL<<37),-reale(31515275,17095LL<<36), + -reale(115123722,7359LL<<39),reale(174060780,58071LL<<36), + -reale(122862790,20125LL<<37),reale(32880337,12981LL<<36), + reale(15824026,705LL<<38),-reale(12943852,57005LL<<36), + -reale(2522257,22495LL<<37),reale(5771316,18225LL<<36), + -reale(1873338,7714415LL<<28),reale(127156179360LL,0xd54f3ea0b98dfLL), + // C4[15], coeff of eps^18, polynomial in n of order 11 + reale(137352006,26079LL<<36),-reale(197705648,26891LL<<37), + reale(213128803,51077LL<<36),-reale(150461460,3135LL<<39), + reale(25445949,34251LL<<36),reale(93962136,11667LL<<37), + -reale(140732252,24207LL<<36),reale(108614245,6273LL<<38), + -reale(48923563,62665LL<<36),reale(10316934,1969LL<<37), + reale(535285,33757LL<<36),-reale(501186,3348667LL<<30), + reale(127156179360LL,0xd54f3ea0b98dfLL), + // C4[15], coeff of eps^17, polynomial in n of order 12 + reale(15155809,205049LL<<34),-reale(39104030,957115LL<<32), + reale(81967212,139599LL<<33),-reale(139468172,993913LL<<32), + reale(190415844,61587LL<<35),-reale(202311488,967447LL<<32), + reale(154439958,403401LL<<33),-reale(61783996,889045LL<<32), + -reale(28504911,66989LL<<34),reale(73132006,1030157LL<<32), + -reale(66442314,293949LL<<33),reale(34502754,346959LL<<32), + -reale(8240730,128798053LL<<25),reale(127156179360LL,0xd54f3ea0b98dfLL), + // C4[15], coeff of eps^16, polynomial in n of order 13 + reale(114172,142577LL<<34),-reale(499141,33119LL<<36), + reale(1750098,174183LL<<34),-reale(5016097,114721LL<<35), + reale(11893006,66221LL<<34),-reale(23470909,19093LL<<37), + reale(38591591,188131LL<<34),-reale(52613301,65223LL<<35), + reale(58753809,139305LL<<34),-reale(52512562,23925LL<<36), + reale(36059714,158111LL<<34),-reale(17726185,93229LL<<35), + reale(5486676,138853LL<<34),-reale(792996,14574745LL<<26), + reale(42385393120LL,0x471a6a35932f5LL), + // C4[15], coeff of eps^15, polynomial in n of order 14 + real(592706205LL<<33),-reale(9147,839013LL<<32), + reale(55355,240865LL<<34),-reale(262940,644275LL<<32), + reale(1011310,29095LL<<33),-reale(3215965,1023905LL<<32), + reale(8575909,35467LL<<35),-reale(19352216,329839LL<<32), + reale(37124659,455473LL<<33),-reale(60529336,778589LL<<32), + reale(83288367,93771LL<<34),-reale(94856196,165035LL<<32), + reale(85043486,110139LL<<33),-reale(50751757,943257LL<<32), + reale(13877433,107462891LL<<25),reale(127156179360LL,0xd54f3ea0b98dfLL), + // C4[16], coeff of eps^29, polynomial in n of order 0 + real(553LL<<31),real(0x292ecb9a960d27d1LL), + // C4[16], coeff of eps^28, polynomial in n of order 1 + -real(61453LL<<36),-real(4754645LL<<34), + reale(19591808,0x57955a5f17535LL), + // C4[16], coeff of eps^27, polynomial in n of order 2 + reale(33770,14237LL<<36),-reale(12917,115767LL<<35), + real(1665987897LL<<31),reale(2148568314LL,0xda506166fe05fLL), + // C4[16], coeff of eps^26, polynomial in n of order 3 + reale(1765351,9719LL<<36),reale(634098,16193LL<<37), + reale(114937,5021LL<<36),-reale(211035,902511LL<<32), + reale(135359803835LL,0xb9c7f85883761LL), + // C4[16], coeff of eps^25, polynomial in n of order 4 + -reale(3041817,11535LL<<37),-reale(2643315,63657LL<<35), + reale(3458443,225LL<<36),-reale(1011407,29251LL<<35), + reale(33755,354965LL<<31),reale(135359803835LL,0xb9c7f85883761LL), + // C4[16], coeff of eps^24, polynomial in n of order 5 + reale(12443946,111847LL<<35),-reale(9978547,23661LL<<37), + reale(264818,24689LL<<35),reale(1196082,9587LL<<36), + reale(477961,17339LL<<35),-reale(375862,243659LL<<32), + reale(135359803835LL,0xb9c7f85883761LL), + // C4[16], coeff of eps^23, polynomial in n of order 6 + reale(11971225,59849LL<<36),reale(9677599,56595LL<<35), + -reale(1515717,2317LL<<37),-reale(7956047,112667LL<<35), + reale(5585704,62147LL<<36),-reale(1169086,64041LL<<35), + -reale(10840,1435009LL<<31),reale(135359803835LL,0xb9c7f85883761LL), + // C4[16], coeff of eps^22, polynomial in n of order 7 + -reale(20905609,47207LL<<36),-reale(27003899,18815LL<<37), + reale(32128586,62715LL<<36),-reale(8207156,6437LL<<38), + -reale(4335741,20931LL<<36),reale(1351161,14763LL<<37), + reale(1583200,44703LL<<36),-reale(734819,355141LL<<32), + reale(135359803835LL,0xb9c7f85883761LL), + // C4[16], coeff of eps^21, polynomial in n of order 8 + reale(119271221,13241LL<<38),-reale(13185134,117885LL<<35), + -reale(34424757,12741LL<<36),reale(12971898,62105LL<<35), + reale(17958221,23929LL<<37),-reale(20751983,45233LL<<35), + reale(8405753,5609LL<<36),-reale(1009805,84123LL<<35), + -reale(126669,998091LL<<31),reale(135359803835LL,0xb9c7f85883761LL), + // C4[16], coeff of eps^20, polynomial in n of order 9 + reale(7281953,46177LL<<36),-reale(132136826,3687LL<<39), + reale(164419146,28463LL<<36),-reale(102943145,13301LL<<37), + reale(20499773,15997LL<<36),reale(17609391,14489LL<<38), + -reale(11127728,9397LL<<36),-reale(3194109,31911LL<<37), + reale(5518515,63129LL<<36),-reale(1729623,95963LL<<34), + reale(135359803835LL,0xb9c7f85883761LL), + // C4[16], coeff of eps^19, polynomial in n of order 10 + -reale(197461925,11965LL<<36),reale(194820269,1667LL<<35), + -reale(119937281,937LL<<38),-reale(1213288,24915LL<<35), + reale(103481944,52469LL<<36),-reale(133891976,7945LL<<35), + reale(96822293,18071LL<<37),-reale(41434588,121951LL<<35), + reale(8025452,27431LL<<36),reale(724406,126187LL<<35), + -reale(459367,1295029LL<<31),reale(135359803835LL,0xb9c7f85883761LL), + // C4[16], coeff of eps^18, polynomial in n of order 11 + -reale(47525285,62545LL<<36),reale(91887649,22453LL<<37), + -reale(145781941,19979LL<<36),reale(186991182,8001LL<<39), + -reale(187151585,35749LL<<36),reale(133148350,20179LL<<37), + -reale(44425461,13151LL<<36),-reale(35371425,9983LL<<38), + reale(71056997,38023LL<<36),-reale(61631567,21647LL<<37), + reale(31499493,50637LL<<36),-reale(7491423,758351LL<<32), + reale(135359803835LL,0xb9c7f85883761LL), + // C4[16], coeff of eps^17, polynomial in n of order 12 + -reale(2259631,24697LL<<37),reale(7098981,78723LL<<35), + -reale(18582556,8879LL<<36),reale(40852668,12369LL<<35), + -reale(75692831,12691LL<<38),reale(118080654,84927LL<<35), + -reale(154130872,52073LL<<36),reale(166118829,74381LL<<35), + -reale(144327913,2259LL<<37),reale(96964720,98683LL<<35), + -reale(46899246,18595LL<<36),reale(14349769,50505LL<<35), + -reale(2057503,1465135LL<<31),reale(135359803835LL,0xb9c7f85883761LL), + // C4[16], coeff of eps^16, polynomial in n of order 13 + -reale(18695,264305LL<<33),reale(95700,8265LL<<35), + -reale(398136,419847LL<<33),reale(1375381,177071LL<<34), + -reale(4004787,429789LL<<33),reale(9930000,13635LL<<36), + -reale(21101250,231795LL<<33),reale(38532718,52073LL<<34), + -reale(60367925,93257LL<<33),reale(80490566,118467LL<<35), + -reale(89511061,246751LL<<33),reale(78923731,162339LL<<34), + -reale(46636750,263349LL<<33),reale(12687939,1991833LL<<30), + reale(135359803835LL,0xb9c7f85883761LL), + // C4[17], coeff of eps^29, polynomial in n of order 0 + -real(280331LL<<31),reale(154847,0x4e6e7be138cdbLL), + // C4[17], coeff of eps^28, polynomial in n of order 1 + -real(82431LL<<38),real(142069LL<<33),reale(989485,0x4511e2f2b39a3LL), + // C4[17], coeff of eps^27, polynomial in n of order 2 + reale(30957,2723LL<<36),reale(7080,38071LL<<35), + -reale(9773,1986585LL<<31),reale(6836353729LL,0x13b9f01928417LL), + // C4[17], coeff of eps^26, polynomial in n of order 3 + -reale(138771,28785LL<<37),reale(154910,14439LL<<38), + -reale(42193,29611LL<<37),real(1108797915LL<<32), + reale(6836353729LL,0x13b9f01928417LL), + // C4[17], coeff of eps^25, polynomial in n of order 4 + -reale(1238256,21701LL<<37),-reale(44811,81027LL<<35), + reale(156785,14859LL<<36),reale(74079,77407LL<<35), + -reale(51372,1082481LL<<31),reale(20509061187LL,0x3b2dd04b78c45LL), + // C4[17], coeff of eps^24, polynomial in n of order 5 + reale(10057115,7495LL<<39),-reale(158283,1579LL<<41), + -reale(7978477,2703LL<<39),reale(5079175,3021LL<<40), + -reale(987192,2101LL<<39),-reale(20806,242401LL<<34), + reale(143563428310LL,0x9e40b2104d5e3LL), + // C4[17], coeff of eps^23, polynomial in n of order 6 + -reale(30405369,16203LL<<36),reale(28904813,10839LL<<35), + -reale(5472666,28841LL<<37),-reale(4538327,21695LL<<35), + reale(1010309,2151LL<<36),reale(1591197,61387LL<<35), + -reale(691600,842821LL<<31),reale(143563428310LL,0x9e40b2104d5e3LL), + // C4[17], coeff of eps^22, polynomial in n of order 7 + reale(2360974,20517LL<<37),-reale(34168343,7885LL<<38), + reale(7988557,399LL<<37),reale(19220645,2761LL<<39), + -reale(19251394,21847LL<<37),reale(7294617,7633LL<<38), + -reale(776533,25709LL<<37),-reale(127091,628387LL<<32), + reale(143563428310LL,0x9e40b2104d5e3LL), + // C4[17], coeff of eps^21, polynomial in n of order 8 + -reale(141970389,5875LL<<38),reale(152285106,31LL<<35), + -reale(85013706,54665LL<<36),reale(10784519,74157LL<<35), + reale(18376223,22989LL<<37),-reale(9415616,123045LL<<35), + -reale(3707065,39939LL<<36),reale(5266887,19945LL<<35), + -reale(1601936,974407LL<<31),reale(143563428310LL,0x9e40b2104d5e3LL), + // C4[17], coeff of eps^20, polynomial in n of order 9 + reale(174732199,7199LL<<38),-reale(91781661,1783LL<<41), + -reale(22947906,1007LL<<38),reale(109147441,451LL<<39), + -reale(126268040,11085LL<<38),reale(86262862,2409LL<<40), + -reale(35185382,1435LL<<38),reale(6220582,7041LL<<39), + reale(846498,391LL<<38),-reale(421214,84365LL<<33), + reale(143563428310LL,0x9e40b2104d5e3LL), + // C4[17], coeff of eps^19, polynomial in n of order 10 + reale(100447726,5039LL<<36),-reale(149758021,121873LL<<35), + reale(181554380,1939LL<<38),-reale(171878757,123903LL<<35), + reale(113962110,29289LL<<36),-reale(29967290,80205LL<<35), + -reale(40353928,13613LL<<37),reale(68655180,86853LL<<35), + -reale(57285125,57949LL<<36),reale(28886745,8439LL<<35), + -reale(6846764,2025561LL<<31),reale(143563428310LL,0x9e40b2104d5e3LL), + // C4[17], coeff of eps^18, polynomial in n of order 11 + reale(9163438,32371LL<<37),-reale(22188557,9937LL<<38), + reale(45681407,15569LL<<37),-reale(80085759,21LL<<40), + reale(119267529,32127LL<<37),-reale(149784698,12951LL<<38), + reale(156408668,30941LL<<37),-reale(132494258,5045LL<<39), + reale(87286969,10059LL<<37),-reale(41608633,10397LL<<38), + reale(12599797,16681LL<<37),-reale(1793721,181577LL<<32), + reale(143563428310LL,0x9e40b2104d5e3LL), + // C4[17], coeff of eps^17, polynomial in n of order 12 + reale(152058,3531LL<<37),-reale(566838,109449LL<<35), + reale(1788421,65069LL<<36),-reale(4828739,49907LL<<35), + reale(11241509,10969LL<<38),-reale(22666304,107837LL<<35), + reale(39633653,283LL<<36),-reale(59939783,113319LL<<35), + reale(77715030,3737LL<<37),-reale(84609105,47985LL<<35), + reale(73498818,52617LL<<36),-reale(43049308,20443LL<<35), + reale(11659187,1311925LL<<31),reale(143563428310LL,0x9e40b2104d5e3LL), + // C4[18], coeff of eps^29, polynomial in n of order 0 + real(35LL<<34),real(0x29845c2bcb5c10d7LL), + // C4[18], coeff of eps^28, polynomial in n of order 1 + reale(3628,18373LL<<37),-reale(4063,232509LL<<34), + reale(3097286791LL,0x8a812bfedbe75LL), + // C4[18], coeff of eps^27, polynomial in n of order 2 + reale(435730,613LL<<39),-reale(110987,3811LL<<38),real(489021323LL<<34), + reale(21681007540LL,0xc98833f803533LL), + // C4[18], coeff of eps^26, polynomial in n of order 3 + -reale(762945,31179LL<<36),reale(988791,87LL<<37), + reale(550009,38375LL<<36),-reale(343815,323189LL<<33), + reale(151767052785LL,0x82b96bc817465LL), + // C4[18], coeff of eps^25, polynomial in n of order 4 + reale(1063744,27LL<<41),-reale(7897635,7767LL<<39), + reale(4613149,699LL<<40),-reale(833936,93LL<<39), + -reale(28054,94387LL<<35),reale(151767052785LL,0x82b96bc817465LL), + // C4[18], coeff of eps^24, polynomial in n of order 5 + reale(25578507,4379LL<<38),-reale(3209600,1553LL<<40), + -reale(4577572,5923LL<<38),reale(702466,1583LL<<39), + reale(1586031,287LL<<38),-reale(651636,122639LL<<35), + reale(151767052785LL,0x82b96bc817465LL), + // C4[18], coeff of eps^23, polynomial in n of order 6 + -reale(32324739,1815LL<<40),reale(3520775,1207LL<<39), + reale(19946468,259LL<<41),-reale(17787966,4575LL<<39), + reale(6336978,3235LL<<40),-reale(589727,5685LL<<39), + -reale(125505,20667LL<<35),reale(151767052785LL,0x82b96bc817465LL), + // C4[18], coeff of eps^22, polynomial in n of order 7 + reale(138884729,22203LL<<36),-reale(69168625,14473LL<<37), + reale(3249237,4577LL<<36),reale(18436830,10301LL<<38), + -reale(7840055,31609LL<<36),-reale(4091705,30595LL<<37), + reale(5021146,27565LL<<36),-reale(1488082,115687LL<<33), + reale(151767052785LL,0x82b96bc817465LL), + // C4[18], coeff of eps^21, polynomial in n of order 8 + -reale(66334778,1299LL<<41),-reale(40377625,16285LL<<38), + reale(111882749,4839LL<<39),-reale(118325119,4711LL<<38), + reale(76858390,3693LL<<40),-reale(29952902,3569LL<<38), + reale(4790818,4941LL<<39),reale(921311,4421LL<<38), + -reale(386621,181821LL<<34),reale(151767052785LL,0x82b96bc817465LL), + // C4[18], coeff of eps^20, polynomial in n of order 9 + -reale(151679112,16629LL<<37),reale(174648786,1667LL<<40), + -reale(156892091,15835LL<<37),reale(96799837,4169LL<<38), + -reale(17949188,6721LL<<37),-reale(43885384,7293LL<<39), + reale(66080580,25305LL<<37),-reale(53357084,1853LL<<38), + reale(26599572,17011LL<<37),-reale(6287689,169979LL<<34), + reale(151767052785LL,0x82b96bc817465LL), + // C4[18], coeff of eps^19, polynomial in n of order 10 + -reale(8594193,5169LL<<39),reale(16702080,5475LL<<38), + -reale(27882498,1245LL<<41),reale(39843622,14413LL<<38), + -reale(48340851,951LL<<39),reale(49066184,11639LL<<38), + -reale(40627946,3165LL<<40),reale(26296855,15713LL<<38), + -reale(12371894,1597LL<<39),reale(3711568,4235LL<<38), + -reale(524991,147555LL<<34),reale(50589017595LL,0x2b9323ed5d177LL), + // C4[18], coeff of eps^18, polynomial in n of order 11 + -reale(768539,29011LL<<36),reale(2243105,18035LL<<37), + -reale(5671852,39713LL<<36),reale(12494515,7255LL<<39), + -reale(24051943,5231LL<<36),reale(40468348,22085LL<<37), + -reale(59307062,46653LL<<36),reale(74994737,5975LL<<38), + -reale(80108014,59787LL<<36),reale(68664012,25623LL<<37), + -reale(39899358,51033LL<<36),reale(10762327,20443LL<<33), + reale(151767052785LL,0x82b96bc817465LL), + // C4[19], coeff of eps^29, polynomial in n of order 0 + -real(69697LL<<34),reale(220556,0x6c98ea537e51fLL), + // C4[19], coeff of eps^28, polynomial in n of order 1 + -real(1238839LL<<41),real(675087LL<<35), + reale(141943813,0x222cc7846d81LL), + // C4[19], coeff of eps^27, polynomial in n of order 2 + reale(876102,3999LL<<40),reale(573743,1451LL<<39), + -reale(328615,14973LL<<34),reale(159970677260LL,0x6732257fe12e7LL), + // C4[19], coeff of eps^26, polynomial in n of order 3 + -reale(7739083,17LL<<46),reale(4186838,53LL<<45),-reale(704448,1LL<<46), + -reale(33249,11241LL<<37),reale(159970677260LL,0x6732257fe12e7LL), + // C4[19], coeff of eps^25, polynomial in n of order 4 + -reale(1360864,133LL<<42),-reale(4500609,2667LL<<40), + reale(427896,299LL<<41),reale(1570943,1191LL<<40), + -reale(614728,45789LL<<35),reale(159970677260LL,0x6732257fe12e7LL), + // C4[19], coeff of eps^24, polynomial in n of order 5 + -reale(379105,631LL<<42),reale(20252634,139LL<<44), + -reale(16388211,705LL<<42),reale(5510947,339LL<<43), + -reale(439601,699LL<<42),-reale(122601,56745LL<<36), + reale(159970677260LL,0x6732257fe12e7LL), + // C4[19], coeff of eps^23, polynomial in n of order 6 + -reale(55355388,567LL<<41),-reale(2520461,2117LL<<40), + reale(18017708,147LL<<42),-reale(6413373,771LL<<40), + -reale(4373212,61LL<<41),reale(4784182,2079LL<<40), + -reale(1386197,54485LL<<35),reale(159970677260LL,0x6732257fe12e7LL), + // C4[19], coeff of eps^22, polynomial in n of order 7 + -reale(54112477,29LL<<46),reale(112419812,35LL<<45), + -reale(110372726,9LL<<46),reale(68510282,53LL<<46), + -reale(25556330,19LL<<46),reale(3652507,1LL<<45),reale(962676,17LL<<46), + -reale(355362,30093LL<<37),reale(159970677260LL,0x6732257fe12e7LL), + // C4[19], coeff of eps^21, polynomial in n of order 8 + reale(166723371,209LL<<42),-reale(142457721,7469LL<<39), + reale(81530379,2787LL<<40),-reale(7977897,3383LL<<39), + -reale(46298043,1775LL<<41),reale(63437092,799LL<<39), + -reale(49803454,3807LL<<40),reale(24585849,2581LL<<39), + -reale(5799325,105875LL<<34),reale(159970677260LL,0x6732257fe12e7LL), + // C4[19], coeff of eps^20, polynomial in n of order 9 + reale(54095236,1729LL<<41),-reale(86448328,33LL<<44), + reale(119042325,527LL<<41),-reale(140012701,875LL<<42), + reale(138519104,1133LL<<41),-reale(112357061,257LL<<43), + reale(71568963,1275LL<<41),-reale(33272498,441LL<<42), + reale(9897515,729LL<<41),-reale(1391838,12705LL<<35), + reale(159970677260LL,0x6732257fe12e7LL), + // C4[19], coeff of eps^19, polynomial in n of order 10 + reale(2731650,3225LL<<40),-reale(6520331,5423LL<<39), + reale(13678206,885LL<<42),-reale(25266687,5569LL<<39), + reale(41073925,3215LL<<40),-reale(58519302,7091LL<<39), + reale(72351138,181LL<<41),-reale(75968694,8133LL<<39), + reale(64333849,3333LL<<40),-reale(37115682,4791LL<<39), + reale(9974839,182105LL<<34),reale(159970677260LL,0x6732257fe12e7LL), + // C4[20], coeff of eps^29, polynomial in n of order 0 + real(1LL<<39),reale(386445,0x44b61aebc827LL), + // C4[20], coeff of eps^28, polynomial in n of order 1 + reale(3670,3431LL<<40),-real(63923791LL<<37), + reale(1044560880,0x57ec63f8653c9LL), + // C4[20], coeff of eps^27, polynomial in n of order 2 + reale(165149,453LL<<43),-reale(25858,471LL<<42),-real(26276299LL<<38), + reale(7311926162LL,0x6776bbcac4a7fLL), + // C4[20], coeff of eps^26, polynomial in n of order 3 + -reale(4343033,595LL<<42),reale(185313,303LL<<43), + reale(1548473,271LL<<42),-reale(580654,777LL<<40), + reale(168174301735LL,0x4baadf37ab169LL), + // C4[20], coeff of eps^25, polynomial in n of order 4 + reale(20236427,149LL<<44),-reale(15067334,133LL<<42), + reale(4797544,165LL<<43),-reale(318599,375LL<<42), + -reale(118861,3875LL<<38),reale(168174301735LL,0x4baadf37ab169LL), + // C4[20], coeff of eps^24, polynomial in n of order 5 + -reale(6870833,1979LL<<41),reale(17282399,281LL<<43), + -reale(5135975,189LL<<41),-reale(4572111,263LL<<42), + reale(4557653,1537LL<<41),-reale(1294702,4061LL<<38), + reale(168174301735LL,0x4baadf37ab169LL), + // C4[20], coeff of eps^23, polynomial in n of order 6 + reale(111332564,131LL<<43),-reale(102611836,439LL<<42), + reale(61113705,49LL<<44),-reale(21849131,865LL<<42), + reale(2742318,257LL<<43),reale(980372,533LL<<42), + -reale(327159,8391LL<<38),reale(168174301735LL,0x4baadf37ab169LL), + // C4[20], coeff of eps^22, polynomial in n of order 7 + -reale(128743521,979LL<<42),reale(67998970,481LL<<43), + reale(279122,855LL<<42),-reale(47847734,245LL<<44), + reale(60794248,257LL<<42),-reale(46583621,181LL<<43), + reale(22803394,43LL<<42),-reale(5369928,2229LL<<40), + reale(168174301735LL,0x4baadf37ab169LL), + // C4[20], coeff of eps^21, polynomial in n of order 8 + -reale(88564699,121LL<<45),reale(117949702,533LL<<42), + -reale(134881895,27LL<<43),reale(130376590,239LL<<42), + -reale(103788735,57LL<<44),reale(65154071,233LL<<42), + -reale(29963298,393LL<<43),reale(8844588,195LL<<42), + -reale(1237189,6873LL<<38),reale(168174301735LL,0x4baadf37ab169LL), + // C4[20], coeff of eps^20, polynomial in n of order 9 + -reale(7362630,999LL<<40),reale(14785858,137LL<<43), + -reale(26321377,9LL<<40),reale(41483460,1083LL<<41), + -reale(57615917,1643LL<<40),reale(69797568,521LL<<42), + -reale(72155594,1933LL<<40),reale(60438019,617LL<<41), + -reale(34641303,3055LL<<40),reale(9278920,21175LL<<37), + reale(168174301735LL,0x4baadf37ab169LL), + // C4[21], coeff of eps^29, polynomial in n of order 0 + -real(2017699LL<<39),reale(144690669,0x92d5d14b2b5b9LL), + // C4[21], coeff of eps^28, polynomial in n of order 1 + -reale(21806,31LL<<47),-real(1751493LL<<42), + reale(7668605487LL,0x6644548ff9f4dLL), + // C4[21], coeff of eps^27, polynomial in n of order 2 + -real(610053LL<<43),reale(66113,223LL<<42),-reale(23877,14131LL<<38), + reale(7668605487LL,0x6644548ff9f4dLL), + // C4[21], coeff of eps^26, polynomial in n of order 3 + -reale(601427,223LL<<44),reale(181759,65LL<<45),-reale(9602,5LL<<44), + -reale(4983,2721LL<<39),reale(7668605487LL,0x6644548ff9f4dLL), + // C4[21], coeff of eps^25, polynomial in n of order 4 + reale(16348405,227LL<<44),-reale(4001511,795LL<<42), + -reale(4705038,397LL<<43),reale(4342393,855LL<<42), + -reale(1212256,1051LL<<38),reale(176377926210LL,0x302398ef74febLL), + // C4[21], coeff of eps^24, polynomial in n of order 5 + -reale(95167920,19LL<<45),reale(54565817,7LL<<47), + -reale(18712410,5LL<<45),reale(2011897,15LL<<46),reale(981374,25LL<<45), + -reale(301721,597LL<<40),reale(176377926210LL,0x302398ef74febLL), + // C4[21], coeff of eps^23, polynomial in n of order 6 + reale(56043535,133LL<<43),reale(7101303,759LL<<42), + -reale(48732132,249LL<<44),reale(58197907,161LL<<42), + -reale(43660867,425LL<<43),reale(21217809,619LL<<42), + -reale(4990122,11039LL<<38),reale(176377926210LL,0x302398ef74febLL), + // C4[21], coeff of eps^22, polynomial in n of order 7 + reale(38792824,189LL<<44),-reale(43241527,125LL<<45), + reale(40920531,151LL<<44),-reale(32022608,39LL<<46), + reale(19836099,97LL<<44),-reale(9032168,63LL<<45), + reale(2647359,187LL<<44),-reale(368524,4161LL<<39), + reale(58792642070LL,0x100bdda526ff9LL), + // C4[21], coeff of eps^21, polynomial in n of order 8 + reale(15813930,121LL<<45),-reale(27228018,205LL<<42), + reale(41726053,443LL<<43),-reale(56628215,983LL<<42), + reale(67341662,57LL<<44),-reale(68636694,193LL<<42), + reale(56918234,105LL<<43),-reale(32430156,715LL<<42), + reale(8660325,15343LL<<38),reale(176377926210LL,0x302398ef74febLL), + // C4[22], coeff of eps^29, polynomial in n of order 0 + -real(229LL<<43),reale(2018939,0x935060fc493cdLL), + // C4[22], coeff of eps^28, polynomial in n of order 1 + reale(64733,61LL<<46),-reale(22613,493LL<<43), + reale(8025284812LL,0x6511ed552f41bLL), + // C4[22], coeff of eps^27, polynomial in n of order 2 + reale(158513,3LL<<48),-reale(6162,29LL<<47),-reale(4786,487LL<<43), + reale(8025284812LL,0x6511ed552f41bLL), + // C4[22], coeff of eps^26, polynomial in n of order 3 + -reale(130438,301LL<<43),-reale(208062,47LL<<44),reale(179942,497LL<<43), + -reale(49466,167LL<<40),reale(8025284812LL,0x6511ed552f41bLL), + // C4[22], coeff of eps^25, polynomial in n of order 4 + reale(2120438,3LL<<47),-reale(697803,39LL<<45),reale(61914,3LL<<46), + reale(42203,115LL<<45),-reale(12120,543LL<<41), + reale(8025284812LL,0x6511ed552f41bLL), + // C4[22], coeff of eps^24, polynomial in n of order 5 + reale(12722577,33LL<<44),-reale(49104495,51LL<<46), + reale(55677556,71LL<<44),-reale(41002422,115LL<<45), + reale(19800840,109LL<<44),-reale(4652345,837LL<<41), + reale(184581550685LL,0x149c52a73ee6dLL), + // C4[22], coeff of eps^23, polynomial in n of order 6 + -reale(124610244,57LL<<46),reale(115654934,113LL<<45), + -reale(89096506,19LL<<47),reale(54518354,119LL<<45), + -reale(24598996,19LL<<46),reale(7163443,125LL<<45), + -reale(992759,1841LL<<41),reale(184581550685LL,0x149c52a73ee6dLL), + // C4[22], coeff of eps^22, polynomial in n of order 7 + -reale(27999005,155LL<<43),reale(41827085,121LL<<44), + -reale(55581037,1LL<<43),reale(64987058,83LL<<45), + -reale(65383321,103LL<<43),reale(53725829,211LL<<44), + -reale(30444636,461LL<<43),reale(8107539,715LL<<40), + reale(184581550685LL,0x149c52a73ee6dLL), + // C4[23], coeff of eps^29, polynomial in n of order 0 + -reale(4289,21LL<<43),reale(1676392827,0x7a5fe79ee0e95LL), + // C4[23], coeff of eps^28, polynomial in n of order 1 + -real(1351LL<<51),-real(234789LL<<44), + reale(1676392827,0x7a5fe79ee0e95LL), + // C4[23], coeff of eps^27, polynomial in n of order 2 + -reale(209744,1LL<<50),reale(171585,3LL<<49),-reale(46526,469LL<<43), + reale(8381964137LL,0x63df861a648e9LL), + // C4[23], coeff of eps^26, polynomial in n of order 3 + -reale(599194,1LL<<51),reale(41297,0),reale(41388,1LL<<51), + -reale(11218,97LL<<45),reale(8381964137LL,0x63df861a648e9LL), + // C4[23], coeff of eps^25, polynomial in n of order 4 + -reale(2134087,7LL<<49),reale(2315275,31LL<<47),-reale(1677358,15LL<<48), + reale(805613,21LL<<47),-reale(189149,1213LL<<41), + reale(8381964137LL,0x63df861a648e9LL), + // C4[23], coeff of eps^24, polynomial in n of order 5 + reale(4740508,1LL<<49),-reale(3599518,1LL<<51),reale(2177844,7LL<<49), + -reale(974429,1LL<<50),reale(282071,5LL<<49),-reale(38931,779LL<<42), + reale(8381964137LL,0x63df861a648e9LL), + // C4[23], coeff of eps^23, polynomial in n of order 6 + reale(1817763,3LL<<48),-reale(2369306,23LL<<47),reale(2727592,1LL<<49), + -reale(2711734,1LL<<47),reale(2209561,1LL<<48),-reale(1245816,11LL<<47), + reale(330919,1979LL<<41),reale(8381964137LL,0x63df861a648e9LL), + // C4[24], coeff of eps^29, polynomial in n of order 0 + -real(1439LL<<46),reale(44813556,0x37a4fd885dffdLL), + // C4[24], coeff of eps^28, polynomial in n of order 1 + reale(32742,3LL<<50),-reale(8770,21LL<<47), + reale(1747728692,0x7a229fc651f8bLL), + // C4[24], coeff of eps^27, polynomial in n of order 2 + reale(4928,1LL<<51),reale(8067,1LL<<50),-reale(2080,43LL<<46), + reale(1747728692,0x7a229fc651f8bLL), + // C4[24], coeff of eps^26, polynomial in n of order 3 + reale(2214330,0),-reale(1581120,0),reale(755790,0), + -reale(177363,7LL<<47),reale(8738643462LL,0x62ad1edf99db7LL), + // C4[24], coeff of eps^25, polynomial in n of order 4 + -reale(1116955,0),reale(668788,3LL<<50),-reale(296917,1LL<<51), + reale(85476,1LL<<50),-reale(11752,63LL<<46), + reale(2912881154LL,0x20e45f9fddf3dLL), + // C4[24], coeff of eps^24, polynomial in n of order 5 + -reale(2320992,3LL<<48),reale(2634056,1LL<<50),-reale(2590155,5LL<<48), + reale(2094168,1LL<<49),-reale(1175298,7LL<<48),reale(311454,11LL<<45), + reale(8738643462LL,0x62ad1edf99db7LL), + // C4[25], coeff of eps^29, polynomial in n of order 0 + -real(3707LL<<46),reale(12720731,0x2bd144a4925efLL), + // C4[25], coeff of eps^28, polynomial in n of order 1 + real(301LL<<53),-real(2379LL<<48),reale(139928042,0xe1fdf3124a145LL), + // C4[25], coeff of eps^27, polynomial in n of order 2 + -reale(298603,1LL<<51),reale(142145,1LL<<50),-reale(33346,63LL<<46), + reale(1819064557,0x79e557edc3081LL), + // C4[25], coeff of eps^26, polynomial in n of order 3 + reale(370617,0),-reale(163358,0),reale(46787,0),-reale(6410,23LL<<47), + reale(1819064557,0x79e557edc3081LL), + // C4[25], coeff of eps^25, polynomial in n of order 4 + reale(508963,0),-reale(495426,3LL<<50),reale(397689,1LL<<51), + -reale(222238,1LL<<50),reale(58764,59LL<<46), + reale(1819064557,0x79e557edc3081LL), + // C4[26], coeff of eps^29, polynomial in n of order 0 + -real(1LL<<49),reale(131359,0xe834f81ee20c1LL), + // C4[26], coeff of eps^28, polynomial in n of order 1 + reale(10305,0),-reale(2417,1LL<<49),reale(145415417,0x1d0ced8b7a293LL), + // C4[26], coeff of eps^27, polynomial in n of order 2 + -reale(11556,0),reale(3294,0),-real(3599LL<<49), + reale(145415417,0x1d0ced8b7a293LL), + // C4[26], coeff of eps^26, polynomial in n of order 3 + -reale(36490,1LL<<51),reale(29097,0),-reale(16195,1LL<<51), + reale(4273,13LL<<48),reale(145415417,0x1d0ced8b7a293LL), + // C4[27], coeff of eps^29, polynomial in n of order 0 + -real(2029LL<<49),reale(16766976,0xd0e6a80084b19LL), + // C4[27], coeff of eps^28, polynomial in n of order 1 + real(7LL<<56),-real(61LL<<50),reale(5588992,0x45a238002c3b3LL), + // C4[27], coeff of eps^27, polynomial in n of order 2 + reale(3080,0),-real(427LL<<54),real(3599LL<<49), + reale(16766976,0xd0e6a80084b19LL), + // C4[28], coeff of eps^29, polynomial in n of order 0 + -real(1LL<<53),reale(827461,0x318a62b8e0a5bLL), + // C4[28], coeff of eps^28, polynomial in n of order 1 + -real(29LL<<55),real(61LL<<52),reale(2482383,0x949f282aa1f11LL), + // C4[29], coeff of eps^29, polynomial in n of order 0 + real(1LL<<53),reale(88602,0xec373d36a45dfLL), + }; // count = 5425 +#else +#error "Bad value for GEOGRAPHICLIB_GEODESICEXACT_ORDER" +#endif + static_assert(sizeof(coeff) / sizeof(real) == + (nC4_ * (nC4_ + 1) * (nC4_ + 5)) / 6, + "Coefficient array size mismatch in C4coeff"); + int o = 0, k = 0; + for (int l = 0; l < nC4_; ++l) { // l is index of C4[l] + for (int j = nC4_ - 1; j >= l; --j) { // coeff of eps^j + int m = nC4_ - j - 1; // order of polynomial in n + _C4x[k++] = Math::polyval(m, coeff + o, _n) / coeff[o + m + 1]; + o += m + 2; + } + } + // Post condition: o == sizeof(coeff) / sizeof(real) && k == nC4x_ + if (!(o == sizeof(coeff) / sizeof(real) && k == nC4x_)) + throw GeographicErr("C4 misalignment"); + } + +} // namespace GeographicLib diff --git a/common/local_libs/GeographicLib/src/GeodesicLine.cpp b/common/local_libs/GeographicLib/src/GeodesicLine.cpp new file mode 100644 index 0000000000..969b13e0d5 --- /dev/null +++ b/common/local_libs/GeographicLib/src/GeodesicLine.cpp @@ -0,0 +1,321 @@ +/** + * \file GeodesicLine.cpp + * \brief Implementation for GeographicLib::GeodesicLine class + * + * Copyright (c) Charles Karney (2009-2020) and licensed + * under the MIT/X11 License. For more information, see + * https://geographiclib.sourceforge.io/ + * + * This is a reformulation of the geodesic problem. The notation is as + * follows: + * - at a general point (no suffix or 1 or 2 as suffix) + * - phi = latitude + * - beta = latitude on auxiliary sphere + * - omega = longitude on auxiliary sphere + * - lambda = longitude + * - alpha = azimuth of great circle + * - sigma = arc length along great circle + * - s = distance + * - tau = scaled distance (= sigma at multiples of pi/2) + * - at northwards equator crossing + * - beta = phi = 0 + * - omega = lambda = 0 + * - alpha = alpha0 + * - sigma = s = 0 + * - a 12 suffix means a difference, e.g., s12 = s2 - s1. + * - s and c prefixes mean sin and cos + **********************************************************************/ + +#include + +namespace GeographicLib { + + using namespace std; + + void GeodesicLine::LineInit(const Geodesic& g, + real lat1, real lon1, + real azi1, real salp1, real calp1, + unsigned caps) { + tiny_ = g.tiny_; + _lat1 = Math::LatFix(lat1); + _lon1 = lon1; + _azi1 = azi1; + _salp1 = salp1; + _calp1 = calp1; + _a = g._a; + _f = g._f; + _b = g._b; + _c2 = g._c2; + _f1 = g._f1; + // Always allow latitude and azimuth and unrolling of longitude + _caps = caps | LATITUDE | AZIMUTH | LONG_UNROLL; + + real cbet1, sbet1; + Math::sincosd(Math::AngRound(_lat1), sbet1, cbet1); sbet1 *= _f1; + // Ensure cbet1 = +epsilon at poles + Math::norm(sbet1, cbet1); cbet1 = max(tiny_, cbet1); + _dn1 = sqrt(1 + g._ep2 * Math::sq(sbet1)); + + // Evaluate alp0 from sin(alp1) * cos(bet1) = sin(alp0), + _salp0 = _salp1 * cbet1; // alp0 in [0, pi/2 - |bet1|] + // Alt: calp0 = hypot(sbet1, calp1 * cbet1). The following + // is slightly better (consider the case salp1 = 0). + _calp0 = hypot(_calp1, _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. + _ssig1 = sbet1; _somg1 = _salp0 * sbet1; + _csig1 = _comg1 = sbet1 != 0 || _calp1 != 0 ? cbet1 * _calp1 : 1; + Math::norm(_ssig1, _csig1); // sig1 in (-pi, pi] + // Math::norm(_somg1, _comg1); -- don't need to normalize! + + _k2 = Math::sq(_calp0) * g._ep2; + real eps = _k2 / (2 * (1 + sqrt(1 + _k2)) + _k2); + + if (_caps & CAP_C1) { + _A1m1 = Geodesic::A1m1f(eps); + Geodesic::C1f(eps, _C1a); + _B11 = Geodesic::SinCosSeries(true, _ssig1, _csig1, _C1a, nC1_); + real s = sin(_B11), c = cos(_B11); + // tau1 = sig1 + B11 + _stau1 = _ssig1 * c + _csig1 * s; + _ctau1 = _csig1 * c - _ssig1 * s; + // Not necessary because C1pa reverts C1a + // _B11 = -SinCosSeries(true, _stau1, _ctau1, _C1pa, nC1p_); + } + + if (_caps & CAP_C1p) + Geodesic::C1pf(eps, _C1pa); + + if (_caps & CAP_C2) { + _A2m1 = Geodesic::A2m1f(eps); + Geodesic::C2f(eps, _C2a); + _B21 = Geodesic::SinCosSeries(true, _ssig1, _csig1, _C2a, nC2_); + } + + if (_caps & CAP_C3) { + g.C3f(eps, _C3a); + _A3c = -_f * _salp0 * g.A3f(eps); + _B31 = Geodesic::SinCosSeries(true, _ssig1, _csig1, _C3a, nC3_-1); + } + + if (_caps & CAP_C4) { + g.C4f(eps, _C4a); + // Multiplier = a^2 * e^2 * cos(alpha0) * sin(alpha0) + _A4 = Math::sq(_a) * _calp0 * _salp0 * g._e2; + _B41 = Geodesic::SinCosSeries(false, _ssig1, _csig1, _C4a, nC4_); + } + + _a13 = _s13 = Math::NaN(); + } + + GeodesicLine::GeodesicLine(const Geodesic& g, + real lat1, real lon1, real azi1, + unsigned caps) { + azi1 = Math::AngNormalize(azi1); + real salp1, calp1; + // Guard against underflow in salp0. Also -0 is converted to +0. + Math::sincosd(Math::AngRound(azi1), salp1, calp1); + LineInit(g, lat1, lon1, azi1, salp1, calp1, caps); + } + + GeodesicLine::GeodesicLine(const Geodesic& g, + real lat1, real lon1, + real azi1, real salp1, real calp1, + unsigned caps, bool arcmode, real s13_a13) { + LineInit(g, lat1, lon1, azi1, salp1, calp1, caps); + GenSetDistance(arcmode, s13_a13); + } + + Math::real GeodesicLine::GenPosition(bool arcmode, real s12_a12, + unsigned outmask, + real& lat2, real& lon2, real& azi2, + real& s12, real& m12, + real& M12, real& M21, + real& S12) const { + outmask &= _caps & OUT_MASK; + if (!( Init() && (arcmode || (_caps & (OUT_MASK & DISTANCE_IN))) )) + // Uninitialized or impossible distance calculation requested + return Math::NaN(); + + // Avoid warning about uninitialized B12. + real sig12, ssig12, csig12, B12 = 0, AB1 = 0; + if (arcmode) { + // Interpret s12_a12 as spherical arc length + sig12 = s12_a12 * Math::degree(); + Math::sincosd(s12_a12, ssig12, csig12); + } else { + // Interpret s12_a12 as distance + real + tau12 = s12_a12 / (_b * (1 + _A1m1)), + s = sin(tau12), + c = cos(tau12); + // tau2 = tau1 + tau12 + B12 = - Geodesic::SinCosSeries(true, + _stau1 * c + _ctau1 * s, + _ctau1 * c - _stau1 * s, + _C1pa, nC1p_); + sig12 = tau12 - (B12 - _B11); + ssig12 = sin(sig12); csig12 = cos(sig12); + if (abs(_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 + ssig2 = _ssig1 * csig12 + _csig1 * ssig12, + csig2 = _csig1 * csig12 - _ssig1 * ssig12; + B12 = Geodesic::SinCosSeries(true, ssig2, csig2, _C1a, nC1_); + real serr = (1 + _A1m1) * (sig12 + (B12 - _B11)) - s12_a12 / _b; + sig12 = sig12 - serr / sqrt(1 + _k2 * Math::sq(ssig2)); + ssig12 = sin(sig12); csig12 = cos(sig12); + // Update B12 below + } + } + + real ssig2, csig2, sbet2, cbet2, salp2, calp2; + // sig2 = sig1 + sig12 + ssig2 = _ssig1 * csig12 + _csig1 * ssig12; + csig2 = _csig1 * csig12 - _ssig1 * ssig12; + real dn2 = sqrt(1 + _k2 * Math::sq(ssig2)); + if (outmask & (DISTANCE | REDUCEDLENGTH | GEODESICSCALE)) { + if (arcmode || abs(_f) > 0.01) + B12 = Geodesic::SinCosSeries(true, ssig2, csig2, _C1a, nC1_); + AB1 = (1 + _A1m1) * (B12 - _B11); + } + // sin(bet2) = cos(alp0) * sin(sig2) + sbet2 = _calp0 * ssig2; + // Alt: cbet2 = hypot(csig2, salp0 * ssig2); + cbet2 = hypot(_salp0, _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 = _salp0; calp2 = _calp0 * csig2; // No need to normalize + + if (outmask & DISTANCE) + s12 = arcmode ? _b * ((1 + _A1m1) * sig12 + AB1) : s12_a12; + + if (outmask & LONGITUDE) { + // tan(omg2) = sin(alp0) * tan(sig2) + real somg2 = _salp0 * ssig2, comg2 = csig2, // No need to normalize + E = copysign(real(1), _salp0); // east-going? + // omg12 = omg2 - omg1 + real omg12 = outmask & LONG_UNROLL + ? E * (sig12 + - (atan2( ssig2, csig2) - atan2( _ssig1, _csig1)) + + (atan2(E * somg2, comg2) - atan2(E * _somg1, _comg1))) + : atan2(somg2 * _comg1 - comg2 * _somg1, + comg2 * _comg1 + somg2 * _somg1); + real lam12 = omg12 + _A3c * + ( sig12 + (Geodesic::SinCosSeries(true, ssig2, csig2, _C3a, nC3_-1) + - _B31)); + real lon12 = lam12 / Math::degree(); + lon2 = outmask & LONG_UNROLL ? _lon1 + lon12 : + Math::AngNormalize(Math::AngNormalize(_lon1) + + Math::AngNormalize(lon12)); + } + + if (outmask & LATITUDE) + lat2 = Math::atan2d(sbet2, _f1 * cbet2); + + if (outmask & AZIMUTH) + azi2 = Math::atan2d(salp2, calp2); + + if (outmask & (REDUCEDLENGTH | GEODESICSCALE)) { + real + B22 = Geodesic::SinCosSeries(true, ssig2, csig2, _C2a, nC2_), + AB2 = (1 + _A2m1) * (B22 - _B21), + J12 = (_A1m1 - _A2m1) * sig12 + (AB1 - AB2); + if (outmask & REDUCEDLENGTH) + // Add parens around (_csig1 * ssig2) and (_ssig1 * csig2) to ensure + // accurate cancellation in the case of coincident points. + m12 = _b * ((dn2 * (_csig1 * ssig2) - _dn1 * (_ssig1 * csig2)) + - _csig1 * csig2 * J12); + if (outmask & GEODESICSCALE) { + real t = _k2 * (ssig2 - _ssig1) * (ssig2 + _ssig1) / (_dn1 + dn2); + M12 = csig12 + (t * ssig2 - csig2 * J12) * _ssig1 / _dn1; + M21 = csig12 - (t * _ssig1 - _csig1 * J12) * ssig2 / dn2; + } + } + + if (outmask & AREA) { + real + B42 = Geodesic::SinCosSeries(false, ssig2, csig2, _C4a, nC4_); + real salp12, calp12; + if (_calp0 == 0 || _salp0 == 0) { + // alp12 = alp2 - alp1, used in atan2 so no need to normalize + salp12 = salp2 * _calp1 - calp2 * _salp1; + calp12 = calp2 * _calp1 + salp2 * _salp1; + // We used to include here some patch up code that purported to deal + // with nearly meridional geodesics properly. However, this turned out + // to be wrong once _salp1 = -0 was allowed (via + // Geodesic::InverseLine). In fact, the calculation of {s,c}alp12 + // was already correct (following the IEEE rules for handling signed + // zeros). So the patch up code was unnecessary (as well as + // dangerous). + } 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 = _calp0 * _salp0 * + (csig12 <= 0 ? _csig1 * (1 - csig12) + ssig12 * _ssig1 : + ssig12 * (_csig1 * ssig12 / (1 + csig12) + _ssig1)); + calp12 = Math::sq(_salp0) + Math::sq(_calp0) * _csig1 * csig2; + } + S12 = _c2 * atan2(salp12, calp12) + _A4 * (B42 - _B41); + } + + return arcmode ? s12_a12 : sig12 / Math::degree(); + } + + void GeodesicLine::SetDistance(real s13) { + _s13 = s13; + real t; + // This will set _a13 to NaN if the GeodesicLine doesn't have the + // DISTANCE_IN capability. + _a13 = GenPosition(false, _s13, 0u, t, t, t, t, t, t, t, t); + } + + void GeodesicLine::SetArc(real a13) { + _a13 = a13; + // In case the GeodesicLine doesn't have the DISTANCE capability. + _s13 = Math::NaN(); + real t; + GenPosition(true, _a13, DISTANCE, t, t, t, _s13, t, t, t, t); + } + + void GeodesicLine::GenSetDistance(bool arcmode, real s13_a13) { + arcmode ? SetArc(s13_a13) : SetDistance(s13_a13); + } + +} // namespace GeographicLib diff --git a/common/local_libs/GeographicLib/src/GeodesicLineExact.cpp b/common/local_libs/GeographicLib/src/GeodesicLineExact.cpp new file mode 100644 index 0000000000..1e35c2f14b --- /dev/null +++ b/common/local_libs/GeographicLib/src/GeodesicLineExact.cpp @@ -0,0 +1,289 @@ +/** + * \file GeodesicLineExact.cpp + * \brief Implementation for GeographicLib::GeodesicLineExact class + * + * Copyright (c) Charles Karney (2012-2020) and licensed + * under the MIT/X11 License. For more information, see + * https://geographiclib.sourceforge.io/ + * + * This is a reformulation of the geodesic problem. The notation is as + * follows: + * - at a general point (no suffix or 1 or 2 as suffix) + * - phi = latitude + * - beta = latitude on auxiliary sphere + * - omega = longitude on auxiliary sphere + * - lambda = longitude + * - alpha = azimuth of great circle + * - sigma = arc length along great circle + * - s = distance + * - tau = scaled distance (= sigma at multiples of pi/2) + * - at northwards equator crossing + * - beta = phi = 0 + * - omega = lambda = 0 + * - alpha = alpha0 + * - sigma = s = 0 + * - a 12 suffix means a difference, e.g., s12 = s2 - s1. + * - s and c prefixes mean sin and cos + **********************************************************************/ + +#include + +namespace GeographicLib { + + using namespace std; + + void GeodesicLineExact::LineInit(const GeodesicExact& g, + real lat1, real lon1, + real azi1, real salp1, real calp1, + unsigned caps) { + tiny_ = g.tiny_; + _lat1 = Math::LatFix(lat1); + _lon1 = lon1; + _azi1 = azi1; + _salp1 = salp1; + _calp1 = calp1; + _a = g._a; + _f = g._f; + _b = g._b; + _c2 = g._c2; + _f1 = g._f1; + _e2 = g._e2; + // Always allow latitude and azimuth and unrolling of longitude + _caps = caps | LATITUDE | AZIMUTH | LONG_UNROLL; + + real cbet1, sbet1; + Math::sincosd(Math::AngRound(_lat1), sbet1, cbet1); sbet1 *= _f1; + // Ensure cbet1 = +epsilon at poles + Math::norm(sbet1, cbet1); cbet1 = max(tiny_, cbet1); + _dn1 = (_f >= 0 ? sqrt(1 + g._ep2 * Math::sq(sbet1)) : + sqrt(1 - _e2 * Math::sq(cbet1)) / _f1); + + // Evaluate alp0 from sin(alp1) * cos(bet1) = sin(alp0), + _salp0 = _salp1 * cbet1; // alp0 in [0, pi/2 - |bet1|] + // Alt: calp0 = hypot(sbet1, calp1 * cbet1). The following + // is slightly better (consider the case salp1 = 0). + _calp0 = hypot(_calp1, _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. + _ssig1 = sbet1; _somg1 = _salp0 * sbet1; + _csig1 = _comg1 = sbet1 != 0 || _calp1 != 0 ? cbet1 * _calp1 : 1; + // Without normalization we have schi1 = somg1. + _cchi1 = _f1 * _dn1 * _comg1; + Math::norm(_ssig1, _csig1); // sig1 in (-pi, pi] + // Math::norm(_somg1, _comg1); -- don't need to normalize! + // Math::norm(_schi1, _cchi1); -- don't need to normalize! + + _k2 = Math::sq(_calp0) * g._ep2; + _E.Reset(-_k2, -g._ep2, 1 + _k2, 1 + g._ep2); + + if (_caps & CAP_E) { + _E0 = _E.E() / (Math::pi() / 2); + _E1 = _E.deltaE(_ssig1, _csig1, _dn1); + real s = sin(_E1), c = cos(_E1); + // tau1 = sig1 + B11 + _stau1 = _ssig1 * c + _csig1 * s; + _ctau1 = _csig1 * c - _ssig1 * s; + // Not necessary because Einv inverts E + // _E1 = -_E.deltaEinv(_stau1, _ctau1); + } + + if (_caps & CAP_D) { + _D0 = _E.D() / (Math::pi() / 2); + _D1 = _E.deltaD(_ssig1, _csig1, _dn1); + } + + if (_caps & CAP_H) { + _H0 = _E.H() / (Math::pi() / 2); + _H1 = _E.deltaH(_ssig1, _csig1, _dn1); + } + + if (_caps & CAP_C4) { + real eps = _k2 / (2 * (1 + sqrt(1 + _k2)) + _k2); + g.C4f(eps, _C4a); + // Multiplier = a^2 * e^2 * cos(alpha0) * sin(alpha0) + _A4 = Math::sq(_a) * _calp0 * _salp0 * _e2; + _B41 = GeodesicExact::CosSeries(_ssig1, _csig1, _C4a, nC4_); + } + + _a13 = _s13 = Math::NaN(); + } + + GeodesicLineExact::GeodesicLineExact(const GeodesicExact& g, + real lat1, real lon1, real azi1, + unsigned caps) { + azi1 = Math::AngNormalize(azi1); + real salp1, calp1; + // Guard against underflow in salp0. Also -0 is converted to +0. + Math::sincosd(Math::AngRound(azi1), salp1, calp1); + LineInit(g, lat1, lon1, azi1, salp1, calp1, caps); + } + + GeodesicLineExact::GeodesicLineExact(const GeodesicExact& g, + real lat1, real lon1, + real azi1, real salp1, real calp1, + unsigned caps, + bool arcmode, real s13_a13) { + LineInit(g, lat1, lon1, azi1, salp1, calp1, caps); + GenSetDistance(arcmode, s13_a13); + } + + Math::real GeodesicLineExact::GenPosition(bool arcmode, real s12_a12, + unsigned outmask, + real& lat2, real& lon2, real& azi2, + real& s12, real& m12, + real& M12, real& M21, + real& S12) const { + outmask &= _caps & OUT_MASK; + if (!( Init() && (arcmode || (_caps & (OUT_MASK & DISTANCE_IN))) )) + // Uninitialized or impossible distance calculation requested + return Math::NaN(); + + // Avoid warning about uninitialized B12. + real sig12, ssig12, csig12, E2 = 0, AB1 = 0; + if (arcmode) { + // Interpret s12_a12 as spherical arc length + sig12 = s12_a12 * Math::degree(); + real s12a = abs(s12_a12); + s12a -= 180 * floor(s12a / 180); + ssig12 = s12a == 0 ? 0 : sin(sig12); + csig12 = s12a == 90 ? 0 : cos(sig12); + } else { + // Interpret s12_a12 as distance + real + tau12 = s12_a12 / (_b * _E0), + s = sin(tau12), + c = cos(tau12); + // tau2 = tau1 + tau12 + E2 = - _E.deltaEinv(_stau1 * c + _ctau1 * s, _ctau1 * c - _stau1 * s); + sig12 = tau12 - (E2 - _E1); + ssig12 = sin(sig12); + csig12 = cos(sig12); + } + + real ssig2, csig2, sbet2, cbet2, salp2, calp2; + // sig2 = sig1 + sig12 + ssig2 = _ssig1 * csig12 + _csig1 * ssig12; + csig2 = _csig1 * csig12 - _ssig1 * ssig12; + real dn2 = _E.Delta(ssig2, csig2); + if (outmask & (DISTANCE | REDUCEDLENGTH | GEODESICSCALE)) { + if (arcmode) { + E2 = _E.deltaE(ssig2, csig2, dn2); + } + AB1 = _E0 * (E2 - _E1); + } + // sin(bet2) = cos(alp0) * sin(sig2) + sbet2 = _calp0 * ssig2; + // Alt: cbet2 = hypot(csig2, salp0 * ssig2); + cbet2 = hypot(_salp0, _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 = _salp0; calp2 = _calp0 * csig2; // No need to normalize + + if (outmask & DISTANCE) + s12 = arcmode ? _b * (_E0 * sig12 + AB1) : s12_a12; + + if (outmask & LONGITUDE) { + real somg2 = _salp0 * ssig2, comg2 = csig2, // No need to normalize + E = copysign(real(1), _salp0); // east-going? + // Without normalization we have schi2 = somg2. + real cchi2 = _f1 * dn2 * comg2; + real chi12 = outmask & LONG_UNROLL + ? E * (sig12 + - (atan2( ssig2, csig2) - atan2( _ssig1, _csig1)) + + (atan2(E * somg2, cchi2) - atan2(E * _somg1, _cchi1))) + : atan2(somg2 * _cchi1 - cchi2 * _somg1, + cchi2 * _cchi1 + somg2 * _somg1); + real lam12 = chi12 - + _e2/_f1 * _salp0 * _H0 * + (sig12 + (_E.deltaH(ssig2, csig2, dn2) - _H1)); + real lon12 = lam12 / Math::degree(); + lon2 = outmask & LONG_UNROLL ? _lon1 + lon12 : + Math::AngNormalize(Math::AngNormalize(_lon1) + + Math::AngNormalize(lon12)); + } + + if (outmask & LATITUDE) + lat2 = Math::atan2d(sbet2, _f1 * cbet2); + + if (outmask & AZIMUTH) + azi2 = Math::atan2d(salp2, calp2); + + if (outmask & (REDUCEDLENGTH | GEODESICSCALE)) { + real J12 = _k2 * _D0 * (sig12 + (_E.deltaD(ssig2, csig2, dn2) - _D1)); + if (outmask & REDUCEDLENGTH) + // Add parens around (_csig1 * ssig2) and (_ssig1 * csig2) to ensure + // accurate cancellation in the case of coincident points. + m12 = _b * ((dn2 * (_csig1 * ssig2) - _dn1 * (_ssig1 * csig2)) + - _csig1 * csig2 * J12); + if (outmask & GEODESICSCALE) { + real t = _k2 * (ssig2 - _ssig1) * (ssig2 + _ssig1) / (_dn1 + dn2); + M12 = csig12 + (t * ssig2 - csig2 * J12) * _ssig1 / _dn1; + M21 = csig12 - (t * _ssig1 - _csig1 * J12) * ssig2 / dn2; + } + } + + if (outmask & AREA) { + real + B42 = GeodesicExact::CosSeries(ssig2, csig2, _C4a, nC4_); + real salp12, calp12; + if (_calp0 == 0 || _salp0 == 0) { + // alp12 = alp2 - alp1, used in atan2 so no need to normalize + salp12 = salp2 * _calp1 - calp2 * _salp1; + calp12 = calp2 * _calp1 + salp2 * _salp1; + // We used to include here some patch up code that purported to deal + // with nearly meridional geodesics properly. However, this turned out + // to be wrong once _salp1 = -0 was allowed (via + // GeodesicExact::InverseLine). In fact, the calculation of {s,c}alp12 + // was already correct (following the IEEE rules for handling signed + // zeros). So the patch up code was unnecessary (as well as + // dangerous). + } 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 = _calp0 * _salp0 * + (csig12 <= 0 ? _csig1 * (1 - csig12) + ssig12 * _ssig1 : + ssig12 * (_csig1 * ssig12 / (1 + csig12) + _ssig1)); + calp12 = Math::sq(_salp0) + Math::sq(_calp0) * _csig1 * csig2; + } + S12 = _c2 * atan2(salp12, calp12) + _A4 * (B42 - _B41); + } + + return arcmode ? s12_a12 : sig12 / Math::degree(); + } + + void GeodesicLineExact::SetDistance(real s13) { + _s13 = s13; + real t; + // This will set _a13 to NaN if the GeodesicLineExact doesn't have the + // DISTANCE_IN capability. + _a13 = GenPosition(false, _s13, 0u, t, t, t, t, t, t, t, t); + } + + void GeodesicLineExact::SetArc(real a13) { + _a13 = a13; + // In case the GeodesicLineExact doesn't have the DISTANCE capability. + _s13 = Math::NaN(); + real t; + GenPosition(true, _a13, DISTANCE, t, t, t, _s13, t, t, t, t); + } + + void GeodesicLineExact::GenSetDistance(bool arcmode, real s13_a13) { + arcmode ? SetArc(s13_a13) : SetDistance(s13_a13); + } + +} // namespace GeographicLib diff --git a/common/local_libs/GeographicLib/src/Geohash.cpp b/common/local_libs/GeographicLib/src/Geohash.cpp new file mode 100644 index 0000000000..5d84c7a032 --- /dev/null +++ b/common/local_libs/GeographicLib/src/Geohash.cpp @@ -0,0 +1,104 @@ +/** + * \file Geohash.cpp + * \brief Implementation for GeographicLib::Geohash class + * + * Copyright (c) Charles Karney (2012-2020) and licensed + * under the MIT/X11 License. For more information, see + * https://geographiclib.sourceforge.io/ + **********************************************************************/ + +#include +#include + +namespace GeographicLib { + + using namespace std; + + const char* const Geohash::lcdigits_ = "0123456789bcdefghjkmnpqrstuvwxyz"; + const char* const Geohash::ucdigits_ = "0123456789BCDEFGHJKMNPQRSTUVWXYZ"; + + void Geohash::Forward(real lat, real lon, int len, string& geohash) { + using std::isnan; // Needed for Centos 7, ubuntu 14 + static const real shift = ldexp(real(1), 45); + static const real loneps = 180 / shift; + static const real lateps = 90 / shift; + if (abs(lat) > 90) + throw GeographicErr("Latitude " + Utility::str(lat) + + "d not in [-90d, 90d]"); + if (isnan(lat) || isnan(lon)) { + geohash = "invalid"; + return; + } + if (lat == 90) lat -= lateps / 2; + lon = Math::AngNormalize(lon); + if (lon == 180) lon = -180; // lon now in [-180,180) + // lon/loneps in [-2^45,2^45); lon/loneps + shift in [0,2^46) + // similarly for lat + len = max(0, min(int(maxlen_), len)); + unsigned long long + ulon = (unsigned long long)(floor(lon/loneps) + shift), + ulat = (unsigned long long)(floor(lat/lateps) + shift); + char geohash1[maxlen_]; + unsigned byte = 0; + for (unsigned i = 0; i < 5 * unsigned(len);) { + if ((i & 1) == 0) { + byte = (byte << 1) + unsigned((ulon & mask_) != 0); + ulon <<= 1; + } else { + byte = (byte << 1) + unsigned((ulat & mask_) != 0); + ulat <<= 1; + } + ++i; + if (i % 5 == 0) { + geohash1[(i/5)-1] = lcdigits_[byte]; + byte = 0; + } + } + geohash.resize(len); + copy(geohash1, geohash1 + len, geohash.begin()); + } + + void Geohash::Reverse(const string& geohash, real& lat, real& lon, + int& len, bool centerp) { + static const real shift = ldexp(real(1), 45); + static const real loneps = 180 / shift; + static const real lateps = 90 / shift; + int len1 = min(int(maxlen_), int(geohash.length())); + if (len1 >= 3 && + ((toupper(geohash[0]) == 'I' && + toupper(geohash[1]) == 'N' && + toupper(geohash[2]) == 'V') || + // Check A first because it is not in a standard geohash + (toupper(geohash[1]) == 'A' && + toupper(geohash[0]) == 'N' && + toupper(geohash[2]) == 'N'))) { + lat = lon = Math::NaN(); + return; + } + unsigned long long ulon = 0, ulat = 0; + for (unsigned k = 0, j = 0; k < unsigned(len1); ++k) { + int byte = Utility::lookup(ucdigits_, geohash[k]); + if (byte < 0) + throw GeographicErr("Illegal character in geohash " + geohash); + for (unsigned m = 16; m; m >>= 1) { + if (j == 0) + ulon = (ulon << 1) + unsigned((byte & m) != 0); + else + ulat = (ulat << 1) + unsigned((byte & m) != 0); + j ^= 1; + } + } + ulon <<= 1; ulat <<= 1; + if (centerp) { + ulon += 1; + ulat += 1; + } + int s = 5 * (maxlen_ - len1); + ulon <<= (s / 2); + ulat <<= s - (s / 2); + lon = ulon * loneps - 180; + lat = ulat * lateps - 90; + len = len1; + } + +} // namespace GeographicLib diff --git a/common/local_libs/GeographicLib/src/Geoid.cpp b/common/local_libs/GeographicLib/src/Geoid.cpp new file mode 100644 index 0000000000..94d928cb6b --- /dev/null +++ b/common/local_libs/GeographicLib/src/Geoid.cpp @@ -0,0 +1,510 @@ +/** + * \file Geoid.cpp + * \brief Implementation for GeographicLib::Geoid class + * + * Copyright (c) Charles Karney (2009-2020) and licensed + * under the MIT/X11 License. For more information, see + * https://geographiclib.sourceforge.io/ + **********************************************************************/ + +#include +// For getenv +#include +#include + +#if !defined(GEOGRAPHICLIB_DATA) +# if defined(_WIN32) +# define GEOGRAPHICLIB_DATA "C:/ProgramData/GeographicLib" +# else +# define GEOGRAPHICLIB_DATA "/usr/local/share/GeographicLib" +# endif +#endif + +#if !defined(GEOGRAPHICLIB_GEOID_DEFAULT_NAME) +# define GEOGRAPHICLIB_GEOID_DEFAULT_NAME "egm96-5" +#endif + +#if defined(_MSC_VER) +// Squelch warnings about unsafe use of getenv +# pragma warning (disable: 4996) +#endif + +namespace GeographicLib { + + using namespace std; + + // This is the transfer matrix for a 3rd order fit with a 12-point stencil + // with weights + // + // \x -1 0 1 2 + // y + // -1 . 1 1 . + // 0 1 2 2 1 + // 1 1 2 2 1 + // 2 . 1 1 . + // + // A algorithm for n-dimensional polynomial fits is described in + // F. H. Lesh, + // Multi-dimensional least-squares polynomial curve fitting, + // CACM 2, 29-30 (1959). + // https://doi.org/10.1145/368424.368443 + // + // Here's the Maxima code to generate this matrix: + // + // /* The stencil and the weights */ + // xarr:[ + // 0, 1, + // -1, 0, 1, 2, + // -1, 0, 1, 2, + // 0, 1]$ + // yarr:[ + // -1,-1, + // 0, 0, 0, 0, + // 1, 1, 1, 1, + // 2, 2]$ + // warr:[ + // 1, 1, + // 1, 2, 2, 1, + // 1, 2, 2, 1, + // 1, 1]$ + // + // /* [x exponent, y exponent] for cubic fit */ + // pows:[ + // [0,0], + // [1,0],[0,1], + // [2,0],[1,1],[0,2], + // [3,0],[2,1],[1,2],[0,3]]$ + // + // basisvec(x,y,pows):=map(lambda([ex],(if ex[1]=0 then 1 else x^ex[1])* + // (if ex[2]=0 then 1 else y^ex[2])),pows)$ + // addterm(x,y,f,w,pows):=block([a,b,bb:basisvec(x,y,pows)], + // a:w*(transpose(bb).bb), + // b:(w*f) * bb, + // [a,b])$ + // + // c3row(k):=block([a,b,c,pows:pows,n], + // n:length(pows), + // a:zeromatrix(n,n), + // b:copylist(part(a,1)), + // c:[a,b], + // for i:1 thru length(xarr) do + // c:c+addterm(xarr[i],yarr[i],if i=k then 1 else 0,warr[i],pows), + // a:c[1],b:c[2], + // part(transpose( a^^-1 . transpose(b)),1))$ + // c3:[]$ + // for k:1 thru length(warr) do c3:endcons(c3row(k),c3)$ + // c3:apply(matrix,c3)$ + // c0:part(ratsimp( + // genmatrix(yc,1,length(warr)).abs(c3).genmatrix(yd,length(pows),1)),2)$ + // c3:c0*c3$ + + const int Geoid::c0_ = 240; // Common denominator + const int Geoid::c3_[stencilsize_ * nterms_] = { + 9, -18, -88, 0, 96, 90, 0, 0, -60, -20, + -9, 18, 8, 0, -96, 30, 0, 0, 60, -20, + 9, -88, -18, 90, 96, 0, -20, -60, 0, 0, + 186, -42, -42, -150, -96, -150, 60, 60, 60, 60, + 54, 162, -78, 30, -24, -90, -60, 60, -60, 60, + -9, -32, 18, 30, 24, 0, 20, -60, 0, 0, + -9, 8, 18, 30, -96, 0, -20, 60, 0, 0, + 54, -78, 162, -90, -24, 30, 60, -60, 60, -60, + -54, 78, 78, 90, 144, 90, -60, -60, -60, -60, + 9, -8, -18, -30, -24, 0, 20, 60, 0, 0, + -9, 18, -32, 0, 24, 30, 0, 0, -60, 20, + 9, -18, -8, 0, -24, -30, 0, 0, 60, 20, + }; + + // Like c3, but with the coeffs of x, x^2, and x^3 constrained to be zero. + // Use this at the N pole so that the height in independent of the longitude + // there. + // + // Here's the Maxima code to generate this matrix (continued from above). + // + // /* figure which terms to exclude so that fit is indep of x at y=0 */ + // mask:part(zeromatrix(1,length(pows)),1)+1$ + // for i:1 thru length(pows) do + // if pows[i][1]>0 and pows[i][2]=0 then mask[i]:0$ + // + // /* Same as c3row but with masked pows. */ + // c3nrow(k):=block([a,b,c,powsa:[],n,d,e], + // for i:1 thru length(mask) do if mask[i]>0 then + // powsa:endcons(pows[i],powsa), + // n:length(powsa), + // a:zeromatrix(n,n), + // b:copylist(part(a,1)), + // c:[a,b], + // for i:1 thru length(xarr) do + // c:c+addterm(xarr[i],yarr[i],if i=k then 1 else 0,warr[i],powsa), + // a:c[1],b:c[2], + // d:part(transpose( a^^-1 . transpose(b)),1), + // e:[], + // for i:1 thru length(mask) do + // if mask[i]>0 then (e:endcons(first(d),e),d:rest(d)) else e:endcons(0,e), + // e)$ + // c3n:[]$ + // for k:1 thru length(warr) do c3n:endcons(c3nrow(k),c3n)$ + // c3n:apply(matrix,c3n)$ + // c0n:part(ratsimp( + // genmatrix(yc,1,length(warr)).abs(c3n).genmatrix(yd,length(pows),1)),2)$ + // c3n:c0n*c3n$ + + const int Geoid::c0n_ = 372; // Common denominator + const int Geoid::c3n_[stencilsize_ * nterms_] = { + 0, 0, -131, 0, 138, 144, 0, 0, -102, -31, + 0, 0, 7, 0, -138, 42, 0, 0, 102, -31, + 62, 0, -31, 0, 0, -62, 0, 0, 0, 31, + 124, 0, -62, 0, 0, -124, 0, 0, 0, 62, + 124, 0, -62, 0, 0, -124, 0, 0, 0, 62, + 62, 0, -31, 0, 0, -62, 0, 0, 0, 31, + 0, 0, 45, 0, -183, -9, 0, 93, 18, 0, + 0, 0, 216, 0, 33, 87, 0, -93, 12, -93, + 0, 0, 156, 0, 153, 99, 0, -93, -12, -93, + 0, 0, -45, 0, -3, 9, 0, 93, -18, 0, + 0, 0, -55, 0, 48, 42, 0, 0, -84, 31, + 0, 0, -7, 0, -48, -42, 0, 0, 84, 31, + }; + + // Like c3n, but y -> 1-y so that h is independent of x at y = 1. Use this + // at the S pole so that the height in independent of the longitude there. + // + // Here's the Maxima code to generate this matrix (continued from above). + // + // /* Transform c3n to c3s by transforming y -> 1-y */ + // vv:[ + // v[11],v[12], + // v[7],v[8],v[9],v[10], + // v[3],v[4],v[5],v[6], + // v[1],v[2]]$ + // poly:expand(vv.(c3n/c0n).transpose(basisvec(x,1-y,pows)))$ + // c3sf[i,j]:=coeff(coeff(coeff(poly,v[i]),x,pows[j][1]),y,pows[j][2])$ + // c3s:genmatrix(c3sf,length(vv),length(pows))$ + // c0s:part(ratsimp( + // genmatrix(yc,1,length(warr)).abs(c3s).genmatrix(yd,length(pows),1)),2)$ + // c3s:c0s*c3s$ + + const int Geoid::c0s_ = 372; // Common denominator + const int Geoid::c3s_[stencilsize_ * nterms_] = { + 18, -36, -122, 0, 120, 135, 0, 0, -84, -31, + -18, 36, -2, 0, -120, 51, 0, 0, 84, -31, + 36, -165, -27, 93, 147, -9, 0, -93, 18, 0, + 210, 45, -111, -93, -57, -192, 0, 93, 12, 93, + 162, 141, -75, -93, -129, -180, 0, 93, -12, 93, + -36, -21, 27, 93, 39, 9, 0, -93, -18, 0, + 0, 0, 62, 0, 0, 31, 0, 0, 0, -31, + 0, 0, 124, 0, 0, 62, 0, 0, 0, -62, + 0, 0, 124, 0, 0, 62, 0, 0, 0, -62, + 0, 0, 62, 0, 0, 31, 0, 0, 0, -31, + -18, 36, -64, 0, 66, 51, 0, 0, -102, 31, + 18, -36, 2, 0, -66, -51, 0, 0, 102, 31, + }; + + Geoid::Geoid(const std::string& name, const std::string& path, bool cubic, + bool threadsafe) + : _name(name) + , _dir(path) + , _cubic(cubic) + , _a( Constants::WGS84_a() ) + , _e2( (2 - Constants::WGS84_f()) * Constants::WGS84_f() ) + , _degree( Math::degree() ) + , _eps( sqrt(numeric_limits::epsilon()) ) + , _threadsafe(false) // Set after cache is read + { + static_assert(sizeof(pixel_t) == pixel_size_, "pixel_t has the wrong size"); + if (_dir.empty()) + _dir = DefaultGeoidPath(); + _filename = _dir + "/" + _name + (pixel_size_ != 4 ? ".pgm" : ".pgm4"); + _file.open(_filename.c_str(), ios::binary); + if (!(_file.good())) + throw GeographicErr("File not readable " + _filename); + string s; + if (!(getline(_file, s) && s == "P5")) + throw GeographicErr("File not in PGM format " + _filename); + _offset = numeric_limits::max(); + _scale = 0; + _maxerror = _rmserror = -1; + _description = "NONE"; + _datetime = "UNKNOWN"; + while (getline(_file, s)) { + if (s.empty()) + continue; + if (s[0] == '#') { + istringstream is(s); + string commentid, key; + if (!(is >> commentid >> key) || commentid != "#") + continue; + if (key == "Description" || key == "DateTime") { + string::size_type p = + s.find_first_not_of(" \t", unsigned(is.tellg())); + if (p != string::npos) + (key == "Description" ? _description : _datetime) = s.substr(p); + } else if (key == "Offset") { + if (!(is >> _offset)) + throw GeographicErr("Error reading offset " + _filename); + } else if (key == "Scale") { + if (!(is >> _scale)) + throw GeographicErr("Error reading scale " + _filename); + } else if (key == (_cubic ? "MaxCubicError" : "MaxBilinearError")) { + // It's not an error if the error can't be read + is >> _maxerror; + } else if (key == (_cubic ? "RMSCubicError" : "RMSBilinearError")) { + // It's not an error if the error can't be read + is >> _rmserror; + } + } else { + istringstream is(s); + if (!(is >> _width >> _height)) + throw GeographicErr("Error reading raster size " + _filename); + break; + } + } + { + unsigned maxval; + if (!(_file >> maxval)) + throw GeographicErr("Error reading maxval " + _filename); + if (maxval != pixel_max_) + throw GeographicErr("Incorrect value of maxval " + _filename); + // Add 1 for whitespace after maxval + _datastart = (unsigned long long)(_file.tellg()) + 1ULL; + _swidth = (unsigned long long)(_width); + } + if (_offset == numeric_limits::max()) + throw GeographicErr("Offset not set " + _filename); + if (_scale == 0) + throw GeographicErr("Scale not set " + _filename); + if (_scale < 0) + throw GeographicErr("Scale must be positive " + _filename); + if (_height < 2 || _width < 2) + // Coarsest grid spacing is 180deg. + throw GeographicErr("Raster size too small " + _filename); + if (_width & 1) + // This is so that longitude grids can be extended thru the poles. + throw GeographicErr("Raster width is odd " + _filename); + if (!(_height & 1)) + // This is so that latitude grid includes the equator. + throw GeographicErr("Raster height is even " + _filename); + _file.seekg(0, ios::end); + if (!_file.good() || + _datastart + pixel_size_ * _swidth * (unsigned long long)(_height) != + (unsigned long long)(_file.tellg())) + // Possibly this test should be "<" because the file contains, e.g., a + // second image. However, for now we are more strict. + throw GeographicErr("File has the wrong length " + _filename); + _rlonres = _width / real(360); + _rlatres = (_height - 1) / real(180); + _cache = false; + _ix = _width; + _iy = _height; + // Ensure that file errors throw exceptions + _file.exceptions(ifstream::eofbit | ifstream::failbit | ifstream::badbit); + if (threadsafe) { + CacheAll(); + _file.close(); + _threadsafe = true; + } + } + + Math::real Geoid::height(real lat, real lon) const { + using std::isnan; // Needed for Centos 7, ubuntu 14 + lat = Math::LatFix(lat); + if (isnan(lat) || isnan(lon)) { + return Math::NaN(); + } + lon = Math::AngNormalize(lon); + real + fx = lon * _rlonres, + fy = -lat * _rlatres; + int + ix = int(floor(fx)), + iy = min((_height - 1)/2 - 1, int(floor(fy))); + fx -= ix; + fy -= iy; + iy += (_height - 1)/2; + ix += ix < 0 ? _width : (ix >= _width ? -_width : 0); + real v00 = 0, v01 = 0, v10 = 0, v11 = 0; + real t[nterms_]; + + if (_threadsafe || !(ix == _ix && iy == _iy)) { + if (!_cubic) { + v00 = rawval(ix , iy ); + v01 = rawval(ix + 1, iy ); + v10 = rawval(ix , iy + 1); + v11 = rawval(ix + 1, iy + 1); + } else { + real v[stencilsize_]; + int k = 0; + v[k++] = rawval(ix , iy - 1); + v[k++] = rawval(ix + 1, iy - 1); + v[k++] = rawval(ix - 1, iy ); + v[k++] = rawval(ix , iy ); + v[k++] = rawval(ix + 1, iy ); + v[k++] = rawval(ix + 2, iy ); + v[k++] = rawval(ix - 1, iy + 1); + v[k++] = rawval(ix , iy + 1); + v[k++] = rawval(ix + 1, iy + 1); + v[k++] = rawval(ix + 2, iy + 1); + v[k++] = rawval(ix , iy + 2); + v[k++] = rawval(ix + 1, iy + 2); + + const int* c3x = iy == 0 ? c3n_ : (iy == _height - 2 ? c3s_ : c3_); + int c0x = iy == 0 ? c0n_ : (iy == _height - 2 ? c0s_ : c0_); + for (unsigned i = 0; i < nterms_; ++i) { + t[i] = 0; + for (unsigned j = 0; j < stencilsize_; ++j) + t[i] += v[j] * c3x[nterms_ * j + i]; + t[i] /= c0x; + } + } + } else { // same cell; used cached coefficients + if (!_cubic) { + v00 = _v00; + v01 = _v01; + v10 = _v10; + v11 = _v11; + } else + copy(_t, _t + nterms_, t); + } + if (!_cubic) { + real + a = (1 - fx) * v00 + fx * v01, + b = (1 - fx) * v10 + fx * v11, + c = (1 - fy) * a + fy * b, + h = _offset + _scale * c; + if (!_threadsafe) { + _ix = ix; + _iy = iy; + _v00 = v00; + _v01 = v01; + _v10 = v10; + _v11 = v11; + } + return h; + } else { + real h = t[0] + fx * (t[1] + fx * (t[3] + fx * t[6])) + + fy * (t[2] + fx * (t[4] + fx * t[7]) + + fy * (t[5] + fx * t[8] + fy * t[9])); + h = _offset + _scale * h; + if (!_threadsafe) { + _ix = ix; + _iy = iy; + copy(t, t + nterms_, _t); + } + return h; + } + } + + void Geoid::CacheClear() const { + if (!_threadsafe) { + _cache = false; + try { + _data.clear(); + // Use swap to release memory back to system + vector< vector >().swap(_data); + } + catch (const exception&) { + } + } + } + + void Geoid::CacheArea(real south, real west, real north, real east) const { + if (_threadsafe) + throw GeographicErr("Attempt to change cache of threadsafe Geoid"); + if (south > north) { + CacheClear(); + return; + } + south = Math::LatFix(south); + north = Math::LatFix(north); + west = Math::AngNormalize(west); // west in [-180, 180) + east = Math::AngNormalize(east); + if (east <= west) + east += 360; // east - west in (0, 360] + int + iw = int(floor(west * _rlonres)), + ie = int(floor(east * _rlonres)), + in = int(floor(-north * _rlatres)) + (_height - 1)/2, + is = int(floor(-south * _rlatres)) + (_height - 1)/2; + in = max(0, min(_height - 2, in)); + is = max(0, min(_height - 2, is)); + is += 1; + ie += 1; + if (_cubic) { + in -= 1; + is += 1; + iw -= 1; + ie += 1; + } + if (ie - iw >= _width - 1) { + // Include entire longitude range + iw = 0; + ie = _width - 1; + } else { + ie += iw < 0 ? _width : (iw >= _width ? -_width : 0); + iw += iw < 0 ? _width : (iw >= _width ? -_width : 0); + } + int oysize = int(_data.size()); + _xsize = ie - iw + 1; + _ysize = is - in + 1; + _xoffset = iw; + _yoffset = in; + + try { + _data.resize(_ysize, vector(_xsize)); + for (int iy = min(oysize, _ysize); iy--;) + _data[iy].resize(_xsize); + } + catch (const bad_alloc&) { + CacheClear(); + throw GeographicErr("Insufficient memory for caching " + _filename); + } + + try { + for (int iy = in; iy <= is; ++iy) { + int iy1 = iy, iw1 = iw; + if (iy < 0 || iy >= _height) { + // Allow points "beyond" the poles to support interpolation + iy1 = iy1 < 0 ? -iy1 : 2 * (_height - 1) - iy1; + iw1 += _width/2; + if (iw1 >= _width) + iw1 -= _width; + } + int xs1 = min(_width - iw1, _xsize); + filepos(iw1, iy1); + Utility::readarray + (_file, &(_data[iy - in][0]), xs1); + if (xs1 < _xsize) { + // Wrap around longitude = 0 + filepos(0, iy1); + Utility::readarray + (_file, &(_data[iy - in][xs1]), _xsize - xs1); + } + } + _cache = true; + } + catch (const exception& e) { + CacheClear(); + throw GeographicErr(string("Error filling cache ") + e.what()); + } + } + + string Geoid::DefaultGeoidPath() { + string path; + char* geoidpath = getenv("GEOGRAPHICLIB_GEOID_PATH"); + if (geoidpath) + path = string(geoidpath); + if (!path.empty()) + return path; + char* datapath = getenv("GEOGRAPHICLIB_DATA"); + if (datapath) + path = string(datapath); + return (!path.empty() ? path : string(GEOGRAPHICLIB_DATA)) + "/geoids"; + } + + string Geoid::DefaultGeoidName() { + string name; + char* geoidname = getenv("GEOGRAPHICLIB_GEOID_NAME"); + if (geoidname) + name = string(geoidname); + return !name.empty() ? name : string(GEOGRAPHICLIB_GEOID_DEFAULT_NAME); + } + +} // namespace GeographicLib diff --git a/common/local_libs/GeographicLib/src/Georef.cpp b/common/local_libs/GeographicLib/src/Georef.cpp new file mode 100644 index 0000000000..ff01cc1f16 --- /dev/null +++ b/common/local_libs/GeographicLib/src/Georef.cpp @@ -0,0 +1,137 @@ +/** + * \file Georef.cpp + * \brief Implementation for GeographicLib::Georef class + * + * Copyright (c) Charles Karney (2015-2020) and licensed + * under the MIT/X11 License. For more information, see + * https://geographiclib.sourceforge.io/ + **********************************************************************/ + +#include +#include + +namespace GeographicLib { + + using namespace std; + + const char* const Georef::digits_ = "0123456789"; + const char* const Georef::lontile_ = "ABCDEFGHJKLMNPQRSTUVWXYZ"; + const char* const Georef::lattile_ = "ABCDEFGHJKLM"; + const char* const Georef::degrees_ = "ABCDEFGHJKLMNPQ"; + + void Georef::Forward(real lat, real lon, int prec, string& georef) { + using std::isnan; // Needed for Centos 7, ubuntu 14 + if (abs(lat) > 90) + throw GeographicErr("Latitude " + Utility::str(lat) + + "d not in [-90d, 90d]"); + if (isnan(lat) || isnan(lon)) { + georef = "INVALID"; + return; + } + lon = Math::AngNormalize(lon); // lon in [-180,180) + if (lat == 90) lat *= (1 - numeric_limits::epsilon() / 2); + prec = max(-1, min(int(maxprec_), prec)); + if (prec == 1) ++prec; // Disallow prec = 1 + // The C++ standard mandates 64 bits for long long. But + // check, to make sure. + static_assert(numeric_limits::digits >= 45, + "long long not wide enough to store 21600e9"); + const long long m = 60000000000LL; + long long + x = (long long)(floor(lon * real(m))) - lonorig_ * m, + y = (long long)(floor(lat * real(m))) - latorig_ * m; + int ilon = int(x / m); int ilat = int(y / m); + char georef1[maxlen_]; + georef1[0] = lontile_[ilon / tile_]; + georef1[1] = lattile_[ilat / tile_]; + if (prec >= 0) { + georef1[2] = degrees_[ilon % tile_]; + georef1[3] = degrees_[ilat % tile_]; + if (prec > 0) { + x -= m * ilon; y -= m * ilat; + long long d = (long long)pow(real(base_), maxprec_ - prec); + x /= d; y /= d; + for (int c = prec; c--;) { + georef1[baselen_ + c ] = digits_[x % base_]; x /= base_; + georef1[baselen_ + c + prec] = digits_[y % base_]; y /= base_; + } + } + } + georef.resize(baselen_ + 2 * prec); + copy(georef1, georef1 + baselen_ + 2 * prec, georef.begin()); + } + + void Georef::Reverse(const string& georef, real& lat, real& lon, + int& prec, bool centerp) { + int len = int(georef.length()); + if (len >= 3 && + toupper(georef[0]) == 'I' && + toupper(georef[1]) == 'N' && + toupper(georef[2]) == 'V') { + lat = lon = Math::NaN(); + return; + } + if (len < baselen_ - 2) + throw GeographicErr("Georef must start with at least 2 letters " + + georef); + int prec1 = (2 + len - baselen_) / 2 - 1; + int k; + k = Utility::lookup(lontile_, georef[0]); + if (k < 0) + throw GeographicErr("Bad longitude tile letter in georef " + georef); + real lon1 = k + lonorig_ / tile_; + k = Utility::lookup(lattile_, georef[1]); + if (k < 0) + throw GeographicErr("Bad latitude tile letter in georef " + georef); + real lat1 = k + latorig_ / tile_; + real unit = 1; + if (len > 2) { + unit *= tile_; + k = Utility::lookup(degrees_, georef[2]); + if (k < 0) + throw GeographicErr("Bad longitude degree letter in georef " + georef); + lon1 = lon1 * tile_ + k; + if (len < 4) + throw GeographicErr("Missing latitude degree letter in georef " + + georef); + k = Utility::lookup(degrees_, georef[3]); + if (k < 0) + throw GeographicErr("Bad latitude degree letter in georef " + georef); + lat1 = lat1 * tile_ + k; + if (prec1 > 0) { + if (georef.find_first_not_of(digits_, baselen_) != string::npos) + throw GeographicErr("Non digits in trailing portion of georef " + + georef.substr(baselen_)); + if (len % 2) + throw GeographicErr("Georef must end with an even number of digits " + + georef.substr(baselen_)); + if (prec1 == 1) + throw GeographicErr("Georef needs at least 4 digits for minutes " + + georef.substr(baselen_)); + if (prec1 > maxprec_) + throw GeographicErr("More than " + Utility::str(2*maxprec_) + + " digits in georef " + + georef.substr(baselen_)); + for (int i = 0; i < prec1; ++i) { + int m = i ? base_ : 6; + unit *= m; + int + x = Utility::lookup(digits_, georef[baselen_ + i]), + y = Utility::lookup(digits_, georef[baselen_ + i + prec1]); + if (!(i || (x < m && y < m))) + throw GeographicErr("Minutes terms in georef must be less than 60 " + + georef.substr(baselen_)); + lon1 = m * lon1 + x; + lat1 = m * lat1 + y; + } + } + } + if (centerp) { + unit *= 2; lat1 = 2 * lat1 + 1; lon1 = 2 * lon1 + 1; + } + lat = (tile_ * lat1) / unit; + lon = (tile_ * lon1) / unit; + prec = prec1; + } + +} // namespace GeographicLib diff --git a/common/local_libs/GeographicLib/src/Gnomonic.cpp b/common/local_libs/GeographicLib/src/Gnomonic.cpp new file mode 100644 index 0000000000..0d7ae1c6e4 --- /dev/null +++ b/common/local_libs/GeographicLib/src/Gnomonic.cpp @@ -0,0 +1,83 @@ +/** + * \file Gnomonic.cpp + * \brief Implementation for GeographicLib::Gnomonic class + * + * Copyright (c) Charles Karney (2010-2020) and licensed + * under the MIT/X11 License. For more information, see + * https://geographiclib.sourceforge.io/ + **********************************************************************/ + +#include + +#if defined(_MSC_VER) +// Squelch warnings about potentially uninitialized local variables and +// constant conditional expressions +# pragma warning (disable: 4701 4127) +#endif + +namespace GeographicLib { + + using namespace std; + + Gnomonic::Gnomonic(const Geodesic& earth) + : eps0_(numeric_limits::epsilon()) + , eps_(real(0.01) * sqrt(eps0_)) + , _earth(earth) + , _a(_earth.EquatorialRadius()) + , _f(_earth.Flattening()) + {} + + void Gnomonic::Forward(real lat0, real lon0, real lat, real lon, + real& x, real& y, real& azi, real& rk) const { + real azi0, m, M, t; + _earth.GenInverse(lat0, lon0, lat, lon, + Geodesic::AZIMUTH | Geodesic::REDUCEDLENGTH | + Geodesic::GEODESICSCALE, + t, azi0, azi, m, M, t, t); + rk = M; + if (M <= 0) + x = y = Math::NaN(); + else { + real rho = m/M; + Math::sincosd(azi0, x, y); + x *= rho; y *= rho; + } + } + + void Gnomonic::Reverse(real lat0, real lon0, real x, real y, + real& lat, real& lon, real& azi, real& rk) const { + real + azi0 = Math::atan2d(x, y), + rho = hypot(x, y), + s = _a * atan(rho/_a); + bool little = rho <= _a; + if (!little) + rho = 1/rho; + GeodesicLine line(_earth.Line(lat0, lon0, azi0, + Geodesic::LATITUDE | Geodesic::LONGITUDE | + Geodesic::AZIMUTH | Geodesic::DISTANCE_IN | + Geodesic::REDUCEDLENGTH | + Geodesic::GEODESICSCALE)); + int count = numit_, trip = 0; + real lat1=0, lon1=0, azi1=0, M=0; + while (count-- || GEOGRAPHICLIB_PANIC) { + real m, t; + line.Position(s, lat1, lon1, azi1, m, M, t); + if (trip) + break; + // If little, solve rho(s) = rho with drho(s)/ds = 1/M^2 + // else solve 1/rho(s) = 1/rho with d(1/rho(s))/ds = -1/m^2 + real ds = little ? (m - rho * M) * M : (rho * m - M) * m; + s -= ds; + // Reversed test to allow escape with NaNs + if (!(abs(ds) >= eps_ * _a)) + ++trip; + } + if (trip) { + lat = lat1; lon = lon1; azi = azi1; rk = M; + } else + lat = lon = azi = rk = Math::NaN(); + return; + } + +} // namespace GeographicLib diff --git a/common/local_libs/GeographicLib/src/GravityCircle.cpp b/common/local_libs/GeographicLib/src/GravityCircle.cpp new file mode 100644 index 0000000000..ab680e5bc2 --- /dev/null +++ b/common/local_libs/GeographicLib/src/GravityCircle.cpp @@ -0,0 +1,159 @@ +/** + * \file GravityCircle.cpp + * \brief Implementation for GeographicLib::GravityCircle class + * + * Copyright (c) Charles Karney (2011-2020) and licensed + * under the MIT/X11 License. For more information, see + * https://geographiclib.sourceforge.io/ + **********************************************************************/ + +#include +#include +#include +#include + +namespace GeographicLib { + + using namespace std; + + GravityCircle::GravityCircle(mask caps, real a, real f, real lat, real h, + real Z, real P, real cphi, real sphi, + real amodel, real GMmodel, + real dzonal0, real corrmult, + real gamma0, real gamma, real frot, + const CircularEngine& gravitational, + const CircularEngine& disturbing, + const CircularEngine& correction) + : _caps(caps) + , _a(a) + , _f(f) + , _lat(Math::LatFix(lat)) + , _h(h) + , _Z(Z) + , _Px(P) + , _invR(1 / hypot(_Px, _Z)) + , _cpsi(_Px * _invR) + , _spsi(_Z * _invR) + , _cphi(cphi) + , _sphi(sphi) + , _amodel(amodel) + , _GMmodel(GMmodel) + , _dzonal0(dzonal0) + , _corrmult(corrmult) + , _gamma0(gamma0) + , _gamma(gamma) + , _frot(frot) + , _gravitational(gravitational) + , _disturbing(disturbing) + , _correction(correction) + {} + + Math::real GravityCircle::Gravity(real lon, + real& gx, real& gy, real& gz) const { + real slam, clam, M[Geocentric::dim2_]; + Math::sincosd(lon, slam, clam); + real Wres = W(slam, clam, gx, gy, gz); + Geocentric::Rotation(_sphi, _cphi, slam, clam, M); + Geocentric::Unrotate(M, gx, gy, gz, gx, gy, gz); + return Wres; + } + + Math::real GravityCircle::Disturbance(real lon, real& deltax, real& deltay, + real& deltaz) const { + real slam, clam, M[Geocentric::dim2_]; + Math::sincosd(lon, slam, clam); + real Tres = InternalT(slam, clam, deltax, deltay, deltaz, true, true); + Geocentric::Rotation(_sphi, _cphi, slam, clam, M); + Geocentric::Unrotate(M, deltax, deltay, deltaz, deltax, deltay, deltaz); + return Tres; + } + + Math::real GravityCircle::GeoidHeight(real lon) const { + if ((_caps & GEOID_HEIGHT) != GEOID_HEIGHT) + return Math::NaN(); + real slam, clam, dummy; + Math::sincosd(lon, slam, clam); + real T = InternalT(slam, clam, dummy, dummy, dummy, false, false); + real correction = _corrmult * _correction(slam, clam); + return T/_gamma0 + correction; + } + + void GravityCircle::SphericalAnomaly(real lon, + real& Dg01, real& xi, real& eta) const { + if ((_caps & SPHERICAL_ANOMALY) != SPHERICAL_ANOMALY) { + Dg01 = xi = eta = Math::NaN(); + return; + } + real slam, clam; + Math::sincosd(lon, slam, clam); + real + deltax, deltay, deltaz, + T = InternalT(slam, clam, deltax, deltay, deltaz, true, false); + // Rotate cartesian into spherical coordinates + real MC[Geocentric::dim2_]; + Geocentric::Rotation(_spsi, _cpsi, slam, clam, MC); + Geocentric::Unrotate(MC, deltax, deltay, deltaz, deltax, deltay, deltaz); + // H+M, Eq 2-151c + Dg01 = - deltaz - 2 * T * _invR; + xi = -(deltay/_gamma) / Math::degree(); + eta = -(deltax/_gamma) / Math::degree(); + } + + Math::real GravityCircle::W(real slam, real clam, + real& gX, real& gY, real& gZ) const { + real Wres = V(slam, clam, gX, gY, gZ) + _frot * _Px / 2; + gX += _frot * clam; + gY += _frot * slam; + return Wres; + } + + Math::real GravityCircle::V(real slam, real clam, + real& GX, real& GY, real& GZ) const { + if ((_caps & GRAVITY) != GRAVITY) { + GX = GY = GZ = Math::NaN(); + return Math::NaN(); + } + real + Vres = _gravitational(slam, clam, GX, GY, GZ), + f = _GMmodel / _amodel; + Vres *= f; + GX *= f; + GY *= f; + GZ *= f; + return Vres; + } + + Math::real GravityCircle::InternalT(real slam, real clam, + real& deltaX, real& deltaY, real& deltaZ, + bool gradp, bool correct) const { + if (gradp) { + if ((_caps & DISTURBANCE) != DISTURBANCE) { + deltaX = deltaY = deltaZ = Math::NaN(); + return Math::NaN(); + } + } else { + if ((_caps & DISTURBING_POTENTIAL) != DISTURBING_POTENTIAL) + return Math::NaN(); + } + if (_dzonal0 == 0) + correct = false; + real T = (gradp + ? _disturbing(slam, clam, deltaX, deltaY, deltaZ) + : _disturbing(slam, clam)); + T = (T / _amodel - (correct ? _dzonal0 : 0) * _invR) * _GMmodel; + if (gradp) { + real f = _GMmodel / _amodel; + deltaX *= f; + deltaY *= f; + deltaZ *= f; + if (correct) { + real r3 = _GMmodel * _dzonal0 * _invR * _invR * _invR; + deltaX += _Px * clam * r3; + deltaY += _Px * slam * r3; + deltaZ += _Z * r3; + } + } + return T; + } + +} // namespace GeographicLib diff --git a/common/local_libs/GeographicLib/src/GravityModel.cpp b/common/local_libs/GeographicLib/src/GravityModel.cpp new file mode 100644 index 0000000000..ef412cec9e --- /dev/null +++ b/common/local_libs/GeographicLib/src/GravityModel.cpp @@ -0,0 +1,377 @@ +/** + * \file GravityModel.cpp + * \brief Implementation for GeographicLib::GravityModel class + * + * Copyright (c) Charles Karney (2011-2020) and licensed + * under the MIT/X11 License. For more information, see + * https://geographiclib.sourceforge.io/ + **********************************************************************/ + +#include +#include +#include +#include +#include +#include + +#if !defined(GEOGRAPHICLIB_DATA) +# if defined(_WIN32) +# define GEOGRAPHICLIB_DATA "C:/ProgramData/GeographicLib" +# else +# define GEOGRAPHICLIB_DATA "/usr/local/share/GeographicLib" +# endif +#endif + +#if !defined(GEOGRAPHICLIB_GRAVITY_DEFAULT_NAME) +# define GEOGRAPHICLIB_GRAVITY_DEFAULT_NAME "egm96" +#endif + +#if defined(_MSC_VER) +// Squelch warnings about unsafe use of getenv +# pragma warning (disable: 4996) +#endif + +namespace GeographicLib { + + using namespace std; + + GravityModel::GravityModel(const std::string& name, const std::string& path, + int Nmax, int Mmax) + : _name(name) + , _dir(path) + , _description("NONE") + , _date("UNKNOWN") + , _amodel(Math::NaN()) + , _GMmodel(Math::NaN()) + , _zeta0(0) + , _corrmult(1) + , _nmx(-1) + , _mmx(-1) + , _norm(SphericalHarmonic::FULL) + { + if (_dir.empty()) + _dir = DefaultGravityPath(); + bool truncate = Nmax >= 0 || Mmax >= 0; + if (truncate) { + if (Nmax >= 0 && Mmax < 0) Mmax = Nmax; + if (Nmax < 0) Nmax = numeric_limits::max(); + if (Mmax < 0) Mmax = numeric_limits::max(); + } + ReadMetadata(_name); + { + string coeff = _filename + ".cof"; + ifstream coeffstr(coeff.c_str(), ios::binary); + if (!coeffstr.good()) + throw GeographicErr("Error opening " + coeff); + char id[idlength_ + 1]; + coeffstr.read(id, idlength_); + if (!coeffstr.good()) + throw GeographicErr("No header in " + coeff); + id[idlength_] = '\0'; + if (_id != string(id)) + throw GeographicErr("ID mismatch: " + _id + " vs " + id); + int N, M; + if (truncate) { N = Nmax; M = Mmax; } + SphericalEngine::coeff::readcoeffs(coeffstr, N, M, _Cx, _Sx, truncate); + if (!(N >= 0 && M >= 0)) + throw GeographicErr("Degree and order must be at least 0"); + if (_Cx[0] != 0) + throw GeographicErr("The degree 0 term should be zero"); + _Cx[0] = 1; // Include the 1/r term in the sum + _gravitational = SphericalHarmonic(_Cx, _Sx, N, N, M, _amodel, _norm); + if (truncate) { N = Nmax; M = Mmax; } + SphericalEngine::coeff::readcoeffs(coeffstr, N, M, _CC, _CS, truncate); + if (N < 0) { + N = M = 0; + _CC.resize(1, real(0)); + } + _CC[0] += _zeta0 / _corrmult; + _correction = SphericalHarmonic(_CC, _CS, N, N, M, real(1), _norm); + int pos = int(coeffstr.tellg()); + coeffstr.seekg(0, ios::end); + if (pos != coeffstr.tellg()) + throw GeographicErr("Extra data in " + coeff); + } + int nmx = _gravitational.Coefficients().nmx(); + _nmx = max(nmx, _correction.Coefficients().nmx()); + _mmx = max(_gravitational.Coefficients().mmx(), + _correction.Coefficients().mmx()); + // Adjust the normalization of the normal potential to match the model. + real mult = _earth._GM / _GMmodel; + real amult = Math::sq(_earth._a / _amodel); + // The 0th term in _zonal should be is 1 + _dzonal0. Instead set it to 1 + // to give exact cancellation with the (0,0) term in the model and account + // for _dzonal0 separately. + _zonal.clear(); _zonal.push_back(1); + _dzonal0 = (_earth.MassConstant() - _GMmodel) / _GMmodel; + for (int n = 2; n <= nmx; n += 2) { + // Only include as many normal zonal terms as matter. Figuring the limit + // in this way works because the coefficients of the normal potential + // (which is smooth) decay much more rapidly that the corresponding + // coefficient of the model potential (which is bumpy). Typically this + // goes out to n = 18. + mult *= amult; + real + r = _Cx[n], // the model term + s = - mult * _earth.Jn(n) / sqrt(real(2 * n + 1)), // the normal term + t = r - s; // the difference + if (t == r) // the normal term is negligible + break; + _zonal.push_back(0); // index = n - 1; the odd terms are 0 + _zonal.push_back(s); + } + int nmx1 = int(_zonal.size()) - 1; + _disturbing = SphericalHarmonic1(_Cx, _Sx, + _gravitational.Coefficients().N(), + nmx, _gravitational.Coefficients().mmx(), + _zonal, + _zonal, // This is not accessed! + nmx1, nmx1, 0, + _amodel, + SphericalHarmonic1::normalization(_norm)); + } + + void GravityModel::ReadMetadata(const string& name) { + const char* spaces = " \t\n\v\f\r"; + _filename = _dir + "/" + name + ".egm"; + ifstream metastr(_filename.c_str()); + if (!metastr.good()) + throw GeographicErr("Cannot open " + _filename); + string line; + getline(metastr, line); + if (!(line.size() >= 6 && line.substr(0,5) == "EGMF-")) + throw GeographicErr(_filename + " does not contain EGMF-n signature"); + string::size_type n = line.find_first_of(spaces, 5); + if (n != string::npos) + n -= 5; + string version(line, 5, n); + if (version != "1") + throw GeographicErr("Unknown version in " + _filename + ": " + version); + string key, val; + real a = Math::NaN(), GM = a, omega = a, f = a, J2 = a; + while (getline(metastr, line)) { + if (!Utility::ParseLine(line, key, val)) + continue; + // Process key words + if (key == "Name") + _name = val; + else if (key == "Description") + _description = val; + else if (key == "ReleaseDate") + _date = val; + else if (key == "ModelRadius") + _amodel = Utility::val(val); + else if (key == "ModelMass") + _GMmodel = Utility::val(val); + else if (key == "AngularVelocity") + omega = Utility::val(val); + else if (key == "ReferenceRadius") + a = Utility::val(val); + else if (key == "ReferenceMass") + GM = Utility::val(val); + else if (key == "Flattening") + f = Utility::fract(val); + else if (key == "DynamicalFormFactor") + J2 = Utility::fract(val); + else if (key == "HeightOffset") + _zeta0 = Utility::fract(val); + else if (key == "CorrectionMultiplier") + _corrmult = Utility::fract(val); + else if (key == "Normalization") { + if (val == "FULL" || val == "Full" || val == "full") + _norm = SphericalHarmonic::FULL; + else if (val == "SCHMIDT" || val == "Schmidt" || val == "schmidt") + _norm = SphericalHarmonic::SCHMIDT; + else + throw GeographicErr("Unknown normalization " + val); + } else if (key == "ByteOrder") { + if (val == "Big" || val == "big") + throw GeographicErr("Only little-endian ordering is supported"); + else if (!(val == "Little" || val == "little")) + throw GeographicErr("Unknown byte ordering " + val); + } else if (key == "ID") + _id = val; + // else unrecognized keywords are skipped + } + // Check values + if (!(isfinite(_amodel) && _amodel > 0)) + throw GeographicErr("Model radius must be positive"); + if (!(isfinite(_GMmodel) && _GMmodel > 0)) + throw GeographicErr("Model mass constant must be positive"); + if (!(isfinite(_corrmult) && _corrmult > 0)) + throw GeographicErr("Correction multiplier must be positive"); + if (!(isfinite(_zeta0))) + throw GeographicErr("Height offset must be finite"); + if (int(_id.size()) != idlength_) + throw GeographicErr("Invalid ID"); + if (isfinite(f) && isfinite(J2)) + throw GeographicErr("Cannot specify both f and J2"); + _earth = NormalGravity(a, GM, omega, + isfinite(f) ? f : J2, isfinite(f)); + } + + Math::real GravityModel::InternalT(real X, real Y, real Z, + real& deltaX, real& deltaY, real& deltaZ, + bool gradp, bool correct) const { + // If correct, then produce the correct T = W - U. Otherwise, neglect the + // n = 0 term (which is proportial to the difference in the model and + // reference values of GM). + if (_dzonal0 == 0) + // No need to do the correction + correct = false; + real T, invR = correct ? 1 / hypot(hypot(X, Y), Z) : 1; + if (gradp) { + // initial values to suppress warnings + deltaX = deltaY = deltaZ = 0; + T = _disturbing(-1, X, Y, Z, deltaX, deltaY, deltaZ); + real f = _GMmodel / _amodel; + deltaX *= f; + deltaY *= f; + deltaZ *= f; + if (correct) { + invR = _GMmodel * _dzonal0 * invR * invR * invR; + deltaX += X * invR; + deltaY += Y * invR; + deltaZ += Z * invR; + } + } else + T = _disturbing(-1, X, Y, Z); + T = (T / _amodel - (correct ? _dzonal0 : 0) * invR) * _GMmodel; + return T; + } + + Math::real GravityModel::V(real X, real Y, real Z, + real& GX, real& GY, real& GZ) const { + real + Vres = _gravitational(X, Y, Z, GX, GY, GZ), + f = _GMmodel / _amodel; + Vres *= f; + GX *= f; + GY *= f; + GZ *= f; + return Vres; + } + + Math::real GravityModel::W(real X, real Y, real Z, + real& gX, real& gY, real& gZ) const { + real fX, fY, + Wres = V(X, Y, Z, gX, gY, gZ) + _earth.Phi(X, Y, fX, fY); + gX += fX; + gY += fY; + return Wres; + } + + void GravityModel::SphericalAnomaly(real lat, real lon, real h, + real& Dg01, real& xi, real& eta) const { + real X, Y, Z, M[Geocentric::dim2_]; + _earth.Earth().IntForward(lat, lon, h, X, Y, Z, M); + real + deltax, deltay, deltaz, + T = InternalT(X, Y, Z, deltax, deltay, deltaz, true, false), + clam = M[3], slam = -M[0], + P = hypot(X, Y), + R = hypot(P, Z), + // psi is geocentric latitude + cpsi = R != 0 ? P / R : M[7], + spsi = R != 0 ? Z / R : M[8]; + // Rotate cartesian into spherical coordinates + real MC[Geocentric::dim2_]; + Geocentric::Rotation(spsi, cpsi, slam, clam, MC); + Geocentric::Unrotate(MC, deltax, deltay, deltaz, deltax, deltay, deltaz); + // H+M, Eq 2-151c + Dg01 = - deltaz - 2 * T / R; + real gammaX, gammaY, gammaZ; + _earth.U(X, Y, Z, gammaX, gammaY, gammaZ); + real gamma = hypot( hypot(gammaX, gammaY), gammaZ); + xi = -(deltay/gamma) / Math::degree(); + eta = -(deltax/gamma) / Math::degree(); + } + + Math::real GravityModel::GeoidHeight(real lat, real lon) const + { + real X, Y, Z; + _earth.Earth().IntForward(lat, lon, 0, X, Y, Z, NULL); + real + gamma0 = _earth.SurfaceGravity(lat), + dummy, + T = InternalT(X, Y, Z, dummy, dummy, dummy, false, false), + invR = 1 / hypot(hypot(X, Y), Z), + correction = _corrmult * _correction(invR * X, invR * Y, invR * Z); + // _zeta0 has been included in _correction + return T/gamma0 + correction; + } + + Math::real GravityModel::Gravity(real lat, real lon, real h, + real& gx, real& gy, real& gz) const { + real X, Y, Z, M[Geocentric::dim2_]; + _earth.Earth().IntForward(lat, lon, h, X, Y, Z, M); + real Wres = W(X, Y, Z, gx, gy, gz); + Geocentric::Unrotate(M, gx, gy, gz, gx, gy, gz); + return Wres; + } + Math::real GravityModel::Disturbance(real lat, real lon, real h, + real& deltax, real& deltay, + real& deltaz) const { + real X, Y, Z, M[Geocentric::dim2_]; + _earth.Earth().IntForward(lat, lon, h, X, Y, Z, M); + real Tres = InternalT(X, Y, Z, deltax, deltay, deltaz, true, true); + Geocentric::Unrotate(M, deltax, deltay, deltaz, deltax, deltay, deltaz); + return Tres; + } + + GravityCircle GravityModel::Circle(real lat, real h, unsigned caps) const { + if (h != 0) + // Disallow invoking GeoidHeight unless h is zero. + caps &= ~(CAP_GAMMA0 | CAP_C); + real X, Y, Z, M[Geocentric::dim2_]; + _earth.Earth().IntForward(lat, 0, h, X, Y, Z, M); + // Y = 0, cphi = M[7], sphi = M[8]; + real + invR = 1 / hypot(X, Z), + gamma0 = (caps & CAP_GAMMA0 ?_earth.SurfaceGravity(lat) + : Math::NaN()), + fx, fy, fz, gamma; + if (caps & CAP_GAMMA) { + _earth.U(X, Y, Z, fx, fy, fz); // fy = 0 + gamma = hypot(fx, fz); + } else + gamma = Math::NaN(); + _earth.Phi(X, Y, fx, fy); + return GravityCircle(GravityCircle::mask(caps), + _earth._a, _earth._f, lat, h, Z, X, M[7], M[8], + _amodel, _GMmodel, _dzonal0, _corrmult, + gamma0, gamma, fx, + caps & CAP_G ? + _gravitational.Circle(X, Z, true) : + CircularEngine(), + // N.B. If CAP_DELTA is set then CAP_T should be too. + caps & CAP_T ? + _disturbing.Circle(-1, X, Z, (caps&CAP_DELTA) != 0) : + CircularEngine(), + caps & CAP_C ? + _correction.Circle(invR * X, invR * Z, false) : + CircularEngine()); + } + + string GravityModel::DefaultGravityPath() { + string path; + char* gravitypath = getenv("GEOGRAPHICLIB_GRAVITY_PATH"); + if (gravitypath) + path = string(gravitypath); + if (!path.empty()) + return path; + char* datapath = getenv("GEOGRAPHICLIB_DATA"); + if (datapath) + path = string(datapath); + return (!path.empty() ? path : string(GEOGRAPHICLIB_DATA)) + "/gravity"; + } + + string GravityModel::DefaultGravityName() { + string name; + char* gravityname = getenv("GEOGRAPHICLIB_GRAVITY_NAME"); + if (gravityname) + name = string(gravityname); + return !name.empty() ? name : string(GEOGRAPHICLIB_GRAVITY_DEFAULT_NAME); + } + +} // namespace GeographicLib diff --git a/common/local_libs/GeographicLib/src/LambertConformalConic.cpp b/common/local_libs/GeographicLib/src/LambertConformalConic.cpp new file mode 100644 index 0000000000..39f4326872 --- /dev/null +++ b/common/local_libs/GeographicLib/src/LambertConformalConic.cpp @@ -0,0 +1,455 @@ +/** + * \file LambertConformalConic.cpp + * \brief Implementation for GeographicLib::LambertConformalConic class + * + * Copyright (c) Charles Karney (2010-2020) and licensed + * under the MIT/X11 License. For more information, see + * https://geographiclib.sourceforge.io/ + **********************************************************************/ + +#include + +namespace GeographicLib { + + using namespace std; + + LambertConformalConic::LambertConformalConic(real a, real f, + real stdlat, real k0) + : eps_(numeric_limits::epsilon()) + , epsx_(Math::sq(eps_)) + , ahypover_(Math::digits() * log(real(numeric_limits::radix)) + 2) + , _a(a) + , _f(f) + , _fm(1 - _f) + , _e2(_f * (2 - _f)) + , _es((_f < 0 ? -1 : 1) * sqrt(abs(_e2))) + { + if (!(isfinite(_a) && _a > 0)) + throw GeographicErr("Equatorial radius is not positive"); + if (!(isfinite(_f) && _f < 1)) + throw GeographicErr("Polar semi-axis is not positive"); + if (!(isfinite(k0) && k0 > 0)) + throw GeographicErr("Scale is not positive"); + if (!(abs(stdlat) <= 90)) + throw GeographicErr("Standard latitude not in [-90d, 90d]"); + real sphi, cphi; + Math::sincosd(stdlat, sphi, cphi); + Init(sphi, cphi, sphi, cphi, k0); + } + + LambertConformalConic::LambertConformalConic(real a, real f, + real stdlat1, real stdlat2, + real k1) + : eps_(numeric_limits::epsilon()) + , epsx_(Math::sq(eps_)) + , ahypover_(Math::digits() * log(real(numeric_limits::radix)) + 2) + , _a(a) + , _f(f) + , _fm(1 - _f) + , _e2(_f * (2 - _f)) + , _es((_f < 0 ? -1 : 1) * sqrt(abs(_e2))) + { + if (!(isfinite(_a) && _a > 0)) + throw GeographicErr("Equatorial radius is not positive"); + if (!(isfinite(_f) && _f < 1)) + throw GeographicErr("Polar semi-axis is not positive"); + if (!(isfinite(k1) && k1 > 0)) + throw GeographicErr("Scale is not positive"); + if (!(abs(stdlat1) <= 90)) + throw GeographicErr("Standard latitude 1 not in [-90d, 90d]"); + if (!(abs(stdlat2) <= 90)) + throw GeographicErr("Standard latitude 2 not in [-90d, 90d]"); + real sphi1, cphi1, sphi2, cphi2; + Math::sincosd(stdlat1, sphi1, cphi1); + Math::sincosd(stdlat2, sphi2, cphi2); + Init(sphi1, cphi1, sphi2, cphi2, k1); + } + + LambertConformalConic::LambertConformalConic(real a, real f, + real sinlat1, real coslat1, + real sinlat2, real coslat2, + real k1) + : eps_(numeric_limits::epsilon()) + , epsx_(Math::sq(eps_)) + , ahypover_(Math::digits() * log(real(numeric_limits::radix)) + 2) + , _a(a) + , _f(f) + , _fm(1 - _f) + , _e2(_f * (2 - _f)) + , _es((_f < 0 ? -1 : 1) * sqrt(abs(_e2))) + { + if (!(isfinite(_a) && _a > 0)) + throw GeographicErr("Equatorial radius is not positive"); + if (!(isfinite(_f) && _f < 1)) + throw GeographicErr("Polar semi-axis is not positive"); + if (!(isfinite(k1) && k1 > 0)) + throw GeographicErr("Scale is not positive"); + if (!(coslat1 >= 0)) + throw GeographicErr("Standard latitude 1 not in [-90d, 90d]"); + if (!(coslat2 >= 0)) + throw GeographicErr("Standard latitude 2 not in [-90d, 90d]"); + if (!(abs(sinlat1) <= 1 && coslat1 <= 1) || (coslat1 == 0 && sinlat1 == 0)) + throw GeographicErr("Bad sine/cosine of standard latitude 1"); + if (!(abs(sinlat2) <= 1 && coslat2 <= 1) || (coslat2 == 0 && sinlat2 == 0)) + throw GeographicErr("Bad sine/cosine of standard latitude 2"); + if (coslat1 == 0 || coslat2 == 0) + if (!(coslat1 == coslat2 && sinlat1 == sinlat2)) + throw GeographicErr + ("Standard latitudes must be equal is either is a pole"); + Init(sinlat1, coslat1, sinlat2, coslat2, k1); + } + + void LambertConformalConic::Init(real sphi1, real cphi1, + real sphi2, real cphi2, real k1) { + { + real r; + r = hypot(sphi1, cphi1); + sphi1 /= r; cphi1 /= r; + r = hypot(sphi2, cphi2); + sphi2 /= r; cphi2 /= r; + } + bool polar = (cphi1 == 0); + cphi1 = max(epsx_, cphi1); // Avoid singularities at poles + cphi2 = max(epsx_, cphi2); + // Determine hemisphere of tangent latitude + _sign = sphi1 + sphi2 >= 0 ? 1 : -1; + // Internally work with tangent latitude positive + sphi1 *= _sign; sphi2 *= _sign; + if (sphi1 > sphi2) { + swap(sphi1, sphi2); swap(cphi1, cphi2); // Make phi1 < phi2 + } + real + tphi1 = sphi1/cphi1, tphi2 = sphi2/cphi2, tphi0; + // + // Snyder: 15-8: n = (log(m1) - log(m2))/(log(t1)-log(t2)) + // + // m = cos(bet) = 1/sec(bet) = 1/sqrt(1+tan(bet)^2) + // bet = parametric lat, tan(bet) = (1-f)*tan(phi) + // + // t = tan(pi/4-chi/2) = 1/(sec(chi) + tan(chi)) = sec(chi) - tan(chi) + // log(t) = -asinh(tan(chi)) = -psi + // chi = conformal lat + // tan(chi) = tan(phi)*cosh(xi) - sinh(xi)*sec(phi) + // xi = eatanhe(sin(phi)), eatanhe(x) = e * atanh(e*x) + // + // n = (log(sec(bet2))-log(sec(bet1)))/(asinh(tan(chi2))-asinh(tan(chi1))) + // + // Let log(sec(bet)) = b(tphi), asinh(tan(chi)) = c(tphi) + // Then n = Db(tphi2, tphi1)/Dc(tphi2, tphi1) + // In limit tphi2 -> tphi1, n -> sphi1 + // + real + tbet1 = _fm * tphi1, scbet1 = hyp(tbet1), + tbet2 = _fm * tphi2, scbet2 = hyp(tbet2); + real + scphi1 = 1/cphi1, + xi1 = Math::eatanhe(sphi1, _es), shxi1 = sinh(xi1), chxi1 = hyp(shxi1), + tchi1 = chxi1 * tphi1 - shxi1 * scphi1, scchi1 = hyp(tchi1), + scphi2 = 1/cphi2, + xi2 = Math::eatanhe(sphi2, _es), shxi2 = sinh(xi2), chxi2 = hyp(shxi2), + tchi2 = chxi2 * tphi2 - shxi2 * scphi2, scchi2 = hyp(tchi2), + psi1 = asinh(tchi1); + if (tphi2 - tphi1 != 0) { + // Db(tphi2, tphi1) + real num = Dlog1p(Math::sq(tbet2)/(1 + scbet2), + Math::sq(tbet1)/(1 + scbet1)) + * Dhyp(tbet2, tbet1, scbet2, scbet1) * _fm; + // Dc(tphi2, tphi1) + real den = Dasinh(tphi2, tphi1, scphi2, scphi1) + - Deatanhe(sphi2, sphi1) * Dsn(tphi2, tphi1, sphi2, sphi1); + _n = num/den; + + if (_n < 0.25) + _nc = sqrt((1 - _n) * (1 + _n)); + else { + // Compute nc = cos(phi0) = sqrt((1 - n) * (1 + n)), evaluating 1 - n + // carefully. First write + // + // Dc(tphi2, tphi1) * (tphi2 - tphi1) + // = log(tchi2 + scchi2) - log(tchi1 + scchi1) + // + // then den * (1 - n) = + // (log((tchi2 + scchi2)/(2*scbet2)) - + // log((tchi1 + scchi1)/(2*scbet1))) / (tphi2 - tphi1) + // = Dlog1p(a2, a1) * (tchi2+scchi2 + tchi1+scchi1)/(4*scbet1*scbet2) + // * fm * Q + // + // where + // a1 = ( (tchi1 - scbet1) + (scchi1 - scbet1) ) / (2 * scbet1) + // Q = ((scbet2 + scbet1)/fm)/((scchi2 + scchi1)/D(tchi2, tchi1)) + // - (tbet2 + tbet1)/(scbet2 + scbet1) + real t; + { + real + // s1 = (scbet1 - scchi1) * (scbet1 + scchi1) + s1 = (tphi1 * (2 * shxi1 * chxi1 * scphi1 - _e2 * tphi1) - + Math::sq(shxi1) * (1 + 2 * Math::sq(tphi1))), + s2 = (tphi2 * (2 * shxi2 * chxi2 * scphi2 - _e2 * tphi2) - + Math::sq(shxi2) * (1 + 2 * Math::sq(tphi2))), + // t1 = scbet1 - tchi1 + t1 = tchi1 < 0 ? scbet1 - tchi1 : (s1 + 1)/(scbet1 + tchi1), + t2 = tchi2 < 0 ? scbet2 - tchi2 : (s2 + 1)/(scbet2 + tchi2), + a2 = -(s2 / (scbet2 + scchi2) + t2) / (2 * scbet2), + a1 = -(s1 / (scbet1 + scchi1) + t1) / (2 * scbet1); + t = Dlog1p(a2, a1) / den; + } + // multiply by (tchi2 + scchi2 + tchi1 + scchi1)/(4*scbet1*scbet2) * fm + t *= ( ( (tchi2 >= 0 ? scchi2 + tchi2 : 1/(scchi2 - tchi2)) + + (tchi1 >= 0 ? scchi1 + tchi1 : 1/(scchi1 - tchi1)) ) / + (4 * scbet1 * scbet2) ) * _fm; + + // Rewrite + // Q = (1 - (tbet2 + tbet1)/(scbet2 + scbet1)) - + // (1 - ((scbet2 + scbet1)/fm)/((scchi2 + scchi1)/D(tchi2, tchi1))) + // = tbm - tam + // where + real tbm = ( ((tbet1 > 0 ? 1/(scbet1+tbet1) : scbet1 - tbet1) + + (tbet2 > 0 ? 1/(scbet2+tbet2) : scbet2 - tbet2)) / + (scbet1+scbet2) ); + + // tam = (1 - ((scbet2+scbet1)/fm)/((scchi2+scchi1)/D(tchi2, tchi1))) + // + // Let + // (scbet2 + scbet1)/fm = scphi2 + scphi1 + dbet + // (scchi2 + scchi1)/D(tchi2, tchi1) = scphi2 + scphi1 + dchi + // then + // tam = D(tchi2, tchi1) * (dchi - dbet) / (scchi1 + scchi2) + real + // D(tchi2, tchi1) + dtchi = den / Dasinh(tchi2, tchi1, scchi2, scchi1), + // (scbet2 + scbet1)/fm - (scphi2 + scphi1) + dbet = (_e2/_fm) * ( 1 / (scbet2 + _fm * scphi2) + + 1 / (scbet1 + _fm * scphi1) ); + + // dchi = (scchi2 + scchi1)/D(tchi2, tchi1) - (scphi2 + scphi1) + // Let + // tzet = chxiZ * tphi - shxiZ * scphi + // tchi = tzet + nu + // scchi = sczet + mu + // where + // xiZ = eatanhe(1), shxiZ = sinh(xiZ), chxiZ = cosh(xiZ) + // nu = scphi * (shxiZ - shxi) - tphi * (chxiZ - chxi) + // mu = - scphi * (chxiZ - chxi) + tphi * (shxiZ - shxi) + // then + // dchi = ((mu2 + mu1) - D(nu2, nu1) * (scphi2 + scphi1)) / + // D(tchi2, tchi1) + real + xiZ = Math::eatanhe(real(1), _es), + shxiZ = sinh(xiZ), chxiZ = hyp(shxiZ), + // These are differences not divided differences + // dxiZ1 = xiZ - xi1; dshxiZ1 = shxiZ - shxi; dchxiZ1 = chxiZ - chxi + dxiZ1 = Deatanhe(real(1), sphi1)/(scphi1*(tphi1+scphi1)), + dxiZ2 = Deatanhe(real(1), sphi2)/(scphi2*(tphi2+scphi2)), + dshxiZ1 = Dsinh(xiZ, xi1, shxiZ, shxi1, chxiZ, chxi1) * dxiZ1, + dshxiZ2 = Dsinh(xiZ, xi2, shxiZ, shxi2, chxiZ, chxi2) * dxiZ2, + dchxiZ1 = Dhyp(shxiZ, shxi1, chxiZ, chxi1) * dshxiZ1, + dchxiZ2 = Dhyp(shxiZ, shxi2, chxiZ, chxi2) * dshxiZ2, + // mu1 + mu2 + amu12 = (- scphi1 * dchxiZ1 + tphi1 * dshxiZ1 + - scphi2 * dchxiZ2 + tphi2 * dshxiZ2), + // D(xi2, xi1) + dxi = Deatanhe(sphi1, sphi2) * Dsn(tphi2, tphi1, sphi2, sphi1), + // D(nu2, nu1) + dnu12 = + ( (_f * 4 * scphi2 * dshxiZ2 > _f * scphi1 * dshxiZ1 ? + // Use divided differences + (dshxiZ1 + dshxiZ2)/2 * Dhyp(tphi1, tphi2, scphi1, scphi2) + - ( (scphi1 + scphi2)/2 + * Dsinh(xi1, xi2, shxi1, shxi2, chxi1, chxi2) * dxi ) : + // Use ratio of differences + (scphi2 * dshxiZ2 - scphi1 * dshxiZ1)/(tphi2 - tphi1)) + + ( (tphi1 + tphi2)/2 * Dhyp(shxi1, shxi2, chxi1, chxi2) + * Dsinh(xi1, xi2, shxi1, shxi2, chxi1, chxi2) * dxi ) + - (dchxiZ1 + dchxiZ2)/2 ), + // dtchi * dchi + dchia = (amu12 - dnu12 * (scphi2 + scphi1)), + tam = (dchia - dtchi * dbet) / (scchi1 + scchi2); + t *= tbm - tam; + _nc = sqrt(max(real(0), t) * (1 + _n)); + } + { + real r = hypot(_n, _nc); + _n /= r; + _nc /= r; + } + tphi0 = _n / _nc; + } else { + tphi0 = tphi1; + _nc = 1/hyp(tphi0); + _n = tphi0 * _nc; + if (polar) + _nc = 0; + } + + _scbet0 = hyp(_fm * tphi0); + real shxi0 = sinh(Math::eatanhe(_n, _es)); + _tchi0 = tphi0 * hyp(shxi0) - shxi0 * hyp(tphi0); _scchi0 = hyp(_tchi0); + _psi0 = asinh(_tchi0); + + _lat0 = atan(_sign * tphi0) / Math::degree(); + _t0nm1 = expm1(- _n * _psi0); // Snyder's t0^n - 1 + // a * k1 * m1/t1^n = a * k1 * m2/t2^n = a * k1 * n * (Snyder's F) + // = a * k1 / (scbet1 * exp(-n * psi1)) + _scale = _a * k1 / scbet1 * + // exp(n * psi1) = exp(- (1 - n) * psi1) * exp(psi1) + // with (1-n) = nc^2/(1+n) and exp(-psi1) = scchi1 + tchi1 + exp( - (Math::sq(_nc)/(1 + _n)) * psi1 ) + * (tchi1 >= 0 ? scchi1 + tchi1 : 1 / (scchi1 - tchi1)); + // Scale at phi0 = k0 = k1 * (scbet0*exp(-n*psi0))/(scbet1*exp(-n*psi1)) + // = k1 * scbet0/scbet1 * exp(n * (psi1 - psi0)) + // psi1 - psi0 = Dasinh(tchi1, tchi0) * (tchi1 - tchi0) + _k0 = k1 * (_scbet0/scbet1) * + exp( - (Math::sq(_nc)/(1 + _n)) * + Dasinh(tchi1, _tchi0, scchi1, _scchi0) * (tchi1 - _tchi0)) + * (tchi1 >= 0 ? scchi1 + tchi1 : 1 / (scchi1 - tchi1)) / + (_scchi0 + _tchi0); + _nrho0 = polar ? 0 : _a * _k0 / _scbet0; + { + // Figure _drhomax using code at beginning of Forward with lat = -90 + real + sphi = -1, cphi = epsx_, + tphi = sphi/cphi, + scphi = 1/cphi, shxi = sinh(Math::eatanhe(sphi, _es)), + tchi = hyp(shxi) * tphi - shxi * scphi, scchi = hyp(tchi), + psi = asinh(tchi), + dpsi = Dasinh(tchi, _tchi0, scchi, _scchi0) * (tchi - _tchi0); + _drhomax = - _scale * (2 * _nc < 1 && dpsi != 0 ? + (exp(Math::sq(_nc)/(1 + _n) * psi ) * + (tchi > 0 ? 1/(scchi + tchi) : (scchi - tchi)) + - (_t0nm1 + 1))/(-_n) : + Dexp(-_n * psi, -_n * _psi0) * dpsi); + } + } + + const LambertConformalConic& LambertConformalConic::Mercator() { + static const LambertConformalConic mercator(Constants::WGS84_a(), + Constants::WGS84_f(), + real(0), real(1)); + return mercator; + } + + void LambertConformalConic::Forward(real lon0, real lat, real lon, + real& x, real& y, + real& gamma, real& k) const { + lon = Math::AngDiff(lon0, lon); + // From Snyder, we have + // + // theta = n * lambda + // x = rho * sin(theta) + // = (nrho0 + n * drho) * sin(theta)/n + // y = rho0 - rho * cos(theta) + // = nrho0 * (1-cos(theta))/n - drho * cos(theta) + // + // where nrho0 = n * rho0, drho = rho - rho0 + // and drho is evaluated with divided differences + real sphi, cphi; + Math::sincosd(Math::LatFix(lat) * _sign, sphi, cphi); + cphi = max(epsx_, cphi); + real + lam = lon * Math::degree(), + tphi = sphi/cphi, scbet = hyp(_fm * tphi), + scphi = 1/cphi, shxi = sinh(Math::eatanhe(sphi, _es)), + tchi = hyp(shxi) * tphi - shxi * scphi, scchi = hyp(tchi), + psi = asinh(tchi), + theta = _n * lam, stheta = sin(theta), ctheta = cos(theta), + dpsi = Dasinh(tchi, _tchi0, scchi, _scchi0) * (tchi - _tchi0), + drho = - _scale * (2 * _nc < 1 && dpsi != 0 ? + (exp(Math::sq(_nc)/(1 + _n) * psi ) * + (tchi > 0 ? 1/(scchi + tchi) : (scchi - tchi)) + - (_t0nm1 + 1))/(-_n) : + Dexp(-_n * psi, -_n * _psi0) * dpsi); + x = (_nrho0 + _n * drho) * (_n != 0 ? stheta / _n : lam); + y = _nrho0 * + (_n != 0 ? + (ctheta < 0 ? 1 - ctheta : Math::sq(stheta)/(1 + ctheta)) / _n : 0) + - drho * ctheta; + k = _k0 * (scbet/_scbet0) / + (exp( - (Math::sq(_nc)/(1 + _n)) * dpsi ) + * (tchi >= 0 ? scchi + tchi : 1 / (scchi - tchi)) / (_scchi0 + _tchi0)); + y *= _sign; + gamma = _sign * theta / Math::degree(); + } + + void LambertConformalConic::Reverse(real lon0, real x, real y, + real& lat, real& lon, + real& gamma, real& k) const { + // From Snyder, we have + // + // x = rho * sin(theta) + // rho0 - y = rho * cos(theta) + // + // rho = hypot(x, rho0 - y) + // drho = (n*x^2 - 2*y*nrho0 + n*y^2)/(hypot(n*x, nrho0-n*y) + nrho0) + // theta = atan2(n*x, nrho0-n*y) + // + // From drho, obtain t^n-1 + // psi = -log(t), so + // dpsi = - Dlog1p(t^n-1, t0^n-1) * drho / scale + y *= _sign; + real + // Guard against 0 * inf in computation of ny + nx = _n * x, ny = _n != 0 ? _n * y : 0, y1 = _nrho0 - ny, + den = hypot(nx, y1) + _nrho0, // 0 implies origin with polar aspect + // isfinite test is to avoid inf/inf + drho = ((den != 0 && isfinite(den)) + ? (x*nx + y * (ny - 2*_nrho0)) / den + : den); + drho = min(drho, _drhomax); + if (_n == 0) + drho = max(drho, -_drhomax); + real + tnm1 = _t0nm1 + _n * drho/_scale, + dpsi = (den == 0 ? 0 : + (tnm1 + 1 != 0 ? - Dlog1p(tnm1, _t0nm1) * drho / _scale : + ahypover_)); + real tchi; + if (2 * _n <= 1) { + // tchi = sinh(psi) + real + psi = _psi0 + dpsi, tchia = sinh(psi), scchi = hyp(tchia), + dtchi = Dsinh(psi, _psi0, tchia, _tchi0, scchi, _scchi0) * dpsi; + tchi = _tchi0 + dtchi; // Update tchi using divided difference + } else { + // tchi = sinh(-1/n * log(tn)) + // = sinh((1-1/n) * log(tn) - log(tn)) + // = + sinh((1-1/n) * log(tn)) * cosh(log(tn)) + // - cosh((1-1/n) * log(tn)) * sinh(log(tn)) + // (1-1/n) = - nc^2/(n*(1+n)) + // cosh(log(tn)) = (tn + 1/tn)/2; sinh(log(tn)) = (tn - 1/tn)/2 + real + tn = tnm1 + 1 == 0 ? epsx_ : tnm1 + 1, + sh = sinh( -Math::sq(_nc)/(_n * (1 + _n)) * + (2 * tn > 1 ? log1p(tnm1) : log(tn)) ); + tchi = sh * (tn + 1/tn)/2 - hyp(sh) * (tnm1 * (tn + 1)/tn)/2; + } + + // log(t) = -asinh(tan(chi)) = -psi + gamma = atan2(nx, y1); + real + tphi = Math::tauf(tchi, _es), + scbet = hyp(_fm * tphi), scchi = hyp(tchi), + lam = _n != 0 ? gamma / _n : x / y1; + lat = Math::atand(_sign * tphi); + lon = lam / Math::degree(); + lon = Math::AngNormalize(lon + Math::AngNormalize(lon0)); + k = _k0 * (scbet/_scbet0) / + (exp(_nc != 0 ? - (Math::sq(_nc)/(1 + _n)) * dpsi : 0) + * (tchi >= 0 ? scchi + tchi : 1 / (scchi - tchi)) / (_scchi0 + _tchi0)); + gamma /= _sign * Math::degree(); + } + + void LambertConformalConic::SetScale(real lat, real k) { + if (!(isfinite(k) && k > 0)) + throw GeographicErr("Scale is not positive"); + if (!(abs(lat) <= 90)) + throw GeographicErr("Latitude for SetScale not in [-90d, 90d]"); + if (abs(lat) == 90 && !(_nc == 0 && lat * _n > 0)) + throw GeographicErr("Incompatible polar latitude in SetScale"); + real x, y, gamma, kold; + Forward(0, lat, 0, x, y, gamma, kold); + k /= kold; + _scale *= k; + _k0 *= k; + } + +} // namespace GeographicLib diff --git a/common/local_libs/GeographicLib/src/LocalCartesian.cpp b/common/local_libs/GeographicLib/src/LocalCartesian.cpp new file mode 100644 index 0000000000..b29156d1f1 --- /dev/null +++ b/common/local_libs/GeographicLib/src/LocalCartesian.cpp @@ -0,0 +1,62 @@ +/** + * \file LocalCartesian.cpp + * \brief Implementation for GeographicLib::LocalCartesian class + * + * Copyright (c) Charles Karney (2008-2015) and licensed + * under the MIT/X11 License. For more information, see + * https://geographiclib.sourceforge.io/ + **********************************************************************/ + +#include + +namespace GeographicLib { + + using namespace std; + + void LocalCartesian::Reset(real lat0, real lon0, real h0) { + _lat0 = Math::LatFix(lat0); + _lon0 = Math::AngNormalize(lon0); + _h0 = h0; + _earth.Forward(_lat0, _lon0, _h0, _x0, _y0, _z0); + real sphi, cphi, slam, clam; + Math::sincosd(_lat0, sphi, cphi); + Math::sincosd(_lon0, slam, clam); + Geocentric::Rotation(sphi, cphi, slam, clam, _r); + } + + void LocalCartesian::MatrixMultiply(real M[dim2_]) const { + // M = r' . M + real t[dim2_]; + copy(M, M + dim2_, t); + for (size_t i = 0; i < dim2_; ++i) { + size_t row = i / dim_, col = i % dim_; + M[i] = _r[row] * t[col] + _r[row+3] * t[col+3] + _r[row+6] * t[col+6]; + } + } + + void LocalCartesian::IntForward(real lat, real lon, real h, + real& x, real& y, real& z, + real M[dim2_]) const { + real xc, yc, zc; + _earth.IntForward(lat, lon, h, xc, yc, zc, M); + xc -= _x0; yc -= _y0; zc -= _z0; + x = _r[0] * xc + _r[3] * yc + _r[6] * zc; + y = _r[1] * xc + _r[4] * yc + _r[7] * zc; + z = _r[2] * xc + _r[5] * yc + _r[8] * zc; + if (M) + MatrixMultiply(M); + } + + void LocalCartesian::IntReverse(real x, real y, real z, + real& lat, real& lon, real& h, + real M[dim2_]) const { + real + xc = _x0 + _r[0] * x + _r[1] * y + _r[2] * z, + yc = _y0 + _r[3] * x + _r[4] * y + _r[5] * z, + zc = _z0 + _r[6] * x + _r[7] * y + _r[8] * z; + _earth.IntReverse(xc, yc, zc, lat, lon, h, M); + if (M) + MatrixMultiply(M); + } + +} // namespace GeographicLib diff --git a/common/local_libs/GeographicLib/src/MGRS.cpp b/common/local_libs/GeographicLib/src/MGRS.cpp new file mode 100644 index 0000000000..af86b8b086 --- /dev/null +++ b/common/local_libs/GeographicLib/src/MGRS.cpp @@ -0,0 +1,464 @@ +/** + * \file MGRS.cpp + * \brief Implementation for GeographicLib::MGRS class + * + * Copyright (c) Charles Karney (2008-2020) and licensed + * under the MIT/X11 License. For more information, see + * https://geographiclib.sourceforge.io/ + **********************************************************************/ + +#include +#include + +namespace GeographicLib { + + using namespace std; + + const char* const MGRS::hemispheres_ = "SN"; + const char* const MGRS::utmcols_[] = { "ABCDEFGH", "JKLMNPQR", "STUVWXYZ" }; + const char* const MGRS::utmrow_ = "ABCDEFGHJKLMNPQRSTUV"; + const char* const MGRS::upscols_[] = + { "JKLPQRSTUXYZ", "ABCFGHJKLPQR", "RSTUXYZ", "ABCFGHJ" }; + const char* const MGRS::upsrows_[] = + { "ABCDEFGHJKLMNPQRSTUVWXYZ", "ABCDEFGHJKLMNP" }; + const char* const MGRS::latband_ = "CDEFGHJKLMNPQRSTUVWX"; + const char* const MGRS::upsband_ = "ABYZ"; + const char* const MGRS::digits_ = "0123456789"; + + const int MGRS::mineasting_[] = + { minupsSind_, minupsNind_, minutmcol_, minutmcol_ }; + const int MGRS::maxeasting_[] = + { maxupsSind_, maxupsNind_, maxutmcol_, maxutmcol_ }; + const int MGRS::minnorthing_[] = + { minupsSind_, minupsNind_, + minutmSrow_, minutmSrow_ - (maxutmSrow_ - minutmNrow_) }; + const int MGRS::maxnorthing_[] = + { maxupsSind_, maxupsNind_, + maxutmNrow_ + (maxutmSrow_ - minutmNrow_), maxutmNrow_ }; + + void MGRS::Forward(int zone, bool northp, real x, real y, real lat, + int prec, std::string& mgrs) { + using std::isnan; // Needed for Centos 7, ubuntu 14 + // The smallest angle s.t., 90 - angeps() < 90 (approx 50e-12 arcsec) + // 7 = ceil(log_2(90)) + static const real angeps = ldexp(real(1), -(Math::digits() - 7)); + if (zone == UTMUPS::INVALID || + isnan(x) || isnan(y) || isnan(lat)) { + mgrs = "INVALID"; + return; + } + bool utmp = zone != 0; + CheckCoords(utmp, northp, x, y); + if (!(zone >= UTMUPS::MINZONE && zone <= UTMUPS::MAXZONE)) + throw GeographicErr("Zone " + Utility::str(zone) + " not in [0,60]"); + if (!(prec >= -1 && prec <= maxprec_)) + throw GeographicErr("MGRS precision " + Utility::str(prec) + + " not in [-1, " + + Utility::str(int(maxprec_)) + "]"); + // Fixed char array for accumulating string. Allow space for zone, 3 block + // letters, easting + northing. Don't need to allow for terminating null. + char mgrs1[2 + 3 + 2 * maxprec_]; + int + zone1 = zone - 1, + z = utmp ? 2 : 0, + mlen = z + 3 + 2 * prec; + if (utmp) { + mgrs1[0] = digits_[ zone / base_ ]; + mgrs1[1] = digits_[ zone % base_ ]; + // This isn't necessary...! Keep y non-neg + // if (!northp) y -= maxutmSrow_ * tile_; + } + // The C++ standard mandates 64 bits for long long. But + // check, to make sure. + static_assert(numeric_limits::digits >= 44, + "long long not wide enough to store 10e12"); + long long + ix = (long long)(floor(x * mult_)), + iy = (long long)(floor(y * mult_)), + m = (long long)(mult_) * (long long)(tile_); + int xh = int(ix / m), yh = int(iy / m); + if (utmp) { + int + // Correct fuzziness in latitude near equator + iband = abs(lat) > angeps ? LatitudeBand(lat) : (northp ? 0 : -1), + icol = xh - minutmcol_, + irow = UTMRow(iband, icol, yh % utmrowperiod_); + if (irow != yh - (northp ? minutmNrow_ : maxutmSrow_)) + throw GeographicErr("Latitude " + Utility::str(lat) + + " is inconsistent with UTM coordinates"); + mgrs1[z++] = latband_[10 + iband]; + mgrs1[z++] = utmcols_[zone1 % 3][icol]; + mgrs1[z++] = utmrow_[(yh + (zone1 & 1 ? utmevenrowshift_ : 0)) + % utmrowperiod_]; + } else { + bool eastp = xh >= upseasting_; + int iband = (northp ? 2 : 0) + (eastp ? 1 : 0); + mgrs1[z++] = upsband_[iband]; + mgrs1[z++] = upscols_[iband][xh - (eastp ? upseasting_ : + (northp ? minupsNind_ : + minupsSind_))]; + mgrs1[z++] = upsrows_[northp][yh - (northp ? minupsNind_ : minupsSind_)]; + } + if (prec > 0) { + ix -= m * xh; iy -= m * yh; + long long d = (long long)(pow(real(base_), maxprec_ - prec)); + ix /= d; iy /= d; + for (int c = prec; c--;) { + mgrs1[z + c ] = digits_[ix % base_]; ix /= base_; + mgrs1[z + c + prec] = digits_[iy % base_]; iy /= base_; + } + } + mgrs.resize(mlen); + copy(mgrs1, mgrs1 + mlen, mgrs.begin()); + } + + void MGRS::Forward(int zone, bool northp, real x, real y, + int prec, std::string& mgrs) { + real lat, lon; + if (zone > 0) { + // Does a rough estimate for latitude determine the latitude band? + real ys = northp ? y : y - utmNshift_; + // A cheap calculation of the latitude which results in an "allowed" + // latitude band would be + // lat = ApproxLatitudeBand(ys) * 8 + 4; + // + // Here we do a more careful job using the band letter corresponding to + // the actual latitude. + ys /= tile_; + if (abs(ys) < 1) + lat = real(0.9) * ys; // accurate enough estimate near equator + else { + real + // The poleward bound is a fit from above of lat(x,y) + // for x = 500km and y = [0km, 950km] + latp = real(0.901) * ys + (ys > 0 ? 1 : -1) * real(0.135), + // The equatorward bound is a fit from below of lat(x,y) + // for x = 900km and y = [0km, 950km] + late = real(0.902) * ys * (1 - real(1.85e-6) * ys * ys); + if (LatitudeBand(latp) == LatitudeBand(late)) + lat = latp; + else + // bounds straddle a band boundary so need to compute lat accurately + UTMUPS::Reverse(zone, northp, x, y, lat, lon); + } + } else + // Latitude isn't needed for UPS specs or for INVALID + lat = 0; + Forward(zone, northp, x, y, lat, prec, mgrs); + } + + void MGRS::Reverse(const string& mgrs, + int& zone, bool& northp, real& x, real& y, + int& prec, bool centerp) { + int + p = 0, + len = int(mgrs.length()); + if (len >= 3 && + toupper(mgrs[0]) == 'I' && + toupper(mgrs[1]) == 'N' && + toupper(mgrs[2]) == 'V') { + zone = UTMUPS::INVALID; + northp = false; + x = y = Math::NaN(); + prec = -2; + return; + } + int zone1 = 0; + while (p < len) { + int i = Utility::lookup(digits_, mgrs[p]); + if (i < 0) + break; + zone1 = 10 * zone1 + i; + ++p; + } + if (p > 0 && !(zone1 >= UTMUPS::MINUTMZONE && zone1 <= UTMUPS::MAXUTMZONE)) + throw GeographicErr("Zone " + Utility::str(zone1) + " not in [1,60]"); + if (p > 2) + throw GeographicErr("More than 2 digits at start of MGRS " + + mgrs.substr(0, p)); + if (len - p < 1) + throw GeographicErr("MGRS string too short " + mgrs); + bool utmp = zone1 != UTMUPS::UPS; + int zonem1 = zone1 - 1; + const char* band = utmp ? latband_ : upsband_; + int iband = Utility::lookup(band, mgrs[p++]); + if (iband < 0) + throw GeographicErr("Band letter " + Utility::str(mgrs[p-1]) + " not in " + + (utmp ? "UTM" : "UPS") + " set " + band); + bool northp1 = iband >= (utmp ? 10 : 2); + if (p == len) { // Grid zone only (ignore centerp) + // Approx length of a degree of meridian arc in units of tile. + real deg = real(utmNshift_) / (90 * tile_); + zone = zone1; + northp = northp1; + if (utmp) { + // Pick central meridian except for 31V + x = ((zone == 31 && iband == 17) ? 4 : 5) * tile_; + // Pick center of 8deg latitude bands + y = floor(8 * (iband - real(9.5)) * deg + real(0.5)) * tile_ + + (northp ? 0 : utmNshift_); + } else { + // Pick point at lat 86N or 86S + x = ((iband & 1 ? 1 : -1) * floor(4 * deg + real(0.5)) + + upseasting_) * tile_; + // Pick point at lon 90E or 90W. + y = upseasting_ * tile_; + } + prec = -1; + return; + } else if (len - p < 2) + throw GeographicErr("Missing row letter in " + mgrs); + const char* col = utmp ? utmcols_[zonem1 % 3] : upscols_[iband]; + const char* row = utmp ? utmrow_ : upsrows_[northp1]; + int icol = Utility::lookup(col, mgrs[p++]); + if (icol < 0) + throw GeographicErr("Column letter " + Utility::str(mgrs[p-1]) + + " not in " + + (utmp ? "zone " + mgrs.substr(0, p-2) : + "UPS band " + Utility::str(mgrs[p-2])) + + " set " + col ); + int irow = Utility::lookup(row, mgrs[p++]); + if (irow < 0) + throw GeographicErr("Row letter " + Utility::str(mgrs[p-1]) + " not in " + + (utmp ? "UTM" : + "UPS " + Utility::str(hemispheres_[northp1])) + + " set " + row); + if (utmp) { + if (zonem1 & 1) + irow = (irow + utmrowperiod_ - utmevenrowshift_) % utmrowperiod_; + iband -= 10; + irow = UTMRow(iband, icol, irow); + if (irow == maxutmSrow_) + throw GeographicErr("Block " + mgrs.substr(p-2, 2) + + " not in zone/band " + mgrs.substr(0, p-2)); + + irow = northp1 ? irow : irow + 100; + icol = icol + minutmcol_; + } else { + bool eastp = iband & 1; + icol += eastp ? upseasting_ : (northp1 ? minupsNind_ : minupsSind_); + irow += northp1 ? minupsNind_ : minupsSind_; + } + int prec1 = (len - p)/2; + real + unit = 1, + x1 = icol, + y1 = irow; + for (int i = 0; i < prec1; ++i) { + unit *= base_; + int + ix = Utility::lookup(digits_, mgrs[p + i]), + iy = Utility::lookup(digits_, mgrs[p + i + prec1]); + if (ix < 0 || iy < 0) + throw GeographicErr("Encountered a non-digit in " + mgrs.substr(p)); + x1 = base_ * x1 + ix; + y1 = base_ * y1 + iy; + } + if ((len - p) % 2) { + if (Utility::lookup(digits_, mgrs[len - 1]) < 0) + throw GeographicErr("Encountered a non-digit in " + mgrs.substr(p)); + else + throw GeographicErr("Not an even number of digits in " + + mgrs.substr(p)); + } + if (prec1 > maxprec_) + throw GeographicErr("More than " + Utility::str(2*maxprec_) + + " digits in " + mgrs.substr(p)); + if (centerp) { + unit *= 2; x1 = 2 * x1 + 1; y1 = 2 * y1 + 1; + } + zone = zone1; + northp = northp1; + x = (tile_ * x1) / unit; + y = (tile_ * y1) / unit; + prec = prec1; + } + + void MGRS::CheckCoords(bool utmp, bool& northp, real& x, real& y) { + // Limits are all multiples of 100km and are all closed on the lower end + // and open on the upper end -- and this is reflected in the error + // messages. However if a coordinate lies on the excluded upper end (e.g., + // after rounding), it is shifted down by eps. This also folds UTM + // northings to the correct N/S hemisphere. + + // The smallest length s.t., 1.0e7 - eps() < 1.0e7 (approx 1.9 nm) + // 25 = ceil(log_2(2e7)) -- use half circumference here because + // northing 195e5 is a legal in the "southern" hemisphere. + static const real eps = ldexp(real(1), -(Math::digits() - 25)); + int + ix = int(floor(x / tile_)), + iy = int(floor(y / tile_)), + ind = (utmp ? 2 : 0) + (northp ? 1 : 0); + if (! (ix >= mineasting_[ind] && ix < maxeasting_[ind]) ) { + if (ix == maxeasting_[ind] && x == maxeasting_[ind] * tile_) + x -= eps; + else + throw GeographicErr("Easting " + Utility::str(int(floor(x/1000))) + + "km not in MGRS/" + + (utmp ? "UTM" : "UPS") + " range for " + + (northp ? "N" : "S" ) + " hemisphere [" + + Utility::str(mineasting_[ind]*tile_/1000) + + "km, " + + Utility::str(maxeasting_[ind]*tile_/1000) + + "km)"); + } + if (! (iy >= minnorthing_[ind] && iy < maxnorthing_[ind]) ) { + if (iy == maxnorthing_[ind] && y == maxnorthing_[ind] * tile_) + y -= eps; + else + throw GeographicErr("Northing " + Utility::str(int(floor(y/1000))) + + "km not in MGRS/" + + (utmp ? "UTM" : "UPS") + " range for " + + (northp ? "N" : "S" ) + " hemisphere [" + + Utility::str(minnorthing_[ind]*tile_/1000) + + "km, " + + Utility::str(maxnorthing_[ind]*tile_/1000) + + "km)"); + } + + // Correct the UTM northing and hemisphere if necessary + if (utmp) { + if (northp && iy < minutmNrow_) { + northp = false; + y += utmNshift_; + } else if (!northp && iy >= maxutmSrow_) { + if (y == maxutmSrow_ * tile_) + // If on equator retain S hemisphere + y -= eps; + else { + northp = true; + y -= utmNshift_; + } + } + } + } + + int MGRS::UTMRow(int iband, int icol, int irow) { + // Input is iband = band index in [-10, 10) (as returned by LatitudeBand), + // icol = column index in [0,8) with origin of easting = 100km, and irow = + // periodic row index in [0,20) with origin = equator. Output is true row + // index in [-90, 95). Returns maxutmSrow_ = 100, if irow and iband are + // incompatible. + + // Estimate center row number for latitude band + // 90 deg = 100 tiles; 1 band = 8 deg = 100*8/90 tiles + real c = 100 * (8 * iband + 4)/real(90); + bool northp = iband >= 0; + // These are safe bounds on the rows + // iband minrow maxrow + // -10 -90 -81 + // -9 -80 -72 + // -8 -71 -63 + // -7 -63 -54 + // -6 -54 -45 + // -5 -45 -36 + // -4 -36 -27 + // -3 -27 -18 + // -2 -18 -9 + // -1 -9 -1 + // 0 0 8 + // 1 8 17 + // 2 17 26 + // 3 26 35 + // 4 35 44 + // 5 44 53 + // 6 53 62 + // 7 62 70 + // 8 71 79 + // 9 80 94 + int + minrow = iband > -10 ? + int(floor(c - real(4.3) - real(0.1) * northp)) : -90, + maxrow = iband < 9 ? + int(floor(c + real(4.4) - real(0.1) * northp)) : 94, + baserow = (minrow + maxrow) / 2 - utmrowperiod_ / 2; + // Offset irow by the multiple of utmrowperiod_ which brings it as close as + // possible to the center of the latitude band, (minrow + maxrow) / 2. + // (Add maxutmSrow_ = 5 * utmrowperiod_ to ensure operand is positive.) + irow = (irow - baserow + maxutmSrow_) % utmrowperiod_ + baserow; + if (!( irow >= minrow && irow <= maxrow )) { + // Outside the safe bounds, so need to check... + // Northing = 71e5 and 80e5 intersect band boundaries + // y = 71e5 in scol = 2 (x = [3e5,4e5] and x = [6e5,7e5]) + // y = 80e5 in scol = 1 (x = [2e5,3e5] and x = [7e5,8e5]) + // This holds for all the ellipsoids given in NGA.SIG.0012_2.0.0_UTMUPS. + // The following deals with these special cases. + int + // Fold [-10,-1] -> [9,0] + sband = iband >= 0 ? iband : -iband - 1, + // Fold [-90,-1] -> [89,0] + srow = irow >= 0 ? irow : -irow - 1, + // Fold [4,7] -> [3,0] + scol = icol < 4 ? icol : -icol + 7; + // For example, the safe rows for band 8 are 71 - 79. However row 70 is + // allowed if scol = [2,3] and row 80 is allowed if scol = [0,1]. + if ( ! ( (srow == 70 && sband == 8 && scol >= 2) || + (srow == 71 && sband == 7 && scol <= 2) || + (srow == 79 && sband == 9 && scol >= 1) || + (srow == 80 && sband == 8 && scol <= 1) ) ) + irow = maxutmSrow_; + } + return irow; + } + + void MGRS::Check() { + real lat, lon, x, y, t = tile_; int zone; bool northp; + UTMUPS::Reverse(31, true , 1*t, 0*t, lat, lon); + if (!( lon < 0 )) + throw GeographicErr("MGRS::Check: equator coverage failure"); + UTMUPS::Reverse(31, true , 1*t, 95*t, lat, lon); + if (!( lat > 84 )) + throw GeographicErr("MGRS::Check: UTM doesn't reach latitude = 84"); + UTMUPS::Reverse(31, false, 1*t, 10*t, lat, lon); + if (!( lat < -80 )) + throw GeographicErr("MGRS::Check: UTM doesn't reach latitude = -80"); + UTMUPS::Forward(56, 3, zone, northp, x, y, 32); + if (!( x > 1*t )) + throw GeographicErr("MGRS::Check: Norway exception creates a gap"); + UTMUPS::Forward(72, 21, zone, northp, x, y, 35); + if (!( x > 1*t )) + throw GeographicErr("MGRS::Check: Svalbard exception creates a gap"); + UTMUPS::Reverse(0, true , 20*t, 13*t, lat, lon); + if (!( lat < 84 )) + throw + GeographicErr("MGRS::Check: North UPS doesn't reach latitude = 84"); + UTMUPS::Reverse(0, false, 20*t, 8*t, lat, lon); + if (!( lat > -80 )) + throw + GeographicErr("MGRS::Check: South UPS doesn't reach latitude = -80"); + // Entries are [band, x, y] either side of the band boundaries. Units for + // x, y are t = 100km. + const short tab[] = { + 0, 5, 0, 0, 9, 0, // south edge of band 0 + 0, 5, 8, 0, 9, 8, // north edge of band 0 + 1, 5, 9, 1, 9, 9, // south edge of band 1 + 1, 5, 17, 1, 9, 17, // north edge of band 1 + 2, 5, 18, 2, 9, 18, // etc. + 2, 5, 26, 2, 9, 26, + 3, 5, 27, 3, 9, 27, + 3, 5, 35, 3, 9, 35, + 4, 5, 36, 4, 9, 36, + 4, 5, 44, 4, 9, 44, + 5, 5, 45, 5, 9, 45, + 5, 5, 53, 5, 9, 53, + 6, 5, 54, 6, 9, 54, + 6, 5, 62, 6, 9, 62, + 7, 5, 63, 7, 9, 63, + 7, 5, 70, 7, 7, 70, 7, 7, 71, 7, 9, 71, // y = 71t crosses boundary + 8, 5, 71, 8, 6, 71, 8, 6, 72, 8, 9, 72, // between bands 7 and 8. + 8, 5, 79, 8, 8, 79, 8, 8, 80, 8, 9, 80, // y = 80t crosses boundary + 9, 5, 80, 9, 7, 80, 9, 7, 81, 9, 9, 81, // between bands 8 and 9. + 9, 5, 95, 9, 9, 95, // north edge of band 9 + }; + const int bandchecks = sizeof(tab) / (3 * sizeof(short)); + for (int i = 0; i < bandchecks; ++i) { + UTMUPS::Reverse(38, true, tab[3*i+1]*t, tab[3*i+2]*t, lat, lon); + if (!( LatitudeBand(lat) == tab[3*i+0] )) + throw GeographicErr("MGRS::Check: Band error, b = " + + Utility::str(tab[3*i+0]) + ", x = " + + Utility::str(tab[3*i+1]) + "00km, y = " + + Utility::str(tab[3*i+2]) + "00km"); + } + } + +} // namespace GeographicLib diff --git a/common/local_libs/GeographicLib/src/MagneticCircle.cpp b/common/local_libs/GeographicLib/src/MagneticCircle.cpp new file mode 100644 index 0000000000..7599ac4785 --- /dev/null +++ b/common/local_libs/GeographicLib/src/MagneticCircle.cpp @@ -0,0 +1,52 @@ +/** + * \file MagneticCircle.cpp + * \brief Implementation for GeographicLib::MagneticCircle class + * + * Copyright (c) Charles Karney (2011-2015) and licensed + * under the MIT/X11 License. For more information, see + * https://geographiclib.sourceforge.io/ + **********************************************************************/ + +#include +#include +#include +#include + +namespace GeographicLib { + + using namespace std; + + void MagneticCircle::Field(real lon, bool diffp, + real& Bx, real& By, real& Bz, + real& Bxt, real& Byt, real& Bzt) const { + real slam, clam; + Math::sincosd(lon, slam, clam); + real M[Geocentric::dim2_]; + Geocentric::Rotation(_sphi, _cphi, slam, clam, M); + real BX0, BY0, BZ0, BX1, BY1, BZ1; // Components in geocentric basis + real BXc = 0, BYc = 0, BZc = 0; + _circ0(slam, clam, BX0, BY0, BZ0); + _circ1(slam, clam, BX1, BY1, BZ1); + if (_constterm) + _circ2(slam, clam, BXc, BYc, BZc); + if (_interpolate) { + BX1 = (BX1 - BX0) / _dt0; + BY1 = (BY1 - BY0) / _dt0; + BZ1 = (BZ1 - BZ0) / _dt0; + } + BX0 += _t1 * BX1 + BXc; + BY0 += _t1 * BY1 + BYc; + BZ0 += _t1 * BZ1 + BZc; + if (diffp) { + Geocentric::Unrotate(M, BX1, BY1, BZ1, Bxt, Byt, Bzt); + Bxt *= - _a; + Byt *= - _a; + Bzt *= - _a; + } + Geocentric::Unrotate(M, BX0, BY0, BZ0, Bx, By, Bz); + Bx *= - _a; + By *= - _a; + Bz *= - _a; + } + +} // namespace GeographicLib diff --git a/common/local_libs/GeographicLib/src/MagneticModel.cpp b/common/local_libs/GeographicLib/src/MagneticModel.cpp new file mode 100644 index 0000000000..bb73dffea3 --- /dev/null +++ b/common/local_libs/GeographicLib/src/MagneticModel.cpp @@ -0,0 +1,281 @@ +/** + * \file MagneticModel.cpp + * \brief Implementation for GeographicLib::MagneticModel class + * + * Copyright (c) Charles Karney (2011-2020) and licensed + * under the MIT/X11 License. For more information, see + * https://geographiclib.sourceforge.io/ + **********************************************************************/ + +#include +#include +#include +#include +#include + +#if !defined(GEOGRAPHICLIB_DATA) +# if defined(_WIN32) +# define GEOGRAPHICLIB_DATA "C:/ProgramData/GeographicLib" +# else +# define GEOGRAPHICLIB_DATA "/usr/local/share/GeographicLib" +# endif +#endif + +#if !defined(GEOGRAPHICLIB_MAGNETIC_DEFAULT_NAME) +# define GEOGRAPHICLIB_MAGNETIC_DEFAULT_NAME "wmm2020" +#endif + +#if defined(_MSC_VER) +// Squelch warnings about unsafe use of getenv +# pragma warning (disable: 4996) +#endif + +namespace GeographicLib { + + using namespace std; + + MagneticModel::MagneticModel(const std::string& name, const std::string& path, + const Geocentric& earth, int Nmax, int Mmax) + : _name(name) + , _dir(path) + , _description("NONE") + , _date("UNKNOWN") + , _t0(Math::NaN()) + , _dt0(1) + , _tmin(Math::NaN()) + , _tmax(Math::NaN()) + , _a(Math::NaN()) + , _hmin(Math::NaN()) + , _hmax(Math::NaN()) + , _Nmodels(1) + , _Nconstants(0) + , _nmx(-1) + , _mmx(-1) + , _norm(SphericalHarmonic::SCHMIDT) + , _earth(earth) + { + if (_dir.empty()) + _dir = DefaultMagneticPath(); + bool truncate = Nmax >= 0 || Mmax >= 0; + if (truncate) { + if (Nmax >= 0 && Mmax < 0) Mmax = Nmax; + if (Nmax < 0) Nmax = numeric_limits::max(); + if (Mmax < 0) Mmax = numeric_limits::max(); + } + ReadMetadata(_name); + _G.resize(_Nmodels + 1 + _Nconstants); + _H.resize(_Nmodels + 1 + _Nconstants); + { + string coeff = _filename + ".cof"; + ifstream coeffstr(coeff.c_str(), ios::binary); + if (!coeffstr.good()) + throw GeographicErr("Error opening " + coeff); + char id[idlength_ + 1]; + coeffstr.read(id, idlength_); + if (!coeffstr.good()) + throw GeographicErr("No header in " + coeff); + id[idlength_] = '\0'; + if (_id != string(id)) + throw GeographicErr("ID mismatch: " + _id + " vs " + id); + for (int i = 0; i < _Nmodels + 1 + _Nconstants; ++i) { + int N, M; + if (truncate) { N = Nmax; M = Mmax; } + SphericalEngine::coeff::readcoeffs(coeffstr, N, M, _G[i], _H[i], + truncate); + if (!(M < 0 || _G[i][0] == 0)) + throw GeographicErr("A degree 0 term is not permitted"); + _harm.push_back(SphericalHarmonic(_G[i], _H[i], N, N, M, _a, _norm)); + _nmx = max(_nmx, _harm.back().Coefficients().nmx()); + _mmx = max(_mmx, _harm.back().Coefficients().mmx()); + } + int pos = int(coeffstr.tellg()); + coeffstr.seekg(0, ios::end); + if (pos != coeffstr.tellg()) + throw GeographicErr("Extra data in " + coeff); + } + } + + void MagneticModel::ReadMetadata(const string& name) { + const char* spaces = " \t\n\v\f\r"; + _filename = _dir + "/" + name + ".wmm"; + ifstream metastr(_filename.c_str()); + if (!metastr.good()) + throw GeographicErr("Cannot open " + _filename); + string line; + getline(metastr, line); + if (!(line.size() >= 6 && line.substr(0,5) == "WMMF-")) + throw GeographicErr(_filename + " does not contain WMMF-n signature"); + string::size_type n = line.find_first_of(spaces, 5); + if (n != string::npos) + n -= 5; + string version(line, 5, n); + if (!(version == "1" || version == "2")) + throw GeographicErr("Unknown version in " + _filename + ": " + version); + string key, val; + while (getline(metastr, line)) { + if (!Utility::ParseLine(line, key, val)) + continue; + // Process key words + if (key == "Name") + _name = val; + else if (key == "Description") + _description = val; + else if (key == "ReleaseDate") + _date = val; + else if (key == "Radius") + _a = Utility::val(val); + else if (key == "Type") { + if (!(val == "Linear" || val == "linear")) + throw GeographicErr("Only linear models are supported"); + } else if (key == "Epoch") + _t0 = Utility::val(val); + else if (key == "DeltaEpoch") + _dt0 = Utility::val(val); + else if (key == "NumModels") + _Nmodels = Utility::val(val); + else if (key == "NumConstants") + _Nconstants = Utility::val(val); + else if (key == "MinTime") + _tmin = Utility::val(val); + else if (key == "MaxTime") + _tmax = Utility::val(val); + else if (key == "MinHeight") + _hmin = Utility::val(val); + else if (key == "MaxHeight") + _hmax = Utility::val(val); + else if (key == "Normalization") { + if (val == "FULL" || val == "Full" || val == "full") + _norm = SphericalHarmonic::FULL; + else if (val == "SCHMIDT" || val == "Schmidt" || val == "schmidt") + _norm = SphericalHarmonic::SCHMIDT; + else + throw GeographicErr("Unknown normalization " + val); + } else if (key == "ByteOrder") { + if (val == "Big" || val == "big") + throw GeographicErr("Only little-endian ordering is supported"); + else if (!(val == "Little" || val == "little")) + throw GeographicErr("Unknown byte ordering " + val); + } else if (key == "ID") + _id = val; + // else unrecognized keywords are skipped + } + // Check values + if (!(isfinite(_a) && _a > 0)) + throw GeographicErr("Reference radius must be positive"); + if (!(_t0 > 0)) + throw GeographicErr("Epoch time not defined"); + if (_tmin >= _tmax) + throw GeographicErr("Min time exceeds max time"); + if (_hmin >= _hmax) + throw GeographicErr("Min height exceeds max height"); + if (int(_id.size()) != idlength_) + throw GeographicErr("Invalid ID"); + if (_Nmodels < 1) + throw GeographicErr("NumModels must be positive"); + if (!(_Nconstants == 0 || _Nconstants == 1)) + throw GeographicErr("NumConstants must be 0 or 1"); + if (!(_dt0 > 0)) { + if (_Nmodels > 1) + throw GeographicErr("DeltaEpoch must be positive"); + else + _dt0 = 1; + } + } + + void MagneticModel::Field(real t, real lat, real lon, real h, bool diffp, + real& Bx, real& By, real& Bz, + real& Bxt, real& Byt, real& Bzt) const { + t -= _t0; + int n = max(min(int(floor(t / _dt0)), _Nmodels - 1), 0); + bool interpolate = n + 1 < _Nmodels; + t -= n * _dt0; + real X, Y, Z; + real M[Geocentric::dim2_]; + _earth.IntForward(lat, lon, h, X, Y, Z, M); + // Components in geocentric basis + // initial values to suppress warning + real BX0 = 0, BY0 = 0, BZ0 = 0, BX1 = 0, BY1 = 0, BZ1 = 0; + real BXc = 0, BYc = 0, BZc = 0; + _harm[n](X, Y, Z, BX0, BY0, BZ0); + _harm[n + 1](X, Y, Z, BX1, BY1, BZ1); + if (_Nconstants) + _harm[_Nmodels + 1](X, Y, Z, BXc, BYc, BZc); + if (interpolate) { + // Convert to a time derivative + BX1 = (BX1 - BX0) / _dt0; + BY1 = (BY1 - BY0) / _dt0; + BZ1 = (BZ1 - BZ0) / _dt0; + } + BX0 += t * BX1 + BXc; + BY0 += t * BY1 + BYc; + BZ0 += t * BZ1 + BZc; + if (diffp) { + Geocentric::Unrotate(M, BX1, BY1, BZ1, Bxt, Byt, Bzt); + Bxt *= - _a; + Byt *= - _a; + Bzt *= - _a; + } + Geocentric::Unrotate(M, BX0, BY0, BZ0, Bx, By, Bz); + Bx *= - _a; + By *= - _a; + Bz *= - _a; + } + + MagneticCircle MagneticModel::Circle(real t, real lat, real h) const { + real t1 = t - _t0; + int n = max(min(int(floor(t1 / _dt0)), _Nmodels - 1), 0); + bool interpolate = n + 1 < _Nmodels; + t1 -= n * _dt0; + real X, Y, Z, M[Geocentric::dim2_]; + _earth.IntForward(lat, 0, h, X, Y, Z, M); + // Y = 0, cphi = M[7], sphi = M[8]; + + return (_Nconstants == 0 ? + MagneticCircle(_a, _earth._f, lat, h, t, + M[7], M[8], t1, _dt0, interpolate, + _harm[n].Circle(X, Z, true), + _harm[n + 1].Circle(X, Z, true)) : + MagneticCircle(_a, _earth._f, lat, h, t, + M[7], M[8], t1, _dt0, interpolate, + _harm[n].Circle(X, Z, true), + _harm[n + 1].Circle(X, Z, true), + _harm[_Nmodels + 1].Circle(X, Z, true))); + } + + void MagneticModel::FieldComponents(real Bx, real By, real Bz, + real Bxt, real Byt, real Bzt, + real& H, real& F, real& D, real& I, + real& Ht, real& Ft, + real& Dt, real& It) { + H = hypot(Bx, By); + Ht = H != 0 ? (Bx * Bxt + By * Byt) / H : hypot(Bxt, Byt); + D = H != 0 ? Math::atan2d(Bx, By) : Math::atan2d(Bxt, Byt); + Dt = (H != 0 ? (By * Bxt - Bx * Byt) / Math::sq(H) : 0) / Math::degree(); + F = hypot(H, Bz); + Ft = F != 0 ? (H * Ht + Bz * Bzt) / F : hypot(Ht, Bzt); + I = F != 0 ? Math::atan2d(-Bz, H) : Math::atan2d(-Bzt, Ht); + It = (F != 0 ? (Bz * Ht - H * Bzt) / Math::sq(F) : 0) / Math::degree(); + } + + string MagneticModel::DefaultMagneticPath() { + string path; + char* magneticpath = getenv("GEOGRAPHICLIB_MAGNETIC_PATH"); + if (magneticpath) + path = string(magneticpath); + if (!path.empty()) + return path; + char* datapath = getenv("GEOGRAPHICLIB_DATA"); + if (datapath) + path = string(datapath); + return (!path.empty() ? path : string(GEOGRAPHICLIB_DATA)) + "/magnetic"; + } + + string MagneticModel::DefaultMagneticName() { + string name; + char* magneticname = getenv("GEOGRAPHICLIB_MAGNETIC_NAME"); + if (magneticname) + name = string(magneticname); + return !name.empty() ? name : string(GEOGRAPHICLIB_MAGNETIC_DEFAULT_NAME); + } + +} // namespace GeographicLib diff --git a/common/local_libs/GeographicLib/src/Math.cpp b/common/local_libs/GeographicLib/src/Math.cpp new file mode 100644 index 0000000000..dac9174efd --- /dev/null +++ b/common/local_libs/GeographicLib/src/Math.cpp @@ -0,0 +1,346 @@ +/** + * \file Math.cpp + * \brief Implementation for GeographicLib::Math class + * + * Copyright (c) Charles Karney (2015-2020) and licensed + * under the MIT/X11 License. For more information, see + * https://geographiclib.sourceforge.io/ + **********************************************************************/ + +#include + +#if defined(_MSC_VER) +// Squelch warnings about constant conditional expressions +# pragma warning (disable: 4127) +#endif + +namespace GeographicLib { + + using namespace std; + + void Math::dummy() { + static_assert(GEOGRAPHICLIB_PRECISION >= 1 && GEOGRAPHICLIB_PRECISION <= 5, + "Bad value of precision"); + } + + int Math::digits() { +#if GEOGRAPHICLIB_PRECISION != 5 + return numeric_limits::digits; +#else + return numeric_limits::digits(); +#endif + } + + int Math::set_digits(int ndigits) { +#if GEOGRAPHICLIB_PRECISION != 5 + (void)ndigits; +#else + mpfr::mpreal::set_default_prec(ndigits >= 2 ? ndigits : 2); +#endif + return digits(); + } + + int Math::digits10() { +#if GEOGRAPHICLIB_PRECISION != 5 + return numeric_limits::digits10; +#else + return numeric_limits::digits10(); +#endif + } + + int Math::extra_digits() { + return + digits10() > numeric_limits::digits10 ? + digits10() - numeric_limits::digits10 : 0; + } + + template T Math::hypot(T x, T y) { + using std::hypot; return hypot(x, y); + } + + template T Math::expm1(T x) { + using std::expm1; return expm1(x); + } + + template T Math::log1p(T x) { + using std::log1p; return log1p(x); + } + + template T Math::asinh(T x) { + using std::asinh; return asinh(x); + } + + template T Math::atanh(T x) { + using std::atanh; return atanh(x); + } + + template T Math::copysign(T x, T y) { + using std::copysign; return copysign(x, y); + } + + template T Math::cbrt(T x) { + using std::cbrt; return cbrt(x); + } + + template T Math::remainder(T x, T y) { + using std::remainder; return remainder(x, y); + } + + template T Math::remquo(T x, T y, int* n) { + using std::remquo; return remquo(x, y, n); + } + + template T Math::round(T x) { + using std::round; return round(x); + } + + template long Math::lround(T x) { + using std::lround; return lround(x); + } + + template T Math::fma(T x, T y, T z) { + using std::fma; return fma(x, y, z); + } + + template T Math::sum(T u, T v, T& t) { + GEOGRAPHICLIB_VOLATILE T s = u + v; + GEOGRAPHICLIB_VOLATILE T up = s - v; + GEOGRAPHICLIB_VOLATILE T vpp = s - up; + up -= u; + vpp -= v; + t = -(up + vpp); + // u + v = s + t + // = round(u + v) + t + return s; + } + + template T Math::AngRound(T x) { + static const T z = 1/T(16); + if (x == 0) return 0; + GEOGRAPHICLIB_VOLATILE T y = abs(x); + // The compiler mustn't "simplify" z - (z - y) to y + y = y < z ? z - (z - y) : y; + return x < 0 ? -y : y; + } + + template void Math::sincosd(T x, T& sinx, T& cosx) { + // In order to minimize round-off errors, this function exactly reduces + // the argument to the range [-45, 45] before converting it to radians. + using std::remquo; + T r; int q; + // N.B. the implementation of remquo in glibc pre 2.22 were buggy. See + // https://sourceware.org/bugzilla/show_bug.cgi?id=17569 + // This was fixed in version 2.22 on 2015-08-05 + r = remquo(x, T(90), &q); // now abs(r) <= 45 + r *= degree(); + // g++ -O turns these two function calls into a call to sincos + T 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 + } + // Set sign of 0 results. -0 only produced for sin(-0) + if (x != 0) { sinx += T(0); cosx += T(0); } + } + + template T Math::sind(T x) { + // See sincosd + using std::remquo; + T r; int q; + r = remquo(x, T(90), &q); // now abs(r) <= 45 + r *= degree(); + unsigned p = unsigned(q); + r = p & 1U ? cos(r) : sin(r); + if (p & 2U) r = -r; + if (x != 0) r += T(0); + return r; + } + + template T Math::cosd(T x) { + // See sincosd + using std::remquo; + T r; int q; + r = remquo(x, T(90), &q); // now abs(r) <= 45 + r *= degree(); + unsigned p = unsigned(q + 1); + r = p & 1U ? cos(r) : sin(r); + if (p & 2U) r = -r; + return T(0) + r; + } + + template T Math::tand(T x) { + static const T overflow = 1 / sq(numeric_limits::epsilon()); + T s, c; + sincosd(x, s, c); + return c != 0 ? s / c : (s < 0 ? -overflow : overflow); + } + + template T Math::atan2d(T y, T 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; + if (abs(y) > abs(x)) { swap(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] + T 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; + // + // and handle mpfr as in AngRound. + case 1: ang = (y >= 0 ? 180 : -180) - ang; break; + case 2: ang = 90 - ang; break; + case 3: ang = -90 + ang; break; + } + return ang; + } + + template T Math::atand(T x) + { return atan2d(x, T(1)); } + + template T Math::eatanhe(T x, T es) { + using std::atanh; + return es > T(0) ? es * atanh(es * x) : -es * atan(es * x); + } + + template T Math::taupf(T tau, T es) { + // Need this test, otherwise tau = +/-inf gives taup = nan. + using std::isfinite; using std::hypot; + if (isfinite(tau)) { + T tau1 = hypot(T(1), tau), + sig = sinh( eatanhe(tau / tau1, es ) ); + return hypot(T(1), sig) * tau - sig * tau1; + } else + return tau; + } + + template T Math::tauf(T taup, T es) { + using std::hypot; + static const int numit = 5; + // min iterations = 1, max iterations = 2; mean = 1.95 + static const T tol = sqrt(numeric_limits::epsilon()) / 10; + static const T taumax = 2 / sqrt(numeric_limits::epsilon()); + T e2m = T(1) - sq(es), + // To lowest order in e^2, taup = (1 - e^2) * tau = _e2m * tau; so use + // tau = taup/e2m as a starting guess. Only 1 iteration is needed for + // |lat| < 3.35 deg, otherwise 2 iterations are needed. If, instead, tau + // = taup is used the mean number of iterations increases to 1.999 (2 + // iterations are needed except near tau = 0). + // + // For large tau, taup = exp(-es*atanh(es)) * tau. Use this as for the + // initial guess for |taup| > 70 (approx |phi| > 89deg). Then for + // sufficiently large tau (such that sqrt(1+tau^2) = |tau|), we can exit + // with the intial guess and avoid overflow problems. This also reduces + // the mean number of iterations slightly from 1.963 to 1.954. + tau = abs(taup) > 70 ? taup * exp(eatanhe(T(1), es)) : taup/e2m, + stol = tol * max(T(1), abs(taup)); + if (!(abs(tau) < taumax)) return tau; // handles +/-inf and nan + for (int i = 0; i < numit || GEOGRAPHICLIB_PANIC; ++i) { + T taupa = taupf(tau, es), + dtau = (taup - taupa) * (1 + e2m * sq(tau)) / + ( e2m * hypot(T(1), tau) * hypot(T(1), taupa) ); + tau += dtau; + if (!(abs(dtau) >= stol)) + break; + } + return tau; + } + + template bool Math::isfinite(T x) { + using std::isfinite; return isfinite(x); + } + + template T Math::NaN() { +#if defined(_MSC_VER) + return numeric_limits::has_quiet_NaN ? + numeric_limits::quiet_NaN() : + (numeric_limits::max)(); +#else + return numeric_limits::has_quiet_NaN ? + numeric_limits::quiet_NaN() : + numeric_limits::max(); +#endif + } + + template bool Math::isnan(T x) { + using std::isnan; return isnan(x); + } + + template T Math::infinity() { +#if defined(_MSC_VER) + return numeric_limits::has_infinity ? + numeric_limits::infinity() : + (numeric_limits::max)(); +#else + return numeric_limits::has_infinity ? + numeric_limits::infinity() : + numeric_limits::max(); +#endif + } + + /// \cond SKIP + // Instantiate +#define GEOGRAPHICLIB_MATH_INSTANTIATE(T) \ + template T GEOGRAPHICLIB_EXPORT Math::hypot (T, T); \ + template T GEOGRAPHICLIB_EXPORT Math::expm1 (T); \ + template T GEOGRAPHICLIB_EXPORT Math::log1p (T); \ + template T GEOGRAPHICLIB_EXPORT Math::asinh (T); \ + template T GEOGRAPHICLIB_EXPORT Math::atanh (T); \ + template T GEOGRAPHICLIB_EXPORT Math::cbrt (T); \ + template T GEOGRAPHICLIB_EXPORT Math::remainder(T, T); \ + template T GEOGRAPHICLIB_EXPORT Math::remquo (T, T, int*); \ + template T GEOGRAPHICLIB_EXPORT Math::round (T); \ + template long GEOGRAPHICLIB_EXPORT Math::lround (T); \ + template T GEOGRAPHICLIB_EXPORT Math::copysign (T, T); \ + template T GEOGRAPHICLIB_EXPORT Math::fma (T, T, T); \ + template T GEOGRAPHICLIB_EXPORT Math::sum (T, T, T&); \ + template T GEOGRAPHICLIB_EXPORT Math::AngRound (T); \ + template void GEOGRAPHICLIB_EXPORT Math::sincosd (T, T&, T&); \ + template T GEOGRAPHICLIB_EXPORT Math::sind (T); \ + template T GEOGRAPHICLIB_EXPORT Math::cosd (T); \ + template T GEOGRAPHICLIB_EXPORT Math::tand (T); \ + template T GEOGRAPHICLIB_EXPORT Math::atan2d (T, T); \ + template T GEOGRAPHICLIB_EXPORT Math::atand (T); \ + template T GEOGRAPHICLIB_EXPORT Math::eatanhe (T, T); \ + template T GEOGRAPHICLIB_EXPORT Math::taupf (T, T); \ + template T GEOGRAPHICLIB_EXPORT Math::tauf (T, T); \ + template bool GEOGRAPHICLIB_EXPORT Math::isfinite (T); \ + template T GEOGRAPHICLIB_EXPORT Math::NaN (); \ + template bool GEOGRAPHICLIB_EXPORT Math::isnan (T); \ + template T GEOGRAPHICLIB_EXPORT Math::infinity (); + + // Instantiate with the standard floating type + GEOGRAPHICLIB_MATH_INSTANTIATE(float) + GEOGRAPHICLIB_MATH_INSTANTIATE(double) +#if GEOGRAPHICLIB_HAVE_LONG_DOUBLE + // Instantiate if long double is distinct from double + GEOGRAPHICLIB_MATH_INSTANTIATE(long double) +#endif +#if GEOGRAPHICLIB_PRECISION > 3 + // Instantiate with the high precision type + GEOGRAPHICLIB_MATH_INSTANTIATE(Math::real) +#endif + +#undef GEOGRAPHICLIB_MATH_INSTANTIATE + + // Also we need int versions for Utility::nummatch + template int GEOGRAPHICLIB_EXPORT Math::NaN (); + template int GEOGRAPHICLIB_EXPORT Math::infinity(); + /// \endcond + +} // namespace GeographicLib diff --git a/common/local_libs/GeographicLib/src/NormalGravity.cpp b/common/local_libs/GeographicLib/src/NormalGravity.cpp new file mode 100644 index 0000000000..5647aa54c4 --- /dev/null +++ b/common/local_libs/GeographicLib/src/NormalGravity.cpp @@ -0,0 +1,301 @@ +/** + * \file NormalGravity.cpp + * \brief Implementation for GeographicLib::NormalGravity class + * + * Copyright (c) Charles Karney (2011-2020) and licensed + * under the MIT/X11 License. For more information, see + * https://geographiclib.sourceforge.io/ + **********************************************************************/ + +#include + +#if defined(_MSC_VER) +// Squelch warnings about constant conditional expressions +# pragma warning (disable: 4127) +#endif + +namespace GeographicLib { + + using namespace std; + + void NormalGravity::Initialize(real a, real GM, real omega, real f_J2, + bool geometricp) { + _a = a; + if (!(isfinite(_a) && _a > 0)) + throw GeographicErr("Equatorial radius is not positive"); + _GM = GM; + if (!isfinite(_GM)) + throw GeographicErr("Gravitational constant is not finite"); + _omega = omega; + _omega2 = Math::sq(_omega); + _aomega2 = Math::sq(_omega * _a); + if (!(isfinite(_omega2) && isfinite(_aomega2))) + throw GeographicErr("Rotation velocity is not finite"); + _f = geometricp ? f_J2 : J2ToFlattening(_a, _GM, _omega, f_J2); + _b = _a * (1 - _f); + if (!(isfinite(_b) && _b > 0)) + throw GeographicErr("Polar semi-axis is not positive"); + _J2 = geometricp ? FlatteningToJ2(_a, _GM, _omega, f_J2) : f_J2; + _e2 = _f * (2 - _f); + _ep2 = _e2 / (1 - _e2); + real ex2 = _f < 0 ? -_e2 : _ep2; + _Q0 = Qf(ex2, _f < 0); + _earth = Geocentric(_a, _f); + _E = _a * sqrt(abs(_e2)); // H+M, Eq 2-54 + // H+M, Eq 2-61 + _U0 = _GM * atanzz(ex2, _f < 0) / _b + _aomega2 / 3; + real P = Hf(ex2, _f < 0) / (6 * _Q0); + // H+M, Eq 2-73 + _gammae = _GM / (_a * _b) - (1 + P) * _a * _omega2; + // H+M, Eq 2-74 + _gammap = _GM / (_a * _a) + 2 * P * _b * _omega2; + // k = gammae * (b * gammap / (a * gammae) - 1) + // = (b * gammap - a * gammae) / a + _k = -_e2 * _GM / (_a * _b) + + _omega2 * (P * (_a + 2 * _b * (1 - _f)) + _a); + // f* = (gammap - gammae) / gammae + _fstar = (-_f * _GM / (_a * _b) + _omega2 * (P * (_a + 2 * _b) + _a)) / + _gammae; + } + + NormalGravity::NormalGravity(real a, real GM, real omega, real f_J2, + bool geometricp) { + Initialize(a, GM, omega, f_J2, geometricp); + } + + const NormalGravity& NormalGravity::WGS84() { + static const NormalGravity wgs84(Constants::WGS84_a(), + Constants::WGS84_GM(), + Constants::WGS84_omega(), + Constants::WGS84_f(), true); + return wgs84; + } + + const NormalGravity& NormalGravity::GRS80() { + static const NormalGravity grs80(Constants::GRS80_a(), + Constants::GRS80_GM(), + Constants::GRS80_omega(), + Constants::GRS80_J2(), false); + return grs80; + } + + Math::real NormalGravity::atan7series(real x) { + // compute -sum( (-x)^n/(2*n+7), n, 0, inf) + // = -1/7 + x/9 - x^2/11 + x^3/13 ... + // = (atan(sqrt(x))/sqrt(x)-(1-x/3+x^2/5)) / x^3 (x > 0) + // = (atanh(sqrt(-x))/sqrt(-x)-(1-x/3+x^2/5)) / x^3 (x < 0) + // require abs(x) < 1/2, but better to restrict calls to abs(x) < 1/4 + static const real lg2eps_ = + -log(numeric_limits::epsilon() / 2) / log(real(2)); + int e; + frexp(x, &e); + e = max(-e, 1); // Here's where abs(x) < 1/2 is assumed + // x = [0.5,1) * 2^(-e) + // estimate n s.t. x^n/n < 1/7 * epsilon/2 + // a stronger condition is x^n < epsilon/2 + // taking log2 of both sides, a stronger condition is n*(-e) < -lg2eps; + // or n*e > lg2eps or n > ceiling(lg2eps/e) + int n = int(ceil(lg2eps_ / e)); + Math::real v = 0; + while (n--) // iterating from n-1 down to 0 + v = - x * v - 1/Math::real(2*n + 7); + return v; + } + + Math::real NormalGravity::atan5series(real x) { + // Compute Taylor series approximations to + // (atan(z)-(z-z^3/3))/z^5, + // z = sqrt(x) + // require abs(x) < 1/2, but better to restrict calls to abs(x) < 1/4 + return 1/real(5) + x * atan7series(x); + } + + Math::real NormalGravity::Qf(real x, bool alt) { + // Compute + // Q(z) = (((1 + 3/z^2) * atan(z) - 3/z)/2) / z^3 + // = q(z)/z^3 with q(z) defined by H+M, Eq 2-57 with z = E/u + // z = sqrt(x) + real y = alt ? -x / (1 + x) : x; + return !(4 * abs(y) < 1) ? // Backwards test to allow NaNs through + ((1 + 3/y) * atanzz(x, alt) - 3/y) / (2 * y) : + (3 * (3 + y) * atan5series(y) - 1) / 6; + } + + Math::real NormalGravity::Hf(real x, bool alt) { + // z = sqrt(x) + // Compute + // H(z) = (3*Q(z)+z*diff(Q(z),z))*(1+z^2) + // = (3 * (1 + 1/z^2) * (1 - atan(z)/z) - 1) / z^2 + // = q'(z)/z^2, with q'(z) defined by H+M, Eq 2-67, with z = E/u + real y = alt ? -x / (1 + x) : x; + return !(4 * abs(y) < 1) ? // Backwards test to allow NaNs through + (3 * (1 + 1/y) * (1 - atanzz(x, alt)) - 1) / y : + 1 - 3 * (1 + y) * atan5series(y); + } + + Math::real NormalGravity::QH3f(real x, bool alt) { + // z = sqrt(x) + // (Q(z) - H(z)/3) / z^2 + // = - (1+z^2)/(3*z) * d(Q(z))/dz - Q(z) + // = ((15+9*z^2)*atan(z)-4*z^3-15*z)/(6*z^7) + // = ((25+15*z^2)*atan7+3)/10 + real y = alt ? -x / (1 + x) : x; + return !(4 * abs(y) < 1) ? // Backwards test to allow NaNs through + ((9 + 15/y) * atanzz(x, alt) - 4 - 15/y) / (6 * Math::sq(y)) : + ((25 + 15*y) * atan7series(y) + 3)/10; + } + + Math::real NormalGravity::Jn(int n) const { + // Note Jn(0) = -1; Jn(2) = _J2; Jn(odd) = 0 + if (n & 1 || n < 0) + return 0; + n /= 2; + real e2n = 1; // Perhaps this should just be e2n = pow(-_e2, n); + for (int j = n; j--;) + e2n *= -_e2; + return // H+M, Eq 2-92 + -3 * e2n * ((1 - n) + 5 * n * _J2 / _e2) / ((2 * n + 1) * (2 * n + 3)); + } + + Math::real NormalGravity::SurfaceGravity(real lat) const { + real sphi = Math::sind(Math::LatFix(lat)); + // H+M, Eq 2-78 + return (_gammae + _k * Math::sq(sphi)) / sqrt(1 - _e2 * Math::sq(sphi)); + } + + Math::real NormalGravity::V0(real X, real Y, real Z, + real& GammaX, real& GammaY, real& GammaZ) const + { + // See H+M, Sec 6-2 + real + p = hypot(X, Y), + clam = p != 0 ? X/p : 1, + slam = p != 0 ? Y/p : 0, + r = hypot(p, Z); + if (_f < 0) swap(p, Z); + real + Q = Math::sq(r) - Math::sq(_E), + t2 = Math::sq(2 * _E * Z), + disc = sqrt(Math::sq(Q) + t2), + // This is H+M, Eq 6-8a, but generalized to deal with Q negative + // accurately. + u = sqrt((Q >= 0 ? (Q + disc) : t2 / (disc - Q)) / 2), + uE = hypot(u, _E), + // H+M, Eq 6-8b + sbet = u != 0 ? Z * uE : copysign(sqrt(-Q), Z), + cbet = u != 0 ? p * u : p, + s = hypot(cbet, sbet); + sbet = s != 0 ? sbet/s : 1; + cbet = s != 0 ? cbet/s : 0; + real + z = _E/u, + z2 = Math::sq(z), + den = hypot(u, _E * sbet); + if (_f < 0) { + swap(sbet, cbet); + swap(u, uE); + } + real + invw = uE / den, // H+M, Eq 2-63 + bu = _b / (u != 0 || _f < 0 ? u : _E), + // Qf(z2->inf, false) = pi/(4*z^3) + q = ((u != 0 || _f < 0 ? Qf(z2, _f < 0) : Math::pi() / 4) / _Q0) * + bu * Math::sq(bu), + qp = _b * Math::sq(bu) * (u != 0 || _f < 0 ? Hf(z2, _f < 0) : 2) / _Q0, + ang = (Math::sq(sbet) - 1/real(3)) / 2, + // H+M, Eqs 2-62 + 6-9, but omitting last (rotational) term. + Vres = _GM * (u != 0 || _f < 0 ? + atanzz(z2, _f < 0) / u : + Math::pi() / (2 * _E)) + _aomega2 * q * ang, + // H+M, Eq 6-10 + gamu = - (_GM + (_aomega2 * qp * ang)) * invw / Math::sq(uE), + gamb = _aomega2 * q * sbet * cbet * invw / uE, + t = u * invw / uE, + gamp = t * cbet * gamu - invw * sbet * gamb; + // H+M, Eq 6-12 + GammaX = gamp * clam; + GammaY = gamp * slam; + GammaZ = invw * sbet * gamu + t * cbet * gamb; + return Vres; + } + + Math::real NormalGravity::Phi(real X, real Y, real& fX, real& fY) const { + fX = _omega2 * X; + fY = _omega2 * Y; + // N.B. fZ = 0; + return _omega2 * (Math::sq(X) + Math::sq(Y)) / 2; + } + + Math::real NormalGravity::U(real X, real Y, real Z, + real& gammaX, real& gammaY, real& gammaZ) const { + real fX, fY; + real Ures = V0(X, Y, Z, gammaX, gammaY, gammaZ) + Phi(X, Y, fX, fY); + gammaX += fX; + gammaY += fY; + return Ures; + } + + Math::real NormalGravity::Gravity(real lat, real h, + real& gammay, real& gammaz) const { + real X, Y, Z; + real M[Geocentric::dim2_]; + _earth.IntForward(lat, 0, h, X, Y, Z, M); + real gammaX, gammaY, gammaZ, + Ures = U(X, Y, Z, gammaX, gammaY, gammaZ); + // gammax = M[0] * gammaX + M[3] * gammaY + M[6] * gammaZ; + gammay = M[1] * gammaX + M[4] * gammaY + M[7] * gammaZ; + gammaz = M[2] * gammaX + M[5] * gammaY + M[8] * gammaZ; + return Ures; + } + + Math::real NormalGravity::J2ToFlattening(real a, real GM, + real omega, real J2) { + // Solve + // f = e^2 * (1 - K * e/q0) - 3 * J2 = 0 + // for e^2 using Newton's method + static const real maxe_ = 1 - numeric_limits::epsilon(); + static const real eps2_ = sqrt(numeric_limits::epsilon()) / 100; + real + K = 2 * Math::sq(a * omega) * a / (15 * GM), + J0 = (1 - 4 * K / Math::pi()) / 3; + if (!(GM > 0 && isfinite(K) && K >= 0)) + return Math::NaN(); + if (!(isfinite(J2) && J2 <= J0)) return Math::NaN(); + if (J2 == J0) return 1; + // Solve e2 - f1 * f2 * K / Q0 - 3 * J2 = 0 for J2 close to J0; + // subst e2 = ep2/(1+ep2), f2 = 1/(1+ep2), f1 = 1/sqrt(1+ep2), J2 = J0-dJ2, + // Q0 = pi/(4*z^3) - 2/z^4 + (3*pi)/(4*z^5), z = sqrt(ep2), and balance two + // leading terms to give + real + ep2 = max(Math::sq(32 * K / (3 * Math::sq(Math::pi()) * (J0 - J2))), + -maxe_), + e2 = min(ep2 / (1 + ep2), maxe_); + for (int j = 0; j < maxit_ || GEOGRAPHICLIB_PANIC; ++j) { + real + e2a = e2, ep2a = ep2, + f2 = 1 - e2, // (1 - f)^2 + f1 = sqrt(f2), // (1 - f) + Q0 = Qf(e2 < 0 ? -e2 : ep2, e2 < 0), + h = e2 - f1 * f2 * K / Q0 - 3 * J2, + dh = 1 - 3 * f1 * K * QH3f(e2 < 0 ? -e2 : ep2, e2 < 0) / + (2 * Math::sq(Q0)); + e2 = min(e2a - h / dh, maxe_); + ep2 = max(e2 / (1 - e2), -maxe_); + if (abs(h) < eps2_ || e2 == e2a || ep2 == ep2a) + break; + } + return e2 / (1 + sqrt(1 - e2)); + } + + Math::real NormalGravity::FlatteningToJ2(real a, real GM, + real omega, real f) { + real + K = 2 * Math::sq(a * omega) * a / (15 * GM), + f1 = 1 - f, + f2 = Math::sq(f1), + e2 = f * (2 - f); + // H+M, Eq 2-90 + 2-92' + return (e2 - K * f1 * f2 / Qf(f < 0 ? -e2 : e2 / f2, f < 0)) / 3; + } + +} // namespace GeographicLib diff --git a/common/local_libs/GeographicLib/src/OSGB.cpp b/common/local_libs/GeographicLib/src/OSGB.cpp new file mode 100644 index 0000000000..af7fc418c6 --- /dev/null +++ b/common/local_libs/GeographicLib/src/OSGB.cpp @@ -0,0 +1,169 @@ +/** + * \file OSGB.cpp + * \brief Implementation for GeographicLib::OSGB class + * + * Copyright (c) Charles Karney (2010-2020) and licensed + * under the MIT/X11 License. For more information, see + * https://geographiclib.sourceforge.io/ + **********************************************************************/ + +#include +#include + +namespace GeographicLib { + + using namespace std; + + const char* const OSGB::letters_ = "ABCDEFGHJKLMNOPQRSTUVWXYZ"; + const char* const OSGB::digits_ = "0123456789"; + + const TransverseMercator& OSGB::OSGBTM() { + static const TransverseMercator osgbtm(EquatorialRadius(), Flattening(), + CentralScale()); + return osgbtm; + } + + Math::real OSGB::computenorthoffset() { + real x, y; + static const real northoffset = + ( (void)OSGBTM().Forward(real(0), OriginLatitude(), real(0), x, y), + FalseNorthing() - y ); + return northoffset; + } + + void OSGB::GridReference(real x, real y, int prec, std::string& gridref) { + using std::isnan; // Needed for Centos 7, ubuntu 14 + CheckCoords(x, y); + if (!(prec >= 0 && prec <= maxprec_)) + throw GeographicErr("OSGB precision " + Utility::str(prec) + + " not in [0, " + + Utility::str(int(maxprec_)) + "]"); + if (isnan(x) || isnan(y)) { + gridref = "INVALID"; + return; + } + char grid[2 + 2 * maxprec_]; + int + xh = int(floor(x / tile_)), + yh = int(floor(y / tile_)); + real + xf = x - tile_ * xh, + yf = y - tile_ * yh; + xh += tileoffx_; + yh += tileoffy_; + int z = 0; + grid[z++] = letters_[(tilegrid_ - (yh / tilegrid_) - 1) + * tilegrid_ + (xh / tilegrid_)]; + grid[z++] = letters_[(tilegrid_ - (yh % tilegrid_) - 1) + * tilegrid_ + (xh % tilegrid_)]; + // Need extra real because, since C++11, pow(float, int) returns double + real mult = real(pow(real(base_), max(tilelevel_ - prec, 0))); + int + ix = int(floor(xf / mult)), + iy = int(floor(yf / mult)); + for (int c = min(prec, int(tilelevel_)); c--;) { + grid[z + c] = digits_[ ix % base_ ]; + ix /= base_; + grid[z + c + prec] = digits_[ iy % base_ ]; + iy /= base_; + } + if (prec > tilelevel_) { + xf -= floor(xf / mult); + yf -= floor(yf / mult); + mult = real(pow(real(base_), prec - tilelevel_)); + ix = int(floor(xf * mult)); + iy = int(floor(yf * mult)); + for (int c = prec - tilelevel_; c--;) { + grid[z + c + tilelevel_] = digits_[ ix % base_ ]; + ix /= base_; + grid[z + c + tilelevel_ + prec] = digits_[ iy % base_ ]; + iy /= base_; + } + } + int mlen = z + 2 * prec; + gridref.resize(mlen); + copy(grid, grid + mlen, gridref.begin()); + } + + void OSGB::GridReference(const std::string& gridref, + real& x, real& y, int& prec, + bool centerp) { + int + len = int(gridref.size()), + p = 0; + if (len >= 2 && + toupper(gridref[0]) == 'I' && + toupper(gridref[1]) == 'N') { + x = y = Math::NaN(); + prec = -2; // For compatibility with MGRS::Reverse. + return; + } + char grid[2 + 2 * maxprec_]; + for (int i = 0; i < len; ++i) { + if (!isspace(gridref[i])) { + if (p >= 2 + 2 * maxprec_) + throw GeographicErr("OSGB string " + gridref + " too long"); + grid[p++] = gridref[i]; + } + } + len = p; + p = 0; + if (len < 2) + throw GeographicErr("OSGB string " + gridref + " too short"); + if (len % 2) + throw GeographicErr("OSGB string " + gridref + + " has odd number of characters"); + int + xh = 0, + yh = 0; + while (p < 2) { + int i = Utility::lookup(letters_, grid[p++]); + if (i < 0) + throw GeographicErr("Illegal prefix character " + gridref); + yh = yh * tilegrid_ + tilegrid_ - (i / tilegrid_) - 1; + xh = xh * tilegrid_ + (i % tilegrid_); + } + xh -= tileoffx_; + yh -= tileoffy_; + + int prec1 = (len - p)/2; + real + unit = tile_, + x1 = unit * xh, + y1 = unit * yh; + for (int i = 0; i < prec1; ++i) { + unit /= base_; + int + ix = Utility::lookup(digits_, grid[p + i]), + iy = Utility::lookup(digits_, grid[p + i + prec1]); + if (ix < 0 || iy < 0) + throw GeographicErr("Encountered a non-digit in " + gridref); + x1 += unit * ix; + y1 += unit * iy; + } + if (centerp) { + x1 += unit/2; + y1 += unit/2; + } + x = x1; + y = y1; + prec = prec1; + } + + void OSGB::CheckCoords(real x, real y) { + // Limits are all multiples of 100km and are all closed on the lower end + // and open on the upper end -- and this is reflected in the error + // messages. NaNs are let through. + if (x < minx_ || x >= maxx_) + throw GeographicErr("Easting " + Utility::str(int(floor(x/1000))) + + "km not in OSGB range [" + + Utility::str(minx_/1000) + "km, " + + Utility::str(maxx_/1000) + "km)"); + if (y < miny_ || y >= maxy_) + throw GeographicErr("Northing " + Utility::str(int(floor(y/1000))) + + "km not in OSGB range [" + + Utility::str(miny_/1000) + "km, " + + Utility::str(maxy_/1000) + "km)"); + } + +} // namespace GeographicLib diff --git a/common/local_libs/GeographicLib/src/PolarStereographic.cpp b/common/local_libs/GeographicLib/src/PolarStereographic.cpp new file mode 100644 index 0000000000..e7635ff74a --- /dev/null +++ b/common/local_libs/GeographicLib/src/PolarStereographic.cpp @@ -0,0 +1,109 @@ +/** + * \file PolarStereographic.cpp + * \brief Implementation for GeographicLib::PolarStereographic class + * + * Copyright (c) Charles Karney (2008-2020) and licensed + * under the MIT/X11 License. For more information, see + * https://geographiclib.sourceforge.io/ + **********************************************************************/ + +#include + +namespace GeographicLib { + + using namespace std; + + PolarStereographic::PolarStereographic(real a, real f, real k0) + : _a(a) + , _f(f) + , _e2(_f * (2 - _f)) + , _es((_f < 0 ? -1 : 1) * sqrt(abs(_e2))) + , _e2m(1 - _e2) + , _c( (1 - _f) * exp(Math::eatanhe(real(1), _es)) ) + , _k0(k0) + { + if (!(isfinite(_a) && _a > 0)) + throw GeographicErr("Equatorial radius is not positive"); + if (!(isfinite(_f) && _f < 1)) + throw GeographicErr("Polar semi-axis is not positive"); + if (!(isfinite(_k0) && _k0 > 0)) + throw GeographicErr("Scale is not positive"); + } + + const PolarStereographic& PolarStereographic::UPS() { + static const PolarStereographic ups(Constants::WGS84_a(), + Constants::WGS84_f(), + Constants::UPS_k0()); + return ups; + } + + // This formulation converts to conformal coordinates by tau = tan(phi) and + // tau' = tan(phi') where phi' is the conformal latitude. The formulas are: + // tau = tan(phi) + // secphi = hypot(1, tau) + // sig = sinh(e * atanh(e * tau / secphi)) + // taup = tan(phip) = tau * hypot(1, sig) - sig * hypot(1, tau) + // c = (1 - f) * exp(e * atanh(e)) + // + // Forward: + // rho = (2*k0*a/c) / (hypot(1, taup) + taup) (taup >= 0) + // = (2*k0*a/c) * (hypot(1, taup) - taup) (taup < 0) + // + // Reverse: + // taup = ((2*k0*a/c) / rho - rho / (2*k0*a/c))/2 + // + // Scale: + // k = (rho/a) * secphi * sqrt((1-e2) + e2 / secphi^2) + // + // In limit rho -> 0, tau -> inf, taup -> inf, secphi -> inf, secphip -> inf + // secphip = taup = exp(-e * atanh(e)) * tau = exp(-e * atanh(e)) * secphi + + void PolarStereographic::Forward(bool northp, real lat, real lon, + real& x, real& y, + real& gamma, real& k) const { + lat = Math::LatFix(lat); + lat *= northp ? 1 : -1; + real + tau = Math::tand(lat), + secphi = hypot(real(1), tau), + taup = Math::taupf(tau, _es), + rho = hypot(real(1), taup) + abs(taup); + rho = taup >= 0 ? (lat != 90 ? 1/rho : 0) : rho; + rho *= 2 * _k0 * _a / _c; + k = lat != 90 ? (rho / _a) * secphi * sqrt(_e2m + _e2 / Math::sq(secphi)) : + _k0; + Math::sincosd(lon, x, y); + x *= rho; + y *= (northp ? -rho : rho); + gamma = Math::AngNormalize(northp ? lon : -lon); + } + + void PolarStereographic::Reverse(bool northp, real x, real y, + real& lat, real& lon, + real& gamma, real& k) const { + real + rho = hypot(x, y), + t = rho != 0 ? rho / (2 * _k0 * _a / _c) : + Math::sq(numeric_limits::epsilon()), + taup = (1 / t - t) / 2, + tau = Math::tauf(taup, _es), + secphi = hypot(real(1), tau); + k = rho != 0 ? (rho / _a) * secphi * sqrt(_e2m + _e2 / Math::sq(secphi)) : + _k0; + lat = (northp ? 1 : -1) * Math::atand(tau); + lon = Math::atan2d(x, northp ? -y : y ); + gamma = Math::AngNormalize(northp ? lon : -lon); + } + + void PolarStereographic::SetScale(real lat, real k) { + if (!(isfinite(k) && k > 0)) + throw GeographicErr("Scale is not positive"); + if (!(-90 < lat && lat <= 90)) + throw GeographicErr("Latitude must be in (-90d, 90d]"); + real x, y, gamma, kold; + _k0 = 1; + Forward(true, lat, 0, x, y, gamma, kold); + _k0 *= k/kold; + } + +} // namespace GeographicLib diff --git a/common/local_libs/GeographicLib/src/PolygonArea.cpp b/common/local_libs/GeographicLib/src/PolygonArea.cpp new file mode 100644 index 0000000000..96414d7144 --- /dev/null +++ b/common/local_libs/GeographicLib/src/PolygonArea.cpp @@ -0,0 +1,180 @@ +/** + * \file PolygonArea.cpp + * \brief Implementation for GeographicLib::PolygonAreaT class + * + * Copyright (c) Charles Karney (2010-2019) and licensed + * under the MIT/X11 License. For more information, see + * https://geographiclib.sourceforge.io/ + **********************************************************************/ + +#include + +namespace GeographicLib { + + using namespace std; + + template + void PolygonAreaT::AddPoint(real lat, real lon) { + lat = Math::LatFix(lat); + lon = Math::AngNormalize(lon); + if (_num == 0) { + _lat0 = _lat1 = lat; + _lon0 = _lon1 = lon; + } else { + real s12, S12, t; + _earth.GenInverse(_lat1, _lon1, lat, lon, _mask, + s12, t, t, t, t, t, S12); + _perimetersum += s12; + if (!_polyline) { + _areasum += S12; + _crossings += transit(_lon1, lon); + } + _lat1 = lat; _lon1 = lon; + } + ++_num; + } + + template + void PolygonAreaT::AddEdge(real azi, real s) { + if (_num) { // Do nothing if _num is zero + real lat, lon, S12, t; + _earth.GenDirect(_lat1, _lon1, azi, false, s, _mask, + lat, lon, t, t, t, t, t, S12); + _perimetersum += s; + if (!_polyline) { + _areasum += S12; + _crossings += transitdirect(_lon1, lon); + lon = Math::AngNormalize(lon); + } + _lat1 = lat; _lon1 = lon; + ++_num; + } + } + + template + unsigned PolygonAreaT::Compute(bool reverse, bool sign, + real& perimeter, real& area) const + { + real s12, S12, t; + if (_num < 2) { + perimeter = 0; + if (!_polyline) + area = 0; + return _num; + } + if (_polyline) { + perimeter = _perimetersum(); + return _num; + } + _earth.GenInverse(_lat1, _lon1, _lat0, _lon0, _mask, + s12, t, t, t, t, t, S12); + perimeter = _perimetersum(s12); + Accumulator<> tempsum(_areasum); + tempsum += S12; + int crossings = _crossings + transit(_lon1, _lon0); + AreaReduce(tempsum, crossings, reverse, sign); + area = 0 + tempsum(); + return _num; + } + + template + unsigned PolygonAreaT::TestPoint(real lat, real lon, + bool reverse, bool sign, + real& perimeter, real& area) const + { + if (_num == 0) { + perimeter = 0; + if (!_polyline) + area = 0; + return 1; + } + perimeter = _perimetersum(); + real tempsum = _polyline ? 0 : _areasum(); + int crossings = _crossings; + unsigned num = _num + 1; + for (int i = 0; i < (_polyline ? 1 : 2); ++i) { + real s12, S12, t; + _earth.GenInverse(i == 0 ? _lat1 : lat, i == 0 ? _lon1 : lon, + i != 0 ? _lat0 : lat, i != 0 ? _lon0 : lon, + _mask, s12, t, t, t, t, t, S12); + perimeter += s12; + if (!_polyline) { + tempsum += S12; + crossings += transit(i == 0 ? _lon1 : lon, + i != 0 ? _lon0 : lon); + } + } + + if (_polyline) + return num; + + AreaReduce(tempsum, crossings, reverse, sign); + area = 0 + tempsum; + return num; + } + + template + unsigned PolygonAreaT::TestEdge(real azi, real s, + bool reverse, bool sign, + real& perimeter, real& area) const + { + if (_num == 0) { // we don't have a starting point! + perimeter = Math::NaN(); + if (!_polyline) + area = Math::NaN(); + return 0; + } + unsigned num = _num + 1; + perimeter = _perimetersum() + s; + if (_polyline) + return num; + + real tempsum = _areasum(); + int crossings = _crossings; + { + real lat, lon, s12, S12, t; + _earth.GenDirect(_lat1, _lon1, azi, false, s, _mask, + lat, lon, t, t, t, t, t, S12); + tempsum += S12; + crossings += transitdirect(_lon1, lon); + lon = Math::AngNormalize(lon); + _earth.GenInverse(lat, lon, _lat0, _lon0, _mask, + s12, t, t, t, t, t, S12); + perimeter += s12; + tempsum += S12; + crossings += transit(lon, _lon0); + } + + AreaReduce(tempsum, crossings, reverse, sign); + area = 0 + tempsum; + return num; + } + + template + template + void PolygonAreaT::AreaReduce(T& area, int crossings, bool reverse, + bool sign) const { + Remainder(area); + if (crossings & 1) area += (area < 0 ? 1 : -1) * _area0/2; + // area is with the clockwise sense. If !reverse convert to + // counter-clockwise convention. + if (!reverse) area *= -1; + // If sign put area in (-_area0/2, _area0/2], else put area in [0, _area0) + if (sign) { + if (area > _area0/2) + area -= _area0; + else if (area <= -_area0/2) + area += _area0; + } else { + if (area >= _area0) + area -= _area0; + else if (area < 0) + area += _area0; + } + } + + template class GEOGRAPHICLIB_EXPORT PolygonAreaT; + template class GEOGRAPHICLIB_EXPORT PolygonAreaT; + template class GEOGRAPHICLIB_EXPORT PolygonAreaT; + +} // namespace GeographicLib diff --git a/common/local_libs/GeographicLib/src/Rhumb.cpp b/common/local_libs/GeographicLib/src/Rhumb.cpp new file mode 100644 index 0000000000..741fd74b4a --- /dev/null +++ b/common/local_libs/GeographicLib/src/Rhumb.cpp @@ -0,0 +1,384 @@ +/** + * \file Rhumb.cpp + * \brief Implementation for GeographicLib::Rhumb and GeographicLib::RhumbLine + * classes + * + * Copyright (c) Charles Karney (2014-2020) and licensed + * under the MIT/X11 License. For more information, see + * https://geographiclib.sourceforge.io/ + **********************************************************************/ + +#include +#include + +namespace GeographicLib { + + using namespace std; + + Rhumb::Rhumb(real a, real f, bool exact) + : _ell(a, f) + , _exact(exact) + , _c2(_ell.Area() / 720) + { + // Generated by Maxima on 2015-05-15 08:24:04-04:00 +#if GEOGRAPHICLIB_RHUMBAREA_ORDER == 4 + static const real coeff[] = { + // R[0]/n^0, polynomial in n of order 4 + 691, 7860, -20160, 18900, 0, 56700, + // R[1]/n^1, polynomial in n of order 3 + 1772, -5340, 6930, -4725, 14175, + // R[2]/n^2, polynomial in n of order 2 + -1747, 1590, -630, 4725, + // R[3]/n^3, polynomial in n of order 1 + 104, -31, 315, + // R[4]/n^4, polynomial in n of order 0 + -41, 420, + }; // count = 20 +#elif GEOGRAPHICLIB_RHUMBAREA_ORDER == 5 + static const real coeff[] = { + // R[0]/n^0, polynomial in n of order 5 + -79036, 22803, 259380, -665280, 623700, 0, 1871100, + // R[1]/n^1, polynomial in n of order 4 + 41662, 58476, -176220, 228690, -155925, 467775, + // R[2]/n^2, polynomial in n of order 3 + 18118, -57651, 52470, -20790, 155925, + // R[3]/n^3, polynomial in n of order 2 + -23011, 17160, -5115, 51975, + // R[4]/n^4, polynomial in n of order 1 + 5480, -1353, 13860, + // R[5]/n^5, polynomial in n of order 0 + -668, 5775, + }; // count = 27 +#elif GEOGRAPHICLIB_RHUMBAREA_ORDER == 6 + static const real coeff[] = { + // R[0]/n^0, polynomial in n of order 6 + 128346268, -107884140, 31126095, 354053700, -908107200, 851350500, 0, + 2554051500LL, + // R[1]/n^1, polynomial in n of order 5 + -114456994, 56868630, 79819740, -240540300, 312161850, -212837625, + 638512875, + // R[2]/n^2, polynomial in n of order 4 + 51304574, 24731070, -78693615, 71621550, -28378350, 212837625, + // R[3]/n^3, polynomial in n of order 3 + 1554472, -6282003, 4684680, -1396395, 14189175, + // R[4]/n^4, polynomial in n of order 2 + -4913956, 3205800, -791505, 8108100, + // R[5]/n^5, polynomial in n of order 1 + 1092376, -234468, 2027025, + // R[6]/n^6, polynomial in n of order 0 + -313076, 2027025, + }; // count = 35 +#elif GEOGRAPHICLIB_RHUMBAREA_ORDER == 7 + static const real coeff[] = { + // R[0]/n^0, polynomial in n of order 7 + -317195588, 385038804, -323652420, 93378285, 1062161100, -2724321600LL, + 2554051500LL, 0, 7662154500LL, + // R[1]/n^1, polynomial in n of order 6 + 258618446, -343370982, 170605890, 239459220, -721620900, 936485550, + -638512875, 1915538625, + // R[2]/n^2, polynomial in n of order 5 + -248174686, 153913722, 74193210, -236080845, 214864650, -85135050, + 638512875, + // R[3]/n^3, polynomial in n of order 4 + 114450437, 23317080, -94230045, 70270200, -20945925, 212837625, + // R[4]/n^4, polynomial in n of order 3 + 15445736, -103193076, 67321800, -16621605, 170270100, + // R[5]/n^5, polynomial in n of order 2 + -27766753, 16385640, -3517020, 30405375, + // R[6]/n^6, polynomial in n of order 1 + 4892722, -939228, 6081075, + // R[7]/n^7, polynomial in n of order 0 + -3189007, 14189175, + }; // count = 44 +#elif GEOGRAPHICLIB_RHUMBAREA_ORDER == 8 + static const real coeff[] = { + // R[0]/n^0, polynomial in n of order 8 + 71374704821LL, -161769749880LL, 196369790040LL, -165062734200LL, + 47622925350LL, 541702161000LL, -1389404016000LL, 1302566265000LL, 0, + 3907698795000LL, + // R[1]/n^1, polynomial in n of order 7 + -13691187484LL, 65947703730LL, -87559600410LL, 43504501950LL, + 61062101100LL, -184013329500LL, 238803815250LL, -162820783125LL, + 488462349375LL, + // R[2]/n^2, polynomial in n of order 6 + 30802104839LL, -63284544930LL, 39247999110LL, 18919268550LL, + -60200615475LL, 54790485750LL, -21709437750LL, 162820783125LL, + // R[3]/n^3, polynomial in n of order 5 + -8934064508LL, 5836972287LL, 1189171080, -4805732295LL, 3583780200LL, + -1068242175, 10854718875LL, + // R[4]/n^4, polynomial in n of order 4 + 50072287748LL, 3938662680LL, -26314234380LL, 17167059000LL, + -4238509275LL, 43418875500LL, + // R[5]/n^5, polynomial in n of order 3 + 359094172, -9912730821LL, 5849673480LL, -1255576140, 10854718875LL, + // R[6]/n^6, polynomial in n of order 2 + -16053944387LL, 8733508770LL, -1676521980, 10854718875LL, + // R[7]/n^7, polynomial in n of order 1 + 930092876, -162639357, 723647925, + // R[8]/n^8, polynomial in n of order 0 + -673429061, 1929727800, + }; // count = 54 +#else +#error "Bad value for GEOGRAPHICLIB_RHUMBAREA_ORDER" +#endif + static_assert(sizeof(coeff) / sizeof(real) == + ((maxpow_ + 1) * (maxpow_ + 4))/2, + "Coefficient array size mismatch for Rhumb"); + real d = 1; + int o = 0; + for (int l = 0; l <= maxpow_; ++l) { + int m = maxpow_ - l; + // R[0] is just an integration constant so it cancels when evaluating a + // definite integral. So don't bother computing it. It won't be used + // when invoking SinCosSeries. + if (l) + _R[l] = d * Math::polyval(m, coeff + o, _ell._n) / coeff[o + m + 1]; + o += m + 2; + d *= _ell._n; + } + // Post condition: o == sizeof(alpcoeff) / sizeof(real) + } + + const Rhumb& Rhumb::WGS84() { + static const Rhumb + wgs84(Constants::WGS84_a(), Constants::WGS84_f(), false); + return wgs84; + } + + void Rhumb::GenInverse(real lat1, real lon1, real lat2, real lon2, + unsigned outmask, + real& s12, real& azi12, real& S12) const { + real + lon12 = Math::AngDiff(lon1, lon2), + psi1 = _ell.IsometricLatitude(lat1), + psi2 = _ell.IsometricLatitude(lat2), + psi12 = psi2 - psi1, + h = hypot(lon12, psi12); + if (outmask & AZIMUTH) + azi12 = Math::atan2d(lon12, psi12); + if (outmask & DISTANCE) { + real dmudpsi = DIsometricToRectifying(psi2, psi1); + s12 = h * dmudpsi * _ell.QuarterMeridian() / 90; + } + if (outmask & AREA) + S12 = _c2 * lon12 * + MeanSinXi(psi2 * Math::degree(), psi1 * Math::degree()); + } + + RhumbLine Rhumb::Line(real lat1, real lon1, real azi12) const + { return RhumbLine(*this, lat1, lon1, azi12, _exact); } + + void Rhumb::GenDirect(real lat1, real lon1, real azi12, real s12, + unsigned outmask, + real& lat2, real& lon2, real& S12) const + { Line(lat1, lon1, azi12).GenPosition(s12, outmask, lat2, lon2, S12); } + + Math::real Rhumb::DE(real x, real y) const { + const EllipticFunction& ei = _ell._ell; + real d = x - y; + if (x * y <= 0) + return d != 0 ? (ei.E(x) - ei.E(y)) / d : 1; + // See DLMF: Eqs (19.11.2) and (19.11.4) letting + // theta -> x, phi -> -y, psi -> z + // + // (E(x) - E(y)) / d = E(z)/d - k2 * sin(x) * sin(y) * sin(z)/d + // + // tan(z/2) = (sin(x)*Delta(y) - sin(y)*Delta(x)) / (cos(x) + cos(y)) + // = d * Dsin(x,y) * (sin(x) + sin(y))/(cos(x) + cos(y)) / + // (sin(x)*Delta(y) + sin(y)*Delta(x)) + // = t = d * Dt + // sin(z) = 2*t/(1+t^2); cos(z) = (1-t^2)/(1+t^2) + // Alt (this only works for |z| <= pi/2 -- however, this conditions holds + // if x*y > 0): + // sin(z) = d * Dsin(x,y) * (sin(x) + sin(y))/ + // (sin(x)*cos(y)*Delta(y) + sin(y)*cos(x)*Delta(x)) + // cos(z) = sqrt((1-sin(z))*(1+sin(z))) + real sx = sin(x), sy = sin(y), cx = cos(x), cy = cos(y); + real Dt = Dsin(x, y) * (sx + sy) / + ((cx + cy) * (sx * ei.Delta(sy, cy) + sy * ei.Delta(sx, cx))), + t = d * Dt, Dsz = 2 * Dt / (1 + t*t), + sz = d * Dsz, cz = (1 - t) * (1 + t) / (1 + t*t); + return ((sz != 0 ? ei.E(sz, cz, ei.Delta(sz, cz)) / sz : 1) + - ei.k2() * sx * sy) * Dsz; + } + + Math::real Rhumb::DRectifying(real latx, real laty) const { + real + tbetx = _ell._f1 * Math::tand(latx), + tbety = _ell._f1 * Math::tand(laty); + return (Math::pi()/2) * _ell._b * _ell._f1 * DE(atan(tbetx), atan(tbety)) + * Dtan(latx, laty) * Datan(tbetx, tbety) / _ell.QuarterMeridian(); + } + + Math::real Rhumb::DIsometric(real latx, real laty) const { + real + phix = latx * Math::degree(), tx = Math::tand(latx), + phiy = laty * Math::degree(), ty = Math::tand(laty); + return Dasinh(tx, ty) * Dtan(latx, laty) + - Deatanhe(sin(phix), sin(phiy)) * Dsin(phix, phiy); + } + + Math::real Rhumb::SinCosSeries(bool sinp, + real x, real y, const real c[], int n) { + // N.B. n >= 0 and c[] has n+1 elements 0..n, of which c[0] is ignored. + // + // Use Clenshaw summation to evaluate + // m = (g(x) + g(y)) / 2 -- mean value + // s = (g(x) - g(y)) / (x - y) -- average slope + // where + // g(x) = sum(c[j]*SC(2*j*x), j = 1..n) + // SC = sinp ? sin : cos + // CS = sinp ? cos : sin + // + // This function returns only s; m is discarded. + // + // Write + // t = [m; s] + // t = sum(c[j] * f[j](x,y), j = 1..n) + // where + // f[j](x,y) = [ (SC(2*j*x)+SC(2*j*y))/2 ] + // [ (SC(2*j*x)-SC(2*j*y))/d ] + // + // = [ cos(j*d)*SC(j*p) ] + // [ +/-(2/d)*sin(j*d)*CS(j*p) ] + // (+/- = sinp ? + : -) and + // p = x+y, d = x-y + // + // f[j+1](x,y) = A * f[j](x,y) - f[j-1](x,y) + // + // A = [ 2*cos(p)*cos(d) -sin(p)*sin(d)*d] + // [ -4*sin(p)*sin(d)/d 2*cos(p)*cos(d) ] + // + // Let b[n+1] = b[n+2] = [0 0; 0 0] + // b[j] = A * b[j+1] - b[j+2] + c[j] * I for j = n..1 + // t = (c[0] * I - b[2]) * f[0](x,y) + b[1] * f[1](x,y) + // c[0] is not accessed for s = t[2] + real p = x + y, d = x - y, + cp = cos(p), cd = cos(d), + sp = sin(p), sd = d != 0 ? sin(d)/d : 1, + m = 2 * cp * cd, s = sp * sd; + // 2x2 matrices stored in row-major order + const real a[4] = {m, -s * d * d, -4 * s, m}; + real ba[4] = {0, 0, 0, 0}; + real bb[4] = {0, 0, 0, 0}; + real* b1 = ba; + real* b2 = bb; + if (n > 0) b1[0] = b1[3] = c[n]; + for (int j = n - 1; j > 0; --j) { // j = n-1 .. 1 + swap(b1, b2); + // b1 = A * b2 - b1 + c[j] * I + b1[0] = a[0] * b2[0] + a[1] * b2[2] - b1[0] + c[j]; + b1[1] = a[0] * b2[1] + a[1] * b2[3] - b1[1]; + b1[2] = a[2] * b2[0] + a[3] * b2[2] - b1[2]; + b1[3] = a[2] * b2[1] + a[3] * b2[3] - b1[3] + c[j]; + } + // Here are the full expressions for m and s + // m = (c[0] - b2[0]) * f01 - b2[1] * f02 + b1[0] * f11 + b1[1] * f12; + // s = - b2[2] * f01 + (c[0] - b2[3]) * f02 + b1[2] * f11 + b1[3] * f12; + if (sinp) { + // real f01 = 0, f02 = 0; + real f11 = cd * sp, f12 = 2 * sd * cp; + // m = b1[0] * f11 + b1[1] * f12; + s = b1[2] * f11 + b1[3] * f12; + } else { + // real f01 = 1, f02 = 0; + real f11 = cd * cp, f12 = - 2 * sd * sp; + // m = c[0] - b2[0] + b1[0] * f11 + b1[1] * f12; + s = - b2[2] + b1[2] * f11 + b1[3] * f12; + } + return s; + } + + Math::real Rhumb::DConformalToRectifying(real chix, real chiy) const { + return 1 + SinCosSeries(true, chix, chiy, + _ell.ConformalToRectifyingCoeffs(), tm_maxord); + } + + Math::real Rhumb::DRectifyingToConformal(real mux, real muy) const { + return 1 - SinCosSeries(true, mux, muy, + _ell.RectifyingToConformalCoeffs(), tm_maxord); + } + + Math::real Rhumb::DIsometricToRectifying(real psix, real psiy) const { + if (_exact) { + real + latx = _ell.InverseIsometricLatitude(psix), + laty = _ell.InverseIsometricLatitude(psiy); + return DRectifying(latx, laty) / DIsometric(latx, laty); + } else { + psix *= Math::degree(); + psiy *= Math::degree(); + return DConformalToRectifying(gd(psix), gd(psiy)) * Dgd(psix, psiy); + } + } + + Math::real Rhumb::DRectifyingToIsometric(real mux, real muy) const { + real + latx = _ell.InverseRectifyingLatitude(mux/Math::degree()), + laty = _ell.InverseRectifyingLatitude(muy/Math::degree()); + return _exact ? + DIsometric(latx, laty) / DRectifying(latx, laty) : + Dgdinv(Math::taupf(Math::tand(latx), _ell._es), + Math::taupf(Math::tand(laty), _ell._es)) * + DRectifyingToConformal(mux, muy); + } + + Math::real Rhumb::MeanSinXi(real psix, real psiy) const { + return Dlog(cosh(psix), cosh(psiy)) * Dcosh(psix, psiy) + + SinCosSeries(false, gd(psix), gd(psiy), _R, maxpow_) * Dgd(psix, psiy); + } + + RhumbLine::RhumbLine(const Rhumb& rh, real lat1, real lon1, real azi12, + bool exact) + : _rh(rh) + , _exact(exact) + , _lat1(Math::LatFix(lat1)) + , _lon1(lon1) + , _azi12(Math::AngNormalize(azi12)) + { + real alp12 = _azi12 * Math::degree(); + _salp = _azi12 == -180 ? 0 : sin(alp12); + _calp = abs(_azi12) == 90 ? 0 : cos(alp12); + _mu1 = _rh._ell.RectifyingLatitude(lat1); + _psi1 = _rh._ell.IsometricLatitude(lat1); + _r1 = _rh._ell.CircleRadius(lat1); + } + + void RhumbLine::GenPosition(real s12, unsigned outmask, + real& lat2, real& lon2, real& S12) const { + real + mu12 = s12 * _calp * 90 / _rh._ell.QuarterMeridian(), + mu2 = _mu1 + mu12; + real psi2, lat2x, lon2x; + if (abs(mu2) <= 90) { + if (_calp != 0) { + lat2x = _rh._ell.InverseRectifyingLatitude(mu2); + real psi12 = _rh.DRectifyingToIsometric( mu2 * Math::degree(), + _mu1 * Math::degree()) * mu12; + lon2x = _salp * psi12 / _calp; + psi2 = _psi1 + psi12; + } else { + lat2x = _lat1; + lon2x = _salp * s12 / (_r1 * Math::degree()); + psi2 = _psi1; + } + if (outmask & AREA) + S12 = _rh._c2 * lon2x * + _rh.MeanSinXi(_psi1 * Math::degree(), psi2 * Math::degree()); + lon2x = outmask & LONG_UNROLL ? _lon1 + lon2x : + Math::AngNormalize(Math::AngNormalize(_lon1) + lon2x); + } else { + // Reduce to the interval [-180, 180) + mu2 = Math::AngNormalize(mu2); + // Deal with points on the anti-meridian + if (abs(mu2) > 90) mu2 = Math::AngNormalize(180 - mu2); + lat2x = _rh._ell.InverseRectifyingLatitude(mu2); + lon2x = Math::NaN(); + if (outmask & AREA) + S12 = Math::NaN(); + } + if (outmask & LATITUDE) lat2 = lat2x; + if (outmask & LONGITUDE) lon2 = lon2x; + } + +} // namespace GeographicLib diff --git a/common/local_libs/GeographicLib/src/SphericalEngine.cpp b/common/local_libs/GeographicLib/src/SphericalEngine.cpp new file mode 100644 index 0000000000..2de7589d9a --- /dev/null +++ b/common/local_libs/GeographicLib/src/SphericalEngine.cpp @@ -0,0 +1,510 @@ +/** + * \file SphericalEngine.cpp + * \brief Implementation for GeographicLib::SphericalEngine class + * + * Copyright (c) Charles Karney (2011-2020) and licensed + * under the MIT/X11 License. For more information, see + * https://geographiclib.sourceforge.io/ + * + * The general sum is\verbatim + V(r, theta, lambda) = sum(n = 0..N) sum(m = 0..n) + q^(n+1) * (C[n,m] * cos(m*lambda) + S[n,m] * sin(m*lambda)) * P[n,m](t) +\endverbatim + * where t = cos(theta), q = a/r. In addition write u = + * sin(theta). + * + * P[n,m] is a normalized associated Legendre function of degree + * n and order m. Here the formulas are given for full + * normalized functions (usually denoted Pbar). + * + * Rewrite outer sum\verbatim + V(r, theta, lambda) = sum(m = 0..N) * P[m,m](t) * q^(m+1) * + [Sc[m] * cos(m*lambda) + Ss[m] * sin(m*lambda)] +\endverbatim + * where the inner sums are\verbatim + Sc[m] = sum(n = m..N) q^(n-m) * C[n,m] * P[n,m](t)/P[m,m](t) + Ss[m] = sum(n = m..N) q^(n-m) * S[n,m] * P[n,m](t)/P[m,m](t) +\endverbatim + * Evaluate sums via Clenshaw method. The overall framework is similar to + * Deakin with the following changes: + * - Clenshaw summation is used to roll the computation of + * cos(m*lambda) and sin(m*lambda) into the evaluation of + * the outer sum (rather than independently computing an array of these + * trigonometric terms). + * - Scale the coefficients to guard against overflow when N is large. + * . + * For the general framework of Clenshaw, see + * http://mathworld.wolfram.com/ClenshawRecurrenceFormula.html + * + * Let\verbatim + S = sum(k = 0..N) c[k] * F[k](x) + F[n+1](x) = alpha[n](x) * F[n](x) + beta[n](x) * F[n-1](x) +\endverbatim + * Evaluate S with\verbatim + y[N+2] = y[N+1] = 0 + y[k] = alpha[k] * y[k+1] + beta[k+1] * y[k+2] + c[k] + S = c[0] * F[0] + y[1] * F[1] + beta[1] * F[0] * y[2] +\endverbatim + * \e IF F[0](x) = 1 and beta(0,x) = 0, then F[1](x) = + * alpha(0,x) and we can continue the recursion for y[k] until + * y[0], giving\verbatim + S = y[0] +\endverbatim + * + * Evaluating the inner sum\verbatim + l = n-m; n = l+m + Sc[m] = sum(l = 0..N-m) C[l+m,m] * q^l * P[l+m,m](t)/P[m,m](t) + F[l] = q^l * P[l+m,m](t)/P[m,m](t) +\endverbatim + * Holmes + Featherstone, Eq. (11), give\verbatim + P[n,m] = sqrt((2*n-1)*(2*n+1)/((n-m)*(n+m))) * t * P[n-1,m] - + sqrt((2*n+1)*(n+m-1)*(n-m-1)/((n-m)*(n+m)*(2*n-3))) * P[n-2,m] +\endverbatim + * thus\verbatim + alpha[l] = t * q * sqrt(((2*n+1)*(2*n+3))/ + ((n-m+1)*(n+m+1))) + beta[l+1] = - q^2 * sqrt(((n-m+1)*(n+m+1)*(2*n+5))/ + ((n-m+2)*(n+m+2)*(2*n+1))) +\endverbatim + * In this case, F[0] = 1 and beta[0] = 0, so the Sc[m] + * = y[0]. + * + * Evaluating the outer sum\verbatim + V = sum(m = 0..N) Sc[m] * q^(m+1) * cos(m*lambda) * P[m,m](t) + + sum(m = 0..N) Ss[m] * q^(m+1) * cos(m*lambda) * P[m,m](t) + F[m] = q^(m+1) * cos(m*lambda) * P[m,m](t) [or sin(m*lambda)] +\endverbatim + * Holmes + Featherstone, Eq. (13), give\verbatim + P[m,m] = u * sqrt((2*m+1)/((m>1?2:1)*m)) * P[m-1,m-1] +\endverbatim + * also, we have\verbatim + cos((m+1)*lambda) = 2*cos(lambda)*cos(m*lambda) - cos((m-1)*lambda) +\endverbatim + * thus\verbatim + alpha[m] = 2*cos(lambda) * sqrt((2*m+3)/(2*(m+1))) * u * q + = cos(lambda) * sqrt( 2*(2*m+3)/(m+1) ) * u * q + beta[m+1] = -sqrt((2*m+3)*(2*m+5)/(4*(m+1)*(m+2))) * u^2 * q^2 + * (m == 0 ? sqrt(2) : 1) +\endverbatim + * Thus\verbatim + F[0] = q [or 0] + F[1] = cos(lambda) * sqrt(3) * u * q^2 [or sin(lambda)] + beta[1] = - sqrt(15/4) * u^2 * q^2 +\endverbatim + * + * Here is how the various components of the gradient are computed + * + * Differentiate wrt r\verbatim + d q^(n+1) / dr = (-1/r) * (n+1) * q^(n+1) +\endverbatim + * so multiply C[n,m] by n+1 in inner sum and multiply the + * sum by -1/r. + * + * Differentiate wrt lambda\verbatim + d cos(m*lambda) = -m * sin(m*lambda) + d sin(m*lambda) = m * cos(m*lambda) +\endverbatim + * so multiply terms by m in outer sum and swap sine and cosine + * variables. + * + * Differentiate wrt theta\verbatim + dV/dtheta = V' = -u * dV/dt = -u * V' +\endverbatim + * here ' denotes differentiation wrt theta.\verbatim + d/dtheta (Sc[m] * P[m,m](t)) = Sc'[m] * P[m,m](t) + Sc[m] * P'[m,m](t) +\endverbatim + * Now P[m,m](t) = const * u^m, so P'[m,m](t) = m * t/u * + * P[m,m](t), thus\verbatim + d/dtheta (Sc[m] * P[m,m](t)) = (Sc'[m] + m * t/u * Sc[m]) * P[m,m](t) +\endverbatim + * Clenshaw recursion for Sc[m] reads\verbatim + y[k] = alpha[k] * y[k+1] + beta[k+1] * y[k+2] + c[k] +\endverbatim + * Substituting alpha[k] = const * t, alpha'[k] = -u/t * + * alpha[k], beta'[k] = c'[k] = 0 gives\verbatim + y'[k] = alpha[k] * y'[k+1] + beta[k+1] * y'[k+2] - u/t * alpha[k] * y[k+1] +\endverbatim + * + * Finally, given the derivatives of V, we can compute the components + * of the gradient in spherical coordinates and transform the result into + * cartesian coordinates. + **********************************************************************/ + +#include +#include +#include + +#if defined(_MSC_VER) +// Squelch warnings about constant conditional expressions and potentially +// uninitialized local variables +# pragma warning (disable: 4127 4701) +#endif + +namespace GeographicLib { + + using namespace std; + + vector& SphericalEngine::sqrttable() { + static vector sqrttable(0); + return sqrttable; + } + + template + Math::real SphericalEngine::Value(const coeff c[], const real f[], + real x, real y, real z, real a, + real& gradx, real& grady, real& gradz) + { + static_assert(L > 0, "L must be positive"); + static_assert(norm == FULL || norm == SCHMIDT, "Unknown normalization"); + int N = c[0].nmx(), M = c[0].mmx(); + + real + p = hypot(x, y), + cl = p != 0 ? x / p : 1, // cos(lambda); at pole, pick lambda = 0 + sl = p != 0 ? y / p : 0, // sin(lambda) + r = hypot(z, p), + t = r != 0 ? z / r : 0, // cos(theta); at origin, pick theta = pi/2 + u = r != 0 ? max(p / r, eps()) : 1, // sin(theta); but avoid the pole + q = a / r; + real + q2 = Math::sq(q), + uq = u * q, + uq2 = Math::sq(uq), + tu = t / u; + // Initialize outer sum + real vc = 0, vc2 = 0, vs = 0, vs2 = 0; // v [N + 1], v [N + 2] + // vr, vt, vl and similar w variable accumulate the sums for the + // derivatives wrt r, theta, and lambda, respectively. + real vrc = 0, vrc2 = 0, vrs = 0, vrs2 = 0; // vr[N + 1], vr[N + 2] + real vtc = 0, vtc2 = 0, vts = 0, vts2 = 0; // vt[N + 1], vt[N + 2] + real vlc = 0, vlc2 = 0, vls = 0, vls2 = 0; // vl[N + 1], vl[N + 2] + int k[L]; + const vector& root( sqrttable() ); + for (int m = M; m >= 0; --m) { // m = M .. 0 + // Initialize inner sum + real + wc = 0, wc2 = 0, ws = 0, ws2 = 0, // w [N - m + 1], w [N - m + 2] + wrc = 0, wrc2 = 0, wrs = 0, wrs2 = 0, // wr[N - m + 1], wr[N - m + 2] + wtc = 0, wtc2 = 0, wts = 0, wts2 = 0; // wt[N - m + 1], wt[N - m + 2] + for (int l = 0; l < L; ++l) + k[l] = c[l].index(N, m) + 1; + for (int n = N; n >= m; --n) { // n = N .. m; l = N - m .. 0 + real w, A, Ax, B, R; // alpha[l], beta[l + 1] + switch (norm) { + case FULL: + w = root[2 * n + 1] / (root[n - m + 1] * root[n + m + 1]); + Ax = q * w * root[2 * n + 3]; + A = t * Ax; + B = - q2 * root[2 * n + 5] / + (w * root[n - m + 2] * root[n + m + 2]); + break; + case SCHMIDT: + w = root[n - m + 1] * root[n + m + 1]; + Ax = q * (2 * n + 1) / w; + A = t * Ax; + B = - q2 * w / (root[n - m + 2] * root[n + m + 2]); + break; + default: break; // To suppress warning message from Visual Studio + } + R = c[0].Cv(--k[0]); + for (int l = 1; l < L; ++l) + R += c[l].Cv(--k[l], n, m, f[l]); + R *= scale(); + w = A * wc + B * wc2 + R; wc2 = wc; wc = w; + if (gradp) { + w = A * wrc + B * wrc2 + (n + 1) * R; wrc2 = wrc; wrc = w; + w = A * wtc + B * wtc2 - u*Ax * wc2; wtc2 = wtc; wtc = w; + } + if (m) { + R = c[0].Sv(k[0]); + for (int l = 1; l < L; ++l) + R += c[l].Sv(k[l], n, m, f[l]); + R *= scale(); + w = A * ws + B * ws2 + R; ws2 = ws; ws = w; + if (gradp) { + w = A * wrs + B * wrs2 + (n + 1) * R; wrs2 = wrs; wrs = w; + w = A * wts + B * wts2 - u*Ax * ws2; wts2 = wts; wts = w; + } + } + } + // Now Sc[m] = wc, Ss[m] = ws + // Sc'[m] = wtc, Ss'[m] = wtc + if (m) { + real v, A, B; // alpha[m], beta[m + 1] + switch (norm) { + case FULL: + v = root[2] * root[2 * m + 3] / root[m + 1]; + A = cl * v * uq; + B = - v * root[2 * m + 5] / (root[8] * root[m + 2]) * uq2; + break; + case SCHMIDT: + v = root[2] * root[2 * m + 1] / root[m + 1]; + A = cl * v * uq; + B = - v * root[2 * m + 3] / (root[8] * root[m + 2]) * uq2; + break; + default: break; // To suppress warning message from Visual Studio + } + v = A * vc + B * vc2 + wc ; vc2 = vc ; vc = v; + v = A * vs + B * vs2 + ws ; vs2 = vs ; vs = v; + if (gradp) { + // Include the terms Sc[m] * P'[m,m](t) and Ss[m] * P'[m,m](t) + wtc += m * tu * wc; wts += m * tu * ws; + v = A * vrc + B * vrc2 + wrc; vrc2 = vrc; vrc = v; + v = A * vrs + B * vrs2 + wrs; vrs2 = vrs; vrs = v; + v = A * vtc + B * vtc2 + wtc; vtc2 = vtc; vtc = v; + v = A * vts + B * vts2 + wts; vts2 = vts; vts = v; + v = A * vlc + B * vlc2 + m*ws; vlc2 = vlc; vlc = v; + v = A * vls + B * vls2 - m*wc; vls2 = vls; vls = v; + } + } else { + real A, B, qs; + switch (norm) { + case FULL: + A = root[3] * uq; // F[1]/(q*cl) or F[1]/(q*sl) + B = - root[15]/2 * uq2; // beta[1]/q + break; + case SCHMIDT: + A = uq; + B = - root[3]/2 * uq2; + break; + default: break; // To suppress warning message from Visual Studio + } + qs = q / scale(); + vc = qs * (wc + A * (cl * vc + sl * vs ) + B * vc2); + if (gradp) { + qs /= r; + // The components of the gradient in spherical coordinates are + // r: dV/dr + // theta: 1/r * dV/dtheta + // lambda: 1/(r*u) * dV/dlambda + vrc = - qs * (wrc + A * (cl * vrc + sl * vrs) + B * vrc2); + vtc = qs * (wtc + A * (cl * vtc + sl * vts) + B * vtc2); + vlc = qs / u * ( A * (cl * vlc + sl * vls) + B * vlc2); + } + } + } + + if (gradp) { + // Rotate into cartesian (geocentric) coordinates + gradx = cl * (u * vrc + t * vtc) - sl * vlc; + grady = sl * (u * vrc + t * vtc) + cl * vlc; + gradz = t * vrc - u * vtc ; + } + return vc; + } + + template + CircularEngine SphericalEngine::Circle(const coeff c[], const real f[], + real p, real z, real a) { + + static_assert(L > 0, "L must be positive"); + static_assert(norm == FULL || norm == SCHMIDT, "Unknown normalization"); + int N = c[0].nmx(), M = c[0].mmx(); + + real + r = hypot(z, p), + t = r != 0 ? z / r : 0, // cos(theta); at origin, pick theta = pi/2 + u = r != 0 ? max(p / r, eps()) : 1, // sin(theta); but avoid the pole + q = a / r; + real + q2 = Math::sq(q), + tu = t / u; + CircularEngine circ(M, gradp, norm, a, r, u, t); + int k[L]; + const vector& root( sqrttable() ); + for (int m = M; m >= 0; --m) { // m = M .. 0 + // Initialize inner sum + real + wc = 0, wc2 = 0, ws = 0, ws2 = 0, // w [N - m + 1], w [N - m + 2] + wrc = 0, wrc2 = 0, wrs = 0, wrs2 = 0, // wr[N - m + 1], wr[N - m + 2] + wtc = 0, wtc2 = 0, wts = 0, wts2 = 0; // wt[N - m + 1], wt[N - m + 2] + for (int l = 0; l < L; ++l) + k[l] = c[l].index(N, m) + 1; + for (int n = N; n >= m; --n) { // n = N .. m; l = N - m .. 0 + real w, A, Ax, B, R; // alpha[l], beta[l + 1] + switch (norm) { + case FULL: + w = root[2 * n + 1] / (root[n - m + 1] * root[n + m + 1]); + Ax = q * w * root[2 * n + 3]; + A = t * Ax; + B = - q2 * root[2 * n + 5] / + (w * root[n - m + 2] * root[n + m + 2]); + break; + case SCHMIDT: + w = root[n - m + 1] * root[n + m + 1]; + Ax = q * (2 * n + 1) / w; + A = t * Ax; + B = - q2 * w / (root[n - m + 2] * root[n + m + 2]); + break; + default: break; // To suppress warning message from Visual Studio + } + R = c[0].Cv(--k[0]); + for (int l = 1; l < L; ++l) + R += c[l].Cv(--k[l], n, m, f[l]); + R *= scale(); + w = A * wc + B * wc2 + R; wc2 = wc; wc = w; + if (gradp) { + w = A * wrc + B * wrc2 + (n + 1) * R; wrc2 = wrc; wrc = w; + w = A * wtc + B * wtc2 - u*Ax * wc2; wtc2 = wtc; wtc = w; + } + if (m) { + R = c[0].Sv(k[0]); + for (int l = 1; l < L; ++l) + R += c[l].Sv(k[l], n, m, f[l]); + R *= scale(); + w = A * ws + B * ws2 + R; ws2 = ws; ws = w; + if (gradp) { + w = A * wrs + B * wrs2 + (n + 1) * R; wrs2 = wrs; wrs = w; + w = A * wts + B * wts2 - u*Ax * ws2; wts2 = wts; wts = w; + } + } + } + if (!gradp) + circ.SetCoeff(m, wc, ws); + else { + // Include the terms Sc[m] * P'[m,m](t) and Ss[m] * P'[m,m](t) + wtc += m * tu * wc; wts += m * tu * ws; + circ.SetCoeff(m, wc, ws, wrc, wrs, wtc, wts); + } + } + + return circ; + } + + void SphericalEngine::RootTable(int N) { + // Need square roots up to max(2 * N + 5, 15). + vector& root( sqrttable() ); + int L = max(2 * N + 5, 15) + 1, oldL = int(root.size()); + if (oldL >= L) + return; + root.resize(L); + for (int l = oldL; l < L; ++l) + root[l] = sqrt(real(l)); + } + + void SphericalEngine::coeff::readcoeffs(istream& stream, int& N, int& M, + vector& C, + vector& S, + bool truncate) { + if (truncate) { + if (!((N >= M && M >= 0) || (N == -1 && M == -1))) + // The last condition is that M = -1 implies N = -1. + throw GeographicErr("Bad requested degree and order " + + Utility::str(N) + " " + Utility::str(M)); + } + int nm[2]; + Utility::readarray(stream, nm, 2); + int N0 = nm[0], M0 = nm[1]; + if (!((N0 >= M0 && M0 >= 0) || (N0 == -1 && M0 == -1))) + // The last condition is that M0 = -1 implies N0 = -1. + throw GeographicErr("Bad degree and order " + + Utility::str(N0) + " " + Utility::str(M0)); + N = truncate ? min(N, N0) : N0; + M = truncate ? min(M, M0) : M0; + C.resize(SphericalEngine::coeff::Csize(N, M)); + S.resize(SphericalEngine::coeff::Ssize(N, M)); + int skip = (SphericalEngine::coeff::Csize(N0, M0) - + SphericalEngine::coeff::Csize(N0, M )) * sizeof(double); + if (N == N0) { + Utility::readarray(stream, C); + if (skip) stream.seekg(streamoff(skip), ios::cur); + Utility::readarray(stream, S); + if (skip) stream.seekg(streamoff(skip), ios::cur); + } else { + for (int m = 0, k = 0; m <= M; ++m) { + Utility::readarray(stream, &C[k], N + 1 - m); + stream.seekg((N0 - N) * sizeof(double), ios::cur); + k += N + 1 - m; + } + if (skip) stream.seekg(streamoff(skip), ios::cur); + for (int m = 1, k = 0; m <= M; ++m) { + Utility::readarray(stream, &S[k], N + 1 - m); + stream.seekg((N0 - N) * sizeof(double), ios::cur); + k += N + 1 - m; + } + if (skip) stream.seekg(streamoff(skip), ios::cur); + } + return; + } + + /// \cond SKIP + template Math::real GEOGRAPHICLIB_EXPORT + SphericalEngine::Value + (const coeff[], const real[], real, real, real, real, real&, real&, real&); + template Math::real GEOGRAPHICLIB_EXPORT + SphericalEngine::Value + (const coeff[], const real[], real, real, real, real, real&, real&, real&); + template Math::real GEOGRAPHICLIB_EXPORT + SphericalEngine::Value + (const coeff[], const real[], real, real, real, real, real&, real&, real&); + template Math::real GEOGRAPHICLIB_EXPORT + SphericalEngine::Value + (const coeff[], const real[], real, real, real, real, real&, real&, real&); + + template Math::real GEOGRAPHICLIB_EXPORT + SphericalEngine::Value + (const coeff[], const real[], real, real, real, real, real&, real&, real&); + template Math::real GEOGRAPHICLIB_EXPORT + SphericalEngine::Value + (const coeff[], const real[], real, real, real, real, real&, real&, real&); + template Math::real GEOGRAPHICLIB_EXPORT + SphericalEngine::Value + (const coeff[], const real[], real, real, real, real, real&, real&, real&); + template Math::real GEOGRAPHICLIB_EXPORT + SphericalEngine::Value + (const coeff[], const real[], real, real, real, real, real&, real&, real&); + + template Math::real GEOGRAPHICLIB_EXPORT + SphericalEngine::Value + (const coeff[], const real[], real, real, real, real, real&, real&, real&); + template Math::real GEOGRAPHICLIB_EXPORT + SphericalEngine::Value + (const coeff[], const real[], real, real, real, real, real&, real&, real&); + template Math::real GEOGRAPHICLIB_EXPORT + SphericalEngine::Value + (const coeff[], const real[], real, real, real, real, real&, real&, real&); + template Math::real GEOGRAPHICLIB_EXPORT + SphericalEngine::Value + (const coeff[], const real[], real, real, real, real, real&, real&, real&); + + template CircularEngine GEOGRAPHICLIB_EXPORT + SphericalEngine::Circle + (const coeff[], const real[], real, real, real); + template CircularEngine GEOGRAPHICLIB_EXPORT + SphericalEngine::Circle + (const coeff[], const real[], real, real, real); + template CircularEngine GEOGRAPHICLIB_EXPORT + SphericalEngine::Circle + (const coeff[], const real[], real, real, real); + template CircularEngine GEOGRAPHICLIB_EXPORT + SphericalEngine::Circle + (const coeff[], const real[], real, real, real); + + template CircularEngine GEOGRAPHICLIB_EXPORT + SphericalEngine::Circle + (const coeff[], const real[], real, real, real); + template CircularEngine GEOGRAPHICLIB_EXPORT + SphericalEngine::Circle + (const coeff[], const real[], real, real, real); + template CircularEngine GEOGRAPHICLIB_EXPORT + SphericalEngine::Circle + (const coeff[], const real[], real, real, real); + template CircularEngine GEOGRAPHICLIB_EXPORT + SphericalEngine::Circle + (const coeff[], const real[], real, real, real); + + template CircularEngine GEOGRAPHICLIB_EXPORT + SphericalEngine::Circle + (const coeff[], const real[], real, real, real); + template CircularEngine GEOGRAPHICLIB_EXPORT + SphericalEngine::Circle + (const coeff[], const real[], real, real, real); + template CircularEngine GEOGRAPHICLIB_EXPORT + SphericalEngine::Circle + (const coeff[], const real[], real, real, real); + template CircularEngine GEOGRAPHICLIB_EXPORT + SphericalEngine::Circle + (const coeff[], const real[], real, real, real); + /// \endcond + +} // namespace GeographicLib diff --git a/common/local_libs/GeographicLib/src/TransverseMercator.cpp b/common/local_libs/GeographicLib/src/TransverseMercator.cpp new file mode 100644 index 0000000000..8a6c746816 --- /dev/null +++ b/common/local_libs/GeographicLib/src/TransverseMercator.cpp @@ -0,0 +1,594 @@ +/** + * \file TransverseMercator.cpp + * \brief Implementation for GeographicLib::TransverseMercator class + * + * Copyright (c) Charles Karney (2008-2020) and licensed + * under the MIT/X11 License. For more information, see + * https://geographiclib.sourceforge.io/ + * + * This implementation follows closely JHS 154, ETRS89 - + * järjestelmään liittyvät karttaprojektiot, + * tasokoordinaatistot ja karttalehtijako (Map projections, plane + * coordinates, and map sheet index for ETRS89), published by JUHTA, Finnish + * Geodetic Institute, and the National Land Survey of Finland (2006). + * + * The relevant section is available as the 2008 PDF file + * http://docs.jhs-suositukset.fi/jhs-suositukset/JHS154/JHS154_liite1.pdf + * + * This is a straight transcription of the formulas in this paper with the + * following exceptions: + * - use of 6th order series instead of 4th order series. This reduces the + * error to about 5nm for the UTM range of coordinates (instead of 200nm), + * with a speed penalty of only 1%; + * - use Newton's method instead of plain iteration to solve for latitude in + * terms of isometric latitude in the Reverse method; + * - use of Horner's representation for evaluating polynomials and Clenshaw's + * method for summing trigonometric series; + * - several modifications of the formulas to improve the numerical accuracy; + * - evaluating the convergence and scale using the expression for the + * projection or its inverse. + * + * If the preprocessor variable GEOGRAPHICLIB_TRANSVERSEMERCATOR_ORDER is set + * to an integer between 4 and 8, then this specifies the order of the series + * used for the forward and reverse transformations. The default value is 6. + * (The series accurate to 12th order is given in \ref tmseries.) + **********************************************************************/ + +#include +#include + +namespace GeographicLib { + + using namespace std; + + TransverseMercator::TransverseMercator(real a, real f, real k0) + : _a(a) + , _f(f) + , _k0(k0) + , _e2(_f * (2 - _f)) + , _es((_f < 0 ? -1 : 1) * sqrt(abs(_e2))) + , _e2m(1 - _e2) + // _c = sqrt( pow(1 + _e, 1 + _e) * pow(1 - _e, 1 - _e) ) ) + // See, for example, Lee (1976), p 100. + , _c( sqrt(_e2m) * exp(Math::eatanhe(real(1), _es)) ) + , _n(_f / (2 - _f)) + { + if (!(isfinite(_a) && _a > 0)) + throw GeographicErr("Equatorial radius is not positive"); + if (!(isfinite(_f) && _f < 1)) + throw GeographicErr("Polar semi-axis is not positive"); + if (!(isfinite(_k0) && _k0 > 0)) + throw GeographicErr("Scale is not positive"); + + // Generated by Maxima on 2015-05-14 22:55:13-04:00 +#if GEOGRAPHICLIB_TRANSVERSEMERCATOR_ORDER/2 == 2 + static const real b1coeff[] = { + // b1*(n+1), polynomial in n2 of order 2 + 1, 16, 64, 64, + }; // count = 4 +#elif GEOGRAPHICLIB_TRANSVERSEMERCATOR_ORDER/2 == 3 + static const real b1coeff[] = { + // b1*(n+1), polynomial in n2 of order 3 + 1, 4, 64, 256, 256, + }; // count = 5 +#elif GEOGRAPHICLIB_TRANSVERSEMERCATOR_ORDER/2 == 4 + static const real b1coeff[] = { + // b1*(n+1), polynomial in n2 of order 4 + 25, 64, 256, 4096, 16384, 16384, + }; // count = 6 +#else +#error "Bad value for GEOGRAPHICLIB_TRANSVERSEMERCATOR_ORDER" +#endif + +#if GEOGRAPHICLIB_TRANSVERSEMERCATOR_ORDER == 4 + static const real alpcoeff[] = { + // alp[1]/n^1, polynomial in n of order 3 + 164, 225, -480, 360, 720, + // alp[2]/n^2, polynomial in n of order 2 + 557, -864, 390, 1440, + // alp[3]/n^3, polynomial in n of order 1 + -1236, 427, 1680, + // alp[4]/n^4, polynomial in n of order 0 + 49561, 161280, + }; // count = 14 +#elif GEOGRAPHICLIB_TRANSVERSEMERCATOR_ORDER == 5 + static const real alpcoeff[] = { + // alp[1]/n^1, polynomial in n of order 4 + -635, 328, 450, -960, 720, 1440, + // alp[2]/n^2, polynomial in n of order 3 + 4496, 3899, -6048, 2730, 10080, + // alp[3]/n^3, polynomial in n of order 2 + 15061, -19776, 6832, 26880, + // alp[4]/n^4, polynomial in n of order 1 + -171840, 49561, 161280, + // alp[5]/n^5, polynomial in n of order 0 + 34729, 80640, + }; // count = 20 +#elif GEOGRAPHICLIB_TRANSVERSEMERCATOR_ORDER == 6 + static const real alpcoeff[] = { + // alp[1]/n^1, polynomial in n of order 5 + 31564, -66675, 34440, 47250, -100800, 75600, 151200, + // alp[2]/n^2, polynomial in n of order 4 + -1983433, 863232, 748608, -1161216, 524160, 1935360, + // alp[3]/n^3, polynomial in n of order 3 + 670412, 406647, -533952, 184464, 725760, + // alp[4]/n^4, polynomial in n of order 2 + 6601661, -7732800, 2230245, 7257600, + // alp[5]/n^5, polynomial in n of order 1 + -13675556, 3438171, 7983360, + // alp[6]/n^6, polynomial in n of order 0 + 212378941, 319334400, + }; // count = 27 +#elif GEOGRAPHICLIB_TRANSVERSEMERCATOR_ORDER == 7 + static const real alpcoeff[] = { + // alp[1]/n^1, polynomial in n of order 6 + 1804025, 2020096, -4267200, 2204160, 3024000, -6451200, 4838400, 9676800, + // alp[2]/n^2, polynomial in n of order 5 + 4626384, -9917165, 4316160, 3743040, -5806080, 2620800, 9676800, + // alp[3]/n^3, polynomial in n of order 4 + -67102379, 26816480, 16265880, -21358080, 7378560, 29030400, + // alp[4]/n^4, polynomial in n of order 3 + 155912000, 72618271, -85060800, 24532695, 79833600, + // alp[5]/n^5, polynomial in n of order 2 + 102508609, -109404448, 27505368, 63866880, + // alp[6]/n^6, polynomial in n of order 1 + -12282192400LL, 2760926233LL, 4151347200LL, + // alp[7]/n^7, polynomial in n of order 0 + 1522256789, 1383782400, + }; // count = 35 +#elif GEOGRAPHICLIB_TRANSVERSEMERCATOR_ORDER == 8 + static const real alpcoeff[] = { + // alp[1]/n^1, polynomial in n of order 7 + -75900428, 37884525, 42422016, -89611200, 46287360, 63504000, -135475200, + 101606400, 203212800, + // alp[2]/n^2, polynomial in n of order 6 + 148003883, 83274912, -178508970, 77690880, 67374720, -104509440, + 47174400, 174182400, + // alp[3]/n^3, polynomial in n of order 5 + 318729724, -738126169, 294981280, 178924680, -234938880, 81164160, + 319334400, + // alp[4]/n^4, polynomial in n of order 4 + -40176129013LL, 14967552000LL, 6971354016LL, -8165836800LL, 2355138720LL, + 7664025600LL, + // alp[5]/n^5, polynomial in n of order 3 + 10421654396LL, 3997835751LL, -4266773472LL, 1072709352, 2490808320LL, + // alp[6]/n^6, polynomial in n of order 2 + 175214326799LL, -171950693600LL, 38652967262LL, 58118860800LL, + // alp[7]/n^7, polynomial in n of order 1 + -67039739596LL, 13700311101LL, 12454041600LL, + // alp[8]/n^8, polynomial in n of order 0 + 1424729850961LL, 743921418240LL, + }; // count = 44 +#else +#error "Bad value for GEOGRAPHICLIB_TRANSVERSEMERCATOR_ORDER" +#endif + +#if GEOGRAPHICLIB_TRANSVERSEMERCATOR_ORDER == 4 + static const real betcoeff[] = { + // bet[1]/n^1, polynomial in n of order 3 + -4, 555, -960, 720, 1440, + // bet[2]/n^2, polynomial in n of order 2 + -437, 96, 30, 1440, + // bet[3]/n^3, polynomial in n of order 1 + -148, 119, 3360, + // bet[4]/n^4, polynomial in n of order 0 + 4397, 161280, + }; // count = 14 +#elif GEOGRAPHICLIB_TRANSVERSEMERCATOR_ORDER == 5 + static const real betcoeff[] = { + // bet[1]/n^1, polynomial in n of order 4 + -3645, -64, 8880, -15360, 11520, 23040, + // bet[2]/n^2, polynomial in n of order 3 + 4416, -3059, 672, 210, 10080, + // bet[3]/n^3, polynomial in n of order 2 + -627, -592, 476, 13440, + // bet[4]/n^4, polynomial in n of order 1 + -3520, 4397, 161280, + // bet[5]/n^5, polynomial in n of order 0 + 4583, 161280, + }; // count = 20 +#elif GEOGRAPHICLIB_TRANSVERSEMERCATOR_ORDER == 6 + static const real betcoeff[] = { + // bet[1]/n^1, polynomial in n of order 5 + 384796, -382725, -6720, 932400, -1612800, 1209600, 2419200, + // bet[2]/n^2, polynomial in n of order 4 + -1118711, 1695744, -1174656, 258048, 80640, 3870720, + // bet[3]/n^3, polynomial in n of order 3 + 22276, -16929, -15984, 12852, 362880, + // bet[4]/n^4, polynomial in n of order 2 + -830251, -158400, 197865, 7257600, + // bet[5]/n^5, polynomial in n of order 1 + -435388, 453717, 15966720, + // bet[6]/n^6, polynomial in n of order 0 + 20648693, 638668800, + }; // count = 27 +#elif GEOGRAPHICLIB_TRANSVERSEMERCATOR_ORDER == 7 + static const real betcoeff[] = { + // bet[1]/n^1, polynomial in n of order 6 + -5406467, 6156736, -6123600, -107520, 14918400, -25804800, 19353600, + 38707200, + // bet[2]/n^2, polynomial in n of order 5 + 829456, -5593555, 8478720, -5873280, 1290240, 403200, 19353600, + // bet[3]/n^3, polynomial in n of order 4 + 9261899, 3564160, -2708640, -2557440, 2056320, 58060800, + // bet[4]/n^4, polynomial in n of order 3 + 14928352, -9132761, -1742400, 2176515, 79833600, + // bet[5]/n^5, polynomial in n of order 2 + -8005831, -1741552, 1814868, 63866880, + // bet[6]/n^6, polynomial in n of order 1 + -261810608, 268433009, 8302694400LL, + // bet[7]/n^7, polynomial in n of order 0 + 219941297, 5535129600LL, + }; // count = 35 +#elif GEOGRAPHICLIB_TRANSVERSEMERCATOR_ORDER == 8 + static const real betcoeff[] = { + // bet[1]/n^1, polynomial in n of order 7 + 31777436, -37845269, 43097152, -42865200, -752640, 104428800, -180633600, + 135475200, 270950400, + // bet[2]/n^2, polynomial in n of order 6 + 24749483, 14930208, -100683990, 152616960, -105719040, 23224320, 7257600, + 348364800, + // bet[3]/n^3, polynomial in n of order 5 + -232468668, 101880889, 39205760, -29795040, -28131840, 22619520, + 638668800, + // bet[4]/n^4, polynomial in n of order 4 + 324154477, 1433121792, -876745056, -167270400, 208945440, 7664025600LL, + // bet[5]/n^5, polynomial in n of order 3 + 457888660, -312227409, -67920528, 70779852, 2490808320LL, + // bet[6]/n^6, polynomial in n of order 2 + -19841813847LL, -3665348512LL, 3758062126LL, 116237721600LL, + // bet[7]/n^7, polynomial in n of order 1 + -1989295244, 1979471673, 49816166400LL, + // bet[8]/n^8, polynomial in n of order 0 + 191773887257LL, 3719607091200LL, + }; // count = 44 +#else +#error "Bad value for GEOGRAPHICLIB_TRANSVERSEMERCATOR_ORDER" +#endif + + static_assert(sizeof(b1coeff) / sizeof(real) == maxpow_/2 + 2, + "Coefficient array size mismatch for b1"); + static_assert(sizeof(alpcoeff) / sizeof(real) == + (maxpow_ * (maxpow_ + 3))/2, + "Coefficient array size mismatch for alp"); + static_assert(sizeof(betcoeff) / sizeof(real) == + (maxpow_ * (maxpow_ + 3))/2, + "Coefficient array size mismatch for bet"); + int m = maxpow_/2; + _b1 = Math::polyval(m, b1coeff, Math::sq(_n)) / (b1coeff[m + 1] * (1+_n)); + // _a1 is the equivalent radius for computing the circumference of + // ellipse. + _a1 = _b1 * _a; + int o = 0; + real d = _n; + for (int l = 1; l <= maxpow_; ++l) { + m = maxpow_ - l; + _alp[l] = d * Math::polyval(m, alpcoeff + o, _n) / alpcoeff[o + m + 1]; + _bet[l] = d * Math::polyval(m, betcoeff + o, _n) / betcoeff[o + m + 1]; + o += m + 2; + d *= _n; + } + // Post condition: o == sizeof(alpcoeff) / sizeof(real) && + // o == sizeof(betcoeff) / sizeof(real) + } + + const TransverseMercator& TransverseMercator::UTM() { + static const TransverseMercator utm(Constants::WGS84_a(), + Constants::WGS84_f(), + Constants::UTM_k0()); + return utm; + } + + // Engsager and Poder (2007) use trigonometric series to convert between phi + // and phip. Here are the series... + // + // Conversion from phi to phip: + // + // phip = phi + sum(c[j] * sin(2*j*phi), j, 1, 6) + // + // c[1] = - 2 * n + // + 2/3 * n^2 + // + 4/3 * n^3 + // - 82/45 * n^4 + // + 32/45 * n^5 + // + 4642/4725 * n^6; + // c[2] = 5/3 * n^2 + // - 16/15 * n^3 + // - 13/9 * n^4 + // + 904/315 * n^5 + // - 1522/945 * n^6; + // c[3] = - 26/15 * n^3 + // + 34/21 * n^4 + // + 8/5 * n^5 + // - 12686/2835 * n^6; + // c[4] = 1237/630 * n^4 + // - 12/5 * n^5 + // - 24832/14175 * n^6; + // c[5] = - 734/315 * n^5 + // + 109598/31185 * n^6; + // c[6] = 444337/155925 * n^6; + // + // Conversion from phip to phi: + // + // phi = phip + sum(d[j] * sin(2*j*phip), j, 1, 6) + // + // d[1] = 2 * n + // - 2/3 * n^2 + // - 2 * n^3 + // + 116/45 * n^4 + // + 26/45 * n^5 + // - 2854/675 * n^6; + // d[2] = 7/3 * n^2 + // - 8/5 * n^3 + // - 227/45 * n^4 + // + 2704/315 * n^5 + // + 2323/945 * n^6; + // d[3] = 56/15 * n^3 + // - 136/35 * n^4 + // - 1262/105 * n^5 + // + 73814/2835 * n^6; + // d[4] = 4279/630 * n^4 + // - 332/35 * n^5 + // - 399572/14175 * n^6; + // d[5] = 4174/315 * n^5 + // - 144838/6237 * n^6; + // d[6] = 601676/22275 * n^6; + // + // In order to maintain sufficient relative accuracy close to the pole use + // + // S = sum(c[i]*sin(2*i*phi),i,1,6) + // taup = (tau + tan(S)) / (1 - tau * tan(S)) + + // In Math::taupf and Math::tauf we evaluate the forward transform explicitly + // and solve the reverse one by Newton's method. + // + // There are adapted from TransverseMercatorExact (taup and taupinv). tau = + // tan(phi), taup = sinh(psi) + + void TransverseMercator::Forward(real lon0, real lat, real lon, + real& x, real& y, + real& gamma, real& k) const { + lat = Math::LatFix(lat); + lon = Math::AngDiff(lon0, lon); + // Explicitly enforce the parity + int + latsign = (lat < 0) ? -1 : 1, + lonsign = (lon < 0) ? -1 : 1; + lon *= lonsign; + lat *= latsign; + bool backside = lon > 90; + if (backside) { + if (lat == 0) + latsign = -1; + lon = 180 - lon; + } + real sphi, cphi, slam, clam; + Math::sincosd(lat, sphi, cphi); + Math::sincosd(lon, slam, clam); + // phi = latitude + // phi' = conformal latitude + // psi = isometric latitude + // tau = tan(phi) + // tau' = tan(phi') + // [xi', eta'] = Gauss-Schreiber TM coordinates + // [xi, eta] = Gauss-Krueger TM coordinates + // + // We use + // tan(phi') = sinh(psi) + // sin(phi') = tanh(psi) + // cos(phi') = sech(psi) + // denom^2 = 1-cos(phi')^2*sin(lam)^2 = 1-sech(psi)^2*sin(lam)^2 + // sin(xip) = sin(phi')/denom = tanh(psi)/denom + // cos(xip) = cos(phi')*cos(lam)/denom = sech(psi)*cos(lam)/denom + // cosh(etap) = 1/denom = 1/denom + // sinh(etap) = cos(phi')*sin(lam)/denom = sech(psi)*sin(lam)/denom + real etap, xip; + if (lat != 90) { + real + tau = sphi / cphi, + taup = Math::taupf(tau, _es); + xip = atan2(taup, clam); + // Used to be + // etap = Math::atanh(sin(lam) / cosh(psi)); + etap = asinh(slam / hypot(taup, clam)); + // convergence and scale for Gauss-Schreiber TM (xip, etap) -- gamma0 = + // atan(tan(xip) * tanh(etap)) = atan(tan(lam) * sin(phi')); + // sin(phi') = tau'/sqrt(1 + tau'^2) + // Krueger p 22 (44) + gamma = Math::atan2d(slam * taup, clam * hypot(real(1), taup)); + // k0 = sqrt(1 - _e2 * sin(phi)^2) * (cos(phi') / cos(phi)) * cosh(etap) + // Note 1/cos(phi) = cosh(psip); + // and cos(phi') * cosh(etap) = 1/hypot(sinh(psi), cos(lam)) + // + // This form has cancelling errors. This property is lost if cosh(psip) + // is replaced by 1/cos(phi), even though it's using "primary" data (phi + // instead of psip). + k = sqrt(_e2m + _e2 * Math::sq(cphi)) * hypot(real(1), tau) + / hypot(taup, clam); + } else { + xip = Math::pi()/2; + etap = 0; + gamma = lon; + k = _c; + } + // {xi',eta'} is {northing,easting} for Gauss-Schreiber transverse Mercator + // (for eta' = 0, xi' = bet). {xi,eta} is {northing,easting} for transverse + // Mercator with constant scale on the central meridian (for eta = 0, xip = + // rectifying latitude). Define + // + // zeta = xi + i*eta + // zeta' = xi' + i*eta' + // + // The conversion from conformal to rectifying latitude can be expressed as + // a series in _n: + // + // zeta = zeta' + sum(h[j-1]' * sin(2 * j * zeta'), j = 1..maxpow_) + // + // where h[j]' = O(_n^j). The reversion of this series gives + // + // zeta' = zeta - sum(h[j-1] * sin(2 * j * zeta), j = 1..maxpow_) + // + // which is used in Reverse. + // + // Evaluate sums via Clenshaw method. See + // https://en.wikipedia.org/wiki/Clenshaw_algorithm + // + // Let + // + // S = sum(a[k] * phi[k](x), k = 0..n) + // phi[k+1](x) = alpha[k](x) * phi[k](x) + beta[k](x) * phi[k-1](x) + // + // Evaluate S with + // + // b[n+2] = b[n+1] = 0 + // b[k] = alpha[k](x) * b[k+1] + beta[k+1](x) * b[k+2] + a[k] + // S = (a[0] + beta[1](x) * b[2]) * phi[0](x) + b[1] * phi[1](x) + // + // Here we have + // + // x = 2 * zeta' + // phi[k](x) = sin(k * x) + // alpha[k](x) = 2 * cos(x) + // beta[k](x) = -1 + // [ sin(A+B) - 2*cos(B)*sin(A) + sin(A-B) = 0, A = k*x, B = x ] + // n = maxpow_ + // a[k] = _alp[k] + // S = b[1] * sin(x) + // + // For the derivative we have + // + // x = 2 * zeta' + // phi[k](x) = cos(k * x) + // alpha[k](x) = 2 * cos(x) + // beta[k](x) = -1 + // [ cos(A+B) - 2*cos(B)*cos(A) + cos(A-B) = 0, A = k*x, B = x ] + // a[0] = 1; a[k] = 2*k*_alp[k] + // S = (a[0] - b[2]) + b[1] * cos(x) + // + // Matrix formulation (not used here): + // phi[k](x) = [sin(k * x); k * cos(k * x)] + // alpha[k](x) = 2 * [cos(x), 0; -sin(x), cos(x)] + // beta[k](x) = -1 * [1, 0; 0, 1] + // a[k] = _alp[k] * [1, 0; 0, 1] + // b[n+2] = b[n+1] = [0, 0; 0, 0] + // b[k] = alpha[k](x) * b[k+1] + beta[k+1](x) * b[k+2] + a[k] + // N.B., for all k: b[k](1,2) = 0; b[k](1,1) = b[k](2,2) + // S = (a[0] + beta[1](x) * b[2]) * phi[0](x) + b[1] * phi[1](x) + // phi[0](x) = [0; 0] + // phi[1](x) = [sin(x); cos(x)] + real + c0 = cos(2 * xip), ch0 = cosh(2 * etap), + s0 = sin(2 * xip), sh0 = sinh(2 * etap); + complex a(2 * c0 * ch0, -2 * s0 * sh0); // 2 * cos(2*zeta') + int n = maxpow_; + complex + y0(n & 1 ? _alp[n] : 0), y1, // default initializer is 0+i0 + z0(n & 1 ? 2*n * _alp[n] : 0), z1; + if (n & 1) --n; + while (n) { + y1 = a * y0 - y1 + _alp[n]; + z1 = a * z0 - z1 + 2*n * _alp[n]; + --n; + y0 = a * y1 - y0 + _alp[n]; + z0 = a * z1 - z0 + 2*n * _alp[n]; + --n; + } + a /= real(2); // cos(2*zeta') + z1 = real(1) - z1 + a * z0; + a = complex(s0 * ch0, c0 * sh0); // sin(2*zeta') + y1 = complex(xip, etap) + a * y0; + // Fold in change in convergence and scale for Gauss-Schreiber TM to + // Gauss-Krueger TM. + gamma -= Math::atan2d(z1.imag(), z1.real()); + k *= _b1 * abs(z1); + real xi = y1.real(), eta = y1.imag(); + y = _a1 * _k0 * (backside ? Math::pi() - xi : xi) * latsign; + x = _a1 * _k0 * eta * lonsign; + if (backside) + gamma = 180 - gamma; + gamma *= latsign * lonsign; + gamma = Math::AngNormalize(gamma); + k *= _k0; + } + + void TransverseMercator::Reverse(real lon0, real x, real y, + real& lat, real& lon, + real& gamma, real& k) const { + // This undoes the steps in Forward. The wrinkles are: (1) Use of the + // reverted series to express zeta' in terms of zeta. (2) Newton's method + // to solve for phi in terms of tan(phi). + real + xi = y / (_a1 * _k0), + eta = x / (_a1 * _k0); + // Explicitly enforce the parity + int + xisign = (xi < 0) ? -1 : 1, + etasign = (eta < 0) ? -1 : 1; + xi *= xisign; + eta *= etasign; + bool backside = xi > Math::pi()/2; + if (backside) + xi = Math::pi() - xi; + real + c0 = cos(2 * xi), ch0 = cosh(2 * eta), + s0 = sin(2 * xi), sh0 = sinh(2 * eta); + complex a(2 * c0 * ch0, -2 * s0 * sh0); // 2 * cos(2*zeta) + int n = maxpow_; + complex + y0(n & 1 ? -_bet[n] : 0), y1, // default initializer is 0+i0 + z0(n & 1 ? -2*n * _bet[n] : 0), z1; + if (n & 1) --n; + while (n) { + y1 = a * y0 - y1 - _bet[n]; + z1 = a * z0 - z1 - 2*n * _bet[n]; + --n; + y0 = a * y1 - y0 - _bet[n]; + z0 = a * z1 - z0 - 2*n * _bet[n]; + --n; + } + a /= real(2); // cos(2*zeta) + z1 = real(1) - z1 + a * z0; + a = complex(s0 * ch0, c0 * sh0); // sin(2*zeta) + y1 = complex(xi, eta) + a * y0; + // Convergence and scale for Gauss-Schreiber TM to Gauss-Krueger TM. + gamma = Math::atan2d(z1.imag(), z1.real()); + k = _b1 / abs(z1); + // JHS 154 has + // + // phi' = asin(sin(xi') / cosh(eta')) (Krueger p 17 (25)) + // lam = asin(tanh(eta') / cos(phi') + // psi = asinh(tan(phi')) + real + xip = y1.real(), etap = y1.imag(), + s = sinh(etap), + c = max(real(0), cos(xip)), // cos(pi/2) might be negative + r = hypot(s, c); + if (r != 0) { + lon = Math::atan2d(s, c); // Krueger p 17 (25) + // Use Newton's method to solve for tau + real + sxip = sin(xip), + tau = Math::tauf(sxip/r, _es); + gamma += Math::atan2d(sxip * tanh(etap), c); // Krueger p 19 (31) + lat = Math::atand(tau); + // Note cos(phi') * cosh(eta') = r + k *= sqrt(_e2m + _e2 / (1 + Math::sq(tau))) * + hypot(real(1), tau) * r; + } else { + lat = 90; + lon = 0; + k *= _c; + } + lat *= xisign; + if (backside) + lon = 180 - lon; + lon *= etasign; + lon = Math::AngNormalize(lon + lon0); + if (backside) + gamma = 180 - gamma; + gamma *= xisign * etasign; + gamma = Math::AngNormalize(gamma); + k *= _k0; + } + +} // namespace GeographicLib diff --git a/common/local_libs/GeographicLib/src/TransverseMercatorExact.cpp b/common/local_libs/GeographicLib/src/TransverseMercatorExact.cpp new file mode 100644 index 0000000000..cc297b0409 --- /dev/null +++ b/common/local_libs/GeographicLib/src/TransverseMercatorExact.cpp @@ -0,0 +1,462 @@ +/** + * \file TransverseMercatorExact.cpp + * \brief Implementation for GeographicLib::TransverseMercatorExact class + * + * Copyright (c) Charles Karney (2008-2020) and licensed + * under the MIT/X11 License. For more information, see + * https://geographiclib.sourceforge.io/ + * + * The relevant section of Lee's paper is part V, pp 67--101, + * Conformal + * Projections Based On Jacobian Elliptic Functions. + * + * The method entails using the Thompson Transverse Mercator as an + * intermediate projection. The projections from the intermediate + * coordinates to [\e phi, \e lam] and [\e x, \e y] are given by elliptic + * functions. The inverse of these projections are found by Newton's method + * with a suitable starting guess. + * + * This implementation and notation closely follows Lee, with the following + * exceptions: + *
+ *
Lee here Description + *
x/a xi Northing (unit Earth) + *
y/a eta Easting (unit Earth) + *
s/a sigma xi + i * eta + *
y x Easting + *
x y Northing + *
k e eccentricity + *
k^2 mu elliptic function parameter + *
k'^2 mv elliptic function complementary parameter + *
m k scale + *
zeta zeta complex longitude = Mercator = chi in paper + *
s sigma complex GK = zeta in paper + *
+ * + * Minor alterations have been made in some of Lee's expressions in an + * attempt to control round-off. For example atanh(sin(phi)) is replaced by + * asinh(tan(phi)) which maintains accuracy near phi = pi/2. Such changes + * are noted in the code. + **********************************************************************/ + +#include + +#if defined(_MSC_VER) +// Squelch warnings about constant conditional expressions +# pragma warning (disable: 4127) +#endif + +namespace GeographicLib { + + using namespace std; + + TransverseMercatorExact::TransverseMercatorExact(real a, real f, real k0, + bool extendp) + : tol_(numeric_limits::epsilon()) + , tol2_(real(0.1) * tol_) + , taytol_(pow(tol_, real(0.6))) + , _a(a) + , _f(f) + , _k0(k0) + , _mu(_f * (2 - _f)) // e^2 + , _mv(1 - _mu) // 1 - e^2 + , _e(sqrt(_mu)) + , _extendp(extendp) + , _Eu(_mu) + , _Ev(_mv) + { + if (!(isfinite(_a) && _a > 0)) + throw GeographicErr("Equatorial radius is not positive"); + if (!(_f > 0)) + throw GeographicErr("Flattening is not positive"); + if (!(_f < 1)) + throw GeographicErr("Polar semi-axis is not positive"); + if (!(isfinite(_k0) && _k0 > 0)) + throw GeographicErr("Scale is not positive"); + } + + const TransverseMercatorExact& TransverseMercatorExact::UTM() { + static const TransverseMercatorExact utm(Constants::WGS84_a(), + Constants::WGS84_f(), + Constants::UTM_k0()); + return utm; + } + + void TransverseMercatorExact::zeta(real /*u*/, real snu, real cnu, real dnu, + real /*v*/, real snv, real cnv, real dnv, + real& taup, real& lam) const { + // Lee 54.17 but write + // atanh(snu * dnv) = asinh(snu * dnv / sqrt(cnu^2 + _mv * snu^2 * snv^2)) + // atanh(_e * snu / dnv) = + // asinh(_e * snu / sqrt(_mu * cnu^2 + _mv * cnv^2)) + // Overflow value s.t. atan(overflow) = pi/2 + static const real + overflow = 1 / Math::sq(numeric_limits::epsilon()); + real + d1 = sqrt(Math::sq(cnu) + _mv * Math::sq(snu * snv)), + d2 = sqrt(_mu * Math::sq(cnu) + _mv * Math::sq(cnv)), + t1 = (d1 != 0 ? snu * dnv / d1 : (snu < 0 ? -overflow : overflow)), + t2 = (d2 != 0 ? sinh( _e * asinh(_e * snu / d2) ) : + (snu < 0 ? -overflow : overflow)); + // psi = asinh(t1) - asinh(t2) + // taup = sinh(psi) + taup = t1 * hypot(real(1), t2) - t2 * hypot(real(1), t1); + lam = (d1 != 0 && d2 != 0) ? + atan2(dnu * snv, cnu * cnv) - _e * atan2(_e * cnu * snv, dnu * cnv) : + 0; + } + + void TransverseMercatorExact::dwdzeta(real /*u*/, + real snu, real cnu, real dnu, + real /*v*/, + real snv, real cnv, real dnv, + real& du, real& dv) const { + // Lee 54.21 but write (1 - dnu^2 * snv^2) = (cnv^2 + _mu * snu^2 * snv^2) + // (see A+S 16.21.4) + real d = _mv * Math::sq(Math::sq(cnv) + _mu * Math::sq(snu * snv)); + du = cnu * dnu * dnv * (Math::sq(cnv) - _mu * Math::sq(snu * snv)) / d; + dv = -snu * snv * cnv * (Math::sq(dnu * dnv) + _mu * Math::sq(cnu)) / d; + } + + // Starting point for zetainv + bool TransverseMercatorExact::zetainv0(real psi, real lam, + real& u, real& v) const { + bool retval = false; + if (psi < -_e * Math::pi()/4 && + lam > (1 - 2 * _e) * Math::pi()/2 && + psi < lam - (1 - _e) * Math::pi()/2) { + // N.B. this branch is normally not taken because psi < 0 is converted + // psi > 0 by Forward. + // + // There's a log singularity at w = w0 = Eu.K() + i * Ev.K(), + // corresponding to the south pole, where we have, approximately + // + // psi = _e + i * pi/2 - _e * atanh(cos(i * (w - w0)/(1 + _mu/2))) + // + // Inverting this gives: + real + psix = 1 - psi / _e, + lamx = (Math::pi()/2 - lam) / _e; + u = asinh(sin(lamx) / hypot(cos(lamx), sinh(psix))) * + (1 + _mu/2); + v = atan2(cos(lamx), sinh(psix)) * (1 + _mu/2); + u = _Eu.K() - u; + v = _Ev.K() - v; + } else if (psi < _e * Math::pi()/2 && + lam > (1 - 2 * _e) * Math::pi()/2) { + // At w = w0 = i * Ev.K(), we have + // + // zeta = zeta0 = i * (1 - _e) * pi/2 + // zeta' = zeta'' = 0 + // + // including the next term in the Taylor series gives: + // + // zeta = zeta0 - (_mv * _e) / 3 * (w - w0)^3 + // + // When inverting this, we map arg(w - w0) = [-90, 0] to + // arg(zeta - zeta0) = [-90, 180] + real + dlam = lam - (1 - _e) * Math::pi()/2, + rad = hypot(psi, dlam), + // atan2(dlam-psi, psi+dlam) + 45d gives arg(zeta - zeta0) in range + // [-135, 225). Subtracting 180 (since multiplier is negative) makes + // range [-315, 45). Multiplying by 1/3 (for cube root) gives range + // [-105, 15). In particular the range [-90, 180] in zeta space maps + // to [-90, 0] in w space as required. + ang = atan2(dlam-psi, psi+dlam) - real(0.75) * Math::pi(); + // Error using this guess is about 0.21 * (rad/e)^(5/3) + retval = rad < _e * taytol_; + rad = cbrt(3 / (_mv * _e) * rad); + ang /= 3; + u = rad * cos(ang); + v = rad * sin(ang) + _Ev.K(); + } else { + // Use spherical TM, Lee 12.6 -- writing atanh(sin(lam) / cosh(psi)) = + // asinh(sin(lam) / hypot(cos(lam), sinh(psi))). This takes care of the + // log singularity at zeta = Eu.K() (corresponding to the north pole) + v = asinh(sin(lam) / hypot(cos(lam), sinh(psi))); + u = atan2(sinh(psi), cos(lam)); + // But scale to put 90,0 on the right place + u *= _Eu.K() / (Math::pi()/2); + v *= _Eu.K() / (Math::pi()/2); + } + return retval; + } + + // Invert zeta using Newton's method + void TransverseMercatorExact::zetainv(real taup, real lam, + real& u, real& v) const { + real + psi = asinh(taup), + scal = 1/hypot(real(1), taup); + if (zetainv0(psi, lam, u, v)) + return; + real stol2 = tol2_ / Math::sq(max(psi, real(1))); + // min iterations = 2, max iterations = 6; mean = 4.0 + for (int i = 0, trip = 0; i < numit_ || GEOGRAPHICLIB_PANIC; ++i) { + real snu, cnu, dnu, snv, cnv, dnv; + _Eu.sncndn(u, snu, cnu, dnu); + _Ev.sncndn(v, snv, cnv, dnv); + real tau1, lam1, du1, dv1; + zeta(u, snu, cnu, dnu, v, snv, cnv, dnv, tau1, lam1); + dwdzeta(u, snu, cnu, dnu, v, snv, cnv, dnv, du1, dv1); + tau1 -= taup; + lam1 -= lam; + tau1 *= scal; + real + delu = tau1 * du1 - lam1 * dv1, + delv = tau1 * dv1 + lam1 * du1; + u -= delu; + v -= delv; + if (trip) + break; + real delw2 = Math::sq(delu) + Math::sq(delv); + if (!(delw2 >= stol2)) + ++trip; + } + } + + void TransverseMercatorExact::sigma(real /*u*/, real snu, real cnu, real dnu, + real v, real snv, real cnv, real dnv, + real& xi, real& eta) const { + // Lee 55.4 writing + // dnu^2 + dnv^2 - 1 = _mu * cnu^2 + _mv * cnv^2 + real d = _mu * Math::sq(cnu) + _mv * Math::sq(cnv); + xi = _Eu.E(snu, cnu, dnu) - _mu * snu * cnu * dnu / d; + eta = v - _Ev.E(snv, cnv, dnv) + _mv * snv * cnv * dnv / d; + } + + void TransverseMercatorExact::dwdsigma(real /*u*/, + real snu, real cnu, real dnu, + real /*v*/, + real snv, real cnv, real dnv, + real& du, real& dv) const { + // Reciprocal of 55.9: dw/ds = dn(w)^2/_mv, expanding complex dn(w) using + // A+S 16.21.4 + real d = _mv * Math::sq(Math::sq(cnv) + _mu * Math::sq(snu * snv)); + real + dnr = dnu * cnv * dnv, + dni = - _mu * snu * cnu * snv; + du = (Math::sq(dnr) - Math::sq(dni)) / d; + dv = 2 * dnr * dni / d; + } + + // Starting point for sigmainv + bool TransverseMercatorExact::sigmainv0(real xi, real eta, + real& u, real& v) const { + bool retval = false; + if (eta > real(1.25) * _Ev.KE() || + (xi < -real(0.25) * _Eu.E() && xi < eta - _Ev.KE())) { + // sigma as a simple pole at w = w0 = Eu.K() + i * Ev.K() and sigma is + // approximated by + // + // sigma = (Eu.E() + i * Ev.KE()) + 1/(w - w0) + real + x = xi - _Eu.E(), + y = eta - _Ev.KE(), + r2 = Math::sq(x) + Math::sq(y); + u = _Eu.K() + x/r2; + v = _Ev.K() - y/r2; + } else if ((eta > real(0.75) * _Ev.KE() && xi < real(0.25) * _Eu.E()) + || eta > _Ev.KE()) { + // At w = w0 = i * Ev.K(), we have + // + // sigma = sigma0 = i * Ev.KE() + // sigma' = sigma'' = 0 + // + // including the next term in the Taylor series gives: + // + // sigma = sigma0 - _mv / 3 * (w - w0)^3 + // + // When inverting this, we map arg(w - w0) = [-pi/2, -pi/6] to + // arg(sigma - sigma0) = [-pi/2, pi/2] + // mapping arg = [-pi/2, -pi/6] to [-pi/2, pi/2] + real + deta = eta - _Ev.KE(), + rad = hypot(xi, deta), + // Map the range [-90, 180] in sigma space to [-90, 0] in w space. See + // discussion in zetainv0 on the cut for ang. + ang = atan2(deta-xi, xi+deta) - real(0.75) * Math::pi(); + // Error using this guess is about 0.068 * rad^(5/3) + retval = rad < 2 * taytol_; + rad = cbrt(3 / _mv * rad); + ang /= 3; + u = rad * cos(ang); + v = rad * sin(ang) + _Ev.K(); + } else { + // Else use w = sigma * Eu.K/Eu.E (which is correct in the limit _e -> 0) + u = xi * _Eu.K()/_Eu.E(); + v = eta * _Eu.K()/_Eu.E(); + } + return retval; + } + + // Invert sigma using Newton's method + void TransverseMercatorExact::sigmainv(real xi, real eta, + real& u, real& v) const { + if (sigmainv0(xi, eta, u, v)) + return; + // min iterations = 2, max iterations = 7; mean = 3.9 + for (int i = 0, trip = 0; i < numit_ || GEOGRAPHICLIB_PANIC; ++i) { + real snu, cnu, dnu, snv, cnv, dnv; + _Eu.sncndn(u, snu, cnu, dnu); + _Ev.sncndn(v, snv, cnv, dnv); + real xi1, eta1, du1, dv1; + sigma(u, snu, cnu, dnu, v, snv, cnv, dnv, xi1, eta1); + dwdsigma(u, snu, cnu, dnu, v, snv, cnv, dnv, du1, dv1); + xi1 -= xi; + eta1 -= eta; + real + delu = xi1 * du1 - eta1 * dv1, + delv = xi1 * dv1 + eta1 * du1; + u -= delu; + v -= delv; + if (trip) + break; + real delw2 = Math::sq(delu) + Math::sq(delv); + if (!(delw2 >= tol2_)) + ++trip; + } + } + + void TransverseMercatorExact::Scale(real tau, real /*lam*/, + real snu, real cnu, real dnu, + real snv, real cnv, real dnv, + real& gamma, real& k) const { + real sec2 = 1 + Math::sq(tau); // sec(phi)^2 + // Lee 55.12 -- negated for our sign convention. gamma gives the bearing + // (clockwise from true north) of grid north + gamma = atan2(_mv * snu * snv * cnv, cnu * dnu * dnv); + // Lee 55.13 with nu given by Lee 9.1 -- in sqrt change the numerator + // from + // + // (1 - snu^2 * dnv^2) to (_mv * snv^2 + cnu^2 * dnv^2) + // + // to maintain accuracy near phi = 90 and change the denomintor from + // + // (dnu^2 + dnv^2 - 1) to (_mu * cnu^2 + _mv * cnv^2) + // + // to maintain accuracy near phi = 0, lam = 90 * (1 - e). Similarly + // rewrite sqrt term in 9.1 as + // + // _mv + _mu * c^2 instead of 1 - _mu * sin(phi)^2 + k = sqrt(_mv + _mu / sec2) * sqrt(sec2) * + sqrt( (_mv * Math::sq(snv) + Math::sq(cnu * dnv)) / + (_mu * Math::sq(cnu) + _mv * Math::sq(cnv)) ); + } + + void TransverseMercatorExact::Forward(real lon0, real lat, real lon, + real& x, real& y, + real& gamma, real& k) const { + lat = Math::LatFix(lat); + lon = Math::AngDiff(lon0, lon); + // Explicitly enforce the parity + int + latsign = (!_extendp && lat < 0) ? -1 : 1, + lonsign = (!_extendp && lon < 0) ? -1 : 1; + lon *= lonsign; + lat *= latsign; + bool backside = !_extendp && lon > 90; + if (backside) { + if (lat == 0) + latsign = -1; + lon = 180 - lon; + } + real + lam = lon * Math::degree(), + tau = Math::tand(lat); + + // u,v = coordinates for the Thompson TM, Lee 54 + real u, v; + if (lat == 90) { + u = _Eu.K(); + v = 0; + } else if (lat == 0 && lon == 90 * (1 - _e)) { + u = 0; + v = _Ev.K(); + } else + // tau = tan(phi), taup = sinh(psi) + zetainv(Math::taupf(tau, _e), lam, u, v); + + real snu, cnu, dnu, snv, cnv, dnv; + _Eu.sncndn(u, snu, cnu, dnu); + _Ev.sncndn(v, snv, cnv, dnv); + + real xi, eta; + sigma(u, snu, cnu, dnu, v, snv, cnv, dnv, xi, eta); + if (backside) + xi = 2 * _Eu.E() - xi; + y = xi * _a * _k0 * latsign; + x = eta * _a * _k0 * lonsign; + + if (lat == 90) { + gamma = lon; + k = 1; + } else { + // Recompute (tau, lam) from (u, v) to improve accuracy of Scale + zeta(u, snu, cnu, dnu, v, snv, cnv, dnv, tau, lam); + tau = Math::tauf(tau, _e); + Scale(tau, lam, snu, cnu, dnu, snv, cnv, dnv, gamma, k); + gamma /= Math::degree(); + } + if (backside) + gamma = 180 - gamma; + gamma *= latsign * lonsign; + k *= _k0; + } + + void TransverseMercatorExact::Reverse(real lon0, real x, real y, + real& lat, real& lon, + real& gamma, real& k) const { + // This undoes the steps in Forward. + real + xi = y / (_a * _k0), + eta = x / (_a * _k0); + // Explicitly enforce the parity + int + latsign = !_extendp && y < 0 ? -1 : 1, + lonsign = !_extendp && x < 0 ? -1 : 1; + xi *= latsign; + eta *= lonsign; + bool backside = !_extendp && xi > _Eu.E(); + if (backside) + xi = 2 * _Eu.E()- xi; + + // u,v = coordinates for the Thompson TM, Lee 54 + real u, v; + if (xi == 0 && eta == _Ev.KE()) { + u = 0; + v = _Ev.K(); + } else + sigmainv(xi, eta, u, v); + + real snu, cnu, dnu, snv, cnv, dnv; + _Eu.sncndn(u, snu, cnu, dnu); + _Ev.sncndn(v, snv, cnv, dnv); + real phi, lam, tau; + if (v != 0 || u != _Eu.K()) { + zeta(u, snu, cnu, dnu, v, snv, cnv, dnv, tau, lam); + tau = Math::tauf(tau, _e); + phi = atan(tau); + lat = phi / Math::degree(); + lon = lam / Math::degree(); + Scale(tau, lam, snu, cnu, dnu, snv, cnv, dnv, gamma, k); + gamma /= Math::degree(); + } else { + lat = 90; + lon = lam = gamma = 0; + k = 1; + } + + if (backside) + lon = 180 - lon; + lon *= lonsign; + lon = Math::AngNormalize(lon + Math::AngNormalize(lon0)); + lat *= latsign; + if (backside) + gamma = 180 - gamma; + gamma *= latsign * lonsign; + k *= _k0; + } + +} // namespace GeographicLib diff --git a/common/local_libs/GeographicLib/src/UTMUPS.cpp b/common/local_libs/GeographicLib/src/UTMUPS.cpp new file mode 100644 index 0000000000..9e3b33860e --- /dev/null +++ b/common/local_libs/GeographicLib/src/UTMUPS.cpp @@ -0,0 +1,299 @@ +/** + * \file UTMUPS.cpp + * \brief Implementation for GeographicLib::UTMUPS class + * + * Copyright (c) Charles Karney (2008-2020) and licensed + * under the MIT/X11 License. For more information, see + * https://geographiclib.sourceforge.io/ + **********************************************************************/ + +#include +#include +#include +#include +#include + +namespace GeographicLib { + + using namespace std; + + const int UTMUPS::falseeasting_[] = + { MGRS::upseasting_ * MGRS::tile_, MGRS::upseasting_ * MGRS::tile_, + MGRS::utmeasting_ * MGRS::tile_, MGRS::utmeasting_ * MGRS::tile_ }; + const int UTMUPS::falsenorthing_[] = + { MGRS::upseasting_ * MGRS::tile_, MGRS::upseasting_ * MGRS::tile_, + MGRS::maxutmSrow_ * MGRS::tile_, MGRS::minutmNrow_ * MGRS::tile_ }; + const int UTMUPS::mineasting_[] = + { MGRS::minupsSind_ * MGRS::tile_, MGRS::minupsNind_ * MGRS::tile_, + MGRS::minutmcol_ * MGRS::tile_, MGRS::minutmcol_ * MGRS::tile_ }; + const int UTMUPS::maxeasting_[] = + { MGRS::maxupsSind_ * MGRS::tile_, MGRS::maxupsNind_ * MGRS::tile_, + MGRS::maxutmcol_ * MGRS::tile_, MGRS::maxutmcol_ * MGRS::tile_ }; + const int UTMUPS::minnorthing_[] = + { MGRS::minupsSind_ * MGRS::tile_, MGRS::minupsNind_ * MGRS::tile_, + MGRS::minutmSrow_ * MGRS::tile_, + (MGRS::minutmNrow_ + MGRS::minutmSrow_ - MGRS::maxutmSrow_) + * MGRS::tile_ }; + const int UTMUPS::maxnorthing_[] = + { MGRS::maxupsSind_ * MGRS::tile_, MGRS::maxupsNind_ * MGRS::tile_, + (MGRS::maxutmSrow_ + MGRS::maxutmNrow_ - MGRS::minutmNrow_) * + MGRS::tile_, + MGRS::maxutmNrow_ * MGRS::tile_ }; + + int UTMUPS::StandardZone(real lat, real lon, int setzone) { + using std::isnan; // Needed for Centos 7, ubuntu 14 + if (!(setzone >= MINPSEUDOZONE && setzone <= MAXZONE)) + throw GeographicErr("Illegal zone requested " + Utility::str(setzone)); + if (setzone >= MINZONE || setzone == INVALID) + return setzone; + if (isnan(lat) || isnan(lon)) // Check if lat or lon is a NaN + return INVALID; + if (setzone == UTM || (lat >= -80 && lat < 84)) { + int ilon = int(floor(Math::AngNormalize(lon))); + if (ilon == 180) ilon = -180; // ilon now in [-180,180) + int zone = (ilon + 186)/6; + int band = MGRS::LatitudeBand(lat); + if (band == 7 && zone == 31 && ilon >= 3) // The Norway exception + zone = 32; + else if (band == 9 && ilon >= 0 && ilon < 42) // The Svalbard exception + zone = 2 * ((ilon + 183)/12) + 1; + return zone; + } else + return UPS; + } + + void UTMUPS::Forward(real lat, real lon, + int& zone, bool& northp, real& x, real& y, + real& gamma, real& k, + int setzone, bool mgrslimits) { + if (abs(lat) > 90) + throw GeographicErr("Latitude " + Utility::str(lat) + + "d not in [-90d, 90d]"); + bool northp1 = lat >= 0; + int zone1 = StandardZone(lat, lon, setzone); + if (zone1 == INVALID) { + zone = zone1; + northp = northp1; + x = y = gamma = k = Math::NaN(); + return; + } + real x1, y1, gamma1, k1; + bool utmp = zone1 != UPS; + if (utmp) { + real + lon0 = CentralMeridian(zone1), + dlon = lon - lon0; + dlon = abs(dlon - 360 * floor((dlon + 180)/360)); + if (!(dlon <= 60)) + // Check isn't really necessary because CheckCoords catches this case. + // But this allows a more meaningful error message to be given. + throw GeographicErr("Longitude " + Utility::str(lon) + + "d more than 60d from center of UTM zone " + + Utility::str(zone1)); + TransverseMercator::UTM().Forward(lon0, lat, lon, x1, y1, gamma1, k1); + } else { + if (abs(lat) < 70) + // Check isn't really necessary ... (see above). + throw GeographicErr("Latitude " + Utility::str(lat) + + "d more than 20d from " + + (northp1 ? "N" : "S") + " pole"); + PolarStereographic::UPS().Forward(northp1, lat, lon, x1, y1, gamma1, k1); + } + int ind = (utmp ? 2 : 0) + (northp1 ? 1 : 0); + x1 += falseeasting_[ind]; + y1 += falsenorthing_[ind]; + if (! CheckCoords(zone1 != UPS, northp1, x1, y1, mgrslimits, false) ) + throw GeographicErr("Latitude " + Utility::str(lat) + + ", longitude " + Utility::str(lon) + + " out of legal range for " + + (utmp ? "UTM zone " + Utility::str(zone1) : + "UPS")); + zone = zone1; + northp = northp1; + x = x1; + y = y1; + gamma = gamma1; + k = k1; + } + + void UTMUPS::Reverse(int zone, bool northp, real x, real y, + real& lat, real& lon, real& gamma, real& k, + bool mgrslimits) { + using std::isnan; // Needed for Centos 7, ubuntu 14 + if (zone == INVALID || isnan(x) || isnan(y)) { + lat = lon = gamma = k = Math::NaN(); + return; + } + if (!(zone >= MINZONE && zone <= MAXZONE)) + throw GeographicErr("Zone " + Utility::str(zone) + + " not in range [0, 60]"); + bool utmp = zone != UPS; + CheckCoords(utmp, northp, x, y, mgrslimits); + int ind = (utmp ? 2 : 0) + (northp ? 1 : 0); + x -= falseeasting_[ind]; + y -= falsenorthing_[ind]; + if (utmp) + TransverseMercator::UTM().Reverse(CentralMeridian(zone), + x, y, lat, lon, gamma, k); + else + PolarStereographic::UPS().Reverse(northp, x, y, lat, lon, gamma, k); + } + + bool UTMUPS::CheckCoords(bool utmp, bool northp, real x, real y, + bool mgrslimits, bool throwp) { + // Limits are all multiples of 100km and are all closed on the both ends. + // Failure tests are such that NaNs succeed. + real slop = mgrslimits ? 0 : MGRS::tile_; + int ind = (utmp ? 2 : 0) + (northp ? 1 : 0); + if (x < mineasting_[ind] - slop || x > maxeasting_[ind] + slop) { + if (!throwp) return false; + throw GeographicErr("Easting " + Utility::str(x/1000) + "km not in " + + (mgrslimits ? "MGRS/" : "") + + (utmp ? "UTM" : "UPS") + " range for " + + (northp ? "N" : "S" ) + " hemisphere [" + + Utility::str((mineasting_[ind] - slop)/1000) + + "km, " + + Utility::str((maxeasting_[ind] + slop)/1000) + + "km]"); + } + if (y < minnorthing_[ind] - slop || y > maxnorthing_[ind] + slop) { + if (!throwp) return false; + throw GeographicErr("Northing " + Utility::str(y/1000) + "km not in " + + (mgrslimits ? "MGRS/" : "") + + (utmp ? "UTM" : "UPS") + " range for " + + (northp ? "N" : "S" ) + " hemisphere [" + + Utility::str((minnorthing_[ind] - slop)/1000) + + "km, " + + Utility::str((maxnorthing_[ind] + slop)/1000) + + "km]"); + } + return true; + } + + void UTMUPS::Transfer(int zonein, bool northpin, real xin, real yin, + int zoneout, bool northpout, real& xout, real& yout, + int& zone) { + bool northp = northpin; + if (zonein != zoneout) { + // Determine lat, lon + real lat, lon; + GeographicLib::UTMUPS::Reverse(zonein, northpin, xin, yin, lat, lon); + // Try converting to zoneout + real x, y; + int zone1; + GeographicLib::UTMUPS::Forward(lat, lon, zone1, northp, x, y, + zoneout == UTMUPS::MATCH + ? zonein : zoneout); + if (zone1 == 0 && northp != northpout) + throw GeographicErr + ("Attempt to transfer UPS coordinates between hemispheres"); + zone = zone1; + xout = x; + yout = y; + } else { + if (zoneout == 0 && northp != northpout) + throw GeographicErr + ("Attempt to transfer UPS coordinates between hemispheres"); + zone = zoneout; + xout = xin; + yout = yin; + } + if (northp != northpout) + // Can't get here if UPS + yout += (northpout ? -1 : 1) * MGRS::utmNshift_; + return; + } + + void UTMUPS::DecodeZone(const string& zonestr, int& zone, bool& northp) + { + unsigned zlen = unsigned(zonestr.size()); + if (zlen == 0) + throw GeographicErr("Empty zone specification"); + // Longest zone spec is 32north, 42south, invalid = 7 + if (zlen > 7) + throw GeographicErr("More than 7 characters in zone specification " + + zonestr); + + const char* c = zonestr.c_str(); + char* q; + int zone1 = strtol(c, &q, 10); + // if (zone1 == 0) zone1 = UPS; (not necessary) + + if (zone1 == UPS) { + if (!(q == c)) + // Don't allow 0n as an alternative to n for UPS coordinates + throw GeographicErr("Illegal zone 0 in " + zonestr + + ", use just the hemisphere for UPS"); + } else if (!(zone1 >= MINUTMZONE && zone1 <= MAXUTMZONE)) + throw GeographicErr("Zone " + Utility::str(zone1) + + " not in range [1, 60]"); + else if (!isdigit(zonestr[0])) + throw GeographicErr("Must use unsigned number for zone " + + Utility::str(zone1)); + else if (q - c > 2) + throw GeographicErr("More than 2 digits use to specify zone " + + Utility::str(zone1)); + + string hemi(zonestr, q - c); + for (string::iterator p = hemi.begin(); p != hemi.end(); ++p) + *p = char(tolower(*p)); + if (q == c && (hemi == "inv" || hemi == "invalid")) { + zone = INVALID; + northp = false; + return; + } + bool northp1 = hemi == "north" || hemi == "n"; + if (!(northp1 || hemi == "south" || hemi == "s")) + throw GeographicErr(string("Illegal hemisphere ") + hemi + " in " + + zonestr + ", specify north or south"); + zone = zone1; + northp = northp1; + } + + string UTMUPS::EncodeZone(int zone, bool northp, bool abbrev) { + if (zone == INVALID) + return string(abbrev ? "inv" : "invalid"); + if (!(zone >= MINZONE && zone <= MAXZONE)) + throw GeographicErr("Zone " + Utility::str(zone) + + " not in range [0, 60]"); + ostringstream os; + if (zone != UPS) + os << setfill('0') << setw(2) << zone; + if (abbrev) + os << (northp ? 'n' : 's'); + else + os << (northp ? "north" : "south"); + return os.str(); + } + + void UTMUPS::DecodeEPSG(int epsg, int& zone, bool& northp) { + northp = false; + if (epsg >= epsg01N && epsg <= epsg60N) { + zone = (epsg - epsg01N) + MINUTMZONE; + northp = true; + } else if (epsg == epsgN) { + zone = UPS; + northp = true; + } else if (epsg >= epsg01S && epsg <= epsg60S) { + zone = (epsg - epsg01S) + MINUTMZONE; + } else if (epsg == epsgS) { + zone = UPS; + } else { + zone = INVALID; + } + } + + int UTMUPS::EncodeEPSG(int zone, bool northp) { + int epsg = -1; + if (zone == UPS) + epsg = epsgS; + else if (zone >= MINUTMZONE && zone <= MAXUTMZONE) + epsg = (zone - MINUTMZONE) + epsg01S; + if (epsg >= 0 && northp) + epsg += epsgN - epsgS; + return epsg; + } + + Math::real UTMUPS::UTMShift() { return real(MGRS::utmNshift_); } + +} // namespace GeographicLib diff --git a/common/local_libs/GeographicLib/src/Utility.cpp b/common/local_libs/GeographicLib/src/Utility.cpp new file mode 100644 index 0000000000..1c0e3e44f4 --- /dev/null +++ b/common/local_libs/GeographicLib/src/Utility.cpp @@ -0,0 +1,54 @@ +/** + * \file Utility.cpp + * \brief Implementation for GeographicLib::Utility class + * + * Copyright (c) Charles Karney (2011-2020) and licensed + * under the MIT/X11 License. For more information, see + * https://geographiclib.sourceforge.io/ + **********************************************************************/ + +#include +#include + +#if defined(_MSC_VER) +// Squelch warnings about unsafe use of getenv +# pragma warning (disable: 4996) +#endif + +namespace GeographicLib { + + using namespace std; + + bool Utility::ParseLine(const std::string& line, + std::string& key, std::string& value, + char delim) { + key.clear(); value.clear(); + string::size_type n = line.find('#'); + string linea = trim(line.substr(0, n)); + if (linea.empty()) return false; + n = delim ? linea.find(delim) : linea.find_first_of(" \t\n\v\f\r"); // + key = trim(linea.substr(0, n)); + if (key.empty()) return false; + if (n != string::npos) value = trim(linea.substr(n + 1)); + return true; + } + + bool Utility::ParseLine(const std::string& line, + std::string& key, std::string& value) { + return ParseLine(line, key, value, '\0'); + } + + int Utility::set_digits(int ndigits) { +#if GEOGRAPHICLIB_PRECISION == 5 + if (ndigits <= 0) { + char* digitenv = getenv("GEOGRAPHICLIB_DIGITS"); + if (digitenv) + ndigits = strtol(digitenv, NULL, 0); + if (ndigits <= 0) + ndigits = 256; + } +#endif + return Math::set_digits(ndigits); + } + +} // namespace GeographicLib diff --git a/common/local_libs/SMCalloutView/SMCalloutView.m b/common/local_libs/SMCalloutView/SMCalloutView.m index 866aea4788..a0b6037862 100755 --- a/common/local_libs/SMCalloutView/SMCalloutView.m +++ b/common/local_libs/SMCalloutView/SMCalloutView.m @@ -71,10 +71,11 @@ - (id)initWithFrame:(CGRect)frame { } - (BOOL)supportsHighlighting { - if (![self.delegate respondsToSelector:@selector(calloutViewClicked:)]) + id dg = self.delegate; + if (![dg respondsToSelector:@selector(calloutViewClicked:)]) return NO; - if ([self.delegate respondsToSelector:@selector(calloutViewShouldHighlight:)]) - return [self.delegate calloutViewShouldHighlight:self]; + if ([dg respondsToSelector:@selector(calloutViewShouldHighlight:)]) + return [dg calloutViewShouldHighlight:self]; return YES; } @@ -82,8 +83,9 @@ - (void)highlightIfNecessary { if (self.supportsHighlighting) self.backgroundVie - (void)unhighlightIfNecessary { if (self.supportsHighlighting) self.backgroundView.highlighted = NO; } - (void)calloutClicked { - if ([self.delegate respondsToSelector:@selector(calloutViewClicked:)]) - [self.delegate calloutViewClicked:self]; + id dg = self.delegate; + if ([dg respondsToSelector:@selector(calloutViewClicked:)]) + [dg calloutViewClicked:self]; } - (UIView *)titleViewOrDefault { @@ -377,8 +379,9 @@ - (void)presentCalloutFromRect:(CGRect)rect inLayer:(CALayer *)layer ofView:(UIV NSTimeInterval delay = 0; self.popupCancelled = NO; // reset this before calling our delegate below - if ([self.delegate respondsToSelector:@selector(calloutView:delayForRepositionWithSize:)] && !CGSizeEqualToSize(offset, CGSizeZero)) - delay = [self.delegate calloutView:(id)self delayForRepositionWithSize:offset]; + id dg = self.delegate; + if ([dg respondsToSelector:@selector(calloutView:delayForRepositionWithSize:)] && !CGSizeEqualToSize(offset, CGSizeZero)) + delay = [dg calloutView:(id)self delayForRepositionWithSize:offset]; // there's a chance that user code in the delegate method may have called -dismissCalloutAnimated to cancel things; if that // happened then we need to bail! @@ -406,33 +409,35 @@ - (void)presentCalloutFromRect:(CGRect)rect inLayer:(CALayer *)layer ofView:(UIV - (void)animationDidStart:(CAAnimation *)anim { BOOL presenting = [[anim valueForKey:@"presenting"] boolValue]; + id dg = self.delegate; if (presenting) { - if ([_delegate respondsToSelector:@selector(calloutViewWillAppear:)]) - [_delegate calloutViewWillAppear:(id)self]; + if ([dg respondsToSelector:@selector(calloutViewWillAppear:)]) + [dg calloutViewWillAppear:(id)self]; // ok, animation is on, let's make ourselves visible! self.hidden = NO; } else if (!presenting) { - if ([_delegate respondsToSelector:@selector(calloutViewWillDisappear:)]) - [_delegate calloutViewWillDisappear:(id)self]; + if ([dg respondsToSelector:@selector(calloutViewWillDisappear:)]) + [dg calloutViewWillDisappear:(id)self]; } } - (void)animationDidStop:(CAAnimation *)anim finished:(BOOL)finished { BOOL presenting = [[anim valueForKey:@"presenting"] boolValue]; + id dg = self.delegate; if (presenting && finished) { - if ([_delegate respondsToSelector:@selector(calloutViewDidAppear:)]) - [_delegate calloutViewDidAppear:(id)self]; + if ([dg respondsToSelector:@selector(calloutViewDidAppear:)]) + [dg calloutViewDidAppear:(id)self]; } else if (!presenting && finished) { [self removeFromParent]; [self.layer removeAnimationForKey:@"dismiss"]; - if ([_delegate respondsToSelector:@selector(calloutViewDidDisappear:)]) - [_delegate calloutViewDidDisappear:(id)self]; + if ([dg respondsToSelector:@selector(calloutViewDidDisappear:)]) + [dg calloutViewDidDisappear:(id)self]; } } diff --git a/common/local_libs/lodepng/wgmaplyCMakeLists.txt b/common/local_libs/lodepng/wgmaplyCMakeLists.txt new file mode 100644 index 0000000000..40fe8b8fc0 --- /dev/null +++ b/common/local_libs/lodepng/wgmaplyCMakeLists.txt @@ -0,0 +1,17 @@ +cmake_minimum_required(VERSION 3.4.1) + +target_include_directories( + ${WGTARGET} + + PUBLIC + + "${CMAKE_CURRENT_LIST_DIR}" +) + +target_sources( + ${WGTARGET} + + PUBLIC + + "${CMAKE_CURRENT_LIST_DIR}/lodepng.cpp" +) diff --git a/ios/apps/AutoTester/Assets.xcassets/AccentColor.colorset/Contents.json b/ios/apps/AutoTester/Assets.xcassets/AccentColor.colorset/Contents.json new file mode 100644 index 0000000000..eb87897008 --- /dev/null +++ b/ios/apps/AutoTester/Assets.xcassets/AccentColor.colorset/Contents.json @@ -0,0 +1,11 @@ +{ + "colors" : [ + { + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/ios/apps/AutoTester/Assets.xcassets/AppIcon.appiconset/Contents.json b/ios/apps/AutoTester/Assets.xcassets/AppIcon.appiconset/Contents.json new file mode 100644 index 0000000000..4e4cb5b7f1 --- /dev/null +++ b/ios/apps/AutoTester/Assets.xcassets/AppIcon.appiconset/Contents.json @@ -0,0 +1,116 @@ +{ + "images" : [ + { + "filename" : "icon_20pt@2x.png", + "idiom" : "iphone", + "scale" : "2x", + "size" : "20x20" + }, + { + "filename" : "icon_20pt@3x.png", + "idiom" : "iphone", + "scale" : "3x", + "size" : "20x20" + }, + { + "filename" : "icon_29pt@2x.png", + "idiom" : "iphone", + "scale" : "2x", + "size" : "29x29" + }, + { + "filename" : "icon_29pt@3x.png", + "idiom" : "iphone", + "scale" : "3x", + "size" : "29x29" + }, + { + "filename" : "icon_40pt@2x.png", + "idiom" : "iphone", + "scale" : "2x", + "size" : "40x40" + }, + { + "filename" : "icon_40pt@3x.png", + "idiom" : "iphone", + "scale" : "3x", + "size" : "40x40" + }, + { + "filename" : "icon_60pt@2x.png", + "idiom" : "iphone", + "scale" : "2x", + "size" : "60x60" + }, + { + "filename" : "icon_60pt@3x.png", + "idiom" : "iphone", + "scale" : "3x", + "size" : "60x60" + }, + { + "filename" : "icon_20pt.png", + "idiom" : "ipad", + "scale" : "1x", + "size" : "20x20" + }, + { + "filename" : "icon_20pt@2x-1.png", + "idiom" : "ipad", + "scale" : "2x", + "size" : "20x20" + }, + { + "filename" : "icon_29pt.png", + "idiom" : "ipad", + "scale" : "1x", + "size" : "29x29" + }, + { + "filename" : "icon_29pt@2x-1.png", + "idiom" : "ipad", + "scale" : "2x", + "size" : "29x29" + }, + { + "filename" : "icon_40pt.png", + "idiom" : "ipad", + "scale" : "1x", + "size" : "40x40" + }, + { + "filename" : "icon_40pt@2x-1.png", + "idiom" : "ipad", + "scale" : "2x", + "size" : "40x40" + }, + { + "filename" : "icon_76pt.png", + "idiom" : "ipad", + "scale" : "1x", + "size" : "76x76" + }, + { + "filename" : "icon_76pt@2x.png", + "idiom" : "ipad", + "scale" : "2x", + "size" : "76x76" + }, + { + "filename" : "icon_83.5@2x.png", + "idiom" : "ipad", + "scale" : "2x", + "size" : "83.5x83.5" + }, + { + "filename" : "Icon.png", + "idiom" : "ios-marketing", + "scale" : "1x", + "size" : "1024x1024" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/ios/apps/AutoTester/Assets.xcassets/AppIcon.appiconset/Icon.png b/ios/apps/AutoTester/Assets.xcassets/AppIcon.appiconset/Icon.png new file mode 100644 index 0000000000..7b7855f54b Binary files /dev/null and b/ios/apps/AutoTester/Assets.xcassets/AppIcon.appiconset/Icon.png differ diff --git a/ios/apps/AutoTester/Assets.xcassets/AppIcon.appiconset/icon_20pt.png b/ios/apps/AutoTester/Assets.xcassets/AppIcon.appiconset/icon_20pt.png new file mode 100644 index 0000000000..d419830178 Binary files /dev/null and b/ios/apps/AutoTester/Assets.xcassets/AppIcon.appiconset/icon_20pt.png differ diff --git a/ios/apps/AutoTester/Assets.xcassets/AppIcon.appiconset/icon_20pt@2x-1.png b/ios/apps/AutoTester/Assets.xcassets/AppIcon.appiconset/icon_20pt@2x-1.png new file mode 100644 index 0000000000..47bf9738cf Binary files /dev/null and b/ios/apps/AutoTester/Assets.xcassets/AppIcon.appiconset/icon_20pt@2x-1.png differ diff --git a/ios/apps/AutoTester/Assets.xcassets/AppIcon.appiconset/icon_20pt@2x.png b/ios/apps/AutoTester/Assets.xcassets/AppIcon.appiconset/icon_20pt@2x.png new file mode 100644 index 0000000000..47bf9738cf Binary files /dev/null and b/ios/apps/AutoTester/Assets.xcassets/AppIcon.appiconset/icon_20pt@2x.png differ diff --git a/ios/apps/AutoTester/Assets.xcassets/AppIcon.appiconset/icon_20pt@3x.png b/ios/apps/AutoTester/Assets.xcassets/AppIcon.appiconset/icon_20pt@3x.png new file mode 100644 index 0000000000..b9e98dbfa5 Binary files /dev/null and b/ios/apps/AutoTester/Assets.xcassets/AppIcon.appiconset/icon_20pt@3x.png differ diff --git a/ios/apps/AutoTester/Assets.xcassets/AppIcon.appiconset/icon_29pt.png b/ios/apps/AutoTester/Assets.xcassets/AppIcon.appiconset/icon_29pt.png new file mode 100644 index 0000000000..9066f8b45e Binary files /dev/null and b/ios/apps/AutoTester/Assets.xcassets/AppIcon.appiconset/icon_29pt.png differ diff --git a/ios/apps/AutoTester/Assets.xcassets/AppIcon.appiconset/icon_29pt@2x-1.png b/ios/apps/AutoTester/Assets.xcassets/AppIcon.appiconset/icon_29pt@2x-1.png new file mode 100644 index 0000000000..fab2dd8499 Binary files /dev/null and b/ios/apps/AutoTester/Assets.xcassets/AppIcon.appiconset/icon_29pt@2x-1.png differ diff --git a/ios/apps/AutoTester/Assets.xcassets/AppIcon.appiconset/icon_29pt@2x.png b/ios/apps/AutoTester/Assets.xcassets/AppIcon.appiconset/icon_29pt@2x.png new file mode 100644 index 0000000000..fab2dd8499 Binary files /dev/null and b/ios/apps/AutoTester/Assets.xcassets/AppIcon.appiconset/icon_29pt@2x.png differ diff --git a/ios/apps/AutoTester/Assets.xcassets/AppIcon.appiconset/icon_29pt@3x.png b/ios/apps/AutoTester/Assets.xcassets/AppIcon.appiconset/icon_29pt@3x.png new file mode 100644 index 0000000000..3fc5a63907 Binary files /dev/null and b/ios/apps/AutoTester/Assets.xcassets/AppIcon.appiconset/icon_29pt@3x.png differ diff --git a/ios/apps/AutoTester/Assets.xcassets/AppIcon.appiconset/icon_40pt.png b/ios/apps/AutoTester/Assets.xcassets/AppIcon.appiconset/icon_40pt.png new file mode 100644 index 0000000000..47bf9738cf Binary files /dev/null and b/ios/apps/AutoTester/Assets.xcassets/AppIcon.appiconset/icon_40pt.png differ diff --git a/ios/apps/AutoTester/Assets.xcassets/AppIcon.appiconset/icon_40pt@2x-1.png b/ios/apps/AutoTester/Assets.xcassets/AppIcon.appiconset/icon_40pt@2x-1.png new file mode 100644 index 0000000000..453ab94f16 Binary files /dev/null and b/ios/apps/AutoTester/Assets.xcassets/AppIcon.appiconset/icon_40pt@2x-1.png differ diff --git a/ios/apps/AutoTester/Assets.xcassets/AppIcon.appiconset/icon_40pt@2x.png b/ios/apps/AutoTester/Assets.xcassets/AppIcon.appiconset/icon_40pt@2x.png new file mode 100644 index 0000000000..453ab94f16 Binary files /dev/null and b/ios/apps/AutoTester/Assets.xcassets/AppIcon.appiconset/icon_40pt@2x.png differ diff --git a/ios/apps/AutoTester/Assets.xcassets/AppIcon.appiconset/icon_40pt@3x.png b/ios/apps/AutoTester/Assets.xcassets/AppIcon.appiconset/icon_40pt@3x.png new file mode 100644 index 0000000000..1e5b98a365 Binary files /dev/null and b/ios/apps/AutoTester/Assets.xcassets/AppIcon.appiconset/icon_40pt@3x.png differ diff --git a/ios/apps/AutoTester/Assets.xcassets/AppIcon.appiconset/icon_60pt@2x.png b/ios/apps/AutoTester/Assets.xcassets/AppIcon.appiconset/icon_60pt@2x.png new file mode 100644 index 0000000000..1e5b98a365 Binary files /dev/null and b/ios/apps/AutoTester/Assets.xcassets/AppIcon.appiconset/icon_60pt@2x.png differ diff --git a/ios/apps/AutoTester/Assets.xcassets/AppIcon.appiconset/icon_60pt@3x.png b/ios/apps/AutoTester/Assets.xcassets/AppIcon.appiconset/icon_60pt@3x.png new file mode 100644 index 0000000000..7226d3e94b Binary files /dev/null and b/ios/apps/AutoTester/Assets.xcassets/AppIcon.appiconset/icon_60pt@3x.png differ diff --git a/ios/apps/AutoTester/Assets.xcassets/AppIcon.appiconset/icon_76pt.png b/ios/apps/AutoTester/Assets.xcassets/AppIcon.appiconset/icon_76pt.png new file mode 100644 index 0000000000..249de7056a Binary files /dev/null and b/ios/apps/AutoTester/Assets.xcassets/AppIcon.appiconset/icon_76pt.png differ diff --git a/ios/apps/AutoTester/Assets.xcassets/AppIcon.appiconset/icon_76pt@2x.png b/ios/apps/AutoTester/Assets.xcassets/AppIcon.appiconset/icon_76pt@2x.png new file mode 100644 index 0000000000..9b1c45a31b Binary files /dev/null and b/ios/apps/AutoTester/Assets.xcassets/AppIcon.appiconset/icon_76pt@2x.png differ diff --git a/ios/apps/AutoTester/Assets.xcassets/AppIcon.appiconset/icon_83.5@2x.png b/ios/apps/AutoTester/Assets.xcassets/AppIcon.appiconset/icon_83.5@2x.png new file mode 100644 index 0000000000..cb31df0bca Binary files /dev/null and b/ios/apps/AutoTester/Assets.xcassets/AppIcon.appiconset/icon_83.5@2x.png differ diff --git a/ios/apps/AutoTester/Assets.xcassets/Contents.json b/ios/apps/AutoTester/Assets.xcassets/Contents.json new file mode 100644 index 0000000000..73c00596a7 --- /dev/null +++ b/ios/apps/AutoTester/Assets.xcassets/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/ios/apps/AutoTester/AutoTester.xcodeproj/project.pbxproj b/ios/apps/AutoTester/AutoTester.xcodeproj/project.pbxproj index 991a07a479..9eecbcabcc 100644 --- a/ios/apps/AutoTester/AutoTester.xcodeproj/project.pbxproj +++ b/ios/apps/AutoTester/AutoTester.xcodeproj/project.pbxproj @@ -73,6 +73,19 @@ 2B4BA6D91C83A22000DC1562 /* uturn.geojson in Resources */ = {isa = PBXBuildFile; fileRef = 2B4BA6CD1C83A22000DC1562 /* uturn.geojson */; }; 2B5E8AF51E943560000B39E5 /* greensquare.png in Resources */ = {isa = PBXBuildFile; fileRef = 2B5E8AF41E943560000B39E5 /* greensquare.png */; }; 2B60F4462452706200CF9339 /* VectorMBTilesTestCase.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2B1C264E1C90FFFC00C71B0A /* VectorMBTilesTestCase.swift */; }; + 2B6611E625D1C35D009D228F /* AirwayTestCase.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2B6611E525D1C35D009D228F /* AirwayTestCase.swift */; }; + 2B66120D25D1E6D8009D228F /* Airspace_Boundary.prj in Resources */ = {isa = PBXBuildFile; fileRef = 2B66120125D1E6D7009D228F /* Airspace_Boundary.prj */; }; + 2B66120E25D1E6D8009D228F /* Airspace_Boundary.cpg in Resources */ = {isa = PBXBuildFile; fileRef = 2B66120225D1E6D7009D228F /* Airspace_Boundary.cpg */; }; + 2B66120F25D1E6D8009D228F /* ATS_Route.cpg in Resources */ = {isa = PBXBuildFile; fileRef = 2B66120325D1E6D8009D228F /* ATS_Route.cpg */; }; + 2B66121025D1E6D8009D228F /* Airspace_Boundary.dbf in Resources */ = {isa = PBXBuildFile; fileRef = 2B66120425D1E6D8009D228F /* Airspace_Boundary.dbf */; }; + 2B66121125D1E6D8009D228F /* ATS_Route.dbf in Resources */ = {isa = PBXBuildFile; fileRef = 2B66120525D1E6D8009D228F /* ATS_Route.dbf */; }; + 2B66121225D1E6D8009D228F /* ATS_Route.shp in Resources */ = {isa = PBXBuildFile; fileRef = 2B66120625D1E6D8009D228F /* ATS_Route.shp */; }; + 2B66121325D1E6D8009D228F /* ATS_Route.shx in Resources */ = {isa = PBXBuildFile; fileRef = 2B66120725D1E6D8009D228F /* ATS_Route.shx */; }; + 2B66121425D1E6D8009D228F /* Airspace_Boundary.shp in Resources */ = {isa = PBXBuildFile; fileRef = 2B66120825D1E6D8009D228F /* Airspace_Boundary.shp */; }; + 2B66121525D1E6D8009D228F /* Airspace_Boundary.shx in Resources */ = {isa = PBXBuildFile; fileRef = 2B66120925D1E6D8009D228F /* Airspace_Boundary.shx */; }; + 2B66121625D1E6D8009D228F /* ATS_Route.xml in Resources */ = {isa = PBXBuildFile; fileRef = 2B66120A25D1E6D8009D228F /* ATS_Route.xml */; }; + 2B66121725D1E6D8009D228F /* ATS_Route.prj in Resources */ = {isa = PBXBuildFile; fileRef = 2B66120B25D1E6D8009D228F /* ATS_Route.prj */; }; + 2B66121825D1E6D8009D228F /* Airspace_Boundary.xml in Resources */ = {isa = PBXBuildFile; fileRef = 2B66120C25D1E6D8009D228F /* Airspace_Boundary.xml */; }; 2B68A43C225D06A8009CC720 /* ImageReloadTestCase.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2B68A43B225D06A8009CC720 /* ImageReloadTestCase.swift */; }; 2B6953681C7E67DD007FC51E /* colorramp.png in Resources */ = {isa = PBXBuildFile; fileRef = 2B6953671C7E67DD007FC51E /* colorramp.png */; }; 2B73D6AE207C0D5F00AF5095 /* NotoSans-Bold.ttf in Resources */ = {isa = PBXBuildFile; fileRef = 2B8F3258201021CF006DDE27 /* NotoSans-Bold.ttf */; }; @@ -102,15 +115,24 @@ 2BC41C8321F00AA6002926B7 /* BNGCustomMapTestCase.swift in Sources */ = {isa = PBXBuildFile; fileRef = D84AA5931C1A1C9700413B76 /* BNGCustomMapTestCase.swift */; }; 2BC41C8521F00AA9002926B7 /* BNGTestCase.swift in Sources */ = {isa = PBXBuildFile; fileRef = D84AA5981C1A2B9800413B76 /* BNGTestCase.swift */; }; 2BD358F31DD3EA0400081EA8 /* AnimatedMarkersTestCase.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2BD358F21DD3EA0400081EA8 /* AnimatedMarkersTestCase.swift */; }; + 2BD914BD256F1C8D00E724A8 /* icon_167.png in Resources */ = {isa = PBXBuildFile; fileRef = 2BD914BB256F1C8D00E724A8 /* icon_167.png */; }; + 2BD914BE256F1C8D00E724A8 /* icon_152.png in Resources */ = {isa = PBXBuildFile; fileRef = 2BD914BC256F1C8D00E724A8 /* icon_152.png */; }; + 2BD914E8256F1F8A00E724A8 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 2BD914E7256F1F8A00E724A8 /* Assets.xcassets */; }; 2BDEB3021C924842003259B3 /* ExtrudedModelTestCase.m in Sources */ = {isa = PBXBuildFile; fileRef = 2BDEB3011C924842003259B3 /* ExtrudedModelTestCase.m */; }; 2BDEB3141C9391EB003259B3 /* GreatCircleTestCase.m in Sources */ = {isa = PBXBuildFile; fileRef = 2BDEB3131C9391EB003259B3 /* GreatCircleTestCase.m */; }; 2BF29B8C24D2268200553963 /* LegendViewController.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 2BF29B8B24D2268200553963 /* LegendViewController.storyboard */; }; 2BF29B8F24D226B000553963 /* LegendViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2BF29B8E24D226B000553963 /* LegendViewController.swift */; }; 2BFC7E511D132DCB0040E2A3 /* ScreenMarkersTestCase.m in Sources */ = {isa = PBXBuildFile; fileRef = 2BFC7E501D132DCB0040E2A3 /* ScreenMarkersTestCase.m */; }; + 313C521625D329070036C22E /* MovingScreenMarkersTestCase.swift in Sources */ = {isa = PBXBuildFile; fileRef = 313C521425D329070036C22E /* MovingScreenMarkersTestCase.swift */; }; + 313C522325D369270036C22E /* GlobeRotationTestCase.swift in Sources */ = {isa = PBXBuildFile; fileRef = 313C522225D369260036C22E /* GlobeRotationTestCase.swift */; }; + 316B5CF3260526AD000B4F15 /* maptiler_expr_test.json in Resources */ = {isa = PBXBuildFile; fileRef = 316B5CF1260526AD000B4F15 /* maptiler_expr_test.json */; }; + 316B5CFC260A5573000B4F15 /* maptiler_test_circles.json in Resources */ = {isa = PBXBuildFile; fileRef = 316B5CFA260A5573000B4F15 /* maptiler_test_circles.json */; }; + 316B5CFE260A58A8000B4F15 /* MapTilerCircleTestCase.swift in Sources */ = {isa = PBXBuildFile; fileRef = 316B5CFD260A58A8000B4F15 /* MapTilerCircleTestCase.swift */; }; + 31833793259E5291005FEF70 /* ChangeVectorsTestCase.mm in Sources */ = {isa = PBXBuildFile; fileRef = 31833792259E5291005FEF70 /* ChangeVectorsTestCase.mm */; }; + 3183380F25A67CD8005FEF70 /* RepresentationsTestCase.mm in Sources */ = {isa = PBXBuildFile; fileRef = 3183380C25A67CD8005FEF70 /* RepresentationsTestCase.mm */; }; 8820852B1DC81051008F8E76 /* Issue721TestCase.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8820852A1DC81051008F8E76 /* Issue721TestCase.swift */; }; 88BF11491BCD3DA40002205E /* MaplyTestCase.m in Sources */ = {isa = PBXBuildFile; fileRef = 88BF11481BCD3DA40002205E /* MaplyTestCase.m */; }; 88E4B8CA1B83B6AB0050D21B /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 88E4B8C81B83B6AB0050D21B /* Main.storyboard */; }; - 88E4B8CC1B83B6AB0050D21B /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 88E4B8CB1B83B6AB0050D21B /* Images.xcassets */; }; 88E4B8CF1B83B6AB0050D21B /* LaunchScreen.xib in Resources */ = {isa = PBXBuildFile; fileRef = 88E4B8CD1B83B6AB0050D21B /* LaunchScreen.xib */; }; 88F0951F1ECD041300E7E686 /* AutoTesterJenkins.plist in Resources */ = {isa = PBXBuildFile; fileRef = 88F0951E1ECD041300E7E686 /* AutoTesterJenkins.plist */; }; 88FF5BE71BC31D53002F15CE /* map_pin.png in Resources */ = {isa = PBXBuildFile; fileRef = 88FF5BE11BC31D53002F15CE /* map_pin.png */; }; @@ -664,6 +686,19 @@ 2B4BA6CC1C83A22000DC1562 /* track.geojson */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = track.geojson; sourceTree = ""; }; 2B4BA6CD1C83A22000DC1562 /* uturn.geojson */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = uturn.geojson; sourceTree = ""; }; 2B5E8AF41E943560000B39E5 /* greensquare.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = greensquare.png; path = resources/greensquare.png; sourceTree = ""; }; + 2B6611E525D1C35D009D228F /* AirwayTestCase.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AirwayTestCase.swift; sourceTree = ""; }; + 2B66120125D1E6D7009D228F /* Airspace_Boundary.prj */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; name = Airspace_Boundary.prj; path = ../../../../resources/vectors/faa/Airspace_Boundary.prj; sourceTree = ""; }; + 2B66120225D1E6D7009D228F /* Airspace_Boundary.cpg */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; name = Airspace_Boundary.cpg; path = ../../../../resources/vectors/faa/Airspace_Boundary.cpg; sourceTree = ""; }; + 2B66120325D1E6D8009D228F /* ATS_Route.cpg */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; name = ATS_Route.cpg; path = ../../../../resources/vectors/faa/ATS_Route.cpg; sourceTree = ""; }; + 2B66120425D1E6D8009D228F /* Airspace_Boundary.dbf */ = {isa = PBXFileReference; lastKnownFileType = file; name = Airspace_Boundary.dbf; path = ../../../../resources/vectors/faa/Airspace_Boundary.dbf; sourceTree = ""; }; + 2B66120525D1E6D8009D228F /* ATS_Route.dbf */ = {isa = PBXFileReference; lastKnownFileType = file; name = ATS_Route.dbf; path = ../../../../resources/vectors/faa/ATS_Route.dbf; sourceTree = ""; }; + 2B66120625D1E6D8009D228F /* ATS_Route.shp */ = {isa = PBXFileReference; lastKnownFileType = file; name = ATS_Route.shp; path = ../../../../resources/vectors/faa/ATS_Route.shp; sourceTree = ""; }; + 2B66120725D1E6D8009D228F /* ATS_Route.shx */ = {isa = PBXFileReference; lastKnownFileType = file; name = ATS_Route.shx; path = ../../../../resources/vectors/faa/ATS_Route.shx; sourceTree = ""; }; + 2B66120825D1E6D8009D228F /* Airspace_Boundary.shp */ = {isa = PBXFileReference; lastKnownFileType = file; name = Airspace_Boundary.shp; path = ../../../../resources/vectors/faa/Airspace_Boundary.shp; sourceTree = ""; }; + 2B66120925D1E6D8009D228F /* Airspace_Boundary.shx */ = {isa = PBXFileReference; lastKnownFileType = file; name = Airspace_Boundary.shx; path = ../../../../resources/vectors/faa/Airspace_Boundary.shx; sourceTree = ""; }; + 2B66120A25D1E6D8009D228F /* ATS_Route.xml */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xml; name = ATS_Route.xml; path = ../../../../resources/vectors/faa/ATS_Route.xml; sourceTree = ""; }; + 2B66120B25D1E6D8009D228F /* ATS_Route.prj */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; name = ATS_Route.prj; path = ../../../../resources/vectors/faa/ATS_Route.prj; sourceTree = ""; }; + 2B66120C25D1E6D8009D228F /* Airspace_Boundary.xml */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xml; name = Airspace_Boundary.xml; path = ../../../../resources/vectors/faa/Airspace_Boundary.xml; sourceTree = ""; }; 2B68A43B225D06A8009CC720 /* ImageReloadTestCase.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ImageReloadTestCase.swift; sourceTree = ""; }; 2B6953671C7E67DD007FC51E /* colorramp.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = colorramp.png; path = AutoTester/colorramp.png; sourceTree = ""; }; 2B73D6B2207C106C00AF5095 /* GlobeSamplerTestCase.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GlobeSamplerTestCase.swift; sourceTree = ""; }; @@ -686,6 +721,9 @@ 2BC2DCEE1D2345AF0088D350 /* PagingLayerTestCase.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PagingLayerTestCase.h; sourceTree = ""; }; 2BC2DCEF1D2345AF0088D350 /* PagingLayerTestCase.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PagingLayerTestCase.m; sourceTree = ""; }; 2BD358F21DD3EA0400081EA8 /* AnimatedMarkersTestCase.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AnimatedMarkersTestCase.swift; sourceTree = ""; }; + 2BD914BB256F1C8D00E724A8 /* icon_167.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = icon_167.png; path = icons/icon_167.png; sourceTree = ""; }; + 2BD914BC256F1C8D00E724A8 /* icon_152.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = icon_152.png; path = icons/icon_152.png; sourceTree = ""; }; + 2BD914E7256F1F8A00E724A8 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 2BDEB3001C924842003259B3 /* ExtrudedModelTestCase.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ExtrudedModelTestCase.h; sourceTree = ""; }; 2BDEB3011C924842003259B3 /* ExtrudedModelTestCase.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ExtrudedModelTestCase.m; sourceTree = ""; }; 2BDEB3121C9391EB003259B3 /* GreatCircleTestCase.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GreatCircleTestCase.h; sourceTree = ""; }; @@ -695,6 +733,15 @@ 2BF29B8E24D226B000553963 /* LegendViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LegendViewController.swift; sourceTree = ""; }; 2BFC7E4F1D132DCB0040E2A3 /* ScreenMarkersTestCase.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ScreenMarkersTestCase.h; sourceTree = ""; }; 2BFC7E501D132DCB0040E2A3 /* ScreenMarkersTestCase.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ScreenMarkersTestCase.m; sourceTree = ""; }; + 313C521425D329070036C22E /* MovingScreenMarkersTestCase.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MovingScreenMarkersTestCase.swift; sourceTree = ""; }; + 313C522225D369260036C22E /* GlobeRotationTestCase.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GlobeRotationTestCase.swift; sourceTree = ""; }; + 316B5CF1260526AD000B4F15 /* maptiler_expr_test.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = maptiler_expr_test.json; sourceTree = ""; }; + 316B5CFA260A5573000B4F15 /* maptiler_test_circles.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = maptiler_test_circles.json; sourceTree = ""; }; + 316B5CFD260A58A8000B4F15 /* MapTilerCircleTestCase.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MapTilerCircleTestCase.swift; sourceTree = ""; }; + 31833791259E5291005FEF70 /* ChangeVectorsTestCase.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ChangeVectorsTestCase.h; sourceTree = ""; }; + 31833792259E5291005FEF70 /* ChangeVectorsTestCase.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = ChangeVectorsTestCase.mm; sourceTree = ""; }; + 3183380C25A67CD8005FEF70 /* RepresentationsTestCase.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = RepresentationsTestCase.mm; sourceTree = ""; }; + 3183380E25A67CD8005FEF70 /* RepresentationsTestCase.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RepresentationsTestCase.h; sourceTree = ""; }; 8810B2D11BA9D45F00446CE3 /* MaplyTesterBridge.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MaplyTesterBridge.h; sourceTree = ""; }; 8820852A1DC81051008F8E76 /* Issue721TestCase.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Issue721TestCase.swift; sourceTree = ""; }; 886FCCC21BA73C1D00494BB0 /* StartupViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = StartupViewController.swift; sourceTree = ""; }; @@ -707,7 +754,6 @@ 88E4B8C31B83B6AB0050D21B /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 88E4B8C41B83B6AB0050D21B /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; 88E4B8C91B83B6AB0050D21B /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; - 88E4B8CB1B83B6AB0050D21B /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Images.xcassets; sourceTree = ""; }; 88E4B8CE1B83B6AB0050D21B /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/LaunchScreen.xib; sourceTree = ""; }; 88F0951E1ECD041300E7E686 /* AutoTesterJenkins.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = AutoTesterJenkins.plist; sourceTree = ""; }; 88FF5BE11BC31D53002F15CE /* map_pin.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = map_pin.png; path = resources/map_pin.png; sourceTree = ""; }; @@ -1228,6 +1274,8 @@ 2B4B30B92395F07000854073 /* vector maps */ = { isa = PBXGroup; children = ( + 316B5CFA260A5573000B4F15 /* maptiler_test_circles.json */, + 316B5CF1260526AD000B4F15 /* maptiler_expr_test.json */, 2B4B30BD2395F08C00854073 /* default.sld */, 2B4B30C02395F08D00854073 /* mapbox_satellite-streets-v9.json */, 2B4B30BF2395F08C00854073 /* mapbox_satellite-v9.json */, @@ -1260,6 +1308,25 @@ path = resources; sourceTree = ""; }; + 2B6611E825D1C967009D228F /* airways */ = { + isa = PBXGroup; + children = ( + 2B66120225D1E6D7009D228F /* Airspace_Boundary.cpg */, + 2B66120425D1E6D8009D228F /* Airspace_Boundary.dbf */, + 2B66120125D1E6D7009D228F /* Airspace_Boundary.prj */, + 2B66120825D1E6D8009D228F /* Airspace_Boundary.shp */, + 2B66120925D1E6D8009D228F /* Airspace_Boundary.shx */, + 2B66120C25D1E6D8009D228F /* Airspace_Boundary.xml */, + 2B66120325D1E6D8009D228F /* ATS_Route.cpg */, + 2B66120525D1E6D8009D228F /* ATS_Route.dbf */, + 2B66120B25D1E6D8009D228F /* ATS_Route.prj */, + 2B66120625D1E6D8009D228F /* ATS_Route.shp */, + 2B66120725D1E6D8009D228F /* ATS_Route.shx */, + 2B66120A25D1E6D8009D228F /* ATS_Route.xml */, + ); + path = airways; + sourceTree = ""; + }; 3651D5D283D23F0C2EFDE845 /* Frameworks */ = { isa = PBXGroup; children = ( @@ -1278,90 +1345,98 @@ 88BF115A1BCDA9530002205E /* testCases */ = { isa = PBXGroup; children = ( - 2B29944C243BA16000677DE4 /* SimpleStyleTestCase.swift */, - 2B4B30A72395E0DE00854073 /* GlyphProblemTestCase.h */, - 2B4B30A62395E0DE00854073 /* GlyphProblemTestCase.m */, - 2B4B30A82395E0DE00854073 /* LayerStartupShutdownTestCase.swift */, - 2B4B30A32395E0DE00854073 /* MapboxTestCase.swift */, - 2B4B30A92395E0DE00854073 /* MapTilerTestCase.swift */, - 2B4B30A52395E0DE00854073 /* StartupShutdownTestCase.swift */, - 88BF11471BCD3DA40002205E /* MaplyTestCase.h */, - 88BF11481BCD3DA40002205E /* MaplyTestCase.m */, - D84433EC1C14E4FE00A52117 /* CartoDBInterpreter.h */, - D84433ED1C14E4FE00A52117 /* CartoDBInterpreter.m */, - 2B73D6B2207C106C00AF5095 /* GlobeSamplerTestCase.swift */, - 2B127BFE201802BB0099F405 /* OpenMapTilesHybridTestCase.swift */, - E5679F4C1CB7669400369A15 /* FullAnimationTest.h */, - E5679F4D1CB7669400369A15 /* FullAnimationTest.m */, - E5679F421CB72DE800369A15 /* LabelAnimationTestCase.h */, - E5679F431CB72DE800369A15 /* LabelAnimationTestCase.m */, - E5679F441CB72DE800369A15 /* WMSTestCase.h */, - E5679F451CB72DE800369A15 /* WMSTestCase.m */, - E5679F461CB72DE800369A15 /* FindHeightTestCase.h */, - E5679F471CB72DE800369A15 /* FindHeightTestCase.m */, + 2B2A417C1D83454900098E12 /* ActiveObjectTestCase.h */, + 2B2A417D1D83454900098E12 /* ActiveObjectTestCase.m */, D8E12ECC1BDE9FD500BB7BC7 /* AnimatedBasemapTestCase.swift */, + 2BD358F21DD3EA0400081EA8 /* AnimatedMarkersTestCase.swift */, + E5D2D6741DEDEABF00E02305 /* AnimationDelegateTestCase.swift */, + 2B6611E525D1C35D009D228F /* AirwayTestCase.swift */, + 2B1E85A522B44D5800AB7208 /* BillboardTestCase.swift */, D84AA5931C1A1C9700413B76 /* BNGCustomMapTestCase.swift */, D84AA5981C1A2B9800413B76 /* BNGTestCase.swift */, + D84433EC1C14E4FE00A52117 /* CartoDBInterpreter.h */, + D84433ED1C14E4FE00A52117 /* CartoDBInterpreter.m */, + 2B7787C921CDA47F006ABF5D /* CartoDBLightTestCase.swift */, D84433E91C14E4E700A52117 /* CartoDBTestCase.h */, D84433EA1C14E4E700A52117 /* CartoDBTestCase.m */, + 31833791259E5291005FEF70 /* ChangeVectorsTestCase.h */, + 31833792259E5291005FEF70 /* ChangeVectorsTestCase.mm */, D8228AA71BE786BE001D6914 /* ClusteredMarkersTestCase.swift */, 2BDEB3001C924842003259B3 /* ExtrudedModelTestCase.h */, 2BDEB3011C924842003259B3 /* ExtrudedModelTestCase.m */, + E5679F461CB72DE800369A15 /* FindHeightTestCase.h */, + E5679F471CB72DE800369A15 /* FindHeightTestCase.m */, + E5679F4C1CB7669400369A15 /* FullAnimationTest.h */, + E5679F4D1CB7669400369A15 /* FullAnimationTest.m */, 2B249F3E23F4A82600CFA3D0 /* GeographyClass.swift */, + E5941DC01E0CEE7300E1C8B3 /* GeoJSONStyleTestCase.swift */, + 313C522225D369260036C22E /* GlobeRotationTestCase.swift */, + 2B73D6B2207C106C00AF5095 /* GlobeSamplerTestCase.swift */, + 2B4B30A72395E0DE00854073 /* GlyphProblemTestCase.h */, + 2B4B30A62395E0DE00854073 /* GlyphProblemTestCase.m */, 2BDEB3121C9391EB003259B3 /* GreatCircleTestCase.h */, 2BDEB3131C9391EB003259B3 /* GreatCircleTestCase.m */, + 2B68A43B225D06A8009CC720 /* ImageReloadTestCase.swift */, + 8820852A1DC81051008F8E76 /* Issue721TestCase.swift */, + E5679F421CB72DE800369A15 /* LabelAnimationTestCase.h */, + E5679F431CB72DE800369A15 /* LabelAnimationTestCase.m */, D8F2FE251BE7BD630058A310 /* LabelsTestCase.h */, D8F2FE261BE7BD630058A310 /* LabelsTestCase.m */, + 2B4B30A82395E0DE00854073 /* LayerStartupShutdownTestCase.swift */, + 2B8849E91E37F7CB0027C397 /* LIDARTestCase.h */, + 2B8849EA1E37F7CB0027C397 /* LIDARTestCase.mm */, + E5CC4E1F1DF7CBEA00C0D4DE /* LocationTrackingRealTestCase.swift */, + E5D2D65E1DE65CE400E02305 /* LocationTrackingSimTestCase.swift */, D8200CA21BE9624300B22CF5 /* LoftedPolysTestCase.h */, D8200CA31BE9624300B22CF5 /* LoftedPolysTestCase.m */, + 2B4B30A32395E0DE00854073 /* MapboxTestCase.swift */, + 88BF11471BCD3DA40002205E /* MaplyTestCase.h */, + 88BF11481BCD3DA40002205E /* MaplyTestCase.m */, + 2B4B30A92395E0DE00854073 /* MapTilerTestCase.swift */, D8F2FE281BE7C2000058A310 /* MarkersTestCase.swift */, D8F2FE2A1BE7CAD30058A310 /* MegaMarkersTestCase.h */, D8F2FE2B1BE7CAD30058A310 /* MegaMarkersTestCase.m */, D8F2FE2D1BE7CF310058A310 /* ModelsTestCase.swift */, D8228A911BE77816001D6914 /* MovingScreenLabelsTestCase.swift */, + 313C521425D329070036C22E /* MovingScreenMarkersTestCase.swift */, D84433E61C14A46100A52117 /* NASAGIBSTestCase.swift */, + 2B4B63AD23611D690008C8C1 /* OfflineRenderTestCase.swift */, + 2B127BFE201802BB0099F405 /* OpenMapTilesHybridTestCase.swift */, + 2BC2DCEE1D2345AF0088D350 /* PagingLayerTestCase.h */, + 2BC2DCEF1D2345AF0088D350 /* PagingLayerTestCase.m */, 8895D8AE1CA4ABA9004387FB /* ParticleTest.h */, 8895D8AF1CA4ABA9004387FB /* ParticleTest.mm */, D8D3E0671C4EDA1800F0D006 /* ParticleTestCase.swift */, + 3183380E25A67CD8005FEF70 /* RepresentationsTestCase.h */, + 3183380C25A67CD8005FEF70 /* RepresentationsTestCase.mm */, 2B392B6F1C5AC59D001EE40B /* RunwayBuilderTestCase.h */, 2B392B701C5AC59D001EE40B /* RunwayBuilderTestCase.m */, D8341A721BE2DFE100411A46 /* ScreenLabelsTestCase.swift */, 2BFC7E4F1D132DCB0040E2A3 /* ScreenMarkersTestCase.h */, 2BFC7E501D132DCB0040E2A3 /* ScreenMarkersTestCase.m */, + 2BC2DCCB1D1F4CE20088D350 /* ShapefileTestCase.h */, + 2BC2DCCC1D1F4CE20088D350 /* ShapefileTestCase.m */, D8200C9D1BE9516300B22CF5 /* ShapesTestCase.h */, D8200C9E1BE9516300B22CF5 /* ShapesTestCase.m */, + 2B29944C243BA16000677DE4 /* SimpleStyleTestCase.swift */, 88BF115E1BCDA9730002205E /* StamenWatercolorRemote.swift */, D8200C971BE93E6E00B22CF5 /* StarsSunTestCase.swift */, + 2B4B30A52395E0DE00854073 /* StartupShutdownTestCase.swift */, D8200CA01BE9563F00B22CF5 /* StickersTestCase.swift */, - 2B1C264E1C90FFFC00C71B0A /* VectorMBTilesTestCase.swift */, - D8341A6F1BE2C8D200411A46 /* VectorsTestCase.h */, - D8341A701BE2C8D200411A46 /* VectorsTestCase.m */, 2BC0FB761DCAA18A004125F1 /* TextureVectorTestCase.h */, 2BC0FB771DCAA18A004125F1 /* TextureVectorTestCase.m */, 2B158BED1D5D19E900103E04 /* VectorHoleTestCase.h */, 2B158BEE1D5D19E900103E04 /* VectorHoleTestCase.m */, - D8200C8F1BE92B2F00B22CF5 /* WideVectorsTestCase.h */, - D8200C901BE92B2F00B22CF5 /* WideVectorsTestCase.m */, - 2BC2DCCB1D1F4CE20088D350 /* ShapefileTestCase.h */, - 2BC2DCCC1D1F4CE20088D350 /* ShapefileTestCase.m */, - 2BC2DCEE1D2345AF0088D350 /* PagingLayerTestCase.h */, - 2BC2DCEF1D2345AF0088D350 /* PagingLayerTestCase.m */, + 2B1C264E1C90FFFC00C71B0A /* VectorMBTilesTestCase.swift */, + D8341A6F1BE2C8D200411A46 /* VectorsTestCase.h */, + D8341A701BE2C8D200411A46 /* VectorsTestCase.m */, 2BBB70811D5E9079009B67A6 /* VectorStyleTestCase.h */, 2BBB70821D5E9079009B67A6 /* VectorStyleTestCase.m */, - 2B2A417C1D83454900098E12 /* ActiveObjectTestCase.h */, - 2B2A417D1D83454900098E12 /* ActiveObjectTestCase.m */, - 8820852A1DC81051008F8E76 /* Issue721TestCase.swift */, - 2BD358F21DD3EA0400081EA8 /* AnimatedMarkersTestCase.swift */, - E5D2D65E1DE65CE400E02305 /* LocationTrackingSimTestCase.swift */, - E5D2D6741DEDEABF00E02305 /* AnimationDelegateTestCase.swift */, - E5CC4E1F1DF7CBEA00C0D4DE /* LocationTrackingRealTestCase.swift */, - E5941DC01E0CEE7300E1C8B3 /* GeoJSONStyleTestCase.swift */, - 2B8849E91E37F7CB0027C397 /* LIDARTestCase.h */, - 2B8849EA1E37F7CB0027C397 /* LIDARTestCase.mm */, - 2B7787C921CDA47F006ABF5D /* CartoDBLightTestCase.swift */, - 2B68A43B225D06A8009CC720 /* ImageReloadTestCase.swift */, - 2B1E85A522B44D5800AB7208 /* BillboardTestCase.swift */, - 2B4B63AD23611D690008C8C1 /* OfflineRenderTestCase.swift */, + D8200C8F1BE92B2F00B22CF5 /* WideVectorsTestCase.h */, + D8200C901BE92B2F00B22CF5 /* WideVectorsTestCase.m */, + E5679F441CB72DE800369A15 /* WMSTestCase.h */, + E5679F451CB72DE800369A15 /* WMSTestCase.m */, + 316B5CFD260A58A8000B4F15 /* MapTilerCircleTestCase.swift */, ); path = testCases; sourceTree = ""; @@ -1406,7 +1481,6 @@ 88F0951E1ECD041300E7E686 /* AutoTesterJenkins.plist */, 88E4B8CD1B83B6AB0050D21B /* LaunchScreen.xib */, 88E4B8C41B83B6AB0050D21B /* AppDelegate.swift */, - 88E4B8CB1B83B6AB0050D21B /* Images.xcassets */, 8810B2D11BA9D45F00446CE3 /* MaplyTesterBridge.h */, 88E4B8C31B83B6AB0050D21B /* Info.plist */, ); @@ -1416,6 +1490,10 @@ 88FF5BDF1BC31D3C002F15CE /* Resources */ = { isa = PBXGroup; children = ( + 2B6611E825D1C967009D228F /* airways */, + 2BD914E7256F1F8A00E724A8 /* Assets.xcassets */, + 2BD914BC256F1C8D00E724A8 /* icon_152.png */, + 2BD914BB256F1C8D00E724A8 /* icon_167.png */, 88FF5BE11BC31D53002F15CE /* map_pin.png */, 88FF5BE21BC31D53002F15CE /* Smiley_Face_Avatar_by_PixelTwist.png */, 88FF5BE41BC31D53002F15CE /* Star.png */, @@ -1971,7 +2049,7 @@ isa = PBXProject; attributes = { LastSwiftUpdateCheck = 0710; - LastUpgradeCheck = 1210; + LastUpgradeCheck = 1220; ORGANIZATIONNAME = "mousebird consulting"; TargetAttributes = { 88E4B8BE1B83B6AB0050D21B = { @@ -2054,6 +2132,7 @@ 2B4B30C62395F08D00854073 /* mapbox_satellite-v9.json in Resources */, 88FF5C821BC31D6D002F15CE /* airport-24@2x.png in Resources */, 88FF5C811BC31D6D002F15CE /* airfield-24@2x.png in Resources */, + 2B66121325D1E6D8009D228F /* ATS_Route.shx in Resources */, 88FF5ED01BC31D95002F15CE /* TJK.geojson in Resources */, 88FF5ED51BC31D95002F15CE /* TTO.geojson in Resources */, 88FF5E061BC31D95002F15CE /* BDI.geojson in Resources */, @@ -2100,6 +2179,7 @@ 2B033BFD24F6DA4600B55248 /* Roboto-Condensed-Light.ttf in Resources */, 88FF5E7E1BC31D95002F15CE /* MAR.geojson in Resources */, 2B7E288C200EC109007E2965 /* SE_Basic.json in Resources */, + 2B66121525D1E6D8009D228F /* Airspace_Boundary.shx in Resources */, 88FF5EDD1BC31D95002F15CE /* UMI.geojson in Resources */, 88FF5E771BC31D95002F15CE /* LKA.geojson in Resources */, 88FF5E7B1BC31D95002F15CE /* LVA.geojson in Resources */, @@ -2122,6 +2202,7 @@ 88FF5C791BC31D6D002F15CE /* lowres_wtb_2x0.pvrtc in Resources */, 88FF5E401BC31D95002F15CE /* FRA.geojson in Resources */, 88FF5CA31BC31D6D002F15CE /* farm-24@2x.png in Resources */, + 2BD914E8256F1F8A00E724A8 /* Assets.xcassets in Resources */, 88FF5E8B1BC31D95002F15CE /* MNP.geojson in Resources */, 88FF5E731BC31D95002F15CE /* LBR.geojson in Resources */, 2B033BFE24F6DA4600B55248 /* Roboto-Condensed-LightItalic.ttf in Resources */, @@ -2129,6 +2210,7 @@ 88FF5EEE1BC31D95002F15CE /* tl_2013_06075_roads.dbf in Resources */, 88FF5E3A1BC31D95002F15CE /* ESP.geojson in Resources */, 88FF5BEA1BC31D53002F15CE /* Star.png in Resources */, + 2B66121625D1E6D8009D228F /* ATS_Route.xml in Resources */, 88FF5CDC1BC31D6D002F15CE /* swimming-24@2x.png in Resources */, 88FF5DFD1BC31D95002F15CE /* ARG.geojson in Resources */, 88FF5ECD1BC31D95002F15CE /* TCD.geojson in Resources */, @@ -2149,6 +2231,7 @@ 88FF5EE61BC31D95002F15CE /* VNM.geojson in Resources */, 2B033C0D24F6F75300B55248 /* OpenSans-ExtraBoldItalic.ttf in Resources */, 88FF5E5B1BC31D95002F15CE /* IDN.geojson in Resources */, + 2B66120D25D1E6D8009D228F /* Airspace_Boundary.prj in Resources */, 88FF5E121BC31D95002F15CE /* BLZ.geojson in Resources */, 88FF5E9E1BC31D95002F15CE /* NRU.geojson in Resources */, 88FF5C9C1BC31D6D002F15CE /* cross-24@2x.png in Resources */, @@ -2161,6 +2244,7 @@ 88FF5CE01BC31D6D002F15CE /* toilets-24@2x.png in Resources */, 88FF5C951BC31D6D002F15CE /* circle-24@2x.png in Resources */, 88FF5C9F1BC31D6D002F15CE /* disability-24@2x.png in Resources */, + 316B5CFC260A5573000B4F15 /* maptiler_test_circles.json in Resources */, 88FF5EA11BC31D95002F15CE /* PAK.geojson in Resources */, 88FF5CB11BC31D6D002F15CE /* library-24@2x.png in Resources */, 88FF5E281BC31D95002F15CE /* CPV.geojson in Resources */, @@ -2197,8 +2281,10 @@ 88FF5CE21BC31D6D002F15CE /* town-hall-24@2x.png in Resources */, 2B4BA6D81C83A22000DC1562 /* track.geojson in Resources */, 88FF5CF21BC31D6D002F15CE /* starcatalog_orig.txt in Resources */, + 2B66121225D1E6D8009D228F /* ATS_Route.shp in Resources */, 2B033BFB24F6DA4600B55248 /* Roboto-Condensed-BoldItalic.ttf in Resources */, 2B73D6AE207C0D5F00AF5095 /* NotoSans-Bold.ttf in Resources */, + 2BD914BD256F1C8D00E724A8 /* icon_167.png in Resources */, E53CF5E81E1EAF73000FB5C1 /* school.png in Resources */, 88FF5ECA1BC31D95002F15CE /* SYC.geojson in Resources */, 2B4BA6D01C83A22000DC1562 /* mowing-lawn.geojson in Resources */, @@ -2286,7 +2372,6 @@ 88FF5CC71BC31D6D002F15CE /* post-24@2x.png in Resources */, 88FF5E931BC31D95002F15CE /* MYT.geojson in Resources */, 88FF5EE41BC31D95002F15CE /* VGB.geojson in Resources */, - 88E4B8CC1B83B6AB0050D21B /* Images.xcassets in Resources */, 88FF5EC01BC31D95002F15CE /* SPM.geojson in Resources */, 88FF5E241BC31D95002F15CE /* COG.geojson in Resources */, 2B1C26531C91037100C71B0A /* France.mbtiles in Resources */, @@ -2316,6 +2401,7 @@ 88FF5EB91BC31D95002F15CE /* SHN.geojson in Resources */, 88FF5E971BC31D95002F15CE /* NFK.geojson in Resources */, 88FF5C9E1BC31D6D002F15CE /* danger-24@2x.png in Resources */, + 2B66121025D1E6D8009D228F /* Airspace_Boundary.dbf in Resources */, 2B8849FB1E37F9B30027C397 /* stadium-utm-quad-data.sqlite in Resources */, 2B29944F243BA31900677DE4 /* cube.obj in Resources */, 88FF5C971BC31D6D002F15CE /* city-24@2x.png in Resources */, @@ -2338,6 +2424,7 @@ 88FF5CCC1BC31D6D002F15CE /* religious-christian-24@2x.png in Resources */, 88FF5E4F1BC31D95002F15CE /* GRD.geojson in Resources */, 88FF5E101BC31D95002F15CE /* BLM.geojson in Resources */, + 2B66120E25D1E6D8009D228F /* Airspace_Boundary.cpg in Resources */, 88FF5C911BC31D6D002F15CE /* campsite-24@2x.png in Resources */, 88FF5EA51BC31D95002F15CE /* PHL.geojson in Resources */, 88FF5E7C1BC31D95002F15CE /* MAC.geojson in Resources */, @@ -2354,6 +2441,7 @@ 88FF5E0E1BC31D95002F15CE /* BHS.geojson in Resources */, 88FF5CBA1BC31D6D002F15CE /* museum-24@2x.png in Resources */, 88FF5CDB1BC31D6D002F15CE /* suitcase-24@2x.png in Resources */, + 2B66121425D1E6D8009D228F /* Airspace_Boundary.shp in Resources */, 2B033C0F24F6F75300B55248 /* OpenSans-Light.ttf in Resources */, 88FF5DF91BC31D95002F15CE /* ALA.geojson in Resources */, 2B033C1324F6F75300B55248 /* OpenSans-SemiBoldItalic.ttf in Resources */, @@ -2376,6 +2464,7 @@ 88FF5E8A1BC31D95002F15CE /* MNG.geojson in Resources */, 88FF5EE01BC31D95002F15CE /* UZB.geojson in Resources */, 2B4BA6D61C83A22000DC1562 /* square.geojson in Resources */, + 2B66121725D1E6D8009D228F /* ATS_Route.prj in Resources */, 88FF5E581BC31D95002F15CE /* HRV.geojson in Resources */, 88FF5C8D1BC31D6D002F15CE /* building-24@2x.png in Resources */, 88FF5C781BC31D6D002F15CE /* lowres_wtb_1x1.pvrtc in Resources */, @@ -2400,10 +2489,12 @@ 88FF5CD01BC31D6D002F15CE /* roadblock-24@2x.png in Resources */, 88FF5CCF1BC31D6D002F15CE /* restaurant-24@2x.png in Resources */, 88FF5E671BC31D95002F15CE /* JOR.geojson in Resources */, + 316B5CF3260526AD000B4F15 /* maptiler_expr_test.json in Resources */, 88FF5CBB1BC31D6D002F15CE /* music-24@2x.png in Resources */, 88FF5CAD1BC31D6D002F15CE /* hospital-24@2x.png in Resources */, 88FF5C921BC31D6D002F15CE /* car-24@2x.png in Resources */, 88FF5E1F1BC31D95002F15CE /* CHL.geojson in Resources */, + 2B66121825D1E6D8009D228F /* Airspace_Boundary.xml in Resources */, 88FF5E741BC31D95002F15CE /* LBY.geojson in Resources */, 88FF5CB21BC31D6D002F15CE /* lighthouse-24@2x.png in Resources */, E5C82FCC1E221C890074DF23 /* cemetery.png in Resources */, @@ -2417,6 +2508,7 @@ 88FF5ED21BC31D95002F15CE /* TKM.geojson in Resources */, 88FF5E321BC31D95002F15CE /* DMA.geojson in Resources */, 88FF5E951BC31D95002F15CE /* NCL.geojson in Resources */, + 2B66121125D1E6D8009D228F /* ATS_Route.dbf in Resources */, 88FF5C891BC31D6D002F15CE /* baseball-24@2x.png in Resources */, 88FF5EA61BC31D95002F15CE /* PLW.geojson in Resources */, E53CF5E41E1EAF73000FB5C1 /* firehouse.png in Resources */, @@ -2453,6 +2545,7 @@ 2B033BEC24F6D7BA00B55248 /* Roboto-Thin.ttf in Resources */, 88FF5EDB1BC31D95002F15CE /* UGA.geojson in Resources */, 88FF5E2D1BC31D95002F15CE /* CYM.geojson in Resources */, + 2BD914BE256F1C8D00E724A8 /* icon_152.png in Resources */, 88FF5E3B1BC31D95002F15CE /* EST.geojson in Resources */, 88FF5CAE1BC31D6D002F15CE /* industrial-24@2x.png in Resources */, 88FF5CF11BC31D6D002F15CE /* vp.mtl in Resources */, @@ -2481,6 +2574,7 @@ 88FF5CDF1BC31D6D002F15CE /* theatre-24@2x.png in Resources */, 88FF5CC21BC31D6D002F15CE /* pitch-24@2x.png in Resources */, 2B4BA6D91C83A22000DC1562 /* uturn.geojson in Resources */, + 2B66120F25D1E6D8009D228F /* ATS_Route.cpg in Resources */, 88FF5E5F1BC31D95002F15CE /* IRL.geojson in Resources */, 2B033BE824F6D7BA00B55248 /* Roboto-LightItalic.ttf in Resources */, 88FF5ECC1BC31D95002F15CE /* TCA.geojson in Resources */, @@ -2526,6 +2620,7 @@ D8200CA41BE9624300B22CF5 /* LoftedPolysTestCase.m in Sources */, 2B29944D243BA16000677DE4 /* SimpleStyleTestCase.swift in Sources */, 2B73D6B4207C106C00AF5095 /* GlobeSamplerTestCase.swift in Sources */, + 313C522325D369270036C22E /* GlobeRotationTestCase.swift in Sources */, 2BC41C8521F00AA9002926B7 /* BNGTestCase.swift in Sources */, 88BF11491BCD3DA40002205E /* MaplyTestCase.m in Sources */, D8341A731BE2DFE100411A46 /* ScreenLabelsTestCase.swift in Sources */, @@ -2533,6 +2628,7 @@ D8E12ECD1BDE9FD500BB7BC7 /* AnimatedBasemapTestCase.swift in Sources */, 2BDEB3141C9391EB003259B3 /* GreatCircleTestCase.m in Sources */, 2BD358F31DD3EA0400081EA8 /* AnimatedMarkersTestCase.swift in Sources */, + 316B5CFE260A58A8000B4F15 /* MapTilerCircleTestCase.swift in Sources */, 2B4B30AE2395E0DE00854073 /* MapTilerTestCase.swift in Sources */, 2B7787CA21CDA47F006ABF5D /* CartoDBLightTestCase.swift in Sources */, 2B4B30AD2395E0DE00854073 /* LayerStartupShutdownTestCase.swift in Sources */, @@ -2545,6 +2641,7 @@ D8200CA11BE9563F00B22CF5 /* StickersTestCase.swift in Sources */, 2BC3D6F3220B901000CE91D0 /* AppDelegate.swift in Sources */, 2BC3D6F2220B900D00CE91D0 /* TestCell.swift in Sources */, + 31833793259E5291005FEF70 /* ChangeVectorsTestCase.mm in Sources */, 2BB8A40021ED46E10025DA98 /* StamenWatercolorRemote.swift in Sources */, 2B392B711C5AC59D001EE40B /* RunwayBuilderTestCase.m in Sources */, 2B68A43C225D06A8009CC720 /* ImageReloadTestCase.swift in Sources */, @@ -2556,6 +2653,7 @@ 2BC2DCCD1D1F4CE20088D350 /* ShapefileTestCase.m in Sources */, D8F2FE271BE7BD630058A310 /* LabelsTestCase.m in Sources */, E5679F4E1CB7669400369A15 /* FullAnimationTest.m in Sources */, + 313C521625D329070036C22E /* MovingScreenMarkersTestCase.swift in Sources */, E5D2D65F1DE65CE400E02305 /* LocationTrackingSimTestCase.swift in Sources */, E5CC4E201DF7CBEA00C0D4DE /* LocationTrackingRealTestCase.swift in Sources */, 2BBCE41C2220A4170013E158 /* PagingLayerTestCase.m in Sources */, @@ -2573,7 +2671,9 @@ 8820852B1DC81051008F8E76 /* Issue721TestCase.swift in Sources */, 2B753DDB21EEB41700DE98BA /* NASAGIBSTestCase.swift in Sources */, 2BC0FB781DCAA18A004125F1 /* TextureVectorTestCase.m in Sources */, + 2B6611E625D1C35D009D228F /* AirwayTestCase.swift in Sources */, 2B249F3F23F4A82600CFA3D0 /* GeographyClass.swift in Sources */, + 3183380F25A67CD8005FEF70 /* RepresentationsTestCase.mm in Sources */, D8F2FE291BE7C2000058A310 /* MarkersTestCase.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; @@ -2657,7 +2757,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 8.4; + IPHONEOS_DEPLOYMENT_TARGET = 12.0; MTL_ENABLE_DEBUG_INFO = YES; ONLY_ACTIVE_ARCH = YES; SDKROOT = iphoneos; @@ -2709,7 +2809,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 8.4; + IPHONEOS_DEPLOYMENT_TARGET = 12.0; MTL_ENABLE_DEBUG_INFO = NO; SDKROOT = iphoneos; SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; @@ -2725,6 +2825,7 @@ CODE_SIGN_IDENTITY = "iPhone Developer"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 3; DEVELOPMENT_TEAM = BFXSGS6V8N; HEADER_SEARCH_PATHS = ( "$(inherited)", @@ -2739,7 +2840,8 @@ INFOPLIST_FILE = AutoTester/Info.plist; IPHONEOS_DEPLOYMENT_TARGET = 12.0; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; - PRODUCT_BUNDLE_IDENTIFIER = com.mousebirdconsulting.AutoTester; + MARKETING_VERSION = 3.1; + PRODUCT_BUNDLE_IDENTIFIER = com.mousebirdconsulting.AutoTesterApp; PRODUCT_NAME = AutoTester; PROVISIONING_PROFILE = ""; PROVISIONING_PROFILE_SPECIFIER = ""; @@ -2757,6 +2859,7 @@ CODE_SIGN_IDENTITY = "iPhone Developer"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 3; DEVELOPMENT_TEAM = BFXSGS6V8N; HEADER_SEARCH_PATHS = ( "$(inherited)", @@ -2771,7 +2874,8 @@ INFOPLIST_FILE = AutoTester/Info.plist; IPHONEOS_DEPLOYMENT_TARGET = 12.0; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; - PRODUCT_BUNDLE_IDENTIFIER = com.mousebirdconsulting.AutoTester; + MARKETING_VERSION = 3.1; + PRODUCT_BUNDLE_IDENTIFIER = com.mousebirdconsulting.AutoTesterApp; PRODUCT_NAME = AutoTester; PROVISIONING_PROFILE_SPECIFIER = ""; SWIFT_OBJC_BRIDGING_HEADER = "$(SRCROOT)/AutoTester/MaplyTesterBridge.h"; diff --git a/ios/apps/AutoTester/AutoTester.xcodeproj/xcshareddata/xcschemes/AutoTester.xcscheme b/ios/apps/AutoTester/AutoTester.xcodeproj/xcshareddata/xcschemes/AutoTester.xcscheme index 7a9db6b765..296d787f78 100644 --- a/ios/apps/AutoTester/AutoTester.xcodeproj/xcshareddata/xcschemes/AutoTester.xcscheme +++ b/ios/apps/AutoTester/AutoTester.xcodeproj/xcshareddata/xcschemes/AutoTester.xcscheme @@ -1,6 +1,6 @@ en
CFBundleExecutable $(EXECUTABLE_NAME) + CFBundleIconFile + CFBundleIdentifier $(PRODUCT_BUNDLE_IDENTIFIER) CFBundleInfoDictionaryVersion @@ -15,7 +17,7 @@ CFBundlePackageType APPL CFBundleShortVersionString - 1.1 + $(MARKETING_VERSION) CFBundleSignature ???? CFBundleURLTypes @@ -30,7 +32,7 @@ CFBundleVersion - 2 + $(CURRENT_PROJECT_VERSION) Dropbox Key im7y3rwt7y23v3j LSApplicationQueriesSchemes @@ -45,7 +47,7 @@ NSLocationWhenInUseUsageDescription - Allow AutoTester to access your device's location when in use. + Allow AutoTester to access your device's location when in use. UIAppFonts NotoSans-Bold.ttf diff --git a/ios/apps/AutoTester/AutoTester/MaplyTesterBridge.h b/ios/apps/AutoTester/AutoTester/MaplyTesterBridge.h index 1daaac6792..92365e5a5d 100644 --- a/ios/apps/AutoTester/AutoTester/MaplyTesterBridge.h +++ b/ios/apps/AutoTester/AutoTester/MaplyTesterBridge.h @@ -3,7 +3,7 @@ // WhirlyGlobeSwiftTester // // Created by jmnavarro on 16/09/15. -// Copyright (c) 2015-2019 mousebird consulting. All rights reserved. +// Copyright (c) 2015-2020 mousebird consulting. All rights reserved. // #ifndef AutoTester_MaplyTesterBridge_h @@ -35,5 +35,7 @@ #import "ActiveObjectTestCase.h" #import "GeoJSONSource.h" #import "GlyphProblemTestCase.h" +#import "ChangeVectorsTestCase.h" +#import "RepresentationsTestCase.h" #endif diff --git a/ios/apps/AutoTester/AutoTester/StartupViewController.swift b/ios/apps/AutoTester/AutoTester/StartupViewController.swift index 54861a419a..a94b228b39 100644 --- a/ios/apps/AutoTester/AutoTester/StartupViewController.swift +++ b/ios/apps/AutoTester/AutoTester/StartupViewController.swift @@ -3,7 +3,7 @@ // WhirlyGlobeSwiftTester // // Created by jmnavarro on 14/09/15. -// Copyright (c) 2015-2019 mousebird consulting. All rights reserved. +// Copyright (c) 2015-2021 mousebird consulting. All rights reserved. // import UIKit @@ -27,8 +27,10 @@ class StartupViewController: UITableViewController, UIPopoverPresentationControl AnimatedMarkersTestCase(), ClusteredMarkersTestCase(), LabelAnimationTestCase(), + MovingScreenMarkersTestCase(), VectorsTestCase(), + ChangeVectorsTestCase(), GreatCircleTestCase(), VectorStyleTestCase(), VectorHoleTestCase(), @@ -38,15 +40,18 @@ class StartupViewController: UITableViewController, UIPopoverPresentationControl SimpleStyleTestCase(), GeoJSONStyleTestCase(), LoftedPolysTestCase(), + RepresentationsTestCase(), + AirwayTestCase(), // Note: 3D labels are currently broken -// LabelsTestCase(), + LabelsTestCase(), StickersTestCase(), PagingLayerTestCase(), VectorMBTilesTestCase(), CartoDBTestCase(), MapTilerTestCase(), + MapTilerCircleTestCase(), MapboxTestCase(), ShapesTestCase(), @@ -59,6 +64,7 @@ class StartupViewController: UITableViewController, UIPopoverPresentationControl FindHeightTestCase(), FullAnimationTest(), AnimationDelegateTestCase(), + GlobeRotationTestCase(), LocationTrackingSimTestCase(), LocationTrackingRealTestCase(), diff --git a/ios/apps/AutoTester/AutoTester/testCases/AirwayTestCase.swift b/ios/apps/AutoTester/AutoTester/testCases/AirwayTestCase.swift new file mode 100644 index 0000000000..25fa603322 --- /dev/null +++ b/ios/apps/AutoTester/AutoTester/testCases/AirwayTestCase.swift @@ -0,0 +1,263 @@ +// +// AirwayTestCase.swift +// AutoTester +// +// Created by Steve Gifford on 2/8/21. +// Copyright © 2021 mousebird consulting. All rights reserved. +// + +import Foundation + +// A very dumb graph builder +class GraphBuilder { + // Sort endpoints (gross) + var points = [String: (loc: CLLocationCoordinate2D, count: Int, uuid: String)]() + + // Convert point to a simple string for caching (barf) + func stringPoint(_ loc: CLLocationCoordinate2D) -> String { + return String(format: "%.4f_%.4f", loc.longitude, loc.latitude) + } + + // Add an entry for the given point + func addPoint(_ loc: CLLocation) { + let coord = loc.coordinate + let ptAsStr = stringPoint(coord) + + if let val = points[ptAsStr] { + points[ptAsStr] = (coord, val.count+1, val.uuid) + } else { + points[ptAsStr] = (coord, 1, UUID().uuidString) + } + } + + // Look for the given point + func getPoint(_ loc: CLLocation) -> (loc: CLLocationCoordinate2D, count: Int, uuid: String)? { + let ptAsStr = stringPoint(loc.coordinate) + return points[ptAsStr] + } +} + +class AirwayTestCase: MaplyTestCase { + + override init() { + super.init() + + self.name = "Airways & Airspaces" + self.implementations = [.globe, .map] + } + + let baseCase = StamenWatercolorRemote() + + let buildPointMarkers = true + let buildPointLabels = false + let buildAirways = false + let buildAirspaces = true + let buildLineLabels = true + + func setupAirways(_ viewC: MaplyBaseViewController) { + DispatchQueue.global(qos: .default).async { + guard let vecObj = MaplyVectorObject(fromShapeFile: "ATS_Route") else { + print("Failed to load ATS_Route shapefile") + return + } + + viewC.startMaskTarget(nil) + + var markerTextures: [MaplyTexture] = [] + markerTextures.append(viewC.addTexture(UIImage(named: "rocket-24@2x.png")!, desc: nil, mode: .current)!) // 0, not used + markerTextures.append(viewC.addTexture(UIImage(named: "rocket-24@2x.png")!, desc: nil, mode: .current)!) // 1 + markerTextures.append(viewC.addTexture(UIImage(named: "star-stroked-24@2x.png")!, desc: nil, mode: .current)!) // 2 + markerTextures.append(viewC.addTexture(UIImage(named: "shop-24@2x.png")!, desc: nil, mode: .current)!) // 3 + markerTextures.append(viewC.addTexture(UIImage(named: "london-underground-24@2x.png")!, desc: nil, mode: .current)!) // 4 + markerTextures.append(viewC.addTexture(UIImage(named: "post-24@2x.png")!, desc: nil, mode: .current)!) // 5 + markerTextures.append(viewC.addTexture(UIImage(named: "town-hall-24@2x.png")!, desc: nil, mode: .current)!) // 6 + + let graphBuilder = GraphBuilder() + + // Work through the airway identifying start and end points + var segments = [MaplyVectorObject]() + for airwayObj in vecObj.splitVectors() { + if let locArr = airwayObj.asCLLocationArrays()?.first as? [CLLocation], + let locStart = locArr.first, + let locEnd = locArr.last { + graphBuilder.addPoint(locStart) + graphBuilder.addPoint(locEnd) + segments.append(airwayObj) + } + } + + // One marker for each node + if (self.buildPointMarkers) { + var markers: [MaplyScreenMarker] = [] + for pt in graphBuilder.points { + let marker = MaplyScreenMarker() + marker.loc = MaplyCoordinateMakeWithDegrees(Float(pt.value.loc.longitude), Float(pt.value.loc.latitude)) + marker.image = markerTextures[min(pt.value.count,markerTextures.count-1)] + // marker.layoutImportance = MAXFLOAT + marker.size = CGSize(width: 24.0, height: 24.0) + marker.maskID = pt.value.uuid + markers.append(marker) + } + viewC.addScreenMarkers(markers, desc: nil) + } + + // One label for each node + if (self.buildPointLabels) { + var labels: [MaplyScreenLabel] = [] + for pt in graphBuilder.points { + let label = MaplyScreenLabel() + label.loc = MaplyCoordinateMakeWithDegrees(Float(pt.value.loc.longitude), Float(pt.value.loc.latitude)) + label.text = "foo" + label.maskID = pt.value.uuid + label.offset = CGPoint(x: 0.0, y: -18.0) + label.layoutPlacement = 0 + labels.append(label) + } + viewC.addScreenLabels(labels, desc: [kMaplyFont: UIFont.systemFont(ofSize: 36.0)]) + } + + // For each segment, we want to add the two endpoints as masks + var labels: [MaplyScreenLabel] = [] + var lines: [MaplyVectorObject] = [] + for seg in segments { + var include = true +// if let highVal = seg.attributes?["US_HIGH"] as? Int { +// if highVal > 0 { +// include = true +// } +// } + guard let text = seg.attributes?["IDENT"] as? String else { + continue + } + // Used for testing +// if seg.attributes?["OBJECTID"] as? Int != 14807 { +// include = false +// } + + if include { + if let locArr = seg.asCLLocationArrays()?.first as? [CLLocation], + let locStart = locArr.first, + let locEnd = locArr.last { + if let markStart = graphBuilder.getPoint(locStart), + let markEnd = graphBuilder.getPoint(locEnd) { + seg.attributes?["maskID0"] = markStart.uuid + seg.attributes?["maskID1"] = markEnd.uuid + } + } + + if self.buildLineLabels { + // Put a label along the line + let label = MaplyScreenLabel() + label.layoutVec = seg + label.text = text + label.loc = seg.center() + label.layoutImportance = 1.0 + labels.append(label) + } + + lines.append(seg) + } + } + + viewC.addWideVectors(lines, desc: [kMaplyVecWidth: 2.0, + kMaplyWideVecImpl: kMaplyWideVecImplPerf, + kMaplyColor: UIColor.blue], + mode: .any) + if !labels.isEmpty { + viewC.addScreenLabels(labels, desc: [kMaplyFont: UIFont.boldSystemFont(ofSize: 24.0), + kMaplyTextColor: UIColor.purple, + kMaplyTextLayoutOffset: 26.0, + kMaplyTextLayoutSpacing: 24.0, // 100 pixels between + kMaplyTextLayoutRepeat: 1, + kMaplyJustify: kMaplyTextJustifyCenter], + mode: .any) + } + } + } + + func setupAirspaces(_ viewC: MaplyBaseViewController) { + guard let vecObj = MaplyVectorObject(fromShapeFile: "Airspace_Boundary") else { + print("Failed to load Airspace_Boundary shapefile") + return + } + + // Put the airspace vectors together + var airspaceVecs = [MaplyVectorObject]() + var labels = [MaplyScreenLabel]() + for vec in vecObj.splitVectors() { + var include = false + if let highVal = vec.attributes?["US_HIGH"] as? Int { + if highVal > 0 { + if vec.vectorType() == .arealType { + include = true + } + } + } + guard let name = vec.attributes?["NAME"] as? String else { + continue + } +// include = name == "PACIFIC HIGH" + + if include { +// name = "!!!!!!!!!!" + // Put a label in the middle + let label = MaplyScreenLabel() + label.layoutVec = vec + label.loc = vec.centroid() + label.text = name + label.layoutImportance = 1.0 + + if label.text != nil { + labels.append(label) + airspaceVecs.append(vec) + } + } + } + + if (!airspaceVecs.isEmpty) { + viewC.addWideVectors(airspaceVecs, desc: [kMaplyVecWidth: 6.0, + kMaplyWideVecImpl: kMaplyWideVecImplPerf, + kMaplyColor: UIColor.blue], mode: .any) + } + if (!labels.isEmpty) { + viewC.addScreenLabels(labels, desc: [kMaplyFont: UIFont.boldSystemFont(ofSize: 24.0), + kMaplyTextColor: UIColor.purple, + kMaplyTextLayoutOffset: -26.0, + kMaplyTextLayoutSpacing: 100.0, // 100 pixels between +// kMaplyTextLayoutSpacing: 0.0, // 100 pixels between +// kMaplyTextLayoutRepeat: 4, // As many as fit + kMaplyTextLayoutDebug: true + ], + mode: .any) + } + } + + override func setUpWithGlobe(_ globeVC: WhirlyGlobeViewController) { + baseCase.setUpWithGlobe(globeVC) + +// globeVC.keepNorthUp = false + globeVC.animate(toPosition: MaplyCoordinateMakeWithDegrees(-110.0, 40.5023056), time: 1.0) + + if buildAirways { + setupAirways(globeVC) + } + + if buildAirspaces { + setupAirspaces(globeVC) + } + } + + override func setUpWithMap(_ mapVC: MaplyViewController) { + baseCase.setUpWithMap(mapVC) + + mapVC.animate(toPosition: MaplyCoordinateMakeWithDegrees(-110.0, 40.5023056), time: 1.0) + + if buildAirways { + setupAirways(mapVC) + } + + if buildAirspaces { + setupAirspaces(mapVC) + } + } +} diff --git a/ios/apps/AutoTester/AutoTester/testCases/AnimatedBasemapTestCase.swift b/ios/apps/AutoTester/AutoTester/testCases/AnimatedBasemapTestCase.swift index 83ee0f846a..8ec1470312 100644 --- a/ios/apps/AutoTester/AutoTester/testCases/AnimatedBasemapTestCase.swift +++ b/ios/apps/AutoTester/AutoTester/testCases/AnimatedBasemapTestCase.swift @@ -50,7 +50,6 @@ class AnimatedBasemapTestCase: MaplyTestCase { sampleParams.coordSys = MaplySphericalMercator(webStandard: ()) sampleParams.coverPoles = false sampleParams.edgeMatching = false - sampleParams.minZoom = 0 sampleParams.maxZoom = 6 sampleParams.singleLevel = true sampleParams.minImportance = 1024.0*1024.0 diff --git a/ios/apps/AutoTester/AutoTester/testCases/BNGCustomMapTestCase.swift b/ios/apps/AutoTester/AutoTester/testCases/BNGCustomMapTestCase.swift index c333659666..7264b6e6fc 100644 --- a/ios/apps/AutoTester/AutoTester/testCases/BNGCustomMapTestCase.swift +++ b/ios/apps/AutoTester/AutoTester/testCases/BNGCustomMapTestCase.swift @@ -3,7 +3,7 @@ // AutoTester // // Created by jmnavarro on 10/12/15. -// Copyright © 2015-2017 mousebird consulting. +// Copyright © 2015-2021 mousebird consulting. // import UIKit @@ -13,7 +13,7 @@ class BNGCustomMapTestCase: MaplyTestCase { override init() { super.init() - self.name = "British National Grid (custom map)" + self.name = "British National Grid (custom map) (broken)" self.implementations = [.map] } @@ -64,7 +64,6 @@ class BNGCustomMapTestCase: MaplyTestCase { sampleParams.coordSys = bngCoordSys sampleParams.coverPoles = false sampleParams.edgeMatching = false - sampleParams.minZoom = 0 sampleParams.maxZoom = 22 sampleParams.singleLevel = true diff --git a/ios/apps/AutoTester/AutoTester/testCases/BNGTestCase.swift b/ios/apps/AutoTester/AutoTester/testCases/BNGTestCase.swift index 5ec5f7c734..d98f7ac2b2 100644 --- a/ios/apps/AutoTester/AutoTester/testCases/BNGTestCase.swift +++ b/ios/apps/AutoTester/AutoTester/testCases/BNGTestCase.swift @@ -12,7 +12,7 @@ class BNGTestCase: MaplyTestCase { override init(){ super.init() - self.name = "British National Grid" + self.name = "British National Grid (broken)" self.implementations = [.globe, .map] } diff --git a/ios/apps/AutoTester/AutoTester/testCases/CartoDBLightTestCase.swift b/ios/apps/AutoTester/AutoTester/testCases/CartoDBLightTestCase.swift index 28b1561fd0..4219df595b 100644 --- a/ios/apps/AutoTester/AutoTester/testCases/CartoDBLightTestCase.swift +++ b/ios/apps/AutoTester/AutoTester/testCases/CartoDBLightTestCase.swift @@ -34,14 +34,15 @@ class CartoDBLightTestCase: MaplyTestCase { sampleParams.coordSys = MaplySphericalMercator(webStandard: ()) sampleParams.coverPoles = true sampleParams.edgeMatching = true - sampleParams.minZoom = tileInfo.minZoom() sampleParams.maxZoom = tileInfo.maxZoom() sampleParams.singleLevel = true guard let imageLoader = MaplyQuadImageLoader(params: sampleParams, tileInfo: tileInfo, viewC: baseVC) else { return nil } +#if !targetEnvironment(simulator) imageLoader.imageFormat = .imageUShort565; +#endif // imageLoader.debugMode = true return imageLoader diff --git a/ios/apps/AutoTester/AutoTester/testCases/ChangeVectorsTestCase.h b/ios/apps/AutoTester/AutoTester/testCases/ChangeVectorsTestCase.h new file mode 100644 index 0000000000..1570bef829 --- /dev/null +++ b/ios/apps/AutoTester/AutoTester/testCases/ChangeVectorsTestCase.h @@ -0,0 +1,16 @@ +// +// ChangeVectorsTestCase.h +// AutoTester +// +// Created by Tim Sylvester on 31 Dec 2020. +// Copyright © 2020 mousebird consulting. +// + +#import "MaplyTestCase.h" + +@class VectorsTestCase; + +@interface ChangeVectorsTestCase : MaplyTestCase + +@property (nonatomic) VectorsTestCase *baseCase; +@end diff --git a/ios/apps/AutoTester/AutoTester/testCases/ChangeVectorsTestCase.mm b/ios/apps/AutoTester/AutoTester/testCases/ChangeVectorsTestCase.mm new file mode 100644 index 0000000000..74e170445c --- /dev/null +++ b/ios/apps/AutoTester/AutoTester/testCases/ChangeVectorsTestCase.mm @@ -0,0 +1,168 @@ +// +// VectorsTestCase.m +// AutoTester +// +// Created by Tim Sylvester on 31 Dec. 2020. +// Copyright © 2020 mousebird consulting. +// + +#import "VectorsTestCase.h" +#import "ChangeVectorsTestCase.h" +#import "MaplyBaseViewController.h" +#import "MaplyViewController.h" +#import "WhirlyGlobeViewController.h" +#import "AutoTester-Swift.h" + +#include + +@interface ChangeVectorsTestCase() +@end + +@implementation ChangeVectorsTestCase { + NSTimer *_animationTimer; + MaplyComponentObject *_vecObj; + MaplyComponentObject *_wideVecObj; + MaplyComponentObject *_wideTexVecObj; + MaplyTexture *_dashedLineTex; +} + +- (instancetype)init +{ + if (self = [super init]) { + self.name = @"Change Vectors"; + self.implementations = MaplyTestCaseImplementationMap | MaplyTestCaseImplementationGlobe; + } + + return self; +} + +- (void)subdivide:(MaplyVectorObject *)obj withVC:(MaplyBaseViewController *)vc epsilon:(float)epsilon { + const bool isGlobe = [vc isKindOfClass:[WhirlyGlobeViewController class]]; + if (isGlobe) [obj subdivideToGlobeGreatCircle:epsilon]; + else [obj subdivideToFlatGreatCircle:epsilon]; +} + +- (void)setupWithBaseVC:(MaplyBaseViewController *)vc { + _animationTimer = [NSTimer scheduledTimerWithTimeInterval:2.0 + target:self + selector:@selector(animationCallback) + userInfo:nil + repeats:YES]; +} + +- (void)setUpWithGlobe:(WhirlyGlobeViewController *)vc +{ + self.baseCase = [[VectorsTestCase alloc]init]; + [self.baseCase setUpWithGlobe:vc]; + [self setupWithBaseVC:vc]; + [vc animateToPosition:MaplyCoordinateMakeWithDegrees(50, -65) height:0.5 heading:0 time:1.0]; +} + +- (void)setUpWithMap:(MaplyViewController *)vc +{ + self.baseCase = [[VectorsTestCase alloc]init]; + [self.baseCase setUpWithMap:vc]; + [self setupWithBaseVC:vc]; + [vc animateToPosition:MaplyCoordinateMakeWithDegrees(50, -65) height:0.5 heading:0 time:1.0]; +} + ++ (nonnull UIColor *)randomColor { + return [UIColor colorWithRed:(arc4random()%255)/255.0f + green:(arc4random()%255)/255.0f + blue:(arc4random()%255)/255.0f + alpha:1.0f]; +} + +- (void) animationCallback +{ + if (!_dashedLineTex) + { + auto lineTexBuilder = [[MaplyLinearTextureBuilder alloc] init]; + [lineTexBuilder setPattern:@[@(2),@(2)]]; + _dashedLineTex = [self.baseViewController addTexture:[lineTexBuilder makeImage] + desc:@{kMaplyTexMinFilter: kMaplyMinFilterNearest, + kMaplyTexMagFilter: kMaplyMinFilterNearest, + kMaplyTexWrapX: @true, + kMaplyTexWrapY: @true, + kMaplyTexFormat: @(MaplyImageIntRGBA)} + mode:MaplyThreadCurrent]; + } + + if (!_vecObj) + { + const MaplyCoordinate pts[] = { MaplyCoordinateMakeWithDegrees(50, -65), MaplyCoordinateMakeWithDegrees(150, -65) }; + const auto vec = [[MaplyVectorObject alloc] initWithLineString:pts numCoords:2 attributes:nil]; + [self subdivide:vec withVC:self.baseViewController epsilon:0.0001]; + _vecObj = [self.baseViewController addVectors:@[vec] desc:@{ + kMaplyEnable: @(YES), + kMaplyColor: [UIColor magentaColor] + }]; + } + if (!_wideVecObj) + { + const MaplyCoordinate pts[] = { MaplyCoordinateMakeWithDegrees(50, -66), MaplyCoordinateMakeWithDegrees(150, -66) }; + const auto vec = [[MaplyVectorObject alloc] initWithLineString:pts numCoords:2 attributes:nil]; + [self subdivide:vec withVC:self.baseViewController epsilon:0.0001]; + _wideVecObj = [self.baseViewController addWideVectors:@[vec] desc:@{ + kMaplyEnable: @(YES), + kMaplyColor: [UIColor redColor], + kMaplyVecWidth: @(5.0) + }]; + } + if (!_wideTexVecObj) + { + const MaplyCoordinate pts[] = { MaplyCoordinateMakeWithDegrees(50, -67), MaplyCoordinateMakeWithDegrees(150, -67) }; + const auto vec = [[MaplyVectorObject alloc] initWithLineString:pts numCoords:2 attributes:nil]; + [self subdivide:vec withVC:self.baseViewController epsilon:0.0001]; + _wideTexVecObj = [self.baseViewController addWideVectors:@[vec] desc:@{ + kMaplyEnable: @(YES), + kMaplyColor: [UIColor blackColor], + kMaplyVecWidth: @(5.0), + kMaplyVecTexture: _dashedLineTex + }]; + } + + if (_vecObj) + { + [self.baseViewController changeVector:_vecObj desc:@{ + kMaplyEnable: @((arc4random()%10) ? YES : NO), + kMaplyColor: [ChangeVectorsTestCase randomColor], + kMaplyDrawPriority: @(kMaplyVectorDrawPriorityDefault) + }]; + } + if (_wideVecObj) + { + [self.baseViewController changeVector:_wideVecObj desc:@{ + kMaplyEnable: @((arc4random()%10) ? YES : NO), + kMaplyColor: [ChangeVectorsTestCase randomColor], + kMaplyVecWidth: @((arc4random()%33) / 4.0f), + kMaplyDrawPriority: @(kMaplyVectorDrawPriorityDefault) + }]; + } + if (_wideTexVecObj) + { + [self.baseViewController changeVector:_wideTexVecObj desc:@{ + kMaplyEnable: @((arc4random()%10) ? YES : NO), + kMaplyColor: [ChangeVectorsTestCase randomColor], + kMaplyVecWidth: @((arc4random()%33) / 4.0f), + kMaplyDrawPriority: @(kMaplyVectorDrawPriorityDefault) + }]; + } +} + +- (void) stop +{ + if (_animationTimer) { + [_animationTimer invalidate]; + _animationTimer = nil; + } + + _vecObj = nil; + _wideVecObj = nil; + _wideTexVecObj = nil; + _dashedLineTex = nil; + + [self.baseCase stop]; +} + +@end diff --git a/ios/apps/AutoTester/AutoTester/testCases/FindHeightTestCase.m b/ios/apps/AutoTester/AutoTester/testCases/FindHeightTestCase.m index 91339d1f8d..80c084d93a 100644 --- a/ios/apps/AutoTester/AutoTester/testCases/FindHeightTestCase.m +++ b/ios/apps/AutoTester/AutoTester/testCases/FindHeightTestCase.m @@ -15,6 +15,7 @@ @implementation FindHeightTestCase { MaplyBaseViewController *_baseVC; CartoDBLightTestCase *baseView; + bool stopped; } - (instancetype)init @@ -22,6 +23,7 @@ - (instancetype)init if (self = [super init]) { self.name = @"Find Height"; self.implementations = MaplyTestCaseImplementationMap | MaplyTestCaseImplementationGlobe; + stopped = false; } return self; } @@ -44,46 +46,57 @@ - (void)addBoundingBox:(MaplyBoundingBox)bbox baseVC:(MaplyBaseViewController *) - (void)setUpWithGlobe:(WhirlyGlobeViewController *)globeVC { + stopped = false; baseView = [[CartoDBLightTestCase alloc] init]; [baseView setUpWithGlobe:globeVC]; [self setupWithBaseVC:(MaplyBaseViewController *)globeVC]; - [globeVC setPosition:MaplyCoordinateMakeWithDegrees(-98.58, 39.83) height:1.5]; + [globeVC animateToPosition:MaplyCoordinateMakeWithDegrees(-98.58, 39.83) height:1.5 heading:0 time:1.0]; - dispatch_after( dispatch_time(DISPATCH_TIME_NOW, 5.0 * NSEC_PER_SEC), dispatch_get_main_queue(), + dispatch_after( dispatch_time(DISPATCH_TIME_NOW, 1.0 * NSEC_PER_SEC), dispatch_get_main_queue(), ^{ - MaplyBoundingBox bbox; - bbox.ll = MaplyCoordinateMakeWithDegrees(7.05090689853, 47.7675500593); - bbox.ur = MaplyCoordinateMakeWithDegrees(8.06813647023, 49.0562323851); - MaplyCoordinate center = MaplyCoordinateMakeWithDegrees((7.05090689853+8.06813647023)/2, (47.7675500593+49.0562323851)/2); - double height = [globeVC findHeightToViewBounds:bbox pos:center]; - globeVC.height = height; - [globeVC animateToPosition:center time:1.0]; - NSLog(@"height = %f",height); - + if (self->stopped) { + return; + } + MaplyBoundingBox bbox; + bbox.ll = MaplyCoordinateMakeWithDegrees(7.05090689853, 47.7675500593); + bbox.ur = MaplyCoordinateMakeWithDegrees(8.06813647023, 49.0562323851); [self addBoundingBox:bbox baseVC:globeVC]; + + const MaplyCoordinate center = MaplyCoordinateMakeWithDegrees((7.05090689853+8.06813647023)/2, (47.7675500593+49.0562323851)/2); + const double height = [globeVC findHeightToViewBounds:bbox pos:center]; + [globeVC animateToPosition:center height:height*1.1 heading:0 time:3.0]; + NSLog(@"height = %f",height); }); } - (void)setUpWithMap:(MaplyViewController *)mapVC { + stopped = false; baseView = [[CartoDBLightTestCase alloc] init]; [baseView setUpWithMap:mapVC]; [self setupWithBaseVC:(MaplyBaseViewController *)mapVC]; - [mapVC animateToPosition:MaplyCoordinateMakeWithDegrees(-98.58, 39.83) time:0.0]; - - dispatch_after( dispatch_time(DISPATCH_TIME_NOW, 5.0 * NSEC_PER_SEC), dispatch_get_main_queue(), + [mapVC animateToPosition:MaplyCoordinateMakeWithDegrees(-98.58, 39.83) height:1.5 time:1.0]; + + dispatch_after( dispatch_time(DISPATCH_TIME_NOW, 1.0 * NSEC_PER_SEC), dispatch_get_main_queue(), ^{ + if (self->stopped) { + return; + } MaplyBoundingBox bbox; bbox.ll = MaplyCoordinateMakeWithDegrees(7.05090689853, 47.7675500593); bbox.ur = MaplyCoordinateMakeWithDegrees(8.06813647023, 49.0562323851); - MaplyCoordinate center = MaplyCoordinateMakeWithDegrees((7.05090689853+8.06813647023)/2, (47.7675500593+49.0562323851)/2); - double height = [mapVC findHeightToViewBounds:bbox pos:center marginX:20.0 marginY:100.0]; - mapVC.height = height; - [mapVC animateToPosition:center time:1.0]; - NSLog(@"height = %f",height); - [self addBoundingBox:bbox baseVC:mapVC]; + + const MaplyCoordinate center = MaplyCoordinateMakeWithDegrees((7.05090689853+8.06813647023)/2, (47.7675500593+49.0562323851)/2); + const double height = [mapVC findHeightToViewBounds:bbox pos:center marginX:20.0 marginY:100.0]; + [mapVC animateToPosition:center height:height heading:mapVC.heading time:3.0]; + NSLog(@"height = %f",height); }); } +- (void)stop { + stopped = true; + [super stop]; +} + @end diff --git a/ios/apps/AutoTester/AutoTester/testCases/GlobeRotationTestCase.swift b/ios/apps/AutoTester/AutoTester/testCases/GlobeRotationTestCase.swift new file mode 100644 index 0000000000..471b05a2b1 --- /dev/null +++ b/ios/apps/AutoTester/AutoTester/testCases/GlobeRotationTestCase.swift @@ -0,0 +1,29 @@ +// +// GlobeRotationTestCase.swift +// AutoTester +// +// Created by Tim Sylvester on 2/9/21. +// Copyright © 2021 mousebird consulting. All rights reserved. +// + +import Foundation + +class GlobeRotationTestCase: MaplyTestCase { + + override init() { + super.init() + + self.name = "Globe Rotation (#1286)" + self.implementations = [.globe] + } + + let baseCase : VectorsTestCase = VectorsTestCase() + + override func setUpWithGlobe(_ globeVC: WhirlyGlobeViewController) { + baseCase.setUpWithGlobe(globeVC) + globeVC.animate(toPosition: MaplyCoordinateMakeWithDegrees(0, 20), height:0.75, heading:0, time:0.5) + } + + override func stop() { + } +} diff --git a/ios/apps/AutoTester/AutoTester/testCases/GlobeSamplerTestCase.swift b/ios/apps/AutoTester/AutoTester/testCases/GlobeSamplerTestCase.swift index 79d792929e..25303c1da5 100644 --- a/ios/apps/AutoTester/AutoTester/testCases/GlobeSamplerTestCase.swift +++ b/ios/apps/AutoTester/AutoTester/testCases/GlobeSamplerTestCase.swift @@ -35,7 +35,6 @@ class GlobeSamplerTestCase: MaplyTestCase { sampleParams.coordSys = MaplySphericalMercator(webStandard: ()) sampleParams.coverPoles = true sampleParams.edgeMatching = true - sampleParams.minZoom = tileInfo.minZoom() sampleParams.maxZoom = tileInfo.maxZoom() sampleParams.singleLevel = true @@ -44,7 +43,9 @@ class GlobeSamplerTestCase: MaplyTestCase { } let interp = MaplyOvlDebugImageLoaderInterpreter(viewC: baseVC) imageLoader.setInterpreter(interp) +#if !targetEnvironment(simulator) imageLoader.imageFormat = .imageUShort565; +#endif // imageLoader.debugMode = true return imageLoader diff --git a/ios/apps/AutoTester/AutoTester/testCases/GreatCircleTestCase.m b/ios/apps/AutoTester/AutoTester/testCases/GreatCircleTestCase.m index a402204063..052922d178 100644 --- a/ios/apps/AutoTester/AutoTester/testCases/GreatCircleTestCase.m +++ b/ios/apps/AutoTester/AutoTester/testCases/GreatCircleTestCase.m @@ -34,6 +34,14 @@ -(NSArray *)addLongRoute:(MaplyBaseViewController *)viewC globe:(bool)isGlobe MaplyCoordinate y = MaplyCoordinateMakeWithDegrees(151.177, -33.946); MaplyCoordinate z[] = { x, y }; MaplyVectorObject *v0 = [[MaplyVectorObject alloc] initWithLineString:z numCoords:2 attributes:nil]; + +// UIImage *alcohol = [UIImage imageNamed:@"alcohol-shop-24@2x"]; +// MaplyScreenMarker *marker = [[MaplyScreenMarker alloc]init]; +// marker.image = alcohol; +// marker.loc = v0.centroid; +// marker.selectable = true; +// marker.layoutImportance = 1.0; + [v0 subdivideToGlobeGreatCircle:0.001]; [compObjs addObject:[viewC addWideVectors:@[v0] desc:@{ @@ -41,6 +49,8 @@ -(NSArray *)addLongRoute:(MaplyBaseViewController *)viewC globe:(bool)isGlobe kMaplyEnable: @(YES), kMaplyVecWidth: @(6.0), }]]; + +// [compObjs addObject:[viewC addScreenMarkers:@[marker] desc:nil]]; } if (true) { diff --git a/ios/apps/AutoTester/AutoTester/testCases/ImageReloadTestCase.swift b/ios/apps/AutoTester/AutoTester/testCases/ImageReloadTestCase.swift index 5b76edeb23..211cdac8d0 100644 --- a/ios/apps/AutoTester/AutoTester/testCases/ImageReloadTestCase.swift +++ b/ios/apps/AutoTester/AutoTester/testCases/ImageReloadTestCase.swift @@ -32,7 +32,6 @@ class ImageReloadTestCase: MaplyTestCase sampleParams.coordSys = MaplySphericalMercator(webStandard: ()) sampleParams.coverPoles = true sampleParams.edgeMatching = true - sampleParams.minZoom = tileInfo.minZoom() sampleParams.maxZoom = tileInfo.maxZoom() sampleParams.singleLevel = true @@ -40,7 +39,9 @@ class ImageReloadTestCase: MaplyTestCase return } self.imageLoader = imageLoader - imageLoader.imageFormat = .imageUShort565 + #if !targetEnvironment(simulator) + imageLoader.imageFormat = .imageUShort565; + #endif // imageLoader.debugMode = true // Let things settle and then change the source diff --git a/ios/apps/AutoTester/AutoTester/testCases/LabelsTestCase.m b/ios/apps/AutoTester/AutoTester/testCases/LabelsTestCase.m index 209c7ce626..1751d87cd0 100644 --- a/ios/apps/AutoTester/AutoTester/testCases/LabelsTestCase.m +++ b/ios/apps/AutoTester/AutoTester/testCases/LabelsTestCase.m @@ -3,7 +3,7 @@ // AutoTester // // Created by jmnavarro on 2/11/15. -// Copyright © 2015-2017 mousebird consulting. +// Copyright © 2015-2021 mousebird consulting. // #import "LabelsTestCase.h" @@ -19,7 +19,7 @@ @implementation LabelsTestCase - (instancetype)init { if (self = [super init]) { - self.name = @"Labels"; + self.name = @"Labels (broken)"; self.implementations = MaplyTestCaseImplementationMap | MaplyTestCaseImplementationGlobe; } return self; diff --git a/ios/apps/AutoTester/AutoTester/testCases/LayerStartupShutdownTestCase.swift b/ios/apps/AutoTester/AutoTester/testCases/LayerStartupShutdownTestCase.swift index 7c8bc53d99..148a48f5ae 100644 --- a/ios/apps/AutoTester/AutoTester/testCases/LayerStartupShutdownTestCase.swift +++ b/ios/apps/AutoTester/AutoTester/testCases/LayerStartupShutdownTestCase.swift @@ -21,14 +21,20 @@ class LayerStartupShutdownTestCase: MaplyTestCase { // var testCase = GeographyClassTestCase() var testCase = VectorMBTilesTestCase() + var run = true + func startGlobeLayer() { self.testCase.globeViewController = globeViewController self.testCase.baseViewController = globeViewController self.testCase.setUpWithGlobe(self.globeViewController!) + run = true + // Shut it down in a bit DispatchQueue.main.asyncAfter(deadline: .now() + 2.0) { - self.stopGlobeLayer() + if self.run { + self.stopGlobeLayer() + } } } @@ -38,7 +44,9 @@ class LayerStartupShutdownTestCase: MaplyTestCase { // Start it back up again in a bit // Note: Check to see if we're still valid here DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) { - self.startGlobeLayer() + if self.run { + self.startGlobeLayer() + } } } @@ -49,7 +57,9 @@ class LayerStartupShutdownTestCase: MaplyTestCase { // Shut it down in a bit DispatchQueue.main.asyncAfter(deadline: .now() + 2.0) { - self.stopMapLayer() + if self.run { + self.stopMapLayer() + } } } @@ -59,7 +69,9 @@ class LayerStartupShutdownTestCase: MaplyTestCase { // Start it back up again in a bit // Note: Check to see if we're still valid here DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) { - self.startMapLayer() + if self.run { + self.startMapLayer() + } } } @@ -87,4 +99,7 @@ class LayerStartupShutdownTestCase: MaplyTestCase { startMapLayer() } + override func stop() { + run = false + } } diff --git a/ios/apps/AutoTester/AutoTester/testCases/LocationTrackingRealTestCase.swift b/ios/apps/AutoTester/AutoTester/testCases/LocationTrackingRealTestCase.swift index 803497e03d..64137e470a 100644 --- a/ios/apps/AutoTester/AutoTester/testCases/LocationTrackingRealTestCase.swift +++ b/ios/apps/AutoTester/AutoTester/testCases/LocationTrackingRealTestCase.swift @@ -32,7 +32,7 @@ class LocationTrackingRealTestCase: MaplyTestCase, MaplyLocationTrackerDelegate segCtrl?.clipsToBounds = true baseVC.view.addSubview(segCtrl!) - baseVC.startLocationTracking(with: self, useHeading: true, useCourse: true, simulate: false) + baseVC.startLocationTracking(with: self, useHeading: true, useCourse: true) } override func setUpWithGlobe(_ globeVC: WhirlyGlobeViewController) { diff --git a/ios/apps/AutoTester/AutoTester/testCases/LocationTrackingSimTestCase.swift b/ios/apps/AutoTester/AutoTester/testCases/LocationTrackingSimTestCase.swift index d73aa2d0fa..781a8cf76f 100644 --- a/ios/apps/AutoTester/AutoTester/testCases/LocationTrackingSimTestCase.swift +++ b/ios/apps/AutoTester/AutoTester/testCases/LocationTrackingSimTestCase.swift @@ -8,10 +8,11 @@ import UIKit -class LocationTrackingSimTestCase: MaplyTestCase, MaplyLocationTrackerDelegate { +class LocationTrackingSimTestCase: MaplyTestCase, MaplyLocationTrackerDelegate, MaplyLocationSimulatorDelegate { var segCtrl: UISegmentedControl? - var simPointIndex: Int? + var simPointIndex: Int = 0 + var simFailIndex: Int = 0 var simPointData: [[Float]]? var baseLayer : MaplyTestCase? = nil @@ -35,8 +36,8 @@ class LocationTrackingSimTestCase: MaplyTestCase, MaplyLocationTrackerDelegate { segCtrl?.layer.cornerRadius = 4 segCtrl?.clipsToBounds = true baseVC.view.addSubview(segCtrl!) - - baseVC.startLocationTracking(with: self, useHeading: true, useCourse: true, simulate: true) + + baseVC.startLocationTracking(with: self, simulator: self, simInterval: 0.75, useHeading: true, useCourse: true) } override func setUpWithGlobe(_ globeVC: WhirlyGlobeViewController) { @@ -46,7 +47,8 @@ class LocationTrackingSimTestCase: MaplyTestCase, MaplyLocationTrackerDelegate { setupLocationTracking(baseVC: globeVC) - globeVC.animate(toPosition: MaplyCoordinateMakeWithDegrees(2.3508, 48.8567), height: 0.5, heading: 0.0, time: 0.5) + globeVC.animate(toPosition: MaplyCoordinateMakeWithDegrees(16.382910,48.211350), height: 0.0025, heading: 0.0, time: 0.5) + globeVC.setZoomLimitsMin(0.0001, max: 1.0) } override func setUpWithMap(_ mapVC: MaplyViewController) { @@ -55,8 +57,8 @@ class LocationTrackingSimTestCase: MaplyTestCase, MaplyLocationTrackerDelegate { setupLocationTracking(baseVC: mapVC) - mapVC.animate(toPosition:MaplyCoordinateMakeWithDegrees(2.3508, 48.8567), height: 0.5, time: 1.0) - mapVC.setZoomLimitsMin(0.0005, max: 4.0) + mapVC.animate(toPosition:MaplyCoordinateMakeWithDegrees(16.382910,48.211350), height: 0.0025, time: 0.5) + mapVC.setZoomLimitsMin(0.0001, max: 4.0) } @objc func onSegChange() { @@ -80,10 +82,17 @@ class LocationTrackingSimTestCase: MaplyTestCase, MaplyLocationTrackerDelegate { } func getSimulationPoint() -> MaplyLocationTrackerSimulationPoint { - let pointData:[Float] = (simPointData?[simPointIndex!])! - simPointIndex = (simPointIndex! + 1) % (simPointData?.count)! - - return MaplyLocationTrackerSimulationPoint(lonDeg: pointData[0], latDeg: pointData[1], headingDeg: pointData[2]) + if let data = simPointData { + let pointData:[Float] = data[simPointIndex] + simPointIndex = (simPointIndex + 1) % data.count + return MaplyLocationTrackerSimulationPoint(lonDeg: pointData[0], latDeg: pointData[1], headingDeg: pointData[2]) + } + return MaplyLocationTrackerSimulationPoint() + } + + func hasValidLocation() -> Bool { + simFailIndex += 1 + return (simPointData != nil) && (simFailIndex % 20) != 0 } func setSimulationPoints() { diff --git a/ios/apps/AutoTester/AutoTester/testCases/MapTilerCircleTestCase.swift b/ios/apps/AutoTester/AutoTester/testCases/MapTilerCircleTestCase.swift new file mode 100644 index 0000000000..91ffae81f4 --- /dev/null +++ b/ios/apps/AutoTester/AutoTester/testCases/MapTilerCircleTestCase.swift @@ -0,0 +1,29 @@ +// +// MapTilerCircleTestCase.swift +// AutoTester +// +// Created by Tim Sylvester on 3/23/21. +// Copyright © 2021 mousebird consulting. All rights reserved. +// +import UIKit + +class MapTilerCircleTestCase: MapTilerTestCase { + + init() { + super.init("MapTiler Circles") + mapTilerStyle = 0 + } + + override func setup(_ map: MapboxKindaMap) { + super.setup(map) + map.styleSettings.markerScale = 1.0 + } + + override func getStyles() -> [(name: String, sheet: String)] { + return [ + ("Custom", "maptiler_test_circles") + ] + } + +} + diff --git a/ios/apps/AutoTester/AutoTester/testCases/MapTilerTestCase.swift b/ios/apps/AutoTester/AutoTester/testCases/MapTilerTestCase.swift index 687b63e44f..b8018ccac1 100644 --- a/ios/apps/AutoTester/AutoTester/testCases/MapTilerTestCase.swift +++ b/ios/apps/AutoTester/AutoTester/testCases/MapTilerTestCase.swift @@ -3,31 +3,41 @@ // AutoTester // // Created by Steve Gifford on 11/8/19. -// Copyright © 2019 mousebird consulting. All rights reserved. +// Copyright © 2021 mousebird consulting. All rights reserved. // import UIKit class MapTilerTestCase: MaplyTestCase { - - override init() { + + init(_ name: String, _ impl: MaplyTestCaseImplementations = [.map,.globe]) { super.init() + self.name = name + self.implementations = impl + self.styles = getStyles() + } + + override convenience init() { + self.init("MapTiler Test Cases",[.map,.globe]) - self.name = "MapTiler Test Cases" - self.implementations = [.map,.globe] + let env = ProcessInfo.processInfo.environment + mapTilerStyle = NumberFormatter().number(from: env["MAPTILER_STYLE"] ?? "")?.intValue ?? mapTilerStyle } - - // Styles included in the bundle - let styles : [(name: String, sheet: String)] = - [("Basic", "maptiler_basic"), - ("Hybrid Satellite", "maptiler_hybrid_satellite"), - ("Streets", "maptiler_streets"), -// ("Topo", "maptiler_topo") - ] - let MapTilerStyle = 0 - + + func getStyles() -> [(name: String, sheet: String)] { + return [ + ("Basic", "maptiler_basic"), + ("Hybrid Satellite", "maptiler_hybrid_satellite"), + ("Streets", "maptiler_streets"), + // ("Topo", "maptiler_topo"), // ? + ("Custom", "maptiler_expr_test") + ] + } + + var styles = [(name: String, sheet: String)]() + var mapTilerStyle = 2 var mapboxMap : MapboxKindaMap? = nil - + // Start fetching the required pieces for a Mapbox style map func startMap(_ style: (name: String, sheet: String), viewC: MaplyBaseViewController, round: Bool) { guard let fileName = Bundle.main.url(forResource: style.sheet, withExtension: "json") else { @@ -35,6 +45,8 @@ class MapTilerTestCase: MaplyTestCase { return } + print("Starting map with \(style.name) / \(style.sheet)") + // Maptiler token // Go to maptiler.com, setup an account and get your own. // Go to Edit Scheme, select Run, Arguments, and add an "MAPTILER_TOKEN" entry to Environment Variables. @@ -74,10 +86,15 @@ class MapTilerTestCase: MaplyTestCase { self.legendVC = legendVC } } + setup(mapboxMap) mapboxMap.start() self.mapboxMap = mapboxMap } + func setup(_ map: MapboxKindaMap) { + map.styleSettings.textScale = 1.1 + } + var legendVisibile = false var legendVC: LegendViewController? = nil @@ -106,15 +123,29 @@ class MapTilerTestCase: MaplyTestCase { } override func setUpWithMap(_ mapVC: MaplyViewController) { - mapVC.performanceOutput = true - - let env = ProcessInfo.processInfo.environment - let styleIdx = NumberFormatter().number(from: env["MAPTILER_STYLE"] ?? "")?.intValue ?? MapTilerStyle - startMap(styles[styleIdx], viewC: mapVC, round: false) + //mapVC.performanceOutput = true + startMap(styles[mapTilerStyle], viewC: mapVC, round: false) + mapVC.rotateGestureThreshold = 15; + runProgram(mapVC) + } + + override func setUpWithGlobe(_ mapVC: WhirlyGlobeViewController) { + //mapVC.performanceOutput = true + + startMap(styles[mapTilerStyle], viewC: mapVC, round: true) + + runProgram(mapVC) + } + + private func runProgram(_ viewC: MaplyBaseViewController) { + let map = (viewC as? MaplyViewController) + let globe = (viewC as? WhirlyGlobeViewController) + // e.g., "35.66,139.835,0.025,0.0025,2,20" + let env = ProcessInfo.processInfo.environment if let program = env["MAPTILER_PROGRAM"] { let components = program.components(separatedBy: ",") .map(NumberFormatter().number) @@ -126,21 +157,27 @@ class MapTilerTestCase: MaplyTestCase { let inHeight = components[3]?.floatValue ?? 0.001 let interval = TimeInterval(components[4]?.doubleValue ?? 1) let count = components[5]?.intValue ?? 1 - mapVC.setPosition(center, height: outHeight) + + map?.setPosition(center, height: outHeight) + globe?.setPosition(center, height: outHeight) + for i in 0 ... 2 * count { Timer.scheduledTimer(withTimeInterval: TimeInterval(i) * interval, repeats: false) { _ in - mapVC.animate(toPosition: center, height: (i % 2 == 0) ? inHeight : outHeight, time: interval) + let height = (i % 2 == 0) ? inHeight : outHeight + map?.animate(toPosition: center, height: height, time: interval) + globe?.animate(toPosition: center, height: height, heading: 0, time: interval) } } } } } - - override func setUpWithGlobe(_ mapVC: WhirlyGlobeViewController) { - mapVC.performanceOutput = true - - startMap(styles[MapTilerStyle], viewC: mapVC, round: true) - } + override func maplyViewController(_ viewC: MaplyViewController, didTapAt coord: MaplyCoordinate) { + mapboxMap?.stop() + mapboxMap = nil + + mapTilerStyle = (mapTilerStyle + 1) % styles.count + startMap(styles[mapTilerStyle], viewC: viewC, round: false) + } } diff --git a/ios/apps/AutoTester/AutoTester/testCases/MovingScreenMarkersTestCase.swift b/ios/apps/AutoTester/AutoTester/testCases/MovingScreenMarkersTestCase.swift new file mode 100644 index 0000000000..9a99f0409d --- /dev/null +++ b/ios/apps/AutoTester/AutoTester/testCases/MovingScreenMarkersTestCase.swift @@ -0,0 +1,84 @@ +// +// MovingScreenMarkersTestCase.swift +// AutoTester +// +// Created by Tim Sylvester on 9 Feb. 2021 +// Copyright © 2021 mousebird consulting. +// + +import Foundation + +class MovingScreenMarkersTestCase: MaplyTestCase { + + override init() { + super.init() + + self.name = "Moving Screen Markers" + self.implementations = [.globe, .map] + } + + let baseCase : VectorsTestCase = VectorsTestCase() + + override func setUpWithGlobe(_ globeVC: WhirlyGlobeViewController) { + baseCase.setUpWithGlobe(globeVC) + setUp(globeVC) + globeVC.animate(toPosition: MaplyCoordinateMakeWithDegrees(0, 10), height:0.5, heading:0, time:0.5) + } + + override func setUpWithMap(_ mapVC: MaplyViewController) { + baseCase.setUpWithMap(mapVC) + setUp(mapVC) + mapVC.setPosition(MaplyCoordinateMakeWithDegrees(0, 10), height: 0.5) + } + + func setUp(_ vc: MaplyBaseViewController) { + self.markerObj = makeMarkers() + timer = Timer.scheduledTimer(withTimeInterval: duration, repeats: true) { [weak self] _ in + guard let self = self else { return } + self.clearMarkers() + self.markerObj = self.makeMarkers() + } + } + + func clearMarkers() { + if let obj = markerObj { + baseViewController?.remove(obj) + markerObj = nil + } + } + + func makeMarkers() -> MaplyComponentObject? { + let pts = [ + MaplyCoordinateMakeWithDegrees(0.0, 0.0), + MaplyCoordinateMakeWithDegrees(10.0, 10.0), + MaplyCoordinateMakeWithDegrees(0.0, 20.0), + MaplyCoordinateMakeWithDegrees(-10.0, 10.0) + ] + let images = [ + UIImage(named: "marker-24@2x")!, + UIImage(named: "marker-stroked-24@2x")! + ] + let markers = (0.. MaplyMovingScreenMarker in + let marker = MaplyMovingScreenMarker() + marker.duration = duration + marker.period = duration / 2 + marker.loc = pts[i] + marker.endLoc = pts[(i + 1) % pts.count] + marker.size = CGSize(width: 32, height: 32) + marker.images = images + marker.layoutImportance = Float.greatestFiniteMagnitude + return marker + } + return baseViewController?.addScreenMarkers(markers, desc: [:]) + } + + override func stop() { + timer?.invalidate() + timer = nil + clearMarkers() + } + + let duration = 5.0 + var timer: Timer? + var markerObj: MaplyComponentObject? +} diff --git a/ios/apps/AutoTester/AutoTester/testCases/NASAGIBSTestCase.swift b/ios/apps/AutoTester/AutoTester/testCases/NASAGIBSTestCase.swift index 54ef5a1fcf..6423c08ac5 100644 --- a/ios/apps/AutoTester/AutoTester/testCases/NASAGIBSTestCase.swift +++ b/ios/apps/AutoTester/AutoTester/testCases/NASAGIBSTestCase.swift @@ -47,7 +47,6 @@ class NASAGIBSTestCase: MaplyTestCase { sampleParams.coordSys = MaplySphericalMercator(webStandard: ()) sampleParams.coverPoles = true sampleParams.edgeMatching = true - sampleParams.minZoom = tileInfo.minZoom() sampleParams.maxZoom = tileInfo.maxZoom() sampleParams.singleLevel = true @@ -55,8 +54,10 @@ class NASAGIBSTestCase: MaplyTestCase { return nil } imageLoader.baseDrawPriority = kMaplyImageLayerDrawPriorityDefault - imageLoader.imageFormat = .imageUShort565; - + #if !targetEnvironment(simulator) + imageLoader.imageFormat = .imageUShort565; + #endif + return imageLoader } @@ -73,7 +74,6 @@ class NASAGIBSTestCase: MaplyTestCase { sampleParams.coordSys = MaplySphericalMercator(webStandard: ()) sampleParams.coverPoles = false sampleParams.edgeMatching = false - sampleParams.minZoom = tileInfo.minZoom() sampleParams.maxZoom = tileInfo.maxZoom() sampleParams.singleLevel = true @@ -81,8 +81,10 @@ class NASAGIBSTestCase: MaplyTestCase { return nil } imageLoader.baseDrawPriority = kMaplyImageLayerDrawPriorityDefault+1000 - imageLoader.imageFormat = .imageUShort565; - + #if !targetEnvironment(simulator) + imageLoader.imageFormat = .imageUShort565; + #endif + return imageLoader } diff --git a/ios/apps/AutoTester/AutoTester/testCases/OfflineRenderTestCase.swift b/ios/apps/AutoTester/AutoTester/testCases/OfflineRenderTestCase.swift index e1f244425e..0d92d5155d 100644 --- a/ios/apps/AutoTester/AutoTester/testCases/OfflineRenderTestCase.swift +++ b/ios/apps/AutoTester/AutoTester/testCases/OfflineRenderTestCase.swift @@ -33,7 +33,6 @@ class OfflineRenderTestCase: MaplyTestCase { sampleParams.coordSys = MaplySphericalMercator(webStandard: ()) sampleParams.coverPoles = true sampleParams.edgeMatching = true - sampleParams.minZoom = tileInfo.minZoom() sampleParams.maxZoom = tileInfo.maxZoom() sampleParams.singleLevel = true // sampleParams.minImportance = 1024.0 * 1024.0 diff --git a/ios/apps/AutoTester/AutoTester/testCases/OpenMapTilesHybridTestCase.swift b/ios/apps/AutoTester/AutoTester/testCases/OpenMapTilesHybridTestCase.swift index ce9a1190fb..15e1990ff7 100644 --- a/ios/apps/AutoTester/AutoTester/testCases/OpenMapTilesHybridTestCase.swift +++ b/ios/apps/AutoTester/AutoTester/testCases/OpenMapTilesHybridTestCase.swift @@ -98,7 +98,6 @@ class OpenMapTilesHybridTestCase: MaplyTestCase { sampleParams.coverPoles = false sampleParams.edgeMatching = false } - sampleParams.minZoom = 0 sampleParams.maxZoom = tileInfo.maxZoom() guard let imageLoader = MaplyQuadImageLoader(params: sampleParams, tileInfo: tileInfo, viewC: baseVC) else { diff --git a/ios/apps/AutoTester/AutoTester/testCases/RepresentationsTestCase.h b/ios/apps/AutoTester/AutoTester/testCases/RepresentationsTestCase.h new file mode 100644 index 0000000000..c0e4ef6158 --- /dev/null +++ b/ios/apps/AutoTester/AutoTester/testCases/RepresentationsTestCase.h @@ -0,0 +1,16 @@ +// +// ChangeVectorsTestCase.h +// AutoTester +// +// Created by Tim Sylvester on 31 Dec 2020. +// Copyright © 2020 mousebird consulting. +// + +#import "MaplyTestCase.h" + +@class VectorsTestCase; + +@interface RepresentationsTestCase : MaplyTestCase + +@property (nonatomic) VectorsTestCase *baseCase; +@end diff --git a/ios/apps/AutoTester/AutoTester/testCases/RepresentationsTestCase.mm b/ios/apps/AutoTester/AutoTester/testCases/RepresentationsTestCase.mm new file mode 100644 index 0000000000..99eeb0be5e --- /dev/null +++ b/ios/apps/AutoTester/AutoTester/testCases/RepresentationsTestCase.mm @@ -0,0 +1,282 @@ +// +// RepresentationsTestCase.m +// AutoTester +// +// Created by Tim Sylvester on 6 Jan. 2020. +// Copyright © 2021 mousebird consulting. +// + +#import "VectorsTestCase.h" +#import "RepresentationsTestCase.h" +#import "MaplyBaseViewController.h" +#import "MaplyViewController.h" +#import "WhirlyGlobeViewController.h" +#import "AutoTester-Swift.h" + +#include + +@interface RepresentationsTestCase() +@end + +@implementation RepresentationsTestCase { + NSTimer *_animationTimer; + NSArray *_vectorObjs; + NSArray *_markerObjs; + NSArray *_labelObjs; + MaplyTexture *_dashedLineTex; +} + +- (instancetype)init +{ + if (self = [super init]) { + self.name = @"Alternate Representations"; + self.implementations = MaplyTestCaseImplementationMap | MaplyTestCaseImplementationGlobe; + } + + return self; +} + +- (void)setupWithBaseVC:(MaplyBaseViewController *)vc { + _animationTimer = [NSTimer scheduledTimerWithTimeInterval:1.0 + target:self + selector:@selector(animationCallback) + userInfo:nil + repeats:NO]; +} + +- (void)setUpWithGlobe:(WhirlyGlobeViewController *)vc +{ + self.baseCase = [[VectorsTestCase alloc]init]; + [self.baseCase setUpWithGlobe:vc]; + [self setupWithBaseVC:vc]; + [vc setPosition:MaplyCoordinateMakeWithDegrees(100, -70) height:1.0]; +} + +- (void)setUpWithMap:(MaplyViewController *)vc +{ + self.baseCase = [[VectorsTestCase alloc]init]; + [self.baseCase setUpWithMap:vc]; + [self setupWithBaseVC:vc]; + [vc setPosition:MaplyCoordinateMakeWithDegrees(100, -70) height:1.0]; +} + +- (void) animationCallback +{ + if (!_dashedLineTex) + { + auto lineTexBuilder = [[MaplyLinearTextureBuilder alloc] init]; + [lineTexBuilder setPattern:@[@(2),@(2)]]; + _dashedLineTex = [self.baseViewController addTexture:[lineTexBuilder makeImage] + desc:@{kMaplyTexMinFilter: kMaplyMinFilterNearest, + kMaplyTexMagFilter: kMaplyMinFilterNearest, + kMaplyTexWrapX: @true, + kMaplyTexWrapY: @true, + kMaplyTexFormat: @(MaplyImageIntRGBA)} + mode:MaplyThreadCurrent]; + } + + const char* const vecUUID = "abc—⃘�—⃘123"; + const char* const labelUUID = "0302eb46-7ff0-4db0-a915-ed2096bf1517"; + static const char* const reps[] = { "", "hilite", "�subtle", "detail" }; + static const int repCount = sizeof(reps) / sizeof(reps[0]); + static int idx = 0; + + if (!_vectorObjs) + { + // First time + + // Set a representation before any objects are added + // "detail" version should appear first: dashed magenta line with alternate geometry + [self.baseViewController setRepresentation:@(reps[3]) ofUUIDs:@[@(vecUUID)]]; + + const MaplyCoordinate pts[] = { MaplyCoordinateMakeWithDegrees(50, -65), MaplyCoordinateMakeWithDegrees(150, -65) }; + const auto vec = [[MaplyVectorObject alloc] initWithLineString:pts numCoords:sizeof(pts)/sizeof(pts[0]) attributes:nil]; + _vectorObjs = @[ + [self.baseViewController addWideVectors: @[vec] desc:@{ + kMaplyEnable: @(false), + kMaplyColor: [UIColor magentaColor], + kMaplySubdivType: kMaplySubdivGreatCircle, + kMaplySubdivEpsilon: @0.01, + kMaplyVecWidth: @3, + kMaplyWideVecOffset: @-1.5, + kMaplyUUID: @(vecUUID), + //kMaplyRepresentation: @"" // not set + }], + [self.baseViewController addWideVectors: @[vec] desc:@{ + kMaplyEnable: @(false), + kMaplyColor: [UIColor redColor], + kMaplySubdivType: kMaplySubdivGreatCircle, + kMaplySubdivEpsilon: @0.001, + kMaplyVecWidth: @10, + kMaplyWideVecOffset: @-10, + kMaplyUUID: @(vecUUID), + kMaplyRepresentation: @(reps[1]) + }], + [self.baseViewController addWideVectors: @[vec] desc:@{ + kMaplyEnable: @(false), + kMaplyColor: [[UIColor blackColor] colorWithAlphaComponent:0.5], + kMaplySubdivType: kMaplySubdivGreatCircle, + kMaplySubdivEpsilon: @0.0001, + kMaplyVecWidth: @3, + kMaplyWideVecOffset: @-1.5, + kMaplyUUID: @(vecUUID), + kMaplyRepresentation: @(reps[2]) + }], + [self.baseViewController addWideVectors: @[vec] desc:@{ + kMaplyEnable: @(false), + kMaplyColor: [UIColor magentaColor], + kMaplySubdivType: kMaplySubdivGreatCircle, + kMaplySubdivEpsilon: @0.00001, + kMaplyVecWidth: @6, + kMaplyWideVecOffset: @-3, + kMaplyVecTexture: _dashedLineTex, + kMaplyUUID: @(vecUUID), + kMaplyRepresentation: @(reps[3]) + }], + ]; + + const auto vecSubdiv = [[MaplyVectorObject alloc] initWithLineString:pts numCoords:sizeof(pts)/sizeof(pts[0]) attributes:nil]; + [vecSubdiv subdivideToGlobe:0.0001]; + + const auto m1 = [MaplyScreenMarker new]; + m1.loc = [vecSubdiv center]; + m1.size = CGSizeMake(20, 20); + m1.color = [UIColor magentaColor]; + m1.layoutImportance = MAXFLOAT; + + const auto m2 = [MaplyScreenMarker new]; + m2.loc = [vecSubdiv centroid]; + m2.size = CGSizeMake(50, 50); + m2.color = [UIColor redColor]; + m1.rotation = M_PI_4; + m2.layoutImportance = MAXFLOAT; + + const auto m3 = [MaplyScreenMarker new]; + m3.loc = [vecSubdiv linearMiddle:self.baseViewController.coordSystem]; + m3.size = CGSizeMake(30, 30); + m3.color = [[UIColor blackColor] colorWithAlphaComponent:0.5]; + m3.layoutImportance = MAXFLOAT; + + _markerObjs = @[ + [self.baseViewController addScreenMarkers:@[m1] desc:@{ + kMaplyEnable: @(false), + kMaplyUUID: @(vecUUID), + //kMaplyRepresentation: @"" // not set + }], + [self.baseViewController addScreenMarkers:@[m2] desc:@{ + kMaplyEnable: @(false), + kMaplyUUID: @(vecUUID), + kMaplyRepresentation: @(reps[1]) + }], + [self.baseViewController addScreenMarkers:@[m3] desc:@{ + kMaplyEnable: @(false), + kMaplyUUID: @(vecUUID), + kMaplyRepresentation: @(reps[2]) + }], + ]; + + auto label0 = [[MaplyScreenLabel alloc] init]; + label0.loc = MaplyCoordinateMakeWithDegrees(100.0, -68.0); + label0.text = [@"rep:" stringByAppendingString:@(reps[0])]; + label0.layoutImportance = MAXFLOAT; + label0.layoutPlacement = kMaplyLayoutCenter; + + auto label1 = [[MaplyScreenLabel alloc] init]; + label1.loc = MaplyCoordinateMakeWithDegrees(100.0, -68.0); + label1.text = [@"rep:" stringByAppendingString:@(reps[1])]; + label1.layoutImportance = MAXFLOAT; + label1.layoutPlacement = kMaplyLayoutCenter; + + auto label2 = [[MaplyScreenLabel alloc] init]; + label2.loc = MaplyCoordinateMakeWithDegrees(100.0, -68.0); + label2.text = [@"rep:" stringByAppendingString:@(reps[2])]; + label2.layoutImportance = MAXFLOAT; + label2.layoutPlacement = kMaplyLayoutCenter; + + auto label3 = [[MaplyScreenLabel alloc] init]; + label3.loc = MaplyCoordinateMakeWithDegrees(100.0, -68.0); + label3.text = [@"rep:" stringByAppendingString:@(reps[3])]; + label3.layoutImportance = MAXFLOAT; + label3.layoutPlacement = kMaplyLayoutCenter; + + _labelObjs = @[ + [self.baseViewController addScreenLabels:@[label0] desc:@{ + kMaplyFont: [UIFont boldSystemFontOfSize:24.0], + kMaplyTextOutlineColor: [UIColor whiteColor], + kMaplyTextOutlineSize: @1.0, + kMaplyTextColor: [UIColor blackColor], + //kMaplyDrawPriority: + kMaplyEnable: @(false), + kMaplyUUID: @(labelUUID), + //kMaplyRepresentation: @"" // not set + }], + [self.baseViewController addScreenLabels:@[label1] desc:@{ + kMaplyFont: [UIFont boldSystemFontOfSize:24.0], + kMaplyTextOutlineColor: [UIColor whiteColor], + kMaplyTextOutlineSize: @1.0, + kMaplyTextColor: [UIColor blackColor], + kMaplyEnable: @(false), + kMaplyUUID: @(labelUUID), + kMaplyRepresentation: @(reps[1]) + }], + // No label for "subtle", display should return to the default + //[self.baseViewController addScreenLabels:@[label2] desc:@{ + // kMaplyFont: [UIFont boldSystemFontOfSize:24.0], + // kMaplyTextOutlineColor: [UIColor whiteColor], + // kMaplyTextOutlineSize: @1.0, + // kMaplyTextColor: [UIColor blackColor], + // kMaplyEnable: @(false), + // kMaplyUUID: @(labelUUID), + // kMaplyRepresentation: @(reps[2]) + //}], + [self.baseViewController addScreenLabels:@[label3] desc:@{ + kMaplyFont: [UIFont boldSystemFontOfSize:24.0], + kMaplyTextOutlineColor: [UIColor whiteColor], + kMaplyTextOutlineSize: @1.0, + kMaplyTextColor: [UIColor blackColor], + kMaplyEnable: @(false), + kMaplyUUID: @(labelUUID), + kMaplyRepresentation: @(reps[3]) + }] + ]; + } + else + { + // Not the first time + + // Cycle the current representation. + NSString *rep = @(reps[idx % repCount]); + [self.baseViewController setRepresentation:rep ofUUIDs:@[@(vecUUID)]]; + + // Alternate between the default fallback and falling back on the "detailed" representation. + // That is, the grey lines should correspond to "rep:" and "rep:hilite" alternatively. + NSString *fallback = nil; + if ((idx % repCount) == 2) + { + static int fb = 0; + fallback = (fb++ % 2) ? @(reps[1]) : nil; + } + [self.baseViewController setRepresentation:rep fallbackRepName:fallback ofUUIDs:@[@(labelUUID)]]; + + idx += 1; + } + + _animationTimer = [NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:@selector(animationCallback) userInfo:nil repeats:NO]; +} + +- (void) stop +{ + if (_animationTimer) { + [_animationTimer invalidate]; + _animationTimer = nil; + } + + _vectorObjs = nil; + _markerObjs = nil; + _labelObjs = nil; + _dashedLineTex = nil; + + [self.baseCase stop]; +} + +@end diff --git a/ios/apps/AutoTester/AutoTester/testCases/ScreenLabelsTestCase.swift b/ios/apps/AutoTester/AutoTester/testCases/ScreenLabelsTestCase.swift index b11db2854c..5ae8a2a774 100644 --- a/ios/apps/AutoTester/AutoTester/testCases/ScreenLabelsTestCase.swift +++ b/ios/apps/AutoTester/AutoTester/testCases/ScreenLabelsTestCase.swift @@ -61,6 +61,8 @@ class ScreenLabelsTestCase: MaplyTestCase { kMaplyShadowSize: 1.0, kMaplySelectable: true, kMaplyTextColor: UIColor.red, + kMaplyMinVis: 0.1, + kMaplyMaxVis: 0.5, kMaplyBackgroundColor: UIColor.blue]) { labelList.append(comp) } diff --git a/ios/apps/AutoTester/AutoTester/testCases/ShapefileTestCase.m b/ios/apps/AutoTester/AutoTester/testCases/ShapefileTestCase.m index 550ce85c0a..aef1b16ae9 100644 --- a/ios/apps/AutoTester/AutoTester/testCases/ShapefileTestCase.m +++ b/ios/apps/AutoTester/AutoTester/testCases/ShapefileTestCase.m @@ -33,7 +33,11 @@ - (void)overlayShapefile MaplyVectorObject *vecObj = [[MaplyVectorObject alloc] initWithShapeFile:@"ne_10m_roads"]; NSLog(@"Adding vectors..."); - compObj = [baseViewC addVectors:@[vecObj] desc:@{kMaplyColor: [UIColor whiteColor]}]; +// compObj = [baseViewC addVectors:@[vecObj] desc:@{kMaplyColor: [UIColor whiteColor]}]; + compObj = [baseViewC addWideVectors:@[vecObj] desc:@{kMaplyColor: [UIColor whiteColor], + kMaplyVecWidth: @(20.0), + kMaplyWideVecImpl: kMaplyWideVecImplPerf + }]; NSLog(@"Done."); } diff --git a/ios/apps/AutoTester/AutoTester/testCases/SimpleStyleTestCase.swift b/ios/apps/AutoTester/AutoTester/testCases/SimpleStyleTestCase.swift index a8fb3528de..c02a567bc7 100644 --- a/ios/apps/AutoTester/AutoTester/testCases/SimpleStyleTestCase.swift +++ b/ios/apps/AutoTester/AutoTester/testCases/SimpleStyleTestCase.swift @@ -3,7 +3,7 @@ // AutoTester // // Created by Steve Gifford on 3/31/20. -// Copyright © 2020 mousebird consulting. All rights reserved. +// Copyright © 2021 mousebird consulting. All rights reserved. // import UIKit @@ -19,28 +19,80 @@ class SimpleStyleTestCase: MaplyTestCase { let baseCase = StamenWatercolorRemote() - let geoJSON = """ - { - "type": "FeatureCollection", - "features": [ + static func prop(_ name: String, _ value: String?, _ quote: Bool) -> String? { + (value != nil) ? ("\"\(name)\": " + (quote ? "\"\(value!)\"" : value!)) : nil + } + static func marker(_ title: String, _ lat: Double, _ lon: Double, m: String? = nil, bg: String? = nil, + c: Bool? = nil, mC: String? = nil, fC: String? = nil, fA: Double? = nil, s: Double? = nil, + sC: String? = nil, sA: Double? = nil, mSz: String? = nil, ox: Double? = nil, oy: Double? = nil) -> String { + """ { "type": "Feature", "properties": { - "marker-color": "#00aa00", - "marker-size": "large", - "marker-symbol": "bar" + """ + + [prop("title", title, true), + prop("marker-size", mSz ?? "large", true), + prop("marker-color", mC, true), + prop("marker-symbol", m, true), + prop("marker-background-symbol", bg, true), + prop("marker-circle", "\(!(c ?? true))", false), + prop("marker-color", mC, true), + prop("fill-color", fC, true), + prop("fill-opacity", (fA != nil) ? "\(fA!)" : nil, false), + prop("stroke-width", (s != nil) ? "\(s!)" : nil, false), + prop("stroke-color", sC, true), + prop("stroke-opacity", (sA != nil) ? "\(sA!)" : nil, false), + prop("marker-offset-x", (ox != nil) ? "\(ox!)" : nil, false), + prop("marker-offset-y", (oy != nil) ? "\(oy!)" : nil, false) + ].compactMap { $0 }.joined(separator: ",") + + """ }, - "geometry": { - "type": "Point", - "coordinates": [ - 151.211111, - -33.859972 - ] - } - }, + "geometry": { "type": "Point", "coordinates": [ \(lon), \(lat) ] } + } + """ + } + + static func markers1() -> String { + let startLon = 142.0, latStep = 0.05, lonStep = 0.05, rowSize = 64 + var lat = -30.0, lon = startLon, n = 0 + return [nil, "bar"].flatMap { m in + [nil, "marker-stroked"].flatMap { bg in + ["small", "medium", "large"].flatMap { sz in + [0.0, 2.0, 5.0].flatMap { sWidth in + [0.0, 0.5, 1.0].flatMap { fA in + [true, false].flatMap { c in + ["f0f", "0f0",].flatMap { mColor in + ["0fa", "a0f",].flatMap { fColor in + ["fa0", "0af",].map { sColor -> String in + lon += lonStep + if (n % rowSize) == 0 { lat -= latStep; lon = startLon } + n += 1 + return marker("", lat, lon, m: m, bg: bg, c: c, mC: mColor, + fC: fColor, fA: fA, s: sWidth, sC: sColor, sA: 0.8, mSz: sz) + } } } } } } } } + }.joined(separator: ",") + } + + static func markers2() -> String { + return [-4.0, -1.5, -0.5, 0.0, 0.5, 1.0].flatMap { ox in + [-5.0, -1.5, -0.5, 0.0, 0.5, 1.0].map { oy in + return marker("", -30, -140, m: nil, bg: "marker-stroked", c: false, + mC: "0a0", fC: "050", fA: 0.8, s: nil, sC: "020", sA: 0.8, + mSz: "medium", ox: ox, oy: oy) + } + }.joined(separator: ",") + } + + let geoJSON = """ + { + "type": "FeatureCollection", + "features": [ + \(markers1()), + \(markers2()), { "type": "Feature", "properties": { + "title": "poly", "fill": "#ff0000", "stroke": "#ffffff" }, @@ -75,6 +127,7 @@ class SimpleStyleTestCase: MaplyTestCase { { "type": "Feature", "properties": { + "title": "line", "stroke": "#0000ff", "stroke-width": 10.0 }, @@ -124,15 +177,9 @@ class SimpleStyleTestCase: MaplyTestCase { } """ - var styleMan : MaplySimpleStyleManager? = nil - func runExamples(_ vc: MaplyBaseViewController) { - styleMan = MaplySimpleStyleManager(viewC: vc) - guard let styleMan = styleMan else { - return - } - + let styleMan = MaplySimpleStyleManager(viewC: vc) if let data = geoJSON.data(using: .utf8), let vecObj = MaplyVectorObject(fromGeoJSON: data) { styleMan.addFeatures([vecObj], mode: .current) diff --git a/ios/apps/AutoTester/AutoTester/testCases/StamenWatercolorRemote.swift b/ios/apps/AutoTester/AutoTester/testCases/StamenWatercolorRemote.swift index 40a4647d83..4de98bdda7 100644 --- a/ios/apps/AutoTester/AutoTester/testCases/StamenWatercolorRemote.swift +++ b/ios/apps/AutoTester/AutoTester/testCases/StamenWatercolorRemote.swift @@ -33,7 +33,6 @@ class StamenWatercolorRemote: MaplyTestCase { sampleParams.coordSys = MaplySphericalMercator(webStandard: ()) sampleParams.coverPoles = true sampleParams.edgeMatching = true - sampleParams.minZoom = tileInfo.minZoom() sampleParams.maxZoom = tileInfo.maxZoom() sampleParams.singleLevel = true sampleParams.minImportance = 1024.0 * 1024.0 / 2.0 diff --git a/ios/apps/AutoTester/AutoTester/testCases/StarsSunTestCase.swift b/ios/apps/AutoTester/AutoTester/testCases/StarsSunTestCase.swift index 8324f0ddba..6b1c21b539 100644 --- a/ios/apps/AutoTester/AutoTester/testCases/StarsSunTestCase.swift +++ b/ios/apps/AutoTester/AutoTester/testCases/StarsSunTestCase.swift @@ -3,7 +3,7 @@ // AutoTester // // Created by jmnavarro on 3/11/15. -// Copyright © 2015-2017 mousebird consulting. +// Copyright © 2015-2021 mousebird consulting. // import UIKit @@ -17,7 +17,7 @@ class StarsSunTestCase: MaplyTestCase { super.init() - self.name = "Stars/Sun" + self.name = "Stars/Sun (broken)" self.implementations = [.globe] } diff --git a/ios/apps/AutoTester/AutoTester/testCases/StartupShutdownTestCase.swift b/ios/apps/AutoTester/AutoTester/testCases/StartupShutdownTestCase.swift index 2409c4dcd1..181e2cbf82 100644 --- a/ios/apps/AutoTester/AutoTester/testCases/StartupShutdownTestCase.swift +++ b/ios/apps/AutoTester/AutoTester/testCases/StartupShutdownTestCase.swift @@ -32,21 +32,35 @@ class StartupShutdownTestCase: MaplyTestCase { self.testCase.globeViewController = globeViewController self.testCase.baseViewController = globeViewController testCase.setUpWithGlobe(globeViewController!) - + // Shut it down in a bit - DispatchQueue.main.asyncAfter(deadline: .now() + 2.0) { - self.stopGlobe() + DispatchQueue.main.asyncAfter(deadline: .now() + 2.0) { [weak self] in + if let self = self { + self.stopGlobe() + } } } func stopGlobe() { // globeViewController?.teardown() + testCase.stop() + + // If the user backed out on their own, stop + if let nav = self.nav { + if (globeViewController != nil && nav.topViewController !== globeViewController) || + (mapViewController != nil && nav.topViewController !== mapViewController) { + return; + } + } + globeViewController?.navigationController?.popViewController(animated: true) // Start it back up again in a bit // Note: Check to see if we're still valid here - DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) { - self.startGlobe(self.nav!) + DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) { [weak self] in + if let self = self, let nav = self.nav { + self.startGlobe(nav) + } } } @@ -64,19 +78,36 @@ class StartupShutdownTestCase: MaplyTestCase { testCase.setUpWithMap(mapViewController!) // Shut it down in a bit - DispatchQueue.main.asyncAfter(deadline: .now() + 2.0) { - self.stopMap() + DispatchQueue.main.asyncAfter(deadline: .now() + 2.0) { [weak self] in + if let self = self { + self.stopMap() + } } } func stopMap() { // globeViewController?.teardown() + testCase.stop() + + // If the user backed out on their own, stop + if let nav = self.nav { + if (globeViewController != nil && nav.topViewController !== globeViewController) || + (mapViewController != nil && nav.topViewController !== mapViewController) { + return; + } + } + mapViewController?.navigationController?.popViewController(animated: true) // Start it back up again in a bit // Note: Check to see if we're still valid here - DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) { - self.startMap(self.nav!) + DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) { [weak self] in + if let self = self { + self.startMap(self.nav!) + } } } + + override func stop() { + } } diff --git a/ios/apps/AutoTester/AutoTester/testCases/TextureVectorTestCase.m b/ios/apps/AutoTester/AutoTester/testCases/TextureVectorTestCase.m index 063890452e..e776f883d4 100644 --- a/ios/apps/AutoTester/AutoTester/testCases/TextureVectorTestCase.m +++ b/ios/apps/AutoTester/AutoTester/testCases/TextureVectorTestCase.m @@ -3,7 +3,7 @@ // AutoTester // // Created by Steve Gifford on 11/2/16. -// Copyright © 2016-2017 mousebird consulting. +// Copyright © 2016-2021 mousebird consulting. // #import "TextureVectorTestCase.h" @@ -18,7 +18,7 @@ - (instancetype)init { if (self = [super init]) { self.name = @"Textured Vectors"; - self.implementations = MaplyTestCaseImplementationGlobe; + self.implementations = MaplyTestCaseImplementationGlobe | MaplyTestCaseImplementationMap; } return self; @@ -34,9 +34,15 @@ - (void) overlayCountries: (MaplyBaseViewController*) baseVC globeMode:(bool)glo NSMutableArray *tessObjs = [NSMutableArray array]; NSArray * paths = [[NSBundle mainBundle] pathsForResourcesOfType:@"geojson" inDirectory:nil]; + // Sort so that the results don't depend on the bundle ordering + NSArray * sortedPaths = [paths sortedArrayUsingDescriptors:@[ + [NSSortDescriptor sortDescriptorWithKey:@"" + ascending:NO // first 20 are slow, use the last 20 + selector:@selector(localizedStandardCompare:)] + ]]; int count = 0; // Work through the individual GeoJSON files - for (NSString* fileName in paths) { + for (NSString* fileName in sortedPaths) { NSLog(@"Loading %@...", fileName); if (count++ > 20) break; @@ -55,11 +61,11 @@ - (void) overlayCountries: (MaplyBaseViewController*) baseVC globeMode:(bool)glo vecObj.attributes[kMaplyVecCenterX] = @(center.x); vecObj.attributes[kMaplyVecCenterY] = @(center.y); + float thisClipGridLon = ClipGridSize; if (globeMode) { // We adjust the grid clipping size based on the latitude // This helps a lot near the poles. Otherwise we're way oversampling - float thisClipGridLon = ClipGridSize; if (ABS(center.y) > 60.0/180.0 * M_PI) thisClipGridLon *= 4.0; else if (ABS(center.y) > 45.0/180.0 * M_PI) @@ -67,11 +73,10 @@ - (void) overlayCountries: (MaplyBaseViewController*) baseVC globeMode:(bool)glo // We clip the vector to a grid and then tesselate the results // This forms the vector closer to the globe, make it look nicer - tessObj = [[vecObj clipToGrid:CGSizeMake(thisClipGridLon, ClipGridSize)] tesselate]; - } else { - tessObj = [vecObj tesselate]; } + tessObj = [[vecObj clipToGrid:CGSizeMake(thisClipGridLon, ClipGridSize)] tesselate]; + // Don't add them yet, it's more efficient later if (tessObj) [tessObjs addObject:tessObj]; @@ -88,7 +93,7 @@ - (void) overlayCountries: (MaplyBaseViewController*) baseVC globeMode:(bool)glo // We'll apply this texture when filled kMaplyVecTexture: dotsTexture, // The texture is applied with a tanget plane from the center - kMaplyVecTextureProjection: kMaplyProjectionTangentPlane, + kMaplyVecTextureProjection: globeMode ? kMaplyProjectionTangentPlane : kMaplyProjectionNone, // The texture coordinates will be scaled like so kMaplyVecTexScaleX: @(6.0), kMaplyVecTexScaleY: @(6.0), @@ -96,7 +101,7 @@ - (void) overlayCountries: (MaplyBaseViewController*) baseVC globeMode:(bool)glo }]; // Turn this on to see the tessellation over the top. Good for debugging - // [baseVC addVectors:tessObjs desc:@{kMaplyDrawPriority: @(kMaplyVectorDrawPriorityDefault+1000)} mode:MaplyThreadCurrent]; + //[baseVC addVectors:tessObjs desc:@{kMaplyDrawPriority: @(kMaplyVectorDrawPriorityDefault+1000)} mode:MaplyThreadCurrent]; } - (void)setupTexture:(MaplyBaseViewController *)viewC diff --git a/ios/apps/AutoTester/AutoTester/testCases/VectorMBTilesTestCase.swift b/ios/apps/AutoTester/AutoTester/testCases/VectorMBTilesTestCase.swift index f38c1e6b5f..4c9a44c734 100644 --- a/ios/apps/AutoTester/AutoTester/testCases/VectorMBTilesTestCase.swift +++ b/ios/apps/AutoTester/AutoTester/testCases/VectorMBTilesTestCase.swift @@ -35,7 +35,6 @@ class VectorMBTilesTestCase: MaplyTestCase { sampleParams.singleLevel = true sampleParams.coverPoles = false sampleParams.edgeMatching = false - sampleParams.minZoom = 0 sampleParams.maxZoom = tileFetcher.maxZoom() // This parses the actual vector data and turns it into geometry diff --git a/ios/apps/AutoTester/AutoTester/testCases/VectorsTestCase.m b/ios/apps/AutoTester/AutoTester/testCases/VectorsTestCase.m index 2217009872..60b8f258f9 100644 --- a/ios/apps/AutoTester/AutoTester/testCases/VectorsTestCase.m +++ b/ios/apps/AutoTester/AutoTester/testCases/VectorsTestCase.m @@ -43,11 +43,16 @@ - (void) overlayCountries: (MaplyBaseViewController*) baseVC kMaplyVecWidth: @(4.0)}; NSArray * paths = [[NSBundle mainBundle] pathsForResourcesOfType:@"geojson" inDirectory:nil]; for (NSString* fileName in paths) { + // We only want the three letter countries + NSString *baseName = [[fileName lastPathComponent] stringByDeletingPathExtension]; + if ([baseName length] != 3) + continue; NSData *jsonData = [NSData dataWithContentsOfFile:fileName]; if (jsonData) { MaplyVectorObject *wgVecObj = [MaplyVectorObject VectorObjectFromGeoJSON:jsonData]; if (wgVecObj) { + NSLog(@"Loading vector %@",fileName); NSString *vecName = [[wgVecObj attributes] objectForKey:@"ADMIN"]; wgVecObj.attributes[@"title"] = vecName; wgVecObj.selectable = true; diff --git a/ios/apps/AutoTester/AutoTester/testCases/WMSTestCase.m b/ios/apps/AutoTester/AutoTester/testCases/WMSTestCase.m index e3a03314de..b6e972edfe 100644 --- a/ios/apps/AutoTester/AutoTester/testCases/WMSTestCase.m +++ b/ios/apps/AutoTester/AutoTester/testCases/WMSTestCase.m @@ -108,7 +108,6 @@ - (bool)startWMSLayerBaseURL:(NSString *)baseURL xml:(DDXMLDocument *)XMLDocumen sampleParams.coordSys = coordSys; sampleParams.coverPoles = false; sampleParams.edgeMatching = false; - sampleParams.minZoom = tileSource.minZoom; sampleParams.maxZoom = tileSource.maxZoom; sampleParams.singleLevel = true; diff --git a/ios/apps/AutoTester/AutoTester/testCases/WideVectorsTestCase.m b/ios/apps/AutoTester/AutoTester/testCases/WideVectorsTestCase.m index 9620fa8996..11a866f3fc 100644 --- a/ios/apps/AutoTester/AutoTester/testCases/WideVectorsTestCase.m +++ b/ios/apps/AutoTester/AutoTester/testCases/WideVectorsTestCase.m @@ -68,37 +68,51 @@ - (void) loadShapeFile: (MaplyBaseViewController*) baseViewC }); } -- (NSArray *)addGeoJson:(NSString*)name dashPattern:(NSArray*)dashPattern width:(CGFloat)width viewC:(MaplyBaseViewController *)baseViewC +- (NSArray *)addGeoJson:(NSString*)name + dashPattern:(NSArray*)dashPattern + width:(CGFloat)width + edge:(double)edge + simple:(bool)simple + viewC:(MaplyBaseViewController *)baseViewC { - MaplyLinearTextureBuilder *lineTexBuilder = [[MaplyLinearTextureBuilder alloc] init]; - [lineTexBuilder setPattern:dashPattern]; - UIImage *lineImage = [lineTexBuilder makeImage]; - MaplyTexture *lineTexture = [baseViewC addTexture:lineImage - imageFormat:MaplyImageIntRGBA - wrapFlags:MaplyImageWrapY - mode:MaplyThreadCurrent]; - + MaplyTexture *lineTexture = nil; + if (dashPattern) { + MaplyLinearTextureBuilder *lineTexBuilder = [[MaplyLinearTextureBuilder alloc] init]; + [lineTexBuilder setPattern:dashPattern]; + UIImage *lineImage = [lineTexBuilder makeImage]; + lineTexture = [baseViewC addTexture:lineImage + imageFormat:MaplyImageIntRGBA + wrapFlags:MaplyImageWrapY + mode:MaplyThreadCurrent]; + } + NSString *path = [[NSBundle mainBundle] pathForResource:name ofType:nil]; if(path) { NSData *data = [NSData dataWithContentsOfFile:path]; MaplyVectorObject *vecObj = [[MaplyVectorObject alloc] initWithGeoJSON:data]; if(vecObj) { [vecObj subdivideToGlobe:0.0001]; - MaplyComponentObject *obj1 = [baseViewC addWideVectors:@[vecObj] - desc: @{kMaplyColor: [UIColor colorWithRed:1 green:0 blue:0 alpha:1.0], - kMaplyFilled: @NO, - kMaplyEnable: @YES, - kMaplyFade: @0, - kMaplyDrawPriority: @(kMaplyVectorDrawPriorityDefault + 1), - kMaplyVecCentered: @YES, - kMaplyVecTexture: lineTexture, - kMaplyWideVecEdgeFalloff: @(1.0), - kMaplyWideVecJoinType: kMaplyWideVecMiterJoin, - kMaplyWideVecCoordType: kMaplyWideVecCoordTypeScreen, - // More than 10 degrees need a bevel join - kMaplyWideVecMiterLimit: @(10), - kMaplyVecWidth: @(width)} - mode:MaplyThreadCurrent]; + + //NSMutableDictionary *wideDesc = [NSMutableDictionary dictionaryWithDictionary:@{ + NSDictionary *wideDesc = @{ + kMaplyColor: [UIColor colorWithRed:1 green:0 blue:0 alpha:1.0], + kMaplyFilled: @NO, + kMaplyEnable: @YES, + kMaplyFade: @0, + kMaplyDrawPriority: @(kMaplyVectorDrawPriorityDefault + 1), + kMaplyVecCentered: @YES, + kMaplyVecTexture: lineTexture ? lineTexture : [NSNull null], + kMaplyWideVecEdgeFalloff:@(edge), + kMaplyWideVecJoinType: kMaplyWideVecMiterJoin, + kMaplyWideVecCoordType: kMaplyWideVecCoordTypeScreen, + kMaplyWideVecCoordType: kMaplyWideVecCoordTypeScreen, + kMaplyWideVecOffset: @(10.0), + kMaplyWideVecMiterLimit: @(10.0), // More than 10 degrees need a bevel join + kMaplyVecWidth: @(width), + kMaplyWideVecImpl: simple ? kMaplyWideVecImplPerf : kMaplyWideVecImpl, + }; + + MaplyComponentObject *obj1 = [baseViewC addWideVectors:@[vecObj] desc: wideDesc mode:MaplyThreadCurrent]; MaplyComponentObject *obj2 = [baseViewC addVectors:@[vecObj] desc: @{kMaplyColor: [UIColor blackColor], kMaplyFilled: @NO, @@ -116,13 +130,24 @@ - (NSArray *)addGeoJson:(NSString*)name dashPattern:(NSArray*)dashPattern width: return nil; } +- (NSArray *)addGeoJson:(NSString*)name + dashPattern:(NSArray*)dashPattern + width:(CGFloat)width + viewC:(MaplyBaseViewController *)baseViewC +{ + return [self addGeoJson:name dashPattern:dashPattern width:width edge:1.0 simple:false viewC:baseViewC]; +} + - (NSArray *)addGeoJson:(NSString*)name viewC:(MaplyBaseViewController *)viewC { -// return [self addGeoJson:name dashPattern:@[@8, @8] width:4 viewC:viewC]; - return [self addGeoJson:name dashPattern:@[@8, @8] width:20 viewC:viewC]; + return [self addGeoJson:name dashPattern:@[@8, @8] width:4 viewC:viewC]; +// return [self addGeoJson:name dashPattern:@[@8, @8] width:100 viewC:viewC]; } -- (NSArray *)addWideVectors:(MaplyVectorObject *)vecObj baseViewC: (MaplyBaseViewController*) baseViewC dashedLineTex: (MaplyTexture*) dashedLineTex filledLineTex: (MaplyTexture*) filledLineTex +- (NSArray *)addWideVectors:(MaplyVectorObject *)vecObj + baseViewC:(MaplyBaseViewController*)baseViewC + dashedLineTex:(MaplyTexture*)dashedLineTex + filledLineTex:(MaplyTexture*)filledLineTex { UIColor *color = [UIColor blueColor]; float fade = 0.25; @@ -140,7 +165,7 @@ - (NSArray *)addWideVectors:(MaplyVectorObject *)vecObj baseViewC: (MaplyBaseVie kMaplyVecTexture: filledLineTex, kMaplyWideVecCoordType: kMaplyWideVecCoordTypeScreen, kMaplyWideVecJoinType: kMaplyWideVecMiterJoin, - kMaplyWideVecMiterLimit: @(1.01), +// kMaplyWideVecMiterLimit: @(1.01), kMaplyWideVecTexRepeatLen: @(8), kMaplyMaxVis: @(0.00032424763776361942), kMaplyMinVis: @(0.00011049506429117173) @@ -153,7 +178,7 @@ - (NSArray *)addWideVectors:(MaplyVectorObject *)vecObj baseViewC: (MaplyBaseVie kMaplyVecWidth: @(10.0/6371000), kMaplyWideVecCoordType: kMaplyWideVecCoordTypeReal, kMaplyWideVecJoinType: kMaplyWideVecMiterJoin, - kMaplyWideVecMiterLimit: @(1.01), +// kMaplyWideVecMiterLimit: @(1.01), // Repeat every 10m kMaplyWideVecTexRepeatLen: @(10/6371000.f), kMaplyMaxVis: @(0.00011049506429117173), @@ -212,19 +237,20 @@ - (NSArray *)addWideVectors:(MaplyVectorObject *)vecObj baseViewC: (MaplyBaseVie - (void)wideLineTest:(MaplyBaseViewController *)viewC { - [self addGeoJson:@"sawtooth.geojson" viewC:viewC]; + [self addGeoJson:@"sawtooth.geojson" dashPattern:nil width:50.0 edge:20.0 simple:false viewC:viewC]; [self addGeoJson:@"moving-lawn.geojson" viewC:viewC]; [self addGeoJson:@"spiral.geojson" viewC:viewC]; - [self addGeoJson:@"square.geojson" dashPattern:@[@2, @2] width:1.0 viewC:viewC]; + [self addGeoJson:@"square.geojson" dashPattern:@[@2, @2] width:10.0 viewC:viewC]; [self addGeoJson:@"track.geojson" viewC:viewC]; - [self addGeoJson:@"uturn2.geojson" dashPattern:@[@16, @16] width:40 viewC:viewC]; - +// [self addGeoJson:@"uturn2.geojson" dashPattern:@[@16, @16] width:40 viewC:viewC]; + [self addGeoJson:@"USA.geojson" viewC:viewC]; // [self addGeoJson:@"testJson.json" viewC:viewC]; // [self addGeoJson:@"straight.geojson"]; // [self addGeoJson:@"uturn.geojson"]; + } diff --git a/ios/apps/AutoTester/Icon.sketch b/ios/apps/AutoTester/Icon.sketch new file mode 100644 index 0000000000..3e9ed87b97 Binary files /dev/null and b/ios/apps/AutoTester/Icon.sketch differ diff --git a/ios/apps/AutoTester/icons/Icon.png b/ios/apps/AutoTester/icons/Icon.png new file mode 100644 index 0000000000..7b7855f54b Binary files /dev/null and b/ios/apps/AutoTester/icons/Icon.png differ diff --git a/ios/apps/AutoTester/icons/icon_152.png b/ios/apps/AutoTester/icons/icon_152.png new file mode 100644 index 0000000000..2299bb7944 Binary files /dev/null and b/ios/apps/AutoTester/icons/icon_152.png differ diff --git a/ios/apps/AutoTester/icons/icon_167.png b/ios/apps/AutoTester/icons/icon_167.png new file mode 100644 index 0000000000..5a0c1a33e6 Binary files /dev/null and b/ios/apps/AutoTester/icons/icon_167.png differ diff --git a/ios/apps/AutoTester/icons/icon_20pt.png b/ios/apps/AutoTester/icons/icon_20pt.png new file mode 100644 index 0000000000..d419830178 Binary files /dev/null and b/ios/apps/AutoTester/icons/icon_20pt.png differ diff --git a/ios/apps/AutoTester/icons/icon_20pt@2x.png b/ios/apps/AutoTester/icons/icon_20pt@2x.png new file mode 100644 index 0000000000..47bf9738cf Binary files /dev/null and b/ios/apps/AutoTester/icons/icon_20pt@2x.png differ diff --git a/ios/apps/AutoTester/icons/icon_20pt@3x.png b/ios/apps/AutoTester/icons/icon_20pt@3x.png new file mode 100644 index 0000000000..b9e98dbfa5 Binary files /dev/null and b/ios/apps/AutoTester/icons/icon_20pt@3x.png differ diff --git a/ios/apps/AutoTester/icons/icon_29pt.png b/ios/apps/AutoTester/icons/icon_29pt.png new file mode 100644 index 0000000000..9066f8b45e Binary files /dev/null and b/ios/apps/AutoTester/icons/icon_29pt.png differ diff --git a/ios/apps/AutoTester/icons/icon_29pt@2x.png b/ios/apps/AutoTester/icons/icon_29pt@2x.png new file mode 100644 index 0000000000..fab2dd8499 Binary files /dev/null and b/ios/apps/AutoTester/icons/icon_29pt@2x.png differ diff --git a/ios/apps/AutoTester/icons/icon_29pt@3x.png b/ios/apps/AutoTester/icons/icon_29pt@3x.png new file mode 100644 index 0000000000..3fc5a63907 Binary files /dev/null and b/ios/apps/AutoTester/icons/icon_29pt@3x.png differ diff --git a/ios/apps/AutoTester/icons/icon_40pt.png b/ios/apps/AutoTester/icons/icon_40pt.png new file mode 100644 index 0000000000..47bf9738cf Binary files /dev/null and b/ios/apps/AutoTester/icons/icon_40pt.png differ diff --git a/ios/apps/AutoTester/icons/icon_40pt@2x.png b/ios/apps/AutoTester/icons/icon_40pt@2x.png new file mode 100644 index 0000000000..453ab94f16 Binary files /dev/null and b/ios/apps/AutoTester/icons/icon_40pt@2x.png differ diff --git a/ios/apps/AutoTester/icons/icon_40pt@3x.png b/ios/apps/AutoTester/icons/icon_40pt@3x.png new file mode 100644 index 0000000000..1e5b98a365 Binary files /dev/null and b/ios/apps/AutoTester/icons/icon_40pt@3x.png differ diff --git a/ios/apps/AutoTester/icons/icon_60pt@2x.png b/ios/apps/AutoTester/icons/icon_60pt@2x.png new file mode 100644 index 0000000000..1e5b98a365 Binary files /dev/null and b/ios/apps/AutoTester/icons/icon_60pt@2x.png differ diff --git a/ios/apps/AutoTester/icons/icon_60pt@3x.png b/ios/apps/AutoTester/icons/icon_60pt@3x.png new file mode 100644 index 0000000000..7226d3e94b Binary files /dev/null and b/ios/apps/AutoTester/icons/icon_60pt@3x.png differ diff --git a/ios/apps/AutoTester/icons/icon_76pt.png b/ios/apps/AutoTester/icons/icon_76pt.png new file mode 100644 index 0000000000..249de7056a Binary files /dev/null and b/ios/apps/AutoTester/icons/icon_76pt.png differ diff --git a/ios/apps/AutoTester/icons/icon_76pt@2x.png b/ios/apps/AutoTester/icons/icon_76pt@2x.png new file mode 100644 index 0000000000..9b1c45a31b Binary files /dev/null and b/ios/apps/AutoTester/icons/icon_76pt@2x.png differ diff --git a/ios/apps/AutoTester/icons/icon_83.5@2x.png b/ios/apps/AutoTester/icons/icon_83.5@2x.png new file mode 100644 index 0000000000..cb31df0bca Binary files /dev/null and b/ios/apps/AutoTester/icons/icon_83.5@2x.png differ diff --git a/ios/apps/AutoTester/resources/SE_Basic.json b/ios/apps/AutoTester/resources/SE_Basic.json index 6cbce5b366..8f4bad7f69 100644 --- a/ios/apps/AutoTester/resources/SE_Basic.json +++ b/ios/apps/AutoTester/resources/SE_Basic.json @@ -19,10 +19,10 @@ "sources": { "openmaptiles": { "type": "vector", - "url": "https://free.tilehosting.com/data/v3.json?key=8iZUKgsBTIFhFIZjA5lm" + "url": "https://free.tilehosting.com/data/v3.json?key=MapTilerKey" } }, - "glyphs": "https://free.tilehosting.com/fonts/{fontstack}/{range}.pbf?key=8iZUKgsBTIFhFIZjA5lm", + "glyphs": "https://free.tilehosting.com/fonts/{fontstack}/{range}.pbf?key=MapTilerKey", "layers": [ { "id": "background", diff --git a/ios/library/WhirlyGlobe-MaplyComponent/WhirlyGlobeMaplyComponent.xcodeproj/project.pbxproj b/ios/library/WhirlyGlobe-MaplyComponent/WhirlyGlobeMaplyComponent.xcodeproj/project.pbxproj index b2306f5ced..eb730e02c7 100644 --- a/ios/library/WhirlyGlobe-MaplyComponent/WhirlyGlobeMaplyComponent.xcodeproj/project.pbxproj +++ b/ios/library/WhirlyGlobe-MaplyComponent/WhirlyGlobeMaplyComponent.xcodeproj/project.pbxproj @@ -129,6 +129,8 @@ 2B4A816A25391A0D0016618C /* lodepng.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 2B4A816825391A0D0016618C /* lodepng.cpp */; }; 2B4B63A5236102DA0008C8C1 /* MaplyGlobeRenderController.h in Headers */ = {isa = PBXBuildFile; fileRef = 2B4B63A4236102DA0008C8C1 /* MaplyGlobeRenderController.h */; }; 2B4B63A7236102EC0008C8C1 /* MaplyGlobeRenderController.mm in Sources */ = {isa = PBXBuildFile; fileRef = 2B4B63A6236102EC0008C8C1 /* MaplyGlobeRenderController.mm */; }; + 2B50CEB025798F3200BD4004 /* RawPNGImage.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 2B50CEAF25798F3200BD4004 /* RawPNGImage.cpp */; }; + 2B50CEB425798F4800BD4004 /* RawPNGImage.h in Headers */ = {isa = PBXBuildFile; fileRef = 2B50CEB325798F4800BD4004 /* RawPNGImage.h */; }; 2B526BDC240DE04B00647336 /* MetalPerformanceShaders.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 2B526BDB240DE04B00647336 /* MetalPerformanceShaders.framework */; }; 2B541C171ECFAA2300EC35A0 /* MaplyRenderTarget.h in Headers */ = {isa = PBXBuildFile; fileRef = 2B541C161ECFAA2300EC35A0 /* MaplyRenderTarget.h */; }; 2B60F43C24523B5800CF9339 /* MapboxVectorTiles.mm in Sources */ = {isa = PBXBuildFile; fileRef = 2BA827C8225E6F2100324594 /* MapboxVectorTiles.mm */; }; @@ -453,7 +455,7 @@ 2B84ED1E1F83FC9F00B34D73 /* libz.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = 2BE53AC41D249E0600B60FAD /* libz.tbd */; }; 2B8796B721FFB2DE00EF801D /* Platform.mm in Sources */ = {isa = PBXBuildFile; fileRef = 2B446B2A21F7A4820078A975 /* Platform.mm */; }; 2B8796C821FFC57700EF801D /* ShapeReader.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 2B446B8721FB97D50078A975 /* ShapeReader.cpp */; }; - 2B8796CE21FFC59D00EF801D /* Dictinary_NSDictionary.mm in Sources */ = {isa = PBXBuildFile; fileRef = 2B23133D21FA91A4006AA344 /* Dictinary_NSDictionary.mm */; }; + 2B8796CE21FFC59D00EF801D /* Dictionary_NSDictionary.mm in Sources */ = {isa = PBXBuildFile; fileRef = 2B23133D21FA91A4006AA344 /* Dictionary_NSDictionary.mm */; }; 2B8796CF21FFC59D00EF801D /* RawData_NSData.mm in Sources */ = {isa = PBXBuildFile; fileRef = 2B23133521F93969006AA344 /* RawData_NSData.mm */; }; 2B8796D021FFC59D00EF801D /* UIImage+Stuff.mm in Sources */ = {isa = PBXBuildFile; fileRef = 2B82B5491E82E2490095FB14 /* UIImage+Stuff.mm */; }; 2B8796D121FFC59D00EF801D /* NSDictionary+Stuff.m in Sources */ = {isa = PBXBuildFile; fileRef = 2B82B54A1E82E2490095FB14 /* NSDictionary+Stuff.m */; }; @@ -630,6 +632,10 @@ 2BC90D6622405DD200D8B606 /* Sun.h in Headers */ = {isa = PBXBuildFile; fileRef = 2BC90D6422405DD200D8B606 /* Sun.h */; }; 2BC90D6922405DE700D8B606 /* Sun.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 2BC90D6722405DE700D8B606 /* Sun.cpp */; }; 2BC90D6A22405DE700D8B606 /* Moon.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 2BC90D6822405DE700D8B606 /* Moon.cpp */; }; + 2BD645E125F0574B00727680 /* LinearTextBuilder.h in Headers */ = {isa = PBXBuildFile; fileRef = 2BD645E025F0574B00727680 /* LinearTextBuilder.h */; }; + 2BD645E525F0576900727680 /* LinearTextBuilder.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 2BD645E425F0576900727680 /* LinearTextBuilder.cpp */; }; + 2BD645EF25F1AF8C00727680 /* VectorOffset.h in Headers */ = {isa = PBXBuildFile; fileRef = 2BD645EE25F1AF8C00727680 /* VectorOffset.h */; }; + 2BD645F325F1AF9B00727680 /* VectorOffset.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 2BD645F225F1AF9A00727680 /* VectorOffset.cpp */; }; 2BD6FA64254B478000FD8374 /* DictionaryC.h in Headers */ = {isa = PBXBuildFile; fileRef = 2BD6FA63254B477F00FD8374 /* DictionaryC.h */; }; 2BD6FA68254B47B000FD8374 /* DictionaryC.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 2BD6FA67254B47AF00FD8374 /* DictionaryC.cpp */; }; 2BE1E7392208A97100815D9C /* MaplyPinchDelegate.mm in Sources */ = {isa = PBXBuildFile; fileRef = 2B361DED21233CBE0074D06D /* MaplyPinchDelegate.mm */; }; @@ -900,6 +906,90 @@ 313363AB253E5A2B007C2F27 /* WorkRegion_private.h in Headers */ = {isa = PBXBuildFile; fileRef = 313363AA253E5A24007C2F27 /* WorkRegion_private.h */; }; 315082CA254CD29000A0A2B2 /* VectorTilePBFParser.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 315082C9254CD29000A0A2B2 /* VectorTilePBFParser.cpp */; }; 315082D0254CD2BF00A0A2B2 /* VectorTilePBFParser.h in Headers */ = {isa = PBXBuildFile; fileRef = 315082CF254CD2BF00A0A2B2 /* VectorTilePBFParser.h */; }; + 31833112259112BA005FEF70 /* LambertConformalConic.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 318330BF259112BA005FEF70 /* LambertConformalConic.hpp */; }; + 31833113259112BA005FEF70 /* MGRS.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 318330C0259112BA005FEF70 /* MGRS.hpp */; }; + 31833114259112BA005FEF70 /* LocalCartesian.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 318330C1259112BA005FEF70 /* LocalCartesian.hpp */; }; + 31833115259112BA005FEF70 /* GeodesicLine.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 318330C2259112BA005FEF70 /* GeodesicLine.hpp */; }; + 31833116259112BA005FEF70 /* GravityCircle.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 318330C3259112BA005FEF70 /* GravityCircle.hpp */; }; + 31833117259112BA005FEF70 /* UTMUPS.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 318330C4259112BA005FEF70 /* UTMUPS.hpp */; }; + 31833118259112BA005FEF70 /* Ellipsoid.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 318330C5259112BA005FEF70 /* Ellipsoid.hpp */; }; + 31833119259112BA005FEF70 /* PolarStereographic.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 318330C6259112BA005FEF70 /* PolarStereographic.hpp */; }; + 3183311A259112BA005FEF70 /* AlbersEqualArea.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 318330C7259112BA005FEF70 /* AlbersEqualArea.hpp */; }; + 3183311B259112BA005FEF70 /* Utility.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 318330C8259112BA005FEF70 /* Utility.hpp */; }; + 3183311C259112BA005FEF70 /* MagneticModel.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 318330C9259112BA005FEF70 /* MagneticModel.hpp */; }; + 3183311D259112BA005FEF70 /* SphericalHarmonic1.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 318330CA259112BA005FEF70 /* SphericalHarmonic1.hpp */; }; + 3183311E259112BA005FEF70 /* TransverseMercator.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 318330CB259112BA005FEF70 /* TransverseMercator.hpp */; }; + 3183311F259112BA005FEF70 /* TransverseMercatorExact.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 318330CC259112BA005FEF70 /* TransverseMercatorExact.hpp */; }; + 31833120259112BA005FEF70 /* Config.h in Headers */ = {isa = PBXBuildFile; fileRef = 318330CD259112BA005FEF70 /* Config.h */; }; + 31833121259112BA005FEF70 /* SphericalHarmonic2.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 318330CE259112BA005FEF70 /* SphericalHarmonic2.hpp */; }; + 31833122259112BA005FEF70 /* Geoid.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 318330CF259112BA005FEF70 /* Geoid.hpp */; }; + 31833123259112BA005FEF70 /* Rhumb.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 318330D0259112BA005FEF70 /* Rhumb.hpp */; }; + 31833124259112BA005FEF70 /* SphericalHarmonic.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 318330D1259112BA005FEF70 /* SphericalHarmonic.hpp */; }; + 31833125259112BA005FEF70 /* NormalGravity.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 318330D2259112BA005FEF70 /* NormalGravity.hpp */; }; + 31833126259112BA005FEF70 /* SphericalEngine.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 318330D3259112BA005FEF70 /* SphericalEngine.hpp */; }; + 31833127259112BA005FEF70 /* GeodesicExact.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 318330D4259112BA005FEF70 /* GeodesicExact.hpp */; }; + 31833128259112BA005FEF70 /* GARS.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 318330D5259112BA005FEF70 /* GARS.hpp */; }; + 31833129259112BA005FEF70 /* Geocentric.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 318330D6259112BA005FEF70 /* Geocentric.hpp */; }; + 3183312A259112BA005FEF70 /* Accumulator.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 318330D7259112BA005FEF70 /* Accumulator.hpp */; }; + 3183312B259112BA005FEF70 /* PolygonArea.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 318330D8259112BA005FEF70 /* PolygonArea.hpp */; }; + 3183312C259112BA005FEF70 /* Math.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 318330D9259112BA005FEF70 /* Math.hpp */; }; + 3183312D259112BA005FEF70 /* Geodesic.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 318330DA259112BA005FEF70 /* Geodesic.hpp */; }; + 3183312E259112BA005FEF70 /* AzimuthalEquidistant.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 318330DB259112BA005FEF70 /* AzimuthalEquidistant.hpp */; }; + 3183312F259112BA005FEF70 /* Constants.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 318330DC259112BA005FEF70 /* Constants.hpp */; }; + 31833130259112BA005FEF70 /* OSGB.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 318330DD259112BA005FEF70 /* OSGB.hpp */; }; + 31833131259112BA005FEF70 /* GeoCoords.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 318330DE259112BA005FEF70 /* GeoCoords.hpp */; }; + 31833132259112BA005FEF70 /* Config-ac.h.in in Resources */ = {isa = PBXBuildFile; fileRef = 318330DF259112BA005FEF70 /* Config-ac.h.in */; }; + 31833133259112BA005FEF70 /* CassiniSoldner.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 318330E0259112BA005FEF70 /* CassiniSoldner.hpp */; }; + 31833134259112BA005FEF70 /* MagneticCircle.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 318330E1259112BA005FEF70 /* MagneticCircle.hpp */; }; + 31833135259112BA005FEF70 /* NearestNeighbor.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 318330E2259112BA005FEF70 /* NearestNeighbor.hpp */; }; + 31833136259112BA005FEF70 /* DMS.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 318330E3259112BA005FEF70 /* DMS.hpp */; }; + 31833137259112BA005FEF70 /* Georef.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 318330E4259112BA005FEF70 /* Georef.hpp */; }; + 31833138259112BA005FEF70 /* Gnomonic.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 318330E5259112BA005FEF70 /* Gnomonic.hpp */; }; + 31833139259112BA005FEF70 /* GravityModel.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 318330E6259112BA005FEF70 /* GravityModel.hpp */; }; + 3183313A259112BA005FEF70 /* GeodesicLineExact.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 318330E7259112BA005FEF70 /* GeodesicLineExact.hpp */; }; + 3183313B259112BA005FEF70 /* CircularEngine.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 318330E8259112BA005FEF70 /* CircularEngine.hpp */; }; + 3183313C259112BA005FEF70 /* Geohash.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 318330E9259112BA005FEF70 /* Geohash.hpp */; }; + 3183313D259112BA005FEF70 /* EllipticFunction.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 318330EA259112BA005FEF70 /* EllipticFunction.hpp */; }; + 3183313E259112BA005FEF70 /* DMS.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 318330EC259112BA005FEF70 /* DMS.cpp */; }; + 3183313F259112BA005FEF70 /* Georef.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 318330ED259112BA005FEF70 /* Georef.cpp */; }; + 31833140259112BA005FEF70 /* Gnomonic.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 318330EE259112BA005FEF70 /* Gnomonic.cpp */; }; + 31833141259112BA005FEF70 /* GravityModel.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 318330EF259112BA005FEF70 /* GravityModel.cpp */; }; + 31833142259112BA005FEF70 /* GeodesicLineExact.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 318330F0259112BA005FEF70 /* GeodesicLineExact.cpp */; }; + 31833143259112BA005FEF70 /* CircularEngine.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 318330F1259112BA005FEF70 /* CircularEngine.cpp */; }; + 31833144259112BA005FEF70 /* Geohash.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 318330F2259112BA005FEF70 /* Geohash.cpp */; }; + 31833145259112BA005FEF70 /* EllipticFunction.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 318330F3259112BA005FEF70 /* EllipticFunction.cpp */; }; + 31833146259112BA005FEF70 /* PolygonArea.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 318330F4259112BA005FEF70 /* PolygonArea.cpp */; }; + 31833147259112BA005FEF70 /* Accumulator.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 318330F5259112BA005FEF70 /* Accumulator.cpp */; }; + 31833148259112BA005FEF70 /* Math.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 318330F6259112BA005FEF70 /* Math.cpp */; }; + 31833149259112BA005FEF70 /* Geodesic.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 318330F7259112BA005FEF70 /* Geodesic.cpp */; }; + 3183314A259112BA005FEF70 /* AzimuthalEquidistant.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 318330F8259112BA005FEF70 /* AzimuthalEquidistant.cpp */; }; + 3183314B259112BA005FEF70 /* OSGB.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 318330F9259112BA005FEF70 /* OSGB.cpp */; }; + 3183314C259112BA005FEF70 /* GeoCoords.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 318330FA259112BA005FEF70 /* GeoCoords.cpp */; }; + 3183314D259112BA005FEF70 /* CassiniSoldner.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 318330FB259112BA005FEF70 /* CassiniSoldner.cpp */; }; + 3183314E259112BA005FEF70 /* MagneticCircle.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 318330FC259112BA005FEF70 /* MagneticCircle.cpp */; }; + 3183314F259112BA005FEF70 /* Rhumb.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 318330FD259112BA005FEF70 /* Rhumb.cpp */; }; + 31833150259112BA005FEF70 /* NormalGravity.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 318330FE259112BA005FEF70 /* NormalGravity.cpp */; }; + 31833151259112BA005FEF70 /* SphericalEngine.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 318330FF259112BA005FEF70 /* SphericalEngine.cpp */; }; + 31833152259112BA005FEF70 /* GeodesicExact.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 31833100259112BA005FEF70 /* GeodesicExact.cpp */; }; + 31833153259112BA005FEF70 /* GeodesicExactC4.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 31833101259112BA005FEF70 /* GeodesicExactC4.cpp */; }; + 31833154259112BA005FEF70 /* GARS.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 31833102259112BA005FEF70 /* GARS.cpp */; }; + 31833155259112BA005FEF70 /* Geocentric.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 31833103259112BA005FEF70 /* Geocentric.cpp */; }; + 31833156259112BA005FEF70 /* LambertConformalConic.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 31833104259112BA005FEF70 /* LambertConformalConic.cpp */; }; + 31833157259112BA005FEF70 /* MGRS.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 31833105259112BA005FEF70 /* MGRS.cpp */; }; + 31833158259112BA005FEF70 /* GravityCircle.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 31833106259112BA005FEF70 /* GravityCircle.cpp */; }; + 31833159259112BA005FEF70 /* LocalCartesian.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 31833107259112BA005FEF70 /* LocalCartesian.cpp */; }; + 3183315A259112BA005FEF70 /* GeodesicLine.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 31833108259112BA005FEF70 /* GeodesicLine.cpp */; }; + 3183315B259112BA005FEF70 /* PolarStereographic.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 31833109259112BA005FEF70 /* PolarStereographic.cpp */; }; + 3183315C259112BA005FEF70 /* UTMUPS.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 3183310A259112BA005FEF70 /* UTMUPS.cpp */; }; + 3183315D259112BA005FEF70 /* Ellipsoid.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 3183310B259112BA005FEF70 /* Ellipsoid.cpp */; }; + 3183315E259112BA005FEF70 /* AlbersEqualArea.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 3183310C259112BA005FEF70 /* AlbersEqualArea.cpp */; }; + 3183315F259112BA005FEF70 /* MagneticModel.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 3183310D259112BA005FEF70 /* MagneticModel.cpp */; }; + 31833160259112BA005FEF70 /* Utility.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 3183310E259112BA005FEF70 /* Utility.cpp */; }; + 31833161259112BA005FEF70 /* TransverseMercator.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 3183310F259112BA005FEF70 /* TransverseMercator.cpp */; }; + 31833162259112BA005FEF70 /* TransverseMercatorExact.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 31833110259112BA005FEF70 /* TransverseMercatorExact.cpp */; }; + 31833163259112BA005FEF70 /* Geoid.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 31833111259112BA005FEF70 /* Geoid.cpp */; }; + 31833167259114A0005FEF70 /* GeographicLib.h in Headers */ = {isa = PBXBuildFile; fileRef = 31833166259114A0005FEF70 /* GeographicLib.h */; }; + 3183316B259114B0005FEF70 /* GeographicLib.mm in Sources */ = {isa = PBXBuildFile; fileRef = 3183316A259114B0005FEF70 /* GeographicLib.mm */; }; 31872FD52550ED71002C9C73 /* maply_pb_names.h in Headers */ = {isa = PBXBuildFile; fileRef = 31872FD42550ED71002C9C73 /* maply_pb_names.h */; }; 31942FC6254B5C0A0006B499 /* maply_pb_common.c in Sources */ = {isa = PBXBuildFile; fileRef = 31942F9C254B5C0A0006B499 /* maply_pb_common.c */; }; 31942FCD254B5C0A0006B499 /* maply_pb_decode.c in Sources */ = {isa = PBXBuildFile; fileRef = 31942FA4254B5C0A0006B499 /* maply_pb_decode.c */; }; @@ -972,7 +1062,7 @@ 2B23133721F942D1006AA344 /* Dictionary.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Dictionary.h; path = ../../../../common/WhirlyGlobeLib/include/Dictionary.h; sourceTree = ""; }; 2B23133921F942E1006AA344 /* Dictionary.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = Dictionary.cpp; path = ../../../../common/WhirlyGlobeLib/src/Dictionary.cpp; sourceTree = ""; }; 2B23133B21FA919D006AA344 /* Dictionary_NSDictionary.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Dictionary_NSDictionary.h; sourceTree = ""; }; - 2B23133D21FA91A4006AA344 /* Dictinary_NSDictionary.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = Dictinary_NSDictionary.mm; sourceTree = ""; }; + 2B23133D21FA91A4006AA344 /* Dictionary_NSDictionary.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = Dictionary_NSDictionary.mm; sourceTree = ""; }; 2B2EA04C23427F88006F2F34 /* DrawableMTL.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = DrawableMTL.mm; sourceTree = ""; }; 2B2EA04E23427FB7006F2F34 /* DrawableMTL.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DrawableMTL.h; sourceTree = ""; }; 2B361DE621233CBE0074D06D /* MaplyTwoFingerTapDelegate.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = MaplyTwoFingerTapDelegate.mm; sourceTree = ""; }; @@ -1071,6 +1161,8 @@ 2B4A816825391A0D0016618C /* lodepng.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = lodepng.cpp; path = ../../../../../common/local_libs/lodepng/lodepng.cpp; sourceTree = ""; }; 2B4B63A4236102DA0008C8C1 /* MaplyGlobeRenderController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MaplyGlobeRenderController.h; sourceTree = ""; }; 2B4B63A6236102EC0008C8C1 /* MaplyGlobeRenderController.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = MaplyGlobeRenderController.mm; sourceTree = ""; }; + 2B50CEAF25798F3200BD4004 /* RawPNGImage.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = RawPNGImage.cpp; path = ../../../../common/WhirlyGlobeLib/src/RawPNGImage.cpp; sourceTree = ""; }; + 2B50CEB325798F4800BD4004 /* RawPNGImage.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = RawPNGImage.h; path = ../../../../common/WhirlyGlobeLib/include/RawPNGImage.h; sourceTree = ""; }; 2B526BDB240DE04B00647336 /* MetalPerformanceShaders.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = MetalPerformanceShaders.framework; path = Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.15.sdk/System/Library/Frameworks/MetalPerformanceShaders.framework; sourceTree = DEVELOPER_DIR; }; 2B541C161ECFAA2300EC35A0 /* MaplyRenderTarget.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MaplyRenderTarget.h; sourceTree = ""; }; 2B63C45E243E44A0002B481C /* MapboxVectorStyleSetC.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = MapboxVectorStyleSetC.h; path = ../../../../common/WhirlyGlobeLib/include/MapboxVectorStyleSetC.h; sourceTree = ""; }; @@ -1520,6 +1612,10 @@ 2BC90D6422405DD200D8B606 /* Sun.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Sun.h; path = ../../../../common/WhirlyGlobeLib/include/Sun.h; sourceTree = ""; }; 2BC90D6722405DE700D8B606 /* Sun.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = Sun.cpp; path = ../../../../common/WhirlyGlobeLib/src/Sun.cpp; sourceTree = ""; }; 2BC90D6822405DE700D8B606 /* Moon.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = Moon.cpp; path = ../../../../common/WhirlyGlobeLib/src/Moon.cpp; sourceTree = ""; }; + 2BD645E025F0574B00727680 /* LinearTextBuilder.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = LinearTextBuilder.h; path = ../../../../common/WhirlyGlobeLib/include/LinearTextBuilder.h; sourceTree = ""; }; + 2BD645E425F0576900727680 /* LinearTextBuilder.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = LinearTextBuilder.cpp; path = ../../../../common/WhirlyGlobeLib/src/LinearTextBuilder.cpp; sourceTree = ""; }; + 2BD645EE25F1AF8C00727680 /* VectorOffset.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = VectorOffset.h; path = ../../../../common/WhirlyGlobeLib/include/VectorOffset.h; sourceTree = ""; }; + 2BD645F225F1AF9A00727680 /* VectorOffset.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = VectorOffset.cpp; path = ../../../../common/WhirlyGlobeLib/src/VectorOffset.cpp; sourceTree = ""; }; 2BD6FA63254B477F00FD8374 /* DictionaryC.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = DictionaryC.h; path = ../../../../common/WhirlyGlobeLib/include/DictionaryC.h; sourceTree = ""; }; 2BD6FA67254B47AF00FD8374 /* DictionaryC.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = DictionaryC.cpp; path = ../../../../common/WhirlyGlobeLib/src/DictionaryC.cpp; sourceTree = ""; }; 2BE1E7572208F32900815D9C /* SingleLabel_iOS.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SingleLabel_iOS.h; sourceTree = ""; }; @@ -1827,6 +1923,90 @@ 313363AA253E5A24007C2F27 /* WorkRegion_private.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = WorkRegion_private.h; sourceTree = ""; }; 315082C9254CD29000A0A2B2 /* VectorTilePBFParser.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = VectorTilePBFParser.cpp; path = ../../../../common/WhirlyGlobeLib/src/VectorTilePBFParser.cpp; sourceTree = ""; }; 315082CF254CD2BF00A0A2B2 /* VectorTilePBFParser.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = VectorTilePBFParser.h; path = ../../../../common/WhirlyGlobeLib/include/VectorTilePBFParser.h; sourceTree = ""; }; + 318330BF259112BA005FEF70 /* LambertConformalConic.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = LambertConformalConic.hpp; sourceTree = ""; }; + 318330C0259112BA005FEF70 /* MGRS.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = MGRS.hpp; sourceTree = ""; }; + 318330C1259112BA005FEF70 /* LocalCartesian.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = LocalCartesian.hpp; sourceTree = ""; }; + 318330C2259112BA005FEF70 /* GeodesicLine.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = GeodesicLine.hpp; sourceTree = ""; }; + 318330C3259112BA005FEF70 /* GravityCircle.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = GravityCircle.hpp; sourceTree = ""; }; + 318330C4259112BA005FEF70 /* UTMUPS.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = UTMUPS.hpp; sourceTree = ""; }; + 318330C5259112BA005FEF70 /* Ellipsoid.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = Ellipsoid.hpp; sourceTree = ""; }; + 318330C6259112BA005FEF70 /* PolarStereographic.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = PolarStereographic.hpp; sourceTree = ""; }; + 318330C7259112BA005FEF70 /* AlbersEqualArea.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = AlbersEqualArea.hpp; sourceTree = ""; }; + 318330C8259112BA005FEF70 /* Utility.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = Utility.hpp; sourceTree = ""; }; + 318330C9259112BA005FEF70 /* MagneticModel.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = MagneticModel.hpp; sourceTree = ""; }; + 318330CA259112BA005FEF70 /* SphericalHarmonic1.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = SphericalHarmonic1.hpp; sourceTree = ""; }; + 318330CB259112BA005FEF70 /* TransverseMercator.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = TransverseMercator.hpp; sourceTree = ""; }; + 318330CC259112BA005FEF70 /* TransverseMercatorExact.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = TransverseMercatorExact.hpp; sourceTree = ""; }; + 318330CD259112BA005FEF70 /* Config.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Config.h; sourceTree = ""; }; + 318330CE259112BA005FEF70 /* SphericalHarmonic2.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = SphericalHarmonic2.hpp; sourceTree = ""; }; + 318330CF259112BA005FEF70 /* Geoid.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = Geoid.hpp; sourceTree = ""; }; + 318330D0259112BA005FEF70 /* Rhumb.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = Rhumb.hpp; sourceTree = ""; }; + 318330D1259112BA005FEF70 /* SphericalHarmonic.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = SphericalHarmonic.hpp; sourceTree = ""; }; + 318330D2259112BA005FEF70 /* NormalGravity.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = NormalGravity.hpp; sourceTree = ""; }; + 318330D3259112BA005FEF70 /* SphericalEngine.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = SphericalEngine.hpp; sourceTree = ""; }; + 318330D4259112BA005FEF70 /* GeodesicExact.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = GeodesicExact.hpp; sourceTree = ""; }; + 318330D5259112BA005FEF70 /* GARS.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = GARS.hpp; sourceTree = ""; }; + 318330D6259112BA005FEF70 /* Geocentric.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = Geocentric.hpp; sourceTree = ""; }; + 318330D7259112BA005FEF70 /* Accumulator.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = Accumulator.hpp; sourceTree = ""; }; + 318330D8259112BA005FEF70 /* PolygonArea.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = PolygonArea.hpp; sourceTree = ""; }; + 318330D9259112BA005FEF70 /* Math.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = Math.hpp; sourceTree = ""; }; + 318330DA259112BA005FEF70 /* Geodesic.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = Geodesic.hpp; sourceTree = ""; }; + 318330DB259112BA005FEF70 /* AzimuthalEquidistant.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = AzimuthalEquidistant.hpp; sourceTree = ""; }; + 318330DC259112BA005FEF70 /* Constants.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = Constants.hpp; sourceTree = ""; }; + 318330DD259112BA005FEF70 /* OSGB.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = OSGB.hpp; sourceTree = ""; }; + 318330DE259112BA005FEF70 /* GeoCoords.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = GeoCoords.hpp; sourceTree = ""; }; + 318330DF259112BA005FEF70 /* Config-ac.h.in */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = "Config-ac.h.in"; sourceTree = ""; }; + 318330E0259112BA005FEF70 /* CassiniSoldner.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = CassiniSoldner.hpp; sourceTree = ""; }; + 318330E1259112BA005FEF70 /* MagneticCircle.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = MagneticCircle.hpp; sourceTree = ""; }; + 318330E2259112BA005FEF70 /* NearestNeighbor.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = NearestNeighbor.hpp; sourceTree = ""; }; + 318330E3259112BA005FEF70 /* DMS.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = DMS.hpp; sourceTree = ""; }; + 318330E4259112BA005FEF70 /* Georef.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = Georef.hpp; sourceTree = ""; }; + 318330E5259112BA005FEF70 /* Gnomonic.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = Gnomonic.hpp; sourceTree = ""; }; + 318330E6259112BA005FEF70 /* GravityModel.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = GravityModel.hpp; sourceTree = ""; }; + 318330E7259112BA005FEF70 /* GeodesicLineExact.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = GeodesicLineExact.hpp; sourceTree = ""; }; + 318330E8259112BA005FEF70 /* CircularEngine.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = CircularEngine.hpp; sourceTree = ""; }; + 318330E9259112BA005FEF70 /* Geohash.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = Geohash.hpp; sourceTree = ""; }; + 318330EA259112BA005FEF70 /* EllipticFunction.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = EllipticFunction.hpp; sourceTree = ""; }; + 318330EC259112BA005FEF70 /* DMS.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = DMS.cpp; sourceTree = ""; }; + 318330ED259112BA005FEF70 /* Georef.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Georef.cpp; sourceTree = ""; }; + 318330EE259112BA005FEF70 /* Gnomonic.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Gnomonic.cpp; sourceTree = ""; }; + 318330EF259112BA005FEF70 /* GravityModel.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = GravityModel.cpp; sourceTree = ""; }; + 318330F0259112BA005FEF70 /* GeodesicLineExact.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = GeodesicLineExact.cpp; sourceTree = ""; }; + 318330F1259112BA005FEF70 /* CircularEngine.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = CircularEngine.cpp; sourceTree = ""; }; + 318330F2259112BA005FEF70 /* Geohash.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Geohash.cpp; sourceTree = ""; }; + 318330F3259112BA005FEF70 /* EllipticFunction.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = EllipticFunction.cpp; sourceTree = ""; }; + 318330F4259112BA005FEF70 /* PolygonArea.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = PolygonArea.cpp; sourceTree = ""; }; + 318330F5259112BA005FEF70 /* Accumulator.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Accumulator.cpp; sourceTree = ""; }; + 318330F6259112BA005FEF70 /* Math.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Math.cpp; sourceTree = ""; }; + 318330F7259112BA005FEF70 /* Geodesic.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Geodesic.cpp; sourceTree = ""; }; + 318330F8259112BA005FEF70 /* AzimuthalEquidistant.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = AzimuthalEquidistant.cpp; sourceTree = ""; }; + 318330F9259112BA005FEF70 /* OSGB.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = OSGB.cpp; sourceTree = ""; }; + 318330FA259112BA005FEF70 /* GeoCoords.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = GeoCoords.cpp; sourceTree = ""; }; + 318330FB259112BA005FEF70 /* CassiniSoldner.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = CassiniSoldner.cpp; sourceTree = ""; }; + 318330FC259112BA005FEF70 /* MagneticCircle.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = MagneticCircle.cpp; sourceTree = ""; }; + 318330FD259112BA005FEF70 /* Rhumb.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Rhumb.cpp; sourceTree = ""; }; + 318330FE259112BA005FEF70 /* NormalGravity.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = NormalGravity.cpp; sourceTree = ""; }; + 318330FF259112BA005FEF70 /* SphericalEngine.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SphericalEngine.cpp; sourceTree = ""; }; + 31833100259112BA005FEF70 /* GeodesicExact.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = GeodesicExact.cpp; sourceTree = ""; }; + 31833101259112BA005FEF70 /* GeodesicExactC4.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = GeodesicExactC4.cpp; sourceTree = ""; }; + 31833102259112BA005FEF70 /* GARS.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = GARS.cpp; sourceTree = ""; }; + 31833103259112BA005FEF70 /* Geocentric.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Geocentric.cpp; sourceTree = ""; }; + 31833104259112BA005FEF70 /* LambertConformalConic.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = LambertConformalConic.cpp; sourceTree = ""; }; + 31833105259112BA005FEF70 /* MGRS.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = MGRS.cpp; sourceTree = ""; }; + 31833106259112BA005FEF70 /* GravityCircle.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = GravityCircle.cpp; sourceTree = ""; }; + 31833107259112BA005FEF70 /* LocalCartesian.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = LocalCartesian.cpp; sourceTree = ""; }; + 31833108259112BA005FEF70 /* GeodesicLine.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = GeodesicLine.cpp; sourceTree = ""; }; + 31833109259112BA005FEF70 /* PolarStereographic.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = PolarStereographic.cpp; sourceTree = ""; }; + 3183310A259112BA005FEF70 /* UTMUPS.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = UTMUPS.cpp; sourceTree = ""; }; + 3183310B259112BA005FEF70 /* Ellipsoid.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Ellipsoid.cpp; sourceTree = ""; }; + 3183310C259112BA005FEF70 /* AlbersEqualArea.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = AlbersEqualArea.cpp; sourceTree = ""; }; + 3183310D259112BA005FEF70 /* MagneticModel.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = MagneticModel.cpp; sourceTree = ""; }; + 3183310E259112BA005FEF70 /* Utility.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Utility.cpp; sourceTree = ""; }; + 3183310F259112BA005FEF70 /* TransverseMercator.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = TransverseMercator.cpp; sourceTree = ""; }; + 31833110259112BA005FEF70 /* TransverseMercatorExact.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = TransverseMercatorExact.cpp; sourceTree = ""; }; + 31833111259112BA005FEF70 /* Geoid.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Geoid.cpp; sourceTree = ""; }; + 31833166259114A0005FEF70 /* GeographicLib.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GeographicLib.h; sourceTree = ""; }; + 3183316A259114B0005FEF70 /* GeographicLib.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = GeographicLib.mm; sourceTree = ""; }; 31872FD42550ED71002C9C73 /* maply_pb_names.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = maply_pb_names.h; sourceTree = ""; }; 31942F9C254B5C0A0006B499 /* maply_pb_common.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = maply_pb_common.c; sourceTree = ""; }; 31942FA4254B5C0A0006B499 /* maply_pb_decode.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = maply_pb_decode.c; sourceTree = ""; }; @@ -1921,6 +2101,7 @@ 2B23133721F942D1006AA344 /* Dictionary.h */, 2B446B2621F7A0D70078A975 /* Platform.h */, 2B23132D21F93660006AA344 /* RawData.h */, + 2B50CEB325798F4800BD4004 /* RawPNGImage.h */, 2B446AB921F25C330078A975 /* WhirlyKitLog.h */, 2B446ABA21F25C330078A975 /* WhirlyTypes.h */, ); @@ -1933,6 +2114,7 @@ 2BD6FA67254B47AF00FD8374 /* DictionaryC.cpp */, 2B23133921F942E1006AA344 /* Dictionary.cpp */, 2B23132F21F936CD006AA344 /* RawData.cpp */, + 2B50CEAF25798F3200BD4004 /* RawPNGImage.cpp */, ); name = util; sourceTree = ""; @@ -1940,6 +2122,7 @@ 2B446ABE21F268360078A975 /* external */ = { isa = PBXGroup; children = ( + 318330BC259112BA005FEF70 /* GeographicLib */, 31942F9B254B5C0A0006B499 /* nanopb */, 2B4A816625391A070016618C /* lodepng */, 2B82B4041E82E2490095FB14 /* shapelib-1.3.0b2 */, @@ -1955,6 +2138,7 @@ isa = PBXGroup; children = ( 2B446AF821F79A600078A975 /* GridClipper.h */, + 2BD645E025F0574B00727680 /* LinearTextBuilder.h */, 2B446AEF21F79A5F0078A975 /* OverlapHelper.h */, 2B446B2221F79BDF0078A975 /* QuadTreeNew.h */, 2B446B8C21FB99C00078A975 /* ScreenImportance.h */, @@ -1962,6 +2146,7 @@ 2B446AF521F79A5F0078A975 /* Tesselator.h */, 2B446B7A21FB948B0078A975 /* VectorData.h */, 2B810090221E07EE00CFF779 /* VectorObject.h */, + 2BD645EE25F1AF8C00727680 /* VectorOffset.h */, 2B446AF221F79A5F0078A975 /* WhirlyGeometry.h */, 2B446AF621F79A5F0078A975 /* WhirlyOctEncoding.h */, 2B446AF721F79A5F0078A975 /* WhirlyVector.h */, @@ -1973,6 +2158,7 @@ isa = PBXGroup; children = ( 2B446B0921F79AD00078A975 /* GridClipper.cpp */, + 2BD645E425F0576900727680 /* LinearTextBuilder.cpp */, 2B446B0C21F79AD00078A975 /* OverlapHelper.cpp */, 2B446B2421F79BF30078A975 /* QuadTreeNew.cpp */, 2B446B8E21FB99D60078A975 /* ScreenImportance.cpp */, @@ -1980,6 +2166,7 @@ 2B446B0821F79AD00078A975 /* Tesselator.cpp */, 2B446B7C21FB94A00078A975 /* VectorData.cpp */, 2B810092221E080700CFF779 /* VectorObject.cpp */, + 2BD645F225F1AF9A00727680 /* VectorOffset.cpp */, 2B446B0D21F79AD00078A975 /* WhirlyGeometry.cpp */, 2B446B0A21F79AD00078A975 /* WhirlyOctEncoding.cpp */, 2B446B0E21F79AD00078A975 /* WhirlyVector.cpp */, @@ -2600,7 +2787,7 @@ 2B82B54D1E82E2490095FB14 /* Cocoa Utils */ = { isa = PBXGroup; children = ( - 2B23133D21FA91A4006AA344 /* Dictinary_NSDictionary.mm */, + 2B23133D21FA91A4006AA344 /* Dictionary_NSDictionary.mm */, 2B23133521F93969006AA344 /* RawData_NSData.mm */, 2B82B5491E82E2490095FB14 /* UIImage+Stuff.mm */, 2B82B54A1E82E2490095FB14 /* NSDictionary+Stuff.m */, @@ -3133,6 +3320,7 @@ 2BC90D5B223308B900D8B606 /* geometry utils */ = { isa = PBXGroup; children = ( + 31833166259114A0005FEF70 /* GeographicLib.h */, 2BC90D5C223308C700D8B606 /* ScreenObject_iOS.h */, ); name = "geometry utils"; @@ -3141,6 +3329,7 @@ 2BC90D5E223308CF00D8B606 /* geometry utils */ = { isa = PBXGroup; children = ( + 3183316A259114B0005FEF70 /* GeographicLib.mm */, 2BC90D5F223308DB00D8B606 /* ScreenObject_iOS.mm */, ); name = "geometry utils"; @@ -3584,6 +3773,120 @@ name = loading; sourceTree = ""; }; + 318330BC259112BA005FEF70 /* GeographicLib */ = { + isa = PBXGroup; + children = ( + 318330BD259112BA005FEF70 /* include */, + 318330EB259112BA005FEF70 /* src */, + ); + name = GeographicLib; + path = ../../../../common/local_libs/GeographicLib; + sourceTree = ""; + }; + 318330BD259112BA005FEF70 /* include */ = { + isa = PBXGroup; + children = ( + 318330BE259112BA005FEF70 /* GeographicLib */, + ); + path = include; + sourceTree = ""; + }; + 318330BE259112BA005FEF70 /* GeographicLib */ = { + isa = PBXGroup; + children = ( + 318330BF259112BA005FEF70 /* LambertConformalConic.hpp */, + 318330C0259112BA005FEF70 /* MGRS.hpp */, + 318330C1259112BA005FEF70 /* LocalCartesian.hpp */, + 318330C2259112BA005FEF70 /* GeodesicLine.hpp */, + 318330C3259112BA005FEF70 /* GravityCircle.hpp */, + 318330C4259112BA005FEF70 /* UTMUPS.hpp */, + 318330C5259112BA005FEF70 /* Ellipsoid.hpp */, + 318330C6259112BA005FEF70 /* PolarStereographic.hpp */, + 318330C7259112BA005FEF70 /* AlbersEqualArea.hpp */, + 318330C8259112BA005FEF70 /* Utility.hpp */, + 318330C9259112BA005FEF70 /* MagneticModel.hpp */, + 318330CA259112BA005FEF70 /* SphericalHarmonic1.hpp */, + 318330CB259112BA005FEF70 /* TransverseMercator.hpp */, + 318330CC259112BA005FEF70 /* TransverseMercatorExact.hpp */, + 318330CD259112BA005FEF70 /* Config.h */, + 318330CE259112BA005FEF70 /* SphericalHarmonic2.hpp */, + 318330CF259112BA005FEF70 /* Geoid.hpp */, + 318330D0259112BA005FEF70 /* Rhumb.hpp */, + 318330D1259112BA005FEF70 /* SphericalHarmonic.hpp */, + 318330D2259112BA005FEF70 /* NormalGravity.hpp */, + 318330D3259112BA005FEF70 /* SphericalEngine.hpp */, + 318330D4259112BA005FEF70 /* GeodesicExact.hpp */, + 318330D5259112BA005FEF70 /* GARS.hpp */, + 318330D6259112BA005FEF70 /* Geocentric.hpp */, + 318330D7259112BA005FEF70 /* Accumulator.hpp */, + 318330D8259112BA005FEF70 /* PolygonArea.hpp */, + 318330D9259112BA005FEF70 /* Math.hpp */, + 318330DA259112BA005FEF70 /* Geodesic.hpp */, + 318330DB259112BA005FEF70 /* AzimuthalEquidistant.hpp */, + 318330DC259112BA005FEF70 /* Constants.hpp */, + 318330DD259112BA005FEF70 /* OSGB.hpp */, + 318330DE259112BA005FEF70 /* GeoCoords.hpp */, + 318330DF259112BA005FEF70 /* Config-ac.h.in */, + 318330E0259112BA005FEF70 /* CassiniSoldner.hpp */, + 318330E1259112BA005FEF70 /* MagneticCircle.hpp */, + 318330E2259112BA005FEF70 /* NearestNeighbor.hpp */, + 318330E3259112BA005FEF70 /* DMS.hpp */, + 318330E4259112BA005FEF70 /* Georef.hpp */, + 318330E5259112BA005FEF70 /* Gnomonic.hpp */, + 318330E6259112BA005FEF70 /* GravityModel.hpp */, + 318330E7259112BA005FEF70 /* GeodesicLineExact.hpp */, + 318330E8259112BA005FEF70 /* CircularEngine.hpp */, + 318330E9259112BA005FEF70 /* Geohash.hpp */, + 318330EA259112BA005FEF70 /* EllipticFunction.hpp */, + ); + path = GeographicLib; + sourceTree = ""; + }; + 318330EB259112BA005FEF70 /* src */ = { + isa = PBXGroup; + children = ( + 318330EC259112BA005FEF70 /* DMS.cpp */, + 318330ED259112BA005FEF70 /* Georef.cpp */, + 318330EE259112BA005FEF70 /* Gnomonic.cpp */, + 318330EF259112BA005FEF70 /* GravityModel.cpp */, + 318330F0259112BA005FEF70 /* GeodesicLineExact.cpp */, + 318330F1259112BA005FEF70 /* CircularEngine.cpp */, + 318330F2259112BA005FEF70 /* Geohash.cpp */, + 318330F3259112BA005FEF70 /* EllipticFunction.cpp */, + 318330F4259112BA005FEF70 /* PolygonArea.cpp */, + 318330F5259112BA005FEF70 /* Accumulator.cpp */, + 318330F6259112BA005FEF70 /* Math.cpp */, + 318330F7259112BA005FEF70 /* Geodesic.cpp */, + 318330F8259112BA005FEF70 /* AzimuthalEquidistant.cpp */, + 318330F9259112BA005FEF70 /* OSGB.cpp */, + 318330FA259112BA005FEF70 /* GeoCoords.cpp */, + 318330FB259112BA005FEF70 /* CassiniSoldner.cpp */, + 318330FC259112BA005FEF70 /* MagneticCircle.cpp */, + 318330FD259112BA005FEF70 /* Rhumb.cpp */, + 318330FE259112BA005FEF70 /* NormalGravity.cpp */, + 318330FF259112BA005FEF70 /* SphericalEngine.cpp */, + 31833100259112BA005FEF70 /* GeodesicExact.cpp */, + 31833101259112BA005FEF70 /* GeodesicExactC4.cpp */, + 31833102259112BA005FEF70 /* GARS.cpp */, + 31833103259112BA005FEF70 /* Geocentric.cpp */, + 31833104259112BA005FEF70 /* LambertConformalConic.cpp */, + 31833105259112BA005FEF70 /* MGRS.cpp */, + 31833106259112BA005FEF70 /* GravityCircle.cpp */, + 31833107259112BA005FEF70 /* LocalCartesian.cpp */, + 31833108259112BA005FEF70 /* GeodesicLine.cpp */, + 31833109259112BA005FEF70 /* PolarStereographic.cpp */, + 3183310A259112BA005FEF70 /* UTMUPS.cpp */, + 3183310B259112BA005FEF70 /* Ellipsoid.cpp */, + 3183310C259112BA005FEF70 /* AlbersEqualArea.cpp */, + 3183310D259112BA005FEF70 /* MagneticModel.cpp */, + 3183310E259112BA005FEF70 /* Utility.cpp */, + 3183310F259112BA005FEF70 /* TransverseMercator.cpp */, + 31833110259112BA005FEF70 /* TransverseMercatorExact.cpp */, + 31833111259112BA005FEF70 /* Geoid.cpp */, + ); + path = src; + sourceTree = ""; + }; 31942F9B254B5C0A0006B499 /* nanopb */ = { isa = PBXGroup; children = ( @@ -3608,10 +3911,12 @@ 2B0D978C24490B4B00F64852 /* MapboxVectorStyleLine.h in Headers */, 2BC3D6AC22024EB300CE91D0 /* MaplyAnimateTranslation.h in Headers */, E5EF69F61D6B931500A2A660 /* SLDExpressions.h in Headers */, + 31833112259112BA005FEF70 /* LambertConformalConic.hpp in Headers */, 2B82B5F31E82E2490095FB14 /* sweep.h in Headers */, 2BE5395E1D249BEF00B60FAD /* AAEquationOfTime.h in Headers */, 2B82B6131E82E2490095FB14 /* JSONPreparse.h in Headers */, 2B846F0E21F158E100EF2A82 /* MarkerManager.h in Headers */, + 31833134259112BA005FEF70 /* MagneticCircle.hpp in Headers */, 2BE5397C1D249BEF00B60FAD /* AAPlanetaryPhenomena.h in Headers */, 2B82B6181E82E2490095FB14 /* JSONStream.h in Headers */, 2BE538271D249A1200B60FAD /* MaplyStarsModel.h in Headers */, @@ -3634,6 +3939,7 @@ 2B846F1121F158E100EF2A82 /* BillboardManager.h in Headers */, 2B82B6DA1E82E24A0095FB14 /* NSDictionary+Stuff.h in Headers */, 2B82B6141E82E2490095FB14 /* JSONSharedString.h in Headers */, + 3183313B259112BA005FEF70 /* CircularEngine.hpp in Headers */, 2BE538311D249A1200B60FAD /* MaplyControllerLayer.h in Headers */, 2BE53A861D249C8300B60FAD /* NSString+DDXML.h in Headers */, 2BE53A981D249C9000B60FAD /* DDXMLNode.h in Headers */, @@ -3663,6 +3969,7 @@ 2BA827CB225E719D00324594 /* MapboxVectorTiles_private.h in Headers */, 2B82B63B1E82E2490095FB14 /* geodesic.h in Headers */, 2BE539511D249BEF00B60FAD /* AAAberration.h in Headers */, + 31833114259112BA005FEF70 /* LocalCartesian.hpp in Headers */, 2BE538221D249A1200B60FAD /* MaplyScreenObject.h in Headers */, 2B23133C21FA919E006AA344 /* Dictionary_NSDictionary.h in Headers */, 2B82B71D1E82E24A0095FB14 /* LayerThread.h in Headers */, @@ -3671,6 +3978,7 @@ 2B3F4518243F8F8100F85414 /* MaplyVectorStyleC.h in Headers */, 2B846F0921F158E100EF2A82 /* ShapeManager.h in Headers */, 2BE538721D249A1200B60FAD /* WhirlyGlobeViewController.h in Headers */, + 3183313A259112BA005FEF70 /* GeodesicLineExact.hpp in Headers */, 2BE538101D249A1200B60FAD /* MaplyLight.h in Headers */, 2BE5386C1D249A1200B60FAD /* MapnikStyle.h in Headers */, 2BE538431D249A1200B60FAD /* MaplyParticleSystem_private.h in Headers */, @@ -3682,12 +3990,17 @@ 2BE5384F1D249A1200B60FAD /* MaplyUpdateLayer_private.h in Headers */, 2B82B60E1E82E2490095FB14 /* JSONMemoryPool.h in Headers */, 2BE539601D249BEF00B60FAD /* AAFK5.h in Headers */, + 31833128259112BA005FEF70 /* GARS.hpp in Headers */, 2BE539721D249BEF00B60FAD /* AANeptune.h in Headers */, + 3183312B259112BA005FEF70 /* PolygonArea.hpp in Headers */, 31942FE2254B5C0A0006B499 /* maply_pb.h in Headers */, 2BE5380B1D249A1200B60FAD /* MaplyGeomBuilder.h in Headers */, + 31833119259112BA005FEF70 /* PolarStereographic.hpp in Headers */, 2BE539881D249BEF00B60FAD /* AAUranus.h in Headers */, 2B446B0421F79A600078A975 /* GridClipper.h in Headers */, 313363AB253E5A2B007C2F27 /* WorkRegion_private.h in Headers */, + 3183312F259112BA005FEF70 /* Constants.hpp in Headers */, + 3183311F259112BA005FEF70 /* TransverseMercatorExact.hpp in Headers */, 2B8E608B20D47FEE00FB96F0 /* MaplyRemoteTileFetcher.h in Headers */, 2BB8A3CD21ED43A40025DA98 /* MaplyTileSourceNew.h in Headers */, 2BE538031D249A1200B60FAD /* MaplyColorRampGenerator.h in Headers */, @@ -3699,10 +4012,14 @@ 2B846F1221F158E100EF2A82 /* IntersectionManager.h in Headers */, 2BE538141D249A1200B60FAD /* MaplyMoon.h in Headers */, 2B446B5621F7E7B80078A975 /* BillboardDrawableBuilder.h in Headers */, + 31833120259112BA005FEF70 /* Config.h in Headers */, 2B446B4A21F7E7B80078A975 /* ParticleSystemDrawable.h in Headers */, 2BE538181D249A1200B60FAD /* MaplyParticleSystem.h in Headers */, + 3183312D259112BA005FEF70 /* Geodesic.hpp in Headers */, + 31833124259112BA005FEF70 /* SphericalHarmonic.hpp in Headers */, 2B446B4921F7E7B80078A975 /* ScreenSpaceDrawableBuilder.h in Headers */, 2BBC337C22163AE90038A229 /* QuadSamplingController.h in Headers */, + 3183311D259112BA005FEF70 /* SphericalHarmonic1.hpp in Headers */, 2B82B6091E82E2490095FB14 /* JSONDefs.h in Headers */, 2BE539781D249BEF00B60FAD /* AAPhysicalJupiter.h in Headers */, 2B446B4D21F7E7B80078A975 /* TextureAtlas.h in Headers */, @@ -3712,7 +4029,9 @@ 2BE539841D249BEF00B60FAD /* AASaturnRings.h in Headers */, 2BC3D6BF2202537600CE91D0 /* GlobeView_iOS.h in Headers */, 2BE5396A1D249BEF00B60FAD /* AAMoon.h in Headers */, + 31833126259112BA005FEF70 /* SphericalEngine.hpp in Headers */, 2B6597EB24E4AF2300FA26A9 /* StringIndexer.h in Headers */, + 31833121259112BA005FEF70 /* SphericalHarmonic2.hpp in Headers */, 2B82B7181E82E24A0095FB14 /* LayoutLayer.h in Headers */, 2B63C45F243E44A0002B481C /* MapboxVectorStyleSetC.h in Headers */, 2BE538211D249A1200B60FAD /* MaplyScreenMarker.h in Headers */, @@ -3722,6 +4041,7 @@ 2B446B5521F7E7B80078A975 /* DynamicTextureAtlas.h in Headers */, 2BE539621D249BEF00B60FAD /* AAGlobe.h in Headers */, 2B82B60A1E82E2490095FB14 /* JSONGlobals.h in Headers */, + 31833127259112BA005FEF70 /* GeodesicExact.hpp in Headers */, 2BE538061D249A1200B60FAD /* MaplyCoordinate.h in Headers */, 2B446B9221FBA8250078A975 /* FontTextureManager.h in Headers */, 2B23131A21F8DD61006AA344 /* MaplyFlatView.h in Headers */, @@ -3743,6 +4063,7 @@ 2BE1E78B22138A8100815D9C /* MaplyQuadLoader_private.h in Headers */, 2B699857228DD31F00C31E3F /* BillboardDrawableBuilderMTL.h in Headers */, 2BE538231D249A1200B60FAD /* MaplyShader.h in Headers */, + 31833113259112BA005FEF70 /* MGRS.hpp in Headers */, 2B0387F8206ABD7B00DD5C40 /* MaplyQuadSampler.h in Headers */, 2BE5398A1D249BEF00B60FAD /* stdafx.h in Headers */, 2BB8A3F521ED43D10025DA98 /* MaplyPanDelegate.h in Headers */, @@ -3750,6 +4071,7 @@ 2BB8A3F321ED43D10025DA98 /* MaplyTapDelegate.h in Headers */, 2BE539751D249BEF00B60FAD /* AAParabolic.h in Headers */, 2BC90D5122319FD700D8B606 /* WhirlyGlobe_iOS.h in Headers */, + 3183311E259112BA005FEF70 /* TransverseMercator.hpp in Headers */, 2B8A786B2284DACC008B0A1F /* VertexAttribute.h in Headers */, 2B82B5FA1E82E2490095FB14 /* libbase64++.h in Headers */, 2BE5382D1D249A1200B60FAD /* MaplyUpdateLayer.h in Headers */, @@ -3774,6 +4096,7 @@ 2BE539521D249BEF00B60FAD /* AAAngularSeparation.h in Headers */, 2B82B5FD1E82E2490095FB14 /* internalJSONNode.h in Headers */, 2BE538391D249A1200B60FAD /* MaplyBaseInteractionLayer_private.h in Headers */, + 3183312E259112BA005FEF70 /* AzimuthalEquidistant.hpp in Headers */, 2BE538241D249A1200B60FAD /* MaplyShape.h in Headers */, 2BE538411D249A1200B60FAD /* MaplyInteractionLayer_private.h in Headers */, 2B82B6051E82E2490095FB14 /* GNU_C.h in Headers */, @@ -3789,6 +4112,7 @@ 2BC90D6622405DD200D8B606 /* Sun.h in Headers */, 2B446B2721F7A0D70078A975 /* Platform.h in Headers */, 2B82B5E71E82E2490095FB14 /* memalloc.h in Headers */, + 3183312A259112BA005FEF70 /* Accumulator.hpp in Headers */, 2B82B5EF1E82E2490095FB14 /* priorityq.h in Headers */, 2BB8A3F121ED43D10025DA98 /* GlobeTwoFingerTapDelegate.h in Headers */, 2BE5397A1D249BEF00B60FAD /* AAPhysicalMoon.h in Headers */, @@ -3809,6 +4133,7 @@ 2B446AFC21F79A600078A975 /* FlatMath.h in Headers */, 2BE5380E1D249A1200B60FAD /* MaplyImageTile.h in Headers */, 2B846F0721F158E100EF2A82 /* LoftManager.h in Headers */, + 31833135259112BA005FEF70 /* NearestNeighbor.hpp in Headers */, 2B82B6071E82E2490095FB14 /* Unknown_C.h in Headers */, 2B82B7151E82E24A0095FB14 /* DataLayer.h in Headers */, 2BE5397F1D249BEF00B60FAD /* AAPrecession.h in Headers */, @@ -3832,6 +4157,7 @@ 2BB8A3EE21ED43D10025DA98 /* MaplyDoubleTapDragDelegate.h in Headers */, 2BE538661D249A1200B60FAD /* MaplyVectorTileLineStyle.h in Headers */, 2BE539741D249BEF00B60FAD /* AANutation.h in Headers */, + 3183313C259112BA005FEF70 /* Geohash.hpp in Headers */, 2BE538121D249A1200B60FAD /* MaplyMatrix.h in Headers */, 2B82B6371E82E2490095FB14 /* emess.h in Headers */, 2B446B9621FBA8520078A975 /* Program.h in Headers */, @@ -3839,6 +4165,7 @@ 2B73D6C2207D224100AF5095 /* MaplyQuadImageLoader_private.h in Headers */, 2BE538341D249A1200B60FAD /* NSData+Zlib.h in Headers */, 2BE53AB41D249CAF00B60FAD /* SMCalloutView.h in Headers */, + 31833125259112BA005FEF70 /* NormalGravity.hpp in Headers */, 2B82B71C1E82E24A0095FB14 /* LayerViewWatcher.h in Headers */, E56E02A71E11F99500C1DD85 /* GeoJSONSource.h in Headers */, 2B63C463243E474E002B481C /* MapboxVectorStyleSet_private.h in Headers */, @@ -3858,11 +4185,16 @@ 2BB8A3FD21ED43D10025DA98 /* MaplyDoubleTapDelegate.h in Headers */, 2B23131921F8DD61006AA344 /* GlobeView.h in Headers */, 2BD6FA64254B478000FD8374 /* DictionaryC.h in Headers */, + 31833118259112BA005FEF70 /* Ellipsoid.hpp in Headers */, + 31833122259112BA005FEF70 /* Geoid.hpp in Headers */, 2B699854228DD31F00C31E3F /* ParticleSystemDrawableBuilderMTL.h in Headers */, 2B846F1021F158E100EF2A82 /* LayoutManager.h in Headers */, + 31833123259112BA005FEF70 /* Rhumb.hpp in Headers */, + 31833131259112BA005FEF70 /* GeoCoords.hpp in Headers */, 2B699848228DD31F00C31E3F /* BasicDrawableMTL.h in Headers */, 2BE537F81D249A1200B60FAD /* Maply3dTouchPreviewDelegate.h in Headers */, 2BE539651D249BEF00B60FAD /* AAJewishCalendar.h in Headers */, + 31833115259112BA005FEF70 /* GeodesicLine.hpp in Headers */, 2BE538421D249A1200B60FAD /* MaplyMatrix_private.h in Headers */, 2BC3D6AB22024EB300CE91D0 /* GlobeAnimateRotation.h in Headers */, 2BE5382A1D249A1200B60FAD /* MaplyTexture.h in Headers */, @@ -3872,6 +4204,7 @@ 2B446B8321FB97C40078A975 /* ShapeReader.h in Headers */, 2B0D978924490B4B00F64852 /* MapboxVectorStyleSymbol.h in Headers */, 2B446B4E21F7E7B80078A975 /* Drawable.h in Headers */, + 2BD645EF25F1AF8C00727680 /* VectorOffset.h in Headers */, 2BE538071D249A1200B60FAD /* MaplyCoordinateSystem.h in Headers */, 2BC90D58223306D300D8B606 /* ScreenObject.h in Headers */, 2BE539711D249BEF00B60FAD /* AANearParabolic.h in Headers */, @@ -3895,6 +4228,7 @@ 2B846F0F21F158E100EF2A82 /* LabelManager.h in Headers */, 2B446AE121F288090078A975 /* LabelRenderer.h in Headers */, 2BE1E7922213977F00815D9C /* QuadTileBuilder.h in Headers */, + 3183311C259112BA005FEF70 /* MagneticModel.hpp in Headers */, 2B82B5F81E82E2490095FB14 /* JSONOptions.h in Headers */, 2BE538371D249A1200B60FAD /* MaplyActiveObject_private.h in Headers */, 2BE1E7A522161BD600815D9C /* QuadLoaderReturn.h in Headers */, @@ -3911,6 +4245,7 @@ E5EF69F81D6B931500A2A660 /* SLDSymbolizers.h in Headers */, 2B846F1321F158E100EF2A82 /* BaseInfo.h in Headers */, 2BE53A831D249C7E00B60FAD /* DDXMLPrivate.h in Headers */, + 31833136259112BA005FEF70 /* DMS.hpp in Headers */, 2B82B61C1E82E2490095FB14 /* JSONWorker.h in Headers */, 2BE5383A1D249A1200B60FAD /* MaplyBaseViewController_private.h in Headers */, 2BB8A3F021ED43D10025DA98 /* GlobeDoubleTapDragDelegate.h in Headers */, @@ -3920,6 +4255,10 @@ 2B82B7C41E82E68E0095FB14 /* clipper.hpp in Headers */, 2BC3D6EC220B713700CE91D0 /* sqlhelpers.h in Headers */, 2B699849228DD31F00C31E3F /* SceneMTL.h in Headers */, + 31833129259112BA005FEF70 /* Geocentric.hpp in Headers */, + 31833116259112BA005FEF70 /* GravityCircle.hpp in Headers */, + 2B50CEB425798F4800BD4004 /* RawPNGImage.h in Headers */, + 31833117259112BA005FEF70 /* UTMUPS.hpp in Headers */, 2B82B6DC1E82E24A0095FB14 /* NSString+Stuff.h in Headers */, 2BE538701D249A1200B60FAD /* WGCoordinate.h in Headers */, 2BE5382F1D249A1200B60FAD /* MaplyVertexAttribute.h in Headers */, @@ -3930,6 +4269,8 @@ 2B69984A228DD31F00C31E3F /* BasicDrawableInstanceBuilderMTL.h in Headers */, 2B69984F228DD31F00C31E3F /* ParticleSystemDrawableMTL.h in Headers */, 2B82B5E31E82E2490095FB14 /* dict.h in Headers */, + 3183313D259112BA005FEF70 /* EllipticFunction.hpp in Headers */, + 31833167259114A0005FEF70 /* GeographicLib.h in Headers */, 2BB8A3F421ED43D10025DA98 /* MaplyZoomGestureDelegate.h in Headers */, 2B699852228DD31F00C31E3F /* WrapperMTL.h in Headers */, 2B82B5E11E82E2490095FB14 /* dict-list.h in Headers */, @@ -3948,6 +4289,7 @@ 2B541C171ECFAA2300EC35A0 /* MaplyRenderTarget.h in Headers */, 2B82B60D1E82E2490095FB14 /* JSONMemory.h in Headers */, 2BB8A3CE21ED43A40025DA98 /* MaplyMBTileFetcher.h in Headers */, + 3183311B259112BA005FEF70 /* Utility.hpp in Headers */, 2BE537F91D249A1200B60FAD /* MaplyActiveObject.h in Headers */, 2B82B5E91E82E2490095FB14 /* mesh.h in Headers */, E56DB3D51D6B1B17007000D2 /* SLDStyleSet.h in Headers */, @@ -3972,10 +4314,12 @@ 2B82B6101E82E2490095FB14 /* JSONNode.h in Headers */, 2B23131B21F8DD61006AA344 /* MaplyView.h in Headers */, 2BE5384D1D249A1200B60FAD /* MaplyTexture_private.h in Headers */, + 31833137259112BA005FEF70 /* Georef.hpp in Headers */, 2B7B84D821223F0300D11447 /* MaplyTextureAtlas_private.h in Headers */, 2BBC337B22163AE90038A229 /* QuadSamplingParams.h in Headers */, 2B846F0821F158E100EF2A82 /* SelectionManager.h in Headers */, 2B82B5EB1E82E2490095FB14 /* normal.h in Headers */, + 31833139259112BA005FEF70 /* GravityModel.hpp in Headers */, 2B82B61F1E82E2490095FB14 /* NumberToString.h in Headers */, 2B4A816925391A0D0016618C /* lodepng.h in Headers */, 2BC3D6AD22024EB300CE91D0 /* GlobeAnimateViewMomentum.h in Headers */, @@ -3989,14 +4333,20 @@ 2BE539591D249BEF00B60FAD /* AAEaster.h in Headers */, 2B82B6391E82E2490095FB14 /* geocent.h in Headers */, 2BE5394E1D249BEF00B60FAD /* AA+.h in Headers */, + 31833130259112BA005FEF70 /* OSGB.hpp in Headers */, 2B446B3721F7E6780078A975 /* Lighting.h in Headers */, 2BE538711D249A1200B60FAD /* WhirlyGlobeComponent.h in Headers */, 2B446ABC21F25C330078A975 /* WhirlyTypes.h in Headers */, 2BE1E7582208F32900815D9C /* SingleLabel_iOS.h in Headers */, + 3183312C259112BA005FEF70 /* Math.hpp in Headers */, 2B127C03201911D40099F405 /* MapboxVectorInterpreter.h in Headers */, 2BE539771D249BEF00B60FAD /* AAParallax.h in Headers */, + 2BD645E125F0574B00727680 /* LinearTextBuilder.h in Headers */, + 31833138259112BA005FEF70 /* Gnomonic.hpp in Headers */, + 3183311A259112BA005FEF70 /* AlbersEqualArea.hpp in Headers */, 2BE538251D249A1200B60FAD /* MaplySharedAttributes.h in Headers */, 2BE538531D249A1200B60FAD /* MaplyControllerLayer_private.h in Headers */, + 31833133259112BA005FEF70 /* CassiniSoldner.hpp in Headers */, 2BE5384A1D249A1200B60FAD /* MaplyShader_private.h in Headers */, 2B7B84DA2122403700D11447 /* MaplyTapMessage.h in Headers */, 2B23131C21F8DD61006AA344 /* WhirlyKitView.h in Headers */, @@ -4090,6 +4440,7 @@ isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( + 31833132259112BA005FEF70 /* Config-ac.h.in in Resources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -4111,6 +4462,8 @@ 2B82B65A1E82E2490095FB14 /* PJ_eck3.c in Sources */, 2B82B67E1E82E24A0095FB14 /* PJ_larr.c in Sources */, 2BE539C11D249BEF00B60FAD /* AASun.cpp in Sources */, + 31833157259112BA005FEF70 /* MGRS.cpp in Sources */, + 2BD645E525F0576900727680 /* LinearTextBuilder.cpp in Sources */, 2B87971D2203BF2E00EF801D /* MaplyCoordinate.mm in Sources */, 2BE5398E1D249BEF00B60FAD /* AACoordinateTransformation.cpp in Sources */, 2B8A78B8228A1A1B008B0A1F /* Identifiable.cpp in Sources */, @@ -4147,6 +4500,7 @@ 2B82B6571E82E2490095FB14 /* pj_deriv.c in Sources */, 2BE1E7452208CCB400815D9C /* GlobeRotateDelegate.mm in Sources */, 2BE539B91D249BEF00B60FAD /* AAPrecession.cpp in Sources */, + 2B50CEB025798F3200BD4004 /* RawPNGImage.cpp in Sources */, 2B446B1021F79AD00078A975 /* GridClipper.cpp in Sources */, 2B82B5E81E82E2490095FB14 /* mesh.c in Sources */, 2B82B68E1E82E24A0095FB14 /* pj_mlfn.c in Sources */, @@ -4157,6 +4511,7 @@ 2BE7E7BB221B22E500E4EFBA /* QuadImageFrameLoader_iOS.mm in Sources */, 2B69986B228DD36A00C31E3F /* BasicDrawableBuilderMTL.mm in Sources */, 2B82B6491E82E2490095FB14 /* PJ_bacon.c in Sources */, + 31833160259112BA005FEF70 /* Utility.cpp in Sources */, 2BC3D6EE220B714100CE91D0 /* sqlhelpers.mm in Sources */, 2B82B6771E82E24A0095FB14 /* pj_initcache.c in Sources */, 2B3F452B243FD82200F85414 /* SLDSymbolizers.m in Sources */, @@ -4169,6 +4524,7 @@ 2B699875228DD36A00C31E3F /* BasicDrawableMTL.mm in Sources */, 2BE539AF1D249BEF00B60FAD /* AAParabolic.cpp in Sources */, 2B8796EF220375E900EF801D /* GlobeAnimateRotation.cpp in Sources */, + 31833161259112BA005FEF70 /* TransverseMercator.cpp in Sources */, 2B8A78B4228A1610008B0A1F /* Drawable.cpp in Sources */, 2B8797152203B77900EF801D /* MaplyIconManager.mm in Sources */, 2BB8E1FF21FF93CB00154CDC /* MaplyView.cpp in Sources */, @@ -4185,25 +4541,30 @@ 2B8797212203BF7900EF801D /* MaplyLight.m in Sources */, 2B0D979624490BAD00F64852 /* MapboxVectorStyleBackground.cpp in Sources */, 2B87971E2203BF2E00EF801D /* MaplyCoordinateSystem.mm in Sources */, + 31833154259112BA005FEF70 /* GARS.cpp in Sources */, 2B8A78A522864E4F008B0A1F /* SphericalEarthChunkManager.cpp in Sources */, 2B82B64D1E82E2490095FB14 /* PJ_cass.c in Sources */, + 3183313E259112BA005FEF70 /* DMS.cpp in Sources */, 2B82B6B41E82E24A0095FB14 /* PJ_tmerc.c in Sources */, 2B8796D321FFC59D00EF801D /* NSString+Stuff.mm in Sources */, 2B3F451E243FD82200F85414 /* MaplyVectorStyle.mm in Sources */, 2B8A788B228607B3008B0A1F /* Program.cpp in Sources */, 2B8797222203BF7900EF801D /* MaplyBillboard.mm in Sources */, 2BC90D60223308DB00D8B606 /* ScreenObject_iOS.mm in Sources */, + 31833141259112BA005FEF70 /* GravityModel.cpp in Sources */, 2BE1E766220A567100815D9C /* MaplyControllerLayer.mm in Sources */, 2B60F43E24523FAB00CF9339 /* MapboxVectorStyleSet.mm in Sources */, 2B82B64C1E82E2490095FB14 /* PJ_bonne.c in Sources */, 31942FCD254B5C0A0006B499 /* maply_pb_decode.c in Sources */, 2B82B6111E82E2490095FB14 /* JSONNode_Mutex.cpp in Sources */, + 31833159259112BA005FEF70 /* LocalCartesian.cpp in Sources */, 2B82B6BA1E82E24A0095FB14 /* PJ_urmfps.c in Sources */, 2B82B6721E82E24A0095FB14 /* PJ_hatano.c in Sources */, 2BC3D6E7220B6AEF00CE91D0 /* GeometryOBJReader.cpp in Sources */, 2B4A816A25391A0D0016618C /* lodepng.cpp in Sources */, 2B82B6231E82E2490095FB14 /* shpopen.c in Sources */, 2B82B6621E82E24A0095FB14 /* pj_factors.c in Sources */, + 31833142259112BA005FEF70 /* GeodesicLineExact.cpp in Sources */, 2BC3D6D12203EA4A00CE91D0 /* MaplySticker.mm in Sources */, 2BE1E7A12215F70400815D9C /* ImageTile_iOS.mm in Sources */, 2B8A78B3228A1539008B0A1F /* VertexAttribute.cpp in Sources */, @@ -4213,7 +4574,9 @@ 2B82B6831E82E24A0095FB14 /* pj_list.c in Sources */, 2B82B63D1E82E2490095FB14 /* nad_cvt.c in Sources */, 2BE539B11D249BEF00B60FAD /* AAParallax.cpp in Sources */, + 2BD645F325F1AF9B00727680 /* VectorOffset.cpp in Sources */, 2B82B64A1E82E2490095FB14 /* PJ_bipc.c in Sources */, + 31833144259112BA005FEF70 /* Geohash.cpp in Sources */, 2B82B66E1E82E24A0095FB14 /* pj_gridinfo.c in Sources */, 2B8A78E4228C8543008B0A1F /* MaplyShader.mm in Sources */, 2B82B68B1E82E24A0095FB14 /* PJ_mbtfpq.c in Sources */, @@ -4234,6 +4597,7 @@ 2BE539AE1D249BEF00B60FAD /* AANutation.cpp in Sources */, 2BE539BB1D249BEF00B60FAD /* AARiseTransitSet.cpp in Sources */, 2BE1E7462208CEA300815D9C /* GlobeTapDelegate.mm in Sources */, + 31833162259112BA005FEF70 /* TransverseMercatorExact.cpp in Sources */, 2BE53A971D249C9000B60FAD /* DDXMLElement.m in Sources */, 2B8796D221FFC59D00EF801D /* UIColor+Stuff.mm in Sources */, 2B82B6AA1E82E24A0095FB14 /* PJ_robin.c in Sources */, @@ -4247,9 +4611,11 @@ 2B8796C821FFC57700EF801D /* ShapeReader.cpp in Sources */, 2B699877228DD36A00C31E3F /* WideVectorDrawableBuilderMTL.mm in Sources */, 2B82B67F1E82E24A0095FB14 /* PJ_lask.c in Sources */, + 3183314E259112BA005FEF70 /* MagneticCircle.cpp in Sources */, 2B82B5F21E82E2490095FB14 /* sweep.c in Sources */, 2B82B6A81E82E24A0095FB14 /* pj_qsfn.c in Sources */, 2B3F451D243FD82200F85414 /* GeoJSONSource.mm in Sources */, + 3183316B259114B0005FEF70 /* GeographicLib.mm in Sources */, 2BE539971D249BEF00B60FAD /* AAElliptical.cpp in Sources */, 2BE539B21D249BEF00B60FAD /* AAPhysicalJupiter.cpp in Sources */, 2BE1E75B2208F33900815D9C /* SingleLabel_iOS.mm in Sources */, @@ -4266,10 +4632,13 @@ 2B6597ED24E4AF3600FA26A9 /* StringIndexer.cpp in Sources */, 2BE539A51D249BEF00B60FAD /* AAMoonIlluminatedFraction.cpp in Sources */, 2BE5399B1D249BEF00B60FAD /* AAGalileanMoons.cpp in Sources */, + 3183314B259112BA005FEF70 /* OSGB.cpp in Sources */, 2B82B6A71E82E24A0095FB14 /* PJ_putp6.c in Sources */, 2BE1E73D2208BD4500815D9C /* Maply3dTouchPreviewDelegate.mm in Sources */, + 3183315D259112BA005FEF70 /* Ellipsoid.cpp in Sources */, 2B8796F022037A1A00EF801D /* GlobeAnimateViewMomentum.cpp in Sources */, 2B82B66F1E82E24A0095FB14 /* pj_gridlist.c in Sources */, + 3183315C259112BA005FEF70 /* UTMUPS.cpp in Sources */, 2B8A78A82289D539008B0A1F /* BasicDrawableInstance.cpp in Sources */, 2B82B6AF1E82E24A0095FB14 /* PJ_sterea.c in Sources */, 2BE539B51D249BEF00B60FAD /* AAPhysicalSun.cpp in Sources */, @@ -4287,6 +4656,7 @@ 2B8796B721FFB2DE00EF801D /* Platform.mm in Sources */, 2B3F4521243FD82200F85414 /* MaplyVectorTileMarkerStyle.mm in Sources */, 2B8A789D2286474A008B0A1F /* LabelManager.cpp in Sources */, + 31833145259112BA005FEF70 /* EllipticFunction.cpp in Sources */, 2BE539AA1D249BEF00B60FAD /* AAMoslemCalendar.cpp in Sources */, 2B82B6AE1E82E24A0095FB14 /* PJ_stere.c in Sources */, 2B8A78AA2289DA3D008B0A1F /* RenderTarget.cpp in Sources */, @@ -4296,6 +4666,7 @@ 2B846EEC21F1382300EF2A82 /* pj_fileapi.c in Sources */, 2B8A78DF228C8526008B0A1F /* MaplyRenderController.mm in Sources */, 2B82B69A1E82E24A0095FB14 /* PJ_ocea.c in Sources */, + 31833158259112BA005FEF70 /* GravityCircle.cpp in Sources */, 2B446B1E21F79AE40078A975 /* GlobeMath.cpp in Sources */, 2B82B6B01E82E24A0095FB14 /* pj_strerrno.c in Sources */, 2B82B69E1E82E24A0095FB14 /* PJ_ortho.c in Sources */, @@ -4328,6 +4699,7 @@ 2B6997EE228CAF7C00C31E3F /* ChangeRequest.cpp in Sources */, 2B82B6121E82E2490095FB14 /* JSONPreparse.cpp in Sources */, 2B846EE721F137D300EF2A82 /* emess.c in Sources */, + 31833163259112BA005FEF70 /* Geoid.cpp in Sources */, 2B8A78A422864D64008B0A1F /* ShapeManager.cpp in Sources */, 2BC3D6E0220B553100CE91D0 /* MaplyVectorObject.mm in Sources */, 2BC3D6DA220B520D00CE91D0 /* MaplyAtmosphere.mm in Sources */, @@ -4360,6 +4732,7 @@ 2B82B6741E82E24A0095FB14 /* PJ_igh.c in Sources */, 2B699872228DD36A00C31E3F /* SceneRendererMTL.mm in Sources */, 2B846EE921F1380D00EF2A82 /* aasincos.c in Sources */, + 31833153259112BA005FEF70 /* GeodesicExactC4.cpp in Sources */, 2B8A78A322864B5C008B0A1F /* ShapeDrawableBuilder.cpp in Sources */, 2B82B6901E82E24A0095FB14 /* PJ_moll.c in Sources */, 2B8A78BC228B3AE9008B0A1F /* TextureAtlas.cpp in Sources */, @@ -4371,6 +4744,7 @@ 2B82B5FF1E82E2490095FB14 /* JSONAllocator.cpp in Sources */, 2B8A78C6228B5D0A008B0A1F /* ParticleSystemDrawableBuilder.cpp in Sources */, 2B82B6B31E82E24A0095FB14 /* PJ_tcea.c in Sources */, + 31833155259112BA005FEF70 /* Geocentric.cpp in Sources */, 2BE1E73C2208B8ED00815D9C /* MaplyDoubleTapDragDelegate.mm in Sources */, 2B82B69C1E82E24A0095FB14 /* PJ_omerc.c in Sources */, 2B8797232203BF7900EF801D /* MaplyComponentObject.mm in Sources */, @@ -4380,6 +4754,8 @@ 2B82B7C31E82E68E0095FB14 /* clipper.cpp in Sources */, 2B82B5EE1E82E2490095FB14 /* priorityq.c in Sources */, 2B82B5F61E82E2490095FB14 /* tessmono.c in Sources */, + 3183315A259112BA005FEF70 /* GeodesicLine.cpp in Sources */, + 3183314C259112BA005FEF70 /* GeoCoords.cpp in Sources */, 2B60F43C24523B5800CF9339 /* MapboxVectorTiles.mm in Sources */, 2B846EDC21F1365000EF2A82 /* pj_apply_gridshift.c in Sources */, 2BC3D6DB220B526D00CE91D0 /* ViewPlacementActiveModel.mm in Sources */, @@ -4388,6 +4764,7 @@ 2B846EEE21F1393900EF2A82 /* dict.c in Sources */, 2B63C461243E44B6002B481C /* MapboxVectorStyleSetC.cpp in Sources */, 2B0D979724490BAD00F64852 /* MapboxVectorStyleCircle.cpp in Sources */, + 3183315B259112BA005FEF70 /* PolarStereographic.cpp in Sources */, 2BE539AC1D249BEF00B60FAD /* AANeptune.cpp in Sources */, 2BE539B41D249BEF00B60FAD /* AAPhysicalMoon.cpp in Sources */, 2B87971B2203BF2E00EF801D /* WGCoordinate.mm in Sources */, @@ -4403,8 +4780,10 @@ 2BC3D6D9220B51C500CE91D0 /* MaplyRenderTarget.mm in Sources */, 2B82B6671E82E24A0095FB14 /* pj_gauss.c in Sources */, 2B82B6541E82E2490095FB14 /* pj_datum_set.c in Sources */, + 3183314D259112BA005FEF70 /* CassiniSoldner.cpp in Sources */, 2B699879228DD36A00C31E3F /* TextureMTL.mm in Sources */, 2B82B6C61E82E24A0095FB14 /* proj_etmerc.c in Sources */, + 31833149259112BA005FEF70 /* Geodesic.cpp in Sources */, 2BC3D6E4220B5ACE00CE91D0 /* VectorData_iOS.mm in Sources */, 2BE539911D249BEF00B60FAD /* AADynamicalTime.cpp in Sources */, 2B3D7E3722873B6D0065FA18 /* BillboardManager.cpp in Sources */, @@ -4416,9 +4795,11 @@ 2B82B6381E82E2490095FB14 /* geocent.c in Sources */, 2BE539B01D249BEF00B60FAD /* AAParallactic.cpp in Sources */, 2B82B66C1E82E24A0095FB14 /* PJ_gnom.c in Sources */, + 31833146259112BA005FEF70 /* PolygonArea.cpp in Sources */, 2B82B6AD1E82E24A0095FB14 /* PJ_somerc.c in Sources */, 2B8797172203B77900EF801D /* MaplyGeomBuilder.mm in Sources */, 2B8A78A122864B25008B0A1F /* SceneGraphManager.cpp in Sources */, + 3183314A259112BA005FEF70 /* AzimuthalEquidistant.cpp in Sources */, 2B82B6191E82E2490095FB14 /* JSONValidator.cpp in Sources */, 2B82B6981E82E24A0095FB14 /* PJ_nzmg.c in Sources */, 2B82B6861E82E24A0095FB14 /* PJ_loxim.c in Sources */, @@ -4457,6 +4838,7 @@ 2B8A78DD228C8509008B0A1F /* MaplyQuadImageLoader.mm in Sources */, 2B82B6891E82E24A0095FB14 /* PJ_mbt_fps.c in Sources */, 2BC3D6CD2203E9FE00CE91D0 /* MaplyScreenLabel.m in Sources */, + 31833143259112BA005FEF70 /* CircularEngine.cpp in Sources */, 2B846EEA21F1381400EF2A82 /* pj_strtod.c in Sources */, 2B3F4529243FD82200F85414 /* SLDExpressions.m in Sources */, 2BC3D6C722025CAC00CE91D0 /* MaplyAnimateTranslation.cpp in Sources */, @@ -4464,8 +4846,10 @@ 2B462EF823A954870050438C /* NSDictionary+StyleRules.m in Sources */, 2BE5399C1D249BEF00B60FAD /* AAGlobe.cpp in Sources */, 2B82B6441E82E2490095FB14 /* PJ_aitoff.c in Sources */, + 31833150259112BA005FEF70 /* NormalGravity.cpp in Sources */, 2B446B1421F79AD00078A975 /* WhirlyGeometry.cpp in Sources */, - 2B8796CE21FFC59D00EF801D /* Dictinary_NSDictionary.mm in Sources */, + 31833151259112BA005FEF70 /* SphericalEngine.cpp in Sources */, + 2B8796CE21FFC59D00EF801D /* Dictionary_NSDictionary.mm in Sources */, 2B8A78DB228B9718008B0A1F /* FontTextureManager_iOS.mm in Sources */, 2B8A78D6228B93D1008B0A1F /* WideVectorDrawableBuilder.cpp in Sources */, 2B8796D121FFC59D00EF801D /* NSDictionary+Stuff.m in Sources */, @@ -4479,6 +4863,7 @@ 2BC3D6D22203EA6000CE91D0 /* MaplySun.mm in Sources */, 2B699874228DD36A00C31E3F /* WrapperMTL.mm in Sources */, 2B82B6601E82E24A0095FB14 /* PJ_eqdc.c in Sources */, + 31833156259112BA005FEF70 /* LambertConformalConic.cpp in Sources */, 2BE1E73F2208BE0F00815D9C /* MaplyTapDelegate.mm in Sources */, 2B8A789E22864758008B0A1F /* LayoutManager.cpp in Sources */, 2B8A78E1228C8530008B0A1F /* MaplyViewController.mm in Sources */, @@ -4511,10 +4896,12 @@ 2B69986C228DD36A00C31E3F /* ProgramMTL.mm in Sources */, 2BE7E7BC221B99FA00E4EFBA /* MaplyQuadLoader.mm in Sources */, 2BC3D6C6220255EA00CE91D0 /* MapView_iOS.mm in Sources */, + 3183315F259112BA005FEF70 /* MagneticModel.cpp in Sources */, 2BE53AB51D249CAF00B60FAD /* SMCalloutView.m in Sources */, 2B82B6BC1E82E24A0095FB14 /* PJ_vandg.c in Sources */, 2B846EDD21F1365000EF2A82 /* pj_apply_vgridshift.c in Sources */, 2BC3D6DF220B550400CE91D0 /* MaplyVariableTarget.mm in Sources */, + 3183314F259112BA005FEF70 /* Rhumb.cpp in Sources */, 2BE1E7532208ECA700815D9C /* MaplyAnnotation.mm in Sources */, 2B8796D021FFC59D00EF801D /* UIImage+Stuff.mm in Sources */, 2BE1E74E2208E8D500815D9C /* MaplyTileSourceNew.mm in Sources */, @@ -4527,6 +4914,7 @@ 2BB8E1FE21FF93CB00154CDC /* MaplyFlatView.cpp in Sources */, 2B8A78BD228B3AF3008B0A1F /* Lighting.cpp in Sources */, 2B8A78612284C408008B0A1F /* BasicDrawableBuilder.cpp in Sources */, + 3183315E259112BA005FEF70 /* AlbersEqualArea.cpp in Sources */, 2B82B6551E82E2490095FB14 /* pj_datums.c in Sources */, 2BE53A8B1D249C8900B60FAD /* DDXMLElementAdditions.m in Sources */, 2BE539A41D249BEF00B60FAD /* AAMoon.cpp in Sources */, @@ -4542,6 +4930,7 @@ 2B82B68C1E82E24A0095FB14 /* PJ_merc.c in Sources */, 2B699880228E26B500C31E3F /* wkDefaultShaders.metal in Sources */, 2B82B61B1E82E2490095FB14 /* JSONWorker.cpp in Sources */, + 31833140259112BA005FEF70 /* Gnomonic.cpp in Sources */, 2BE1E764220A43FD00815D9C /* WGInteractionLayer.mm in Sources */, 2BC3D6D52203EA9C00CE91D0 /* MaplyZoomGestureDelegate.mm in Sources */, 2BE1E7472208CEA300815D9C /* GlobeTapMessage.mm in Sources */, @@ -4550,12 +4939,16 @@ 2B82B6511E82E2490095FB14 /* PJ_collg.c in Sources */, 2B82B6651E82E24A0095FB14 /* pj_fwd.c in Sources */, 2BC3D6CB2203E9D700CE91D0 /* MaplyMoon.mm in Sources */, + 31833148259112BA005FEF70 /* Math.cpp in Sources */, 2B82B6BD1E82E24A0095FB14 /* PJ_vandg2.c in Sources */, + 31833152259112BA005FEF70 /* GeodesicExact.cpp in Sources */, 2BE539A21D249BEF00B60FAD /* AAMars.cpp in Sources */, 2BC3D6CA2203E9AB00CE91D0 /* MaplyPoints.mm in Sources */, 2BE5399F1D249BEF00B60FAD /* AAJewishCalendar.cpp in Sources */, + 31833147259112BA005FEF70 /* Accumulator.cpp in Sources */, 2BE539981D249BEF00B60FAD /* AAEquationOfTime.cpp in Sources */, 2BE539A91D249BEF00B60FAD /* AAMoonPhases.cpp in Sources */, + 3183313F259112BA005FEF70 /* Georef.cpp in Sources */, 2B82B68D1E82E24A0095FB14 /* PJ_mill.c in Sources */, 2B846EDE21F1366400EF2A82 /* PJ_gall.c in Sources */, 2BE1E767220A567500815D9C /* WGViewControllerLayer.mm in Sources */, @@ -4643,6 +5036,7 @@ CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_REPEATED_USE_OF_WEAK = YES_AGGRESSIVE; CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; @@ -4705,6 +5099,7 @@ CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_REPEATED_USE_OF_WEAK = YES_AGGRESSIVE; CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; @@ -4778,6 +5173,7 @@ ../../../common/local_libs/include/, ../../../common/local_libs/clipper, ../../../common/local_libs/glues/include/, + ../../../common/local_libs/GeographicLib/include/, ); INFOPLIST_FILE = WhirlyGlobeMaplyComponent/Info.plist; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; @@ -4832,6 +5228,7 @@ ../../../common/local_libs/include/, ../../../common/local_libs/clipper, ../../../common/local_libs/glues/include/, + ../../../common/local_libs/GeographicLib/include/, ); INFOPLIST_FILE = WhirlyGlobeMaplyComponent/Info.plist; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; diff --git a/ios/library/WhirlyGlobe-MaplyComponent/WhirlyGlobeMaplyComponent.xcodeproj/xcshareddata/xcschemes/WhirlyGlobeMaplyComponent.xcscheme b/ios/library/WhirlyGlobe-MaplyComponent/WhirlyGlobeMaplyComponent.xcodeproj/xcshareddata/xcschemes/WhirlyGlobeMaplyComponent.xcscheme index 576aae8d47..57f32663c0 100644 --- a/ios/library/WhirlyGlobe-MaplyComponent/WhirlyGlobeMaplyComponent.xcodeproj/xcshareddata/xcschemes/WhirlyGlobeMaplyComponent.xcscheme +++ b/ios/library/WhirlyGlobe-MaplyComponent/WhirlyGlobeMaplyComponent.xcodeproj/xcshareddata/xcschemes/WhirlyGlobeMaplyComponent.xcscheme @@ -1,6 +1,6 @@ *__nonnull)uuids; + +/** + Set the representation to use for the matching UUIDs by specifying the UUIDs directly. + + @param repName The representation value to apply, nil to return to the default + @param fallbackRepName The representation to use if there are no matches + @param uuids Array of NSString, the UUIDs to update +*/ +- (void)setRepresentation:(NSString *__nullable)repName + fallbackRepName:(NSString *__nullable)fallbackRepName + ofUUIDs:(NSArray *__nonnull)uuids; + +/** + Set the representation to use for the matching UUIDs by specifying the UUIDs directly. + + @param uuids Array of NSString, the UUIDs to update + @param repName The representation value to apply, nil to return to the default + @param threadMode For MaplyThreadAny we'll do the enable on another thread. For MaplyThreadCurrent we'll block the current thread to finish the enable. MaplyThreadAny is preferred. +*/ +- (void)setRepresentation:(NSString *__nullable)repName + ofUUIDs:(NSArray *__nonnull)uuids + mode:(MaplyThreadMode)threadMode; + +/** + Set the representation to use for the matching UUIDs by specifying the UUIDs directly. + + @param uuids Array of NSString, the UUIDs to update + @param repName The representation value to apply, nil to return to the default + @param fallbackRepName The representation to use if there are no matches + @param threadMode For MaplyThreadAny we'll do the enable on another thread. For MaplyThreadCurrent we'll block the current thread to finish the enable. MaplyThreadAny is preferred. +*/ +- (void)setRepresentation:(NSString *__nullable)repName + fallbackRepName:(NSString *__nullable)fallbackRepName + ofUUIDs:(NSArray *__nonnull)uuids + mode:(MaplyThreadMode)threadMode; + +/** + Set the representation to use for the UUIDs of the given objects. + + @param objects Array of ComponentObject, the UUIDs to update + @param repName The representation value to apply, nil to return to the default + @param fallbackRepName The representation to use if there are no matches +*/ +- (void)setRepresentation:(NSString *__nullable)repName + ofObjects:(NSArray *__nonnull)objects; + +/** + Set the representation to use for the UUIDs of the given objects. + + @param objects Array of ComponentObject, the UUIDs to update + @param repName The representation value to apply, nil to return to the default +*/ +- (void)setRepresentation:(NSString *__nullable)repName + fallbackRepName:(NSString *__nullable)fallbackRepName + ofObjects:(NSArray *__nonnull)objects; + +/** + Set the representation to use for the UUIDs of the given objects. + + @param objects Array of ComponentObject, the UUIDs to update + @param repName The representation value to apply, nil to return to the default + @param fallbackRepName The representation to use if there are no matches + @param threadMode For MaplyThreadAny we'll do the enable on another thread. For MaplyThreadCurrent we'll block the current thread to finish the enable. MaplyThreadAny is preferred. +*/ +- (void)setRepresentation:(NSString *__nullable)repName + fallbackRepName:(NSString *__nullable)fallbackRepName + ofObjects:(NSArray *__nonnull)objects + mode:(MaplyThreadMode)threadMode; + /** Pass a uniform block through to a shader. Only for Metal. @@ -1354,7 +1447,28 @@ @param useCourse Use location services course information as fallback if heading unavailable */ -- (void)startLocationTrackingWithDelegate:(NSObject *__nullable)delegate useHeading:(bool)useHeading useCourse:(bool)useCourse simulate:(bool)simulate; +- (void)startLocationTrackingWithDelegate:(NSObject *__nullable)delegate + useHeading:(bool)useHeading + useCourse:(bool)useCourse; + +/** + Start location tracking + + @param delegate The MaplyLocationTrackerDelegate for receiving location event callbacks + + @param simulator The MaplyLocationSimulatorDelegate for producing locations + + @param simInterval The time interval on which to update + + @param useHeading Use location services heading information (requires physical magnetometer) + + @param useCourse Use location services course information as fallback if heading unavailable + */ +- (void)startLocationTrackingWithDelegate:(NSObject *__nullable)delegate + simulator:(NSObject *__nullable)simulator + simInterval:(NSTimeInterval)simInterval + useHeading:(bool)useHeading + useCourse:(bool)useCourse; /** Return the current location tracker, if there is one. diff --git a/ios/library/WhirlyGlobe-MaplyComponent/include/control/MaplyRenderController.h b/ios/library/WhirlyGlobe-MaplyComponent/include/control/MaplyRenderController.h index ea46cb6d1e..59020be6f1 100644 --- a/ios/library/WhirlyGlobe-MaplyComponent/include/control/MaplyRenderController.h +++ b/ios/library/WhirlyGlobe-MaplyComponent/include/control/MaplyRenderController.h @@ -3,7 +3,7 @@ * WhirlyGlobeMaplyComponent * * Created by Stephen Gifford on 1/19/18. - * Copyright 2012-2018 Saildrone Inc. + * Copyright 2012-2021 Saildrone Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -74,11 +74,11 @@ typedef NS_ENUM(NSInteger, MaplyRenderType) { }; /** - Render Controler Protocol defines the methods required of a render controller. + Render Controller Protocol defines the methods required of a render controller. - The view controllers and offscreen rendere implement this protocol. + The view controllers and offscreen renderers implement this protocol. */ -@protocol MaplyRenderControllerProtocol +@protocol MaplyRenderControllerProtocol /** Set the offset for the screen space objects. @@ -167,6 +167,8 @@ typedef NS_ENUM(NSInteger, MaplyRenderType) { |kMaplyEnable|NSNumber boolean|On by default, but if off then the feature exists, but is not turned on. It can be enabled with enableObjects:| |kMaplyEnableStart|NSNumber|If set, this controls when the resulting objects will be activated.| |kMaplyEnableEnd|NSNumber|If set, this controls when the resulting objects will be deactivated.| + |kMaplyUUID|NSString|Unique ID to match up alternate representations of the same element.| + |kMaplyRepresentation|NSString|Name of the representation presented by this object.| |kMaplyClusterGroup|NSNumber|If set, the screen markers will be clustered together according to the given group ID. Off by default, but 0 is the default cluster.| @@ -204,7 +206,8 @@ typedef NS_ENUM(NSInteger, MaplyRenderType) { |kMaplyZBufferRead|NSNumber boolean|If set this geometry will respect the z buffer. It's off by default, meaning that the geometry will draw on top of anything (respecting the kMaplyDrawPriority).| |kMaplyZBufferWrite|NSNumber boolean|If set this geometry will write to the z buffer. That means following geometry that reads the z buffer will be occluded. This is off by default.| |kMaplyEnable|NSNumber boolean|On by default, but if off then the feature exists, but is not turned on. It can be enabled with enableObjects:| - + |kMaplyUUID|NSString|Unique ID to match up alternate representations of the same element.| + |kMaplyRepresentation|NSString|Name of the representation presented by this object.| @param threadMode MaplyThreadAny is preferred and will use another thread, thus not blocking the one you're on. MaplyThreadCurrent will make the changes immediately, blocking this thread. @@ -247,7 +250,8 @@ typedef NS_ENUM(NSInteger, MaplyRenderType) { |kMaplyEnable|NSNumber boolean|On by default, but if off then the feature exists, but is not turned on. It can be enabled with enableObjects:| |kMaplyEnableStart|NSNumber|If set, this controls when the resulting objects will be activated.| |kMaplyEnableEnd|NSNumber|If set, this controls when the resulting objects will be deactivated.| - + |kMaplyUUID|NSString|Unique ID to match up alternate representations of the same element.| + |kMaplyRepresentation|NSString|Name of the representation presented by this object.| @param threadMode MaplyThreadAny is preferred and will use another thread, thus not blocking the one you're on. MaplyThreadCurrent will make the changes immediately, blocking this thread. @@ -287,8 +291,9 @@ typedef NS_ENUM(NSInteger, MaplyRenderType) { |kMaplyZBufferRead|NSNumber boolean|If set this geometry will respect the z buffer. It's off by default, meaning that the geometry will draw on top of anything (respecting the kMaplyDrawPriority).| |kMaplyZBufferWrite|NSNumber boolean|If set this geometry will write to the z buffer. That means following geometry that reads the z buffer will be occluded. This is off by default.| |kMaplyEnable|NSNumber boolean|On by default, but if off then the feature exists, but is not turned on. It can be enabled with enableObjects:| - - + |kMaplyUUID|NSString|Unique ID to match up alternate representations of the same element.| + |kMaplyRepresentation|NSString|Name of the representation presented by this object.| + @param threadMode MaplyThreadAny is preferred and will use another thread, thus not blocking the one you're on. MaplyThreadCurrent will make the changes immediately, blocking this thread. @@ -329,8 +334,9 @@ typedef NS_ENUM(NSInteger, MaplyRenderType) { |kMaplyZBufferWrite|NSNumber boolean|If set this geometry will write to the z buffer. That means following geometry that reads the z buffer will be occluded. This is off by default.| |kMaplyEnable|NSNumber boolean|On by default, but if off then the feature exists, but is not turned on. It can be enabled with enableObjects:| |kMaplySelectable|NSNumber boolean|Off by default. When enabled, the vector feature will be selectable by a user.| - - + |kMaplyUUID|NSString|Unique ID to match up alternate representations of the same element.| + |kMaplyRepresentation|NSString|Name of the representation presented by this object.| + @param threadMode MaplyThreadAny is preferred and will use another thread, thus not blocking the one you're on. MaplyThreadCurrent will make the changes immediately, blocking this thread. @@ -364,8 +370,9 @@ typedef NS_ENUM(NSInteger, MaplyRenderType) { |kMaplyViewableCenterZ|MaplyCoordinate3dWrapper|When evaulating min/max viewer distance, we'll use this center Z coordinate.| |kMaplyDrawPriority|NSNumber|Geometry is sorted by this value before being drawn. This ensures that some objects can come out on top of others. By default this is kMaplyVectorDrawPriorityDefault.| |kMaplyEnable|NSNumber boolean|On by default, but if off then the feature exists, but is not turned on. It can be enabled with enableObjects:| - - + |kMaplyUUID|NSString|Unique ID to match up alternate representations of the same element.| + |kMaplyRepresentation|NSString|Name of the representation presented by this object.| + @param threadMode MaplyThreadAny is preferred and will use another thread, thus not blocking the one you're on. MaplyThreadCurrent will make the changes immediately, blocking this thread. @@ -399,8 +406,9 @@ typedef NS_ENUM(NSInteger, MaplyRenderType) { |kMaplyViewableCenterZ|MaplyCoordinate3dWrapper|When evaulating min/max viewer distance, we'll use this center Z coordinate.| |kMaplyDrawPriority|NSNumber|Geometry is sorted by this value before being drawn. This ensures that some objects can come out on top of others. By default this is kMaplyVectorDrawPriorityDefault.| |kMaplyEnable|NSNumber boolean|On by default, but if off then the feature exists, but is not turned on. It can be enabled with enableObjects:| - - + |kMaplyUUID|NSString|Unique ID to match up alternate representations of the same element.| + |kMaplyRepresentation|NSString|Name of the representation presented by this object.| + @param threadMode MaplyThreadAny is preferred and will use another thread, thus not blocking the one you're on. MaplyThreadCurrent will make the changes immediately, blocking this thread. @@ -420,8 +428,9 @@ typedef NS_ENUM(NSInteger, MaplyRenderType) { |:--|:---|:----------| |kMaplySelectable|NSNumber boolean|Off by default. When enabled, the vector feature will be selectable by a user.| |kMaplyEnable|NSNumber boolean|On by default, but if off then the feature exists, but is not turned on. It can be enabled with enableObjects:| - - + |kMaplyUUID|NSString|Unique ID to match up alternate representations of the same element.| + |kMaplyRepresentation|NSString|Name of the representation presented by this object.| + @param threadMode MaplyThreadAny is preferred and will use another thread, thus not blocking the one you're on. MaplyThreadCurrent will make the changes immediately, blocking this thread. @@ -441,8 +450,9 @@ typedef NS_ENUM(NSInteger, MaplyRenderType) { |:--|:---|:----------| |kMaplySelectable|NSNumber boolean|Off by default. When enabled, the vector feature will be selectable by a user.| |kMaplyEnable|NSNumber boolean|On by default, but if off then the feature exists, but is not turned on. It can be enabled with enableObjects:| - - + |kMaplyUUID|NSString|Unique ID to match up alternate representations of the same element.| + |kMaplyRepresentation|NSString|Name of the representation presented by this object.| + @param threadMode MaplyThreadAny is preferred and will use another thread, thus not blocking the one you're on. MaplyThreadCurrent will make the changes immediately, blocking this thread. @@ -477,8 +487,9 @@ typedef NS_ENUM(NSInteger, MaplyRenderType) { |kMaplyZBufferRead|NSNumber boolean|If set this geometry will respect the z buffer. It's on by default, meaning that the geometry can be occluded by things drawn first.| |kMaplyZBufferWrite|NSNumber boolean|If set this geometry will write to the z buffer. That means following geometry that reads the z buffer will be occluded. This is off by default.| |kMaplyEnable|NSNumber boolean|On by default, but if off then the feature exists, but is not turned on. It can be enabled with enableObjects:| - - + |kMaplyUUID|NSString|Unique ID to match up alternate representations of the same element.| + |kMaplyRepresentation|NSString|Name of the representation presented by this object.| + @param threadMode MaplyThreadAny is preferred and will use another thread, thus not blocking the one you're on. MaplyThreadCurrent will make the changes immediately, blocking this thread. @@ -513,8 +524,9 @@ typedef NS_ENUM(NSInteger, MaplyRenderType) { |kMaplyZBufferWrite|NSNumber boolean|If set this geometry will write to the z buffer. That means following geometry that reads the z buffer will be occluded. This is off by default.| |kMaplyEnable|NSNumber boolean|On by default, but if off then the feature exists, but is not turned on. It can be enabled with enableObjects:| |kMaplyShader|NSString|If set, this is the name of the MaplyShader to use when rendering the sticker(s).| - - + |kMaplyUUID|NSString|Unique ID to match up alternate representations of the same element.| + |kMaplyRepresentation|NSString|Name of the representation presented by this object.| + @param threadMode MaplyThreadAny is preferred and will use another thread, thus not blocking the one you're on. MaplyThreadCurrent will make the changes immediately, blocking this thread. @@ -648,8 +660,9 @@ typedef NS_ENUM(NSInteger, MaplyRenderType) { |kMaplyZBufferRead|NSNumber boolean|If set this geometry will respect the z buffer. It's on by default, meaning that it can be occluded by geometry coming before it.| |kMaplyZBufferWrite|NSNumber boolean|If set this geometry will write to the z buffer. That means following geometry that reads the z buffer will be occluded. This is off by default.| |kMaplyEnable|NSNumber boolean|On by default, but if off then the feature exists, but is not turned on. It can be enabled with enableObjects:| - - + |kMaplyUUID|NSString|Unique ID to match up alternate representations of the same element.| + |kMaplyRepresentation|NSString|Name of the representation presented by this object.| + @return Returns a MaplyComponentObject, which can be used to make modifications or delete the objects created. */ - (MaplyComponentObject *__nullable)addLoftedPolys:(NSArray *__nonnull)polys desc:(NSDictionary *__nullable)desc mode:(MaplyThreadMode)threadMode; @@ -681,8 +694,9 @@ typedef NS_ENUM(NSInteger, MaplyRenderType) { |kMaplyZBufferRead|NSNumber boolean|If set this geometry will respect the z buffer. It's on by default, meaning that it can be occluded by geometry coming before it.| |kMaplyZBufferWrite|NSNumber boolean|If set this geometry will write to the z buffer. That means following geometry that reads the z buffer will be occluded. This is off by default.| |kMaplyEnable|NSNumber boolean|On by default, but if off then the feature exists, but is not turned on. It can be enabled with enableObjects:| - - + |kMaplyUUID|NSString|Unique ID to match up alternate representations of the same element.| + |kMaplyRepresentation|NSString|Name of the representation presented by this object.| + @return Returns a MaplyComponentObject, which can be used to make modifications or delete the objects created. */ - (MaplyComponentObject *__nullable)addPoints:(NSArray * __nonnull)points desc:(NSDictionary *__nullable)desc mode:(MaplyThreadMode)threadMode; @@ -775,6 +789,16 @@ typedef NS_ENUM(NSInteger, MaplyRenderType) { */ - (void)removeRenderTarget:(MaplyRenderTarget * _Nonnull)renderTarget; +/** + Set up the the mask render target. We use it to keep one set of features from render on top of another set. + */ +- (void)startMaskTarget:(NSNumber * __nullable)scale; + +/** + Turn off the render target for masks. + */ +- (void)stopMaskTarget; + /** Normally the layout layer runs periodically if you change something or when you move around. You can ask it to run ASAP right here. Layout runs on its own thread, so there may still be a delay. @@ -822,6 +846,30 @@ typedef NS_ENUM(NSInteger, MaplyRenderType) { */ - (void)enableObjects:(NSArray *__nonnull)theObjs mode:(MaplyThreadMode)threadMode; +/** + Set the representation to use for one or more UUIDs + + @param uuids Array of NSString UUIDs to update + @param repName The representation name to apply, nil to return to the default + @param threadMode For MaplyThreadAny we'll do the enable on another thread. For MaplyThreadCurrent we'll block the current thread to finish the enable. MaplyThreadAny is preferred. + */ +- (void)setRepresentation:(NSString *__nullable)repName + ofUUIDs:(NSArray *__nonnull)uuids + mode:(MaplyThreadMode)threadMode; + +/** + Set the representation to use for one or more UUIDs + + @param uuids Array of NSString UUIDs to update + @param repName The representation name to apply, nil to return to the default + @param fallbackRepName The representation to use of no item with `repName` exists. + @param threadMode For MaplyThreadAny we'll do the enable on another thread. For MaplyThreadCurrent we'll block the current thread to finish the enable. MaplyThreadAny is preferred. + */ +- (void)setRepresentation:(NSString *__nullable)repName + fallbackRepName:(NSString *__nullable)fallbackRepName + ofUUIDs:(NSArray *__nonnull)uuids + mode:(MaplyThreadMode)threadMode; + /** Pass a uniform block through to a shader. Only for Metal. diff --git a/ios/library/WhirlyGlobe-MaplyComponent/include/control/WhirlyGlobeViewController.h b/ios/library/WhirlyGlobe-MaplyComponent/include/control/WhirlyGlobeViewController.h index 7d1e9561cb..ecad7387c6 100644 --- a/ios/library/WhirlyGlobe-MaplyComponent/include/control/WhirlyGlobeViewController.h +++ b/ios/library/WhirlyGlobe-MaplyComponent/include/control/WhirlyGlobeViewController.h @@ -669,6 +669,15 @@ */ - (bool)geoPointFromScreen:(CGPoint)screenPt geoCoord:(MaplyCoordinate *__nonnull)geoCoord; +/** + Calculate a geo coordinate from a point on the screen. + + @param screenPt Location on the screen. + + @return The corresponding MaplyCoordinate wrapped in an NSValue if the point was on the globe, nil otherwise. + */ +- (nullable NSValue *)geoPointFromScreen:(CGPoint)screenPt; + /** Calculate a geocentric coordinate from a point on the screen. diff --git a/ios/library/WhirlyGlobe-MaplyComponent/include/helpers/MaplyLocationTracker.h b/ios/library/WhirlyGlobe-MaplyComponent/include/helpers/MaplyLocationTracker.h index e2a19a3515..3ca2210d28 100644 --- a/ios/library/WhirlyGlobe-MaplyComponent/include/helpers/MaplyLocationTracker.h +++ b/ios/library/WhirlyGlobe-MaplyComponent/include/helpers/MaplyLocationTracker.h @@ -37,11 +37,10 @@ typedef enum {MaplyLocationLockNone, MaplyLocationLockNorthUp, MaplyLocationLock /* Implement the MaplyLocationTrackerDelegate protocol to receive location services callbacks. - - This is to handle problems / failures further up the line. */ @protocol MaplyLocationTrackerDelegate +// This is to handle problems / failures further up the line. - (void) locationManager:(CLLocationManager * __nonnull)manager didFailWithError:(NSError * __nonnull)error; - (void) locationManager:(CLLocationManager * __nonnull)manager didChangeAuthorizationStatus:(CLAuthorizationStatus)status; @@ -50,8 +49,19 @@ typedef enum {MaplyLocationLockNone, MaplyLocationLockNorthUp, MaplyLocationLock - (void) updateLocation:(CLLocation * __nonnull)location; +@end + +/* + Implement the MaplyLocationSimulatorDelegate protocol to provide simulated locations +*/ +@protocol MaplyLocationSimulatorDelegate + - (MaplyLocationTrackerSimulationPoint)getSimulationPoint; +@optional + +- (bool)hasValidLocation; + @end /* @@ -64,6 +74,19 @@ typedef enum {MaplyLocationLockNone, MaplyLocationLockNorthUp, MaplyLocationLock /// Exposes MaplyLocationTracker's location manager for use elsewhere @property (nonatomic, readonly, nullable) CLLocationManager *locationManager; +/** + MaplyLocationTracker constructor + + @param viewC The globe or map view controller + + @param useHeading Use location services heading information (requires physical magnetometer) + + @param useCourse Use location services course information as fallback if heading unavailable + */ +- (nonnull instancetype)initWithViewC:(MaplyBaseViewController *__nullable)viewC + useHeading:(bool)useHeading + useCourse:(bool)useCourse; + /** MaplyLocationTracker constructor @@ -75,7 +98,30 @@ typedef enum {MaplyLocationLockNone, MaplyLocationLockNorthUp, MaplyLocationLock @param useCourse Use location services course information as fallback if heading unavailable */ -- (nonnull instancetype)initWithViewC:(MaplyBaseViewController *__nullable)viewC delegate:(NSObject *__nullable)delegate useHeading:(bool)useHeading useCourse:(bool)useCourse simulate:(bool)simulate; +- (nonnull instancetype)initWithViewC:(MaplyBaseViewController *__nullable)viewC + delegate:(NSObject *__nullable)delegate + useHeading:(bool)useHeading + useCourse:(bool)useCourse; + +/** + MaplyLocationTracker constructor + + @param viewC The globe or map view controller + + @param delegate The MaplyLocationTrackerDelegate for receiving location event callbacks + + @param simulator The MaplyLocationSimulatorDelegate for generating simulated locations + + @param useHeading Use location services heading information (requires physical magnetometer) + + @param useCourse Use location services course information as fallback if heading unavailable + */ +- (nonnull instancetype)initWithViewC:(MaplyBaseViewController *__nullable)viewC + delegate:(NSObject *__nullable)delegate + simulator:(NSObject *__nullable)simulator + simInterval:(NSTimeInterval)simInterval + useHeading:(bool)useHeading + useCourse:(bool)useCourse; /** Min/max visibility for the marker assigned to follow location. @@ -108,5 +154,21 @@ typedef enum {MaplyLocationLockNone, MaplyLocationLockNorthUp, MaplyLocationLock */ - (MaplyCoordinate)getLocation; +/** + Set the current simulated location. + */ +- (void) setLocation:(MaplyLocationTrackerSimulationPoint)point + altitude:(double)altitude; + +/** + Set the current simulated location. + */ +- (void) setLocation:(MaplyLocationTrackerSimulationPoint)point + altitude:(double)altitude + horizontalAccuracy:(double)horizontalAccuracy + verticalAccuracy:(double)verticalAccuracy + speed:(double)speed; +; + @end diff --git a/ios/library/WhirlyGlobe-MaplyComponent/include/loading/MaplyQuadImageLoader.h b/ios/library/WhirlyGlobe-MaplyComponent/include/loading/MaplyQuadImageLoader.h index b84846724c..e2612147dd 100644 --- a/ios/library/WhirlyGlobe-MaplyComponent/include/loading/MaplyQuadImageLoader.h +++ b/ios/library/WhirlyGlobe-MaplyComponent/include/loading/MaplyQuadImageLoader.h @@ -106,6 +106,10 @@ The difference is we'll use a direct PNG reader to tease it out, rather than UIImage. */ @interface MaplyRawPNGImageLoaderInterpreter : MaplyImageLoaderInterpreter + +/// In some cases we just want to pick values out of the input +- (void)addMappingFrom:(int)inVal to:(int)outVal; + @end /// Name of the shared MaplyRemoteTileFetcher diff --git a/ios/library/WhirlyGlobe-MaplyComponent/include/loading/MaplyQuadLoader.h b/ios/library/WhirlyGlobe-MaplyComponent/include/loading/MaplyQuadLoader.h index 8e95268a28..cc0a516381 100644 --- a/ios/library/WhirlyGlobe-MaplyComponent/include/loading/MaplyQuadLoader.h +++ b/ios/library/WhirlyGlobe-MaplyComponent/include/loading/MaplyQuadLoader.h @@ -54,6 +54,9 @@ /// Return the first data object. You're probably only expecting the one. - (id __nullable)getFirstData; +/// Set when the QuadLoader cancels a tile. You can check this in your dataForTile: +- (bool)isCancelled; + /// If this is set, the tile failed to parse /// You can set it and the system will deal with the results @property (nonatomic,strong) NSError * __nullable error; diff --git a/ios/library/WhirlyGlobe-MaplyComponent/include/loading/MaplyQuadSampler.h b/ios/library/WhirlyGlobe-MaplyComponent/include/loading/MaplyQuadSampler.h index 4be565a405..bdc4307ebe 100644 --- a/ios/library/WhirlyGlobe-MaplyComponent/include/loading/MaplyQuadSampler.h +++ b/ios/library/WhirlyGlobe-MaplyComponent/include/loading/MaplyQuadSampler.h @@ -34,7 +34,7 @@ /// The coordinate system we'll be sampling from. @property (nonatomic,nonnull,strong) MaplyCoordinateSystem *coordSys; -/// Min zoom level for sampling +/// Min zoom level for sampling. Don't set this to anything other than 0 or 1 @property (nonatomic) int minZoom; /// Max zoom level for sampling diff --git a/ios/library/WhirlyGlobe-MaplyComponent/include/loading/MaplySimpleTileFetcher.h b/ios/library/WhirlyGlobe-MaplyComponent/include/loading/MaplySimpleTileFetcher.h index 6175f36618..7c7858446d 100644 --- a/ios/library/WhirlyGlobe-MaplyComponent/include/loading/MaplySimpleTileFetcher.h +++ b/ios/library/WhirlyGlobe-MaplyComponent/include/loading/MaplySimpleTileFetcher.h @@ -68,3 +68,21 @@ - (void)shutdown; @end + +// Internal object used by the QuadImageLoader to generate tile load info +@interface MaplySimpleTileInfo : NSObject + +// Initialize with a min/max zoom +- (instancetype __nonnull)initWithMinZoom:(int)inMinZoom maxZoom:(int)inMaxZoom; + +@end + + +// Encapsulates a single tile load request +@interface MaplySimpleTileFetchInfo : NSObject + +@property (nonatomic,assign) int x; +@property (nonatomic,assign) int y; +@property (nonatomic,assign) int level; + +@end diff --git a/ios/library/WhirlyGlobe-MaplyComponent/include/math/MaplyCoordinate.h b/ios/library/WhirlyGlobe-MaplyComponent/include/math/MaplyCoordinate.h index db955c6e3e..54468cbb6d 100644 --- a/ios/library/WhirlyGlobe-MaplyComponent/include/math/MaplyCoordinate.h +++ b/ios/library/WhirlyGlobe-MaplyComponent/include/math/MaplyCoordinate.h @@ -117,6 +117,21 @@ static const MaplyBoundingBox kMaplyNullBoundingBox = { .ur = {.x = FLT_MIN, .y = FLT_MIN} }; +/** + A category that uses NSValue to store MaplyCoordinate data + */ +@interface NSValue (MaplyCoordinate) ++ (instancetype)valueWithMaplyCoordinate:(MaplyCoordinate)value; +@property (readonly) MaplyCoordinate maplyCoordinateValue; +@end + +/** + A category that uses NSValue to store MaplyCoordinateD data + */ +@interface NSValue (MaplyCoordinateD) ++ (instancetype)valueWithMaplyCoordinateD:(MaplyCoordinateD)value; +@property (readonly) MaplyCoordinateD maplyCoordinateDValue; +@end /** A category that uses NSValue to store MaplyBoundingBox data @@ -269,6 +284,22 @@ bool MaplyBoundingBoxContains(MaplyBoundingBox bbox, MaplyCoordinate c); */ MaplyBoundingBox MaplyBoundingBoxFromLocations(const CLLocationCoordinate2D locs[], unsigned int numLocs); +/** + Set up a bounding box from a list of 2D coordinates + */ +MaplyBoundingBox MaplyBoundingBoxFromCoordinates(const MaplyCoordinate coords[], unsigned int numCoords); +MaplyBoundingBox MaplyBoundingBoxFromCoordinatesD(const MaplyCoordinateD coords[], unsigned int numCoords); +MaplyBoundingBoxD MaplyBoundingBoxDFromCoordinates(const MaplyCoordinate coords[], unsigned int numCoords); +MaplyBoundingBoxD MaplyBoundingBoxDFromCoordinatesD(const MaplyCoordinateD coords[], unsigned int numCoords); + +/** + Expand a bounding box with a list of 2D coordinates + */ +MaplyBoundingBox MaplyBoundingBoxAddCoordinates(MaplyBoundingBox box, const MaplyCoordinate coords[], unsigned int numCoords); +MaplyBoundingBox MaplyBoundingBoxAddCoordinatesD(MaplyBoundingBox box, const MaplyCoordinateD coords[], unsigned int numCoords); +MaplyBoundingBoxD MaplyBoundingBoxDAddCoordinates(MaplyBoundingBoxD box, const MaplyCoordinate coords[], unsigned int numCoords); +MaplyBoundingBoxD MaplyBoundingBoxDAddCoordinatesD(MaplyBoundingBoxD box, const MaplyCoordinateD coords[], unsigned int numCoords); + /** Return the intersection of two bounding boxes. */ diff --git a/ios/library/WhirlyGlobe-MaplyComponent/include/private/MaplyBaseInteractionLayer_private.h b/ios/library/WhirlyGlobe-MaplyComponent/include/private/MaplyBaseInteractionLayer_private.h index 0641c1e843..0c47c3829b 100644 --- a/ios/library/WhirlyGlobe-MaplyComponent/include/private/MaplyBaseInteractionLayer_private.h +++ b/ios/library/WhirlyGlobe-MaplyComponent/include/private/MaplyBaseInteractionLayer_private.h @@ -3,7 +3,7 @@ * MaplyComponent * * Created by Steve Gifford on 12/14/12. - * Copyright 2012-2019 mousebird consulting + * Copyright 2012-2021 mousebird consulting * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -48,7 +48,7 @@ NSArray *layerThreads; // Used to track groups of low level objects and vectors - WhirlyKit::ComponentManager_iOS *compManager; + WhirlyKit::ComponentManager_iOSRef compManager; std::mutex imageLock; // Used to track textures @@ -57,6 +57,10 @@ // Texture atlas manager MaplyTextureAtlasGroup *atlasGroup; + // If set, the render target used for masking features against each other + WhirlyKit::SimpleIdentity maskRenderTargetID; + WhirlyKit::SimpleIdentity maskTexID; + /// Active shaders NSMutableArray *shaders; @@ -154,6 +158,18 @@ // Disable objects, but just generate the changes don't flush them - (void)disableObjects:(NSArray *__nonnull)userObjs changes:(WhirlyKit::ChangeSet &)changes; +// Set the representation to use for the specified UUIDs +- (void)setRepresentation:(NSString *__nullable)repName + fallbackRepName:(NSString *__nullable)fallbackRepName + ofUUIDs:(NSArray *__nonnull)uuids + mode:(MaplyThreadMode)threadMode; + +// Generate changes for setting the representation to use for the specified UUIDs +- (void)setRepresentation:(NSString *__nullable)repName + fallbackRepName:(NSString *__nullable)fallbackRepName + ofUUIDs:(NSArray *__nonnull)uuids + changes:(WhirlyKit::ChangeSet &)changes; + // Pass through a uniform block - (void)setUniformBlock:(NSData *__nonnull)uniBlock buffer:(int)bufferID forObjects:(NSArray *__nonnull)compObjs mode:(MaplyThreadMode)threadMode; diff --git a/ios/library/WhirlyGlobe-MaplyComponent/include/private/MaplyBaseViewController_private.h b/ios/library/WhirlyGlobe-MaplyComponent/include/private/MaplyBaseViewController_private.h index 9fc99d1699..22bb964b2b 100644 --- a/ios/library/WhirlyGlobe-MaplyComponent/include/private/MaplyBaseViewController_private.h +++ b/ios/library/WhirlyGlobe-MaplyComponent/include/private/MaplyBaseViewController_private.h @@ -49,7 +49,7 @@ // List of annotations we're tracking for location NSMutableArray *annotations; - + /// View Placement logic used to move annotations around WhirlyKit::ViewPlacementActiveModelRef viewPlacementModel; diff --git a/ios/library/WhirlyGlobe-MaplyComponent/include/private/MaplyComponentObject_private.h b/ios/library/WhirlyGlobe-MaplyComponent/include/private/MaplyComponentObject_private.h index c7d7e06d9d..02a4ea4c2f 100644 --- a/ios/library/WhirlyGlobe-MaplyComponent/include/private/MaplyComponentObject_private.h +++ b/ios/library/WhirlyGlobe-MaplyComponent/include/private/MaplyComponentObject_private.h @@ -35,6 +35,8 @@ WhirlyKit::ComponentObject_iOSRef contents; } -- (id)initWithRef:(WhirlyKit::ComponentObject_iOSRef)compObj; +- (id __nullable)initWithRef:(WhirlyKit::ComponentObject_iOSRef)compObj; + +- (NSString *__nullable)getUUID; @end diff --git a/ios/library/WhirlyGlobe-MaplyComponent/include/private/MaplyGeomModel_private.h b/ios/library/WhirlyGlobe-MaplyComponent/include/private/MaplyGeomModel_private.h index 9c001ed094..877fe2006d 100644 --- a/ios/library/WhirlyGlobe-MaplyComponent/include/private/MaplyGeomModel_private.h +++ b/ios/library/WhirlyGlobe-MaplyComponent/include/private/MaplyGeomModel_private.h @@ -56,7 +56,10 @@ class GeomStringWrapper - (void)asRawGeometry:(std::vector &)rawGeom withTexMapping:(const std::vector &)texFileMap; // Return the ID for or generate a base model in the Geometry Manager -- (WhirlyKit::SimpleIdentity)getBaseModel:(MaplyBaseInteractionLayer *)inLayer fontTexManager:(WhirlyKit::FontTextureManager_iOS *)fontTexManager compObj:(MaplyComponentObject *)compObj mode:(MaplyThreadMode)threadMode; +- (WhirlyKit::SimpleIdentity)getBaseModel:(MaplyBaseInteractionLayer *)inLayer + fontTexManager:(const WhirlyKit::FontTextureManager_iOSRef &)fontTexManager + compObj:(MaplyComponentObject *)compObj + mode:(MaplyThreadMode)threadMode; @end diff --git a/ios/library/WhirlyGlobe-MaplyComponent/include/private/MaplyQuadLoader_private.h b/ios/library/WhirlyGlobe-MaplyComponent/include/private/MaplyQuadLoader_private.h index 64b37c8299..173ee71336 100644 --- a/ios/library/WhirlyGlobe-MaplyComponent/include/private/MaplyQuadLoader_private.h +++ b/ios/library/WhirlyGlobe-MaplyComponent/include/private/MaplyQuadLoader_private.h @@ -66,6 +66,9 @@ // Force a reload of all tiles in a bounding box - (void)reloadAreas:(NSArray* __nullable)bounds; +// Called on the layer thread. Recalculates loading priorites and sends those to the fetcher. +- (void)updatePriorities; + @end @interface MaplyLoaderReturn() diff --git a/ios/library/WhirlyGlobe-MaplyComponent/include/private/MaplyRenderController_private.h b/ios/library/WhirlyGlobe-MaplyComponent/include/private/MaplyRenderController_private.h index 8f85d86786..f593508f24 100644 --- a/ios/library/WhirlyGlobe-MaplyComponent/include/private/MaplyRenderController_private.h +++ b/ios/library/WhirlyGlobe-MaplyComponent/include/private/MaplyRenderController_private.h @@ -93,6 +93,10 @@ /// Shared tile fetcher used by default for loaders std::vector tileFetchers; + + // Used for masking features against each other + MaplyTexture *maskTex; + MaplyRenderTarget *maskRenderTarget; /// Number of simultaneous tile fetcher connections (per tile fetcher) int tileFetcherConnections; diff --git a/ios/library/WhirlyGlobe-MaplyComponent/include/private/MaplyVectorObject_private.h b/ios/library/WhirlyGlobe-MaplyComponent/include/private/MaplyVectorObject_private.h index 39ec505a11..a03f1850d7 100644 --- a/ios/library/WhirlyGlobe-MaplyComponent/include/private/MaplyVectorObject_private.h +++ b/ios/library/WhirlyGlobe-MaplyComponent/include/private/MaplyVectorObject_private.h @@ -31,9 +31,9 @@ } // Construct as a wrapper -- (id)initWithRef:(WhirlyKit::VectorObjectRef)vecObj; +- (id)initWithRef:(const WhirlyKit::VectorObjectRef&)vecObj; // Construct a vector object from the Vector DB raw format -- (void)addShape:(WhirlyKit::VectorShapeRef)shape; +- (void)addShape:(const WhirlyKit::VectorShapeRef&)shape; @end diff --git a/ios/library/WhirlyGlobe-MaplyComponent/include/private/MaplyVectorStyle_private.h b/ios/library/WhirlyGlobe-MaplyComponent/include/private/MaplyVectorStyle_private.h index 916aee37c9..8d00520ed4 100644 --- a/ios/library/WhirlyGlobe-MaplyComponent/include/private/MaplyVectorStyle_private.h +++ b/ios/library/WhirlyGlobe-MaplyComponent/include/private/MaplyVectorStyle_private.h @@ -3,7 +3,7 @@ * WhirlyGlobe-MaplyComponent * * Created by Steve Gifford on 1/3/14. -* Copyright 2011-2017 mousebird consulting +* Copyright 2011-2021 mousebird consulting * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -38,6 +38,30 @@ @end +// This wraps C++ style implementations to be called from ObjC +@interface MaplyVectorStyleReverseWrapper: NSObject + +/// The C++ style we're wrapping +- (id __nonnull)initWithCStyle:(WhirlyKit::VectorStyleImplRef)vectorStyle; + +/// Unique Identifier for this style +- (long long) uuid; + +/// Category used for sorting +- (NSString * _Nullable) getCategory; + +/// Set if this geometry is additive (e.g. sticks around) rather than replacement +- (bool) geomAdditive; + +/// Construct objects related to this style based on the input data. +- (void)buildObjects:(NSArray * _Nonnull)vecObjs + forTile:(MaplyVectorTileData * __nonnull)tileData + viewC:(NSObject * _Nonnull)viewC + desc:(NSDictionary * _Nullable)desc; +; + +@end + namespace WhirlyKit { @@ -45,41 +69,51 @@ namespace WhirlyKit class MapboxVectorStyleSetImpl_iOS : public MapboxVectorStyleSetImpl { public: - MapboxVectorStyleSetImpl_iOS(Scene *scene,CoordSystem *coordSys,VectorStyleSettingsImplRef settings); + MapboxVectorStyleSetImpl_iOS(Scene *_Nonnull scene, + CoordSystem *_Nonnull coordSys, + const VectorStyleSettingsImplRef &settings); ~MapboxVectorStyleSetImpl_iOS(); - NSObject * __weak viewC; + NSObject *_Nullable __weak viewC; /// Parse the style set - virtual bool parse(PlatformThreadInfo *inst,DictionaryRef dict) override; + virtual bool parse(PlatformThreadInfo *_Nullable inst, + const DictionaryRef &dict) override; /// Local platform implementation for generating a circle and adding it as a texture - virtual SimpleIdentity makeCircleTexture(PlatformThreadInfo *inst, + virtual SimpleIdentity makeCircleTexture(PlatformThreadInfo *_Nullable inst, double radius, const RGBAColor &fillColor, const RGBAColor &strokeColor, - float strokeWidth,Point2f *circleSize) override; + float strokeWidth, + Point2f *_Nullable circleSize) override; /// Local platform implementation for generating a repeating line texture - virtual SimpleIdentity makeLineTexture(PlatformThreadInfo *inst,const std::vector &dashComponents) override; + virtual SimpleIdentity makeLineTexture(PlatformThreadInfo *_Nullable inst, + const std::vector &dashComponents) override; /// Make platform specific label info object (ideally we're caching these) - virtual LabelInfoRef makeLabelInfo(PlatformThreadInfo *inst,const std::vector &fontName,float fontSize) override; + virtual LabelInfoRef makeLabelInfo(PlatformThreadInfo *_Nullable inst, + const std::vector &fontName, + float fontSize) override; /// Create a local platform label (fonts are local, and other stuff) - virtual SingleLabelRef makeSingleLabel(PlatformThreadInfo *inst,const std::string &text) override; + virtual SingleLabelRef makeSingleLabel(PlatformThreadInfo *_Nullable inst, + const std::string &text) override; /// Create a platform specific variant of the component object - ComponentObjectRef makeComponentObject(PlatformThreadInfo *inst) override; + ComponentObjectRef makeComponentObject(PlatformThreadInfo *_Nullable inst, const Dictionary *_Nullable desc) override; /// Tie a selection ID to the given vector object - void addSelectionObject(SimpleIdentity selectID,VectorObjectRef vecObj,ComponentObjectRef compObj) override; + void addSelectionObject(SimpleIdentity selectID,const VectorObjectRef &vecObj,const ComponentObjectRef &compObj) override; /// Return the width of the given line of text - double calculateTextWidth(PlatformThreadInfo *threadInfo,LabelInfoRef labelInfo,const std::string &testStr) override; + double calculateTextWidth(PlatformThreadInfo *_Nullable threadInfo, + const LabelInfoRef &labelInfo, + const std::string &testStr) override; /// Add a sprite sheet - void addSprites(MapboxVectorStyleSpritesRef newSprites,MaplyTexture *tex); + void addSprites(MapboxVectorStyleSpritesRef newSprites,MaplyTexture *_Nonnull tex); // Textures we're holding on to (so they don't get released) std::vector textures; @@ -93,28 +127,29 @@ typedef std::shared_ptr MapboxVectorStyleSetImpl_i class VectorStyleDelegateWrapper : public VectorStyleDelegateImpl { public: - VectorStyleDelegateWrapper(NSObject *viewC,NSObject *delegate); - - virtual std::vector stylesForFeature(PlatformThreadInfo *inst, + VectorStyleDelegateWrapper(NSObject *_Nonnull viewC, + NSObject *_Nullable delegate); + + virtual std::vector stylesForFeature(PlatformThreadInfo *_Nullable inst, const Dictionary &attrs, const QuadTreeIdentifier &tileID, const std::string &layerName) override; - - virtual bool layerShouldDisplay(PlatformThreadInfo *inst, + + virtual bool layerShouldDisplay(PlatformThreadInfo *_Nullable inst, const std::string &name, const QuadTreeNew::Node &tileID) override; - virtual VectorStyleImplRef styleForUUID(PlatformThreadInfo *inst,long long uuid) override; + virtual VectorStyleImplRef styleForUUID(PlatformThreadInfo *_Nullable inst,long long uuid) override; - virtual std::vector allStyles(PlatformThreadInfo *inst) override; + virtual std::vector allStyles(PlatformThreadInfo *_Nullable inst) override; - virtual VectorStyleImplRef backgroundStyle(PlatformThreadInfo *inst) const override; + virtual VectorStyleImplRef backgroundStyle(PlatformThreadInfo *_Nullable inst) const override; + + virtual RGBAColorRef backgroundColor(PlatformThreadInfo *_Nullable inst,double zoom) override; - virtual RGBAColorRef backgroundColor(PlatformThreadInfo *inst,double zoom) override; - protected: - NSObject * __weak viewC; - NSObject *delegate; + NSObject *_Nullable __weak viewC; + NSObject *_Nullable delegate; }; typedef std::shared_ptr VectorStyleDelegateWrapperRef; @@ -125,16 +160,20 @@ typedef std::shared_ptr VectorStyleDelegateWrapperRe class VectorStyleWrapper : public VectorStyleImpl { public: - VectorStyleWrapper(NSObject *viewC,NSObject *style); - - virtual long long getUuid(PlatformThreadInfo *inst); - virtual std::string getCategory(PlatformThreadInfo *inst); - virtual bool geomAdditive(PlatformThreadInfo *inst); - virtual void buildObjects(PlatformThreadInfo *inst,std::vector &vecObjs,VectorTileDataRef tileInfo); - + VectorStyleWrapper(NSObject *_Nonnull viewC, + NSObject *_Nonnull style); + + virtual long long getUuid(PlatformThreadInfo *_Nullable inst) override; + virtual std::string getCategory(PlatformThreadInfo *_Nullable inst) override; + virtual bool geomAdditive(PlatformThreadInfo *_Nullable inst) override; + virtual void buildObjects(PlatformThreadInfo *_Nullable inst, + const std::vector &vecObjs, + const VectorTileDataRef &tileData, + const Dictionary *_Nullable desc) override; + protected: - NSObject * __weak viewC; - NSObject * __weak style; + NSObject *_Nullable __weak viewC; + NSObject *_Nullable __weak style; }; typedef std::shared_ptr VectorStyleWrapperRef; diff --git a/ios/library/WhirlyGlobe-MaplyComponent/include/rendering/MaplyVariableTarget.h b/ios/library/WhirlyGlobe-MaplyComponent/include/rendering/MaplyVariableTarget.h index 375bf96919..54698537b4 100644 --- a/ios/library/WhirlyGlobe-MaplyComponent/include/rendering/MaplyVariableTarget.h +++ b/ios/library/WhirlyGlobe-MaplyComponent/include/rendering/MaplyVariableTarget.h @@ -48,6 +48,9 @@ /// If set (by default), then we clear out the render target every frame @property (nonatomic,assign) bool clearEveryFrame; +/// When we're clearing, use this value. 0 by default +@property (nonatomic,assign) float clearVal; + /// Shader used to draw the render target to the screen. /// Leave this empty and we'll provide our own @property (nonatomic,strong,nullable) MaplyShader *shader; diff --git a/ios/library/WhirlyGlobe-MaplyComponent/include/vector_styles/MaplyVectorStyle.h b/ios/library/WhirlyGlobe-MaplyComponent/include/vector_styles/MaplyVectorStyle.h index e69108ddbb..8efc78feb6 100644 --- a/ios/library/WhirlyGlobe-MaplyComponent/include/vector_styles/MaplyVectorStyle.h +++ b/ios/library/WhirlyGlobe-MaplyComponent/include/vector_styles/MaplyVectorStyle.h @@ -3,7 +3,7 @@ * WhirlyGlobe-MaplyComponent * * Created by Steve Gifford on 1/3/14. - * Copyright 2011-2017 mousebird consulting + * Copyright 2011-2021 mousebird consulting * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -142,7 +142,10 @@ - (bool) geomAdditive; /// Construct objects related to this style based on the input data. -- (void)buildObjects:(NSArray * _Nonnull)vecObjs forTile:(MaplyVectorTileData * __nonnull)tileData viewC:(NSObject * _Nonnull)viewC; +- (void)buildObjects:(NSArray * _Nonnull)vecObjs + forTile:(MaplyVectorTileData * __nonnull)tileData + viewC:(NSObject * _Nonnull)viewC + desc:(NSDictionary * _Nullable)desc; @end @@ -163,7 +166,40 @@ extern "C" { @param threadMode MaplyThreadCurrent will block until all the features are added. MaplyThreadAny will do some of the work on another thread. */ -NSArray * _Nonnull AddMaplyVectorsUsingStyle(NSArray * _Nonnull vecObjs,NSObject * _Nonnull styleDelegate,NSObject * _Nonnull viewC,MaplyThreadMode threadMode); +NSArray * _Nonnull AddMaplyVectorsUsingStyle(NSArray * _Nonnull vecObjs, + NSObject * _Nonnull styleDelegate, + NSObject * _Nonnull viewC, + MaplyThreadMode threadMode); + +/** + Use a style delegate to interpret vector data. + + Run the style delegate against the given vectors. The resulting features are added to the + given view controller using the thread mode specified. + + @param vecObjs An array of MaplyVectorObject. + + @param styleDelegate The style delegate that controls how the vectors will look. + + @param viewC View controller to add the geometry to. + + @param tileId The tile where the feature originates. + + @param enable Automatically enable the generated objects + + @param threadMode MaplyThreadCurrent will block until all the features are added. MaplyThreadAny will do some of the work on another thread. + + @param desc Additional attributes to include with the generated component objects + */ +NSArray * _Nonnull AddMaplyVectorsUsingStyleAndAttributes( + NSArray * _Nonnull vecObjs, + NSObject * _Nonnull styleDelegate, + NSObject * _Nonnull viewC, + MaplyTileID tileId, + bool enable, + MaplyThreadMode threadMode, + NSDictionary * _Nullable desc); + #ifdef __cplusplus } #endif diff --git a/ios/library/WhirlyGlobe-MaplyComponent/include/visual_objects/MaplyScreenLabel.h b/ios/library/WhirlyGlobe-MaplyComponent/include/visual_objects/MaplyScreenLabel.h index 7dee0aa18d..c48bf418a2 100644 --- a/ios/library/WhirlyGlobe-MaplyComponent/include/visual_objects/MaplyScreenLabel.h +++ b/ios/library/WhirlyGlobe-MaplyComponent/include/visual_objects/MaplyScreenLabel.h @@ -21,6 +21,8 @@ #import #import "math/MaplyCoordinate.h" +@class MaplyVectorObject; + /// Don't move the label at all #define kMaplyLayoutNone (1<<0) /// Okay to place centered on point @@ -136,6 +138,18 @@ */ @property (nonatomic,assign) CGSize layoutSize; +/** + If this is present, we'll render an ID into the mask layer to be used by other features to mask against. + */ +@property (nonatomic,retain,nullable) NSString *maskID; + +/** + If set, we'll lay out the the text along the given linear or areal feature. + Takes the first feature in the vector, if there are multiple. + */ +@property (nonatomic,retain,nullable) MaplyVectorObject *layoutVec; + + /** Used to resolve to resolve labels that show the same thing. diff --git a/ios/library/WhirlyGlobe-MaplyComponent/include/visual_objects/MaplyScreenMarker.h b/ios/library/WhirlyGlobe-MaplyComponent/include/visual_objects/MaplyScreenMarker.h index cb52991c4c..8d03e3c8ab 100644 --- a/ios/library/WhirlyGlobe-MaplyComponent/include/visual_objects/MaplyScreenMarker.h +++ b/ios/library/WhirlyGlobe-MaplyComponent/include/visual_objects/MaplyScreenMarker.h @@ -130,6 +130,11 @@ */ @property (nonatomic,retain,nullable) NSString *uniqueID; +/** + If this is present, we'll render an ID into the mask layer to be used by other features to mask against. + */ +@property (nonatomic,retain,nullable) NSString *maskID; + /** User data object for selection diff --git a/ios/library/WhirlyGlobe-MaplyComponent/include/visual_objects/MaplyVectorObject.h b/ios/library/WhirlyGlobe-MaplyComponent/include/visual_objects/MaplyVectorObject.h index ef18d91d80..5ab46eed8a 100644 --- a/ios/library/WhirlyGlobe-MaplyComponent/include/visual_objects/MaplyVectorObject.h +++ b/ios/library/WhirlyGlobe-MaplyComponent/include/visual_objects/MaplyVectorObject.h @@ -133,35 +133,35 @@ typedef NS_ENUM(NSInteger, MaplyVectorObjectType) { This version takes a single coordinate and the attributes to go with it. */ -- (nonnull instancetype)initWithPointRef:(MaplyCoordinate *__nonnull)coord attributes:(NSDictionary *__nullable)attr; +- (nonnull instancetype)initWithPointRef:(const MaplyCoordinate *__nonnull)coord attributes:(NSDictionary *__nullable)attr; /** Initialize with a linear feature. This version takes an array of coordinate pairs (as NSNumber) and the attribution. With this it will make a linear feature. */ -- (nonnull instancetype)initWithLineString:(NSArray *__nonnull)coords attributes:(NSDictionary *__nullable)attr; +- (nonnull instancetype)initWithLineString:(const NSArray *__nonnull)coords attributes:(NSDictionary *__nullable)attr; /** Initialize with a linear feature. This version takes an array of coordinates, the size of that array and the attribution. With this it will make a linear feature. */ -- (nonnull instancetype)initWithLineString:(MaplyCoordinate *__nonnull)coords numCoords:(int)numCoords attributes:(NSDictionary *__nullable)attr; +- (nonnull instancetype)initWithLineString:(const MaplyCoordinate *__nonnull)coords numCoords:(int)numCoords attributes:(NSDictionary *__nullable)attr; /** Inintialize as an areal feature. This version takes an array of coordinates, the size of that array and the attribution. With this it will make a single area feature with one (exterior) loop. To add loops, call addHole:numCoords: */ -- (nonnull instancetype)initWithAreal:(MaplyCoordinate *__nonnull)coords numCoords:(int)numCoords attributes:(NSDictionary *__nullable)attr; +- (nonnull instancetype)initWithAreal:(const MaplyCoordinate *__nonnull)coords numCoords:(int)numCoords attributes:(NSDictionary *__nullable)attr; /** Inintialize as an areal feature. This version takes an array of coordinates (2 numbers per coordinate). With this it will make a single area feature with one (exterior) loop. To add loops, call addHole:numCoords: */ -- (nonnull instancetype)initWithArealArray:(NSArray *__nonnull)coords attributes:(NSDictionary *__nullable)attr; +- (nonnull instancetype)initWithArealArray:(const NSArray *__nonnull)coords attributes:(NSDictionary *__nullable)attr; /** Initializes with vectors parsed from geoJSON. @@ -266,7 +266,7 @@ typedef NS_ENUM(NSInteger, MaplyVectorObjectType) { This method is expecting to find exactly one areal feature. If it finds one, it will add the given hole as a loop on the end of the list of loops. */ -- (void)addHole:(MaplyCoordinate *__nonnull)coords numCoords:(int)numCoords; +- (void)addHole:(const MaplyCoordinate *__nonnull)coords numCoords:(int)numCoords; /** Returns the type of the vector feature. @@ -443,7 +443,7 @@ typedef NS_ENUM(NSInteger, MaplyVectorObjectType) { - (NSArray *__nullable)asNumbers; /** - Split up ths feature into individual features and return an array of them. + Split up this feature into individual features and return an array of them. A vector object can represent multiple features with no real rhyme or reason to it. This method will make one vector object per feature, allowing you to operate on those individually. diff --git a/ios/library/WhirlyGlobe-MaplyComponent/src/control/MaplyBaseInteractionLayer.mm b/ios/library/WhirlyGlobe-MaplyComponent/src/control/MaplyBaseInteractionLayer.mm index 0e73acdc89..d85f03359d 100644 --- a/ios/library/WhirlyGlobe-MaplyComponent/src/control/MaplyBaseInteractionLayer.mm +++ b/ios/library/WhirlyGlobe-MaplyComponent/src/control/MaplyBaseInteractionLayer.mm @@ -3,7 +3,7 @@ * MaplyComponent * * Created by Steve Gifford on 12/14/12. - * Copyright 2012-2019 mousebird consulting + * Copyright 2012-2021 mousebird consulting * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -50,6 +50,8 @@ #import "MaplyBaseViewController_private.h" #import "WorkRegion_private.h" +#import + using namespace Eigen; using namespace WhirlyKit; @@ -89,24 +91,27 @@ - (void) clusterID:(SimpleIdentity)clusterID params:(ClusterGenerator::ClusterCl MaplyBaseInteractionLayer * __weak layer; // Called right before we start generating layout objects - void startLayoutObjects() + virtual void startLayoutObjects(PlatformThreadInfo *) override { [layer startLayoutObjects]; } // Figure out - void makeLayoutObject(int clusterID,const std::vector &layoutObjects,LayoutObject &retObj) + virtual void makeLayoutObject(PlatformThreadInfo *,int clusterID, + const std::vector &layoutObjects, + LayoutObject &retObj) override { [layer makeLayoutObject:clusterID layoutObjects:layoutObjects retObj:retObj]; } // Called right after all the layout objects are generated - virtual void endLayoutObjects() + virtual void endLayoutObjects(PlatformThreadInfo *) override { [layer endLayoutObjects]; } - void paramsForClusterClass(int clusterID,ClusterClassParams &clusterParams) + virtual void paramsForClusterClass(PlatformThreadInfo *,int clusterID, + ClusterClassParams &clusterParams) override { return [layer clusterID:clusterID params:clusterParams]; } @@ -129,6 +134,13 @@ @implementation MaplyBaseInteractionLayer SimpleIdentity screenSpaceMotionProgram,screenSpaceDefaultProgram; } +// Check a boolean value in a dictionary by key, protecting against unexpected value types +static inline bool dictBool(const NSDictionary *dict, const NSString *key, bool defValue = false) +{ + const id value = dict[key]; + return [value isKindOfClass:[NSNumber class]] ? [value boolValue] : defValue; +} + - (instancetype)initWithView:(WhirlyKit::ViewRef)inVisualView { self = [super init]; @@ -140,6 +152,8 @@ - (instancetype)initWithView:(WhirlyKit::ViewRef)inVisualView visualView = inVisualView; mainThread = [NSThread currentThread]; numActiveWorkers = 0; + maskRenderTargetID = EmptyIdentity; + maskTexID = EmptyIdentity; // Grab everything to force people to wait, hopefully imageLock.lock(); @@ -162,21 +176,19 @@ - (void)dealloc - (void)startWithThread:(WhirlyKitLayerThread *)inLayerThread scene:(WhirlyKit::Scene *)inScene { layerThread = inLayerThread; - offlineMode = layerThread == nil; + offlineMode = inLayerThread == nil; scene = inScene; - sceneRender = layerThread.renderer; - - compManager = (ComponentManager_iOS *)scene->getManager(kWKComponentManager); - + sceneRender = inLayerThread.renderer; + + compManager = scene->getManager(kWKComponentManager); + atlasGroup = [[MaplyTextureAtlasGroup alloc] initWithScene:scene sceneRender:sceneRender]; - - if (layerThread) - setupInfo = layerThread.renderer->getRenderSetupInfo(); - - if (layerThread) + + if (inLayerThread) { + setupInfo = inLayerThread.renderer->getRenderSetupInfo(); ourClusterGen.layer = self; - compManager->layoutManager->addClusterGenerator(&ourClusterGen); + compManager->layoutManager->addClusterGenerator(nullptr,&ourClusterGen); } // We locked these in hopes of slowing down anyone trying to race us. Unlock 'em. @@ -214,12 +226,13 @@ - (void)teardown - (void)lockingShutdown { // This shouldn't happen - if (isShuttingDown || (!layerThread && !offlineMode)) + const auto __strong thread = layerThread; + if (isShuttingDown || (!thread && !offlineMode)) return; - if ([NSThread currentThread] != layerThread) + if ([NSThread currentThread] != thread) { - [self performSelector:@selector(lockingShutdown) onThread:layerThread withObject:nil waitUntilDone:YES]; + [self performSelector:@selector(lockingShutdown) onThread:thread withObject:nil waitUntilDone:YES]; return; } @@ -260,21 +273,16 @@ - (void)endOfWork // In that case, render everything now. - (MaplyThreadMode)resolveThreadMode:(MaplyThreadMode)threadMode { - if (!layerThread) - return MaplyThreadCurrent; - - return threadMode; + return layerThread ? threadMode : MaplyThreadCurrent; } - (Texture *)createTexture:(UIImage *)image desc:(NSDictionary *)desc mode:(MaplyThreadMode)threadMode { - //threadMode = [self resolveThreadMode:threadMode]; - - int imageFormat = [desc intForKey:kMaplyTexFormat default:MaplyImageIntRGBA]; - bool wrapX = [desc boolForKey:kMaplyTexWrapX default:false]; - bool wrapY = [desc boolForKey:kMaplyTexWrapY default:false]; - int magFilter = [desc enumForKey:kMaplyTexMagFilter values:@[kMaplyMinFilterNearest,kMaplyMinFilterLinear] default:0]; - bool mipmap = [desc boolForKey:kMaplyTexMipmap default:false]; + const int imageFormat = [desc intForKey:kMaplyTexFormat default:MaplyImageIntRGBA]; + const bool wrapX = [desc boolForKey:kMaplyTexWrapX default:false]; + const bool wrapY = [desc boolForKey:kMaplyTexWrapY default:false]; + const int magFilter = [desc enumForKey:kMaplyTexMagFilter values:@[kMaplyMinFilterNearest,kMaplyMinFilterLinear] default:0]; + const bool mipmap = [desc boolForKey:kMaplyTexMipmap default:false]; int imgWidth,imgHeight; if (image) @@ -427,8 +435,7 @@ - (MaplyTexture *)addTexture:(UIImage *)image desc:(NSDictionary *)desc mode:(Ma imageTextures.push_back(maplyTex); } - if (!changes.empty()) - [self flushChanges:changes mode:threadMode]; + [self flushChanges:changes mode:threadMode]; return maplyTex; } @@ -617,10 +624,11 @@ - (void)delayedRemoveTextures:(NSArray *)texs // We flush out changes in different ways depending on the thread mode - (void)flushChanges:(ChangeSet &)changes mode:(MaplyThreadMode)threadMode { - threadMode = [self resolveThreadMode:threadMode]; - if (changes.empty()) return; + + threadMode = [self resolveThreadMode:threadMode]; + // This means we beat the layer thread setup, so we'll put this in orbit if (!scene) threadMode = MaplyThreadAny; @@ -740,6 +748,7 @@ - (void)resolveVertexAttrs:(SingleVertexAttributeSet &)destAttrs from:(NSArray * destAttrs.insert(attr->attr); } +// Resolve draw priority into a single number - (void)resolveDrawPriority:(NSDictionary *)desc info:(BaseInfo *)info drawPriority:(int)drawPriority offset:(int)offsetPriority { NSNumber *setting = desc[@"drawPriority"]; @@ -753,6 +762,20 @@ - (void)resolveDrawPriority:(NSDictionary *)desc info:(BaseInfo *)info drawPrior } } +// Remap the mask strings to IDs +- (void)resolveMaskIDs:(NSMutableDictionary *)desc compObj:(MaplyComponentObject *)compObj +{ + for (unsigned int ii=0;iiretainMaskByName([obj cStringUsingEncoding:NSUTF8StringEncoding]); + desc[attrName] = @(maskID); + compObj->contents->maskIDs.insert(maskID); + } + } +} + // Actually add the markers. // Called in an unknown thread - (void)addScreenMarkersRun:(NSArray *)argArray @@ -761,12 +784,17 @@ - (void)addScreenMarkersRun:(NSArray *)argArray return; NSArray *markers = [argArray objectAtIndex:0]; + if (markers.count == 0) + { + return; + } + MaplyComponentObject *compObj = [argArray objectAtIndex:1]; NSDictionary *inDesc = [argArray objectAtIndex:2]; MaplyThreadMode threadMode = (MaplyThreadMode)[[argArray objectAtIndex:3] intValue]; - - TimeInterval now = scene->getCurrentTime(); - + + const TimeInterval now = scene->getCurrentTime(); + bool isMotionMarkers = false; if ([[markers objectAtIndex:0] isKindOfClass:[MaplyMovingScreenMarker class]]) isMotionMarkers = true; @@ -860,6 +888,12 @@ - (void)addScreenMarkersRun:(NSArray *)argArray } wgMarker->offset = Point2d(marker.offset.x,marker.offset.y); + if (marker.maskID) { + wgMarker->maskID = compManager->retainMaskByName([marker.maskID cStringUsingEncoding:NSUTF8StringEncoding]); + compObj->contents->maskIDs.insert(wgMarker->maskID); + wgMarker->maskRenderTargetID = maskRenderTargetID; + } + // Now for the motion related fields if ([marker isKindOfClass:[MaplyMovingScreenMarker class]]) { @@ -883,13 +917,17 @@ - (void)addScreenMarkersRun:(NSArray *)argArray // Set up a description and create the markers in the marker layer ChangeSet changes; SimpleIdentity markerID = compManager->markerManager->addMarkers(wgMarkers, markerInfo, changes); + for (auto marker: wgMarkers) delete marker; + wgMarkers.clear(); + if (markerID != EmptyIdentity) compObj->contents->markerIDs.insert(markerID); + + compManager->addComponentObject(compObj->contents, changes); + [self flushChanges:changes mode:threadMode]; - - compManager->addComponentObject(compObj->contents); } // Called in the main thread. @@ -899,14 +937,7 @@ - (MaplyComponentObject *)addScreenMarkers:(NSArray *)markers desc:(NSDictionary MaplyComponentObject *compObj = [[MaplyComponentObject alloc] initWithDesc:desc]; compObj->contents->underConstruction = true; - - if ([markers count] == 0) - { - compManager->addComponentObject(compObj->contents); - return compObj; - } - NSArray *argArray = @[markers, compObj, [NSDictionary dictionaryWithDictionary:desc], @(threadMode)]; switch (threadMode) @@ -981,7 +1012,7 @@ - (void)setupLayoutObject:(LayoutObject &)retObj asBestOfLayoutObjects:(const st if (topObject->obj.hasRotation()) retObj.setRotation(topObject->obj.getRotation()); - std::vector allGeometry = topObject->obj.getGeometry(); + std::vector allGeometry = topObject->obj.getGeometry(); if (allGeometry.empty()) return; @@ -1021,7 +1052,7 @@ - (void)setupLayoutObject:(LayoutObject &)retObj asAverageOfLayoutObjects:(const MaplyClusterGroup *group = [clusterGen makeClusterGroup:clusterInfo]; // Geometry for the new cluster object - ScreenSpaceObject::ConvexGeometry smGeom; + ScreenSpaceConvexGeometry smGeom; smGeom.progID = progID; smGeom.coords.push_back(Point2d(-group.size.width/2.0,-group.size.height/2.0)); smGeom.texCoords.push_back(TexCoord(0,1)); @@ -1135,13 +1166,18 @@ - (void)addMarkersRun:(NSArray *)argArray NSArray *markers = [argArray objectAtIndex:0]; MaplyComponentObject *compObj = [argArray objectAtIndex:1]; NSDictionary *inDesc = [argArray objectAtIndex:2]; - MaplyThreadMode threadMode = (MaplyThreadMode)[[argArray objectAtIndex:3] intValue]; + const auto threadMode = (MaplyThreadMode)[[argArray objectAtIndex:3] intValue]; // Note: This assumes everything has images bool hasMultiTex = false; for (MaplyMarker *marker in markers) + { if (marker.images) + { hasMultiTex = true; + break; + } + } iosDictionary dictWrap(inDesc); MarkerInfo markerInfo(dictWrap,false); @@ -1149,10 +1185,15 @@ - (void)addMarkersRun:(NSArray *)argArray [self resolveDrawPriority:inDesc info:&markerInfo drawPriority:kMaplyMarkerDrawPriorityDefault offset:0]; // Convert to WG markers - std::vector wgMarkers; + std::vector wgMarkers; + // Automatically delete on return or exception + std::vector> wgMarkerOwner; + wgMarkers.reserve(markers.count); + wgMarkerOwner.reserve(markers.count); + for (MaplyMarker *marker in markers) { - Marker *wgMarker = new Marker(); + auto wgMarker = std::make_unique(); wgMarker->loc = GeoCoord(marker.loc.x,marker.loc.y); std::vector texs; @@ -1161,27 +1202,36 @@ - (void)addMarkersRun:(NSArray *)argArray if ([marker.image isKindOfClass:[UIImage class]]) { texs.push_back([self addImage:marker.image imageFormat:MaplyImageIntRGBA mode:threadMode]); - } else if ([marker.image isKindOfClass:[MaplyTexture class]]) + } + else if ([marker.image isKindOfClass:[MaplyTexture class]]) { texs.push_back((MaplyTexture *)marker.image); } - } else if (marker.images) + } + else if (marker.images) { for (id image in marker.images) { if ([image isKindOfClass:[UIImage class]]) + { texs.push_back([self addImage:image imageFormat:MaplyImageIntRGBA wrapFlags:0 interpType:TexInterpLinear mode:threadMode]); + } else if ([image isKindOfClass:[MaplyTexture class]]) + { texs.push_back((MaplyTexture *)image); + } } } if (texs.size() > 1) + { wgMarker->period = marker.period; + } + compObj->contents->texs.insert(texs.begin(),texs.end()); - if (!texs.empty()) + wgMarker->texIDs.reserve(wgMarker->texIDs.size() + texs.size()); + for (const auto& tex : texs) { - for (unsigned int ii=0;iitexIDs.push_back(texs[ii].texID); + wgMarker->texIDs.push_back(tex.texID); } wgMarker->width = marker.size.width; @@ -1191,30 +1241,33 @@ - (void)addMarkersRun:(NSArray *)argArray wgMarker->isSelectable = true; wgMarker->selectID = Identifiable::genId(); } - - wgMarkers.push_back(wgMarker); - + + const auto selectId = wgMarker->selectID; + + wgMarkers.push_back(wgMarker.get()); + wgMarkerOwner.push_back(std::move(wgMarker)); + if (marker.selectable) { - compManager->addSelectObject(wgMarker->selectID,marker); - compObj->contents->selectIDs.insert(wgMarker->selectID); + compManager->addSelectObject(selectId,marker); + compObj->contents->selectIDs.insert(selectId); } } - + // Set up a description and create the markers in the marker layer - MarkerManager *markerManager = (MarkerManager *)scene->getManager(kWKMarkerManager); - if (markerManager) + ChangeSet changes; + if (auto markerManager = scene->getManager(kWKMarkerManager)) { - ChangeSet changes; SimpleIdentity markerID = markerManager->addMarkers(wgMarkers, markerInfo, changes); - for (auto marker: wgMarkers) - delete marker; if (markerID != EmptyIdentity) + { compObj->contents->markerIDs.insert(markerID); - [self flushChanges:changes mode:threadMode]; + } } - compManager->addComponentObject(compObj->contents); + compManager->addComponentObject(compObj->contents, changes); + + [self flushChanges:changes mode:threadMode]; } // Add 3D markers @@ -1225,12 +1278,6 @@ - (MaplyComponentObject *)addMarkers:(NSArray *)markers desc:(NSDictionary *)des MaplyComponentObject *compObj = [[MaplyComponentObject alloc] initWithDesc:desc]; compObj->contents->underConstruction = true; - if ([markers count] == 0) - { - compManager->addComponentObject(compObj->contents); - return compObj; - } - NSArray *argArray = @[markers, compObj, [NSDictionary dictionaryWithDictionary:desc], @(threadMode)]; switch (threadMode) { @@ -1255,27 +1302,29 @@ - (void)addScreenLabelsRun:(NSArray *)argArray NSArray *labels = [argArray objectAtIndex:0]; MaplyComponentObject *compObj = [argArray objectAtIndex:1]; NSDictionary *inDesc = [argArray objectAtIndex:2]; - MaplyThreadMode threadMode = (MaplyThreadMode)[[argArray objectAtIndex:3] intValue]; - - TimeInterval now = scene->getCurrentTime(); - - bool isMotionLabels = false; - if ([[labels objectAtIndex:0] isKindOfClass:[MaplyMovingScreenLabel class]]) - isMotionLabels = true; + const auto threadMode = (MaplyThreadMode)[[argArray objectAtIndex:3] intValue]; + + const TimeInterval now = scene->getCurrentTime(); + + const bool isMotionLabels = ([[labels objectAtIndex:0] isKindOfClass:[MaplyMovingScreenLabel class]]); iosDictionary dictWrap(inDesc); - LabelInfo_iOS labelInfo(inDesc,dictWrap,true); + LabelInfo_iOS labelInfo(inDesc,dictWrap, /*screenObject=*/true); [self resolveInfoDefaults:inDesc info:&labelInfo defaultShader:(isMotionLabels ? kMaplyScreenSpaceDefaultMotionProgram : kMaplyScreenSpaceDefaultProgram)]; [self resolveDrawPriority:inDesc info:&labelInfo drawPriority:kMaplyLabelDrawPriorityDefault offset:_screenObjectDrawPriorityOffset]; if (!labelInfo.font) + { labelInfo.font = [UIFont systemFontOfSize:32.0]; + } // Convert to WG screen labels std::vector wgLabels; + // Automatic cleanup + std::vector> wgLabelOwner; for (MaplyScreenLabel *label in labels) { - SingleLabel_iOS *wgLabel = new SingleLabel_iOS(); + auto wgLabel = std::make_unique(); wgLabel->loc = GeoCoord(label.loc.x,label.loc.y); wgLabel->rotation = label.rotation; wgLabel->text = label.text; @@ -1320,6 +1369,12 @@ - (void)addScreenLabelsRun:(NSArray *)argArray wgLabel->isSelectable = true; wgLabel->selectID = Identifiable::genId(); } + + if (label.maskID) { + wgLabel->maskID = compManager->retainMaskByName([label.maskID cStringUsingEncoding:NSUTF8StringEncoding]); + compObj->contents->maskIDs.insert(wgLabel->maskID); + wgLabel->maskRenderTargetID = maskRenderTargetID; + } // Now for the motion related fields if ([label isKindOfClass:[MaplyMovingScreenLabel class]]) @@ -1330,28 +1385,49 @@ - (void)addScreenLabelsRun:(NSArray *)argArray wgLabel->startTime = now; wgLabel->endTime = now + movingLabel.duration; } - - wgLabels.push_back(wgLabel); + if (label.layoutVec && !label.layoutVec->vObj->shapes.empty()) { + for (auto shape: label.layoutVec->vObj->shapes) { + auto shapeLin = std::dynamic_pointer_cast(shape); + if (shapeLin) { + wgLabel->layoutShape = shapeLin->pts; + break; + } else { + auto shapeAr = std::dynamic_pointer_cast(shape); + if (shapeAr && !shapeAr->loops.empty()) { + wgLabel->layoutShape = shapeAr->loops[0]; + break; + } + } + } + } + + const auto selectId = wgLabel->selectID; + + wgLabels.push_back(wgLabel.get()); + wgLabelOwner.push_back(std::move(wgLabel)); + if (label.selectable) { - compManager->addSelectObject(wgLabel->selectID,label); - compObj->contents->selectIDs.insert(wgLabel->selectID); + compManager->addSelectObject(selectId,label); + compObj->contents->selectIDs.insert(selectId); } } - - LabelManager *labelManager = (LabelManager *)scene->getManager(kWKLabelManager); - if (labelManager) + + ChangeSet changes; + if (auto labelManager = scene->getManager(kWKLabelManager)) { // Set up a description and create the markers in the marker layer - ChangeSet changes; SimpleIdentity labelID = labelManager->addLabels(NULL, wgLabels, labelInfo, changes); - [self flushChanges:changes mode:threadMode]; if (labelID != EmptyIdentity) + { compObj->contents->labelIDs.insert(labelID); + } } - compManager->addComponentObject(compObj->contents); + compManager->addComponentObject(compObj->contents, changes); + + [self flushChanges:changes mode:threadMode]; } // Add screen space (2D) labels @@ -1361,13 +1437,7 @@ - (MaplyComponentObject *)addScreenLabels:(NSArray *)labels desc:(NSDictionary * MaplyComponentObject *compObj = [[MaplyComponentObject alloc] initWithDesc:desc]; compObj->contents->underConstruction = true; - - if ([labels count] == 0) - { - compManager->addComponentObject(compObj->contents); - return compObj; - } - + NSArray *argArray = @[labels, compObj, [NSDictionary dictionaryWithDictionary:desc], @(threadMode)]; switch (threadMode) @@ -1393,23 +1463,26 @@ - (void)addLabelsRun:(NSArray *)argArray NSArray *labels = [argArray objectAtIndex:0]; MaplyComponentObject *compObj = [argArray objectAtIndex:1]; NSDictionary *inDesc = [argArray objectAtIndex:2]; - MaplyThreadMode threadMode = (MaplyThreadMode)[[argArray objectAtIndex:3] intValue]; - + const auto threadMode = (MaplyThreadMode)[[argArray objectAtIndex:3] intValue]; + iosDictionary dictWrap(inDesc); - LabelInfo_iOS labelInfo(inDesc,dictWrap,false); + LabelInfo_iOS labelInfo(inDesc,dictWrap, /*screenObject=*/false); [self resolveInfoDefaults:inDesc info:&labelInfo defaultShader:kMaplyShaderDefaultTri]; [self resolveDrawPriority:inDesc info:&labelInfo drawPriority:kMaplyLabelDrawPriorityDefault offset:0]; // Convert to WG labels std::vector wgLabels; + std::vector> wgLabelOwner; for (MaplyLabel *label in labels) { - SingleLabel_iOS *wgLabel = new SingleLabel_iOS(); + auto wgLabel = std::make_unique(); NSMutableDictionary *desc = [NSMutableDictionary dictionary]; wgLabel->loc = GeoCoord(label.loc.x,label.loc.y); wgLabel->text = label.text; + MaplyTexture *tex = nil; - if (label.iconImage2) { + if (label.iconImage2) + { tex = [self addImage:label.iconImage2 imageFormat:MaplyImageIntRGBA mode:threadMode]; compObj->contents->texs.insert(tex); } @@ -1438,28 +1511,32 @@ - (void)addLabelsRun:(NSArray *)argArray break; } - wgLabels.push_back(wgLabel); - + const auto selectId = wgLabel->selectID; + + wgLabels.push_back(wgLabel.get()); + wgLabelOwner.push_back(std::move(wgLabel)); + if (label.selectable) { - compManager->addSelectObject(wgLabel->selectID,label); - compObj->contents->selectIDs.insert(wgLabel->selectID); + compManager->addSelectObject(selectId,label); + compObj->contents->selectIDs.insert(selectId); } } - - LabelManager *labelManager = (LabelManager *)scene->getManager(kWKLabelManager); - - if (labelManager) + + ChangeSet changes; + if (auto labelManager = scene->getManager(kWKLabelManager)) { - ChangeSet changes; // Set up a description and create the markers in the marker layer SimpleIdentity labelID = labelManager->addLabels(NULL, wgLabels, labelInfo, changes); - [self flushChanges:changes mode:threadMode]; if (labelID != EmptyIdentity) + { compObj->contents->labelIDs.insert(labelID); + } } - - compManager->addComponentObject(compObj->contents); + + compManager->addComponentObject(compObj->contents, changes); + + [self flushChanges:changes mode:threadMode]; } // Add 3D labels @@ -1469,13 +1546,7 @@ - (MaplyComponentObject *)addLabels:(NSArray *)labels desc:(NSDictionary *)desc MaplyComponentObject *compObj = [[MaplyComponentObject alloc] initWithDesc:desc]; compObj->contents->underConstruction = true; - - if ([labels count] == 0) - { - compManager->addComponentObject(compObj->contents); - return compObj; - } - + NSArray *argArray = @[labels, compObj, [NSDictionary dictionaryWithDictionary:desc], @(threadMode)]; switch (threadMode) @@ -1491,6 +1562,32 @@ - (MaplyComponentObject *)addLabels:(NSArray *)labels desc:(NSDictionary *)desc return compObj; } +- (MaplyTexture * __nullable)getTextureFromDesc:(NSDictionary*)inDesc mode:(MaplyThreadMode)threadMode +{ + if (const id theImage = inDesc[kMaplyVecTexture]) + { + if ([theImage isKindOfClass:[UIImage class]]) + { + auto fmt = MaplyImage4Layer8Bit; + if (const id value = inDesc[kMaplyVecTextureFormat]) + { + if ([value isKindOfClass:[NSNumber class]]) + { + fmt = (MaplyQuadImageFormat)[value intValue]; + } + } + auto tex = [self addImage:theImage imageFormat:fmt mode:threadMode]; + return (tex.texID != EmptyIdentity) ? tex : nil; + } + else if ([theImage isKindOfClass:[MaplyTexture class]]) + { + auto tex = (MaplyTexture *)theImage; + return (tex.texID != EmptyIdentity) ? tex : nil; + } + } + return nil; +} + // Actually add the vectors. // Called in an unknown thread - (void)addVectorsRun:(NSArray *)argArray @@ -1501,8 +1598,8 @@ - (void)addVectorsRun:(NSArray *)argArray NSArray *vectors = [argArray objectAtIndex:0]; MaplyComponentObject *compObj = [argArray objectAtIndex:1]; NSDictionary *inDesc = [argArray objectAtIndex:2]; - bool makeVisible = [[argArray objectAtIndex:3] boolValue]; - MaplyThreadMode threadMode = (MaplyThreadMode)[[argArray objectAtIndex:4] intValue]; + const bool makeVisible = [[argArray objectAtIndex:3] boolValue]; + const auto threadMode = (MaplyThreadMode)[[argArray objectAtIndex:4] intValue]; iosDictionary dictWrap(inDesc); VectorInfo vectorInfo(dictWrap); @@ -1510,39 +1607,44 @@ - (void)addVectorsRun:(NSArray *)argArray // Might be a custom shader on these NSString *shaderName = !vectorInfo.filled ? kMaplyShaderDefaultLine : kMaplyDefaultTriangleShader; if (vectorInfo.texProj == TextureProjectionScreen) + { shaderName = kMaplyShaderDefaultTriScreenTex; + } [self resolveInfoDefaults:inDesc info:&vectorInfo defaultShader:shaderName]; [self resolveDrawPriority:inDesc info:&vectorInfo drawPriority:kMaplyVectorDrawPriorityDefault offset:0]; // Look for a texture and add it - if (inDesc[kMaplyVecTexture]) + if (const auto tex = [self getTextureFromDesc:inDesc mode:threadMode]) { - UIImage *theImage = inDesc[kMaplyVecTexture]; - MaplyTexture *tex = nil; - if ([theImage isKindOfClass:[UIImage class]]) - tex = [self addImage:theImage imageFormat:MaplyImage4Layer8Bit mode:threadMode]; - else if ([theImage isKindOfClass:[MaplyTexture class]]) - tex = (MaplyTexture *)theImage; - if (tex.texID) - vectorInfo.texId = tex.texID; + vectorInfo.texId = tex.texID; } - ShapeSet shapes; - for (MaplyVectorObject *vecObj in vectors) + // Estimate the number of items that will be present. + // This can make a big difference with tens of thousands of objects. + size_t shapeCount = 0; + for (const MaplyVectorObject *vecObj in vectors) + { + shapeCount += vecObj->vObj->shapes.size(); + } + + ShapeSet shapes(shapeCount); + for (const MaplyVectorObject *vecObj in vectors) { // Maybe need to make a copy if we're going to sample if (vectorInfo.subdivEps != 0.0) { - float eps = vectorInfo.subdivEps; + const float eps = vectorInfo.subdivEps; NSString *subdivType = inDesc[kMaplySubdivType]; - bool greatCircle = ![subdivType compare:kMaplySubdivGreatCircle]; - bool grid = ![subdivType compare:kMaplySubdivGrid]; - bool staticSubdiv = ![subdivType compare:kMaplySubdivStatic]; + const bool greatCircle = ![subdivType compare:kMaplySubdivGreatCircle]; + const bool grid = ![subdivType compare:kMaplySubdivGrid]; + const bool staticSubdiv = ![subdivType compare:kMaplySubdivStatic]; MaplyVectorObject *newVecObj = [vecObj deepCopy2]; // Note: This logic needs to be moved down a level // Along with the subdivision routines above if (greatCircle) + { [newVecObj subdivideToGlobeGreatCircle:eps]; + } else if (grid) { // The manager has to handle this one @@ -1550,45 +1652,58 @@ - (void)addVectorsRun:(NSArray *)argArray else if (staticSubdiv) { // Note: Fill this in - } else + } + else + { [newVecObj subdivideToGlobe:eps]; + } shapes.insert(newVecObj->vObj->shapes.begin(),newVecObj->vObj->shapes.end()); - } else + } + else + { // We'll just reference it shapes.insert(vecObj->vObj->shapes.begin(),vecObj->vObj->shapes.end()); + } } - + + ChangeSet changes; if (makeVisible) { - VectorManager *vectorManager = (VectorManager *)scene->getManager(kWKVectorManager); - - if (vectorManager) + if (const auto vectorManager = scene->getManager(kWKVectorManager)) { - ChangeSet changes; - SimpleIdentity vecID = vectorManager->addVectors(&shapes, vectorInfo, changes); - [self flushChanges:changes mode:threadMode]; + const SimpleIdentity vecID = vectorManager->addVectors(&shapes, vectorInfo, changes); if (vecID != EmptyIdentity) + { compObj->contents->vectorIDs.insert(vecID); + } } } - + // If the vectors are selectable we want to keep them around - id selVal = inDesc[@"selectable"]; - if (selVal && [selVal boolValue]) + if (dictBool(inDesc, kMaplySelectable)) { - if ([inDesc[kMaplyVecCentered] boolValue]) + if (dictBool(inDesc, kMaplyVecCentered)) { - if (inDesc[kMaplyVecCenterX]) - compObj->contents->vectorOffset.x() = [inDesc[kMaplyVecCenterX] doubleValue]; - if (inDesc[kMaplyVecCenterY]) - compObj->contents->vectorOffset.y() = [inDesc[kMaplyVecCenterY] doubleValue]; + if (const id cx = inDesc[kMaplyVecCenterX]) + { + compObj->contents->vectorOffset.x() = [cx doubleValue]; + } + if (const id cy = inDesc[kMaplyVecCenterY]) + { + compObj->contents->vectorOffset.y() = [cy doubleValue]; + } } + compObj->contents->vecObjs.reserve(compObj->contents->vecObjs.size() + vectors.count); for (MaplyVectorObject *vObj in vectors) + { compObj->contents->vecObjs.push_back(vObj->vObj); + } } - compManager->addComponentObject(compObj->contents); + compManager->addComponentObject(compObj->contents, changes); + + [self flushChanges:changes mode:threadMode]; } // Add vectors @@ -1598,13 +1713,7 @@ - (MaplyComponentObject *)addVectors:(NSArray *)vectors desc:(NSDictionary *)des MaplyComponentObject *compObj = [[MaplyComponentObject alloc] initWithDesc:desc]; compObj->contents->underConstruction = true; - - if ([vectors count] == 0) - { - compManager->addComponentObject(compObj->contents); - return compObj; - } - + NSArray *argArray = @[vectors, compObj, [NSDictionary dictionaryWithDictionary:desc], [NSNumber numberWithBool:YES], @(threadMode)]; switch (threadMode) { @@ -1626,47 +1735,46 @@ - (void)addWideVectorsRun:(NSArray *)argArray if (isShuttingDown || (!layerThread && !offlineMode)) return; - NSArray *vectors = [argArray objectAtIndex:0]; + const NSArray *vectors = [argArray objectAtIndex:0]; MaplyComponentObject *compObj = [argArray objectAtIndex:1]; NSDictionary *inDesc = [argArray objectAtIndex:2]; - MaplyThreadMode threadMode = (MaplyThreadMode)[[argArray objectAtIndex:3] intValue]; + const auto threadMode = (MaplyThreadMode)[[argArray objectAtIndex:3] intValue]; iosDictionary dictWrap(inDesc); WideVectorInfo vectorInfo(dictWrap); - [self resolveInfoDefaults:inDesc info:&vectorInfo defaultShader:kMaplyShaderDefaultWideVector]; + [self resolveInfoDefaults:inDesc info:&vectorInfo defaultShader:(vectorInfo.implType == WideVecImplBasic ? kMaplyShaderDefaultWideVector : kMaplyShaderWideVectorPerformance )]; [self resolveDrawPriority:inDesc info:&vectorInfo drawPriority:kMaplyVectorDrawPriorityDefault offset:0]; // Look for a texture and add it - if (inDesc[kMaplyVecTexture]) + if (const auto tex = [self getTextureFromDesc:inDesc mode:threadMode]) { - UIImage *theImage = inDesc[kMaplyVecTexture]; - MaplyTexture *tex = nil; - if ([theImage isKindOfClass:[UIImage class]]) - tex = [self addImage:theImage imageFormat:MaplyImage4Layer8Bit mode:threadMode]; - else if ([theImage isKindOfClass:[MaplyTexture class]]) - tex = (MaplyTexture *)theImage; - if (tex.texID) { - vectorInfo.texID = tex.texID; - compObj->contents->texs.insert(tex); - } + vectorInfo.texID = tex.texID; + compObj->contents->texs.insert(tex); } - ShapeSet shapes; - for (MaplyVectorObject *vecObj in vectors) + std::vector shapes; + for (const MaplyVectorObject *vecObj in vectors) { + for (auto shape: vecObj->vObj->shapes) { + auto dict = std::dynamic_pointer_cast(shape->getAttrDict()); + [self resolveMaskIDs:dict->dict compObj:compObj]; + } + // Maybe need to make a copy if we're going to sample if (vectorInfo.subdivEps != 0.0) { - float eps = vectorInfo.subdivEps; - NSString *subdivType = inDesc[kMaplySubdivType]; - bool greatCircle = ![subdivType compare:kMaplySubdivGreatCircle]; - bool grid = ![subdivType compare:kMaplySubdivGrid]; - bool staticSubdiv = ![subdivType compare:kMaplySubdivStatic]; + const float eps = vectorInfo.subdivEps; + const NSString *subdivType = inDesc[kMaplySubdivType]; + const bool greatCircle = ![subdivType compare:kMaplySubdivGreatCircle]; + const bool grid = ![subdivType compare:kMaplySubdivGrid]; + const bool staticSubdiv = ![subdivType compare:kMaplySubdivStatic]; MaplyVectorObject *newVecObj = [vecObj deepCopy2]; // Note: This logic needs to be moved down a level // Along with the subdivision routines above if (greatCircle) + { [newVecObj subdivideToGlobeGreatCircle:eps]; + } else if (grid) { // The manager has to handle this one @@ -1674,35 +1782,43 @@ - (void)addWideVectorsRun:(NSArray *)argArray else if (staticSubdiv) { // Note: Fill this in - } else + } + else + { [newVecObj subdivideToGlobe:eps]; - - shapes.insert(newVecObj->vObj->shapes.begin(),newVecObj->vObj->shapes.end()); - } else + } + + shapes.insert(shapes.end(),newVecObj->vObj->shapes.begin(),newVecObj->vObj->shapes.end()); + } + else + { // We'll just reference it - shapes.insert(vecObj->vObj->shapes.begin(),vecObj->vObj->shapes.end()); + shapes.insert(shapes.end(),vecObj->vObj->shapes.begin(),vecObj->vObj->shapes.end()); + } } - - WideVectorManager *vectorManager = (WideVectorManager *)scene->getManager(kWKWideVectorManager); - - if (vectorManager) + + ChangeSet changes; + if (const auto manager = scene->getManager(kWKWideVectorManager)) { - ChangeSet changes; - SimpleIdentity vecID = vectorManager->addVectors(shapes, vectorInfo, changes); - [self flushChanges:changes mode:threadMode]; + const auto vecID = manager->addVectors(shapes, vectorInfo, changes); if (vecID != EmptyIdentity) + { compObj->contents->wideVectorIDs.insert(vecID); + } } // If the vectors are selectable we want to keep them around - id selVal = inDesc[@"selectable"]; - if (selVal && [selVal boolValue]) { - for (MaplyVectorObject *vObj in vectors) + if (dictBool(inDesc, kMaplySelectable)) + { + for (const MaplyVectorObject *vObj in vectors) + { compObj->contents->vecObjs.push_back(vObj->vObj); - + } } - - compManager->addComponentObject(compObj->contents); + + compManager->addComponentObject(compObj->contents, changes); + + [self flushChanges:changes mode:threadMode]; } - (MaplyComponentObject *)addWideVectors:(NSArray *)vectors desc:(NSDictionary *)desc mode:(MaplyThreadMode)threadMode @@ -1711,13 +1827,7 @@ - (MaplyComponentObject *)addWideVectors:(NSArray *)vectors desc:(NSDictionary * MaplyComponentObject *compObj = [[MaplyComponentObject alloc] initWithDesc:desc]; compObj->contents->underConstruction = true; - - if ([vectors count] == 0) - { - compManager->addComponentObject(compObj->contents); - return compObj; - } - + NSArray *argArray = @[vectors, compObj, [NSDictionary dictionaryWithDictionary:desc], @(threadMode)]; switch (threadMode) { @@ -1743,8 +1853,8 @@ - (void)instanceVectorsRun:(NSArray *)argArray MaplyComponentObject *compObj = [argArray objectAtIndex:1]; compObj->contents->vecObjs = baseObj->contents->vecObjs; NSDictionary *inDesc = [argArray objectAtIndex:2]; - bool makeVisible = [[argArray objectAtIndex:3] boolValue]; - MaplyThreadMode threadMode = (MaplyThreadMode)[[argArray objectAtIndex:4] intValue]; + const bool makeVisible = [[argArray objectAtIndex:3] boolValue]; + const auto threadMode = (MaplyThreadMode)[[argArray objectAtIndex:4] intValue]; iosDictionary dictWrap(inDesc); VectorInfo vectorInfo(dictWrap); @@ -1752,49 +1862,50 @@ - (void)instanceVectorsRun:(NSArray *)argArray [self resolveDrawPriority:inDesc info:&vectorInfo drawPriority:kMaplyVectorDrawPriorityDefault offset:0]; // Look for a texture and add it - if (inDesc[kMaplyVecTexture]) + if (const auto tex = [self getTextureFromDesc:inDesc mode:threadMode]) { - UIImage *theImage = inDesc[kMaplyVecTexture]; - MaplyTexture *tex = nil; - if ([theImage isKindOfClass:[UIImage class]]) - tex = [self addImage:theImage imageFormat:MaplyImage4Layer8Bit mode:threadMode]; - else if ([theImage isKindOfClass:[MaplyTexture class]]) - tex = (MaplyTexture *)theImage; - if (tex.texID) - vectorInfo.texId = tex.texID; + vectorInfo.texId = tex.texID; } + ChangeSet changes; if (makeVisible) { - VectorManager *vectorManager = (VectorManager *)scene->getManager(kWKVectorManager); - WideVectorManager *wideVectorManager = (WideVectorManager *)scene->getManager(kWKWideVectorManager); - - ChangeSet changes; - if (vectorManager && !baseObj->contents->vectorIDs.empty()) + if (!baseObj->contents->vectorIDs.empty()) { - for (SimpleIDSet::iterator it = baseObj->contents->vectorIDs.begin();it != baseObj->contents->vectorIDs.end(); ++it) + if (const auto vectorManager = scene->getManager(kWKVectorManager)) { - SimpleIdentity instID = vectorManager->instanceVectors(*it, vectorInfo, changes); - if (instID != EmptyIdentity) - compObj->contents->vectorIDs.insert(instID); + for (auto vid : baseObj->contents->vectorIDs) + { + SimpleIdentity instID = vectorManager->instanceVectors(vid, vectorInfo, changes); + if (instID != EmptyIdentity) + { + compObj->contents->vectorIDs.insert(instID); + } + } } } - if (wideVectorManager && !baseObj->contents->wideVectorIDs.empty()) + if (!baseObj->contents->wideVectorIDs.empty()) { - iosDictionary dictWrap(inDesc); - WideVectorInfo vectorInfo(dictWrap); - - for (SimpleIDSet::iterator it = baseObj->contents->wideVectorIDs.begin();it != baseObj->contents->wideVectorIDs.end(); ++it) + if (const auto wideVectorManager = scene->getManager(kWKWideVectorManager)) { - SimpleIdentity instID = wideVectorManager->instanceVectors(*it, vectorInfo, changes); - if (instID != EmptyIdentity) - compObj->contents->wideVectorIDs.insert(instID); + iosDictionary dictWrap(inDesc); + WideVectorInfo vectorInfo(dictWrap); + + for (const auto vid : baseObj->contents->wideVectorIDs) + { + SimpleIdentity instID = wideVectorManager->instanceVectors(vid, vectorInfo, changes); + if (instID != EmptyIdentity) + { + compObj->contents->wideVectorIDs.insert(instID); + } + } } } - [self flushChanges:changes mode:threadMode]; } - compManager->addComponentObject(compObj->contents); + compManager->addComponentObject(compObj->contents, changes); + + [self flushChanges:changes mode:threadMode]; } // Instance vectors @@ -1824,13 +1935,7 @@ - (MaplyComponentObject *)addSelectionVectors:(NSArray *)vectors desc:(NSDiction { MaplyComponentObject *compObj = [[MaplyComponentObject alloc] initWithDesc:desc]; compObj->contents->underConstruction = false; - - if ([vectors count] == 0) - { - compManager->addComponentObject(compObj->contents); - return compObj; - } - + NSArray *argArray = @[vectors, compObj, [NSDictionary dictionaryWithDictionary:desc], [NSNumber numberWithBool:NO], @(MaplyThreadCurrent)]; [self addVectorsRun:argArray]; @@ -1852,24 +1957,42 @@ - (void)changeVectorRun:(NSArray *)argArray if (!compManager->hasComponentObject(vecObj->contents->getId())) return; - VectorManager *vectorManager = (VectorManager *)scene->getManager(kWKVectorManager); + ChangeSet changes; - if (vectorManager) + if (!vecObj->contents->vectorIDs.empty()) { iosDictionary dictWrap(desc); VectorInfo vectorInfo(dictWrap); - // Visual changes - ChangeSet changes; - for (SimpleIDSet::iterator it = vecObj->contents->vectorIDs.begin(); - it != vecObj->contents->vectorIDs.end(); ++it) - vectorManager->changeVectors(*it, vectorInfo, changes); - + if (const auto vectorManager = scene->getManager(kWKVectorManager)) + { + for (const auto vid : vecObj->contents->vectorIDs) + { + vectorManager->changeVectors(vid, vectorInfo, changes); + } + } + // On/off compManager->enableComponentObject(vecObj->contents->getId(), vectorInfo.enable, changes); + } + if (!vecObj->contents->wideVectorIDs.empty()) + { + iosDictionary dictWrap(desc); + WideVectorInfo wideVecInfo(dictWrap); + + if (const auto wideManager = scene->getManager(kWKWideVectorManager)) + { + for (const auto vid : vecObj->contents->wideVectorIDs) + { + wideManager->changeVectors(vid, wideVecInfo, changes); + } + } - [self flushChanges:changes mode:threadMode]; + // On/off + compManager->enableComponentObject(vecObj->contents->getId(), wideVecInfo.enable, changes); } + + [self flushChanges:changes mode:threadMode]; } } @@ -1910,51 +2033,72 @@ - (void)addShapesRun:(NSArray *)argArray NSArray *shapes = [argArray objectAtIndex:0]; MaplyComponentObject *compObj = [argArray objectAtIndex:1]; NSDictionary *inDesc = [argArray objectAtIndex:2]; - MaplyThreadMode threadMode = (MaplyThreadMode)[[argArray objectAtIndex:3] intValue]; - + const auto threadMode = (MaplyThreadMode)[[argArray objectAtIndex:3] intValue]; + iosDictionary dictWrap(inDesc); ShapeInfo shapeInfo(dictWrap); shapeInfo.insideOut = false; [self resolveInfoDefaults:inDesc info:&shapeInfo defaultShader:kMaplyDefaultTriangleShader]; [self resolveDrawPriority:inDesc info:&shapeInfo drawPriority:kMaplyShapeDrawPriorityDefault offset:0]; - + // Need to convert shapes to the form the API is expecting - std::vector ourShapes,specialShapes; + std::vector ourShapes; + std::vector specialShapes; std::set textures; + + // automatic cleanup + std::vector> shapeOwner; + for (MaplyShape *shape in shapes) { Shape *baseShape = NULL; if ([shape isKindOfClass:[MaplyShapeCircle class]]) { - MaplyShapeCircle *circle = (MaplyShapeCircle *)shape; - Circle *newCircle = (Circle *)[circle asWKShape:inDesc]; + auto circle = (MaplyShapeCircle *)shape; + auto newCircle = (Circle *)[circle asWKShape:inDesc]; + shapeOwner.emplace_back(newCircle); + baseShape = newCircle; ourShapes.push_back(baseShape); - } else - if ([shape isKindOfClass:[MaplyShapeSphere class]]) + } + else if ([shape isKindOfClass:[MaplyShapeSphere class]]) { - MaplyShapeSphere *sphere = (MaplyShapeSphere *)shape; - Sphere *newSphere = (Sphere *)[sphere asWKShape:inDesc]; + auto sphere = (MaplyShapeSphere *)shape; + auto newSphere = (Sphere *)[sphere asWKShape:inDesc]; + shapeOwner.emplace_back(newSphere); + baseShape = newSphere; ourShapes.push_back(baseShape); - } else if ([shape isKindOfClass:[MaplyShapeCylinder class]]) + } + else if ([shape isKindOfClass:[MaplyShapeCylinder class]]) { - MaplyShapeCylinder *cyl = (MaplyShapeCylinder *)shape; - Cylinder *newCyl = (Cylinder *)[cyl asWKShape:inDesc]; + auto cyl = (MaplyShapeCylinder *)shape; + auto newCyl = (Cylinder *)[cyl asWKShape:inDesc]; + shapeOwner.emplace_back(newCyl); + baseShape = newCyl; ourShapes.push_back(baseShape); - } else if ([shape isKindOfClass:[MaplyShapeGreatCircle class]]) + } + else if ([shape isKindOfClass:[MaplyShapeGreatCircle class]]) { - MaplyShapeGreatCircle *gc = (MaplyShapeGreatCircle *)shape; - Linear *lin = new Linear(); + auto gc = (MaplyShapeGreatCircle *)shape; + auto lin = std::make_unique(); + float eps = 0.001; - if ([inDesc[kMaplySubdivEpsilon] isKindOfClass:[NSNumber class]]) - eps = [inDesc[kMaplySubdivEpsilon] floatValue]; - bool isStatic = [inDesc[kMaplySubdivType] isEqualToString:kMaplySubdivStatic]; + if (const id descEps = inDesc[kMaplySubdivEpsilon]) + { + if ([descEps isKindOfClass:[NSNumber class]]) + { + eps = [descEps floatValue]; + } + } + + const bool isStatic = [inDesc[kMaplySubdivType] isEqualToString:kMaplySubdivStatic]; if (isStatic) SampleGreatCircleStatic(Point2d(gc.startPt.x,gc.startPt.y),Point2d(gc.endPt.x,gc.endPt.y),gc.height,lin->pts,visualView->coordAdapter,eps); else SampleGreatCircle(Point2d(gc.startPt.x,gc.startPt.y),Point2d(gc.endPt.x,gc.endPt.y),gc.height,lin->pts,visualView->coordAdapter,eps); + lin->lineWidth = gc.lineWidth; if (gc.color) { @@ -1962,12 +2106,16 @@ - (void)addShapesRun:(NSArray *)argArray RGBAColor color = [gc.color asRGBAColor]; lin->color = color; } - baseShape = lin; - specialShapes.push_back(lin); - } else if ([shape isKindOfClass:[MaplyShapeRectangle class]]) + baseShape = lin.get(); + specialShapes.push_back(lin.get()); + shapeOwner.push_back(std::move(lin)); + } + else if ([shape isKindOfClass:[MaplyShapeRectangle class]]) { - MaplyShapeRectangle *rc = (MaplyShapeRectangle *)shape; - Rectangle *rect = (Rectangle *)[rc asWKShape:inDesc]; + const auto rc = (MaplyShapeRectangle *)shape; + auto rect = (Rectangle *)[rc asWKShape:inDesc]; + shapeOwner.emplace_back(rect); + if (rc.color) { rect->useColor = true; @@ -1981,44 +2129,49 @@ - (void)addShapesRun:(NSArray *)argArray } // Note: Selectability ourShapes.push_back(rect); - } else if ([shape isKindOfClass:[MaplyShapeLinear class]]) + } + else if ([shape isKindOfClass:[MaplyShapeLinear class]]) { - MaplyShapeLinear *lin = (MaplyShapeLinear *)shape; - Linear *newLin = (Linear *)[lin asWKShape:inDesc coordAdapter:coordAdapter]; + auto lin = (MaplyShapeLinear *)shape; + auto newLin = (Linear *)[lin asWKShape:inDesc coordAdapter:coordAdapter]; + shapeOwner.emplace_back(newLin); + baseShape = newLin; ourShapes.push_back(newLin); - } else if ([shape isKindOfClass:[MaplyShapeExtruded class]]) + } + else if ([shape isKindOfClass:[MaplyShapeExtruded class]]) { - MaplyShapeExtruded *ex = (MaplyShapeExtruded *)shape; - Extruded *newEx = (Extruded *)[ex asWKShape:inDesc]; + auto ex = (MaplyShapeExtruded *)shape; + auto newEx = (Extruded *)[ex asWKShape:inDesc]; + shapeOwner.emplace_back(newEx); + baseShape = newEx; ourShapes.push_back(newEx); } // Handle selection - if (baseShape) { - if (shape.selectable) - { - baseShape->isSelectable = true; - baseShape->selectID = Identifiable::genId(); - compManager->addSelectObject(baseShape->selectID,shape); - compObj->contents->selectIDs.insert(baseShape->selectID); - } + if (baseShape && shape.selectable) + { + baseShape->isSelectable = true; + baseShape->selectID = Identifiable::genId(); + compManager->addSelectObject(baseShape->selectID,shape); + compObj->contents->selectIDs.insert(baseShape->selectID); } } - + compObj->contents->texs = textures; - - ShapeManager *shapeManager = (ShapeManager *)scene->getManager(kWKShapeManager); - if (shapeManager) + + ChangeSet changes; + if (const auto shapeManager = scene->getManager(kWKShapeManager)) { - ChangeSet changes; if (!ourShapes.empty()) { SimpleIdentity shapeID = shapeManager->addShapes(ourShapes, shapeInfo, changes); if (shapeID != EmptyIdentity) + { compObj->contents->shapeIDs.insert(shapeID); + } } if (!specialShapes.empty()) { @@ -2029,12 +2182,15 @@ - (void)addShapesRun:(NSArray *)argArray } SimpleIdentity shapeID = shapeManager->addShapes(specialShapes, shapeInfo, changes); if (shapeID != EmptyIdentity) + { compObj->contents->shapeIDs.insert(shapeID); + } } - [self flushChanges:changes mode:threadMode]; } - - compManager->addComponentObject(compObj->contents); + + compManager->addComponentObject(compObj->contents, changes); + + [self flushChanges:changes mode:threadMode]; } // Add shapes @@ -2044,13 +2200,7 @@ - (MaplyComponentObject *)addShapes:(NSArray *)shapes desc:(NSDictionary *)desc MaplyComponentObject *compObj = [[MaplyComponentObject alloc] initWithDesc:desc]; compObj->contents->underConstruction = true; - - if ([shapes count] == 0) - { - compManager->addComponentObject(compObj->contents); - return compObj; - } - + NSArray *argArray = @[shapes, compObj, [NSDictionary dictionaryWithDictionary:desc], @(threadMode)]; switch (threadMode) { @@ -2096,46 +2246,51 @@ - (void)addModelInstancesRun:(NSArray *)argArray NSArray *modelInstances = argArray[0]; MaplyComponentObject *compObj = argArray[1]; NSDictionary *inDesc = argArray[2]; - MaplyThreadMode threadMode = (MaplyThreadMode)[[argArray objectAtIndex:3] intValue]; + const auto threadMode = (MaplyThreadMode)[[argArray objectAtIndex:3] intValue]; iosDictionary dictWrap(inDesc); GeometryInfo geomInfo(dictWrap); [self resolveInfoDefaults:inDesc info:&geomInfo defaultShader:kMaplyShaderDefaultModelTri]; [self resolveDrawPriority:inDesc info:&geomInfo drawPriority:kMaplyModelDrawPriorityDefault offset:0]; - GeometryManager *geomManager = (GeometryManager *)scene->getManager(kWKGeometryManager); - FontTextureManager_iOS *fontTexManager = (FontTextureManager_iOS *)scene->getFontTextureManager(); + const auto geomManager = scene->getManager(kWKGeometryManager); + const auto fontTexManager = std::dynamic_pointer_cast(scene->getFontTextureManager()); // Sort the instances with their models GeomModelInstancesSet instSort; std::vector gpuInsts; - for (id theInst in modelInstances) + for (const id theInst in modelInstances) { - if ([theInst isKindOfClass:[MaplyGeomModelInstance class]]) { + if ([theInst isKindOfClass:[MaplyGeomModelInstance class]]) + { MaplyGeomModelInstance *mInst = theInst; if (mInst.model) { GeomModelInstances searchInst(mInst.model); - GeomModelInstancesSet::iterator it = instSort.find(&searchInst); + const auto it = instSort.find(&searchInst); if (it != instSort.end()) { (*it)->instances.push_back(mInst); - } else { - GeomModelInstances *newInsts = new GeomModelInstances(mInst.model); + } + else + { + auto newInsts = new GeomModelInstances(mInst.model); newInsts->instances.push_back(mInst); instSort.insert(newInsts); } } - } else if ([theInst isKindOfClass:[MaplyGeomModelGPUInstance class]]) { + } + else if ([theInst isKindOfClass:[MaplyGeomModelGPUInstance class]]) + { gpuInsts.push_back(theInst); } } // Add each model with its group of instances + ChangeSet changes; if (geomManager) { // Regular geometry instances - ChangeSet changes; for (auto it : instSort) { // Set up the textures and convert the geometry @@ -2171,7 +2326,9 @@ - (void)addModelInstancesRun:(NSArray *)argArray { xAxis = Point3d(1,0,0); yAxis = Point3d(0,1,0); - } else { + } + else + { Point3d north(0,0,1); // Note: Also check if we're at a pole xAxis = north.cross(norm); xAxis.normalize(); @@ -2236,12 +2393,15 @@ - (void)addModelInstancesRun:(NSArray *)argArray SimpleIdentity geomID = geomManager->addGeometryInstances(baseModelID, matInst, geomInfo, changes); if (geomID != EmptyIdentity) + { compObj->contents->geomIDs.insert(geomID); + } } } // GPU Geometry Instances - for (auto geomInst : gpuInsts) { + for (auto geomInst : gpuInsts) + { // Set up the textures and convert the geometry MaplyGeomModel *model = geomInst.model; @@ -2263,17 +2423,19 @@ - (void)addModelInstancesRun:(NSArray *)argArray SimpleIdentity geomID = geomManager->addGPUGeomInstance(baseModelID, programID, srcTexID, srcProgramID, geomInfo, changes); if (geomID != EmptyIdentity) + { compObj->contents->geomIDs.insert(geomID); + } } - - [self flushChanges:changes mode:threadMode]; } // Clean up the instances we sorted for (auto it : instSort) delete it; - - compManager->addComponentObject(compObj->contents); + + compManager->addComponentObject(compObj->contents, changes); + + [self flushChanges:changes mode:threadMode]; } // Called in the layer thread @@ -2285,19 +2447,16 @@ - (void)addGeometryRun:(NSArray *)argArray NSArray *geom = argArray[0]; MaplyComponentObject *compObj = argArray[1]; NSMutableDictionary *inDesc = argArray[2]; - MaplyThreadMode threadMode = (MaplyThreadMode)[[argArray objectAtIndex:3] intValue]; + const auto threadMode = (MaplyThreadMode)[[argArray objectAtIndex:3] intValue]; GeometryInfo geomInfo; [self resolveInfoDefaults:inDesc info:&geomInfo defaultShader:kMaplyDefaultTriangleShader]; [self resolveDrawPriority:inDesc info:&geomInfo drawPriority:kMaplyStickerDrawPriorityDefault offset:0]; - GeometryManager *geomManager = (GeometryManager *)scene->getManager(kWKGeometryManager); - // Add each raw geometry model - if (geomManager) + ChangeSet changes; + if (const auto geomManager = scene->getManager(kWKGeometryManager)) { - ChangeSet changes; - for (MaplyGeomModel *model in geom) { // This is intended to be instanced, but we can use it @@ -2310,11 +2469,11 @@ - (void)addGeometryRun:(NSArray *)argArray if (geomID != EmptyIdentity) compObj->contents->geomIDs.insert(geomID); } - - [self flushChanges:changes mode:threadMode]; } - compManager->addComponentObject(compObj->contents); + compManager->addComponentObject(compObj->contents, changes); + + [self flushChanges:changes mode:threadMode]; } - (MaplyComponentObject *)addModelInstances:(NSArray *)modelInstances desc:(NSDictionary *)desc mode:(MaplyThreadMode)threadMode @@ -2323,13 +2482,7 @@ - (MaplyComponentObject *)addModelInstances:(NSArray *)modelInstances desc:(NSDi MaplyComponentObject *compObj = [[MaplyComponentObject alloc] initWithDesc:desc]; compObj->contents->underConstruction = true; - - if ([modelInstances count] == 0) - { - compManager->addComponentObject(compObj->contents); - return compObj; - } - + NSArray *argArray = @[modelInstances, compObj, [NSDictionary dictionaryWithDictionary:desc], @(threadMode)]; switch (threadMode) { @@ -2350,13 +2503,7 @@ - (MaplyComponentObject *)addGeometry:(NSArray *)geom desc:(NSDictionary *)desc MaplyComponentObject *compObj = [[MaplyComponentObject alloc] initWithDesc:desc]; compObj->contents->underConstruction = true; - - if ([geom count] == 0) - { - compManager->addComponentObject(compObj->contents); - return compObj; - } - + NSArray *argArray = @[geom, compObj, [NSDictionary dictionaryWithDictionary:desc], @(threadMode)]; switch (threadMode) { @@ -2380,14 +2527,14 @@ - (void)addStickersRun:(NSArray *)argArray NSArray *stickers = argArray[0]; MaplyComponentObject *compObj = argArray[1]; NSDictionary *inDesc = argArray[2]; - MaplyThreadMode threadMode = (MaplyThreadMode)[[argArray objectAtIndex:3] intValue]; + const auto threadMode = (MaplyThreadMode)[[argArray objectAtIndex:3] intValue]; iosDictionary dictWrap(inDesc); SphericalChunkInfo chunkInfo(dictWrap); [self resolveInfoDefaults:inDesc info:&chunkInfo defaultShader:kMaplyDefaultTriangleShader]; [self resolveDrawPriority:inDesc info:&chunkInfo drawPriority:kMaplyStickerDrawPriorityDefault offset:0]; - SphericalChunkManager *chunkManager = (SphericalChunkManager *)scene->getManager(kWKSphericalChunkManager); + const auto chunkManager = scene->getManager(kWKSphericalChunkManager); ChangeSet changes; std::vector chunks; @@ -2395,14 +2542,18 @@ - (void)addStickersRun:(NSArray *)argArray for (MaplySticker *sticker in stickers) { std::vector texIDs; - if (sticker.image) { + if (sticker.image) + { if ([sticker.image isKindOfClass:[UIImage class]]) { MaplyTexture *tex = [self addImage:sticker.image imageFormat:sticker.imageFormat mode:threadMode]; if (tex) + { texIDs.push_back(tex.texID); + } compObj->contents->texs.insert(tex); - } else if ([sticker.image isKindOfClass:[MaplyTexture class]]) + } + else if ([sticker.image isKindOfClass:[MaplyTexture class]]) { MaplyTexture *tex = (MaplyTexture *)sticker.image; texIDs.push_back(tex.texID); @@ -2415,9 +2566,12 @@ - (void)addStickersRun:(NSArray *)argArray { MaplyTexture *tex = [self addImage:image imageFormat:sticker.imageFormat mode:threadMode]; if (tex) + { texIDs.push_back(tex.texID); + } compObj->contents->texs.insert(tex); - } else if ([image isKindOfClass:[MaplyTexture class]]) + } + else if ([image isKindOfClass:[MaplyTexture class]]) { MaplyTexture *tex = (MaplyTexture *)image; texIDs.push_back(tex.texID); @@ -2425,7 +2579,7 @@ - (void)addStickersRun:(NSArray *)argArray } } SphericalChunk chunk; - Mbr mbr(Point2f(sticker.ll.x,sticker.ll.y), Point2f(sticker.ur.x,sticker.ur.y)); + const Mbr mbr(Point2f(sticker.ll.x,sticker.ll.y), Point2f(sticker.ur.x,sticker.ur.y)); chunk.mbr = mbr; chunk.texIDs = texIDs; // Note: Move this over to info logic @@ -2434,10 +2588,15 @@ - (void)addStickersRun:(NSArray *)argArray if (chunk.sampleX == 0) chunk.sampleX = 10; if (chunk.sampleY == 0) chunk.sampleY = 10; chunk.programID = chunkInfo.programID; + if (inDesc[kMaplySubdivEpsilon] != nil) + { chunk.eps = [inDesc[kMaplySubdivEpsilon] floatValue]; + } if (sticker.coordSys) + { chunk.coordSys = [sticker.coordSys getCoordSystem]; + } chunk.rotation = sticker.rotation; chunks.push_back(chunk); @@ -2447,11 +2606,14 @@ - (void)addStickersRun:(NSArray *)argArray { SimpleIdentity chunkID = chunkManager->addChunks(chunks, chunkInfo, changes); if (chunkID != EmptyIdentity) + { compObj->contents->chunkIDs.insert(chunkID); + } } + compManager->addComponentObject(compObj->contents, changes); + [self flushChanges:changes mode:threadMode]; - compManager->addComponentObject(compObj->contents); } // Add stickers @@ -2461,13 +2623,7 @@ - (MaplyComponentObject *)addStickers:(NSArray *)stickers desc:(NSDictionary *)d MaplyComponentObject *compObj = [[MaplyComponentObject alloc] initWithDesc:desc]; compObj->contents->underConstruction = true; - - if ([stickers count] == 0) - { - compManager->addComponentObject(compObj->contents); - return compObj; - } - + NSArray *argArray = @[stickers, compObj, [NSDictionary dictionaryWithDictionary:desc], @(threadMode)]; switch (threadMode) { @@ -2497,9 +2653,7 @@ - (void)changeStickerRun:(NSArray *)argArray if (!compManager->hasComponentObject(stickerObj->contents->getId())) return; - SphericalChunkManager *chunkManager = (SphericalChunkManager *)scene->getManager(kWKSphericalChunkManager); - - if (chunkManager) + if (const auto chunkManager = scene->getManager(kWKSphericalChunkManager)) { // Change the images being displayed NSArray *newImages = desc[kMaplyStickerImages]; @@ -2579,7 +2733,7 @@ - (void)addLoftedPolysRun:(NSArray *)argArray MaplyComponentObject *compObj = [argArray objectAtIndex:1]; NSDictionary *inDesc = [argArray objectAtIndex:2]; - MaplyThreadMode threadMode = (MaplyThreadMode)[[argArray objectAtIndex:3] intValue]; + const auto threadMode = (MaplyThreadMode)[[argArray objectAtIndex:3] intValue]; iosDictionary dictWrap(inDesc); LoftedPolyInfo loftInfo(dictWrap); @@ -2588,19 +2742,24 @@ - (void)addLoftedPolysRun:(NSArray *)argArray ShapeSet shapes; for (MaplyVectorObject *vecObj in vectors) + { shapes.insert(vecObj->vObj->shapes.begin(),vecObj->vObj->shapes.end()); + } ChangeSet changes; - LoftManager *loftManager = (LoftManager *)scene->getManager(kWKLoftedPolyManager); - if (loftManager) + if (const auto loftManager = scene->getManager(kWKLoftedPolyManager)) { SimpleIdentity loftID = loftManager->addLoftedPolys(&shapes, loftInfo, changes); - compObj->contents->loftIDs.insert(loftID); + if (loftID) + { + compObj->contents->loftIDs.insert(loftID); + } compObj->contents->isSelectable = false; } - [self flushChanges:changes mode:threadMode]; - compManager->addComponentObject(compObj->contents); + compManager->addComponentObject(compObj->contents, changes); + + [self flushChanges:changes mode:threadMode]; } // Add lofted polys @@ -2610,13 +2769,7 @@ - (MaplyComponentObject *)addLoftedPolys:(NSArray *)vectors desc:(NSDictionary * MaplyComponentObject *compObj = [[MaplyComponentObject alloc] initWithDesc:desc]; compObj->contents->underConstruction = true; - - if ([vectors count] == 0) - { - compManager->addComponentObject(compObj->contents); - return compObj; - } - + NSArray *argArray = @[vectors, compObj, [NSDictionary dictionaryWithDictionary:desc], @(threadMode)]; switch (threadMode) { @@ -2640,7 +2793,7 @@ - (void)addBillboardsRun:(NSArray *)argArray NSArray *bills = argArray[0]; MaplyComponentObject *compObj = argArray[1]; NSDictionary *inDesc = argArray[2]; - MaplyThreadMode threadMode = (MaplyThreadMode)[[argArray objectAtIndex:3] intValue]; + const auto threadMode = (MaplyThreadMode)[[argArray objectAtIndex:3] intValue]; CoordSystemDisplayAdapter *coordAdapter = visualView->coordAdapter; CoordSystem *coordSys = coordAdapter->getCoordSystem(); @@ -2653,8 +2806,8 @@ - (void)addBillboardsRun:(NSArray *)argArray [self resolveDrawPriority:inDesc info:&billInfo drawPriority:kMaplyBillboardDrawPriorityDefault offset:0]; ChangeSet changes; - BillboardManager *billManager = (BillboardManager *)scene->getManager(kWKBillboardManager); - FontTextureManager_iOS *fontTexManager = (FontTextureManager_iOS *)scene->getFontTextureManager(); + const auto billManager = scene->getManager(kWKBillboardManager); + const auto fontTexManager = std::dynamic_pointer_cast(scene->getFontTextureManager()); if (billManager && fontTexManager) { std::vector wkBills; @@ -2750,12 +2903,16 @@ - (void)addBillboardsRun:(NSArray *)argArray } SimpleIdentity billId = billManager->addBillboards(wkBills, billInfo, changes); - compObj->contents->billIDs.insert(billId); + if (billId) + { + compObj->contents->billIDs.insert(billId); + } compObj->contents->isSelectable = false; } - [self flushChanges:changes mode:threadMode]; + + compManager->addComponentObject(compObj->contents, changes); - compManager->addComponentObject(compObj->contents); + [self flushChanges:changes mode:threadMode]; } // Add billboards @@ -2765,13 +2922,7 @@ - (MaplyComponentObject *)addBillboards:(NSArray *)bboards desc:(NSDictionary *) MaplyComponentObject *compObj = [[MaplyComponentObject alloc] initWithDesc:desc]; compObj->contents->underConstruction = true; - - if ([bboards count] == 0) - { - compManager->addComponentObject(compObj->contents); - return compObj; - } - + NSArray *argArray = @[bboards, compObj, [NSDictionary dictionaryWithDictionary:desc], @(threadMode)]; switch (threadMode) { @@ -2794,7 +2945,7 @@ - (void)addParticleSystemRun:(NSArray *)argArray MaplyParticleSystem *partSys = argArray[0]; MaplyComponentObject *compObj = argArray[1]; NSDictionary *inDesc = argArray[2]; - MaplyThreadMode threadMode = (MaplyThreadMode)[[argArray objectAtIndex:3] intValue]; + const auto threadMode = (MaplyThreadMode)[[argArray objectAtIndex:3] intValue]; iosDictionary dictWrap(inDesc); BaseInfo partInfo(dictWrap); @@ -2803,23 +2954,21 @@ - (void)addParticleSystemRun:(NSArray *)argArray SimpleIdentity partSysShaderID = [inDesc[kMaplyShader] intValue]; if (partSysShaderID == EmptyIdentity) + { partSysShaderID = [self getProgramID:kMaplyShaderParticleSystemPointDefault]; - if (partSys.renderShader) { - partSysShaderID = [partSys.renderShader getShaderID]; } - SimpleIdentity calcShaderID = EmptyIdentity; - if (partSys.positionShader) { - calcShaderID = [partSys.positionShader getShaderID]; + if (partSys.renderShader) + { + partSysShaderID = [partSys.renderShader getShaderID]; } + const auto calcShaderID = (partSys.positionShader) ? [partSys.positionShader getShaderID] : EmptyIdentity; - ParticleSystemManager *partSysManager = (ParticleSystemManager *)scene->getManager(kWKParticleSystemManager); - ChangeSet changes; - if (partSysManager) + if (const auto partSysManager = scene->getManager(kWKParticleSystemManager)) { ParticleSystem wkPartSys; wkPartSys.setId(partSys.ident); - wkPartSys.enable = inDesc[kMaplyEnable] != nil ? [inDesc[kMaplyEnable] boolValue] : true; + wkPartSys.enable = dictBool(inDesc, kMaplyEnable); wkPartSys.drawPriority = [inDesc[kMaplyDrawPriority] intValue]; wkPartSys.pointSize = [inDesc[kMaplyPointSize] floatValue]; wkPartSys.name = [partSys.name cStringUsingEncoding:NSASCIIStringEncoding]; @@ -2831,8 +2980,8 @@ - (void)addParticleSystemRun:(NSArray *)argArray wkPartSys.vertexSize = partSys.vertexSize; wkPartSys.baseTime = partSys.baseTime; wkPartSys.continuousUpdate = partSys.continuousUpdate; - wkPartSys.zBufferRead = [inDesc[kMaplyZBufferRead] boolValue]; - wkPartSys.zBufferWrite = [inDesc[kMaplyZBufferWrite] boolValue]; + wkPartSys.zBufferRead = dictBool(inDesc, kMaplyZBufferRead); + wkPartSys.zBufferWrite = dictBool(inDesc, kMaplyZBufferWrite); wkPartSys.renderTargetID = partSys.renderTargetID; // Type switch (partSys.type) @@ -2895,10 +3044,10 @@ - (void)addParticleSystemRun:(NSArray *)argArray partSys.ident = partSysID; compObj->contents->partSysIDs.insert(partSysID); } + + compManager->addComponentObject(compObj->contents, changes); [self flushChanges:changes mode:threadMode]; - - compManager->addComponentObject(compObj->contents); } - (MaplyComponentObject *)addParticleSystem:(MaplyParticleSystem *)partSys desc:(NSDictionary *)desc mode:(MaplyThreadMode)threadMode @@ -2924,9 +3073,8 @@ - (MaplyComponentObject *)addParticleSystem:(MaplyParticleSystem *)partSys desc: - (void)changeParticleSystem:(MaplyComponentObject *)compObj renderTarget:(MaplyRenderTarget *)target { - ParticleSystemManager *partSysManager = (ParticleSystemManager *)scene->getManager(kWKParticleSystemManager); - - if (partSysManager) { + if (const auto partSysManager = scene->getManager(kWKParticleSystemManager)) + { ChangeSet changes; SimpleIdentity targetID = target ? target.renderTargetID : EmptyIdentity; @@ -2943,29 +3091,26 @@ - (void)addParticleSystemBatchRun:(NSArray *)argArray if (isShuttingDown || (!layerThread && !offlineMode)) return; - MaplyParticleBatch *batch = argArray[0]; - MaplyThreadMode threadMode = (MaplyThreadMode)[[argArray objectAtIndex:1] intValue]; - - ParticleSystemManager *partSysManager = (ParticleSystemManager *)scene->getManager(kWKParticleSystemManager); + const MaplyParticleBatch *batch = argArray[0]; + const MaplyThreadMode threadMode = (MaplyThreadMode)[[argArray objectAtIndex:1] intValue]; ChangeSet changes; - if (partSysManager) + if (const auto partSysManager = scene->getManager(kWKParticleSystemManager)) { - bool validBatch = true; + const auto __strong ps = batch.partSys; + ParticleBatch wkBatch; - wkBatch.batchSize = batch.partSys.batchSize; - + wkBatch.batchSize = ps.batchSize; // For Metal, we just pass through the data - wkBatch.data = RawNSDataReaderRef(new RawNSDataReader(batch.data)); - - if (validBatch) - partSysManager->addParticleBatch(batch.partSys.ident, wkBatch, changes); + wkBatch.data = std::make_shared(batch.data); + + partSysManager->addParticleBatch(ps.ident, wkBatch, changes); } - + // We always want a glFlush here changes.push_back(NULL); - + [self flushChanges:changes mode:threadMode]; } @@ -2990,7 +3135,7 @@ - (void)addPointsRun:(NSArray *)argArray NSArray *pointsArray = argArray[0]; MaplyComponentObject *compObj = argArray[1]; NSDictionary *inDesc = argArray[2]; - MaplyThreadMode threadMode = (MaplyThreadMode)[[argArray objectAtIndex:3] intValue]; + const auto threadMode = (MaplyThreadMode)[[argArray objectAtIndex:3] intValue]; iosDictionary dictWrap(inDesc); GeometryInfo geomInfo(dictWrap); @@ -3001,27 +3146,23 @@ - (void)addPointsRun:(NSArray *)argArray compObj->contents->isSelectable = false; - GeometryManager *geomManager = (GeometryManager *)scene->getManager(kWKGeometryManager); - ChangeSet changes; - if (geomManager) + if (const auto geomManager = scene->getManager(kWKGeometryManager)) { for (MaplyPoints *points : pointsArray) { - Matrix4d mat = Matrix4d::Identity(); - if (points.transform) - { - mat = points.transform.mat; - } + const Matrix4d mat = points.transform ? points.transform.mat : Matrix4d::Identity(); SimpleIdentity geomID = geomManager->addGeometryPoints(points->points, mat, geomInfo, changes); if (geomID != EmptyIdentity) + { compObj->contents->geomIDs.insert(geomID); + } } } - + + compManager->addComponentObject(compObj->contents, changes); + [self flushChanges:changes mode:threadMode]; - - compManager->addComponentObject(compObj->contents); } - (MaplyComponentObject *)addPoints:(NSArray *)points desc:(NSDictionary *)desc mode:(MaplyThreadMode)threadMode @@ -3190,6 +3331,7 @@ - (void)removeObjectByIDRun:(NSArray *)argArray ChangeSet changes; compManager->removeComponentObjects(NULL, idSet, changes); + [self flushChanges:changes mode:threadMode]; } @@ -3293,8 +3435,8 @@ - (void)enableObjectsRun:(NSArray *)argArray return; NSArray *theObjs = argArray[0]; - bool enable = [argArray[1] boolValue]; - MaplyThreadMode threadMode = (MaplyThreadMode)[[argArray objectAtIndex:2] intValue]; + const bool enable = [argArray[1] boolValue]; + const auto threadMode = (MaplyThreadMode)[[argArray objectAtIndex:2] intValue]; ChangeSet changes; [self enableObjectsImpl:theObjs enable:enable changes:changes]; @@ -3360,6 +3502,83 @@ - (void)disableObjects:(NSArray *)userObjs mode:(MaplyThreadMode)threadMode } } +// Set the representation to use for the specified UUIDs +- (void)setRepresentation:(NSString *__nullable)repName + fallbackRepName:(NSString *__nullable)fallbackRepName + ofUUIDs:(NSArray *__nonnull)uuids + mode:(MaplyThreadMode)threadMode +{ + NSArray *argArray = @[ + repName ? repName : @"", + fallbackRepName ? fallbackRepName : @"", + uuids, + @(threadMode)]; + + threadMode = [self resolveThreadMode:threadMode]; + switch (threadMode) + { + case MaplyThreadCurrent: + [self setRepresentationRun:argArray]; + break; + case MaplyThreadAny: + { + [self performSelector:@selector(setRepresentationRun:) onThread:layerThread withObject:argArray waitUntilDone:NO]; + break; + } + } +} + +- (void)setRepresentation:(NSString *__nullable)repName + fallbackRepName:(NSString *__nullable)fallbackRepName + ofUUIDs:(NSArray *__nonnull)uuids + changes:(ChangeSet &)changes +{ + if (auto wr = WorkRegion(self)) + { + [self setRepresentationImpl:repName ofUUIDs:uuids changes:changes]; + } +} + +- (void)setRepresentationImpl:(NSString *__nullable)repName + ofUUIDs:(NSArray *__nonnull)uuids + changes:(ChangeSet &)changes +{ + [self setRepresentationImpl:repName fallbackRepName:@"" ofUUIDs:uuids changes:changes]; +} + +- (void)setRepresentationImpl:(NSString *__nullable)repName + fallbackRepName:(NSString *__nullable)fallbackRepName + ofUUIDs:(NSArray *__nonnull)uuids + changes:(ChangeSet &)changes +{ + if (isShuttingDown || (!layerThread && !offlineMode)) + return; + + const auto repStr = [repName asStdString]; + const auto fallbackStr = [fallbackRepName asStdString]; + std::unordered_set uuidSet(uuids.count); + for (const NSString *str in uuids) + { + uuidSet.insert([str asStdString]); + } + compManager->setRepresentation(repStr, fallbackStr, uuidSet, changes); +} + +- (void)setRepresentationRun:(NSArray *)argArray +{ + if (isShuttingDown || (!layerThread && !offlineMode)) + return; + + NSString *repName = argArray[0]; + NSString *fallbackRepName = argArray[1]; + NSArray *uuids = argArray[2]; + const MaplyThreadMode threadMode = (MaplyThreadMode)[[argArray objectAtIndex:3] intValue]; + + ChangeSet changes; + [self setRepresentationImpl:repName fallbackRepName:fallbackRepName ofUUIDs:uuids changes:changes]; + [self flushChanges:changes mode:threadMode]; +} + // Search for a point inside any of our vector objects // Runs in layer thread diff --git a/ios/library/WhirlyGlobe-MaplyComponent/src/control/MaplyBaseViewController.mm b/ios/library/WhirlyGlobe-MaplyComponent/src/control/MaplyBaseViewController.mm index 79923ff923..a7233c06d6 100644 --- a/ios/library/WhirlyGlobe-MaplyComponent/src/control/MaplyBaseViewController.mm +++ b/ios/library/WhirlyGlobe-MaplyComponent/src/control/MaplyBaseViewController.mm @@ -3,7 +3,7 @@ * MaplyComponent * * Created by Steve Gifford on 12/14/12. - * Copyright 2012-2019 mousebird consulting + * Copyright 2012-2021 mousebird consulting * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -508,6 +508,16 @@ - (void)removeShaderProgram:(MaplyShader *__nonnull)shader [renderControl removeShaderProgram:shader]; } +- (void)startMaskTarget:(NSNumber * __nullable)inScale +{ + [renderControl startMaskTarget:inScale]; +} + +- (void)stopMaskTarget +{ + [renderControl stopMaskTarget]; +} + #pragma mark - Defaults and descriptions // Set new hints and update any related settings @@ -981,7 +991,7 @@ - (void)removeRenderTarget:(MaplyRenderTarget *)renderTarget - (void)setMaxLayoutObjects:(int)maxLayoutObjects { - LayoutManager *layoutManager = (LayoutManager *)renderControl->scene->getManager(kWKLayoutManager); + LayoutManagerRef layoutManager = std::dynamic_pointer_cast(renderControl->scene->getManager(kWKLayoutManager)); if (layoutManager) layoutManager->setMaxDisplayObjects(maxLayoutObjects); } @@ -995,7 +1005,7 @@ - (void)setLayoutOverrideIDs:(NSArray *)uuids uuidSet.insert(uuidStr); } - LayoutManager *layoutManager = (LayoutManager *)renderControl->scene->getManager(kWKLayoutManager); + LayoutManagerRef layoutManager = std::dynamic_pointer_cast(renderControl->scene->getManager(kWKLayoutManager)); if (layoutManager) layoutManager->setOverrideUUIDs(uuidSet); } @@ -1052,6 +1062,90 @@ - (void)enableObjects:(NSArray *)theObjs mode:(MaplyThreadMode)threadMode [renderControl enableObjects:theObjs mode:threadMode]; } +- (void)setRepresentation:(NSString *__nullable)repName + ofUUIDs:(NSArray *__nonnull)uuids +{ + if (uuids.count) + { + [renderControl setRepresentation:repName ofUUIDs:uuids mode:MaplyThreadAny]; + } +} + +- (void)setRepresentation:(NSString *__nullable)repName + fallbackRepName:(NSString *__nullable)fallbackRepName + ofUUIDs:(NSArray *__nonnull)uuids +{ + if (uuids.count) + { + [renderControl setRepresentation:repName fallbackRepName:fallbackRepName ofUUIDs:uuids mode:MaplyThreadAny]; + } +} + +- (void)setRepresentation:(NSString *__nullable)repName + ofUUIDs:(NSArray *__nonnull)uuids + mode:(MaplyThreadMode)threadMode +{ + if (uuids.count) + { + [renderControl setRepresentation:repName ofUUIDs:uuids mode:threadMode]; + } +} + +- (void)setRepresentation:(NSString *__nullable)repName + fallbackRepName:(NSString *__nullable)fallbackRepName + ofUUIDs:(NSArray *__nonnull)uuids + mode:(MaplyThreadMode)threadMode +{ + if (uuids.count) + { + [renderControl setRepresentation:repName fallbackRepName:fallbackRepName ofUUIDs:uuids mode:threadMode]; + } +} + +- (void)setRepresentation:(NSString *__nullable)repName + ofObjects:(NSArray *__nonnull)objs +{ + [self setRepresentation:repName ofObjects:objs mode:MaplyThreadAny]; +} + +- (void)setRepresentation:(NSString *__nullable)repName + fallbackRepName:(NSString *__nullable)fallbackRepName + ofObjects:(NSArray *__nonnull)objs +{ + [self setRepresentation:repName fallbackRepName:fallbackRepName ofObjects:objs mode:MaplyThreadAny]; +} + +- (void)setRepresentation:(NSString *__nullable)repName + ofObjects:(NSArray *__nonnull)objs + mode:(MaplyThreadMode)threadMode +{ + [self setRepresentation:repName fallbackRepName:nil ofObjects:objs mode:threadMode]; +} + +- (void)setRepresentation:(NSString *__nullable)repName + fallbackRepName:(NSString *__nullable)fallbackRepName + ofObjects:(NSArray *__nonnull)objs + mode:(MaplyThreadMode)threadMode +{ + if (!objs.count) + { + return; + } + NSMutableArray *theUUIDs = [NSMutableArray new]; + for (const MaplyComponentObject *obj in objs) + { + if (auto uuid = [obj getUUID]) + { + [theUUIDs addObject:uuid]; + } + } + if (![theUUIDs count]) + { + return; + } + [renderControl setRepresentation:repName fallbackRepName:fallbackRepName ofUUIDs:theUUIDs mode:threadMode]; +} + - (void)setUniformBlock:(NSData *__nonnull)uniBlock buffer:(int)bufferID forObjects:(NSArray *__nonnull)compObjs mode:(MaplyThreadMode)threadMode { if (!compObjs) @@ -1363,11 +1457,28 @@ - (void)requirePanGestureRecognizerToFailForGesture:(UIGestureRecognizer *__null // Implement in derived class. } +- (void)startLocationTrackingWithDelegate:(NSObject *)delegate + useHeading:(bool)useHeading + useCourse:(bool)useCourse { + [self startLocationTrackingWithDelegate:delegate + simulator:nil + simInterval:0 + useHeading:useHeading + useCourse:useCourse]; +} -- (void)startLocationTrackingWithDelegate:(NSObject *)delegate useHeading:(bool)useHeading useCourse:(bool)useCourse simulate:(bool)simulate { - if (_locationTracker) - [self stopLocationTracking]; - _locationTracker = [[MaplyLocationTracker alloc] initWithViewC:self delegate:delegate useHeading:useHeading useCourse:useCourse simulate:simulate]; +- (void)startLocationTrackingWithDelegate:(NSObject *)delegate + simulator:(NSObject *__nullable)simulator + simInterval:(NSTimeInterval)simInterval + useHeading:(bool)useHeading + useCourse:(bool)useCourse { + [self stopLocationTracking]; + _locationTracker = [[MaplyLocationTracker alloc] initWithViewC:self + delegate:delegate + simulator:simulator + simInterval:simInterval + useHeading:useHeading + useCourse:useCourse]; } - (MaplyLocationTracker *)getLocationTracker @@ -1386,8 +1497,6 @@ - (void)changeLocationTrackingLockType:(MaplyLocationLockType)lockType forwardTr } - (void)stopLocationTracking { - if (!_locationTracker) - return; [_locationTracker teardown]; _locationTracker = nil; } diff --git a/ios/library/WhirlyGlobe-MaplyComponent/src/control/MaplyInteractionLayer.mm b/ios/library/WhirlyGlobe-MaplyComponent/src/control/MaplyInteractionLayer.mm index e087116efd..78e5e14e54 100644 --- a/ios/library/WhirlyGlobe-MaplyComponent/src/control/MaplyInteractionLayer.mm +++ b/ios/library/WhirlyGlobe-MaplyComponent/src/control/MaplyInteractionLayer.mm @@ -95,7 +95,7 @@ - (void) userDidTapLayerThread:(MaplyTapMessage *)msg - (NSMutableArray*)selectMultipleLabelsAndMarkersForScreenPoint:(CGPoint)screenPoint { - SelectionManager *selectManager = (SelectionManager *)scene->getManager(kWKSelectionManager); + SelectionManagerRef selectManager = std::dynamic_pointer_cast(scene->getManager(kWKSelectionManager)); std::vector selectedObjs; selectManager->pickObjects(Point2f(screenPoint.x,screenPoint.y),10.0,mapView->makeViewState(layerThread.renderer),selectedObjs); diff --git a/ios/library/WhirlyGlobe-MaplyComponent/src/control/MaplyRenderController.mm b/ios/library/WhirlyGlobe-MaplyComponent/src/control/MaplyRenderController.mm index 13bcfb2939..ecb7ae1303 100644 --- a/ios/library/WhirlyGlobe-MaplyComponent/src/control/MaplyRenderController.mm +++ b/ios/library/WhirlyGlobe-MaplyComponent/src/control/MaplyRenderController.mm @@ -1,9 +1,9 @@ /* - * MaplyRenderController.h + * MaplyRenderController.mm * WhirlyGlobeMaplyComponent * * Created by Stephen Gifford on 1/19/18. - * Copyright 2012-2018 Saildrone Inc. + * Copyright 2012-2021 Saildrone Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -212,7 +212,7 @@ - (void)loadSetup_scene:(MaplyBaseInteractionLayer *)newInteractLayer sceneRenderer->setScene(scene); // Set up a Font Texture Manager - fontTexManager = FontTextureManager_iOSRef(new FontTextureManager_iOS(sceneRenderer.get(),scene)); + fontTexManager = std::make_shared(sceneRenderer.get(),scene); scene->setFontTextureManager(fontTexManager); layerThreads = [NSMutableArray array]; @@ -658,6 +658,61 @@ - (void)removeRenderTarget:(MaplyRenderTarget * _Nonnull)renderTarget } } +- (void)startMaskTarget:(NSNumber * __nullable)inScale +{ + if (maskRenderTarget) + return; + + double scale = inScale ? [inScale doubleValue] : 0.5; + + const auto __strong vc = self; + CGSize screenSize = [vc getFramebufferSize]; + + // Can get into a race on framebuffer setup + if (screenSize.width == 0.0) { + dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.01 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ + [self startMaskTarget:inScale]; + }); + return; + } + + screenSize.width *= scale; + screenSize.height *= scale; + + // Set up the render target + maskTex = [vc createTexture:@{kMaplyTexFormat: @(MaplyImageUInt32)} sizeX:screenSize.width sizeY:screenSize.height mode:MaplyThreadCurrent]; + maskRenderTarget = [[MaplyRenderTarget alloc] init]; + maskRenderTarget.texture = maskTex; + [self addRenderTarget:maskRenderTarget]; + + if (interactLayer) { + interactLayer->maskRenderTargetID = [maskRenderTarget renderTargetID]; + interactLayer->maskTexID = maskTex.texID; + } + + // Any programs that needs the mask texture get it wired in + if (Program *prog = scene->findProgramByName(MaplyDefaultWideVectorShader)) { + scene->addChangeRequest(new ShaderAddTextureReq(prog->getId(),0,maskTex.texID,0)); + } +} + +- (void)stopMaskTarget +{ + if (interactLayer) { + interactLayer->maskRenderTargetID = EmptyIdentity; + interactLayer->maskTexID = EmptyIdentity; + } + + if (maskRenderTarget) { + [self removeRenderTarget:maskRenderTarget]; + maskRenderTarget = nil; + } + if (maskTex) { + [self removeTextures:@[maskTex] mode:MaplyThreadCurrent]; + } +} + + - (void)removeObjects:(NSArray *__nonnull)theObjs mode:(MaplyThreadMode)threadMode { if (auto wr = WorkRegion(interactLayer)) { @@ -686,6 +741,27 @@ - (void)enableObjects:(NSArray *__nonnull)theObjs mode:(MaplyThreadMode)threadMo } } +- (void)setRepresentation:(NSString * _Nullable)repName + ofUUIDs:(NSArray * _Nonnull)uuids + mode:(MaplyThreadMode)threadMode +{ + if (auto wr = WorkRegion(interactLayer)) + { + [interactLayer setRepresentation:repName fallbackRepName:nil ofUUIDs:uuids mode:threadMode]; + } +} + +- (void)setRepresentation:(NSString * _Nullable)repName + fallbackRepName:(NSString *__nullable)fallbackRepName + ofUUIDs:(NSArray * _Nonnull)uuids + mode:(MaplyThreadMode)threadMode +{ + if (auto wr = WorkRegion(interactLayer)) + { + [interactLayer setRepresentation:repName fallbackRepName:fallbackRepName ofUUIDs:uuids mode:threadMode]; + } +} + - (void)setUniformBlock:(NSData *__nonnull)uniBlock buffer:(int)bufferID forObjects:(NSArray *__nonnull)compObjs mode:(MaplyThreadMode)threadMode { if (auto wr = WorkRegion(interactLayer)) { @@ -868,7 +944,11 @@ ProgramRef billboardProg(new ProgramMTL([kMaplyShaderBillboardGround cStringUsin program:ProgramRef(new ProgramMTL([kMaplyShaderWideVectorExp cStringUsingEncoding:NSASCIIStringEncoding], [mtlLib newFunctionWithName:@"vertexTri_wideVecExp"], [mtlLib newFunctionWithName:@"fragmentTri_wideVec"]))]; - + [self addShader:kMaplyShaderWideVectorPerformance + program:ProgramRef(new ProgramMTL([kMaplyShaderWideVectorPerformance cStringUsingEncoding:NSASCIIStringEncoding], + [mtlLib newFunctionWithName:@"vertexTri_wideVecPerf"], + [mtlLib newFunctionWithName:@"fragmentTri_wideVecPerf"]))]; + // Screen Space (motion and regular are the same) ProgramRef screenSpace = ProgramRef(new ProgramMTL([kMaplyScreenSpaceDefaultProgram cStringUsingEncoding:NSASCIIStringEncoding], @@ -877,6 +957,13 @@ ProgramRef billboardProg(new ProgramMTL([kMaplyShaderBillboardGround cStringUsin [self addShader:kMaplyScreenSpaceDefaultProgram program:screenSpace]; [self addShader:kMaplyScreenSpaceDefaultMotionProgram program:screenSpace]; + // Renders the mask ID to the screen + ProgramRef screenSpaceMask = ProgramRef(new + ProgramMTL(MaplyScreenSpaceMaskShader, + [mtlLib newFunctionWithName:@"vertexTri_screenSpace"], + [mtlLib newFunctionWithName:@"fragmentTri_mask"])); + [self addShader:kMaplyScreenSpaceMaskProgram program:screenSpaceMask]; + // Screen Space that handles expressions ProgramRef screenSpaceExp = ProgramRef(new ProgramMTL([kMaplyScreenSpaceExpProgram cStringUsingEncoding:NSASCIIStringEncoding], diff --git a/ios/library/WhirlyGlobe-MaplyComponent/src/control/MaplySharedAttributes.m b/ios/library/WhirlyGlobe-MaplyComponent/src/control/MaplySharedAttributes.m index 0cca0239ac..559c70f03c 100644 --- a/ios/library/WhirlyGlobe-MaplyComponent/src/control/MaplySharedAttributes.m +++ b/ios/library/WhirlyGlobe-MaplyComponent/src/control/MaplySharedAttributes.m @@ -3,7 +3,7 @@ * WhirlyGlobe-MaplyComponent * * Created by Ranen Ghosh on 2/24/16. - * Copyright 2011-2019 mousebird consulting + * Copyright 2011-2021 mousebird consulting * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -72,7 +72,10 @@ NSString* const kMaplyZBufferWrite = MaplyZBufferWrite; /// Set the render target if the given geometry type supports it NSString* const kMaplyRenderTarget = MaplyRenderTargetDesc; - +/// The the UUID of the object +NSString* const kMaplyUUID = MaplyUUIDDesc; +/// The representation of the UUID which this object embodies +NSString* const kMaplyRepresentation = MaplyRepresentationDesc; /// Assign a shader program to a particular feature. Use the shader program's name NSString* const kMaplyShader = MaplyShaderString; /// An NSDictionary containing uniforms to apply to a shader before drawing @@ -134,6 +137,10 @@ NSString* const kMaplyTextJustifyRight = MaplyTextJustifyRight; NSString* const kMaplyTextJustifyLeft = MaplyTextJustifyLeft; NSString* const kMaplyTextJustifyCenter = MaplyTextJustifyCenter; +NSString* const kMaplyTextLayoutOffset = MaplyTextLayoutOffset; +NSString* const kMaplyTextLayoutSpacing = MaplyTextLayoutSpacing; +NSString* const kMaplyTextLayoutRepeat = MaplyTextLayoutRepeat; +NSString* const kMaplyTextLayoutDebug = MaplyTextLayoutDebug; /// These are used for screen and regular markers. NSString* const kMaplyClusterGroup = MaplyClusterGroupID; @@ -150,6 +157,8 @@ /// If set, the texture to apply to the feature NSString* const kMaplyVecTexture = MaplyVecTexture; +/// The format of the image given by kMaplyVecTexture, default MaplyImage4Layer8Bit +NSString* const kMaplyVecTextureFormat = MaplyVecTextureFormat; /// X scale for textures applied to vectors NSString* const kMaplyVecTexScaleX = MaplyVecTexScaleX; /// Y scale for textures applied to vectors @@ -161,6 +170,8 @@ NSString* const kMaplyProjectionTangentPlane = MaplyVecProjectionTangentPlane; /// Screen space "projection" for texture coordinates NSString* const kMaplyProjectionScreen = MaplyVecProjectionScreen; +/// No projection for texture coordinates +NSString* const kMaplyProjectionNone = MaplyVecProjectionNone; /// If set to true we'll centered any drawables we create for features /// This fixes the jittering problem when zoomed in close @@ -213,6 +224,12 @@ /// It's real world coordinates for kMaplyWideVecCoordTypeReal and pixel size for kMaplyWideVecCoordTypeScreen NSString* const kMaplyWideVecTexRepeatLen = MaplyWideVecTexRepeatLen; +NSString* const kMaplyWideVecImpl = MaplyWideVecImpl; +NSString* const kMaplyWideVecImplPerf = MaplyWideVecImplPerf; + +/// Offset to left (negative) or right (positive) of the centerline +NSString* const kMaplyWideVecOffset = MaplyWideVecOffset; + /// If set we'll break up a vector feature to the given epsilon on a globe surface NSString* const kMaplySubdivEpsilon = MaplySubdivEpsilon; /// If subdiv epsilon is set we'll look for a subdivision type. Default is simple. @@ -335,9 +352,11 @@ NSString* const kMaplyShaderDefaultWideVector = @"Default Wide Vector"; NSString* const kMaplyShaderWideVectorExp = @"Default Wide Vector with expressions"; +NSString* const kMaplyShaderWideVectorPerformance = @"Wide Vector performance"; NSString* const kMaplyScreenSpaceDefaultMotionProgram = @"Default Screenspace Motion"; NSString* const kMaplyScreenSpaceDefaultProgram = @"Default Screenspace"; +NSString* const kMaplyScreenSpaceMaskProgram = @"Screenspace mask"; NSString* const kMaplyScreenSpaceExpProgram = @"Screenspace with expressions"; NSString* const kMaplyShaderParticleSystemPointDefault = @"Default Part Sys (Point)"; diff --git a/ios/library/WhirlyGlobe-MaplyComponent/src/control/MaplyUpdateLayer.mm b/ios/library/WhirlyGlobe-MaplyComponent/src/control/MaplyUpdateLayer.mm index daea55aef6..3d3d0f9e8a 100644 --- a/ios/library/WhirlyGlobe-MaplyComponent/src/control/MaplyUpdateLayer.mm +++ b/ios/library/WhirlyGlobe-MaplyComponent/src/control/MaplyUpdateLayer.mm @@ -77,10 +77,9 @@ - (bool)startLayer:(WhirlyKitLayerThread *)inLayerThread scene:(WhirlyKit::Scene layerThread = inLayerThread; // We want view updates, but only occasionally - if (layerThread.viewWatcher) - [layerThread.viewWatcher addWatcherTarget:self selector:@selector(viewUpdate:) minTime:_minTime minDist:0.0 maxLagTime:_maxLag]; + [inLayerThread.viewWatcher addWatcherTarget:self selector:@selector(viewUpdate:) minTime:_minTime minDist:0.0 maxLagTime:_maxLag]; - [self performSelector:@selector(startOnThread) onThread:layerThread withObject:nil waitUntilDone:NO]; + [self performSelector:@selector(startOnThread) onThread:inLayerThread withObject:nil waitUntilDone:NO]; return true; } @@ -116,10 +115,9 @@ - (void)viewUpdate:(WhirlyKitViewStateWrapper *)inViewState - (void)cleanupLayers:(WhirlyKitLayerThread *)inLayerThread scene:(WhirlyKit::Scene *)scene { [NSObject cancelPreviousPerformRequestsWithTarget:self]; - - if (layerThread.viewWatcher) - [layerThread.viewWatcher removeWatcherTarget:self selector:@selector(viewUpdate:)]; - + + [layerThread.viewWatcher removeWatcherTarget:self selector:@selector(viewUpdate:)]; + [delegate teardown:self]; } diff --git a/ios/library/WhirlyGlobe-MaplyComponent/src/control/MaplyViewController.mm b/ios/library/WhirlyGlobe-MaplyComponent/src/control/MaplyViewController.mm index 851399cbae..96c7cc282f 100644 --- a/ios/library/WhirlyGlobe-MaplyComponent/src/control/MaplyViewController.mm +++ b/ios/library/WhirlyGlobe-MaplyComponent/src/control/MaplyViewController.mm @@ -280,7 +280,7 @@ - (void)dealloc - (void)setDelegate:(NSObject *)delegate { _delegate = delegate; - delegateRespondsToViewUpdate = [_delegate respondsToSelector:@selector(maplyViewController:didMove:)]; + delegateRespondsToViewUpdate = [delegate respondsToSelector:@selector(maplyViewController:didMove:)]; } // Called by the globe view when something changes @@ -390,12 +390,13 @@ - (void) loadSetup rotateDelegate = [MaplyRotateDelegate rotateDelegateForView:wrapView mapView:mapView.get()]; rotateDelegate.rotateThreshold = _rotateGestureThreshold; } + const auto __strong tapRecognizer = tapDelegate.gestureRecognizer; if(_doubleTapZoomGesture) { doubleTapDelegate = [MaplyDoubleTapDelegate doubleTapDelegateForView:wrapView mapView:mapView]; doubleTapDelegate.minZoom = mapView->minHeightAboveSurface(); doubleTapDelegate.maxZoom = mapView->maxHeightAboveSurface(); - [tapDelegate.gestureRecognizer requireGestureRecognizerToFail:doubleTapDelegate.gestureRecognizer]; + [tapRecognizer requireGestureRecognizerToFail:doubleTapDelegate.gestureRecognizer]; } if(_twoFingerTapGesture) { @@ -404,14 +405,14 @@ - (void) loadSetup twoFingerTapDelegate.maxZoom = mapView->maxHeightAboveSurface(); if (pinchDelegate) [twoFingerTapDelegate.gestureRecognizer requireGestureRecognizerToFail:pinchDelegate.gestureRecognizer]; - [tapDelegate.gestureRecognizer requireGestureRecognizerToFail:twoFingerTapDelegate.gestureRecognizer]; + [tapRecognizer requireGestureRecognizerToFail:twoFingerTapDelegate.gestureRecognizer]; } if (_doubleTapDragGesture) { doubleTapDragDelegate = [MaplyDoubleTapDragDelegate doubleTapDragDelegateForView:wrapView mapView:mapView]; doubleTapDragDelegate.minZoom = mapView->minHeightAboveSurface(); doubleTapDragDelegate.maxZoom = mapView->maxHeightAboveSurface(); - [tapDelegate.gestureRecognizer requireGestureRecognizerToFail:doubleTapDragDelegate.gestureRecognizer]; + [tapRecognizer requireGestureRecognizerToFail:doubleTapDragDelegate.gestureRecognizer]; [panDelegate.gestureRecognizer requireGestureRecognizerToFail:doubleTapDragDelegate.gestureRecognizer]; } if(_cancelAnimationOnTouch) @@ -1265,11 +1266,15 @@ - (float)findHeightToViewBounds:(MaplyBoundingBox)bbox pos:(MaplyCoordinate)pos - (float)findHeightToViewBounds:(MaplyBoundingBox)bbox pos:(MaplyCoordinate)pos marginX:(double)marginX marginY:(double)marginY { - Point2d margin(marginX,marginY); - Maply::MapView tempMapView(*(mapView.get())); + const Point2d margin(marginX,marginY); + + if (!mapView) { + return 0; + } + Maply::MapView tempMapView(*mapView); - Point3d oldLoc = tempMapView.getLoc(); - Point3d newLoc = Point3d(pos.x,pos.y,oldLoc.z()); + const Point3d oldLoc = tempMapView.getLoc(); + const Point3d newLoc = Point3d(pos.x,pos.y,oldLoc.z()); tempMapView.setLoc(newLoc, false); Mbr mbr(Point2f(bbox.ll.x,bbox.ll.y),Point2f(bbox.ur.x,bbox.ur.y)); @@ -1330,48 +1335,43 @@ - (void)handleSelection:(MaplyTapMessage *)msg didSelect:(NSArray *)selectedObjs if (coord.x > M_PI) coord.x -= 2*M_PI * std::ceil((coord.x - M_PI)/(2*M_PI)); + const auto delegate = _delegate; if ([selectedObjs count] > 0 && self.selection) { // The user selected something, so let the delegate know - if (_delegate) - { - if ([_delegate respondsToSelector:@selector(maplyViewController:allSelect:atLoc:onScreen:)]) - [_delegate maplyViewController:self allSelect:selectedObjs atLoc:coord onScreen:msg.touchLoc]; - else { - MaplySelectedObject *selectVecObj = nil; - MaplySelectedObject *selObj = nil; - // If the selected objects are vectors, use the draw priority - for (MaplySelectedObject *whichObj in selectedObjs) + if ([delegate respondsToSelector:@selector(maplyViewController:allSelect:atLoc:onScreen:)]) + [delegate maplyViewController:self allSelect:selectedObjs atLoc:coord onScreen:msg.touchLoc]; + else { + MaplySelectedObject *selectVecObj = nil; + MaplySelectedObject *selObj = nil; + // If the selected objects are vectors, use the draw priority + for (MaplySelectedObject *whichObj in selectedObjs) + { + if ([whichObj.selectedObj isKindOfClass:[MaplyVectorObject class]]) { - if ([whichObj.selectedObj isKindOfClass:[MaplyVectorObject class]]) - { - MaplyVectorObject *vecObj0 = selectVecObj.selectedObj; - MaplyVectorObject *vecObj1 = whichObj.selectedObj; - if (!vecObj0 || ([vecObj1.attributes[kMaplyDrawPriority] intValue] > [vecObj0.attributes[kMaplyDrawPriority] intValue])) - selectVecObj = whichObj; - } else { - // If there's a non-vector object just pick it - selectVecObj = nil; - selObj = whichObj; - break; - } + MaplyVectorObject *vecObj0 = selectVecObj.selectedObj; + MaplyVectorObject *vecObj1 = whichObj.selectedObj; + if (!vecObj0 || ([vecObj1.attributes[kMaplyDrawPriority] intValue] > [vecObj0.attributes[kMaplyDrawPriority] intValue])) + selectVecObj = whichObj; + } else { + // If there's a non-vector object just pick it + selectVecObj = nil; + selObj = whichObj; + break; } - if (selectVecObj) - selObj = selectVecObj; - - if ([_delegate respondsToSelector:@selector(maplyViewController:didSelect:atLoc:onScreen:)]) - [_delegate maplyViewController:self didSelect:selObj.selectedObj atLoc:coord onScreen:msg.touchLoc]; - else if ([_delegate respondsToSelector:@selector(maplyViewController:didSelect:)]) - [_delegate maplyViewController:self didSelect:selObj.selectedObj]; } + if (selectVecObj) + selObj = selectVecObj; + + if ([delegate respondsToSelector:@selector(maplyViewController:didSelect:atLoc:onScreen:)]) + [delegate maplyViewController:self didSelect:selObj.selectedObj atLoc:coord onScreen:msg.touchLoc]; + else if ([delegate respondsToSelector:@selector(maplyViewController:didSelect:)]) + [delegate maplyViewController:self didSelect:selObj.selectedObj]; } } else { // The user didn't select anything, let the delegate know. - if (_delegate) - { - if ([_delegate respondsToSelector:@selector(maplyViewController:didTapAt:)]) - [_delegate maplyViewController:self didTapAt:coord]; - } + if ([delegate respondsToSelector:@selector(maplyViewController:didTapAt:)]) + [delegate maplyViewController:self didTapAt:coord]; if (_autoMoveToTap) [self animateToPosition:coord time:1.0]; } @@ -1467,8 +1467,9 @@ - (void)handleStartMoving:(bool)userMotion { if (!isPanning && !isZooming && !isAnimating) { - if([self.delegate respondsToSelector:@selector(maplyViewControllerDidStartMoving:userMotion:)]) - [self.delegate maplyViewControllerDidStartMoving:self userMotion:userMotion]; + const auto __strong delegate = self.delegate; + if ([delegate respondsToSelector:@selector(maplyViewControllerDidStartMoving:userMotion:)]) + [delegate maplyViewControllerDidStartMoving:self userMotion:userMotion]; } } @@ -1478,11 +1479,12 @@ - (void)handleStopMoving:(bool)userMotion if (isPanning || isZooming || isAnimating) return; - if([self.delegate respondsToSelector:@selector(maplyViewController:didStopMoving:userMotion:)]) + const auto __strong delegate = self.delegate; + if ([delegate respondsToSelector:@selector(maplyViewController:didStopMoving:userMotion:)]) { MaplyCoordinate corners[4]; [self corners:corners]; - [self.delegate maplyViewController:self didStopMoving:corners userMotion:userMotion]; + [delegate maplyViewController:self didStopMoving:corners userMotion:userMotion]; } } @@ -1542,21 +1544,22 @@ - (MaplyBoundingBox) getCurrentExtents - (void)calloutViewClicked:(SMCalloutView *)calloutView { - if([self.delegate respondsToSelector:@selector(maplyViewController:didTapAnnotation:)]) { + const auto __strong delegate = self.delegate; + if([delegate respondsToSelector:@selector(maplyViewController:didTapAnnotation:)]) { for(MaplyAnnotation *annotation in self.annotations) { if(annotation.calloutView == calloutView) { - [self.delegate maplyViewController:self didTapAnnotation:annotation]; + [delegate maplyViewController:self didTapAnnotation:annotation]; return; } } } - if([self.delegate respondsToSelector:@selector(maplyViewController:didClickAnnotation:)]) { + if([delegate respondsToSelector:@selector(maplyViewController:didClickAnnotation:)]) { for(MaplyAnnotation *annotation in self.annotations) { if(annotation.calloutView == calloutView) { #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wdeprecated-declarations" - [self.delegate maplyViewController:self didClickAnnotation:annotation]; + [delegate maplyViewController:self didClickAnnotation:annotation]; return; #pragma clang diagostic pop } @@ -1565,8 +1568,9 @@ - (void)calloutViewClicked:(SMCalloutView *)calloutView } - (void)requirePanGestureRecognizerToFailForGesture:(UIGestureRecognizer *)other { - if (panDelegate && panDelegate.gestureRecognizer) - [other requireGestureRecognizerToFail:panDelegate.gestureRecognizer]; + auto const __strong recognizer = panDelegate.gestureRecognizer; + if (recognizer) + [other requireGestureRecognizerToFail:recognizer]; } diff --git a/ios/library/WhirlyGlobe-MaplyComponent/src/control/WGInteractionLayer.mm b/ios/library/WhirlyGlobe-MaplyComponent/src/control/WGInteractionLayer.mm index c489743e36..0c182b8443 100644 --- a/ios/library/WhirlyGlobe-MaplyComponent/src/control/WGInteractionLayer.mm +++ b/ios/library/WhirlyGlobe-MaplyComponent/src/control/WGInteractionLayer.mm @@ -157,7 +157,7 @@ - (NSObject*)selectLabelsAndMarkerForScreenPoint:(CGPoint)screenPoint { - (NSMutableArray*)selectMultipleLabelsAndMarkersForScreenPoint:(CGPoint)screenPoint { - SelectionManager *selectManager = (SelectionManager *)scene->getManager(kWKSelectionManager); + SelectionManagerRef selectManager = std::dynamic_pointer_cast(scene->getManager(kWKSelectionManager)); std::vector selectedObjs; selectManager->pickObjects(Point2f(screenPoint.x,screenPoint.y),10.0,globeView->makeViewState(layerThread.renderer),selectedObjs); diff --git a/ios/library/WhirlyGlobe-MaplyComponent/src/control/WhirlyGlobeViewController.mm b/ios/library/WhirlyGlobe-MaplyComponent/src/control/WhirlyGlobeViewController.mm index c1b5c015e6..80ef00392f 100644 --- a/ios/library/WhirlyGlobe-MaplyComponent/src/control/WhirlyGlobeViewController.mm +++ b/ios/library/WhirlyGlobe-MaplyComponent/src/control/WhirlyGlobeViewController.mm @@ -186,7 +186,7 @@ - (void) dealloc - (void)setDelegate:(NSObject *)delegate { _delegate = delegate; - delegateRespondsToViewUpdate = [_delegate respondsToSelector:@selector(globeViewController:didMove:)]; + delegateRespondsToViewUpdate = [delegate respondsToSelector:@selector(globeViewController:didMove:)]; } // Called by the globe view when something changes @@ -242,6 +242,7 @@ - (void) loadSetup doubleTapDelegate.zoomTapFactor = _zoomTapFactor; doubleTapDelegate.zoomAnimationDuration = _zoomTapAnimationDuration; } + const auto tapRecognizer = tapDelegate.gestureRecognizer; if(_twoFingerTapGesture) { twoFingerTapDelegate = [WhirlyGlobeTwoFingerTapDelegate twoFingerTapDelegateForView:wrapView globeView:globeView.get()]; @@ -249,17 +250,21 @@ - (void) loadSetup twoFingerTapDelegate.maxZoom = pinchDelegate.maxHeight; twoFingerTapDelegate.zoomTapFactor = _zoomTapFactor; twoFingerTapDelegate.zoomAnimationDuration = _zoomTapAnimationDuration; - if (pinchDelegate) - [twoFingerTapDelegate.gestureRecognizer requireGestureRecognizerToFail:pinchDelegate.gestureRecognizer]; - [tapDelegate.gestureRecognizer requireGestureRecognizerToFail:twoFingerTapDelegate.gestureRecognizer]; + + const auto twoFingerRecognizer = twoFingerTapDelegate.gestureRecognizer; + if (pinchDelegate) { + [twoFingerRecognizer requireGestureRecognizerToFail:pinchDelegate.gestureRecognizer]; + } + [tapRecognizer requireGestureRecognizerToFail:twoFingerRecognizer]; } if (_doubleTapDragGesture) { doubleTapDragDelegate = [WhirlyGlobeDoubleTapDragDelegate doubleTapDragDelegateForView:wrapView globeView:globeView.get()]; doubleTapDragDelegate.minZoom = pinchDelegate.minHeight; doubleTapDragDelegate.maxZoom = pinchDelegate.maxHeight; - [tapDelegate.gestureRecognizer requireGestureRecognizerToFail:doubleTapDragDelegate.gestureRecognizer]; - [panDelegate.gestureRecognizer requireGestureRecognizerToFail:doubleTapDragDelegate.gestureRecognizer]; + const auto doubleTapRecognizer = doubleTapDragDelegate.gestureRecognizer; + [tapRecognizer requireGestureRecognizerToFail:doubleTapRecognizer]; + [panDelegate.gestureRecognizer requireGestureRecognizerToFail:doubleTapRecognizer]; } } @@ -365,6 +370,7 @@ - (void)setZoomAroundPinch:(bool)zoomAroundPinch { - (void)setPinchGesture:(bool)pinchGesture { + auto __strong gr = pinchDelegate.gestureRecognizer; // false positive on weak ref access... annoying if (pinchGesture) { if (!pinchDelegate) @@ -375,14 +381,15 @@ - (void)setPinchGesture:(bool)pinchGesture pinchDelegate.northUp = panDelegate.northUp; pinchDelegate.rotateDelegate = rotateDelegate; tiltDelegate.pinchDelegate = pinchDelegate; - - if (twoFingerTapDelegate) - [twoFingerTapDelegate.gestureRecognizer requireGestureRecognizerToFail:pinchDelegate.gestureRecognizer]; + + gr = pinchDelegate.gestureRecognizer; + + [twoFingerTapDelegate.gestureRecognizer requireGestureRecognizerToFail:gr]; } } else { if (pinchDelegate) { - [wrapView removeGestureRecognizer:pinchDelegate.gestureRecognizer]; + [wrapView removeGestureRecognizer:gr]; pinchDelegate = nil; tiltDelegate.pinchDelegate = nil; } @@ -427,6 +434,7 @@ - (bool)rotateGesture - (void)setTiltGesture:(bool)tiltGesture { + auto __strong tiltRecognizer = tiltDelegate.gestureRecognizer; if (tiltGesture) { if (!tiltDelegate) @@ -434,17 +442,20 @@ - (void)setTiltGesture:(bool)tiltGesture tiltDelegate = [WhirlyGlobeTiltDelegate tiltDelegateForView:wrapView globeView:globeView.get()]; tiltDelegate.pinchDelegate = pinchDelegate; tiltDelegate.tiltCalcDelegate = tiltControlDelegate; + + tiltRecognizer = tiltDelegate.gestureRecognizer; + [tapDelegate.gestureRecognizer requireGestureRecognizerToFail:doubleTapDelegate.gestureRecognizer]; - [tiltDelegate.gestureRecognizer requireGestureRecognizerToFail:twoFingerTapDelegate.gestureRecognizer]; - [tiltDelegate.gestureRecognizer requireGestureRecognizerToFail:doubleTapDragDelegate.gestureRecognizer]; - } - } else { - if (tiltDelegate) - { - [wrapView removeGestureRecognizer:tiltDelegate.gestureRecognizer]; - tiltDelegate = nil; + + [tiltRecognizer requireGestureRecognizerToFail:twoFingerTapDelegate.gestureRecognizer]; + [tiltRecognizer requireGestureRecognizerToFail:doubleTapDragDelegate.gestureRecognizer]; } } + else if (tiltDelegate) + { + [wrapView removeGestureRecognizer:tiltRecognizer]; + tiltDelegate = nil; + } } - (bool)tiltGesture @@ -639,6 +650,7 @@ - (void)setDoubleTapZoomGesture:(bool)doubleTapZoomGesture - (void)setTwoFingerTapGesture:(bool)twoFingerTapGesture { _twoFingerTapGesture = twoFingerTapGesture; + auto __strong twoFingerTapRecognizer = twoFingerTapDelegate.gestureRecognizer; if (twoFingerTapGesture) { if (!twoFingerTapDelegate) @@ -648,13 +660,17 @@ - (void)setTwoFingerTapGesture:(bool)twoFingerTapGesture twoFingerTapDelegate.maxZoom = pinchDelegate.maxHeight; twoFingerTapDelegate.zoomTapFactor = _zoomTapFactor; twoFingerTapDelegate.zoomAnimationDuration = _zoomTapAnimationDuration; - if (pinchDelegate) - [twoFingerTapDelegate.gestureRecognizer requireGestureRecognizerToFail:pinchDelegate.gestureRecognizer]; + + twoFingerTapRecognizer = twoFingerTapDelegate.gestureRecognizer; + + if (pinchDelegate) { + [twoFingerTapRecognizer requireGestureRecognizerToFail:pinchDelegate.gestureRecognizer]; + } } } else { if (twoFingerTapDelegate) { - [wrapView removeGestureRecognizer:twoFingerTapDelegate.gestureRecognizer]; + [wrapView removeGestureRecognizer:twoFingerTapRecognizer]; twoFingerTapDelegate.gestureRecognizer = nil; twoFingerTapDelegate = nil; } @@ -664,6 +680,7 @@ - (void)setTwoFingerTapGesture:(bool)twoFingerTapGesture - (void)setDoubleTapDragGesture:(bool)doubleTapDragGesture { _doubleTapZoomGesture = doubleTapDragGesture; + auto __strong doubleTapRecognizer = doubleTapDragDelegate.gestureRecognizer; if (doubleTapDragGesture) { if (!doubleTapDragDelegate) @@ -671,13 +688,16 @@ - (void)setDoubleTapDragGesture:(bool)doubleTapDragGesture doubleTapDragDelegate = [WhirlyGlobeDoubleTapDragDelegate doubleTapDragDelegateForView:wrapView globeView:globeView.get()]; doubleTapDragDelegate.minZoom = pinchDelegate.minHeight; doubleTapDragDelegate.maxZoom = pinchDelegate.maxHeight; - [tapDelegate.gestureRecognizer requireGestureRecognizerToFail:doubleTapDragDelegate.gestureRecognizer]; - [panDelegate.gestureRecognizer requireGestureRecognizerToFail:doubleTapDragDelegate.gestureRecognizer]; + + doubleTapRecognizer = doubleTapDelegate.gestureRecognizer; + + [tapDelegate.gestureRecognizer requireGestureRecognizerToFail:doubleTapRecognizer]; + [panDelegate.gestureRecognizer requireGestureRecognizerToFail:doubleTapRecognizer]; } } else { if (doubleTapDragDelegate) { - [wrapView removeGestureRecognizer:doubleTapDragDelegate.gestureRecognizer]; + [wrapView removeGestureRecognizer:doubleTapRecognizer]; doubleTapDragDelegate.gestureRecognizer = nil; doubleTapDragDelegate = nil; } @@ -1047,17 +1067,16 @@ - (void)getPositionD:(MaplyCoordinateD *)pos height:(double *)height // Called back on the main thread after the interaction thread does the selection - (void)handleSelection:(WhirlyGlobeTapMessage *)msg didSelect:(NSArray *)selectedObjs { - MaplyCoordinate coord; - coord.x = msg.whereGeo.lon(); - coord.y = msg.whereGeo.lat(); - - bool tappedOutside = msg.worldLoc == Point3f(0,0,0); + const MaplyCoordinate coord { .x = msg.whereGeo.lon(), .y = msg.whereGeo.lat() }; + + const bool tappedOutside = msg.worldLoc == Point3f(0,0,0); + const auto __strong delegate = _delegate; if ([selectedObjs count] > 0 && self.selection) { // The user selected something, so let the delegate know - if ([_delegate respondsToSelector:@selector(globeViewController:allSelect:atLoc:onScreen:)]) - [_delegate globeViewController:self allSelect:selectedObjs atLoc:coord onScreen:msg.touchLoc]; + if ([delegate respondsToSelector:@selector(globeViewController:allSelect:atLoc:onScreen:)]) + [delegate globeViewController:self allSelect:selectedObjs atLoc:coord onScreen:msg.touchLoc]; else { MaplySelectedObject *selectVecObj = nil; MaplySelectedObject *selObj = nil; @@ -1066,8 +1085,8 @@ - (void)handleSelection:(WhirlyGlobeTapMessage *)msg didSelect:(NSArray *)select { if ([whichObj.selectedObj isKindOfClass:[MaplyVectorObject class]]) { - MaplyVectorObject *vecObj0 = selectVecObj.selectedObj; - MaplyVectorObject *vecObj1 = whichObj.selectedObj; + const MaplyVectorObject *vecObj0 = selectVecObj.selectedObj; + const MaplyVectorObject *vecObj1 = whichObj.selectedObj; if (!vecObj0 || ([vecObj1.attributes[kMaplyDrawPriority] intValue] > [vecObj0.attributes[kMaplyDrawPriority] intValue])) selectVecObj = whichObj; } else { @@ -1080,25 +1099,25 @@ - (void)handleSelection:(WhirlyGlobeTapMessage *)msg didSelect:(NSArray *)select if (selectVecObj) selObj = selectVecObj; - if (_delegate && [_delegate respondsToSelector:@selector(globeViewController:didSelect:atLoc:onScreen:)]) - [_delegate globeViewController:self didSelect:selObj.selectedObj atLoc:coord onScreen:msg.touchLoc]; - else if (_delegate && [_delegate respondsToSelector:@selector(globeViewController:didSelect:)]) + if (delegate && [delegate respondsToSelector:@selector(globeViewController:didSelect:atLoc:onScreen:)]) + [delegate globeViewController:self didSelect:selObj.selectedObj atLoc:coord onScreen:msg.touchLoc]; + else if (delegate && [delegate respondsToSelector:@selector(globeViewController:didSelect:)]) { - [_delegate globeViewController:self didSelect:selObj.selectedObj]; + [delegate globeViewController:self didSelect:selObj.selectedObj]; } } } else { - if (_delegate) + if (delegate) { if (tappedOutside) { // User missed all objects and tapped outside the globe - if ([_delegate respondsToSelector:@selector(globeViewControllerDidTapOutside:)]) - [_delegate globeViewControllerDidTapOutside:self]; + if ([delegate respondsToSelector:@selector(globeViewControllerDidTapOutside:)]) + [delegate globeViewControllerDidTapOutside:self]; } else { // The user didn't select anything, let the delegate know. - if ([_delegate respondsToSelector:@selector(globeViewController:didTapAt:)]) - [_delegate globeViewController:self didTapAt:coord]; + if ([delegate respondsToSelector:@selector(globeViewController:didTapAt:)]) + [delegate globeViewController:self didTapAt:coord]; } } // Didn't select anything, so rotate @@ -1139,8 +1158,9 @@ - (void) handleStartMoving:(bool)userMotion { if (!isPanning && !isRotating && !isZooming && !isAnimating && !isTilting) { - if ([_delegate respondsToSelector:@selector(globeViewControllerDidStartMoving:userMotion:)]) - [_delegate globeViewControllerDidStartMoving:self userMotion:userMotion]; + const auto __strong delegate = _delegate; + if ([delegate respondsToSelector:@selector(globeViewControllerDidStartMoving:userMotion:)]) + [delegate globeViewControllerDidStartMoving:self userMotion:userMotion]; } } @@ -1183,13 +1203,14 @@ - (void)handleStopMoving:(bool)userMotion if (isPanning || isRotating || isZooming || isAnimating || isTilting) return; - if (![_delegate respondsToSelector:@selector(globeViewController:didStopMoving:userMotion:)]) + const auto __strong delegate = _delegate; + if (![delegate respondsToSelector:@selector(globeViewController:didStopMoving:userMotion:)]) return; MaplyCoordinate corners[4]; [self corners:corners forRot:globeView->getRotQuat() viewMat:globeView->calcViewMatrix()]; - [_delegate globeViewController:self didStopMoving:corners userMotion:userMotion]; + [delegate globeViewController:self didStopMoving:corners userMotion:userMotion]; } // Called when the tilt delegate starts moving @@ -1331,7 +1352,10 @@ - (bool)checkCoverage:(Mbr &)mbr globeView:(WhirlyGlobe::GlobeView *)theView hei - (float)findHeightToViewBounds:(MaplyBoundingBox)bbox pos:(MaplyCoordinate)pos { - GlobeView tempGlobe(*globeView.get()); + if (!globeView) { + return 0; + } + GlobeView tempGlobe(*globeView); float oldHeight = globeView->getHeightAboveGlobe(); Eigen::Quaterniond newRotQuat = tempGlobe.makeRotationToGeoCoord(GeoCoord(pos.x,pos.y), true); @@ -1457,6 +1481,15 @@ - (bool)geoPointFromScreen:(CGPoint)screenPt geoCoord:(MaplyCoordinate *)retCoor return false; } +- (nullable NSValue *)geoPointFromScreen:(CGPoint)screenPt +{ + MaplyCoordinate coord; + if ([self geoPointFromScreen:screenPt geoCoord:&coord]) { + return [NSValue valueWithMaplyCoordinate:coord]; + } + return nil; +} + - (NSArray *)geocPointFromScreen:(CGPoint)screenPt { double coords[3]; @@ -1531,7 +1564,7 @@ - (id)findObjectAtLocation:(CGPoint)screenPt return nil; // Look for the object, returns an ID - SelectionManager *selectManager = (SelectionManager *)renderControl->scene->getManager(kWKSelectionManager); + SelectionManagerRef selectManager = std::dynamic_pointer_cast(renderControl->scene->getManager(kWKSelectionManager)); SimpleIdentity objId = selectManager->pickObject(Point2f(screenPt.x,screenPt.y), 10.0, globeView->makeViewState(renderControl->sceneRenderer.get())); if (objId != EmptyIdentity) @@ -1544,8 +1577,9 @@ - (id)findObjectAtLocation:(CGPoint)screenPt } - (void)requirePanGestureRecognizerToFailForGesture:(UIGestureRecognizer *)other { - if (panDelegate && panDelegate.gestureRecognizer) - [other requireGestureRecognizerToFail:panDelegate.gestureRecognizer]; + if (const auto __strong rec = panDelegate.gestureRecognizer) { + [other requireGestureRecognizerToFail:rec]; + } } diff --git a/ios/library/WhirlyGlobe-MaplyComponent/src/gestures/GlobePanDelegate.mm b/ios/library/WhirlyGlobe-MaplyComponent/src/gestures/GlobePanDelegate.mm index 25d5922837..ea87ced24e 100644 --- a/ios/library/WhirlyGlobe-MaplyComponent/src/gestures/GlobePanDelegate.mm +++ b/ios/library/WhirlyGlobe-MaplyComponent/src/gestures/GlobePanDelegate.mm @@ -118,7 +118,7 @@ - (void)startRotateManipulation:(UIPanGestureRecognizer *)pan sceneRender:(Scene spinDate = TimeGetCurrent(); lastTouch = [pan locationInView:wrapView]; - IntersectionManager *intManager = (IntersectionManager *)sceneRender->getScene()->getManager(kWKIntersectionManager); + IntersectionManagerRef intManager = std::dynamic_pointer_cast(sceneRender->getScene()->getManager(kWKIntersectionManager)); // Look for an intersection with grabbable objects Point3d interPt; @@ -191,20 +191,21 @@ - (void)panAction:(id)sender auto frameSize = sceneRender->getFramebufferSizeScaled(); // End for more than one finger + const auto __strong gestureRecognizer = self.gestureRecognizer; if ([pan numberOfTouches] > 1) { panType = PanSuspended; runEndMomentum = false; - if ([_gestureRecognizer isKindOfClass:[MinDelayPanGestureRecognizer class]]) { + if ([gestureRecognizer isKindOfClass:[MinDelayPanGestureRecognizer class]]) { // Don't cancel if interoperating with a scroll view. (Otherwise // the globe view would be paged away.) - MinDelayPanGestureRecognizer *minDelayPanGestureRecognizer = (MinDelayPanGestureRecognizer *)_gestureRecognizer; + MinDelayPanGestureRecognizer *minDelayPanGestureRecognizer = (MinDelayPanGestureRecognizer *)gestureRecognizer; [minDelayPanGestureRecognizer forceEnd]; } else { // Cancel gesture - _gestureRecognizer.enabled = false; - _gestureRecognizer.enabled = true; + gestureRecognizer.enabled = false; + gestureRecognizer.enabled = true; } return; } @@ -222,8 +223,8 @@ - (void)panAction:(id)sender // When interoperating with a scroll view, this allows a horizontal pan gesture // to be interpreted as a swipe, and thus trigger paging the scroll view. if (panType == PanNone) { - self.gestureRecognizer.enabled = NO; - self.gestureRecognizer.enabled = YES; + gestureRecognizer.enabled = NO; + gestureRecognizer.enabled = YES; return; } [[NSNotificationCenter defaultCenter] postNotificationName:kPanDelegateDidStart object:globeView->tag]; @@ -258,8 +259,8 @@ - (void)panAction:(id)sender // Cancel when they do that if (!onSphere && globeView->getTilt() != 0.0) { - self.gestureRecognizer.enabled = NO; - self.gestureRecognizer.enabled = YES; + gestureRecognizer.enabled = NO; + gestureRecognizer.enabled = YES; return; } diff --git a/ios/library/WhirlyGlobe-MaplyComponent/src/gestures/GlobePinchDelegate.mm b/ios/library/WhirlyGlobe-MaplyComponent/src/gestures/GlobePinchDelegate.mm index 24ef6b72c3..5caab82fc0 100644 --- a/ios/library/WhirlyGlobe-MaplyComponent/src/gestures/GlobePinchDelegate.mm +++ b/ios/library/WhirlyGlobe-MaplyComponent/src/gestures/GlobePinchDelegate.mm @@ -128,13 +128,15 @@ - (void)pinchGesture:(id)sender return; } - IntersectionManager *intManager = (IntersectionManager *)sceneRender->getScene()->getManager(kWKIntersectionManager); + IntersectionManagerRef intManager = std::dynamic_pointer_cast(sceneRender->getScene()->getManager(kWKIntersectionManager)); if (!intManager) return; if (pinch.numberOfTouches != 2) valid = false; - + + const auto __strong gestureRecognizer = self.gestureRecognizer; + auto frameSizeScaled = sceneRender->getFramebufferSizeScaled(); switch (theState) { @@ -308,8 +310,8 @@ - (void)pinchGesture:(id)sender if (!onSphere && globeView->getTilt() != 0.0) { globeView->setRotQuat(oldQuat); - self.gestureRecognizer.enabled = NO; - self.gestureRecognizer.enabled = YES; + gestureRecognizer.enabled = NO; + gestureRecognizer.enabled = YES; return; } @@ -321,9 +323,8 @@ - (void)pinchGesture:(id)sender globeView->setTilt(newTilt); } - if (_rotateDelegate) - [_rotateDelegate updateWithCenter:[pinch locationInView:wrapView] touch:[pinch locationOfTouch:0 inView:wrapView ] wrapView:wrapView]; - + [_rotateDelegate updateWithCenter:[pinch locationInView:wrapView] touch:[pinch locationOfTouch:0 inView:wrapView ] wrapView:wrapView]; + globeView->runViewUpdates(); } break; diff --git a/ios/library/WhirlyGlobe-MaplyComponent/src/gestures/GlobeRotateDelegate.mm b/ios/library/WhirlyGlobe-MaplyComponent/src/gestures/GlobeRotateDelegate.mm index ca86bc4876..df50265be1 100644 --- a/ios/library/WhirlyGlobe-MaplyComponent/src/gestures/GlobeRotateDelegate.mm +++ b/ios/library/WhirlyGlobe-MaplyComponent/src/gestures/GlobeRotateDelegate.mm @@ -136,7 +136,7 @@ - (void)rotateGesture:(id)sender return; } - IntersectionManager *intManager = (IntersectionManager *)sceneRender->getScene()->getManager(kWKIntersectionManager); + IntersectionManagerRef intManager = std::dynamic_pointer_cast(sceneRender->getScene()->getManager(kWKIntersectionManager)); switch (rotate.state) { diff --git a/ios/library/WhirlyGlobe-MaplyComponent/src/gestures/GlobeTiltDelegate.mm b/ios/library/WhirlyGlobe-MaplyComponent/src/gestures/GlobeTiltDelegate.mm index 27f662c6fd..a060c93461 100644 --- a/ios/library/WhirlyGlobe-MaplyComponent/src/gestures/GlobeTiltDelegate.mm +++ b/ios/library/WhirlyGlobe-MaplyComponent/src/gestures/GlobeTiltDelegate.mm @@ -68,24 +68,28 @@ - (void)panAction:(id)sender UIView *wrapView = (UIView *)pan.view; // SceneRenderer *sceneRenderer = [wrapView getRenderer]; + const auto __strong pinchDelegate = _pinchDelegate; + const auto __strong pinchRecognizer = pinchDelegate.gestureRecognizer; if (pan.state == UIGestureRecognizerStateCancelled) { [[NSNotificationCenter defaultCenter] postNotificationName:kTiltDelegateDidEnd object:globeView->tag]; active = false; - if (turnedOffPinch && !_pinchDelegate.gestureRecognizer.enabled) - _pinchDelegate.gestureRecognizer.enabled = true; + if (turnedOffPinch && !pinchRecognizer.enabled) + pinchRecognizer.enabled = true; return; } // Need three fingers for tilt if ([pan numberOfTouches] != 3) { - self.gestureRecognizer.enabled = false; - self.gestureRecognizer.enabled = true; + // reset? + const auto __strong gr = self.gestureRecognizer; + gr.enabled = false; + gr.enabled = true; return; } - CGSize frameSize = wrapView.frame.size; + const CGSize frameSize = wrapView.frame.size; switch (pan.state) { @@ -95,10 +99,10 @@ - (void)panAction:(id)sender startTilt = globeView->getTilt(); startTouch = [pan locationInView:wrapView]; active = true; - if (_pinchDelegate.gestureRecognizer.enabled) + if (pinchRecognizer.enabled) { turnedOffPinch = true; - _pinchDelegate.gestureRecognizer.enabled = false; + pinchRecognizer.enabled = false; } [[NSNotificationCenter defaultCenter] postNotificationName:kTiltDelegateDidStart object:globeView->tag]; @@ -127,8 +131,8 @@ - (void)panAction:(id)sender active = false; if (turnedOffPinch) { - if (!_pinchDelegate.gestureRecognizer.enabled) - _pinchDelegate.gestureRecognizer.enabled = true; + if (!pinchRecognizer.enabled) + pinchRecognizer.enabled = true; turnedOffPinch = false; } break; @@ -138,8 +142,8 @@ - (void)panAction:(id)sender active = false; if (turnedOffPinch) { - if (!_pinchDelegate.gestureRecognizer.enabled) - _pinchDelegate.gestureRecognizer.enabled = true; + if (!pinchRecognizer.enabled) + pinchRecognizer.enabled = true; turnedOffPinch = false; } } diff --git a/ios/library/WhirlyGlobe-MaplyComponent/src/gestures/Maply3dTouchPreviewDelegate.mm b/ios/library/WhirlyGlobe-MaplyComponent/src/gestures/Maply3dTouchPreviewDelegate.mm index a055d9ab42..a023fcae56 100644 --- a/ios/library/WhirlyGlobe-MaplyComponent/src/gestures/Maply3dTouchPreviewDelegate.mm +++ b/ios/library/WhirlyGlobe-MaplyComponent/src/gestures/Maply3dTouchPreviewDelegate.mm @@ -37,30 +37,28 @@ + (Maply3dTouchPreviewDelegate * _Nonnull)touchDelegate:(MaplyBaseViewController - (UIViewController * _Nullable)previewingContext:(id _Nonnull)previewingContext viewControllerForLocation:(CGPoint)location { - NSObject *selectedObject = [interactLayer selectLabelsAndMarkerForScreenPoint:location]; + const auto __strong vc = viewC; + const auto __strong layer = interactLayer; + NSObject *selectedObject = [layer selectLabelsAndMarkerForScreenPoint:location]; if(!selectedObject) { - if([viewC isKindOfClass:[MaplyViewController class]]) + if([vc isKindOfClass:[MaplyViewController class]]) { - MaplyCoordinate coord = [(MaplyViewController*)viewC geoFromScreenPoint:location]; - selectedObject = [[interactLayer findVectorsInPoint:Point2f(coord.x, coord.y)] firstObject]; + const MaplyCoordinate coord = [(MaplyViewController*)vc geoFromScreenPoint:location]; + selectedObject = [[layer findVectorsInPoint:Point2f(coord.x, coord.y)] firstObject]; } else { //globe MaplyCoordinate coord; - if([(WhirlyGlobeViewController*)viewC geoPointFromScreen:location geoCoord:&coord]) + if([(WhirlyGlobeViewController*)vc geoPointFromScreen:location geoCoord:&coord]) { - selectedObject = [[interactLayer findVectorsInPoint:Point2f(coord.x, coord.y)] firstObject]; + selectedObject = [[layer findVectorsInPoint:Point2f(coord.x, coord.y)] firstObject]; } } } - - if(!selectedObject) - { - return nil; - } - return [self.datasource maplyViewController:viewC - previewViewControllerForSelection:selectedObject]; + + return selectedObject ? [self.datasource maplyViewController:vc + previewViewControllerForSelection:selectedObject] : nil; } diff --git a/ios/library/WhirlyGlobe-MaplyComponent/src/helpers/MapboxKindaMap.swift b/ios/library/WhirlyGlobe-MaplyComponent/src/helpers/MapboxKindaMap.swift index 15194df89f..31b3f953ac 100644 --- a/ios/library/WhirlyGlobe-MaplyComponent/src/helpers/MapboxKindaMap.swift +++ b/ios/library/WhirlyGlobe-MaplyComponent/src/helpers/MapboxKindaMap.swift @@ -47,7 +47,10 @@ public class MapboxKindaMap { // font name in the style. Font names in the style often don't map // directly to local font names. public var fontOverride : (_ name: String) -> UIFontDescriptor? = { _ in return nil } - + + // If set, we'll run the stylesheet JSON through it before loading it into a style delegate + public var preStyle: (_ : [AnyHashable:Any]) -> [AnyHashable:Any] = { $0 } + // If set, this will be called right after everything is set up // This is after all the configuration files are fetched so // you can make any final tweaks to loading objects here @@ -259,7 +262,9 @@ public class MapboxKindaMap { } DispatchQueue.main.async { - self.outstandingFetches[fetchIdx1] = nil + if self.outstandingFetches.count > fetchIdx1 { + self.outstandingFetches[fetchIdx1] = nil + } self.checkFinished() } } @@ -279,7 +284,9 @@ public class MapboxKindaMap { } DispatchQueue.main.async { - self.outstandingFetches[fetchIdx2] = nil + if self.outstandingFetches.count > fetchIdx2 { + self.outstandingFetches[fetchIdx2] = nil + } self.checkFinished() } } @@ -322,18 +329,32 @@ public class MapboxKindaMap { // Go get the style sheet (this will also handle local let fetchIdx = self.outstandingFetches.count let dataTask = URLSession.shared.dataTask(with: self.makeURLRequest(styleURL)) { (data, _, error) in - guard error == nil, let data = data else { + guard error == nil, var data = data else { print("Error fetching style sheet:\n\(String(describing: error))") self.stop() return } - + + var jsonDict: [AnyHashable: Any]? + do { + let result = try JSONSerialization.jsonObject(with: data) + jsonDict = result as? [AnyHashable: Any] + if jsonDict != nil { + jsonDict = self.preStyle(jsonDict!) + if let jd = jsonDict { + data = try JSONSerialization.data(withJSONObject: jd) + } + } + } catch { + print("Failed to parse stylesheet: \(String(describing: error))") + } + DispatchQueue.main.async { self.outstandingFetches[fetchIdx] = nil - guard let styleSheet = MapboxVectorStyleSet(json: data, - settings: self.styleSettings, - viewC: viewC) else { + guard let styleSheet = MapboxVectorStyleSet(dict: jsonDict!, + settings: self.styleSettings, + viewC: viewC) else { print("Failed to parse style sheet") self.stop() return @@ -435,7 +456,6 @@ public class MapboxKindaMap { sampleParams.coverPoles = false sampleParams.edgeMatching = false } - sampleParams.minZoom = zoom.min sampleParams.maxZoom = zoom.max sampleParams.reportedMaxZoom = 21; @@ -646,7 +666,6 @@ public class MapboxKindaMap { outstandingFetches.forEach { $0?.cancel() } - outstandingFetches = [] loader?.shutdown() loader = nil diff --git a/ios/library/WhirlyGlobe-MaplyComponent/src/helpers/MaplyIconManager.mm b/ios/library/WhirlyGlobe-MaplyComponent/src/helpers/MaplyIconManager.mm index 1f31c8fdcf..f583fe22bc 100644 --- a/ios/library/WhirlyGlobe-MaplyComponent/src/helpers/MaplyIconManager.mm +++ b/ios/library/WhirlyGlobe-MaplyComponent/src/helpers/MaplyIconManager.mm @@ -1,9 +1,8 @@ -/* - * MaplyIconManager.mm +/* MaplyIconManager.mm * WhirlyGlobe-MaplyComponent * * Created by Steve Gifford on 1/11/14. - * Copyright 2011-2019 mousebird consulting + * Copyright 2011-2021 mousebird consulting * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -15,7 +14,6 @@ * 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. - * */ #import "helpers/MaplyIconManager.h" @@ -25,44 +23,6 @@ @implementation MaplySimpleStyle @end -// Courtesy: https://stackoverflow.com/questions/11598043/get-slightly-lighter-and-darker-color-from-uicolor -@implementation UIColor (Lighter) - -- (UIColor *)lighterColor -{ - CGFloat h, s, b, a; - if ([self getHue:&h saturation:&s brightness:&b alpha:&a]) - return [UIColor colorWithHue:h - saturation:s - brightness:MIN(b * 1.3, 1.0) - alpha:a]; - return nil; -} - -@end - - -namespace WhirlyKit -{ - -class MarkerRep -{ -public: - MarkerRep(); - - // Symbol is small/medium/large - typedef enum {MarkerSmall,MarkerMedium,MarkerLarge} IconSize; - IconSize size; - - // Symbol is the name of a UIImage or a single character - std::string symbol; - - // Background color of the marker - RGBAColor color; -}; - -} - @implementation MaplySimpleStyleManager { NSCache *imageCache; @@ -74,10 +34,10 @@ + (MaplySimpleStyleManager *)shared { static MaplySimpleStyleManager *iInst = nil; - @synchronized(self) - { - if (iInst == NULL) + @synchronized(self) { + if (iInst == nullptr) { iInst = [[self alloc] init]; + } } return iInst; @@ -106,22 +66,25 @@ - (nonnull id)initWithViewC:(NSObject * __nonnull return self; } -- (UIImage *)iconForName:(NSString *)name size:(CGSize)size color:(UIColor *)color circleColor:(UIColor *)circleColor strokeSize:(float)strokeSize strokeColor:(UIColor *)strokeColor +- (UIImage *)iconForName:(NSString *)name size:(CGSize)size color:(UIColor *)color + circleColor:(UIColor *)circleColor strokeSize:(float)strokeSize strokeColor:(UIColor *)strokeColor { // Look for the cached version NSString *cacheKey = [NSString stringWithFormat:@"%@_%d_%d_%.1f_%0.6X_%0.6X", name, (int)size.width, (int)size.height, strokeSize, [color asHexRGB], [strokeColor asHexRGB]]; - id cached = [imageCache objectForKey:cacheKey]; - if (cached) + if (id cached = [imageCache objectForKey:cacheKey]) + { return cached; + } UIImage *iconImage; if(name.isAbsolutePath) { iconImage = [UIImage imageWithContentsOfFile:name]; - } else if (name) + } + else if (name) { NSString *fullName = nil; NSString *fileName = [name lastPathComponent]; @@ -194,43 +157,49 @@ - (UIImage *)iconForName:(NSString *)name size:(CGSize)size color:(UIColor *)col + (UIImage *)iconForName:(NSString *)name size:(CGSize)size { - return [[self shared] iconForName:name size:size color:[UIColor blackColor] circleColor:[UIColor whiteColor] strokeSize:1.0 strokeColor:[UIColor blackColor]]; + return [[MaplySimpleStyleManager shared] iconForName:name size:size color:[UIColor blackColor] + circleColor:[UIColor whiteColor] strokeSize:1.0 strokeColor:[UIColor blackColor]]; } -+ (UIImage *)iconForName:(NSString *)name size:(CGSize)size color:(UIColor *)color circleColor:(UIColor *)circleColor strokeSize:(float)strokeSize strokeColor:(UIColor *)strokeColor ++ (UIImage *)iconForName:(NSString *)name size:(CGSize)size color:(UIColor *)color + circleColor:(UIColor *)circleColor strokeSize:(float)strokeSize strokeColor:(UIColor *)strokeColor { - return [[self shared] iconForName:name size:size color:color circleColor:circleColor strokeSize:strokeSize strokeColor:strokeColor]; + return [[MaplySimpleStyleManager shared] iconForName:name size:size color:color + circleColor:circleColor strokeSize:strokeSize strokeColor:strokeColor]; } -- (UIColor *)parseColor:(NSString *)colorStr default:(UIColor *)defColor +// Colors can be in short form: +// "#ace" +// or long form +// "#aaccee" +// with or without the # prefix. +// Colors are interpreted the same as in CSS, in #RRGGBB and #RGB order. +// But other color formats or named colors are not supported. ++ (UIColor *)parseColor:(NSString *)str default:(UIColor *)defColor { - UIColor *color = defColor; - NSString *str = colorStr; - - if ([str length] > 0 && [str characterAtIndex:0] == '#') { + const int len = [str length]; + if (len > 2) + { NSScanner *scanner = [NSScanner scannerWithString:str]; - [scanner setScanLocation:1]; + if ([str characterAtIndex:0] == '#') { + [scanner setScanLocation:1]; + } unsigned int val; if ([scanner scanHexInt:&val]) { - color = [UIColor colorFromHexRGB:val]; + return (len < 6) ? [UIColor colorFromShortHexRGB:val] : [UIColor colorFromHexRGB:val]; } } - - return color; + return defColor; } -- (CGFloat)parseNumber:(NSNumber *)num default:(CGFloat)defVal ++ (CGFloat)parseNumber:(NSNumber *)num default:(CGFloat)defVal { - if (num) - return [num doubleValue]; - return defVal; + return num ? [num doubleValue] : defVal; } -- (bool)parseBool:(NSString *)val default:(bool)defVal ++ (bool)parseBool:(NSString *)val default:(bool)defVal { - if (val) - return [val boolValue]; - return defVal; + return val ? [val boolValue] : defVal; } - (UIImage *)loadImage:(NSString *)symbol cacheKey:(NSString *)cacheKey @@ -238,11 +207,15 @@ - (UIImage *)loadImage:(NSString *)symbol cacheKey:(NSString *)cacheKey UIImage *mainImage = nil; if (!symbol || [symbol length] == 0) + { return nil; + } - if(symbol.isAbsolutePath) { + if(symbol.isAbsolutePath) + { mainImage = [UIImage imageWithContentsOfFile:symbol]; - } else if (symbol) + } + else if (symbol) { NSString *fullName = nil; NSString *fileName = [symbol length] > 0 ? [symbol lastPathComponent] : symbol; @@ -274,7 +247,7 @@ - (UIImage *)loadImage:(NSString *)symbol cacheKey:(NSString *)cacheKey return mainImage; } -// Does much the same work as the image version above, but is slightly different for the simple styel +// Does much the same work as the image version above, but is slightly different for the simple style - (MaplyTexture *)textureForStyle:(MaplySimpleStyle *)style backSymbol:(NSString *)backSymbol symbol:(NSString *)symbol @@ -282,11 +255,11 @@ - (MaplyTexture *)textureForStyle:(MaplySimpleStyle *)style center:(CGPoint)center clearBackground:(bool)clearBackground { - NSString *cacheKey = [NSString stringWithFormat:@"%@_%@_%d_%d_%.1f_%0.6X", + NSString *cacheKey = [NSString stringWithFormat:@"%@_%@_%d_%d_%.1f_%0.6X_%d", backSymbol, symbol, (int)style.markerSize.width, (int)style.markerSize.height, - strokeSize, [style.color asHexRGB]]; + strokeSize, [style.color asHexRGB], clearBackground]; id cached = [texCache objectForKey:cacheKey]; if ([cached isKindOfClass:[MaplyTexture class]]) @@ -304,10 +277,8 @@ - (MaplyTexture *)textureForStyle:(MaplySimpleStyle *)style // Draw into the image context CGContextRef ctx = UIGraphicsGetCurrentContext(); CGContextSetBlendMode(ctx, kCGBlendModeNormal); - [[UIColor blackColor] setFill]; CGRect rect = CGRectMake(0,0,renderSize.width,renderSize.height); - CGContextFillRect(ctx, rect); - + // We want a custom background image, rather than just the circle if (backImage) { // Courtesy: https://stackoverflow.com/questions/3514066/how-to-tint-a-transparent-png-image-in-iphone @@ -324,11 +295,11 @@ - (MaplyTexture *)textureForStyle:(MaplySimpleStyle *)style CGContextRestoreGState(ctx); } else { if (strokeSize > 0.0) { - UIColor *strokeColor = [style.color lighterColor]; + UIColor *strokeColor = [style.color lighterColor:1.3]; CGContextBeginPath(ctx); CGContextAddEllipseInRect(ctx, CGRectMake(1,1,renderSize.width-2,renderSize.height-2)); - [strokeColor setFill]; - CGContextDrawPath(ctx, kCGPathFill); + [strokeColor setStroke]; + CGContextDrawPath(ctx, kCGPathStroke); } if (!clearBackground) { @@ -379,83 +350,79 @@ - (void)shutdown - (MaplySimpleStyle * __nonnull)makeStyle:(NSDictionary *__nonnull)dict { - @synchronized (self) { - MaplySimpleStyle *style = [[MaplySimpleStyle alloc] init]; - style.title = dict[@"title"]; - style.desc = dict[@"description"]; - - // Sort out marker size - NSString *markerSizeStr = dict[@"marker-size"]; - int markerSizeInt = 1; // Medium - if (markerSizeStr) { - markerSizeStr = [markerSizeStr lowercaseString]; - if ([markerSizeStr isEqualToString:@"small"]) - markerSizeInt = 0; - else if ([markerSizeStr isEqualToString:@"medium"]) - markerSizeInt = 1; - else if ([markerSizeStr isEqualToString:@"large"]) - markerSizeInt = 2; - } - switch (markerSizeInt) { - case 0: - style.markerSize = _smallSize; - break; - case 1: - style.markerSize = _medSize; - break; - case 2: - style.markerSize = _largeSize; - break; - } - style.layoutSize = style.markerSize; - - // It's either a texture or a single character - NSString *symbol = dict[@"marker-symbol"]; - if ([symbol length] == 1) { - style.markerString = symbol; - symbol = nil; - } - NSString *backSymbol = dict[@"marker-background-symbol"]; - - style.color = [self parseColor:dict[@"marker-color"] default:[UIColor whiteColor]]; - style.strokeColor = [self parseColor:dict[@"stroke"] default:[UIColor colorFromHexRGB:0x555555]]; - style.strokeOpacity = [self parseNumber:dict[@"stroke-opactiy"] default:1.0]; - style.strokeWidth = [self parseNumber:dict[@"stroke-width"] default:2.0]; - style.fillColor = [self parseColor:dict[@"fill"] default:[UIColor colorFromHexRGB:0x555555]]; - style.fillOpacity = [self parseNumber:dict[@"fill-opacity"] default:0.6]; - CGPoint center = CGPointMake(-1000.0, -1000.0); - center.x = [self parseNumber:dict[@"marker-background-center-x"] default:center.x]; - center.y = [self parseNumber:dict[@"marker-background-center-y"] default:center.y]; - if (dict[@"marker-offset-x"] || dict[@"marker-offset-y"]) { - CGFloat offsetX = [self parseNumber:dict[@"marker-offset-x"] default:0.0]; - CGFloat offsetY = [self parseNumber:dict[@"marker-offset-y"] default:0.0]; - - style.markerOffset = CGPointMake(offsetX * style.markerSize.width, offsetY * style.markerSize.height); - } - bool clearBackground = [self parseBool:dict[@"marker-circle"] default:true]; - - // Need a texture for the marker - float strokeWidth = [self parseNumber:dict[@"stroke-width"] default:_strokeWidthForIcons]; - style.markerTex = [self textureForStyle:style backSymbol:backSymbol symbol:symbol strokeSize:strokeWidth center:center clearBackground:clearBackground]; - // TODO: Handle the single character case - - return style; + MaplySimpleStyle *style = [[MaplySimpleStyle alloc] init]; + style.title = dict[@"title"]; + style.desc = dict[@"description"]; + + // Sort out marker size + NSString *markerSizeStr = dict[@"marker-size"]; + int markerSizeInt = 1; // Medium + if (markerSizeStr) { + markerSizeStr = [markerSizeStr lowercaseString]; + if ([markerSizeStr isEqualToString:@"small"]) + markerSizeInt = 0; + else if ([markerSizeStr isEqualToString:@"medium"]) + markerSizeInt = 1; + else if ([markerSizeStr isEqualToString:@"large"]) + markerSizeInt = 2; + } + switch (markerSizeInt) { + case 0: + style.markerSize = _smallSize; + break; + case 1: + style.markerSize = _medSize; + break; + case 2: + style.markerSize = _largeSize; + break; + } + style.layoutSize = style.markerSize; + + // It's either a texture or a single character + NSString *symbol = dict[@"marker-symbol"]; + if ([symbol length] == 1) { + style.markerString = symbol; + symbol = nil; + } + NSString *backSymbol = dict[@"marker-background-symbol"]; + + style.color = [MaplySimpleStyleManager parseColor:dict[@"marker-color"] default:[UIColor whiteColor]]; + style.strokeColor = [MaplySimpleStyleManager parseColor:dict[@"stroke"] default:[UIColor colorFromHexRGB:0x555555]]; + style.strokeOpacity = [MaplySimpleStyleManager parseNumber:dict[@"stroke-opactiy"] default:1.0]; + style.strokeWidth = [MaplySimpleStyleManager parseNumber:dict[@"stroke-width"] default:2.0]; + style.fillColor = [MaplySimpleStyleManager parseColor:dict[@"fill"] default:[UIColor colorFromHexRGB:0x555555]]; + style.fillOpacity = [MaplySimpleStyleManager parseNumber:dict[@"fill-opacity"] default:0.6]; + const CGPoint center = CGPointMake( + [MaplySimpleStyleManager parseNumber:dict[@"marker-background-center-x"] default:-1000.0], + [MaplySimpleStyleManager parseNumber:dict[@"marker-background-center-y"] default:-1000.0]); + if (dict[@"marker-offset-x"] || dict[@"marker-offset-y"]) { + const CGFloat offsetX = [MaplySimpleStyleManager parseNumber:dict[@"marker-offset-x"] default:0.0]; + const CGFloat offsetY = [MaplySimpleStyleManager parseNumber:dict[@"marker-offset-y"] default:0.0]; + style.markerOffset = CGPointMake(offsetX * style.markerSize.width, offsetY * style.markerSize.height); } + const bool clearBackground = [MaplySimpleStyleManager parseBool:dict[@"marker-circle"] default:true]; + + // Need a texture for the marker + const float strokeWidth = [MaplySimpleStyleManager parseNumber:dict[@"stroke-width"] default:_strokeWidthForIcons]; + style.markerTex = [self textureForStyle:style backSymbol:backSymbol symbol:symbol strokeSize:strokeWidth center:center clearBackground:clearBackground]; + // TODO: Handle the single character case + + return style; } -- (UIColor *)resolveColor:(UIColor *)color opacity:(CGFloat)opacity ++ (UIColor *)resolveColor:(UIColor *)color opacity:(CGFloat)opacity { CGFloat red,green,blue,alpha; [color getRed:&red green:&green blue:&blue alpha:&alpha]; - return [UIColor colorWithRed:red*opacity green:green*opacity blue:blue*opacity alpha:alpha*opacity]; } - (MaplyComponentObject * __nullable)addFeature:(MaplyVectorObject * __nonnull)vecObj mode:(MaplyThreadMode)mode { - MaplyComponentObject *compObj = nil; - MaplySimpleStyle *style = [self makeStyle:vecObj.attributes]; - + const MaplySimpleStyle *style = [self makeStyle:vecObj.attributes]; + + const auto __strong vc = viewC; switch ([vecObj vectorType]) { case MaplyVectorPointType: // It's a screen marker @@ -465,22 +432,21 @@ - (MaplyComponentObject * __nullable)addFeature:(MaplyVectorObject * __nonnull)v marker.image = style.markerTex; marker.size = style.markerSize; marker.offset = style.markerOffset; - compObj = [viewC addScreenMarkers:@[marker] desc:nil mode:mode]; + //marker.layoutImportance = MAXFLOAT; + return [vc addScreenMarkers:@[marker] desc:nil mode:mode]; } break; case MaplyVectorLinearType: - compObj = [viewC addWideVectors:@[vecObj] - desc:@{kMaplyColor: [self resolveColor:style.strokeColor opacity:style.strokeOpacity], + return [vc addWideVectors:@[vecObj] + desc:@{kMaplyColor: [MaplySimpleStyleManager resolveColor:style.strokeColor opacity:style.strokeOpacity], kMaplyVecWidth: @(style.strokeWidth)} mode:mode]; - break; case MaplyVectorArealType: - compObj = [viewC addVectors:@[vecObj] - desc:@{kMaplyColor: [self resolveColor:style.fillColor opacity:style.fillOpacity], + return [vc addVectors:@[vecObj] + desc:@{kMaplyColor: [MaplySimpleStyleManager resolveColor:style.fillColor opacity:style.fillOpacity], kMaplyFilled: @(true) } mode:mode]; - break; case MaplyVectorNoneType: case MaplyVectorMultiType: case MaplyVectorLinear3dType: @@ -488,7 +454,7 @@ - (MaplyComponentObject * __nullable)addFeature:(MaplyVectorObject * __nonnull)v break; } - return compObj; + return nil; } - (NSArray * __nonnull)addFeatures:(NSArray * __nonnull)vecObjs mode:(MaplyThreadMode)mode @@ -497,9 +463,9 @@ - (MaplyComponentObject * __nullable)addFeature:(MaplyVectorObject * __nonnull)v for (MaplyVectorObject *vecObj in vecObjs) { for (MaplyVectorObject *vecObj2 in [vecObj splitVectors]) { - MaplyComponentObject *compObj = [self addFeature:vecObj2 mode:mode]; - if (compObj) + if (auto compObj = [self addFeature:vecObj2 mode:mode]) { [compObjs addObject:compObj]; + } } } diff --git a/ios/library/WhirlyGlobe-MaplyComponent/src/helpers/MaplyLocationTracker.mm b/ios/library/WhirlyGlobe-MaplyComponent/src/helpers/MaplyLocationTracker.mm index 7d2bc82a6b..943f3f88c3 100644 --- a/ios/library/WhirlyGlobe-MaplyComponent/src/helpers/MaplyLocationTracker.mm +++ b/ios/library/WhirlyGlobe-MaplyComponent/src/helpers/MaplyLocationTracker.mm @@ -34,7 +34,8 @@ @implementation MaplyLocationTracker { __weak MaplyViewController *_mapVC; __weak NSObject *_delegate; - + __weak NSObject *_simDelegate; + NSMutableArray *_markerImgs, *_markerImgsDirectional; MaplyComponentObject *_markerObj; @@ -51,36 +52,59 @@ @implementation MaplyLocationTracker { MaplyLocationLockType _lockType; int _forwardTrackOffset; int _markerDrawPriority; + bool updateLocationScheduled; } -- (nonnull instancetype)initWithViewC:(MaplyBaseViewController *__nullable)viewC delegate:(NSObject *__nullable)delegate useHeading:(bool)useHeading useCourse:(bool)useCourse simulate:(bool)simulate { - +- (nonnull instancetype)initWithViewC:(MaplyBaseViewController *__nullable)viewC + useHeading:(bool)useHeading + useCourse:(bool)useCourse { + return [self initWithViewC:viewC delegate:nil simulator:nil simInterval:0 useHeading:useHeading useCourse:useCourse]; +} + +- (nonnull instancetype)initWithViewC:(MaplyBaseViewController *__nullable)viewC + delegate:(NSObject *__nullable)delegate + useHeading:(bool)useHeading + useCourse:(bool)useCourse { + return [self initWithViewC:viewC delegate:delegate simulator:nil simInterval:0 useHeading:useHeading useCourse:useCourse]; +} + +- (nonnull instancetype)initWithViewC:(MaplyBaseViewController *__nullable)viewC + delegate:(NSObject *__nullable)delegate + simulator:(NSObject *__nullable)simulator + simInterval:(NSTimeInterval)simInterval + useHeading:(bool)useHeading + useCourse:(bool)useCourse { self = [super init]; - if (self) { - _theViewC = viewC; - if ([viewC isKindOfClass:[WhirlyGlobeViewController class]]) - _globeVC = (WhirlyGlobeViewController *)viewC; - else if ([viewC isKindOfClass:[MaplyViewController class]]) - _mapVC = (MaplyViewController *)viewC; - - _delegate = delegate; - _useHeading = useHeading; - _useCourse = useCourse; - _simulate = simulate; - _lockType = MaplyLocationLockNone; - _forwardTrackOffset = 0; - _prevLoc = kMaplyNullCoordinate; - _markerMinVis = 0.0; - _markerMaxVis = 1.0; - _markerDrawPriority = kMaplyVectorDrawPriorityDefault+1; - - [self setupMarkerImages]; - if (!_simulate) - [self setupLocationManager]; - else { - _simUpdateTimer = [NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:@selector(simUpdateTimeout) userInfo:nil repeats:YES]; + if (!self) { + return nil; + } + + _theViewC = viewC; + if ([viewC isKindOfClass:[WhirlyGlobeViewController class]]) + _globeVC = (WhirlyGlobeViewController *)viewC; + else if ([viewC isKindOfClass:[MaplyViewController class]]) + _mapVC = (MaplyViewController *)viewC; + + _delegate = delegate; + _simDelegate = simulator; + _useHeading = useHeading; + _useCourse = useCourse; + _simulate = (simulator != nil); + _lockType = MaplyLocationLockNone; + _forwardTrackOffset = 0; + _prevLoc = kMaplyNullCoordinate; + _markerMinVis = 0.0; + _markerMaxVis = 1.0; + _markerDrawPriority = kMaplyVectorDrawPriorityDefault+1; + + [self setupMarkerImages]; + if (!_simulate) { + [self setupLocationManager]; + } else { + if (!simInterval || simInterval <= 0) { + simInterval = 1.0; } - + _simUpdateTimer = [NSTimer scheduledTimerWithTimeInterval:simInterval target:self selector:@selector(simUpdateTimeout) userInfo:nil repeats:YES]; } return self; } @@ -90,18 +114,32 @@ - (CLLocationManager *)locationManager { } - (void) teardown { - if (!_simulate) + [NSObject cancelPreviousPerformRequestsWithTarget:self selector:@selector(updateLocationInternal:) object:nil]; + updateLocationScheduled = false; + + if (!_simulate) { [self teardownLocationManager]; - _delegate = nil; - if (_markerObj) { - [_theViewC removeObjects:@[_markerObj] mode:MaplyThreadCurrent]; - [_theViewC removeObjects:@[_movingMarkerObj] mode:MaplyThreadCurrent]; - _markerObj = nil; - _movingMarkerObj = nil; } - if (_shapeCircleObj) { - [_theViewC removeObjects:@[_shapeCircleObj] mode:MaplyThreadCurrent]; - _shapeCircleObj = nil; + [_simUpdateTimer invalidate]; + _simUpdateTimer = nil; + + _delegate = nil; + + if (const auto __strong vc = _theViewC) { + if (_markerObj) { + [vc removeObjects:@[_markerObj] mode:MaplyThreadCurrent]; + _markerObj = nil; + } + if (_movingMarkerObj) { + [vc removeObjects:@[_movingMarkerObj] mode:MaplyThreadCurrent]; + _movingMarkerObj = nil; + } + if (_shapeCircleObj) { + [vc removeObjects:@[_shapeCircleObj] mode:MaplyThreadCurrent]; + _shapeCircleObj = nil; + } + // clear the view reference so we can't create more markers + _theViewC = nil; } } @@ -111,30 +149,48 @@ - (void) changeLockType:(MaplyLocationLockType)lockType forwardTrackOffset:(int) } - (void) setupMarkerImages { - int size = LOC_TRACKER_POS_MARKER_SIZE*2; + const int size = LOC_TRACKER_POS_MARKER_SIZE*2; UIColor *color0 = [UIColor colorWithRed:1.0 green:1.0 blue:1.0 alpha:1.0]; UIColor *color1 = [UIColor colorWithRed:0.0 green:0.75 blue:1.0 alpha:1.0]; - _markerImgs = [NSMutableArray array]; - _markerImgsDirectional = [NSMutableArray array]; - for (int i=0; i<16; i++) { - [_markerImgs addObject:[_theViewC - addTexture:[self radialGradientMarkerWithSize:size color0:color0 color1:color1 gradLocation:(0.0 + (float)(8-ABS(8-i))/8.0) radius:(float)(size-32-ABS(8-i))/2.0 directional:false] - desc:nil - mode:MaplyThreadCurrent]]; - [_markerImgsDirectional addObject:[_theViewC - addTexture:[self radialGradientMarkerWithSize:size color0:color0 color1:color1 gradLocation:(0.0 + (float)(8-ABS(8-i))/8.0) radius:(float)(size-32-ABS(8-i))/2.0 directional:true] - desc:nil - mode:MaplyThreadCurrent]]; + if (const auto __strong vc = _theViewC) { + _markerImgs = [NSMutableArray array]; + _markerImgsDirectional = [NSMutableArray array]; + for (int i=0; i<16; i++) { + const auto gradLoc = (float)(8-ABS(8-i)) / 8.0f; + const auto radius = (float)(size-32-ABS(8-i)) / 2.0f; + [_markerImgs addObject: + [vc addTexture:[self radialGradientMarkerWithSize:size color0:color0 color1:color1 + gradLocation:gradLoc radius:radius directional:false] + desc:nil mode:MaplyThreadCurrent]]; + [_markerImgsDirectional addObject: + [vc addTexture:[self radialGradientMarkerWithSize:size color0:color0 color1:color1 + gradLocation:gradLoc radius:radius directional:true] + desc:nil mode:MaplyThreadCurrent]]; + } } - - _markerDesc = [NSMutableDictionary dictionaryWithDictionary:@{kMaplyMinVis: @(_markerMinVis), kMaplyMaxVis: @(_markerMaxVis), kMaplyFade: @(0.0), kMaplyDrawPriority:@(_markerDrawPriority), kMaplyEnableEnd: @(MAXFLOAT)}]; - - _movingMarkerDesc = [NSMutableDictionary dictionaryWithDictionary:@{kMaplyMinVis: @(_markerMinVis), kMaplyMaxVis: @(_markerMaxVis), kMaplyFade: @(0.0), kMaplyDrawPriority:@(_markerDrawPriority), kMaplyEnableStart:@(0.0)}]; - - _shapeCircleDesc = [NSMutableDictionary dictionaryWithDictionary:@{kMaplyColor : [UIColor colorWithRed:0.06 green:0.06 blue:0.1 alpha:0.2], kMaplyFade: @(0.0), kMaplyDrawPriority: @(_markerDrawPriority-1), kMaplySampleX: @(100), kMaplyZBufferRead: @(false)}]; - + + _markerDesc = [NSMutableDictionary dictionaryWithDictionary:@{ + kMaplyMinVis: @(_markerMinVis), + kMaplyMaxVis: @(_markerMaxVis), + kMaplyFade: @(0.0), + kMaplyDrawPriority:@(_markerDrawPriority), + kMaplyEnableEnd: @(MAXFLOAT)}]; + + _movingMarkerDesc = [NSMutableDictionary dictionaryWithDictionary:@{ + kMaplyMinVis: @(_markerMinVis), + kMaplyMaxVis: @(_markerMaxVis), + kMaplyFade: @(0.0), + kMaplyDrawPriority:@(_markerDrawPriority), + kMaplyEnableStart:@(0.0)}]; + + _shapeCircleDesc = [NSMutableDictionary dictionaryWithDictionary:@{ + kMaplyColor : [UIColor colorWithRed:0.06 green:0.06 blue:0.1 alpha:0.2], + kMaplyFade: @(0.0), + kMaplyDrawPriority: @(_markerDrawPriority-1), + kMaplySampleX: @(100), + kMaplyZBufferRead: @(false)}]; } - (int)markerDrawPriority @@ -149,75 +205,88 @@ - (void)setMarkerDrawPriority:(int)markerDrawPriority [self setupMarkerImages]; } -- (UIImage *)radialGradientMarkerWithSize:(int)size color0:(UIColor *)color0 color1:(UIColor *)color1 gradLocation:(float)gradLocation radius:(float)radius directional:(bool)directional { +- (UIImage *)radialGradientMarkerWithSize:(int)size color0:(UIColor *)color0 color1:(UIColor *)color1 + gradLocation:(float)gradLocation radius:(float)radius directional:(bool)directional { + /// "When you are done modifying the context, you must call the UIGraphicsEndImageContext() function to clean + /// up the bitmap drawing environment and remove the graphics context from the top of the context stack. You + /// should not use the UIGraphicsPopContext() function to remove this type of context from the stack." UIGraphicsBeginImageContextWithOptions(CGSizeMake(size, size), NO, 0.0f); - CGContextRef ctx = UIGraphicsGetCurrentContext(); - CGContextSaveGState(ctx); - - CGColorSpaceRef baseSpace = CGColorSpaceCreateDeviceRGB(); - - CGFloat colorComponents[8]; - const CGFloat *components0 = CGColorGetComponents(color0.CGColor); - const CGFloat *components1 = CGColorGetComponents(color1.CGColor); - colorComponents[0] = components0[0]; - colorComponents[1] = components0[1]; - colorComponents[2] = components0[2]; - colorComponents[3] = components0[3]; - colorComponents[4] = components1[0]; - colorComponents[5] = components1[1]; - colorComponents[6] = components1[2]; - colorComponents[7] = components1[3]; - - CGFloat locations[] = {0.0, gradLocation}; - - CGGradientRef gradient = CGGradientCreateWithColorComponents(baseSpace, colorComponents, locations, 2); - CGColorSpaceRelease(baseSpace); - - CGPoint gradCenter = CGPointMake(size/2, size/2); - - // Draw translucent outline - CGRect outlineRect = CGRectMake(0, 0, size, size); - UIColor *translucentColor = [[UIColor whiteColor] colorWithAlphaComponent:0.5]; - CGContextSetFillColorWithColor(ctx, translucentColor.CGColor); - CGContextFillEllipseInRect(ctx, outlineRect); - - // Draw direction indicator triangle - if (directional) { - float len = 20.0; - float height = 12.0; - CGMutablePathRef path = CGPathCreateMutable(); - CGPathMoveToPoint(path, NULL, size/2, size/2-radius-len); - CGPathAddLineToPoint(path, NULL, size/2-height, size/2-radius); - CGPathAddLineToPoint(path, NULL, size/2+height, size/2-radius); - CGPathCloseSubpath(path); - CGContextSetFillColorWithColor(ctx, color1.CGColor); - CGContextAddPath(ctx, path); - CGContextFillPath(ctx); - CGPathRelease(path); - } + @try { + CGContextRef ctx = UIGraphicsGetCurrentContext(); + if (!ctx) { + return nil; + } - // Draw white outline - outlineRect = CGRectMake(size/2-radius-4, size/2-radius-4, 2*radius+8, 2*radius+8); - CGContextSetFillColorWithColor(ctx, [UIColor whiteColor].CGColor); - CGContextFillEllipseInRect(ctx, outlineRect); - - // Draw gradient center - CGContextDrawRadialGradient(ctx, gradient, gradCenter, 0, gradCenter, radius, kCGGradientDrawsBeforeStartLocation); - CGGradientRelease(gradient); - - CGContextRestoreGState(ctx); - UIImage *img = UIGraphicsGetImageFromCurrentImageContext(); - UIGraphicsEndImageContext(); - - return img; + CGContextSaveGState(ctx); + + /// "You are responsible for releasing this object by calling CGColorSpaceRelease" + CGColorSpaceRef baseSpace = CGColorSpaceCreateDeviceRGB(); + CGGradientRef gradient; + @try { + const CGFloat locations[] = { 0.0f, gradLocation }; + const CGFloat *components0 = CGColorGetComponents(color0.CGColor); + const CGFloat *components1 = CGColorGetComponents(color1.CGColor); + const CGFloat colorComponents[8] = { + components0[0], components0[1], components0[2], components0[3], + components1[0], components1[1], components1[2], components1[3], + }; + gradient = CGGradientCreateWithColorComponents(baseSpace, colorComponents, locations, 2); + } @finally { + CGColorSpaceRelease(baseSpace); + } + + @try { + const CGPoint gradCenter = CGPointMake(size/2, size/2); + + // Draw translucent outline + CGRect outlineRect = CGRectMake(0, 0, size, size); + UIColor *translucentColor = [[UIColor whiteColor] colorWithAlphaComponent:0.5]; + CGContextSetFillColorWithColor(ctx, translucentColor.CGColor); + CGContextFillEllipseInRect(ctx, outlineRect); + + // Draw direction indicator triangle + if (directional) { + float len = 20.0; + float height = 12.0; + /// "You are responsible for releasing this object." + CGMutablePathRef path = CGPathCreateMutable(); + @try { + CGPathMoveToPoint(path, NULL, size/2, size/2-radius-len); + CGPathAddLineToPoint(path, NULL, size/2-height, size/2-radius); + CGPathAddLineToPoint(path, NULL, size/2+height, size/2-radius); + CGPathCloseSubpath(path); + CGContextSetFillColorWithColor(ctx, color1.CGColor); + CGContextAddPath(ctx, path); + CGContextFillPath(ctx); + } @finally { + CGPathRelease(path); + } + } + + // Draw white outline + outlineRect = CGRectMake(size/2-radius-4, size/2-radius-4, 2*radius+8, 2*radius+8); + CGContextSetFillColorWithColor(ctx, [UIColor whiteColor].CGColor); + CGContextFillEllipseInRect(ctx, outlineRect); + + // Draw gradient center + CGContextDrawRadialGradient(ctx, gradient, gradCenter, 0, gradCenter, radius, kCGGradientDrawsBeforeStartLocation); + } @finally { + CGGradientRelease(gradient); + } + + CGContextRestoreGState(ctx); + return UIGraphicsGetImageFromCurrentImageContext(); + } @finally { + UIGraphicsEndImageContext(); + } } - (void) setupLocationManager { if (_locationManager) return; - CLAuthorizationStatus authStatus = [CLLocationManager authorizationStatus]; + const CLAuthorizationStatus authStatus = [CLLocationManager authorizationStatus]; if (authStatus == kCLAuthorizationStatusRestricted || authStatus == kCLAuthorizationStatusDenied) { return; } @@ -251,7 +320,7 @@ - (void) setupLocationManager { } - (void)orientationChanged:(NSNotification *)notification { - UIInterfaceOrientation orientation = [[UIApplication sharedApplication] statusBarOrientation]; + const auto orientation = [[UIApplication sharedApplication] statusBarOrientation]; switch (orientation) { case UIInterfaceOrientationPortrait: _locationManager.headingOrientation = CLDeviceOrientationPortrait; @@ -313,29 +382,45 @@ - (MaplyShapeCircle *)shapeCircleForCoord:(MaplyCoordinate)coord AndHorizontalAc MaplyShapeCircle *shapeCircle = [[MaplyShapeCircle alloc] init]; shapeCircle.center = coord; - MaplyCoordinate coord1 = [self coordOfPointAtTrueCourse:0.0 andDistanceMeters:horizontalAccuracy fromCoord:coord]; - MaplyCoordinate coord2 = [self coordOfPointAtTrueCourse:90.0 andDistanceMeters:horizontalAccuracy fromCoord:coord]; + const MaplyCoordinate coord1 = [self coordOfPointAtTrueCourse:0.0 andDistanceMeters:horizontalAccuracy fromCoord:coord]; + const MaplyCoordinate coord2 = [self coordOfPointAtTrueCourse:90.0 andDistanceMeters:horizontalAccuracy fromCoord:coord]; + + const auto __strong vc = _theViewC; + const auto __strong mvc = _mapVC; + const auto __strong gvc = _globeVC; - MaplyCoordinate3d dispPt0 = [_theViewC displayPointFromGeo:coord]; - MaplyCoordinate3d dispPt1 = [_theViewC displayPointFromGeo:coord1]; - MaplyCoordinate3d dispPt2 = [_theViewC displayPointFromGeo:coord2]; + const MaplyCoordinate3d dispPt0 = [vc displayPointFromGeo:coord]; + const MaplyCoordinate3d dispPt1 = [vc displayPointFromGeo:coord1]; + const MaplyCoordinate3d dispPt2 = [vc displayPointFromGeo:coord2]; - float d1 = sqrtf(powf(dispPt1.x-dispPt0.x, 2.0) + powf(dispPt1.y-dispPt0.y, 2.0)); - float d2 = sqrtf(powf(dispPt2.x-dispPt0.x, 2.0) + powf(dispPt2.y-dispPt0.y, 2.0)); + const float d1 = sqrtf(powf(dispPt1.x-dispPt0.x, 2.0) + powf(dispPt1.y-dispPt0.y, 2.0)); + const float d2 = sqrtf(powf(dispPt2.x-dispPt0.x, 2.0) + powf(dispPt2.y-dispPt0.y, 2.0)); shapeCircle.radius = (d1 + d2) / 2.0; + float minHeight = 0.0; - if (_globeVC) - minHeight = [_globeVC getZoomLimitsMin]; + if (gvc) + minHeight = [gvc getZoomLimitsMin]; else { float maxHeight; - [_mapVC getZoomLimitsMin:&minHeight max:&maxHeight]; + [mvc getZoomLimitsMin:&minHeight max:&maxHeight]; } shapeCircle.height = minHeight * 0.01; return shapeCircle; } +// When using a simulated track, locations seem to come in fast and furious, overwhelming the renderer. +// This slows things down to a level that can actually be seen - (void)updateLocation:(CLLocation *)location { + if (!updateLocationScheduled) { + updateLocationScheduled = true; + [self performSelector:@selector(updateLocationInternal:) withObject:location afterDelay:0.0]; + } +} + +- (void)updateLocationInternal:(CLLocation *)location { + updateLocationScheduled = false; + __strong MaplyBaseViewController *theViewC = _theViewC; if (!theViewC) return; @@ -346,9 +431,9 @@ - (void)updateLocation:(CLLocation *)location { if (_markerObj || _movingMarkerObj) { startLoc = _prevLoc; if (_markerObj) - [_theViewC removeObjects:@[_markerObj] mode:MaplyThreadCurrent]; + [theViewC removeObjects:@[_markerObj] mode:MaplyThreadCurrent]; if (_movingMarkerObj) - [_theViewC removeObjects:@[_movingMarkerObj] mode:MaplyThreadCurrent]; + [theViewC removeObjects:@[_movingMarkerObj] mode:MaplyThreadCurrent]; _markerObj = nil; _movingMarkerObj = nil; } else @@ -362,7 +447,7 @@ - (void)updateLocation:(CLLocation *)location { if (location.horizontalAccuracy >= 0) { MaplyShapeCircle *shapeCircle = [self shapeCircleForCoord:endLoc AndHorizontalAccuracy:location.horizontalAccuracy]; if (shapeCircle) { - _shapeCircleObj = [_theViewC addShapes:@[shapeCircle] desc:_shapeCircleDesc mode:MaplyThreadCurrent]; + _shapeCircleObj = [theViewC addShapes:@[shapeCircle] desc:_shapeCircleDesc mode:MaplyThreadCurrent]; } NSNumber *orientation; @@ -402,8 +487,8 @@ - (void)updateLocation:(CLLocation *)location { NSTimeInterval ti = [NSDate timeIntervalSinceReferenceDate]+0.5; _markerDesc[kMaplyEnableStart] = _movingMarkerDesc[kMaplyEnableEnd] = @(ti); - _movingMarkerObj = [_theViewC addScreenMarkers:@[movingMarker] desc:_movingMarkerDesc mode:MaplyThreadCurrent]; - _markerObj = [_theViewC addScreenMarkers:@[marker] desc:_markerDesc mode:MaplyThreadCurrent]; + _movingMarkerObj = [theViewC addScreenMarkers:@[movingMarker] desc:_movingMarkerDesc mode:MaplyThreadCurrent]; + _markerObj = [theViewC addScreenMarkers:@[marker] desc:_markerDesc mode:MaplyThreadCurrent]; [self lockToLocation:endLoc heading:(orientation ? orientation.floatValue : 0.0)]; @@ -411,7 +496,7 @@ - (void)updateLocation:(CLLocation *)location { } __strong NSObject *delegate = _delegate; - if (delegate && [delegate respondsToSelector:@selector(updateLocation:)]) { + if ([delegate respondsToSelector:@selector(updateLocation:)]) { [delegate updateLocation:location]; } } @@ -459,65 +544,81 @@ - (void) locationManager:(CLLocationManager *)manager didFailWithError:(NSError __strong NSObject *delegate = _delegate; _prevLoc = kMaplyNullCoordinate; _latestHeading = nil; - if (delegate) - [delegate locationManager:manager didFailWithError:error]; + [delegate locationManager:manager didFailWithError:error]; } - (void) locationManager:(CLLocationManager *)manager didChangeAuthorizationStatus:(CLAuthorizationStatus)status { - __strong NSObject *delegate = _delegate; - if (delegate) - [delegate locationManager:manager didChangeAuthorizationStatus:status]; - - if (status == kCLAuthorizationStatusNotDetermined) { - return; - } - if (status == kCLAuthorizationStatusDenied || status == kCLAuthorizationStatusRestricted) { - [self teardownLocationManager]; - } else if (status == kCLAuthorizationStatusAuthorizedWhenInUse || status == kCLAuthorizationStatusAuthorizedAlways) { - - [_locationManager startUpdatingLocation]; - if (_useHeading) - [_locationManager startUpdatingHeading]; + [delegate locationManager:manager didChangeAuthorizationStatus:status]; + + switch (status) { + case kCLAuthorizationStatusNotDetermined: + break; + case kCLAuthorizationStatusDenied: + case kCLAuthorizationStatusRestricted: + [self teardownLocationManager]; + break; + case kCLAuthorizationStatusAuthorizedWhenInUse: + case kCLAuthorizationStatusAuthorizedAlways: + [_locationManager startUpdatingLocation]; + if (_useHeading) + [_locationManager startUpdatingHeading]; + break; } } - (void) locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray *)locations { - - CLLocation *location = [locations lastObject]; - - [self updateLocation:location]; - + [self updateLocation:[locations lastObject]]; } - (void) locationManager:(CLLocationManager *)manager didUpdateHeading:(nonnull CLHeading *)newHeading { - - if (newHeading.headingAccuracy < 0) - _latestHeading = nil; - else - _latestHeading = @(newHeading.trueHeading); + _latestHeading = (newHeading.headingAccuracy >= 0) ? @(newHeading.trueHeading) : nil; } - (void) simUpdateTimeout { - __strong NSObject *delegate = _delegate; - - if (!delegate || ![delegate respondsToSelector:@selector(getSimulationPoint)]) - return; - - MaplyLocationTrackerSimulationPoint simPoint = [delegate getSimulationPoint]; - - float lonDeg = simPoint.lonDeg; - float latDeg = simPoint.latDeg; - float hdgDeg = simPoint.headingDeg; - - _latestHeading = @(hdgDeg); - CLLocation *location = [[CLLocation alloc] initWithCoordinate:(CLLocationCoordinate2D){latDeg, lonDeg} altitude:10000.0 horizontalAccuracy:250 verticalAccuracy:15 course:hdgDeg speed:0 timestamp:[NSDate date]]; - [self updateLocation:location]; + __strong NSObject *delegate = _simDelegate; + if ([delegate respondsToSelector:@selector(getSimulationPoint)] && + (![delegate respondsToSelector:@selector(hasValidLocation)] || [delegate hasValidLocation])) { + [self setLocation:[delegate getSimulationPoint] + altitude:10000.0 + horizontalAccuracy:250 + verticalAccuracy:15 + speed:0]; // todo: calculate speed from positions + } } - (MaplyCoordinate)getLocation { return _prevLoc; } +- (void) setLocation:(MaplyLocationTrackerSimulationPoint)point + altitude:(double)altitude { + [self setLocation:point + altitude:altitude + horizontalAccuracy:250 + verticalAccuracy:15 + speed:0]; +} + +- (void) setLocation:(MaplyLocationTrackerSimulationPoint)point + altitude:(double)altitude + horizontalAccuracy:(double)horizontalAccuracy + verticalAccuracy:(double)verticalAccuracy + speed:(double)speed { + const float lonDeg = point.lonDeg; + const float latDeg = point.latDeg; + const float hdgDeg = point.headingDeg; + + _latestHeading = @(hdgDeg); + CLLocation *location = [[CLLocation alloc] initWithCoordinate:{latDeg, lonDeg} + altitude:altitude + horizontalAccuracy:horizontalAccuracy + verticalAccuracy:verticalAccuracy + course:hdgDeg + speed:speed + timestamp:[NSDate date]]; + [self updateLocation:location]; +} + @end diff --git a/ios/library/WhirlyGlobe-MaplyComponent/src/helpers/MaplyQuadSampler.mm b/ios/library/WhirlyGlobe-MaplyComponent/src/helpers/MaplyQuadSampler.mm index 3fb0d41739..c3767cb791 100644 --- a/ios/library/WhirlyGlobe-MaplyComponent/src/helpers/MaplyQuadSampler.mm +++ b/ios/library/WhirlyGlobe-MaplyComponent/src/helpers/MaplyQuadSampler.mm @@ -51,6 +51,11 @@ - (int)minZoom - (void)setMinZoom:(int)minZoom { + if (minZoom > 1) { + NSLog(@"\n============Error===============\nDo not set MaplySamplingParams minZoom to anything more than 1.\nInstead, set the minZoom of your tileSource to the right number.============Error===============\n"); + return; + } + params.minZoom = minZoom; } @@ -233,7 +238,7 @@ - (bool)startLayer:(WhirlyKitLayerThread *)inLayerThread scene:(WhirlyKit::Scene _quadLayer = [[WhirlyKitQuadDisplayLayerNew alloc] initWithController:sampleControl.getDisplayControl()]; - [super.layerThread addLayer:_quadLayer]; + [inLayerThread addLayer:_quadLayer]; return true; } diff --git a/ios/library/WhirlyGlobe-MaplyComponent/src/loading/MaplyQuadImageFrameLoader.mm b/ios/library/WhirlyGlobe-MaplyComponent/src/loading/MaplyQuadImageFrameLoader.mm index a5d20406a2..b6625b05d2 100644 --- a/ios/library/WhirlyGlobe-MaplyComponent/src/loading/MaplyQuadImageFrameLoader.mm +++ b/ios/library/WhirlyGlobe-MaplyComponent/src/loading/MaplyQuadImageFrameLoader.mm @@ -44,10 +44,10 @@ - (instancetype)initWithFrameLoader:(MaplyQuadImageFrameLoader *)inLoader viewC: loader = inLoader; viewC = inViewC; _pauseLength = 0.0; - numFrames = [loader getNumFrames]; + numFrames = [inLoader getNumFrames]; self.period = 10.0; - [viewC addActiveObject:self]; + [inViewC addActiveObject:self]; return self; } @@ -56,15 +56,17 @@ - (void)setPeriod:(NSTimeInterval)period { if (period == _period) return; - _period = period; + if (!scene) return; + // Adjust the start time to make the quad loader appear not to move initially + const auto img = [loader getCurrentImage]; if (numFrames > 1) { - startTime = scene->getCurrentTime()-[loader getCurrentImage]/(numFrames-1) * _period; + startTime = scene->getCurrentTime()-img/(numFrames-1) * _period; } else if (numFrames > 0) { - startTime = scene->getCurrentTime()-[loader getCurrentImage] * _period; + startTime = scene->getCurrentTime()-img * _period; } } @@ -80,18 +82,19 @@ - (void)shutdown // Have to do the position update in the setCurrentImage so we're not messing with the rendering loop - (bool)hasUpdate { - if (!viewC || !loader) + const auto __strong ldr = loader; + if (!viewC || !ldr) return false; - TimeInterval now = scene->getCurrentTime(); - TimeInterval totalPeriod = _period + _pauseLength; - double when = fmod(now-startTime,totalPeriod); + const TimeInterval now = scene->getCurrentTime(); + const TimeInterval totalPeriod = _period + _pauseLength; + const double when = fmod(now-startTime,totalPeriod); if (when >= _period) // Snap it to the end for a while - [loader setCurrentImage:numFrames-1]; + [ldr setCurrentImage:numFrames-1]; else { - double where = when/_period * (numFrames-1); - [loader setCurrentImage:where]; + const double where = when/_period * (numFrames-1); + [ldr setCurrentImage:where]; } return false; @@ -168,8 +171,13 @@ - (void)setLoadFrameMode:(MaplyLoadFrameMode)loadFrameMode if (!loader) return; + if (_loadFrameMode == loadFrameMode) + return; + _loadFrameMode = loadFrameMode; loader->setLoadMode(_loadFrameMode == MaplyLoadFrameBroad ? QuadImageFrameLoader::LoadMode::Broad : QuadImageFrameLoader::LoadMode::Narrow); + + [self updatePriorities]; } - (bool)delayedInit @@ -210,7 +218,17 @@ - (void)setCurrentImage:(double)where - (void)setFocus:(int)focusID currentImage:(double)where { double curFrame = std::min(std::max(where,0.0),(double)([loader->frameInfos count]-1)); - loader->setCurFrame(focusID,curFrame); + double oldFrame = loader->getCurFrame(focusID); + loader->setCurFrame(NULL, focusID, curFrame); + + // Update the loading priorities if we're in narrow mode and we changed images + if (_loadFrameMode != MaplyLoadFrameBroad) { + int oldInt = oldFrame; + int newInt = curFrame; + + if (oldInt != newInt) + [self updatePriorities]; + } } - (double)getCurrentImage @@ -280,7 +298,10 @@ - (void)changeTileInfos:(NSArray *)tileInfos - (void)shutdown { - self->samplingLayer.layerThread.scene->removeActiveModel(loader); + if (self->samplingLayer) { + auto layerThread = self->samplingLayer.layerThread; + layerThread.scene->removeActiveModel(loader); + } [super shutdown]; } diff --git a/ios/library/WhirlyGlobe-MaplyComponent/src/loading/MaplyQuadImageLoader.mm b/ios/library/WhirlyGlobe-MaplyComponent/src/loading/MaplyQuadImageLoader.mm index 79321e50c9..da93ac10f1 100644 --- a/ios/library/WhirlyGlobe-MaplyComponent/src/loading/MaplyQuadImageLoader.mm +++ b/ios/library/WhirlyGlobe-MaplyComponent/src/loading/MaplyQuadImageLoader.mm @@ -27,7 +27,6 @@ #import "MaplyRenderTarget_private.h" #import "MaplyRenderController_private.h" #import "MaplyQuadSampler_private.h" -#import "lodepng.h" using namespace WhirlyKit; @@ -47,18 +46,17 @@ - (void)addImageTile:(MaplyImageTile *)image - (void)addImage:(UIImage *)image { - if (!viewC) - return; - - ImageTile_iOSRef imageTile = ImageTile_iOSRef(new ImageTile_iOS(viewC.getRenderControl->renderType)); - imageTile->type = MaplyImgTypeImage; - imageTile->components = 4; - imageTile->width = -1; - imageTile->height = -1; - imageTile->borderSize = 0; - imageTile->imageStuff = image; - - loadReturn->images.push_back(imageTile); + if (const auto __strong vc = viewC) { + ImageTile_iOSRef imageTile = std::make_shared(vc.getRenderControl->renderType); + imageTile->type = MaplyImgTypeImage; + imageTile->components = 4; + imageTile->width = -1; + imageTile->height = -1; + imageTile->borderSize = 0; + imageTile->imageStuff = image; + + loadReturn->images.push_back(imageTile); + } } - (NSArray *)getImages @@ -135,14 +133,13 @@ - (void)setLoader:(MaplyQuadLoaderBase *)loader - (void)dataForTile:(MaplyImageLoaderReturn *)loadReturn loader:(MaplyQuadLoaderBase *)loader { - if (!loadReturn->viewC) - return; - - NSArray *tileDatas = [loadReturn getTileData]; - - for (NSData *tileData in tileDatas) { - MaplyImageTile *imageTile = [[MaplyImageTile alloc] initWithPNGorJPEGData:tileData viewC:loadReturn->viewC]; - [loadReturn addImageTile:imageTile]; + if (const auto __strong vc = loadReturn->viewC) { + NSArray *tileDatas = [loadReturn getTileData]; + + for (NSData *tileData in tileDatas) { + MaplyImageTile *imageTile = [[MaplyImageTile alloc] initWithPNGorJPEGData:tileData viewC:vc]; + [loadReturn addImageTile:imageTile]; + } } } @@ -168,6 +165,8 @@ - (id)initWithViewC:(NSObject *)inViewC - (void)dataForTile:(MaplyImageLoaderReturn *)loadReturn loader:(MaplyQuadLoaderBase *)loader { + auto const __strong vc = viewC; + [super dataForTile:loadReturn loader:loader]; MaplyBoundingBox bbox = [loader geoBoundsForTile:loadReturn.tileID]; @@ -178,14 +177,14 @@ - (void)dataForTile:(MaplyImageLoaderReturn *)loadReturn loader:(MaplyQuadLoader label.text = [NSString stringWithFormat:@"%d: (%d,%d)",loadReturn.tileID.level,loadReturn.tileID.x,loadReturn.tileID.y]; label.layoutImportance = MAXFLOAT; - MaplyComponentObject *labelObj = [viewC addScreenLabels:@[label] desc: + MaplyComponentObject *labelObj = [vc addScreenLabels:@[label] desc: @{kMaplyFont: font, kMaplyTextColor: UIColor.blackColor, kMaplyTextOutlineColor: UIColor.whiteColor, kMaplyTextOutlineSize: @(2.0), kMaplyEnable: @(false), } - mode:MaplyThreadCurrent]; + mode:MaplyThreadCurrent]; MaplyCoordinate coords[5]; coords[0] = bbox.ll; coords[1] = MaplyCoordinateMake(bbox.ur.x, bbox.ll.y); @@ -193,7 +192,7 @@ - (void)dataForTile:(MaplyImageLoaderReturn *)loadReturn loader:(MaplyQuadLoader coords[4] = coords[0]; MaplyVectorObject *vecObj = [[MaplyVectorObject alloc] initWithLineString:coords numCoords:5 attributes:nil]; [vecObj subdivideToGlobe:0.001]; - MaplyComponentObject *outlineObj = [viewC addVectors:@[vecObj] desc:@{kMaplyEnable: @(false)} mode:MaplyThreadCurrent]; + MaplyComponentObject *outlineObj = [vc addVectors:@[vecObj] desc:@{kMaplyEnable: @(false)} mode:MaplyThreadCurrent]; [loadReturn addCompObjs:@[labelObj,outlineObj]]; } @@ -201,44 +200,46 @@ - (void)dataForTile:(MaplyImageLoaderReturn *)loadReturn loader:(MaplyQuadLoader @end @implementation MaplyRawPNGImageLoaderInterpreter +{ + std::vector valueMap; +} + +- (void)addMappingFrom:(int)inVal to:(int)outVal +{ + if (valueMap.empty()) + valueMap.resize(256,-1); + if (inVal < 256) + valueMap[inVal] = outVal; +} - (void)dataForTile:(MaplyImageLoaderReturn *)loadReturn loader:(MaplyQuadLoaderBase *)loader { + const auto __strong vc = loader.viewC; NSArray *tileData = [loadReturn getTileData]; for (unsigned int ii=0;ii<[tileData count];ii++) { NSData *inData = [tileData objectAtIndex:ii]; if (![inData isKindOfClass:[NSData class]]) continue; - unsigned char *outData = NULL; - unsigned int width,height; + unsigned int width=0,height=0; unsigned int err = 0; int byteWidth = -1; - try { - LodePNGState pngState; - lodepng_state_init(&pngState); - err = lodepng_inspect(&width, &height, &pngState, (const unsigned char *)[inData bytes], [inData length]); - if (pngState.info_png.color.colortype == LCT_GREY) { - byteWidth = 1; - err = lodepng_decode_memory(&outData, &width, &height, (const unsigned char *)[inData bytes], [inData length], LCT_GREY, 8); - } else { - byteWidth = 4; - err = lodepng_decode_memory(&outData, &width, &height, (const unsigned char *)[inData bytes], [inData length], LCT_RGBA, 8); - } - } - catch (...) { - err = -1; - } - + unsigned char *outData = RawPNGImageLoaderInterpreter(width,height, + (const unsigned char *)[inData bytes],[inData length], + valueMap, + byteWidth, err); + if (err != 0) { - NSLog(@"Failed to read PNG in MaplyRawPNGImageLoaderInterpreter for tile %d: (%d,%d) frame = %d",loadReturn.tileID.level,loadReturn.tileID.x,loadReturn.tileID.y,loadReturn.frame); + wkLogLevel(Warn, "Failed to read PNG in MaplyRawPNGImageLoaderInterpreter for tile %d: (%d,%d) frame = %d",loadReturn.tileID.level,loadReturn.tileID.x,loadReturn.tileID.y,loadReturn.frame); } else { NSData *retData = [[NSData alloc] initWithBytesNoCopy:outData length:width*height*byteWidth freeWhenDone:YES]; // Build a wrapper around the data and pass it on - MaplyImageTile *tileData = [[MaplyImageTile alloc] initWithRawImage:retData width:width height:height components:byteWidth viewC:loader.viewC]; - loadReturn->loadReturn->images.push_back(tileData->imageTile); + MaplyImageTile *tileData = [[MaplyImageTile alloc] initWithRawImage:retData width:width height:height components:byteWidth viewC:vc]; + if (tileData) { + loadReturn->loadReturn->images.push_back(tileData->imageTile); + } } } } @@ -333,16 +334,17 @@ - (bool)delayedInit if (!valid) return false; - if (!self.viewC || ![self.viewC getRenderControl]) + auto const __strong vc = self.viewC; + if (![vc getRenderControl]) return false; if (!tileFetcher) { - tileFetcher = [[self.viewC getRenderControl] addTileFetcher:MaplyQuadImageLoaderFetcherName]; + tileFetcher = [[vc getRenderControl] addTileFetcher:MaplyQuadImageLoaderFetcherName]; } loader->tileFetcher = tileFetcher; loader->setDebugMode(self.debugMode); - samplingLayer = [[self.viewC getRenderControl] findSamplingLayer:params forUser:self->loader]; + samplingLayer = [[vc getRenderControl] findSamplingLayer:params forUser:self->loader]; // Do this again in case they changed them loader->setSamplingParams(params); @@ -405,7 +407,7 @@ - (bool)delayedInit for (unsigned int ii=0;iigetNumFocus();ii++) { if (loader->getShaderID(ii) == EmptyIdentity) { - MaplyShader *theShader = [self.viewC getShaderByName:kMaplyShaderDefaultTriMultiTex]; + MaplyShader *theShader = [vc getShaderByName:kMaplyShaderDefaultTriMultiTex]; if (theShader) loader->setShaderID(ii,[theShader getShaderID]); } @@ -415,7 +417,8 @@ - (bool)delayedInit loader->setFlipY(self.flipY); loader->setBaseDrawPriority(_baseDrawPriority); loader->setDrawPriorityPerLevel(_drawPriorityPerLevel); - RGBAColor color = [_color asRGBAColor]; + + const RGBAColor color = [_color asRGBAColor]; loader->setColor(color,NULL); return true; @@ -449,10 +452,11 @@ - (void)setColor:(UIColor *)newColor { _color = newColor; - if (samplingLayer.layerThread) - [self performSelector:@selector(setColorThread:) onThread:samplingLayer.layerThread withObject:_color waitUntilDone:NO]; + const auto __strong thread = samplingLayer.layerThread; + if (thread) + [self performSelector:@selector(setColorThread:) onThread:thread withObject:_color waitUntilDone:NO]; else if (loader) { - RGBAColor color = [_color asRGBAColor]; + const RGBAColor color = [_color asRGBAColor]; loader->setColor(color, NULL); } } @@ -461,7 +465,7 @@ - (void)setColor:(UIColor *)newColor - (void)setColorThread:(UIColor *)newColor { ChangeSet changes; - RGBAColor color = [_color asRGBAColor]; + const RGBAColor color = [_color asRGBAColor]; loader->setColor(color,&changes); [samplingLayer.layerThread addChangeRequests:changes]; diff --git a/ios/library/WhirlyGlobe-MaplyComponent/src/loading/MaplyQuadLoader.mm b/ios/library/WhirlyGlobe-MaplyComponent/src/loading/MaplyQuadLoader.mm index 9c7b2137a9..3864226eba 100644 --- a/ios/library/WhirlyGlobe-MaplyComponent/src/loading/MaplyQuadLoader.mm +++ b/ios/library/WhirlyGlobe-MaplyComponent/src/loading/MaplyQuadLoader.mm @@ -84,6 +84,14 @@ - (id __nullable)getFirstData return tileData[0]; } +- (bool)isCancelled +{ + if (!loadReturn) + return true; + + return loadReturn->cancel; +} + - (void)setError:(NSError *)error { _error = error; @@ -243,8 +251,9 @@ - (void)changeTileInfos:(NSArray *> *)tileInfos if (!samplingLayer) return; - if ([NSThread currentThread] != samplingLayer.layerThread) { - [self performSelector:@selector(changeTileInfos:) onThread:samplingLayer.layerThread withObject:tileInfos waitUntilDone:false]; + const auto __strong thread = samplingLayer.layerThread; + if ([NSThread currentThread] != thread) { + [self performSelector:@selector(changeTileInfos:) onThread:thread withObject:tileInfos waitUntilDone:false]; return; } @@ -252,7 +261,7 @@ - (void)changeTileInfos:(NSArray *> *)tileInfos ChangeSet changes; loader->setTileInfos(tileInfos); loader->reload(NULL,-1,changes); - [samplingLayer.layerThread addChangeRequests:changes]; + [thread addChangeRequests:changes]; } - (void)changeInterpreter:(NSObject *)interp @@ -260,15 +269,16 @@ - (void)changeInterpreter:(NSObject *)interp if (!samplingLayer) return; - if ([NSThread currentThread] != samplingLayer.layerThread) { - [self performSelector:@selector(changeInterpreter:) onThread:samplingLayer.layerThread withObject:interp waitUntilDone:false]; + const auto __strong thread = samplingLayer.layerThread; + if ([NSThread currentThread] != thread) { + [self performSelector:@selector(changeInterpreter:) onThread:thread withObject:interp waitUntilDone:false]; return; } ChangeSet changes; loadInterp = interp; loader->reload(NULL,-1,changes); - [samplingLayer.layerThread addChangeRequests:changes]; + [thread addChangeRequests:changes]; } - (void)reload @@ -286,8 +296,9 @@ - (void)reloadAreas:(NSArray*)bounds if (!samplingLayer) return; - if ([NSThread currentThread] != samplingLayer.layerThread) { - [self performSelector:@selector(reloadAreas:) onThread:samplingLayer.layerThread withObject:bounds waitUntilDone:false]; + const auto __strong thread = samplingLayer.layerThread; + if ([NSThread currentThread] != thread) { + [self performSelector:@selector(reloadAreas:) onThread:thread withObject:bounds waitUntilDone:false]; return; } @@ -304,7 +315,21 @@ - (void)reloadAreas:(NSArray*)bounds ChangeSet changes; loader->reload(nullptr,-1,boxPtr,(int)boxes.size(),changes); - [samplingLayer.layerThread addChangeRequests:changes]; + [thread addChangeRequests:changes]; +} + +- (void)updatePriorities +{ + if (!samplingLayer) + return; + + const auto __strong thread = samplingLayer.layerThread; + if ([NSThread currentThread] != thread) { + [self performSelector:@selector(updatePriorities) onThread:thread withObject:nil waitUntilDone:false]; + return; + } + + loader->updatePriorities(NULL); } // Called on a random dispatch queue @@ -423,45 +448,41 @@ - (void)mergeFetchRequest:(MaplyLoaderReturn *)loadReturn // Hold on to these till the task runs NSObject *theLoadInterp = self->loadInterp; - MaplyQuadSamplingLayer *samplingLayer = self->samplingLayer; dispatch_async(theQueue, ^{ if (!self->valid || !self->_viewC) return; + auto loadAndMerge = ^{ + // No load interpreter means the fetcher created the objects. Hopefully. + if (theLoadInterp && !loadReturn->loadReturn->cancel) + [theLoadInterp dataForTile:loadReturn loader:self]; + + // Merge in the results on the sampling layer thread. + // If the load was canceled, or we're shutting down and the thread no + // longer exists, then we need to clean up the results to avoid leaks. + const auto thread = self->samplingLayer.layerThread; + if (!thread || [thread isCancelled]) { + [self cleanupLoadedData:loadReturn]; + } else { + [self performSelector:@selector(mergeLoadedTile:) onThread:thread withObject:loadReturn waitUntilDone:NO]; + } + }; + if (theSemaphore) { // Need to limit the number of simultaneous loader return parses dispatch_semaphore_wait(theSemaphore, DISPATCH_TIME_FOREVER); - dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ - // No load interpreter means the fetcher created the objects. Hopefully. - if (theLoadInterp && !loadReturn->loadReturn->cancel) - [theLoadInterp dataForTile:loadReturn loader:self]; - - // Need to clean up the loader return objects - if ([samplingLayer.layerThread isCancelled]) { - [self cleanupLoadedData:loadReturn]; - return; + @try { + loadAndMerge(); + } @finally { + // _wait and _signal calls must be balanced + dispatch_semaphore_signal(theSemaphore); } - - [self performSelector:@selector(mergeLoadedTile:) onThread:self->samplingLayer.layerThread withObject:loadReturn waitUntilDone:NO]; - - dispatch_semaphore_signal(theSemaphore); }); } else { // Just run it on this queue right here - - // No load interpreter means the fetcher created the objects. Hopefully. - if (theLoadInterp) - [theLoadInterp dataForTile:loadReturn loader:self]; - - // Need to clean up the loader return objects - if ([samplingLayer.layerThread isCancelled]) { - [self cleanupLoadedData:loadReturn]; - return; - } - - [self performSelector:@selector(mergeLoadedTile:) onThread:self->samplingLayer.layerThread withObject:loadReturn waitUntilDone:NO]; + loadAndMerge(); } }); } @@ -470,30 +491,40 @@ - (void)mergeFetchRequest:(MaplyLoaderReturn *)loadReturn // Called on the SamplingLayer.LayerThread - (void)mergeLoadedTile:(MaplyLoaderReturn *)loadReturn { - if (!loader || !valid) { + const auto __strong thread = samplingLayer.layerThread; + if (!loader || !thread || !valid) { [self cleanupLoadedData:loadReturn]; return; } ChangeSet changes; if (!loadReturn->loadReturn->changes.empty()) { - [samplingLayer.layerThread addChangeRequests:loadReturn->loadReturn->changes]; + [thread addChangeRequests:loadReturn->loadReturn->changes]; loadReturn->loadReturn->changes.clear(); } loader->mergeLoadedTile(NULL,loadReturn->loadReturn.get(),changes); loader->setLoadReturnRef(loadReturn->loadReturn->ident,loadReturn->loadReturn->frame,NULL); - [samplingLayer.layerThread addChangeRequests:changes]; + [thread addChangeRequests:changes]; } - (void)cleanup { ChangeSet changes; - loader->cleanup(NULL,changes); - [samplingLayer.layerThread addChangeRequests:changes]; - [samplingLayer.layerThread flushChangeRequests]; + + if (!changes.empty()) { + const auto __strong thread = samplingLayer.layerThread; + if (thread) { + [thread addChangeRequests:changes]; + [thread flushChangeRequests]; + } else { + for (auto change : changes) { + delete change; + } + } + } dispatch_async(dispatch_get_main_queue(), ^{ [[self.viewC getRenderControl] releaseSamplingLayer:self->samplingLayer forUser:self->loader]; @@ -508,8 +539,9 @@ - (void)shutdown { valid = false; - if (self->samplingLayer && self->samplingLayer.layerThread) - [self performSelector:@selector(cleanup) onThread:self->samplingLayer.layerThread withObject:nil waitUntilDone:NO]; + const auto __strong thread = samplingLayer.layerThread; + if (thread) + [self performSelector:@selector(cleanup) onThread:thread withObject:nil waitUntilDone:NO]; } @end diff --git a/ios/library/WhirlyGlobe-MaplyComponent/src/loading/MaplyQuadPagingLoader.mm b/ios/library/WhirlyGlobe-MaplyComponent/src/loading/MaplyQuadPagingLoader.mm index d433966f32..edfd98963f 100644 --- a/ios/library/WhirlyGlobe-MaplyComponent/src/loading/MaplyQuadPagingLoader.mm +++ b/ios/library/WhirlyGlobe-MaplyComponent/src/loading/MaplyQuadPagingLoader.mm @@ -101,13 +101,14 @@ - (bool)delayedInit { loader->layer = self; + const auto __strong vc = self.viewC; if (!tileFetcher) { - tileFetcher = [self.viewC addTileFetcher:MaplyQuadImageLoaderFetcherName]; + tileFetcher = [vc addTileFetcher:MaplyQuadImageLoaderFetcherName]; } loader->tileFetcher = tileFetcher; loader->layer = self; - samplingLayer = [[self.viewC getRenderControl] findSamplingLayer:params forUser:self->loader]; + samplingLayer = [[vc getRenderControl] findSamplingLayer:params forUser:self->loader]; // Do this again in case they changed them loader->setSamplingParams(params); loader->setFlipY(self.flipY); diff --git a/ios/library/WhirlyGlobe-MaplyComponent/src/loading/MaplyRemoteTileFetcher.mm b/ios/library/WhirlyGlobe-MaplyComponent/src/loading/MaplyRemoteTileFetcher.mm index 8651b90abb..219ef5a16a 100644 --- a/ios/library/WhirlyGlobe-MaplyComponent/src/loading/MaplyRemoteTileFetcher.mm +++ b/ios/library/WhirlyGlobe-MaplyComponent/src/loading/MaplyRemoteTileFetcher.mm @@ -269,7 +269,8 @@ - (void)addStats:(MaplyRemoteTileFetcherStats * __nonnull)stats - (void)dump { - NSLog(@"---MaplyTileFetcher %@ Stats---",_fetcher.name); + const auto __strong fetcher = _fetcher; + NSLog(@"---MaplyTileFetcher %@ Stats---",fetcher.name); NSLog(@" Active Requests = %d",_activeRequests); NSLog(@" Max Active Requests = %d",_maxActiveRequests); NSLog(@" Total Requests = %d",_totalRequests); @@ -281,7 +282,7 @@ - (void)dump NSLog(@" Average request size = %.2fKB",_remoteData / _remoteRequests / 1024.0); } NSLog(@" Cached Data = %.2fMB",_localData / (1024.0*1024.0)); - NSLog(@" Num Simultaneous = %d",_fetcher.numConnections); + NSLog(@" Num Simultaneous = %d",fetcher.numConnections); } @end @@ -705,19 +706,17 @@ - (void)updateLoading ^(NSData * _Nullable data, NSURLResponse * _Nullable inResponse, NSError * _Nullable error) { NSHTTPURLResponse *response = (NSHTTPURLResponse *)inResponse; - dispatch_queue_t queue = [weakSelf getQueue]; - if (queue) - dispatch_async(queue, - ^{ - if (weakSelf) - [weakSelf handleData:data response:response error:error tile:tile fetchStart:fetchStartTile]; + if (dispatch_queue_t queue = [weakSelf getQueue]) + dispatch_async(queue, ^{ + [weakSelf handleData:data response:response error:error tile:tile fetchStart:fetchStartTile]; }); }]; // Look for it in local storage first bool inLocalStorage = false; - if (localStorage) { - NSData *data = [localStorage dataForTile:tile->fetchInfo tileID:tile->tileID]; + const auto __strong ls = localStorage; + if (ls) { + NSData *data = [ls dataForTile:tile->fetchInfo tileID:tile->tileID]; if (data) { inLocalStorage = true; @@ -760,12 +759,10 @@ - (void)handleData:(NSData *)data response:(NSHTTPURLResponse *)response error:( success = false; // The dev has one more chance to provide the data before we give up - if (secondChance) { - data = [secondChance dataForTile:tile->fetchInfo tileID:tile->tileID]; - if (data) { - success = true; - useCache = false; - } + data = [secondChance dataForTile:tile->fetchInfo tileID:tile->tileID]; + if (data) { + success = true; + useCache = false; } } else { // We do nothing for a cancel @@ -886,9 +883,9 @@ - (void)finishedLoading:(TileInfoRef)tile data:(NSData *)data error:(NSError *)e if (theQueue) { dispatch_async(theQueue, ^{ - [weakSelf finishTile:tile]; - - [weakSelf updateLoading]; + const auto __strong s = weakSelf; + [s finishTile:tile]; + [s updateLoading]; }); } else { tile->clear(); diff --git a/ios/library/WhirlyGlobe-MaplyComponent/src/loading/MaplySimpleTileFetcher.mm b/ios/library/WhirlyGlobe-MaplyComponent/src/loading/MaplySimpleTileFetcher.mm index ce4f9a720d..3bcef7343d 100644 --- a/ios/library/WhirlyGlobe-MaplyComponent/src/loading/MaplySimpleTileFetcher.mm +++ b/ios/library/WhirlyGlobe-MaplyComponent/src/loading/MaplySimpleTileFetcher.mm @@ -23,22 +23,9 @@ using namespace WhirlyKit; -// Encapsulates a single tile load request -@interface MaplySimpleTileFetchInfo : NSObject - -@property (nonatomic,assign) int x; -@property (nonatomic,assign) int y; -@property (nonatomic,assign) int level; - -@end - @implementation MaplySimpleTileFetchInfo @end -// Internal object used by the QuadImageLoader to generate tile load info -@interface MaplySimpleTileInfo : NSObject -@end - @implementation MaplySimpleTileInfo { int minZoom,maxZoom; diff --git a/ios/library/WhirlyGlobe-MaplyComponent/src/math/MaplyCoordinate.mm b/ios/library/WhirlyGlobe-MaplyComponent/src/math/MaplyCoordinate.mm index f2b5d7f806..def10563f2 100644 --- a/ios/library/WhirlyGlobe-MaplyComponent/src/math/MaplyCoordinate.mm +++ b/ios/library/WhirlyGlobe-MaplyComponent/src/math/MaplyCoordinate.mm @@ -23,159 +23,134 @@ using namespace WhirlyKit; -MaplyCoordinate MaplyCoordinateMake(float radLon,float radLat) -{ - MaplyCoordinate coord; - coord.x = radLon; - coord.y = radLat; - - return coord; -} - -MaplyCoordinateD MaplyCoordinateDMake(double radLon,double radLat) -{ - MaplyCoordinateD coord; - coord.x = radLon; - coord.y = radLat; - - return coord; -} +MaplyCoordinate MaplyCoordinateMake(float radLon,float radLat) { return { radLon, radLat }; } +MaplyCoordinateD MaplyCoordinateDMake(double radLon,double radLat) { return { radLon, radLat }; } +MaplyCoordinate MaplyCoordinateMakeWithDegrees(float degLon,float degLat) { return { DegToRad(degLon), DegToRad(degLat) }; } +MaplyCoordinateD MaplyCoordinateDMakeWithDegrees(double degLon, double degLat) { return { DegToRad(degLon), DegToRad(degLat) }; } +MaplyCoordinateD MaplyCoordinateDMakeWithMaplyCoordinate(MaplyCoordinate c) { return { c.x, c.y }; } +MaplyCoordinate3d MaplyCoordinate3dMake(float x, float y, float z) { return { x, y, z}; } +MaplyCoordinate3dD MaplyCoordinate3dDMake(double x, double y, double z) { return { x, y, z }; } - -MaplyCoordinate MaplyCoordinateMakeWithDegrees(float degLon,float degLat) -{ - MaplyCoordinate coord; - coord.x = DegToRad(degLon); - coord.y = DegToRad(degLat); - - return coord; -} - -MaplyCoordinateD MaplyCoordinateDMakeWithDegrees(double degLon,double degLat) -{ - MaplyCoordinateD coord; - coord.x = DegToRad(degLon); - coord.y = DegToRad(degLat); - - return coord; +inline MaplyBoundingBox MaplyBoundingBoxMakeFromMbr(Mbr mbr) { + return { MaplyCoordinateMake(mbr.ll().x(), mbr.ll().y()), MaplyCoordinateMake(mbr.ur().x(), mbr.ur().y()) }; } - -MaplyCoordinateD MaplyCoordinateDMakeWithMaplyCoordinate(MaplyCoordinate c) -{ - MaplyCoordinateD coord; - coord.x = c.x; - coord.y = c.y; - return coord; +inline MaplyBoundingBox MaplyBoundingBoxMakeFromMbrD(MbrD mbr) { + return { MaplyCoordinateMake(mbr.ll().x(), mbr.ll().y()), MaplyCoordinateMake(mbr.ur().x(), mbr.ur().y()) }; } - -MaplyCoordinate3d MaplyCoordinate3dMake(float x,float y,float z) -{ - MaplyCoordinate3d coord; - coord.x = x; coord.y = y; coord.z = z; - return coord; +inline MaplyBoundingBoxD MaplyBoundingBoxDMakeFromMbr(Mbr mbr) { + return { MaplyCoordinateDMake(mbr.ll().x(), mbr.ll().y()), MaplyCoordinateDMake(mbr.ur().x(), mbr.ur().y()) }; } - -MaplyCoordinate3dD MaplyCoordinate3dDMake(double x,double y,double z) -{ - MaplyCoordinate3dD coord; - coord.x = x; coord.y = y; coord.z = z; - return coord; +inline MaplyBoundingBoxD MaplyBoundingBoxDMakeFromMbrD(MbrD mbr) { + return { MaplyCoordinateDMake(mbr.ll().x(), mbr.ll().y()), MaplyCoordinateDMake(mbr.ur().x(), mbr.ur().y()) }; } MaplyBoundingBox MaplyBoundingBoxMakeWithDegrees(float degLon0,float degLat0,float degLon1,float degLat1) { - MaplyBoundingBox bbox; - bbox.ll = MaplyCoordinateMakeWithDegrees(degLon0, degLat0); - bbox.ur = MaplyCoordinateMakeWithDegrees(degLon1, degLat1); - - return bbox; + return { + MaplyCoordinateMakeWithDegrees(degLon0, degLat0), + MaplyCoordinateMakeWithDegrees(degLon1, degLat1) + }; } MaplyBoundingBoxD MaplyBoundingBoxDMakeWithDegrees(double degLon0,double degLat0,double degLon1,double degLat1) { - MaplyBoundingBoxD bbox; - bbox.ll = MaplyCoordinateDMakeWithDegrees(degLon0, degLat0); - bbox.ur = MaplyCoordinateDMakeWithDegrees(degLon1, degLat1); - - return bbox; + return { + MaplyCoordinateDMakeWithDegrees(degLon0, degLat0), + MaplyCoordinateDMakeWithDegrees(degLon1, degLat1) + }; } bool MaplyBoundingBoxesOverlap(MaplyBoundingBox bbox0,MaplyBoundingBox bbox1) { - Mbr mbr0,mbr1; - mbr0.ll() = Point2f(bbox0.ll.x,bbox0.ll.y); - mbr0.ur() = Point2f(bbox0.ur.x,bbox0.ur.y); - mbr1.ll() = Point2f(bbox1.ll.x,bbox1.ll.y); - mbr1.ur() = Point2f(bbox1.ur.x,bbox1.ur.y); - + const Mbr mbr0(Point2f(bbox0.ll.x,bbox0.ll.y), Point2f(bbox0.ur.x,bbox0.ur.y)); + const Mbr mbr1(Point2f(bbox1.ll.x,bbox1.ll.y), Point2f(bbox1.ur.x,bbox1.ur.y)); return mbr0.overlaps(mbr1); } bool MaplyBoundingBoxContains(MaplyBoundingBox bbox, MaplyCoordinate c) { - Mbr mbr; - Point2f point = Point2f(c.x, c.y); - mbr.ll() = Point2f(bbox.ll.x,bbox.ll.y); - mbr.ur() = Point2f(bbox.ur.x,bbox.ur.y); - - return mbr.insideOrOnEdge(point); + const Mbr mbr(Point2f(bbox.ll.x,bbox.ll.y), Point2f(bbox.ur.x,bbox.ur.y)); + return mbr.insideOrOnEdge(Point2f(c.x, c.y)); } MaplyBoundingBox MaplyBoundingBoxFromLocations(const CLLocationCoordinate2D locs[], unsigned int numLocs) { Mbr mbr; - for (unsigned int ii=0;ii + static TBox MbrAdd(TBox box, const TPoints pts[], unsigned count) { + for (auto i = 0; i < count; ++i) { + box.addPoint(Point2d(pts[i].x, pts[i].y)); + } + return box; + } + template + static MaplyBoundingBox ToBox(TMbr mbr) { + return MaplyBoundingBox { + MaplyCoordinate { mbr.ll().x(), mbr.ll().y() }, + MaplyCoordinate { mbr.ur().x(), mbr.ur().y() } + }; + } + template + static MaplyBoundingBoxD ToBoxD(TMbr mbr) { + return MaplyBoundingBoxD { + MaplyCoordinateD { mbr.ll().x(), mbr.ll().y() }, + MaplyCoordinateD { mbr.ur().x(), mbr.ur().y() } + }; + } +} + +MaplyBoundingBox MaplyBoundingBoxFromCoordinates(const MaplyCoordinate coords[], unsigned int numCoords) { + return ToBox(MbrAdd(Mbr(), coords, numCoords)); +} +MaplyBoundingBox MaplyBoundingBoxFromCoordinatesD(const MaplyCoordinateD coords[], unsigned int numCoords) { + return ToBox(MbrAdd(Mbr(), coords, numCoords)); +} +MaplyBoundingBoxD MaplyBoundingBoxDFromCoordinates(const MaplyCoordinate coords[], unsigned int numCoords) { + return ToBoxD(MbrAdd(MbrD(), coords, numCoords)); +} +MaplyBoundingBoxD MaplyBoundingBoxDFromCoordinatesD(const MaplyCoordinateD coords[], unsigned int numCoords) { + return ToBoxD(MbrAdd(MbrD(), coords, numCoords)); +} +MaplyBoundingBox MaplyBoundingBoxAddCoordinates(MaplyBoundingBox box, const MaplyCoordinate coords[], unsigned int numCoords) { + return ToBox(MbrAdd(Mbr(Point2f(box.ll.x, box.ll.y), Point2f(box.ur.x, box.ur.y)), coords, numCoords)); +} +MaplyBoundingBox MaplyBoundingBoxAddCoordinatesD(MaplyBoundingBox box, const MaplyCoordinateD coords[], unsigned int numCoords) { + return ToBox(MbrAdd(Mbr(Point2f(box.ll.x, box.ll.y), Point2f(box.ur.x, box.ur.y)), coords, numCoords)); +} +MaplyBoundingBoxD MaplyBoundingBoxDAddCoordinates(MaplyBoundingBoxD box, const MaplyCoordinate coords[], unsigned int numCoords) { + return ToBoxD(MbrAdd(MbrD(Point2d(box.ll.x, box.ll.y), Point2d(box.ur.x, box.ur.y)), coords, numCoords)); +} +MaplyBoundingBoxD MaplyBoundingBoxDAddCoordinatesD(MaplyBoundingBoxD box, const MaplyCoordinateD coords[], unsigned int numCoords) { + return ToBoxD(MbrAdd(MbrD(Point2d(box.ll.x, box.ll.y), Point2d(box.ur.x, box.ur.y)), coords, numCoords)); } MaplyBoundingBox MaplyBoundingBoxIntersection(MaplyBoundingBox bbox0,MaplyBoundingBox bbox1) { - Mbr mbr0; - mbr0.ll() = Point2f(bbox0.ll.x,bbox0.ll.y); - mbr0.ur() = Point2f(bbox0.ur.x,bbox0.ur.y); - Mbr mbr1; - mbr1.ll() = Point2f(bbox1.ll.x,bbox1.ll.y); - mbr1.ur() = Point2f(bbox1.ur.x,bbox1.ur.y); - Mbr inter = mbr0.intersect(mbr1); - - MaplyBoundingBox ret; - ret.ll.x = inter.ll().x(); ret.ll.y = inter.ll().y(); - ret.ur.x = inter.ur().x(); ret.ur.y = inter.ur().y(); - - return ret; + const Mbr mbr0(Point2f(bbox0.ll.x,bbox0.ll.y), Point2f(bbox0.ur.x,bbox0.ur.y)); + const Mbr mbr1(Point2f(bbox1.ll.x,bbox1.ll.y), Point2f(bbox1.ur.x,bbox1.ur.y)); + return MaplyBoundingBoxMakeFromMbr(mbr0.intersect(mbr1)); } MaplyBoundingBox MaplyBoundingBoxExpandByFraction(MaplyBoundingBox bbox, float buffer) { - Mbr mbr; - mbr.ll() = Point2f(bbox.ll.x,bbox.ll.y); - mbr.ur() = Point2f(bbox.ur.x,bbox.ur.y); - + Mbr mbr(Point2f(bbox.ll.x,bbox.ll.y), Point2f(bbox.ur.x,bbox.ur.y)); mbr.expandByFraction(buffer); - MaplyBoundingBox r; - r.ll = MaplyCoordinateMake(mbr.ll().x(), mbr.ll().y()); - r.ur = MaplyCoordinateMake(mbr.ur().x(), mbr.ur().y()); - - return r; + return MaplyBoundingBoxMakeFromMbr(mbr); } - - double MaplyGreatCircleDistance(MaplyCoordinate p0,MaplyCoordinate p1) { - double delta = acos(sin(p0.y)*sin(p1.y) + cos(p0.y)*cos(p1.y)*cos(p1.x-p0.x)); - return delta * EarthRadius; + return EarthRadius * acos(sin(p0.y)*sin(p1.y) + cos(p0.y)*cos(p1.y)*cos(p1.x-p0.x)); } @implementation MaplyCoordinate3dWrapper @@ -203,6 +178,28 @@ - (instancetype)initWithCoord:(MaplyCoordinate3dD)coord @end +@implementation NSValue (MaplyCoordinate) ++ (instancetype)valueWithMaplyCoordinate:(MaplyCoordinate)value { + return [self valueWithBytes:&value objCType:@encode(MaplyCoordinate)]; +} +- (MaplyCoordinate)maplyCoordinateValue { + MaplyCoordinate c; + [self getValue:&c]; + return c; +} +@end + +@implementation NSValue (MaplyCoordinateD) ++ (instancetype)valueWithMaplyCoordinateD:(MaplyCoordinateD)value { + return [self valueWithBytes:&value objCType:@encode(MaplyCoordinateD)]; +} +- (MaplyCoordinateD)maplyCoordinateDValue { + MaplyCoordinateD c; + [self getValue:&c]; + return c; +} +@end + @implementation NSValue (MaplyBoundingBox) + (instancetype)valueWithMaplyBoundingBox:(MaplyBoundingBox)value { return [self valueWithBytes:&value objCType:@encode(MaplyBoundingBox)]; @@ -213,3 +210,4 @@ - (MaplyBoundingBox)maplyBoundingBoxValue { return box; } @end + diff --git a/ios/library/WhirlyGlobe-MaplyComponent/src/rendering/MaplyAtmosphere.mm b/ios/library/WhirlyGlobe-MaplyComponent/src/rendering/MaplyAtmosphere.mm index 53e885e77c..844c98a7a9 100644 --- a/ios/library/WhirlyGlobe-MaplyComponent/src/rendering/MaplyAtmosphere.mm +++ b/ios/library/WhirlyGlobe-MaplyComponent/src/rendering/MaplyAtmosphere.mm @@ -487,19 +487,21 @@ - (void)complexAtmosphere sphere.center = MaplyCoordinateMake(0, 0); sphere.height = -1.0; sphere.radius = _outerRadius; - compObj = [viewC addShapes:@[sphere] desc:@{kMaplyZBufferRead: @(NO), - kMaplyZBufferWrite: @(NO), - kMaplyShapeSampleX: @(120), - kMaplyShapeSampleY: @(60), - kMaplyShapeInsideOut: @(YES), - kMaplyShapeCenterX: @(0.0), - kMaplyShapeCenterY: @(0.0), - kMaplyShapeCenterZ: @(0.0), - kMaplyDrawPriority: @(kMaplyAtmosphereDrawPriorityDefault), - kMaplyShader: kAtmosphereShader}]; - sunUpdater = [[SunUpdater alloc] initWithShader:shader groundShader:_groundShader atm:self viewC:viewC]; - [viewC addActiveObject:sunUpdater]; + const auto __strong vc = viewC; + compObj = [vc addShapes:@[sphere] desc:@{kMaplyZBufferRead: @(NO), + kMaplyZBufferWrite: @(NO), + kMaplyShapeSampleX: @(120), + kMaplyShapeSampleY: @(60), + kMaplyShapeInsideOut: @(YES), + kMaplyShapeCenterX: @(0.0), + kMaplyShapeCenterY: @(0.0), + kMaplyShapeCenterZ: @(0.0), + kMaplyDrawPriority: @(kMaplyAtmosphereDrawPriorityDefault), + kMaplyShader: kAtmosphereShader}]; + + sunUpdater = [[SunUpdater alloc] initWithShader:shader groundShader:_groundShader atm:self viewC:vc]; + [vc addActiveObject:sunUpdater]; } - (MaplyShader *)setupGroundShader @@ -537,11 +539,12 @@ - (MaplyShader *)setupShader - (void)removeFromViewC { + const auto __strong vc = viewC; if (compObj) - [viewC removeObject:compObj]; + [vc removeObject:compObj]; compObj = nil; if (sunUpdater) - [viewC removeActiveObject:sunUpdater]; + [vc removeActiveObject:sunUpdater]; sunUpdater = nil; // Note: Should remove shader } diff --git a/ios/library/WhirlyGlobe-MaplyComponent/src/rendering/MaplyRenderTarget.mm b/ios/library/WhirlyGlobe-MaplyComponent/src/rendering/MaplyRenderTarget.mm index 34c4315c90..7e11b0d6a2 100644 --- a/ios/library/WhirlyGlobe-MaplyComponent/src/rendering/MaplyRenderTarget.mm +++ b/ios/library/WhirlyGlobe-MaplyComponent/src/rendering/MaplyRenderTarget.mm @@ -40,49 +40,40 @@ - (id)init - (NSData *)getValueAtX:(int)x y:(int)y { - if (!_renderControl) - return nil; - SceneRendererMTLRef sceneRenderer = std::dynamic_pointer_cast(_renderControl->sceneRenderer); - if (!sceneRenderer) - return nil; - - RawDataRef dataRef = sceneRenderer->getSnapshotAt(_renderTargetID, x, y); - RawNSDataReaderRef rawData = std::dynamic_pointer_cast(dataRef); - if (rawData) - return rawData->getData(); - + if (const auto __strong rc = _renderControl) { + if (const auto sceneRenderer = dynamic_cast(rc->sceneRenderer.get())) { + const auto dataRef = sceneRenderer->getSnapshotAt(_renderTargetID, x, y); + if (const auto rawData = dynamic_cast(dataRef.get())) { + return rawData->getData(); + } + } + } return nil; } - (NSData *)getSnapshot { - if (!_renderControl) - return nil; - SceneRendererMTLRef sceneRenderer = std::dynamic_pointer_cast(_renderControl->sceneRenderer); - if (!sceneRenderer) - return nil; - - RawDataRef dataRef = sceneRenderer->getSnapshot(_renderTargetID); - RawNSDataReaderRef rawData = std::dynamic_pointer_cast(dataRef); - if (rawData) - return rawData->getData(); - + if (const auto __strong rc = _renderControl) { + if (const auto sceneRenderer = dynamic_cast(rc->sceneRenderer.get())) { + const auto dataRef = sceneRenderer->getSnapshot(_renderTargetID); + if (const auto rawData = dynamic_cast(dataRef.get())) { + return rawData->getData(); + } + } + } return nil; } - (NSData *)getMinMaxValues { - if (!_renderControl) - return nil; - SceneRendererMTLRef sceneRenderer = std::dynamic_pointer_cast(_renderControl->sceneRenderer); - if (!sceneRenderer) - return nil; - - RawDataRef dataRef = sceneRenderer->getSnapshotMinMax(_renderTargetID); - RawNSDataReaderRef rawData = std::dynamic_pointer_cast(dataRef); - if (rawData) - return rawData->getData(); - + if (const auto __strong rc = _renderControl) { + if (const auto sceneRenderer = dynamic_cast(rc->sceneRenderer.get())) { + const auto dataRef = sceneRenderer->getSnapshotMinMax(_renderTargetID); + if (const auto rawData = dynamic_cast(dataRef.get())) { + return rawData->getData(); + } + } + } return nil; } diff --git a/ios/library/WhirlyGlobe-MaplyComponent/src/rendering/MaplyShader.mm b/ios/library/WhirlyGlobe-MaplyComponent/src/rendering/MaplyShader.mm index 344301eebf..b5b7713bde 100644 --- a/ios/library/WhirlyGlobe-MaplyComponent/src/rendering/MaplyShader.mm +++ b/ios/library/WhirlyGlobe-MaplyComponent/src/rendering/MaplyShader.mm @@ -60,7 +60,7 @@ - (instancetype)initMetalWithName:(NSString *)inName vertex:(id)ver ProgramMTLRef prog(new ProgramMTL(name,vertexFunc,fragFunc)); _program = prog; - MaplyRenderController *renderControl = [viewC getRenderControl]; + MaplyRenderController *renderControl = [baseViewC getRenderControl]; if (!renderControl) return nil; diff --git a/ios/library/WhirlyGlobe-MaplyComponent/src/rendering/MaplyVariableTarget.mm b/ios/library/WhirlyGlobe-MaplyComponent/src/rendering/MaplyVariableTarget.mm index 0acfd8a530..8ee6f92df8 100644 --- a/ios/library/WhirlyGlobe-MaplyComponent/src/rendering/MaplyVariableTarget.mm +++ b/ios/library/WhirlyGlobe-MaplyComponent/src/rendering/MaplyVariableTarget.mm @@ -45,6 +45,7 @@ - (instancetype)initWithType:(MaplyQuadImageFormat)type viewC:(NSObject *theViewC = viewC; + + if (_rectObj) { + [theViewC removeObjects:@[_rectObj] mode:MaplyThreadCurrent]; + _rectObj = nil; + } + + // Set up a rectangle right over the view to render the render target + MaplyShapeRectangle *rect = [[MaplyShapeRectangle alloc] init]; + rect.ll = MaplyCoordinate3dDMake(-1.0, -1.0, 0.0); + rect.ur = MaplyCoordinate3dDMake(1.0, 1.0, 0.0); + rect.clipCoords = true; + [rect addTexture:_renderTex]; + for (MaplyVariableTarget *auxTarget : auxTargets) + [rect addTexture:auxTarget.renderTex]; + NSString *shaderName = nil; + if (_shader) + shaderName = [_shader name]; + else + shaderName = kMaplyShaderDefaultTriNoLighting; + _rectObj = [theViewC addShapes:@[rect] + desc:@{kMaplyColor: _color, + kMaplyDrawPriority: @(_drawPriority), + kMaplyShader: shaderName, + kMaplyZBufferRead: @(_zBuffer), + kMaplyZBufferWrite: @(NO) + } + mode:MaplyThreadCurrent]; + + // Pass through the uniform blocks if they've been set up + for (auto block : uniBlocks) { + [theViewC setUniformBlock:block.second buffer:block.first forObjects:@[_rectObj] mode:MaplyThreadCurrent]; + } } - (void)delayedSetup { if (!valid) return; - - CGSize screenSize = [viewC getFramebufferSize]; + + const auto __strong vc = viewC; + CGSize screenSize = [vc getFramebufferSize]; // Can get into a race on framebuffer setup if (screenSize.width == 0.0) { @@ -79,41 +122,15 @@ - (void)delayedSetup _texSize = screenSize; // Set up the render target - _renderTex = [viewC createTexture:@{kMaplyTexFormat: @(_type)} sizeX:screenSize.width sizeY:screenSize.height mode:MaplyThreadCurrent]; + _renderTex = [vc createTexture:@{kMaplyTexFormat: @(_type)} sizeX:screenSize.width sizeY:screenSize.height mode:MaplyThreadCurrent]; _renderTarget.texture = _renderTex; _renderTarget.clearEveryFrame = _clearEveryFrame; - [viewC addRenderTarget:_renderTarget]; + _renderTarget.clearVal = _clearVal; + [vc addRenderTarget:_renderTarget]; if (_buildRectangle) { - // Set up a rectangle right over the view to render the render target - MaplyShapeRectangle *rect = [[MaplyShapeRectangle alloc] init]; - rect.ll = MaplyCoordinate3dDMake(-1.0, -1.0, 0.0); - rect.ur = MaplyCoordinate3dDMake(1.0, 1.0, 0.0); - rect.clipCoords = true; - [rect addTexture:_renderTex]; - for (MaplyVariableTarget *auxTarget : auxTargets) - [rect addTexture:auxTarget.renderTex]; - NSString *shaderName = nil; - if (_shader) - shaderName = [_shader name]; - else - shaderName = kMaplyShaderDefaultTriNoLighting; - _rectObj = [viewC addShapes:@[rect] - desc:@{kMaplyColor: _color, - kMaplyDrawPriority: @(_drawPriority), - kMaplyShader: shaderName, - kMaplyZBufferRead: @(_zBuffer), - kMaplyZBufferWrite: @(NO) - } - mode:MaplyThreadCurrent]; - - // Pass through the uniform blocks if they've been set up - for (auto block : uniBlocks) { - [viewC setUniformBlock:block.second buffer:block.first forObjects:@[_rectObj] mode:MaplyThreadCurrent]; - } + [self setupRectangle]; } - - auxTargets.clear(); } /// Scale the screen by this amount for the render target @@ -134,27 +151,30 @@ - (void)setUniformBlock:(NSData *__nonnull)uniBlock buffer:(int)bufferID - (void)clear { - if (_renderTarget && [viewC isKindOfClass:[MaplyBaseViewController class]]) - [(MaplyBaseViewController *)viewC clearRenderTarget:_renderTarget mode:MaplyThreadCurrent]; + const auto vc = viewC; + if (_renderTarget && [vc isKindOfClass:[MaplyBaseViewController class]]) + [(MaplyBaseViewController *)vc clearRenderTarget:_renderTarget mode:MaplyThreadCurrent]; } /// Stop rendering to the target and release everything - (void)shutdown { valid = false; + auxTargets.clear(); + const auto __strong vc = viewC; if (_rectObj) { - [viewC removeObjects:@[_rectObj] mode:MaplyThreadCurrent]; + [vc removeObjects:@[_rectObj] mode:MaplyThreadCurrent]; _rectObj = nil; } if (_renderTarget) { - [viewC removeRenderTarget:_renderTarget]; + [vc removeRenderTarget:_renderTarget]; #ifndef __clang_analyzer__ // override __nonnull _renderTarget = nil; #endif } if (_renderTex) { - [viewC removeTextures:@[_renderTex] mode:MaplyThreadCurrent]; + [vc removeTextures:@[_renderTex] mode:MaplyThreadCurrent]; _renderTex = nil; } } diff --git a/ios/library/WhirlyGlobe-MaplyComponent/src/vector_tiles/GeoJSONSource.mm b/ios/library/WhirlyGlobe-MaplyComponent/src/vector_tiles/GeoJSONSource.mm index 4d233b637c..a9c6e71b54 100644 --- a/ios/library/WhirlyGlobe-MaplyComponent/src/vector_tiles/GeoJSONSource.mm +++ b/ios/library/WhirlyGlobe-MaplyComponent/src/vector_tiles/GeoJSONSource.mm @@ -158,7 +158,7 @@ - (void)startParseWithCompletion:(nonnull void (^)()) completionBlock { for(id key in symbolizerKeys) { NSObject *symbolizer = [self->_styleSet styleForUUID:[key longValue] viewC:baseVC]; NSArray *features = featureStyles[key]; - [symbolizer buildObjects:features forTile:tileInfo viewC:baseVC]; + [symbolizer buildObjects:features forTile:tileInfo viewC:baseVC desc:nil]; } self->_compObjs = [tileInfo componentObjects]; diff --git a/ios/library/WhirlyGlobe-MaplyComponent/src/vector_tiles/MapboxVectorInterpreter.mm b/ios/library/WhirlyGlobe-MaplyComponent/src/vector_tiles/MapboxVectorInterpreter.mm index 2dca1381a0..fdbf6f18ba 100644 --- a/ios/library/WhirlyGlobe-MaplyComponent/src/vector_tiles/MapboxVectorInterpreter.mm +++ b/ios/library/WhirlyGlobe-MaplyComponent/src/vector_tiles/MapboxVectorInterpreter.mm @@ -83,18 +83,18 @@ - (instancetype) initWithImageStyle:(NSObject *)inImag if ([testImageStyle respondsToSelector:@selector(getVectorStyleImpl)]) { imageStyle = [testImageStyle getVectorStyleImpl]; } else - imageStyle = VectorStyleDelegateImplRef(new VectorStyleDelegateWrapper(viewC,inImageStyle)); + imageStyle = std::make_shared(inViewC,inImageStyle); // Same for the vector, uh, vector styles NSObject *testVecStyle = (NSObject *)inVectorStyle; if ([testVecStyle respondsToSelector:@selector(getVectorStyleImpl)]) { vecStyle = [testVecStyle getVectorStyleImpl]; } else - vecStyle = VectorStyleDelegateImplRef(new VectorStyleDelegateWrapper(viewC,inVectorStyle)); + vecStyle = std::make_shared(inViewC,inVectorStyle); - imageTileParser = MapboxVectorTileParserRef(new MapboxVectorTileParser(NULL,imageStyle)); - imageTileParser->localCoords = true; - vecTileParser = MapboxVectorTileParserRef(new MapboxVectorTileParser(NULL,vecStyle)); + imageTileParser = std::make_shared(nullptr,imageStyle); + imageTileParser->setLocalCoords(); + vecTileParser = std::make_shared(nullptr,vecStyle); return self; } @@ -110,26 +110,33 @@ - (instancetype) initWithVectorStyle:(NSObject *)inVec if ([testVecStyle respondsToSelector:@selector(getVectorStyleImpl)]) { vecStyle = [testVecStyle getVectorStyleImpl]; } else - vecStyle = VectorStyleDelegateImplRef(new VectorStyleDelegateWrapper(viewC,inVectorStyle)); + vecStyle = std::make_shared(inViewC,inVectorStyle); + + vecTileParser = std::make_shared(nullptr,vecStyle); - vecTileParser = MapboxVectorTileParserRef(new MapboxVectorTileParser(NULL,vecStyle)); - return self; } - (void)setUUIDName:(NSString *)inUuidName uuidValues:(NSArray *)uuids { - std::string uuidName = [inUuidName cStringUsingEncoding:NSUTF8StringEncoding]; - std::set uuidValues; - for (NSString *uuid in uuids) { - std::string uuidStr = [uuid cStringUsingEncoding:NSUTF8StringEncoding]; - uuidValues.insert(uuidStr); + if (imageTileParser || vecTileParser) + { + std::string uuidName = [inUuidName cStringUsingEncoding:NSUTF8StringEncoding]; + std::set uuidValues; + for (NSString *uuid in uuids) + { + uuidValues.emplace([uuid cStringUsingEncoding:NSUTF8StringEncoding]); + } + + if (imageTileParser) + { + imageTileParser->setAttributeFilter(uuidName,uuidValues); + } + if (vecTileParser) + { + vecTileParser->setAttributeFilter(uuidName,uuidValues); + } } - - if (imageTileParser) - imageTileParser->setUUIDs(uuidName,uuidValues); - if (vecTileParser) - vecTileParser->setUUIDs(uuidName,uuidValues); } - (void)setLoader:(MaplyQuadLoaderBase *)inLoader @@ -173,7 +180,8 @@ - (void)dataForTile:(MaplyImageLoaderReturn *)loadReturn loader:(MaplyQuadLoader const MaplyTileID tileID = loadReturn.tileID; std::vector pbfDatas; std::vector images; - + const auto __strong vc = viewC; + // Uncompress any of the data we recieved NSArray *tileData = [loadReturn getTileData]; for (unsigned int ii=0;ii<[tileData count];ii++) { @@ -272,7 +280,7 @@ - (void)dataForTile:(MaplyImageLoaderReturn *)loadReturn loader:(MaplyQuadLoader std::vector compObjs,ovlCompObjs; for (NSData *thisTileData : pbfDatas) { // Use a separate work item for each tile, so that we react quickly if told to shut down - WorkRegion wr(viewC); + WorkRegion wr(vc); if (!wr) { return; } @@ -294,10 +302,10 @@ - (void)dataForTile:(MaplyImageLoaderReturn *)loadReturn loader:(MaplyQuadLoader } if ([loadReturn isKindOfClass:[MaplyImageLoaderReturn class]]) { - if (auto wr = WorkRegion(viewC)) { + if (auto wr = WorkRegion(vc)) { if (offlineRender) { // Rendered image goes in first - auto tileImage = [[MaplyImageTile alloc] initWithRawImage:imageData width:offlineRender.getFramebufferSize.width height:offlineRender.getFramebufferSize.height viewC:viewC]; + auto tileImage = [[MaplyImageTile alloc] initWithRawImage:imageData width:offlineRender.getFramebufferSize.width height:offlineRender.getFramebufferSize.height viewC:vc]; [loadReturn addImageTile:tileImage]; } else if (images.empty()) { // Make a single color background image @@ -315,13 +323,13 @@ - (void)dataForTile:(MaplyImageLoaderReturn *)loadReturn loader:(MaplyQuadLoader data++; } - auto tileImage = [[MaplyImageTile alloc] initWithRawImage:backImageData width:BackImageWidth height:BackImageHeight viewC:viewC]; + auto tileImage = [[MaplyImageTile alloc] initWithRawImage:backImageData width:BackImageWidth height:BackImageHeight viewC:vc]; [loadReturn addImageTile:tileImage]; } // Any additional images are tacked on for (UIImage *image : images) { - MaplyImageTile *tileData = [[MaplyImageTile alloc] initWithImage:image viewC:viewC]; + MaplyImageTile *tileData = [[MaplyImageTile alloc] initWithImage:image viewC:vc]; [loadReturn addImageTile:tileData]; } } diff --git a/ios/library/WhirlyGlobe-MaplyComponent/src/vector_tiles/MapboxVectorStyleSet.mm b/ios/library/WhirlyGlobe-MaplyComponent/src/vector_tiles/MapboxVectorStyleSet.mm index dd65da626d..f709aa850d 100644 --- a/ios/library/WhirlyGlobe-MaplyComponent/src/vector_tiles/MapboxVectorStyleSet.mm +++ b/ios/library/WhirlyGlobe-MaplyComponent/src/vector_tiles/MapboxVectorStyleSet.mm @@ -47,11 +47,12 @@ - (id __nullable)initWithDict:(NSDictionary * __nonnull)styleDict if (settings) styleSettings = settings->impl; else - styleSettings = VectorStyleSettingsImplRef(new VectorStyleSettingsImpl([UIScreen mainScreen].scale)); - - MapboxVectorStyleSetImpl_iOS *styleSetImpl = new MapboxVectorStyleSetImpl_iOS([viewC getRenderControl]->scene,[viewC getRenderControl]->visualView->coordAdapter->getCoordSystem(),styleSettings); - style = MapboxVectorStyleSetImpl_iOSRef(styleSetImpl); - styleSetImpl->viewC = viewC; + styleSettings = std::make_shared([UIScreen mainScreen].scale); + + style = std::make_shared([viewC getRenderControl]->scene, + [viewC getRenderControl]->visualView->coordAdapter->getCoordSystem(), + styleSettings); + style->viewC = viewC; // iosDictionaryRef dictWrap(new iosDictionary(styleDict)); @@ -67,7 +68,7 @@ - (id __nullable)initWithDict:(NSDictionary * __nonnull)styleDict NSMutableArray *sources = [NSMutableArray array]; for (NSString *sourceName in sourceStyles.allKeys) { NSDictionary *styleEntry = sourceStyles[sourceName]; - MaplyMapboxVectorStyleSource *source = [[MaplyMapboxVectorStyleSource alloc] initWithName:sourceName styleEntry:styleEntry styleSet:self viewC:_viewC]; + MaplyMapboxVectorStyleSource *source = [[MaplyMapboxVectorStyleSource alloc] initWithName:sourceName styleEntry:styleEntry styleSet:self viewC:viewC]; if (source) [sources addObject:source]; } @@ -99,19 +100,18 @@ - (bool)addSprites:(NSDictionary * __nonnull)spriteDict image:(UIImage * __nonnu // Make sure this wasn't alreayd added if (spriteImage) return true; - + spriteImage = image; MaplyTexture *wholeTex = [_viewC addTexture:image desc:nil mode:MaplyThreadCurrent]; - - MapboxVectorStyleSpritesRef newSprites(new MapboxVectorStyleSprites(wholeTex.texID,(int)image.size.width,(int)image.size.height)); - iosDictionaryRef dictWrap(new iosDictionary(spriteDict)); - if (newSprites->parse(style, dictWrap)) { + + auto newSprites = std::make_shared(wholeTex.texID,(int)image.size.width,(int)image.size.height); + auto dictWrap = std::make_shared(spriteDict); + if (newSprites->parse(style, dictWrap)) + { style->addSprites(newSprites,wholeTex); - } else { - return false; + return true; } - - return true; + return false; } - (UIColor * __nullable)backgroundColorForZoom:(double)zoom @@ -166,7 +166,6 @@ - (void)setLayerVisible:(NSString *__nonnull)inLayerName visible:(bool)visible for (auto layer : style->layers) { if (layer->ident == layerName) { layer->visible = visible; - return; } } } @@ -305,74 +304,61 @@ - (UIImage *)imageForCircle:(UIColor *)color size:(CGSize)size - (NSArray * __nonnull)layerLegend:(CGSize)imageSize group:(bool)useGroups { - NSMutableArray *legend = [NSMutableArray array]; + NSMutableArray *legend = [NSMutableArray arrayWithCapacity:(NSUInteger)style->layers.size()]; NSMutableDictionary *groups = [NSMutableDictionary dictionary]; - for (auto layer : style->layers) { + for (const auto &layer : style->layers) { + if (!layer->representation.empty()) { + // This is an alternate representation of another layer, e.g., "selected" + continue; + } + UIImage *image = nil; - auto layerBackground = std::dynamic_pointer_cast(layer); - if (layerBackground) { + if (auto layerBackground = dynamic_cast(layer.get())) { if (layerBackground->paint.color) { image = [self imageForPolygon:[UIColor colorFromRGBA:layerBackground->paint.color->colorForZoom(0.0)] size:imageSize]; } - } else { - auto layerSymbol = std::dynamic_pointer_cast(layer); - if (layerSymbol) { - if (layerSymbol->layout.iconImageField) { - MapboxRegexField textField = layerSymbol->layout.iconImageField->textForZoom(0.0); - if (!textField.chunks.empty()) - image = [self imageForSymbol:textField.chunks[0].str size:imageSize]; - } else if (layerSymbol->paint.textColor) { - image = [self imageForText:[UIColor colorFromRGBA:layerSymbol->paint.textColor->colorForZoom(0.0)] size:imageSize]; - } - } else { - auto layerCircle = std::dynamic_pointer_cast(layer); - if (layerCircle) { - auto color = layerCircle->paint.fillColor; - if (color) - image = [self imageForCircle:[UIColor colorFromRGBA:*color] size:imageSize]; - } else { - auto layerLine = std::dynamic_pointer_cast(layer); - if (layerLine) { - auto color = layerLine->paint.color; - if (color) { - image = [self imageForLinear:[UIColor colorFromRGBA:color->colorForZoom(0.0)] size:imageSize]; - } - } else { - auto layerFill = std::dynamic_pointer_cast(layer); - if (layerFill) { - auto color = layerFill->paint.color; - if (color) { - image = [self imageForPolygon:[UIColor colorFromRGBA:color->colorForZoom(0.0)] size:imageSize]; - } - } - } + } else if (auto layerSymbol = dynamic_cast(layer.get())) { + if (layerSymbol->layout.iconImageField) { + MapboxRegexField textField = layerSymbol->layout.iconImageField->textForZoom(0.0); + if (!textField.chunks.empty()) { + image = [self imageForSymbol:textField.chunks[0].str size:imageSize]; } + } else if (layerSymbol->paint.textColor) { + image = [self imageForText:[UIColor colorFromRGBA:layerSymbol->paint.textColor->colorForZoom(0.0)] size:imageSize]; + } + } else if (auto layerCircle = dynamic_cast(layer.get())) { + if (const auto &color = layerCircle->paint.fillColor) { + image = [self imageForCircle:[UIColor colorFromRGBA:*color] size:imageSize]; + } + } else if (auto layerLine = dynamic_cast(layer.get())) { + if (const auto &color = layerLine->paint.color) { + image = [self imageForLinear:[UIColor colorFromRGBA:color->colorForZoom(0.0)] size:imageSize]; + } + } else if (auto layerFill = dynamic_cast(layer.get())) { + if (const auto &color = layerFill->paint.color) { + image = [self imageForPolygon:[UIColor colorFromRGBA:color->colorForZoom(0.0)] size:imageSize]; } } - + if (!layer->ident.empty()) { - std::string groupName = ""; - std::string name; + std::string groupName; + std::string name = layer->ident; if (useGroups) { // Parse the name apart - auto pos = layer->ident.find_first_of('_'); + const auto pos = layer->ident.find_first_of('_'); if (pos != std::string::npos) { groupName = layer->ident.substr(0,pos); name = layer->ident.substr(pos+1); - } else - name = layer->ident; - } else - name = layer->ident; - NSString *nameStr = [NSString stringWithUTF8String:name.c_str()]; - if (nameStr) { + } + } + if (NSString *nameStr = [NSString stringWithUTF8String:name.c_str()]) { MaplyLegendEntry *entry = [[MaplyLegendEntry alloc] init]; entry.name = nameStr; entry.image = image; if (!groupName.empty()) { - NSString *groupNameStr = [NSString stringWithUTF8String:groupName.c_str()]; - if (groupNameStr) { + if (NSString *groupNameStr = [NSString stringWithUTF8String:groupName.c_str()]) { MaplyLegendEntry *group = groups[groupNameStr]; if (!group) { group = [[MaplyLegendEntry alloc] init]; @@ -384,8 +370,9 @@ - (UIImage *)imageForCircle:(UIColor *)color size:(CGSize)size [group.entries addObject:entry]; } - } else + } else { [legend addObject:entry]; + } } } } @@ -393,24 +380,41 @@ - (UIImage *)imageForCircle:(UIColor *)color size:(CGSize)size return legend; } -// These are here just to satisfy the compiler. We use the underlying C++ calls instead +// These wrap the style if someone is using a non-standard path to call it +// We do that in at least one place - (nullable NSArray *)stylesForFeatureWithAttributes:(NSDictionary *__nonnull)attributes onTile:(MaplyTileID)tileID inLayer:(NSString *__nonnull)layer viewC:(NSObject *__nonnull)viewC { - return nil; + MutableDictionaryCRef dictWrap = [attributes toDictionaryC]; + const QuadTreeIdentifier tileIDc(tileID.x,tileID.y,tileID.level); + const std::string layerName = [layer cStringUsingEncoding:NSUTF8StringEncoding]; + + auto styles = style->stylesForFeature(nil, *(dictWrap.get()), tileIDc, layerName); + + // Build up a wrapper for each one + NSMutableArray *retStyles = [NSMutableArray array]; + for (auto theStyle: styles) { + [retStyles addObject:[[MaplyVectorStyleReverseWrapper alloc] initWithCStyle:theStyle]]; + } + + return retStyles; } - (BOOL)layerShouldDisplay:(NSString *__nonnull)layer tile:(MaplyTileID)tileID { - return false; + const std::string layerName = [layer cStringUsingEncoding:NSUTF8StringEncoding]; + const QuadTreeIdentifier tileIDc(tileID.x,tileID.y,tileID.level); + return style->layerShouldDisplay(nil, layerName, tileIDc); } -- (nullable NSObject *)styleForUUID:(long long)uiid viewC:(NSObject *__nonnull)viewC +- (nullable NSObject *)styleForUUID:(long long)uuid viewC:(NSObject *__nonnull)viewC { - return nil; + auto theStyle = style->styleForUUID(NULL, uuid); + + return [[MaplyVectorStyleReverseWrapper alloc] initWithCStyle:theStyle]; } - (nullable NSObject *)backgroundStyleViewC:(NSObject *)viewC @@ -420,7 +424,15 @@ - (BOOL)layerShouldDisplay:(NSString *__nonnull)layer tile:(MaplyTileID)tileID - (NSArray * __nonnull)allStyles { - return [NSMutableArray array]; + auto styles = style->allStyles(NULL); + + // Build up a wrapper for each one + NSMutableArray *retStyles = [NSMutableArray array]; + for (auto theStyle: styles) { + [retStyles addObject:[[MaplyVectorStyleReverseWrapper alloc] initWithCStyle:theStyle]]; + } + + return retStyles; } // Returns the C++ class that does the work diff --git a/ios/library/WhirlyGlobe-MaplyComponent/src/vector_tiles/MapboxVectorTiles.mm b/ios/library/WhirlyGlobe-MaplyComponent/src/vector_tiles/MapboxVectorTiles.mm index 9183fe379c..7dae53ea30 100644 --- a/ios/library/WhirlyGlobe-MaplyComponent/src/vector_tiles/MapboxVectorTiles.mm +++ b/ios/library/WhirlyGlobe-MaplyComponent/src/vector_tiles/MapboxVectorTiles.mm @@ -4,7 +4,7 @@ * * Created by Jesse Crocker, Trailbehind inc. on 3/31/14. * Recreated by Steve Gifford on 4/10/19. - * Copyright 2011-2019 mousebird consulting + * Copyright 2011-2021 mousebird consulting * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -53,30 +53,19 @@ - (id)initWithID:(MaplyTileID)tileID bbox:(MaplyBoundingBoxD)bbox geoBBox:(Maply - (MaplyTileID) tileID { - MaplyTileID newTileID; - newTileID.x = data->ident.x; - newTileID.y = data->ident.y; - newTileID.level = data->ident.level; - - return newTileID; + return { data->ident.x, data->ident.y, data->ident.level }; } - (MaplyBoundingBoxD)bounds { - MaplyBoundingBoxD ret; - ret.ll.x = data->bbox.ll().x(); ret.ll.y = data->bbox.ll().y(); - ret.ur.x = data->bbox.ur().x(); ret.ur.y = data->bbox.ur().y(); - - return ret; + return { { data->bbox.ll().x(), data->bbox.ll().y() }, + { data->bbox.ur().x(), data->bbox.ur().y() } }; } - (MaplyBoundingBoxD)geoBounds { - MaplyBoundingBoxD ret; - ret.ll.x = data->geoBBox.ll().x(); ret.ll.y = data->geoBBox.ll().y(); - ret.ur.x = data->geoBBox.ur().x(); ret.ur.y = data->geoBBox.ur().y(); - - return ret; + return { { data->geoBBox.ll().x(), data->geoBBox.ll().y() }, + { data->geoBBox.ur().x(), data->geoBBox.ur().y() } }; } - (void)addComponentObject:(MaplyComponentObject *)compObj @@ -99,10 +88,15 @@ - (void)addComponentObjects:(NSArray *)inCompObjs - (NSArray *)componentObjects { NSMutableArray *ret = [[NSMutableArray alloc] init]; - for (auto compObj : data->compObjs) { - ComponentObject_iOSRef compObjIOS = std::dynamic_pointer_cast(compObj); - MaplyComponentObject *newCompObj = [[MaplyComponentObject alloc] initWithRef:compObjIOS]; - [ret addObject:newCompObj]; + for (auto compObj : data->compObjs) + { + if (auto compObjIOS = std::dynamic_pointer_cast(compObj)) + { + if (auto newCompObj = [[MaplyComponentObject alloc] initWithRef:compObjIOS]) + { + [ret addObject:newCompObj]; + } + } } return ret; diff --git a/ios/library/WhirlyGlobe-MaplyComponent/src/vector_tiles/MaplyVectorStyle.mm b/ios/library/WhirlyGlobe-MaplyComponent/src/vector_tiles/MaplyVectorStyle.mm index 43b5d0d225..7a13bac312 100644 --- a/ios/library/WhirlyGlobe-MaplyComponent/src/vector_tiles/MaplyVectorStyle.mm +++ b/ios/library/WhirlyGlobe-MaplyComponent/src/vector_tiles/MaplyVectorStyle.mm @@ -3,7 +3,7 @@ * WhirlyGlobe-MaplyComponent * * Created by Steve Gifford on 1/3/14. - * Copyright 2011-2019 mousebird consulting + * Copyright 2011-2021 mousebird consulting * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -25,6 +25,7 @@ #import "helpers/MaplyTextureBuilder.h" #import "WhirlyGlobe.h" #import "MaplyTexture_private.h" +#import "Dictionary_NSDictionary.h" using namespace WhirlyKit; @@ -39,7 +40,7 @@ - (instancetype)initWithScale:(CGFloat)scale { self = [super init]; - impl = VectorStyleSettingsImplRef(new VectorStyleSettingsImpl(scale)); + impl = std::make_shared(scale); impl->baseDrawPriority = kMaplyVectorDrawPriorityDefault; return self; @@ -257,80 +258,140 @@ - (NSString *)fontName @end -NSArray * _Nonnull AddMaplyVectorsUsingStyle(NSArray * _Nonnull vecObjs,NSObject * _Nonnull styleDelegate,NSObject * _Nonnull viewC,MaplyThreadMode threadMode) +@implementation MaplyVectorStyleReverseWrapper { - MaplyTileID tileID = {0, 0, 0}; - MaplyBoundingBoxD geoBBox; - geoBBox.ll.x = -M_PI; geoBBox.ll.y = -M_PI/2.0; - geoBBox.ur.x = M_PI; geoBBox.ur.y = M_PI/2.0; + WhirlyKit::VectorStyleImplRef vectorStyle; +} + +- (id)initWithCStyle:(WhirlyKit::VectorStyleImplRef)inVectorStyle +{ + self = [super init]; + vectorStyle = inVectorStyle; + + return self; +} + +- (long long) uuid +{ + return vectorStyle->getUuid(NULL); +} + +- (NSString * _Nullable) getCategory +{ + const std::string category = vectorStyle->getCategory(NULL); + if (category.empty()) + return nil; + + return [NSString stringWithUTF8String:category.c_str()]; +} + +- (bool) geomAdditive +{ + return vectorStyle->geomAdditive(NULL); +} + +- (void)buildObjects:(NSArray *_Nonnull)vecObjs + forTile:(MaplyVectorTileData *_Nonnull)tileData + viewC:(NSObject *_Nonnull)viewC + desc:(NSDictionary *_Nullable)desc +{ + std::vector localVecObjs; + for (MaplyVectorObject *vecObj in vecObjs) + localVecObjs.push_back(vecObj->vObj); + + auto lDesc = desc ? iosMutableDictionary(desc) : iosMutableDictionary(); + vectorStyle->buildObjects(nullptr, localVecObjs, tileData->data, &lDesc); +} + +@end + +static MapboxGeometryType ConvertGeomType(MaplyVectorObjectType type) +{ + switch (type) + { + case MaplyVectorPointType: return MapboxGeometryType::GeomTypePoint; + case MaplyVectorLinearType: + case MaplyVectorLinear3dType: return MapboxGeometryType::GeomTypeLineString; + case MaplyVectorArealType: return MapboxGeometryType::GeomTypePolygon; + default: return MapboxGeometryType::GeomTypeUnknown; + } +} + +NSArray * _Nonnull AddMaplyVectorsUsingStyle( + NSArray * _Nonnull vecObjs, + NSObject * _Nonnull styleDelegate, + NSObject * _Nonnull viewC, + MaplyThreadMode threadMode) +{ + return AddMaplyVectorsUsingStyleAndAttributes(vecObjs, styleDelegate, viewC, + /*tileId=*/{0,0,0}, /*enable=*/true, + threadMode, /*desc=*/nil); +} + +NSArray * _Nonnull AddMaplyVectorsUsingStyleAndAttributes( + NSArray * _Nonnull vecObjs, + NSObject * _Nonnull styleDelegate, + NSObject * _Nonnull viewC, + MaplyTileID tileID, + bool enable, + MaplyThreadMode threadMode, + NSDictionary * _Nullable desc) +{ + const MaplyBoundingBoxD geoBBox = { { -M_PI, -M_PI_2 }, { M_PI, M_PI_2 } }; MaplyVectorTileData *tileData = [[MaplyVectorTileData alloc] initWithID:tileID bbox:geoBBox geoBBox:geoBBox]; NSMutableDictionary *featureStyles = [[NSMutableDictionary alloc] init]; - + // First we sort by the styles that each feature uses. int whichLayer = 0; - for (MaplyVectorObject *thisVecObj in vecObjs) + for (const MaplyVectorObject *thisVecObj in vecObjs) { - for (MaplyVectorObject *vecObj in [thisVecObj splitVectors]) + for (const MaplyVectorObject *vecObj in [thisVecObj splitVectors]) { NSString *layer = vecObj.attributes[@"layer"]; if (!layer) layer = vecObj.attributes[@"layer_name"]; if (layer && ![styleDelegate layerShouldDisplay:layer tile:tileData.tileID]) continue; - + if (!layer) layer = [NSString stringWithFormat:@"layer%d",whichLayer]; - + // Need to set a geometry type - MapboxGeometryType geomType = MapboxGeometryType::GeomTypeUnknown; - switch ([vecObj vectorType]) - { - case MaplyVectorPointType: - geomType = MapboxGeometryType::GeomTypePoint; - break; - case MaplyVectorLinearType: - case MaplyVectorLinear3dType: - geomType = MapboxGeometryType::GeomTypeLineString; - break; - case MaplyVectorArealType: - geomType = MapboxGeometryType::GeomTypePolygon; - break; - case MaplyVectorMultiType: - break; - default: - break; - } + const auto geomType = ConvertGeomType([vecObj vectorType]); vecObj.attributes[@"geometry_type"] = @(geomType); - + NSArray *styles = [styleDelegate stylesForFeatureWithAttributes:vecObj.attributes onTile:tileData.tileID inLayer:layer viewC:viewC]; - if (styles.count == 0) - continue; - for (NSObject *style in styles) { NSMutableArray *featuresForStyle = featureStyles[@(style.uuid)]; - if(!featuresForStyle) { + if(!featuresForStyle) + { featuresForStyle = [NSMutableArray new]; featureStyles[@(style.uuid)] = featuresForStyle; } [featuresForStyle addObject:vecObj]; } } - + whichLayer++; } // Then we add each of those styles as a group for efficiency NSArray *symbolizerKeys = [featureStyles.allKeys sortedArrayUsingDescriptors:@[[NSSortDescriptor sortDescriptorWithKey:@"self" ascending:YES]]]; - for(id key in symbolizerKeys) { + for(id key in symbolizerKeys) + { NSObject *symbolizer = [styleDelegate styleForUUID:[key longLongValue] viewC:viewC]; NSArray *features = featureStyles[key]; - [symbolizer buildObjects:features forTile:tileData viewC:viewC]; + [symbolizer buildObjects:features forTile:tileData viewC:viewC desc:desc]; } - - // Turn all the objects on + NSArray *compObjs = [tileData componentObjects]; - [viewC enableObjects:compObjs mode:threadMode]; + + if (enable) + { + // Turn all the objects on + [viewC enableObjects:compObjs mode:threadMode]; + } return compObjs; } @@ -338,8 +399,10 @@ - (NSString *)fontName namespace WhirlyKit { -MapboxVectorStyleSetImpl_iOS::MapboxVectorStyleSetImpl_iOS(Scene *scene,CoordSystem *coordSys,VectorStyleSettingsImplRef settings) -: MapboxVectorStyleSetImpl(scene,coordSys,settings) +MapboxVectorStyleSetImpl_iOS::MapboxVectorStyleSetImpl_iOS(Scene *scene, + CoordSystem *coordSys, + const VectorStyleSettingsImplRef &settings) + : MapboxVectorStyleSetImpl(scene,coordSys,settings) { } @@ -347,7 +410,7 @@ - (NSString *)fontName { } -bool MapboxVectorStyleSetImpl_iOS::parse(PlatformThreadInfo *inst,DictionaryRef dict) +bool MapboxVectorStyleSetImpl_iOS::parse(PlatformThreadInfo *inst,const DictionaryRef &dict) { // Release the textures we're standing on textures.clear(); @@ -364,15 +427,18 @@ - (NSString *)fontName Point2f *circleSize) { // We want the texture a bit bigger than specified - float scale = tileStyleSettings->markerScale * 2; + const float scale = tileStyleSettings->markerScale * 2; // Build an image for the circle - float buffer = 1.0; - float radius = inRadius*scale; - float strokeWidth = inStrokeWidth*scale; - float size = ceil(buffer + radius + strokeWidth)*2; - circleSize->x() = size / 2; - circleSize->y() = size / 2; + const float buffer = 1.0; + const float radius = inRadius*scale; + const float strokeWidth = inStrokeWidth*scale; + const float size = ceil(buffer + radius + strokeWidth)*2; + if (circleSize) + { + circleSize->x() = size / 2; + circleSize->y() = size / 2; + } UIGraphicsBeginImageContext(CGSizeMake(size, size)); // TODO: Use the opacity [[UIColor clearColor] setFill]; @@ -461,6 +527,11 @@ - (NSString *)fontName if (!font) { font = [UIFont fontWithName:[NSString stringWithFormat:@"%@-%@%@",[components objectAtIndex:0],[components objectAtIndex:1],[components objectAtIndex:2]] size:fontSize]; } + + // And try an even stupider construction + if (!font) { + font = [UIFont fontWithName:[NSString stringWithFormat:@"%@-%@_%@-%@",[components objectAtIndex:0],[components objectAtIndex:2],[components objectAtIndex:1],[components objectAtIndex:2]] size:fontSize]; + } } break; default: @@ -485,41 +556,41 @@ - (NSString *)fontName font = [UIFont systemFontOfSize:fontSize]; NSLog(@"Failed to find font %s",fontNames[0].c_str()); } - - LabelInfoRef labelInfo(new LabelInfo_iOS(font,true)); + + auto labelInfo = std::make_shared(font,/*screenObject=*/true); labelInfo->programID = screenMarkerProgramID; - + return labelInfo; } SingleLabelRef MapboxVectorStyleSetImpl_iOS::makeSingleLabel(PlatformThreadInfo *inst,const std::string &text) { - NSString *textStr = [NSString stringWithUTF8String:text.c_str()]; - - SingleLabel_iOS *label = new SingleLabel_iOS(); - label->text = textStr; - - return SingleLabelRef(label); + return std::make_shared([NSString stringWithUTF8String:text.c_str()]); } -ComponentObjectRef MapboxVectorStyleSetImpl_iOS::makeComponentObject(PlatformThreadInfo *inst) +ComponentObjectRef MapboxVectorStyleSetImpl_iOS::makeComponentObject(PlatformThreadInfo *inst, const Dictionary *_Nullable desc) { - return ComponentObjectRef(new ComponentObject_iOS()); + NSDictionary *nsDesc = nil; + if (desc && !desc->empty()) + { + nsDesc = [NSMutableDictionary fromDictionaryCPointer:desc]; + } + return std::make_shared(/*enabled=*/false, /*isSelectable=*/false, nsDesc); } -void MapboxVectorStyleSetImpl_iOS::addSelectionObject(SimpleIdentity selectID,VectorObjectRef vecObj,ComponentObjectRef compObj) +void MapboxVectorStyleSetImpl_iOS::addSelectionObject(SimpleIdentity selectID,const VectorObjectRef &vecObj,const ComponentObjectRef &compObj) { - if (!compManage) - return; - - ComponentManager_iOS *compManage_iOS = (ComponentManager_iOS *)compManage; - - MaplyVectorObject *vectorObj = [[MaplyVectorObject alloc] initWithRef:vecObj]; - compManage_iOS->addSelectObject(selectID, vectorObj); + if (auto compManage_iOS = dynamic_cast(compManage.get())) + { + if (MaplyVectorObject *vectorObj = [[MaplyVectorObject alloc] initWithRef:vecObj]) + { + compManage_iOS->addSelectObject(selectID, vectorObj); + } + } } -double MapboxVectorStyleSetImpl_iOS::calculateTextWidth(PlatformThreadInfo *inst,LabelInfoRef inLabelInfo,const std::string &testStr) +double MapboxVectorStyleSetImpl_iOS::calculateTextWidth(PlatformThreadInfo *inst,const LabelInfoRef &inLabelInfo,const std::string &testStr) { LabelInfo_iOSRef labelInfo = std::dynamic_pointer_cast(inLabelInfo); if (!labelInfo) @@ -534,7 +605,7 @@ - (NSString *)fontName void MapboxVectorStyleSetImpl_iOS::addSprites(MapboxVectorStyleSpritesRef newSprites,MaplyTexture *tex) { textures.push_back(tex); - MapboxVectorStyleSetImpl::addSprites(newSprites); + MapboxVectorStyleSetImpl::addSprites(std::move(newSprites)); } VectorStyleDelegateWrapper::VectorStyleDelegateWrapper(NSObject *viewC,NSObject *delegate) @@ -548,7 +619,7 @@ - (NSString *)fontName const QuadTreeIdentifier &tileID, const std::string &layerName) { - NSDictionary *dict = nil; + const NSDictionary *dict = nil; if (const auto dictRef = dynamic_cast(&attrs)) { dict = dictRef->dict; } else if (const auto dictRef = dynamic_cast(&attrs)) { @@ -560,10 +631,12 @@ - (NSString *)fontName return std::vector(); } - MaplyTileID theTileID; - theTileID.x = tileID.x; theTileID.y = tileID.y; theTileID.level = tileID.level; + const MaplyTileID theTileID = { tileID.x, tileID.y, tileID.level }; NSString *layerStr = [NSString stringWithFormat:@"%s",layerName.c_str()]; - NSArray *styles = [delegate stylesForFeatureWithAttributes:dict onTile:theTileID inLayer:layerStr viewC:viewC]; + NSArray *styles = [delegate stylesForFeatureWithAttributes:const_cast(dict) + onTile:theTileID + inLayer:layerStr + viewC:viewC]; std::vector retStyles; retStyles.reserve([styles count]); @@ -578,8 +651,7 @@ - (NSString *)fontName const std::string &name, const QuadTreeNew::Node &tileID) { - MaplyTileID theTileID; - theTileID.x = tileID.x; theTileID.y = tileID.y; theTileID.level = tileID.level; + const MaplyTileID theTileID = { tileID.x, tileID.y, tileID.level }; NSString *layerStr = [NSString stringWithFormat:@"%s",name.c_str()]; return [delegate layerShouldDisplay:layerStr tile:theTileID]; } @@ -587,7 +659,7 @@ - (NSString *)fontName VectorStyleImplRef VectorStyleDelegateWrapper::styleForUUID(PlatformThreadInfo *inst,long long uuid) { NSObject *style = [delegate styleForUUID:uuid viewC:viewC]; - return VectorStyleImplRef(new VectorStyleWrapper(viewC,style)); + return std::make_shared(viewC,style); } std::vector VectorStyleDelegateWrapper::allStyles(PlatformThreadInfo *inst) @@ -596,8 +668,7 @@ - (NSString *)fontName std::vector retStyles; for (NSObject *style : styles) { - VectorStyleWrapperRef wrap(new VectorStyleWrapper(viewC,style)); - retStyles.push_back(wrap); + retStyles.push_back(std::make_shared(viewC,style)); } return retStyles; @@ -611,7 +682,7 @@ - (NSString *)fontName RGBAColorRef VectorStyleDelegateWrapper::backgroundColor(PlatformThreadInfo *inst,double zoom) { - return RGBAColorRef(new RGBAColor(RGBAColor::black())); + return std::make_shared(RGBAColor::black()); } VectorStyleWrapper::VectorStyleWrapper(NSObject *viewC,NSObject *style) @@ -626,11 +697,8 @@ - (NSString *)fontName std::string VectorStyleWrapper::getCategory(PlatformThreadInfo *inst) { - std::string category; NSString *catStr = [style getCategory]; - category = [catStr cStringUsingEncoding:NSUTF8StringEncoding]; - - return category; + return [catStr cStringUsingEncoding:NSUTF8StringEncoding]; } bool VectorStyleWrapper::geomAdditive(PlatformThreadInfo *inst) @@ -639,20 +707,35 @@ - (NSString *)fontName } void VectorStyleWrapper::buildObjects(PlatformThreadInfo *inst, - std::vector &vecObjs, - VectorTileDataRef tileInfo) + const std::vector &vecObjs, + const VectorTileDataRef &tileInfo, + const Dictionary *desc) { - MaplyVectorTileData *tileData = [[MaplyVectorTileData alloc] init]; - tileData->data = tileInfo; - - NSMutableArray *vecArray = [NSMutableArray array]; - for (VectorObjectRef vecObj: vecObjs) { - MaplyVectorObject *mVecObj = [[MaplyVectorObject alloc] init]; - mVecObj->vObj = vecObj; - [vecArray addObject:mVecObj]; + if (auto tileData = [[MaplyVectorTileData alloc] init]) + { + tileData->data = tileInfo; + + NSMutableArray *vecArray = [NSMutableArray array]; + for (VectorObjectRef vecObj: vecObjs) + { + if (auto mVecObj = [[MaplyVectorObject alloc] init]) + { + mVecObj->vObj = vecObj; + [vecArray addObject:mVecObj]; + } + } + + NSDictionary* nsDesc = nil; + if (auto iosDesc = dynamic_cast(desc)) + { + nsDesc = iosDesc->dict; + } + else if (desc) + { + nsDesc = [NSMutableDictionary fromDictionaryCPointer:desc]; + } + [style buildObjects:vecArray forTile:tileData viewC:viewC desc:nsDesc]; } - - [style buildObjects:vecArray forTile:tileData viewC:viewC]; } } diff --git a/ios/library/WhirlyGlobe-MaplyComponent/src/vector_tiles/MaplyVectorStyleSimple.m b/ios/library/WhirlyGlobe-MaplyComponent/src/vector_tiles/MaplyVectorStyleSimple.m index ab1ed44bff..c48a1d7916 100644 --- a/ios/library/WhirlyGlobe-MaplyComponent/src/vector_tiles/MaplyVectorStyleSimple.m +++ b/ios/library/WhirlyGlobe-MaplyComponent/src/vector_tiles/MaplyVectorStyleSimple.m @@ -8,6 +8,7 @@ #import "vector_styles/MaplyVectorStyleSimple.h" #import "visual_objects/MaplyScreenLabel.h" +#import "NSDictionary+Stuff.h" @implementation MaplyVectorStyleSimpleGenerator { @@ -122,7 +123,10 @@ - (NSString *)getCategory return nil; } -- (void)buildObjects:(NSArray * _Nonnull)vecObjs forTile:(MaplyVectorTileData *)tileInfo viewC:(NSObject * _Nonnull)viewC +- (void)buildObjects:(NSArray * _Nonnull)vecObjs + forTile:(MaplyVectorTileData *)tileInfo + viewC:(NSObject * _Nonnull)viewC + desc:(NSDictionary * _Nullable)desc { } @@ -137,26 +141,37 @@ - (id)initWithGen:(MaplyVectorStyleSimpleGenerator *)gen viewC:(NSObject * _Nonnull)viewC +- (void)buildObjects:(NSArray * _Nonnull)vecObjs + forTile:(MaplyVectorTileData *)tileInfo + viewC:(NSObject * _Nonnull)viewC + desc:(NSDictionary * _Nullable)extraDesc { NSMutableArray *tessObjs = [NSMutableArray array]; for (MaplyVectorObject *vecObj in vecObjs) { MaplyVectorObject *tessObj = [vecObj tesselate]; if (tessObj) + { [tessObjs addObject:tessObj]; + } } - - MaplyComponentObject *compObj = [super.viewC addVectors:tessObjs desc:@{kMaplyColor: _color, - kMaplyFilled: @(YES), - kMaplyDrawPriority: @(self.drawPriority) - } mode:MaplyThreadCurrent]; + + NSDictionary *desc = @{ + kMaplyColor: _color, + kMaplyFilled: @(YES), + kMaplyDrawPriority: @(self.drawPriority) + }; + desc = [desc dictionaryByMergingWith:extraDesc]; + + MaplyComponentObject *compObj = [super.viewC addVectors:tessObjs desc:desc mode:MaplyThreadCurrent]; if (compObj) + { [tileInfo addComponentObject:compObj]; + } } @end @@ -171,7 +186,10 @@ - (id)initWithGen:(MaplyVectorStyleSimpleGenerator *)gen viewC:(NSObject * _Nonnull)viewC +- (void)buildObjects:(NSArray * _Nonnull)vecObjs + forTile:(MaplyVectorTileData *)tileInfo + viewC:(NSObject * _Nonnull)viewC + desc:(NSDictionary * _Nullable)extraDesc { NSMutableArray *labels = [NSMutableArray array]; @@ -194,8 +212,14 @@ - (void)buildObjects:(NSArray * _Nonnull)vecObjs forTile:(MaplyVectorTileData *) if (labels.count == 0) return; - MaplyComponentObject *compObj = [self.viewC addScreenLabels:labels desc:@{kMaplyTextColor: [UIColor blackColor], - kMaplyFont: _font} + NSDictionary *desc = @{ + kMaplyTextColor: [UIColor blackColor], + kMaplyFont: _font + }; + desc = [desc dictionaryByMergingWith:extraDesc]; + + MaplyComponentObject *compObj = [self.viewC addScreenLabels:labels + desc:desc mode:MaplyThreadCurrent]; if (compObj) @@ -217,16 +241,24 @@ - (id)initWithGen:(MaplyVectorStyleSimpleGenerator *)gen viewC:(NSObject * _Nonnull)viewC +- (void)buildObjects:(NSArray * _Nonnull)vecObjs + forTile:(MaplyVectorTileData *)tileInfo + viewC:(NSObject * _Nonnull)viewC + desc:(NSDictionary * _Nullable)extraDesc { - MaplyComponentObject *compObj = [super.viewC addVectors:vecObjs desc:@{kMaplyColor: _color, - kMaplyDrawPriority: @(self.drawPriority), - kMaplyFilled: @(NO), - kMaplyVecWidth: @(4.0) - } mode:MaplyThreadCurrent]; - + NSDictionary *desc = @{ + kMaplyColor: _color, + kMaplyDrawPriority: @(self.drawPriority), + kMaplyFilled: @(NO), + kMaplyVecWidth: @(4.0) + }; + desc = [desc dictionaryByMergingWith:extraDesc]; + + MaplyComponentObject *compObj = [super.viewC addVectors:vecObjs desc:desc mode:MaplyThreadCurrent]; if (compObj) + { [tileInfo addComponentObject:compObj]; + } } diff --git a/ios/library/WhirlyGlobe-MaplyComponent/src/vector_tiles/MaplyVectorTileLineStyle.mm b/ios/library/WhirlyGlobe-MaplyComponent/src/vector_tiles/MaplyVectorTileLineStyle.mm index 64adf539f8..dd81e08f8b 100644 --- a/ios/library/WhirlyGlobe-MaplyComponent/src/vector_tiles/MaplyVectorTileLineStyle.mm +++ b/ios/library/WhirlyGlobe-MaplyComponent/src/vector_tiles/MaplyVectorTileLineStyle.mm @@ -24,6 +24,7 @@ #import "helpers/MaplyTextureBuilder.h" #import #import "vector_tiles/MapboxVectorTiles.h" +#import "NSDictionary+Stuff.h" // Line styles @implementation MaplyVectorTileStyleLine @@ -152,30 +153,50 @@ - (instancetype)initWithStyleEntry:(NSDictionary *)style settings:(MaplyVectorSt return self; } -- (void)buildObjects:(NSArray *)vecObjs forTile:(MaplyVectorTileData *)tileInfo viewC:(NSObject *)viewC; +- (void)buildObjects:(NSArray *)vecObjs + forTile:(MaplyVectorTileData *)tileInfo + viewC:(NSObject *)viewC + desc:(NSDictionary * _Nullable)extraDesc { MaplyComponentObject *baseWideObj = nil; MaplyComponentObject *baseRegObj = nil; NSMutableArray *compObjs = [NSMutableArray array]; int which = 0; - for (NSDictionary *desc in subStyles) + for (__strong NSDictionary *desc in subStyles) { + if (extraDesc) + { + desc = [desc dictionaryByMergingWith:extraDesc]; + } + MaplyComponentObject *compObj = nil; if (wideVecs[which]) { - if (!baseWideObj) { + if (!baseWideObj) + { baseWideObj = compObj = [viewC addWideVectors:vecObjs desc:desc mode:MaplyThreadCurrent]; - } else + } + else + { compObj = [viewC instanceVectors:baseWideObj desc:desc mode:MaplyThreadCurrent]; - } else { - if (!baseRegObj) { + } + } + else + { + if (!baseRegObj) + { baseRegObj = compObj = [viewC addVectors:vecObjs desc:desc mode:MaplyThreadCurrent]; - } else + } + else + { compObj = [viewC instanceVectors:baseRegObj desc:desc mode:MaplyThreadCurrent]; + } } if (compObj) + { [compObjs addObject:compObj]; + } which++; } diff --git a/ios/library/WhirlyGlobe-MaplyComponent/src/vector_tiles/MaplyVectorTileMarkerStyle.mm b/ios/library/WhirlyGlobe-MaplyComponent/src/vector_tiles/MaplyVectorTileMarkerStyle.mm index fe03213090..d6370e94c9 100644 --- a/ios/library/WhirlyGlobe-MaplyComponent/src/vector_tiles/MaplyVectorTileMarkerStyle.mm +++ b/ios/library/WhirlyGlobe-MaplyComponent/src/vector_tiles/MaplyVectorTileMarkerStyle.mm @@ -21,6 +21,7 @@ #import "vector_styles/MaplyVectorTileMarkerStyle.h" #import "helpers/MaplyIconManager.h" #import "vector_tiles/MapboxVectorTiles.h" +#import "NSDictionary+Stuff.h" @interface MaplyVectorTileSubStyleMarker : NSObject { @@ -136,9 +137,12 @@ - (instancetype)initWithStyleEntry:(NSDictionary *)styles settings:(MaplyVectorS return self; } -- (void)buildObjects:(NSArray *)vecObjs forTile:(MaplyVectorTileData *)tileInfo viewC:(NSObject *)viewC; +- (void)buildObjects:(NSArray *)vecObjs + forTile:(MaplyVectorTileData *)tileInfo + viewC:(NSObject *)viewC + desc:(NSDictionary * _Nullable)extraDesc { - bool isRetina = [UIScreen mainScreen].scale > 1.0; + const bool isRetina = [UIScreen mainScreen].scale > 1.0; // One marker per object NSMutableArray *compObjs = [NSMutableArray array]; @@ -151,10 +155,13 @@ - (void)buildObjects:(NSArray *)vecObjs forTile:(MaplyVectorTileData *)tileInfo marker.userObject = vec; marker.selectable = self.selectable; if(subStyle->markerImage) + { marker.image = subStyle->markerImage; - else { + } + else + { NSString *markerName = [self formatText:subStyle->markerImageTemplate forObject:vec]; - marker.image = [MaplySimpleStyleManager iconForName:markerName + marker.image = [MaplySimpleStyleManager iconForName:markerName size:CGSizeMake(settings.markerScale*subStyle->width+2, settings.markerScale*subStyle->height+2) color:[UIColor blackColor] @@ -162,31 +169,41 @@ - (void)buildObjects:(NSArray *)vecObjs forTile:(MaplyVectorTileData *)tileInfo strokeSize:settings.markerScale*subStyle->strokeWidth strokeColor:subStyle->strokeColor]; if ([marker.image isKindOfClass:[NSNull class]]) + { marker.image = nil; + } } - if (marker.image) { + if (marker.image) + { marker.loc = [vec center]; - if (subStyle->allowOverlap) - marker.layoutImportance = MAXFLOAT; - else - marker.layoutImportance = settings.markerImportance; + marker.layoutImportance = (subStyle->allowOverlap) ? MAXFLOAT : settings.markerImportance; marker.selectable = true; if (marker.image) { - marker.size = CGSizeMake(settings.markerScale*subStyle->width, settings.markerScale*subStyle->height); // The markers will be scaled up on a retina display, so compensate - if (isRetina) - marker.size = CGSizeMake(settings.markerScale*subStyle->width/2.0, settings.markerScale*subStyle->height/2.0); - } else + marker.size = isRetina ? + CGSizeMake(settings.markerScale*subStyle->width/2.0, settings.markerScale*subStyle->height/2.0) : + CGSizeMake(settings.markerScale*subStyle->width, settings.markerScale*subStyle->height); + } + else + { marker.size = CGSizeMake(settings.markerScale*subStyle->width, settings.markerScale*subStyle->height); + } [markers addObject:marker]; } } - MaplyComponentObject *compObj = [viewC addScreenMarkers:markers desc:subStyle->desc mode:MaplyThreadCurrent]; + NSDictionary* desc = subStyle->desc ? subStyle->desc : extraDesc; + if (subStyle->desc && extraDesc) + { + [desc dictionaryByMergingWith:extraDesc]; + } + MaplyComponentObject *compObj = [viewC addScreenMarkers:markers desc:desc mode:MaplyThreadCurrent]; if (compObj) + { [compObjs addObject:compObj]; + } } [tileInfo addComponentObjects:compObjs]; diff --git a/ios/library/WhirlyGlobe-MaplyComponent/src/vector_tiles/MaplyVectorTilePolygonStyle.mm b/ios/library/WhirlyGlobe-MaplyComponent/src/vector_tiles/MaplyVectorTilePolygonStyle.mm index d53bae214f..f90259847b 100644 --- a/ios/library/WhirlyGlobe-MaplyComponent/src/vector_tiles/MaplyVectorTilePolygonStyle.mm +++ b/ios/library/WhirlyGlobe-MaplyComponent/src/vector_tiles/MaplyVectorTilePolygonStyle.mm @@ -21,6 +21,7 @@ #import "control/WhirlyGlobeViewController.h" #import "vector_styles/MaplyVectorTilePolygonStyle.h" #import "vector_tiles/MapboxVectorTiles.h" +#import "NSDictionary+Stuff.h" // Filled polygons styles @implementation MaplyVectorTileStylePolygon @@ -98,18 +99,26 @@ - (instancetype)initWithStyleEntry:(NSDictionary *)styles settings:(MaplyVectorS return self; } -- (void)buildObjects:(NSArray *)vecObjs forTile:(MaplyVectorTileData *)tileData viewC:(NSObject *)viewC; +- (void)buildObjects:(NSArray *)vecObjs + forTile:(MaplyVectorTileData *)tileData + viewC:(NSObject *)viewC + desc:(NSDictionary * _Nullable)extraDesc { MaplyComponentObject *baseObj = nil; NSMutableArray *compObjs = [NSMutableArray array]; - - float ClipGridSize = 2.0/180.0*M_PI; - - for (NSDictionary *desc in subStyles) + + const float ClipGridSize = 2.0/180.0*M_PI; + + for (__strong NSDictionary *desc in subStyles) { MaplyComponentObject *compObj = nil; if (!baseObj) { + if (extraDesc) + { + desc = [desc dictionaryByMergingWith:extraDesc]; + } + // Tesselate everything here, rather than tying up the layer thread NSMutableArray *tessObjs = [NSMutableArray array]; for (MaplyVectorObject *vec in vecObjs) @@ -127,15 +136,21 @@ - (void)buildObjects:(NSArray *)vecObjs forTile:(MaplyVectorTileData *)tileData tessVec = [vec tesselate]; if (tessVec) + { [tessObjs addObject:tessVec]; + } } baseObj = compObj = [viewC addVectors:tessObjs desc:desc mode:MaplyThreadCurrent]; - } else { + } + else + { compObj = [viewC instanceVectors:baseObj desc:desc mode:MaplyThreadCurrent]; } if (compObj) + { [compObjs addObject:compObj]; + } } [tileData addComponentObjects:compObjs]; diff --git a/ios/library/WhirlyGlobe-MaplyComponent/src/vector_tiles/MaplyVectorTileStyle.mm b/ios/library/WhirlyGlobe-MaplyComponent/src/vector_tiles/MaplyVectorTileStyle.mm index 772e8453ca..738687f6d4 100644 --- a/ios/library/WhirlyGlobe-MaplyComponent/src/vector_tiles/MaplyVectorTileStyle.mm +++ b/ios/library/WhirlyGlobe-MaplyComponent/src/vector_tiles/MaplyVectorTileStyle.mm @@ -147,7 +147,10 @@ - (void)resolveVisibility:(NSDictionary *)styleEntry settings:(MaplyVectorStyleS } } -- (void)buildObjects:(NSArray *)vecObjs forTile:(MaplyVectorTileData *)tileInfo viewC:(NSObject *)viewC; +- (void)buildObjects:(NSArray *)vecObjs + forTile:(MaplyVectorTileData *)tileInfo + viewC:(NSObject *)viewC + desc:(NSDictionary * _Nullable)extraDesc { } diff --git a/ios/library/WhirlyGlobe-MaplyComponent/src/vector_tiles/MaplyVectorTileTextStyle.mm b/ios/library/WhirlyGlobe-MaplyComponent/src/vector_tiles/MaplyVectorTileTextStyle.mm index ca05e7081a..8a99d6133d 100644 --- a/ios/library/WhirlyGlobe-MaplyComponent/src/vector_tiles/MaplyVectorTileTextStyle.mm +++ b/ios/library/WhirlyGlobe-MaplyComponent/src/vector_tiles/MaplyVectorTileTextStyle.mm @@ -21,6 +21,7 @@ #import "vector_styles/MaplyVectorTileTextStyle.h" #import "visual_objects/MaplyScreenLabel.h" #import "vector_tiles/MapboxVectorTiles.h" +#import "NSDictionary+Stuff.h" typedef enum { TextPlacementPoint, @@ -248,10 +249,13 @@ - (instancetype)initWithStyleEntry:(NSDictionary *)styles settings:(MaplyVectorS return self; } -- (void)buildObjects:(NSArray *)vecObjs forTile:(MaplyVectorTileData *)tileInfo viewC:(NSObject *)viewC; +- (void)buildObjects:(NSArray *)vecObjs + forTile:(MaplyVectorTileData *)tileInfo + viewC:(NSObject *)viewC + desc:(NSDictionary * _Nullable)extraDesc { MaplyCoordinateSystem *displaySystem = viewC.coordSystem; - + NSMutableArray *compObjs = [NSMutableArray array]; for (MaplyVectorTileSubStyleText *subStyle in subStyles) { @@ -284,31 +288,38 @@ - (void)buildObjects:(NSArray *)vecObjs forTile:(MaplyVectorTileData *)tileInfo { MaplyCoordinate center = [vec center]; label.loc = center; - } else if (subStyle->placement == TextPlacementLine) + } + else if (subStyle->placement == TextPlacementLine) { MaplyCoordinate middle; double rot; if ([vec linearMiddle:&middle rot:&rot displayCoordSys:displaySystem]) { //TODO: text-max-char-angle-delta - //TODO: rotation calculation is not ideal, it is between 2 points, but it needs to be avergared over a longer distance + //TODO: rotation calculation is not ideal, it is between 2 points, but it needs to be averaged over a longer distance label.loc = middle; label.layoutPlacement = kMaplyLayoutCenter; label.rotation = -1 * rot+M_PI/2.0; - if(label.rotation > M_PI_2 || label.rotation < -M_PI_2) { + if(label.rotation > M_PI_2 || label.rotation < -M_PI_2) + { label.rotation += M_PI; } label.keepUpright = true; - } else { + } else + { label = nil; } - } else if(subStyle->placement == TextPlacementVertex) + } + else if(subStyle->placement == TextPlacementVertex) { MaplyCoordinate vertex; - if([vec middleCoordinate:&vertex]) { + if([vec middleCoordinate:&vertex]) + { label.loc = vertex; - } else { + } + else + { label = nil; } } @@ -322,12 +333,22 @@ - (void)buildObjects:(NSArray *)vecObjs forTile:(MaplyVectorTileData *)tileInfo } } if (subStyle->layoutPlacement) + { label.layoutPlacement = [subStyle->layoutPlacement intValue]; + } } - MaplyComponentObject *compObj = [viewC addScreenLabels:labels desc:subStyle->desc mode:MaplyThreadCurrent]; + NSDictionary *desc = subStyle->desc ? subStyle->desc : extraDesc; + if (subStyle->desc && extraDesc) + { + [desc dictionaryByMergingWith:extraDesc]; + } + + MaplyComponentObject *compObj = [viewC addScreenLabels:labels desc:desc mode:MaplyThreadCurrent]; if (compObj) + { [compObjs addObject:compObj]; + } } [tileInfo addComponentObjects:compObjs]; diff --git a/ios/library/WhirlyGlobe-MaplyComponent/src/visual_objects/MaplyComponentObject.mm b/ios/library/WhirlyGlobe-MaplyComponent/src/visual_objects/MaplyComponentObject.mm index 4b9cc718ba..c01f71381d 100644 --- a/ios/library/WhirlyGlobe-MaplyComponent/src/visual_objects/MaplyComponentObject.mm +++ b/ios/library/WhirlyGlobe-MaplyComponent/src/visual_objects/MaplyComponentObject.mm @@ -27,10 +27,7 @@ @implementation MaplyComponentObject - (instancetype)init { self = [super init]; - contents = ComponentObject_iOSRef(new ComponentObject_iOS()); - contents->isSelectable = true; - contents->enable = true; - + contents = std::make_shared(/*enable=*/true, /*isSelected=*/true, /*desc=*/nil); return self; } @@ -38,21 +35,18 @@ - (id)initWithRef:(WhirlyKit::ComponentObject_iOSRef)compObj { self = [super init]; contents = compObj; - return self; } - (instancetype)initWithDesc:(NSDictionary *)desc { self = [super init]; - contents = ComponentObject_iOSRef(new ComponentObject_iOS()); - contents->isSelectable = true; - contents->enable = true; - id enable = desc[kMaplyEnable]; - if (enable) - contents->enable = [enable boolValue]; - + contents = std::make_shared(/*enable=*/true, /*isSelected=*/true, /*desc=*/desc); return self; } +- (NSString *__nullable)getUUID { + return contents->uuid.empty() ? nil : [NSString stringWithUTF8String:contents->uuid.c_str()]; +} + @end diff --git a/ios/library/WhirlyGlobe-MaplyComponent/src/visual_objects/MaplyGeomModel.mm b/ios/library/WhirlyGlobe-MaplyComponent/src/visual_objects/MaplyGeomModel.mm index e0f2927604..01cad31372 100644 --- a/ios/library/WhirlyGlobe-MaplyComponent/src/visual_objects/MaplyGeomModel.mm +++ b/ios/library/WhirlyGlobe-MaplyComponent/src/visual_objects/MaplyGeomModel.mm @@ -91,7 +91,7 @@ - (void)asRawGeometry:(std::vector &)outRawGeom withTexM } // Return the ID for or generate a base model in the Geometry Manager -- (WhirlyKit::SimpleIdentity)getBaseModel:(MaplyBaseInteractionLayer *)inLayer fontTexManager:(WhirlyKit::FontTextureManager_iOS *)fontTexManager compObj:(MaplyComponentObject *)compObj mode:(MaplyThreadMode)threadMode +- (WhirlyKit::SimpleIdentity)getBaseModel:(MaplyBaseInteractionLayer *)inLayer fontTexManager:(const WhirlyKit::FontTextureManager_iOSRef &)fontTexManager compObj:(MaplyComponentObject *)compObj mode:(MaplyThreadMode)threadMode { @synchronized(self) { @@ -108,7 +108,7 @@ - (void)asRawGeometry:(std::vector &)outRawGeom withTexM if (shape) { - ShapeManager *shapeManager = (ShapeManager *)layer->scene->getManager(kWKShapeManager); + ShapeManagerRef shapeManager = std::dynamic_pointer_cast(inLayer->scene->getManager(kWKShapeManager)); WhirlyKit::Shape *wkShape = nil; if ([shape isKindOfClass:[MaplyShapeCircle class]]) @@ -130,7 +130,7 @@ - (void)asRawGeometry:(std::vector &)outRawGeom withTexM int whichTex = 0; for (const std::string &texFileName : texFileNames) { - MaplyTexture *tex = [layer addImage:[UIImage imageNamed:[NSString stringWithFormat:@"%s",texFileName.c_str()]] imageFormat:MaplyImage4Layer8Bit mode:threadMode]; + MaplyTexture *tex = [inLayer addImage:[UIImage imageNamed:[NSString stringWithFormat:@"%s",texFileName.c_str()]] imageFormat:MaplyImage4Layer8Bit mode:threadMode]; if (tex) { maplyTextures.insert(tex); @@ -208,12 +208,12 @@ - (void)asRawGeometry:(std::vector &)outRawGeom withTexM for (auto &it : stringGeom) procGeom.push_back(it.second); - GeometryManager *geomManager = (GeometryManager *)layer->scene->getManager(kWKGeometryManager); + GeometryManagerRef geomManager = std::dynamic_pointer_cast(inLayer->scene->getManager(kWKGeometryManager)); GeometryInfo geomInfo; baseModelID = geomManager->addBaseGeometry(procGeom, geomInfo, changes); // Need to flush these changes immediately - layer->scene->addChangeRequests(changes); + inLayer->scene->addChangeRequests(changes); return baseModelID; } diff --git a/ios/library/WhirlyGlobe-MaplyComponent/src/visual_objects/MaplyParticleSystem.mm b/ios/library/WhirlyGlobe-MaplyComponent/src/visual_objects/MaplyParticleSystem.mm index e25b44bf11..7c23c76a5b 100644 --- a/ios/library/WhirlyGlobe-MaplyComponent/src/visual_objects/MaplyParticleSystem.mm +++ b/ios/library/WhirlyGlobe-MaplyComponent/src/visual_objects/MaplyParticleSystem.mm @@ -74,7 +74,7 @@ - (instancetype)initWithName:(NSString *)name viewC:(NSObjectscene->getCurrentTime(); _renderTargetID = EmptyIdentity; _numRegAttrs = 0; @@ -132,7 +132,8 @@ - (id) initWithParticleSystem:(MaplyParticleSystem *)partSys - (bool) addAttribute:(NSString *)attrName values:(NSData *)data { // Look for the name - for (auto attr : _partSys.attrs) + const auto __strong ps = _partSys; + for (auto attr : ps.attrs) { if ([attrName isEqualToString:attr.name]) { @@ -140,7 +141,7 @@ - (bool) addAttribute:(NSString *)attrName values:(NSData *)data WhirlyKit::ParticleSystemAttrVals attrVals; attrVals.attrID = attr.getId(); attrVals.data = data; - if ([data length] != attr.dataSize() * _partSys.batchSize) + if ([data length] != attr.dataSize() * ps.batchSize) return false; self.attrVals.push_back(attrVals); diff --git a/ios/library/WhirlyGlobe-MaplyComponent/src/visual_objects/MaplyStarsModel.mm b/ios/library/WhirlyGlobe-MaplyComponent/src/visual_objects/MaplyStarsModel.mm index 6875de2e68..0dc5260772 100644 --- a/ios/library/WhirlyGlobe-MaplyComponent/src/visual_objects/MaplyStarsModel.mm +++ b/ios/library/WhirlyGlobe-MaplyComponent/src/visual_objects/MaplyStarsModel.mm @@ -140,10 +140,10 @@ - (void)addToViewC:(WhirlyGlobeViewController *)inViewC date:(NSDate *)date desc MaplyTexture *starTex = nil; if (image) - starTex = [viewC addTexture:image imageFormat:MaplyImageIntRGBA wrapFlags:0 mode:MaplyThreadCurrent]; + starTex = [inViewC addTexture:image imageFormat:MaplyImageIntRGBA wrapFlags:0 mode:MaplyThreadCurrent]; // Set up a simple particle system (that doesn't move) - partSys = [[MaplyParticleSystem alloc] initWithName:@"Stars" viewC:viewC]; + partSys = [[MaplyParticleSystem alloc] initWithName:@"Stars" viewC:inViewC]; partSys.type = MaplyParticleSystemTypePoint; partSys.lifetime = 1e20; partSys.totalParticles = (int)stars.size(); @@ -154,7 +154,7 @@ - (void)addToViewC:(WhirlyGlobeViewController *)inViewC date:(NSDate *)date desc [partSys addTexture:starTex]; [partSys addAttribute:@"a_position" type:MaplyShaderAttrTypeFloat3]; [partSys addAttribute:@"a_size" type:MaplyShaderAttrTypeFloat]; - partSysObj = [viewC addParticleSystem:partSys desc:desc mode:mode]; + partSysObj = [inViewC addParticleSystem:partSys desc:desc mode:mode]; // Data arrays for particles // We'll clear them out in case we don't fill them out completely @@ -194,10 +194,10 @@ - (void)addToViewC:(WhirlyGlobeViewController *)inViewC date:(NSDate *)date desc // Set up the particle batch MaplyParticleBatch *batch = [[MaplyParticleBatch alloc] initWithParticleSystem:partSys]; - batch.time = viewC->renderControl->scene->getCurrentTime(); + batch.time = inViewC->renderControl->scene->getCurrentTime(); [batch addAttribute:@"a_position" values:posData]; [batch addAttribute:@"a_size" values:sizeData]; - [viewC addParticleBatch:batch mode:mode]; + [inViewC addParticleBatch:batch mode:mode]; } - (void)removeFromViewC diff --git a/ios/library/WhirlyGlobe-MaplyComponent/src/visual_objects/MaplyTexture.mm b/ios/library/WhirlyGlobe-MaplyComponent/src/visual_objects/MaplyTexture.mm index 286f67a672..cf87f2df12 100644 --- a/ios/library/WhirlyGlobe-MaplyComponent/src/visual_objects/MaplyTexture.mm +++ b/ios/library/WhirlyGlobe-MaplyComponent/src/visual_objects/MaplyTexture.mm @@ -44,9 +44,10 @@ - (void)clear if (_texID != EmptyIdentity) { // NSLog(@"Clearing texture %lx, for interactLayer %lx",(long)self,(long)_interactLayer); - if (auto wr = WorkRegion(_interactLayer)) + auto const __strong layer = _interactLayer; + if (auto wr = WorkRegion(layer)) { - [_interactLayer clearTexture:self when:0.0]; + [layer clearTexture:self when:0.0]; } } } diff --git a/ios/library/WhirlyGlobe-MaplyComponent/src/visual_objects/MaplyVectorObject.mm b/ios/library/WhirlyGlobe-MaplyComponent/src/visual_objects/MaplyVectorObject.mm index 3a7d0d1eef..f76d50d224 100644 --- a/ios/library/WhirlyGlobe-MaplyComponent/src/visual_objects/MaplyVectorObject.mm +++ b/ios/library/WhirlyGlobe-MaplyComponent/src/visual_objects/MaplyVectorObject.mm @@ -104,7 +104,7 @@ - (instancetype)init return self; } -- (id)initWithRef:(WhirlyKit::VectorObjectRef)vecObj +- (id)initWithRef:(const WhirlyKit::VectorObjectRef&)vecObj { self = [super init]; @@ -120,7 +120,7 @@ - (instancetype)initWithPoint:(MaplyCoordinate)coord attributes:(NSDictionary *) } /// Construct with a single point ref -- (instancetype)initWithPointRef:(MaplyCoordinate *)coord attributes:(NSDictionary *)attr +- (instancetype)initWithPointRef:(const MaplyCoordinate *)coord attributes:(NSDictionary *)attr { self = [super init]; @@ -128,11 +128,9 @@ - (instancetype)initWithPointRef:(MaplyCoordinate *)coord attributes:(NSDictiona { VectorPointsRef pts = VectorPoints::createPoints(); pts->pts.push_back(GeoCoord(coord->x,coord->y)); - iosMutableDictionary *dict = new iosMutableDictionary([NSMutableDictionary dictionaryWithDictionary:attr]); - + auto dict = std::make_shared([NSMutableDictionary dictionaryWithDictionary:attr]); vObj = std::make_shared(); - - pts->setAttrDict(MutableDictionaryRef(dict)); + pts->setAttrDict(dict); pts->initGeoMbr(); vObj->shapes.insert(pts); } @@ -140,27 +138,24 @@ - (instancetype)initWithPointRef:(MaplyCoordinate *)coord attributes:(NSDictiona return self; } -- (instancetype)initWithLineString:(NSArray *)inCoords attributes:(NSDictionary *)attr +- (instancetype)initWithLineString:(const NSArray *__nonnull)inCoords attributes:(NSDictionary *)attr { - int numCoords = [inCoords count]/2; - MaplyCoordinate *coords = (MaplyCoordinate *) malloc(sizeof(MaplyCoordinate) * numCoords); + const int numCoords = [inCoords count]/2; + std::vector coords(numCoords); for (int i = 0; i < numCoords; i++) { - float x = [inCoords[2*i] floatValue]; - float y = [inCoords[2*i+1] floatValue]; - + const float x = [inCoords[2*i] floatValue]; + const float y = [inCoords[2*i+1] floatValue]; coords[i] = MaplyCoordinateMakeWithDegrees(x, y); } - self = [self initWithLineString:coords numCoords:numCoords attributes:attr]; - - free(coords); + self = [self initWithLineString:&coords[0] numCoords:numCoords attributes:attr]; return self; } /// Construct with a linear feature (e.g. line string) -- (instancetype)initWithLineString:(MaplyCoordinate *)coords numCoords:(int)numCoords attributes:(NSDictionary *)attr +- (instancetype)initWithLineString:(const MaplyCoordinate *)coords numCoords:(int)numCoords attributes:(NSDictionary *)attr { self = [super init]; @@ -171,8 +166,8 @@ - (instancetype)initWithLineString:(MaplyCoordinate *)coords numCoords:(int)numC VectorLinearRef lin = VectorLinear::createLinear(); for (unsigned int ii=0;iipts.push_back(GeoCoord(coords[ii].x,coords[ii].y)); - iosMutableDictionary *dict = new iosMutableDictionary([NSMutableDictionary dictionaryWithDictionary:attr]); - lin->setAttrDict(MutableDictionaryRef(dict)); + auto dict = std::make_shared([NSMutableDictionary dictionaryWithDictionary:attr]); + lin->setAttrDict(dict); lin->initGeoMbr(); vObj->shapes.insert(lin); } @@ -181,7 +176,7 @@ - (instancetype)initWithLineString:(MaplyCoordinate *)coords numCoords:(int)numC } /// Construct as an areal with an exterior -- (instancetype)initWithAreal:(MaplyCoordinate *)coords numCoords:(int)numCoords attributes:(NSDictionary *)attr +- (instancetype)initWithAreal:(const MaplyCoordinate *)coords numCoords:(int)numCoords attributes:(NSDictionary *)attr { self = [super init]; @@ -194,8 +189,8 @@ - (instancetype)initWithAreal:(MaplyCoordinate *)coords numCoords:(int)numCoords for (unsigned int ii=0;iiloops.push_back(pts); - iosMutableDictionary *dict = new iosMutableDictionary([NSMutableDictionary dictionaryWithDictionary:attr]); - areal->setAttrDict(MutableDictionaryRef(dict)); + auto dict = std::make_shared([NSMutableDictionary dictionaryWithDictionary:attr]); + areal->setAttrDict(dict); areal->initGeoMbr(); vObj->shapes.insert(areal); } @@ -203,7 +198,7 @@ - (instancetype)initWithAreal:(MaplyCoordinate *)coords numCoords:(int)numCoords return self; } -- (nonnull instancetype)initWithArealArray:(NSArray *__nonnull)coords attributes:(NSDictionary *__nullable)attr +- (nonnull instancetype)initWithArealArray:(const NSArray *__nonnull)coords attributes:(NSDictionary *__nullable)attr { self = [super init]; if ([coords count] % 1 != 0) { @@ -220,8 +215,8 @@ - (nonnull instancetype)initWithArealArray:(NSArray *__nonnull)coord for (unsigned int ii=0;ii<[coords count];ii+=2) pts.push_back(GeoCoord([[coords objectAtIndex:ii] doubleValue],[[coords objectAtIndex:ii+1] doubleValue])); areal->loops.push_back(pts); - iosMutableDictionary *dict = new iosMutableDictionary([NSMutableDictionary dictionaryWithDictionary:attr]); - areal->setAttrDict(MutableDictionaryRef(dict)); + auto dict = std::make_shared([NSMutableDictionary dictionaryWithDictionary:attr]); + areal->setAttrDict(dict); areal->initGeoMbr(); vObj->shapes.insert(areal); } @@ -243,8 +238,8 @@ - (instancetype)initWithGeoJSON:(NSData *)geoJSON NSString *nsStr = [[NSString alloc] initWithData:geoJSON encoding:NSUTF8StringEncoding]; if (!nsStr) return nil; - std::string str = [nsStr UTF8String]; - std::string crs = ""; + const std::string str = [nsStr UTF8String]; + std::string crs; if (!vObj->fromGeoJSON(str, crs)) return nil; @@ -359,7 +354,7 @@ - (void)mergeVectorsFrom:(MaplyVectorObject *)otherVec } /// Add a hole to an existing areal feature -- (void)addHole:(MaplyCoordinate *)coords numCoords:(int)numCoords +- (void)addHole:(const MaplyCoordinate *)coords numCoords:(int)numCoords { if (vObj->shapes.size() != 1) return; @@ -372,31 +367,16 @@ - (void)addHole:(MaplyCoordinate *)coords numCoords:(int)numCoords - (MaplyVectorObjectType)vectorType { - auto type = vObj->getVectorType(); - MaplyVectorObjectType retType = MaplyVectorNoneType; - switch (type) + switch (vObj->getVectorType()) { - case VectorNoneType: - retType = MaplyVectorNoneType; - break; - case VectorPointType: - retType = MaplyVectorPointType; - break; - case VectorLinearType: - retType = MaplyVectorLinearType; - break; - case VectorLinear3dType: - retType = MaplyVectorLinear3dType; - break; - case VectorArealType: - retType = MaplyVectorArealType; - break; - case VectorMultiType: - retType = MaplyVectorMultiType; - break; + case VectorNoneType: return MaplyVectorNoneType; + case VectorPointType: return MaplyVectorPointType; + case VectorLinearType: return MaplyVectorLinearType; + case VectorLinear3dType: return MaplyVectorLinear3dType; + case VectorArealType: return MaplyVectorArealType; + case VectorMultiType: return MaplyVectorMultiType; + default: return MaplyVectorNoneType; } - - return retType; } - (NSString *)log @@ -718,6 +698,11 @@ - (NSArray *)asNumbers - (NSArray *)splitVectors { + // If the split will amount to a copy, just return this one + if (vObj->shapes.size() < 2) { + return @[self]; + } + std::vector newVecs; vObj->splitVectors(newVecs); @@ -801,7 +786,7 @@ - (MaplyVectorObject *) clipToMbr:(MaplyCoordinate)ll upperRight:(MaplyCoordinat return newVec; } -- (void)addShape:(WhirlyKit::VectorShapeRef)shape { +- (void)addShape:(const WhirlyKit::VectorShapeRef&)shape { vObj->shapes.insert(shape); } diff --git a/ios/library/WhirlyGlobeLib/include/BasicDrawableBuilderMTL.h b/ios/library/WhirlyGlobeLib/include/BasicDrawableBuilderMTL.h index 86e524b94f..48dd2d4dfb 100644 --- a/ios/library/WhirlyGlobeLib/include/BasicDrawableBuilderMTL.h +++ b/ios/library/WhirlyGlobeLib/include/BasicDrawableBuilderMTL.h @@ -1,9 +1,8 @@ -/* - * BasicDrawableBuilderMTL.h +/* BasicDrawableBuilderMTL.h * WhirlyGlobeLib * * Created by Steve Gifford on 5/16/19. - * Copyright 2011-2019 mousebird consulting + * Copyright 2011-2021 mousebird consulting * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -15,7 +14,6 @@ * 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. - * */ #import "BasicDrawableBuilder.h" @@ -44,7 +42,7 @@ class BasicDrawableBuilderMTL : virtual public BasicDrawableBuilder virtual BasicDrawableRef getDrawable() override; virtual void setupStandardAttributes(int numReserve=0) override; - + protected: bool drawableGotten; }; diff --git a/ios/library/WhirlyGlobeLib/include/BasicDrawableInstanceBuilderMTL.h b/ios/library/WhirlyGlobeLib/include/BasicDrawableInstanceBuilderMTL.h index 88d07f96ec..d9a9174732 100644 --- a/ios/library/WhirlyGlobeLib/include/BasicDrawableInstanceBuilderMTL.h +++ b/ios/library/WhirlyGlobeLib/include/BasicDrawableInstanceBuilderMTL.h @@ -33,7 +33,7 @@ class BasicDrawableInstanceBuilderMTL : public BasicDrawableInstanceBuilder ~BasicDrawableInstanceBuilderMTL(); /// Fill out and return the drawable - virtual BasicDrawableInstanceRef getDrawable(); + virtual BasicDrawableInstanceRef getDrawable() override; protected: }; diff --git a/ios/library/WhirlyGlobeLib/include/BasicDrawableInstanceMTL.h b/ios/library/WhirlyGlobeLib/include/BasicDrawableInstanceMTL.h index 54b8bcf5ea..35cc765126 100644 --- a/ios/library/WhirlyGlobeLib/include/BasicDrawableInstanceMTL.h +++ b/ios/library/WhirlyGlobeLib/include/BasicDrawableInstanceMTL.h @@ -77,7 +77,7 @@ class BasicDrawableInstanceMTL : virtual public BasicDrawableInstance, virtual p id getCalcRenderPipelineState(SceneRendererMTL *sceneRender,Scene *scene,ProgramMTL *program,RenderTargetMTL *renderTarget); // Set up the memory and defaults for the argument buffers (vertex, fragment, calculate) - void setupArgBuffers(id mtlDevice,RenderSetupInfoMTL *setupInfo,SceneMTL *scene,BufferBuilderMTL &buffBuild); + void setupArgBuffers(id mtlDevice,RenderSetupInfoMTL *setupInfo,SceneMTL *scene,BufferBuilderMTL *buffBuild); void updateColorDefaultAttr(); diff --git a/ios/library/WhirlyGlobeLib/include/BasicDrawableMTL.h b/ios/library/WhirlyGlobeLib/include/BasicDrawableMTL.h index 0243765e29..85429f22e3 100644 --- a/ios/library/WhirlyGlobeLib/include/BasicDrawableMTL.h +++ b/ios/library/WhirlyGlobeLib/include/BasicDrawableMTL.h @@ -102,7 +102,7 @@ class BasicDrawableMTL : virtual public BasicDrawable, virtual public DrawableMT id getRenderPipelineState(SceneRendererMTL *sceneRender,Scene *scene,ProgramMTL *program,RenderTargetMTL *renderTarget); // Set up the memory and defaults for the argument buffers (vertex, fragment, calculate) - void setupArgBuffers(id mtlDevice,RenderSetupInfoMTL *setupInfo,SceneMTL *scene,BufferBuilderMTL &buffBuild); + void setupArgBuffers(id mtlDevice,RenderSetupInfoMTL *setupInfo,SceneMTL *scene,BufferBuilderMTL *buffBuild); bool setupForMTL; std::vector tris; diff --git a/ios/library/WhirlyGlobeLib/include/BillboardDrawableBuilderMTL.h b/ios/library/WhirlyGlobeLib/include/BillboardDrawableBuilderMTL.h index 34d227d2b3..efd696f623 100644 --- a/ios/library/WhirlyGlobeLib/include/BillboardDrawableBuilderMTL.h +++ b/ios/library/WhirlyGlobeLib/include/BillboardDrawableBuilderMTL.h @@ -1,9 +1,8 @@ -/* - * BillboardDrawableBuilderMTL.h +/* BillboardDrawableBuilderMTL.h * WhirlyGlobeLib * * Created by Steve Gifford on 5/16/19. - * Copyright 2011-2019 mousebird consulting + * Copyright 2011-2021 mousebird consulting * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -15,7 +14,6 @@ * 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. - * */ #import "BillboardDrawableBuilder.h" @@ -24,17 +22,6 @@ namespace WhirlyKit { -// Passes in the uniform values the shader is expecting -class BillboardTweakerMTL : public DrawableTweaker -{ -public: - BillboardTweakerMTL(); - - virtual void tweakForFrame(Drawable *inDraw,RendererFrameInfo *frameInfo); - - bool groundMode; -}; - /** Metal version of BillboardDrawable Builder */ class BillboardDrawableBuilderMTL : virtual public BasicDrawableBuilderMTL, virtual public BillboardDrawableBuilder diff --git a/ios/library/WhirlyGlobeLib/include/ComponentManager_iOS.h b/ios/library/WhirlyGlobeLib/include/ComponentManager_iOS.h index 3daf2f84ee..7fff8a9f87 100644 --- a/ios/library/WhirlyGlobeLib/include/ComponentManager_iOS.h +++ b/ios/library/WhirlyGlobeLib/include/ComponentManager_iOS.h @@ -28,20 +28,25 @@ class ComponentObject_iOS : public ComponentObject { public: ComponentObject_iOS(); - + + // Create the component object with the specified defaults and + // apply the properties from the descriptor dictionary, if present. + // (i.e., `kMaplyEnabled` in `desc` overrides `enable`) + ComponentObject_iOS(bool enable, bool isSelectable, const NSDictionary *_Nullable desc); + // Textures we're holding on to // If we let them release, they go away std::set texs; }; - + typedef std::shared_ptr ComponentObject_iOSRef; // Used to map IDs to individual user objects (e.g. markers, labels) class SelectObject { public: - SelectObject(WhirlyKit::SimpleIdentity selID) : selID(selID) { } - SelectObject(WhirlyKit::SimpleIdentity selID,NSObject *obj) : selID(selID), obj(obj) { } + SelectObject(WhirlyKit::SimpleIdentity selID) : selID(selID), obj(nullptr) { } + SelectObject(WhirlyKit::SimpleIdentity selID,NSObject *__nullable obj) : selID(selID), obj(obj) { } // Comparison operator sorts on select ID bool operator < (const SelectObject &that) const @@ -50,7 +55,7 @@ class SelectObject } WhirlyKit::SimpleIdentity selID; - NSObject * __strong obj; + NSObject *__nullable __strong obj; }; typedef std::set SelectObjectSet; @@ -65,13 +70,15 @@ class ComponentManager_iOS : public ComponentManager virtual ~ComponentManager_iOS(); /// Associate the given object with the selection ID - void addSelectObject(SimpleIdentity selectID,NSObject *obj); + void addSelectObject(SimpleIdentity selectID,NSObject *_Nonnull obj); /// Return the NSObject (marker, label) corresponding to a selection - NSObject *getSelectObject(SimpleIdentity selID); + NSObject *_Nonnull getSelectObject(SimpleIdentity selID); /// Need to remove select IDs before we let the superclass clean up - virtual void removeComponentObjects(PlatformThreadInfo *threadInfo,const SimpleIDSet &compIDs,ChangeSet &changes) override; + virtual void removeComponentObjects(PlatformThreadInfo *_Nullable threadInfo, + const SimpleIDSet &compIDs, + ChangeSet &changes) override; /// Clear out anything we're holding void clear(); @@ -80,7 +87,7 @@ class ComponentManager_iOS : public ComponentManager void dumpStats(); protected: - virtual ComponentObjectRef makeComponentObject() override; + virtual ComponentObjectRef makeComponentObject(const Dictionary *_Nullable desc) override; /// Remove the given selectable object void removeSelectObjects(SimpleIDSet selID); @@ -89,5 +96,6 @@ class ComponentManager_iOS : public ComponentManager // Use to map IDs in the selection layer to objects the user passed in SelectObjectSet selectObjectSet; }; +typedef std::shared_ptr ComponentManager_iOSRef; } diff --git a/ios/library/WhirlyGlobeLib/include/DefaultShadersMTL.h b/ios/library/WhirlyGlobeLib/include/DefaultShadersMTL.h index d3b6ceb9a2..cc4df82eb9 100644 --- a/ios/library/WhirlyGlobeLib/include/DefaultShadersMTL.h +++ b/ios/library/WhirlyGlobeLib/include/DefaultShadersMTL.h @@ -53,29 +53,35 @@ struct ColorExp { // Basic vertex attribute positions typedef enum { WKSVertexPositionAttribute = 0, - WKSVertexColorAttribute, - WKSVertexNormalAttribute, - WKSVertexTextureBaseAttribute + WKSVertexColorAttribute = 1, + WKSVertexNormalAttribute = 2, + WKSVertexTextureBaseAttribute = 3, + // Need some space for textures + WKSVertexMaskAttribute = 5 + // And another space for extra mask } WKSVertexAttributes; // Wide Vector vertex attribute positions typedef enum { - WKSVertexWideVecTexInfoAttribute = 5, + WKSVertexWideVecTexInfoAttribute = 7, + // We don't use these at the same time + WKSVertexWideVecInstIndexAttribute = 7, WKSVertexWideVecP1Attribute, WKSVertexWideVecN0Attribute, - WKSVertexWideVecC0Attribute + WKSVertexWideVecC0Attribute, + WKSVertexWideVecOffsetAttribute } WKSVertexWideVecAttributes; // Screen space vertex attribute positions typedef enum { - WKSVertexScreenSpaceOffsetAttribute = 5, + WKSVertexScreenSpaceOffsetAttribute = 7, WKSVertexScreenSpaceRotAttribute, WKSVertexScreenSpaceDirAttribute } WKSVertexScreenSpaceAttributes; // Model instance vertex attribute positions typedef enum { - WKSVertexInstanceColorAttribute = 5, + WKSVertexInstanceColorAttribute = 7, WKSVertexInstanceMatrixAttribute, WKSVertexInstanceCenterAttribute, WKSVertexInstanceDirAttribute @@ -84,7 +90,7 @@ typedef enum { // Billboard offsets // TODO: Billboards should be instances typedef enum { - WKSVertexBillboardOffsetAttribute = 6 + WKSVertexBillboardOffsetAttribute = 8 } WKSVertexBillboardAttributes; // Maximum number of textures we currently support @@ -93,20 +99,21 @@ typedef enum { #define WKSTextureEntryLookup 5 #define MaxZoomSlots 32 +#define MaxMaskSlots 2 // All the buffer entries (other than stage_in) for the vertex shaders typedef enum { - WKSVertUniformArgBuffer = 10, - WKSVertLightingArgBuffer = 11, + WKSVertUniformArgBuffer = 12, + WKSVertLightingArgBuffer, // These are free form with their own subsections - WKSVertexArgBuffer = 12, + WKSVertexArgBuffer, // Textures are optional - WKSVertTextureArgBuffer = 13, + WKSVertTextureArgBuffer, // Model instances - WKSVertModelInstanceArgBuffer = 14, + WKSVertModelInstanceArgBuffer, // If we're using the indirect instancing (can be driven by the GPU) this is // where the indirect buffer lives - WKSVertInstanceIndirectBuffer = 15, + WKSVertInstanceIndirectBuffer, WKSVertMaxBuffer } WKSVertexArgumentBuffers; @@ -167,7 +174,7 @@ struct UniformDrawStateA { float minVisibleFadeBand,maxVisibleFadeBand; int zoomSlot; // Used to pass continuous zoom info bool clipCoords; // If set, the geometry coordinates aren't meant to be transformed - bool hasExp; // Look for a UniformWideVecExp structure for color, opacity, and width + bool hasExp; // Look for a UniformWideVecExp structure for color, opacity, and width }; // Uniform expressions optionally passed to basic polygon shaders @@ -220,18 +227,36 @@ struct Lighting { // Instructions to the wide vector shaders, usually per-drawable struct UniformWideVec { float w2; // Width / 2.0 in screen space + float offset; // Offset from center in screen space float edge; // Edge falloff control float texRepeat; // Texture scaling specific to wide vectors - simd::float4 color; // Color override. TODO: Use the standard one. Seriously. bool hasExp; // Look for a UniformWideVecExp structure for color, opacity, and width }; // For variable width (and color, etc) lines we'll struct UniformWideVecExp { FloatExp widthExp; + FloatExp offsetExp; FloatExp opacityExp; ColorExp colorExp; }; + +// Instance info for the wide vector (new) vertex shader +typedef struct +{ + // Center of the point on the line + simd::float3 center; + // Upward direction (for 3D lines) + simd::float3 up; + // Length of the line up to this point + float len; + // Color for the whole line + simd::float4 color; + // Used to track loops and such + int prev,next; + // Mask IDs for comparison + int mask0,mask1; +} VertexTriWideVecInstance; // Instructions to the screen space shaders, usually per-drawable struct UniformScreenSpace { @@ -313,6 +338,7 @@ struct ProjVertexTriA { float4 position [[invariant]] [[position]]; float4 color; float2 texCoord; + uint2 maskIDs; }; // Triangle vertex with a couple of texture coordinates @@ -342,10 +368,14 @@ struct ProjVertexTriB { struct VertexTriWideVec { float3 position [[attribute(WhirlyKitShader::WKSVertexPositionAttribute)]]; + float4 color [[attribute(WhirlyKitShader::WKSVertexColorAttribute)]]; float3 normal [[attribute(WhirlyKitShader::WKSVertexNormalAttribute)]]; float4 texInfo [[attribute(WhirlyKitShader::WKSVertexWideVecTexInfoAttribute)]]; + int mask0 [[attribute(WhirlyKitShader::WKSVertexMaskAttribute+0)]]; + int mask1 [[attribute(WhirlyKitShader::WKSVertexMaskAttribute+1)]]; float3 p1 [[attribute(WhirlyKitShader::WKSVertexWideVecP1Attribute)]]; float3 n0 [[attribute(WhirlyKitShader::WKSVertexWideVecN0Attribute)]]; + float3 offset [[attribute(WhirlyKitShader::WKSVertexWideVecOffsetAttribute)]]; float c0 [[attribute(WhirlyKitShader::WKSVertexWideVecC0Attribute)]]; }; @@ -356,6 +386,24 @@ struct ProjVertexTriWideVec { float2 texCoord; float dotProd; float w2; + uint2 maskIDs; +}; + +// Vertex definition for wide vector (new version) +struct VertexTriWideVecB +{ + // x, y offset around the center + float3 screenPos [[attribute(WhirlyKitShader::WKSVertexPositionAttribute)]]; + int index [[attribute(WhirlyKitShader::WKSVertexWideVecInstIndexAttribute)]]; +}; + +// Wide vector vertex passed to fragment shader (new version) +struct ProjVertexTriWideVecPerf { + float4 position [[invariant]] [[position]]; + float4 color; + float2 texCoord; + float w2; + uint2 maskIDs; }; // Input vertex data for Screen Space shaders @@ -365,6 +413,7 @@ struct VertexTriScreenSpace float3 normal [[attribute(WhirlyKitShader::WKSVertexNormalAttribute)]]; float2 texCoord [[attribute(WhirlyKitShader::WKSVertexTextureBaseAttribute)]]; float4 color [[attribute(WhirlyKitShader::WKSVertexColorAttribute)]]; + int maskID [[attribute(WhirlyKitShader::WKSVertexMaskAttribute)]]; float2 offset [[attribute(WhirlyKitShader::WKSVertexScreenSpaceOffsetAttribute)]]; float3 rot [[attribute(WhirlyKitShader::WKSVertexScreenSpaceRotAttribute)]]; float3 dir [[attribute(WhirlyKitShader::WKSVertexScreenSpaceDirAttribute)]]; @@ -382,13 +431,24 @@ struct VertexTriBillboard typedef struct RegularTextures { // A bit per texture that's present - int texPresent [[ id(WKSTexBufTexPresent) ]]; + uint32_t texPresent [[ id(WKSTexBufTexPresent) ]]; // Texture indirection (for accessing sub-textures) - float offset [[ id(WKSTexBuffIndirectOffset) ]] [2*WKSTextureMax]; - float scale [[ id(WKSTexBuffIndirectScale) ]] [2*WKSTextureMax]; - metal::texture2d tex [[ id(WKSTexBuffTextures) ]] [WKSTextureMax]; + metal::array offset [[ id(WKSTexBuffIndirectOffset) ]]; + metal::array scale [[ id(WKSTexBuffIndirectScale) ]]; + metal::array, WKSTextureMax> tex [[ id(WKSTexBuffTextures) ]]; } RegularTextures; +typedef struct WideVecTextures { + // A bit per texture that's present + uint32_t texPresent [[ id(WKSTexBufTexPresent) ]]; + // Texture indirection (for accessing sub-textures) + metal::array offset [[ id(WKSTexBuffIndirectOffset) ]]; + metal::array scale [[ id(WKSTexBuffIndirectScale) ]]; + metal::array, WKSTextureEntryLookup> tex [[ id(WKSTexBuffTextures) ]]; + metal::texture2d maskTex [[id(WKSTexBuffTextures+WKSTextureEntryLookup)]]; + metal::array, WKSTextureMax-WKSTextureEntryLookup> tex2 [[ id(WKSTexBuffTextures+WKSTextureEntryLookup+1) ]]; +} WideVecTextures; + struct VertexTriArgBufferA { WhirlyKitShader::UniformDrawStateA uniDrawState [[ id(WhirlyKitShader::WKSUniformDrawStateEntry) ]]; bool hasTextures; diff --git a/ios/library/WhirlyGlobeLib/include/Dictionary_NSDictionary.h b/ios/library/WhirlyGlobeLib/include/Dictionary_NSDictionary.h index 53c2524345..15ab131ec1 100644 --- a/ios/library/WhirlyGlobeLib/include/Dictionary_NSDictionary.h +++ b/ios/library/WhirlyGlobeLib/include/Dictionary_NSDictionary.h @@ -64,11 +64,14 @@ class iosDictionary : public Dictionary { public: iosDictionary(); - iosDictionary(NSDictionary *dict); + iosDictionary(const NSDictionary *dict); // Copy constructor iosDictionary(const iosDictionary &that); virtual ~iosDictionary() override; - + + virtual int count() const override; + virtual bool empty() const override { return count() == 0; } + /// Returns true if the field exists bool hasField(const std::string &name) const override; @@ -100,7 +103,7 @@ class iosDictionary : public Dictionary // Return an array of key names virtual std::vector getKeys() const override; public: - NSDictionary *dict; + const NSDictionary *dict; }; class iosMutableDictionary; @@ -111,13 +114,17 @@ class iosMutableDictionary : public MutableDictionary { public: iosMutableDictionary(); + iosMutableDictionary(const NSDictionary *dict); iosMutableDictionary(NSMutableDictionary *dict); - iosMutableDictionary(MutableDictionaryRef dict); + iosMutableDictionary(const MutableDictionaryRef &dict); // Assignment operator virtual iosMutableDictionary &operator = (const iosMutableDictionary &that); virtual ~iosMutableDictionary() override; virtual MutableDictionaryRef copy() const override; + virtual int count() const override; + virtual bool empty() const override { return count() == 0; } + /// Returns true if the field exists bool hasField(const std::string &name) const override; @@ -157,6 +164,8 @@ class iosMutableDictionary : public MutableDictionary /// Set field as int void setInt(const std::string &name,int val) override; + /// Set field as int + void setInt64(const std::string &name,int64_t val) override; /// Set field as 64 bit unique value void setIdentifiable(const std::string &name,SimpleIdentity val) override; /// Set field as double @@ -184,7 +193,8 @@ class iosMutableDictionary : public MutableDictionary @interface NSMutableDictionary (DictionaryC) // Convert one of the DictionaryC objects to an NSDictionary (actually works on dictionary in the right interface) -+ (NSMutableDictionary *) fromDictionaryC:(WhirlyKit::MutableDictionaryRef)dict; -+ (NSMutableDictionary *) fromDictionaryCPointer:(const WhirlyKit::MutableDictionary *)dict; ++ (NSMutableDictionary *) fromDictionaryC:(const WhirlyKit::DictionaryRef &)dict; ++ (NSMutableDictionary *) fromMutableDictionaryC:(const WhirlyKit::MutableDictionaryRef &)dict; ++ (NSMutableDictionary *) fromDictionaryCPointer:(const WhirlyKit::Dictionary *)dict; @end diff --git a/ios/library/WhirlyGlobeLib/include/DrawableMTL.h b/ios/library/WhirlyGlobeLib/include/DrawableMTL.h index cb116eb68e..9552f1e110 100644 --- a/ios/library/WhirlyGlobeLib/include/DrawableMTL.h +++ b/ios/library/WhirlyGlobeLib/include/DrawableMTL.h @@ -44,7 +44,7 @@ class ArgBuffContentsMTL { RenderSetupInfoMTL *setupInfoMTL, id func, int bufferArgIdx, - BufferBuilderMTL &buffBuild); + BufferBuilderMTL *buffBuild); // Check if this is just empty bool isEmpty(); @@ -117,7 +117,7 @@ class ArgBuffRegularTexturesMTL RenderSetupInfoMTL *setupInfoMTL, id mtlFunction, int bufferArgIdx, - BufferBuilderMTL &buildBuff); + BufferBuilderMTL *buildBuff); // Add a texture to encode void addTexture(const Point2f &offset,const Point2f &scale,id tex); diff --git a/ios/library/WhirlyGlobeLib/include/FontTextureManager_iOS.h b/ios/library/WhirlyGlobeLib/include/FontTextureManager_iOS.h index 9be4df49f3..8e84747539 100644 --- a/ios/library/WhirlyGlobeLib/include/FontTextureManager_iOS.h +++ b/ios/library/WhirlyGlobeLib/include/FontTextureManager_iOS.h @@ -33,10 +33,13 @@ class FontManager_iOS : public FontManager { public: FontManager_iOS(CTFontRef font); - ~FontManager_iOS(); - - virtual bool operator < (const FontManager_iOS &that) const; + virtual ~FontManager_iOS(); + virtual bool operator <(const FontManager &that) const override; + bool operator <(const FontManager_iOS &that) const; + + virtual void teardown(PlatformThreadInfo*) override { } + CTFontRef font; UIColor *colorUI; UIColor *backColorUI; @@ -55,7 +58,9 @@ class FontTextureManager_iOS : public FontTextureManager /// Add the given string. Caller is responsible for deleting the DrawableString WhirlyKit::DrawableString *addString(PlatformThreadInfo *threadInfo,NSAttributedString *str,ChangeSet &changes); - + + virtual void teardown(PlatformThreadInfo*) override; + protected: NSData *renderGlyph(CGGlyph glyph,FontManager_iOSRef fm,Point2f &size,Point2f &glyphSize,Point2f &offset,Point2f &textureOffset); FontManager_iOSRef findFontManagerForFont(UIFont *uiFont,UIColor *colorUI,UIColor *backColorUI,UIColor *outlineColorUI,float outlinesize); diff --git a/ios/library/WhirlyGlobeLib/include/GeographicLib.h b/ios/library/WhirlyGlobeLib/include/GeographicLib.h new file mode 100644 index 0000000000..63294b72e6 --- /dev/null +++ b/ios/library/WhirlyGlobeLib/include/GeographicLib.h @@ -0,0 +1,100 @@ +// +// geowrap.h +// WhirlyGlobeLib +// +// Created by Tim Sylvester on 12/14/20. +// Copyright © 2020 mousebird consulting. All rights reserved. +// + +#ifndef GeographicLib_Wrapper_h +#define GeographicLib_Wrapper_h + +#import "math/MaplyCoordinate.h" + +typedef struct GeoLibInv_t { + double distance; // meters + double azimuth1; // radians + double azimuth2; // radians +} GeoLibInv; + +typedef struct GeoLibInt_t { + MaplyCoordinateD intersection; + bool intersects; +} GeoLibInt; + +typedef struct GeoLibIntPair_t { + MaplyCoordinateD intersections[2]; + double distances[2]; + unsigned int count; +} GeoLibIntPair; + +typedef struct GeoLibOrthoDist_t { + double downtrackDistance; + double crosstrackDistance; + double segmentLength; +} GeoLibOrthoDist; + +#if defined __cplusplus +extern "C" { +#endif + +/// Solve the direct geodesic problem where the length of the geodesic is specified in terms of distance. +/// azimuth in radians, distance in meters +MaplyCoordinateD GeoLibCalcDirectF(MaplyCoordinate origin, double azimuth, double distance); +MaplyCoordinateD GeoLibCalcDirectD(MaplyCoordinateD origin, double azimuth, double distance); + +// Solve the inverse geodesic problem +GeoLibInv GeoLibCalcInverseF(MaplyCoordinate p1, MaplyCoordinate p2); +GeoLibInv GeoLibCalcInverseD(MaplyCoordinateD p1, MaplyCoordinateD p2); + +// Test for a point lying inside the specified polygon +bool MaplyCoordinateInPolygon(MaplyCoordinate p, const MaplyCoordinate polygon[], unsigned count); +bool MaplyCoordinateDInPolygon(MaplyCoordinateD p, const MaplyCoordinate polygon[], unsigned count); +bool MaplyCoordinateInPolygonD(MaplyCoordinate p, const MaplyCoordinateD polygon[], unsigned count); +bool MaplyCoordinateDInPolygonD(MaplyCoordinateD p, const MaplyCoordinateD polygon[], unsigned count); + +double GeoLibDistanceD(MaplyCoordinateD startPt, MaplyCoordinateD endPt); + +// Test for a segment intersecting a polygon. +// Note that if the line is completely within the polygon the result is false. +bool GeoLibLineDIntersectsPolygonD(MaplyCoordinateD startPt, MaplyCoordinateD endPt, const MaplyCoordinateD[], unsigned count); + +// Compute the intersection point of two geodesic segments +GeoLibInt GeoLibIntersectD(MaplyCoordinateD a, MaplyCoordinateD b, MaplyCoordinateD c, MaplyCoordinateD d); + +// Determine where a great circle intersects a small circle +GeoLibIntPair GeoLibLineDIntersectCircleD(MaplyCoordinateD startPt, MaplyCoordinateD endPt, MaplyCoordinateD center, double radiusMeters); + +// Determine whether there's an intersection without bothering to compute its location +bool GeoLibLineDIntersectsCircleD(MaplyCoordinateD startPt, MaplyCoordinateD endPt, MaplyCoordinateD center, double radiusMeters); + +// Compute the orthogonal distances for a point. +// +// Given a segment and a point, find the perpendicular intersection point (the closest point along the +// segment) and compute the distance from that point to the segment starting point (down-track) and +// to the specified point (cross-track). +// +// negative down-track distance C +// v | <- negative cross-track distance +// - - - - - A--------B +// | | <- positive cross-track distance +// C C +// ---- +// ^ positive down-track-distance +// +// If the point lies "before" the segment start point, the down-track distance will be negative. +// If the point lies "after" the segment end point, the down-track distance will be greater than the segment length. +// If the point lies to the right of the segment, the cross-track distance will be positive. +// If the point lies to the left of the segment, the cross-track distance will be negative. +GeoLibOrthoDist GeoLibOrthoDistD(MaplyCoordinateD a, MaplyCoordinateD b, MaplyCoordinateD c); + +// Generate points along an arc +double GeoLibSampleArcD(MaplyCoordinateD center, double radiusMeters, + double beginAzimuthRad, double endAziumthRad, bool clockwise, + MaplyCoordinateD points[], unsigned count); + +#if defined __cplusplus +} // extern "C" +#endif + +#endif /* GeographicLib_Wrapper_h */ diff --git a/ios/library/WhirlyGlobeLib/include/NSDictionary+Stuff.h b/ios/library/WhirlyGlobeLib/include/NSDictionary+Stuff.h index 1a6e1bcc61..d47c1819b6 100644 --- a/ios/library/WhirlyGlobeLib/include/NSDictionary+Stuff.h +++ b/ios/library/WhirlyGlobeLib/include/NSDictionary+Stuff.h @@ -25,30 +25,34 @@ /// Return the object correspond to name if it's the right type /// Returns default if missing or wrong type -- (id)objectForKey:(NSString *)name checkType:(id)theType default:(id)theDefault; +- (id _Nullable)objectForKey:(NSString *_Nonnull)name checkType:(id _Nonnull)theType default:(id _Nullable)theDefault; /// Return a float for the given name if it exists /// Returns default if not or wrong type -- (float)floatForKey:(NSString *)name default:(float)theDefault; +- (float)floatForKey:(NSString *_Nonnull)name default:(float)theDefault; /// Return a double for the given name if it exists /// Returns default if not or wrong type -- (double)doubleForKey:(NSString *)name default:(double)theDefault; +- (double)doubleForKey:(NSString *_Nonnull)name default:(double)theDefault; /// Return an integer for the given name if it exists /// Returns default if not or wrong type -- (int)intForKey:(NSString *)name default:(int)theDefault; +- (int)intForKey:(NSString *_Nonnull)name default:(int)theDefault; /// Return a boolean for the given name if it exists /// Returns default if not or wrong type -- (BOOL)boolForKey:(NSString *)name default:(BOOL)theDefault; +- (BOOL)boolForKey:(NSString *_Nonnull)name default:(BOOL)theDefault; /// Return a string for the given name if it exists /// Returns default if not or wrong type -- (NSString *)stringForKey:(NSString *)name default:(NSString *)theDefault; +- (NSString *_Nullable)stringForKey:(NSString *_Nonnull)name default:(NSString *_Nullable)theDefault; /// Parse an enumerated type and return an int -- (int)enumForKey:(NSString *)name values:(NSArray *)values default:(int)theDefault; +- (int)enumForKey:(NSString *_Nonnull)name values:(NSArray *_Nonnull)values default:(int)theDefault; + +/// Merge dictionaries ++ (NSDictionary *_Nonnull) dictionaryByMerging:(NSDictionary *_Nullable) dict1 with:(NSDictionary *_Nullable)dict2; +- (NSDictionary *_Nonnull) dictionaryByMergingWith:(NSDictionary *_Nullable)dict; @end diff --git a/ios/library/WhirlyGlobeLib/include/QuadImageFrameLoader_iOS.h b/ios/library/WhirlyGlobeLib/include/QuadImageFrameLoader_iOS.h index ec31eb87cf..ceb001084d 100644 --- a/ios/library/WhirlyGlobeLib/include/QuadImageFrameLoader_iOS.h +++ b/ios/library/WhirlyGlobeLib/include/QuadImageFrameLoader_iOS.h @@ -128,7 +128,7 @@ class QuadImageFrameLoader_ios : public QuadImageFrameLoader // Change the tile sources for upcoming loads virtual void setTileInfos(NSArray *> *tileInfos); - + protected: // Convenience routine used to set up C++ version of frames void setupFrames(); diff --git a/ios/library/WhirlyGlobeLib/include/SceneMTL.h b/ios/library/WhirlyGlobeLib/include/SceneMTL.h index 42b64f6e10..621f44665f 100644 --- a/ios/library/WhirlyGlobeLib/include/SceneMTL.h +++ b/ios/library/WhirlyGlobeLib/include/SceneMTL.h @@ -40,7 +40,7 @@ class SceneMTL : public Scene /// Explicitly tear everything down in OpenGL ES. /// We're assuming the context has been set. - virtual void teardown(); + virtual void teardown(PlatformThreadInfo*) override; }; } diff --git a/ios/library/WhirlyGlobeLib/include/SceneRendererMTL.h b/ios/library/WhirlyGlobeLib/include/SceneRendererMTL.h index 13a3745e89..882b867f36 100644 --- a/ios/library/WhirlyGlobeLib/include/SceneRendererMTL.h +++ b/ios/library/WhirlyGlobeLib/include/SceneRendererMTL.h @@ -191,10 +191,11 @@ class SceneRendererMTL : public SceneRenderer // Explicit wait for shutdown of ongoing frames void shutdown(); - + + bool isShuttingDown() const { return *_isShuttingDown; } + public: RenderTargetMTLRef getRenderTarget(SimpleIdentity renderTargetID); - bool isShuttingDown; id lastCmdBuff; // If set, we'll use indirect rendering @@ -205,6 +206,9 @@ class SceneRendererMTL : public SceneRenderer RenderSetupInfoMTL setupInfo; std::vector *> snapshotDelegates; dispatch_queue_t releaseQueue; + +private: + const std::shared_ptr _isShuttingDown; }; typedef std::shared_ptr SceneRendererMTLRef; diff --git a/ios/library/WhirlyGlobeLib/include/ScreenSpaceDrawableBuilderMTL.h b/ios/library/WhirlyGlobeLib/include/ScreenSpaceDrawableBuilderMTL.h index 39235b9bf5..e3967a385a 100644 --- a/ios/library/WhirlyGlobeLib/include/ScreenSpaceDrawableBuilderMTL.h +++ b/ios/library/WhirlyGlobeLib/include/ScreenSpaceDrawableBuilderMTL.h @@ -31,9 +31,9 @@ class ScreenSpaceDrawableBuilderMTL : virtual public BasicDrawableBuilderMTL, vi public: ScreenSpaceDrawableBuilderMTL(const std::string &name,Scene *scene); - virtual void Init(bool hasMotion,bool hasRotation, bool buildAnyway = false) override; - - ScreenSpaceTweaker *makeTweaker() override; + virtual void ScreenSpaceInit(bool hasMotion,bool hasRotation, bool buildAnyway = false) override; + + virtual DrawableTweakerRef makeTweaker() const override; /// Fill out and return the drawable virtual BasicDrawableRef getDrawable() override; diff --git a/ios/library/WhirlyGlobeLib/include/SingleLabel_iOS.h b/ios/library/WhirlyGlobeLib/include/SingleLabel_iOS.h index d6404faac3..04e11a0d06 100644 --- a/ios/library/WhirlyGlobeLib/include/SingleLabel_iOS.h +++ b/ios/library/WhirlyGlobeLib/include/SingleLabel_iOS.h @@ -28,8 +28,14 @@ namespace WhirlyKit class SingleLabel_iOS: public SingleLabel { public: + SingleLabel_iOS(NSString* s = nullptr) : text(s) {} + // Used to build the drawable string on specific platforms - virtual std::vector generateDrawableStrings(PlatformThreadInfo *threadInfo,const LabelInfo *,FontTextureManager *fontTexManager,float &lineHeight,ChangeSet &changes) override; + virtual std::vector generateDrawableStrings(PlatformThreadInfo *threadInfo, + const LabelInfo *, + const FontTextureManagerRef &fontTexManager, + float &lineHeight, + ChangeSet &changes) override; // Pass this around as an NSString NSString *text; diff --git a/ios/library/WhirlyGlobeLib/include/UIColor+Stuff.h b/ios/library/WhirlyGlobeLib/include/UIColor+Stuff.h index 8f47b89463..3ae9fe49ed 100644 --- a/ios/library/WhirlyGlobeLib/include/UIColor+Stuff.h +++ b/ios/library/WhirlyGlobeLib/include/UIColor+Stuff.h @@ -1,9 +1,8 @@ -/* - * UIColor+Stuff.h +/* UIColor+Stuff.h * WhirlyGlobeLib * * Created by Steve Gifford on 3/15/11. - * Copyright 2011-2019 mousebird consulting + * Copyright 2011-2021 mousebird consulting * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -15,7 +14,6 @@ * 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. - * */ #import @@ -23,9 +21,15 @@ @interface UIColor(Stuff) -/// Build a UIColor from a hex value +- (UIColor *)lighterColor; +- (UIColor *)lighterColor:(float)withFactor; + +/// Build a UIColor from a hex value (#RRGGBB) + (UIColor *) colorFromHexRGB:(int)hexColor; +/// Build a UIColor from a hex value (#RGB) ++ (UIColor *) colorFromShortHexRGB:(int)hexColor; + /// Build a UIColor from an RGBAColor + (UIColor *) colorFromRGBA:(const WhirlyKit::RGBAColor &)color; diff --git a/ios/library/WhirlyGlobeLib/include/WideVectorDrawableBuilderMTL.h b/ios/library/WhirlyGlobeLib/include/WideVectorDrawableBuilderMTL.h index b8de067040..34ba257150 100644 --- a/ios/library/WhirlyGlobeLib/include/WideVectorDrawableBuilderMTL.h +++ b/ios/library/WhirlyGlobeLib/include/WideVectorDrawableBuilderMTL.h @@ -1,9 +1,8 @@ -/* - * WideVectorDrawableBuilderMTL.h +/* WideVectorDrawableBuilderMTL.h * WhirlyGlobeLib * * Created by Steve Gifford on 5/16/19. - * Copyright 2011-2019 mousebird consulting + * Copyright 2011-2021 mousebird consulting * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -15,36 +14,49 @@ * 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. - * */ #import "WideVectorDrawableBuilder.h" -#import "BasicDrawableBuilderMTL.h" #import "BaseInfo.h" namespace WhirlyKit { - -/// Metal version sets up one Uniform structure -class WideVectorTweakerMTL : public WideVectorTweaker -{ - void tweakForFrame(Drawable *inDraw,RendererFrameInfo *frameInfo); -}; /// Metal version of the WideVectorDrawable Builder -class WideVectorDrawableBuilderMTL : virtual public BasicDrawableBuilderMTL, virtual public WideVectorDrawableBuilder +class WideVectorDrawableBuilderMTL : virtual public WideVectorDrawableBuilder { public: - WideVectorDrawableBuilderMTL(const std::string &name,Scene *scene); + WideVectorDrawableBuilderMTL(const std::string &name,const SceneRenderer *sceneRenderer,Scene *scene); // Initialize with an estimate on the number of vertices and triangles - virtual void Init(unsigned int numVert,unsigned int numTri,bool globeMode) override; + virtual void Init(unsigned int numVert,unsigned int numTri,unsigned int numCenterLine, + WideVecImplType implType, + bool globeMode, + const WideVectorInfo *vecInfo) override; + + // Add the given attribute + virtual int addAttribute(BDAttributeDataType dataType,StringIdentity nameID,int slot=-1,int numThings = -1) override; - WideVectorTweaker *makeTweaker() override; + DrawableTweakerRef makeTweaker() const override; - virtual BasicDrawableRef getDrawable() override; + // Return the basic drawable for the simple and complex cases + virtual BasicDrawableRef getBasicDrawable() override; + // Return the drawable instance for the complec case + virtual BasicDrawableInstanceRef getInstanceDrawable() override; + + // A guess at how many instances we can support (max line length, basically) + virtual int maxInstances() const override; + protected: + // Uniform block used for basic wide vector implementation + BasicDrawable::UniformBlock wideVecUniBlock(); + + // Uniform block used when we're doing expressions + BasicDrawable::UniformBlock wideVecExpUniBlock(); + + bool drawableGotten; + bool instanceGotten; }; } diff --git a/ios/library/WhirlyGlobeLib/include/WrapperMTL.h b/ios/library/WhirlyGlobeLib/include/WrapperMTL.h index 50c49d15a6..3f8421cc66 100644 --- a/ios/library/WhirlyGlobeLib/include/WrapperMTL.h +++ b/ios/library/WhirlyGlobeLib/include/WrapperMTL.h @@ -188,6 +188,9 @@ class HeapManagerMTL id mtlDevice; HeapGroup heapGroups[MaxType]; HeapGroup texGroups; + + // Keep Metal allocations aligned to this + size_t memAlign; }; /// Passed around to various init and teardown routines diff --git a/ios/library/WhirlyGlobeLib/src/BasicDrawableBuilderMTL.mm b/ios/library/WhirlyGlobeLib/src/BasicDrawableBuilderMTL.mm index 8e8be9fee1..e1aa11579e 100644 --- a/ios/library/WhirlyGlobeLib/src/BasicDrawableBuilderMTL.mm +++ b/ios/library/WhirlyGlobeLib/src/BasicDrawableBuilderMTL.mm @@ -38,13 +38,17 @@ void BasicDrawableBuilderMTL::setupStandardAttributes(int numReserve) { - basicDraw->colorEntry = addAttribute(BDChar4Type,a_colorNameID); + basicDraw->colorEntry = findAttribute(a_colorNameID); + if (basicDraw->colorEntry < 0) + basicDraw->colorEntry = addAttribute(BDChar4Type,a_colorNameID); VertexAttributeMTL *colorAttr = (VertexAttributeMTL *)basicDraw->vertexAttributes[basicDraw->colorEntry]; colorAttr->slot = WhirlyKitShader::WKSVertexColorAttribute; colorAttr->setDefaultColor(RGBAColor(255,255,255,255)); colorAttr->reserve(numReserve); - - basicDraw->normalEntry = addAttribute(BDFloat3Type,a_normalNameID); + + basicDraw->normalEntry = findAttribute(a_normalNameID); + if (basicDraw->normalEntry < 0) + basicDraw->normalEntry = addAttribute(BDFloat3Type,a_normalNameID); VertexAttributeMTL *normalAttr = (VertexAttributeMTL *)basicDraw->vertexAttributes[basicDraw->normalEntry]; normalAttr->slot = WhirlyKitShader::WKSVertexNormalAttribute; normalAttr->setDefaultVector3f(Vector3f(1.0,1.0,1.0)); @@ -54,7 +58,7 @@ BasicDrawableBuilderMTL::~BasicDrawableBuilderMTL() { if (!drawableGotten && basicDraw) - basicDraw = NULL; + basicDraw.reset(); } int BasicDrawableBuilderMTL::addAttribute(BDAttributeDataType dataType,StringIdentity nameID,int slot,int numThings) @@ -90,7 +94,7 @@ BasicDrawableRef BasicDrawableBuilderMTL::getDrawable() { if (!basicDraw) - return NULL; + return nullptr; BasicDrawableMTLRef draw = std::dynamic_pointer_cast(basicDraw); @@ -123,5 +127,5 @@ return draw; } - + } diff --git a/ios/library/WhirlyGlobeLib/src/BasicDrawableInstanceMTL.mm b/ios/library/WhirlyGlobeLib/src/BasicDrawableInstanceMTL.mm index 7b5dd95aa1..e9b984ec5b 100644 --- a/ios/library/WhirlyGlobeLib/src/BasicDrawableInstanceMTL.mm +++ b/ios/library/WhirlyGlobeLib/src/BasicDrawableInstanceMTL.mm @@ -58,38 +58,44 @@ if (instanceStyle == LocalStyle) { bzero(&uniMI,sizeof(uniMI)); - - // Set up the instances in their own array - std::vector insts(instances.size()); - for (int which = 0;which < instances.size();which++) { - auto &inst = instances[which]; - auto &outInst = insts[which]; - - // Color override - if (inst.colorOverride) { - uniMI.useInstanceColor = true; - float colors[4]; - inst.color.asUnitFloats(colors); - CopyIntoMtlFloat4(outInst.color, colors); - } else { - outInst.color[0] = 1.0; outInst.color[1] = 1.0; outInst.color[2] = 1.0; outInst.color[3] = 1.0; + + // In this version we just have the raw data + if (instData && numInstances > 0) { + buffBuild.addData(instData->getRawData(), instData->getLen(), &instBuffer); + numInst = numInstances; + } else { + // Set up the instances in their own array + std::vector insts(instances.size()); + for (int which = 0;which < instances.size();which++) { + auto &inst = instances[which]; + auto &outInst = insts[which]; + + // Color override + if (inst.colorOverride) { + uniMI.useInstanceColor = true; + float colors[4]; + inst.color.asUnitFloats(colors); + CopyIntoMtlFloat4(outInst.color, colors); + } else { + outInst.color[0] = 1.0; outInst.color[1] = 1.0; outInst.color[2] = 1.0; outInst.color[3] = 1.0; + } + + // Center + CopyIntoMtlFloat3(outInst.center, inst.center); + + // Rotation/translation/scale + CopyIntoMtlFloat4x4(outInst.mat, inst.mat); + + // EndCenter/direction + Point3d dir = moving ? (inst.endCenter - inst.center)/inst.duration : Point3d(0.0,0.0,0.0); + CopyIntoMtlFloat3(outInst.dir, dir); + uniMI.hasMotion |= moving; } - - // Center - CopyIntoMtlFloat3(outInst.center, inst.center); - - // Rotation/translation/scale - CopyIntoMtlFloat4x4(outInst.mat, inst.mat); - - // EndCenter/direction - Point3d dir = moving ? (inst.endCenter - inst.center)/inst.duration : Point3d(0.0,0.0,0.0); - CopyIntoMtlFloat3(outInst.dir, dir); - uniMI.hasMotion |= moving; - } - int bufferSize = sizeof(WhirlyKitShader::VertexTriModelInstance) * insts.size(); - buffBuild.addData(&insts[0], bufferSize, &instBuffer); - numInst = insts.size(); + int bufferSize = sizeof(WhirlyKitShader::VertexTriModelInstance) * insts.size(); + buffBuild.addData(&insts[0], bufferSize, &instBuffer); + numInst = insts.size(); + } } else if (instanceStyle == GPUStyle) { // Basic values for the uniforms bzero(&uniMI,sizeof(uniMI)); @@ -99,7 +105,7 @@ } // Build the argument buffers with all their attendent memory, ready to copy into - setupArgBuffers(setupInfo->mtlDevice,setupInfo,scene,buffBuild); + setupArgBuffers(setupInfo->mtlDevice,setupInfo,scene,&buffBuild); // And let's fault in the vertex descriptor as well ProgramMTL *program = (ProgramMTL *)scene->getProgram(programID); @@ -201,14 +207,20 @@ return renderState; } -void BasicDrawableInstanceMTL::setupArgBuffers(id mtlDevice,RenderSetupInfoMTL *setupInfo,SceneMTL *scene,BufferBuilderMTL &buffBuild) +void BasicDrawableInstanceMTL::setupArgBuffers(id mtlDevice,RenderSetupInfoMTL *setupInfo,SceneMTL *scene,BufferBuilderMTL *buffBuild) { ProgramMTL *prog = (ProgramMTL *)scene->getProgram(programID); if (!prog) { NSLog(@"Missing program in BasicDrawableInstanceMTL"); return; } - + + // GPU frame capture doesn't like when we stuff an argument buffer with textures inside another buffer + BufferBuilderMTL *texBuffBuild = NULL; +#if !DEBUG + texBuffBuild = buffBuild; +#endif + // All of these are optional, but here's what we're expecting // Uniforms // Lighting @@ -236,7 +248,7 @@ setupInfo, prog->vertFunc, WhirlyKitShader::WKSVertTextureArgBuffer, - buffBuild); + texBuffBuild); } if (prog->fragFunc) { fragABInfo = std::make_shared(mtlDevice, @@ -251,7 +263,7 @@ setupInfo, prog->fragFunc, WhirlyKitShader::WKSFragTextureArgBuffer, - buffBuild); + texBuffBuild); } } diff --git a/ios/library/WhirlyGlobeLib/src/BasicDrawableMTL.mm b/ios/library/WhirlyGlobeLib/src/BasicDrawableMTL.mm index 7940c805ab..3a1b1b80bd 100644 --- a/ios/library/WhirlyGlobeLib/src/BasicDrawableMTL.mm +++ b/ios/library/WhirlyGlobeLib/src/BasicDrawableMTL.mm @@ -87,7 +87,7 @@ } // Build the argument buffers with all their attendent memory, ready to copy into - setupArgBuffers(setupInfo->mtlDevice,setupInfo,scene,buffBuild); + setupArgBuffers(setupInfo->mtlDevice,setupInfo,scene,&buffBuild); // And let's fault in the vertex descriptor as well ProgramMTL *program = (ProgramMTL *)scene->getProgram(programId); @@ -329,12 +329,18 @@ const static std::string hasLighting("hasLighting"); } -void BasicDrawableMTL::setupArgBuffers(id mtlDevice,RenderSetupInfoMTL *setupInfo,SceneMTL *scene,BufferBuilderMTL &buffBuild) +void BasicDrawableMTL::setupArgBuffers(id mtlDevice,RenderSetupInfoMTL *setupInfo,SceneMTL *scene,BufferBuilderMTL *buffBuild) { ProgramMTL *prog = (ProgramMTL *)scene->getProgram(programId); if (!prog) // This happens if we're being used by an instance return; + // GPU frame capture doesn't like when we stuff an argument buffer with textures inside another buffer + BufferBuilderMTL *texBuffBuild = NULL; +#if !DEBUG + texBuffBuild = buffBuild; +#endif + // All of these are optional, but here's what we're expecting // Uniforms // Lighting @@ -363,7 +369,7 @@ setupInfo, prog->vertFunc, WhirlyKitShader::WKSVertTextureArgBuffer, - buffBuild); + texBuffBuild); } if (prog->fragFunc) { fragABInfo = std::make_shared(mtlDevice, @@ -380,7 +386,7 @@ setupInfo, prog->fragFunc, WhirlyKitShader::WKSFragTextureArgBuffer, - buffBuild); + texBuffBuild); } } @@ -622,6 +628,10 @@ [cmdEncode drawPrimitives:MTLPrimitiveTypeLine vertexStart:0 vertexCount:numPts]; break; case Triangles: + if (numTris == 0) { + NSLog(@"BasicDrawableMTL: Found a drawable with no triangles."); + return; + } // This actually draws the triangles (well, in a bit) [cmdEncode drawIndexedPrimitives:MTLPrimitiveTypeTriangle indexCount:numTris*3 indexType:MTLIndexTypeUInt16 indexBuffer:triBuffer.buffer indexBufferOffset:triBuffer.offset]; break; diff --git a/ios/library/WhirlyGlobeLib/src/BillboardDrawableBuilderMTL.mm b/ios/library/WhirlyGlobeLib/src/BillboardDrawableBuilderMTL.mm index c0c4b7896d..fe85bee692 100644 --- a/ios/library/WhirlyGlobeLib/src/BillboardDrawableBuilderMTL.mm +++ b/ios/library/WhirlyGlobeLib/src/BillboardDrawableBuilderMTL.mm @@ -1,9 +1,8 @@ -/* - * BillboardDrawableBuilderMTL.mm +/* BillboardDrawableBuilderMTL.mm * WhirlyGlobeLib * * Created by Steve Gifford on 5/16/19. - * Copyright 2011-2019 mousebird consulting + * Copyright 2011-2021 mousebird consulting * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -15,7 +14,6 @@ * 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. - * */ #import "BillboardDrawableBuilderMTL.h" diff --git a/ios/library/WhirlyGlobeLib/src/ComponentManager_iOS.mm b/ios/library/WhirlyGlobeLib/src/ComponentManager_iOS.mm index 3b82ef5c54..b58e33dfa5 100644 --- a/ios/library/WhirlyGlobeLib/src/ComponentManager_iOS.mm +++ b/ios/library/WhirlyGlobeLib/src/ComponentManager_iOS.mm @@ -3,7 +3,7 @@ * WhirlyGlobeLib * * Created by Steve Gifford on 2/15/19. - * Copyright 2011-2019 mousebird consulting + * Copyright 2011-2021 mousebird consulting * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -21,19 +21,26 @@ #import #import "ComponentManager_iOS.h" #import "WhirlyKitLog.h" +#import "NSString+Stuff.h" +#import "Dictionary_NSDictionary.h" namespace WhirlyKit { - + ComponentObject_iOS::ComponentObject_iOS() { } +ComponentObject_iOS::ComponentObject_iOS(bool enable, bool isSelectable, const NSDictionary *_Nullable desc) : + ComponentObject(enable, isSelectable, iosDictionary(desc)) +{ +} + // The scene wants a component manager early in the process // This gives it an iOS specific one -ComponentManager *MakeComponentManager() +ComponentManagerRef MakeComponentManager() { - return new ComponentManager_iOS(); + return std::make_shared(); } ComponentManager_iOS::ComponentManager_iOS() @@ -59,13 +66,19 @@ return nil; } -ComponentObjectRef ComponentManager_iOS::makeComponentObject() +ComponentObjectRef ComponentManager_iOS::makeComponentObject(const Dictionary *desc) { - return ComponentObject_iOSRef(new ComponentObject_iOS()); + NSDictionary *nsDesc = desc ? [NSMutableDictionary fromDictionaryCPointer:desc] : nil; + return std::make_shared(/*enabled=*/false, /*isSelectable=*/false, nsDesc); } void ComponentManager_iOS::removeSelectObjects(SimpleIDSet selIDs) { + if (selIDs.empty()) + { + return; + } + std::lock_guard guardLock(selectLock); for (auto selID : selIDs) { @@ -85,9 +98,13 @@ SimpleIDSet selectIDs; - for (auto compID: compIDs) { - auto it = compObjs.find(compID); - selectIDs.insert(it->second->selectIDs.begin(),it->second->selectIDs.end()); + for (auto compID: compIDs) + { + const auto it = compObjsById.find(compID); + if (it != compObjsById.end() && it->second) + { + selectIDs.insert(it->second->selectIDs.begin(),it->second->selectIDs.end()); + } } removeSelectObjects(selectIDs); @@ -95,7 +112,7 @@ ComponentManager::removeComponentObjects(threadInfo,compIDs, changes); } - + void ComponentManager_iOS::clear() { std::lock_guard guardLock(selectLock); @@ -105,7 +122,10 @@ void ComponentManager_iOS::dumpStats() { std::lock_guard guardLock(selectLock); - wkLogLevel(Debug, "Component Objects: %d",selectObjectSet.size()); + wkLogLevel(Debug, "Component Objects: %d",compObjsById.size()); + wkLogLevel(Debug, "Selectable Objects: %d",selectObjectSet.size()); + wkLogLevel(Debug, "Objects w/ UUID: %d",compObjsByUUID.size()); + wkLogLevel(Debug, "Representations: %d",representations.size()); } - + } diff --git a/ios/library/WhirlyGlobeLib/src/Dictinary_NSDictionary.mm b/ios/library/WhirlyGlobeLib/src/Dictionary_NSDictionary.mm similarity index 91% rename from ios/library/WhirlyGlobeLib/src/Dictinary_NSDictionary.mm rename to ios/library/WhirlyGlobeLib/src/Dictionary_NSDictionary.mm index 4a85f08ea8..5a75b06cd8 100644 --- a/ios/library/WhirlyGlobeLib/src/Dictinary_NSDictionary.mm +++ b/ios/library/WhirlyGlobeLib/src/Dictionary_NSDictionary.mm @@ -146,14 +146,14 @@ MutableDictionaryRef MutableDictionaryMake() dict = [[NSDictionary alloc] init]; } -iosDictionary::iosDictionary(NSDictionary *inDict) +iosDictionary::iosDictionary(const NSDictionary *inDict) { dict = inDict; } iosDictionary::iosDictionary(const iosDictionary &that) { - dict = [[NSDictionary alloc] initWithDictionary:that.dict]; + dict = [[NSDictionary alloc] initWithDictionary:const_cast(that.dict)]; } iosDictionary::~iosDictionary() @@ -161,6 +161,11 @@ MutableDictionaryRef MutableDictionaryMake() dict = nil; } +int iosDictionary::count() const +{ + return dict.count; +} + /// Returns true if the field exists bool iosDictionary::hasField(const std::string &name) const { @@ -313,18 +318,28 @@ MutableDictionaryRef MutableDictionaryMake() dict = [[NSMutableDictionary alloc] init]; } +iosMutableDictionary::iosMutableDictionary(const NSDictionary *inDict) +{ + dict = inDict ? [NSMutableDictionary dictionaryWithDictionary:const_cast(inDict)] : [NSMutableDictionary new]; +} + iosMutableDictionary::iosMutableDictionary(NSMutableDictionary *inDict) { dict = inDict; } - -iosMutableDictionary::iosMutableDictionary(MutableDictionaryRef inDict) + +iosMutableDictionary::iosMutableDictionary(const MutableDictionaryRef &inDict) { iosMutableDictionary *other = dynamic_cast(inDict.get()); if (other) dict = [[NSMutableDictionary alloc] initWithDictionary:other->dict]; } +int iosMutableDictionary::count() const +{ + return dict.count; +} + // Assignment operator iosMutableDictionary & iosMutableDictionary::operator = (const iosMutableDictionary &that) { @@ -499,6 +514,12 @@ MutableDictionaryRef MutableDictionaryMake() dict[StdStringToString(name)] = @(val); } +/// Set field as int +void iosMutableDictionary::setInt64(const std::string &name,int64_t val) +{ + dict[StdStringToString(name)] = @(val); +} + /// Set field as 64 bit unique value void iosMutableDictionary::setIdentifiable(const std::string &name,SimpleIdentity val) { @@ -522,7 +543,7 @@ MutableDictionaryRef MutableDictionaryMake() { const iosDictionary *iosDict = dynamic_cast(other); if (iosDict) { - [dict addEntriesFromDictionary:iosDict->dict]; + [dict addEntriesFromDictionary:const_cast(iosDict->dict)]; } else { const iosMutableDictionary *iosMutDict = dynamic_cast(other); if (iosMutDict) { @@ -605,7 +626,7 @@ - (MutableDictionaryCRef) toDictionaryC dict->setInt(keyStr, [val intValue]); break; default: - NSLog(@"Unsupported type found in NSDictionary toDictionaryC for key %@",key); + NSLog(@"Unsupported type %d found in NSDictionary toDictionaryC for key %@",(int)cType,key); break; } } else if ([val isKindOfClass:[NSString class]]) { @@ -623,8 +644,8 @@ - (MutableDictionaryCRef) toDictionaryC auto valDict = [(NSDictionary *)val toDictionaryC]; if (valDict) dict->setDict(keyStr, valDict); - } else { - NSLog(@"Unsupported type found in NSDictionary toDictionaryC for key %@",key); + } else if (val != nil && ![val isKindOfClass:[NSNull class]]) { + NSLog(@"Unsupported type %@ found in NSDictionary toDictionaryC for key %@",[val class],key); } } @@ -670,24 +691,33 @@ + (id) fromEntry:(const DictionaryEntryRef &)entry return nil; } -+ (NSMutableDictionary *) fromDictionaryCPointer:(const WhirlyKit::MutableDictionary *)inDict ++ (NSMutableDictionary *) fromDictionaryC:(const WhirlyKit::DictionaryRef &)dict +{ + return dict ? [NSMutableDictionary fromDictionaryCPointer:dict.get()] : [NSMutableDictionary new]; +} + ++ (NSMutableDictionary *) fromMutableDictionaryC:(const WhirlyKit::MutableDictionaryRef &)dict +{ + return dict ? [NSMutableDictionary fromDictionaryCPointer:dict.get()] : [NSMutableDictionary new]; +} + ++ (NSMutableDictionary *) fromDictionaryCPointer:(const WhirlyKit::Dictionary *)inDict { NSMutableDictionary *outDict = [[NSMutableDictionary alloc] init]; - - auto keys = inDict->getKeys(); - for (const auto &key : keys) { - NSString *keyStr = [NSString stringWithUTF8String:key.c_str()]; - auto entry = inDict->getEntry(key); - id valEntry = [NSMutableDictionary fromEntry:entry]; - outDict[keyStr] = valEntry; + + if (inDict) + { + auto keys = inDict->getKeys(); + for (const auto &key : keys) + { + NSString *keyStr = [NSString stringWithUTF8String:key.c_str()]; + auto entry = inDict->getEntry(key); + id valEntry = [NSMutableDictionary fromEntry:entry]; + outDict[keyStr] = valEntry; + } } return outDict; } -+ (NSMutableDictionary *) fromDictionaryC:(WhirlyKit::MutableDictionaryRef)inDict -{ - return [NSMutableDictionary fromDictionaryCPointer:inDict.get()]; -} - @end diff --git a/ios/library/WhirlyGlobeLib/src/DrawableMTL.mm b/ios/library/WhirlyGlobeLib/src/DrawableMTL.mm index d3c663dd0e..1f4a7b4808 100644 --- a/ios/library/WhirlyGlobeLib/src/DrawableMTL.mm +++ b/ios/library/WhirlyGlobeLib/src/DrawableMTL.mm @@ -25,7 +25,7 @@ namespace WhirlyKit { -ArgBuffContentsMTL::ArgBuffContentsMTL(id mtlDevice,RenderSetupInfoMTL *inSetupInfoMTL,id func,int bufferArgIdx,BufferBuilderMTL &buffBuild) +ArgBuffContentsMTL::ArgBuffContentsMTL(id mtlDevice,RenderSetupInfoMTL *inSetupInfoMTL,id func,int bufferArgIdx,BufferBuilderMTL *buffBuild) { valid = false; setupInfoMTL = inSetupInfoMTL; @@ -54,7 +54,8 @@ } // Create a buffer to store the arguments in - buffBuild.reserveData([encode encodedLength], &buff); + if (buffBuild) + buffBuild->reserveData([encode encodedLength], &buff); isSetup = true; valid = true; @@ -107,11 +108,13 @@ resources.addEntry(tmpBuff); } -ArgBuffRegularTexturesMTL::ArgBuffRegularTexturesMTL(id mtlDevice, RenderSetupInfoMTL *setupInfoMTL, id mtlFunction, int bufferArgIdx, BufferBuilderMTL &buildBuff) +ArgBuffRegularTexturesMTL::ArgBuffRegularTexturesMTL(id mtlDevice, RenderSetupInfoMTL *setupInfoMTL, id mtlFunction, int bufferArgIdx, BufferBuilderMTL *buildBuff) { encode = [mtlFunction newArgumentEncoderWithBufferIndex:bufferArgIdx]; size = [encode encodedLength]; - buildBuff.reserveData(size, &buffer); + buffer = setupInfoMTL->heapManage.allocateBuffer(HeapManagerMTL::HeapType::Drawable, size); + if (buildBuff) + buildBuff->reserveData(size, &buffer); } void ArgBuffRegularTexturesMTL::addTexture(const Point2f &offset,const Point2f &scale,id tex) @@ -132,7 +135,7 @@ memcpy([encode constantDataAtIndex:WKSTexBuffIndirectScale], &scales[0], sizeof(float)*2*scales.size()); // Then the textures, which are largely opaque - int texturesPresent = 0; + uint32_t texturesPresent = 0; for (unsigned int ii=0;ii tex = ii>=texs.size() ? nil : texs[ii]; [encode setTexture:tex atIndex:WKSTexBuffTextures+ii]; @@ -141,7 +144,7 @@ texturesPresent |= (1<(&that)) + { + return operator<(*p); + } + assert(!"Unexpected type passed to comparison"); + return false; +} + bool FontManager_iOS::operator < (const FontManager_iOS &that) const { return font < that.font; } - + FontTextureManager_iOS::FontTextureManager_iOS(SceneRenderer *sceneRender,Scene *scene) : FontTextureManager(sceneRender,scene) { @@ -58,7 +68,15 @@ FontTextureManager_iOS::~FontTextureManager_iOS() { } - + +void FontTextureManager_iOS::teardown(PlatformThreadInfo* inst) +{ + for (const auto& kv : fontManagers) { + kv.second->teardown(inst); + } + fontManagers.clear(); +} + // Look for an existing font that will match the UIFont given FontManager_iOSRef FontTextureManager_iOS::findFontManagerForFont(UIFont *uiFont,UIColor *colorUI,UIColor *backColorUI,UIColor *outlineColorUI,float outlineSize) { @@ -86,7 +104,7 @@ CTFontRef font; uiFont = [UIFont fontWithDescriptor:uiFont.fontDescriptor size:pointSize]; font = (__bridge CTFontRef)uiFont; - FontManager_iOSRef fm = FontManager_iOSRef(new FontManager_iOS(font)); + auto fm = std::make_shared(font); fm->fontName = fontName; fm->color = color; fm->colorUI = colorUI; diff --git a/ios/library/WhirlyGlobeLib/src/GeographicLib.mm b/ios/library/WhirlyGlobeLib/src/GeographicLib.mm new file mode 100644 index 0000000000..52b8a4dc8f --- /dev/null +++ b/ios/library/WhirlyGlobeLib/src/GeographicLib.mm @@ -0,0 +1,644 @@ +// +// geowrap.c +// WhirlyGlobeLib +// +// Created by Tim Sylvester on 12/14/20. +// Copyright © 2020 mousebird consulting. All rights reserved. +// + +#import "GeographicLib.h" +#import "CoordSystem.h" +#import "WhirlyGeometry.h" + +#import "GeographicLib/Geodesic.hpp" +#import "GeographicLib/Geocentric.hpp" + +#import +#import + +#if !defined(M_2PI) +# define M_2PI (2 * M_PI) +#endif + +typedef WhirlyKit::Point3d Point3d; + +namespace { + // Generic geodesic initialized for WGS84 ellipsoid. + // We assume this is thread-safe because we only read from it. + static const GeographicLib::Geodesic &wgs84Geodesic = GeographicLib::Geodesic::WGS84(); + static const GeographicLib::Geocentric &wgs84Geocentric = GeographicLib::Geocentric::WGS84(); +} + +MaplyCoordinateD GeoLibCalcDirectF(MaplyCoordinate origin, double azimuth, double distance) +{ + return GeoLibCalcDirectD(MaplyCoordinateD { origin.x, origin.y }, azimuth, distance); +} + +MaplyCoordinateD GeoLibCalcDirectD(MaplyCoordinateD origin, double azimuthRadians, double distanceMeters) +{ + const auto lat1 = WhirlyKit::RadToDeg(origin.y); + const auto lon1 = WhirlyKit::RadToDeg(origin.x); + const auto azDeg = WhirlyKit::RadToDeg(azimuthRadians); + + double lat2 = 0.0, lon2 = 0.0; + const auto res = wgs84Geodesic.Direct(lat1, lon1, azDeg, distanceMeters, lat2, lon2); + + return std::isfinite(res) + ? MaplyCoordinateD { WhirlyKit::DegToRad(lon2), WhirlyKit::DegToRad(lat2) } + : MaplyCoordinateD { std::numeric_limits::quiet_NaN(), std::numeric_limits::quiet_NaN() }; +} + +namespace { + template + static GeoLibInv CalcInv(T p1, T p2) + { + const auto lat1 = WhirlyKit::RadToDeg(p1.y); + const auto lon1 = WhirlyKit::RadToDeg(p1.x); + const auto lat2 = WhirlyKit::RadToDeg(p2.y); + const auto lon2 = WhirlyKit::RadToDeg(p2.x); + double dist = 0.0, az1 = 0.0, az2 = 0.0; + wgs84Geodesic.Inverse(lat1, lon1, lat2, lon2, dist, az1, az2); + return GeoLibInv { dist, WhirlyKit::DegToRad(az1), WhirlyKit::DegToRad(az2) }; + } +} + +GeoLibInv GeoLibCalcInverseF(MaplyCoordinate p1, MaplyCoordinate p2) { return CalcInv(p1, p2); } +GeoLibInv GeoLibCalcInverseD(MaplyCoordinateD p1, MaplyCoordinateD p2) { return CalcInv(p1, p2); } + +namespace { + template + static bool InPoly(TPoint p, const TPolyPt poly[], unsigned count) { + if (count < 2) + { + return false; + } + // TODO: do this without needing to make a copy of the points + WhirlyKit::Point2dVector pts; + pts.reserve(count + 1); + for (unsigned i = 0; i < count; ++i) { + pts.emplace_back(poly[i].x, poly[i].y); + } + // Ensure that the polygon is closed + const auto &f = pts.front(), &b = pts.back(); + if (f.x() != b.x() || f.y() != b.y()) + { + pts.push_back(f); + } + return WhirlyKit::PointInPolygon(WhirlyKit::Point2d(p.x, p.y), pts); + } + + static inline Point3d CoordToGeocentric(const MaplyCoordinateD &p, double geoidHeight = 0.0) + { + double x = 0, y = 0, z = 0; + wgs84Geocentric.Forward(WhirlyKit::RadToDeg(p.y), WhirlyKit::RadToDeg(p.x), geoidHeight, x, y, z); + return {x,y,z}; + } + + static inline MaplyCoordinateD GeocentricToCoord(const Point3d &p) + { + double lat = 0, lon = 0, height = 0; + wgs84Geocentric.Reverse(p.x(), p.y(), p.z(), lat, lon, height); + return { WhirlyKit::DegToRad(lon), WhirlyKit::DegToRad(lat) }; + } +} + +bool MaplyCoordinateInPolygon(MaplyCoordinate p, const MaplyCoordinate polygon[], unsigned count) { return InPoly(p, polygon, count); } +bool MaplyCoordinateDInPolygon(MaplyCoordinateD p, const MaplyCoordinate polygon[], unsigned count) { return InPoly(p, polygon, count); } +bool MaplyCoordinateInPolygonD(MaplyCoordinate p, const MaplyCoordinateD polygon[], unsigned count) { return InPoly(p, polygon, count); } +bool MaplyCoordinateDInPolygonD(MaplyCoordinateD p, const MaplyCoordinateD polygon[], unsigned count) { return InPoly(p, polygon, count); } + + +namespace { + + template + inline static void combine(double* eqA, double* eqB) + { + if (eqB[0] == 0.0) + { + return; + } + if (std::fabs(eqA[0]) >= std::fabs(eqB[0])) + { + auto const f = eqB[0] / eqA[0]; + eqB[0] = 0; + for (auto i = 1; i < N; ++i) + { + eqB[i] -= f * eqA[i]; + } + } + else + { + auto const f = eqA[0] / eqB[0]; + eqA[0] = eqB[0]; + eqB[0] = 0; + for (auto i = 1; i < N; ++i) + { + std::swap(eqA[i], eqB[i]); + eqB[i] -= f * eqA[i]; + } + } + } + + // for n = 3, partially solve, zeroing the coefficients below the diagonal. + inline static void solveLinear3A(double* eq0, double* eq1, double* eq2) + { + combine<4>(eq1, eq2); + combine<4>(eq0, eq1); + combine<3>(eq1 + 1, eq2 + 1); + + if (eq2[3] != 0.0) + { + eq2[3] = (eq2[2] == 0 ? INFINITY : eq2[3] / eq2[2]); + } + eq2[2] = 1; + } + + // Complete the solution started by solveLinear3Lower + inline static void solveLinear3B(double* eq0, double* eq1, double* eq2) + { + eq1[3] -= eq1[2] * eq2[3]; + eq1[2] = 0; + if (eq1[3] != 0.0) + { + eq1[3] = (eq1[1] == 0.0 ? INFINITY : eq1[3] / eq1[1]); + } + eq1[1] = 1; + eq0[3] -= eq0[2] * eq2[3] + eq0[1] * eq1[3]; + eq0[1] = 0; + eq0[2] = 0; + if (eq0[3] != 0) + { + eq0[3] = (eq0[0] == 0.0 ? INFINITY : eq0[3] / eq0[0]); + } + eq0[0] = 1; + } + + // Project a vector onto the ellipsoid surface + static Point3d project(const Point3d &p, const GeographicLib::Geodesic &geo) + { + const auto re = geo.EquatorialRadius(); + const auto rp = re - re * geo.Flattening(); + const auto x = p.x(); + const auto y = p.y(); + const auto z = p.z(); + const auto r = std::sqrt((x * x + y * y) / (re * re) + z * z / (rp * rp)); + return (r == 0) ? Point3d{ rp, y, z } : // it's a pole + Point3d{ x / r, y / r, z / r }; // anything else + } + + const static Point3d ptZero = { 0.0, 0.0, 0.0 }; + + // Find the intersection of geodesics A-B and C-D. + // Returns the intersection point iff CalcInt==true + template + static inline std::tuple intersection( + const Point3d &a, const Point3d &b, const Point3d &c, const Point3d &d, + const GeographicLib::Geodesic &geo) + { + // Set equal the equations defining the two geodesics, as defined by the + // intersection of the ellipsoid and a plane through its center, and solve + // the resulting system of equations, yielding two antipodal solutions. + // + // ((x4 - x3) * s + x3) * t = (x2 - x1) * r + x1 + // ((y4 - y3) * s + y3) * t = (y2 - y1) * r + y1 + // ((z4 - z3) * s + z3) * t = (z2 - z1) * r + z1 + + double e0[4] = { d.x() - c.x(), c.x(), a.x() - b.x(), a.x() }; + double e1[4] = { d.y() - c.y(), c.y(), a.y() - b.y(), a.y() }; + double e2[4] = { d.z() - c.z(), c.z(), a.z() - b.z(), a.z() }; + solveLinear3A(e0, e1, e2); + + const auto r = e2[3]; + if (r >= 0.0 && r <= 1.0) + { + solveLinear3B(e0, e1, e2); + + // Solution on the right side, and within the segments? + const auto t = e1[3]; + if (t > 0.0) + { + const auto s = e0[3] / t; + if (s >= 0.0 && s <= 1.0) + { + // Yes! Project the solution vector onto the ellipsoid + return std::make_tuple(true, + CalcInt ? project((b - a) * r + a, geo) : ptZero); + } + } + } + + return std::make_tuple(false, ptZero); + } + +} + +GeoLibInt GeoLibIntersectD(MaplyCoordinateD a, MaplyCoordinateD b, MaplyCoordinateD c, MaplyCoordinateD d) +{ + const auto gca = CoordToGeocentric(a); + const auto gcb = CoordToGeocentric(b); + const auto gcc = CoordToGeocentric(c); + const auto gcd = CoordToGeocentric(d); + + bool res; + Point3d p; + std::tie(res,p) = intersection(gca, gcb, gcc, gcd, wgs84Geodesic); + + return res ? GeoLibInt{ GeocentricToCoord(p), true } : GeoLibInt{ { 0, 0 }, false }; +} + +bool GeoLibLineDIntersectsPolygonD(MaplyCoordinateD startPt, MaplyCoordinateD endPt, const MaplyCoordinateD points[], unsigned count) +{ + if (count < 2) + { + return false; + } + + const auto gcStart = CoordToGeocentric(startPt); + const auto gcEnd = CoordToGeocentric(endPt); + + Point3d gca = CoordToGeocentric(points[0]); + Point3d gcb; + + for (unsigned i = 0; i < count; ++i) + { + const auto& p = points[(i + 1) % count]; + + // Alternate with each new point so we only convert each point once, and we don't care about direction + ((i & 1) ? gca : gcb) = CoordToGeocentric(p); + + if (gca != gcb) + { + if (std::get<0>(intersection(gcStart, gcEnd, gca, gcb, wgs84Geodesic))) + { + return true; + } + } + } + return false; +} + +double GeoLibDistanceD(MaplyCoordinateD startPt, MaplyCoordinateD endPt) +{ + double s12 = 0.0; + wgs84Geodesic.Inverse(WhirlyKit::RadToDeg(startPt.y), + WhirlyKit::RadToDeg(startPt.x), + WhirlyKit::RadToDeg(endPt.y), + WhirlyKit::RadToDeg(endPt.x), + s12); + return s12; +} + +namespace { + + // Project a geocentric point into the orthographic projection defined by the origin + static Point3d projectOrtho(const Point3d &origin, const Point3d &p) + { + const double sinLat = origin.z(); + const double cosLat = std::sqrt(origin.x() * origin.x() + origin.y() * origin.y()); + const double sinLon = origin.x() / cosLat; + const double cosLon = -origin.y() / cosLat; + const double x1 = p.x() * cosLon + p.y() * sinLon; + const double y1 = -p.x() * sinLon + p.y() * cosLon; + return { x1, y1 * sinLat + p.z() * cosLat, -y1 * cosLat + p.z() * sinLat }; + } + + // Invert orthographic projection + static Point3d unprojectOrtho(const Point3d &origin, const Point3d &p) + { + const double sinLat = origin.z(); + const double cosLat = std::sqrt(origin.x() * origin.x() + origin.y() * origin.y()); + const double sinLon = origin.x() / cosLat; + const double cosLon = -origin.y() / cosLat; + const double y1 = p.y() * sinLat - p.z() * cosLat; + const double z1 = p.y() * cosLat + p.z() * sinLat; + return { p.x() * cosLon - y1 * sinLon, p.x() * sinLon + y1 * cosLon, z1 }; + } + + static const double sinpi4 = std::sin(M_PI_4); + + // Calculate the angle between two vectors. + // Both vectors must be normalized! + static double angle(Point3d a, Point3d b) + { + const double dp = a.dot(b); + if (std::fabs(dp) < sinpi4) + { + return std::acos(dp); + } + + const auto m = a.cross(b).norm(); + return (dp < 0) ? M_PI - std::asin(m) : std::asin(m); + } + + // TODO: This isn't fully accounting for eccentricity, so it's not super precise. + static GeoLibOrthoDist _GeoLibOrthoDistD(const Point3d &gca, const Point3d &gcb, const Point3d &gcc) + { + // Calculate the unit normal of the geodesic segment + const auto geoNorm = gca.cross(gcb).normalized(); + + // Calculate the unit normal of that and the point of interest + const auto orthoNorm = gcc.cross(geoNorm).normalized(); + + // Find the the point where the line to the target point is perpendicular + const auto cp = geoNorm.cross(orthoNorm); + + // Calculate the angles along and aside the segment + const auto t0 = angle(gca, gcb); + const auto t1 = angle(cp, gca); + const auto t2 = angle(cp, gcc); + + // Work out which quadrant we're in and fix the signs + const auto s1 = (cp.dot(geoNorm.cross(gca)) < 0) ? -1. : 1.; + const auto s2 = (gcc.dot(geoNorm) > 0) ? -1. : 1.; + + // Convert the angles to distances + const auto rad = GeographicLib::Constants::WGS84_a(); + return { rad * t1 * s1, rad * t2 * s2, rad * t0 }; + } + + static double initialHeadingD(const Point3d &startPt, const Point3d &endPt) + { + // Find the location of the endpoint in the orthographic projection defined by the start point + const auto end = projectOrtho(startPt, endPt); + + // If x==y==0 they are the same point and the heading is undefined + return std::atan2(end.x(), end.y()); + } + //static double finalHeadingD(const Point3d &startPt, const Point3d &endPt) + //{ + // return std::fmod(initialHeadingD(endPt, startPt) + M_2PI, M_2PI); + //} + + static Point3d orthoDirect(const Point3d &start, double azimuthRad, double distMeters) + { + const double theta = distMeters / wgs84Geodesic.EquatorialRadius(); + const double r = sin(theta); + const double hdg = M_PI_2 - azimuthRad; + return unprojectOrtho(start, { r * std::cos(hdg), r * std::sin(hdg), std::cos(theta) }); + } +} + + +double GeoLibInitialHeadingD(MaplyCoordinateD startPt, MaplyCoordinateD endPt) +{ + const auto gcStart = CoordToGeocentric(startPt); + const auto gcEnd = CoordToGeocentric(endPt); + return initialHeadingD(gcStart, gcEnd); +} + +bool GeoLibLineDIntersectsCircleD(MaplyCoordinateD startPt, MaplyCoordinateD endPt, MaplyCoordinateD center, double radiusMeters) +{ + // If either endpoint of the line is within the circle, we're done. + // TODO: Is this fast enough to be worthwhile, when we get the same info below? + if (GeoLibDistanceD(startPt, center) <= radiusMeters || + GeoLibDistanceD(endPt, center) <= radiusMeters) + { + return true; + } + + // Find the point on the line closest to the circle center. + // If that point is within the length of the segment and within the radius, they intersect. + // If it's within the radius, then the line intersects the circle. + auto const res = GeoLibOrthoDistD(startPt, endPt, center); + return (0 < res.downtrackDistance && res.downtrackDistance < res.segmentLength && + std::fabs(res.crosstrackDistance) <= radiusMeters); +} + +GeoLibIntPair GeoLibLineDIntersectCircleD(MaplyCoordinateD startPt, MaplyCoordinateD endPt, MaplyCoordinateD center, double radiusMeters) +{ + const auto gcStart = CoordToGeocentric(startPt).normalized(); + const auto gcEnd = CoordToGeocentric(endPt).normalized(); + const auto gcCenter = CoordToGeocentric(center).normalized(); + + GeoLibIntPair result = { {{ 0.0, 0.0 }, { 0.0, 0.0 }}, { 0.0, 0.0 }, 0 }; + + auto const res = _GeoLibOrthoDistD(gcStart, gcEnd, gcCenter); + if (res.downtrackDistance < -radiusMeters || // preceeds the GC segment by more than the radius + res.downtrackDistance > res.segmentLength + radiusMeters || // follows the GC segment by more than the radius + std::fabs(res.crosstrackDistance) > radiusMeters) // to the side of the GC segment by more than the radius + { + return result; + } + + // Use the right-triangle-simplified law of cosines for triangles on a sphere + + const double earthRad = wgs84Geodesic.EquatorialRadius(); + const double a = earthRad * std::acos(std::cos(radiusMeters / earthRad) / + std::cos(std::fabs(res.crosstrackDistance) / earthRad)); + + // The intersections must be symmetric around the perpendicular intercept + const double dist1 = res.downtrackDistance + a; + const double dist2 = res.downtrackDistance - a; + + const double hdg = initialHeadingD(gcStart, gcEnd); + + if (dist1 >= 0.0 && dist1 <= res.segmentLength) + { + result.distances[0] = dist1; + result.intersections[0] = GeocentricToCoord(orthoDirect(gcStart, hdg, dist1)); + result.count += 1; + } + if (dist2 >= 0.0 && dist2 <= res.segmentLength) + { + result.distances[result.count] = dist2; + result.intersections[result.count] = GeocentricToCoord(orthoDirect(gcStart, hdg, dist2)); + result.count += 1; + } + + return result; +} + +namespace { + +} + +GeoLibOrthoDist GeoLibOrthoDistD(MaplyCoordinateD a, MaplyCoordinateD b, MaplyCoordinateD c) +{ + const auto gca = CoordToGeocentric(a).normalized(); + const auto gcb = CoordToGeocentric(b).normalized(); + const auto gcc = CoordToGeocentric(c).normalized(); + return _GeoLibOrthoDistD(gca, gcb, gcc); +} + +namespace { + + static inline double normalizeAzimuth(double a) + { + a = std::fmod(a, M_2PI); + while (a < 0.0) { + a += M_2PI; + } + return a; + } + + static inline double arcSpan(double beginAzimuthRad, double endAzimuthRad, bool clockwise) + { + if (beginAzimuthRad == endAzimuthRad) + { + // special case for a full circle + return clockwise ? M_2PI : -M_2PI; + } + // Account for going the "long way around" + return clockwise ? normalizeAzimuth(endAzimuthRad + M_2PI - beginAzimuthRad) + : -normalizeAzimuth(beginAzimuthRad + M_2PI - endAzimuthRad); + } +} + +double GeoLibSampleArcD(MaplyCoordinateD center, double radiusMeters, + double beginAzimuthRad, double endAzimuthRad, bool clockwise, + MaplyCoordinateD points[], unsigned count) +{ + if (count < 2) + { + return 0.0; + } + + beginAzimuthRad = normalizeAzimuth(beginAzimuthRad); + endAzimuthRad = normalizeAzimuth(endAzimuthRad); + + auto const span = arcSpan(beginAzimuthRad, endAzimuthRad, clockwise); + auto const increment = span / (count - 1); + + double azimuth = beginAzimuthRad; + for (unsigned i = 0; i < count; ++i, azimuth += increment) + { + // Use the exact end angle, in case we introduced any error + if (i == count - 1) + { + azimuth = endAzimuthRad; + } + points[i] = GeoLibCalcDirectD(center, azimuth, radiusMeters); + } + + return std::fabs(increment); +} + +//#define RUN_UNIT_TEST 1 +#if RUN_UNIT_TEST +namespace { + static double d2r(double deg) { return WhirlyKit::DegToRad(deg); } + static MaplyCoordinateD cd(double lat, double lon) { return MaplyCoordinateD{d2r(lon), d2r(lat)}; } + static bool eq(double a, double b, double e) { return std::fabs(a-b) < e; } + static void assertEq(double a, double b, double e) { assert(eq(a,b,e)); } + static struct Test { + Test() { + TestDistance(); + TestLineLineInt(); + TestLinePolyInt(); + TestOrthoDist(); + TestLineCircleInt(); + TestSampleArc(); + } + void TestDistance() { + // Values from 10.5281/zenodo.32156 + assertEq(GeoLibDistanceD(cd(21.219268205986, 0), cd(21.210895095821835985, 0.000470899511575113)), 928.3575608, 1.0e-6); + assertEq(GeoLibDistanceD(cd(27.095351435163, 0), cd(-27.095351435168394093, 179.462448686689092514)), 19977267.8165311, 1.0e-6); + } + void TestLineLineInt() { + // trivial case + const auto a = GeoLibIntersectD(cd(0, -10), cd(0, 10), cd(-10, 0), cd(10, 0)); + assert(a.intersects); + assert(a.intersection.y == 0); + assert(a.intersection.x == 0); + // polar intersection + const auto b = GeoLibIntersectD(cd(80, 180), cd(80, 0), cd(80, 90), cd(80, -90)); + assert(b.intersects); + assert(b.intersection.y == d2r(90.)); + // simple case, reference value from Google Earth + const auto c = GeoLibIntersectD(cd(45, -120), cd(40, -110), cd(45, -110), cd(40, -120)); + assert(c.intersects); + assertEq(c.intersection.y, d2r(42.708939), 1e-6); + assertEq(c.intersection.x, d2r(-115), 1e-6); + } + void TestLinePolyInt() { + const MaplyCoordinateD poly[] = { cd(10, 0), cd(0, 10), cd(-10, 0), cd(0, -10 )}; + assert( GeoLibLineDIntersectsPolygonD(cd(0, 0), cd(10, 10), poly, 4)); + assert( GeoLibLineDIntersectsPolygonD(cd(0, 0), cd(-10, 10), poly, 4)); + assert( GeoLibLineDIntersectsPolygonD(cd(0, 0), cd(10, -10), poly, 4)); + assert( GeoLibLineDIntersectsPolygonD(cd(0, 0), cd(-10, -10), poly, 4)); + assert(!GeoLibLineDIntersectsPolygonD(cd(0, 0), cd(1, 1), poly, 4)); + } + void TestOrthoDist() { + const auto p0 = cd(0,0); + const auto p1 = GeoLibCalcDirectD(p0, /*az=*/0, /*dist=*/1000); + const auto p2 = GeoLibCalcDirectD(p0, /*az=*/0, /*dist=*/500); // half way down + const auto p3 = GeoLibCalcDirectD(p2, /*az=*/M_PI_2, /*dist=*/20); // to the right + + auto res = GeoLibOrthoDistD(p0, p1, p3); + assertEq(res.segmentLength, 1000, 1e-6); + assertEq(res.downtrackDistance, 500, 1e-6); // both positive + assertEq(res.crosstrackDistance, 20, 1e-6); + + const auto p4 = GeoLibCalcDirectD(p2, /*az=*/3 * M_PI_2, /*dist=*/20); // to the left + res = GeoLibOrthoDistD(p0, p1, p4); + assertEq(res.segmentLength, 1000, 1e-6); + assertEq(res.downtrackDistance, 500, 1e-6); + assertEq(res.crosstrackDistance, -20, 1e-6); // crosstrack is negative + + const auto p5 = GeoLibCalcDirectD(p0, /*az=*/M_PI, /*dist=*/500); // go backwards instead + const auto p6 = GeoLibCalcDirectD(p5, /*az=*/M_PI_2, /*dist=*/20); + res = GeoLibOrthoDistD(p0, p1, p6); + assertEq(res.segmentLength, 1000, 1e-6); + assertEq(res.downtrackDistance, -500, 1e-6); // downtrack is negative + assertEq(res.crosstrackDistance, 20, 1e-6); + } + void TestLineCircleInt() { + //bool GeoLibLineDIntersectsCircleD(MaplyCoordinateD startPt, MaplyCoordinateD endPt, MaplyCoordinateD center, double radiusMeters) + assert( GeoLibLineDIntersectsCircleD(cd(0,0), cd(1,1), cd(0,0), 1000)); + assert( GeoLibLineDIntersectsCircleD(cd(0,0.0001), cd(1,1), cd(0,0), 1000)); + assert( GeoLibLineDIntersectsCircleD(cd(1,1), cd(0,0), cd(0,0), 1000)); + assert( GeoLibLineDIntersectsCircleD(cd(1,1), cd(0.0001,0), cd(0,0), 1000)); + assert(!GeoLibLineDIntersectsCircleD(cd(0,1), cd(1,1), cd(0,0), 1000)); + + auto p0 = GeoLibCalcDirectD(cd(0,0), /*az=*/M_PI_2, /*dist=*/1000 - 1e-6); + auto p1 = GeoLibCalcDirectD(p0, /*az=*/0, /*dist=*/100); + auto p2 = GeoLibCalcDirectD(p0, /*az=*/M_PI, /*dist=*/100); + assert( GeoLibLineDIntersectsCircleD(p1, p2, cd(0,0), 1000)); + + p0 = GeoLibCalcDirectD(cd(0,0), /*az=*/M_PI_2, /*dist=*/1000 + 1e-6); + p1 = GeoLibCalcDirectD(p0, /*az=*/0, /*dist=*/100); + p2 = GeoLibCalcDirectD(p0, /*az=*/M_PI, /*dist=*/100); + assert(!GeoLibLineDIntersectsCircleD(p1, p2, cd(0,0), 1000)); + + p0 = GeoLibCalcDirectD(cd(0,0), /*az=*/3*M_PI_2, /*dist=*/1000 - 1e-6); + p1 = GeoLibCalcDirectD(p0, /*az=*/0, /*dist=*/100); + p2 = GeoLibCalcDirectD(p0, /*az=*/M_PI, /*dist=*/100); + assert( GeoLibLineDIntersectsCircleD(p1, p2, cd(0,0), 1000)); + + p0 = GeoLibCalcDirectD(cd(0,0), /*az=*/3*M_PI_2, /*dist=*/1000 + 1e-6); + p1 = GeoLibCalcDirectD(p0, /*az=*/0, /*dist=*/100); + p2 = GeoLibCalcDirectD(p0, /*az=*/M_PI, /*dist=*/100); + assert(!GeoLibLineDIntersectsCircleD(p1, p2, cd(0,0), 1000)); + + p1 = GeoLibCalcDirectD(p0, /*az=*/0, /*dist=*/1); + p2 = GeoLibCalcDirectD(p0, /*az=*/0, /*dist=*/100); + assert(!GeoLibLineDIntersectsCircleD(p1, p2, cd(0,0), 1000)); + + p1 = GeoLibCalcDirectD(p0, /*az=*/M_PI, /*dist=*/100); + p2 = GeoLibCalcDirectD(p0, /*az=*/M_PI, /*dist=*/1); + assert(!GeoLibLineDIntersectsCircleD(p1, p2, cd(0,0), 1000)); + } + void TestSampleArc() { + MaplyCoordinateD p[11]; + assertEq( M_PI/20, GeoLibSampleArcD(cd(0,0), 1000., 0., M_PI_2, true, p, sizeof(p)/sizeof(p[0])), 1e-6); + assertEq(3*M_PI/20, GeoLibSampleArcD(cd(0,0), 1000., 0., M_PI_2, false, p, sizeof(p)/sizeof(p[0])), 1e-6); + assertEq(3*M_PI/20, GeoLibSampleArcD(cd(0,0), 1000., M_PI_2, 0, true, p, sizeof(p)/sizeof(p[0])), 1e-6); + assertEq( M_PI/20, GeoLibSampleArcD(cd(0,0), 1000., M_PI_2, 0, false, p, sizeof(p)/sizeof(p[0])), 1e-6); + assertEq( M_PI/20, GeoLibSampleArcD(cd(0,0), 1000., -M_PI_4, M_PI_4, true, p, sizeof(p)/sizeof(p[0])), 1e-6); + assertEq(3*M_PI/20, GeoLibSampleArcD(cd(0,0), 1000., -M_PI_4, M_PI_4, false, p, sizeof(p)/sizeof(p[0])), 1e-6); + assertEq(3*M_PI/20, GeoLibSampleArcD(cd(0,0), 1000., M_PI_4, -M_PI_4, true, p, sizeof(p)/sizeof(p[0])), 1e-6); + assertEq( M_PI/20, GeoLibSampleArcD(cd(0,0), 1000., M_PI_4, -M_PI_4, false, p, sizeof(p)/sizeof(p[0])), 1e-6); + } + void TestInitialHeading() { + // TODO + } + void TestLineIntersectsCircle() { + // TODO + } + void TestLineIntersectCircle() { + // TODO + } + } test; +} +#endif + diff --git a/ios/library/WhirlyGlobeLib/src/LayerThread.mm b/ios/library/WhirlyGlobeLib/src/LayerThread.mm index 9a3628c1dc..4c0ab1249c 100644 --- a/ios/library/WhirlyGlobeLib/src/LayerThread.mm +++ b/ios/library/WhirlyGlobeLib/src/LayerThread.mm @@ -23,6 +23,7 @@ #import "GlobeView.h" #import "Platform.h" #import "SceneRendererMTL.h" +#import "WhirlyKitLog.h" using namespace WhirlyKit; @@ -77,6 +78,11 @@ - (id)initWithScene:(WhirlyKit::Scene *)inScene view:(View *)inView renderer:(Sc - (void)dealloc { +#if DEBUG + if (!changeRequests.empty()) { + wkLogLevel(Error, "Layer thread dealloc with %d changes", (int)changeRequests.size()); + } +#endif } - (void)addLayer:(NSObject *)layer @@ -181,10 +187,15 @@ - (void)requestFlush - (void)runAddChangeRequests { - if ([self isCancelled]) - // Note: Hey, should we be deleting these? + if ([self isCancelled]) { + std::lock_guard guardLock(changeLock); + for (auto change : changeRequests) { + delete change; + } + changeRequests.clear(); return; - + } + inRunAddChangeRequests = true; for (NSObject *layer in layers) { if ([layer respondsToSelector:@selector(preSceneFlush:)]) @@ -247,6 +258,10 @@ - (void)nothingInteresting - (void)cancel { [super cancel]; + if (paused) { + // Wake up from the pause lock to recognize the cancel + [self unpause]; + } CFRunLoopStop(self.runLoop.getCFRunLoop); } @@ -348,7 +363,7 @@ - (void)main } // Tear the scene down. It's unsafe to do it elsewhere - _scene->teardown(); + _scene->teardown(nullptr); } else { // Okay, we're shutting down, so release the existence lock existenceLock.unlock(); diff --git a/ios/library/WhirlyGlobeLib/src/LayerViewWatcher.mm b/ios/library/WhirlyGlobeLib/src/LayerViewWatcher.mm index 579aa6c187..2a24870a34 100644 --- a/ios/library/WhirlyGlobeLib/src/LayerViewWatcher.mm +++ b/ios/library/WhirlyGlobeLib/src/LayerViewWatcher.mm @@ -103,7 +103,7 @@ - (id)initWithView:(View *)inView thread:(WhirlyKitLayerThread *)inLayerThread layerThread = inLayerThread; view = inView; watchers = [NSMutableArray array]; - lastViewState = inView->makeViewState(layerThread.renderer); + lastViewState = inView->makeViewState(inLayerThread.renderer); viewWatchWrapper.viewWatcher = self; inView->addWatcher(&viewWatchWrapper); } @@ -128,10 +128,14 @@ - (void)addWatcherTarget:(id)target selector:(SEL)selector minTime:(TimeInterval { [watchers addObject:watch]; } - - if (!lastViewState && layerThread.renderer->framebufferWidth != 0) + + if (!lastViewState) { - lastViewState = view->makeViewState(layerThread.renderer); + const auto __strong thread = layerThread; + if (thread.renderer->framebufferWidth != 0) + { + lastViewState = view->makeViewState(thread.renderer); + } } // Make sure it gets a starting update @@ -146,10 +150,12 @@ - (void)removeWatcherTarget:(id)target selector:(SEL)selector LocalWatcher *toRemove = [[LocalWatcher alloc] init]; toRemove->target = target; toRemove->selector = selector; - if ([NSThread currentThread] == layerThread) + + const auto __strong thread = layerThread; + if ([NSThread currentThread] == thread) [self removeWatcherTargetLayer:toRemove]; else - [self performSelector:@selector(removeWatcherTargetLayer:) onThread:layerThread withObject:toRemove waitUntilDone:NO]; + [self performSelector:@selector(removeWatcherTargetLayer:) onThread:thread withObject:toRemove waitUntilDone:NO]; } - (void)removeWatcherTargetLayer:(LocalWatcher *)toRemove @@ -158,9 +164,10 @@ - (void)removeWatcherTargetLayer:(LocalWatcher *)toRemove @synchronized(self) { + const id __strong removeTarget = toRemove->target; for (LocalWatcher *watch in watchers) { - if (watch->target == toRemove->target && watch->selector == toRemove->selector) + if (watch->target == removeTarget && watch->selector == toRemove->selector) { found = watch; break; @@ -177,11 +184,12 @@ - (void)removeWatcherTargetLayer:(LocalWatcher *)toRemove // This is called in the main thread - (void)viewUpdated:(View *)inView { - if (layerThread.renderer == nil) + const auto __strong thread = layerThread; + if (thread.renderer == nil) return; // The view has to be valid first - if (layerThread.renderer->framebufferWidth <= 0.0) + if (thread.renderer->framebufferWidth <= 0.0) { // Let's check back every so often [NSObject cancelPreviousPerformRequestsWithTarget:self selector:@selector(viewUpdated:) object:nil]; @@ -189,7 +197,7 @@ - (void)viewUpdated:(View *)inView return; } - ViewStateRef viewState = view->makeViewState(layerThread.renderer); + ViewStateRef viewState = view->makeViewState(thread.renderer); // lastViewState = viewState; @synchronized(self) @@ -198,7 +206,7 @@ - (void)viewUpdated:(View *)inView if (!kickoffScheduled) { kickoffScheduled = true; - [self performSelector:@selector(kickoffViewUpdated) onThread:layerThread withObject:nil waitUntilDone:NO]; + [self performSelector:@selector(kickoffViewUpdated) onThread:thread withObject:nil waitUntilDone:NO]; } } } diff --git a/ios/library/WhirlyGlobeLib/src/LayoutLayer.mm b/ios/library/WhirlyGlobeLib/src/LayoutLayer.mm index d413746001..b0f66483c0 100644 --- a/ios/library/WhirlyGlobeLib/src/LayoutLayer.mm +++ b/ios/library/WhirlyGlobeLib/src/LayoutLayer.mm @@ -61,18 +61,16 @@ - (void)startWithThread:(WhirlyKitLayerThread *)inLayerThread scene:(WhirlyKit:: scene = inScene; // Get us view updates, but we'll filter them - if (layerThread.viewWatcher) - [layerThread.viewWatcher addWatcherTarget:self selector:@selector(viewUpdate:) minTime:0.0 minDist:0.0 maxLagTime:0.0]; - + [inLayerThread.viewWatcher addWatcherTarget:self selector:@selector(viewUpdate:) minTime:0.0 minDist:0.0 maxLagTime:0.0]; + [self checkUpdate]; } - (void)teardown { scene = NULL; - if (layerThread.viewWatcher) - [layerThread.viewWatcher removeWatcherTarget:self selector:@selector(viewUpdate:)]; - + [layerThread.viewWatcher removeWatcherTarget:self selector:@selector(viewUpdate:)]; + [NSObject cancelPreviousPerformRequestsWithTarget:self selector:@selector(delayCheck) object:nil]; [NSObject cancelPreviousPerformRequestsWithTarget:self selector:@selector(checkUpdate) object:nil]; } @@ -120,7 +118,7 @@ - (void)viewUpdate:(WhirlyKitViewStateWrapper *)inViewState // We also need to check on updates outside of the layer thread - (void)checkUpdate { - LayoutManager *layoutManager = (LayoutManager *)scene->getManager(kWKLayoutManager); + LayoutManagerRef layoutManager = std::dynamic_pointer_cast(scene->getManager(kWKLayoutManager)); if (viewState && layoutManager && layoutManager->hasChanges()) { [NSObject cancelPreviousPerformRequestsWithTarget:self selector:@selector(delayCheck) object:nil]; @@ -147,7 +145,7 @@ - (void)delayCheck - (void)setMaxDisplayObjects:(int)maxDisplayObjects { _maxDisplayObjects = maxDisplayObjects; - LayoutManager *layoutManager = (LayoutManager *)scene->getManager(kWKLayoutManager); + LayoutManagerRef layoutManager = std::dynamic_pointer_cast(scene->getManager(kWKLayoutManager)); if (layoutManager) layoutManager->setMaxDisplayObjects(_maxDisplayObjects); } @@ -160,11 +158,11 @@ - (void)updateLayout return; lastUpdate = scene->getCurrentTime(); - LayoutManager *layoutManager = (LayoutManager *)scene->getManager(kWKLayoutManager); + LayoutManagerRef layoutManager = std::dynamic_pointer_cast(scene->getManager(kWKLayoutManager)); if (layoutManager) { ChangeSet changes; - layoutManager->updateLayout(viewState,changes); + layoutManager->updateLayout(nullptr,viewState,changes); [layerThread addChangeRequests:changes]; } } @@ -177,7 +175,7 @@ - (void)addLayoutObjects:(const std::vector &)newObject return; } - LayoutManager *layoutManager = (LayoutManager *)scene->getManager(kWKLayoutManager); + LayoutManagerRef layoutManager = std::dynamic_pointer_cast(scene->getManager(kWKLayoutManager)); if (layoutManager) layoutManager->addLayoutObjects(newObjects); @@ -193,7 +191,7 @@ - (void)removeLayoutObjects:(const SimpleIDSet &)objectIDs return; } - LayoutManager *layoutManager = (LayoutManager *)scene->getManager(kWKLayoutManager); + LayoutManagerRef layoutManager = std::dynamic_pointer_cast(scene->getManager(kWKLayoutManager)); if (layoutManager) layoutManager->removeLayoutObjects(objectIDs); diff --git a/ios/library/WhirlyGlobeLib/src/NSDictionary+Stuff.m b/ios/library/WhirlyGlobeLib/src/NSDictionary+Stuff.m index c5beb7f2e5..58557b21e9 100644 --- a/ios/library/WhirlyGlobeLib/src/NSDictionary+Stuff.m +++ b/ios/library/WhirlyGlobeLib/src/NSDictionary+Stuff.m @@ -22,7 +22,7 @@ @implementation NSDictionary(Stuff) -- (id)objectForKey:(NSString *)name checkType:(id)theType default:(id)theDefault +- (id _Nullable)objectForKey:(NSString *)name checkType:(id _Nonnull)theType default:(id _Nullable)theDefault { id what = [self objectForKey:name]; if (!what || ![what isKindOfClass:theType]) @@ -31,7 +31,7 @@ - (id)objectForKey:(NSString *)name checkType:(id)theType default:(id)theDefault return what; } -- (float)floatForKey:(NSString *)name default:(float)theDefault +- (float)floatForKey:(NSString *_Nonnull)name default:(float)theDefault { id what = [self objectForKey:name]; if (!what || ![what isKindOfClass:[NSNumber class]]) @@ -41,7 +41,7 @@ - (float)floatForKey:(NSString *)name default:(float)theDefault return [num floatValue]; } -- (double)doubleForKey:(NSString *)name default:(double)theDefault +- (double)doubleForKey:(NSString *_Nonnull)name default:(double)theDefault { id what = [self objectForKey:name]; if (!what || ![what isKindOfClass:[NSNumber class]]) @@ -51,7 +51,7 @@ - (double)doubleForKey:(NSString *)name default:(double)theDefault return [num doubleValue]; } -- (int)intForKey:(NSString *)name default:(int)theDefault +- (int)intForKey:(NSString *_Nonnull)name default:(int)theDefault { id what = [self objectForKey:name]; if (!what || ![what isKindOfClass:[NSNumber class]]) @@ -61,7 +61,7 @@ - (int)intForKey:(NSString *)name default:(int)theDefault return [num intValue]; } -- (BOOL)boolForKey:(NSString *)name default:(BOOL)theDefault +- (BOOL)boolForKey:(NSString *_Nonnull)name default:(BOOL)theDefault { id what = [self objectForKey:name]; if (!what || ![what isKindOfClass:[NSNumber class]]) @@ -71,7 +71,7 @@ - (BOOL)boolForKey:(NSString *)name default:(BOOL)theDefault return [num boolValue]; } -- (NSString *)stringForKey:(NSString *)name default:(NSString *)theDefault +- (NSString *_Nullable)stringForKey:(NSString *_Nonnull)name default:(NSString *_Nullable)theDefault { id what = [self objectForKey:name]; if (!what) @@ -85,7 +85,7 @@ - (NSString *)stringForKey:(NSString *)name default:(NSString *)theDefault } /// Parse an enumerated type and return an int -- (int)enumForKey:(NSString *)name values:(NSArray *)values default:(int)theDefault +- (int)enumForKey:(NSString *_Nonnull)name values:(NSArray *_Nonnull)values default:(int)theDefault { id what = [self objectForKey:name]; if (!what || ![what isKindOfClass:[NSString class]]) @@ -105,6 +105,21 @@ - (int)enumForKey:(NSString *)name values:(NSArray *)values default:(int)theDefa return theDefault; } ++ (NSDictionary *_Nonnull) dictionaryByMerging:(NSDictionary *_Nullable) dict1 with:(NSDictionary *_Nullable)dict2 +{ + NSMutableDictionary *result = dict1 ? [NSMutableDictionary dictionaryWithDictionary:dict1] : [NSMutableDictionary new]; + if (dict2) + { + [result addEntriesFromDictionary:dict2]; + } + return result; +} + +- (NSDictionary *_Nonnull) dictionaryByMergingWith:(NSDictionary *_Nullable) dict +{ + return [[self class] dictionaryByMerging:self with:dict]; +} + @end void NSDictionaryDummyFunc() diff --git a/ios/library/WhirlyGlobeLib/src/NSString+Stuff.mm b/ios/library/WhirlyGlobeLib/src/NSString+Stuff.mm index d202ea1fe8..55aa317893 100644 --- a/ios/library/WhirlyGlobeLib/src/NSString+Stuff.mm +++ b/ios/library/WhirlyGlobeLib/src/NSString+Stuff.mm @@ -24,11 +24,8 @@ @implementation NSString(Stuff) - (std::string) asStdString { - const char *tmpStr = [self cStringUsingEncoding:NSASCIIStringEncoding]; - if (!tmpStr) - return ""; - std::string newStr(tmpStr); - return newStr; + const char * const tmpStr = [self cStringUsingEncoding:NSUTF8StringEncoding]; + return tmpStr ? std::string(tmpStr) : std::string(); } // Courtesy: http://stackoverflow.com/questions/3552195/how-to-convert-stdstring-to-nsstring diff --git a/ios/library/WhirlyGlobeLib/src/ParticleSystemDrawableMTL.mm b/ios/library/WhirlyGlobeLib/src/ParticleSystemDrawableMTL.mm index 9ba600c6dd..30a15af3ba 100644 --- a/ios/library/WhirlyGlobeLib/src/ParticleSystemDrawableMTL.mm +++ b/ios/library/WhirlyGlobeLib/src/ParticleSystemDrawableMTL.mm @@ -22,6 +22,7 @@ #import "TextureMTL.h" #import #import "DefaultShadersMTL.h" +#import "WhirlyKitLog.h" namespace WhirlyKit { @@ -64,7 +65,12 @@ // Set up a particle buffers to read from and render to // Note: Not clear we really need two, but it simplifies debugging - int len = numTotalPoints * vertexSize; + const int len = numTotalPoints * vertexSize; + if (len < 1) { + wkLogLevel(Error, "Invalid particle system"); + return; + } + curPointBuffer = 0; pointBuffer[0] = [setupInfo->mtlDevice newBufferWithLength:len options:MTLStorageModeShared]; if (calculateProgramId != EmptyIdentity) diff --git a/ios/library/WhirlyGlobeLib/src/QuadDisplayLayerNew.mm b/ios/library/WhirlyGlobeLib/src/QuadDisplayLayerNew.mm index 1fafa984ed..1d5599c013 100644 --- a/ios/library/WhirlyGlobeLib/src/QuadDisplayLayerNew.mm +++ b/ios/library/WhirlyGlobeLib/src/QuadDisplayLayerNew.mm @@ -51,24 +51,22 @@ - (void)startWithThread:(WhirlyKitLayerThread *)inLayerThread scene:(Scene *)inS _layerThread = inLayerThread; // We want view updates, but only every so often - if (_layerThread.viewWatcher) - [_layerThread.viewWatcher addWatcherTarget:self selector:@selector(viewUpdate:) minTime:controller->getViewUpdatePeriod() minDist:0.0 maxLagTime:10.0]; - + [inLayerThread.viewWatcher addWatcherTarget:self selector:@selector(viewUpdate:) minTime:controller->getViewUpdatePeriod() minDist:0.0 maxLagTime:10.0]; + controller->start(); } - (void)teardown -{ - ChangeSet changes; - - if (_layerThread.viewWatcher) - [_layerThread.viewWatcher removeWatcherTarget:self selector:@selector(viewUpdate:)]; +{ + const auto lt = _layerThread; + [lt.viewWatcher removeWatcherTarget:self selector:@selector(viewUpdate:)]; + ChangeSet changes; controller->stop(NULL,changes); controller = NULL; - - [_layerThread addChangeRequests:changes]; - [_layerThread flushChangeRequests]; + + [lt addChangeRequests:changes]; + [lt flushChangeRequests]; } static const float DelayPeriod = 0.1; diff --git a/ios/library/WhirlyGlobeLib/src/QuadImageFrameLoader_iOS.mm b/ios/library/WhirlyGlobeLib/src/QuadImageFrameLoader_iOS.mm index 3019c35233..b81374625e 100644 --- a/ios/library/WhirlyGlobeLib/src/QuadImageFrameLoader_iOS.mm +++ b/ios/library/WhirlyGlobeLib/src/QuadImageFrameLoader_iOS.mm @@ -162,9 +162,11 @@ [layer fetchRequestSuccess:request tileID:tileID frame:frame->frameIndex data:nil]; } else { request.success = ^(MaplyTileFetchRequest *request, id data) { + // TODO: do we need to clean anything up if layer==nil? [layer fetchRequestSuccess:request tileID:tileID frame:frame->frameIndex data:data]; }; request.failure = ^(MaplyTileFetchRequest *request, NSError *error) { + // TODO: do we need to clean anything up if layer==nil? [layer fetchRequestFail:request tileID:tileID frame:frame->frameIndex error:error]; }; [batchOps->toStart addObject:request]; diff --git a/ios/library/WhirlyGlobeLib/src/RenderTargetMTL.mm b/ios/library/WhirlyGlobeLib/src/RenderTargetMTL.mm index b45e2e125b..f67594c0e8 100644 --- a/ios/library/WhirlyGlobeLib/src/RenderTargetMTL.mm +++ b/ios/library/WhirlyGlobeLib/src/RenderTargetMTL.mm @@ -113,7 +113,7 @@ static size_t calcPixelSize(MTLPixelFormat pixFormat) NSMutableData *data = [[NSMutableData alloc] initWithLength:width*height*pixSize]; [tex getBytes:[data mutableBytes] bytesPerRow:width*pixSize fromRegion:region mipmapLevel:0]; - return RawDataRef(new RawNSDataReader(data)); + return std::make_shared(data); } RawDataRef RenderTargetMTL::snapshot(int startX,int startY,int snapWidth,int snapHeight) diff --git a/ios/library/WhirlyGlobeLib/src/SceneMTL.mm b/ios/library/WhirlyGlobeLib/src/SceneMTL.mm index 2352474fdd..b280caa5ed 100644 --- a/ios/library/WhirlyGlobeLib/src/SceneMTL.mm +++ b/ios/library/WhirlyGlobeLib/src/SceneMTL.mm @@ -1,9 +1,8 @@ -/* - * SceneMTL.mm +/* SceneMTL.mm * WhirlyGlobeLib * * Created by Steve Gifford on 5/16/19. - * Copyright 2011-2019 mousebird consulting + * Copyright 2011-2021 mousebird consulting * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -15,12 +14,12 @@ * 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. - * */ #import "SceneMTL.h" #import "TextureMTL.h" #import "DrawableMTL.h" +#import "FontTextureManager.h" namespace WhirlyKit { @@ -48,18 +47,21 @@ return ret; } -void SceneMTL::teardown() +void SceneMTL::teardown(PlatformThreadInfo *inst) { - for (auto it : drawables) { - DrawableMTL *draw = dynamic_cast(it.second.get()); - if (draw) - draw->teardownForRenderer((RenderSetupInfoMTL *)setupInfo,this,NULL); + for (const auto &it : drawables) { + if (auto draw = dynamic_cast(it.second.get())) { + draw->teardownForRenderer((RenderSetupInfoMTL *)setupInfo,this,nullptr); + } } drawables.clear(); for (auto it : textures) { it.second->destroyInRenderer(setupInfo,this); } textures.clear(); + if (fontTextureManager) { + fontTextureManager->teardown(inst); + } } } diff --git a/ios/library/WhirlyGlobeLib/src/SceneRendererMTL.mm b/ios/library/WhirlyGlobeLib/src/SceneRendererMTL.mm index 3172d7ee0c..d6332fea62 100644 --- a/ios/library/WhirlyGlobeLib/src/SceneRendererMTL.mm +++ b/ios/library/WhirlyGlobeLib/src/SceneRendererMTL.mm @@ -80,7 +80,8 @@ } SceneRendererMTL::SceneRendererMTL(id mtlDevice,id mtlLibrary, float inScale) -: setupInfo(mtlDevice,mtlLibrary), isShuttingDown(false) + : setupInfo(mtlDevice,mtlLibrary) + , _isShuttingDown(std::make_shared(false)) { offscreenBlendEnable = false; indirectRender = false; @@ -93,7 +94,7 @@ #endif init(); - + // Calculation shaders workGroups.push_back(WorkGroupRef(new WorkGroupMTL(WorkGroup::Calculation))); // Offscreen target render group @@ -132,6 +133,11 @@ void SceneRendererMTL::setScene(Scene *newScene) { SceneRenderer::setScene(newScene); + + // Slots we need to refer to on the C++ side + slotMap[a_maskNameID] = WhirlyKitShader::WKSVertexMaskAttribute; + for (unsigned int ii=0;ii buff = [setupInfo.mtlDevice newBufferWithBytes:&uniforms length:sizeof(uniforms) options:MTLResourceStorageModeShared]; - [bltEncode copyFromBuffer:buff sourceOffset:0 toBuffer:sceneRender->setupInfo.uniformBuff.buffer destinationOffset:sceneRender->setupInfo.uniformBuff.offset size:sizeof(uniforms)]; + auto buff = setupInfo.heapManage.allocateBuffer(HeapManagerMTL::HeapType::Drawable, &uniforms, sizeof(uniforms)); + [bltEncode copyFromBuffer:buff.buffer sourceOffset:buff.offset toBuffer:sceneRender->setupInfo.uniformBuff.buffer destinationOffset:sceneRender->setupInfo.uniformBuff.offset size:sizeof(uniforms)]; } void SceneRendererMTL::setupLightBuffer(SceneMTL *scene,RendererFrameInfoMTL *frameInfo,id bltEncode) @@ -260,8 +266,8 @@ // Copy this to a buffer and then blit that buffer into place // TODO: Try to reuse these - id buff = [setupInfo.mtlDevice newBufferWithBytes:&lighting length:sizeof(lighting) options:MTLResourceStorageModeShared]; - [bltEncode copyFromBuffer:buff sourceOffset:0 toBuffer:sceneRender->setupInfo.lightingBuff.buffer destinationOffset:sceneRender->setupInfo.lightingBuff.offset size:sizeof(lighting)]; + auto buff = setupInfo.heapManage.allocateBuffer(HeapManagerMTL::HeapType::Drawable, &lighting, sizeof(lighting)); + [bltEncode copyFromBuffer:buff.buffer sourceOffset:buff.offset toBuffer:sceneRender->setupInfo.lightingBuff.buffer destinationOffset:sceneRender->setupInfo.lightingBuff.offset size:sizeof(lighting)]; } void SceneRendererMTL::setupDrawStateA(WhirlyKitShader::UniformDrawStateA &drawState) @@ -321,8 +327,8 @@ // Build the indirect command buffers if they're available if (@available(iOS 13.0, *)) { - for (auto &workGroup : workGroups) { - for (auto targetContainer : workGroup->renderTargetContainers) { + for (const auto &workGroup : workGroups) { + for (const auto &targetContainer : workGroup->renderTargetContainers) { if (targetContainer->drawables.empty() && !targetContainer->modified) continue; RenderTargetContainerMTLRef targetContainerMTL = std::dynamic_pointer_cast(targetContainer); @@ -335,13 +341,21 @@ } else { renderTarget = std::dynamic_pointer_cast(targetContainer->renderTarget); } + if (!renderTarget) + { + continue; + } // Sort the drawables into draw groups by Z buffer usage DrawGroupMTLRef drawGroup; bool dgZBufferRead = false, dgZBufferWrite = false; - for (auto draw : targetContainer->drawables) { + for (const auto &draw : targetContainer->drawables) { DrawableMTL *drawMTL = dynamic_cast(draw.get()); - + if (!drawMTL) { + wkLogLevel(Error, "SceneRendererMTL: Invalid drawable. Skipping."); + continue; + } + // Sort out what the zbuffer should be bool zBufferWrite;// = (zBufferMode == zBufferOn); bool zBufferRead;// = (zBufferMode == zBufferOn); @@ -390,16 +404,24 @@ cmdBuffDesc.maxFragmentBufferBindCount = WhirlyKitShader::WKSFragMaxBuffer; // Build up indirect buffers for each draw group - for (auto drawGroup : targetContainerMTL->drawGroups) { + for (const auto &drawGroup : targetContainerMTL->drawGroups) { int curCommand = 0; drawGroup->numCommands = drawGroup->drawables.size(); drawGroup->indCmdBuff = [setupInfo.mtlDevice newIndirectCommandBufferWithDescriptor:cmdBuffDesc maxCommandCount:drawGroup->numCommands options:0]; + if (!drawGroup->indCmdBuff) { + wkLogLevel(Error, "SceneRendererMTL: Failed to allocate indirect command buffer. Skipping."); + continue; + } // Just run the calculation portion if (workGroup->groupType == WorkGroup::Calculation) { // Work through the drawables - for (auto &draw : targetContainer->drawables) { + for (const auto &draw : targetContainer->drawables) { DrawableMTL *drawMTL = dynamic_cast(draw.get()); + if (!drawMTL) { + wkLogLevel(Error, "SceneRendererMTL: Invalid drawable. Skipping."); + continue; + } SimpleIdentity calcProgID = drawMTL->getCalculationProgram(); // Figure out the program to use for drawing @@ -407,7 +429,7 @@ continue; ProgramMTL *calcProgram = (ProgramMTL *)scene->getProgram(calcProgID); if (!calcProgram) { - NSLog(@"Invalid calculation program for drawable. Skipping."); + wkLogLevel(Error, "SceneRendererMTL: Invalid calculation program for drawable. Skipping."); continue; } @@ -417,8 +439,12 @@ } } else { // Work through the drawables - for (auto &draw : drawGroup->drawables) { + for (const auto &draw : drawGroup->drawables) { DrawableMTL *drawMTL = dynamic_cast(draw.get()); + if (!drawMTL) { + wkLogLevel(Error, "SceneRendererMTL: Invalid drawable"); + continue; + } // Figure out the program to use for drawing SimpleIdentity drawProgramId = drawMTL->getProgram(); @@ -689,11 +715,15 @@ if (indirectRender) { // Run pre-process on the draw groups - for (auto &drawGroup : targetContainerMTL->drawGroups) { + for (const auto &drawGroup : targetContainerMTL->drawGroups) { if (drawGroup->numCommands > 0) { bool resourcesChanged = false; for (auto &draw : drawGroup->drawables) { DrawableMTL *drawMTL = dynamic_cast(draw.get()); + if (!drawMTL) { + wkLogLevel(Error, "SceneRendererMTL: Invalid drawable. Skipping."); + continue; + } drawMTL->runTweakers(&baseFrameInfo); if (drawMTL->preProcess(this, cmdBuff, bltEncode, sceneMTL)) resourcesChanged = true; @@ -702,8 +732,9 @@ if (resourcesChanged) { drawGroup->resources.clear(); for (auto &draw : drawGroup->drawables) { - DrawableMTL *drawMTL = dynamic_cast(draw.get()); - drawMTL->enumerateResources(&baseFrameInfo, drawGroup->resources); + if (const auto drawMTL = dynamic_cast(draw.get())) { + drawMTL->enumerateResources(&baseFrameInfo, drawGroup->resources); + } } } resources.addResources(drawGroup->resources); @@ -711,11 +742,12 @@ } } else { // Run pre-process ahead of time - for (auto &draw : targetContainer->drawables) { - DrawableMTL *drawMTL = dynamic_cast(draw.get()); - drawMTL->runTweakers(&baseFrameInfo); - drawMTL->preProcess(this, cmdBuff, bltEncode, sceneMTL); - drawMTL->enumerateResources(&baseFrameInfo, resources); + for (const auto &draw : targetContainer->drawables) { + if (const auto drawMTL = dynamic_cast(draw.get())) { + drawMTL->runTweakers(&baseFrameInfo); + drawMTL->preProcess(this, cmdBuff, bltEncode, sceneMTL); + drawMTL->enumerateResources(&baseFrameInfo, resources); + } } } @@ -727,7 +759,7 @@ // Triggered after rendering is finished id renderFence = [mtlDevice newFence]; - + // If we're forcing a mipmap calculation, then we're just going to use this render target once // If not, then we run some program over it multiple times // TODO: Make the reduce operation more explicit @@ -744,20 +776,20 @@ // This happens if the dev wants an instantaneous render if (!renderPassDesc) renderPassDesc = renderTarget->getRenderPassDesc(level); - + baseFrameInfo.renderPassDesc = renderPassDesc; } else { baseFrameInfo.renderPassDesc = renderTarget->getRenderPassDesc(level); } cmdEncode = [cmdBuff renderCommandEncoderWithDescriptor:baseFrameInfo.renderPassDesc]; [cmdEncode waitForFence:preProcessFence beforeStages:MTLRenderStageVertex]; - + resources.use(cmdEncode); if (indirectRender) { if (@available(iOS 12.0, *)) { [cmdEncode setCullMode:MTLCullModeFront]; - for (auto drawGroup : targetContainerMTL->drawGroups) { + for (const auto &drawGroup : targetContainerMTL->drawGroups) { if (drawGroup->numCommands > 0) { [cmdEncode setDepthStencilState:drawGroup->depthStencil]; [cmdEncode executeCommandsInBuffer:drawGroup->indCmdBuff withRange:NSMakeRange(0,drawGroup->numCommands)]; @@ -768,9 +800,13 @@ // Just run the calculation portion if (workGroup->groupType == WorkGroup::Calculation) { // Work through the drawables - for (auto &draw : targetContainer->drawables) { + for (const auto &draw : targetContainer->drawables) { DrawableMTL *drawMTL = dynamic_cast(draw.get()); - SimpleIdentity calcProgID = drawMTL->getCalculationProgram(); + if (!drawMTL) { + wkLogLevel(Error, "SceneRendererMTL: Invalid drawable. Skipping."); + continue; + } + const SimpleIdentity calcProgID = drawMTL->getCalculationProgram(); // Figure out the program to use for drawing if (calcProgID == EmptyIdentity) @@ -778,7 +814,7 @@ ProgramMTL *calcProgram = (ProgramMTL *)scene->getProgram(calcProgID); if (!calcProgram) { - NSLog(@"Invalid calculation program for drawable. Skipping."); + wkLogLevel(Error, "SceneRendererMTL: Invalid calculation program for drawable. Skipping."); continue; } baseFrameInfo.program = calcProgram; @@ -803,11 +839,15 @@ [cmdEncode setCullMode:MTLCullModeFront]; // Work through the drawables - for (auto const &draw : targetContainer->drawables) { - auto drawMTL = dynamic_cast(draw.get()); + for (const auto &draw : targetContainer->drawables) { + auto drawMTL = std::dynamic_pointer_cast(draw); + if (!drawMTL) { + wkLogLevel(Error, "SceneRendererMTL: Invalid drawable. Skipping."); + continue; + } // Figure out the program to use for drawing - SimpleIdentity drawProgramId = drawMTL->getProgram(); + const SimpleIdentity drawProgramId = drawMTL->getProgram(); ProgramMTL *program = (ProgramMTL *)scene->getProgram(drawProgramId); if (!program) { wkLogLevel(Error, "SceneRendererMTL: Drawable without Program"); @@ -886,19 +926,30 @@ [cmdBuff presentDrawable:drawable]; } lastCmdBuff = cmdBuff; - + + // Capture shutdown signal in case `this` is destroyed before the blocks below execute. + // This isn't 100% because we could still be destroyed while the blocks are executing, + // unless we can be guaranteed that we're always destroyed on the main queue? + // We might need `std::enable_shared_from_this` here so that we can keep `this` alive + // within the blocks we create here. + const auto shuttingDown = this->_isShuttingDown; + // This particular target may want a snapshot [cmdBuff addCompletedHandler:^(id _Nonnull) { - if (isShuttingDown) + if (*shuttingDown) return; // TODO: Sort these into the render targets dispatch_async(dispatch_get_main_queue(), ^{ - if (isShuttingDown) + if (*shuttingDown) return; - + // Look for the snapshot delegate that wants this render target for (auto snapshotDelegate : snapshotDelegates) { + if (*shuttingDown) { + break; + } + if (![snapshotDelegate needSnapshot:now]) continue; @@ -959,7 +1010,7 @@ void SceneRendererMTL::shutdown() { - isShuttingDown = true; + *_isShuttingDown = true; if (lastCmdBuff) [lastCmdBuff waitUntilCompleted]; @@ -1033,7 +1084,7 @@ WideVectorDrawableBuilderRef SceneRendererMTL::makeWideVectorDrawableBuilder(const std::string &name) const { - return std::make_shared(name,scene); + return std::make_shared(name,this,scene); } RenderTargetRef SceneRendererMTL::makeRenderTarget() const diff --git a/ios/library/WhirlyGlobeLib/src/ScreenSpaceDrawableBuilderMTL.mm b/ios/library/WhirlyGlobeLib/src/ScreenSpaceDrawableBuilderMTL.mm index 429f72c6ec..a8ed03ec02 100644 --- a/ios/library/WhirlyGlobeLib/src/ScreenSpaceDrawableBuilderMTL.mm +++ b/ios/library/WhirlyGlobeLib/src/ScreenSpaceDrawableBuilderMTL.mm @@ -31,11 +31,12 @@ this->scene = scene; } -void ScreenSpaceDrawableBuilderMTL::Init(bool hasMotion,bool hasRotation,bool buildAnyway) +void ScreenSpaceDrawableBuilderMTL::ScreenSpaceInit(bool hasMotion,bool hasRotation,bool buildAnyway) { basicDraw = std::make_shared("Screen Space"); // Need the entries even if we don't bother to fill them in - ScreenSpaceDrawableBuilder::Init(hasMotion,hasRotation,true); + // TODO: Just add in the ones we need. This is a waste. + ScreenSpaceDrawableBuilder::ScreenSpaceInit(hasMotion,hasRotation,true); // Wire up the buffers // TODO: Merge these into a single data structure @@ -44,9 +45,9 @@ ((VertexAttributeMTL *)basicDraw->vertexAttributes[dirIndex])->slot = WhirlyKitShader::WKSVertexScreenSpaceDirAttribute; } -ScreenSpaceTweaker *ScreenSpaceDrawableBuilderMTL::makeTweaker() +DrawableTweakerRef ScreenSpaceDrawableBuilderMTL::makeTweaker() const { - return NULL; + return {}; } BasicDrawableRef ScreenSpaceDrawableBuilderMTL::getDrawable() diff --git a/ios/library/WhirlyGlobeLib/src/SingleLabel_iOS.mm b/ios/library/WhirlyGlobeLib/src/SingleLabel_iOS.mm index b16a0e6ae3..f7c4df20c9 100644 --- a/ios/library/WhirlyGlobeLib/src/SingleLabel_iOS.mm +++ b/ios/library/WhirlyGlobeLib/src/SingleLabel_iOS.mm @@ -39,9 +39,9 @@ // Used to build the drawable string on specific platforms -std::vector SingleLabel_iOS::generateDrawableStrings(PlatformThreadInfo *threadInfo,const LabelInfo *inLabelInfo,FontTextureManager *inFontTexManager,float &lineHeight,ChangeSet &changes) +std::vector SingleLabel_iOS::generateDrawableStrings(PlatformThreadInfo *threadInfo,const LabelInfo *inLabelInfo,const FontTextureManagerRef &inFontTexManager,float &lineHeight,ChangeSet &changes) { - FontTextureManager_iOS *fontTexManager = (FontTextureManager_iOS *)inFontTexManager; + FontTextureManager_iOSRef fontTexManager = std::dynamic_pointer_cast(inFontTexManager); const LabelInfo_iOS *labelInfo = (LabelInfo_iOS *)inLabelInfo; NSArray *strings = [text componentsSeparatedByString:@"\n"]; diff --git a/ios/library/WhirlyGlobeLib/src/TextureMTL.mm b/ios/library/WhirlyGlobeLib/src/TextureMTL.mm index 0a13155235..a82d4ac524 100644 --- a/ios/library/WhirlyGlobeLib/src/TextureMTL.mm +++ b/ios/library/WhirlyGlobeLib/src/TextureMTL.mm @@ -3,7 +3,7 @@ * WhirlyGlobeLib * * Created by Steve Gifford on 5/16/19. - * Copyright 2011-2019 mousebird consulting + * Copyright 2011-2021 mousebird consulting * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -156,6 +156,21 @@ MTLPixelFormat pixFormat = MTLPixelFormatR32Uint; int bytesPerRow = 0; + // "Don't use the following pixel formats: r8Unorm_srgb, b5g6r5Unorm, a1bgr5Unorm, abgr4Unorm, bgr5A1Unorm, or any XR10 or YUV formats." + // https://developer.apple.com/documentation/metal/developing_metal_apps_that_run_in_simulator +#if TARGET_OS_SIMULATOR + switch (format) + { + //case TexTypeUnsignedByte: // is this r8Unorm_srgb? + case TexTypeShort565: // b5g6r5Unorm + case TexTypeShort4444: // abgr4Unorm + case TexTypeShort5551: // bgr5A1Unorm + wkLogLevel(Warn, "Texture not loaded: pixel format %d not supported", format); + return false; + default: break; + } +#endif + // TODO: Missing all the compressed formats switch (format) { diff --git a/ios/library/WhirlyGlobeLib/src/UIColor+Stuff.mm b/ios/library/WhirlyGlobeLib/src/UIColor+Stuff.mm index db8a8a6286..3ee0ff57bd 100644 --- a/ios/library/WhirlyGlobeLib/src/UIColor+Stuff.mm +++ b/ios/library/WhirlyGlobeLib/src/UIColor+Stuff.mm @@ -25,6 +25,23 @@ @implementation UIColor(Stuff) +- (UIColor *)lighterColor +{ + return [self lighterColor:1.3]; +} + +// Courtesy: https://stackoverflow.com/questions/11598043/get-slightly-lighter-and-darker-color-from-uicolor +- (UIColor *)lighterColor:(float)withFactor +{ + CGFloat h, s, b, a; + if ([self getHue:&h saturation:&s brightness:&b alpha:&a]) + return [UIColor colorWithHue:h + saturation:s + brightness:MIN(b * withFactor, 1.0) + alpha:a]; + return nil; +} + + (UIColor *) colorFromHexRGB:(int)hexColor { float red = (((hexColor) >> 16) & 0xFF)/255.0; @@ -34,6 +51,15 @@ + (UIColor *) colorFromHexRGB:(int)hexColor return [UIColor colorWithRed:red green:green blue:blue alpha:1.0]; } ++ (UIColor *) colorFromShortHexRGB:(int)hexColor +{ + int red = (((hexColor) >> 12) & 0xF); red |= red << 4; + int green = (((hexColor) >> 4) & 0xF); green |= green << 4; + int blue = (((hexColor) >> 0) & 0xF); blue |= blue << 4; + + return [UIColor colorWithRed:red/255.0f green:green/255.0f blue:blue/255.0f alpha:1.0]; +} + + (UIColor *) colorFromRGBA:(const WhirlyKit::RGBAColor &)color { float red = color.r / 255.0; @@ -105,7 +131,7 @@ - (Vector4f) asVec4 break; } - return color; + return color; } @end diff --git a/ios/library/WhirlyGlobeLib/src/UpdateDisplayLayer.mm b/ios/library/WhirlyGlobeLib/src/UpdateDisplayLayer.mm index 39e0cb1d89..021d4c512d 100644 --- a/ios/library/WhirlyGlobeLib/src/UpdateDisplayLayer.mm +++ b/ios/library/WhirlyGlobeLib/src/UpdateDisplayLayer.mm @@ -53,12 +53,11 @@ - (void)startWithThread:(WhirlyKitLayerThread *)inLayerThread scene:(Scene *)inS { layerThread = inLayerThread; scene = inScene; - + // We want view updates, but only occasionally - if (layerThread.viewWatcher) - [(WhirlyKitLayerViewWatcher *)layerThread.viewWatcher addWatcherTarget:self selector:@selector(viewUpdate:) minTime:_minTime minDist:0.0 maxLagTime:0.0]; - - [self performSelector:@selector(startOnThread) onThread:layerThread withObject:nil waitUntilDone:NO]; + [(WhirlyKitLayerViewWatcher *)inLayerThread.viewWatcher addWatcherTarget:self selector:@selector(viewUpdate:) minTime:_minTime minDist:0.0 maxLagTime:0.0]; + + [self performSelector:@selector(startOnThread) onThread:inLayerThread withObject:nil waitUntilDone:NO]; } - (void)startOnThread @@ -69,10 +68,9 @@ - (void)startOnThread - (void)teardown { [NSObject cancelPreviousPerformRequestsWithTarget:self]; - - if (layerThread.viewWatcher) - [(WhirlyKitLayerViewWatcher *)layerThread.viewWatcher removeWatcherTarget:self selector:@selector(viewUpdate:)]; - + + [(WhirlyKitLayerViewWatcher *)layerThread.viewWatcher removeWatcherTarget:self selector:@selector(viewUpdate:)]; + [_dataSource teardown]; } diff --git a/ios/library/WhirlyGlobeLib/src/VectorData_iOS.mm b/ios/library/WhirlyGlobeLib/src/VectorData_iOS.mm index 95c0bbf7d1..d6a315e45a 100644 --- a/ios/library/WhirlyGlobeLib/src/VectorData_iOS.mm +++ b/ios/library/WhirlyGlobeLib/src/VectorData_iOS.mm @@ -191,9 +191,9 @@ bool VectorParseFeature(ShapeSet &shapes,NSDictionary *jsonDict) // Apply the attributes if there are any if ([prop isKindOfClass:[NSDictionary class]]) - for (ShapeSet::iterator it = shapes.begin(); it != shapes.end(); ++it) { - iosMutableDictionary *dict = new iosMutableDictionary([NSMutableDictionary dictionaryWithDictionary:prop]); - (*it)->setAttrDict(MutableDictionaryRef(dict)); + for (auto shape : shapes) { + auto dict = new iosMutableDictionary([NSMutableDictionary dictionaryWithDictionary:prop]); + shape->setAttrDict(MutableDictionaryRef(dict)); } // Apply the identity if there is one @@ -231,6 +231,19 @@ bool VectorParseGeoJSON(ShapeSet &shapes,NSDictionary *jsonDict) else return false; } + } else if (![type compare:@"Feature"]) { + ShapeSet featShapes; + if (VectorParseFeature(featShapes,jsonDict)) + shapes.insert(featShapes.begin(),featShapes.end()); + else + return false; + } else { + // Sometimes they just include geometry + ShapeSet rawShapes; + if (VectorParseGeometry(rawShapes,jsonDict)) + shapes.insert(rawShapes.begin(),rawShapes.end()); + else + return false; } return true; diff --git a/ios/library/WhirlyGlobeLib/src/WideVectorDrawableBuilderMTL.mm b/ios/library/WhirlyGlobeLib/src/WideVectorDrawableBuilderMTL.mm index 05f780ae4f..6d949c390a 100644 --- a/ios/library/WhirlyGlobeLib/src/WideVectorDrawableBuilderMTL.mm +++ b/ios/library/WhirlyGlobeLib/src/WideVectorDrawableBuilderMTL.mm @@ -18,7 +18,10 @@ * */ +#import #import "WideVectorDrawableBuilderMTL.h" +#import "BasicDrawableMTL.h" +#import "BasicDrawableInstanceBuilderMTL.h" #import "DefaultShadersMTL.h" #import "ProgramMTL.h" #import "RawData_NSData.h" @@ -26,74 +29,149 @@ namespace WhirlyKit { -WideVectorDrawableBuilderMTL::WideVectorDrawableBuilderMTL(const std::string &name,Scene *scene) -: BasicDrawableBuilderMTL(name,scene) +WideVectorDrawableBuilderMTL::WideVectorDrawableBuilderMTL(const std::string &name,const SceneRenderer *sceneRenderer,Scene *scene) +: WideVectorDrawableBuilder(name, sceneRenderer, scene), + drawableGotten(false), instanceGotten(false) { } -void WideVectorDrawableBuilderMTL::Init(unsigned int numVert, unsigned int numTri, bool globeMode) +void WideVectorDrawableBuilderMTL::Init(unsigned int numVert, unsigned int numTri, unsigned int numCenterLine, + WideVecImplType implType, bool globeMode, const WideVectorInfo *vecInfo) { - basicDraw = std::make_shared("Wide Vector"); - WideVectorDrawableBuilder::Init(numVert,numTri,globeMode); + WideVectorDrawableBuilder::Init(numVert,numTri,numCenterLine,implType,globeMode,vecInfo); - // Wire up the buffers - // TODO: Merge these into a single data structure - if (globeMode) - ((VertexAttributeMTL *)basicDraw->vertexAttributes[basicDraw->normalEntry])->slot = WhirlyKitShader::WKSVertexNormalAttribute; - ((VertexAttributeMTL *)basicDraw->vertexAttributes[basicDraw->colorEntry])->slot = WhirlyKitShader::WKSVertexColorAttribute; - ((VertexAttributeMTL *)basicDraw->vertexAttributes[p1_index])->slot = WhirlyKitShader::WKSVertexWideVecP1Attribute; - ((VertexAttributeMTL *)basicDraw->vertexAttributes[tex_index])->slot = WhirlyKitShader::WKSVertexWideVecTexInfoAttribute; - ((VertexAttributeMTL *)basicDraw->vertexAttributes[n0_index])->slot = WhirlyKitShader::WKSVertexWideVecN0Attribute; - ((VertexAttributeMTL *)basicDraw->vertexAttributes[c0_index])->slot = WhirlyKitShader::WKSVertexWideVecC0Attribute; + if (implType == WideVecImplBasic) { + // Wire up the buffers + // TODO: Merge these into a single data structure + if (globeMode) + ((VertexAttributeMTL *)basicDrawable->basicDraw->vertexAttributes[basicDrawable->basicDraw->normalEntry])->slot = WhirlyKitShader::WKSVertexNormalAttribute; + ((VertexAttributeMTL *)basicDrawable->basicDraw->vertexAttributes[basicDrawable->basicDraw->colorEntry])->slot = WhirlyKitShader::WKSVertexColorAttribute; + ((VertexAttributeMTL *)basicDrawable->basicDraw->vertexAttributes[p1_index])->slot = WhirlyKitShader::WKSVertexWideVecP1Attribute; + ((VertexAttributeMTL *)basicDrawable->basicDraw->vertexAttributes[tex_index])->slot = WhirlyKitShader::WKSVertexWideVecTexInfoAttribute; + ((VertexAttributeMTL *)basicDrawable->basicDraw->vertexAttributes[n0_index])->slot = WhirlyKitShader::WKSVertexWideVecN0Attribute; + ((VertexAttributeMTL *)basicDrawable->basicDraw->vertexAttributes[offset_index])->slot = WhirlyKitShader::WKSVertexWideVecOffsetAttribute; + ((VertexAttributeMTL *)basicDrawable->basicDraw->vertexAttributes[c0_index])->slot = WhirlyKitShader::WKSVertexWideVecC0Attribute; + } else { + ((VertexAttributeMTL *)basicDrawable->basicDraw->vertexAttributes[inst_index])->slot = WhirlyKitShader::WKSVertexWideVecInstIndexAttribute; + } } -WideVectorTweaker *WideVectorDrawableBuilderMTL::makeTweaker() +int WideVectorDrawableBuilderMTL::addAttribute(BDAttributeDataType dataType,StringIdentity nameID,int slot,int numThings) { - return NULL; + return basicDrawable->addAttribute(dataType, nameID, slot, numThings); } -BasicDrawableRef WideVectorDrawableBuilderMTL::getDrawable() + +DrawableTweakerRef WideVectorDrawableBuilderMTL::makeTweaker() const { - if (drawableGotten) - return BasicDrawableBuilderMTL::getDrawable(); - - BasicDrawableRef theDraw = BasicDrawableBuilderMTL::getDrawable(); + return nullptr; +} +BasicDrawable::UniformBlock WideVectorDrawableBuilderMTL::wideVecUniBlock() +{ // Uniforms for regular wide vectors WhirlyKitShader::UniformWideVec uniWV; memset(&uniWV,0,sizeof(uniWV)); uniWV.w2 = lineWidth/2.0; + uniWV.offset = lineOffset; uniWV.edge = edgeSize; uniWV.texRepeat = texRepeat; - uniWV.color[0] = color.r/255.0; - uniWV.color[1] = color.g/255.0; - uniWV.color[2] = color.b/255.0; - uniWV.color[3] = color.a/255.0; - uniWV.hasExp = widthExp || colorExp || opacityExp || includeExp; - + uniWV.hasExp = widthExp || offsetExp || colorExp || opacityExp; + BasicDrawable::UniformBlock uniBlock; uniBlock.blockData = RawDataRef(new RawNSDataReader([[NSData alloc] initWithBytes:&uniWV length:sizeof(uniWV)])); uniBlock.bufferID = WhirlyKitShader::WKSUniformWideVecEntry; - basicDraw->setUniBlock(uniBlock); - + + return uniBlock; +} + +BasicDrawable::UniformBlock WideVectorDrawableBuilderMTL::wideVecExpUniBlock() +{ // Expression uniforms, if we're using those - if (uniWV.hasExp) { - WhirlyKitShader::UniformWideVecExp wideVecExp; - memset(&wideVecExp, 0, sizeof(wideVecExp)); - if (widthExp) - FloatExpressionToMtl(widthExp,wideVecExp.widthExp); - if (opacityExp) - FloatExpressionToMtl(opacityExp,wideVecExp.opacityExp); - if (colorExp) - ColorExpressionToMtl(colorExp,wideVecExp.colorExp); - - BasicDrawable::UniformBlock uniBlock; - uniBlock.blockData = RawDataRef(new RawNSDataReader([[NSData alloc] initWithBytes:&wideVecExp length:sizeof(wideVecExp)])); - uniBlock.bufferID = WhirlyKitShader::WKSUniformWideVecEntryExp; - basicDraw->setUniBlock(uniBlock); + WhirlyKitShader::UniformWideVecExp wideVecExp; + memset(&wideVecExp, 0, sizeof(wideVecExp)); + if (widthExp) + FloatExpressionToMtl(widthExp,wideVecExp.widthExp); + if (offsetExp) + FloatExpressionToMtl(offsetExp,wideVecExp.offsetExp); + + if (opacityExp) + FloatExpressionToMtl(opacityExp,wideVecExp.opacityExp); + if (colorExp) + ColorExpressionToMtl(colorExp,wideVecExp.colorExp); + + BasicDrawable::UniformBlock uniBlock; + uniBlock.blockData = RawDataRef(new RawNSDataReader([[NSData alloc] initWithBytes:&wideVecExp length:sizeof(wideVecExp)])); + uniBlock.bufferID = WhirlyKitShader::WKSUniformWideVecEntryExp; + + return uniBlock; +} + +BasicDrawableRef WideVectorDrawableBuilderMTL::getBasicDrawable() +{ + if (drawableGotten) + return basicDrawable->basicDraw; + + basicDrawable->getDrawable(); + + drawableGotten = true; + VertexAttributeMTL *colorAttr = (VertexAttributeMTL *)basicDrawable->basicDraw->vertexAttributes[basicDrawable->basicDraw->colorEntry]; + colorAttr->setDefaultColor(basicDrawable->color); + + // Apply uniform blocks that control general function + if (implType == WideVecImplBasic) { + basicDrawable->basicDraw->setUniBlock(wideVecUniBlock()); + if (widthExp || offsetExp || colorExp || opacityExp) + basicDrawable->basicDraw->setUniBlock(wideVecExpUniBlock()); + } + + return basicDrawable->basicDraw; +} + +BasicDrawableInstanceRef WideVectorDrawableBuilderMTL::getInstanceDrawable() +{ + if (instanceGotten) + return instDrawable->drawInst; + + instanceGotten = true; + + if (!instDrawable) + return nullptr; + + instDrawable->getDrawable(); + + // Apply uniform blocks to control general function + instDrawable->drawInst->setUniBlock(wideVecUniBlock()); + if (widthExp || offsetExp || colorExp || opacityExp) + instDrawable->drawInst->setUniBlock(wideVecExpUniBlock()); + + // Instances also go into their own buffer + std::vector vecInsts(centerline.size()); + for (unsigned int ii=0;iicenter,inPtr->center); + CopyIntoMtlFloat3(outPtr->up, inPtr->up); + outPtr->len = inPtr->len; + float color[4]; + inPtr->color.asUnitFloats(color); + CopyIntoMtlFloat4(outPtr->color,color); + outPtr->prev = inPtr->prev; + outPtr->next = inPtr->next; + outPtr->mask0 = inPtr->maskIDs[0]; + outPtr->mask1 = inPtr->maskIDs[1]; } + NSData *data = [[NSData alloc] initWithBytes:(void *)&vecInsts[0] length:centerline.size()*sizeof(WhirlyKitShader::VertexTriWideVecInstance)]; + instDrawable->setInstanceData(centerline.size(), std::make_shared(data)); + + return instDrawable->drawInst; +} - return theDraw; +int WideVectorDrawableBuilderMTL::maxInstances() const +{ + // Just figure out big a buffer we'll have. 32MB seems plenty + int instSize = std::min(256,(int)sizeof(WhirlyKitShader::VertexTriWideVecInstance)); + return 32*1024*1024 / instSize; } } diff --git a/ios/library/WhirlyGlobeLib/src/WrapperMTL.mm b/ios/library/WhirlyGlobeLib/src/WrapperMTL.mm index d172bbb8e9..11fdd8f991 100644 --- a/ios/library/WhirlyGlobeLib/src/WrapperMTL.mm +++ b/ios/library/WhirlyGlobeLib/src/WrapperMTL.mm @@ -298,12 +298,11 @@ void CopyIntoMtlFloat4(simd::float4 &dest,const float vals[4]) HeapManagerMTL::HeapManagerMTL(id mtlDevice) : mtlDevice(mtlDevice) { + memAlign = [mtlDevice heapBufferSizeAndAlignWithLength:1 options:MTLResourceUsageRead].align; } id HeapManagerMTL::findHeap(HeapType heapType,size_t &size) { - std::lock_guard guardLock(lock); - HeapGroup &heapGroup = heapGroups[heapType]; for (auto heap : heapGroup.heaps) { MTLSizeAndAlign sAlign = [mtlDevice heapBufferSizeAndAlignWithLength:size options:MTLResourceUsageRead]; @@ -328,8 +327,6 @@ void CopyIntoMtlFloat4(simd::float4 &dest,const float vals[4]) id HeapManagerMTL::findTextureHeap(MTLTextureDescriptor *desc,size_t size) { - std::lock_guard guardLock(texLock); - for (auto heap : texGroups.heaps) { MTLSizeAndAlign sAlign = [mtlDevice heapBufferSizeAndAlignWithLength:size options:MTLResourceUsageRead]; size = sAlign.size; @@ -356,8 +353,12 @@ void CopyIntoMtlFloat4(simd::float4 &dest,const float vals[4]) { BufferEntryMTL buffer; if (UseHeaps) { - buffer.heap = findHeap(heapType,size); - buffer.buffer = [buffer.heap newBufferWithLength:size options:MTLResourceStorageModeShared]; + { + std::lock_guard guardLock(lock); + + buffer.heap = findHeap(heapType,size); + buffer.buffer = [buffer.heap newBufferWithLength:size options:MTLResourceStorageModeShared]; + } if (!buffer.buffer) { NSLog(@"Uh oh! Ran out of buffer space [heap type %d, alloc %zu]", heapType, size); buffer.valid = false; @@ -365,6 +366,11 @@ void CopyIntoMtlFloat4(simd::float4 &dest,const float vals[4]) } buffer.offset = 0; } else { + size_t extra = size % memAlign; + if (extra > 0) { + size += memAlign - extra; + } + buffer.buffer = [mtlDevice newBufferWithLength:size options:MTLResourceStorageModeShared]; buffer.heap = nil; buffer.offset = 0; @@ -378,8 +384,12 @@ void CopyIntoMtlFloat4(simd::float4 &dest,const float vals[4]) { BufferEntryMTL buffer; if (UseHeaps) { - buffer.heap = findHeap(heapType,size); - buffer.buffer = [buffer.heap newBufferWithLength:size options:MTLResourceStorageModeShared]; + { + std::lock_guard guardLock(lock); + + buffer.heap = findHeap(heapType,size); + buffer.buffer = [buffer.heap newBufferWithLength:size options:MTLResourceStorageModeShared]; + } if (!buffer.buffer) { NSLog(@"Uh oh! Ran out of buffer space [heap type %d, alloc %zu]", heapType, size); buffer.valid = false; @@ -407,6 +417,8 @@ void CopyIntoMtlFloat4(simd::float4 &dest,const float vals[4]) // It turns out that our estimates on size aren't valid for some formats, so try a few times with bigger estimates for (unsigned int ii=0;ii<3;ii++) if (!tex.tex) { + std::lock_guard guardLock(texLock); + tex.heap = findTextureHeap(desc, (1< 0) { + int numTextures = TexturesBase(texArgs.texPresent); + if (numTextures > 0) { constexpr sampler sampler2d(coord::normalized, filter::linear); return vert.color * texArgs.tex[0].sample(sampler2d, vert.texCoord); } return vert.color; } +// Fragment shader that pulls the mask ID out only +fragment unsigned int fragmentTri_mask(ProjVertexTriA vert [[stage_in]], + constant Uniforms &uniforms [[ buffer(WKSFragUniformArgBuffer) ]], + constant FragTriArgBufferB & fragArgs [[buffer(WKSFragmentArgBuffer)]], + constant RegularTextures & texArgs [[buffer(WKSFragTextureArgBuffer)]]) +{ + return vert.maskIDs[0]; +} + // Vertex shader that handles up to two textures vertex ProjVertexTriB vertexTri_multiTex( VertexTriB vert [[stage_in]], @@ -498,7 +514,8 @@ vertex ProjVertexTriB vertexTri_multiTex( if (vertArgs.uniDrawState.clipCoords) outVert.position = float4(vertPos,1.0); else { - float4 pt = uniforms.pMatrix * (uniforms.mvMatrix * float4(vertPos,1.0) + uniforms.mvMatrixDiff * float4(vertPos,1.0)); + float4 pt = uniforms.pMatrix * (uniforms.mvMatrix * vertArgs.uniDrawState.singleMat * float4(vert.position,1.0) + + uniforms.mvMatrixDiff * vertArgs.uniDrawState.singleMat * float4(vert.position,1.0)); pt /= pt.w; outVert.position = pt; } @@ -593,25 +610,40 @@ vertex ProjVertexTriWideVec vertexTri_wideVec( constant RegularTextures & texArgs [[buffer(WKSVertTextureArgBuffer)]]) { ProjVertexTriWideVec outVert; - + outVert.maskIDs[0] = vert.mask0; + outVert.maskIDs[1] = vert.mask1; + float3 pos = (vertArgs.uniDrawState.singleMat * float4(vert.position.xyz,1.0)).xyz; - + // Pull out the width and possibly calculate one float w2 = vertArgs.wideVec.w2; if (w2 > 0.0) { w2 = w2 + vertArgs.wideVec.edge; } + + // Vary the offset over time for testing +// float centerLine = vert.offset.z * (fmod(uniforms.currentTime,10.0)/10.0 * 200.0 - 100.0); + float centerLine = vert.offset.z * vertArgs.wideVec.offset; - outVert.color = vertArgs.wideVec.color * calculateFade(uniforms,vertArgs.uniDrawState); + outVert.color = vert.color * calculateFade(uniforms,vertArgs.uniDrawState); + + float pixScale = min(uniforms.screenSizeInDisplayCoords.x,uniforms.screenSizeInDisplayCoords.y) / min(uniforms.frameSize.x,uniforms.frameSize.y); + float realWidth2 = w2 * pixScale; + float realCenterLine = centerLine * pixScale; + + float t0 = vert.c0 * (realWidth2 + realCenterLine); + t0 = clamp(t0,-4.0,5.0); + float3 dir = normalize(vert.p1 - vert.position); + float3 realPosOffset = (vert.p1 - vert.position) * t0 + + dir * realWidth2 * vert.offset.y + + vert.n0 * (realCenterLine + realWidth2) + + vert.n0 * realWidth2 * vert.offset.x; - float realWidth2 = w2 * min(uniforms.screenSizeInDisplayCoords.x,uniforms.screenSizeInDisplayCoords.y) / min(uniforms.frameSize.x,uniforms.frameSize.y); - float t0 = vert.c0 * realWidth2; - t0 = clamp(t0,0.0,1.0); - float3 realPos = (vert.p1 - vert.position) * t0 + vert.n0 * realWidth2 + pos; float texScale = min(uniforms.frameSize.x,uniforms.frameSize.y)/(uniforms.screenSizeInDisplayCoords.x * vertArgs.wideVec.texRepeat); float texPos = ((vert.texInfo.z - vert.texInfo.y) * t0 + vert.texInfo.y + vert.texInfo.w * realWidth2) * texScale; outVert.texCoord = float2(vert.texInfo.x, texPos); - float4 screenPos = uniforms.pMatrix * (uniforms.mvMatrix * float4(realPos,1.0) + uniforms.mvMatrixDiff * float4(realPos,1.0)); + float4 screenPos = uniforms.pMatrix * (uniforms.mvMatrix * float4(pos,1.0) + uniforms.mvMatrixDiff * float4(pos,1.0)) + + uniforms.pMatrix * (uniforms.mvMatrix * float4(realPosOffset,0.0) + uniforms.mvMatrixDiff * float4(realPosOffset,0.0)); screenPos /= screenPos.w; outVert.position = float4(screenPos.xy,0,1.0); @@ -638,28 +670,42 @@ vertex ProjVertexTriWideVec vertexTri_wideVecExp( ProjVertexTriWideVec outVert; float3 pos = (vertArgs.uniDrawState.singleMat * float4(vert.position.xyz,1.0)).xyz; - + float zoom = ZoomFromSlot(uniforms, vertArgs.uniDrawState.zoomSlot); + // Pull out the width and possibly calculate one float w2 = vertArgs.wideVec.w2; - if (vertArgs.wideVec.hasExp) { - float zoom = ZoomFromSlot(uniforms, vertArgs.uniDrawState.zoomSlot); + if (vertArgs.wideVec.hasExp) w2 = ExpCalculateFloat(vertArgs.wideVecExp.widthExp, zoom, 2.0*w2)/2.0; - } if (w2 > 0.0) { w2 = w2 + vertArgs.wideVec.edge; } + + // Pull out the center line offset, or calculate one + float centerLine = vertArgs.wideVec.offset; + if (vertArgs.wideVec.hasExp) { + centerLine = ExpCalculateFloat(vertArgs.wideVecExp.offsetExp, zoom, centerLine); + } + centerLine = vert.offset.z * centerLine; - outVert.color = vertArgs.wideVec.color * calculateFade(uniforms,vertArgs.uniDrawState); + outVert.color = vert.color * calculateFade(uniforms,vertArgs.uniDrawState); + + float pixScale = min(uniforms.screenSizeInDisplayCoords.x,uniforms.screenSizeInDisplayCoords.y) / min(uniforms.frameSize.x,uniforms.frameSize.y); + float realWidth2 = w2 * pixScale; + float realCenterLine = centerLine * pixScale; + + float t0 = vert.c0 * (realWidth2 + realCenterLine); + t0 = clamp(t0,-4.0,5.0); + float3 dir = normalize(vert.p1 - vert.position); + float3 realPosOffset = (vert.p1 - vert.position) * t0 + + dir * realWidth2 * vert.offset.y + + vert.n0 * (realCenterLine + realWidth2) + + vert.n0 * realWidth2 * vert.offset.x; - float realWidth2 = w2 * min(uniforms.screenSizeInDisplayCoords.x,uniforms.screenSizeInDisplayCoords.y) / min(uniforms.frameSize.x,uniforms.frameSize.y); - float t0 = vert.c0 * realWidth2; - t0 = clamp(t0,0.0,1.0); - float3 posOffset = (vert.p1 - vert.position) * t0 + vert.n0 * realWidth2; float texScale = min(uniforms.frameSize.x,uniforms.frameSize.y)/(uniforms.screenSizeInDisplayCoords.x * vertArgs.wideVec.texRepeat); float texPos = ((vert.texInfo.z - vert.texInfo.y) * t0 + vert.texInfo.y + vert.texInfo.w * realWidth2) * texScale; outVert.texCoord = float2(vert.texInfo.x, texPos); float4 screenPos = uniforms.pMatrix * (uniforms.mvMatrix * float4(pos,1.0) + uniforms.mvMatrixDiff * float4(pos,1.0)) + - uniforms.pMatrix * (uniforms.mvMatrix * float4(posOffset,0.0) + uniforms.mvMatrixDiff * float4(posOffset,0.0)); + uniforms.pMatrix * (uniforms.mvMatrix * float4(realPosOffset,0.0) + uniforms.mvMatrixDiff * float4(realPosOffset,0.0)); screenPos /= screenPos.w; outVert.position = float4(screenPos.xy,0,1.0); @@ -679,16 +725,30 @@ struct TriWideArgBufferFrag { fragment float4 fragmentTri_wideVec( ProjVertexTriWideVec vert [[stage_in]], constant Uniforms &uniforms [[ buffer(WKSFragUniformArgBuffer) ]], + constant Lighting &lighting [[ buffer(WKSVertLightingArgBuffer) ]], constant TriWideArgBufferFrag & fragArgs [[buffer(WKSFragmentArgBuffer)]], - constant RegularTextures & texArgs [[buffer(WKSFragTextureArgBuffer)]]) + constant WideVecTextures & texArgs [[buffer(WKSFragTextureArgBuffer)]]) { int numTextures = TexturesBase(texArgs.texPresent); // Dot/dash pattern - float patternVal = 1.0; + float4 patternColor(1.0,1.0,1.0,1.0); + if (texArgs.texPresent & (1< 0 || vert.maskIDs[1] > 0) { + // Pull the maskID from the input texture + constexpr sampler sampler2d(coord::normalized, filter::linear); + float2 loc(vert.position.x/uniforms.frameSize.x,vert.position.y/uniforms.frameSize.y); + unsigned int maskID = texArgs.maskTex.sample(sampler2d, loc).r; + if (vert.maskIDs[0] == maskID || vert.maskIDs[1] == maskID) + discard_fragment(); + } + } + if (numTextures > 0) { constexpr sampler sampler2d(coord::normalized, address::repeat, filter::linear); - patternVal = texArgs.tex[0].sample(sampler2d, float2(0.5,vert.texCoord.y)).r; + // Just pulling the alpha at the moment + // If we use the rest, we get interpolation down to zero, which isn't quite what we want here + patternColor.a = texArgs.tex[0].sample(sampler2d, vert.texCoord).a; } float alpha = 1.0; float across = vert.w2 * vert.texCoord.x; @@ -697,9 +757,231 @@ fragment float4 fragmentTri_wideVec( if (across > vert.w2-fragArgs.wideVec.edge) alpha = (vert.w2-across)/fragArgs.wideVec.edge; - return vert.dotProd > 0.0 ? float4(fragArgs.wideVec.color.rgb,fragArgs.wideVec.color.a*alpha) * patternVal : float4(0.0); + return vert.dotProd > 0.0 ? + float4(vert.color.rgb*patternColor.rgb,vert.color.a*alpha*patternColor.a) : float4(0.0); +} + +struct IntersectInfo { + bool valid; + float2 interPt; + float ta,tb; +}; + +// Intersect two offset lines +IntersectInfo intersectWideLines(float2 p0,float2 p1,float2 p2, + float2 n0,float2 n1) +{ + IntersectInfo iInfo; + iInfo.valid = false; + + float2 lineA[2]; + lineA[0] = p0 + n0; + lineA[1] = p1 + n0; + float2 lineB[2]; + lineB[0] = p1 + n1; + lineB[1] = p2 + n1; + + float denom = (lineA[0].x-lineA[1].x)*(lineB[0].y-lineB[1].y) - (lineA[0].y - lineA[1].y)*(lineB[0].x - lineB[1].x); + if (denom == 0.0) + return iInfo; + + float termA = (lineA[0].x * lineA[1].y - lineA[0].y * lineA[1].x); + float termB = (lineB[0].x * lineB[1].y - lineB[0].y * lineB[1].x); + iInfo.interPt.x = ( termA * (lineB[0].x - lineB[1].x) - (lineA[0].x - lineA[1].x) * termB)/denom; + iInfo.interPt.y = ( termA * (lineB[0].y - lineB[1].y) - (lineA[0].y - lineA[1].y) * termB)/denom; + + iInfo.ta = 0.0; iInfo.tb = 0.0; + iInfo.valid = true; + + return iInfo; +} + +struct TriWideArgBufferC { + UniformDrawStateA uniDrawState [[ id(WKSUniformDrawStateEntry) ]]; + UniformWideVec wideVec [[ id(WKSUniformWideVecEntry) ]]; + UniformWideVecExp wideVecExp [[ id(WKSUniformWideVecEntryExp) ]]; + bool hasTextures; +}; + +// Used to track what info we have about a center point +struct CenterInfo { + float2 screenPos; + float2 dir; + float2 nDir; + float2 norm; +}; + +// Performance version of wide vector shader +vertex ProjVertexTriWideVecPerf vertexTri_wideVecPerf( + VertexTriWideVecB vert [[stage_in]], + constant Uniforms &uniforms [[ buffer(WKSVertUniformArgBuffer) ]], + constant Lighting &lighting [[ buffer(WKSVertLightingArgBuffer) ]], + constant TriWideArgBufferC &vertArgs [[buffer(WKSVertexArgBuffer)]], + uint instanceID [[instance_id]], + constant VertexTriWideVecInstance *wideVecInsts [[ buffer(WKSVertModelInstanceArgBuffer) ]], + constant RegularTextures & texArgs [[buffer(WKSVertTextureArgBuffer)]]) +{ + ProjVertexTriWideVecPerf outVert; + + int whichVert = (vert.index >> 16) & 0xffff; + int whichPoly = vert.index & 0xffff; + + // Find the various instances representing center points + // We need one behind and two ahead of us + bool instValid[4]; + VertexTriWideVecInstance inst[4]; + inst[1] = wideVecInsts[instanceID]; + instValid[1] = true; + if (inst[1].prev != -1) { + inst[0] = wideVecInsts[inst[1].prev]; + instValid[0] = true; + } else + instValid[0] = false; + if (inst[1].next != -1) { + inst[2] = wideVecInsts[inst[1].next]; + instValid[2] = true; + } else + instValid[2] = false; + if (instValid[2] && inst[2].next != -1) { + inst[3] = wideVecInsts[inst[2].next]; + instValid[3] = true; + } else + instValid[3] = false; + + float dotProd = 1.0; + + // Figure out position on the screen for every center point + CenterInfo centers[4]; + for (unsigned int ii=0;ii<4;ii++) + if (instValid[ii]) { + float3 centerPos = (vertArgs.uniDrawState.singleMat * float4(inst[ii].center,1.0)).xyz; + float4 screenPt = uniforms.pMatrix * (uniforms.mvMatrix * float4(centerPos,1.0) + uniforms.mvMatrixDiff * float4(centerPos,1.0)); + screenPt /= screenPt.w; + centers[ii].screenPos = screenPt.xy; + + // Make sure the object is facing the user (only for the globe) + if (uniforms.globeMode && ii == 1) { + float4 pt = uniforms.mvMatrix * float4(centerPos,1.0); + pt /= pt.w; + + float4 testNorm = uniforms.mvNormalMatrix * float4(centerPos,0.0); + dotProd = dot(-pt.xyz,testNorm.xyz); + if (pt.z > 0.0) + dotProd = -1.0; + } + } + + // Size of pixels + float2 screenScale(2.0/uniforms.frameSize.x,2.0/uniforms.frameSize.y); + + // Direction and normal info for three segments we may look at + bool isValid = instValid[2]; + for (unsigned int ii=1;ii<4;ii++) { + if (instValid[ii-1]) { + centers[ii].dir = centers[ii].screenPos - centers[ii-1].screenPos; + centers[ii].nDir = normalize(centers[ii].dir); + centers[ii].norm = normalize(float2(-centers[ii].dir.y,centers[ii].dir.x) * screenScale); + } + } + + float zoom = ZoomFromSlot(uniforms, vertArgs.uniDrawState.zoomSlot); + + // Pull out the width and possibly calculate one + float w2 = vertArgs.wideVec.w2; + if (vertArgs.wideVec.hasExp) + w2 = ExpCalculateFloat(vertArgs.wideVecExp.widthExp, zoom, 2.0*w2)/2.0; + if (w2 > 0.0) { + w2 = w2 + vertArgs.wideVec.edge; + } + + // Pull out the center line offset, or calculate one +// float centerLine = vertArgs.wideVec.offset; + float centerLine = 0.0; +// if (vertArgs.wideVec.hasExp) +// centerLine = ExpCalculateFloat(vertArgs.wideVecExp.offsetExp, zoom, centerLine); + + // Intersect on the left or right depending + float interDir = whichVert & 0x1 ? 1.0 : -1.0; + + // Turn off the end caps for the moment + switch (whichPoly) { + case 0: + isValid &= false; + break; + case 1: + isValid &= true; + break; + case 2: + isValid &= false; + break; + } + + // Do the offset intersection +// float angleBetween = 0.0; + bool iPtsValid = false; + float2 iPts; + if (isValid) { + // We'll reject + float nearDist = 1.42 * w2 * max(screenScale.x,screenScale.y); + + // We only need one intersection depending + int interPt = (whichVert == 6 || whichVert == 7) ? 1 : 0; + if (instValid[interPt] && instValid[interPt+1] && instValid[interPt+2]) { + float dotProd = dot(centers[interPt+1].nDir, centers[interPt+2].nDir); + if (dotProd > -0.99999998476 && dotProd < 0.99999998476) { + // Acute angles tend to break things +// angleBetween = acos(dotProd); +// if (angleBetween < 4.0 / 180.0 * M_PI_F) { +//// iPtsValid = false; +// } + + float2 nudge = w2 * interDir * screenScale; + IntersectInfo interInfo = intersectWideLines(centers[interPt].screenPos,centers[interPt+1].screenPos,centers[interPt+2].screenPos, + nudge * centers[interPt+1].norm, nudge * centers[interPt+2].norm); + if (interInfo.valid) { + // If the intersection is too far away, we'll drop it + if (distance_squared(centers[interPt+1].screenPos,interInfo.interPt) > nearDist*nearDist) { + iPtsValid = false; + } else { + iPtsValid = true; + iPts = interInfo.interPt; + } + } + } + } + } + + outVert.color = inst[1].color * calculateFade(uniforms,vertArgs.uniDrawState); + outVert.w2 = vertArgs.wideVec.w2; + + if (isValid && dotProd > 0.0) { + if (iPtsValid) { + outVert.position = float4(iPts, 0.0, 1.0); + } else { + // Return a vertex offset from the base + int basePt = (whichVert == 6 || whichVert == 7) ? 2 : 1; + float2 offset = (centers[2].norm * interDir) * screenScale * (vertArgs.wideVec.w2 + centerLine + vertArgs.wideVec.edge); + + outVert.position = float4(centers[basePt].screenPos + offset, 0.0, 1.0); + } + } else { + outVert.position = float4(0.0,0.0,-1000.0,1.0); + } + + return outVert; } +// Fragment share that takes the back of the globe into account +fragment float4 fragmentTri_wideVecPerf( + ProjVertexTriWideVecPerf vert [[stage_in]], + constant Uniforms &uniforms [[ buffer(WKSFragUniformArgBuffer) ]], + constant TriWideArgBufferFrag & fragArgs [[buffer(WKSFragmentArgBuffer)]], + constant WideVecTextures & texArgs [[buffer(WKSFragTextureArgBuffer)]]) +{ + return vert.color; +} + + struct VertexTriSSArgBufferA { UniformDrawStateA uniDrawState [[ id(WKSUniformDrawStateEntry) ]]; UniformScreenSpace ss [[ id(WKSUniformScreenSpaceEntry) ]]; @@ -714,7 +996,8 @@ vertex ProjVertexTriA vertexTri_screenSpace( constant RegularTextures & texArgs [[buffer(WKSVertTextureArgBuffer)]]) { ProjVertexTriA outVert; - + outVert.maskIDs = uint2(0,0); + float3 pos = (vertArgs.uniDrawState.singleMat * float4(vert.position,1.0)).xyz; if (vertArgs.ss.hasMotion) pos += (uniforms.currentTime - vertArgs.ss.startTime) * vert.dir; @@ -747,6 +1030,8 @@ vertex ProjVertexTriA vertexTri_screenSpace( } else screenOffset = vert.offset; + outVert.maskIDs[0] = vert.maskID; + float2 scale = float2(2.0/uniforms.frameSize.x,2.0/uniforms.frameSize.y); outVert.position = (dotProd > 0.0 && pt.z <= 0.0) ? float4(screenPt.xy + float2(screenOffset.x*scale.x,screenOffset.y*scale.y),0.0,1.0) : float4(0.0,0.0,0.0,0.0); @@ -768,7 +1053,8 @@ vertex ProjVertexTriA vertexTri_screenSpaceExp( constant RegularTextures & texArgs [[buffer(WKSVertTextureArgBuffer)]]) { ProjVertexTriA outVert; - + outVert.maskIDs = uint2(0,0); + float zoomScale = 1.0; if (vertArgs.ss.hasExp) { float zoom = ZoomFromSlot(uniforms, vertArgs.uniDrawState.zoomSlot); @@ -807,6 +1093,8 @@ vertex ProjVertexTriA vertexTri_screenSpaceExp( } else screenOffset = vert.offset; + outVert.maskIDs[0] = vert.maskID; + float2 scale = float2(2.0/uniforms.frameSize.x,2.0/uniforms.frameSize.y) * zoomScale; outVert.position = (dotProd > 0.0 && pt.z <= 0.0) ? float4(screenPt.xy + float2(screenOffset.x*scale.x,screenOffset.y*scale.y),0.0,1.0) : float4(0.0,0.0,0.0,0.0); @@ -866,7 +1154,8 @@ vertex ProjVertexTriA vertexTri_billboard( constant VertexTriBillboardArgBuffer & vertArgs [[buffer(WKSVertexArgBuffer)]]) { ProjVertexTriA outVert; - + outVert.maskIDs = uint2(0,0); + float3 vertPos = (vertArgs.uniDrawState.singleMat * float4(vert.position,1.0)).xyz; float3 newPos; diff --git a/resources b/resources index 711934eed9..e6d6353d6e 160000 --- a/resources +++ b/resources @@ -1 +1 @@ -Subproject commit 711934eed947084d4e306ddb36ee8147aae9ab52 +Subproject commit e6d6353d6e3621dca4c92f0140b515335b702593