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}