|
5 | 5 | import math
|
6 | 6 | import os
|
7 | 7 | import re
|
8 |
| -import struct |
9 | 8 | import tempfile
|
10 | 9 | import uuid
|
11 |
| -import zlib |
12 | 10 | from contextlib import contextmanager
|
13 | 11 | from typing import (
|
14 | 12 | Any,
|
|
34 | 32 | _parse_size,
|
35 | 33 | none_max,
|
36 | 34 | none_min,
|
| 35 | + write_png, |
37 | 36 | )
|
38 | 37 |
|
39 | 38 | try:
|
@@ -207,99 +206,6 @@ def _is_url(url: str) -> bool:
|
207 | 206 | return False
|
208 | 207 |
|
209 | 208 |
|
210 |
| -def write_png( |
211 |
| - data: Any, |
212 |
| - origin: str = "upper", |
213 |
| - colormap: Optional[Callable] = None, |
214 |
| -) -> bytes: |
215 |
| - """ |
216 |
| - Transform an array of data into a PNG string. |
217 |
| - This can be written to disk using binary I/O, or encoded using base64 |
218 |
| - for an inline PNG like this: |
219 |
| -
|
220 |
| - >>> png_str = write_png(array) |
221 |
| - >>> "data:image/png;base64," + png_str.encode("base64") |
222 |
| -
|
223 |
| - Inspired from |
224 |
| - https://stackoverflow.com/questions/902761/saving-a-numpy-array-as-an-image |
225 |
| -
|
226 |
| - Parameters |
227 |
| - ---------- |
228 |
| - data: numpy array or equivalent list-like object. |
229 |
| - Must be NxM (mono), NxMx3 (RGB) or NxMx4 (RGBA) |
230 |
| -
|
231 |
| - origin : ['upper' | 'lower'], optional, default 'upper' |
232 |
| - Place the [0,0] index of the array in the upper left or lower left |
233 |
| - corner of the axes. |
234 |
| -
|
235 |
| - colormap : callable, used only for `mono` image. |
236 |
| - Function of the form [x -> (r,g,b)] or [x -> (r,g,b,a)] |
237 |
| - for transforming a mono image into RGB. |
238 |
| - It must output iterables of length 3 or 4, with values between |
239 |
| - 0. and 1. Hint: you can use colormaps from `matplotlib.cm`. |
240 |
| -
|
241 |
| - Returns |
242 |
| - ------- |
243 |
| - PNG formatted byte string |
244 |
| -
|
245 |
| - """ |
246 |
| - colormap = colormap or (lambda x: (x, x, x, 1)) |
247 |
| - |
248 |
| - arr = np.atleast_3d(data) |
249 |
| - height, width, nblayers = arr.shape |
250 |
| - |
251 |
| - if nblayers not in [1, 3, 4]: |
252 |
| - raise ValueError("Data must be NxM (mono), NxMx3 (RGB), or NxMx4 (RGBA)") |
253 |
| - assert arr.shape == (height, width, nblayers) |
254 |
| - |
255 |
| - if nblayers == 1: |
256 |
| - arr = np.array(list(map(colormap, arr.ravel()))) |
257 |
| - nblayers = arr.shape[1] |
258 |
| - if nblayers not in [3, 4]: |
259 |
| - raise ValueError( |
260 |
| - "colormap must provide colors of length 3 (RGB) or 4 (RGBA)" |
261 |
| - ) |
262 |
| - arr = arr.reshape((height, width, nblayers)) |
263 |
| - assert arr.shape == (height, width, nblayers) |
264 |
| - |
265 |
| - if nblayers == 3: |
266 |
| - arr = np.concatenate((arr, np.ones((height, width, 1))), axis=2) |
267 |
| - nblayers = 4 |
268 |
| - assert arr.shape == (height, width, nblayers) |
269 |
| - assert nblayers == 4 |
270 |
| - |
271 |
| - # Normalize to uint8 if it isn't already. |
272 |
| - if arr.dtype != "uint8": |
273 |
| - with np.errstate(divide="ignore", invalid="ignore"): |
274 |
| - arr = arr * 255.0 / arr.max(axis=(0, 1)).reshape((1, 1, 4)) |
275 |
| - arr[~np.isfinite(arr)] = 0 |
276 |
| - arr = arr.astype("uint8") |
277 |
| - |
278 |
| - # Eventually flip the image. |
279 |
| - if origin == "lower": |
280 |
| - arr = arr[::-1, :, :] |
281 |
| - |
282 |
| - # Transform the array to bytes. |
283 |
| - raw_data = b"".join([b"\x00" + arr[i, :, :].tobytes() for i in range(height)]) |
284 |
| - |
285 |
| - def png_pack(png_tag, data): |
286 |
| - chunk_head = png_tag + data |
287 |
| - return ( |
288 |
| - struct.pack("!I", len(data)) |
289 |
| - + chunk_head |
290 |
| - + struct.pack("!I", 0xFFFFFFFF & zlib.crc32(chunk_head)) |
291 |
| - ) |
292 |
| - |
293 |
| - return b"".join( |
294 |
| - [ |
295 |
| - b"\x89PNG\r\n\x1a\n", |
296 |
| - png_pack(b"IHDR", struct.pack("!2I5B", width, height, 8, 6, 0, 0, 0)), |
297 |
| - png_pack(b"IDAT", zlib.compress(raw_data, 9)), |
298 |
| - png_pack(b"IEND", b""), |
299 |
| - ] |
300 |
| - ) |
301 |
| - |
302 |
| - |
303 | 209 | def mercator_transform(
|
304 | 210 | data: Any,
|
305 | 211 | lat_bounds: Tuple[float, float],
|
|
0 commit comments