ovtile/
lib.rs

1#![no_std]
2#![deny(missing_docs)]
3#![feature(stmt_expr_attributes)]
4#![feature(register_tool)]
5#![register_tool(tarpaulin)]
6//! # Open Vector Tile
7//!
8//! ## Description
9//! The `open-vector-tile` Rust crate provides functionalities to read and write
10//! Open Vector Tile Spec messages. This crate uses `no_std` and is intended to be available for
11//! embedded systems and WASM applications.
12//!
13//! Types of layers include:
14//! - Vector data - vector points, lines, and polygons with 3D coordinates, properties, and/or m-values
15//! - Image data - raster data that is RGB(A) encoded
16//! - 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
17//!
18//! ### Reading
19//!
20//! ```rust,ignore
21//! use ovtile::{VectorTile, VectorLayerMethods};
22//!
23//! let data: Vec<u8> = vec![];
24//! let mut tile = VectorTile::new(data, None);
25//!
26//! // VECTOR API
27//!
28//! let landuse = tile.layer("landuse").unwrap();
29//!
30//! // grab the first feature
31//! let firstFeature = landuse.feature(0).unwrap();
32//! // grab the geometry
33//! let geometry = firstFeature.load_geometry();
34//!
35//! // OR specifically ask for a geometry type
36//! let points = firstFeature.load_points();
37//! let lines = firstFeature.load_lines();
38//!
39//! // If you want to take advantage of the pre-tessellated and indexed geometries
40//! // and you're loading the data for a renderer, you can grab the pre-tessellated geometry
41//! let (geometry_flat, indices) = firstFeature.load_geometry_flat();
42//!
43//! // IMAGE API
44//!
45//! let satellite = tile.images.get("satellite").unwrap();
46//! // grab the image data
47//! let data = &satellite.image;
48//!
49//! // GRID API
50//!
51//! let elevation = tile.grids.get("elevation").unwrap();
52//! // grab the grid data
53//! let data = &elevation.data;
54//! ```
55//!
56//! ### Writing
57//!
58//! ```rust
59//! // NOTE: Be sure to include the `serde_json` crate
60//! extern crate alloc;
61//! use std::collections::BTreeMap;
62//! use ovtile::{
63//! base::{
64//!     BaseVectorFeature, BaseVectorLayer, BaseVectorLines3DFeature, BaseVectorLinesFeature,
65//!     BaseVectorPoints3DFeature, BaseVectorPointsFeature, BaseVectorPolys3DFeature,
66//!     BaseVectorPolysFeature, BaseVectorTile,
67//! },
68//! open::{
69//!     Extent, FeatureType, GridData, ImageData, ImageType, PrimitiveValue, Value,
70//!     ValuePrimitiveType, ValueType,
71//! },
72//! write_tile, BBox, BBox3D, Point, Point3D, VectorGeometry, VectorLayerMethods,
73//! VectorLine3DWithOffset, VectorLineWithOffset, VectorTile, BBOX,
74//! };
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(BTreeMap::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 util::*;
169pub use vector_tile::*;
170
171#[cfg(any(target_arch = "wasm32", feature = "wasm"))]
172use alloc::{boxed::Box, slice, vec::Vec};
173#[cfg(any(target_arch = "wasm32", feature = "wasm"))]
174use core::mem;
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"))]
186mod wasm_specific {
187    #[panic_handler]
188    fn panic(_info: &core::panic::PanicInfo) -> ! {
189        loop {}
190    }
191}
192
193// Expose the function to JavaScript via #[export_name]
194#[cfg(any(target_arch = "wasm32", feature = "wasm"))]
195#[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#[cfg(any(target_arch = "wasm32", feature = "wasm"))]
211#[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#[cfg(any(target_arch = "wasm32", feature = "wasm"))]
221#[no_mangle]
222pub unsafe extern "C" fn allocUnicodeArray(size: usize) -> *mut u8 {
223    // Allocate memory
224    let mut buffer: Vec<u8> = Vec::with_capacity(size);
225    buffer.capacity();
226    // Ensure capacity matches size to avoid resizing
227    buffer.set_len(size);
228    // Get a raw pointer to the allocated memory
229    let ptr = buffer.as_mut_ptr();
230    // Prevent the buffer from being deallocated when it goes out of scope
231    mem::forget(buffer);
232
233    ptr
234}
235
236#[cfg(any(target_arch = "wasm32", feature = "wasm"))]
237#[no_mangle]
238pub unsafe extern "C" fn free(ptr: *mut u8, size: usize) {
239    // Convert the pointer to a slice and then drop it
240    let _ = core::slice::from_raw_parts_mut(ptr, size);
241
242    // Deallocate the memory
243    alloc::alloc::dealloc(ptr as *mut u8, alloc::alloc::Layout::array::<u8>(size).unwrap());
244}