Closed lorenh closed 3 years ago
can you share the code that throws the error? maybe can be easier to understand what happens, in the meantime...
anyway, geometries can be read using
var reader = new GeoPackageGeoReader();
return reader.Read(bytes);
but this allows you "only" to read the geometry part of a geopackage table, you need to read the "attributes" and the binary field by yourself using ADO.NET . Check the test source code to have some suggestions about how to handle this
static void Main(string[] args)
{
var bytes = File.ReadAllBytes(@"myfile.gpkg");
var reader = new GeoPackageGeoReader();
var geometry = reader.Read(bytes);
}
Throws this:
System.ArgumentException HResult=0x80070057 Message=Geometry type not recognized. GeometryCode: 0 Source=NetTopologySuite StackTrace: at NetTopologySuite.IO.WKBReader.Read(BinaryReader reader) at NetTopologySuite.IO.WKBReader.Read(Stream stream) at NetTopologySuite.IO.GeoPackageGeoReader.Read(Stream stream) at NetTopologySuite.IO.GeoPackageGeoReader.Read(Byte[] blob) at GeoPkgTest.Program.Main(String[] args) in C:\Research\GeoPkgTest\GeoPkgTest\Program.cs:line 15
OK, I had a completely flawed view of how to work with this library. IGNORE the previous post (after you stop laughing). After going through the unit test code it is MUCH clearer how to open the file first using ADO.NET and then the geometry reader is just applied to the bytes in the geometry column. I think I'm on the right track now.
after you stop laughing
no problem at all: it's a common practice for me to laugh reading the code I wrote few years ago, so... welcome to the club
I would like to ask two related questions. Using the method described about, I could read Geometry data from .gpkg file, but failed in trying to do these two things:
var precisionModel = new PrecisionModel(PrecisionModels.**FloatingSingle**); // **Fixed** will not have decimal digit
var coordinateSequenceFactory = NtsGeometryServices.Instance.DefaultCoordinateSequenceFactory;
var reader = new GeoPackageGeoReader(coordinateSequenceFactory, precisionModel)
{
HandleOrdinates = Ordinates.XYZ
};
var geometry = reader.Read(byteVal);
// Here is a sample coordinate from the above output **(-96.10387420654297,45.18833541870117)**. What is the proper way of limiting the decimal point digits to 6?
var options = new JsonSerializerOptions()
{
WriteIndented = false,
//IgnoreNullValues = true,
DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull,
};
options.Converters.Add(new GeoJsonConverterFactory());
JsonSerializer.Serialize(utf8MemoryWriter, feature, jsonSerializerOptions);
// here is an example GeoJson output:
{"type":"Feature",**"bbox":[-96,45,-96,45]**,"geometry":{"type":"Polygon","coordinates":[[[-96,45],[-96,45],[-96,45],[-96,45],[-96,45],[-96,45],[-96,45]]]},"properties":{"parcelnumb":"01-0318-000","owner":"STOCK/PAUL \u0026 CINDY/ETAL","address":"2555 95TH ST, akron, mn 56227","description":"Private"}}
Thx.
Set lat / lon to 6 digits after decimal point:
Just tested this code against latest main and it works on my machine (@2007 CodingHorror)
var p = GeometryFactory.Default.CreatePoint(new Coordinate(-93.4252045886767, 41.8704218292759));
var writer = new GeoPackageGeoWriter();
byte[] bytes = writer.Write(p);
var precisionModel = new PrecisionModel(PrecisionModels.FloatingSingle);
var coordinateSequenceFactory = NtsGeometryServices.Instance.DefaultCoordinateSequenceFactory;
var reader = new GeoPackageGeoReader(coordinateSequenceFactory, precisionModel);
var geometry = reader.Read(bytes);
Console.WriteLine(geometry.ToText()); // POINT (-93.425201 41.870422)
Set the BoundingBoxto null for the output.
Again, I was unable to reproduce your output using this code
var fac = GeometryFactory.Default;
var p = fac.CreatePoint(new Coordinate(-93.4252045886767, 41.8704218292759));
var a = new AttributesTable
{
{ "id", 1 }
};
var f = new Feature(p, a);
var options = new JsonSerializerOptions()
{
WriteIndented = false
};
options.Converters.Add(new GeoJsonConverterFactory(fac));
string s = JsonSerializer.Serialize(f, options);
Console.WriteLine(s); // {"type":"Feature","geometry":{"type":"Point","coordinates":[-93.4252045886767,41.8704218292759]},"properties":{"id":1}}
Thx for looking into this, and sorry for the late response. I need to clarify my questions - I failed to set 6-digit & exclude null BBox when writing the data out to a GeoJson file.
Using your example, and just updating the output method:
var p = GeometryFactory.Default.CreatePoint(new Coordinate(-93.4252045886767, 41.8704218292759));
var writer = new GeoPackageGeoWriter();
byte[] bytes = writer.Write(p);
var precisionModel = new PrecisionModel(PrecisionModels.FloatingSingle); // single precision
var coordinateSequenceFactory = NtsGeometryServices.Instance.DefaultCoordinateSequenceFactory;
var reader = new GeoPackageGeoReader(coordinateSequenceFactory, precisionModel);
var geometry = reader.Read(bytes);
// I need to output the data read from .gpkg file into a GeoJson file
var geometryFactory = NtsGeometryServices.Instance.CreateGeometryFactory(precisionModel); // Set precision to 6 digits
var converterFactory = new GeoJsonConverterFactory(geometryFactory);
var options = new JsonSerializerOptions()
{
WriteIndented = false,
DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull,
Converters = { converterFactory }
};
// the Serializer actually outputs more digits than the original data, though both the reader & writer has precision model set.
var geometryString = JsonSerializer.Serialize(geometry, options); // in my real code, the JsonSerializer will write to a file
Console.WriteLine(geometryString); // {"type":"Point","coordinates":[-93.42520141601562,41.87042236328125]}
For the 2nd question, it's similar. The feature read from .gpkg file has a BoundingBox value of null, and I have the writeGeometryBBox set to false, but the file output still has bbox populated
var feature = // read from .gpkg file (I don't have a simple way to create a sample here)
var geometryFactory = NtsGeometryServices.Instance.CreateGeometryFactory(precisionModel); // I want the FloatingSingle precision
var writeGeometryBBox = false; // I don't want the BBox
var converterFactory = new GeoJsonConverterFactory(geometryFactory, writeGeometryBBox);
var options = new JsonSerializerOptions()
{
WriteIndented = false,
DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull,
Converters = { converterFactory }
};
var featureString = JsonSerializer.Serialize(feature, options); // this will still write bbox out to file
So my original two questions stand unanswered, and I hope there is a way to get the output right.
p.s.: The GeoJson file output for coordinates has these decimal digits based on setting. It seems to me that FloatingSingle & Floating gets switched around. I'm interested to know how to set it to 6 digits.
PrecisionModels.FloatingSingle ==> 14 decimal digits
PrecisionModels.Floating ==> 7 decimal digits
PrecisionModels.Fixed ==> No decimal digits
It looks like using the scale will get the digits right. Now I just need to figure out a way to remove the bbox in output.
var precisionModel = new PrecisionModel(1000000); // this will set decimal digits to 6
JsonSerializer
writes the coordinate data with the [maximum precision] available (https://github.com/NetTopologySuite/NetTopologySuite.IO.GeoJSON/blob/develop/src/NetTopologySuite.IO.GeoJSON4STJ/Converters/StjGeometryConverter.Coordinates.cs#L32)
var fac = new GeometryFactory(new PrecisionModel(100));
var p = fac.CreatePoint(new Coordinate(-93.4252045886767, 41.8704218292759));
Console.WriteLine(p.ToText()); //POINT (-93.425 41.87)
Console.WriteLine($"{p.CoordinateSequence.GetX(0)} {p.CoordinateSequence.GetY(0)}"); // -93.4252045886767 41.8704218292759
var a = new AttributesTable { { "id", 1 } };
var f = new Feature(p, a);
var converterFactory = new GeoJsonConverterFactory(fac, false);
var options = new JsonSerializerOptions() { Converters = { converterFactory } };
string s = JsonSerializer.Serialize(f, options);
Console.WriteLine(s); // {"type":"Feature","geometry":{"type":"Point","coordinates":[-93.4252045886767,41.8704218292759]},"properties":{"id":1}}
I was unable to reproduce your issue with the bbox
same behavious using newtonsoft-based serializer
var fac = new GeometryFactory(new PrecisionModel(100));
var p = fac.CreatePoint(new Coordinate(-93.4252045886767, 41.8704218292759));
Console.WriteLine(p.ToText());
Console.WriteLine($"{p.CoordinateSequence.GetX(0)} {p.CoordinateSequence.GetY(0)}");
var a = new AttributesTable { { "id", 1 }, { "test", 2 } };
var f = new Feature(p, a);
var serializer = GeoJsonSerializer.CreateDefault();
var sb = new StringBuilder();
using (var sw = new StringWriter(sb))
{
serializer.Serialize(sw, f);
}
Console.WriteLine(sb.ToString());
OK, here is the code to reproduce the bbox issue. It looks like that a polygon will produce this. If you look at the feature object in debugging mode, it's BoundingBox has null value.
Anyway, my goal is to get rid of bbox (in the feature) from GeoJson file output.
var fc = new FeatureCollection();
var fac = new GeometryFactory(new PrecisionModel(1000000));
var converterFactory = new GeoJsonConverterFactory(fac, false); // should NOT write out bbox in any of the output
var options = new JsonSerializerOptions() { Converters = { converterFactory } };
var a = new AttributesTable { { "id", 1 } };
var coors = new Coordinate[] {
new Coordinate(-89.863283,47.963199),
new Coordinate(-89.862819,47.963009),
new Coordinate(-89.86361,47.961897),
new Coordinate(-89.863596,47.963326),
new Coordinate(-89.863283,47.963199)
};
var poly = fac.CreatePolygon(coors);
var feature = new Feature(poly, a);
fc.Add(feature);
string jsonString1 = JsonSerializer.Serialize(feature, options); // feature: "bbox":[-89.867797,47.948167,-89.846676,47.963326]
string jsonString2 = JsonSerializer.Serialize(fc, options); // FeatureCollection: bbox is null, and inside feature has bbox
I finally reproduced the issue, looks that writeGeometryBbox is not considered during serialization of feature (and feature collection) bbox
Have you tried to prohibit output of null
values on the json serializer side?
var options = new JsonSerializerOptions() {
Converters = { converterFactory },
// now
DefaultIgnoreCondition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingNull
//old
//IgnoreNullValues = true
};
@FObermaier yep, but this workaround still writes the bbox for the feature object. additionally, this prevent to write all the null values, not only the bbox.
Here you have an example:
await using SqliteConnection connection = new(@$"Data Source=gis_data.gpkg");
await connection.OpenAsync();
await using SqliteCommand command = connection.CreateCommand();
command.CommandText = @"
SELECT the_geom, name,
FROM countries
";
var gpkgReader = new GeoPackageGeoReader();
await using SqliteDataReader dataReader = command.ExecuteReader();
while (await dataReader .ReadAsync())
{
byte[] geometryBlob = await dataReader .GetFieldValueAsync<byte[]>(0);
Geometry geometry = gpkgReader.Read(geometryBlob);
string name = dataReader .GetString(1);
Console.WriteLine($"{geometry.GeometryType}: {name}");
}
I'm trying to piece together how to do this, but get a "Geometry type not recognized. GeometryCode: 0", so I know I'm not doing something right. A working example of reading a *.gpkg would really help.