jpk-reader-rs 0.0.1__tar.gz → 0.0.2__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {jpk_reader_rs-0.0.1 → jpk_reader_rs-0.0.2}/Cargo.lock +11 -3
- {jpk_reader_rs-0.0.1 → jpk_reader_rs-0.0.2}/PKG-INFO +4 -1
- {jpk_reader_rs-0.0.1 → jpk_reader_rs-0.0.2}/py/Cargo.toml +3 -2
- jpk_reader_rs-0.0.2/py/README.md +0 -0
- jpk_reader_rs-0.0.2/py/examples/qi_data.py +12 -0
- jpk_reader_rs-0.0.2/py/jpk_reader_rs.pyi +46 -0
- jpk_reader_rs-0.0.2/py/src/lib.rs +145 -0
- {jpk_reader_rs-0.0.1 → jpk_reader_rs-0.0.2}/pyproject.toml +5 -0
- {jpk_reader_rs-0.0.1 → jpk_reader_rs-0.0.2}/rs/Cargo.toml +2 -2
- jpk_reader_rs-0.0.2/rs/src/lib.rs +13 -0
- {jpk_reader_rs-0.0.1 → jpk_reader_rs-0.0.2}/rs/src/properties.rs +10 -0
- {jpk_reader_rs-0.0.1 → jpk_reader_rs-0.0.2}/rs/src/qi_map/mod.rs +148 -67
- {jpk_reader_rs-0.0.1 → jpk_reader_rs-0.0.2}/rs/src/qi_map/v2_0/lcd_info.rs +69 -20
- {jpk_reader_rs-0.0.1 → jpk_reader_rs-0.0.2}/rs/src/qi_map/v2_0/mod.rs +354 -138
- {jpk_reader_rs-0.0.1 → jpk_reader_rs-0.0.2}/rs/tests/jpk_qi_data.rs +62 -22
- jpk_reader_rs-0.0.1/py/examples/qi_data.py +0 -14
- jpk_reader_rs-0.0.1/py/src/lib.rs +0 -99
- jpk_reader_rs-0.0.1/rs/src/lib.rs +0 -2
- {jpk_reader_rs-0.0.1 → jpk_reader_rs-0.0.2}/Cargo.toml +0 -0
- {jpk_reader_rs-0.0.1 → jpk_reader_rs-0.0.2}/py/.github/workflows/CI.yml +0 -0
- {jpk_reader_rs-0.0.1 → jpk_reader_rs-0.0.2}/py/.gitignore +0 -0
- {jpk_reader_rs-0.0.1 → jpk_reader_rs-0.0.2}/rs/jpkfile-structure_of_jpk_archives.pdf +0 -0
|
@@ -636,7 +636,7 @@ dependencies = [
|
|
|
636
636
|
|
|
637
637
|
[[package]]
|
|
638
638
|
name = "jpk-reader-rs"
|
|
639
|
-
version = "0.0.
|
|
639
|
+
version = "0.0.2"
|
|
640
640
|
dependencies = [
|
|
641
641
|
"arrow",
|
|
642
642
|
"jpk_reader",
|
|
@@ -1164,6 +1164,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
|
1164
1164
|
checksum = "f9e442fc33d7fdb45aa9bfeb312c095964abdf596f7567261062b2a7107aaabd"
|
|
1165
1165
|
dependencies = [
|
|
1166
1166
|
"deranged",
|
|
1167
|
+
"js-sys",
|
|
1167
1168
|
"num-conv",
|
|
1168
1169
|
"powerfmt",
|
|
1169
1170
|
"serde_core",
|
|
@@ -1267,6 +1268,12 @@ dependencies = [
|
|
|
1267
1268
|
"syn",
|
|
1268
1269
|
]
|
|
1269
1270
|
|
|
1271
|
+
[[package]]
|
|
1272
|
+
name = "typed-path"
|
|
1273
|
+
version = "0.12.0"
|
|
1274
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
1275
|
+
checksum = "7922f2cdc51280d47b491af9eafc41eb0cdab85eabcb390c854412fcbf26dbe8"
|
|
1276
|
+
|
|
1270
1277
|
[[package]]
|
|
1271
1278
|
name = "typenum"
|
|
1272
1279
|
version = "1.19.0"
|
|
@@ -1473,9 +1480,9 @@ dependencies = [
|
|
|
1473
1480
|
|
|
1474
1481
|
[[package]]
|
|
1475
1482
|
name = "zip"
|
|
1476
|
-
version = "7.
|
|
1483
|
+
version = "7.2.0"
|
|
1477
1484
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
1478
|
-
checksum = "
|
|
1485
|
+
checksum = "c42e33efc22a0650c311c2ef19115ce232583abbe80850bc8b66509ebef02de0"
|
|
1479
1486
|
dependencies = [
|
|
1480
1487
|
"aes",
|
|
1481
1488
|
"bzip2",
|
|
@@ -1493,6 +1500,7 @@ dependencies = [
|
|
|
1493
1500
|
"ppmd-rust",
|
|
1494
1501
|
"sha1",
|
|
1495
1502
|
"time",
|
|
1503
|
+
"typed-path",
|
|
1496
1504
|
"zeroize",
|
|
1497
1505
|
"zopfli",
|
|
1498
1506
|
"zstd",
|
|
@@ -1,8 +1,11 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: jpk-reader-rs
|
|
3
|
-
Version: 0.0.
|
|
3
|
+
Version: 0.0.2
|
|
4
4
|
Classifier: Programming Language :: Rust
|
|
5
5
|
Classifier: Programming Language :: Python :: Implementation :: CPython
|
|
6
6
|
Classifier: Programming Language :: Python :: Implementation :: PyPy
|
|
7
7
|
Requires-Dist: pyarrow
|
|
8
8
|
Requires-Python: >=3.8
|
|
9
|
+
Project-URL: Homepage, https://github.com/bicarlsen/jpk_reader
|
|
10
|
+
Project-URL: Issues, https://github.com/bicarlsen/jpk_reader/issues
|
|
11
|
+
Project-URL: Source, https://github.com/bicarlsen/jpk_reader
|
|
@@ -1,15 +1,16 @@
|
|
|
1
1
|
[package]
|
|
2
2
|
name = "jpk-reader-rs"
|
|
3
|
-
version = "0.0.
|
|
3
|
+
version = "0.0.2"
|
|
4
4
|
edition = "2024"
|
|
5
5
|
|
|
6
6
|
authors = ["Brian Carlsen <carlsen.bri@gmail.com>"]
|
|
7
7
|
description = "Python bindings for JPK data reader."
|
|
8
|
-
license = "
|
|
8
|
+
license = "MIT OR Apache-2.0"
|
|
9
9
|
|
|
10
10
|
repository = "https://github.com/bicarlsen/jpk_reader"
|
|
11
11
|
keywords = ["afm", "atomic force microscopy", "jpk", "bruker"]
|
|
12
12
|
categories = ["science"]
|
|
13
|
+
readme = "README.md"
|
|
13
14
|
|
|
14
15
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
|
15
16
|
[lib]
|
|
File without changes
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
# %%
|
|
2
|
+
import jpk_reader_rs as jpk
|
|
3
|
+
|
|
4
|
+
# DATA_PATH = "../../data/qi_data/qi_data-2_0-lg.jpk-qi-data"
|
|
5
|
+
DATA_PATH = "s:\\_öffentlich_TAUSCHordner\\Mitarbeitende\\carlsen_brian\\degradation\\00-preliminary\\01/carlsen-asfaw-postdegradation-data-2026.01.08-16.35.28.552.jpk-qi-data"
|
|
6
|
+
# %%
|
|
7
|
+
reader = jpk.QIMapReader(DATA_PATH)
|
|
8
|
+
# %%
|
|
9
|
+
metadata = reader.all_metadata()
|
|
10
|
+
# %%
|
|
11
|
+
data = reader.all_data()
|
|
12
|
+
# %%
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
from typing import Optional
|
|
2
|
+
import pyarrow
|
|
3
|
+
|
|
4
|
+
class QIMapReader:
|
|
5
|
+
"""A JPK QI Map data (`.jpk-qi-data`) reader."""
|
|
6
|
+
|
|
7
|
+
def __init__(self, path: str) -> None: ...
|
|
8
|
+
def len(self) -> int:
|
|
9
|
+
"""
|
|
10
|
+
Returns:
|
|
11
|
+
int: Number of files in the archive.
|
|
12
|
+
"""
|
|
13
|
+
|
|
14
|
+
def files(self) -> list[str]:
|
|
15
|
+
"""Get all files names in the archive.
|
|
16
|
+
|
|
17
|
+
Returns:
|
|
18
|
+
list[str]: Sorted list of file names.
|
|
19
|
+
"""
|
|
20
|
+
|
|
21
|
+
def all_data(self) -> pyarrow.RecordBatch:
|
|
22
|
+
"""Gets all data from the file.
|
|
23
|
+
|
|
24
|
+
Returns:
|
|
25
|
+
pyarrow.RecordBatch: All file data.
|
|
26
|
+
|
|
27
|
+
Raises:
|
|
28
|
+
RuntimeError: If the file can not be read.
|
|
29
|
+
"""
|
|
30
|
+
|
|
31
|
+
def all_metadata(
|
|
32
|
+
self,
|
|
33
|
+
) -> dict[tuple[str, Optional[int], Optional[int]], dict[str, str]]:
|
|
34
|
+
"""Get all metadata from the file.
|
|
35
|
+
|
|
36
|
+
Returns:
|
|
37
|
+
dict[tuple[str, Optional[int], Optional[int]], dict[str, str]]: Dictionary keyed by the property type.
|
|
38
|
+
Keys are of the form `(type, index, segment)`:
|
|
39
|
+
+ ("dataset", None, None)
|
|
40
|
+
+ ("shared_data", None, None)
|
|
41
|
+
+ ("index", int, None)
|
|
42
|
+
+ ("segment", int, int)
|
|
43
|
+
|
|
44
|
+
Raises:
|
|
45
|
+
RuntimeError: If the file can not be read.
|
|
46
|
+
"""
|
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
use arrow::{
|
|
2
|
+
array::{
|
|
3
|
+
ArrayRef, Float64Builder, ListBuilder, RecordBatch, StringBuilder, UInt8Builder,
|
|
4
|
+
UInt32Builder,
|
|
5
|
+
},
|
|
6
|
+
datatypes::{DataType, Field, Schema},
|
|
7
|
+
pyarrow::PyArrowType,
|
|
8
|
+
};
|
|
9
|
+
use jpk_reader::{self as jpk, ArchiveReader, qi_map::QIMapReader as _};
|
|
10
|
+
use pyo3::{exceptions::PyRuntimeError, prelude::*, types::PyDict};
|
|
11
|
+
use std::{borrow::Cow, path::PathBuf, sync::Arc};
|
|
12
|
+
|
|
13
|
+
const CHANNEL_NAME_LEN_HINT: usize = 10;
|
|
14
|
+
|
|
15
|
+
/// Python exports
|
|
16
|
+
#[pymodule]
|
|
17
|
+
mod jpk_reader_rs {
|
|
18
|
+
#[pymodule_export]
|
|
19
|
+
use super::QIMapReader;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
#[pyclass]
|
|
23
|
+
pub struct QIMapReader {
|
|
24
|
+
inner: jpk::qi_map::VersionedFileReader,
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
#[pymethods]
|
|
28
|
+
impl QIMapReader {
|
|
29
|
+
#[new]
|
|
30
|
+
fn new(path: PathBuf) -> PyResult<Self> {
|
|
31
|
+
let reader = jpk::qi_map::FileReader::new_versioned(path)
|
|
32
|
+
.map_err(|err| PyRuntimeError::new_err(err.to_string()))?;
|
|
33
|
+
Ok(Self { inner: reader })
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
fn len(&self) -> PyResult<usize> {
|
|
37
|
+
Ok(self.inner.len())
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
fn files(&self) -> PyResult<Vec<&str>> {
|
|
41
|
+
const SEPARATOR: char = '/';
|
|
42
|
+
let mut files = self.inner.files();
|
|
43
|
+
let max_digits = files.len().ilog10() as usize + 1;
|
|
44
|
+
files.sort_by_key(|file| {
|
|
45
|
+
file.split(SEPARATOR)
|
|
46
|
+
.map(|component| {
|
|
47
|
+
if let Ok(value) = component.parse::<usize>() {
|
|
48
|
+
Cow::Owned(format!("{:0>width$}", value, width = max_digits))
|
|
49
|
+
} else {
|
|
50
|
+
Cow::Borrowed(component)
|
|
51
|
+
}
|
|
52
|
+
})
|
|
53
|
+
.collect::<String>()
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
return Ok(files);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
fn all_data(&mut self) -> PyResult<PyArrowType<RecordBatch>> {
|
|
60
|
+
let data = self
|
|
61
|
+
.inner
|
|
62
|
+
.query_data(&jpk::qi_map::DataQuery::select_all())
|
|
63
|
+
.map_err(|err| PyRuntimeError::new_err(err.to_string()))?;
|
|
64
|
+
|
|
65
|
+
let schema = Schema::new(vec![
|
|
66
|
+
Field::new("index", DataType::UInt32, false),
|
|
67
|
+
Field::new("segment", DataType::UInt8, false),
|
|
68
|
+
Field::new("channel", DataType::Utf8, false),
|
|
69
|
+
Field::new("data", DataType::new_list(DataType::Float64, true), false), // TODO: Can List be non-nullable?
|
|
70
|
+
]);
|
|
71
|
+
|
|
72
|
+
let (index, values) = data.into_parts();
|
|
73
|
+
let mut idx_index = UInt32Builder::with_capacity(index.len());
|
|
74
|
+
let mut idx_segment = UInt8Builder::with_capacity(index.len());
|
|
75
|
+
let mut idx_channel =
|
|
76
|
+
StringBuilder::with_capacity(index.len(), index.len() * CHANNEL_NAME_LEN_HINT);
|
|
77
|
+
for idx in index {
|
|
78
|
+
let jpk::qi_map::DataIndex {
|
|
79
|
+
index,
|
|
80
|
+
segment,
|
|
81
|
+
channel,
|
|
82
|
+
} = idx;
|
|
83
|
+
idx_index.append_value(index);
|
|
84
|
+
idx_segment.append_value(segment);
|
|
85
|
+
idx_channel.append_value(channel);
|
|
86
|
+
}
|
|
87
|
+
let idx_i = idx_index.finish();
|
|
88
|
+
let idx_segment = idx_segment.finish();
|
|
89
|
+
let idx_channel = idx_channel.finish();
|
|
90
|
+
|
|
91
|
+
let mut value_builder = ListBuilder::with_capacity(Float64Builder::new(), values.len());
|
|
92
|
+
for data in values {
|
|
93
|
+
let data = data.into_iter().map(|value| Some(value));
|
|
94
|
+
value_builder.append_value(data);
|
|
95
|
+
}
|
|
96
|
+
let values = value_builder.finish();
|
|
97
|
+
|
|
98
|
+
let schema = Arc::new(schema);
|
|
99
|
+
let data = vec![
|
|
100
|
+
Arc::new(idx_i) as ArrayRef,
|
|
101
|
+
Arc::new(idx_segment) as ArrayRef,
|
|
102
|
+
Arc::new(idx_channel) as ArrayRef,
|
|
103
|
+
Arc::new(values) as ArrayRef,
|
|
104
|
+
];
|
|
105
|
+
let records = RecordBatch::try_new(schema, data).unwrap();
|
|
106
|
+
Ok(records.into())
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
fn all_metadata(&mut self, py: Python) -> PyResult<Py<PyAny>> {
|
|
110
|
+
let data = self
|
|
111
|
+
.inner
|
|
112
|
+
.query_metadata(&jpk_reader::qi_map::MetadataQuery::All)
|
|
113
|
+
.map_err(|err| PyRuntimeError::new_err(err.to_string()))?;
|
|
114
|
+
|
|
115
|
+
let dict = PyDict::new(py);
|
|
116
|
+
for (index, properties) in data.into_iter() {
|
|
117
|
+
let props = PyDict::new(py);
|
|
118
|
+
for (key, value) in properties.into_iter() {
|
|
119
|
+
props.set_item(key, value)?;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
let mut idx = ("", None, None);
|
|
123
|
+
match index {
|
|
124
|
+
jpk_reader::qi_map::MetadataIndex::Dataset => {
|
|
125
|
+
idx.0 = "dataset";
|
|
126
|
+
}
|
|
127
|
+
jpk_reader::qi_map::MetadataIndex::SharedData => {
|
|
128
|
+
idx.0 = "shared_data";
|
|
129
|
+
}
|
|
130
|
+
jpk_reader::qi_map::MetadataIndex::Index(index) => {
|
|
131
|
+
idx.0 = "index";
|
|
132
|
+
idx.1 = Some(index);
|
|
133
|
+
}
|
|
134
|
+
jpk_reader::qi_map::MetadataIndex::Segment { index, segment } => {
|
|
135
|
+
idx.0 = "segment";
|
|
136
|
+
idx.1 = Some(index);
|
|
137
|
+
idx.2 = Some(segment);
|
|
138
|
+
}
|
|
139
|
+
};
|
|
140
|
+
dict.set_item(idx, props)?;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
Ok(dict.into())
|
|
144
|
+
}
|
|
145
|
+
}
|
|
@@ -14,6 +14,11 @@ dynamic = ["version"]
|
|
|
14
14
|
|
|
15
15
|
dependencies = ["pyarrow"]
|
|
16
16
|
|
|
17
|
+
[project.urls]
|
|
18
|
+
Issues = "https://github.com/bicarlsen/jpk_reader/issues"
|
|
19
|
+
Source = "https://github.com/bicarlsen/jpk_reader"
|
|
20
|
+
Homepage = "https://github.com/bicarlsen/jpk_reader"
|
|
21
|
+
|
|
17
22
|
[tool]
|
|
18
23
|
|
|
19
24
|
[tool.maturin]
|
|
@@ -12,13 +12,13 @@ keywords = ["afm", "atomic force microscopy", "jpk", "bruker"]
|
|
|
12
12
|
categories = ["science"]
|
|
13
13
|
|
|
14
14
|
[dependencies]
|
|
15
|
-
derive_more = { workspace = true, features = ["from", "deref"] }
|
|
15
|
+
derive_more = { workspace = true, features = ["from", "deref", "deref_mut"] }
|
|
16
16
|
rayon = "1.11.0"
|
|
17
17
|
tracing = { workspace = true, optional = true }
|
|
18
18
|
tracing-subscriber = { workspace = true, features = [
|
|
19
19
|
"env-filter",
|
|
20
20
|
], optional = true }
|
|
21
|
-
zip = "7.
|
|
21
|
+
zip = "7.2"
|
|
22
22
|
|
|
23
23
|
[dev-dependencies]
|
|
24
24
|
tracing-test = { workspace = true }
|
|
@@ -62,7 +62,17 @@ impl Properties {
|
|
|
62
62
|
}
|
|
63
63
|
}
|
|
64
64
|
|
|
65
|
+
impl IntoIterator for Properties {
|
|
66
|
+
type Item = (String, String);
|
|
67
|
+
type IntoIter = std::vec::IntoIter<Self::Item>;
|
|
68
|
+
|
|
69
|
+
fn into_iter(self) -> Self::IntoIter {
|
|
70
|
+
self.properties.into_iter()
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
|
|
65
74
|
/// The format of the file was invalid.
|
|
75
|
+
#[derive(Debug)]
|
|
66
76
|
pub struct InvalidFormat;
|
|
67
77
|
|
|
68
78
|
pub enum PropertyError {
|