diff --git a/MEOS.NET.Tests/CatalogValueTypeTests.cs b/MEOS.NET.Tests/CatalogValueTypeTests.cs new file mode 100644 index 0000000..0f4ceab --- /dev/null +++ b/MEOS.NET.Tests/CatalogValueTypeTests.cs @@ -0,0 +1,49 @@ +using MEOS.NET.Enums; +using MEOS.NET.Structures; +using MEOS.NET.Types; + +namespace MEOS.NET.Tests +{ + /// + /// Whatever the wrapper already states as a C# value — an enum the catalog + /// names, a scalar behind a MEOS typedef, a struct carried by value — the + /// object layer hands on as that value. + /// + [TestClass] + public class CatalogValueTypeTests : MeosTest + { + [TestMethod] + public void AnEnumArgumentIsTheEnumTheCatalogNames() + { + TJsonb temp = (TJsonb)TJsonb.In( + "[{\"a\": [1, 2]}@2024-12-06, {\"a\": [1, 2]}@2024-12-08]")!; + + Temporal? first = temp.ArrayElement(0, true, NullHandleType.NullJsonNull); + + Assert.IsNotNull(first); + } + + [TestMethod] + public void AScalarBehindATypedefComesBackAsThatScalar() + { + Geo point = Geo.FromText("POINT(1 1)", 4326)!; + + ulong cell = point.ToS2cellCell(10); + + Assert.AreNotEqual(0UL, cell); + } + + [TestMethod] + public void AStructReturnedByValueComesBackAsTheStruct() + { + TPoint trip = (TPoint)TGeomPoint.In( + "[POINT(0 0)@2024-12-06, POINT(2 2)@2024-12-08]")!; + STBox bounds = STBox.In("STBOX X((-1,-1),(3,3))")!; + + MvtGeom mvt = trip.AsMvtgeom(bounds, 4096, 0, true); + + Assert.AreNotEqual(IntPtr.Zero, mvt.geom); + Assert.IsTrue(mvt.count > 0); + } + } +} diff --git a/MEOS.NET/Functions/Meos.Native.g.cs b/MEOS.NET/Functions/Meos.Native.g.cs index fea9327..6b22b0d 100644 --- a/MEOS.NET/Functions/Meos.Native.g.cs +++ b/MEOS.NET/Functions/Meos.Native.g.cs @@ -9213,6 +9213,24 @@ private static partial class Native [LibraryImport(DllPath, EntryPoint = "geo_edge_ctx_make", StringMarshalling = StringMarshalling.Utf8)] internal static partial IntPtr GeoEdgeCtxMake(IntPtr gs); + [LibraryImport(DllPath, EntryPoint = "geo_is_planar_linear", StringMarshalling = StringMarshalling.Utf8)] + [return: MarshalAs(UnmanagedType.U1)] + internal static partial bool GeoIsPlanarLinear(IntPtr gs); + + [LibraryImport(DllPath, EntryPoint = "geo_is_point_set", StringMarshalling = StringMarshalling.Utf8)] + [return: MarshalAs(UnmanagedType.U1)] + internal static partial bool GeoIsPointSet(IntPtr gs); + + [LibraryImport(DllPath, EntryPoint = "geo_meos_supported", StringMarshalling = StringMarshalling.Utf8)] + [return: MarshalAs(UnmanagedType.U1)] + internal static partial bool GeoMeosSupported(IntPtr gs); + + [LibraryImport(DllPath, EntryPoint = "geo_points_covered", StringMarshalling = StringMarshalling.Utf8)] + internal static partial IntPtr GeoPointsCovered(IntPtr pts, IntPtr gs, [MarshalAs(UnmanagedType.U1)] bool covered); + + [LibraryImport(DllPath, EntryPoint = "geo_clip_linear_geom", StringMarshalling = StringMarshalling.Utf8)] + internal static partial IntPtr GeoClipLinearGeom(IntPtr line, IntPtr gs, [MarshalAs(UnmanagedType.U1)] bool inside); + [LibraryImport(DllPath, EntryPoint = "geo_edge_ctx_free", StringMarshalling = StringMarshalling.Utf8)] internal static partial void GeoEdgeCtxFree(IntPtr ctx); diff --git a/MEOS.NET/Functions/Meos.meos_internal_geo.g.cs b/MEOS.NET/Functions/Meos.meos_internal_geo.g.cs index 04d0ff3..e204ad7 100644 --- a/MEOS.NET/Functions/Meos.meos_internal_geo.g.cs +++ b/MEOS.NET/Functions/Meos.meos_internal_geo.g.cs @@ -252,6 +252,21 @@ public static IntPtr TgeoseqsetRestrictStbox(IntPtr ss, IntPtr box, bool border_ public static IntPtr GeoEdgeCtxMake(IntPtr gs) => SafeExecution(() => Native.GeoEdgeCtxMake(gs)); + public static bool GeoIsPlanarLinear(IntPtr gs) + => SafeExecution(() => Native.GeoIsPlanarLinear(gs)); + + public static bool GeoIsPointSet(IntPtr gs) + => SafeExecution(() => Native.GeoIsPointSet(gs)); + + public static bool GeoMeosSupported(IntPtr gs) + => SafeExecution(() => Native.GeoMeosSupported(gs)); + + public static IntPtr GeoPointsCovered(IntPtr pts, IntPtr gs, bool covered) + => SafeExecution(() => Native.GeoPointsCovered(pts, gs, covered)); + + public static IntPtr GeoClipLinearGeom(IntPtr line, IntPtr gs, bool inside) + => SafeExecution(() => Native.GeoClipLinearGeom(line, gs, inside)); + public static void GeoEdgeCtxFree(IntPtr ctx) => SafeExecution(() => Native.GeoEdgeCtxFree(ctx)); diff --git a/MEOS.NET/Types/Geo.g.cs b/MEOS.NET/Types/Geo.g.cs index 0a1b42e..6993294 100644 --- a/MEOS.NET/Types/Geo.g.cs +++ b/MEOS.NET/Types/Geo.g.cs @@ -96,6 +96,9 @@ public ulong ToH3indexCell(int resolution) public ulong ToQuadbinCell(int resolution) => Meos.GeoToQuadbinCell(this.Ptr, resolution); + public ulong ToS2cellCell(int level) + => Meos.GeoToS2cellCell(this.Ptr, level); + public Set? ToSet() => MEOSFactory.WrapSet(Meos.GeoToSet(this.Ptr)); diff --git a/MEOS.NET/Types/Jsonb.g.cs b/MEOS.NET/Types/Jsonb.g.cs index 3f390e7..b38b1a1 100644 --- a/MEOS.NET/Types/Jsonb.g.cs +++ b/MEOS.NET/Types/Jsonb.g.cs @@ -233,6 +233,12 @@ public bool ToBool() public string ToCstring() => Meos.JsonbToCstring(this.Ptr); + public float ToFloat4() + => Meos.JsonbToFloat4(this.Ptr); + + public double ToFloat8() + => Meos.JsonbToFloat8(this.Ptr); + public short ToInt16() => Meos.JsonbToInt16(this.Ptr); diff --git a/MEOS.NET/Types/Raquet.g.cs b/MEOS.NET/Types/Raquet.g.cs index eb367d6..f159d41 100644 --- a/MEOS.NET/Types/Raquet.g.cs +++ b/MEOS.NET/Types/Raquet.g.cs @@ -143,6 +143,25 @@ public int Width() public static Raquet? In(string str) => MEOSFactory.WrapRaquet(Meos.RaquetIn(str)); + public static Raquet? Make(ulong quadbin, int width, int height, MeosPixType pixtype, double nodata, bool has_nodata, byte[] pixels) + { + GCHandle _pixels = GCHandle.Alloc(pixels, GCHandleType.Pinned); + try + { + return MEOSFactory.WrapRaquet(Meos.RaquetMake(quadbin, width, height, (int) pixtype, nodata, has_nodata, _pixels.AddrOfPinnedObject(), (ulong) pixels.Length)); + } + finally + { + _pixels.Free(); + } + } + + public static MeosPixType PixtypeFromString(string str) + => (MeosPixType) Meos.RaquetPixtypeFromString(str); + + public static ulong PixtypeSize(MeosPixType pixtype) + => Meos.RaquetPixtypeSize((int) pixtype); + public static Raquet? Read(string path, ulong quadbin) => MEOSFactory.WrapRaquet(Meos.RaquetRead(path, quadbin)); diff --git a/MEOS.NET/Types/TGeo.g.cs b/MEOS.NET/Types/TGeo.g.cs index 3de465c..ad0bcda 100644 --- a/MEOS.NET/Types/TGeo.g.cs +++ b/MEOS.NET/Types/TGeo.g.cs @@ -61,6 +61,9 @@ internal TGeo(IntPtr ptr) : base(ptr) { } public STBox?[] SpaceBoxes(double xsize, double ysize, double zsize, Geo sorigin, bool bitmatrix, bool border_inc) => MEOSFactory.WrapSTBoxArray(Meos.TgeoSpaceBoxes(this.Ptr, xsize, ysize, zsize, sorigin.Ptr, bitmatrix, border_inc)); + public SpaceSplit SpaceSplit(double xsize, double ysize, double zsize, Geo sorigin, bool bitmatrix, bool border_inc) + => Meos.TgeoSpaceSplit(this.Ptr, xsize, ysize, zsize, sorigin.Ptr, bitmatrix, border_inc); + public STBox?[] SpaceTimeBoxes(double xsize, double ysize, double zsize, Interval duration, Geo sorigin, DateTime torigin, bool bitmatrix, bool border_inc) { IntPtr _duration = Marshal.AllocHGlobal(Marshal.SizeOf()); @@ -75,6 +78,20 @@ internal TGeo(IntPtr ptr) : base(ptr) { } } } + public SpaceTimeSplit SpaceTimeSplit(double xsize, double ysize, double zsize, Interval duration, Geo sorigin, DateTime torigin, bool bitmatrix, bool border_inc) + { + IntPtr _duration = Marshal.AllocHGlobal(Marshal.SizeOf()); + try + { + Marshal.StructureToPtr(duration, _duration, false); + return Meos.TgeoSpaceTimeSplit(this.Ptr, xsize, ysize, zsize, _duration, sorigin.Ptr, MEOSConvert.ToTimestampTz(torigin), bitmatrix, border_inc); + } + finally + { + Marshal.FreeHGlobal(_duration); + } + } + public STBox?[] SplitEachNStboxes(int elem_count) => MEOSFactory.WrapSTBoxArray(Meos.TgeoSplitEachNStboxes(this.Ptr, elem_count)); diff --git a/MEOS.NET/Types/TJsonb.g.cs b/MEOS.NET/Types/TJsonb.g.cs index 9d58489..9534c9a 100644 --- a/MEOS.NET/Types/TJsonb.g.cs +++ b/MEOS.NET/Types/TJsonb.g.cs @@ -18,6 +18,9 @@ internal TJsonb(IntPtr ptr) : base(ptr) { } public override string ToString() => this.Out(); + public Temporal? ArrayElement(int idx, bool astext, NullHandleType null_handle) + => MEOSFactory.WrapTemporal(Meos.TjsonbArrayElement(this.Ptr, idx, astext, (int) null_handle)); + public Temporal? ArrayLength() => MEOSFactory.WrapTemporal(Meos.TjsonbArrayLength(this.Ptr)); @@ -131,6 +134,25 @@ public override string ToString() } } + public Temporal? ExtractPath(Text[] path_elems, bool astext, NullHandleType null_handle) + { + IntPtr[] _path_elemsValues = new IntPtr[path_elems.Length]; + for (int i = 0; i < path_elems.Length; i++) + { + _path_elemsValues[i] = path_elems[i].Ptr; + } + + GCHandle _path_elems = GCHandle.Alloc(_path_elemsValues, GCHandleType.Pinned); + try + { + return MEOSFactory.WrapTemporal(Meos.TjsonbExtractPath(this.Ptr, _path_elems.AddrOfPinnedObject(), path_elems.Length, astext, (int) null_handle)); + } + finally + { + _path_elems.Free(); + } + } + public Temporal? Insert(Text[] keys, Jsonb newjb, bool after) { IntPtr[] _keysValues = new IntPtr[keys.Length]; @@ -153,6 +175,9 @@ public override string ToString() public Temporal? MinusValue(Jsonb jsb) => MEOSFactory.WrapTemporal(Meos.TjsonbMinusValue(this.Ptr, jsb.Ptr)); + public Temporal? ObjectField(Text key, bool astext, NullHandleType null_handle) + => MEOSFactory.WrapTemporal(Meos.TjsonbObjectField(this.Ptr, key.Ptr, astext, (int) null_handle)); + public string Out() => Meos.TjsonbOut(this.Ptr); @@ -184,9 +209,21 @@ public string Out() public Temporal? StripNulls(bool strip_in_arrays) => MEOSFactory.WrapTemporal(Meos.TjsonbStripNulls(this.Ptr, strip_in_arrays)); + public Temporal? ToTbool(string key, NullHandleType null_handle) + => MEOSFactory.WrapTemporal(Meos.TjsonbToTbool(this.Ptr, key, (int) null_handle)); + + public Temporal? ToTfloat(string key, InterpType interp, NullHandleType null_handle) + => MEOSFactory.WrapTemporal(Meos.TjsonbToTfloat(this.Ptr, key, (int) interp, (int) null_handle)); + + public Temporal? ToTint(string key, NullHandleType null_handle) + => MEOSFactory.WrapTemporal(Meos.TjsonbToTint(this.Ptr, key, (int) null_handle)); + public Temporal? ToTtext() => MEOSFactory.WrapTemporal(Meos.TjsonbToTtext(this.Ptr)); + public Temporal? ToTtextKey(string key, NullHandleType null_handle) + => MEOSFactory.WrapTemporal(Meos.TjsonbToTtextKey(this.Ptr, key, (int) null_handle)); + public Jsonb? ValueAtTimestamptz(DateTime t, bool strict) { IntPtr _value = Marshal.AllocHGlobal(8); diff --git a/MEOS.NET/Types/TPoint.g.cs b/MEOS.NET/Types/TPoint.g.cs index f17ed83..0bbf72a 100644 --- a/MEOS.NET/Types/TPoint.g.cs +++ b/MEOS.NET/Types/TPoint.g.cs @@ -17,6 +17,9 @@ internal TPoint(IntPtr ptr) : base(ptr) { } public Temporal? AngularDifference() => MEOSFactory.WrapTemporal(Meos.TpointAngularDifference(this.Ptr)); + public MvtGeom AsMvtgeom(STBox bounds, int extent, int buffer, bool clip_geom) + => Meos.TpointAsMvtgeom(this.Ptr, bounds.Ptr, extent, buffer, clip_geom); + public Temporal? AtElevation(Span s) => MEOSFactory.WrapTemporal(Meos.TpointAtElevation(this.Ptr, s.Ptr)); diff --git a/MEOS.NET/Types/Text.g.cs b/MEOS.NET/Types/Text.g.cs index fca12d3..4148362 100644 --- a/MEOS.NET/Types/Text.g.cs +++ b/MEOS.NET/Types/Text.g.cs @@ -18,6 +18,12 @@ internal Text(IntPtr ptr) : base(ptr) { } public override string ToString() => this.Out(); + public uint Hash(uint collid) + => Meos.TextHash(this.Ptr, collid); + + public ulong HashExtended(ulong seed, uint collid) + => Meos.TextHashExtended(this.Ptr, seed, collid); + public Text? Initcap() => MEOSFactory.WrapText(Meos.TextInitcap(this.Ptr)); diff --git a/tools/objectgen.py b/tools/objectgen.py index f5c48f3..6f9cae7 100644 --- a/tools/objectgen.py +++ b/tools/objectgen.py @@ -74,14 +74,18 @@ "DateADT *": ("DateOnly", 4, "MEOSConvert.ToDateOnly(Marshal.ReadInt32({0}))"), } -# C types the object layer passes and returns as C# scalars, keyed by the cleaned -# C spelling. Everything else is either a wrapped class pointer or deferred. -PASSTHROUGH_C = { - "bool", "char", "int", "int8", "int8_t", "uint8", "uint8_t", "int16", "int16_t", - "uint16", "uint16_t", "int32", "int32_t", "uint32", "uint32_t", "int64", - "int64_t", "uint64", "uint64_t", "long", "double", "float", "size_t", - "char *", "void", -} +def is_scalar(cs_type: str) -> bool: + """Whether the wrapper already states this as a C# value the layer hands on. + + The wrapper's own C# type is what decides it: codegen resolves every scalar + the catalog names — through the typedefs it carries, so `S2CellId` reads + `ulong` and `float8` reads `double` — and a struct it carries by value is a + value too. A second list of C spellings here would answer for the ones the + layer was told about and defer the rest.""" + return cs_type in _SCALAR_CS or cs_type in codegen.BY_VALUE_STRUCTS + + +_SCALAR_CS = set(codegen.SCALAR_MAP.values()) | {"void", "bool", "string", "string?"} # Acronym runs the error names carry, kept upper-case so the C# spelling reads # the way the catalog's own camelCase names do (`asMFJSON`). @@ -308,7 +312,9 @@ def map_return(self, f: dict, wrapper_ret: str) -> tuple[str, str] | None: return ("DateOnly", "MEOSConvert.ToDateOnly($)") if wrapper_ret in ("string", "string?"): return (wrapper_ret, "$") - if c in PASSTHROUGH_C and wrapper_ret != "IntPtr": + if c in codegen.ENUM_TYPES: + return (enum_type_name(c), f"({enum_type_name(c)}) $") + if is_scalar(wrapper_ret): return (wrapper_ret, "$") cls = self.m.class_for_ctype(c) if cls and wrapper_ret == "IntPtr": @@ -361,9 +367,13 @@ def map_param(self, c_type: str, cs_type: str, name: str) -> tuple[str, str] | N return ("DateTime", f"MEOSConvert.ToTimestampTz({name})") if c == "DateADT": return ("DateOnly", f"MEOSConvert.ToDateADT({name})") - if c == "interpType": - return ("InterpType", f"(int) {name}") - if c in PASSTHROUGH_C and cs_type != "IntPtr": + if c in codegen.ENUM_TYPES: + # The wrapper takes the enum's value as an int, and the catalog + # already names every enum the surface uses — so a method takes the + # C# enum whichever one it is, rather than the one enum the layer + # was told about. + return (enum_type_name(c), f"(int) {name}") + if is_scalar(cs_type): return (cs_type, name) if cs_type == "string": return ("string", name)