Cogs.Core
JsonSerialization.cpp
1#include "Utilities/Parsing.h"
2
3#include "JsonSerialization.h"
4#include "BaseRasterSource.h"
5#include "FloatRasterSource.h"
6#include "ColorRasterSource.h"
7#include "NullRasterSource.h"
8#include "WCSRasterSource.h"
9#include "WMSRasterSource.h"
10#include "DiskCache.h"
11#include "MemoryCache.h"
12
13#include "Foundation/Logging/Logger.h"
14#include "Foundation/Platform/IO.h"
15
16#include "../Libraries/tiny-AES-c/aes.hpp"
17
18#include "rapidjson/document.h"
19#include "rapidjson/prettywriter.h"
20
21#include "base64.h"
22
23#include <cassert>
24
25namespace rj = rapidjson;
26
27
28
29namespace {
30 using namespace Cogs::Core;
31 using namespace Cogs::Core::TerrainProvider;
32 Cogs::Logging::Log logger = Cogs::Logging::getLogger("JsonSerialization");
33
34 bool getDouble(double& rv, const rj::Value& parent, const char* name)
35 {
36 auto it = parent.FindMember(name);
37 if (it == parent.MemberEnd()) {
38 LOG_ERROR(logger, "Missing required member %s", name);
39 return false;
40 }
41 else if (!it->value.IsNumber()) {
42 LOG_ERROR(logger, "Member %s is not a number", name);
43 return false;
44 }
45 rv = it->value.GetDouble();
46 return true;
47 }
48
49 bool getBool(bool& rv, const rj::Value& parent, const char* name, bool required)
50 {
51 auto it = parent.FindMember(name);
52 if (it == parent.MemberEnd()) {
53 if(required) LOG_ERROR(logger, "Missing required member %s", name);
54 return false;
55 }
56 else if(!it->value.IsBool()) {
57 LOG_ERROR(logger, "Member %s is not a boolean value", name);
58 return false;
59 }
60 rv = it->value.GetBool();
61 return true;
62 }
63
64 bool getUnsigned(unsigned& rv, const rj::Value& parent, const char* name, bool required = true)
65 {
66 auto it = parent.FindMember(name);
67 if (it == parent.MemberEnd()) {
68 if (required) LOG_ERROR(logger, "Missing required member %s", name);
69 return false;
70 }
71 if (!it->value.IsUint()) {
72 if (required) LOG_ERROR(logger, "Member %s is not an unsigned number", name);
73 return false;
74 }
75 rv = it->value.GetUint();
76 return true;
77 }
78
79 Cogs::Core::StringRef findStringMember(const rj::Value& parent, const char* name, bool required = true)
80 {
81 auto it = parent.FindMember(name);
82 if (it == parent.MemberEnd()) {
83 if (required) LOG_ERROR(logger, "Missing required %s member", name);
84 return Cogs::Core::NoString;
85 }
86 if (!it->value.IsString()) {
87 LOG_ERROR(logger, "Member %s is not a string", name);
88 return Cogs::Core::NoString;
89 }
90 return Cogs::Core::Strings::add(it->value.GetString());
91 }
92
93 bool deserializeCoordSys(Cogs::Core::TerrainProvider::CoordSys& cs, Cogs::StringView text)
94 {
95 std::string tmp(text.data(), text.length());
96
97 if (tmp.find("EPSG:") == 0) {
98 cs.kind = CoordSys::Kind::EPSG;
99 char* ptr = nullptr;
100 cs.id = std::strtol(text.data() + 5, &ptr, 10);
101 if (ptr == nullptr || *ptr != '\0') {
102 LOG_ERROR(logger, "Error parsing coordinate system '%s'", tmp.c_str());
103 return false;
104 }
105 return true;
106 }
107 LOG_ERROR(logger, "Error parsing coordinate system '%s'", tmp.c_str());
108 return false;
109 }
110
111 Cogs::Core::StringRef serializeCoordSys(CoordSys coordSys)
112 {
113 std::string tmp;
114 switch (coordSys.kind) {
115 case CoordSys::Kind::EPSG:
116 tmp.append("EPSG:");
117 tmp.append(std::to_string(coordSys.id));
118 return Cogs::Core::Strings::add(tmp);
119 default:
120 assert(false && "Illegal coordysys kind");
121 return Cogs::Core::NoString;
122 }
123 }
124
125 bool deserializeTiling(Tiling& tiling, const rj::Document& doc)
126 {
127 auto it = doc.FindMember("Tiling");
128 if (it == doc.MemberEnd()) {
129 LOG_ERROR(logger, "Missing required member Tiling");
130 return false;
131 }
132 if (!it->value.IsObject()) {
133 LOG_ERROR(logger, "Member Tiling is not an object");
134 return false;
135 }
136 if (!getUnsigned(tiling.levels, it->value, "Levels", false)) tiling.levels = 1u;
137 if (!getUnsigned(tiling.overlapStart, it->value, "OverlapStart", false)) tiling.overlapStart = 0u;
138 if (!getUnsigned(tiling.overlapEnd, it->value, "OverlapEnd", false)) tiling.overlapEnd = 0u;
139 return
140 getDouble(tiling.size.x, it->value, "LevelZeroDeltaX") &&
141 getDouble(tiling.size.y, it->value, "LevelZeroDeltaY") &&
142 getUnsigned(tiling.width, it->value, "TileWidth") &&
143 getUnsigned(tiling.height, it->value, "TileHeight");
144 }
145
146 bool serializeTiling(rj::Document& doc, const Tiling& tiling)
147 {
148 auto& allocator = doc.GetAllocator();
149 rapidjson::Value tilingValue;
150 tilingValue.SetObject();
151 tilingValue.AddMember("LevelZeroDeltaX", tiling.size.x, allocator);
152 tilingValue.AddMember("LevelZeroDeltaY", tiling.size.y, allocator);
153 tilingValue.AddMember("TileWidth", tiling.width, allocator);
154 tilingValue.AddMember("TileHeight", tiling.height, allocator);
155 if (1 < tiling.levels) tilingValue.AddMember("Levels", tiling.levels, allocator);
156 if (tiling.overlapStart != 0) tilingValue.AddMember("OverlapStart", tiling.overlapStart, allocator);
157 if (tiling.overlapEnd != 0) tilingValue.AddMember("OverlapEnd", tiling.overlapEnd, allocator);
158 doc.AddMember("Tiling", tilingValue, allocator);
159 return true;
160 }
161
162 bool deserializeExtent(Extent& extent, const rj::Document& doc)
163 {
164 auto it = doc.FindMember("Extents");
165 if (it == doc.MemberEnd()) {
166 LOG_ERROR(logger, "Missing required member Extetns");
167 return false;
168 }
169 if (!it->value.IsObject()) {
170 LOG_ERROR(logger, "Member Extents is not an object.");
171 return false;
172 }
173 return
174 getDouble(extent.min.x, it->value, "MinX") &&
175 getDouble(extent.min.y, it->value, "MinY") &&
176 getDouble(extent.max.x, it->value, "MaxX") &&
177 getDouble(extent.max.y, it->value, "MaxY");
178 }
179
180 bool serializeExtent(rj::Document& doc, const Extent& extent)
181 {
182 auto& allocator = doc.GetAllocator();
183 rj::Value extents;
184 extents.SetObject();
185 extents.AddMember("MinX", extent.min.x, allocator);
186 extents.AddMember("MaxX", extent.max.x, allocator);
187 extents.AddMember("MinY", extent.min.y, allocator);
188 extents.AddMember("MaxY", extent.max.y, allocator);
189 doc.AddMember("Extents", extents, allocator);
190 return true;
191 }
192
193 bool deserializeBaseConf(BaseConfig& conf, const rj::Document& doc)
194 {
195 auto common_it = doc.FindMember("Common");
196 if (common_it == doc.MemberEnd()) {
197 LOG_ERROR(logger, "Missing required Common member");
198 return false;
199 }
200 if (!common_it->value.IsObject()) {
201 LOG_ERROR(logger, "Common member is not an object");
202 return false;
203 }
204 auto cs_it = common_it->value.FindMember("SRS");
205 if (cs_it == common_it->value.MemberEnd()) {
206 LOG_ERROR(logger, "Missing required SRS member");
207 return false;
208 }
209 if (!cs_it->value.IsString()) {
210 LOG_ERROR(logger, "SRS member is not a string");
211 return false;
212 }
213 if(!deserializeCoordSys(conf.coordsys, Cogs::StringView(cs_it->value.GetString()))) return false;
214
215 if (auto name = findStringMember(doc, "Name"); name != Cogs::Core::NoString) {
216 conf.name = name;
217 }
218 else return false;
219
220 conf.cacheKey = findStringMember(doc, "CacheKey", false);
221
222 auto common = doc.FindMember("Common");
223 if (common != doc.MemberEnd() && common->value.IsObject()) {
224 getBool(conf.offline, common->value, "Offline", false);
225 }
226
227 auto tf_it = common_it->value.FindMember("TextureFormat");
228 if (tf_it != common_it->value.MemberEnd()) {
229 if (!tf_it->value.IsString()) {
230 LOG_ERROR(logger, "Member TextureFormat is not a string");
231 return false;
232 }
233 LOG_DEBUG(logger, "MOO: %s", tf_it->value.GetString());
234 conf.textureFormat = Cogs::Core::parseTextureFormat(Cogs::StringView(tf_it->value.GetString()), Cogs::TextureFormat::Unknown);
235 }
236
237 if (auto mimeType = findStringMember(common_it->value, "MimeType", false); mimeType != NoString) {
238 conf.mimeType = parseMimeType(Strings::get(mimeType));
239 }
240
241 auto nd_it = common_it->value.FindMember("NoData");
242 if (nd_it != common_it->value.MemberEnd()) {
243 if (nd_it->value.IsString()) {
244 char* e = nullptr;
245 conf.noData = std::strtof(nd_it->value.GetString(), &e);
246 if (e == nullptr || *e != '\0') {
247 LOG_ERROR(logger, "Failed to parse Common.NoData value '%s' as a double.", nd_it->value.GetString());
248 return false;
249 }
250 }
251 else if (nd_it->value.IsNumber()) {
252 conf.noData = nd_it->value.GetFloat();
253 }
254 else {
255 LOG_ERROR(logger, "Common.NoData is neither string nor float.");
256 return false;
257 }
258 }
259 return
260 deserializeExtent(conf.extent, doc) &&
261 deserializeTiling(conf.tiling, doc);
262 }
263
264 bool serializeBaseConf(rj::Document& doc, const BaseConfig& conf)
265 {
266 auto& allocator = doc.GetAllocator();
267 rj::Value common;
268 common.SetObject();
269 common.AddMember("SRS", rj::StringRef(Cogs::Core::Strings::getC(serializeCoordSys(conf.coordsys))), allocator);
270 if (conf.textureFormat != Cogs::TextureFormat::Unknown) {
271 auto* formatInfo = Cogs::getFormatInfo(conf.textureFormat);
272 if (formatInfo) common.AddMember("TextureFormat", rapidjson::StringRef(formatInfo->name), allocator);
273 }
274 if (conf.offline) common.AddMember("Offline", conf.offline, allocator);
275
276 if (conf.cacheKey != Cogs::Core::NoString) doc.AddMember("CacheKey", rj::StringRef(Cogs::Core::Strings::getC(conf.cacheKey)), allocator);
277 if (conf.name != Cogs::Core::NoString) doc.AddMember("Name", rj::StringRef(Cogs::Core::Strings::getC(conf.name)), allocator);
278 if (conf.mimeType != MimeType::None) common.AddMember("MimeType", rj::StringRef(Cogs::Core::Strings::getC(mimeTypeString(conf.mimeType))), allocator);
279
280 doc.AddMember("Common", common, allocator);
281 return
282 serializeExtent(doc, conf.extent) &&
283 serializeTiling(doc, conf.tiling);
284
285 }
286
287 bool deserializeFloatConf(FloatConfig& conf, rj::Document& doc)
288 {
289 if (!deserializeBaseConf(conf, doc)) return false;
290
291 if (auto it = doc.FindMember("FloatValue"); it != doc.MemberEnd()) {
292 if (!it->value.IsNumber()) {
293 LOG_ERROR(logger, "Member FloatValue is not a number.");
294 return false;
295 }
296 conf.value = it->value.GetFloat();
297 }
298 return true;
299 }
300
301 bool serializeFloatRasterSource(rj::Document& doc, const FloatConfig& conf)
302 {
303 auto& allocator = doc.GetAllocator();
304 if (conf.value != 0.f) {
305 doc.AddMember("FloatValue", conf.value, allocator);
306 }
307 return serializeBaseConf(doc, conf);
308 }
309
310 bool deserializeColorConf(ColorConfig& conf, const rj::Document& doc)
311 {
312 if (!deserializeBaseConf(conf, doc)) return false;
313
314 auto col_it = doc.FindMember("Colors");
315 if (col_it == doc.MemberEnd()) {
316 LOG_ERROR(logger, "Required member Colors missing");
317 return false;
318 }
319 if (!col_it->value.IsArray()) {
320 LOG_ERROR(logger, "Member Colors is not an array");
321 return false;
322 }
323 for (auto& col : col_it->value.GetArray()) {
324 if (!col.IsArray()) {
325 LOG_ERROR(logger, "Item of Color array is not an array");
326 return false;
327 }
328 auto colarr = col.GetArray();
329 if (colarr.Size() != 4) {
330 LOG_ERROR(logger, "Item of color array is not of length 4");
331 return false;
332 }
333 glm::vec4 color;
334 for (rj::SizeType i = 0; i < 4; i++) {
335 if (!colarr[i].IsNumber()) {
336 LOG_ERROR(logger, "Item of array in color array is not a number");
337 return false;
338 }
339 color[i] = colarr[i].GetFloat();
340 }
341 conf.colors.push_back(color);
342 }
343 return true;
344 }
345
346 bool serializeColorRasterSource(rj::Document& doc, const ColorConfig& conf)
347 {
348 auto& allocator = doc.GetAllocator();
349
350 rj::Value colors;
351 colors.SetArray();
352 for (auto& color : conf.colors) {
353 rj::Value c;
354 c.SetArray();
355 c.PushBack(color.r, allocator);
356 c.PushBack(color.g, allocator);
357 c.PushBack(color.b, allocator);
358 c.PushBack(color.a, allocator);
359 colors.PushBack(c, allocator);
360 }
361 doc.AddMember("Colors", colors, allocator);
362 return serializeBaseConf(doc, conf);
363 }
364
365 bool deserializeHTTPConf(HTTPConfig& conf, const rj::Document& doc)
366 {
367 if (!deserializeBaseConf(conf, doc)) return false;
368
369 conf.baseUrl = findStringMember(doc, "Url", true);
370 if (conf.baseUrl == NoString) return false;
371 conf.username = findStringMember(doc, "Username", false);
372 if (auto pw_it = doc.FindMember("Password"); pw_it != doc.MemberEnd()) {
373 if (!pw_it->value.IsString()) { LOG_ERROR(logger, "Member 'Password' is not a string"); return false; }
374 auto buffer = base64_decode(pw_it->value.GetString());
375 if ((buffer.size() % AES_BLOCKLEN) != 0) { LOG_ERROR(logger, "Key is not a multiple of %u", AES_BLOCKLEN); return false; }
376 if ((buffer.size() < 2 * AES_BLOCKLEN)) { LOG_ERROR(logger, "Encrypted key must be at least %u bytes", 2 * AES_BLOCKLEN); return false; }
377
378 // Key hashed by SHA256
379 uint8_t key[AES_KEYLEN] = {
380 0xdf, 0x8c, 0x7c, 0x5d, 0x27, 0x21, 0xec, 0x1a,
381 0xd1, 0x1f, 0xfb, 0x76, 0x76, 0x37, 0x4e, 0x48,
382 0x18, 0x61, 0x10, 0x89, 0xe0, 0xec, 0x16, 0x0d,
383 0x41, 0xb9, 0x28, 0x7a, 0x77, 0xe2, 0xd1, 0x80
384 };
385
386 AES_ctx ctx{};
387 AES_init_ctx(&ctx, key);
388 AES_CBC_decrypt_buffer(&ctx, (uint8_t*)buffer.data(), static_cast<uint32_t>(buffer.size()));
389
390 // Data has an initialization vector of blocklen that we skip
391 auto* start = buffer.c_str() + AES_BLOCKLEN;
392
393 // Assuming PKCS#7, padding is number of bytes padded, a whole block of padding
394 // is present when data length is a multiple of blocklen.
395 auto* end = buffer.c_str() + buffer.size();
396 end = end - end[-1];
397 if (end < start) { LOG_ERROR(logger, "Key has invalid padding"); return false; }
398
399 conf.password = Strings::add(Cogs::StringView(start, end - start));
400 }
401 return true;
402 }
403
404 bool serializeHTTPConf(rj::Document& doc, const HTTPConfig& conf, bool include_credentials)
405 {
406 auto& allocator = doc.GetAllocator();
407 if (conf.baseUrl != NoString) doc.AddMember("Url", rj::StringRef(Strings::getC(conf.baseUrl)), allocator);
408 if (include_credentials) {
409 if (conf.username != NoString) doc.AddMember("Username", rj::StringRef(Strings::getC(conf.username)), allocator);
410 if (conf.password != NoString) doc.AddMember("Password", rj::StringRef(Strings::getC(conf.password)), allocator);
411 }
412 return serializeBaseConf(doc, conf);
413 }
414
415 bool deserializeWCSConf(WCSConfig& conf, const rj::Document& doc)
416 {
417 if (!deserializeHTTPConf(conf, doc)) return false;
418
419 conf.layer = findStringMember(doc, "Layer", true);
420 if (conf.layer == NoString)
421 return false;
422
423 conf.format = findStringMember(doc, "Format", true);
424 if (conf.format == NoString)
425 return false;
426
427 conf.interpolation = findStringMember(doc, "Interpolation", false);
428 conf.interpolationFieldName = findStringMember(doc, "InterpolationFieldName", false);
429 getBool(conf.invertZ, doc, "InvertZ", false);
430 getBool(conf.allowNoData, doc, "AllowNoData", false);
431 return true;
432 }
433
434 bool serializeWCSRasterSource(rj::Document& doc, const WCSConfig& conf, bool include_credentials)
435 {
436 WCSConfig def{};
437
438 auto& allocator = doc.GetAllocator();
439 if (conf.layer != NoString) doc.AddMember("Layer", rj::StringRef(Strings::getC(conf.layer)), allocator);
440 if (conf.format != NoString) doc.AddMember("Format", rj::StringRef(Strings::getC(conf.format)), allocator);
441 if (conf.interpolation != NoString &&
442 conf.interpolation != def.interpolation) {
443 doc.AddMember("Interpolation", rj::StringRef(Strings::getC(conf.interpolation)), allocator);
444 }
445 if (conf.interpolationFieldName != NoString &&
446 conf.interpolationFieldName != def.interpolationFieldName) {
447 doc.AddMember("InterpolationFieldName", rj::StringRef(Strings::getC(conf.interpolationFieldName)), allocator);
448 }
449 if (conf.invertZ) doc.AddMember("InvertZ", conf.invertZ, allocator);
450 if (conf.allowNoData) doc.AddMember("AllowNoData", conf.allowNoData, allocator);
451 return serializeHTTPConf(doc, conf, include_credentials);
452 }
453
454 bool deserializeWMSOtherArguments(std::vector<std::pair<StringRef, StringRef>>& otherArguments, rj::Value& parent)
455 {
456 auto common_it = parent.FindMember("OtherArguments");
457 if (common_it == parent.MemberEnd()) return true; // Not required
458 if (!common_it->value.IsArray()) { LOG_ERROR(logger, "OtherArguments is not an Array"); return false; }
459 for (auto& item : common_it->value.GetArray()) {
460 if (!item.IsObject()) { LOG_ERROR(logger, "OtherArguments item is not an Object"); return false; }
461 if (item.MemberCount() != 2) { LOG_ERROR(logger, "OtherArguments item should have exactly two members."); return false; }
462 auto key = item.FindMember("Key");
463 if (key == item.MemberEnd()) { LOG_ERROR(logger, "OtherArguments item is missing 'Key' item"); return false; }
464 if (!key->value.IsString()) { LOG_ERROR(logger, "OtherArguments item has 'Key' item that has non-string value"); return false; }
465 auto value = item.FindMember("Value");
466 if (value == item.MemberEnd()) { LOG_ERROR(logger, "OtherArguments item is missing 'Value' item"); return false; }
467 if (!value->value.IsString()) { LOG_ERROR(logger, "OtherArguments item has 'Value' item that has non-string value"); return false; }
468 otherArguments.emplace_back(std::make_pair(Strings::add(key->value.GetString()),
469 Strings::add(value->value.GetString())));
470 }
471 return true;
472 }
473
474 bool serializeWMSOtherArguments(rj::Document& doc, rj::Value& parent, const std::vector<std::pair<StringRef, StringRef>>& otherArguments)
475 {
476 if (otherArguments.empty()) return true;
477 auto& allocator = doc.GetAllocator();
478 rj::Value container;
479 container.SetArray();
480 for (auto& arg : otherArguments) {
481 if (arg.first == NoString || arg.second == NoString) { LOG_ERROR(logger, "Malformed WMS OtherArgument"); return false; }
482 rj::Value item;
483 item.SetObject();
484 item.AddMember("Key", rj::StringRef(Strings::getC(arg.first)), allocator);
485 item.AddMember("Value", rj::StringRef(Strings::getC(arg.second)), allocator);
486 container.PushBack(item, allocator);
487 }
488 parent.AddMember("OtherArguments", container, allocator);
489 return true;
490 }
491
492 bool deserializeWMSLayerDescriptions(std::vector<WMSLayerDescription>& layerDescriptions, rj::Value& parent)
493 {
494 auto desc_it = parent.FindMember("Layers");
495 if (desc_it == parent.MemberEnd()) { LOG_ERROR(logger, "Missing required item 'Layers'"); return false; }
496 if (!desc_it->value.IsArray()) { LOG_ERROR(logger, "Value of item 'Layers' is not an array."); return false; }
497 if (desc_it->value.GetArray().Empty()) { LOG_ERROR(logger, "Item 'Layers' needs at least on item"); return false; }
498 for (auto& item : desc_it->value.GetArray()) {
499 WMSLayerDescription layer{};
500 layer.name = NoString;
501 if (auto it = item.FindMember("Name"); it != item.MemberEnd()) {
502 if (!it->value.IsString()) { LOG_ERROR(logger, "Layer Name is not a string"); return false; }
503 layer.name = Strings::add(it->value.GetString());
504 } else { LOG_ERROR(logger, "Layer missing compulsory member 'Name'"); return false; }
505 if (auto it = item.FindMember("Style"); it != item.MemberEnd()) {
506 if (!it->value.IsString()) { LOG_ERROR(logger, "Layer Style is not a string"); return false; }
507 layer.style = Strings::add(it->value.GetString());
508 }
509 if (auto it = item.FindMember("MinLevel"); it != item.MemberEnd()) {
510 if (!it->value.IsUint()) { LOG_ERROR(logger, "Layer MinLevel is not an unsigned integer."); return false; }
511 layer.minLevel = it->value.GetUint();
512 }
513 if (auto it = item.FindMember("MaxLevel"); it != item.MemberEnd()) {
514 if (!it->value.IsUint()) { LOG_ERROR(logger, "Layer MaxLevel is not an unsigned integer."); return false; }
515 layer.maxLevel = it->value.GetUint();
516 }
517 if (!deserializeWMSOtherArguments(layer.otherArguments, item)) return false;
518 layerDescriptions.emplace_back(std::move(layer));
519 }
520 return true;
521 }
522
523 bool serializeWMSLayerDescriptions(rj::Document& doc, rj::Value& parent, const std::vector<WMSLayerDescription>& layerDescriptions)
524 {
525 if (layerDescriptions.empty()) { LOG_ERROR(logger, "No layers defined"); return false; }
526 auto& allocator = doc.GetAllocator();
527 rj::Value container;
528 container.SetArray();
529 static const WMSLayerDescription layerDefaults{};
530 for (auto& layer : layerDescriptions) {
531 if (layer.name == NoString) { LOG_ERROR(logger, "Layer without name"); return false; }
532 rj::Value item;
533 item.SetObject();
534 item.AddMember("Name", rj::StringRef(Strings::getC(layer.name)), allocator);
535 if (layer.style != layerDefaults.style) {
536 if (layer.style == NoString) { LOG_ERROR(logger, "Layer Style is an empty string"); return false; }
537 item.AddMember("Style", rj::StringRef(Strings::getC(layer.style)), allocator);
538 }
539 if (layer.minLevel != layerDefaults.minLevel) {
540 item.AddMember("MinLevel", layer.minLevel, allocator);
541 }
542 if (layer.maxLevel != layerDefaults.maxLevel) {
543 item.AddMember("MaxLevel", layer.maxLevel, allocator);
544 }
545 if (!serializeWMSOtherArguments(doc, item, layer.otherArguments)) return false;
546 container.PushBack(item, allocator);
547 }
548 parent.AddMember("Layers", container, allocator);
549 return true;
550 }
551
552 bool deserializeWMSConf(WMSConfig& conf, rj::Document& doc)
553 {
554 if (!deserializeHTTPConf(conf, doc)) return false;
555
556 if (auto version = findStringMember(doc, "Version", false); version != NoString) {
557 switch (Strings::get(version).hash()) {
558 case Cogs::hash("1.0.0"): conf.version = WMSVersion::v1_0_0; break;
559 case Cogs::hash("1.1.0"): conf.version = WMSVersion::v1_1_0; break;
560 case Cogs::hash("1.1.1"): conf.version = WMSVersion::v1_1_1; break;
561 case Cogs::hash("1.3.0"): conf.version = WMSVersion::v1_3_0; break;
562 default:
563 LOG_ERROR(logger, "Unrecognized WMS version '%s'", Strings::getC(version));
564 return false;
565 }
566 }
567
568 if (auto dataType = findStringMember(doc, "ValueDataType", false); dataType != NoString) {
569 switch (Strings::get(dataType).hash()) {
570 case Cogs::hash("Short"): conf.dataType = WMSValueDataType::Short; break;
571 case Cogs::hash("UnsignedSHort"): conf.dataType = WMSValueDataType::UnsignedSHort; break;
572 case Cogs::hash("Float"): conf.dataType = WMSValueDataType::Float; break;
573 case Cogs::hash("Double"): conf.dataType = WMSValueDataType::Double; break;
574 default:
575 LOG_ERROR(logger, "Unrecognized WMS value datatype '%s'", Strings::getC(dataType));
576 return false;
577 }
578 }
579
580 getBool(conf.serverIsLittleEndian, doc, "IsLittleEndian", false);
581 getBool(conf.retryFailedRequests, doc, "Retry", false);
582 if (!deserializeWMSLayerDescriptions(conf.layerDescriptions, doc)) return false;
583 if (!deserializeWMSOtherArguments(conf.otherArguments, doc)) return false;
584
585 return true;
586 }
587
588 bool serializeWMSRasterSource(rj::Document& doc, const WMSConfig& conf, bool include_credentials)
589 {
590 WMSConfig confDefaults{};
591 auto& allocator = doc.GetAllocator();
592 if (conf.version != confDefaults.version) {
593 switch (conf.version) {
594 case WMSVersion::v1_0_0: doc.AddMember("Version", rj::StringRef("1.0.0"), allocator); break;
595 case WMSVersion::v1_1_0: doc.AddMember("Version", rj::StringRef("1.1.0"), allocator); break;
596 case WMSVersion::v1_1_1: doc.AddMember("Version", rj::StringRef("1.1.1"), allocator); break;
597 case WMSVersion::v1_3_0: doc.AddMember("Version", rj::StringRef("1.3.0"), allocator); break;
598 default: LOG_ERROR(logger, "Illegal WMS version"); return false;
599 }
600 }
601 if (conf.dataType != confDefaults.dataType) {
602 switch (conf.dataType) {
603 case WMSValueDataType::Short: doc.AddMember("ValueDataType", rj::StringRef("Short"), allocator); break;
604 case WMSValueDataType::UnsignedSHort: doc.AddMember("ValueDataType", rj::StringRef("UnsignedSHort"), allocator); break;
605 case WMSValueDataType::Float: doc.AddMember("ValueDataType", rj::StringRef("Float"), allocator); break;
606 case WMSValueDataType::Double: doc.AddMember("ValueDataType", rj::StringRef("Double"), allocator); break;
607 default: LOG_ERROR(logger, "Illegal WMS dataType"); return false;
608 }
609 }
610 if (conf.serverIsLittleEndian != confDefaults.serverIsLittleEndian) {
611 doc.AddMember( "IsLittleEndian", conf.serverIsLittleEndian, allocator);
612 }
613 if (conf.retryFailedRequests != confDefaults.retryFailedRequests) {
614 doc.AddMember("Retry", conf.retryFailedRequests, allocator);
615 }
616 if (!serializeWMSLayerDescriptions(doc, doc, conf.layerDescriptions)) return false;
617 if (!serializeWMSOtherArguments(doc, doc, conf.otherArguments)) return false;
618 return serializeHTTPConf(doc, conf, include_credentials);
619 }
620
621 std::unique_ptr<Cogs::Core::TerrainProvider::BaseConfig> deserializeConfig(rj::Document& doc)
622 {
623 auto rasterType = findStringMember(doc, "RasterType");
624 if (rasterType == NoString) return nullptr;
625
626 switch (Strings::get(rasterType).hash()) {
627 case Cogs::hash("FloatRasterSource"):
628 if (auto conf = std::make_unique<FloatConfig>(); deserializeFloatConf(*conf, doc)) {
629 return conf;
630 }
631 break;
632 case Cogs::hash("ColorRasterSource"):
633 if (auto conf = std::make_unique<ColorConfig>(); deserializeColorConf(*conf, doc)) {
634 return conf;
635 }
636 break;
637 case Cogs::hash("WcsRasterSource"):
638 if (auto conf = std::make_unique<WCSConfig>(); deserializeWCSConf(*conf, doc)) {
639 return conf;
640 }
641 break;
642 case Cogs::hash("WmsRasterSource"):
643 if (auto conf = std::make_unique<WMSConfig>(); deserializeWMSConf(*conf, doc)) {
644 return conf;
645 }
646 break;
647
648 default:
649 LOG_ERROR(logger, "Unrecognized rastertype %s", Strings::getC(rasterType));
650 return nullptr;
651 }
652
653 return nullptr;
654 }
655
656 std::unique_ptr<ICache> openCache(Context* context, BaseConfig* providerConf, Cogs::StringView cacheRoot)
657 {
658 if (providerConf->cacheKey != NoString) {
659 auto diskCache = std::make_unique<DiskCache>();
660 if (diskCache->init(context, providerConf, cacheRoot)) {
661 return diskCache;
662 }
663 }
664 return nullptr;
665 }
666
667}
668
669std::unique_ptr<Cogs::Core::TerrainProvider::BaseConfig> Cogs::Core::TerrainProvider::deserializeRastersourceConfig(Context* /*context*/, const StringView json)
670{
671 rapidjson::Document doc;
672 if (doc.Parse<rapidjson::kParseTrailingCommasFlag | rapidjson::kParseCommentsFlag | rapidjson::kParseNanAndInfFlag>(json.data(), json.size()).HasParseError()) {
673 return nullptr;
674 }
675 if (!doc.IsObject()) {
676 LOG_ERROR(logger, "conf json root is not an object");
677 return nullptr;
678 }
679
680 return deserializeConfig(doc);
681}
682
683Cogs::Core::TerrainProvider::IRasterSource* Cogs::Core::TerrainProvider::deserialize(Context* context, const StringView cacheRoot, const StringView json)
684{
685 std::unique_ptr<BaseConfig> providerConf = deserializeRastersourceConfig(context, json);
686 if (!providerConf) return nullptr;
687
688 if (providerConf->offline) {
689 auto cache = openCache(context, providerConf.get(), cacheRoot);
690 if (!cache) return nullptr;
691
692 auto* rastersource = new NullRasterSource(context);
693 if (rastersource->init(*(providerConf.get()), std::move(cache))) {
694 LOG_DEBUG(logger, "Created NullRasterSource");
695 return rastersource;
696 }
697 delete rastersource;
698 }
699 else if (auto* floatConf = dynamic_cast<FloatConfig*>(providerConf.get())) {
700 auto* rastersource = new FloatRasterSource(context);
701 if (rastersource->init(*floatConf, std::make_unique<MemoryCache>())) {
702 LOG_DEBUG(logger, "Created FloatRasterSource");
703 return rastersource;
704 }
705 delete rastersource;
706 }
707 else if (auto* colorConf = dynamic_cast<ColorConfig*>(providerConf.get())) {
708 auto* rastersource = new ColorRasterSource(context);
709 if (rastersource->init(*colorConf, std::make_unique<MemoryCache>())) {
710 LOG_DEBUG(logger, "Created ColorRasterSource");
711 return rastersource;
712 }
713 delete rastersource;
714 }
715 else if (auto* wcsConf = dynamic_cast<WCSConfig*>(providerConf.get())) {
716 auto cache = openCache(context, wcsConf, cacheRoot);
717 if (!cache) return nullptr;
718
719 auto* rastersource = new WCSRasterSource(context);
720 if (rastersource->init(*wcsConf, std::move(cache))) {
721 LOG_DEBUG(logger, "Created WCSRasterSource");
722 return rastersource;
723 }
724 delete rastersource;
725 }
726 else if (auto* wmsConf = dynamic_cast<WMSConfig*>(providerConf.get())) {
727 auto cache = openCache(context, wmsConf, cacheRoot);
728 if (!cache) return nullptr;
729
730 auto* rastersource = new WMSRasterSource(context);
731 if (rastersource->init(*wmsConf, std::move(cache))) {
732 LOG_DEBUG(logger, "Created WMSRasterSource");
733 return rastersource;
734 }
735 delete rastersource;
736 }
737 else {
738 LOG_ERROR(logger, "Unsupported rastersource configuration");
739 }
740
741 return nullptr;
742}
743
744bool Cogs::Core::TerrainProvider::serializeConfig(Memory::MemoryBuffer& buffer, const BaseConfig* conf, bool include_credentials)
745{
746 if (conf == nullptr) {
747 LOG_ERROR(logger, "Cannot serialzie null pointer");
748 return false;
749 }
750
751 rapidjson::Document doc;
752 auto& allocator = doc.GetAllocator();
753
754 doc.SetObject();
755 if (auto* frs = dynamic_cast<const FloatConfig*>(conf)) {
756 doc.AddMember("RasterType", rj::StringRef("FloatRasterSource"), allocator);
757 if (!serializeFloatRasterSource(doc, *frs)) return false;
758 }
759 else if (auto* crs = dynamic_cast<const ColorConfig*>(conf)) {
760 doc.AddMember("RasterType", rj::StringRef("ColorRasterSource"), allocator);
761 if (!serializeColorRasterSource(doc, *crs)) return false;
762 }
763 else if (auto* wcs = dynamic_cast<const WCSConfig*>(conf)) {
764 doc.AddMember("RasterType", rj::StringRef("WcsRasterSource"), allocator);
765 if (!serializeWCSRasterSource(doc, *wcs, include_credentials)) return false;
766 }
767 else if (auto* wms = dynamic_cast<const WMSConfig*>(conf)) {
768 doc.AddMember("RasterType", rj::StringRef("WmsRasterSource"), allocator);
769 if (!serializeWMSRasterSource(doc, *wms, include_credentials)) return false;
770 }
771 else {
772 LOG_ERROR(logger, "Unrecognized raster source");
773 return false;
774 }
775
776 // Generate json
777 rapidjson::StringBuffer sb;
778 rapidjson::PrettyWriter<decltype(sb),
779 rapidjson::UTF8<>,
780 rapidjson::UTF8<>,
781 rapidjson::CrtAllocator,
782 rapidjson::kWriteNanAndInfFlag> writer(sb);
783 doc.Accept(writer);
784
785 // Copy to output buffer
786 buffer.resize(sb.GetSize(), false);
787 std::memcpy(buffer.data(), sb.GetString(), sb.GetSize());
788 return true;
789}
790
791bool Cogs::Core::TerrainProvider::serializeConfig(Memory::MemoryBuffer& buffer, const IRasterSource* rasterSource, bool include_credentials)
792{
793 if (rasterSource == nullptr) {
794 LOG_ERROR(logger, "Cannot serialzie null pointer");
795 return false;
796 }
797
798 rapidjson::Document doc;
799 auto& allocator = doc.GetAllocator();
800
801 doc.SetObject();
802 if (auto* frs = dynamic_cast<const FloatRasterSource*>(rasterSource)) {
803 doc.AddMember("RasterType", rj::StringRef("FloatRasterSource"), allocator);
804 FloatConfig conf{};
805 frs->getConfig(conf);
806 if (!serializeFloatRasterSource(doc, conf)) return false;
807 }
808 else if (auto* crs = dynamic_cast<const ColorRasterSource*>(rasterSource)) {
809 doc.AddMember("RasterType", rj::StringRef("ColorRasterSource"), allocator);
810 ColorConfig conf{};
811 crs->getConfig(conf);
812 if (!serializeColorRasterSource(doc, conf)) return false;
813 }
814 else if (auto* wcs = dynamic_cast<const WCSRasterSource*>(rasterSource)) {
815 doc.AddMember("RasterType", rj::StringRef("WcsRasterSource"), allocator);
816 WCSConfig conf{};
817 wcs->getConfig(conf);
818 if (!serializeWCSRasterSource(doc, conf, include_credentials)) return false;
819 }
820 else if (auto* wms = dynamic_cast<const WMSRasterSource*>(rasterSource)) {
821 doc.AddMember("RasterType", rj::StringRef("WmsRasterSource"), allocator);
822 WMSConfig conf{};
823 wms->getConfig(conf);
824 if (!serializeWMSRasterSource(doc, conf, include_credentials)) return false;
825 }
826 else {
827 LOG_ERROR(logger, "Unrecognized raster source");
828 return false;
829 }
830
831 // Generate json
832 rapidjson::StringBuffer sb;
833 rapidjson::PrettyWriter<decltype(sb),
834 rapidjson::UTF8<>,
835 rapidjson::UTF8<>,
836 rapidjson::CrtAllocator,
837 rapidjson::kWriteNanAndInfFlag> writer(sb);
838 doc.Accept(writer);
839
840 // Copy to output buffer
841 buffer.resize(sb.GetSize(), false);
842 std::memcpy(buffer.data(), sb.GetString(), sb.GetSize());
843 return true;
844}
A Context instance contains all the services, systems and runtime components needed to use Cogs.
Definition: Context.h:83
Log implementation class.
Definition: LogManager.h:139
Provides a weakly referenced view over the contents of a string.
Definition: StringView.h:24
constexpr const char * data() const noexcept
Get the sequence of characters referenced by the string view.
Definition: StringView.h:171
constexpr size_t size() const noexcept
Get the size of the string.
Definition: StringView.h:178
constexpr size_t length() const noexcept
Get the length of the string.
Definition: StringView.h:185
Contains the Engine, Renderer, resource managers and other systems needed to run Cogs....
constexpr Log getLogger(const char(&name)[LEN]) noexcept
Definition: LogManager.h:180
constexpr size_t hash() noexcept
Simple getter function that returns the initial value for fnv1a hashing.
Definition: HashFunctions.h:62
STL namespace.
Definition: aes.h:44