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