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