nt25 0.1.7__tar.gz → 0.1.9__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.
@@ -12,14 +12,16 @@ uv.lock
12
12
 
13
13
  # package
14
14
  *.spec
15
+ scripts/pub.sh
15
16
 
16
17
  # dataset
17
18
  ds/
18
19
 
19
20
  # output
21
+ out.csv
20
22
  output/
21
23
 
22
- # others
23
- tests/*.jpg
24
- tests/*.png
24
+ # test
25
+ tests/**/*.jpg
26
+ tests/**/*.png
25
27
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: nt25
3
- Version: 0.1.7
3
+ Version: 0.1.9
4
4
  Summary: Neo's Tools of Python
5
5
  Requires-Python: >=3.10
6
6
  Requires-Dist: exif>=1.6.1
@@ -9,6 +9,7 @@ Requires-Dist: openpyxl>=3.1.5
9
9
  Requires-Dist: pandas>=2.3.2
10
10
  Requires-Dist: pillow>=11.3.0
11
11
  Requires-Dist: pyinstaller>=6.15.0
12
+ Requires-Dist: requests>=2.32.5
12
13
  Requires-Dist: scikit-learn>=1.7.1
13
14
  Requires-Dist: sympy>=1.14.0
14
15
  Description-Content-Type: text/markdown
@@ -23,10 +24,10 @@ Neo's Tools of Python in 2025
23
24
  - [x] calc
24
25
  - [x] draw
25
26
  - [x] et: EXIF tools
27
+ - [x] mt: MapXYZ Transfer tool
28
+ - [x] ttp: TIFF to PNG, thread support
26
29
  - [ ] mysql
27
30
  - [ ] redis
28
- - [ ] maptrans
29
- - [ ] ttp
30
31
 
31
32
  ## scripts from init
32
33
 
@@ -8,10 +8,10 @@ Neo's Tools of Python in 2025
8
8
  - [x] calc
9
9
  - [x] draw
10
10
  - [x] et: EXIF tools
11
+ - [x] mt: MapXYZ Transfer tool
12
+ - [x] ttp: TIFF to PNG, thread support
11
13
  - [ ] mysql
12
14
  - [ ] redis
13
- - [ ] maptrans
14
- - [ ] ttp
15
15
 
16
16
  ## scripts from init
17
17
 
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "nt25"
3
- version = "0.1.7"
3
+ version = "0.1.9"
4
4
  description = "Neo's Tools of Python"
5
5
  readme = "README.md"
6
6
  requires-python = ">=3.10"
@@ -13,11 +13,14 @@ dependencies = [
13
13
  "scikit-learn>=1.7.1",
14
14
  "sympy>=1.14.0",
15
15
  "pillow>=11.3.0",
16
+ "requests>=2.32.5",
16
17
  ]
17
18
 
18
19
  [project.scripts]
19
20
  demo = "nt25.demo:main"
20
21
  et = "nt25.lib.et:main"
22
+ mt = "nt25.mt:main"
23
+ ttp = "nt25.ttp:main"
21
24
 
22
25
  [build-system]
23
26
  requires = ["hatchling"]
@@ -1,4 +1,3 @@
1
- import io
2
1
  import os
3
2
  import time
4
3
  import json
@@ -10,7 +9,8 @@ from PIL import Image as pi
10
9
  from datetime import UTC, datetime, timedelta, timezone
11
10
  from exif import Image, DATETIME_STR_FORMAT
12
11
 
13
- VERSION = "0.1.3"
12
+ VERSION = "0.1.4"
13
+ MAX_WIDTH = 1080
14
14
  COMMENT_SEGMENT = b"\xff\xfe"
15
15
  EPOCH = datetime.fromtimestamp(0, UTC)
16
16
 
@@ -34,12 +34,23 @@ def gpsDt2Dt(date, time, offset=8):
34
34
  return utc.astimezone(timezone(timedelta(hours=offset)))
35
35
 
36
36
 
37
- def optimizeFile(file, q=80):
37
+ def optimizeFile(file, q=80, mw=MAX_WIDTH):
38
38
  less = False
39
39
  ofile = file + '.jpg'
