demtools 0.1.0__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.
- demtools-0.1.0/MANIFEST.in +3 -0
- demtools-0.1.0/PKG-INFO +86 -0
- demtools-0.1.0/README.md +67 -0
- demtools-0.1.0/demtools/__init__.py +17 -0
- demtools-0.1.0/demtools/chgnodata.py +184 -0
- demtools-0.1.0/demtools/csv2tif.py +157 -0
- demtools-0.1.0/demtools/describe.py +78 -0
- demtools-0.1.0/demtools/setnodata.py +164 -0
- demtools-0.1.0/demtools/tests/__init__.py +0 -0
- demtools-0.1.0/demtools.egg-info/PKG-INFO +86 -0
- demtools-0.1.0/demtools.egg-info/SOURCES.txt +17 -0
- demtools-0.1.0/demtools.egg-info/dependency_links.txt +1 -0
- demtools-0.1.0/demtools.egg-info/entry_points.txt +5 -0
- demtools-0.1.0/demtools.egg-info/requires.txt +1 -0
- demtools-0.1.0/demtools.egg-info/top_level.txt +1 -0
- demtools-0.1.0/pyproject.toml +31 -0
- demtools-0.1.0/requirements.txt +1 -0
- demtools-0.1.0/setup.cfg +4 -0
- demtools-0.1.0/setup.py +34 -0
demtools-0.1.0/PKG-INFO
ADDED
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: demtools
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: A collection of tools for working with DEM (Digital Elevation Model) raster files
|
|
5
|
+
Home-page: https://github.com/AaronOET/demtools
|
|
6
|
+
Author: aaronchh
|
|
7
|
+
Author-email: aaronchh <aaronhsu219@gmail.com>
|
|
8
|
+
License: MIT
|
|
9
|
+
Project-URL: Homepage, https://github.com/AaronOET/demtools
|
|
10
|
+
Classifier: Programming Language :: Python :: 3
|
|
11
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
12
|
+
Classifier: Operating System :: OS Independent
|
|
13
|
+
Requires-Python: >=3.8
|
|
14
|
+
Description-Content-Type: text/markdown
|
|
15
|
+
Requires-Dist: numpy>=1.20.0
|
|
16
|
+
Dynamic: author
|
|
17
|
+
Dynamic: home-page
|
|
18
|
+
Dynamic: requires-python
|
|
19
|
+
|
|
20
|
+
# DEMTOOLS
|
|
21
|
+
|
|
22
|
+
A collection of Python tools for working with DEM (Digital Elevation Model) raster files.
|
|
23
|
+
|
|
24
|
+
> **GDAL Installation**: GDAL is required for this package. For conda environments, use `conda install gdal` to install GDAL. For non-conda environments, download the appropriate wheel file from [https://github.com/cgohlke/geospatial-wheels/releases](https://github.com/cgohlke/geospatial-wheels/releases) to install GDAL.
|
|
25
|
+
|
|
26
|
+
## Installation
|
|
27
|
+
|
|
28
|
+
```bash
|
|
29
|
+
pip install -e .
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
## Features
|
|
33
|
+
|
|
34
|
+
- **chgnodata**: Convert (replace) the nodata value of GeoTIFF files, updating pixel data accordingly
|
|
35
|
+
- **setnodata**: Assign a nodata value to GeoTIFF files without modifying pixel data (metadata only)
|
|
36
|
+
- **csv2tif**: Convert a plain-numeric CSV raster grid to a GeoTIFF file
|
|
37
|
+
|
|
38
|
+
## Usage
|
|
39
|
+
|
|
40
|
+
### Command-line
|
|
41
|
+
|
|
42
|
+
```bash
|
|
43
|
+
# List all available commands
|
|
44
|
+
demtools-info
|
|
45
|
+
|
|
46
|
+
# Convert nodata values for all TIF files in the current directory
|
|
47
|
+
chgnodata -a
|
|
48
|
+
|
|
49
|
+
# Convert nodata of a single file to -9999
|
|
50
|
+
chgnodata -i dem.tif -v -9999
|
|
51
|
+
|
|
52
|
+
# Set nodata metadata for all TIF files (no pixel data change)
|
|
53
|
+
setnodata -a -v -9999
|
|
54
|
+
|
|
55
|
+
# Convert a CSV grid to GeoTIFF
|
|
56
|
+
csv2tif -i grid.csv -o dem.tif --xll 250000 --yll 2500000 --cellsize 5 --epsg 32648
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
### Python API
|
|
60
|
+
|
|
61
|
+
```python
|
|
62
|
+
from demtools import chgnodata, setnodata, csv2tif
|
|
63
|
+
|
|
64
|
+
# Convert nodata value in a raster file
|
|
65
|
+
chgnodata.convert_nodata_value("dem.tif", output_nodata=-9999)
|
|
66
|
+
|
|
67
|
+
# Assign nodata metadata without touching pixel values
|
|
68
|
+
setnodata.define_nodata_value("dem.tif", nodata_value=-9999)
|
|
69
|
+
|
|
70
|
+
# Convert CSV grid to GeoTIFF
|
|
71
|
+
csv2tif.csv_to_tif(
|
|
72
|
+
input_csv="grid.csv",
|
|
73
|
+
output_tif="dem.tif",
|
|
74
|
+
xll=250000,
|
|
75
|
+
yll=2500000,
|
|
76
|
+
cellsize=5,
|
|
77
|
+
nodata=-999,
|
|
78
|
+
epsg=32648,
|
|
79
|
+
)
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
## Notes
|
|
83
|
+
|
|
84
|
+
- `chgnodata` and `setnodata` automatically create a `RAS_BAK/` directory with backup copies before modifying files.
|
|
85
|
+
- `csv2tif` expects a plain numeric CSV (no headers), with rows ordered from north to south.
|
|
86
|
+
- GDAL must be installed separately via conda or a pre-built wheel; it is not listed in `requirements.txt` as it cannot be reliably installed via pip on all platforms.
|
demtools-0.1.0/README.md
ADDED
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
# DEMTOOLS
|
|
2
|
+
|
|
3
|
+
A collection of Python tools for working with DEM (Digital Elevation Model) raster files.
|
|
4
|
+
|
|
5
|
+
> **GDAL Installation**: GDAL is required for this package. For conda environments, use `conda install gdal` to install GDAL. For non-conda environments, download the appropriate wheel file from [https://github.com/cgohlke/geospatial-wheels/releases](https://github.com/cgohlke/geospatial-wheels/releases) to install GDAL.
|
|
6
|
+
|
|
7
|
+
## Installation
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
pip install -e .
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
## Features
|
|
14
|
+
|
|
15
|
+
- **chgnodata**: Convert (replace) the nodata value of GeoTIFF files, updating pixel data accordingly
|
|
16
|
+
- **setnodata**: Assign a nodata value to GeoTIFF files without modifying pixel data (metadata only)
|
|
17
|
+
- **csv2tif**: Convert a plain-numeric CSV raster grid to a GeoTIFF file
|
|
18
|
+
|
|
19
|
+
## Usage
|
|
20
|
+
|
|
21
|
+
### Command-line
|
|
22
|
+
|
|
23
|
+
```bash
|
|
24
|
+
# List all available commands
|
|
25
|
+
demtools-info
|
|
26
|
+
|
|
27
|
+
# Convert nodata values for all TIF files in the current directory
|
|
28
|
+
chgnodata -a
|
|
29
|
+
|
|
30
|
+
# Convert nodata of a single file to -9999
|
|
31
|
+
chgnodata -i dem.tif -v -9999
|
|
32
|
+
|
|
33
|
+
# Set nodata metadata for all TIF files (no pixel data change)
|
|
34
|
+
setnodata -a -v -9999
|
|
35
|
+
|
|
36
|
+
# Convert a CSV grid to GeoTIFF
|
|
37
|
+
csv2tif -i grid.csv -o dem.tif --xll 250000 --yll 2500000 --cellsize 5 --epsg 32648
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
### Python API
|
|
41
|
+
|
|
42
|
+
```python
|
|
43
|
+
from demtools import chgnodata, setnodata, csv2tif
|
|
44
|
+
|
|
45
|
+
# Convert nodata value in a raster file
|
|
46
|
+
chgnodata.convert_nodata_value("dem.tif", output_nodata=-9999)
|
|
47
|
+
|
|
48
|
+
# Assign nodata metadata without touching pixel values
|
|
49
|
+
setnodata.define_nodata_value("dem.tif", nodata_value=-9999)
|
|
50
|
+
|
|
51
|
+
# Convert CSV grid to GeoTIFF
|
|
52
|
+
csv2tif.csv_to_tif(
|
|
53
|
+
input_csv="grid.csv",
|
|
54
|
+
output_tif="dem.tif",
|
|
55
|
+
xll=250000,
|
|
56
|
+
yll=2500000,
|
|
57
|
+
cellsize=5,
|
|
58
|
+
nodata=-999,
|
|
59
|
+
epsg=32648,
|
|
60
|
+
)
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
## Notes
|
|
64
|
+
|
|
65
|
+
- `chgnodata` and `setnodata` automatically create a `RAS_BAK/` directory with backup copies before modifying files.
|
|
66
|
+
- `csv2tif` expects a plain numeric CSV (no headers), with rows ordered from north to south.
|
|
67
|
+
- GDAL must be installed separately via conda or a pre-built wheel; it is not listed in `requirements.txt` as it cannot be reliably installed via pip on all platforms.
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
"""
|
|
2
|
+
DEMTOOLS - A collection of tools for working with DEM (Digital Elevation Model) raster files.
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
__version__ = '0.1.0'
|
|
6
|
+
|
|
7
|
+
__all__ = [
|
|
8
|
+
'chgnodata',
|
|
9
|
+
'csv2tif',
|
|
10
|
+
'setnodata',
|
|
11
|
+
'describe',
|
|
12
|
+
]
|
|
13
|
+
|
|
14
|
+
from . import chgnodata
|
|
15
|
+
from . import csv2tif
|
|
16
|
+
from . import setnodata
|
|
17
|
+
from . import describe
|
|
@@ -0,0 +1,184 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""
|
|
3
|
+
chgnodata - Convert nodata values for TIF raster files
|
|
4
|
+
Uses GDAL/OGR Python bindings
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
import glob
|
|
8
|
+
import argparse
|
|
9
|
+
import os
|
|
10
|
+
import sys
|
|
11
|
+
from osgeo import gdal, gdalconst
|
|
12
|
+
|
|
13
|
+
def convert_nodata_value(input_file, output_nodata=-999):
|
|
14
|
+
"""
|
|
15
|
+
Convert nodata value of a raster file to specified value
|
|
16
|
+
|
|
17
|
+
Args:
|
|
18
|
+
input_file (str): Path to input TIF file
|
|
19
|
+
output_nodata (float): New nodata value (default: -999)
|
|
20
|
+
"""
|
|
21
|
+
print(f"Processing: {input_file}")
|
|
22
|
+
|
|
23
|
+
# Open the input dataset
|
|
24
|
+
src_ds = gdal.Open(input_file, gdalconst.GA_ReadOnly)
|
|
25
|
+
if src_ds is None:
|
|
26
|
+
print(f"Error: Could not open {input_file}")
|
|
27
|
+
return False
|
|
28
|
+
|
|
29
|
+
# Get raster band (assuming single band, modify if multi-band)
|
|
30
|
+
src_band = src_ds.GetRasterBand(1)
|
|
31
|
+
|
|
32
|
+
# Get current nodata value
|
|
33
|
+
current_nodata = src_band.GetNoDataValue()
|
|
34
|
+
print(f" Current nodata value: {current_nodata}")
|
|
35
|
+
|
|
36
|
+
if current_nodata == output_nodata:
|
|
37
|
+
print(f" Nodata value already set to {output_nodata}, skipping...")
|
|
38
|
+
src_ds = None
|
|
39
|
+
return True
|
|
40
|
+
|
|
41
|
+
# Read the data
|
|
42
|
+
data = src_band.ReadAsArray()
|
|
43
|
+
|
|
44
|
+
# Get raster properties
|
|
45
|
+
rows, cols = data.shape
|
|
46
|
+
geotransform = src_ds.GetGeoTransform()
|
|
47
|
+
projection = src_ds.GetProjection()
|
|
48
|
+
data_type = src_band.DataType
|
|
49
|
+
|
|
50
|
+
# Create backup directory
|
|
51
|
+
backup_dir = "RAS_BAK"
|
|
52
|
+
if not os.path.exists(backup_dir):
|
|
53
|
+
os.makedirs(backup_dir)
|
|
54
|
+
print(f" Created backup directory: {backup_dir}")
|
|
55
|
+
|
|
56
|
+
backup_filename = os.path.basename(input_file)
|
|
57
|
+
backup_file = os.path.join(backup_dir, backup_filename)
|
|
58
|
+
|
|
59
|
+
# Create backup
|
|
60
|
+
print(f" Creating backup: {backup_file}")
|
|
61
|
+
gdal.GetDriverByName('GTiff').CreateCopy(backup_file, src_ds)
|
|
62
|
+
|
|
63
|
+
# Close source dataset
|
|
64
|
+
src_ds = None
|
|
65
|
+
|
|
66
|
+
# Create new dataset (overwrite original)
|
|
67
|
+
driver = gdal.GetDriverByName('GTiff')
|
|
68
|
+
out_ds = driver.Create(input_file, cols, rows, 1, data_type)
|
|
69
|
+
|
|
70
|
+
# Set geotransform and projection
|
|
71
|
+
out_ds.SetGeoTransform(geotransform)
|
|
72
|
+
out_ds.SetProjection(projection)
|
|
73
|
+
|
|
74
|
+
# Get output band
|
|
75
|
+
out_band = out_ds.GetRasterBand(1)
|
|
76
|
+
|
|
77
|
+
# Replace current nodata values with new nodata value
|
|
78
|
+
if current_nodata is not None:
|
|
79
|
+
data[data == current_nodata] = output_nodata
|
|
80
|
+
|
|
81
|
+
# Write the data
|
|
82
|
+
out_band.WriteArray(data)
|
|
83
|
+
|
|
84
|
+
# Set the new nodata value
|
|
85
|
+
out_band.SetNoDataValue(output_nodata)
|
|
86
|
+
|
|
87
|
+
# Flush data to disk
|
|
88
|
+
out_band.FlushCache()
|
|
89
|
+
out_ds = None
|
|
90
|
+
|
|
91
|
+
print(f" Successfully converted nodata value to {output_nodata}")
|
|
92
|
+
return True
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
def main():
|
|
96
|
+
parser = argparse.ArgumentParser(
|
|
97
|
+
prog='chgnodata',
|
|
98
|
+
description='Convert nodata values for TIF raster files',
|
|
99
|
+
formatter_class=argparse.RawDescriptionHelpFormatter,
|
|
100
|
+
epilog="""
|
|
101
|
+
Examples:
|
|
102
|
+
chgnodata -a # Process all *.tif in current directory (nodata=-999)
|
|
103
|
+
chgnodata -a -v -9999 # Process all *.tif, set nodata to -9999
|
|
104
|
+
chgnodata -i dem.tif # Process a single file (nodata=-999)
|
|
105
|
+
chgnodata -i dem.tif -v -9999 # Process a single file, set nodata to -9999
|
|
106
|
+
"""
|
|
107
|
+
)
|
|
108
|
+
|
|
109
|
+
# Mutually exclusive group: -a or -i
|
|
110
|
+
group = parser.add_mutually_exclusive_group(required=True)
|
|
111
|
+
group.add_argument(
|
|
112
|
+
'-a', '--all',
|
|
113
|
+
action='store_true',
|
|
114
|
+
help='Process all *.tif files in the current directory'
|
|
115
|
+
)
|
|
116
|
+
group.add_argument(
|
|
117
|
+
'-i', '--input',
|
|
118
|
+
metavar='FILE',
|
|
119
|
+
help='Process a single TIF file'
|
|
120
|
+
)
|
|
121
|
+
|
|
122
|
+
parser.add_argument(
|
|
123
|
+
'-v', '--value',
|
|
124
|
+
dest='nodata_value',
|
|
125
|
+
type=float,
|
|
126
|
+
default=-999,
|
|
127
|
+
metavar='NODATA',
|
|
128
|
+
help='New nodata value to set (default: -999)'
|
|
129
|
+
)
|
|
130
|
+
|
|
131
|
+
args = parser.parse_args()
|
|
132
|
+
new_nodata = args.nodata_value
|
|
133
|
+
|
|
134
|
+
print(f"chgnodata: converting nodata values to {new_nodata}")
|
|
135
|
+
print("=" * 60)
|
|
136
|
+
|
|
137
|
+
if args.all:
|
|
138
|
+
tif_files = glob.glob("*.tif")
|
|
139
|
+
if not tif_files:
|
|
140
|
+
print("No TIF files found in current directory.")
|
|
141
|
+
sys.exit(0)
|
|
142
|
+
|
|
143
|
+
print(f"Found {len(tif_files)} TIF file(s):")
|
|
144
|
+
for f in tif_files:
|
|
145
|
+
print(f" - {f}")
|
|
146
|
+
print(f"\nProcessing files...")
|
|
147
|
+
print("-" * 40)
|
|
148
|
+
|
|
149
|
+
success_count = 0
|
|
150
|
+
for tif_file in tif_files:
|
|
151
|
+
if convert_nodata_value(tif_file, new_nodata):
|
|
152
|
+
success_count += 1
|
|
153
|
+
print()
|
|
154
|
+
|
|
155
|
+
print("Conversion complete!")
|
|
156
|
+
print(f"Successfully processed: {success_count}/{len(tif_files)} files")
|
|
157
|
+
if success_count < len(tif_files):
|
|
158
|
+
print("Some files failed to process. Check error messages above.")
|
|
159
|
+
|
|
160
|
+
elif args.input:
|
|
161
|
+
input_file = args.input
|
|
162
|
+
if not os.path.isfile(input_file):
|
|
163
|
+
print(f"Error: File not found: {input_file}")
|
|
164
|
+
sys.exit(1)
|
|
165
|
+
|
|
166
|
+
print("-" * 40)
|
|
167
|
+
success = convert_nodata_value(input_file, new_nodata)
|
|
168
|
+
print()
|
|
169
|
+
if success:
|
|
170
|
+
print("Conversion complete!")
|
|
171
|
+
else:
|
|
172
|
+
print("Conversion failed.")
|
|
173
|
+
sys.exit(1)
|
|
174
|
+
|
|
175
|
+
|
|
176
|
+
if __name__ == "__main__":
|
|
177
|
+
gdal.UseExceptions()
|
|
178
|
+
|
|
179
|
+
try:
|
|
180
|
+
main()
|
|
181
|
+
except Exception as e:
|
|
182
|
+
print(f"Error: {e}")
|
|
183
|
+
import traceback
|
|
184
|
+
traceback.print_exc()
|
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""
|
|
3
|
+
csv2tif - Convert a CSV raster grid to a GeoTIFF file
|
|
4
|
+
Uses GDAL/OGR Python bindings
|
|
5
|
+
|
|
6
|
+
Usage:
|
|
7
|
+
csv2tif -i input.csv
|
|
8
|
+
csv2tif -i input.csv -o output.tif
|
|
9
|
+
csv2tif -i input.csv -o output.tif --xll 0.0 --yll 0.0 --cellsize 1.0
|
|
10
|
+
csv2tif -i input.csv --epsg 32648
|
|
11
|
+
"""
|
|
12
|
+
|
|
13
|
+
import argparse
|
|
14
|
+
import os
|
|
15
|
+
import sys
|
|
16
|
+
|
|
17
|
+
import numpy as np
|
|
18
|
+
from osgeo import gdal, osr
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
def csv_to_tif(input_csv, output_tif, xll=0.0, yll=0.0, cellsize=1.0,
|
|
22
|
+
nodata=-999, epsg=None):
|
|
23
|
+
"""
|
|
24
|
+
Convert a CSV raster grid to a GeoTIFF.
|
|
25
|
+
|
|
26
|
+
The CSV is expected to be a plain numeric grid (no headers) where rows
|
|
27
|
+
correspond to raster rows from top (north) to bottom (south).
|
|
28
|
+
|
|
29
|
+
Args:
|
|
30
|
+
input_csv (str): Path to input CSV file.
|
|
31
|
+
output_tif (str): Path to output GeoTIFF file.
|
|
32
|
+
xll (float): X coordinate of the lower-left corner (default 0.0).
|
|
33
|
+
yll (float): Y coordinate of the lower-left corner (default 0.0).
|
|
34
|
+
cellsize (float): Cell/pixel size in map units (default 1.0).
|
|
35
|
+
nodata (float): NoData value to assign (default -999).
|
|
36
|
+
epsg (int): EPSG code for the spatial reference (optional).
|
|
37
|
+
"""
|
|
38
|
+
print(f"Input CSV : {input_csv}")
|
|
39
|
+
print(f"Output TIF : {output_tif}")
|
|
40
|
+
|
|
41
|
+
# --- Read CSV -------------------------------------------------------
|
|
42
|
+
try:
|
|
43
|
+
data = np.genfromtxt(input_csv, delimiter=",", dtype=np.float32)
|
|
44
|
+
except Exception as exc:
|
|
45
|
+
print(f"Error reading CSV: {exc}")
|
|
46
|
+
sys.exit(1)
|
|
47
|
+
|
|
48
|
+
if data.ndim == 1:
|
|
49
|
+
# Single-row CSV — treat as one-row raster
|
|
50
|
+
data = data.reshape(1, -1)
|
|
51
|
+
|
|
52
|
+
rows, cols = data.shape
|
|
53
|
+
print(f"Grid size : {cols} cols x {rows} rows")
|
|
54
|
+
|
|
55
|
+
# --- Build GeoTransform ---------------------------------------------
|
|
56
|
+
# GDAL geotransform: (x_top_left, pixel_width, 0, y_top_left, 0, -pixel_height)
|
|
57
|
+
x_top_left = xll
|
|
58
|
+
y_top_left = yll + rows * cellsize
|
|
59
|
+
geotransform = (x_top_left, cellsize, 0.0, y_top_left, 0.0, -cellsize)
|
|
60
|
+
|
|
61
|
+
# --- Create output GeoTIFF -----------------------------------------
|
|
62
|
+
driver = gdal.GetDriverByName("GTiff")
|
|
63
|
+
out_ds = driver.Create(output_tif, cols, rows, 1, gdal.GDT_Float32)
|
|
64
|
+
if out_ds is None:
|
|
65
|
+
print(f"Error: Could not create {output_tif}")
|
|
66
|
+
sys.exit(1)
|
|
67
|
+
|
|
68
|
+
out_ds.SetGeoTransform(geotransform)
|
|
69
|
+
|
|
70
|
+
# Spatial reference
|
|
71
|
+
srs = osr.SpatialReference()
|
|
72
|
+
if epsg is not None:
|
|
73
|
+
srs.ImportFromEPSG(epsg)
|
|
74
|
+
out_ds.SetProjection(srs.ExportToWkt())
|
|
75
|
+
print(f"Projection : EPSG:{epsg}")
|
|
76
|
+
else:
|
|
77
|
+
print("Projection : none (no EPSG provided)")
|
|
78
|
+
|
|
79
|
+
# Write band
|
|
80
|
+
out_band = out_ds.GetRasterBand(1)
|
|
81
|
+
out_band.SetNoDataValue(nodata)
|
|
82
|
+
out_band.WriteArray(data)
|
|
83
|
+
out_band.FlushCache()
|
|
84
|
+
|
|
85
|
+
out_ds.FlushCache()
|
|
86
|
+
out_ds = None
|
|
87
|
+
|
|
88
|
+
print(f"NoData : {nodata}")
|
|
89
|
+
print(f"Done -> {output_tif}")
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
def main():
|
|
93
|
+
parser = argparse.ArgumentParser(
|
|
94
|
+
prog='csv2tif',
|
|
95
|
+
description="Convert a CSV raster grid to a GeoTIFF",
|
|
96
|
+
formatter_class=argparse.RawDescriptionHelpFormatter,
|
|
97
|
+
epilog="""
|
|
98
|
+
Examples:
|
|
99
|
+
csv2tif -i input.csv
|
|
100
|
+
csv2tif -i input.csv -o output.tif
|
|
101
|
+
csv2tif -i input.csv -o output.tif --xll 250000 --yll 2500000 --cellsize 5
|
|
102
|
+
csv2tif -i input.csv --epsg 32648
|
|
103
|
+
"""
|
|
104
|
+
)
|
|
105
|
+
parser.add_argument(
|
|
106
|
+
"-i", "--input", required=True,
|
|
107
|
+
help="Input CSV file path"
|
|
108
|
+
)
|
|
109
|
+
parser.add_argument(
|
|
110
|
+
"-o", "--output", default=None,
|
|
111
|
+
help="Output TIF file path (default: same name as input with .tif extension)"
|
|
112
|
+
)
|
|
113
|
+
parser.add_argument(
|
|
114
|
+
"--xll", type=float, default=0.0,
|
|
115
|
+
help="X coordinate of lower-left corner (default: 0.0)"
|
|
116
|
+
)
|
|
117
|
+
parser.add_argument(
|
|
118
|
+
"--yll", type=float, default=0.0,
|
|
119
|
+
help="Y coordinate of lower-left corner (default: 0.0)"
|
|
120
|
+
)
|
|
121
|
+
parser.add_argument(
|
|
122
|
+
"--cellsize", type=float, default=1.0,
|
|
123
|
+
help="Cell size in map units (default: 1.0)"
|
|
124
|
+
)
|
|
125
|
+
parser.add_argument(
|
|
126
|
+
"-n", "--nodata", type=float, default=-999,
|
|
127
|
+
help="NoData value to assign (default: -999)"
|
|
128
|
+
)
|
|
129
|
+
parser.add_argument(
|
|
130
|
+
"--epsg", type=int, default=None,
|
|
131
|
+
help="EPSG code for spatial reference (optional)"
|
|
132
|
+
)
|
|
133
|
+
|
|
134
|
+
args = parser.parse_args()
|
|
135
|
+
|
|
136
|
+
if not os.path.isfile(args.input):
|
|
137
|
+
print(f"Error: Input file not found: {args.input}")
|
|
138
|
+
sys.exit(1)
|
|
139
|
+
|
|
140
|
+
output_tif = args.output
|
|
141
|
+
if output_tif is None:
|
|
142
|
+
base = os.path.splitext(args.input)[0]
|
|
143
|
+
output_tif = base + ".tif"
|
|
144
|
+
|
|
145
|
+
csv_to_tif(
|
|
146
|
+
input_csv=args.input,
|
|
147
|
+
output_tif=output_tif,
|
|
148
|
+
xll=args.xll,
|
|
149
|
+
yll=args.yll,
|
|
150
|
+
cellsize=args.cellsize,
|
|
151
|
+
nodata=args.nodata,
|
|
152
|
+
epsg=args.epsg,
|
|
153
|
+
)
|
|
154
|
+
|
|
155
|
+
|
|
156
|
+
if __name__ == "__main__":
|
|
157
|
+
main()
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
#!/usr/bin/env python
|
|
2
|
+
"""
|
|
3
|
+
Command-line utility to display descriptions of demtools functionality.
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
import argparse
|
|
7
|
+
import sys
|
|
8
|
+
from textwrap import dedent
|
|
9
|
+
|
|
10
|
+
TOOL_DESCRIPTIONS = {
|
|
11
|
+
'chgnodata': """
|
|
12
|
+
Convert nodata values for TIF raster files.
|
|
13
|
+
|
|
14
|
+
This tool reads one or more GeoTIFF files, replaces the existing nodata
|
|
15
|
+
value with a new one (re-writing pixel data), and saves a backup of the
|
|
16
|
+
original file in the RAS_BAK/ directory.
|
|
17
|
+
|
|
18
|
+
Examples:
|
|
19
|
+
chgnodata -a # Process all *.tif (nodata=-999)
|
|
20
|
+
chgnodata -a -v -9999 # Process all *.tif, set nodata to -9999
|
|
21
|
+
chgnodata -i dem.tif # Process a single file (nodata=-999)
|
|
22
|
+
chgnodata -i dem.tif -v -9999 # Process a single file, set nodata to -9999
|
|
23
|
+
""",
|
|
24
|
+
'setnodata': """
|
|
25
|
+
Define a nodata value for TIF raster files without modifying pixel data.
|
|
26
|
+
|
|
27
|
+
This tool updates only the nodata *metadata* of one or more GeoTIFF files
|
|
28
|
+
without touching the actual pixel values. A backup is saved in RAS_BAK/.
|
|
29
|
+
|
|
30
|
+
Examples:
|
|
31
|
+
setnodata -a # Process all *.tif (nodata=-999)
|
|
32
|
+
setnodata -a -v -9999 # Process all *.tif, define nodata as -9999
|
|
33
|
+
setnodata -i dem.tif # Process a single file (nodata=-999)
|
|
34
|
+
setnodata -i dem.tif -v -9999 # Process a single file, define nodata as -9999
|
|
35
|
+
""",
|
|
36
|
+
'csv2tif': """
|
|
37
|
+
Convert a plain-numeric CSV raster grid to a GeoTIFF file.
|
|
38
|
+
|
|
39
|
+
The CSV must contain only numeric values arranged in rows (top→bottom) and
|
|
40
|
+
columns (left→right), with no header row. Georeferencing parameters
|
|
41
|
+
(lower-left corner coordinates, cell size, EPSG) can be provided via options.
|
|
42
|
+
|
|
43
|
+
Examples:
|
|
44
|
+
csv2tif -i grid.csv
|
|
45
|
+
csv2tif -i grid.csv -o dem.tif
|
|
46
|
+
csv2tif -i grid.csv --xll 250000 --yll 2500000 --cellsize 5 --epsg 32648
|
|
47
|
+
csv2tif -i grid.csv -n -9999
|
|
48
|
+
""",
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
def main():
|
|
53
|
+
parser = argparse.ArgumentParser(
|
|
54
|
+
prog='demtools-info',
|
|
55
|
+
description='Display descriptions of demtools commands',
|
|
56
|
+
formatter_class=argparse.RawDescriptionHelpFormatter,
|
|
57
|
+
)
|
|
58
|
+
parser.add_argument(
|
|
59
|
+
'tool',
|
|
60
|
+
nargs='?',
|
|
61
|
+
choices=list(TOOL_DESCRIPTIONS.keys()),
|
|
62
|
+
help='Tool name to describe (omit to list all tools)',
|
|
63
|
+
)
|
|
64
|
+
args = parser.parse_args()
|
|
65
|
+
|
|
66
|
+
if args.tool:
|
|
67
|
+
print(f"\n--- {args.tool} ---")
|
|
68
|
+
print(dedent(TOOL_DESCRIPTIONS[args.tool]))
|
|
69
|
+
else:
|
|
70
|
+
print("\nDEMTOOLS — DEM raster utility commands\n")
|
|
71
|
+
for tool, desc in TOOL_DESCRIPTIONS.items():
|
|
72
|
+
first_line = dedent(desc).strip().splitlines()[0]
|
|
73
|
+
print(f" {tool:<14} {first_line}")
|
|
74
|
+
print("\nRun 'demtools-info <tool>' for details on a specific command.")
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
if __name__ == "__main__":
|
|
78
|
+
main()
|
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""
|
|
3
|
+
setnodata - Define (assign) a nodata value for TIF raster files without modifying pixel data
|
|
4
|
+
Uses GDAL/OGR Python bindings
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
import glob
|
|
8
|
+
import argparse
|
|
9
|
+
import os
|
|
10
|
+
import sys
|
|
11
|
+
from osgeo import gdal, gdalconst
|
|
12
|
+
|
|
13
|
+
def define_nodata_value(input_file, nodata_value=-999):
|
|
14
|
+
"""
|
|
15
|
+
Define a nodata value for a raster file without changing any pixel data.
|
|
16
|
+
Only the nodata metadata is updated in the file.
|
|
17
|
+
|
|
18
|
+
Args:
|
|
19
|
+
input_file (str): Path to input TIF file
|
|
20
|
+
nodata_value (float): Nodata value to assign (default: -999)
|
|
21
|
+
"""
|
|
22
|
+
print(f"Processing: {input_file}")
|
|
23
|
+
|
|
24
|
+
# Open the input dataset in read-only to inspect current state
|
|
25
|
+
src_ds = gdal.Open(input_file, gdalconst.GA_ReadOnly)
|
|
26
|
+
if src_ds is None:
|
|
27
|
+
print(f"Error: Could not open {input_file}")
|
|
28
|
+
return False
|
|
29
|
+
|
|
30
|
+
# Get raster band (assuming single band, modify if multi-band)
|
|
31
|
+
src_band = src_ds.GetRasterBand(1)
|
|
32
|
+
|
|
33
|
+
# Get current nodata value
|
|
34
|
+
current_nodata = src_band.GetNoDataValue()
|
|
35
|
+
print(f" Current nodata value: {current_nodata}")
|
|
36
|
+
|
|
37
|
+
if current_nodata == nodata_value:
|
|
38
|
+
print(f" Nodata value already set to {nodata_value}, skipping...")
|
|
39
|
+
src_ds = None
|
|
40
|
+
return True
|
|
41
|
+
|
|
42
|
+
# Close read-only handle before backup/update
|
|
43
|
+
src_ds = None
|
|
44
|
+
|
|
45
|
+
# Create backup directory
|
|
46
|
+
backup_dir = "RAS_BAK"
|
|
47
|
+
if not os.path.exists(backup_dir):
|
|
48
|
+
os.makedirs(backup_dir)
|
|
49
|
+
print(f" Created backup directory: {backup_dir}")
|
|
50
|
+
|
|
51
|
+
backup_filename = os.path.basename(input_file)
|
|
52
|
+
backup_file = os.path.join(backup_dir, backup_filename)
|
|
53
|
+
|
|
54
|
+
# Create backup before modifying
|
|
55
|
+
print(f" Creating backup: {backup_file}")
|
|
56
|
+
src_ds = gdal.Open(input_file, gdalconst.GA_ReadOnly)
|
|
57
|
+
gdal.GetDriverByName('GTiff').CreateCopy(backup_file, src_ds)
|
|
58
|
+
src_ds = None
|
|
59
|
+
|
|
60
|
+
# Re-open in update mode to set nodata metadata only (no pixel data changed)
|
|
61
|
+
ds = gdal.Open(input_file, gdalconst.GA_Update)
|
|
62
|
+
if ds is None:
|
|
63
|
+
print(f"Error: Could not open {input_file} for update")
|
|
64
|
+
return False
|
|
65
|
+
|
|
66
|
+
band = ds.GetRasterBand(1)
|
|
67
|
+
band.SetNoDataValue(nodata_value)
|
|
68
|
+
band.FlushCache()
|
|
69
|
+
ds = None
|
|
70
|
+
|
|
71
|
+
print(f" Successfully defined nodata value as {nodata_value}")
|
|
72
|
+
return True
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
def main():
|
|
76
|
+
parser = argparse.ArgumentParser(
|
|
77
|
+
prog='setnodata',
|
|
78
|
+
description='Define a nodata value for TIF raster files without modifying pixel data',
|
|
79
|
+
formatter_class=argparse.RawDescriptionHelpFormatter,
|
|
80
|
+
epilog="""
|
|
81
|
+
Examples:
|
|
82
|
+
setnodata -a # Process all *.tif in current directory (nodata=-999)
|
|
83
|
+
setnodata -a -v -9999 # Process all *.tif, define nodata as -9999
|
|
84
|
+
setnodata -i dem.tif # Process a single file (nodata=-999)
|
|
85
|
+
setnodata -i dem.tif -v -9999 # Process a single file, define nodata as -9999
|
|
86
|
+
"""
|
|
87
|
+
)
|
|
88
|
+
|
|
89
|
+
# Mutually exclusive group: -a or -i
|
|
90
|
+
group = parser.add_mutually_exclusive_group(required=True)
|
|
91
|
+
group.add_argument(
|
|
92
|
+
'-a', '--all',
|
|
93
|
+
action='store_true',
|
|
94
|
+
help='Process all *.tif files in the current directory'
|
|
95
|
+
)
|
|
96
|
+
group.add_argument(
|
|
97
|
+
'-i', '--input',
|
|
98
|
+
metavar='FILE',
|
|
99
|
+
help='Process a single TIF file'
|
|
100
|
+
)
|
|
101
|
+
|
|
102
|
+
parser.add_argument(
|
|
103
|
+
'-v', '--value',
|
|
104
|
+
dest='nodata_value',
|
|
105
|
+
type=float,
|
|
106
|
+
default=-999,
|
|
107
|
+
metavar='NODATA',
|
|
108
|
+
help='Nodata value to assign (default: -999)'
|
|
109
|
+
)
|
|
110
|
+
|
|
111
|
+
args = parser.parse_args()
|
|
112
|
+
new_nodata = args.nodata_value
|
|
113
|
+
|
|
114
|
+
print(f"setnodata: defining nodata value as {new_nodata}")
|
|
115
|
+
print("=" * 60)
|
|
116
|
+
|
|
117
|
+
if args.all:
|
|
118
|
+
tif_files = glob.glob("*.tif")
|
|
119
|
+
if not tif_files:
|
|
120
|
+
print("No TIF files found in current directory.")
|
|
121
|
+
sys.exit(0)
|
|
122
|
+
|
|
123
|
+
print(f"Found {len(tif_files)} TIF file(s):")
|
|
124
|
+
for f in tif_files:
|
|
125
|
+
print(f" - {f}")
|
|
126
|
+
print(f"\nProcessing files...")
|
|
127
|
+
print("-" * 40)
|
|
128
|
+
|
|
129
|
+
success_count = 0
|
|
130
|
+
for tif_file in tif_files:
|
|
131
|
+
if define_nodata_value(tif_file, new_nodata):
|
|
132
|
+
success_count += 1
|
|
133
|
+
print()
|
|
134
|
+
|
|
135
|
+
print("Done!")
|
|
136
|
+
print(f"Successfully processed: {success_count}/{len(tif_files)} files")
|
|
137
|
+
if success_count < len(tif_files):
|
|
138
|
+
print("Some files failed to process. Check error messages above.")
|
|
139
|
+
|
|
140
|
+
elif args.input:
|
|
141
|
+
input_file = args.input
|
|
142
|
+
if not os.path.isfile(input_file):
|
|
143
|
+
print(f"Error: File not found: {input_file}")
|
|
144
|
+
sys.exit(1)
|
|
145
|
+
|
|
146
|
+
print("-" * 40)
|
|
147
|
+
success = define_nodata_value(input_file, new_nodata)
|
|
148
|
+
print()
|
|
149
|
+
if success:
|
|
150
|
+
print("Done!")
|
|
151
|
+
else:
|
|
152
|
+
print("Failed.")
|
|
153
|
+
sys.exit(1)
|
|
154
|
+
|
|
155
|
+
|
|
156
|
+
if __name__ == "__main__":
|
|
157
|
+
gdal.UseExceptions()
|
|
158
|
+
|
|
159
|
+
try:
|
|
160
|
+
main()
|
|
161
|
+
except Exception as e:
|
|
162
|
+
print(f"Error: {e}")
|
|
163
|
+
import traceback
|
|
164
|
+
traceback.print_exc()
|
|
File without changes
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: demtools
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: A collection of tools for working with DEM (Digital Elevation Model) raster files
|
|
5
|
+
Home-page: https://github.com/AaronOET/demtools
|
|
6
|
+
Author: aaronchh
|
|
7
|
+
Author-email: aaronchh <aaronhsu219@gmail.com>
|
|
8
|
+
License: MIT
|
|
9
|
+
Project-URL: Homepage, https://github.com/AaronOET/demtools
|
|
10
|
+
Classifier: Programming Language :: Python :: 3
|
|
11
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
12
|
+
Classifier: Operating System :: OS Independent
|
|
13
|
+
Requires-Python: >=3.8
|
|
14
|
+
Description-Content-Type: text/markdown
|
|
15
|
+
Requires-Dist: numpy>=1.20.0
|
|
16
|
+
Dynamic: author
|
|
17
|
+
Dynamic: home-page
|
|
18
|
+
Dynamic: requires-python
|
|
19
|
+
|
|
20
|
+
# DEMTOOLS
|
|
21
|
+
|
|
22
|
+
A collection of Python tools for working with DEM (Digital Elevation Model) raster files.
|
|
23
|
+
|
|
24
|
+
> **GDAL Installation**: GDAL is required for this package. For conda environments, use `conda install gdal` to install GDAL. For non-conda environments, download the appropriate wheel file from [https://github.com/cgohlke/geospatial-wheels/releases](https://github.com/cgohlke/geospatial-wheels/releases) to install GDAL.
|
|
25
|
+
|
|
26
|
+
## Installation
|
|
27
|
+
|
|
28
|
+
```bash
|
|
29
|
+
pip install -e .
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
## Features
|
|
33
|
+
|
|
34
|
+
- **chgnodata**: Convert (replace) the nodata value of GeoTIFF files, updating pixel data accordingly
|
|
35
|
+
- **setnodata**: Assign a nodata value to GeoTIFF files without modifying pixel data (metadata only)
|
|
36
|
+
- **csv2tif**: Convert a plain-numeric CSV raster grid to a GeoTIFF file
|
|
37
|
+
|
|
38
|
+
## Usage
|
|
39
|
+
|
|
40
|
+
### Command-line
|
|
41
|
+
|
|
42
|
+
```bash
|
|
43
|
+
# List all available commands
|
|
44
|
+
demtools-info
|
|
45
|
+
|
|
46
|
+
# Convert nodata values for all TIF files in the current directory
|
|
47
|
+
chgnodata -a
|
|
48
|
+
|
|
49
|
+
# Convert nodata of a single file to -9999
|
|
50
|
+
chgnodata -i dem.tif -v -9999
|
|
51
|
+
|
|
52
|
+
# Set nodata metadata for all TIF files (no pixel data change)
|
|
53
|
+
setnodata -a -v -9999
|
|
54
|
+
|
|
55
|
+
# Convert a CSV grid to GeoTIFF
|
|
56
|
+
csv2tif -i grid.csv -o dem.tif --xll 250000 --yll 2500000 --cellsize 5 --epsg 32648
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
### Python API
|
|
60
|
+
|
|
61
|
+
```python
|
|
62
|
+
from demtools import chgnodata, setnodata, csv2tif
|
|
63
|
+
|
|
64
|
+
# Convert nodata value in a raster file
|
|
65
|
+
chgnodata.convert_nodata_value("dem.tif", output_nodata=-9999)
|
|
66
|
+
|
|
67
|
+
# Assign nodata metadata without touching pixel values
|
|
68
|
+
setnodata.define_nodata_value("dem.tif", nodata_value=-9999)
|
|
69
|
+
|
|
70
|
+
# Convert CSV grid to GeoTIFF
|
|
71
|
+
csv2tif.csv_to_tif(
|
|
72
|
+
input_csv="grid.csv",
|
|
73
|
+
output_tif="dem.tif",
|
|
74
|
+
xll=250000,
|
|
75
|
+
yll=2500000,
|
|
76
|
+
cellsize=5,
|
|
77
|
+
nodata=-999,
|
|
78
|
+
epsg=32648,
|
|
79
|
+
)
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
## Notes
|
|
83
|
+
|
|
84
|
+
- `chgnodata` and `setnodata` automatically create a `RAS_BAK/` directory with backup copies before modifying files.
|
|
85
|
+
- `csv2tif` expects a plain numeric CSV (no headers), with rows ordered from north to south.
|
|
86
|
+
- GDAL must be installed separately via conda or a pre-built wheel; it is not listed in `requirements.txt` as it cannot be reliably installed via pip on all platforms.
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
MANIFEST.in
|
|
2
|
+
README.md
|
|
3
|
+
pyproject.toml
|
|
4
|
+
requirements.txt
|
|
5
|
+
setup.py
|
|
6
|
+
demtools/__init__.py
|
|
7
|
+
demtools/chgnodata.py
|
|
8
|
+
demtools/csv2tif.py
|
|
9
|
+
demtools/describe.py
|
|
10
|
+
demtools/setnodata.py
|
|
11
|
+
demtools.egg-info/PKG-INFO
|
|
12
|
+
demtools.egg-info/SOURCES.txt
|
|
13
|
+
demtools.egg-info/dependency_links.txt
|
|
14
|
+
demtools.egg-info/entry_points.txt
|
|
15
|
+
demtools.egg-info/requires.txt
|
|
16
|
+
demtools.egg-info/top_level.txt
|
|
17
|
+
demtools/tests/__init__.py
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
numpy>=1.20.0
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
demtools
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["setuptools>=61.0", "wheel"]
|
|
3
|
+
build-backend = "setuptools.build_meta"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "demtools"
|
|
7
|
+
version = "0.1.0"
|
|
8
|
+
authors = [
|
|
9
|
+
{ name = "aaronchh", email = "aaronhsu219@gmail.com" },
|
|
10
|
+
]
|
|
11
|
+
description = "A collection of tools for working with DEM (Digital Elevation Model) raster files"
|
|
12
|
+
readme = "README.md"
|
|
13
|
+
license = { text = "MIT" }
|
|
14
|
+
requires-python = ">=3.8"
|
|
15
|
+
dependencies = [
|
|
16
|
+
"numpy>=1.20.0",
|
|
17
|
+
]
|
|
18
|
+
classifiers = [
|
|
19
|
+
"Programming Language :: Python :: 3",
|
|
20
|
+
"License :: OSI Approved :: MIT License",
|
|
21
|
+
"Operating System :: OS Independent",
|
|
22
|
+
]
|
|
23
|
+
|
|
24
|
+
[project.urls]
|
|
25
|
+
Homepage = "https://github.com/AaronOET/demtools"
|
|
26
|
+
|
|
27
|
+
[project.scripts]
|
|
28
|
+
demtools-info = "demtools.describe:main"
|
|
29
|
+
chgnodata = "demtools.chgnodata:main"
|
|
30
|
+
setnodata = "demtools.setnodata:main"
|
|
31
|
+
csv2tif = "demtools.csv2tif:main"
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
numpy>=1.20.0
|
demtools-0.1.0/setup.cfg
ADDED
demtools-0.1.0/setup.py
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
from setuptools import setup, find_packages
|
|
2
|
+
|
|
3
|
+
with open("README.md", "r", encoding="utf-8") as fh:
|
|
4
|
+
long_description = fh.read()
|
|
5
|
+
|
|
6
|
+
with open("requirements.txt", "r", encoding="utf-8") as f:
|
|
7
|
+
requirements = f.read().splitlines()
|
|
8
|
+
|
|
9
|
+
setup(
|
|
10
|
+
name="demtools",
|
|
11
|
+
version="0.1.0",
|
|
12
|
+
author="aaronchh",
|
|
13
|
+
author_email="aaronhsu219@gmail.com",
|
|
14
|
+
description="A collection of tools for working with DEM (Digital Elevation Model) raster files",
|
|
15
|
+
long_description=long_description,
|
|
16
|
+
long_description_content_type="text/markdown",
|
|
17
|
+
url="https://github.com/AaronOET/demtools",
|
|
18
|
+
packages=find_packages(),
|
|
19
|
+
classifiers=[
|
|
20
|
+
"Programming Language :: Python :: 3",
|
|
21
|
+
"License :: OSI Approved :: MIT License",
|
|
22
|
+
"Operating System :: OS Independent",
|
|
23
|
+
],
|
|
24
|
+
python_requires=">=3.8",
|
|
25
|
+
install_requires=requirements,
|
|
26
|
+
entry_points={
|
|
27
|
+
"console_scripts": [
|
|
28
|
+
"demtools-info=demtools.describe:main",
|
|
29
|
+
"chgnodata=demtools.chgnodata:main",
|
|
30
|
+
"setnodata=demtools.setnodata:main",
|
|
31
|
+
"csv2tif=demtools.csv2tif:main",
|
|
32
|
+
],
|
|
33
|
+
},
|
|
34
|
+
)
|