Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Port GeoJSON #6

Merged
merged 4 commits into from
Jan 29, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions PolygonClipper.sln
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution
README.md = README.md
EndProjectSection
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GeoJson", "tests\GeoJson\GeoJson.csproj", "{F881441F-D3B6-4B48-8CEF-8DC0746D4578}"
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".github", ".github", "{6F7A3AE6-C7D1-441F-A203-BA6575931CE2}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "workflows", "workflows", "{EEA3B5E9-3337-42EE-B0BF-FA586A1BC435}"
Expand All @@ -46,6 +47,10 @@ Global
{6997B09A-A419-4469-A51C-D21973E4F800}.Debug|Any CPU.Build.0 = Debug|Any CPU
{6997B09A-A419-4469-A51C-D21973E4F800}.Release|Any CPU.ActiveCfg = Release|Any CPU
{6997B09A-A419-4469-A51C-D21973E4F800}.Release|Any CPU.Build.0 = Release|Any CPU
{F881441F-D3B6-4B48-8CEF-8DC0746D4578}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{F881441F-D3B6-4B48-8CEF-8DC0746D4578}.Debug|Any CPU.Build.0 = Debug|Any CPU
{F881441F-D3B6-4B48-8CEF-8DC0746D4578}.Release|Any CPU.ActiveCfg = Release|Any CPU
{F881441F-D3B6-4B48-8CEF-8DC0746D4578}.Release|Any CPU.Build.0 = Release|Any CPU
{F1965B36-D896-4BC1-8941-7FE6CEB82CD5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{F1965B36-D896-4BC1-8941-7FE6CEB82CD5}.Debug|Any CPU.Build.0 = Debug|Any CPU
{F1965B36-D896-4BC1-8941-7FE6CEB82CD5}.Release|Any CPU.ActiveCfg = Release|Any CPU
Expand All @@ -57,6 +62,7 @@ Global
GlobalSection(NestedProjects) = preSolution
{3C8D945E-6074-437E-B6EA-237BD0C80411} = {F0882E95-667C-48FE-B43A-24FC55D9F354}
{6997B09A-A419-4469-A51C-D21973E4F800} = {84C4B358-492B-46FC-9F11-FBB76B06BD3F}
{F881441F-D3B6-4B48-8CEF-8DC0746D4578} = {84C4B358-492B-46FC-9F11-FBB76B06BD3F}
{6F7A3AE6-C7D1-441F-A203-BA6575931CE2} = {6B327CDE-88A5-4715-A8A3-E9A8C80EA10C}
{EEA3B5E9-3337-42EE-B0BF-FA586A1BC435} = {6F7A3AE6-C7D1-441F-A203-BA6575931CE2}
{F1965B36-D896-4BC1-8941-7FE6CEB82CD5} = {84C4B358-492B-46FC-9F11-FBB76B06BD3F}
Expand Down
61 changes: 61 additions & 0 deletions tests/GeoJson/Converters/BoundingBoxConverter.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
// Copyright © Joerg Battermann 2014, Matt Hunt 2017

using GeoJSON.Text.Geometry;
using System;
using System.Collections.ObjectModel;
using System.Text.Json;
using System.Text.Json.Serialization;

namespace GeoJSON.Text.Converters
{
/// <summary>
/// Converts <see cref="IGeometryObject"/> types to and from JSON.
/// </summary>
public class BoundingBoxConverter : JsonConverter<double[]>
{
/// <summary>
/// Determines whether this instance can convert the specified object type.
/// </summary>
/// <param name="objectType">Type of the object.</param>
/// <returns>
/// <c>true</c> if this instance can convert the specified object type; otherwise, <c>false</c>.
/// </returns>
public override bool CanConvert(Type objectType)
{
return typeof(double[]).IsAssignableFromType(objectType);
}

/// <summary>
/// Reads the JSON representation of the object.
/// </summary>
/// <param name="reader">The <see cref="T:Newtonsoft.Json.JsonReader" /> to read from.</param>
/// <param name="objectType">Type of the object.</param>
/// <param name="existingValue">The existing value of object being read.</param>
/// <param name="serializer">The calling serializer.</param>
/// <returns>
/// The object value.
/// </returns>
public override double[] Read(
ref Utf8JsonReader reader,
Type type,
JsonSerializerOptions options)
{
return JsonSerializer.Deserialize<double[]>(ref reader, options);
}

/// <summary>
/// Writes the JSON representation of the object.
/// </summary>
/// <param name="writer">The <see cref="T:Newtonsoft.Json.JsonWriter" /> to write to.</param>
/// <param name="value">The value.</param>
/// <param name="serializer">The calling serializer.</param>
public override void Write(
Utf8JsonWriter writer,
double[] value,
JsonSerializerOptions options)
{
// Standard serialization
JsonSerializer.Serialize(writer, value, typeof(double[]), options);
}
}
}
37 changes: 37 additions & 0 deletions tests/GeoJson/Converters/CRSBaseRequiredPropertyConverter.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
using GeoJSON.Text.CoordinateReferenceSystem;
using System;
using System.Text.Json;
using System.Text.Json.Serialization;

namespace GeoJSON.Text.Converters
{
public class CRSBaseRequiredPropertyConverter : JsonConverter<CRSBase>
{
public override CRSBase Read(
ref Utf8JsonReader reader,
Type type,
JsonSerializerOptions options)
{
// Don't pass in options when recursively calling Deserialize.
CRSBase? crsBaseClass = JsonSerializer.Deserialize<CRSBase>(ref reader);

if (crsBaseClass.Type == default)
throw new JsonException("Required property Type not set in the JSON");

if (crsBaseClass.Properties == default)
throw new JsonException("Required property Properties not set in the JSON");

// Check for required fields set by values in JSON
return crsBaseClass;
}

public override void Write(
Utf8JsonWriter writer,
CRSBase crsBaseClass,
JsonSerializerOptions options)
{
// Don't pass in options when recursively calling Serialize.
JsonSerializer.Serialize(writer, crsBaseClass);
}
}
}
150 changes: 150 additions & 0 deletions tests/GeoJson/Converters/CrsConverter.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,150 @@
// Copyright © Joerg Battermann 2014, Matt Hunt 2017

using System;
using System.Text.Json;
using System.Text.Json.Serialization;
using GeoJSON.Text.CoordinateReferenceSystem;

namespace GeoJSON.Text.Converters
{
/// <summary>
/// Converts <see cref="ICRSObject"/> types to and from JSON.
/// </summary>
public class CrsConverter : JsonConverter<object>
{
public override bool HandleNull => true;

/// <summary>
/// Determines whether this instance can convert the specified object type.
/// </summary>
/// <param name="objectType">Type of the object.</param>
/// <returns>
/// <c>true</c> if this instance can convert the specified object type; otherwise, <c>false</c>.
/// </returns>
public override bool CanConvert(Type objectType)
{
return typeof(ICRSObject).IsAssignableFromType(objectType);
}

/// <summary>
/// Reads the JSON representation of the object.
/// </summary>
/// <param name="reader">The <see cref="T:Newtonsoft.Json.JsonReader" /> to read from.</param>
/// <param name="objectType">Type of the object.</param>
/// <param name="existingValue">The existing value of object being read.</param>
/// <param name="serializer">The calling serializer.</param>
/// <returns>
/// The object value.
/// </returns>
/// <exception cref="Newtonsoft.Json.JsonReaderException">
/// CRS must be null or a json object
/// or
/// CRS must have a "type" property
/// </exception>
public override object Read(
ref Utf8JsonReader reader,
Type type,
JsonSerializerOptions options)
{
if (reader.TokenType == JsonTokenType.Null)
{
return new UnspecifiedCRS();
}
if (reader.TokenType != JsonTokenType.StartObject)
{
throw new JsonException("CRS must be null or a json object");
}

JsonElement jObject = JsonDocument.ParseValue(ref reader).RootElement;
JsonElement token;
if (!jObject.TryGetProperty("type", out token))
{
throw new JsonException("CRS must have a \"type\" property");
}

string? crsType = token.GetString();

if (string.Equals("name", crsType, StringComparison.OrdinalIgnoreCase))
{
if (jObject.TryGetProperty("properties", out JsonElement properties))
{
string? name = properties.GetProperty("name").GetString();

NamedCRS? target = new NamedCRS(name);
NamedCRS? converted = jObject.Deserialize<NamedCRS>();

if (converted.Properties != null)
{
foreach (KeyValuePair<string, object> item in converted?.Properties)
{
target.Properties[item.Key] = item.Value;
}
}

return target;
}
}
else if (string.Equals("link", crsType, StringComparison.OrdinalIgnoreCase))
{
if (jObject.TryGetProperty("properties", out JsonElement properties))
{
string? href = properties.GetProperty("href").GetString();

LinkedCRS? target = new LinkedCRS(href);

LinkedCRS? converted = jObject.Deserialize<LinkedCRS>();

if (converted.Properties != null)
{
foreach (KeyValuePair<string, object> item in converted?.Properties)
{
target.Properties[item.Key] = item.Value;
}
}

return target;
}
}

return new NotSupportedException(string.Format("Type {0} unexpected.", crsType));
}

/// <summary>
/// Writes the JSON representation of the object.
/// </summary>
/// <param name="writer">The <see cref="T:Newtonsoft.Json.JsonWriter" /> to write to.</param>
/// <param name="value">The value.</param>
/// <param name="serializer">The calling serializer.</param>
/// <exception cref="System.ArgumentOutOfRangeException"></exception>
public override void Write(
Utf8JsonWriter writer,
object crsValue,
JsonSerializerOptions options)
{
ICRSObject? value = (ICRSObject)crsValue;

if(value == null)
return;

switch (value.Type)
{
case CRSType.Name:
//var nameObject = (NamedCRS)value;
//var serializedName = JsonSerializer.Serialize(nameObject, options);
JsonSerializer.Serialize(writer, value, typeof(NamedCRS), options);
break;
case CRSType.Link:
//var linkedObject = (LinkedCRS)value;
//var serializedLink = JsonSerializer.Serialize(linkedObject, options);
//writer.WriteRawValue(serializedLink);
JsonSerializer.Serialize(writer, value, typeof(LinkedCRS), options);
break;
case CRSType.Unspecified:
writer.WriteNullValue();
break;
default:
throw new ArgumentOutOfRangeException();
}
}
}
}
117 changes: 117 additions & 0 deletions tests/GeoJson/Converters/FeatureConverter.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
// Copyright © Joerg Battermann 2014, Matt Hunt 2017

using GeoJSON.Text.Feature;
using GeoJSON.Text.Geometry;
using System;
using System.Collections.Generic;
using System.Reflection;
using System.Text.Json;
using System.Text.Json.Serialization;

namespace GeoJSON.Text.Converters
{
/// <summary>
/// Converts <see cref="IGeometryObject"/> types to and from JSON.
/// </summary>
public class FeatureConverter : JsonConverter<object>
{
private static readonly GeometryConverter geometryConverter = new GeometryConverter();
/// <summary>
/// Determines whether this instance can convert the specified object type.
/// </summary>
/// <param name="objectType">Type of the object.</param>
/// <returns>
/// <c>true</c> if this instance can convert the specified object type; otherwise, <c>false</c>.
/// </returns>
public override bool CanConvert(Type objectType)
{
return typeof(Feature.Feature).IsAssignableFromType(objectType)
|| typeof(Feature.Feature<>).IsAssignableFromType(objectType)
|| typeof(Feature.Feature<,>).IsAssignableFromType(objectType);
}

public override object Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{
Type[]? genericArguments = new Type[2];
IGeometryObject geometryObject = null;
object properties = null;
string id = null;
if (reader.TokenType == JsonTokenType.StartObject)
{
int startDepth = reader.CurrentDepth;
while (reader.Read())
{
if (JsonTokenType.EndObject == reader.TokenType && reader.CurrentDepth == startDepth)
{
if (genericArguments.Length == 0)
{
return new Feature.Feature(geometryObject, (IDictionary<string, object>)properties, id);
}
else if (genericArguments.Length == 1)
{
object? typedGeometry = Convert.ChangeType(geometryObject, genericArguments[0]);
return Activator.CreateInstance(
typeof(Feature<>).MakeGenericType(
genericArguments),
BindingFlags.Default,
binder: null,
args: new object[] { typedGeometry, (IDictionary<string, object>)properties, id },
culture: null);
}
else
{
object? typedGeometry = Convert.ChangeType(geometryObject, genericArguments[0]);
object? typedProperty = Convert.ChangeType(properties, genericArguments[1]);
return Activator.CreateInstance(
typeof(Feature<,>).MakeGenericType(
genericArguments),
BindingFlags.Default,
binder: null,
args: new object[] { typedGeometry, typedProperty, id },
culture: null);
}
}

if (reader.TokenType == JsonTokenType.PropertyName)
{
string? propertyName = reader.GetString();
genericArguments = typeToConvert.GetGenericArguments();
if (propertyName == "geometry")
{
reader.Read(); //Move one step forward to get property value
geometryObject = (IGeometryObject)geometryConverter.Read(ref reader, typeof(IGeometryObject), options);
}
else if (propertyName == "properties")
{
if (genericArguments.Length > 1)
{
Type? typeOfProperties = typeToConvert.GetGenericArguments()[1]; // This is the argument for the property if set
properties = JsonSerializer.Deserialize(ref reader, typeOfProperties, options);
}
else
{
properties = JsonSerializer.Deserialize<IDictionary<string, object>>(ref reader, options);
}
}
else if (propertyName == "id")
{
reader.Read(); //Move one step forward to get property value
id = reader.GetString();
}
}
}

throw new JsonException("End of object was not found");
}
else
{
throw new JsonException("Json to parse of not of type object");
}
}

public override void Write(Utf8JsonWriter writer, object value, JsonSerializerOptions options)
{
JsonSerializer.Serialize(writer, value, options);
}
}
}
Loading
Loading