├── src ├── feUtil │ ├── common.cpp │ ├── gcj.h │ ├── common.h │ └── gcj.cpp ├── feModel │ ├── Coord2D.cpp │ ├── Coord2D.h │ ├── PyramidGrid.h │ ├── GeoCoordSys.h │ ├── Tile.h │ ├── Envelope.cpp │ ├── Tile.cpp │ ├── GeoCoordSys.cpp │ ├── PyramidGrid.cpp │ ├── Envelope.h │ ├── Mercator.h │ └── Mercator.cpp ├── api │ ├── main.cpp │ ├── fe_api.cpp │ └── fe_api.h ├── mgpEarth.h ├── feObjects │ ├── Skybox.cpp │ ├── Skybox.h │ ├── GeoNode.h │ ├── GeoNode.cpp │ └── GroundNode.h ├── fmake_wasm_lib.props ├── fmake.props ├── fe3dtiles │ ├── b3dm.h │ ├── TdTilesManager.h │ ├── TdTiles.h │ ├── b3dm.cpp │ ├── TdTilesManager.cpp │ └── TdTiles.cpp ├── feGeo │ ├── Building.h │ ├── GeoLayer.h │ ├── Geometry.h │ └── Building.cpp ├── feTile │ ├── TileLayer.h │ ├── DataActor.h │ ├── TileGeom.hpp │ ├── TileLayer.cpp │ ├── TileData.h │ ├── XyzTileManager.h │ ├── DataActor.cpp │ ├── TileManager.h │ ├── TileData.cpp │ ├── TileManager.cpp │ ├── TileGeom.cpp │ └── XyzTileManager.cpp ├── feCtrl │ ├── TraceBall.h │ ├── EarthApp.h │ ├── EarthAnimation.h │ ├── TraceBall.cpp │ ├── EarthCtrl.h │ └── EarthAnimation.cpp ├── feElevation │ ├── Elevation.h │ ├── ElevationManager.h │ ├── Elevation.cpp │ └── ElevationManager.cpp ├── fmake_wasm.props └── fmake_wasm_debug.props ├── res ├── gltf │ ├── car.bin │ └── gendarmerie-texture.jpg ├── skybox │ ├── skyboxsun25deg │ │ ├── skyboxsun25degtest.txt │ │ ├── nx.jpg │ │ ├── ny.jpg │ │ ├── nz.jpg │ │ ├── px.jpg │ │ ├── py.jpg │ │ └── pz.jpg │ └── MilkyWay │ │ ├── dark-s_nx.jpg │ │ ├── dark-s_ny.jpg │ │ ├── dark-s_nz.jpg │ │ ├── dark-s_px.jpg │ │ ├── dark-s_py.jpg │ │ └── dark-s_pz.jpg └── gm_el_v1_small.png ├── doc ├── index.md ├── license.md ├── build.md ├── gltf.md ├── start.md └── geolayer.md ├── README.md ├── .hgignore ├── sdk └── demo │ ├── index.html │ ├── min.html │ ├── 3dtile.html │ ├── gltf.html │ ├── demo.html │ ├── track.html │ ├── geojson.html │ └── geolayer.html └── .gitignore /src/feUtil/common.cpp: -------------------------------------------------------------------------------- 1 | #include "feUtil/common.h" 2 | -------------------------------------------------------------------------------- /res/gltf/car.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chunquedong/mgpEarth/HEAD/res/gltf/car.bin -------------------------------------------------------------------------------- /res/skybox/skyboxsun25deg/skyboxsun25degtest.txt: -------------------------------------------------------------------------------- 1 | http://reije081.home.xs4all.nl/skyboxes/ 2 | -------------------------------------------------------------------------------- /res/gm_el_v1_small.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chunquedong/mgpEarth/HEAD/res/gm_el_v1_small.png -------------------------------------------------------------------------------- /res/gltf/gendarmerie-texture.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chunquedong/mgpEarth/HEAD/res/gltf/gendarmerie-texture.jpg -------------------------------------------------------------------------------- /res/skybox/MilkyWay/dark-s_nx.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chunquedong/mgpEarth/HEAD/res/skybox/MilkyWay/dark-s_nx.jpg -------------------------------------------------------------------------------- /res/skybox/MilkyWay/dark-s_ny.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chunquedong/mgpEarth/HEAD/res/skybox/MilkyWay/dark-s_ny.jpg -------------------------------------------------------------------------------- /res/skybox/MilkyWay/dark-s_nz.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chunquedong/mgpEarth/HEAD/res/skybox/MilkyWay/dark-s_nz.jpg -------------------------------------------------------------------------------- /res/skybox/MilkyWay/dark-s_px.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chunquedong/mgpEarth/HEAD/res/skybox/MilkyWay/dark-s_px.jpg -------------------------------------------------------------------------------- /res/skybox/MilkyWay/dark-s_py.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chunquedong/mgpEarth/HEAD/res/skybox/MilkyWay/dark-s_py.jpg -------------------------------------------------------------------------------- /res/skybox/MilkyWay/dark-s_pz.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chunquedong/mgpEarth/HEAD/res/skybox/MilkyWay/dark-s_pz.jpg -------------------------------------------------------------------------------- /res/skybox/skyboxsun25deg/nx.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chunquedong/mgpEarth/HEAD/res/skybox/skyboxsun25deg/nx.jpg -------------------------------------------------------------------------------- /res/skybox/skyboxsun25deg/ny.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chunquedong/mgpEarth/HEAD/res/skybox/skyboxsun25deg/ny.jpg -------------------------------------------------------------------------------- /res/skybox/skyboxsun25deg/nz.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chunquedong/mgpEarth/HEAD/res/skybox/skyboxsun25deg/nz.jpg -------------------------------------------------------------------------------- /res/skybox/skyboxsun25deg/px.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chunquedong/mgpEarth/HEAD/res/skybox/skyboxsun25deg/px.jpg -------------------------------------------------------------------------------- /res/skybox/skyboxsun25deg/py.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chunquedong/mgpEarth/HEAD/res/skybox/skyboxsun25deg/py.jpg -------------------------------------------------------------------------------- /res/skybox/skyboxsun25deg/pz.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chunquedong/mgpEarth/HEAD/res/skybox/skyboxsun25deg/pz.jpg -------------------------------------------------------------------------------- /doc/index.md: -------------------------------------------------------------------------------- 1 | 2 | mgpEarth 3D GIS Engine 3 | 4 | [快速开始](start.md) 5 | 6 | [矢量图层](geolayer.md) 7 | 8 | [3D模型加载](gltf.md) 9 | 10 | [授权协议](license.md) 11 | 12 | [构建](build.md) 13 | 14 | [API文档](../sdk/api/fe_api.js) 15 | 16 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | mgpEarth is a 3D GIS Engine 3 | 4 | - High performance with webassembly 5 | - Use open-standards data format like GeoJson, 3dtiles, gltf 6 | 7 | 8 | [文档](doc/index.md), 9 | [更多信息](https://slan.work/product1.html) 10 | -------------------------------------------------------------------------------- /src/feModel/Coord2D.cpp: -------------------------------------------------------------------------------- 1 | #include "feModel/Coord2D.h" 2 | 3 | FE_USING_NAMESPACE 4 | 5 | //double Coord2D::distance(Coord2D &other) { 6 | // double dx = other.x - x; 7 | // double dy = other.y - y; 8 | // return sqrt(dx*dx + dy*dy); 9 | //} 10 | -------------------------------------------------------------------------------- /src/api/main.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2023, chunquedong/yangjiandong 3 | * 4 | * This file is part of mgpEarth project 5 | * all rights reserved 6 | * 7 | */ 8 | #include 9 | 10 | int main() { 11 | printf("main start\n"); 12 | } 13 | 14 | -------------------------------------------------------------------------------- /doc/license.md: -------------------------------------------------------------------------------- 1 | 2 | ## mgpEarth 授权协议 3 | 4 | mgpEarth采用商业和开源许可下的双重许可证。 5 | mgpEarth Javascript API使用LGPL协议。 6 | 7 | mgpEarth商业许可证赋予您根据自己的条款创建和分发软件的全部权利,而无需承担任何开源许可证义务。[商业授权见这里](https://slan.work/felicense.html) 8 | 9 | mgpEarth也可以在GPL v3开源许可证下使用,适用于开源项目、学习/学术目的、业余爱好项目等。 10 | -------------------------------------------------------------------------------- /src/mgpEarth.h: -------------------------------------------------------------------------------- 1 | #ifndef MGP_EARTH_H 2 | #define MGP_EARTH_H 3 | 4 | 5 | #include "feCtrl/EarthApp.h" 6 | #include "feGeo/GeoLayer.h" 7 | #include "feObjects/GroundNode.h" 8 | #include "feModel/GeoCoordSys.h" 9 | #include "feGeo/Building.h" 10 | #include "feElevation/Elevation.h" 11 | #include "api/fe_api.h" 12 | 13 | #endif // FASTEARTH_H 14 | 15 | -------------------------------------------------------------------------------- /src/feObjects/Skybox.cpp: -------------------------------------------------------------------------------- 1 | #include "Skybox.h" 2 | 3 | FE_USING_NAMESPACE 4 | PF_USING_NAMESPACE 5 | 6 | SkyBox::SkyBox(std::vector& faces) : GeoNode("") { 7 | this->faces.swap(faces); 8 | } 9 | 10 | void SkyBox::load() { 11 | UPtr cube = UPtr(new CubeMap()); 12 | cube->load(faces, true); 13 | cube->followCamera = followCamera; 14 | this->setDrawable(cube.dynamicCastTo()); 15 | } 16 | 17 | -------------------------------------------------------------------------------- /src/fmake_wasm_lib.props: -------------------------------------------------------------------------------- 1 | name = mgpEarth 2 | summary = mgpEarth 3 | outType = lib 4 | version = 1.0 5 | depends = mgpCore 1.0, mgpModules 1.0, mgpPro 1.0, jsonc 2.0, draco 1.4.1, clipper2 2.0, bullet 3.24, sric 1.0, waseGraphics 1.0, waseGui 1.0, serial 1.0, waseNanovg 1.0, sricNet 1.0 6 | srcDirs = fe3dtiles/,feCtrl/,feElevation/,feGeo/,feModel/,feObjects/,feTile/,feUtil/,api/c_api.cpp 7 | incDir = ./ 8 | defines=GP_NO_LUA_BINDINGS,GP_GLFW,NO_BRAND,GP_UI 9 | gcc.extConfigs.cppflags = -std=c++17 10 | compiler = emcc -------------------------------------------------------------------------------- /src/fmake.props: -------------------------------------------------------------------------------- 1 | name = mgpEarth 2 | summary = mgpEarth 3 | outType = lib 4 | version = 1.0 5 | depends = mgpCore 1.0, mgpModules 1.0, mgpPro 1.0, glfw 3.3.8, glew 2.2.0, bullet 3.24, freetype 2.4.12, jsonc 2.0, ljs 1.0, curl 8, zlib 1.0, clipper2 2.0, sric 1.0, waseGraphics 1.0, waseGui 1.0, serial 1.0, waseNanovg 1.0, sricNet 1.0 6 | srcDirs = fe3dtiles/,feCtrl/,feModel/,feTile/,feUtil/,feGeo/,feObjects/,feElevation/,api/c_api.cpp 7 | incDir = ./ 8 | win32.defines = UNICODE,GP_NO_LUA_BINDINGS,GP_GLFW,GP_UI 9 | gcc.extConfigs.cppflags = -std=c++14 -------------------------------------------------------------------------------- /.hgignore: -------------------------------------------------------------------------------- 1 | syntax: glob 2 | Makefile 3 | fastEarth/Makefile.fastEarth 4 | *.pro.user 5 | example/Makefile.test 6 | ext/fastEarthAndroid/obj/ 7 | ext/fastEarthAndroid/bin/ 8 | *.5pre1 9 | ext/fastEarthAndroid/libs/ 10 | ext/fastEarthAndroid/gen/ 11 | ext/fastEarthAndroid/jni/com_fastearth_engine_MainJni.h 12 | ext/fastEarthAndroid/jni/libthirdparty.a 13 | ext/fastEarthAndroid/.settings/org.eclipse.jdt.core.prefs 14 | .DS_Store 15 | ext/fastEarth/fastEarth.xcodeproj/xcuserdata 16 | *.user.3.2-pre1 17 | *.user.3.3-pre1 18 | ext/fastEarth/fastEarth.xcodeproj/project.xcworkspace/xcuserdata/ 19 | -------------------------------------------------------------------------------- /sdk/demo/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | mgpEarth Demo 7 | 8 | 9 |

10 | min 11 | demo 12 | 3dtiles 13 | geojson 14 | gltf 15 | track 16 | geolayer 17 |

