diff --git a/bridging/android/java/io/openmobilemaps/mapscore/shared/map/layers/tiled/GeoJsonFeatureParserInterface.kt b/bridging/android/java/io/openmobilemaps/mapscore/shared/map/layers/tiled/GeoJsonFeatureParserInterface.kt index a4ad71411..080631ed3 100644 --- a/bridging/android/java/io/openmobilemaps/mapscore/shared/map/layers/tiled/GeoJsonFeatureParserInterface.kt +++ b/bridging/android/java/io/openmobilemaps/mapscore/shared/map/layers/tiled/GeoJsonFeatureParserInterface.kt @@ -15,6 +15,8 @@ abstract class GeoJsonFeatureParserInterface { abstract fun parse(geoJson: String): ArrayList? + abstract fun parseWithPointGeometry(geoJson: String): ArrayList? + private class CppProxy : GeoJsonFeatureParserInterface { private val nativeRef: Long private val destroyed: AtomicBoolean = AtomicBoolean(false) @@ -35,5 +37,11 @@ abstract class GeoJsonFeatureParserInterface { return native_parse(this.nativeRef, geoJson) } private external fun native_parse(_nativeRef: Long, geoJson: String): ArrayList? + + override fun parseWithPointGeometry(geoJson: String): ArrayList? { + assert(!this.destroyed.get()) { error("trying to use a destroyed object") } + return native_parseWithPointGeometry(this.nativeRef, geoJson) + } + private external fun native_parseWithPointGeometry(_nativeRef: Long, geoJson: String): ArrayList? } } diff --git a/bridging/android/java/io/openmobilemaps/mapscore/shared/map/layers/tiled/GeoJsonPoint.kt b/bridging/android/java/io/openmobilemaps/mapscore/shared/map/layers/tiled/GeoJsonPoint.kt new file mode 100644 index 000000000..001d750bb --- /dev/null +++ b/bridging/android/java/io/openmobilemaps/mapscore/shared/map/layers/tiled/GeoJsonPoint.kt @@ -0,0 +1,9 @@ +// AUTOGENERATED FILE - DO NOT MODIFY! +// This file was generated by Djinni from geo_json_parser.djinni + +package io.openmobilemaps.mapscore.shared.map.layers.tiled + +data class GeoJsonPoint( + val point: io.openmobilemaps.mapscore.shared.map.coordinates.Coord, + val featureInfo: io.openmobilemaps.mapscore.shared.map.layers.tiled.vector.VectorLayerFeatureInfo, +) \ No newline at end of file diff --git a/bridging/android/jni/map/layers/tiled/NativeGeoJsonFeatureParserInterface.cpp b/bridging/android/jni/map/layers/tiled/NativeGeoJsonFeatureParserInterface.cpp index 69cc4641e..8842110e4 100644 --- a/bridging/android/jni/map/layers/tiled/NativeGeoJsonFeatureParserInterface.cpp +++ b/bridging/android/jni/map/layers/tiled/NativeGeoJsonFeatureParserInterface.cpp @@ -3,6 +3,7 @@ #include "NativeGeoJsonFeatureParserInterface.h" // my header #include "Marshal.hpp" +#include "NativeGeoJsonPoint.h" #include "NativeVectorLayerFeatureInfo.h" namespace djinni_generated { @@ -36,4 +37,13 @@ CJNIEXPORT jobject JNICALL Java_io_openmobilemaps_mapscore_shared_map_layers_til } JNI_TRANSLATE_EXCEPTIONS_RETURN(jniEnv, 0 /* value doesn't matter */) } +CJNIEXPORT jobject JNICALL Java_io_openmobilemaps_mapscore_shared_map_layers_tiled_GeoJsonFeatureParserInterface_00024CppProxy_native_1parseWithPointGeometry(JNIEnv* jniEnv, jobject /*this*/, jlong nativeRef, jstring j_geoJson) +{ + try { + const auto& ref = ::djinni::objectFromHandleAddress<::GeoJsonFeatureParserInterface>(nativeRef); + auto r = ref->parseWithPointGeometry(::djinni::String::toCpp(jniEnv, j_geoJson)); + return ::djinni::release(::djinni::Optional>::fromCpp(jniEnv, r)); + } JNI_TRANSLATE_EXCEPTIONS_RETURN(jniEnv, 0 /* value doesn't matter */) +} + } // namespace djinni_generated diff --git a/bridging/android/jni/map/layers/tiled/NativeGeoJsonPoint.cpp b/bridging/android/jni/map/layers/tiled/NativeGeoJsonPoint.cpp new file mode 100644 index 000000000..16cb59706 --- /dev/null +++ b/bridging/android/jni/map/layers/tiled/NativeGeoJsonPoint.cpp @@ -0,0 +1,31 @@ +// AUTOGENERATED FILE - DO NOT MODIFY! +// This file was generated by Djinni from geo_json_parser.djinni + +#include "NativeGeoJsonPoint.h" // my header +#include "NativeCoord.h" +#include "NativeVectorLayerFeatureInfo.h" + +namespace djinni_generated { + +NativeGeoJsonPoint::NativeGeoJsonPoint() = default; + +NativeGeoJsonPoint::~NativeGeoJsonPoint() = default; + +auto NativeGeoJsonPoint::fromCpp(JNIEnv* jniEnv, const CppType& c) -> ::djinni::LocalRef { + const auto& data = ::djinni::JniClass::get(); + auto r = ::djinni::LocalRef{jniEnv->NewObject(data.clazz.get(), data.jconstructor, + ::djinni::get(::djinni_generated::NativeCoord::fromCpp(jniEnv, c.point)), + ::djinni::get(::djinni_generated::NativeVectorLayerFeatureInfo::fromCpp(jniEnv, c.featureInfo)))}; + ::djinni::jniExceptionCheck(jniEnv); + return r; +} + +auto NativeGeoJsonPoint::toCpp(JNIEnv* jniEnv, JniType j) -> CppType { + ::djinni::JniLocalScope jscope(jniEnv, 3); + assert(j != nullptr); + const auto& data = ::djinni::JniClass::get(); + return {::djinni_generated::NativeCoord::toCpp(jniEnv, jniEnv->GetObjectField(j, data.field_point)), + ::djinni_generated::NativeVectorLayerFeatureInfo::toCpp(jniEnv, jniEnv->GetObjectField(j, data.field_featureInfo))}; +} + +} // namespace djinni_generated diff --git a/bridging/android/jni/map/layers/tiled/NativeGeoJsonPoint.h b/bridging/android/jni/map/layers/tiled/NativeGeoJsonPoint.h new file mode 100644 index 000000000..cec2fd58e --- /dev/null +++ b/bridging/android/jni/map/layers/tiled/NativeGeoJsonPoint.h @@ -0,0 +1,33 @@ +// AUTOGENERATED FILE - DO NOT MODIFY! +// This file was generated by Djinni from geo_json_parser.djinni + +#pragma once + +#include "GeoJsonPoint.h" +#include "djinni_support.hpp" + +namespace djinni_generated { + +class NativeGeoJsonPoint final { +public: + using CppType = ::GeoJsonPoint; + using JniType = jobject; + + using Boxed = NativeGeoJsonPoint; + + ~NativeGeoJsonPoint(); + + static CppType toCpp(JNIEnv* jniEnv, JniType j); + static ::djinni::LocalRef fromCpp(JNIEnv* jniEnv, const CppType& c); + +private: + NativeGeoJsonPoint(); + friend ::djinni::JniClass; + + const ::djinni::GlobalRef clazz { ::djinni::jniFindClass("io/openmobilemaps/mapscore/shared/map/layers/tiled/GeoJsonPoint") }; + const jmethodID jconstructor { ::djinni::jniGetMethodID(clazz.get(), "", "(Lio/openmobilemaps/mapscore/shared/map/coordinates/Coord;Lio/openmobilemaps/mapscore/shared/map/layers/tiled/vector/VectorLayerFeatureInfo;)V") }; + const jfieldID field_point { ::djinni::jniGetFieldID(clazz.get(), "point", "Lio/openmobilemaps/mapscore/shared/map/coordinates/Coord;") }; + const jfieldID field_featureInfo { ::djinni::jniGetFieldID(clazz.get(), "featureInfo", "Lio/openmobilemaps/mapscore/shared/map/layers/tiled/vector/VectorLayerFeatureInfo;") }; +}; + +} // namespace djinni_generated diff --git a/bridging/ios/MCGeoJsonFeatureParserInterface+Private.mm b/bridging/ios/MCGeoJsonFeatureParserInterface+Private.mm index 73622dd63..d1be8b9f4 100644 --- a/bridging/ios/MCGeoJsonFeatureParserInterface+Private.mm +++ b/bridging/ios/MCGeoJsonFeatureParserInterface+Private.mm @@ -6,6 +6,7 @@ #import "DJICppWrapperCache+Private.h" #import "DJIError.h" #import "DJIMarshal+Private.h" +#import "MCGeoJsonPoint+Private.h" #import "MCVectorLayerFeatureInfo+Private.h" #include #include @@ -45,6 +46,13 @@ + (nullable MCGeoJsonFeatureParserInterface *)create { } DJINNI_TRANSLATE_EXCEPTIONS() } +- (nullable NSArray *)parseWithPointGeometry:(nonnull NSString *)geoJson { + try { + auto objcpp_result_ = _cppRefHandle.get()->parseWithPointGeometry(::djinni::String::toCpp(geoJson)); + return ::djinni::Optional>::fromCpp(objcpp_result_); + } DJINNI_TRANSLATE_EXCEPTIONS() +} + namespace djinni_generated { auto GeoJsonFeatureParserInterface::toCpp(ObjcType objc) -> CppType diff --git a/bridging/ios/MCGeoJsonFeatureParserInterface.h b/bridging/ios/MCGeoJsonFeatureParserInterface.h index 4550deb01..2d937498c 100644 --- a/bridging/ios/MCGeoJsonFeatureParserInterface.h +++ b/bridging/ios/MCGeoJsonFeatureParserInterface.h @@ -1,6 +1,7 @@ // AUTOGENERATED FILE - DO NOT MODIFY! // This file was generated by Djinni from geo_json_parser.djinni +#import "MCGeoJsonPoint.h" #import "MCVectorLayerFeatureInfo.h" #import @class MCGeoJsonFeatureParserInterface; @@ -12,4 +13,6 @@ - (nullable NSArray *)parse:(nonnull NSString *)geoJson; +- (nullable NSArray *)parseWithPointGeometry:(nonnull NSString *)geoJson; + @end diff --git a/bridging/ios/MCGeoJsonPoint+Private.h b/bridging/ios/MCGeoJsonPoint+Private.h new file mode 100644 index 000000000..5bb660d15 --- /dev/null +++ b/bridging/ios/MCGeoJsonPoint+Private.h @@ -0,0 +1,26 @@ +// AUTOGENERATED FILE - DO NOT MODIFY! +// This file was generated by Djinni from geo_json_parser.djinni +#ifdef __cplusplus + +#import "MCGeoJsonPoint.h" +#include "GeoJsonPoint.h" + +static_assert(__has_feature(objc_arc), "Djinni requires ARC to be enabled for this file"); + +@class MCGeoJsonPoint; + +namespace djinni_generated { + +struct GeoJsonPoint +{ + using CppType = ::GeoJsonPoint; + using ObjcType = MCGeoJsonPoint*; + + using Boxed = GeoJsonPoint; + + static CppType toCpp(ObjcType objc); + static ObjcType fromCpp(const CppType& cpp); +}; + +} // namespace djinni_generated +#endif diff --git a/bridging/ios/MCGeoJsonPoint+Private.mm b/bridging/ios/MCGeoJsonPoint+Private.mm new file mode 100644 index 000000000..decf007f3 --- /dev/null +++ b/bridging/ios/MCGeoJsonPoint+Private.mm @@ -0,0 +1,25 @@ +// AUTOGENERATED FILE - DO NOT MODIFY! +// This file was generated by Djinni from geo_json_parser.djinni + +#import "MCGeoJsonPoint+Private.h" +#import "DJIMarshal+Private.h" +#import "MCCoord+Private.h" +#import "MCVectorLayerFeatureInfo+Private.h" +#include + +namespace djinni_generated { + +auto GeoJsonPoint::toCpp(ObjcType obj) -> CppType +{ + assert(obj); + return {::djinni_generated::Coord::toCpp(obj.point), + ::djinni_generated::VectorLayerFeatureInfo::toCpp(obj.featureInfo)}; +} + +auto GeoJsonPoint::fromCpp(const CppType& cpp) -> ObjcType +{ + return [[MCGeoJsonPoint alloc] initWithPoint:(::djinni_generated::Coord::fromCpp(cpp.point)) + featureInfo:(::djinni_generated::VectorLayerFeatureInfo::fromCpp(cpp.featureInfo))]; +} + +} // namespace djinni_generated diff --git a/bridging/ios/MCGeoJsonPoint.h b/bridging/ios/MCGeoJsonPoint.h new file mode 100644 index 000000000..28bee8c0a --- /dev/null +++ b/bridging/ios/MCGeoJsonPoint.h @@ -0,0 +1,20 @@ +// AUTOGENERATED FILE - DO NOT MODIFY! +// This file was generated by Djinni from geo_json_parser.djinni + +#import "MCCoord.h" +#import "MCVectorLayerFeatureInfo.h" +#import + +@interface MCGeoJsonPoint : NSObject +- (nonnull instancetype)init NS_UNAVAILABLE; ++ (nonnull instancetype)new NS_UNAVAILABLE; +- (nonnull instancetype)initWithPoint:(nonnull MCCoord *)point + featureInfo:(nonnull MCVectorLayerFeatureInfo *)featureInfo NS_DESIGNATED_INITIALIZER; ++ (nonnull instancetype)geoJsonPointWithPoint:(nonnull MCCoord *)point + featureInfo:(nonnull MCVectorLayerFeatureInfo *)featureInfo; + +@property (nonatomic, readonly, nonnull) MCCoord * point; + +@property (nonatomic, readonly, nonnull) MCVectorLayerFeatureInfo * featureInfo; + +@end diff --git a/bridging/ios/MCGeoJsonPoint.mm b/bridging/ios/MCGeoJsonPoint.mm new file mode 100644 index 000000000..db6873d22 --- /dev/null +++ b/bridging/ios/MCGeoJsonPoint.mm @@ -0,0 +1,33 @@ +// AUTOGENERATED FILE - DO NOT MODIFY! +// This file was generated by Djinni from geo_json_parser.djinni + +#import "MCGeoJsonPoint.h" + + +@implementation MCGeoJsonPoint + +- (nonnull instancetype)initWithPoint:(nonnull MCCoord *)point + featureInfo:(nonnull MCVectorLayerFeatureInfo *)featureInfo +{ + if (self = [super init]) { + _point = point; + _featureInfo = featureInfo; + } + return self; +} + ++ (nonnull instancetype)geoJsonPointWithPoint:(nonnull MCCoord *)point + featureInfo:(nonnull MCVectorLayerFeatureInfo *)featureInfo +{ + return [[self alloc] initWithPoint:point + featureInfo:featureInfo]; +} + +#ifndef DJINNI_DISABLE_DESCRIPTION_METHODS +- (NSString *)description +{ + return [NSString stringWithFormat:@"<%@ %p point:%@ featureInfo:%@>", self.class, (void *)self, self.point, self.featureInfo]; +} + +#endif +@end diff --git a/djinni/map/layers/tiled/geo_json_parser.djinni b/djinni/map/layers/tiled/geo_json_parser.djinni index adfe3a3fb..ddbf0930f 100644 --- a/djinni/map/layers/tiled/geo_json_parser.djinni +++ b/djinni/map/layers/tiled/geo_json_parser.djinni @@ -1,7 +1,14 @@ - @extern "../../../yaml/vector_layer_feature_info.yaml" +@extern "../../../yaml/coord.yaml" geo_json_feature_parser_interface = interface +c { static create() : geo_json_feature_parser_interface; parse(geo_json: string): optional>; + + parseWithPointGeometry(geo_json: string): optional>; +} + +geo_json_point = record { + point: coord; + feature_info: vector_layer_feature_info; } diff --git a/djinni/yaml/geo_json_point.yaml b/djinni/yaml/geo_json_point.yaml new file mode 100644 index 000000000..0dcc0704f --- /dev/null +++ b/djinni/yaml/geo_json_point.yaml @@ -0,0 +1,39 @@ +# AUTOGENERATED FILE - DO NOT MODIFY! +# This file was generated by Djinni from geo_json_parser.djinni +name: geo_json_point +typedef: 'record' +params: [] +prefix: "" +cpp: + typename: '::GeoJsonPoint' + header: '"GeoJsonPoint.h"' + byValue: false +objc: + typename: 'MCGeoJsonPoint' + pointer: true + hash: '%s.hash' + boxed: 'MCGeoJsonPoint' + header: '"MCGeoJsonPoint.h"' +objcpp: + translator: '::djinni_generated::GeoJsonPoint' + header: '"MCGeoJsonPoint+Private.h"' +java: + reference: true + typename: 'io.openmobilemaps.mapscore.shared.map.layers.tiled.GeoJsonPoint' + writeToParcel: '%s.writeToParcel(out, flags)' + generic: true + readFromParcel: 'new %s(in)' + hash: '%s.hashCode()' + boxed: 'io.openmobilemaps.mapscore.shared.map.layers.tiled.GeoJsonPoint' +jni: + translator: '::djinni_generated::NativeGeoJsonPoint' + header: '"NativeGeoJsonPoint.h"' + typename: jobject + typeSignature: 'Lio/openmobilemaps/mapscore/shared/map/layers/tiled/GeoJsonPoint;' +wasm: + translator: '::djinni_generated::NativeGeoJsonPoint' + header: '"NativeGeoJsonPoint.h"' + typename: em::val +ts: + typename: GeoJsonPoint + module: './module' diff --git a/ios/graphics/Texture/TextureHolder.swift b/ios/graphics/Texture/TextureHolder.swift index bd9eb7a59..5fcef0928 100644 --- a/ios/graphics/Texture/TextureHolder.swift +++ b/ios/graphics/Texture/TextureHolder.swift @@ -75,6 +75,51 @@ public class TextureHolder: NSObject { let texture = try MetalContext.current.textureLoader.newTexture(data: data, options: options) self.init(texture, textureUsableSize: textureUsableSize) } + + public convenience init(_ size: CGSize, drawCallback: ((CGContext) -> Void)) throws { + guard size.width > 0, size.height > 0 else { + throw TextureHolderError.emptyData + } + + let width : Int = Int(size.width) + let height : Int = Int(size.height) + + let bytesPerPixel = 4 + let bytesPerRow = bytesPerPixel * width + + let td = MTLTextureDescriptor.texture2DDescriptor(pixelFormat: .rgba8Unorm, width: width, height: height, mipmapped: false) + td.usage = .shaderRead + + guard let texture = MetalContext.current.device.makeTexture(descriptor: td) else { + throw TextureHolderError.emptyData + } + + let length = 4 * width * height + guard let imageData = calloc(width * height, 4) else { + throw TextureHolderError.emptyData + } + + let data = NSMutableData(bytesNoCopy: imageData, length: length, freeWhenDone: true) + + let colorSpace = CGColorSpaceCreateDeviceRGB() + let bitmapInfo = CGBitmapInfo(rawValue: CGBitmapInfo.byteOrder32Big.rawValue | CGImageAlphaInfo.premultipliedLast.rawValue) + + guard let context = CGContext(data: data.mutableBytes, width: width, height: height, bitsPerComponent: 8, bytesPerRow: 4 * width, space: colorSpace, bitmapInfo: bitmapInfo.rawValue) else { + throw TextureHolderError.emptyData + } + + context.translateBy(x: 0, y: size.height) + context.scaleBy(x: 1.0, y: -1.0) + + UIGraphicsPushContext(context) + drawCallback(context) + UIGraphicsPopContext() + + let region = MTLRegionMake2D(0, 0, width, height) + texture.replace(region: region, mipmapLevel: 0, withBytes: data.bytes, bytesPerRow: bytesPerRow) + + self.init(texture, textureUsableSize: nil) + } } extension TextureHolder: MCTextureHolderInterface { diff --git a/shared/public/GeoJsonFeatureParser.h b/shared/public/GeoJsonFeatureParser.h index 9ef8cfe9d..c8865e175 100644 --- a/shared/public/GeoJsonFeatureParser.h +++ b/shared/public/GeoJsonFeatureParser.h @@ -18,4 +18,6 @@ class GeoJsonFeatureParser: public GeoJsonFeatureParserInterface { GeoJsonFeatureParser(); std::optional> parse(const std::string & geoJson) override; + + std::optional> parseWithPointGeometry(const std::string & geoJson) override; }; diff --git a/shared/public/GeoJsonFeatureParserInterface.h b/shared/public/GeoJsonFeatureParserInterface.h index 1d6a6f7ea..d679c99fb 100644 --- a/shared/public/GeoJsonFeatureParserInterface.h +++ b/shared/public/GeoJsonFeatureParserInterface.h @@ -9,6 +9,8 @@ #include #include +struct GeoJsonPoint; + class GeoJsonFeatureParserInterface { public: virtual ~GeoJsonFeatureParserInterface() = default; @@ -16,4 +18,6 @@ class GeoJsonFeatureParserInterface { static /*not-null*/ std::shared_ptr create(); virtual std::optional> parse(const std::string & geoJson) = 0; + + virtual std::optional> parseWithPointGeometry(const std::string & geoJson) = 0; }; diff --git a/shared/public/GeoJsonPoint.h b/shared/public/GeoJsonPoint.h new file mode 100644 index 000000000..5c22f2ad9 --- /dev/null +++ b/shared/public/GeoJsonPoint.h @@ -0,0 +1,19 @@ +// AUTOGENERATED FILE - DO NOT MODIFY! +// This file was generated by Djinni from geo_json_parser.djinni + +#pragma once + +#include "Coord.h" +#include "VectorLayerFeatureInfo.h" +#include + +struct GeoJsonPoint final { + ::Coord point; + ::VectorLayerFeatureInfo featureInfo; + + GeoJsonPoint(::Coord point_, + ::VectorLayerFeatureInfo featureInfo_) + : point(std::move(point_)) + , featureInfo(std::move(featureInfo_)) + {} +}; diff --git a/shared/public/Value.h b/shared/public/Value.h index 92d981521..b7dd3f428 100644 --- a/shared/public/Value.h +++ b/shared/public/Value.h @@ -122,6 +122,18 @@ class FeatureContext { initialize(); } + FeatureContext(vtzero::GeomType geomType, + mapType propertiesMap, + const std::string &stringIdentifier): + propertiesMap(std::move(propertiesMap)), + geomType(geomType) { + size_t hash = 0; + std::hash_combine(hash, std::hash{}(stringIdentifier)); + identifier = hash; + + initialize(); + } + FeatureContext(vtzero::feature const &feature) { geomType = feature.geometry_type(); diff --git a/shared/src/map/layers/tiled/vector/geojson/GeoJsonFeatureParser.cpp b/shared/src/map/layers/tiled/vector/geojson/GeoJsonFeatureParser.cpp index ec83af4fc..c9e3c99d8 100644 --- a/shared/src/map/layers/tiled/vector/geojson/GeoJsonFeatureParser.cpp +++ b/shared/src/map/layers/tiled/vector/geojson/GeoJsonFeatureParser.cpp @@ -11,7 +11,7 @@ #include "GeoJsonFeatureParser.h" #include "GeoJsonFeatureParserInterface.h" #include "GeoJsonParser.h" - +#include "GeoJsonPoint.h" GeoJsonFeatureParser::GeoJsonFeatureParser() {} @@ -30,3 +30,13 @@ std::optional> GeoJsonFeatureParser::parse return std::nullopt; } } + +std::optional> GeoJsonFeatureParser::parseWithPointGeometry(const std::string & geoJson) { + try { + const auto& json = nlohmann::json::parse(geoJson); + return GeoJsonParser::getPointsWithProperties(json); + } + catch (nlohmann::json::parse_error &ex) { + return std::nullopt; + } +} diff --git a/shared/src/map/layers/tiled/vector/geojson/GeoJsonParser.h b/shared/src/map/layers/tiled/vector/geojson/GeoJsonParser.h index 2b935b9e1..d974cfaf0 100644 --- a/shared/src/map/layers/tiled/vector/geojson/GeoJsonParser.h +++ b/shared/src/map/layers/tiled/vector/geojson/GeoJsonParser.h @@ -14,6 +14,7 @@ #include #include "simplify.hpp" #include "CoordinateSystemIdentifiers.h" +#include "GeoJsonPoint.h" class UUIDGenerator { private: @@ -45,15 +46,15 @@ class GeoJsonParser { UUIDGenerator generator; std::shared_ptr geoJson = std::make_shared(); - for (const auto feature: geojson["features"]) { + for (const auto &feature: geojson["features"]) { if (!feature["geometry"].is_object() || !feature["geometry"]["type"].is_string() || !feature["geometry"]["coordinates"].is_array()) { LogError <<= "Geojson feature is not valid"; continue; } - const auto geometryType = feature["geometry"]["type"]; - const auto coordinates = feature["geometry"]["coordinates"]; + const auto &geometryType = feature["geometry"]["type"]; + const auto &coordinates = feature["geometry"]["coordinates"]; std::shared_ptr geometry; vtzero::GeomType geomType; if (geometryType == "Point") { @@ -84,9 +85,13 @@ class GeoJsonParser { continue; } - const auto properties = parseProperties(feature["properties"]); + const auto &properties = parseProperties(feature["properties"]); - geometry->featureContext = std::make_shared(geomType, properties, generator.generateUUID()); + if(feature["id"].is_string()) { + geometry->featureContext = std::make_shared(geomType, properties, feature["id"].get()); + } else { + geometry->featureContext = std::make_shared(geomType, properties, generator.generateUUID()); + } geoJson->geometries.push_back(geometry); } @@ -94,46 +99,128 @@ class GeoJsonParser { return geoJson; } + static std::vector<::GeoJsonPoint> getPointsWithProperties(const nlohmann::json &geojson) { + // preconditions + std::vector<::GeoJsonPoint> points; + + if (!geojson["type"].is_string() || + geojson["type"] != "FeatureCollection" || + !geojson["features"].is_array()) { + LogError <<= "Geojson is not valid"; + assert(false); + return points; + } + + for (const auto &feature: geojson["features"]) { + const auto &geometry = feature["geometry"]; + + if (!geometry.is_object()) { + LogError <<= "Geojson feature is not valid"; + continue; + } + + const auto &geometryType = geometry["type"]; + if (!geometryType.is_string() || geometryType != "Point") { + continue; + } + + if(!feature["id"].is_string()) { + continue; + } + + const auto &coordinates = geometry["coordinates"]; + + if(!coordinates.is_array()) { + continue; + } + + points.emplace_back(getPoint(coordinates), GeoJsonParser::getFeatureInfo(feature["properties"], feature["id"].get())); + } + + return points; + } + private: static FeatureContext::mapType parseProperties(const nlohmann::json &properties) { FeatureContext::mapType propertyMap; if (properties.is_object()) { - for (auto&[key, val]: properties.items()) { + for (const auto &[key, val] : properties.items()) { if (val.is_string()){ - propertyMap.push_back(std::make_pair(std::string(key), FeatureContext::valueType(val.get()))); + propertyMap.emplace_back(key, val.get()); } else if (val.is_number_integer()){ - propertyMap.push_back(std::make_pair(std::string(key), FeatureContext::valueType(val.get()))); + propertyMap.emplace_back(key, val.get()); } else if (val.is_number_float()){ - propertyMap.push_back(std::make_pair(std::string(key), FeatureContext::valueType(val.get()))); + propertyMap.emplace_back(key, val.get()); } else if (val.is_boolean()){ - propertyMap.push_back(std::make_pair(std::string(key), FeatureContext::valueType(val.get()))); + propertyMap.emplace_back(key, val.get()); } else if (val.is_array() && !val.empty() && val[0].is_number()){ std::vector valueArray; valueArray.reserve(val.size()); - for(const auto value: val) { - valueArray.push_back(value.get()); + for(const auto &value: val) { + valueArray.emplace_back(value.get()); } - propertyMap.push_back(std::make_pair(std::string(key), FeatureContext::valueType(valueArray))); + propertyMap.emplace_back(key, FeatureContext::valueType(valueArray)); } else if (val.is_array() && !val.empty() && val[0].is_string()){ std::vector valueArray; valueArray.reserve(val.size()); - for(const auto value: val) { + for(const auto &value: val) { valueArray.push_back(value.get()); } - propertyMap.push_back(std::make_pair(std::string(key), FeatureContext::valueType(valueArray))); - + propertyMap.emplace_back(key, valueArray); } } } + return propertyMap; } + static VectorLayerFeatureInfo getFeatureInfo(const nlohmann::json &properties, const std::string& identifier) { + + auto info = VectorLayerFeatureInfo(identifier, {}); + + if (properties.is_object()) { + for (const auto &[key, val] : properties.items()) { + if (val.is_string()){ + info.properties.insert({ key, VectorLayerFeatureInfoValue(val.get(), std::nullopt, std::nullopt, std::nullopt, std::nullopt, std::nullopt, std::nullopt) }); + } else if (val.is_number_integer()) { + info.properties.insert({ key, VectorLayerFeatureInfoValue(std::nullopt, std::nullopt, val.get(), std::nullopt, std::nullopt, std::nullopt, std::nullopt) }); + } else if (val.is_number_float()){ + info.properties.insert({ key, VectorLayerFeatureInfoValue(std::nullopt, val.get(), std::nullopt, std::nullopt, std::nullopt, std::nullopt, std::nullopt) }); + } else if (val.is_boolean()){ + info.properties.insert({ key, VectorLayerFeatureInfoValue(std::nullopt, std::nullopt, std::nullopt, val.get(), std::nullopt, std::nullopt, std::nullopt) }); + } else if (val.is_array() && !val.empty() && val[0].is_number()){ + std::vector valueArray; + valueArray.reserve(val.size()); + for(const auto &value: val) { + valueArray.emplace_back(value.get()); + } + + info.properties.insert({ key, VectorLayerFeatureInfoValue(std::nullopt, std::nullopt, std::nullopt, std::nullopt, std::nullopt, valueArray, std::nullopt) }); + } else if (val.is_array() && !val.empty() && val[0].is_string()){ + std::vector valueArray; + valueArray.reserve(val.size()); + for(const auto &value: val) { + valueArray.push_back(value.get()); + } + + info.properties.insert({ key, VectorLayerFeatureInfoValue(std::nullopt, std::nullopt, std::nullopt, std::nullopt, std::nullopt, std::nullopt, valueArray) }); + } + } + } + + return info; + } + static std::shared_ptr parsePoint(const nlohmann::json &coordinates) { auto geometry = std::make_shared(); geometry->coordinates.emplace_back(std::vector{parseCoordinate(coordinates)}); return geometry; } + static Coord getPoint(const nlohmann::json &coordinates) { + return parseCoordinate(coordinates); + } + static std::shared_ptr parseMultiPoint(const nlohmann::json &coordinates) { auto geometry = std::make_shared(); for (const auto &coord : coordinates) {