40
- pi.open(file).save(ofile, quality=q, optimize=True, progression=True)
41
40
 
42
- if os.path.getsize(ofile) < os.path.getsize(file) * 0.9:
41
+ img = pi.open(file)
42
+ w, h = img.size
43
+ m = max(w, h)
44
+
45
+ if m > mw:
46
+ scale = mw / m
47
+ w = int(scale * w)
48
+ h = int(scale * h)
49
+ img = img.resize((w, h))
50
+
51
+ img.save(ofile, quality=q, optimize=True, progression=True)
52
+
53
+ if os.path.getsize(ofile) < os.path.getsize(file) * 0.8:
43
54
  less = True
44
55
  transplant(file, ofile)
45
56
  os.replace(ofile, file)
@@ -0,0 +1,105 @@
1
+ from nt25 import fio
2
+
3
+ import json
4
+ import requests
5
+ import argparse
6
+
7
+
8
+ kEncoding = 'gbk'
9
+ kWGSName = 'WGS-84'
10
+
11
+ kMaxTranCount = 50
12
+
13
+ kWGSCode = 4326
14
+ k50NCode = 32650
15
+ kCGS2000Z20Code = 4498
16
+ kCGS2000Z21Code = 4499
17
+
18
+ kURLFormatter = 'https://api.maptiler.com/coordinates/transform/' + \
19
+ '{cs}.json?key={key}&s_srs={s}&t_srs={t}'
20
+
21
+
22
+ def genCs4Tran(coordinates):
23
+ cs = []
24
+
25
+ for i in range(min(len(coordinates), kMaxTranCount)):
26
+ c = coordinates[i]
27
+ cs.append('{0},{1}'.format(c[0], c[1]))
28
+
29
+ return ';'.join(cs)
30
+
31
+
32
+ def fetchResult(url):
33
+ result = {}
34
+ res = requests.get(url)
35
+
36
+ try:
37
+ result = json.loads(res.text)
38
+ except Exception as e:
39
+ print(e, '\n\t', res.text)
40
+
41
+ return result
42
+
43
+
44
+ def transform(key, coordinates, startCode, toCode):
45
+ results = []
46
+
47
+ cc = [coordinates[i:i + kMaxTranCount]
48
+ for i in range(0, len(coordinates), kMaxTranCount)]
49
+
50
+ for c in cc:
51
+ url = kURLFormatter.format(cs=genCs4Tran(c),
52
+ key=key, s=startCode, t=toCode)
53
+ result = fetchResult(url)
54
+
55
+ if result and result['results'] and result['results'][0]['x'] is not None:
56
+ results.extend(result['results'])
57
+
58
+ for i in range(len(results)):
59
+ results[i]['z'] = coordinates[i][2] if len(coordinates[i]) > 2 else 0
60
+
61
+ return results
62
+
63
+
64
+ def main():
65
+ parse = argparse.ArgumentParser(description='Map Transform tool, '
66
+ 'from https://docs.maptiler.com/cloud'
67
+ '/api/coordinates/#transform-coordinates')
68
+
69
+ # default=kKey, required=False)
70
+ parse.add_argument('-k', '--key', type=str,
71
+ help='MapTiler key', required=True)
72
+ parse.add_argument('-i', '--input', type=str, required=True,
73
+ help='input file')
74
+ parse.add_argument('-o', '--output', type=str, help='output file',
75
+ default='out.csv')
76
+ parse.add_argument('-s', '--start', type=str, default='4498',
77
+ help='transform start code')
78
+ parse.add_argument('-t', '--to', type=str, default='4236',
79
+ help='transform code to')
80
+
81
+ args = parse.parse_args()
82
+
83
+ key = args.key
84
+ input = args.input
85
+ output = args.output
86
+ start = args.start
87
+ to = args.to
88
+
89
+ csv = fio.getCSV(input, width=3, startLine=1)
90
+
91
+ if csv is not None:
92
+ coordinates = []
93
+ for i in range(len(csv[0])):
94
+ coordinates.append((csv[0][i], csv[1][i], csv[2][i]))
95
+
96
+ result = transform(key, coordinates, start, to)
97
+
98
+ if len(result) > 0:
99
+ content = list(map(lambda r: (r['x'], r['y'], r['z']), result))
100
+ fio.saveCSV(content, output, colsInline=False)
101
+ print(f'Saved {len(result)} rows in {output}')
102
+
103
+
104
+ if __name__ == '__main__':
105
+ main()
@@ -0,0 +1,201 @@
1
+ import re
2
+ import os
3
+ import glob
4
+ import argparse
5
+ import time
6
+
7
+ from threading import Thread, Lock
8
+ from multiprocessing import cpu_count
9
+
10
+ from PIL import Image
11
+ import numpy as np
12
+ import matplotlib.colors as mcolors
13
+
14
+ gTLock = Lock()
15
+ gForceQuit = False
16
+
17
+ gMaxValue = 0
18
+ gShadowValue = 0
19
+
20
+ gCrop = None
21
+ gArea = None
22
+ gLast = None
23
+ gColors = None
24
+
25
+ kTifLevel = 255
26
+
27
+
28
+ def nsort(s):
29
+ sub = re.split(r'(\d+)', s)
30
+ sub = [int(c) if c.isdigit() else c for c in sub]
31
+ return sub
32
+
33
+
34
+ def genGradColors(gradStart, gradStop):
35
+ # ks = kTifLevel * kTifLevel
36
+ # yc = np.linspace(mcolors.to_rgba(gradStart),
37
+ # mcolors.to_rgba(gradStop), ks) * ks
38
+ # colors = [yc[ks - (i - kTifLevel) * (i - kTifLevel)]
39
+ # for i in range(0, kTifLevel)]
40
+
41
+ colors = np.linspace(mcolors.to_rgba(gradStart),
42
+ mcolors.to_rgba(gradStop), kTifLevel) * kTifLevel
43
+ return np.vstack([[0, 0, 0, 0], colors]).astype(np.uint8)
44
+
45
+
46
+ def splitArray(array, times):
47
+ chunk = len(array) // times
48
+ left = len(array) % times
49
+
50
+ result = []
51
+ for i in range(times):
52
+ start = i * chunk + min(i, left)
53
+ end = start + chunk + (1 if i < left else 0)
54
+ result.append(array[start:end])
55
+
56
+ return result
57
+
58
+
59
+ def dump(array):
60
+ shp = array.shape
61
+ for i in range(shp[0]):
62
+ print(array[i])
63
+
64
+
65
+ def ttp(array, path, maxValue):
66
+ global gTLock, gForceQuit, gCrop, gArea, gColors, gMaxValue, gLast
67
+
68
+ if gCrop is None or gArea is None or gColors is None:
69
+ return
70
+
71
+ for tif in array:
72
+ name = os.path.splitext(os.path.basename(tif))[0]
73
+ out = os.path.join(path, name + '.png')
74
+
75
+ image = Image.open(tif)
76
+ ta = np.array(image)
77
+ ta = ta[gCrop[0]:gCrop[1], gCrop[2]:gCrop[3]]
78
+
79
+ ta[ta < 0] = 0
80
+
81
+ with gTLock:
82
+ if ta.max() > gMaxValue:
83
+ gMaxValue = ta.max()
84
+
85
+ if gShadowValue > 0:
86
+ leap = maxValue / 40
87
+ ta = np.where(np.logical_and(gArea > 0, ta < gShadowValue),
88
+ gShadowValue, ta)
89
+ ta = np.where(gLast > ta + leap, gLast - leap, ta)
90
+ gLast = np.array(ta)
91
+ gArea = np.where(ta > gShadowValue, 1, gArea)
92
+
93
+ ta[ta > maxValue] = maxValue
94
+ ta = ta / maxValue * kTifLevel
95
+ Image.fromarray(gColors[ta.astype(np.uint8)], mode='RGBA').save(out)
96
+
97
+ if gForceQuit:
98
+ break
99
+ else:
100
+ print('.', end='', flush=True)
101
+
102
+
103
+ def main():
104
+ global gForceQuit, gCrop, gArea, gColors, gMaxValue, gLast
105
+
106
+ threadCounts = int(cpu_count() / 2)
107
+
108
+ parser = argparse.ArgumentParser(description="TIFF -> PNG")
109
+ parser.add_argument('-d', '--dir', help='Directory to handle',
110
+ default='.', type=str, required=False,)
111
+ parser.add_argument('-s', '--gradStart', help='Gradient color to Start',
112
+ default='#00FFFF', type=str, required=False,)
113
+ parser.add_argument('-t', '--gradStop', help='Gradient color to Stop',
114
+ default='#00008B', type=str, required=False,)
115
+ parser.add_argument('--TB', help='Crop TIFF from Top to Bottom', nargs='+',
116
+ # default=[1700, 3000], type=int, required=False,)
117
+ # default=[1350, 4250], type=int, required=False,)
118
+ default=[1400, 3550], type=int, required=False,)
119
+ # default=[2890, 2895], type=int, required=False,)
120
+ parser.add_argument('--LR', help='Crop TIFF from Left to Right', nargs='+',
121
+ default=[750, 2300], type=int, required=False,)
122
+ # default=[1650, 1655], type=int, required=False,)
123
+ parser.add_argument('-m', '--maxValue', help='Map Max Value',
124
+ default=16, type=int, required=False,)
125
+ parser.add_argument('-v', '--shadowValue', help='keep Value in Shadow',
126
+ default=1, type=int, required=False,)
127
+ parser.add_argument('-c', '--threadCounts', help='Minimum number of Threads',
128
+ default=threadCounts, type=int, required=False,)
129
+
130
+ args = parser.parse_args()
131
+ folder = args.dir
132
+ gradStart = args.gradStart
133
+ gradStop = args.gradStop
134
+
135
+ gColors = genGradColors(gradStart, gradStop)
136
+
137
+ TB = args.TB
138
+ LR = args.LR
139
+ gCrop = [TB[0], TB[1], LR[0], LR[1]]
140
+ gArea = np.zeros([TB[1] - TB[0], LR[1] - LR[0]], dtype=np.uint8)
141
+ gLast = np.zeros([TB[1] - TB[0], LR[1] - LR[0]])
142
+
143
+ maxValue = args.maxValue
144
+ threadCounts = args.threadCounts
145
+
146
+ gShadowValue = args.shadowValue
147
+
148
+ if gShadowValue > 0:
149
+ print(f'Shadow Value: {gShadowValue}')
150
+ threadCounts = 1
151
+
152
+ if threadCounts > 1:
153
+ print(f'Multi-threads to work: {threadCounts}T')
154
+
155
+ files = glob.glob(os.path.join(folder, '*.tif'))
156
+ files = sorted(files, key=nsort)
157
+
158
+ if len(files) == 0:
159
+ print("no TIF found")
160
+ return
161
+
162
+ path = os.path.join(folder, 'out')
163
+ if not os.path.exists(path):
164
+ os.makedirs(path)
165
+
166
+ tfs = splitArray(files, threadCounts)
167
+
168
+ td = []
169
+ for tif in tfs:
170
+ if tif is not None and len(tif) > 0:
171
+ t = Thread(target=ttp, args=(tif, path, maxValue,))
172
+ t.daemon = True
173
+ t.start()
174
+ td.append(t)
175
+
176
+ while True:
177
+ working = False
178
+ for t in td:
179
+ if t.is_alive():
180
+ working = True
181
+ break
182
+
183
+ if working:
184
+ try:
185
+ time.sleep(0.5)
186
+ except KeyboardInterrupt:
187
+ if not gForceQuit:
188
+ print("\n> WARN: Trying to terminate threads...")
189
+
190
+ gForceQuit = True
191
+ else:
192
+ break
193
+
194
+ if gForceQuit:
195
+ print("> WARN: Force quit!")
196
+ else:
197
+ print(f"\nTotal Max value: {maxValue}/{gMaxValue:.2f}")
198
+
199
+
200
+ if __name__ == '__main__':
201
+ main()
@@ -0,0 +1,3 @@
1
+ X,Y,H
2
+ 20510210.743588,4058386.735392,155.55
3
+ 20510196.183790,4057723.195846,123.45
Binary file
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes