diff --git a/src/NetTopologySuite.IO.VectorTiles.Mapbox/MapboxTileWriter.cs b/src/NetTopologySuite.IO.VectorTiles.Mapbox/MapboxTileWriter.cs
index 917c6b5..ba008aa 100644
--- a/src/NetTopologySuite.IO.VectorTiles.Mapbox/MapboxTileWriter.cs
+++ b/src/NetTopologySuite.IO.VectorTiles.Mapbox/MapboxTileWriter.cs
@@ -9,6 +9,12 @@ namespace NetTopologySuite.IO.VectorTiles.Mapbox
// see: https://github.com/mapbox/vector-tile-spec/tree/master/2.1
public static class MapboxTileWriter
{
+ private const uint DefaultMinLinealExtent = 1;
+
+ private const uint DefaultMinPolygonalExtent = 2;
+
+ private const string DefaultIdAttributeName = "id";
+
///
/// Writes the tiles in a /z/x/y.mvt folder structure.
///
@@ -16,7 +22,19 @@ public static class MapboxTileWriter
/// The path.
/// The extent.
/// Replaces the files if they are already present.
+ [Obsolete("Use overload that can specify minLineal- and minPolygonalExtent")]
public static void Write(this VectorTileTree tree, string path, uint extent = 4096)
+ => Write(tree, path, DefaultMinLinealExtent, DefaultMinPolygonalExtent, extent, DefaultIdAttributeName);
+
+ ///
+ /// Writes the tiles in a /z/x/y.mvt folder structure.
+ ///
+ /// The tree.
+ /// The path.
+ /// The extent.
+ /// Replaces the files if they are already present.
+ public static void Write(this VectorTileTree tree, string path, uint minLinealExtent, uint minPolygonalExtent,
+ uint extent = 4096, string idAttributeName = DefaultIdAttributeName)
{
IEnumerable GetTiles()
{
@@ -26,7 +44,7 @@ IEnumerable GetTiles()
}
}
- GetTiles().Write(path, extent);
+ GetTiles().Write(path, minLinealExtent, minPolygonalExtent, extent, idAttributeName);
}
///
@@ -36,7 +54,19 @@ IEnumerable GetTiles()
/// The path.
/// The extent.
/// Replaces the files if they are already present.
+ [Obsolete("Use overload that can specify minLineal- and minPolygonalExtent")]
public static void Write(this IEnumerable vectorTiles, string path, uint extent = 4096)
+ => Write(vectorTiles, path, DefaultMinLinealExtent, DefaultMinPolygonalExtent, extent, DefaultIdAttributeName);
+
+ ///
+ /// Writes the tiles in a /z/x/y.mvt folder structure.
+ ///
+ /// The tiles.
+ /// The path.
+ /// The extent.
+ /// Replaces the files if they are already present.
+ public static void Write(this IEnumerable vectorTiles, string path, uint minLinealExtent, uint minPolygonalExtent,
+ uint extent = 4096, string idAttributeName = DefaultIdAttributeName)
{
foreach (var vectorTile in vectorTiles)
{
@@ -54,7 +84,7 @@ public static void Write(this IEnumerable vectorTiles, string path,
string file = Path.Combine(xFolder, $"{tile.Y}.mvt");
using var stream = File.Open(file, FileMode.Create);
- vectorTile.Write(stream, extent);
+ vectorTile.Write(stream, minLinealExtent, minPolygonalExtent, extent, idAttributeName);
}
}
@@ -65,7 +95,21 @@ public static void Write(this IEnumerable vectorTiles, string path,
/// The stream to write to.
/// The extent.
/// The name of an attribute property to use as the ID for the Feature. Vector tile feature ID's should be integer or ulong numbers.
+ [Obsolete("Use overload that can specify minLineal- and minPolygonalExtent")]
public static void Write(this VectorTile vectorTile, Stream stream, uint extent = 4096, string idAttributeName = "id")
+ => Write(vectorTile, stream, DefaultMinLinealExtent, DefaultMinPolygonalExtent, extent, idAttributeName);
+
+ ///
+ /// Writes the tile to the given stream.
+ ///
+ /// The vector tile.
+ /// The stream to write to.
+ /// The minimum length in pixel one of a lineal feature's extent edges has to have in order for the feature to be written. The default is 1.
+ /// The minimum area in pixel one of a polygonal feature's extent has to have in order for the feature to be written. The default is 2.
+ /// The extent.
+ /// The name of an attribute property to use as the ID for the Feature. Vector tile feature ID's should be integer or ulong numbers.
+ public static void Write(this VectorTile vectorTile, Stream stream, uint minLinealExtent, uint minPolygonalExtent,
+ uint extent = 4096, string idAttributeName = "id")
{
var tile = new Tiles.Tile(vectorTile.TileId);
var tgt = new TileGeometryTransform(tile, extent);
@@ -95,11 +139,11 @@ public static void Write(this VectorTile vectorTile, Stream stream, uint extent
break;
case ILineal lineal:
feature.Type = Tile.GeomType.LineString;
- EncodeTo(feature.Geometry, lineal, tgt);
+ EncodeTo(feature.Geometry, lineal, minLinealExtent, tgt);
break;
case IPolygonal polygonal:
feature.Type = Tile.GeomType.Polygon;
- EncodeTo(feature.Geometry, polygonal, tgt, tile.Zoom);
+ EncodeTo(feature.Geometry, polygonal, minPolygonalExtent, tgt);
break;
default:
feature.Type = Tile.GeomType.Unknown;
@@ -236,39 +280,46 @@ private static void EncodeTo(List destination, IPuntal puntal, TileGeometr
destination[moveToIndex] = GenerateCommandInteger(MapboxCommandType.MoveTo, (destination.Count - moveToIndex) / 2);
}
- private static void EncodeTo(List destination, ILineal lineal, TileGeometryTransform tgt)
+ private static void EncodeTo(List destination, ILineal lineal, uint minLinealExtent, TileGeometryTransform tgt)
{
+ bool HasValidLength((long x, long y) tpl)
+ => tpl.x >= minLinealExtent || tpl.y >= minLinealExtent;
+
var geometry = (Geometry)lineal;
int currentX = 0, currentY = 0;
for (int i = 0; i < geometry.NumGeometries; i++)
{
var lineString = (LineString)geometry.GetGeometryN(i);
- if (tgt.IsGreaterThanOnePixelOfTile(lineString))
+ if (HasValidLength(tgt.ExtentInPixel(lineString.EnvelopeInternal)))
EncodeTo(destination, lineString.CoordinateSequence, tgt, ref currentX, ref currentY, false);
}
}
- private static void EncodeTo(List destination, IPolygonal polygonal, TileGeometryTransform tgt, int zoom)
+ private static void EncodeTo(List destination, IPolygonal polygonal, uint minPolygonalExtent, TileGeometryTransform tgt)
{
+ bool HasValidExtent((long x, long y) tpl)
+ => (tpl.x > minPolygonalExtent && tpl.y > 0) ||
+ (tpl.x > 0 && tpl.y > minPolygonalExtent);
+
var geometry = (Geometry)polygonal;
- //Test the whole polygon geometry is larger than a single pixel.
- if (tgt.IsGreaterThanOnePixelOfTile(geometry))
+ //Test the whole polygonal geometry has a valid extent.
+ if (HasValidExtent(tgt.ExtentInPixel(geometry.EnvelopeInternal)))
{
int currentX = 0, currentY = 0;
for (int i = 0; i < geometry.NumGeometries; i++)
{
var polygon = (Polygon)geometry.GetGeometryN(i);
- // Test that the exterior ring is larger than a single pixel.
- if (!tgt.IsGreaterThanOnePixelOfTile(polygon.ExteriorRing))
+ // Test that the exterior ring has a valid extent
+ if (!HasValidExtent(tgt.ExtentInPixel(polygon.ExteriorRing.EnvelopeInternal)))
continue;
EncodeTo(destination, polygon.ExteriorRing.CoordinateSequence, tgt, ref currentX, ref currentY, true, false);
foreach (var interiorRing in polygon.InteriorRings)
{
- // Test that the interior ring is larger than a single pixel.
- if (!tgt.IsGreaterThanOnePixelOfTile(interiorRing))
+ // Test that the interior ring has a valid extent.
+ if (!HasValidExtent(tgt.ExtentInPixel(interiorRing.EnvelopeInternal)))
continue;
EncodeTo(destination, interiorRing.CoordinateSequence, tgt, ref currentX, ref currentY, true, true);
diff --git a/src/NetTopologySuite.IO.VectorTiles.Mapbox/TileGeometryTransform.cs b/src/NetTopologySuite.IO.VectorTiles.Mapbox/TileGeometryTransform.cs
index b729b32..f99b8f2 100644
--- a/src/NetTopologySuite.IO.VectorTiles.Mapbox/TileGeometryTransform.cs
+++ b/src/NetTopologySuite.IO.VectorTiles.Mapbox/TileGeometryTransform.cs
@@ -119,5 +119,13 @@ public bool IsGreaterThanOnePixelOfTile(Geometry geometry)
// Both must be greater than 0, and at least one of them needs to be larger than 1.
return dx > 0 && dy > 0 && (dx > 1 || dy > 1);
}
+
+ public (long x, long y) ExtentInPixel(Envelope env)
+ {
+ (long minX, long minY) = WebMercatorHandler.FromMetersToPixels(WebMercatorHandler.LatLonToMeters(env.MinY, env.MinX), ZoomResolution);
+ (long maxX, long maxY) = WebMercatorHandler.FromMetersToPixels(WebMercatorHandler.LatLonToMeters(env.MaxY, env.MaxX), ZoomResolution);
+
+ return (maxX - minX, maxY - minY);
+ }
}
}
diff --git a/test/NetTopologySuite.IO.VectorTiles.Samples/Startup.cs b/test/NetTopologySuite.IO.VectorTiles.Samples/Startup.cs
index e9f9593..5dcf396 100644
--- a/test/NetTopologySuite.IO.VectorTiles.Samples/Startup.cs
+++ b/test/NetTopologySuite.IO.VectorTiles.Samples/Startup.cs
@@ -113,7 +113,7 @@ public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
tree.GetExtents(out Pages.IndexModel._BBox, out Pages.IndexModel._MinZoom, out Pages.IndexModel._MaxZoom);
- tree.Write("wwwroot/tiles");
+ tree.Write("wwwroot/tiles", 1, 2);
}
}
}
diff --git a/test/NetTopologySuite.IO.VectorTiles.Tests.Functional/Program.cs b/test/NetTopologySuite.IO.VectorTiles.Tests.Functional/Program.cs
index 15c136c..31624a0 100644
--- a/test/NetTopologySuite.IO.VectorTiles.Tests.Functional/Program.cs
+++ b/test/NetTopologySuite.IO.VectorTiles.Tests.Functional/Program.cs
@@ -56,7 +56,7 @@ private static void RunTest(string fileName, int minZoom, int maxZoom, string st
};
// write the tiles to disk as mvt.
- Mapbox.MapboxTileWriter.Write(tree, "tiles");
+ Mapbox.MapboxTileWriter.Write(tree, "tiles", 1, 2);
stopwatch.Stop();
diff --git a/test/NetTopologySuite.IO.VectorTiles.Tests/Issues/Issue29.cs b/test/NetTopologySuite.IO.VectorTiles.Tests/Issues/Issue29.cs
index fc32748..4478982 100644
--- a/test/NetTopologySuite.IO.VectorTiles.Tests/Issues/Issue29.cs
+++ b/test/NetTopologySuite.IO.VectorTiles.Tests/Issues/Issue29.cs
@@ -34,7 +34,7 @@ private static void WriteTile(int x, int y, int z)
MemoryStream? ms;
using (ms = new MemoryStream(1024 * 32))
{
- vectorTile.Write(ms);
+ vectorTile.Write(ms, 1, 2);
result = ms.ToArray();
}
diff --git a/test/NetTopologySuite.IO.VectorTiles.Tests/Mapbox/FeatureIdTest.cs b/test/NetTopologySuite.IO.VectorTiles.Tests/Mapbox/FeatureIdTest.cs
index 81815f7..adf0793 100644
--- a/test/NetTopologySuite.IO.VectorTiles.Tests/Mapbox/FeatureIdTest.cs
+++ b/test/NetTopologySuite.IO.VectorTiles.Tests/Mapbox/FeatureIdTest.cs
@@ -42,11 +42,11 @@ private void FeatureIdReadWriteTest(Features.Feature featureS, ulong expectedId,
if (string.IsNullOrEmpty(idAttributeName))
{
//No ID property specified when writing.
- vtS.Write(ms);
+ vtS.Write(ms, 1, 2);
}
else
{
- vtS.Write(ms, 4096, idAttributeName);
+ vtS.Write(ms, 1, 2, 4096, idAttributeName);
}
ms.Position = 0;
diff --git a/test/NetTopologySuite.IO.VectorTiles.Tests/RoundTripBase.cs b/test/NetTopologySuite.IO.VectorTiles.Tests/RoundTripBase.cs
index 8815df1..d25d953 100644
--- a/test/NetTopologySuite.IO.VectorTiles.Tests/RoundTripBase.cs
+++ b/test/NetTopologySuite.IO.VectorTiles.Tests/RoundTripBase.cs
@@ -73,7 +73,7 @@ protected void AssertRoundTrip(Geometry inputGeometry, Geometry expectedGeometry
VectorTile? vtD = null;
using (var ms = new MemoryStream())
{
- vtS.Write(ms, Extent);
+ vtS.Write(ms, 1, 2, Extent);
ms.Position = 0;
vtD = new MapboxTileReader(Factory).Read(ms, new VectorTiles.Tiles.Tile(0));
}
@@ -153,7 +153,7 @@ protected void AssertRoundTripEmptyTile(string inputDefinition, string? name = n
VectorTile? vtD = null;
using (var ms = new MemoryStream())
{
- vtS.Write(ms);
+ vtS.Write(ms, 1, 1);
ms.Position = 0;
vtD = new MapboxTileReader(Factory).Read(ms, new VectorTiles.Tiles.Tile(0));
}
diff --git a/test/NetTopologySuite.IO.VectorTiles.Tests/Tilers/MapboxTileWriterTest.cs b/test/NetTopologySuite.IO.VectorTiles.Tests/Tilers/MapboxTileWriterTest.cs
index c09c8a9..65792fc 100644
--- a/test/NetTopologySuite.IO.VectorTiles.Tests/Tilers/MapboxTileWriterTest.cs
+++ b/test/NetTopologySuite.IO.VectorTiles.Tests/Tilers/MapboxTileWriterTest.cs
@@ -83,7 +83,7 @@ private static void DoTestEmpty(Geometry geom, int numGeoms = 0)
var f1 = new Feature(geom, att);
l1.Features.Add(f1);
using var ms = new MemoryStream();
- var e = Record.Exception(() => MapboxTileWriter.Write(vt1, ms));
+ var e = Record.Exception(() => MapboxTileWriter.Write(vt1, ms, 1, 2));
Assert.Null(e);
Assert.True(ms.Length > 0);