18 | 19 | -------------------------------------------------------------------------------- /src/fe3dtiles/b3dm.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2023, chunquedong/yangjiandong 3 | * 4 | * This file is part of mgpEarth project 5 | * Licensed under the GNU GENERAL PUBLIC LICENSE Version 3 6 | * 7 | */ 8 | #ifndef _3DTILE_B3DM_H 9 | #define _3DTILE_B3DM_H 10 | 11 | #include "feUtil/common.h" 12 | #include "mgp.h" 13 | 14 | 15 | FE_BEGIN_NAMESPACE 16 | 17 | struct TdTilesData { 18 | }; 19 | 20 | struct TdB3dm : public TdTilesData { 21 | std::string gltf; 22 | mgp::Vector3 rtcCenter; 23 | 24 | 25 | bool loadB3dm(mgp::Stream* data); 26 | }; 27 | 28 | 29 | FE_END_NAMESPACE 30 | 31 | #endif 32 | -------------------------------------------------------------------------------- /src/feObjects/Skybox.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2023, chunquedong/yangjiandong 3 | * 4 | * This file is part of mgpEarth project 5 | * all rights reserved 6 | * 7 | */ 8 | #ifndef _SKY_BOX_H_ 9 | #define _SKY_BOX_H_ 10 | 11 | #include "feUtil/common.h" 12 | #include "mgp_pro.h" 13 | #include "feModel/Envelope.h" 14 | #include "GeoNode.h" 15 | 16 | FE_BEGIN_NAMESPACE 17 | 18 | class SkyBox : public GeoNode { 19 | public: 20 | bool followCamera = true; 21 | std::vector faces; 22 | SkyBox(std::vector& faces); 23 | void load() override; 24 | }; 25 | 26 | 27 | FE_END_NAMESPACE 28 | 29 | #endif // _SKY_BOX_H_ -------------------------------------------------------------------------------- /src/feGeo/Building.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2023, chunquedong/yangjiandong 3 | * 4 | * This file is part of mgpEarth project 5 | * all rights reserved 6 | * 7 | */ 8 | #ifndef _BUILDING_H 9 | #define _BUILDING_H 10 | 11 | #include "feUtil/common.h" 12 | #include "mgp_pro.h" 13 | #include "feModel/Envelope.h" 14 | #include "GeoLayer.h" 15 | 16 | FE_BEGIN_NAMESPACE 17 | 18 | class Building : public GeoNode { 19 | public: 20 | mgp::Vector4 color; 21 | float heightScale = 4.0; 22 | 23 | Building(const char* uri); 24 | ~Building(); 25 | 26 | void* decodeFile(const char* path, mgp::NetResponse& lastRes, mgp::MultiRequest* req) override; 27 | bool loadOptions(char* json_str); 28 | }; 29 | 30 | FE_END_NAMESPACE 31 | 32 | #endif // _BUILDING_H -------------------------------------------------------------------------------- /src/feModel/Coord2D.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2023, chunquedong/yangjiandong 3 | * 4 | * This file is part of mgpEarth project 5 | * all rights reserved 6 | * 7 | */ 8 | #ifndef COORD2D_H 9 | #define COORD2D_H 10 | 11 | #include "feUtil/common.h" 12 | #include "mgp.h" 13 | 14 | FE_BEGIN_NAMESPACE 15 | 16 | ///** 17 | // * 2D Coordinate 18 | // */ 19 | //struct Coord2D { 20 | // double x; 21 | // double y; 22 | // 23 | // Coord2D() : x(0), y(0) {} 24 | // Coord2D(double x, double y) : x(x), y(y) { 25 | // } 26 | // 27 | // void set(double x, double y) { 28 | // this->x = x; 29 | // this->y = y; 30 | // } 31 | // 32 | // double distance(Coord2D &other); 33 | //}; 34 | 35 | typedef mgp::Vector2 Coord2D; 36 | 37 | FE_END_NAMESPACE 38 | #endif // COORD2D_H 39 | -------------------------------------------------------------------------------- /src/feUtil/gcj.h: -------------------------------------------------------------------------------- 1 | 2 | 3 | int wgs2bd(double lat, double lon, double* pLat, double* pLon); // WGS84=>BD09 地球坐标系=>百度坐标系 4 | int gcj2bd(double lat, double lon, double* pLat, double* pLon); // GCJ02=>BD09 火星坐标系=>百度坐标系 5 | int bd2gcj(double lat, double lon, double* pLat, double* pLon); // BD09=>GCJ02 百度坐标系=>火星坐标系 6 | int wgs2gcj(double lat, double lon, double* pLat, double* pLon);// WGS84=>GCJ02 地球坐标系=>火星坐标系 7 | int gcj2wgs(double lat, double lon, double* pLat, double* pLon);// GCJ02=>WGS84 火星坐标系=>地球坐标系(粗略) 8 | int bd2wgs(double lat, double lon, double* pLat, double* pLon); // BD09=>WGS84 百度坐标系=>地球坐标系(粗略) 9 | int gcj2wgs_Exactly(double lat, double lon, double* wgs_Lat, double* wgs_lon);// GCJ02=>WGS84 火星坐标系=>地球坐标系(精确) 10 | int bd2wgs_Exactly(double lat, double lon, double* pLat, double* pLon);// BD09=>WGS84 百度坐标系=>地球坐标系(精确) 11 | -------------------------------------------------------------------------------- /src/feTile/TileLayer.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2023, chunquedong/yangjiandong 3 | * 4 | * This file is part of mgpEarth project 5 | * all rights reserved 6 | * 7 | */ 8 | #ifndef TILELAYER_H 9 | #define TILELAYER_H 10 | 11 | #include "mgp.h" 12 | #include "feTile/XyzTileManager.h" 13 | 14 | 15 | FE_BEGIN_NAMESPACE 16 | 17 | class TileLayer : public mgp::Node { 18 | TileManager* tileManager; 19 | //DelayUpdater delayUpdater; 20 | bool isTransformInited = false; 21 | public: 22 | float getProgress(); 23 | //void setViewDirty() { delayUpdater.setDirty(); } 24 | //unsigned int draw(RenderInfo* view) override; 25 | virtual void update(float elapsedTime) override; 26 | public: 27 | TileLayer(TileManager* tileManager); 28 | ~TileLayer(); 29 | }; 30 | 31 | FE_END_NAMESPACE 32 | 33 | #endif // TILELAYER_H 34 | -------------------------------------------------------------------------------- /doc/build.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | ## 从源码构建 4 | 5 | 预构建的SDK可以从官网下载。自己编译会比较麻烦。 6 | 7 | ### 需要安装的软件 8 | - JDK 9 | - Python 3 10 | - Node.js 11 | - [Emscripten](https://emscripten.org/docs/getting_started/downloads.html) 12 | - [Fanx](https://github.com/fanx-dev/fanx/releases) 13 | - [fmake](https://github.com/) 14 | 15 | ### 依赖的第三方库 16 | - mgpCore 17 | - mgpModules 18 | - jsonc 19 | - draco 20 | - mgpPro 21 | - sric 22 | 23 | 桌面版本还依赖下列库: 24 | - glfw 25 | - glew 26 | - miniaudio 27 | - bullet 28 | - freetype 29 | - curl 30 | - zlib 31 | 32 | 请联系作者获取预编译的第三方库。 33 | 34 | ### 构建Web版 35 | 需要先配置fmake配置文件,见fmake项目的readme文件中的Emscripten配置。 36 | 37 | 38 | 编译Web版 39 | ``` 40 | sh build.sh 41 | ``` 42 | 43 | ### 构建桌面版 44 | 45 | 编译 46 | ``` 47 | fan famke src/fmake.props -debug 48 | ``` 49 | 生成IDE项目文件 50 | ``` 51 | fan famke src/fmake.props -debug -G 52 | ``` 53 | 54 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | syntax: glob 2 | Makefile 3 | fastEarth/Makefile.fastEarth 4 | *.pro.user 5 | example/Makefile.test 6 | ext/fastEarthAndroid/obj/ 7 | ext/fastEarthAndroid/bin/ 8 | *.5pre1 9 | ext/fastEarthAndroid/libs/ 10 | ext/fastEarthAndroid/gen/ 11 | ext/fastEarthAndroid/jni/com_fastearth_engine_MainJni.h 12 | ext/fastEarthAndroid/jni/libthirdparty.a 13 | ext/fastEarthAndroid/.settings/org.eclipse.jdt.core.prefs 14 | .DS_Store 15 | ext/fastEarth/fastEarth.xcodeproj/xcuserdata 16 | *.user.3.2-pre1 17 | *.user.3.3-pre1 18 | ext/fastEarth/fastEarth.xcodeproj/project.xcworkspace/xcuserdata/ 19 | build/ 20 | .vs/ 21 | server/node_modules/ 22 | server/shanghai/ 23 | sdk/server/node_modules/ 24 | sdk/server/package-lock.json 25 | sdk/server/ 26 | sdk/fastearth-sdk/ 27 | sdk/fastearth-sdk-1.0.2.zip 28 | sdk/mgpEarth-sdk/ 29 | sdk/mgpEarth-sdk-1.1.0.zip 30 | sdk/mgpEarth-sdk-*.zip 31 | -------------------------------------------------------------------------------- /src/feModel/PyramidGrid.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2023, chunquedong/yangjiandong 3 | * 4 | * This file is part of mgpEarth project 5 | * all rights reserved 6 | * 7 | */ 8 | #ifndef PYRAMIDGRID_H 9 | #define PYRAMIDGRID_H 10 | 11 | #include "feUtil/common.h" 12 | #include "feModel/Envelope.h" 13 | #include "feModel/Tile.h" 14 | #include "mgp.h" 15 | 16 | //CF_USING_NAMESPACE 17 | 18 | FE_BEGIN_NAMESPACE 19 | 20 | class PyramidGrid { 21 | double width; 22 | double height; 23 | double baseX; 24 | double baseY; 25 | bool flipY; 26 | Envelope envelope; 27 | public: 28 | PyramidGrid(Envelope &env); 29 | 30 | static PyramidGrid *getDefault(); 31 | static PyramidGrid* getBD(); 32 | /** 33 | * get the tile envelope 34 | */ 35 | void tileEnvelope(Tile &tile, Envelope &env); 36 | 37 | 38 | void tileEnvelopeBL(Tile &tile, Envelope &env); 39 | 40 | void tileScreenSize(int &w, int &h); 41 | 42 | Tile getTileAt(double x, double y, int level); 43 | }; 44 | 45 | FE_END_NAMESPACE 46 | #endif // PYRAMIDGRID_H 47 | -------------------------------------------------------------------------------- /src/feObjects/GeoNode.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2023, chunquedong/yangjiandong 3 | * 4 | * This file is part of mgpEarth project 5 | * all rights reserved 6 | * 7 | */ 8 | #ifndef _GEO_NODE_H 9 | #define _GEO_NODE_H 10 | 11 | #include "feUtil/common.h" 12 | #include "mgp_pro.h" 13 | #include 14 | 15 | FE_BEGIN_NAMESPACE 16 | 17 | class ElevationManager; 18 | class EarthCtrl; 19 | 20 | class GeoNode : public mgp::NetModel { 21 | protected: 22 | bool isVisiable = false; 23 | public: 24 | EarthCtrl* ctrl = NULL; 25 | double minDis = 0; 26 | double maxDis = FLT_MAX; 27 | 28 | GeoNode(const char* uri); 29 | void update(float elapsedTime) override; 30 | }; 31 | 32 | 33 | class GltfNode : public GeoNode { 34 | public: 35 | int lighting = 0; 36 | GltfNode(const char* uri); 37 | 38 | virtual void* decodeFile(const char* path, mgp::NetResponse& lastRes, mgp::MultiRequest* req) override; 39 | void scanAttachedFiles(mgp::NetResponse &res, std::vector& urls, mgp::MultiRequest* req) override; 40 | 41 | }; 42 | 43 | FE_END_NAMESPACE 44 | 45 | #endif // _GEO_NODE_H 46 | -------------------------------------------------------------------------------- /src/feCtrl/TraceBall.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2023, chunquedong/yangjiandong 3 | * 4 | * This file is part of mgpEarth project 5 | * all rights reserved 6 | * 7 | */ 8 | #if 0 9 | #ifndef TRACEBALL_H 10 | #define TRACEBALL_H 11 | 12 | #include "feCtrl/EarthCtrl.h" 13 | 14 | FE_BEGIN_NAMESPACE 15 | 16 | class TraceBall : public EarthCtrl { 17 | Quaternion earthRotation; 18 | public: 19 | TraceBall(); 20 | 21 | virtual Coord2D getPosition() override; 22 | 23 | /** 24 | * set look at surface postion in geo coordinate system 25 | */ 26 | virtual void moveToPostion(Coord2D pos) override; 27 | 28 | virtual void setRotationZ(double rotZ) override; 29 | 30 | virtual void moveByPixel(float dx, float dy) override; 31 | 32 | protected: 33 | virtual void updateCameraTransform(Camera &camera, Rectangle &viewport) override; 34 | 35 | private: 36 | /** 37 | * project screen center near point to earth shpere surface point 38 | */ 39 | void projectToShpere(float x, float y, Vector &vec); 40 | 41 | /** 42 | * convert geo coordinate system to Quaternion rotation 43 | */ 44 | void blToQuaternion(Coord2D &postion, Quaternion &out); 45 | 46 | Coord2D quaternionToBl(Quaternion &quat); 47 | }; 48 | 49 | FE_END_NAMESPACE 50 | #endif // TRACEBALL_H 51 | #endif -------------------------------------------------------------------------------- /src/feTile/DataActor.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2023, chunquedong/yangjiandong 3 | * 4 | * This file is part of mgpEarth project 5 | * all rights reserved 6 | * 7 | */ 8 | #ifndef DATAACTOR_H 9 | #define DATAACTOR_H 10 | 11 | #include "mgp_pro.h" 12 | #include "feTile/XyzTileManager.h" 13 | 14 | FE_BEGIN_NAMESPACE 15 | 16 | struct ViewState { 17 | mgp::Camera camera; 18 | mgp::Rectangle viewport; 19 | mgp::Matrix modelMatrix; 20 | ~ViewState(); 21 | }; 22 | 23 | class DataActor : public mgp::SimpleActor { 24 | ViewState newestState; 25 | ViewState curState; 26 | 27 | std::vector renderableList; 28 | bool renderableDirty; 29 | 30 | TileManager* tileManager; 31 | 32 | std::mutex lock; 33 | public: 34 | DataActor(TileManager* tileManager); 35 | ~DataActor(); 36 | 37 | void sendMakeData(); 38 | void sendViewUpdate(mgp::Camera* camera, mgp::Rectangle* viewport, mgp::Matrix* modelMatrix); 39 | 40 | void getResult(std::vector& list); 41 | bool resultChanged(); 42 | 43 | virtual void onReceive(Message &msg) override; 44 | virtual void onCancel(Message& msg) override; 45 | virtual bool mergeMessage(Message *cur, Message *pre); 46 | }; 47 | 48 | 49 | FE_END_NAMESPACE 50 | 51 | #endif // DATAACTOR_H 52 | -------------------------------------------------------------------------------- /src/feElevation/Elevation.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2023, chunquedong/yangjiandong 3 | * 4 | * This file is part of mgpEarth project 5 | * all rights reserved 6 | * 7 | */ 8 | #ifndef ELEVATION_H 9 | #define ELEVATION_H 10 | 11 | #include "feModel/Tile.h" 12 | #include "mgp.h" 13 | #include "feTile/TileManager.h" 14 | #include "feTile/TileData.h" 15 | 16 | FE_BEGIN_NAMESPACE 17 | 18 | 19 | class Elevation { 20 | public: 21 | virtual double getHeight(double longitude, double latitude, int level) = 0; 22 | virtual double getHeightMercator(double x, double y, int level) = 0; 23 | }; 24 | 25 | class OfflineElevation: public Elevation { 26 | protected: 27 | void* imageData; 28 | int width; 29 | int height; 30 | Envelope envelope; 31 | int format; 32 | public: 33 | double elevationScale; 34 | protected: 35 | OfflineElevation(); 36 | public: 37 | static OfflineElevation* cur(); 38 | static void _setCur(OfflineElevation* elev); 39 | OfflineElevation(const char* uri, int format = 2, Envelope* env = NULL); 40 | virtual double getHeight(double longitude, double latitude, int level) override; 41 | virtual double getHeightMercator(double x, double y, int level) override; 42 | private: 43 | double sampleHeight(int x, int y); 44 | }; 45 | 46 | 47 | FE_END_NAMESPACE 48 | 49 | #endif // ELEVATION_H -------------------------------------------------------------------------------- /doc/gltf.md: -------------------------------------------------------------------------------- 1 | 2 | ## 3D模型加载 3 | 4 | 支持gltf和3dtiles格式。 5 | 6 | 添加图层/模型接口第一个参数为对象名称,用于后续修改和删除操作,名称请勿重复。 7 | 8 | ### 单个gltf加载 9 | ``` 10 | //options参数用于调整模型的默认姿态 11 | mgpEarth.addGroundGltf("gltf", "/data/shanghai/scene.gltf", 12 | 121.507762+0.006, 31.233975-0.003, 118, true, { 13 | "rotateX" : 3.1415926/2, 14 | "rotateY" : 245/360.0*3.1415926*2, 15 | "scale": 520 16 | }); 17 | 18 | //场景中添加灯光,颜色可以使用大于1.0的值。 19 | mgpEarth.addLight("light", 140, 0, 28, 28, 28); 20 | ``` 21 | 22 | ### 加载3dtiles 23 | ``` 24 | //最后的options参数和addGroundGltf类似,用于调整模型的默认姿态,这里使用默认值。 25 | mgpEarth.add3dtiles("da_yan_ta", "/3dtiles/qx-dyt/0/root.json", 108.964164, 34.218175, 35, false); 26 | ``` 27 | 28 | ### 多实例模型 29 | 30 | 使用addGroundGltf加载的gltf模型,多个实例不能共享资源。为提高效率使用addMultiModel来实现多实例支持。 31 | ``` 32 | //加载gltf模型 33 | mgpEarth.addMultiModel("car", "/data/gltf/seatLeon.gltf", false, null); 34 | 35 | //创建car模型的第一个实例 36 | mgpEarth.updateModelInstance("car", -1, 108.964164, 34.218175, 0, { 37 | "rotateX" : -3.1415926/2, 38 | "rotateZ" : 3.1415926, 39 | "scale": 10 40 | }); 41 | 42 | //创建car模型的第二个实例 43 | mgpEarth.updateModelInstance("car", -1, 108.964164, 34.218175, 0, { 44 | "rotateX" : -3.1415926/2, 45 | "rotateZ" : 3.1415926, 46 | "scale": 10 47 | }); 48 | ``` 49 | -------------------------------------------------------------------------------- /src/feObjects/GeoNode.cpp: -------------------------------------------------------------------------------- 1 | #include "GeoNode.h" 2 | #include "feModel/GeoCoordSys.h" 3 | #include "feElevation/Elevation.h" 4 | #include "feCtrl/EarthCtrl.h" 5 | 6 | FE_USING_NAMESPACE 7 | PF_USING_NAMESPACE 8 | 9 | GeoNode::GeoNode(const char* uri): NetModel(uri) { 10 | } 11 | 12 | void GeoNode::update(float elapsedTime) { 13 | if (!ctrl) { 14 | return; 15 | } 16 | double dis = ctrl->getDistanceToSurface(); 17 | bool visible = false; 18 | if (dis > minDis && dis <= maxDis) { 19 | visible = true; 20 | } 21 | if (this->isVisiable != visible) { 22 | this->isVisiable = visible; 23 | if (visible) { 24 | if (!loadState) { 25 | load(); 26 | loadState = 1; 27 | } 28 | } 29 | std::vector drawables; 30 | this->getAllDrawable(drawables); 31 | for (Drawable* d : drawables) { 32 | d->setVisiable(visible); 33 | } 34 | } 35 | NetModel::update(elapsedTime); 36 | } 37 | 38 | 39 | 40 | GltfNode::GltfNode(const char* uri) : GeoNode(uri) { 41 | } 42 | void GltfNode::scanAttachedFiles(NetResponse &res, std::vector& urls, MultiRequest* req) { 43 | GltfModel::scanBinFile(res.result, urls); 44 | } 45 | void* GltfNode::decodeFile(const char* path, NetResponse& lastRes, MultiRequest* req) { 46 | return GltfModel::decodeGltf(path, lighting); 47 | } -------------------------------------------------------------------------------- /src/fmake_wasm.props: -------------------------------------------------------------------------------- 1 | name = mgpEarthApi 2 | summary = mgpEarthApi 3 | outType = exe 4 | version = 1.0 5 | depends = mgpCore 1.0, mgpModules 1.0, mgpPro 1.0, jsonc 2.0, draco 1.4.1, clipper2 2.0, bullet 3.24, sric 1.0, waseGraphics 1.0, waseGui 1.0, serial 1.0, waseNanovg 1.0, sricNet 1.0 6 | srcDirs = ./* 7 | incDir = ./ 8 | defines=GP_NO_LUA_BINDINGS,GP_GLFW,GP_UI 9 | //extIncDirs = /C:/soft/emsdk/upstream/emscripten/system/include/ 10 | extConfigs.linkflags= -O3 -sUSE_GLFW=3 -sMIN_WEBGL_VERSION=2 -sMAX_WEBGL_VERSION=2 -sFULL_ES2 -sFULL_ES3 -sFETCH \ 11 | -sMODULARIZE -s EXPORT_NAME="createMyModule" -sALLOW_MEMORY_GROWTH=1 \ 12 | -sEXPORTED_FUNCTIONS=_main,_malloc,_free -sPTHREAD_POOL_SIZE=4 -sDEFAULT_LIBRARY_FUNCS_TO_INCLUDE=$stringToUTF8OnStack -sEXPORTED_RUNTIME_METHODS=ccall,cwrap,UTF8ToString \ 13 | --js-library ../mgpPro/wasm/library.js --extern-pre-js ../mgpPro/wasm/util.js --extern-pre-js ./sdk/api/fe_api.js \ 14 | --preload-file ../mgp/res/shaders@res/shaders \ 15 | --preload-file ../mgp/res/ui@res/ui --exclude-file ../mgp/res/ui/*.ttf --exclude-file ../mgp/res/ui/*.png~ \ 16 | --preload-file ../mgp/res/image/m2.png@res/image/m2.png \ 17 | --preload-file ../mgp/res/image/text_bubble.png@res/image/text_bubble.png \ 18 | --preload-file ../mgp/res/skybox/MilkyWay@res/skybox/MilkyWay \ 19 | --preload-file ../mgp/res/skybox/skyboxsun25deg@res/skybox/skyboxsun25deg \ 20 | --preload-file ../mgp/res/fastEarth@res/fastEarth 21 | compiler = emcc -------------------------------------------------------------------------------- /src/feModel/GeoCoordSys.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2023, chunquedong/yangjiandong 3 | * 4 | * This file is part of mgpEarth project 5 | * all rights reserved 6 | * 7 | */ 8 | #ifndef GEOCOORDSYS_H 9 | #define GEOCOORDSYS_H 10 | 11 | #include "feUtil/common.h" 12 | #include "mgp.h" 13 | #include "feModel/Coord2D.h" 14 | 15 | FE_BEGIN_NAMESPACE 16 | 17 | class GeoCoordSys { 18 | double radius; 19 | static const double earthRadius; 20 | public: 21 | GeoCoordSys(double radius) : radius(radius) { 22 | } 23 | 24 | double getRadius() { return radius; } 25 | 26 | static GeoCoordSys* earth(); 27 | 28 | /** 29 | ** Get the spherical distance of two geographic coordinates points 30 | **/ 31 | double distance(const Coord2D p1, const Coord2D p2); 32 | 33 | /** 34 | ** From spherical coordinates to rectangular space coordinates 35 | **/ 36 | void lnglatToXyz(const Coord2D& lnglat, double height, Vector &out) { 37 | blToXyz(lnglat, out, radius + height); 38 | } 39 | 40 | void xyzToLnglat(const Vector& point, Coord2D& lnglat, double* height) { 41 | xyzToBl(point, lnglat); 42 | if (height) 43 | *height = point.length() - radius; 44 | } 45 | 46 | static void blToXyz(const Coord2D p, Vector &out, double radius); 47 | 48 | static void xyzToBl(const Vector &vec, Coord2D &p); 49 | 50 | void toMercator(Coord2D *p); 51 | 52 | void fromMercator(Coord2D *p); 53 | }; 54 | 55 | FE_END_NAMESPACE 56 | 57 | #endif // GEOCOORDSYS_H 58 | -------------------------------------------------------------------------------- /src/fmake_wasm_debug.props: -------------------------------------------------------------------------------- 1 | name = mgpEarthApi 2 | summary = mgpEarthApi 3 | outType = exe 4 | version = 1.0 5 | depends = mgpCore 1.0, mgpModules 1.0, mgpPro 1.0, jsonc 2.0, draco 1.4.1, clipper2 2.0, bullet 3.24, sric 1.0, waseGraphics 1.0, waseGui 1.0, serial 1.0, waseNanovg 1.0, sricNet 1.0 6 | srcDirs = ./* 7 | incDir = ./ 8 | defines=GP_NO_LUA_BINDINGS,GP_GLFW,GP_UI 9 | //extIncDirs = /C:/soft/emsdk/upstream/emscripten/system/include/ 10 | extConfigs.linkflags= -O3 -sUSE_GLFW=3 -sMIN_WEBGL_VERSION=2 -sMAX_WEBGL_VERSION=2 -sFULL_ES2 -sFULL_ES3 -sSAFE_HEAP=1 -sFETCH -sASSERTIONS \ 11 | -g -sMODULARIZE -s EXPORT_NAME="createMyModule" -sALLOW_MEMORY_GROWTH=1 \ 12 | -sEXPORTED_FUNCTIONS=_main,_malloc,_free -sPTHREAD_POOL_SIZE=4 -sDEFAULT_LIBRARY_FUNCS_TO_INCLUDE=$stringToUTF8OnStack -sEXPORTED_RUNTIME_METHODS=ccall,cwrap,UTF8ToString \ 13 | --js-library ../mgpPro/wasm/library.js --extern-pre-js ../mgpPro/wasm/util.js --extern-pre-js ./sdk/api/fe_api.js \ 14 | --preload-file ../mgp/res/shaders@res/shaders \ 15 | --preload-file ../mgp/res/ui@res/ui --exclude-file ../mgp/res/ui/*.ttf --exclude-file ../mgp/res/ui/*.png~ \ 16 | --preload-file ../mgp/res/image/m2.png@res/image/m2.png \ 17 | --preload-file ../mgp/res/image/text_bubble.png@res/image/text_bubble.png \ 18 | --preload-file ../mgp/res/skybox/MilkyWay@res/skybox/MilkyWay \ 19 | --preload-file ../mgp/res/skybox/skyboxsun25deg@res/skybox/skyboxsun25deg \ 20 | --preload-file ../mgp/res/fastEarth@res/fastEarth 21 | compiler = emcc -------------------------------------------------------------------------------- /src/feTile/TileGeom.hpp: -------------------------------------------------------------------------------- 1 | // 2 | // TileGeom.hpp 3 | // mgpEarth 4 | // 5 | // Created by yangjiandong on 2017/5/21. 6 | // Copyright © 2017年 yangjiandong. All rights reserved. 7 | // 8 | 9 | #ifndef TileGeom_hpp 10 | #define TileGeom_hpp 11 | 12 | #include "feUtil/common.h" 13 | #include "mgp.h" 14 | #include "feModel/Tile.h" 15 | #include "feModel/Envelope.h" 16 | 17 | FE_BEGIN_NAMESPACE 18 | 19 | class ElevationQuery; 20 | class PyramidGrid; 21 | 22 | class TileGeom : public mgp::Drawable { 23 | mgp::UPtr _mesh; 24 | mgp::UPtr _material; 25 | 26 | mgp::UPtr texture; 27 | mgp::Vector3 tranlation; 28 | public: 29 | PyramidGrid* _pyramid; 30 | mgp::Vector3 getTranlation() { return tranlation; } 31 | mgp::Mesh* getMesh() { return _mesh.get(); } 32 | 33 | TileGeom(PyramidGrid* pyramid); 34 | ~TileGeom(); 35 | void init(mgp::Image *image, Tile &tile, Tile* elevationTile, ElevationQuery* elevation); 36 | 37 | unsigned int draw(mgp::RenderInfo* view) override; 38 | bool doRaycast(mgp::RayQuery& query) override; 39 | const mgp::BoundingSphere* getBoundingSphere() override; 40 | mgp::Material* getMainMaterial() const override { return _material.get(); }; 41 | private: 42 | mgp::UPtr makeMesh(double radius, mgp::Vector3 ¢er, int lod, Tile &tile, mgp::Vector3& translation, Tile* elevationTile, ElevationQuery* elevation); 43 | }; 44 | 45 | 46 | FE_END_NAMESPACE 47 | #endif /* TileGeom_hpp */ 48 | -------------------------------------------------------------------------------- /src/feTile/TileLayer.cpp: -------------------------------------------------------------------------------- 1 | #include "feTile/TileLayer.h" 2 | #include "mgp.h" 3 | //#include "cppfan/Profiler.h" 4 | #include "feTile/TileData.h" 5 | 6 | FE_USING_NAMESPACE 7 | PF_USING_NAMESPACE 8 | 9 | TileLayer::TileLayer(TileManager* tileManager) { 10 | //delayUpdater.setMaxUpdateDelay(20); 11 | this->tileManager = tileManager; 12 | //this->tileManager->start(); 13 | } 14 | TileLayer::~TileLayer() { 15 | //if (tileManager) tileManager->stop(); 16 | SAFE_RELEASE(tileManager); 17 | } 18 | 19 | void TileLayer::update(float elapsedTime) { 20 | Camera* camera = getScene()->getActiveCamera(); 21 | if (!camera) return; 22 | //TODO 23 | Rectangle viewport(0, 0, 1920, 1080); 24 | 25 | if (!isTransformInited) { 26 | Matrix* rootTransform = tileManager->getRootTransform(); 27 | if (rootTransform) { 28 | isTransformInited = true; 29 | Matrix matrix = (*rootTransform) * this->getMatrix(); 30 | this->setMatrix(matrix); 31 | } 32 | } 33 | 34 | Matrix matrix = getWorldMatrix(); 35 | tileManager->update(camera, &viewport, &matrix); 36 | 37 | if (tileManager->resultChanged()) { 38 | std::vector resultList; 39 | tileManager->getResult(resultList); 40 | 41 | removeAllChildren(); 42 | for (TileDataPtr& tile : resultList) { 43 | addChild(uniqueFromInstant(tile->getNode())); 44 | } 45 | } 46 | 47 | Node::update(elapsedTime); 48 | } 49 | 50 | float TileLayer::getProgress() { 51 | return tileManager->getProgress(); 52 | } 53 | -------------------------------------------------------------------------------- /src/feModel/Tile.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2023, chunquedong/yangjiandong 3 | * 4 | * This file is part of mgpEarth project 5 | * all rights reserved 6 | * 7 | */ 8 | #ifndef TILE_H 9 | #define TILE_H 10 | 11 | #include "feUtil/common.h" 12 | #include "feModel/Envelope.h" 13 | 14 | 15 | FE_BEGIN_NAMESPACE 16 | 17 | /** 18 | * 2D Rectangle range 19 | */ 20 | class Tile 21 | { 22 | public: 23 | int x; 24 | int y; 25 | int z; 26 | 27 | Tile(); 28 | 29 | void init(int x, int y, int z) { 30 | this->x = x; 31 | this->y = y; 32 | this->z = z; 33 | } 34 | 35 | /* 36 | 2 | 3 37 | --+-- 38 | 0 | 1 39 | */ 40 | void subTile(Tile subTiles[4]); 41 | 42 | Tile parent(); 43 | 44 | bool operator< (const Tile &other) const { 45 | if (x < other.x) return true; 46 | if (x > other.x) return false; 47 | if (y < other.y) return true; 48 | if (y > other.y) return false; 49 | if (z < other.z) return true; 50 | if (z > other.z) return false; 51 | return false; 52 | } 53 | 54 | bool operator== (const Tile &other) const { 55 | return x == other.x 56 | && y == other.y 57 | && z == other.z; 58 | } 59 | 60 | size_t hashCode() const { 61 | size_t hashValue = x; 62 | hashValue = y + 31 * hashValue; 63 | hashValue = z + 31 * hashValue; 64 | return hashValue; 65 | } 66 | 67 | void toQuadKey(std::string& quadKey); 68 | void fromQuadKey(const std::string& quadKey); 69 | }; 70 | 71 | FE_END_NAMESPACE 72 | 73 | namespace std { 74 | template <> struct hash { 75 | size_t operator()(const mgpEarth::Tile &key) const { 76 | return key.hashCode(); 77 | } 78 | }; 79 | } 80 | 81 | 82 | #endif // TILE_H 83 | -------------------------------------------------------------------------------- /src/fe3dtiles/TdTilesManager.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2023, chunquedong/yangjiandong 3 | * 4 | * This file is part of mgpEarth project 5 | * Licensed under the GNU GENERAL PUBLIC LICENSE Version 3 6 | * 7 | */ 8 | #ifndef _T3DLayer_H 9 | #define _T3DLayer_H 10 | 11 | #include "feUtil/common.h" 12 | #include "mgp.h" 13 | #include "TdTiles.h" 14 | #include "feTile/TileLayer.h" 15 | 16 | FE_BEGIN_NAMESPACE 17 | 18 | struct TdTilesData; 19 | struct TdTile; 20 | 21 | 22 | class TdTilesManager : public TileManager { 23 | 24 | TdTileset tileset; 25 | 26 | std::string uri; 27 | 28 | std::map tokens; 29 | 30 | bool gltfUpAxisZ = false; 31 | 32 | public: 33 | int lighting = 0; 34 | 35 | TdTilesManager(const std::string& uri); 36 | ~TdTilesManager(); 37 | //unsigned int draw(RenderInfo* view) override; 38 | //virtual void update(float elapsedTime) override; 39 | //virtual const BoundingSphere& getBoundingSphere() const override; 40 | 41 | virtual void* decode(mgp::HttpClient* task, mgp::NetResponse &res) override; 42 | virtual void onReceive(mgp::HttpClient* task, mgp::NetResponse &res) override; 43 | 44 | void init(); 45 | 46 | virtual mgp::Matrix* getRootTransform() override; 47 | private: 48 | void loadData(Tile* tile); 49 | 50 | virtual TileDataPtr getRoot(); 51 | virtual TileDataPtr makeTileData(TileKey key); 52 | virtual void getChildren(TileDataPtr &data, std::vector &children); 53 | virtual bool isFitLod(TileDataPtr &data, mgp::Camera &camera, mgp::Rectangle &viewport, mgp::Matrix& modelMatrix); 54 | virtual bool getUri(TileKey key, std::string &uri, std::string &file); 55 | }; 56 | 57 | FE_END_NAMESPACE 58 | 59 | #endif 60 | -------------------------------------------------------------------------------- /src/feTile/TileData.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2023, chunquedong/yangjiandong 3 | * 4 | * This file is part of mgpEarth project 5 | * Licensed under the GNU GENERAL PUBLIC LICENSE Version 3 6 | * 7 | */ 8 | #ifndef TILEDATA_H 9 | #define TILEDATA_H 10 | 11 | #include "feUtil/common.h" 12 | #include "mgp.h" 13 | #include "feModel/Tile.h" 14 | #include "feModel/Envelope.h" 15 | #include "feTile/TileManager.h" 16 | 17 | FE_BEGIN_NAMESPACE 18 | class PyramidGrid; 19 | class TileGeom; 20 | 21 | class TileData : public ITileData { 22 | private: 23 | //geo BL envelope 24 | CF_FIELD_REF(Envelope, envelope) 25 | CF_FIELD_REF(Envelope, envelopeMercator) 26 | 27 | //tile id 28 | CF_FIELD_REF(Tile, tile) 29 | CF_FIELD_REF(Tile, elevationTile) 30 | private: 31 | //world coord bounds 32 | Sphere boundingSphere; 33 | 34 | PyramidGrid *pyramid; 35 | mgp::UPtr _node; 36 | 37 | bool _isFallback; 38 | public: 39 | double _approximateHeight; 40 | 41 | virtual mgp::BoundingSphere& bounding(); 42 | virtual int getState(); 43 | virtual mgp::Node* getNode(); 44 | virtual TileKey tileKey(); 45 | virtual void setAsFallback(bool isFallback); 46 | 47 | //attached mesh 48 | mgp::UPtr geometry; 49 | 50 | mgp::UPtr image; 51 | 52 | public: 53 | 54 | TileData(Tile &tile, PyramidGrid *pyramid); 55 | ~TileData(); 56 | 57 | Tile &getTile() { return _tile; } 58 | 59 | private: 60 | //init boundingSphere 61 | void initBounding(); 62 | void updateTranslation(mgp::Node* node); 63 | public: 64 | void printTile(const char *name); 65 | public: 66 | double computeViewScale(mgp::Camera &camera, mgp::Rectangle &viewport); 67 | }; 68 | 69 | 70 | FE_END_NAMESPACE 71 | 72 | #endif // TILEDATA_H 73 | -------------------------------------------------------------------------------- /doc/start.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | ## 开始使用 4 | 5 | mgpEarth提供C、C++、Javascript API接口。这里以JS接口为例来说明。 6 | 7 | 1.增加mgpEarthApi代码链接 8 | ``` 9 | 10 | ``` 11 | 12 | 2.在html内声明canvas对象作为绘图容器 13 | ``` 14 | 15 | ``` 16 | 17 | 3.加载fasetearth 18 | ``` 19 | let w = window.innerWidth; 20 | let h = window.innerHeight; 21 | mgpEarthInit('canvas', w, h, function(mgpEarth){ 22 | //增加天空盒 23 | mgpEarth.addSkybox(1, 20000, Number.MAX_VALUE); 24 | mgpEarth.addSkybox(0, 0, 20000); 25 | 26 | //增加影像图层 27 | let elevationUri = null; 28 | mgpEarth.addTileLayer("gd", "/gd?style=6&x={x}&y={y}&z={z}", elevationUri); 29 | 30 | //设置初始位置 31 | mgpEarth.setPosition(108.964164, 34.218175, 0); 32 | 33 | //窗口大小改变的时候保持全屏显示 34 | function resizeCanvas() { 35 | var canvas = document.getElementById('canvas'); 36 | // canvas.width = window.innerWidth; 37 | // canvas.height = window.innerHeight; 38 | let w = window.innerWidth; 39 | let h = window.innerHeight; 40 | mgpEarth.Module.setCanvasSize(w*window.devicePixelRatio, h*window.devicePixelRatio); 41 | } 42 | window.addEventListener('resize', resizeCanvas, false); 43 | }); 44 | ``` 45 | 46 | 4.配置路径 47 | 需要修改server/index.js内部的文件路径到下载的文件位置: 48 | ``` 49 | const cachePath = "C:/mgpearth-sdk/cache"; 50 | const codePath = "C:/mgpearth-sdk/mgpearth"; 51 | const dataPath = "C:/mgpearth-sdk/data"; 52 | ``` 53 | 54 | 5.启动后端服务 55 | ``` 56 | cd server 57 | npm install 58 | npm start 59 | ``` 60 | 61 | 注意:高程的数据国内访问不太稳定,导致不显示地图,可以把elevationUri设置为空来解决。 62 | 63 | 64 | ### 了解更多 65 | - API文档见sdk/api/fe_api.js 66 | - 示例代码:sdk/demo/ 67 | -------------------------------------------------------------------------------- /src/feModel/Envelope.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2023, chunquedong/yangjiandong 3 | * 4 | * This file is part of mgpEarth project 5 | * all rights reserved 6 | * 7 | */ 8 | #include "feModel/Envelope.h" 9 | #include 10 | #include 11 | 12 | FE_USING_NAMESPACE 13 | 14 | void Envelope::init(double x1, double y1, double x2, double y2) { 15 | if (x1 < x2) { 16 | _minX = x1; 17 | _maxX = x2; 18 | } else { 19 | _minX = x2; 20 | _maxX = x1; 21 | } 22 | if (y1 < y2) { 23 | _minY = y1; 24 | _maxY = y2; 25 | } else { 26 | _minY = y2; 27 | _maxY = y1; 28 | } 29 | } 30 | 31 | void Envelope::makeInvalid() { 32 | _minX = FLT_MAX; 33 | _maxX = -FLT_MAX; 34 | _minY = FLT_MAX; 35 | _maxY = -FLT_MAX; 36 | } 37 | 38 | void Envelope::set(double x, double y, double w, double h) { 39 | double x2 = x + w; 40 | double y2 = y + h; 41 | init(x, y, x2, y2); 42 | } 43 | 44 | void Envelope::merge(const Envelope &bound) { 45 | if (_minX > bound._minX) { 46 | _minX = bound._minX; 47 | } 48 | if (_minY > bound._minY) { 49 | _minY = bound._minY; 50 | } 51 | if (_maxX < bound._maxX) { 52 | _maxX = bound._maxX; 53 | } 54 | if (_maxY < bound._maxY) { 55 | _maxY = bound._maxY; 56 | } 57 | } 58 | 59 | void Envelope::merge(const Coord2D &point) { 60 | if (_minX > point.x) { 61 | _minX = point.x; 62 | } 63 | if (_minY > point.y) { 64 | _minY = point.y; 65 | } 66 | if (_maxX < point.x) { 67 | _maxX = point.x; 68 | } 69 | if (_maxY < point.y) { 70 | _maxY = point.y; 71 | } 72 | } 73 | 74 | #define MIN(a, b) ((a) < (b) ? (a) : (b)) 75 | #define MAX(a, b) ((a) > (b) ? (a) : (b)) 76 | 77 | Envelope Envelope::intersection(const Envelope &env) { 78 | Envelope r; 79 | r._minX = MAX(this->_minX, env._minX); 80 | r._maxX = MIN(this->_maxX, env._maxX); 81 | r._minY = MAX(this->_minY, env._minY); 82 | r._maxY = MIN(this->_maxY, env._maxY); 83 | return r; 84 | } 85 | -------------------------------------------------------------------------------- /src/feModel/Tile.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2023, chunquedong/yangjiandong 3 | * 4 | * This file is part of mgpEarth project 5 | * all rights reserved 6 | * 7 | */ 8 | #include "feModel/Tile.h" 9 | 10 | FE_USING_NAMESPACE 11 | 12 | Tile::Tile(): x(0), y(0), z(0) { 13 | } 14 | 15 | /* 16 | 2 | 3 17 | --+-- 18 | 0 | 1 19 | */ 20 | void Tile::subTile(Tile subTiles[4]) { 21 | int x = this->x << 1; 22 | int y = this->y << 1; 23 | int z = this->z + 1; 24 | 25 | subTiles[0].init(x, y, z); 26 | subTiles[1].init(x+1, y, z); 27 | subTiles[2].init(x, y+1, z); 28 | subTiles[3].init(x+1, y+1, z); 29 | } 30 | 31 | 32 | Tile Tile::parent() { 33 | if (this->z == 0) { 34 | return *this; 35 | } 36 | 37 | int x = this->x/2; 38 | int y = this->y/2; 39 | int z = this->z-1; 40 | Tile p; 41 | p.init(x, y, z); 42 | return p; 43 | } 44 | 45 | void Tile::toQuadKey(std::string &quadKey) 46 | { 47 | for (int i = z; i > 0; i--) 48 | { 49 | char digit = '0'; 50 | int mask = 1 << (i - 1); 51 | if ((x & (mask)) != 0) 52 | { 53 | ++digit; 54 | } 55 | if ((y & (mask)) != 0) 56 | { 57 | ++digit; 58 | ++digit; 59 | } 60 | quadKey += (digit); 61 | } 62 | } 63 | 64 | void Tile::fromQuadKey(const std::string& quadKey) 65 | { 66 | x = 0; y = 0; 67 | z = quadKey.size(); 68 | for (int i = z; i > 0; i--) 69 | { 70 | int mask = 1 << (i - 1); 71 | switch (quadKey[z - i]) 72 | { 73 | case '0': 74 | break; 75 | 76 | case '1': 77 | x |= mask; 78 | break; 79 | 80 | case '2': 81 | y |= mask; 82 | break; 83 | 84 | case '3': 85 | x |= mask; 86 | y |= mask; 87 | break; 88 | 89 | default: 90 | GP_ERROR("Invalid QuadKey digit sequence."); 91 | } 92 | } 93 | } -------------------------------------------------------------------------------- /src/feTile/XyzTileManager.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2023, chunquedong/yangjiandong 3 | * 4 | * This file is part of mgpEarth project 5 | * all rights reserved 6 | * 7 | */ 8 | #ifndef XYZTILEMANAGER_H 9 | #define XYZTILEMANAGER_H 10 | 11 | #include "feModel/Tile.h" 12 | #include "mgp.h" 13 | #include "feTile/TileManager.h" 14 | #include "feElevation/ElevationManager.h" 15 | 16 | #ifndef __EMSCRIPTEN__ 17 | #define MGP_THREAD 1 18 | #endif 19 | 20 | FE_BEGIN_NAMESPACE 21 | 22 | class PyramidGrid; 23 | 24 | class XyzTileManager: public TileManager { 25 | //the root world tile view 26 | TileDataPtr root; 27 | 28 | std::string cachePath; 29 | mgp::SPtr elevation; 30 | #ifdef MGP_THREAD 31 | mgp::ThreadPool *threadPool; 32 | #endif 33 | mgp::SPtr evlevationQuery; 34 | public: 35 | std::string uri; 36 | int maxLevel; 37 | int minLevel; 38 | PyramidGrid *pyramid; 39 | float viewScale; 40 | 41 | XyzTileManager(const std::string& uri); 42 | ~XyzTileManager(); 43 | 44 | virtual bool resultChanged(); 45 | 46 | void setElevation(ElevationManager* elevation); 47 | 48 | void update(mgp::Camera* camera, mgp::Rectangle* viewport, mgp::Matrix* modelMatrix, bool isSendTask = true) override; 49 | 50 | void setCachePath(const std::string& path); 51 | 52 | protected: 53 | virtual TileDataPtr getRoot(); 54 | virtual TileDataPtr makeTileData(TileKey key); 55 | virtual void getChildren(TileDataPtr &data, std::vector &children); 56 | virtual bool isFitLod(TileDataPtr &data, mgp::Camera &camera, mgp::Rectangle &viewport, mgp::Matrix& modelMatrix); 57 | virtual bool getUri(TileKey key, std::string &uri, std::string &file); 58 | 59 | virtual void* decode(mgp::HttpClient* task, mgp::NetResponse &res); 60 | virtual void onReceive(mgp::HttpClient* task, mgp::NetResponse &res); 61 | virtual void tryInit(TileDataPtr &data, bool isOverview); 62 | 63 | virtual TileDataPtr getParent(TileDataPtr t); 64 | }; 65 | 66 | 67 | FE_END_NAMESPACE 68 | 69 | #endif // XYZTILEMANAGER_H 70 | -------------------------------------------------------------------------------- /src/feModel/GeoCoordSys.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2023, chunquedong/yangjiandong 3 | * 4 | * This file is part of mgpEarth project 5 | * all rights reserved 6 | * 7 | */ 8 | #include "feModel/GeoCoordSys.h" 9 | #include 10 | #include "feModel/Mercator.h" 11 | 12 | FE_USING_NAMESPACE 13 | PF_USING_NAMESPACE 14 | 15 | const double GeoCoordSys::earthRadius = 6378137.0; 16 | 17 | GeoCoordSys* GeoCoordSys::earth() { 18 | static GeoCoordSys earth(earthRadius); 19 | return &earth; 20 | } 21 | 22 | double GeoCoordSys::distance(const Coord2D p1, const Coord2D p2) 23 | { 24 | double longitude1 = Math::toRadians(p1.x); 25 | double latitude1 = Math::toRadians(p1.y); 26 | double longitude2 = Math::toRadians(p2.x); 27 | double latitude2 = Math::toRadians(p2.y); 28 | 29 | double d = acos(sin(latitude1)*sin(latitude2) 30 | +cos(latitude1)*cos(latitude2)*cos(longitude1-longitude2)); 31 | return radius * d; 32 | } 33 | 34 | void GeoCoordSys::blToXyz(const Coord2D p, Vector &out, double radius) 35 | { 36 | double longitude = Math::toRadians(p.x); 37 | double latitude = Math::toRadians(p.y); 38 | 39 | double x = radius * cos(longitude) * cos(latitude); 40 | double y = radius * cos(latitude) * sin(longitude); 41 | double z = radius * sin(latitude); 42 | 43 | return out.set(x, y, z); 44 | } 45 | 46 | void GeoCoordSys::xyzToBl(const Vector &vec_, Coord2D &p) 47 | { 48 | Vector3 vec; 49 | vec_.normalize(&vec); 50 | 51 | double y = asin(vec.z); 52 | double x = atan2(vec.y, vec.x); 53 | 54 | p.x = Math::toDegrees(x); 55 | p.y = Math::toDegrees(y); 56 | } 57 | 58 | void GeoCoordSys::toMercator(Coord2D *p) { 59 | double x = p->x * 20037508.34 / 180; 60 | double y = log(tan((90 + p->y) * Math::PI / 360.0)) / (Math::PI / 180); 61 | y = y * 20037508.34 / 180; 62 | p->x = x; 63 | p->y = y; 64 | } 65 | 66 | void GeoCoordSys::fromMercator(Coord2D *p) { 67 | double x = p->x / 20037508.34 * 180; 68 | double y = p->y / 20037508.34 * 180; 69 | y = 180 / Math::PI * (2 * atan(exp(y * Math::PI / 180)) - Math::PI / 2); 70 | p->x = x; 71 | p->y = y; 72 | } 73 | -------------------------------------------------------------------------------- /sdk/demo/min.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | mgpEarth Demo 7 | 22 | 23 | 24 | 25 | 26 | 55 | 56 | -------------------------------------------------------------------------------- /src/fe3dtiles/TdTiles.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2023, chunquedong/yangjiandong 3 | * 4 | * This file is part of mgpEarth project 5 | * Licensed under the GNU GENERAL PUBLIC LICENSE Version 3 6 | * 7 | */ 8 | #ifndef _TDTILES_H 9 | #define _TDTILES_H 10 | 11 | #include "feUtil/common.h" 12 | #include "mgp.h" 13 | 14 | #include "jvalue.hpp" 15 | 16 | #include 17 | 18 | #include "feTile/TileLayer.h" 19 | 20 | FE_BEGIN_NAMESPACE 21 | 22 | 23 | struct TdTileset; 24 | 25 | struct TdCommonBase { 26 | std::map extensions; 27 | std::string extras; 28 | }; 29 | 30 | /** Bounding volume. Blank when not filled yet. Tile with blank bounding volume 31 | * cannot be serialized. 32 | * 33 | * boost::blank must stay first template argument otherwise 34 | * valid(BoundingVolume) would stop working 35 | */ 36 | using TdBoundingVolume = mgp::BoundingSphere; 37 | 38 | 39 | struct TdTileContent : TdCommonBase { 40 | /** Bounding volume can be invalid. 41 | */ 42 | TdBoundingVolume boundingVolume; 43 | std::string uri; 44 | }; 45 | 46 | struct TdTile : TdCommonBase, public ITileData { 47 | TdBoundingVolume boundingVolume; 48 | TdBoundingVolume viewerRequestVolume; 49 | double geometricError = 0.0; 50 | 51 | enum Refinement { REPLACE, ADD }; 52 | Refinement refine; 53 | 54 | TdTileContent content; 55 | std::vector children; 56 | 57 | mgp::Matrix transform; 58 | 59 | mgp::UPtr renderNode; 60 | 61 | ~TdTile(); 62 | 63 | void parse(jc::Value* node); 64 | 65 | virtual mgp::BoundingSphere& bounding(); 66 | 67 | virtual int getState(); 68 | virtual mgp::Node* getNode(); 69 | virtual TileKey tileKey(); 70 | }; 71 | 72 | struct TdAsset : TdCommonBase { 73 | std::string version; 74 | std::string tilesetVersion; 75 | 76 | TdAsset() : version("1.0") {} 77 | }; 78 | 79 | struct TdTileset : TdCommonBase { 80 | TdAsset asset; 81 | std::map properties; 82 | double geometricError = 0.0; 83 | TdTile* root = NULL; 84 | 85 | std::vector extensionsUsed; 86 | std::vector extensionsRequired; 87 | 88 | 89 | void parse(std::string &str); 90 | 91 | ~TdTileset(); 92 | 93 | }; 94 | 95 | FE_END_NAMESPACE 96 | #endif 97 | -------------------------------------------------------------------------------- /src/api/fe_api.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2023, chunquedong/yangjiandong 3 | * 4 | * This file is part of mgpEarth project 5 | * Licensed under the GNU GENERAL PUBLIC LICENSE Version 3 6 | * 7 | */ 8 | #include "mgpEarth.h" 9 | #include "mgp_pro.h" 10 | #include "jparser.hpp" 11 | 12 | #include 13 | #include 14 | 15 | using namespace jc; 16 | FE_USING_NAMESPACE 17 | PF_USING_NAMESPACE 18 | 19 | EM_JS(void, fe_appInitialize, (void* self), { 20 | fe_onInitialize(self); 21 | }); 22 | 23 | EM_JS(bool, fe_appOnPickNode, (void* self, const char* path, const char* name, int indexOrId, const char* properties), { 24 | return fe_onPickNode(self, path, name, indexOrId, properties); 25 | }); 26 | 27 | class MyEarthApp : public EarthApp { 28 | void initialize() { 29 | showFps(false); 30 | EarthApp::initialize(); 31 | fe_appInitialize(this); 32 | } 33 | 34 | bool onPickNode(PickResult& pickResult) { 35 | std::string jsonstr; 36 | //printf("click %s\n", node->getName()); 37 | int indexOrId = pickResult.userId != -1 ? pickResult.userId : pickResult.drawableIndex; 38 | 39 | GeoLayer* geolayer = dynamic_cast(pickResult.layer); 40 | if (geolayer && geolayer->featureCollection.get() && indexOrId < geolayer->featureCollection->features.size()) { 41 | jc::JsonAllocator allocator; 42 | jc::JsonNode* root = allocator.allocNode(jc::Type::Object); 43 | 44 | int fieldCount = geolayer->featureCollection->getFieldCount(); 45 | for (int i = 0; i < fieldCount; ++i) { 46 | std::string value; 47 | geolayer->featureCollection->features[pickResult.drawableIndex]->getAsStr(i, value); 48 | root->insert_pair(geolayer->featureCollection->getField(i)->name.c_str(), allocator.alloc_str(value.c_str())); 49 | } 50 | root->reverse(); 51 | root->to_json(jsonstr); 52 | } 53 | 54 | return fe_appOnPickNode(this, pickResult.path.c_str(), pickResult.layer->getName(), indexOrId, jsonstr.c_str()); 55 | } 56 | }; 57 | 58 | extern "C" { 59 | 60 | EarthApp* EMSCRIPTEN_KEEPALIVE fe_createApp(int w, int h) { 61 | MyEarthApp* instance = new MyEarthApp(); 62 | Platform::run(instance, "mgpEarth", w, h); 63 | return instance; 64 | } 65 | 66 | } -------------------------------------------------------------------------------- /src/feModel/PyramidGrid.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2023, chunquedong/yangjiandong 3 | * 4 | * This file is part of mgpEarth project 5 | * all rights reserved 6 | * 7 | */ 8 | #include "feModel/PyramidGrid.h" 9 | #include "feModel/Mercator.h" 10 | #include "feModel/GeoCoordSys.h" 11 | 12 | FE_USING_NAMESPACE 13 | 14 | PyramidGrid::PyramidGrid(Envelope &env) : 15 | envelope(env) { 16 | width = env.width(); 17 | height = env.height(); 18 | baseX = env.minX(); 19 | baseY = env.maxY(); 20 | flipY = true; 21 | } 22 | 23 | PyramidGrid *PyramidGrid::getDefault() { 24 | static PyramidGrid *instance = NULL; 25 | if (instance == NULL) { 26 | Envelope env; 27 | env.init(-20037508.3427892, -20037508.3427892, 20037508.3427892, 20037508.3427892); 28 | instance = new PyramidGrid(env); 29 | } 30 | return instance; 31 | } 32 | 33 | PyramidGrid* PyramidGrid::getBD() { 34 | static PyramidGrid* instance = NULL; 35 | if (instance == NULL) { 36 | Envelope env; 37 | env.init(-33554432, -33554432, 33554432, 33554432); 38 | instance = new PyramidGrid(env); 39 | } 40 | return instance; 41 | } 42 | 43 | void PyramidGrid::tileEnvelope(Tile &tile, Envelope &env) { 44 | int num = 1 << tile.z; 45 | double tileWidth = width / num; 46 | double tileHeight = height / num; 47 | double tminX = (tile.x * tileWidth) + baseX; 48 | 49 | double tminY = tile.y * tileHeight; 50 | tminY = flipY ? (baseY - tminY - tileHeight) : baseY + tminY; 51 | 52 | env.set(tminX, tminY, tileWidth, tileHeight); 53 | } 54 | 55 | void PyramidGrid::tileEnvelopeBL(Tile &tile, Envelope &env) { 56 | tileEnvelope(tile, env); 57 | Coord2D p1 = env.minPoint(); 58 | Coord2D p2 = env.maxPoint(); 59 | GeoCoordSys::earth()->fromMercator(&p1); 60 | GeoCoordSys::earth()->fromMercator(&p2); 61 | 62 | env.init(p1.x, p1.y, p2.x, p2.y); 63 | } 64 | 65 | Tile PyramidGrid::getTileAt(double x, double y, int level) { 66 | int num = 1 << level; 67 | double tileWidth = width / num; 68 | double tileHeight = height / num; 69 | 70 | int tx = (x-baseX)/tileWidth; 71 | int ty; 72 | if (flipY) { 73 | ty = (baseY - y) / tileHeight; 74 | } 75 | else { 76 | ty = (y - baseY) / tileHeight; 77 | } 78 | Tile tile; 79 | tile.init(tx, ty, level); 80 | return tile; 81 | } 82 | 83 | void PyramidGrid::tileScreenSize(int &w, int &h) { 84 | w = 256; 85 | h = 256; 86 | } 87 | -------------------------------------------------------------------------------- /src/feCtrl/EarthApp.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2023, chunquedong/yangjiandong 3 | * 4 | * This file is part of mgpEarth project 5 | * Licensed under the GNU GENERAL PUBLIC LICENSE Version 3 6 | * 7 | */ 8 | #ifndef EARTHAPP_H 9 | #define EARTHAPP_H 10 | 11 | #include "mgp_pro.h" 12 | #include "feCtrl/EarthCtrl.h" 13 | #include "feGeo/Building.h" 14 | 15 | FE_BEGIN_NAMESPACE 16 | 17 | 18 | class EarthApp : public mgp::Application, public mgp::PickerListener, public mgp::Refable { 19 | mgp::Node *earth; 20 | mgp::Node* atmosphere = NULL; 21 | mgp::Node* _skybox = NULL; 22 | 23 | EarthCtrl *earthCtrl; 24 | mgp::Gesture gesture; 25 | mgp::SPtr elevation; 26 | mgp::UPtr font; 27 | 28 | mgp::Form* _progressView = nullptr; 29 | std::function _checkProgress; 30 | mgp::WeakPtr _progressNode; 31 | public: 32 | EarthApp(); 33 | ~EarthApp(); 34 | void initialize() override; 35 | void update(float elapsedTime) override; 36 | bool mouseEvent(mgp::MotionEvent &event) override; 37 | void render(float elapsedTime) override; 38 | void finalize() override; 39 | public: 40 | mgp::Node* add3dtiles(const char* name, const char* uri, const Coord2D& coord, double height, int lighting); 41 | XyzTileManager* addTileLayer(const char *name, const char* uri, const char* elevationUri); 42 | void addGeoNode(mgp::UPtr node); 43 | void insertGeoNode(mgp::UPtr node); 44 | void addSkybox(int dark, double minDis, double maxDis); 45 | bool removeNode(const char* name); 46 | bool showLoadProgress(mgp::Node* node); 47 | private: 48 | void updateSkybox(); 49 | void addAtmosphere(); 50 | void drawLocationText(); 51 | public: 52 | EarthCtrl *getEarthCtrl() { return earthCtrl; } 53 | ElevationManager* getElevation() { return elevation.get(); } 54 | mgp::Node* getEarth() { return earth; } 55 | 56 | struct PickResult { 57 | std::string path; 58 | mgp::Node* layer; 59 | long userId; 60 | mgp::Drawable* drawable; 61 | int drawableIndex; 62 | }; 63 | static bool getPickResult(mgp::RayQuery& result, PickResult& pickResult); 64 | protected: 65 | virtual bool onPick(float x, float y, mgp::RayQuery& result) override; 66 | 67 | virtual bool onPickNode(PickResult& pickResult); 68 | }; 69 | 70 | FE_END_NAMESPACE 71 | 72 | #endif // EARTHAPP_H 73 | -------------------------------------------------------------------------------- /sdk/demo/3dtile.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | mgpEarth Demo 7 | 22 | 23 | 24 | 25 | 26 | 61 | 62 | -------------------------------------------------------------------------------- /src/feElevation/ElevationManager.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2023, chunquedong/yangjiandong 3 | * 4 | * This file is part of mgpEarth project 5 | * all rights reserved 6 | * 7 | */ 8 | #ifndef ELEVATION_MANAGER_H 9 | #define ELEVATION_MANAGER_H 10 | 11 | #include "Elevation.h" 12 | 13 | FE_BEGIN_NAMESPACE 14 | 15 | class ElevationManager; 16 | class ElevationQuery : public mgp::Refable, public Elevation { 17 | friend class ElevationManager; 18 | //TileData* _lastTile = nullptr; 19 | mgp::WeakPtr manager; 20 | public: 21 | bool resultDirty = false; 22 | ElevationQuery(); 23 | ~ElevationQuery(); 24 | mgp::HashMap > tiles; 25 | bool isLoaded(); 26 | 27 | double getHeight(double longitude, double latitude, int level); 28 | double getHeightMercator(double x, double y, int level); 29 | double getTileHeight(Tile tile, double x, double y, double defVal, int level); 30 | 31 | //virtual void onReceive(SPtr tileData); 32 | 33 | double getHeightUV(TileData* tileData, double u, double v, double defVal, int level); 34 | double getTileHeight(TileData* tileData, double x, double y, double defVal, int level); 35 | }; 36 | 37 | class ElevationRequest : public mgp::HttpClient { 38 | public: 39 | int queryCount = 0; 40 | Tile tileKey{}; 41 | }; 42 | 43 | class TileData; 44 | class ElevationManager : public mgp::Refable, public mgp::NetListener { 45 | protected: 46 | std::string uri; 47 | 48 | LRUCache cache; 49 | std::list querys; 50 | mgp::HashMap > sendedTask; 51 | 52 | PyramidGrid* pyramid; 53 | std::string cachePath; 54 | 55 | //ElevationQuery approximateElevation; 56 | public: 57 | int maxLevel; 58 | int minLevel; 59 | double elevationScale; 60 | ElevationManager(const std::string& uri); 61 | ~ElevationManager(); 62 | 63 | //ElevationQuery* getApproximateElevation() { return &approximateElevation; } 64 | 65 | mgp::SPtr fromTiles(std::set& tiles); 66 | Tile getTileAt(double x, double y, int level); 67 | Tile getTileAtBL(double longitude, double latitude, int level); 68 | void cancel(ElevationQuery* query); 69 | 70 | virtual void* decode(mgp::HttpClient* task, mgp::NetResponse& res); 71 | virtual void onReceive(mgp::HttpClient* task, mgp::NetResponse& res); 72 | 73 | virtual bool getUri(TileKey key, std::string& uri, std::string& file); 74 | void setCachePath(const std::string& path); 75 | private: 76 | sric::SharedPtr load(Tile key); 77 | 78 | }; 79 | 80 | 81 | FE_END_NAMESPACE 82 | 83 | #endif // ELEVATION_MANAGER_H -------------------------------------------------------------------------------- /src/feTile/DataActor.cpp: -------------------------------------------------------------------------------- 1 | #include "feTile/DataActor.h" 2 | #include "mgp.h" 3 | //#include "cppfan/Profiler.h" 4 | #include "feTile/TileData.h" 5 | #include "feTile/TileGeom.hpp" 6 | #include "feElevation/Elevation.h" 7 | 8 | PF_USING_NAMESPACE 9 | FE_USING_NAMESPACE 10 | 11 | ///////////////////////////////////////////////////////////////////////////////////// 12 | ///////////////////////////////////////////////////////////////////////////////////// 13 | 14 | ViewState::~ViewState() { 15 | camera._setRefCount(0); 16 | } 17 | 18 | 19 | DataActor::DataActor(TileManager* tileManager) : tileManager(tileManager), renderableDirty(false) { 20 | //tileManager->dataListener = this; 21 | } 22 | DataActor::~DataActor() { 23 | SAFE_RELEASE(tileManager); 24 | } 25 | 26 | void DataActor::sendViewUpdate(Camera* camera, Rectangle* viewport, Matrix* modelMatrix) { 27 | std::lock_guard guard(lock); 28 | if (newestState.modelMatrix == *modelMatrix && 29 | newestState.camera.getViewMatrix() == camera->getViewMatrix()) { 30 | return; 31 | } 32 | newestState.camera = *camera; 33 | newestState.viewport = *viewport; 34 | newestState.modelMatrix = *modelMatrix; 35 | 36 | Message msg; 37 | //msg.name = ""; 38 | msg.id = 0; 39 | msg.param = NULL; 40 | send(msg); 41 | 42 | tileManager->releaseCache(); 43 | } 44 | 45 | void DataActor::sendMakeData() { 46 | Message msg; 47 | //msg.name = ""; 48 | msg.id = 1; 49 | msg.param = NULL; 50 | send(msg); 51 | } 52 | 53 | void DataActor::onReceive(Message &msg) { 54 | if (msg.id == 0 || msg.id == 1) { 55 | if (msg.id == 0) { 56 | lock.lock(); 57 | curState = newestState; 58 | lock.unlock(); 59 | tileManager->update(&curState.camera, &curState.viewport, &curState.modelMatrix); 60 | } 61 | if (tileManager->resultChanged()) { 62 | std::vector resultList; 63 | tileManager->getResult(resultList); 64 | 65 | std::lock_guard guard(lock); 66 | renderableList.swap(resultList); 67 | renderableDirty = true; 68 | } 69 | } 70 | } 71 | 72 | bool DataActor::mergeMessage(Message *cur, Message *pre) { 73 | if (cur->id == 0 && cur->id == pre->id) return true; 74 | return false; 75 | } 76 | 77 | void DataActor::getResult(std::vector& list) { 78 | std::lock_guard guard(lock); 79 | renderableList.swap(list); 80 | renderableDirty = false; 81 | } 82 | 83 | bool DataActor::resultChanged() { 84 | std::lock_guard guard(lock); 85 | return renderableDirty; 86 | } 87 | 88 | void DataActor::onCancel(Message& msg) { 89 | } 90 | -------------------------------------------------------------------------------- /sdk/demo/gltf.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | mgpEarth Demo 7 | 22 | 23 | 24 | 25 | 26 | 68 | 69 | -------------------------------------------------------------------------------- /src/feCtrl/EarthAnimation.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2023, chunquedong/yangjiandong 3 | * 4 | * This file is part of mgpEarth project 5 | * all rights reserved 6 | * 7 | */ 8 | #ifndef EARTHANIMATION_H 9 | #define EARTHANIMATION_H 10 | 11 | #include "feUtil/common.h" 12 | #include "mgp.h" 13 | #include "feModel/Coord2D.h" 14 | 15 | FE_BEGIN_NAMESPACE 16 | 17 | class EarthCtrl; 18 | 19 | class AnimChannel { 20 | public: 21 | uint64_t duration = 1000; 22 | bool isRunning = false; 23 | float _elapsedTime = 0; 24 | 25 | bool update(float elapsedTime); 26 | void stop(); 27 | void start(); 28 | protected: 29 | virtual void doUpdate(float elapsedTime, float percentComplete) = 0; 30 | }; 31 | 32 | class FlingAnimChannel : public AnimChannel 33 | { 34 | public: 35 | EarthCtrl *ctrl = NULL; 36 | float vx; 37 | float vy; 38 | float acceleratedX; 39 | float acceleratedY; 40 | 41 | virtual void doUpdate(float elapsedTime, float percentComplete); 42 | }; 43 | 44 | class ZoomAnimChannel : public AnimChannel { 45 | public: 46 | EarthCtrl* ctrl = NULL; 47 | double from = 0; 48 | double to = 0; 49 | 50 | virtual void doUpdate(float elapsedTime, float percentComplete); 51 | }; 52 | 53 | class RotateAnimChannel : public AnimChannel { 54 | public: 55 | EarthCtrl* ctrl = NULL; 56 | float fromRotateX = 0; 57 | float fromRotateZ = 0; 58 | float toRotateX = 0; 59 | float toRotateZ = 0; 60 | 61 | virtual void doUpdate(float elapsedTime, float percentComplete); 62 | }; 63 | 64 | class MoveToAnimChannel : public AnimChannel { 65 | public: 66 | EarthCtrl* ctrl = NULL; 67 | double fromX = 0; 68 | double fromY = 0; 69 | double toX = 0; 70 | double toY = 0; 71 | double fromZoom = 0; 72 | double toZoom = 0; 73 | double middleZoom = 0; 74 | 75 | virtual void doUpdate(float elapsedTime, float percentComplete); 76 | }; 77 | 78 | class EarthAnimation : public mgp::Refable { 79 | FlingAnimChannel *flingChannel = NULL; 80 | ZoomAnimChannel* zoomChannel = NULL; 81 | RotateAnimChannel* rotateChannel = NULL; 82 | MoveToAnimChannel* moveChannel = NULL; 83 | EarthCtrl *ctrl = NULL; 84 | std::vector channelList; 85 | public: 86 | 87 | EarthAnimation(); 88 | ~EarthAnimation(); 89 | 90 | void start(AnimChannel* chnnel); 91 | void stop(); 92 | 93 | bool update(float elapsedTime); 94 | 95 | void init(EarthCtrl *ctrl); 96 | 97 | void fling(float dx, float dy); 98 | void zoomTo(double zoom, uint64_t time); 99 | void rotateTo(float rx, float rz, uint64_t time); 100 | void moveTo(double x, double y, uint64_t time, double zoom = NAN); 101 | }; 102 | 103 | FE_END_NAMESPACE 104 | #endif // EARTHANIMATION_H 105 | -------------------------------------------------------------------------------- /sdk/demo/demo.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | mgpEarth Demo 7 | 22 | 23 | 24 | 25 | 26 | 73 | 74 | -------------------------------------------------------------------------------- /sdk/demo/track.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | mgpEarth Demo 7 | 22 | 23 | 24 | 25 | 26 | 84 | 85 | -------------------------------------------------------------------------------- /src/api/fe_api.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2023, chunquedong/yangjiandong 3 | * 4 | * This file is part of mgpEarth project 5 | * Licensed under the GNU GENERAL PUBLIC LICENSE Version 3 6 | * 7 | */ 8 | 9 | #ifndef __EMSCRIPTEN__ 10 | #define EMSCRIPTEN_KEEPALIVE 11 | #else 12 | #include 13 | #endif 14 | 15 | FE_BEGIN_NAMESPACE 16 | 17 | extern "C" { 18 | 19 | void EMSCRIPTEN_KEEPALIVE fe_showFps(EarthApp* self, bool show); 20 | 21 | bool EMSCRIPTEN_KEEPALIVE fe_removeNode(EarthApp* self, const char* name); 22 | 23 | void EMSCRIPTEN_KEEPALIVE fe_addTileLayer(EarthApp* self, const char* name, const char* uri, const char* elevationUri, char* options); 24 | 25 | void EMSCRIPTEN_KEEPALIVE fe_addSkybox(EarthApp* self, int dark, float min, float max); 26 | 27 | void EMSCRIPTEN_KEEPALIVE fe_addGeoLayer(EarthApp* self, const char* name, const char* uri, char* options); 28 | 29 | void EMSCRIPTEN_KEEPALIVE fe_addBuildingLayer(EarthApp* self, const char* name, const char* uri, char* options); 30 | 31 | void EMSCRIPTEN_KEEPALIVE fe_add3dtiles(EarthApp* self, const char* name, const char* uri, double lng, double lat, double height, int lighting, char* options); 32 | 33 | void EMSCRIPTEN_KEEPALIVE fe_addGroundGltf(EarthApp* self, const char* name, const char* uri, double lng, double lat, double height, int lighting, char* options); 34 | 35 | void EMSCRIPTEN_KEEPALIVE fe_addLight(EarthApp* self, const char* name, double lng, double lat, float r, float g, float b); 36 | 37 | void EMSCRIPTEN_KEEPALIVE fe_setPosition(EarthApp* self, double lng, double lat, double zoom); 38 | 39 | void EMSCRIPTEN_KEEPALIVE fe_moveTop(EarthApp* self, double lng, double lat, int time, double zoom); 40 | void EMSCRIPTEN_KEEPALIVE fe_zoomTo(EarthApp* self, double zoom, uint64_t time); 41 | 42 | void EMSCRIPTEN_KEEPALIVE fe_rotateTo(EarthApp* self, float rx, float rz, uint64_t time); 43 | 44 | void EMSCRIPTEN_KEEPALIVE fe_addMultiModel(EarthApp* self, const char* name, const char* uri, int lighting, char* options); 45 | 46 | int EMSCRIPTEN_KEEPALIVE fe_updateModelInstance(EarthApp* self, const char* name, int id, double lng, double lat, double height, char* options); 47 | 48 | void EMSCRIPTEN_KEEPALIVE fe_removeModelInstance(EarthApp* self, const char* name, int id); 49 | 50 | void EMSCRIPTEN_KEEPALIVE fe_addEmptyGeoLayer(EarthApp* self, const char* name, int geotype, char* options); 51 | 52 | bool EMSCRIPTEN_KEEPALIVE fe_addGeoFeature(EarthApp* self, const char* name, int geotype, double* coords, int pointNum, char* attributes); 53 | 54 | int EMSCRIPTEN_KEEPALIVE fe_removeGeoFeatureLike(EarthApp* self, const char* name, const char* fieldName, const char* value); 55 | bool EMSCRIPTEN_KEEPALIVE fe_removeGeoFeatureAt(EarthApp* self, const char* name, int index); 56 | 57 | float EMSCRIPTEN_KEEPALIVE fe_getLoadProgress(EarthApp* self, const char* name); 58 | 59 | bool EMSCRIPTEN_KEEPALIVE fe_showLoadProgress(EarthApp* self, const char* name); 60 | 61 | bool EMSCRIPTEN_KEEPALIVE fe_syncPick(EarthApp* self, const char* name, int x, int y, char* layerName, double* target, long* idOrIndex); 62 | 63 | double* EMSCRIPTEN_KEEPALIVE fe_xyzToLnglat(EarthApp* self, double x, double y, double z, double* target); 64 | 65 | double* EMSCRIPTEN_KEEPALIVE fe_lnglatToXyz(EarthApp* self, double lng, double lat, double height, double* target); 66 | 67 | void EMSCRIPTEN_KEEPALIVE fe_clearHighlight(EarthApp* self); 68 | 69 | } 70 | 71 | FE_END_NAMESPACE -------------------------------------------------------------------------------- /src/feGeo/GeoLayer.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2023, chunquedong/yangjiandong 3 | * 4 | * This file is part of mgpEarth project 5 | * Licensed under the GNU GENERAL PUBLIC LICENSE Version 3 6 | * 7 | */ 8 | #ifndef _GEO_LAYER_H 9 | #define _GEO_LAYER_H 10 | 11 | #include "feUtil/common.h" 12 | #include "mgp_pro.h" 13 | #include "feModel/Envelope.h" 14 | #include "Geometry.h" 15 | #include "feElevation/Elevation.h" 16 | #include "feObjects/GeoNode.h" 17 | #include 18 | 19 | FE_BEGIN_NAMESPACE 20 | 21 | class GeoLayer; 22 | 23 | class Symbolizer { 24 | friend class GeoLayer; 25 | public: 26 | mgp::LabelSet* _label = nullptr; 27 | mgp::Line* _line = nullptr; 28 | mgp::Polygon* _polygon = nullptr; 29 | GeoLayer* _layer = nullptr; 30 | 31 | public: 32 | double minDis = 0; 33 | double maxDis = FLT_MAX; 34 | 35 | struct Filter { 36 | std::string fieldName; 37 | std::string op = "=="; 38 | FeatureValue value; 39 | FeatureField::Type valueType; 40 | 41 | int64_t getIntValue(); 42 | double getFloatValue(); 43 | }; 44 | std::vector filters; 45 | 46 | mgp::LineStyle lineStyle; 47 | mgp::PolyonStyle polygonStyle; 48 | mgp::LabelStyle labelStyle; 49 | std::string labelField; 50 | bool fillPolygon = true; 51 | bool strokePolygon = true; 52 | bool pickable = true; 53 | double outlineHeightOffset = 100; 54 | int curveType = 0; 55 | 56 | Symbolizer(); 57 | 58 | void initAddTo(mgp::Node* node); 59 | 60 | void beginUpdate(); 61 | void endUpdate(); 62 | bool updateRender(Feature* feature, int id); 63 | private: 64 | void addPoint(Feature* feature, Geometry* geometry, int index, int id); 65 | void addLine(Geometry* geometry, GeoLine& gline, bool isOutline, int id); 66 | void addPolygon(Geometry* geometry, int id); 67 | bool addGeometry(Feature* feature, Geometry* geometry, int id); 68 | public: 69 | bool loadOptions(jc::Value* json); 70 | }; 71 | 72 | class GeoLayer : public GeoNode { 73 | bool _featuresDirty = false; 74 | public: 75 | mgp::UPtr featureCollection; 76 | 77 | double additionalHeight = 0; 78 | bool queryElevation = false; 79 | bool isLnglat = true; 80 | bool polygonInterpolation = false; 81 | mgp::Vector3 baseTranslate; 82 | 83 | std::vector symbolizers; 84 | 85 | Symbolizer* getSymb() { return &symbolizers[0]; } 86 | public: 87 | GeoLayer(const char* uri); 88 | ~GeoLayer(); 89 | 90 | virtual void* decodeFile(const char* path, mgp::NetResponse& lastRes, mgp::MultiRequest* req) override; 91 | protected: 92 | mgp::Node* makeNode(FeatureCollection* fc); 93 | public: 94 | void initEmpty(GeometryType geoType); 95 | void updateData(); 96 | void doUpdateRenderData(); 97 | 98 | //custom lable color 99 | virtual mgp::Vector4* getColor(int i); 100 | protected: 101 | void update(float elapsedTime) override; 102 | void onReceive(mgp::NetResponse& res, mgp::MultiRequest* req) override; 103 | 104 | public: 105 | bool loadOptions(char* json_str); 106 | public: 107 | void coordToXyz(double x, double y, double z, Vector& xyz, double additionalHeight, bool doTranslate = true); 108 | }; 109 | 110 | FE_END_NAMESPACE 111 | 112 | #endif // _GEO_LAYER_H 113 | -------------------------------------------------------------------------------- /src/feModel/Envelope.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2023, chunquedong/yangjiandong 3 | * 4 | * This file is part of mgpEarth project 5 | * all rights reserved 6 | * 7 | */ 8 | #ifndef ENVELOPE_H 9 | #define ENVELOPE_H 10 | 11 | #include "feUtil/common.h" 12 | #include "feModel/Coord2D.h" 13 | 14 | FE_BEGIN_NAMESPACE 15 | 16 | /** 17 | * 2D Rectangle range 18 | */ 19 | class Envelope { 20 | public: 21 | double _minX; 22 | double _minY; 23 | double _maxX; 24 | double _maxY; 25 | 26 | public: 27 | double minX() { return _minX; } 28 | double minY() { return _minY; } 29 | double maxX() { return _maxX; } 30 | double maxY() { return _maxY; } 31 | 32 | /** 33 | * init by two point 34 | */ 35 | void init(double x1, double y1, double x2, double y2); 36 | 37 | /** 38 | * init by xyWH 39 | */ 40 | void set(double x, double y, double w, double h); 41 | 42 | /** 43 | * extends to contains bound 44 | */ 45 | void merge(const Envelope &bound); 46 | 47 | /** 48 | * extends to contains point 49 | */ 50 | void merge(const Coord2D &point); 51 | 52 | /** 53 | * move all coord 54 | */ 55 | void move(double dx, double dy) { 56 | _minX += dx; 57 | _maxX += dx; 58 | _minY += dy; 59 | _maxY += dy; 60 | } 61 | 62 | double width() { return _maxX - _minX; } 63 | double height() { return _maxY - _minY; } 64 | 65 | Coord2D minPoint() { 66 | return Coord2D(minX(), minY()); 67 | } 68 | 69 | Coord2D maxPoint() { 70 | return Coord2D(maxX(), maxY()); 71 | } 72 | 73 | Coord2D leftUp() { 74 | return Coord2D(minX(), maxY()); 75 | } 76 | 77 | Coord2D rightDown() { 78 | return Coord2D(maxX(), minY()); 79 | } 80 | 81 | /** 82 | * expand some buffer 83 | */ 84 | void expand(double d) { 85 | _minX -= d; 86 | _minY -= d; 87 | _maxX += d; 88 | _maxY += d; 89 | } 90 | 91 | /** 92 | * return true if has intersection 93 | */ 94 | bool intersects(const Envelope &env) { 95 | if (this->_maxX < env._minX || env._maxX < this->_minX) { 96 | return false; 97 | } 98 | if (this->_maxY < env._minY || env._maxY < this->_minY) { 99 | return false; 100 | } 101 | return true; 102 | } 103 | 104 | /** 105 | * return the intersection 106 | */ 107 | Envelope intersection(const Envelope &env); 108 | 109 | /** 110 | * return true if is valid 111 | */ 112 | bool valid() { 113 | if (_minX > _maxX) { 114 | return false; 115 | } 116 | 117 | if (_minY > _maxY) { 118 | return false; 119 | } 120 | return true; 121 | } 122 | 123 | /** 124 | * init a invalid envelope 125 | */ 126 | void makeInvalid(); 127 | 128 | bool equals(const Envelope &env) { 129 | return (_minX == env._minX) && (_maxX == env._maxX) 130 | && (_minY == env._minY) && (_maxY == env._maxY); 131 | } 132 | 133 | bool contains(const Envelope &env) { 134 | return ((_minX <= env._minX) && (_maxX >= env._maxX) 135 | && (_minY <= env._minY) && (_maxY >= env._maxY)); 136 | } 137 | 138 | bool containsPoint(double x, double y) { 139 | return ((_minX <= x) && (_maxX >= x) 140 | && (_minY <= y) && (_maxY >= y)); 141 | } 142 | 143 | Coord2D getCenter() { 144 | double x = (_minX + _maxX) / 2.0; 145 | double y = (_minY + _maxY) / 2.0; 146 | return Coord2D(x, y); 147 | } 148 | 149 | double getArea() { 150 | return height() * width(); 151 | } 152 | }; 153 | 154 | FE_END_NAMESPACE 155 | #endif // ENVELOPE_H 156 | -------------------------------------------------------------------------------- /sdk/demo/geojson.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | mgpEarth Demo 7 | 22 | 23 | 24 | 25 | 26 | 90 | 91 | -------------------------------------------------------------------------------- /src/feModel/Mercator.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2023, chunquedong/yangjiandong 3 | * 4 | * This file is part of mgpEarth project 5 | * all rights reserved 6 | * 7 | */ 8 | #ifndef MERCATOR_H 9 | #define MERCATOR_H 10 | 11 | #include "feUtil/common.h" 12 | #include "feModel/Coord2D.h" 13 | 14 | FE_BEGIN_NAMESPACE 15 | 16 | 17 | /** 18 | * Earth ellipsoid 19 | * 20 | */ 21 | class Spheroid { 22 | /** 23 | * 24 | */ 25 | public: const char *name; 26 | 27 | /** 28 | * 29 | */ 30 | public: const double a; 31 | 32 | /** 33 | * 34 | */ 35 | public: const double b; 36 | 37 | /** 38 | * 39 | */ 40 | public: double e; 41 | 42 | /** 43 | * 44 | */ 45 | public: double e2; 46 | 47 | 48 | //========================================================= 49 | 50 | /** 51 | * 52 | */ 53 | public: Spheroid(const char *name, double a, double b); 54 | 55 | private: double computeE(); 56 | 57 | private: double computeE2(); 58 | 59 | }; 60 | 61 | /** 62 | */ 63 | class MercatorReTrans { 64 | /** 65 | */ 66 | private: double k; 67 | 68 | /** 69 | */ 70 | private: double l0; 71 | 72 | /** 73 | */ 74 | private: double e; 75 | 76 | /** 77 | */ 78 | public: virtual void* call(void* a) { convert((Coord2D *)a); return (Coord2D *)a; } 79 | 80 | 81 | //========================================================= 82 | 83 | /** 84 | */ 85 | public: MercatorReTrans(double a, double b, double e, double e2, double b0, double l0); 86 | 87 | /** 88 | */ 89 | public: void convert(Coord2D *p); 90 | 91 | private: double computeB(double x); 92 | 93 | private: double getB(double b, double x); 94 | 95 | private: double my_exp(double b, double x); 96 | 97 | private: double getL(double y); 98 | 99 | }; 100 | 101 | /** 102 | */ 103 | class MercatorTransform 104 | { 105 | /** 106 | */ 107 | private: double k; 108 | 109 | /** 110 | */ 111 | private: double l0; 112 | 113 | /** 114 | */ 115 | private: double e; 116 | 117 | /** 118 | */ 119 | public: virtual void* call(void* a) { convert((Coord2D *)a); return (Coord2D *)a; } 120 | 121 | 122 | //========================================================= 123 | 124 | /** 125 | */ 126 | public: MercatorTransform(double a, double b, double e, double e2, double b0, double l0); 127 | 128 | /** 129 | */ 130 | public: void convert(Coord2D *p); 131 | 132 | private: double lntan(double b, double e); 133 | 134 | }; 135 | 136 | /** 137 | * Mercator Projection 138 | * 139 | */ 140 | class Mercator { 141 | private: 142 | Spheroid _spheroid; 143 | MercatorTransform trans; 144 | MercatorReTrans reTrans; 145 | 146 | /** 147 | * maximum latitude equals to half of width 148 | * 149 | */ 150 | public: static const double maxDis; 151 | 152 | /** 153 | * radius of earth 154 | * 155 | */ 156 | public: static const double radius; 157 | 158 | 159 | //========================================================= 160 | 161 | /** 162 | * 163 | */ 164 | public: virtual MercatorTransform *getTransform(); 165 | 166 | /** 167 | * 168 | */ 169 | public: virtual MercatorReTrans *getReverseTransform(); 170 | 171 | /** 172 | * 173 | */ 174 | public: Mercator(); 175 | 176 | private: Spheroid *spheroid(); 177 | 178 | public: 179 | static Mercator &getInstance(); 180 | 181 | /** 182 | * 183 | */ 184 | public: static double computeK(double a, double b, double e2, double b0); 185 | 186 | }; 187 | 188 | 189 | FE_END_NAMESPACE 190 | #endif // MERCATOR_H 191 | -------------------------------------------------------------------------------- /doc/geolayer.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | ## 地理矢量图层 4 | 5 | ### 概述 6 | 用来显示点、线、面等数据。 7 | 8 | ### 从Geojson文件加载 9 | 10 | 通过addGeoLayer加载已有的geojson文件。 11 | ``` 12 | //最后的options参数用来配置图层显示样式。 13 | mgpEarth.addGeoLayer("polygon", "https://d2ad6b4ur7yvpq.cloudfront.net/naturalearth-3.3.0/ne_110m_admin_1_states_provinces_shp.geojson", { 14 | "symbolizers" :[ 15 | { 16 | "lineStyle": { 17 | "lineWidth": 1, 18 | "depthTest": true 19 | }, 20 | "polygonStyle": { 21 | "fillColor": [0.5,0.7,0.6,0.6] 22 | } 23 | } 24 | ] 25 | }); 26 | 27 | ``` 28 | 29 | ### 样式过滤 30 | symbolizers可以有多个,每个有filters。当遇到第一个filters匹配的时候,使用当前symbolizer样式,后面的symbolizer会被忽略。 31 | ``` 32 | { 33 | "symbolizers" :[ 34 | { 35 | "filters": [{ 36 | "fieldName" : "name_len", 37 | "op": ">", 38 | "value": 10 39 | }], 40 | "lineStyle": { 41 | "lineWidth": 1, 42 | "depthTest": true 43 | }, 44 | "polygonStyle": { 45 | "fillColor": [0.9,0.9,0.6,0.6] 46 | } 47 | }, 48 | { 49 | "lineStyle": { 50 | "lineWidth": 1, 51 | "depthTest": true 52 | }, 53 | "polygonStyle": { 54 | "fillColor": [0.5,0.7,0.6,0.6] 55 | } 56 | } 57 | ] 58 | } 59 | ``` 60 | 61 | 62 | ### 代码创建图层 63 | 64 | 通过addEmptyGeoLayer来增加图层,addGeoFeature对其增加数据。 65 | 66 | 点状数据: 67 | ``` 68 | //创建名称为poi的图层。1表示点状数据。 69 | mgpEarth.addEmptyGeoLayer("poi", 1, { 70 | "symbolizers" :[{ 71 | "labelStyle": { 72 | "iconSize": 20, 73 | "iconImage": "res/image/text_bubble.png", 74 | "labelAlign": 4, 75 | "iconColor": [0.9, 0.5, 0.0, 1.0], 76 | "sphereCulling": false 77 | } 78 | }] 79 | }); 80 | 81 | //为poi图层增加点对象, 1表示几何类型为点。 82 | //坐标为Float32Array类型的经纬度和高程的列表。 83 | //最后的属性会忽略字段数据类型,内部统一用字符串存储。 84 | mgpEarth.addGeoFeature("poi", 1, new Float32Array([108.964164, 34.218175, 100]), {id:"123",name:"test1"} ); 85 | mgpEarth.addGeoFeature("poi", 1, new Float32Array([108.964164+0.1, 34.218175, 100]), {id:"124",name:"test2"} ); 86 | 87 | //删除其中id=123的点 88 | mgpEarth.removeGeoFeature("poi", "id", "123"); 89 | ``` 90 | 91 | 多边形数据: 92 | ``` 93 | //创建名称为line的图层。5表示面状数据。 94 | mgpEarth.addEmptyGeoLayer("line", 5, { 95 | "additionalHeight" : 420, 96 | "maxDis" : 50000, 97 | "symbolizers" : [{ 98 | "lineStyle": { 99 | "lineWidth": 5, 100 | "flowSpeed" : 0.5, 101 | "glowPower" : 1.0, 102 | "depthTest" : false 103 | } 104 | }] 105 | } 106 | ); 107 | 108 | //为poi图层增加点对象, 5表示几何类型为面。 面坐标的起始点必须相同。 109 | mgpEarth.addGeoFeature("line", 5, new Float32Array([ 110 | 108.964164, 34.218175, 500, 111 | 108.964164+0.1, 34.218175, 500, 112 | 108.964164, 34.218175+0.1, 500, 113 | 108.964164, 34.218175, 500 114 | ]), {id:"123",name:"test1"} ); 115 | ``` 116 | 117 | 118 | 修改数据:目前没有专用的接口,可以通过先删除再添加的方式实现。 119 | 120 | 121 | 删除图层: mgpEarth.removeNode("poi") 122 | 123 | 124 | ### 点击拾取 125 | 126 | 通过onPickNode回调。name是图层名称,properties是属性信息。 127 | ``` 128 | mgpEarth.onPickNode = function(path, name, index, properties) { 129 | console.log("pick:"+name+"/"+index+": "+properties); 130 | return true; 131 | }; 132 | ``` 133 | -------------------------------------------------------------------------------- /src/feCtrl/TraceBall.cpp: -------------------------------------------------------------------------------- 1 | #if 0 2 | #include "feCtrl/TraceBall.h" 3 | 4 | #include "mgp.h" 5 | #include "feTile/TileLayer.h" 6 | #include "feModel/GeoCoordSys.h" 7 | 8 | 9 | FE_USING_NAMESPACE 10 | 11 | TraceBall::TraceBall() 12 | { 13 | earthRotation.x = 0; 14 | earthRotation.y = 0; 15 | earthRotation.z = 0; 16 | earthRotation.w = 1; 17 | } 18 | 19 | Coord2D TraceBall::getPosition() 20 | { 21 | return quaternionToBl(earthRotation); 22 | } 23 | 24 | void TraceBall::moveToPostion(Coord2D pos) 25 | { 26 | blToQuaternion(pos, earthRotation); 27 | invalidateCamera(); 28 | } 29 | 30 | void TraceBall::setRotationZ(double rotZ) 31 | { 32 | double delta = rotZ - this->getRotationZ(); 33 | EarthCtrl::setRotationZ(rotZ); 34 | 35 | Quaternion que1; 36 | Vector v1; 37 | v1.set(0, 0, 1); 38 | que1.fromAxis(&v1, Math::toRadians(delta)); 39 | 40 | que1.mult(&earthRotation); 41 | earthRotation = que1; 42 | 43 | invalidateCamera(); 44 | } 45 | 46 | void TraceBall::updateCameraTransform(Camera &camera, Rectangle &viewport) 47 | { 48 | CF_UNUSED(viewport); 49 | //translate 50 | camera.transform.makeIndentity(4); 51 | Transform3D::translate(&camera.transform, 0, 0, -distanceToCenter); 52 | Transform3D::rotate(&camera.transform, rotationX, 1, 0, 0); 53 | 54 | //rotate 55 | Matrix rotate; 56 | earthRotation.getMatrix(&rotate); 57 | 58 | Matrix out; 59 | camera.transform.mult(&rotate, &out); 60 | camera.transform = out; 61 | } 62 | 63 | void TraceBall::moveByPixel(float dx, float dy) 64 | { 65 | if (dx == 0 && dy == 0) { 66 | return; 67 | } 68 | 69 | //projection 70 | Vector v1, v2, rotAxis; 71 | v1.set(0, 0, GeoCoordSys::earth()->getRadius()); 72 | projectToShpere(dx, -dy, v2); 73 | 74 | //get rotate axis 75 | v1.crossProduct3(&v2, &rotAxis); 76 | rotAxis.normalize(); 77 | 78 | //get angle 79 | v1.sub(&v2); 80 | double dis = v1.length() / GeoCoordSys::earth()->getRadius(); 81 | double angle = dis;//asin(dis/2)*2; 82 | Quaternion quat; 83 | 84 | //make quaternion 85 | quat.fromAxis(&rotAxis, angle); 86 | 87 | //apply to rotation 88 | quat.mult(&earthRotation); 89 | earthRotation = quat; 90 | invalidateCamera(); 91 | } 92 | 93 | void TraceBall::blToQuaternion(Coord2D &postion, Quaternion &out) { 94 | float y = Math::clamp(postion.y, -90.0, 90.0); 95 | float x = Math::clamp(postion.x, -180.0, 180.0); 96 | 97 | Vector ya; 98 | ya.set(0, 1, 0); 99 | Vector za; 100 | za.set(0, 0, 1); 101 | Vector xa; 102 | xa.set(1, 0, 0); 103 | 104 | Quaternion qua1; 105 | qua1.fromAxis(&xa, Math::toRadians(-90)); 106 | Quaternion qua2; 107 | qua2.fromAxis(&ya, Math::toRadians(-90)); 108 | qua2.mult(&qua1); 109 | 110 | Quaternion qua3; 111 | qua3.fromAxis(&za, Math::toRadians(-x)); 112 | Quaternion qua4; 113 | qua4.fromAxis(&ya, Math::toRadians(y)); 114 | qua4.mult(&qua3); 115 | 116 | out = qua2; 117 | out.mult(&qua4); 118 | } 119 | 120 | Coord2D TraceBall::quaternionToBl(Quaternion &quat) 121 | { 122 | Vector v1; 123 | v1.set(0, 0, GeoCoordSys::earth()->getRadius()); 124 | Matrix rotate, invertRotate; 125 | quat.getMatrix(&rotate); 126 | rotate.invertByAdjoint(&invertRotate); 127 | 128 | invertRotate.multVector(v1); 129 | Coord2D pos; 130 | GeoCoordSys::xyzToBl(v1, pos); 131 | return pos; 132 | } 133 | 134 | void TraceBall::projectToShpere(float x, float y, Vector &vec) 135 | { 136 | double s = xyScale(); 137 | double dx = x / s; 138 | double dy = y / s; 139 | vec.x = dx; 140 | vec.y = dy; 141 | //vec.w() = 1; 142 | 143 | double proLength2 = (dx*dx) + (dy*dy); 144 | double z2 = (GeoCoordSys::earth()->getRadius()*GeoCoordSys::earth()->getRadius()) - proLength2; 145 | vec.z = z2 < 0 ? 0 : sqrt(z2); 146 | } 147 | #endif -------------------------------------------------------------------------------- /src/fe3dtiles/b3dm.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2023, chunquedong/yangjiandong 3 | * 4 | * This file is part of mgpEarth project 5 | * Licensed under the GNU GENERAL PUBLIC LICENSE Version 3 6 | * 7 | */ 8 | #include "b3dm.h" 9 | #include "jparser.hpp" 10 | 11 | FE_USING_NAMESPACE 12 | 13 | using namespace jc; 14 | 15 | namespace detail { 16 | const char MAGIC[4] = { 'b', '3', 'd', 'm' }; 17 | 18 | struct Header { 19 | std::uint32_t version = 1; 20 | std::uint32_t byteLength = size(); 21 | std::uint32_t featureTableJSONByteLength = 0; 22 | std::uint32_t featureTableBinaryByteLength = 0; 23 | std::uint32_t batchTableJSONByteLength = 0; 24 | std::uint32_t batchTableBinaryByteLength = 0; 25 | 26 | static std::size_t size() { 27 | return 7 * sizeof(std::uint32_t); 28 | } 29 | }; 30 | 31 | 32 | struct Legacy { 33 | enum class Type { none, json, gltf }; 34 | Type type = Type::none; 35 | uint8_t buffer[4]; 36 | }; 37 | 38 | /** Reads b3dm header. 39 | */ 40 | bool read(mgp::Stream *data, Header& header, Legacy& legacy) 41 | { 42 | char magic[4]; 43 | data->read(magic, 4); 44 | 45 | if (std::memcmp(magic, MAGIC, sizeof(MAGIC))) { 46 | return false; 47 | } 48 | 49 | header.version = data->readUInt32(); 50 | header.byteLength = data->readUInt32(); 51 | header.featureTableJSONByteLength = data->readUInt32(); 52 | header.featureTableBinaryByteLength = data->readUInt32(); 53 | 54 | // OK, legacy format ends here, we have to deal with old data 55 | // TODO: detect old format 56 | 57 | header.batchTableJSONByteLength = data->readUInt32(); 58 | header.batchTableBinaryByteLength = data->readUInt32(); 59 | return true; 60 | } 61 | 62 | void readFeatureTable(mgp::Stream* data, Header& header, mgp::Vector3& rtcCenter) 63 | { 64 | if (!header.featureTableJSONByteLength) { return; } 65 | 66 | // read feature table to temporary buffer 67 | std::string buf; 68 | buf.resize(header.featureTableJSONByteLength+1); 69 | data->read((char*)buf.data(), header.featureTableJSONByteLength); 70 | 71 | 72 | JsonAllocator allocator; 73 | JsonParser parser(&allocator); 74 | Value* value0 = parser.parse((char*)buf.c_str()); 75 | 76 | if (!value0 || parser.get_error()[0] != 0) { 77 | printf("parser b3dm json error: %s\n", parser.get_error()); 78 | return; 79 | } 80 | 81 | Value* RTC_CENTER = value0->get("RTC_CENTER"); 82 | if (RTC_CENTER) 83 | { 84 | int size = RTC_CENTER->size(); 85 | if (size != 3) return; 86 | 87 | auto it = RTC_CENTER->begin(); 88 | double v0 = it->as_float(); ++it; 89 | double v1 = it->as_float(); ++it; 90 | double v2 = it->as_float(); ++it; 91 | rtcCenter.set(v0, v1, v2); 92 | } 93 | //json_delete(root); 94 | } 95 | } 96 | 97 | bool TdB3dm::loadB3dm(mgp::Stream* data) 98 | { 99 | detail::Header header; 100 | detail::Legacy legacy; 101 | if (!detail::read(data, header, legacy)) { 102 | return false; 103 | } 104 | 105 | readFeatureTable(data, header, rtcCenter); 106 | 107 | // ignore rest of tables 108 | data->seek(data->position()+header.featureTableBinaryByteLength 109 | + header.batchTableJSONByteLength 110 | + header.batchTableBinaryByteLength); 111 | 112 | //read gltf 113 | long gltfSize = data->length() - data->position(); 114 | gltf.resize(gltfSize); 115 | data->read((char*)gltf.data(), gltfSize); 116 | 117 | return true; 118 | } 119 | -------------------------------------------------------------------------------- /src/feUtil/common.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2023, chunquedong/yangjiandong 3 | * 4 | * This file is part of mgpEarth project 5 | * all rights reserved 6 | * 7 | */ 8 | #ifndef COMMON_H 9 | #define COMMON_H 10 | 11 | //#include "cppfan/cppfan.h" 12 | 13 | #define PF_USING_NAMESPACE using namespace mgp; 14 | 15 | #define FE_BEGIN_NAMESPACE namespace mgpEarth { 16 | #define FE_END_NAMESPACE } 17 | #define FE_USING_NAMESPACE using namespace mgpEarth; 18 | 19 | 20 | /** 21 | * suppress unused warning 22 | */ 23 | #define CF_UNUSED(p) ((void)p) 24 | 25 | /*======================================================================== 26 | * Field 27 | */ 28 | #define CF_FIELD(Type, name) private: Type _##name;\ 29 | public: Type name() const { return _##name; }\ 30 | public: void name(Type name_) { _##name = name_; }\ 31 | private: 32 | 33 | #define CF_READONLY_FIELD(Type, name) private: Type _##name;\ 34 | public: Type name() const { return _##name; }\ 35 | private: 36 | 37 | #define CF_FIELD_POINTER(Type, name) private: Type _##name;\ 38 | public: Type *name() { return &_##name; }\ 39 | private: 40 | 41 | #define CF_FIELD_REF(Type, name) private: Type _##name;\ 42 | public: Type &name() { return _##name; }\ 43 | public: void name(Type &name_) { _##name = name_; }\ 44 | private: 45 | 46 | #define CF_FIELD_CONST_REF(Type, name) private: Type _##name;\ 47 | public: const Type &name() const { return _##name; } \ 48 | public: void name(Type &name_) { _##name = name_; }\ 49 | private: 50 | 51 | ///////////////////////////////////////////////////////////////////////// 52 | #include 53 | 54 | #define cf_tolerance 1e-8 55 | 56 | #ifdef max 57 | #undef max 58 | #endif 59 | 60 | #ifdef min 61 | #undef min 62 | #endif 63 | 64 | FE_BEGIN_NAMESPACE 65 | 66 | namespace Math { 67 | 68 | double const PI = 3.14159265358979323846; 69 | 70 | template 71 | T max(T a, T b) { return (a > b) ? a : b; } 72 | 73 | template 74 | T min(T a, T b) { return (((a) < (b)) ? (a) : (b)); } 75 | 76 | template 77 | T abs(T a, T b) { return (((a) < 0) ? -(a) : (a)); } 78 | 79 | template 80 | T clamp(T a, T amin, T amax) { return (min(max((a), (amin)), (amax))); } 81 | 82 | 83 | inline double toRadians(double f) { return ((f) / 180.0 * PI); } 84 | 85 | inline double toDegrees(double f) { return ((f) / PI * 180.0); } 86 | 87 | /** 88 | * log2(e) 89 | */ 90 | double const log2e = 1.44269504088896340736; //log2(e) 91 | 92 | /** 93 | * log base 2. 94 | */ 95 | inline double log2(double x) { 96 | return ::log(x) * log2e; 97 | } 98 | 99 | /** 100 | * approximately equal. 101 | * if tolerance is -1, then it is computed using the magnitude. 102 | */ 103 | inline bool approx(double a, double b, double tolerance = cf_tolerance) { 104 | double af; 105 | double bf; 106 | if (tolerance == -1) { 107 | af = fabs(a / 1e6); 108 | bf = fabs(b / 1e6); 109 | tolerance = min(af, bf); 110 | } 111 | return fabs(a - b) < tolerance; 112 | } 113 | 114 | } 115 | 116 | FE_END_NAMESPACE 117 | 118 | ///////////////////////////////////////////////////////////////////////// 119 | #include 120 | #include 121 | #include "mgp_pro.h" 122 | 123 | FE_BEGIN_NAMESPACE 124 | 125 | typedef std::string Str; 126 | 127 | template using Array = std::vector; 128 | typedef mgp::Vector3 Vector; 129 | typedef mgp::Rectangle Rect; 130 | typedef mgp::BoundingSphere Sphere; 131 | 132 | class Callback { 133 | public: 134 | virtual ~Callback() {} 135 | virtual void* call(void*) = 0; 136 | }; 137 | 138 | FE_END_NAMESPACE 139 | 140 | #endif // COMMON_H 141 | 142 | -------------------------------------------------------------------------------- /src/feCtrl/EarthCtrl.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2023, chunquedong/yangjiandong 3 | * 4 | * This file is part of mgpEarth project 5 | * all rights reserved 6 | * 7 | */ 8 | #ifndef EARTHCTRL_H 9 | #define EARTHCTRL_H 10 | 11 | #include "feTile/TileLayer.h" 12 | #include "feCtrl/EarthAnimation.h" 13 | 14 | FE_BEGIN_NAMESPACE 15 | 16 | class TrackModel; 17 | 18 | class EarthCtrl : public mgp::Refable, public mgp::GestureListener { 19 | protected: 20 | EarthAnimation* animation; 21 | 22 | //camera to center distance 23 | double distanceToCenter; 24 | //long-lat 25 | Coord2D cameraPosition; 26 | bool cameraDirty; 27 | bool _zoomDirty; 28 | double groundHeight; 29 | 30 | //pitch 31 | double rotationX; 32 | //yaw 33 | double rotationZ; 34 | 35 | //Coord2D lastTouchPosition; 36 | double fieldOfViewY; 37 | 38 | mgp::Rectangle viewport; 39 | 40 | mgp::Matrix cameraTransform; 41 | 42 | uint64_t lastGroundHeightUpdateTime; 43 | 44 | mgp::UPtr _followNode; 45 | int _followTrackModelId = 0; 46 | 47 | mgp::Picker picker; 48 | public: 49 | mgp::Node* _groundNode; 50 | mgp::SceneView* _sceneView; 51 | double maxLevel; 52 | public: 53 | 54 | EarthCtrl(); 55 | ~EarthCtrl(); 56 | 57 | void setFollowNode(mgp::Node* node, int followTrackModelId = -1); 58 | 59 | void finalize(); 60 | 61 | void invalidateCamera() { cameraDirty = true; } 62 | 63 | EarthAnimation* getAnimation() { return animation; } 64 | 65 | mgp::Picker* getPicker() { return &picker; } 66 | public: 67 | 68 | /** 69 | * get the scale of world coordinate to screen 70 | */ 71 | virtual double xyScale(); 72 | 73 | /** 74 | * get current camera look at surface postion in geo coordinate system 75 | */ 76 | virtual Coord2D getPosition() { 77 | return cameraPosition; 78 | } 79 | 80 | double getDistanceToSurface(); 81 | 82 | /** 83 | * set look at surface postion in geo coordinate system 84 | */ 85 | virtual void moveToPostion(Coord2D pos); 86 | 87 | virtual void setRotationX(double rotX); 88 | virtual double getRotationX() { return rotationX; } 89 | 90 | virtual void setRotationZ(double rotZ); 91 | virtual double getRotationZ() { return rotationZ; } 92 | 93 | virtual void setZoom(double dis); 94 | virtual double getZoom(); 95 | virtual void scaleZoom(double scale); 96 | 97 | /** 98 | * really do update the camera matrix 99 | */ 100 | virtual bool updateCamera(float elapsedTime, mgp::Camera &camera, mgp::Rectangle &viewport); 101 | 102 | virtual void moveByPixel(float dx, float dy); 103 | 104 | static bool getGroundPoint(Coord2D position, mgp::Node* node, mgp::Vector3& point); 105 | static bool getGroundPointByNormal(mgp::Vector3& position, mgp::Node* node, mgp::Vector3& point); 106 | bool getScreenGroundPoint(Coord2D screen_position, mgp::Vector3& point); 107 | protected: 108 | virtual void updateCameraTransform(mgp::Camera &camera, mgp::Rectangle &viewport); 109 | virtual void updateTransform(); 110 | 111 | private: 112 | 113 | double yDegreeScale(mgp::Rectangle &viewport); 114 | double xDegreeScale(mgp::Rectangle &viewport, double y); 115 | 116 | void updateGroundHeight(mgp::Node* node); 117 | 118 | 119 | void onZoom(float v, int x, int y) override; 120 | void onPush(float v) override; 121 | void onMultiTouch(float rotate, float zoom, int x, int y) override; 122 | 123 | void onTouch() override; 124 | void onDrag(int dx, int dy) override; 125 | void onFling(float dx, float dy) override; 126 | void onRightDrag(int dx, int dy) override; 127 | void onDoubleClick(int dx, int dy) override; 128 | void onClick(int x, int y) override; 129 | }; 130 | 131 | FE_END_NAMESPACE 132 | #endif // EARTHCTRL_H 133 | -------------------------------------------------------------------------------- /src/feTile/TileManager.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2023, chunquedong/yangjiandong 3 | * 4 | * This file is part of mgpEarth project 5 | * Licensed under the GNU GENERAL PUBLIC LICENSE Version 3 6 | * 7 | */ 8 | #ifndef TILEMANAGER_H 9 | #define TILEMANAGER_H 10 | 11 | #include "feModel/Tile.h" 12 | #include "mgp.h" 13 | 14 | FE_BEGIN_NAMESPACE 15 | 16 | union TileKey { 17 | Tile tile; 18 | void* ptr; 19 | uint64_t id; 20 | 21 | TileKey() { 22 | memset(this, 0, sizeof(TileKey)); 23 | }; 24 | 25 | bool operator== (const TileKey &other) const { 26 | return tile == other.tile; 27 | } 28 | 29 | size_t hashCode() const { 30 | return tile.hashCode(); 31 | } 32 | }; 33 | FE_END_NAMESPACE 34 | 35 | namespace std { 36 | template <> struct hash { 37 | size_t operator()(const mgpEarth::TileKey &key) const { 38 | return key.hashCode(); 39 | } 40 | }; 41 | } 42 | FE_BEGIN_NAMESPACE 43 | 44 | 45 | class ITileData : public mgp::Refable { 46 | public: 47 | //CF_FIELD_REF(SPtr, fallback) 48 | CF_FIELD(TileKey, parent) 49 | public: 50 | virtual mgp::BoundingSphere& bounding() = 0; 51 | bool isReady() { return getState() == 2; } 52 | virtual int getState() = 0; 53 | virtual mgp::Node* getNode() = 0; 54 | virtual TileKey tileKey() = 0; 55 | virtual void setAsFallback(bool isFallback) {} 56 | }; 57 | typedef mgp::SPtr TileDataPtr; 58 | 59 | 60 | class LRUCache : public mgp::Cache { 61 | public: 62 | std::mutex lock; 63 | std::vector toDelete; 64 | LRUCache(long maxSize) : Cache(maxSize) {} 65 | ~LRUCache(); 66 | virtual void onRemove(TileKey &key, TileDataPtr &val) override; 67 | }; 68 | 69 | class DataActor; 70 | 71 | class TileManager : public mgp::Refable, public mgp::NetListener { 72 | protected: 73 | LRUCache cache; 74 | mgp::HashMap curSearchSet; 75 | mgp::HashMap overrviewSearchSet; 76 | private: 77 | //send task container for cancle 78 | mgp::HashMap > sendedTask; 79 | mgp::Cache errorTask; 80 | float _progress = 0; 81 | protected: 82 | bool resultDirty; 83 | public: 84 | //DataActor* dataListener; 85 | public: 86 | TileManager(); 87 | virtual ~TileManager(); 88 | virtual void update(mgp::Camera* camera, mgp::Rectangle* viewport, mgp::Matrix* modelMatrix, bool isSendTask = true); 89 | void getResult(std::vector& list); 90 | virtual bool resultChanged(); 91 | void setResultDirty() { resultDirty = true; } 92 | 93 | void releaseCache(); 94 | 95 | float getProgress(); 96 | 97 | virtual mgp::Matrix* getRootTransform() { return nullptr; }; 98 | private: 99 | void searchTiles(TileDataPtr &tileView, mgp::Camera &camera, mgp::Rectangle &viewport, mgp::Matrix& modelMatrix 100 | , int &count); 101 | protected: 102 | void sendTask(bool isOverview); 103 | void cancelTask(); 104 | protected: 105 | virtual void onTaskDone(TileKey key); 106 | virtual sric::SharedPtr load(TileKey key); 107 | virtual TileDataPtr getParent(TileDataPtr t); 108 | protected: 109 | virtual TileDataPtr getRoot() = 0; 110 | virtual TileDataPtr makeTileData(TileKey key) = 0; 111 | virtual void tryInit(TileDataPtr &data, bool isOverview) {} 112 | virtual void getChildren(TileDataPtr &data, std::vector &children) = 0; 113 | virtual bool isFitLod(TileDataPtr &data, mgp::Camera &camera, mgp::Rectangle &viewport, mgp::Matrix& modelMatrix) = 0; 114 | 115 | virtual bool getUri(TileKey key, std::string &uri, std::string &file) = 0; 116 | 117 | virtual void* decode(mgp::HttpClient* task, mgp::NetResponse &res) = 0; 118 | virtual void onReceive(mgp::HttpClient* task, mgp::NetResponse &res) = 0; 119 | }; 120 | 121 | FE_END_NAMESPACE 122 | 123 | #endif 124 | -------------------------------------------------------------------------------- /sdk/demo/geolayer.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | mgpEarth Demo 7 | 22 | 23 | 24 | 25 | 26 | 101 | 102 | -------------------------------------------------------------------------------- /src/feModel/Mercator.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2023, chunquedong/yangjiandong 3 | * 4 | * This file is part of mgpEarth project 5 | * all rights reserved 6 | * 7 | */ 8 | #include "feModel/Mercator.h" 9 | 10 | FE_USING_NAMESPACE 11 | 12 | 13 | Spheroid::Spheroid(const char *name, double a, double b) : name(name), a(a), b(b) 14 | { 15 | assert(!(a < b)); 16 | this->e = computeE(); 17 | this->e2 = computeE2(); 18 | } 19 | 20 | /** 21 | */ 22 | double Spheroid::computeE() 23 | { 24 | return sqrt(1.0- pow((b / a), 2)); 25 | } 26 | 27 | /** 28 | */ 29 | double Spheroid::computeE2() 30 | { 31 | return sqrt(pow((a / b), 2) - 1.0); 32 | } 33 | 34 | 35 | const double Mercator::maxDis = 20037508.3427892; 36 | const double Mercator::radius = 6378137.0; 37 | 38 | MercatorTransform *Mercator::getTransform() 39 | { 40 | //Spheroid *s = spheroid(); 41 | //static MercatorTransform trans(s->a, s->b, s->e, s->e2, 0.0, 0.0); 42 | return &trans; 43 | } 44 | 45 | MercatorReTrans *Mercator::getReverseTransform() 46 | { 47 | //Spheroid *s = spheroid(); 48 | //static MercatorReTrans reTrans(s->a, s->b, s->e, s->e2, 0.0, 0.0); 49 | return &reTrans; 50 | } 51 | 52 | Mercator::Mercator() : 53 | _spheroid("circle", radius, radius), 54 | trans(_spheroid.a, _spheroid.b, _spheroid.e, _spheroid.e2, 0.0, 0.0), 55 | reTrans(_spheroid.a, _spheroid.b, _spheroid.e, _spheroid.e2, 0.0, 0.0) 56 | { 57 | //name = new Str("Mercator"); 58 | } 59 | 60 | Mercator &Mercator::getInstance() { 61 | static Mercator instance; 62 | return instance; 63 | } 64 | 65 | /** 66 | */ 67 | Spheroid *Mercator::spheroid() 68 | { 69 | //if (cs != NULL && cs->spheroid != NULL) return (Spheroid *)cs->spheroid; 70 | return &_spheroid; 71 | } 72 | 73 | double Mercator::computeK(double a, double b, double e2, double b0) 74 | { 75 | double fz = pow(a, 2) / b; 76 | double fm = sqrt(1 + pow(e2, 2) * pow(cos(b0), 2)); 77 | double r = (fz / fm) * cos(b0); 78 | return r; 79 | } 80 | 81 | 82 | //========================================================= 83 | 84 | MercatorReTrans::MercatorReTrans(double a, double b, double e, double e2, double b0, double l0) 85 | { 86 | this->k = Mercator::computeK(a, b, e2, b0); 87 | this->l0 = l0; 88 | this->e = e; 89 | } 90 | 91 | void MercatorReTrans::convert(Coord2D *p) 92 | { 93 | double B = computeB(p->y); 94 | double L = getL(p->x); 95 | 96 | double b = Math::toDegrees(B); //longitude 97 | double l = Math::toDegrees(L); //latitude 98 | 99 | //return new Coord(l, b); 100 | p->x = l; 101 | p->y = b; 102 | } 103 | 104 | /** 105 | */ 106 | double MercatorReTrans::computeB(double x) 107 | { 108 | double b = getB(0, x); 109 | for (int i = 0; i < 20; i++) 110 | { 111 | double bb = getB(b, x); 112 | if (abs(bb - b) < 0.01f) return bb; 113 | b = bb; 114 | } 115 | return b; 116 | } 117 | 118 | /** 119 | */ 120 | double MercatorReTrans::getB(double b, double x) 121 | { 122 | double B = (Math::PI/2) - 2 * atan(my_exp(b,x)); 123 | return B; 124 | } 125 | 126 | /** 127 | */ 128 | double MercatorReTrans::my_exp(double b, double x) 129 | { 130 | return exp(-(x / k)) 131 | * exp( 132 | (e / 2)* log( 133 | (1 - e * sin(b)) 134 | / (1 + e * sin(b)) 135 | ) 136 | ); 137 | } 138 | 139 | /** 140 | */ 141 | double MercatorReTrans::getL(double y) 142 | { 143 | return (y / k) + l0; 144 | } 145 | 146 | //========================================================= 147 | 148 | MercatorTransform::MercatorTransform(double a, double b, double e, double e2, double b0, double l0) 149 | { 150 | this->k = Mercator::computeK(a, b, e2, b0); 151 | this->l0 = l0; 152 | this->e = e; 153 | } 154 | 155 | void MercatorTransform::convert(Coord2D *p) 156 | { 157 | double b = Math::toDegrees(p->y); //latitude 158 | double l = Math::toDegrees(p->x); //longitude 159 | 160 | double y = k * lntan(b, e); 161 | double x = k * (l - l0); 162 | 163 | //limit 164 | if (y > Mercator::maxDis) 165 | { 166 | y = Mercator::maxDis; 167 | } 168 | else if (y < -Mercator::maxDis) 169 | { 170 | y = -Mercator::maxDis; 171 | } 172 | 173 | //return new Coord(x, y); 174 | p->x = x; 175 | p->y = y; 176 | } 177 | 178 | /** 179 | */ 180 | double MercatorTransform::lntan(double b, double e) 181 | { 182 | double d = tan(Math::PI / 4 + b / 2) * 183 | pow(((1 - e * sin(b)) / (1 + e * sin(b))), (e / 2)); 184 | return log(d); 185 | } 186 | -------------------------------------------------------------------------------- /src/feObjects/GroundNode.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2023, chunquedong/yangjiandong 3 | * 4 | * This file is part of mgpEarth project 5 | * all rights reserved 6 | * 7 | */ 8 | #ifndef _GROUNDMODEL_H 9 | #define _GROUNDMODEL_H 10 | 11 | #include "feUtil/common.h" 12 | #include "mgp_pro.h" 13 | #include "feModel/Envelope.h" 14 | #include "GeoNode.h" 15 | 16 | FE_BEGIN_NAMESPACE 17 | 18 | class EarthApp; 19 | 20 | class GroundModel : public GltfNode { 21 | uint64_t lastUpdateTime; 22 | protected: 23 | Coord2D position; 24 | double height; 25 | mgp::Vector3 direction; 26 | bool dirty; 27 | public: 28 | int autoStickGround = 0; 29 | EarthApp* app = NULL; 30 | mgp::Matrix pose; 31 | uint64_t updateDelay; 32 | GroundModel(const char* uri); 33 | void setPosition(const Coord2D& p, double height); 34 | 35 | virtual void update(float elapsedTime) override; 36 | bool updateHeight(int stickMethod = 1); 37 | }; 38 | 39 | class MultiModel; 40 | class TrackModel : public mgp::Refable { 41 | protected: 42 | //line segment index 43 | int lastPointIndex = 0; 44 | 45 | //animation stop time 46 | uint64_t pathEndTime = 0; 47 | 48 | //animation start time 49 | uint64_t startTime = 0; 50 | 51 | //offset in curernt line segment 52 | double segmentOffset = 0; 53 | 54 | //distance from begin to current position in path 55 | double _offsetLength = 0; 56 | bool _isRuning = false; 57 | mgp::Node* _node = nullptr; 58 | 59 | //height update time 60 | uint64_t lastUpdateTime; 61 | double _height = 0; 62 | public: 63 | int _collisionObject = 0; 64 | MultiModel* parent; 65 | int autoStickGround = 0; 66 | uint64_t updateDelay; 67 | 68 | //current position 69 | mgp::Vector3 _curPosition; 70 | 71 | //current instance id 72 | int _id; 73 | 74 | //animation speed 75 | double speed = 15; 76 | 77 | //delay time to call 'onStop' after end animation 78 | uint64_t afterDelayTime = 0; 79 | uint64_t beforeDelayTime = 0; 80 | 81 | //init pose matrix 82 | mgp::Matrix pose; 83 | 84 | //animation along path 85 | std::vector path; 86 | 87 | //init model drection 88 | mgp::Vector3 direction; 89 | 90 | //user define data 91 | mgp::UPtr userData; 92 | 93 | //callback when animation end 94 | std::function onStop; 95 | 96 | //callback when animation end with delay time 97 | std::function onEnd; 98 | 99 | //callback when position changed 100 | std::function onPositionUpdate; 101 | 102 | TrackModel(); 103 | ~TrackModel(); 104 | 105 | void setNode(mgp::Node* node); 106 | mgp::Node* getNode() { return _node; } 107 | 108 | void setFromLonLat(std::vector& path2d, double height); 109 | 110 | virtual void update(float elapsedTime); 111 | void start(); 112 | void stop(); 113 | void reset(); 114 | void pause(); 115 | bool isRuning(); 116 | 117 | /** 118 | * play first gltf animation 119 | */ 120 | void playAnimation(int repeatCount = mgp::AnimationClip::REPEAT_INDEFINITE); 121 | void stopAnimation(); 122 | 123 | /** 124 | * distance from begin to current position in path 125 | */ 126 | double getOffsetLength() { return _offsetLength; } 127 | double getCurSegmentOffset() { return segmentOffset; } 128 | int getCurSegmentIndex() { return lastPointIndex; } 129 | double getCurHeight() { return _height; } 130 | private: 131 | void setStop(); 132 | bool updateHeight(int stickMethod = 1); 133 | protected: 134 | bool tryUpdateHeight(); 135 | }; 136 | 137 | class MultiModel : public GltfNode, public mgp::PhysicsCollisionObject::CollisionListener { 138 | protected: 139 | mgp::UPtr _templateModel; 140 | int _idCount = 0; 141 | std::map > _instances; 142 | public: 143 | std::function onCollisionEvent; 144 | EarthApp* app = NULL; 145 | 146 | MultiModel(const char* uri); 147 | 148 | int add(mgp::UPtr inst); 149 | void remove(int id); 150 | TrackModel* get(int id); 151 | void clear(); 152 | 153 | virtual void update(float elapsedTime) override; 154 | protected: 155 | virtual void onReceive(mgp::NetResponse& res, mgp::MultiRequest* req) override; 156 | 157 | virtual void collisionEvent(mgp::PhysicsCollisionObject::CollisionListener::EventType type, 158 | const mgp::PhysicsCollisionObject::CollisionPair& collisionPair, 159 | const mgp::Vector3& contactPointA = mgp::Vector3::zero(), 160 | const mgp::Vector3& contactPointB = mgp::Vector3::zero()) override; 161 | }; 162 | 163 | FE_END_NAMESPACE 164 | 165 | #endif // _GEO_LAYER_H 166 | -------------------------------------------------------------------------------- /src/feGeo/Geometry.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2023, chunquedong/yangjiandong 3 | * 4 | * This file is part of mgpEarth project 5 | * all rights reserved 6 | * 7 | */ 8 | #ifndef _GEOMETRY_H 9 | #define _GEOMETRY_H 10 | 11 | #include "feUtil/common.h" 12 | #include "feModel/Envelope.h" 13 | #include "jvalue.hpp" 14 | #include "mgp_pro.h" 15 | 16 | //#include 17 | #include 18 | 19 | FE_BEGIN_NAMESPACE 20 | 21 | enum class GeometryType { 22 | Unknow, 23 | Point, 24 | MultiPoint, 25 | LineString, 26 | MultiLineString, 27 | Polygon, 28 | MultiPolygon, 29 | GeometryCollection, 30 | Mix, 31 | }; 32 | 33 | struct GeoLine { 34 | int startPoint; 35 | int size; 36 | }; 37 | 38 | class Geometry { 39 | public: 40 | Geometry(): type(GeometryType::Unknow) {} 41 | GeometryType type; 42 | std::vector coordinates; 43 | std::vector lines; 44 | std::vector geometries; 45 | 46 | bool parse(jc::Value* geometry); 47 | jc::JsonNode* save(jc::JsonAllocator* allocator); 48 | 49 | void getPoint(mgp::Vector3& point, int i); 50 | int getPointCount(); 51 | 52 | private: 53 | void parsePoint(jc::Value* jcoord); 54 | void parseLine(jc::Value* jcoord); 55 | void parsePolygon(jc::Value* jcoord); 56 | 57 | jc::JsonNode* savePoint(int i, jc::JsonAllocator* allocator); 58 | jc::JsonNode* saveLine(int i, jc::JsonAllocator* allocator); 59 | jc::JsonNode* savePolygon(jc::JsonAllocator* allocator); 60 | jc::JsonNode* saveGeomertyCollection(jc::JsonAllocator* allocater); 61 | }; 62 | 63 | class FeatureField { 64 | public: 65 | enum Type { 66 | Int, Float, String, Bool 67 | }; 68 | Type type; 69 | int index; 70 | std::string name; 71 | std::string desc; 72 | int flags = 0; 73 | }; 74 | 75 | struct FeatureValue { 76 | std::string strValue; 77 | union { 78 | int64_t intValue; 79 | double floatValue; 80 | }; 81 | 82 | FeatureValue() : intValue(0) {} 83 | }; 84 | 85 | class FeatureCollection; 86 | 87 | class Feature : public mgp::Refable { 88 | std::vector properties; 89 | public: 90 | FeatureCollection* parent = nullptr; 91 | Geometry geometry; 92 | 93 | void setFromStr(const std::string& name, const std::string& value); 94 | void getAsStr(const std::string& name, std::string& value); 95 | void getAsStr(int i, std::string& value); 96 | 97 | int64_t getInt(int i) { 98 | if (i >= this->properties.size()) 99 | return 0; 100 | return properties[i].intValue; 101 | } 102 | double getFloat(int i) { 103 | if (i >= this->properties.size()) 104 | return 0; 105 | return properties[i].floatValue; 106 | } 107 | const std::string& getStr(int i) { 108 | if (i >= this->properties.size()) 109 | return ""; 110 | return properties[i].strValue; 111 | } 112 | 113 | void setInt(int i, int64_t value) { 114 | if (i >= this->properties.size()) 115 | properties.resize(i); 116 | properties[i].intValue = value; 117 | } 118 | 119 | void setFloat(int i, double value) { 120 | if (i >= this->properties.size()) 121 | properties.resize(i); 122 | properties[i].floatValue = value; 123 | } 124 | 125 | void setStr(int i, const std::string& value) { 126 | if (i >= this->properties.size()) 127 | properties.resize(i); 128 | properties[i].strValue = value; 129 | } 130 | 131 | void setValue(int i, FeatureValue& value); 132 | FeatureValue* getValue(int i); 133 | FeatureField* makeFieldValue(const std::string& name, FeatureField::Type type); 134 | public: 135 | bool parse(jc::Value* feature); 136 | bool parseProperties(jc::Value* jproperties); 137 | jc::JsonNode* save(jc::JsonAllocator* allocator); 138 | }; 139 | 140 | class FeatureCollection : public mgp::Refable { 141 | friend class Feature; 142 | std::map fieldIndex; 143 | public: 144 | std::string dataVersion; 145 | std::string crs; 146 | GeometryType type; 147 | std::vector > features; 148 | std::vector fields; 149 | 150 | FeatureField* getField(const std::string& name); 151 | FeatureField* getField(int i) { return &fields[i]; } 152 | int getFieldCount() { return fields.size(); } 153 | void addField(FeatureField& field); 154 | 155 | void add(mgp::UPtr f); 156 | Feature* get(int i) { return features[i].get(); } 157 | int size() { return features.size(); } 158 | 159 | int removeLike(const std::string& fieldName, const std::string& value, bool one = true); 160 | void removeAt(int index); 161 | 162 | FeatureCollection(); 163 | ~FeatureCollection(); 164 | 165 | bool parse(std::string& json); 166 | void save(std::string& json); 167 | }; 168 | 169 | 170 | FE_END_NAMESPACE 171 | 172 | #endif // _GEOMETRY_H 173 | -------------------------------------------------------------------------------- /src/feElevation/Elevation.cpp: -------------------------------------------------------------------------------- 1 | #include "Elevation.h" 2 | #include "mgp.h" 3 | //#include "cppfan/Profiler.h" 4 | #include "feTile/TileData.h" 5 | #include "feTile/TileGeom.hpp" 6 | #include "feModel/PyramidGrid.h" 7 | #include "feModel/GeoCoordSys.h" 8 | 9 | #include "3rd/stb_image.h" 10 | 11 | FE_USING_NAMESPACE 12 | 13 | OfflineElevation* g_defaultOfflineElevation; 14 | 15 | OfflineElevation* OfflineElevation::cur() { 16 | return g_defaultOfflineElevation; 17 | } 18 | void OfflineElevation::_setCur(OfflineElevation* elev) { 19 | g_defaultOfflineElevation = elev; 20 | } 21 | 22 | OfflineElevation::OfflineElevation(): imageData(NULL), width(0), height(0), elevationScale(1), format(-1) { 23 | envelope.init(-180, -90, 180, 90); 24 | } 25 | 26 | OfflineElevation::OfflineElevation(const char* uri, int format, Envelope* env) : elevationScale(1) { 27 | if (!env) { 28 | envelope.init(-180, -90, 180, 90); 29 | } 30 | else { 31 | envelope = *env; 32 | } 33 | 34 | // int max = INT_MIN; 35 | // int min = INT_MAX; 36 | 37 | int iw, ih, n; 38 | void* data = 0; 39 | if (format == 2) { 40 | stbi_set_flip_vertically_on_load(false); 41 | data = stbi_load_16(uri, &iw, &ih, &n, 0); 42 | 43 | // for (int i = 0; i < iw; ++i) { 44 | // for (int j = 0; j < ih; ++j) { 45 | // uint16_t* pos = (uint16_t*)data + ((iw * j + i) * n); 46 | // double v = *pos; 47 | // if (v > max) max = v; 48 | // if (v < min) min = v; 49 | // } 50 | // } 51 | } 52 | else if (format == 3) { 53 | stbi_set_flip_vertically_on_load(false); 54 | data = stbi_load(uri, &iw, &ih, &n, 0); 55 | GP_ASSERT(n == 3); 56 | 57 | // for (int i = 0; i < iw; ++i) { 58 | // for (int j = 0; j < ih; ++j) { 59 | // uint8_t* pos = (uint8_t*)data + ((iw * j + i) * n); 60 | // int R = pos[0]; 61 | // int G = pos[1]; 62 | // int B = pos[2]; 63 | // if (R == 6) { 64 | // R = 1; 65 | // } 66 | // double v = -10000 + ((R * 256 * 256 + G * 256 + B) * 0.1); 67 | // if (v > max) max = v; 68 | // if (v < min) min = v; 69 | // } 70 | // } 71 | } 72 | else { 73 | printf("Unknow format:%d\n", format); 74 | abort(); 75 | } 76 | 77 | if (!data) { 78 | printf("load image error:%s\n", uri); 79 | abort(); 80 | } 81 | 82 | this->format = format; 83 | imageData = data; 84 | width = iw; 85 | height = ih; 86 | } 87 | 88 | double OfflineElevation::sampleHeight(int x, int y) { 89 | if (x >= width) x = width - 1; 90 | if (y >= height) y = height - 1; 91 | if (x < 0) x = 0; 92 | if (y < 0) y = 0; 93 | double heightv = 0; 94 | if (format == 3) { 95 | int ppb = 3; 96 | uint8_t* data = (uint8_t*)imageData; 97 | uint8_t* pos = data + ((width * y + x) * ppb); 98 | int R = pos[0]; 99 | int G = pos[1]; 100 | int B = pos[2]; 101 | if (R == 6) { 102 | R = 1; 103 | } 104 | heightv = -10000 + ((R * 256 * 256 + G * 256 + B) * 0.1); 105 | if (heightv < -200) heightv = -200; 106 | } 107 | else if (format == 2) { 108 | uint16_t* data = (uint16_t*)imageData; 109 | heightv = data[width * y + x]; 110 | } 111 | return heightv; 112 | } 113 | 114 | double OfflineElevation::getHeight(double longitude, double latitude, int level) { 115 | double u = (longitude - envelope.minX()) / envelope.width(); 116 | double v = 1.0 - (latitude - envelope.minY()) / envelope.height(); 117 | 118 | double x = u * width; 119 | double y = v * height; 120 | int ix = (int)round(x); 121 | int iy = (int)round(y); 122 | double heightv = 0; 123 | #if 0 124 | heightv = sampleHeight(ix, iy); 125 | return heightv * elevationScale; 126 | #else 127 | int limitLevel = 11; 128 | if (level < limitLevel) { 129 | heightv = sampleHeight(ix, iy); 130 | } 131 | else { 132 | double sumValue = 0; 133 | double sumWeight = 0; 134 | for (int i = -1; i < 2; ++i) { 135 | for (int j = -1; j < 2; ++j) { 136 | int sx = ix + i; 137 | int sy = iy + j; 138 | double dx = (sx - x); 139 | double dy = (sy - y); 140 | double disSq = dx * dx + dy * dy; 141 | if (disSq > 1) continue; 142 | 143 | double value = sampleHeight(sx, sy); 144 | double weight = (1 - disSq) / disSq; 145 | 146 | sumValue += value * weight; 147 | sumWeight += weight; 148 | } 149 | } 150 | heightv = sumValue / sumWeight; 151 | } 152 | #endif 153 | return heightv * elevationScale; 154 | } 155 | 156 | double OfflineElevation::getHeightMercator(double x, double y, int level) { 157 | Coord2D coor(x, y); 158 | GeoCoordSys::earth()->fromMercator(&coor); 159 | return getHeight(coor.x, coor.y, level); 160 | } 161 | -------------------------------------------------------------------------------- /src/feTile/TileData.cpp: -------------------------------------------------------------------------------- 1 | #include "feTile/TileData.h" 2 | #include "mgp.h" 3 | #include "feModel/PyramidGrid.h" 4 | #include "feModel/GeoCoordSys.h" 5 | #include "feTile/TileGeom.hpp" 6 | 7 | FE_USING_NAMESPACE 8 | PF_USING_NAMESPACE 9 | 10 | TileData::TileData(Tile &tile, PyramidGrid *pyramid) : _tile(tile), 11 | geometry(nullptr), pyramid(pyramid), _node(nullptr), image(nullptr), _isFallback(false), _approximateHeight(0) 12 | { 13 | pyramid->tileEnvelopeBL(_tile, _envelope); 14 | pyramid->tileEnvelope(_tile, _envelopeMercator); 15 | } 16 | 17 | TileData::~TileData() { 18 | /*SAFE_RELEASE(geometry); 19 | SAFE_RELEASE(_node); 20 | SAFE_RELEASE(image);*/ 21 | } 22 | 23 | mgp::BoundingSphere& TileData::bounding() { 24 | if (geometry.get() && geometry->getNode()) { 25 | boundingSphere = (mgp::BoundingSphere&)geometry->getMesh()->getBoundingSphere(); 26 | boundingSphere.center += geometry->getTranlation(); 27 | return boundingSphere; 28 | } 29 | initBounding(); 30 | return boundingSphere; 31 | } 32 | 33 | int TileData::getState() { 34 | if (geometry.get()) return 2; 35 | if (image.get()) return 1; 36 | return 0; 37 | } 38 | 39 | TileKey TileData::tileKey() { 40 | TileKey key = {}; 41 | key.tile = _tile; 42 | return key; 43 | } 44 | 45 | void TileData::updateTranslation(Node* node) { 46 | node->setTranslation(geometry->getTranlation()); 47 | if (_isFallback) { 48 | Vector3 v = boundingSphere.center; 49 | v.normalize(); 50 | v.negate(); 51 | int num = 1 << tile().z; 52 | double adjust = 65535.0 / num; 53 | if (adjust < 0) adjust = 0; 54 | node->translate(v * adjust); 55 | } 56 | } 57 | 58 | Node* TileData::getNode() { 59 | if (_node.get()) { 60 | updateTranslation(_node.get()); 61 | return _node.get(); 62 | } 63 | if (!geometry.get()) return nullptr; 64 | 65 | char buf[256]; 66 | snprintf(buf, 256, "%d_%d_%d", _tile.x, _tile.y, _tile.z); 67 | _node = Node::create(buf); 68 | _node->addComponent(uniqueFromInstant(geometry.get())); 69 | //_node->setTranslation(geometry->getTranlation()); 70 | updateTranslation(_node.get()); 71 | return _node.get(); 72 | } 73 | 74 | void TileData::setAsFallback(bool isFallback) { 75 | _isFallback = isFallback; 76 | } 77 | 78 | double TileData::computeViewScale(Camera &camera, Rectangle &viewport) 79 | { 80 | Vector center; 81 | Coord2D c = envelope().getCenter(); 82 | GeoCoordSys::earth()->lnglatToXyz(c, _approximateHeight, center); 83 | camera.getViewMatrix().transformPoint(¢er); 84 | 85 | Vector position; 86 | position.set(0, 0, 0); 87 | 88 | double distance = center.distance(position); 89 | double surfaceHeight = tan(Math::toRadians(camera.getFieldOfView()/2.0)) * distance; 90 | double geoHeight = Math::toRadians(envelope().height()) * (GeoCoordSys::earth()->getRadius()+ _approximateHeight); 91 | 92 | double height = viewport.height * geoHeight / (surfaceHeight+surfaceHeight); 93 | 94 | int w,h; 95 | pyramid->tileScreenSize(w, h); 96 | 97 | float dpiScale = Toolkit::cur()->getScreenScale();//Device::cur()->dpToPixelScale(); 98 | if (dpiScale < 2) { 99 | dpiScale = 1; 100 | } 101 | 102 | //display size / image size 103 | double viewScale = (height * height ) / (w*h *(dpiScale*dpiScale)); 104 | return viewScale * 0.5; 105 | } 106 | 107 | static void project(Coord2D coord, Sphere &boundingSphere, double height) { 108 | Vector point; 109 | GeoCoordSys::earth()->blToXyz(coord, point, GeoCoordSys::earth()->getRadius() + height); 110 | boundingSphere.merge(point); 111 | } 112 | 113 | void TileData::initBounding() { 114 | if (!boundingSphere.isEmpty()) { 115 | return; 116 | } 117 | Envelope &envelope = _envelope; 118 | Tile &tile = _tile; 119 | 120 | project(envelope.minPoint(), boundingSphere, _approximateHeight); 121 | project(envelope.maxPoint(), boundingSphere, _approximateHeight); 122 | project(envelope.leftUp(), boundingSphere, _approximateHeight); 123 | project(envelope.rightDown(), boundingSphere, _approximateHeight); 124 | project(envelope.getCenter(), boundingSphere, _approximateHeight); 125 | 126 | if (tile.z < 16) { 127 | Coord2D coord; 128 | coord.set(envelope.minX()+envelope.width()/4, envelope.minY()+envelope.height()/4); 129 | project(coord, boundingSphere, _approximateHeight); 130 | 131 | coord.set(envelope.minX()+envelope.width()/4*3, envelope.minY()+envelope.height()/4); 132 | project(coord, boundingSphere, _approximateHeight); 133 | 134 | coord.set(envelope.minX()+envelope.width()/4, envelope.minY()+envelope.height()/4*3); 135 | project(coord, boundingSphere, _approximateHeight); 136 | 137 | coord.set(envelope.minX()+envelope.width()/4*3, envelope.minY()+envelope.height()/4*3); 138 | project(coord, boundingSphere, _approximateHeight); 139 | } 140 | 141 | //height is approximate 142 | if (tile.z > 8) { 143 | boundingSphere.radius *= 2; 144 | } 145 | if (tile.z > 13) { 146 | boundingSphere.radius *= 2; 147 | } 148 | if (tile.z > 15) { 149 | boundingSphere.radius *= 2; 150 | } 151 | } 152 | 153 | void TileData::printTile(const char *name) 154 | { 155 | printf("%s: %d,%d,%d. ref:%d, inMem:%d, fallback:nil\n", name, tile().x, tile().y, tile().z 156 | , getRefCount(), isReady()); 157 | fflush(stdout); 158 | } 159 | -------------------------------------------------------------------------------- /src/feGeo/Building.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2023, chunquedong/yangjiandong 3 | * 4 | * This file is part of mgpEarth project 5 | * all rights reserved 6 | * 7 | */ 8 | #include "Building.h" 9 | #include "feModel/GeoCoordSys.h" 10 | #include "jparser.hpp" 11 | 12 | using namespace jc; 13 | FE_USING_NAMESPACE 14 | PF_USING_NAMESPACE 15 | 16 | Building::Building(const char* uri): GeoNode(uri) { 17 | color = Vector4(51 / 255.0, 153 / 255.0, 255 / 255.0, 1.0); 18 | request->setSaveToFile(false); 19 | } 20 | Building::~Building() { 21 | } 22 | 23 | static void decodeBuilding(MeshBatch* mesh, Stream* stream, Vector3& translation, float heightScale) { 24 | int code = stream->readUInt32(); 25 | if (code != 0x9abc) { 26 | printf("building format error\n"); 27 | return; 28 | } 29 | int version = stream->readUInt32(); 30 | int count = stream->readUInt32(); 31 | stream->readUInt32(); 32 | translation.x = stream->readDouble(); 33 | translation.y = stream->readDouble(); 34 | translation.z = stream->readDouble(); 35 | 36 | std::vector indexBuf0; 37 | std::vector vertexBuf0; 38 | std::vector indexBuf; 39 | std::vector vertexBuf; 40 | 41 | Vector3 up = translation; 42 | up.normalize(); 43 | for (int i = 0; i < count; ++i) { 44 | uint8_t vertexCount = stream->readUInt8(); 45 | float height = stream->readFloat() * heightScale; 46 | 47 | vertexBuf0.clear(); 48 | vertexBuf0.resize(vertexCount * 3); 49 | stream->read((char*)vertexBuf0.data(), vertexCount * 3 * sizeof(float)); 50 | 51 | uint8_t indexCount = stream->readUInt8(); 52 | indexBuf0.clear(); 53 | indexBuf0.resize(indexCount * sizeof(uint32_t)); 54 | stream->read((char*)indexBuf0.data(), indexCount, sizeof(uint32_t)); 55 | 56 | indexBuf.clear(); 57 | vertexBuf.clear(); 58 | for (int j = 0; j < vertexBuf0.size(); j+=3) { 59 | float x = vertexBuf0[j]; 60 | float y = vertexBuf0[j+1]; 61 | float z = vertexBuf0[j+2]; 62 | vertexBuf.push_back(x-up.x * 10); 63 | vertexBuf.push_back(y-up.y * 10); 64 | vertexBuf.push_back(z-up.z * 10); 65 | vertexBuf.push_back(0); 66 | 67 | vertexBuf.push_back(x+up.x * height); 68 | vertexBuf.push_back(y+up.y * height); 69 | vertexBuf.push_back(z+up.z * height); 70 | vertexBuf.push_back(height); 71 | } 72 | 73 | for (int j = 0; j < vertexCount; ++j) { 74 | int base = j*2; 75 | if (j == vertexCount -1) { 76 | indexBuf.push_back(base + 0); 77 | indexBuf.push_back(0); 78 | indexBuf.push_back(base + 1); 79 | indexBuf.push_back(base + 1); 80 | indexBuf.push_back(0); 81 | indexBuf.push_back(1); 82 | break; 83 | } 84 | indexBuf.push_back(base + 0); 85 | indexBuf.push_back(base + 2); 86 | indexBuf.push_back(base + 1); 87 | indexBuf.push_back(base + 1); 88 | indexBuf.push_back(base + 2); 89 | indexBuf.push_back(base + 3); 90 | } 91 | 92 | for (int j = 0; j < indexCount; ++j) { 93 | indexBuf.push_back(indexBuf0[j] * 2 + 1); 94 | } 95 | 96 | mesh->add(vertexBuf.data(), vertexCount*2, indexBuf.data(), indexBuf.size()); 97 | } 98 | } 99 | 100 | void* Building::decodeFile(const char* path, NetResponse& res, MultiRequest* req) { 101 | if (res.result.size() == 0) { 102 | return NULL; 103 | } 104 | 105 | UPtr material = Material::create("res/shaders/custom/building.vert", "res/shaders/custom/building.frag"); 106 | // Set initial material state 107 | material->getParameter("u_diffuseColor")->setVector4(color); 108 | 109 | VertexFormat::Element elements[] = 110 | { 111 | VertexFormat::Element(VertexFormat::POSITION, 3), 112 | //VertexFormat::Element(VertexFormat::NORMAL, 3), 113 | VertexFormat::Element("a_height", 1), 114 | }; 115 | VertexFormat vertexFormat(elements, 2); 116 | UPtr mesh = MeshBatch::create(vertexFormat, Mesh::TRIANGLES, std::move(material), Mesh::INDEX32, 10*1024*1024, 10 * 1024*1024); 117 | Vector3 translation; 118 | UPtr stream(new Buffer((uint8_t*)res.result.data(), res.result.size(), false)); 119 | decodeBuilding(mesh.get(), stream.get(), translation, heightScale); 120 | 121 | mesh->setRenderLayer(Drawable::Custom); 122 | 123 | Node* node = Node::createForComponent(std::move(mesh)).take(); 124 | node->setTranslation(translation); 125 | return node; 126 | } 127 | 128 | static void parserColor(Value* json, const char* name, Vector4& color) { 129 | Value* lineColor = json->get(name); 130 | if (lineColor && lineColor->size() == 4) { 131 | auto c = lineColor->begin(); 132 | color.x = c->as_float(); 133 | ++c; 134 | color.y = c->as_float(); 135 | ++c; 136 | color.z = c->as_float(); 137 | ++c; 138 | color.w = c->as_float(); 139 | //++c; 140 | } 141 | } 142 | 143 | bool Building::loadOptions(char* json_str) { 144 | JsonAllocator allocator; 145 | JsonParser parser(&allocator); 146 | Value* value0 = parser.parse(json_str); 147 | 148 | if (!value0 || parser.get_error()[0] != 0) { 149 | printf("parser options json error: %s\n", parser.get_error()); 150 | return false; 151 | } 152 | 153 | Value* max = value0->get("maxDis"); 154 | if (max) { 155 | this->maxDis = max->as_float(); 156 | } 157 | 158 | Value* min = value0->get("minDis"); 159 | if (min) { 160 | this->minDis = min->as_float(); 161 | } 162 | 163 | parserColor(value0, "color", this->color); 164 | return true; 165 | } -------------------------------------------------------------------------------- /src/fe3dtiles/TdTilesManager.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2023, chunquedong/yangjiandong 3 | * 4 | * This file is part of mgpEarth project 5 | * Licensed under the GNU GENERAL PUBLIC LICENSE Version 3 6 | * 7 | */ 8 | #include "fe3dtiles/TdTilesManager.h" 9 | #include "fe3dtiles/b3dm.h" 10 | 11 | #define NODE_DIRTY_BOUNDS 2 12 | 13 | FE_USING_NAMESPACE 14 | PF_USING_NAMESPACE 15 | 16 | TdTilesManager::TdTilesManager(const std::string& uri): uri(uri) { 17 | init(); 18 | } 19 | 20 | TdTilesManager::~TdTilesManager() { 21 | 22 | } 23 | 24 | TileDataPtr TdTilesManager::getRoot() { 25 | TileDataPtr ptr; 26 | ptr = tileset.root; 27 | return ptr; 28 | } 29 | 30 | TileDataPtr TdTilesManager::makeTileData(TileKey key) { 31 | TileDataPtr ptr; 32 | ptr = static_cast(key.ptr); 33 | return ptr; 34 | } 35 | 36 | void TdTilesManager::getChildren(TileDataPtr &data, std::vector &children) { 37 | TdTile* tile = dynamic_cast(data.get()); 38 | for (auto it : tile->children) { 39 | TileKey key{}; 40 | key.ptr = it; 41 | children.push_back(key); 42 | } 43 | } 44 | 45 | bool TdTilesManager::isFitLod(TileDataPtr &data, Camera &camera, Rectangle &viewport, Matrix& modelMatrix) { 46 | TdTile* tile = dynamic_cast(data.get()); 47 | 48 | if (tile->children.size() == 0) { 49 | return true; 50 | } 51 | 52 | TdBoundingVolume bound; 53 | bound.set(tile->boundingVolume); 54 | bound.transform(modelMatrix); 55 | 56 | double distance = camera.getNode()->getTranslationWorld().distance(bound.center); 57 | double e = 0.00957 * distance; 58 | if (e >= tile->geometricError) { 59 | return true; 60 | } 61 | return false; 62 | } 63 | 64 | bool TdTilesManager::getUri(TileKey key, std::string &uri, std::string &file) { 65 | TdTile* tile = static_cast(key.ptr); 66 | if (tile->content.uri.size() == 0) { 67 | return false; 68 | } 69 | 70 | uri = FileSystem::getDirectoryName(this->uri.c_str()); 71 | uri += tile->content.uri; 72 | return true; 73 | } 74 | 75 | void TdTilesManager::init() { 76 | //std::string uri = FileSystem::getDirectoryName(this->uri.c_str()); 77 | //uri += "?v=2"; 78 | 79 | auto client = HttpClient::create(); 80 | client->useCache = true; 81 | client->url = uri; 82 | client->id = NULL; 83 | client->listener = this; 84 | client->send(); 85 | //client->release(); 86 | } 87 | 88 | //const BoundingSphere& TdTilesManager::getBoundingSphere() const { 89 | // if (_dirtyBits & NODE_DIRTY_BOUNDS) 90 | // { 91 | // _dirtyBits &= ~NODE_DIRTY_BOUNDS; 92 | // 93 | // _bounds.set(tileset.root->boundingVolume); 94 | // 95 | // _bounds.transform(getWorldMatrix()); 96 | // } 97 | // return _bounds; 98 | //} 99 | 100 | void* TdTilesManager::decode(HttpClient* task, NetResponse &res) { 101 | if (res.id == NULL) { 102 | return NULL; 103 | } 104 | 105 | if (FileSystem::getExtension(res.url.c_str()) == ".B3DM") { 106 | Stream* stream = new mgp::Buffer((uint8_t*)res.result.data(), res.result.size(), false); 107 | //stream->endian = Endian::Big; 108 | TdB3dm b3dm; 109 | b3dm.loadB3dm(stream); 110 | 111 | GltfLoader loader; 112 | loader.lighting = lighting; 113 | UPtr scene = loader.loadFromBuf(b3dm.gltf.data(), b3dm.gltf.size()); 114 | 115 | stream->close(); 116 | delete stream; 117 | return scene.take(); 118 | } 119 | else if (FileSystem::getExtension(res.url.c_str()) == ".GLB") { 120 | GltfLoader loader; 121 | loader.lighting = lighting; 122 | UPtr scene = loader.loadFromBuf(res.result.data(), res.result.size()); 123 | return scene.take(); 124 | } 125 | return NULL; 126 | } 127 | 128 | void TdTilesManager::onReceive(HttpClient* task, NetResponse &res) { 129 | if ((res.statusCode >= 0 && res.statusCode < 200) || res.statusCode >= 400) { 130 | goto end; 131 | } 132 | 133 | if (res.id == NULL || FileSystem::getExtension(res.url.c_str()) == ".JSON") { 134 | if (!res.id) { 135 | gltfUpAxisZ = res.result.find("gltfUpAxis") != std::string::npos; 136 | 137 | tileset.parse(res.result); 138 | } 139 | else { 140 | TileKey* tileKey = static_cast((void*)res.id); 141 | TdTile* tile = static_cast(tileKey->ptr); 142 | TileLayer* layer = new TileLayer(new TdTilesManager(res.url)); 143 | 144 | /*if (!tile->transform.isIdentity()) { 145 | layer->setMatrix(tile->transform); 146 | }*/ 147 | tile->renderNode = UPtr(layer); 148 | } 149 | 150 | goto end; 151 | return; 152 | } 153 | else if (res.decodeResult) { 154 | TileKey* tileKey = static_cast((void*)res.id); 155 | TdTile* tile = static_cast(tileKey->ptr); 156 | Scene* scene = static_cast(res.decodeResult); 157 | 158 | UPtr node = Node::create(tile->content.uri.c_str()); 159 | scene->getRootNode()->moveChildrenTo(node.get()); 160 | SAFE_RELEASE(scene); 161 | 162 | //if (!tile->transform.isIdentity()) { 163 | // node->setMatrix(tile->transform); 164 | //} 165 | 166 | if (!gltfUpAxisZ) { 167 | //modle y-up. tile bound z-up 168 | node->rotateX(MATH_PI / 2); 169 | } 170 | 171 | tile->renderNode = std::move(node); 172 | } 173 | 174 | end: 175 | if (res.id) { 176 | TileKey* tile = static_cast((void*)res.id); 177 | onTaskDone(*tile); 178 | } 179 | } 180 | 181 | mgp::Matrix* TdTilesManager::getRootTransform() { 182 | if (!tileset.root) { 183 | return nullptr; 184 | } 185 | if (!tileset.root->transform.isIdentity()) { 186 | return &tileset.root->transform; 187 | } 188 | return nullptr; 189 | } 190 | -------------------------------------------------------------------------------- /src/fe3dtiles/TdTiles.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2023, chunquedong/yangjiandong 3 | * 4 | * This file is part of mgpEarth project 5 | * Licensed under the GNU GENERAL PUBLIC LICENSE Version 3 6 | * 7 | */ 8 | #include "fe3dtiles/TdTiles.h" 9 | #include "feUtil/common.h" 10 | #include "jparser.hpp" 11 | 12 | using namespace jc; 13 | FE_USING_NAMESPACE 14 | PF_USING_NAMESPACE 15 | 16 | TdTile::~TdTile() { 17 | for (auto sub : children) { 18 | sub->release(); 19 | } 20 | children.clear(); 21 | } 22 | 23 | void TdTileset::parse(std::string& str) 24 | { 25 | JsonAllocator allocator; 26 | JsonParser parser(&allocator); 27 | Value* value0 = parser.parse((char*)str.c_str()); 28 | 29 | if (!value0 || parser.get_error()[0] != 0) { 30 | printf("parser 3dtiles json error: %s\n", parser.get_error()); 31 | return; 32 | } 33 | 34 | Value* assetNode = value0->get("asset"); 35 | if (assetNode) 36 | { 37 | Value* versionNode = assetNode->get("version"); 38 | if (versionNode) { 39 | const char* version = versionNode->as_str(); 40 | } 41 | } 42 | 43 | Value* rootNode = value0->get("root"); 44 | if (rootNode) { 45 | this->root = new TdTile(); 46 | this->root->parse(rootNode); 47 | } 48 | } 49 | 50 | 51 | TdTileset::~TdTileset() { 52 | SAFE_RELEASE(root); 53 | } 54 | 55 | #define json_get(node, name) node->get(name) 56 | 57 | void parseBoundingVolume(Value* node, TdBoundingVolume* volume) { 58 | Value* box = json_get(node, "box"); 59 | if (box) { 60 | int size = box->size(); 61 | if (size != 12) return; 62 | 63 | auto it = box->begin(); 64 | double v0 = it->as_float(); ++it; 65 | double v1 = it->as_float(); ++it; 66 | double v2 = it->as_float(); ++it; 67 | volume->center.set(v0, v1, v2); 68 | 69 | Vector point0; 70 | v0 = it->as_float(); ++it; 71 | v1 = it->as_float(); ++it; 72 | v2 = it->as_float(); ++it; 73 | point0.set(v0, v1, v2); 74 | 75 | Vector point1; 76 | v0 = it->as_float(); ++it; 77 | v1 = it->as_float(); ++it; 78 | v2 = it->as_float(); ++it; 79 | point1.set(v0, v1, v2); 80 | 81 | Vector point2; 82 | v0 = it->as_float(); ++it; 83 | v1 = it->as_float(); ++it; 84 | v2 = it->as_float(); ++it; 85 | point2.set(v0, v1, v2); 86 | 87 | double dis = (point0 + point1 + point2).length(); 88 | volume->radius = dis; 89 | return; 90 | } 91 | 92 | Value* sphere = json_get(node, "sphere"); 93 | if (sphere) { 94 | int size = sphere->size(); 95 | if (size != 4) return; 96 | 97 | auto it = box->begin(); 98 | double v0 = it->as_float(); ++it; 99 | double v1 = it->as_float(); ++it; 100 | double v2 = it->as_float(); ++it; 101 | volume->center.set(v0, v1, v2); 102 | 103 | double v3 = it->as_float(); ++it; 104 | volume->radius = v3; 105 | return; 106 | } 107 | 108 | Value* region = json_get(node, "region"); 109 | if (region) { 110 | printf("unsupport BoundingVolume: region\n"); 111 | } 112 | } 113 | 114 | void TdTile::parse(Value* node) 115 | { 116 | Value* geometricError = json_get(node, "geometricError"); 117 | if (geometricError) { 118 | this->geometricError = geometricError->as_float(); 119 | } 120 | 121 | Value* boundingVolume = json_get(node, "boundingVolume"); 122 | if (boundingVolume) { 123 | parseBoundingVolume(boundingVolume, &this->boundingVolume); 124 | } 125 | 126 | Value* children = json_get(node, "children"); 127 | if (children) { 128 | //int size = json_size(children); 129 | for (auto i = children->begin(); i != children->end(); ++i) { 130 | Value* sub = *i; 131 | if (sub) { 132 | TdTile* tile = new TdTile(); 133 | tile->parse(sub); 134 | this->children.push_back(tile); 135 | } 136 | } 137 | } 138 | 139 | Value* content = json_get(node, "content"); 140 | if (content) { 141 | Value* uri = json_get(content, "url"); 142 | if (uri) { 143 | const char* str = uri->as_str(); 144 | this->content.uri = std::string(str); 145 | } 146 | else { 147 | uri = json_get(content, "uri"); 148 | if (uri) { 149 | const char* str = uri->as_str(); 150 | this->content.uri = std::string(str); 151 | } 152 | } 153 | Value* boundingVolume = json_get(content, "boundingVolume"); 154 | if (boundingVolume) { 155 | parseBoundingVolume(boundingVolume, &this->content.boundingVolume); 156 | } 157 | } 158 | 159 | Value* refine = json_get(node, "refine"); 160 | if (refine) { 161 | const char* str = refine->as_str(); 162 | std::string refine = std::string(str); 163 | //json_free(str); 164 | 165 | if (refine == "REPLACE") { 166 | this->refine = Refinement::REPLACE; 167 | } 168 | else if (refine == "ADD") { 169 | this->refine = Refinement::ADD; 170 | } 171 | } 172 | 173 | Value* transform = json_get(node, "transform"); 174 | if (transform) { 175 | std::vector values; 176 | values.reserve(16); 177 | for (auto i = transform->begin(); i != transform->end(); ++i) { 178 | Value* sub = *i; 179 | if (sub) { 180 | values.push_back(sub->as_float()); 181 | } 182 | } 183 | if (values.size() == 16) { 184 | this->transform.set(values.data()); 185 | } 186 | } 187 | } 188 | 189 | mgp::BoundingSphere& TdTile::bounding() { 190 | return boundingVolume; 191 | } 192 | 193 | int TdTile::getState() { 194 | if (!renderNode.isNull()) { 195 | return 2; 196 | } 197 | return 0; 198 | } 199 | 200 | mgp::Node* TdTile::getNode() { 201 | return renderNode.get(); 202 | } 203 | 204 | TileKey TdTile::tileKey() { 205 | TileKey key{}; 206 | key.ptr = this; 207 | return key; 208 | } 209 | 210 | -------------------------------------------------------------------------------- /src/feCtrl/EarthAnimation.cpp: -------------------------------------------------------------------------------- 1 | #include "feCtrl/EarthAnimation.h" 2 | #include "feCtrl/EarthCtrl.h" 3 | #include "feModel/GeoCoordSys.h" 4 | 5 | FE_USING_NAMESPACE 6 | PF_USING_NAMESPACE 7 | 8 | EarthAnimation::EarthAnimation() 9 | { 10 | } 11 | EarthAnimation::~EarthAnimation() { 12 | delete flingChannel; 13 | delete zoomChannel; 14 | delete rotateChannel; 15 | delete moveChannel; 16 | channelList.clear(); 17 | } 18 | 19 | void EarthAnimation::init(EarthCtrl *ctrl) 20 | { 21 | this->ctrl = ctrl; 22 | flingChannel = new FlingAnimChannel(); 23 | flingChannel->ctrl = ctrl; 24 | 25 | zoomChannel = new ZoomAnimChannel(); 26 | zoomChannel->ctrl = ctrl; 27 | 28 | rotateChannel = new RotateAnimChannel(); 29 | rotateChannel->ctrl = ctrl; 30 | 31 | moveChannel = new MoveToAnimChannel(); 32 | moveChannel->ctrl = ctrl; 33 | } 34 | 35 | bool EarthAnimation::update(float elapsedTime) { 36 | for (auto it = channelList.begin(); it != channelList.end();) { 37 | AnimChannel* c = *it; 38 | c->update(elapsedTime); 39 | if (!c->isRunning) { 40 | it = channelList.erase(it); 41 | } 42 | else { 43 | ++it; 44 | } 45 | } 46 | return true; 47 | } 48 | 49 | void EarthAnimation::start(AnimChannel* chnnel) { 50 | chnnel->start(); 51 | this->channelList.push_back(chnnel); 52 | } 53 | 54 | void EarthAnimation::stop() { 55 | for (auto it = channelList.begin(); it != channelList.end(); ++it) { 56 | (*it)->stop(); 57 | } 58 | channelList.clear(); 59 | } 60 | 61 | void AnimChannel::stop() { 62 | isRunning = false; 63 | } 64 | void AnimChannel::start() { 65 | _elapsedTime = 0; 66 | isRunning = true; 67 | } 68 | 69 | bool AnimChannel::update(float elapsedTime) { 70 | if (!isRunning) return false; 71 | _elapsedTime += elapsedTime; 72 | float percentComplete = 0; 73 | percentComplete = _elapsedTime / (float)duration; 74 | if (percentComplete > 1) { 75 | isRunning = false; 76 | percentComplete = 1; 77 | } 78 | 79 | doUpdate(elapsedTime, percentComplete); 80 | return true; 81 | } 82 | 83 | void EarthAnimation::fling(float vx, float vy) 84 | { 85 | if (vx == 0.0 && vy == 0.0) { 86 | return; 87 | } 88 | 89 | flingChannel->vx = vx; 90 | flingChannel->vy = vy; 91 | //printf("animation start: %f, %f\n", dx, dy); 92 | #if 1 93 | float acc = 0.0025f; 94 | flingChannel->acceleratedX = -acc * vx; 95 | flingChannel->acceleratedY = -acc * vy; 96 | #else 97 | float acc = 0.008f; 98 | Vector2 v(vx, vy); 99 | v.normalize(); 100 | flingChannel->acceleratedX = -acc * v.x; 101 | flingChannel->acceleratedY = -acc * v.y; 102 | #endif 103 | start(flingChannel); 104 | } 105 | 106 | void EarthAnimation::zoomTo(double zoom, uint64_t time) { 107 | zoomChannel->from = ctrl->getZoom(); 108 | zoomChannel->to = zoom; 109 | zoomChannel->duration = time; 110 | start(zoomChannel); 111 | } 112 | 113 | void EarthAnimation::rotateTo(float rx, float rz, uint64_t time) { 114 | rotateChannel->fromRotateX = ctrl->getRotationX(); 115 | rotateChannel->toRotateX = rx; 116 | rotateChannel->fromRotateZ = ctrl->getRotationZ(); 117 | rotateChannel->toRotateZ = rz; 118 | rotateChannel->duration = time; 119 | start(rotateChannel); 120 | } 121 | 122 | void EarthAnimation::moveTo(double x, double y, uint64_t time, double zoom) { 123 | Coord2D pos = ctrl->getPosition(); 124 | moveChannel->fromX = pos.x; 125 | moveChannel->toX = x; 126 | moveChannel->fromY = pos.y; 127 | moveChannel->toY = y; 128 | moveChannel->fromZoom = ctrl->getZoom(); 129 | 130 | if (isnan(zoom)) { 131 | moveChannel->toZoom = moveChannel->fromZoom; 132 | } 133 | else { 134 | moveChannel->toZoom = zoom; 135 | } 136 | 137 | Coord2D newPos(x, y); 138 | double dis = GeoCoordSys::earth()->distance(pos, newPos); 139 | double fids = GeoCoordSys::earth()->getRadius() / (1 << (int)(moveChannel->fromZoom)); 140 | if (dis > fids * 4) { 141 | moveChannel->middleZoom = std::min(moveChannel->fromZoom, moveChannel->toZoom) - 4; 142 | } 143 | else { 144 | moveChannel->middleZoom = (moveChannel->fromZoom + moveChannel->toZoom) / 2; 145 | } 146 | 147 | moveChannel->duration = time; 148 | start(moveChannel); 149 | } 150 | 151 | void FlingAnimChannel::doUpdate(float elapsedTime, float percentComplete) 152 | { 153 | if (acceleratedX == 0 && acceleratedY == 0) return; 154 | 155 | float t = elapsedTime; 156 | float t2 = t * t; 157 | 158 | float sx = vx * t + acceleratedX * t2 * 0.5; 159 | float sy = vy * t + acceleratedY * t2 * 0.5; 160 | 161 | float nvx = vx + acceleratedX * t; 162 | float nvy = vy + acceleratedY * t; 163 | //printf("animation: %f, %f => %f, %f\n", vx, vy, nvx, nvy); 164 | 165 | if (nvx * vx < 0 || nvy * vy < 0) { 166 | vx = 0; 167 | acceleratedX = 0; 168 | vy = 0; 169 | acceleratedY = 0; 170 | sx = 0; 171 | sy = 0; 172 | } 173 | else { 174 | vx = nvx; 175 | vy = nvy; 176 | } 177 | 178 | if (sx != 0 || sy != 0) { 179 | ctrl->moveByPixel(sx, sy); 180 | } 181 | } 182 | 183 | void ZoomAnimChannel::doUpdate(float elapsedTime, float percentComplete) { 184 | double d = to - from; 185 | double zoom = d * percentComplete + from; 186 | ctrl->setZoom(zoom); 187 | } 188 | 189 | void RotateAnimChannel::doUpdate(float elapsedTime, float percentComplete) { 190 | float dx = toRotateX - fromRotateX; 191 | float rx = dx * percentComplete + fromRotateX; 192 | ctrl->setRotationX(rx); 193 | 194 | float dz = toRotateZ - fromRotateZ; 195 | float rz = dz * percentComplete + fromRotateZ; 196 | ctrl->setRotationZ(rz); 197 | } 198 | 199 | void MoveToAnimChannel::doUpdate(float elapsedTime, float percentComplete) { 200 | double dx = toX - fromX; 201 | double rx = dx * percentComplete + fromX; 202 | double dz = toY - fromY; 203 | double rz = dz * percentComplete + fromY; 204 | 205 | ctrl->moveToPostion(Coord2D(rx, rz)); 206 | 207 | double from = fromZoom; 208 | double to = toZoom; 209 | float percent = 1; 210 | if (percentComplete < 0.5) { 211 | to = middleZoom; 212 | percent = percentComplete * 2; 213 | } 214 | else { 215 | from = middleZoom; 216 | percent = (percentComplete - 0.5) * 2; 217 | } 218 | if (to != from) { 219 | double d = to - from; 220 | double zoom = d * percent + from; 221 | ctrl->setZoom(zoom); 222 | } 223 | } 224 | -------------------------------------------------------------------------------- /src/feUtil/gcj.cpp: -------------------------------------------------------------------------------- 1 | 2 | #include 3 | #include "gcj.h" 4 | 5 | /* 6 | pi: 圆周率。 7 | a: 卫星椭球坐标投影到平面地图坐标系的投影因子。 8 | ee: 椭球的偏心率。 9 | x_pi: 圆周率转换量。 10 | */ 11 | 12 | double pi = 3.14159265358979324; 13 | double a = 6378245.0; 14 | double ee = 0.00669342162296594323; 15 | double x_pi = 3.14159265358979324 * 3000.0 / 180.0; 16 | 17 | int wgs2bd(double lat, double lon, double* pLat, double* pLon); // WGS84=>BD09 地球坐标系=>百度坐标系 18 | int gcj2bd(double lat, double lon, double* pLat, double* pLon); // GCJ02=>BD09 火星坐标系=>百度坐标系 19 | int bd2gcj(double lat, double lon, double* pLat, double* pLon); // BD09=>GCJ02 百度坐标系=>火星坐标系 20 | int wgs2gcj(double lat, double lon, double* pLat, double* pLon);// WGS84=>GCJ02 地球坐标系=>火星坐标系 21 | int gcj2wgs(double lat, double lon, double* pLat, double* pLon);// GCJ02=>WGS84 火星坐标系=>地球坐标系(粗略) 22 | int bd2wgs(double lat, double lon, double* pLat, double* pLon); // BD09=>WGS84 百度坐标系=>地球坐标系(粗略) 23 | int gcj2wgs_Exactly(double lat, double lon, double* wgs_Lat, double* wgs_lon);// GCJ02=>WGS84 火星坐标系=>地球坐标系(精确) 24 | int bd2wgs_Exactly(double lat, double lon, double* pLat, double* pLon);// BD09=>WGS84 百度坐标系=>地球坐标系(精确) 25 | double *OffSet(double lat, double lon); // 偏移量 26 | double transformLat(double x, double y);// 纬度偏移量 27 | double transformLon(double x, double y);// 经度偏移量 28 | int outOfChina(double lat, double lon); 29 | 30 | // WGS84=>BD09 地球坐标系=>百度坐标系 31 | int wgs2bd(double lat, double lon, double* pLat, double* pLon) { 32 | double lat_ = 0.0, lon_ = 0.0; 33 | wgs2gcj(lat, lon, &lat_, &lon_); 34 | gcj2bd(lat_, lon_, pLat, pLon); 35 | return 0; 36 | } 37 | 38 | // GCJ02=>BD09 火星坐标系=>百度坐标系 39 | int gcj2bd(double lat, double lon, double* pLat, double* pLon) { 40 | double x = lon, y = lat; 41 | double z = sqrt(x * x + y * y) + 0.00002 * sin(y * x_pi); 42 | double theta = atan2(y, x) + 0.000003 * cos(x * x_pi); 43 | *pLon = z * cos(theta) + 0.0065; 44 | *pLat = z * sin(theta) + 0.006; 45 | return 0; 46 | } 47 | 48 | // BD09=>GCJ02 百度坐标系=>火星坐标系 49 | int bd2gcj(double lat, double lon, double* pLat, double* pLon) { 50 | double x = lon - 0.0065, y = lat - 0.006; 51 | double z = sqrt(x * x + y * y) - 0.00002 * sin(y * x_pi); 52 | double theta = atan2(y, x) - 0.000003 * cos(x * x_pi); 53 | *pLon = z * cos(theta); 54 | *pLat = z * sin(theta); 55 | return 0; 56 | } 57 | 58 | // WGS84=>GCJ02 地球坐标系=>火星坐标系 59 | int wgs2gcj(double lat, double lon, double* pLat, double* pLon) { 60 | if (outOfChina(lat,lon)) 61 | { 62 | *pLat = lat; 63 | *pLon = lon; 64 | return 0; 65 | } 66 | double dLat = transformLat(lon - 105.0, lat - 35.0); 67 | double dLon = transformLon(lon - 105.0, lat - 35.0); 68 | double radLat = lat / 180.0 * pi; 69 | double magic = sin(radLat); 70 | magic = 1 - ee * magic * magic; 71 | double sqrtMagic = sqrt(magic); 72 | dLat = (dLat * 180.0) / ((a * (1 - ee)) / (magic * sqrtMagic) * pi); 73 | dLon = (dLon * 180.0) / (a / sqrtMagic * cos(radLat) * pi); 74 | *pLat = lat + dLat; 75 | *pLon = lon + dLon; 76 | return 0; 77 | } 78 | 79 | // GCJ02=>WGS84 火星坐标系=>地球坐标系(粗略) 80 | int gcj2wgs(double lat, double lon, double* pLat, double* pLon) { 81 | if (outOfChina(lat,lon)) 82 | { 83 | *pLat = lat; 84 | *pLon = lon; 85 | return 0; 86 | } 87 | double *offset; 88 | offset = OffSet(lat,lon); 89 | *pLat = lat - offset[0]; 90 | *pLon = lon - offset[1]; 91 | return 0; 92 | } 93 | 94 | // GCJ02=>WGS84 火星坐标系=>地球坐标系(精确) 95 | int gcj2wgs_Exactly(double gcjlat, double gcjlon, double* wgs_Lat, double* wgs_lon) { 96 | if (outOfChina(gcjlat,gcjlon)) 97 | { 98 | *wgs_Lat = gcjlat; 99 | *wgs_lon = gcjlon; 100 | return 0; 101 | } 102 | double initDelta = 0.01; 103 | double threshold = 0.000000001; 104 | double dLat = initDelta, dLon = initDelta; 105 | double mLat = gcjlat - dLat, mLon = gcjlon - dLon; 106 | double pLat = gcjlat + dLat, pLon = gcjlon + dLon; 107 | double wgsLat = 0.0, wgslon = 0.0, i = 0.0 ,newgcjlat = 0.0,newgcjlon = 0.0; 108 | 109 | while (true) { 110 | wgsLat = (mLat + pLat) / 2; 111 | wgslon = (mLon + pLon) / 2; 112 | wgs2gcj(wgsLat,wgslon,&newgcjlat,&newgcjlon); 113 | dLon = newgcjlon - gcjlon; 114 | dLat = newgcjlat - gcjlat; 115 | if ((fabs(dLat) < threshold) && (fabs(dLon) < threshold)) 116 | break; 117 | 118 | if (dLat > 0) 119 | pLat = wgsLat; 120 | else 121 | mLat = wgsLat; 122 | if (dLon > 0) 123 | pLon = wgslon; 124 | else 125 | mLon = wgslon; 126 | 127 | if (++i > 10000) 128 | break; 129 | } 130 | *wgs_Lat = wgsLat; 131 | *wgs_lon = wgslon; 132 | return 0; 133 | } 134 | 135 | // BD09=>WGS84 百度坐标系=>地球坐标系(粗略) 136 | int bd2wgs(double lat, double lon, double* pLat, double* pLon) { 137 | double lat_ = 0.0, lon_ = 0.0; 138 | bd2gcj(lat, lon, &lat_, &lon_); 139 | gcj2wgs(lat_, lon_, pLat, pLon); 140 | return 0; 141 | } 142 | 143 | // BD09=>WGS84 百度坐标系=>地球坐标系(精确) 144 | int bd2wgs_Exactly(double lat, double lon, double* pLat, double* pLon) { 145 | double lat_ = 0.0, lon_ = 0.0; 146 | bd2gcj(lat, lon, &lat_, &lon_); 147 | gcj2wgs_Exactly(lat_, lon_, pLat, pLon); 148 | return 0; 149 | } 150 | 151 | // 偏移量 152 | double *OffSet(double lat, double lon) { 153 | double Latlon[2] = {0.0,0.0}; 154 | double dLat = transformLat(lon - 105.0, lat - 35.0); 155 | double dLon = transformLon(lon - 105.0, lat - 35.0); 156 | double radLat = lat / 180.0 * pi; 157 | double magic = sin(radLat); 158 | magic = 1 - ee * magic * magic; 159 | double sqrtMagic = sqrt(magic); 160 | dLat = (dLat * 180.0) / ((a * (1 - ee)) / (magic * sqrtMagic) * pi); 161 | dLon = (dLon * 180.0) / (a / sqrtMagic * cos(radLat) * pi); 162 | Latlon[0] = dLat; 163 | Latlon[1] = dLon; 164 | return Latlon; 165 | } 166 | 167 | // 纬度偏移量 168 | double transformLat(double x, double y) { 169 | double ret = 0.0; 170 | ret = -100.0 + 2.0 * x + 3.0 * y + 0.2 * y * y + 0.1 * x * y + 0.2 * sqrt(fabs(x)); 171 | ret += (20.0 * sin(6.0 * x * pi) + 20.0 * sin(2.0 * x * pi)) * 2.0 / 3.0; 172 | ret += (20.0 * sin(y * pi) + 40.0 * sin(y / 3.0 * pi)) * 2.0 / 3.0; 173 | ret += (160.0 * sin(y / 12.0 * pi) + 320 * sin(y * pi / 30.0)) * 2.0 / 3.0; 174 | return ret; 175 | } 176 | 177 | // 经度偏移量 178 | double transformLon(double x, double y) { 179 | double ret = 0.0; 180 | ret = 300.0 + x + 2.0 * y + 0.1 * x * x + 0.1 * x * y + 0.1 * sqrt(fabs(x)); 181 | ret += (20.0 * sin(6.0 * x * pi) + 20.0 * sin(2.0 * x * pi)) * 2.0 / 3.0; 182 | ret += (20.0 * sin(x * pi) + 40.0 * sin(x / 3.0 * pi)) * 2.0 / 3.0; 183 | ret += (150.0 * sin(x / 12.0 * pi) + 300.0 * sin(x / 30.0 * pi)) * 2.0 / 3.0; 184 | return ret; 185 | } 186 | 187 | /** 188 | * Description: 中国境外返回true,境内返回false 189 | * @param lon 经度 190 | * @param lat 纬度 191 | * @return 192 | */ 193 | int outOfChina(double lat, double lon) { 194 | if (lon < 72.004 || lon > 137.8347) 195 | return true; 196 | if (lat < 0.8293 || lat > 55.8271) 197 | return true; 198 | return false; 199 | } 200 | -------------------------------------------------------------------------------- /src/feTile/TileManager.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2023, chunquedong/yangjiandong 3 | * 4 | * This file is part of mgpEarth project 5 | * all rights reserved 6 | * 7 | */ 8 | #include "feTile/TileManager.h" 9 | #include "mgp.h" 10 | //#include "cppfan/Profiler.h" 11 | #include "feTile/DataActor.h" 12 | 13 | FE_USING_NAMESPACE 14 | PF_USING_NAMESPACE 15 | 16 | void LRUCache::onRemove(TileKey &key, TileDataPtr &val) { 17 | std::lock_guard guard(lock); 18 | toDelete.push_back(val); 19 | } 20 | 21 | LRUCache::~LRUCache() { 22 | toDelete.clear(); 23 | clear(); 24 | } 25 | 26 | TileManager::TileManager(): resultDirty(false), cache(300), curSearchSet(200), sendedTask(200), overrviewSearchSet(200), errorTask(200) 27 | { 28 | } 29 | 30 | TileManager::~TileManager() { 31 | //cache.clear(); 32 | //curSearchSet.clear(); 33 | auto it = sendedTask.begin(); 34 | while (it != sendedTask.end()) { 35 | it->second->cancel(); 36 | //it->second->release(); 37 | ++it; 38 | } 39 | sendedTask.clear(); 40 | } 41 | 42 | void TileManager::releaseCache() { 43 | std::lock_guard guard(cache.lock); 44 | cache.toDelete.clear(); 45 | } 46 | 47 | void TileManager::update(Camera* camera, Rectangle* viewport, Matrix* modelMatrix, bool isSendTask) { 48 | auto oldSearchSet = curSearchSet; 49 | curSearchSet.clear(); 50 | overrviewSearchSet.clear(); 51 | 52 | int touchedTileCount = 0; 53 | TileDataPtr fallback; 54 | TileDataPtr root = getRoot(); 55 | 56 | if (root.isNull()) return; 57 | 58 | //std::vector path; 59 | searchTiles(root, *camera, *viewport, *modelMatrix, touchedTileCount); 60 | 61 | //GP_DEBUG("search size %d, touched:%d\n", curSearchSet.size(), touchedTileCount); 62 | 63 | //increase cache size 64 | if (cache.maxSize() < touchedTileCount) { 65 | cache.maxSize(touchedTileCount * 2); 66 | } 67 | 68 | cancelTask(); 69 | if (isSendTask) { 70 | sendTask(true); 71 | sendTask(false); 72 | } 73 | 74 | if (curSearchSet.size() != oldSearchSet.size()) { 75 | resultDirty = true; 76 | } 77 | else { 78 | for (auto itr = curSearchSet.begin(); itr != curSearchSet.end(); ++itr) { 79 | if (!oldSearchSet.contains(itr->first)) { 80 | resultDirty = true; 81 | } 82 | } 83 | } 84 | } 85 | 86 | void TileManager::getResult(std::vector& resultList) { 87 | auto itr0 = overrviewSearchSet.begin(); 88 | while (itr0 != overrviewSearchSet.end()) { 89 | TileDataPtr& tileView = itr0->second; 90 | if (!tileView->isReady()) { 91 | tryInit(tileView, true); 92 | } 93 | ++itr0; 94 | } 95 | 96 | HashMap fallbacks(50); 97 | auto itr = curSearchSet.begin(); 98 | while (itr != curSearchSet.end()) { 99 | TileDataPtr tileView = itr->second; 100 | //assert(tileView->tile().z <= maxLevel && tileView->tile().z>=0); 101 | tryInit(tileView, false); 102 | 103 | //show all tile view 104 | if (tileView->isReady()) { 105 | tileView->setAsFallback(false); 106 | resultList.push_back(tileView); 107 | } else { 108 | //add fallback 109 | auto parent = getParent(tileView); 110 | while (!parent.isNull()) { 111 | if (parent->isReady()) { 112 | fallbacks.set(parent->tileKey(), parent); 113 | break; 114 | } 115 | parent = getParent(parent); 116 | } 117 | } 118 | ++itr; 119 | } 120 | 121 | if (curSearchSet.size() == 0) { 122 | _progress = 1.0; 123 | } 124 | else { 125 | _progress = resultList.size() / (float)curSearchSet.size(); 126 | } 127 | 128 | for (auto it = fallbacks.begin(); it != fallbacks.end(); ++it) { 129 | TileDataPtr tileView = it->second; 130 | tileView->setAsFallback(true); 131 | resultList.push_back(tileView); 132 | } 133 | 134 | releaseCache(); 135 | 136 | resultDirty = false; 137 | } 138 | 139 | float TileManager::getProgress() { 140 | return _progress; 141 | } 142 | 143 | bool TileManager::resultChanged() { 144 | return resultDirty; 145 | } 146 | 147 | void TileManager::searchTiles(TileDataPtr &tileView, Camera &camera, Rectangle &viewport, Matrix& modelMatrix 148 | , int &count) { 149 | ++count; 150 | 151 | /*for (int i = 1; i < 16; ++i) { 152 | int num = 1 << i; 153 | if (tileView->tileKey().tile.x == 52603/ num && tileView->tileKey().tile.y == 26131/ num && tileView->tileKey().tile.z == 16-i) { 154 | printf("DEBUG\n"); 155 | } 156 | }*/ 157 | 158 | //test intersects 159 | mgp::BoundingSphere bounding = tileView->bounding(); 160 | bounding.transform(modelMatrix); 161 | if (!camera.getFrustum().intersects(bounding)) { 162 | //auto d = tileView->getNode()->getDrawable(); 163 | return; 164 | } 165 | 166 | //test the view scale 167 | if (isFitLod(tileView, camera, viewport, modelMatrix)) { 168 | curSearchSet.set(tileView->tileKey(), tileView); 169 | //assert(tileView->fallback().isNull() || tileView->fallback()->isReady()); 170 | 171 | auto parent = getParent(tileView); 172 | if (!parent.isNull()) { 173 | auto parentParent = getParent(parent); 174 | if (!parentParent.isNull()) { 175 | overrviewSearchSet.set(parentParent->tileKey(), parentParent); 176 | } 177 | } 178 | 179 | return; 180 | } 181 | 182 | //deep into the sub tile 183 | std::vector children; 184 | getChildren(tileView, children); 185 | for (int i=0; iparent(tileView->tileKey()); 194 | cache.set(tileKey, subView); 195 | } 196 | searchTiles(subView, camera, viewport, modelMatrix, count); 197 | } 198 | } 199 | 200 | TileDataPtr TileManager::getParent(TileDataPtr tileView) { 201 | TileDataPtr val; 202 | TileKey key = tileView->parent(); 203 | return cache._get(key, val); 204 | } 205 | 206 | void TileManager::cancelTask() { 207 | //cancel task 208 | std::vector canceled; 209 | auto it = sendedTask.begin(); 210 | while (it != sendedTask.end()) { 211 | TileKey key = it->first; 212 | if (!curSearchSet.contains(key) && !overrviewSearchSet.contains(key)) { 213 | it->second->cancel(); 214 | canceled.push_back(key); 215 | } 216 | ++it; 217 | } 218 | for (int i = 0; i < canceled.size(); ++i) { 219 | sendedTask.remove(canceled[i]); 220 | } 221 | } 222 | 223 | void TileManager::sendTask(bool isOverview) 224 | { 225 | HashMap* list = &curSearchSet; 226 | if (isOverview) { 227 | list = &overrviewSearchSet; 228 | } 229 | 230 | auto itr = list->begin(); 231 | while (itr != list->end()) { 232 | TileKey tileKey = itr->first; 233 | TileDataPtr &tileView = itr->second; 234 | 235 | if (tileView->getState() == 0) { 236 | if (!sendedTask.contains(tileKey) && !errorTask.contains(tileKey)) { 237 | auto task = load(tileKey); 238 | if (task.getPtr()) { 239 | sendedTask.set(tileKey, task); 240 | } 241 | } 242 | } 243 | ++itr; 244 | } 245 | 246 | } 247 | 248 | 249 | void TileManager::onTaskDone(TileKey key) { 250 | //remove from sendTask 251 | sric::SharedPtr task; 252 | task = sendedTask.get(key, task); 253 | if (task.getPtr() == nullptr || task->isCanceled()) { 254 | return; 255 | } 256 | sendedTask.remove(key); 257 | resultDirty = true; 258 | 259 | //if (dataListener) { 260 | // dataListener->sendMakeData(); 261 | //} 262 | TileDataPtr val; 263 | TileDataPtr value = cache._get(key, val); 264 | if (value.get() && value->getState() == 0) { 265 | int v = 1; 266 | errorTask.set(key, v); 267 | } 268 | } 269 | 270 | class TileRequest : public HttpClient { 271 | public: 272 | TileKey tileKey{}; 273 | }; 274 | 275 | sric::SharedPtr TileManager::load(TileKey key) { 276 | 277 | std::string url; 278 | std::string file; 279 | if (!getUri(key, url, file)) { 280 | return sric::SharedPtr(); 281 | } 282 | 283 | auto client = sric::makePtr(); 284 | client->tileKey = key; 285 | client->useCache = true; 286 | client->url = url; 287 | client->cacheFile = file; 288 | client->id = (uint64_t)&client->tileKey; 289 | client->listener = this; 290 | client->send(); 291 | 292 | return (client); 293 | } 294 | -------------------------------------------------------------------------------- /src/feTile/TileGeom.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // TileGeom.cpp 3 | // mgpEarth 4 | // 5 | // Created by yangjiandong on 2017/5/21. 6 | // Copyright © 2017年 yangjiandong. All rights reserved. 7 | // 8 | 9 | #include "feTile/TileGeom.hpp" 10 | #include "mgp.h" 11 | #include "feModel/PyramidGrid.h" 12 | #include "feModel/GeoCoordSys.h" 13 | #include "feTile/TileLayer.h" 14 | #include "feElevation/Elevation.h" 15 | 16 | FE_USING_NAMESPACE 17 | PF_USING_NAMESPACE 18 | 19 | TileGeom::TileGeom(PyramidGrid* pyramid) : _mesh(NULL), _material(NULL), texture(NULL), _pyramid(pyramid) { 20 | //setRenderPass(Drawable::Custom); 21 | _highlightType = Drawable::No; 22 | _pickMask = 2; 23 | } 24 | 25 | TileGeom::~TileGeom() { 26 | /*SAFE_RELEASE(_mesh); 27 | SAFE_RELEASE(_material); 28 | SAFE_RELEASE(texture);*/ 29 | } 30 | 31 | const BoundingSphere* TileGeom::getBoundingSphere() { 32 | return &_mesh->getBoundingSphere(); 33 | } 34 | 35 | static Material *makeMaterial(Material *material, Tile &tile, Image *image) { 36 | CF_UNUSED(tile); 37 | //ShaderProgram *effect = loadShaderProgram("fe.TileView"); 38 | UPtr texture; 39 | if (image) { 40 | texture = Texture::create(uniqueFromInstant(image), true); 41 | } else { 42 | texture = Texture::create("../../fastEarth/res/earth.jpg", false); 43 | if (!texture.get()) { 44 | GP_ERROR("load image error"); 45 | } 46 | } 47 | //material->init(effect, texture); 48 | texture->setWrapMode(Texture::CLAMP, Texture::CLAMP, Texture::CLAMP); 49 | texture->setFilterMode(Texture::LINEAR_MIPMAP_LINEAR, Texture::LINEAR); 50 | texture->setAnisotropy(4.0); 51 | material->getParameter("u_diffuseTexture")->setValue(texture.get()); 52 | //material->getStateBlock()->setCullFace(false); 53 | 54 | //SAFE_RELEASE(texture); 55 | 56 | // if (tile.z >= 2) { 57 | // material->techniques[0].passes[0].state.depthTestEnabled = false; 58 | // } 59 | return material; 60 | } 61 | 62 | 63 | UPtr TileGeom::makeMesh(double radius, Vector ¢er, int lod, Tile &tile, Vector3 &translation, 64 | Tile* elevationTile, ElevationQuery* elevation) { 65 | int latitudeBands = lod; 66 | int longitudeBands = lod; 67 | 68 | int vertexSize = (latitudeBands+3) * (longitudeBands+3); 69 | char *vertices = (char*)malloc(vertexSize * (8 * sizeof(float))); 70 | 71 | VertexFormat::Element elements[3]; 72 | elements[0] = VertexFormat::Element(VertexFormat::POSITION, 3); 73 | elements[1] = VertexFormat::Element(VertexFormat::NORMAL, 3); 74 | elements[2] = VertexFormat::Element(VertexFormat::TEXCOORD0, 2); 75 | VertexFormat format(elements, 3); 76 | UPtr mesh = Mesh::createMesh(format, vertexSize); 77 | 78 | int vertexIndicesSize = (latitudeBands+2) * (longitudeBands+2) * 6;// +((latitudeBands + longitudeBands) * 6); 79 | unsigned short* indexs = (unsigned short*)malloc(vertexIndicesSize * sizeof(unsigned short)); 80 | 81 | 82 | float *postion = (float*)vertices; 83 | unsigned short* indexsPosition = indexs; 84 | 85 | //------------------------------------------------------ 86 | int w,h; 87 | _pyramid->tileScreenSize(w, h); 88 | 89 | Envelope mactorEnv; 90 | _pyramid->tileEnvelope(tile, mactorEnv); 91 | 92 | Envelope blEnv; 93 | _pyramid->tileEnvelopeBL(tile, blEnv); 94 | 95 | if (blEnv.minX() < -180) { 96 | blEnv._minX = -180; 97 | } 98 | else if (blEnv.maxX() > 180) { 99 | blEnv._maxX = 180; 100 | } 101 | 102 | double mactorX = mactorEnv.minX(); 103 | double mactorY = mactorEnv.minY(); 104 | double mactorW = mactorEnv.width(); 105 | double mactorH = mactorEnv.height(); 106 | 107 | double startX = (blEnv.minX()); 108 | double startY = (blEnv.minY()); 109 | double width = (blEnv.width()); 110 | double height = (blEnv.height()); 111 | 112 | if (startY+height > 85) 113 | height = 90-startY; 114 | 115 | if (startY < -85) { 116 | height = height + (90 + startY); 117 | startY = -90; 118 | } 119 | 120 | double skirtPadding = width*0.02; 121 | 122 | //center point offset 123 | Coord2D centerBL(startX+width/2, startY+height/2); 124 | Vector localCenter; 125 | GeoCoordSys::blToXyz(centerBL, localCenter, radius); 126 | translation = (center + localCenter); 127 | 128 | for (int i=-1; i <= latitudeBands+1; i++) { 129 | bool ySkirt = false; 130 | double latitude; 131 | if (i < 0) { 132 | latitude = startY - skirtPadding; 133 | ySkirt = true; 134 | } 135 | else if (i > latitudeBands) { 136 | latitude = height+startY + skirtPadding; 137 | ySkirt = true; 138 | } 139 | else { 140 | latitude = i * height / latitudeBands + startY; 141 | } 142 | 143 | for (int j=-1; j <= longitudeBands+1; j++) { 144 | bool xSkirt = false; 145 | double longitude; 146 | if (j < 0) { 147 | longitude = startX - skirtPadding; 148 | xSkirt = true; 149 | } 150 | else if (j > latitudeBands) { 151 | longitude = width + startX + skirtPadding; 152 | xSkirt = true; 153 | } 154 | else { 155 | longitude = j * width / longitudeBands + startX; 156 | } 157 | 158 | 159 | Coord2D coordBL(longitude, latitude); 160 | //coord now is mercator 161 | Coord2D coordMercator = coordBL; 162 | GeoCoordSys::earth()->toMercator(&coordMercator); 163 | 164 | //must calc texture coord in mercator 165 | double u = /*longNumber /(double)longitudeBands;*/ (coordMercator.x - mactorX) / mactorW; 166 | double v; 167 | if (latitude == 90) { 168 | v = 0; 169 | coordMercator.y = 20037508.34; 170 | } 171 | else if (latitude == -90) { 172 | v = 1.0; 173 | coordMercator.y = -20037508.34; 174 | } 175 | else { 176 | v = /*1-(latNumber / (double)latitudeBands);*/ 1 - (coordMercator.y - mactorY) / mactorH; 177 | } 178 | 179 | double height = 0; 180 | if (elevation) { 181 | height = elevation->getTileHeight(*elevationTile, coordMercator.x, coordMercator.y, 1, tile.z); 182 | } 183 | if (xSkirt || ySkirt) { 184 | height -= mactorW * 0.02; 185 | } 186 | 187 | Vector vector; 188 | GeoCoordSys::blToXyz(coordBL, vector, radius+ height); 189 | 190 | Vector pos = vector + center - translation; 191 | postion[0] = pos.x; 192 | postion[1] = pos.y; 193 | postion[2] = pos.z; 194 | 195 | //TODO: normal 196 | postion[3] = 0; 197 | postion[4] = 0; 198 | postion[5] = 0; 199 | 200 | postion[6] = u; 201 | postion[7] = v; 202 | postion += 8; 203 | } 204 | } 205 | 206 | for (int i=0; i < latitudeBands+2; i++) { 207 | for (int j=0; j < longitudeBands+2; j++) { 208 | int second = (i * (longitudeBands + 3)) + j; 209 | int first = second + longitudeBands + 3; 210 | //set index 211 | indexsPosition[0] = first; 212 | indexsPosition[1] = second; 213 | indexsPosition[2] = first + 1; 214 | indexsPosition[3] = second; 215 | indexsPosition[4] = second + 1; 216 | indexsPosition[5] = first + 1; 217 | indexsPosition += 6; 218 | } 219 | } 220 | 221 | mesh->getVertexBuffer()->setData(vertices, vertexSize * (8 * sizeof(float))); 222 | mesh->setIndex(Mesh::TRIANGLES, vertexIndicesSize); 223 | mesh->getIndexBuffer()->setData((char*)indexs, vertexIndicesSize * sizeof(unsigned short)); 224 | return mesh; 225 | } 226 | 227 | bool TileGeom::doRaycast(RayQuery& query) { 228 | return _mesh->doRaycast(query); 229 | } 230 | 231 | void TileGeom::init(Image *image, Tile &tile, Tile* elevationTile, ElevationQuery* elevation) { 232 | if (_mesh.get()) return; 233 | //inited = true; 234 | //Envelope envelope; 235 | //PyramidGrid::getDefault()->tileEnvelopeBL(tile, envelope); 236 | 237 | //adjust the radius 238 | double radius = GeoCoordSys::earth()->getRadius(); 239 | 240 | /*if (tile.z < 8) { 241 | int num = 1 << tile.z; 242 | double scale = (1.0 - (0.01 / num)); 243 | radius *= scale; 244 | }*/ 245 | 246 | Vector center; 247 | center.set(0,0,0); 248 | 249 | //get the level of details 250 | int lod = 20; 251 | 252 | _mesh = makeMesh(radius, center, lod, tile, tranlation, elevationTile, elevation); 253 | 254 | _material = Material::create("res/shaders/textured.vert", "res/shaders/textured.frag"); 255 | makeMaterial(_material.get(), tile, image); 256 | } 257 | 258 | unsigned int TileGeom::draw(RenderInfo* view) 259 | { 260 | if (!_mesh.get()) return 0; 261 | _mesh->draw(view, this, _material.get()); 262 | 263 | return 1; 264 | } 265 | -------------------------------------------------------------------------------- /src/feTile/XyzTileManager.cpp: -------------------------------------------------------------------------------- 1 | #include "feTile/XyzTileManager.h" 2 | #include "mgp.h" 3 | //#include "cppfan/Profiler.h" 4 | #include "feTile/TileData.h" 5 | #include "feTile/TileGeom.hpp" 6 | #include "feModel/PyramidGrid.h" 7 | #include "feModel/GeoCoordSys.h" 8 | #include "feElevation/Elevation.h" 9 | #include "feTile/DataActor.h" 10 | 11 | FE_USING_NAMESPACE 12 | PF_USING_NAMESPACE 13 | 14 | XyzTileManager::XyzTileManager(const std::string& uri): uri(uri), 15 | #ifdef MGP_THREAD 16 | threadPool(nullptr), 17 | #endif 18 | viewScale(2.0) { 19 | maxLevel = 18; 20 | minLevel = 2; 21 | if (mgp::StringUtil::contains(uri, "bdimg")) { 22 | pyramid = PyramidGrid::getBD(); 23 | minLevel = 3; 24 | } 25 | else { 26 | pyramid = PyramidGrid::getDefault(); 27 | } 28 | Tile rootTile; 29 | root = TileDataPtr(new TileData(rootTile, pyramid)); 30 | } 31 | 32 | XyzTileManager::~XyzTileManager() { 33 | #ifdef MGP_THREAD 34 | if (threadPool) { 35 | threadPool->stop(); 36 | SAFE_DELETE(threadPool); 37 | } 38 | #endif 39 | } 40 | 41 | void XyzTileManager::setElevation(ElevationManager* elevation) 42 | { 43 | this->elevation = elevation; 44 | #ifdef MGP_THREAD 45 | if (!threadPool) { 46 | threadPool = new ThreadPool(1); 47 | threadPool->start(); 48 | } 49 | #endif 50 | } 51 | 52 | void XyzTileManager::update(Camera* camera, Rectangle* viewport, Matrix* modelMatrix, bool isSendTask) { 53 | TileManager::update(camera, viewport, modelMatrix, true); 54 | 55 | if (elevation.get()) { 56 | std::set tiles; 57 | auto itr0 = overrviewSearchSet.begin(); 58 | while (itr0 != overrviewSearchSet.end()) { 59 | TileDataPtr& tileData = itr0->second; 60 | if (!tileData->isReady()) { 61 | TileData* tileView = dynamic_cast(tileData.get()); 62 | Envelope env; 63 | Tile tile = tileView->tileKey().tile; 64 | pyramid->tileEnvelope(tile, env); 65 | Coord2D point = env.getCenter(); 66 | Tile elevationTile = elevation->getTileAt(point.x, point.y, tile.z-2); 67 | tileView->elevationTile(elevationTile); 68 | tiles.insert(elevationTile); 69 | } 70 | ++itr0; 71 | } 72 | auto itr = curSearchSet.begin(); 73 | while (itr != curSearchSet.end()) { 74 | TileDataPtr tileData = itr->second; 75 | if (!tileData->isReady()) { 76 | TileData* tileView = dynamic_cast(tileData.get()); 77 | Envelope env; 78 | Tile tile = tileView->tileKey().tile; 79 | pyramid->tileEnvelope(tile, env); 80 | Coord2D point = env.getCenter(); 81 | Tile elevationTile = elevation->getTileAt(point.x, point.y, tile.z-2); 82 | tileView->elevationTile(elevationTile); 83 | tiles.insert(elevationTile); 84 | } 85 | ++itr; 86 | } 87 | 88 | if (tiles.size() > 0) { 89 | bool ok = false; 90 | if (evlevationQuery.get()) { 91 | ok = true; 92 | for (auto tile : tiles) { 93 | if (!evlevationQuery->tiles.contains(tile)) { 94 | ok = false; 95 | break; 96 | } 97 | } 98 | } 99 | if (!ok) { 100 | auto query = elevation->fromTiles(tiles); 101 | evlevationQuery = query; 102 | } 103 | } 104 | else { 105 | evlevationQuery.clear(); 106 | } 107 | } 108 | } 109 | 110 | void XyzTileManager::setCachePath(const std::string& path) 111 | { 112 | cachePath = path; 113 | } 114 | 115 | TileDataPtr XyzTileManager::getRoot() { 116 | return root; 117 | } 118 | TileDataPtr XyzTileManager::makeTileData(TileKey key) { 119 | TileData* tileData = new TileData(key.tile, pyramid); 120 | if (elevation.get()) { 121 | Envelope env; 122 | pyramid->tileEnvelope(key.tile, env); 123 | Coord2D center = env.getCenter(); 124 | double height = OfflineElevation::cur()->getHeightMercator(center.x, center.y, 0); 125 | tileData->_approximateHeight = height; 126 | } 127 | return TileDataPtr(tileData); 128 | } 129 | 130 | void XyzTileManager::getChildren(TileDataPtr &tileData, std::vector &children) { 131 | TileData* tileView = dynamic_cast(tileData.get()); 132 | //deep into the sub tile 133 | Tile sub[4]; 134 | tileView->tile().subTile(sub); 135 | for (int i=0; i<4; ++i) { 136 | TileKey key{}; 137 | key.tile = sub[i]; 138 | children.push_back(key); 139 | } 140 | } 141 | 142 | bool XyzTileManager::isFitLod(TileDataPtr &tileData, Camera &camera, Rectangle &viewport, Matrix& modelMatrix) { 143 | TileData* tileView = dynamic_cast(tileData.get()); 144 | 145 | int zoom = tileView->tile().z; 146 | 147 | if (zoom < minLevel) { 148 | return false; 149 | } else if (zoom >= maxLevel) { 150 | return true; 151 | } 152 | 153 | float viewScale = tileView->computeViewScale(camera, viewport); 154 | if (viewScale < this->viewScale) { 155 | return true; 156 | } 157 | return false; 158 | } 159 | 160 | bool XyzTileManager::getUri(TileKey key, std::string &uri, std::string &file) { 161 | Tile tile = key.tile; 162 | uri = this->uri; 163 | 164 | mgp::StringUtil::replace(uri, "{random}", std::to_string(rand() % 3)); 165 | 166 | if (mgp::StringUtil::contains(uri, "bdimg")) { 167 | int halfSize = (int)pow(2.0, tile.z - 1); 168 | int x = -halfSize + tile.x; 169 | mgp::StringUtil::replace(uri, "{x}", std::to_string(x)); 170 | int y = halfSize - 1 - tile.y; 171 | mgp::StringUtil::replace(uri, "{y}", std::to_string(y)); 172 | mgp::StringUtil::replace(uri, "{z}", std::to_string(tile.z)); 173 | } 174 | else if (mgp::StringUtil::contains(uri, "{q}")) { 175 | std::string quadKey; 176 | tile.toQuadKey(quadKey); 177 | mgp::StringUtil::replace(uri, "{q}", quadKey); 178 | } 179 | else { 180 | mgp::StringUtil::replace(uri, "{x}", std::to_string(tile.x)); 181 | mgp::StringUtil::replace(uri, "{y}", std::to_string(tile.y)); 182 | mgp::StringUtil::replace(uri, "{z}", std::to_string(tile.z)); 183 | 184 | //TMS 185 | if (mgp::StringUtil::contains(uri, "{-y}")) { 186 | int y = (int)pow(2.0, tile.z) - 1 - tile.y; 187 | mgp::StringUtil::replace(uri, "{-y}", std::to_string(y)); 188 | } 189 | } 190 | 191 | char buffer[256]; 192 | // snprintf(buffer, 256, "%s/%d/%d", cachePath.c_str(), tile.z, tile.x); 193 | // if (!FileSystem::fileExists(buffer)) { 194 | // FileSystem::mkdirs(buffer); 195 | // } 196 | snprintf(buffer, 256, "%s/%d/%d/%d.png", cachePath.c_str(), tile.z, tile.x, tile.y); 197 | file = buffer; 198 | return true; 199 | } 200 | 201 | void* XyzTileManager::decode(HttpClient* task, NetResponse &res) { 202 | //TileKey* tile = static_cast(res.id); 203 | Image* image = Image::createFromBuf(res.result.data(), res.result.size(), false).take(); 204 | if (!image) return NULL; 205 | 206 | if (elevation.get()) { 207 | return image; 208 | } 209 | else { 210 | TileKey* tile = static_cast((void*)res.id); 211 | TileGeom *geometry = new TileGeom(pyramid); 212 | geometry->init(image, tile->tile, NULL, NULL); 213 | SAFE_RELEASE(image); 214 | return geometry; 215 | } 216 | //return image; 217 | } 218 | 219 | void XyzTileManager::onReceive(HttpClient* task, NetResponse &res) { 220 | TileKey* tile = static_cast((void*)res.id); 221 | 222 | TileDataPtr val; 223 | TileData* data = dynamic_cast(cache._get(*tile, val).get()); 224 | if (data && res.decodeResult) { 225 | if (elevation.get()) { 226 | data->image = UPtr((Image*)res.decodeResult); 227 | } 228 | else { 229 | data->geometry = UPtr((TileGeom*)res.decodeResult); 230 | } 231 | } 232 | else if (res.decodeResult) { 233 | if (elevation.get()) { 234 | Image* image = (Image*)res.decodeResult; 235 | SAFE_RELEASE(image); 236 | } 237 | else { 238 | TileGeom* image = (TileGeom*)res.decodeResult; 239 | SAFE_RELEASE(image); 240 | } 241 | } 242 | 243 | onTaskDone(*tile); 244 | } 245 | 246 | bool XyzTileManager::resultChanged() { 247 | if (resultDirty) return true; 248 | if (evlevationQuery.get()) { 249 | return evlevationQuery->resultDirty; 250 | } 251 | return false; 252 | } 253 | 254 | struct MakeDataTask : public Task { 255 | TileDataPtr tileData; 256 | SPtr evlevationQuery; 257 | SPtr manager; 258 | 259 | virtual void run() { 260 | TileData* tileView = dynamic_cast(tileData.get()); 261 | //check again 262 | if (tileView->image.get() && !tileView->geometry.get()) { 263 | TileGeom* geometry = new TileGeom(manager->pyramid); 264 | geometry->init(tileView->image.get(), tileView->tile(), &tileView->elevationTile(), evlevationQuery.get()); 265 | tileView->geometry = UPtr(geometry); 266 | manager->setResultDirty(); 267 | } 268 | } 269 | }; 270 | 271 | void XyzTileManager::tryInit(TileDataPtr& tileData, bool isOverview) { 272 | TileData* tileView = dynamic_cast(tileData.get()); 273 | if (tileView->getState() == 1 && elevation.get()) { 274 | if (evlevationQuery.get()) { 275 | SPtr elevationTileData; 276 | elevationTileData = evlevationQuery->tiles.get(tileView->elevationTile(), elevationTileData); 277 | if (elevationTileData.get() && elevationTileData->getState() == 1) { 278 | SPtr task(new MakeDataTask()); 279 | task->tileData = tileData; 280 | task->evlevationQuery = evlevationQuery; 281 | task->manager = this; 282 | #ifdef MGP_THREAD 283 | threadPool->addTask(task); 284 | #else 285 | task->run(); 286 | task->done(); 287 | #endif 288 | } 289 | } 290 | } 291 | } 292 | 293 | TileDataPtr XyzTileManager::getParent(TileDataPtr t) { 294 | if (t->tileKey().tile.z <= this->minLevel) { 295 | return TileDataPtr(); 296 | } 297 | return TileManager::getParent(t); 298 | } 299 | 300 | -------------------------------------------------------------------------------- /src/feElevation/ElevationManager.cpp: -------------------------------------------------------------------------------- 1 | #include "ElevationManager.h" 2 | #include "mgp.h" 3 | //#include "cppfan/Profiler.h" 4 | #include "feTile/TileData.h" 5 | #include "feTile/TileGeom.hpp" 6 | #include "feModel/PyramidGrid.h" 7 | #include "feModel/GeoCoordSys.h" 8 | 9 | FE_USING_NAMESPACE 10 | PF_USING_NAMESPACE 11 | 12 | ElevationQuery::ElevationQuery(): tiles(100) 13 | { 14 | } 15 | 16 | ElevationQuery::~ElevationQuery() 17 | { 18 | auto mgr = manager.lock(); 19 | if (mgr.get()) mgr->cancel(this); 20 | } 21 | 22 | double ElevationQuery::getHeight(double longitude, double latitude, int level) 23 | { 24 | Coord2D coord(longitude, latitude); 25 | GeoCoordSys::earth()->toMercator(&coord); 26 | return getHeightMercator(coord.x, coord.y, level); 27 | } 28 | 29 | double ElevationQuery::getHeightMercator(double x, double y, int level) { 30 | auto itr = tiles.begin(); 31 | while (itr != tiles.end()) { 32 | TileData* tileData = itr->second.get(); 33 | 34 | double height = getTileHeight(tileData, x, y, NAN, level); 35 | if (!isnan(height)) { 36 | return height; 37 | } 38 | 39 | ++itr; 40 | } 41 | return 0.0; 42 | } 43 | 44 | double ElevationQuery::getTileHeight(Tile tile, double x, double y, double defVal, int level) { 45 | auto it = tiles.find(tile); 46 | if (it == tiles.end()) { 47 | return defVal; 48 | } 49 | double height = getTileHeight(it->second.get(), x, y, defVal, level); 50 | return height; 51 | } 52 | 53 | static double imageSampleOne(uint8_t* data, int w, int h, int x, int y, int ppb) { 54 | if (x >= w) x = w - 1; 55 | if (y >= h) y = h - 1; 56 | if (x < 0) x = 0; 57 | if (y < 0) y = 0; 58 | 59 | uint8_t* pos = data + ((w * y + x) * ppb); 60 | int R = pos[0]; 61 | int G = pos[1]; 62 | int B = pos[2]; 63 | if (R == 6) { 64 | R = 1; 65 | } 66 | double height = -10000 + ((R * 256 * 256 + G * 256 + B) * 0.1); 67 | return height; 68 | } 69 | 70 | static double imageSample(Image* image, double u, double v, int level) { 71 | int w = image->getWidth(); 72 | int h = image->getHeight(); 73 | uint8_t* data = image->getData(); 74 | int ppb = image->getFormat() == Image::RGB ? 3 : 4; 75 | 76 | double x = u * w; 77 | double y = v * h; 78 | int ix = (int)round(x); 79 | int iy = (int)round(y); 80 | 81 | #if 0 82 | double value = imageSampleOne(data, w, h, ix, iy, ppb); 83 | return value; 84 | #else 85 | int limitLevel = 11; 86 | if (level < limitLevel) { 87 | double value = imageSampleOne(data, w, h, ix, iy, ppb); 88 | return value; 89 | } 90 | double sumValue = 0; 91 | double sumWeight = 0; 92 | for (int i = -1; i < 2; ++i) { 93 | for (int j = -1; j < 2; ++j) { 94 | int sx = ix + i; 95 | int sy = iy + j; 96 | double dx = (sx - x); 97 | double dy = (sy - y); 98 | double disSq = dx * dx + dy * dy; 99 | if (disSq >= 1) continue; 100 | 101 | double value = imageSampleOne(data, w, h, sx, sy, ppb); 102 | if (disSq < 0.00001) { 103 | return value; 104 | } 105 | double weight = (1 - disSq) / disSq; 106 | sumValue += value * weight; 107 | sumWeight += weight; 108 | } 109 | } 110 | return sumValue / sumWeight; 111 | #endif 112 | } 113 | 114 | double ElevationQuery::getHeightUV(TileData* tileData, double u, double v, double defVal, int level) { 115 | if (tileData->image.get()) { 116 | 117 | double height = imageSample(tileData->image.get(), u, v, level); 118 | /*if (height > 10000) { 119 | printf("DEBUG\n"); 120 | }*/ 121 | 122 | auto mgr = manager.lock(); 123 | if (mgr.get()) { 124 | height *= mgr->elevationScale; 125 | } 126 | return height; 127 | } 128 | else { 129 | return defVal; 130 | } 131 | } 132 | 133 | double ElevationQuery::getTileHeight(TileData* tileData, double x, double y, double defVal, int level) { 134 | Envelope env = tileData->envelopeMercator(); 135 | //if (env.containsPoint(x, y)) { 136 | double u = (x - env.minX()) / env.width(); 137 | double v = (y - env.minY()) / env.height(); 138 | if (u < 0) { 139 | u = 0; 140 | } 141 | else if (u > 1) { 142 | u = 1; 143 | } 144 | if (v < 0) { 145 | v = 0; 146 | } 147 | else if (v > 1) { 148 | v = 1; 149 | } 150 | 151 | return getHeightUV(tileData, u, 1-v, defVal, level); 152 | //} 153 | return defVal; 154 | } 155 | 156 | //void ElevationQuery::onReceive(SPtr tileData) { 157 | // 158 | //} 159 | 160 | bool ElevationQuery::isLoaded() { 161 | for (auto it = tiles.begin(); it != tiles.end(); ++it) { 162 | if (it->second->getState() == 0) { 163 | return false; 164 | } 165 | } 166 | return true; 167 | } 168 | 169 | /////////////////////////////////////////////////////////////////////////////////////////////////// 170 | /////////////////////////////////////////////////////////////////////////////////////////////////// 171 | 172 | ElevationManager::ElevationManager(const std::string& uri): uri(uri), cache(300), sendedTask(200), elevationScale(1.0) 173 | { 174 | maxLevel = 15; 175 | minLevel = 0; 176 | pyramid = PyramidGrid::getDefault(); 177 | 178 | //Tile tile; 179 | //SPtr data(new TileData(tile, pyramid)); 180 | //approximateElevation.tiles.set(tile, data); 181 | //data->image = UPtr(Image::create("res/fastEarth/0.png", false)); 182 | //TileKey key; 183 | //key.tile = tile; 184 | //cache.set(key, data.dynamicCastTo()); 185 | //approximateElevation.manager = this; 186 | } 187 | 188 | ElevationManager::~ElevationManager() 189 | { 190 | auto it = sendedTask.begin(); 191 | while (it != sendedTask.end()) { 192 | it->second->cancel(); 193 | //it->second->release(); 194 | ++it; 195 | } 196 | sendedTask.clear(); 197 | 198 | //approximateElevation._setRefCount(0); 199 | } 200 | 201 | SPtr ElevationManager::fromTiles(std::set& tiles) { 202 | SPtr query(new ElevationQuery()); 203 | 204 | for (auto it = tiles.begin(); it != tiles.end(); ++it) { 205 | Tile tile = *it; 206 | TileKey key; 207 | key.tile = tile; 208 | TileDataPtr data; 209 | if (cache.contains(key)) { 210 | data = cache.get(key); 211 | auto tileData = data.dynamicCastTo(); 212 | query->tiles.set(tile, tileData); 213 | } 214 | else { 215 | data = TileDataPtr(new TileData(tile, pyramid)); 216 | auto tileData = data.dynamicCastTo(); 217 | query->tiles.set(tile, tileData); 218 | cache.set(key, data); 219 | } 220 | 221 | if (data->getState() == 0) { 222 | if (!sendedTask.contains(tile)) { 223 | auto task = load(tile); 224 | task->queryCount = 1; 225 | if (task.getPtr()) { 226 | sendedTask.set(tile, task); 227 | } 228 | } 229 | else { 230 | sric::SharedPtr empty; 231 | sendedTask.get(tile, empty)->queryCount++; 232 | } 233 | } 234 | } 235 | 236 | query->manager = this; 237 | querys.push_back(query.get()); 238 | return query; 239 | } 240 | 241 | Tile ElevationManager::getTileAt(double longitude, double latitude, int level) { 242 | if (level > maxLevel) { 243 | level = maxLevel; 244 | } 245 | if (level < minLevel) { 246 | level = minLevel; 247 | } 248 | 249 | Tile tile = pyramid->getTileAt(longitude, latitude, level); 250 | return tile; 251 | } 252 | 253 | Tile ElevationManager::getTileAtBL(double longitude, double latitude, int level) { 254 | Coord2D coord(longitude, latitude); 255 | GeoCoordSys::earth()->toMercator(&coord); 256 | return getTileAt(coord.x, coord.y, level); 257 | } 258 | 259 | sric::SharedPtr ElevationManager::load(Tile tile) { 260 | 261 | std::string url; 262 | std::string file; 263 | TileKey key; 264 | key.tile = tile; 265 | if (!getUri(key, url, file)) { 266 | return sric::SharedPtr(); 267 | } 268 | 269 | auto client = sric::makePtr(); 270 | client->tileKey = tile; 271 | client->useCache = true; 272 | client->url = url; 273 | client->cacheFile = file; 274 | client->id = (uint64_t) & client->tileKey; 275 | client->listener = this; 276 | client->send(); 277 | 278 | return (client); 279 | } 280 | 281 | void ElevationManager::cancel(ElevationQuery* query) { 282 | for (auto it = query->tiles.begin(); it != query->tiles.end(); ++it) { 283 | sric::SharedPtr empty; 284 | auto req = sendedTask.get(it->first, empty); 285 | if (req.getPtr()) { 286 | req->queryCount--; 287 | if (req->queryCount == 0) { 288 | req->cancel(); 289 | sendedTask.remove(it->first); 290 | } 291 | } 292 | } 293 | for (auto it = querys.begin(); it != querys.end(); ++it) { 294 | if (*it == query) { 295 | querys.erase(it); 296 | break; 297 | } 298 | } 299 | } 300 | 301 | bool ElevationManager::getUri(TileKey key, std::string& uri, std::string& file) { 302 | Tile tile = key.tile; 303 | uri = this->uri; 304 | mgp::StringUtil::replace(uri, "{x}", std::to_string(tile.x)); 305 | mgp::StringUtil::replace(uri, "{y}", std::to_string(tile.y)); 306 | mgp::StringUtil::replace(uri, "{z}", std::to_string(tile.z)); 307 | mgp::StringUtil::replace(uri, "{random}", std::to_string(rand() % 3)); 308 | 309 | //TMS 310 | if (mgp::StringUtil::contains(uri, "{-y}")) { 311 | int y = (int)pow(2.0, tile.z) - 1 - tile.y; 312 | mgp::StringUtil::replace(uri, "{-y}", std::to_string(y)); 313 | } 314 | 315 | char buffer[256]; 316 | // snprintf(buffer, 256, "%s/%d/%d", cachePath.c_str(), tile.z, tile.x); 317 | // if (!FileSystem::fileExists(buffer)) { 318 | // FileSystem::mkdirs(buffer); 319 | // } 320 | snprintf(buffer, 256, "%s/%d/%d/%d.png", cachePath.c_str(), tile.z, tile.x, tile.y); 321 | file = buffer; 322 | return true; 323 | } 324 | 325 | void ElevationManager::setCachePath(const std::string& path) 326 | { 327 | cachePath = path; 328 | } 329 | 330 | void* ElevationManager::decode(HttpClient* task, NetResponse& res) 331 | { 332 | //TileKey* tile = static_cast(res.id); 333 | Image* image = Image::createFromBuf(res.result.data(), res.result.size(), false).take(); 334 | if (!image) { 335 | image = Image::create(256, 256, Image::Format::RGBA).take(); 336 | } 337 | return image; 338 | } 339 | 340 | void ElevationManager::onReceive(HttpClient* client, NetResponse& res) 341 | { 342 | TileKey* tile = static_cast((void*)res.id); 343 | 344 | TileDataPtr val; 345 | TileData* data = dynamic_cast(cache._get(*tile, val).get()); 346 | if (data && res.decodeResult) { 347 | data->image = UPtr((Image*)res.decodeResult); 348 | } 349 | 350 | sric::SharedPtr task; 351 | task = sendedTask.get(tile->tile, task); 352 | if (task.getPtr() == nullptr || task->isCanceled()) { 353 | return; 354 | } 355 | sendedTask.remove(tile->tile); 356 | 357 | for (auto it = querys.begin(); it != querys.end(); ++it) { 358 | ElevationQuery* query = *it; 359 | if (query->tiles.contains(tile->tile)) { 360 | query->resultDirty = true; 361 | } 362 | } 363 | } --------------------------------------------------------------------------------