open_vector_tile/lib.rs
1#![no_std]
2// this library doesn't use unsafe code, but the wasm bindings do
3// #![forbid(unsafe_code)]
4#![deny(missing_docs)]
5#![feature(stmt_expr_attributes)]
6#![feature(register_tool)]
7#![register_tool(tarpaulin)]
8//! # Open Vector Tile
9//!
10//! ## Description
11//! The `open-vector-tile` Rust crate provides functionalities to read and write
12//! Open Vector Tile Spec messages. This crate uses `no_std` and is intended to be available for
13//! embedded systems and WASM applications.
14//!
15//! Types of layers include:
16//! - Vector data - vector points, lines, and polygons with 3D coordinates, properties, and/or m-values
17//! - Image data - raster data that is RGB(A) encoded
18//! - Grid data: data that has a max-min range, works much like an image but has floating/double precision point values for each point on the grid
19//!
20//! ### Reading
21//!
22//! ```rust,ignore
23//! use open_vector_tile::{VectorTile, VectorLayerMethods};
24//!
25//! let data: Vec<u8> = vec![];
26//! let mut tile = VectorTile::new(data, None);
27//!
28//! // VECTOR API
29//!
30//! let landuse = tile.layer("landuse").unwrap();
31//!
32//! // grab the first feature
33//! let firstFeature = landuse.feature(0).unwrap();
34//! // grab the geometry
35//! let geometry = firstFeature.load_geometry();
36//!
37//! // OR specifically ask for a geometry type
38//! let points = firstFeature.load_points();
39//! let lines = firstFeature.load_lines();
40//!
41//! // If you want to take advantage of the pre-tessellated and indexed geometries
42//! // and you're loading the data for a renderer, you can grab the pre-tessellated geometry
43//! let (geometry_flat, indices) = firstFeature.load_geometry_flat();
44//!
45//! // IMAGE API
46//!
47//! let satellite = tile.images.get("satellite").unwrap();
48//! // grab the image data
49//! let data = &satellite.image;
50//!
51//! // GRID API
52//!
53//! let elevation = tile.grids.get("elevation").unwrap();
54//! // grab the grid data
55//! let data = &elevation.data;
56//! ```
57//!
58//! ### Writing
59//!
60//! ```rust
61//! // NOTE: Be sure to include the `serde_json` crate
62//! extern crate alloc;
63//! use open_vector_tile::{
64//! base::{
65//! BaseVectorFeature, BaseVectorLayer, BaseVectorLines3DFeature, BaseVectorLinesFeature,
66//! BaseVectorPoints3DFeature, BaseVectorPointsFeature, BaseVectorPolys3DFeature,
67//! BaseVectorPolysFeature, BaseVectorTile,
68//! },
69//! open::{
70//! Extent, FeatureType, GridData, ImageData, ImageType,
71//! },
72//! write_tile, Point, Point3D, VectorGeometry, VectorLayerMethods,
73//! VectorLine3DWithOffset, VectorLineWithOffset, VectorTile,
74//! };
75//! use s2json::{BBox, BBox3D, BBOX, ValuePrimitiveType, ValueType, PrimitiveValue, Value};
76//!
77//!
78//! // WRITE VECTOR DATA //-//-//-//-//-//-//-//-//-//-//
79//!
80//! let mut tile = BaseVectorTile::default();
81//!
82//! // setup the property shapes
83//!
84//! let example_value_str = r#"{
85//! "a": -20,
86//! "b": 1,
87//! "c": 2.2
88//! }"#;
89//! let example_value = serde_json::from_str::<Value>(example_value_str).unwrap();
90//! let example_value_str_2 = r#"{
91//! "a": -2,
92//! "b": 1,
93//! "c": 2.2
94//! }"#;
95//! let example_value2 = serde_json::from_str::<Value>(example_value_str_2).unwrap();
96//!
97//! let empty_value = Value::from([
98//! ("a".to_string(), ValueType::Primitive(PrimitiveValue::I64(0))),
99//! ("b".to_string(), ValueType::Primitive(PrimitiveValue::U64(0))),
100//! ("c".to_string(), ValueType::Primitive(PrimitiveValue::F32(0.0))),
101//! ]);
102//!
103//! // WRITE THE POINTS
104//!
105//! let mut points_layer =
106//! BaseVectorLayer::new("points".to_string(), 4096.into(), vec![], None, None);
107//!
108//! let feature = BaseVectorPointsFeature::new(
109//! None,
110//! vec![Point::new_with_m(0, 0, example_value2.clone())],
111//! example_value.clone(),
112//! None,
113//! );
114//! let feature2 = BaseVectorPointsFeature::new(
115//! Some(1),
116//! vec![Point::new_with_m(0, 0, example_value.clone()), Point::new(1, 1)],
117//! example_value2.clone(),
118//! Some(BBox::new(-1.1, 0.0, 1.0, 1.0)),
119//! );
120//!
121//! // add_features
122//! points_layer.add_feature(BaseVectorFeature::BaseVectorPointsFeature(feature));
123//! points_layer.add_feature(BaseVectorFeature::BaseVectorPointsFeature(feature2));
124//!
125//! tile.add_layer(points_layer);
126//!
127//! // Lastly build the tile:
128//! let open_tile_bytes = write_tile(Some(&mut tile), None, None);
129//!
130//!
131//! // WRITE IMAGE DATA //-//-//-//-//-//-//-//-//-//-//
132//!
133//! let image =
134//! ImageData::new("test".to_string(), ImageType::AVIF, 2, 3, Vec::from([1, 2, 3, 10]));
135//!
136//! let open_tile_bytes = write_tile(None, Some(vec![&image]), None);
137//!
138//!
139//! // WRITE GRID DATA //-//-//-//-//-//-//-//-//-//-//
140//!
141//! let elevation_data = GridData::new(
142//! "elevation".to_owned(),
143//! 8_192.into(),
144//! 512.0,
145//! 0.0,
146//! 0.0,
147//! vec![-1.0, 2.0, 3.0, 4.0],
148//! );
149//! let open_tile_bytes = write_tile(None, None, Some(vec![&elevation_data]));
150//! ```
151
152extern crate alloc;
153extern crate pbf;
154
155/// Base Vector containers for Tiles, Layers, and Features
156pub mod base;
157/// Geometry utilities
158pub mod geometry;
159/// Mapbox specification for Layers and Features
160pub mod mapbox;
161/// Open specification for Layers and Features
162pub mod open;
163/// Utilities/functions that are useful across all specifications
164pub mod util;
165/// The vector tile struct that covers both "open" and "mapbox" specifications
166pub mod vector_tile;
167
168pub use geometry::*;
169pub use open::*;
170pub use util::*;
171pub use vector_tile::*;
172
173#[cfg(any(target_arch = "wasm32", feature = "wasm"))]
174use alloc::{boxed::Box, slice};
175
176#[cfg(any(target_arch = "wasm32", feature = "wasm"))]
177use lol_alloc::{AssumeSingleThreaded, FreeListAllocator};
178
179// SAFETY: This application is single threaded, so using AssumeSingleThreaded is allowed.
180#[cfg(any(target_arch = "wasm32", feature = "wasm"))]
181#[global_allocator]
182static ALLOCATOR: AssumeSingleThreaded<FreeListAllocator> =
183 unsafe { AssumeSingleThreaded::new(FreeListAllocator::new()) };
184
185// #[cfg(any(target_arch = "wasm32", feature = "wasm"))]
186// mod wasm_specific {
187// #[panic_handler]
188// fn panic(_info: &core::panic::PanicInfo) -> ! {
189// loop {}
190// }
191// }
192
193/// Expose the function "create_vector_tile" to JavaScript
194/// Creates a new VectorTile instance given input buffer
195#[cfg(any(target_arch = "wasm32", feature = "wasm"))]
196#[unsafe(no_mangle)]
197pub extern "C" fn create_vector_tile(data_ptr: *const u8, data_len: usize) -> *mut VectorTile {
198 // Convert the pointer and length into a slice
199 let data_slice = unsafe { slice::from_raw_parts(data_ptr, data_len) };
200
201 // Convert slice into Vec<u8> (we need to box this data for ownership)
202 let data_vec = data_slice.to_vec();
203
204 // Create the VectorTile instance
205 let vector_tile = VectorTile::new(data_vec, None);
206
207 // Box it and return as raw pointer
208 Box::into_raw(Box::new(vector_tile))
209}
210
211/// Expose the function "free_vector_tile" to JavaScript
212/// Frees the VectorTile instance from memory
213#[cfg(any(target_arch = "wasm32", feature = "wasm"))]
214#[unsafe(no_mangle)]
215pub extern "C" fn free_vector_tile(ptr: *mut VectorTile) {
216 if !ptr.is_null() {
217 unsafe {
218 _ = Box::from_raw(ptr); // Deallocate memory
219 }
220 }
221}
222
223/// Free memory allocated regardless of the type
224#[cfg(any(target_arch = "wasm32", feature = "wasm"))]
225#[unsafe(no_mangle)]
226pub extern "C" fn free(ptr: *mut u8, size: usize) {
227 unsafe {
228 // Convert the pointer to a slice and then drop it
229 let _ = core::slice::from_raw_parts_mut(ptr, size);
230
231 // Deallocate the memory
232 alloc::alloc::dealloc(ptr as *mut u8, alloc::alloc::Layout::array::<u8>(size).unwrap());
233 }
234}