open_vector_tile/
lib.rs

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