3D优化
This commit is contained in:
@@ -399,6 +399,135 @@ Psc::JSON make_map_view_response(Global* global) {
|
||||
ret.append({"current_imagery_key", global->map_resources_config.current_imagery_key});
|
||||
return ret;
|
||||
}
|
||||
bool is_multipart_request(const drogon::HttpRequestPtr& req) {
|
||||
auto content_type = req->getHeader("content-type");
|
||||
return content_type.find("multipart/form-data") != std::string::npos;
|
||||
}
|
||||
std::string normalize_upload_extension(std::string extension) {
|
||||
std::ranges::transform(extension, extension.begin(), [](unsigned char ch) {
|
||||
return static_cast<char>(std::tolower(ch));
|
||||
});
|
||||
if (!extension.empty() && extension.front() != '.') {
|
||||
extension.insert(extension.begin(), '.');
|
||||
}
|
||||
return extension;
|
||||
}
|
||||
std::filesystem::path web_model_directory(Global* global) {
|
||||
return std::filesystem::path(global->web_server_config.get_true_webapp()) / "model";
|
||||
}
|
||||
std::filesystem::path source_model_directory(Global* global) {
|
||||
return std::filesystem::path(global->web_server_config.get_true_webapp()).parent_path() / "model";
|
||||
}
|
||||
bool is_safe_aircraft_model_key(std::string_view key) {
|
||||
return !key.empty() && std::ranges::all_of(key, [](unsigned char ch) {
|
||||
return std::isalnum(ch) != 0 || ch == '_';
|
||||
});
|
||||
}
|
||||
std::string model_file_name(std::string_view model_type, std::string_view aircraft_model_key) {
|
||||
if (model_type == "aircraft") {
|
||||
return "aircraft-custom.glb";
|
||||
}
|
||||
if (model_type == "aircraft_type") {
|
||||
if (!is_safe_aircraft_model_key(aircraft_model_key)) {
|
||||
throw std::invalid_argument("invalid aircraft_model_key");
|
||||
}
|
||||
auto normalized_key = std::string(aircraft_model_key);
|
||||
std::ranges::replace(normalized_key, '_', '-');
|
||||
return "aircraft-" + normalized_key + ".glb";
|
||||
}
|
||||
if (model_type == "base_station") {
|
||||
return "base-station-custom.glb";
|
||||
}
|
||||
throw std::invalid_argument("invalid model_type");
|
||||
}
|
||||
void save_uploaded_model_file(const drogon::HttpFile& upload,
|
||||
std::string_view model_type,
|
||||
std::string_view aircraft_model_key,
|
||||
Global* global) {
|
||||
auto extension = normalize_upload_extension(std::string(upload.getFileExtension()));
|
||||
if (extension != ".glb") {
|
||||
throw std::invalid_argument("model file must be .glb");
|
||||
}
|
||||
if (model_type == "aircraft_type" && !global->map_model_config.aircraft_models.contains(std::string(aircraft_model_key))) {
|
||||
throw std::invalid_argument("unknown aircraft_model_key");
|
||||
}
|
||||
auto upload_dir = std::filesystem::path(drogon::app().getUploadPath());
|
||||
std::filesystem::create_directories(upload_dir);
|
||||
auto temp_name = std::string(model_type) + "_" + std::string(aircraft_model_key) + "_model_upload.tmp";
|
||||
if (upload.saveAs(temp_name) != 0) {
|
||||
throw std::runtime_error("cannot save uploaded model");
|
||||
}
|
||||
auto temp_path = upload_dir / temp_name;
|
||||
auto file_name = model_file_name(model_type, aircraft_model_key);
|
||||
auto web_dir = web_model_directory(global);
|
||||
auto source_dir = source_model_directory(global);
|
||||
std::filesystem::create_directories(web_dir);
|
||||
std::filesystem::create_directories(source_dir);
|
||||
std::filesystem::copy_file(temp_path, web_dir / file_name, std::filesystem::copy_options::overwrite_existing);
|
||||
std::filesystem::copy_file(temp_path, source_dir / file_name, std::filesystem::copy_options::overwrite_existing);
|
||||
std::filesystem::remove(temp_path);
|
||||
auto url = "/ui/model/" + file_name;
|
||||
if (model_type == "aircraft") {
|
||||
global->map_model_config.aircraft_model.url = url;
|
||||
}
|
||||
else if (model_type == "aircraft_type") {
|
||||
global->map_model_config.aircraft_models[std::string(aircraft_model_key)].url = url;
|
||||
}
|
||||
else {
|
||||
global->map_model_config.base_station_model.url = url;
|
||||
}
|
||||
}
|
||||
void handle_map_model_upload(const drogon::HttpRequestPtr& req, Global* global) {
|
||||
drogon::MultiPartParser parser;
|
||||
if (parser.parse(req) != 0) {
|
||||
throw std::invalid_argument("invalid multipart model upload");
|
||||
}
|
||||
auto model_type = parser.getOptionalParameter<std::string>("model_type");
|
||||
if (!model_type || parser.getFiles().size() != 1) {
|
||||
throw std::invalid_argument("model_type or file");
|
||||
}
|
||||
auto aircraft_model_key = parser.getOptionalParameter<std::string>("aircraft_model_key").value_or("");
|
||||
save_uploaded_model_file(parser.getFiles().front(), *model_type, aircraft_model_key, global);
|
||||
Global::save();
|
||||
}
|
||||
void handle_map_model_config_update(const drogon::HttpRequestPtr& req, Global* global) {
|
||||
auto parsed = Psc::try_parse_json(req->body().data());
|
||||
if (!parsed.has_value()) {
|
||||
throw std::invalid_argument("invalid map_models");
|
||||
}
|
||||
global->map_model_config.from_base_json(&parsed.value());
|
||||
Global::save();
|
||||
}
|
||||
void register_map_models_handler() {
|
||||
drogon::app().registerHandlerViaRegex(
|
||||
R"(^/map/models$)",
|
||||
[](const drogon::HttpRequestPtr& req,
|
||||
std::function<void(const drogon::HttpResponsePtr&)>&& callback) {
|
||||
auto global = Global::instance();
|
||||
auto response = drogon::HttpResponse::newHttpResponse();
|
||||
try {
|
||||
if (req->method() == drogon::Post) {
|
||||
if (is_multipart_request(req)) {
|
||||
handle_map_model_upload(req, global);
|
||||
}
|
||||
else {
|
||||
handle_map_model_config_update(req, global);
|
||||
}
|
||||
}
|
||||
response->setStatusCode(drogon::k200OK);
|
||||
response->setContentTypeCode(drogon::CT_APPLICATION_JSON);
|
||||
response->setBody(global->map_model_config.to_base_json().to_json_string());
|
||||
}
|
||||
catch (const std::exception& error) {
|
||||
response->setStatusCode(drogon::k400BadRequest);
|
||||
response->setContentTypeCode(drogon::CT_TEXT_PLAIN);
|
||||
response->setBody(Psc::platform_2_utf8(error.what()));
|
||||
}
|
||||
response->addHeader("Cache-Control", "no-store");
|
||||
callback(response);
|
||||
},
|
||||
{drogon::Get, drogon::Post});
|
||||
}
|
||||
void register_map_view_handler() {
|
||||
drogon::app().registerHandlerViaRegex(
|
||||
R"(^/map/view$)",
|
||||
@@ -426,6 +555,10 @@ void register_map_view_handler() {
|
||||
try {
|
||||
auto next_view = global->map_view_config;
|
||||
next_view.from_base_json(&parsed.value());
|
||||
if (!global->map_resources_config.is_imagery_key(next_view.map2d.current_imagery_key) ||
|
||||
!global->map_resources_config.is_imagery_key(next_view.map3d.current_imagery_key)) {
|
||||
throw std::invalid_argument("invalid current_imagery_key");
|
||||
}
|
||||
global->map_resources_config.current_imagery_key = key.value();
|
||||
global->map_view_config = next_view;
|
||||
Global::save();
|
||||
@@ -453,6 +586,7 @@ void Global::init_tiles() {
|
||||
auto terrain = std::make_shared<Tile_Source>(map_resources_config.terrain);
|
||||
register_map_resources_handler();
|
||||
register_map_graphics_handler();
|
||||
register_map_models_handler();
|
||||
register_map_view_handler();
|
||||
register_tile_handler(R"(^/map/imagery/.*$)", "/map/imagery/", imagery);
|
||||
register_tile_handler(R"(^/tiles/.*$)", "/tiles/", google_imagery);
|
||||
|
||||
Reference in New Issue
Block a user