Skip to main content

oxigdal_node/
lib.rs

1//! Node.js bindings for OxiGDAL
2//!
3//! This crate provides comprehensive Node.js bindings for the OxiGDAL ecosystem,
4//! enabling pure Rust geospatial processing from JavaScript/TypeScript with
5//! zero-copy Buffer integration and full async/await support.
6//!
7//! # Features
8//!
9//! - **Raster I/O**: Read and write GeoTIFF, COG, and other raster formats
10//! - **Vector I/O**: GeoJSON support with full geometry operations
11//! - **Algorithms**: Resampling, terrain analysis, calculator, statistics
12//! - **Async/Await**: Promise-based async operations for I/O and processing
13//! - **Buffer Integration**: Zero-copy data transfer with Node.js Buffers
14//! - **TypeScript**: Comprehensive TypeScript definitions included
15//!
16//! # Example Usage
17//!
18//! ```javascript
19//! const oxigdal = require('@cooljapan/oxigdal-node');
20//!
21//! // Open a raster file
22//! const dataset = oxigdal.openRaster('input.tif');
23//! console.log(`Size: ${dataset.width}x${dataset.height}`);
24//!
25//! // Read a band
26//! const band = dataset.readBand(0);
27//! const stats = band.statistics();
28//! console.log(`Mean: ${stats.mean}, StdDev: ${stats.stddev}`);
29//!
30//! // Compute hillshade
31//! const hillshade = oxigdal.hillshade(band, 315, 45, 1.0);
32//!
33//! // Save result
34//! const output = oxigdal.createRaster(dataset.width, dataset.height, 1, 'uint8');
35//! output.writeBand(0, hillshade);
36//! output.save('hillshade.tif');
37//! ```
38//!
39//! # Async Example
40//!
41//! ```javascript
42//! const oxigdal = require('@cooljapan/oxigdal-node');
43//!
44//! async function processRaster() {
45//!   const dataset = await oxigdal.openRasterAsync('input.tif');
46//!   const band = dataset.readBand(0);
47//!   const slope = await oxigdal.slopeAsync(band, 1.0, false);
48//!
49//!   const output = oxigdal.createRaster(dataset.width, dataset.height, 1, 'float32');
50//!   output.writeBand(0, slope);
51//!   await oxigdal.saveRasterAsync(output, 'slope.tif');
52//! }
53//!
54//! processRaster().catch(console.error);
55//! ```
56
57#![warn(missing_docs)]
58#![warn(clippy::all)]
59#![deny(clippy::unwrap_used)]
60#![allow(clippy::module_name_repetitions)]
61
62mod algorithms;
63mod async_ops;
64mod buffer;
65mod error;
66mod raster;
67mod vector;
68
69use napi_derive::napi;
70
71/// Returns the version of OxiGDAL
72#[napi]
73pub fn version() -> String {
74    oxigdal_core::VERSION.to_string()
75}
76
77/// Returns the OxiGDAL name
78#[napi]
79pub fn name() -> String {
80    "OxiGDAL Node.js Bindings".to_string()
81}
82
83/// Module information
84#[napi(object)]
85pub struct ModuleInfo {
86    /// Version string
87    pub version: String,
88    /// Module name
89    pub name: String,
90    /// Build information
91    pub build_info: String,
92    /// Supported formats
93    pub formats: Vec<String>,
94}
95
96/// Returns module information
97#[napi]
98pub fn get_info() -> ModuleInfo {
99    ModuleInfo {
100        version: oxigdal_core::VERSION.to_string(),
101        name: "OxiGDAL Node.js Bindings".to_string(),
102        build_info: format!(
103            "Built with Rust {} on {}",
104            env!("CARGO_PKG_RUST_VERSION"),
105            std::env::consts::OS
106        ),
107        formats: vec![
108            "GeoTIFF".to_string(),
109            "COG".to_string(),
110            "GeoJSON".to_string(),
111        ],
112    }
113}
114
115/// Data type constants
116#[napi(object)]
117pub struct DataTypes {
118    /// Unsigned 8-bit integer
119    pub uint8: String,
120    /// Signed 16-bit integer
121    pub int16: String,
122    /// Unsigned 16-bit integer
123    pub uint16: String,
124    /// Signed 32-bit integer
125    pub int32: String,
126    /// Unsigned 32-bit integer
127    pub uint32: String,
128    /// 32-bit floating point
129    pub float32: String,
130    /// 64-bit floating point
131    pub float64: String,
132}
133
134/// Returns available data types
135#[napi]
136pub fn get_data_types() -> DataTypes {
137    DataTypes {
138        uint8: "uint8".to_string(),
139        int16: "int16".to_string(),
140        uint16: "uint16".to_string(),
141        int32: "int32".to_string(),
142        uint32: "uint32".to_string(),
143        float32: "float32".to_string(),
144        float64: "float64".to_string(),
145    }
146}
147
148/// Resampling method constants
149#[napi(object)]
150pub struct ResamplingMethods {
151    /// Nearest neighbor (fast, preserves exact values)
152    pub nearest_neighbor: String,
153    /// Bilinear interpolation (smooth, good for continuous data)
154    pub bilinear: String,
155    /// Bicubic interpolation (high quality, slower)
156    pub bicubic: String,
157    /// Lanczos resampling (highest quality, expensive)
158    pub lanczos: String,
159}
160
161/// Returns available resampling methods
162#[napi]
163pub fn get_resampling_methods() -> ResamplingMethods {
164    ResamplingMethods {
165        nearest_neighbor: "NearestNeighbor".to_string(),
166        bilinear: "Bilinear".to_string(),
167        bicubic: "Bicubic".to_string(),
168        lanczos: "Lanczos".to_string(),
169    }
170}
171
172#[cfg(test)]
173mod tests {
174    use super::*;
175
176    #[test]
177    fn test_version() {
178        let ver = version();
179        assert!(!ver.is_empty());
180    }
181
182    #[test]
183    fn test_info() {
184        let info = get_info();
185        assert!(!info.version.is_empty());
186        assert!(!info.formats.is_empty());
187    }
188
189    #[test]
190    fn test_data_types() {
191        let types = get_data_types();
192        assert_eq!(types.uint8, "uint8");
193        assert_eq!(types.float32, "float32");
194    }
195
196    #[test]
197    fn test_data_types_all_fields() {
198        let types = get_data_types();
199        assert_eq!(types.uint8, "uint8");
200        assert_eq!(types.int16, "int16");
201        assert_eq!(types.uint16, "uint16");
202        assert_eq!(types.int32, "int32");
203        assert_eq!(types.uint32, "uint32");
204        assert_eq!(types.float32, "float32");
205        assert_eq!(types.float64, "float64");
206    }
207
208    #[test]
209    fn test_resampling_methods() {
210        let methods = get_resampling_methods();
211        assert_eq!(methods.nearest_neighbor, "NearestNeighbor");
212        assert_eq!(methods.bilinear, "Bilinear");
213        assert_eq!(methods.bicubic, "Bicubic");
214        assert_eq!(methods.lanczos, "Lanczos");
215    }
216
217    #[test]
218    fn test_module_info_name() {
219        let info = get_info();
220        assert!(info.name.contains("OxiGDAL"));
221    }
222
223    #[test]
224    fn test_module_info_formats_contain_geotiff() {
225        let info = get_info();
226        assert!(
227            info.formats
228                .iter()
229                .any(|f| f.contains("GeoTIFF") || f.contains("tiff") || f.contains("TIFF"))
230        );
231    }
232
233    #[test]
234    fn test_module_info_build_info_not_empty() {
235        let info = get_info();
236        assert!(!info.build_info.is_empty());
237    }
238
239    #[test]
240    fn test_name_function() {
241        let n = name();
242        assert!(!n.is_empty());
243        assert!(n.contains("OxiGDAL") || n.contains("Node"));
244    }
245}