Skip to content

Commit ae59cfd

Browse files
committed
feat: add get_feature_as for i32 and i16 support
Use num-traits as direct dependency. geo-types already uses num-traits. Fixes #578
1 parent 849063c commit ae59cfd

File tree

5 files changed

+84
-38
lines changed

5 files changed

+84
-38
lines changed

Cargo.lock

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,8 @@ protoc = ["prost-build"]
1818
protoc-generated = ["prost-build"]
1919

2020
[dependencies]
21-
geo-types = "0.7"
21+
geo-types = { version = "0.7", default-features = false }
22+
num-traits = { version = "0.2", default-features = false, features = ["libm"] }
2223
prost = { version = "0.13", default-features = false, features = ["prost-derive", "std"] }
2324
wasm-bindgen = { version = "0.2", optional = true }
2425
serde-wasm-bindgen = { version = "0.6", optional = true }

src/feature.rs

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,7 @@
99
//! - `Feature`: Represents a feature with geometry, an optional id and optional properties.
1010
1111
use std::collections::HashMap;
12-
13-
use geo_types::Geometry;
12+
use geo_types::{CoordNum, Geometry};
1413

1514
/// An enumeration representing the value of a property associated with a feature.
1615
#[derive(Debug, Clone, PartialEq, PartialOrd)]
@@ -27,9 +26,9 @@ pub enum Value {
2726

2827
/// A structure representing a feature in a vector tile.
2928
#[derive(Debug, Clone)]
30-
pub struct Feature {
29+
pub struct Feature<T: CoordNum = f32> {
3130
/// The geometry of the feature.
32-
pub geometry: Geometry<f32>,
31+
pub geometry: Geometry<T>,
3332

3433
/// Optional identifier for the feature.
3534
pub id: Option<u64>,
@@ -38,7 +37,7 @@ pub struct Feature {
3837
pub properties: Option<HashMap<String, Value>>,
3938
}
4039

41-
impl Feature {
40+
impl<T: CoordNum> Feature<T> {
4241
/// Retrieves the geometry of the feature.
4342
///
4443
/// # Returns
@@ -60,7 +59,7 @@ impl Feature {
6059
/// let geometry = feature.get_geometry();
6160
/// println!("{:?}", geometry);
6261
/// ```
63-
pub fn get_geometry(&self) -> &Geometry<f32> {
62+
pub fn get_geometry(&self) -> &Geometry<T> {
6463
&self.geometry
6564
}
6665
}

src/lib.rs

Lines changed: 51 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -73,9 +73,10 @@ mod vector_tile;
7373

7474
use feature::{Feature, Value};
7575
use geo_types::{
76-
Coord, Geometry, LineString, MultiLineString, MultiPoint, MultiPolygon, Point, Polygon,
76+
Coord, CoordNum, Geometry, LineString, MultiLineString, MultiPoint, MultiPolygon, Point, Polygon,
7777
};
7878
use layer::Layer;
79+
use num_traits::NumCast;
7980
use prost::{Message, bytes::Bytes};
8081
use vector_tile::{Tile, tile::GeomType};
8182

@@ -210,6 +211,41 @@ impl Reader {
210211
/// }
211212
/// ```
212213
pub fn get_features(&self, layer_index: usize) -> Result<Vec<Feature>, error::ParserError> {
214+
self.get_features_as::<f32>(layer_index)
215+
}
216+
217+
/// Retrieves the features of a specific layer with geometry coordinates in the specified numeric type.
218+
///
219+
/// This is a generic version of [`get_features`](Reader::get_features) that allows you to choose
220+
/// the coordinate type for the geometry. Supported types include `f32` (default), `i32`, and `i16`.
221+
///
222+
/// # Arguments
223+
///
224+
/// * `layer_index` - The index of the layer.
225+
///
226+
/// # Type Parameters
227+
///
228+
/// * `T` - The numeric type for geometry coordinates (e.g. `f32`, `i32`, `i16`).
229+
///
230+
/// # Returns
231+
///
232+
/// A result containing a vector of features if successful, or a `ParserError` if there is an error parsing the tile or accessing the layer.
233+
///
234+
/// # Examples
235+
///
236+
/// ```
237+
/// use mvt_reader::Reader;
238+
///
239+
/// let data = vec![/* Vector tile data */];
240+
/// let reader = Reader::new(data).unwrap();
241+
///
242+
/// // Get features with i32 coordinates
243+
/// let features = reader.get_features_as::<i32>(0);
244+
///
245+
/// // Get features with i16 coordinates
246+
/// let features = reader.get_features_as::<i16>(0);
247+
/// ```
248+
pub fn get_features_as<T: CoordNum>(&self, layer_index: usize) -> Result<Vec<Feature<T>>, error::ParserError> {
213249
let layer = self.tile.layers.get(layer_index);
214250
match layer {
215251
Some(layer) => {
@@ -218,7 +254,7 @@ impl Reader {
218254
if let Some(geom_type) = feature.r#type {
219255
match GeomType::try_from(geom_type) {
220256
Ok(geom_type) => {
221-
let parsed_geometry = match parse_geometry(&feature.geometry, geom_type) {
257+
let parsed_geometry = match parse_geometry::<T>(&feature.geometry, geom_type) {
222258
Ok(parsed_geometry) => parsed_geometry,
223259
Err(error) => {
224260
return Err(error);
@@ -321,29 +357,33 @@ fn map_value(value: vector_tile::tile::Value) -> Value {
321357
Value::Null
322358
}
323359

324-
fn shoelace_formula(points: &[Point<f32>]) -> f32 {
360+
fn shoelace_formula<T: CoordNum>(points: &[Point<T>]) -> f32 {
325361
let mut area: f32 = 0.0;
326362
let n = points.len();
327363
let mut v1 = points[n - 1];
328364
for v2 in points.iter().take(n) {
329-
area += (v2.y() - v1.y()) * (v2.x() + v1.x());
365+
let v2y: f32 = NumCast::from(v2.y()).unwrap_or(0.0);
366+
let v1y: f32 = NumCast::from(v1.y()).unwrap_or(0.0);
367+
let v2x: f32 = NumCast::from(v2.x()).unwrap_or(0.0);
368+
let v1x: f32 = NumCast::from(v1.x()).unwrap_or(0.0);
369+
area += (v2y - v1y) * (v2x + v1x);
330370
v1 = *v2;
331371
}
332372
area * 0.5
333373
}
334374

335-
fn parse_geometry(
375+
fn parse_geometry<T: CoordNum>(
336376
geometry_data: &[u32],
337377
geom_type: GeomType,
338-
) -> Result<Geometry<f32>, error::ParserError> {
378+
) -> Result<Geometry<T>, error::ParserError> {
339379
if geom_type == GeomType::Unknown {
340380
return Err(error::ParserError::new(error::GeometryError::new()));
341381
}
342382

343383
// worst case capacity to prevent reallocation. not needed to be exact.
344-
let mut coordinates: Vec<Coord<f32>> = Vec::with_capacity(geometry_data.len());
345-
let mut polygons: Vec<Polygon<f32>> = Vec::new();
346-
let mut linestrings: Vec<LineString<f32>> = Vec::new();
384+
let mut coordinates: Vec<Coord<T>> = Vec::with_capacity(geometry_data.len());
385+
let mut polygons: Vec<Polygon<T>> = Vec::new();
386+
let mut linestrings: Vec<LineString<T>> = Vec::new();
347387

348388
let mut cursor: [i32; 2] = [0, 0];
349389
let mut parameter_count: u32 = 0;
@@ -412,8 +452,8 @@ fn parse_geometry(
412452
None => i32::MAX, // clip value
413453
};
414454
coordinates.push(Coord {
415-
x: cursor[0] as f32,
416-
y: cursor[1] as f32,
455+
x: NumCast::from(cursor[0]).unwrap_or_else(T::zero),
456+
y: NumCast::from(cursor[1]).unwrap_or_else(T::zero),
417457
});
418458
}
419459
parameter_count -= 1;

tests/real_world.rs

Lines changed: 25 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ use std::fs::{DirEntry, read, read_dir};
33
use std::path::PathBuf;
44
use std::{io, result::Result};
55

6+
use geo_types::CoordNum;
67
use mvt_reader::error::TagsError;
78
use mvt_reader::{Reader, error::GeometryError};
89

@@ -69,33 +70,37 @@ fn read_corrupted_tags_fixture() -> Result<(), io::Error> {
6970
}
7071

7172
#[test]
72-
fn read_all_fixtures() -> Result<(), io::Error> {
73+
fn read_all_fixtures_f32() -> Result<(), io::Error> {
74+
assert_all_fixtures_readable::<f32>("f32")
75+
}
76+
77+
#[test]
78+
fn read_all_fixtures_i32() -> Result<(), io::Error> {
79+
assert_all_fixtures_readable::<i32>("i32")
80+
}
81+
82+
#[test]
83+
fn read_all_fixtures_i16() -> Result<(), io::Error> {
84+
assert_all_fixtures_readable::<i16>("i16")
85+
}
86+
87+
fn assert_all_fixtures_readable<T: CoordNum>(label: &str) -> Result<(), io::Error> {
7388
for mvt_file in get_all_real_world_fixtures()?.iter() {
7489
if !mvt_file.extension().unwrap().eq_ignore_ascii_case("mvt") {
7590
println!("Skipped file {:?}", mvt_file);
7691
continue;
7792
}
78-
println!("Read {:?}", mvt_file);
93+
94+
println!("Read {:?} ({})", mvt_file, label);
95+
7996
let bytes = read(mvt_file)?;
80-
let reader_result = Reader::new(bytes.to_vec());
81-
match reader_result {
82-
Ok(reader) => {
83-
let layer_names = match reader.get_layer_names() {
84-
Ok(layer_names) => layer_names,
85-
Err(error) => {
86-
panic!("{}", error);
87-
}
88-
};
89-
for (i, _) in layer_names.iter().enumerate() {
90-
let features = reader.get_features(i);
91-
assert!(!features.unwrap().is_empty());
92-
}
93-
println!("found layer names: {:?}", layer_names);
94-
}
95-
Err(_) => {
96-
panic!("Parsing failed unexpectedly")
97-
}
97+
let reader = Reader::new(bytes.to_vec()).expect("Parsing failed unexpectedly");
98+
let layer_names = reader.get_layer_names().expect("Failed to get layer names");
99+
for (i, _) in layer_names.iter().enumerate() {
100+
let features = reader.get_features_as::<T>(i);
101+
assert!(!features.unwrap().is_empty());
98102
}
103+
println!("found layer names: {:?}", layer_names);
99104
}
100105
Ok(())
101106
}

0 commit comments

Comments
 (0)