多种数据源更新
This commit is contained in:
+1
-1
@@ -6,7 +6,7 @@ if (${CMAKE_SOURCE_DIR} STREQUAL ${CMAKE_CURRENT_LIST_DIR})
|
||||
set(test_maplibre_VERSION 1.0.0)
|
||||
set(CMAKE_CXX_STANDARD 20)
|
||||
include(third_party/build_infra/start.cmake)
|
||||
include(third_party/CPP_Core/main.cmake)
|
||||
|
||||
|
||||
set(test_maplibre_rely QMapLibre_qt_5_15_2)
|
||||
library_is_installed_with_rely(global ok ${test_maplibre_rely})
|
||||
|
||||
+8
-2
@@ -7,10 +7,11 @@ find_package(Qt5 REQUIRED COMPONENTS Widgets)
|
||||
find_package(QMapLibre COMPONENTS Widgets REQUIRED)
|
||||
|
||||
|
||||
file(GLOB_RECURSE srcs ${dir}/*.c ${dir}/*.h ${dir}/*.cpp ${dir}/*.hpp)
|
||||
file(GLOB_RECURSE srcs ${dir}/*.qrc ${dir}/*.c ${dir}/*.h ${dir}/*.cpp ${dir}/*.hpp)
|
||||
add_executable(test_maplibre ${srcs})
|
||||
target_link_libraries(test_maplibre PRIVATE Qt5::Widgets QMapLibre::Widgets)
|
||||
|
||||
|
||||
target_compile_definitions(test_maplibre PRIVATE QT_MAPLIBRE_STATIC)
|
||||
|
||||
|
||||
@@ -20,4 +21,9 @@ if(WIN32)
|
||||
endif()
|
||||
if(MSVC)
|
||||
target_compile_options(test_maplibre PRIVATE /utf-8)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
|
||||
set_property(TARGET test_maplibre PROPERTY AUTOMOC ON)
|
||||
set_property(TARGET test_maplibre PROPERTY AUTOUIC ON)
|
||||
set_property(TARGET test_maplibre PROPERTY AUTORCC ON)
|
||||
+249
-15
@@ -1,32 +1,266 @@
|
||||
#include "httplib.h"
|
||||
#include <QApplication>
|
||||
#include <QDebug>
|
||||
#include <QFileInfo>
|
||||
#include <QByteArray>
|
||||
#include <QComboBox>
|
||||
#include <QFileDialog>
|
||||
#include <QJsonArray>
|
||||
#include <QJsonDocument>
|
||||
#include <QJsonObject>
|
||||
#include <QLineEdit>
|
||||
#include <QMainWindow>
|
||||
#include <QMapLibre/Map>
|
||||
#include <QMapLibre/Settings>
|
||||
#include <QMapLibreWidgets/GLWidget>
|
||||
#include <QPushButton>
|
||||
#include <QSettings>
|
||||
#include <QToolBar>
|
||||
#include <QString>
|
||||
#include <filesystem>
|
||||
#include <fstream>
|
||||
#include <mutex>
|
||||
#include <string>
|
||||
#include <thread>
|
||||
#include <vector>
|
||||
struct MapSource {
|
||||
const char *id;
|
||||
const char *name;
|
||||
const char *tileUrl;
|
||||
const char *overlayUrl;
|
||||
int minZoom;
|
||||
int maxZoom;
|
||||
};
|
||||
const MapSource *find_source(const std::vector<MapSource>& sources, const std::string& id) {
|
||||
for (const auto& source : sources) {
|
||||
if (id == source.id) {
|
||||
return &source;
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
std::string replace_all(std::string value, const std::string& from, const std::string& to) {
|
||||
std::size_t pos = 0;
|
||||
while ((pos = value.find(from, pos)) != std::string::npos) {
|
||||
value.replace(pos, from.size(), to);
|
||||
pos += to.size();
|
||||
}
|
||||
return value;
|
||||
}
|
||||
std::string get_content_type(const std::string& path) {
|
||||
std::string ext = std::filesystem::path(path).extension().string();
|
||||
if (ext == ".png") {
|
||||
return "image/png";
|
||||
}
|
||||
if (ext == ".jpg" || ext == ".jpeg") {
|
||||
return "image/jpeg";
|
||||
}
|
||||
if (ext == ".webp") {
|
||||
return "image/webp";
|
||||
}
|
||||
return "application/octet-stream";
|
||||
}
|
||||
std::string read_binary_file(const std::filesystem::path& path) {
|
||||
std::ifstream file(path, std::ios::binary);
|
||||
return std::string(std::istreambuf_iterator<char>(file), std::istreambuf_iterator<char>());
|
||||
}
|
||||
QJsonObject make_raster_source(const std::string& url, const MapSource& source) {
|
||||
QJsonObject rasterSource;
|
||||
QJsonArray tiles;
|
||||
tiles.append(QString::fromUtf8(url.c_str()));
|
||||
rasterSource["type"] = "raster";
|
||||
rasterSource["tiles"] = tiles;
|
||||
rasterSource["tileSize"] = 256;
|
||||
rasterSource["minzoom"] = source.minZoom;
|
||||
rasterSource["maxzoom"] = source.maxZoom;
|
||||
rasterSource["scheme"] = "xyz";
|
||||
return rasterSource;
|
||||
}
|
||||
QJsonObject make_raster_layer(const char *id, const char *source) {
|
||||
QJsonObject rasterPaint;
|
||||
rasterPaint["raster-opacity"] = 1.0;
|
||||
QJsonObject rasterLayer;
|
||||
rasterLayer["id"] = QString::fromUtf8(id);
|
||||
rasterLayer["type"] = "raster";
|
||||
rasterLayer["source"] = QString::fromUtf8(source);
|
||||
rasterLayer["paint"] = rasterPaint;
|
||||
return rasterLayer;
|
||||
}
|
||||
QByteArray make_raster_style_json(const MapSource& source, const std::string& tiandituKey) {
|
||||
QJsonObject sources;
|
||||
std::string tileUrl = replace_all(source.tileUrl, "{tk}", tiandituKey);
|
||||
sources["base"] = make_raster_source(tileUrl, source);
|
||||
QJsonObject backgroundPaint;
|
||||
backgroundPaint["background-color"] = "#000000";
|
||||
QJsonObject backgroundLayer;
|
||||
backgroundLayer["id"] = "background";
|
||||
backgroundLayer["type"] = "background";
|
||||
backgroundLayer["paint"] = backgroundPaint;
|
||||
QJsonArray layers;
|
||||
layers.append(backgroundLayer);
|
||||
layers.append(make_raster_layer("base", "base"));
|
||||
if (source.overlayUrl && source.overlayUrl[0] != '\0') {
|
||||
std::string overlayUrl = replace_all(source.overlayUrl, "{tk}", tiandituKey);
|
||||
sources["overlay"] = make_raster_source(overlayUrl, source);
|
||||
layers.append(make_raster_layer("overlay", "overlay"));
|
||||
}
|
||||
QJsonObject root;
|
||||
root["version"] = 8;
|
||||
root["name"] = QString::fromUtf8(source.name);
|
||||
root["sources"] = sources;
|
||||
root["layers"] = layers;
|
||||
return QJsonDocument(root).toJson(QJsonDocument::Compact);
|
||||
}
|
||||
void register_tile_server(httplib::Server& server, std::string& localTileDir, std::mutex& localTileDirMutex) {
|
||||
server.Get(R"(/tiles/(.+))", [&localTileDir, &localTileDirMutex](const httplib::Request& req, httplib::Response& res) {
|
||||
std::string root;
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(localTileDirMutex);
|
||||
root = localTileDir;
|
||||
}
|
||||
std::string relative = req.matches[1].str();
|
||||
std::filesystem::path path = std::filesystem::path(root) / std::filesystem::path(relative);
|
||||
if (!std::filesystem::exists(path) || !std::filesystem::is_regular_file(path)) {
|
||||
res.status = 404;
|
||||
res.set_content("tile not found", "text/plain");
|
||||
return;
|
||||
}
|
||||
std::string body = read_binary_file(path);
|
||||
std::string contentType = get_content_type(path.string());
|
||||
res.set_content(body, contentType.c_str());
|
||||
});
|
||||
}
|
||||
int main(int argc, char *argv[]) {
|
||||
QApplication app(argc, argv);
|
||||
QString stylePath = "C:/Users/wyc/CLionProjects/test_maplibre/src/style.json";
|
||||
QFileInfo styleInfo(stylePath);
|
||||
qDebug() << "style exists:" << styleInfo.exists();
|
||||
qDebug() << "style path:" << styleInfo.absoluteFilePath();
|
||||
QSettings appSettings("wyc", "test_maplibre");
|
||||
std::string tiandituKey = appSettings.value("tianditu/key", "").toString().trimmed().toStdString();
|
||||
std::string localTileDir = appSettings.value("local/tile_dir", "D:/tiles").toString().trimmed().toStdString();
|
||||
std::mutex tiandituKeyMutex;
|
||||
std::mutex localTileDirMutex;
|
||||
std::vector<MapSource> sources = {
|
||||
{"local_png", "本地瓦片 PNG", "http://127.0.0.1:18080/tiles/{z}/{x}/{y}.png", nullptr, 0, 20},
|
||||
{"local_jpg", "本地瓦片 JPG", "http://127.0.0.1:18080/tiles/{z}/{x}/{y}.jpg", nullptr, 0, 20},
|
||||
{"osm", "OSM 标准地图", "https://tile.openstreetmap.org/{z}/{x}/{y}.png", nullptr, 0, 19},
|
||||
{"google_m", "谷歌街道 m", "https://mt0.google.com/vt/lyrs=m&x={x}&y={y}&z={z}", nullptr, 0, 20},
|
||||
{"google_p", "谷歌街道 p", "https://mt0.google.com/vt/lyrs=p&x={x}&y={y}&z={z}", nullptr, 0, 20},
|
||||
{"google_r", "谷歌街道 r", "https://mt0.google.com/vt/lyrs=r&x={x}&y={y}&z={z}", nullptr, 0, 20},
|
||||
{"google_t", "谷歌地形 t", "https://mt0.google.com/vt/lyrs=t&x={x}&y={y}&z={z}", nullptr, 0, 20},
|
||||
{"google_s", "谷歌影像 s", "https://mt0.google.com/vt/lyrs=s&x={x}&y={y}&z={z}", nullptr, 0, 20},
|
||||
{"google_y", "谷歌影像含标注 y", "https://mt0.google.com/vt/lyrs=y&x={x}&y={y}&z={z}", nullptr, 0, 20},
|
||||
{"google_h", "谷歌透明标注 h", "https://mt0.google.com/vt/lyrs=h&x={x}&y={y}&z={z}", nullptr, 0, 20},
|
||||
{"google_cn_s", "谷歌中国影像 s", "https://mt0.google.com/vt/lyrs=s&gl=CN&x={x}&y={y}&z={z}", nullptr, 0, 20},
|
||||
{"google_cn_h", "谷歌中国透明标注 h", "https://mt0.google.com/vt/lyrs=h&gl=CN&x={x}&y={y}&z={z}", nullptr, 0, 20},
|
||||
{"google_cn_y", "谷歌中国影像含标注 y", "https://mt0.google.com/vt/lyrs=y&gl=CN&x={x}&y={y}&z={z}", nullptr, 0, 20},
|
||||
{"amap_road", "高德地图", "https://webrd01.is.autonavi.com/appmaptile?lang=zh_cn&size=1&scale=1&style=8&x={x}&y={y}&z={z}", nullptr, 0, 20},
|
||||
{"amap_sat", "高德卫星", "https://webst01.is.autonavi.com/appmaptile?style=6&x={x}&y={y}&z={z}", nullptr, 0, 20},
|
||||
{"amap_sat_label", "高德卫星标注", "https://webst01.is.autonavi.com/appmaptile?style=8&x={x}&y={y}&z={z}", nullptr, 0, 20},
|
||||
{"tdt_img", "天地图影像", "https://t0.tianditu.gov.cn/DataServer?T=img_w&x={x}&y={y}&l={z}&tk={tk}", nullptr, 0, 18},
|
||||
{"tdt_cia", "天地图影像标注", "https://t0.tianditu.gov.cn/DataServer?T=cia_w&x={x}&y={y}&l={z}&tk={tk}", nullptr, 0, 18},
|
||||
{"tdt_img_cia", "天地图影像+标注", "https://t0.tianditu.gov.cn/DataServer?T=img_w&x={x}&y={y}&l={z}&tk={tk}", "https://t0.tianditu.gov.cn/DataServer?T=cia_w&x={x}&y={y}&l={z}&tk={tk}", 0, 18},
|
||||
{"tdt_vec", "天地图矢量", "https://t0.tianditu.gov.cn/DataServer?T=vec_w&x={x}&y={y}&l={z}&tk={tk}", nullptr, 0, 18},
|
||||
{"tdt_cva", "天地图矢量标注", "https://t0.tianditu.gov.cn/DataServer?T=cva_w&x={x}&y={y}&l={z}&tk={tk}", nullptr, 0, 18},
|
||||
{"tdt_vec_cva", "天地图矢量+标注", "https://t0.tianditu.gov.cn/DataServer?T=vec_w&x={x}&y={y}&l={z}&tk={tk}", "https://t0.tianditu.gov.cn/DataServer?T=cva_w&x={x}&y={y}&l={z}&tk={tk}", 0, 18}
|
||||
};
|
||||
httplib::Server server;
|
||||
server.set_mount_point("/", "C:/Users/wyc/CLionProjects/test_maplibre/src");
|
||||
server.set_mount_point("/tiles", "D:/tiles");
|
||||
server.Get(R"(/style/([A-Za-z0-9_]+)\.json)", [&sources, &tiandituKey, &tiandituKeyMutex](const httplib::Request& req, httplib::Response& res) {
|
||||
const MapSource *source = find_source(sources, req.matches[1].str());
|
||||
if (!source) {
|
||||
res.status = 404;
|
||||
res.set_content("style not found", "text/plain");
|
||||
return;
|
||||
}
|
||||
std::string key;
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(tiandituKeyMutex);
|
||||
key = tiandituKey;
|
||||
}
|
||||
QByteArray styleJson = make_raster_style_json(*source, key);
|
||||
res.set_content(styleJson.constData(), styleJson.size(), "application/json");
|
||||
});
|
||||
register_tile_server(server, localTileDir, localTileDirMutex);
|
||||
std::thread serverThread([&server]() {
|
||||
server.listen("127.0.0.1", 18080);
|
||||
});
|
||||
server.wait_until_ready();
|
||||
QMapLibre::Settings settings;
|
||||
QMapLibre::GLWidget widget(settings);
|
||||
widget.resize(1200, 800);
|
||||
widget.setWindowTitle("MapLibre Qt Widgets Demo");
|
||||
widget.show();
|
||||
//widget.map()->setStyleUrl("https://demotiles.maplibre.org/style.json");
|
||||
widget.map()->setStyleUrl("http://127.0.0.1:18080/style.json");
|
||||
widget.map()->setCoordinateZoom(QMapLibre::Coordinate(35.0, 105.0), 4.0);
|
||||
QMainWindow window;
|
||||
QMapLibre::GLWidget *mapWidget = new QMapLibre::GLWidget(settings);
|
||||
QToolBar *toolbar = window.addToolBar("Map Source");
|
||||
QComboBox *sourceBox = new QComboBox(&window);
|
||||
QLineEdit *tiandituKeyEdit = new QLineEdit(&window);
|
||||
QPushButton *applyTiandituKeyButton = new QPushButton("保存天地图Key", &window);
|
||||
QLineEdit *localTileDirEdit = new QLineEdit(&window);
|
||||
QPushButton *selectLocalTileDirButton = new QPushButton("选择瓦片目录", &window);
|
||||
QPushButton *applyLocalTileDirButton = new QPushButton("保存瓦片目录", &window);
|
||||
int styleVersion = 0;
|
||||
for (const auto& source : sources) {
|
||||
sourceBox->addItem(QString::fromUtf8(source.name), QString::fromUtf8(source.id));
|
||||
}
|
||||
tiandituKeyEdit->setText(QString::fromStdString(tiandituKey));
|
||||
tiandituKeyEdit->setPlaceholderText("输入天地图Key");
|
||||
tiandituKeyEdit->setMinimumWidth(320);
|
||||
localTileDirEdit->setText(QString::fromStdString(localTileDir));
|
||||
localTileDirEdit->setPlaceholderText("输入本地瓦片目录,例如 D:/tiles");
|
||||
localTileDirEdit->setMinimumWidth(360);
|
||||
toolbar->addWidget(sourceBox);
|
||||
toolbar->addWidget(tiandituKeyEdit);
|
||||
toolbar->addWidget(applyTiandituKeyButton);
|
||||
toolbar->addWidget(localTileDirEdit);
|
||||
toolbar->addWidget(selectLocalTileDirButton);
|
||||
toolbar->addWidget(applyLocalTileDirButton);
|
||||
window.setCentralWidget(mapWidget);
|
||||
window.resize(1400, 800);
|
||||
window.setWindowTitle("MapLibre Qt Widgets Demo");
|
||||
window.show();
|
||||
auto reloadCurrentSource = [&]() {
|
||||
QString id = sourceBox->currentData().toString();
|
||||
mapWidget->map()->setStyleUrl(QString("http://127.0.0.1:18080/style/%1.json?v=%2").arg(id).arg(++styleVersion));
|
||||
};
|
||||
auto saveTiandituKey = [&]() {
|
||||
QString value = tiandituKeyEdit->text().trimmed();
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(tiandituKeyMutex);
|
||||
tiandituKey = value.toStdString();
|
||||
}
|
||||
appSettings.setValue("tianditu/key", value);
|
||||
appSettings.sync();
|
||||
};
|
||||
auto saveLocalTileDir = [&]() {
|
||||
QString value = localTileDirEdit->text().trimmed();
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(localTileDirMutex);
|
||||
localTileDir = value.toStdString();
|
||||
}
|
||||
appSettings.setValue("local/tile_dir", value);
|
||||
appSettings.sync();
|
||||
};
|
||||
QObject::connect(sourceBox, QOverload<int>::of(&QComboBox::currentIndexChanged), [&](int) {
|
||||
reloadCurrentSource();
|
||||
});
|
||||
QObject::connect(applyTiandituKeyButton, &QPushButton::clicked, [&]() {
|
||||
saveTiandituKey();
|
||||
reloadCurrentSource();
|
||||
});
|
||||
QObject::connect(tiandituKeyEdit, &QLineEdit::returnPressed, [&]() {
|
||||
saveTiandituKey();
|
||||
reloadCurrentSource();
|
||||
});
|
||||
QObject::connect(selectLocalTileDirButton, &QPushButton::clicked, [&]() {
|
||||
QString dir = QFileDialog::getExistingDirectory(&window, "选择本地瓦片目录", localTileDirEdit->text().trimmed());
|
||||
if (!dir.isEmpty()) {
|
||||
localTileDirEdit->setText(dir);
|
||||
saveLocalTileDir();
|
||||
reloadCurrentSource();
|
||||
}
|
||||
});
|
||||
QObject::connect(applyLocalTileDirButton, &QPushButton::clicked, [&]() {
|
||||
saveLocalTileDir();
|
||||
reloadCurrentSource();
|
||||
});
|
||||
QObject::connect(localTileDirEdit, &QLineEdit::returnPressed, [&]() {
|
||||
saveLocalTileDir();
|
||||
reloadCurrentSource();
|
||||
});
|
||||
reloadCurrentSource();
|
||||
mapWidget->map()->setCoordinateZoom(QMapLibre::Coordinate(35.0, 105.0), 4.0);
|
||||
int ret = app.exec();
|
||||
server.stop();
|
||||
serverThread.join();
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
<RCC>
|
||||
<qresource prefix="/style">
|
||||
<file alias="style.json">../style/style.json</file>
|
||||
</qresource>
|
||||
</RCC>
|
||||
Reference in New Issue
Block a user