@gisatcz/deckgl-geolib 0.0.3 → 1.2.0
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.
- package/.eslintignore +2 -0
- package/.eslintrc.cjs +3 -0
- package/dist/cjs/index.js +99799 -18
- package/dist/cjs/index.js.map +1 -1
- package/dist/cjs/index.min.js +5 -0
- package/dist/cjs/index.min.js.map +1 -0
- package/dist/cjs/types/cogbitmaplayer/CogBitmapLayer.d.ts +16 -0
- package/dist/cjs/types/cogterrainlayer/CogTerrainLayer.d.ts +18 -0
- package/dist/cjs/types/cogtiles/cogtiles.d.ts +29 -0
- package/dist/cjs/types/geoimage/geoimage.d.ts +49 -0
- package/dist/cjs/types/index.d.ts +11 -2
- package/dist/cjs/types/utilities/tileurls.d.ts +4 -0
- package/dist/esm/index.js +99797 -18
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/index.min.js +5 -0
- package/dist/esm/index.min.js.map +1 -0
- package/dist/esm/types/cogbitmaplayer/CogBitmapLayer.d.ts +16 -0
- package/dist/esm/types/cogterrainlayer/CogTerrainLayer.d.ts +18 -0
- package/dist/esm/types/cogtiles/cogtiles.d.ts +29 -0
- package/dist/esm/types/geoimage/geoimage.d.ts +49 -0
- package/dist/esm/types/index.d.ts +11 -2
- package/dist/esm/types/utilities/tileurls.d.ts +4 -0
- package/package.json +42 -36
- package/rollup.config.mjs +70 -0
- package/src/cogbitmaplayer/CogBitmapLayer.ts +104 -0
- package/src/cogbitmaplayer/README.md +52 -0
- package/src/cogterrainlayer/CogTerrainLayer.ts +174 -0
- package/src/cogterrainlayer/README.md +41 -0
- package/src/cogtiles/README.md +72 -0
- package/src/cogtiles/cogtiles.ts +355 -0
- package/src/cogtiles/lzw.js +256 -0
- package/src/geoimage/README.md +110 -0
- package/src/geoimage/geoimage.ts +390 -0
- package/src/index.ts +11 -0
- package/src/utilities/tileurls.ts +21 -0
- package/tsconfig.json +3 -0
- package/README.md +0 -1
- package/dist/cjs/classes/geoImage/GeoImage.d.ts +0 -38
- package/dist/cjs/classes/geoImage/index.d.ts +0 -1
- package/dist/cjs/classes/geoImage/interface.d.ts +0 -32
- package/dist/cjs/classes/index.d.ts +0 -1
- package/dist/cjs/hooks/index.d.ts +0 -1
- package/dist/cjs/hooks/useGeoData/index.d.ts +0 -1
- package/dist/cjs/hooks/useGeoData/useGeoData.d.ts +0 -9
- package/dist/cjs/index.d.ts +0 -2
- package/dist/cjs/types/classes/geoImage/GeoImage.d.ts +0 -38
- package/dist/cjs/types/classes/geoImage/index.d.ts +0 -1
- package/dist/cjs/types/classes/geoImage/interface.d.ts +0 -32
- package/dist/cjs/types/classes/index.d.ts +0 -1
- package/dist/cjs/types/hooks/index.d.ts +0 -1
- package/dist/cjs/types/hooks/useGeoData/index.d.ts +0 -1
- package/dist/cjs/types/hooks/useGeoData/useGeoData.d.ts +0 -9
- package/dist/esm/classes/geoImage/GeoImage.d.ts +0 -38
- package/dist/esm/classes/geoImage/index.d.ts +0 -1
- package/dist/esm/classes/geoImage/interface.d.ts +0 -32
- package/dist/esm/classes/index.d.ts +0 -1
- package/dist/esm/hooks/index.d.ts +0 -1
- package/dist/esm/hooks/useGeoData/index.d.ts +0 -1
- package/dist/esm/hooks/useGeoData/useGeoData.d.ts +0 -9
- package/dist/esm/index.d.ts +0 -2
- package/dist/esm/types/classes/geoImage/GeoImage.d.ts +0 -38
- package/dist/esm/types/classes/geoImage/index.d.ts +0 -1
- package/dist/esm/types/classes/geoImage/interface.d.ts +0 -32
- package/dist/esm/types/classes/index.d.ts +0 -1
- package/dist/esm/types/hooks/index.d.ts +0 -1
- package/dist/esm/types/hooks/useGeoData/index.d.ts +0 -1
- package/dist/esm/types/hooks/useGeoData/useGeoData.d.ts +0 -9
- package/dist/index.d.ts +0 -80
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
# GEOIMAGE
|
|
2
|
+
#### A Javascript library for generating bitmaps out of **geoTIFF** files.
|
|
3
|
+
<img src = "/images/example0crop1.png" width = "100%">
|
|
4
|
+
|
|
5
|
+
## Features
|
|
6
|
+
#### Color texture generation
|
|
7
|
+
- Create RGB pictures out of RGB geoTIFF data.
|
|
8
|
+
- Generate pictures out of non-RGB geoTIFF data with different processing options.
|
|
9
|
+
|
|
10
|
+
#### Terrain texture generation
|
|
11
|
+
- Generate heightmaps out of single-channel geoTIFF elevation data.
|
|
12
|
+
- The **elevation data** is encoded into the bitmap as [Mapbox Terrain-RGB](https://docs.mapbox.com/data/tilesets/guides/access-elevation-data/#decode-data).
|
|
13
|
+
|
|
14
|
+
#### Data visualisation options
|
|
15
|
+
- Color
|
|
16
|
+
- Transparency
|
|
17
|
+
- Heatmap (custom color scale example [here](../cogbitmaplayer/README.md#custom-heatmap-color-scale))
|
|
18
|
+
- Data slice
|
|
19
|
+
- Automatic data range
|
|
20
|
+
- Manual data range
|
|
21
|
+
- Assign color to specific data value (example [here](../cogbitmaplayer/README.md#assigning-color-to-specific-data-value))
|
|
22
|
+
## Data processing options
|
|
23
|
+
- `useAutoRange : boolean` - set automatic range of color gradient **(default false)**
|
|
24
|
+
- `useDataForOpacity : boolean` - visualise data with opacity of each pixel according to its value **(default false)**
|
|
25
|
+
- `alpha : number` - visualise data in specific opacity **(if useDataOpacity is false)** **(default 150)**
|
|
26
|
+
- `useHeatMap : boolean` - generate data as a color heatmap **(default true)**
|
|
27
|
+
`useChannel : number | null` - specify a single channel to use **(default null)**
|
|
28
|
+
- `multiplier : number ` - multiplies each value **(default 1.00)**
|
|
29
|
+
- `clipLow : number | null`- generate only data greater than this **(default null)**
|
|
30
|
+
|
|
31
|
+
- `clipHigh : number | null`- generate only data less than this **(default null)**
|
|
32
|
+
- `clippedColor: chroma.Color` - set color for clipped values when using `clipLow` or `clipHigh`, **(default [0, 0, 0, 0])**
|
|
33
|
+
- `colorScale:chroma.Color[]` - array of colors, supports chroma.js color definition such as `'red'`, `[255,0,0]`, `'#FF0000'`, etc. and [Color Brewer pallete names](https://www.datanovia.com/en/wp-content/uploads/dn-tutorials/ggplot2/figures/0101-rcolorbrewer-palette-rcolorbrewer-palettes-colorblind-friendly-1.png) in this format: `chroma.brewer.Greens`
|
|
34
|
+
- `colorScaleValueRange: number[]` - set min and max range values or set any array of values to set exact colors to values, **if useAutoRange is false**, **(default [0,255])**
|
|
35
|
+
- `useColorsBasedOnValues: boolean` - assign pixels colors based on defined data values **(default false)**
|
|
36
|
+
- `colorsBasedOnValues : [number, chroma.Color][]` - array of value-color pairs, used **if useColorsBasedOnValues is true**, supports chroma.js color definition such as `'red'`, `[255,0,0]`, `'#FF0000'`, etc.
|
|
37
|
+
- `unidentifiedColor: chroma.Color` - set color for not identified values **if useColorsBasedOnValues is true**, **(default [0, 0, 0, 0])**
|
|
38
|
+
- `nullColor: chroma.Color` - set color for noData values **(default [0, 0, 0, 0])**
|
|
39
|
+
- `useSingleColor: boolean` - display data values only with single color **(default false)**
|
|
40
|
+
- `color: chroma.Color` - set color when **if useSingleColor is true**, **(default [255, 0, 255, 255])**
|
|
41
|
+
|
|
42
|
+
## Return options
|
|
43
|
+
**Method returns Image DataUrl**
|
|
44
|
+
|
|
45
|
+
- `getMap(returnFormat : "image" | "terrain", input : string | { width : number, height : number, rasters : any[] }, options?: { opacity : number })`
|
|
46
|
+
|
|
47
|
+
If `returnFormat` = `"image"` - If the input has 3 or 4 color channels, return standard RGB or RGBA image. If the input has 1 channel, data gets processed according to data processing options.
|
|
48
|
+
|
|
49
|
+
If `returnFormat` = `"terrain"` - Ignores all options except `multiplier` and returns [Mapbox Terrain-RGB](https://docs.mapbox.com/data/tilesets/guides/access-elevation-data/#decode-data)
|
|
50
|
+
|
|
51
|
+
## Basic example
|
|
52
|
+
#### Initialize the library
|
|
53
|
+
```typescript
|
|
54
|
+
import GeoImage from 'geoimage';
|
|
55
|
+
|
|
56
|
+
const g = new GeoImage();
|
|
57
|
+
```
|
|
58
|
+
#### Get bitmap
|
|
59
|
+
```typescript
|
|
60
|
+
const bitmap = await g.getMap("image", 'image.tif');
|
|
61
|
+
```
|
|
62
|
+
```typescript
|
|
63
|
+
const bitmap = await g.getMap("image", { width : 512, height : 512, rasters : [[...data]] });
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
#### Get heightmap
|
|
69
|
+
|
|
70
|
+
```typescript
|
|
71
|
+
const heightmap = await g.getMap("terrain", 'image.tif');
|
|
72
|
+
```
|
|
73
|
+
```typescript
|
|
74
|
+
const bitmap = await g.getMap("terrain", { width : 512, height : 512, rasters : [[...data]] });
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
## Advanced example
|
|
78
|
+
|
|
79
|
+
```typescript
|
|
80
|
+
//Import the library and initiate GeoImage object:
|
|
81
|
+
import GeoImage from 'geoimage';
|
|
82
|
+
|
|
83
|
+
const g = new GeoImage();
|
|
84
|
+
|
|
85
|
+
//Single-channel geotiff as a transparent heatmap with auto-rage:
|
|
86
|
+
g.useAutoRange(true);
|
|
87
|
+
g.useHeatMap(true);
|
|
88
|
+
g.alpha(120);
|
|
89
|
+
const firstImage = await g.getMap("image", 'image.tif');
|
|
90
|
+
|
|
91
|
+
//Single-channel geotiff as a transparent heatmap with manual range in meters:
|
|
92
|
+
g.useAutoRange(false);
|
|
93
|
+
g.useDataRange(0,250); //Blue at 0m, red at 250m
|
|
94
|
+
g.useHeatMap(true);
|
|
95
|
+
g.alpha(120);
|
|
96
|
+
const secondImage = await g.getBitmap("image", 'image.tif');
|
|
97
|
+
|
|
98
|
+
//Single-channel geotiff with data as transparency:
|
|
99
|
+
g.useAutoRange(true);
|
|
100
|
+
g.useHeatMap(false);
|
|
101
|
+
g.useDataForOpacity(true);
|
|
102
|
+
const thirdImage = await g.getBitmap("image", 'image.tif');
|
|
103
|
+
|
|
104
|
+
//Single-channel geotiff with data slice from 350m to 360m in custom color:
|
|
105
|
+
g.clipLow(350); //generate only data between 350m and 360m
|
|
106
|
+
g.clipHigh(360);
|
|
107
|
+
g.useHeatMap(false);
|
|
108
|
+
g.color[0,255,100];
|
|
109
|
+
const fourthImage = await g.getBitmap("image", 'image.tif');
|
|
110
|
+
```
|
|
@@ -0,0 +1,390 @@
|
|
|
1
|
+
/* eslint 'max-len': [1, { code: 105, comments: 999, ignoreStrings: true, ignoreUrls: true }] */
|
|
2
|
+
|
|
3
|
+
// import { ExtentsLeftBottomRightTop } from '@deck.gl/core/utils/positions';
|
|
4
|
+
import { fromArrayBuffer, GeoTIFFImage, TypedArray } from 'geotiff';
|
|
5
|
+
import chroma from 'chroma-js';
|
|
6
|
+
|
|
7
|
+
export type GeoImageOptions = {
|
|
8
|
+
type: 'image' | 'terrain',
|
|
9
|
+
format?: 'uint8' | 'uint16' | 'uint32' |'int8' | 'int16' | 'int32' | 'float32' | 'float64'
|
|
10
|
+
useHeatMap?: boolean,
|
|
11
|
+
useColorsBasedOnValues? : boolean,
|
|
12
|
+
useAutoRange?: boolean,
|
|
13
|
+
useDataForOpacity?: boolean,
|
|
14
|
+
useChannel?: number | null,
|
|
15
|
+
useSingleColor?: boolean,
|
|
16
|
+
clipLow?: number | null,
|
|
17
|
+
clipHigh?: number | null,
|
|
18
|
+
multiplier?: number,
|
|
19
|
+
color?: Array<number> | chroma.Color,
|
|
20
|
+
colorScale?: Array<string> | Array<chroma.Color>,
|
|
21
|
+
colorScaleValueRange?: number[],
|
|
22
|
+
colorsBasedOnValues? : [number|undefined, chroma.Color][],
|
|
23
|
+
alpha?: number,
|
|
24
|
+
noDataValue?: number
|
|
25
|
+
numOfChannels?: number,
|
|
26
|
+
nullColor?: Array<number> | chroma.Color
|
|
27
|
+
unidentifiedColor?: Array<number> | chroma.Color,
|
|
28
|
+
clippedColor?: Array<number> | chroma.Color
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
const DefaultGeoImageOptions: GeoImageOptions = {
|
|
32
|
+
type: 'image',
|
|
33
|
+
format: 'uint8',
|
|
34
|
+
useHeatMap: true,
|
|
35
|
+
useColorsBasedOnValues: false,
|
|
36
|
+
useAutoRange: false,
|
|
37
|
+
useDataForOpacity: false,
|
|
38
|
+
useSingleColor: false,
|
|
39
|
+
clipLow: null,
|
|
40
|
+
clipHigh: null,
|
|
41
|
+
multiplier: 1.0,
|
|
42
|
+
color: [255, 0, 255, 255],
|
|
43
|
+
colorScale: chroma.brewer.YlOrRd,
|
|
44
|
+
colorScaleValueRange: [0, 255],
|
|
45
|
+
colorsBasedOnValues: null,
|
|
46
|
+
alpha: 255,
|
|
47
|
+
useChannel: null,
|
|
48
|
+
noDataValue: undefined,
|
|
49
|
+
numOfChannels: undefined,
|
|
50
|
+
nullColor: [0, 0, 0, 0],
|
|
51
|
+
unidentifiedColor: [0, 0, 0, 0],
|
|
52
|
+
clippedColor: [0, 0, 0, 0],
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
export default class GeoImage {
|
|
56
|
+
data: GeoTIFFImage | undefined;
|
|
57
|
+
|
|
58
|
+
scale = (
|
|
59
|
+
num: number,
|
|
60
|
+
inMin: number,
|
|
61
|
+
inMax: number,
|
|
62
|
+
outMin: number,
|
|
63
|
+
outMax: number,
|
|
64
|
+
) => ((num - inMin) * (outMax - outMin)) / (inMax - inMin) + outMin;
|
|
65
|
+
|
|
66
|
+
async setUrl(url: string) {
|
|
67
|
+
const response = await fetch(url);
|
|
68
|
+
const arrayBuffer = await response.arrayBuffer();
|
|
69
|
+
const tiff = await fromArrayBuffer(arrayBuffer);
|
|
70
|
+
|
|
71
|
+
const data = await tiff.getImage(0);
|
|
72
|
+
|
|
73
|
+
this.data = data;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
async getMap(
|
|
77
|
+
input: string | {
|
|
78
|
+
width: number,
|
|
79
|
+
height: number,
|
|
80
|
+
rasters: any[]
|
|
81
|
+
},
|
|
82
|
+
options: GeoImageOptions,
|
|
83
|
+
) {
|
|
84
|
+
const mergedOptions = { ...DefaultGeoImageOptions, ...options };
|
|
85
|
+
|
|
86
|
+
switch (mergedOptions.type) {
|
|
87
|
+
case 'image':
|
|
88
|
+
return this.getBitmap(input, mergedOptions);
|
|
89
|
+
case 'terrain':
|
|
90
|
+
return this.getHeightmap(input, mergedOptions);
|
|
91
|
+
default:
|
|
92
|
+
return null;
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
// GetHeightmap uses only "useChannel" and "multiplier" options
|
|
97
|
+
async getHeightmap(
|
|
98
|
+
input: string | {
|
|
99
|
+
width: number,
|
|
100
|
+
height: number,
|
|
101
|
+
rasters: any[] },
|
|
102
|
+
options: GeoImageOptions,
|
|
103
|
+
) {
|
|
104
|
+
let rasters = [];
|
|
105
|
+
let width: number;
|
|
106
|
+
let height: number;
|
|
107
|
+
|
|
108
|
+
if (typeof (input) === 'string') {
|
|
109
|
+
await this.setUrl(input);
|
|
110
|
+
|
|
111
|
+
rasters = (await this.data!.readRasters()) as TypedArray[];
|
|
112
|
+
width = this.data!.getWidth();
|
|
113
|
+
height = this.data!.getHeight();
|
|
114
|
+
} else {
|
|
115
|
+
rasters = input.rasters;
|
|
116
|
+
width = input.width;
|
|
117
|
+
height = input.height;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
let channel = rasters[0];
|
|
121
|
+
|
|
122
|
+
if (options.useChannel != null) {
|
|
123
|
+
if (rasters[options.useChannel]) {
|
|
124
|
+
channel = rasters[options.useChannel];
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
const canvas = document.createElement('canvas');
|
|
129
|
+
canvas.width = width;
|
|
130
|
+
canvas.height = height;
|
|
131
|
+
const c = canvas.getContext('2d');
|
|
132
|
+
const imageData = c!.createImageData(width, height);
|
|
133
|
+
|
|
134
|
+
const channelCount = channel.length / (width * height);
|
|
135
|
+
const s = width * height * 4;
|
|
136
|
+
|
|
137
|
+
let pixel = 0;
|
|
138
|
+
if (options.useChannel != null) {
|
|
139
|
+
pixel = options.useChannel;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
// console.time("heightmap generated in");
|
|
143
|
+
for (let i = 0; i < s; i += 4) {
|
|
144
|
+
channel[pixel] *= options.multiplier!;
|
|
145
|
+
const multiplied = 100000 + channel[pixel] * 10;
|
|
146
|
+
|
|
147
|
+
imageData.data[i] = Math.trunc(multiplied * 0.00001525878);
|
|
148
|
+
imageData.data[i + 1] = Math.trunc(multiplied * 0.00390625) - imageData.data[i] * 256;
|
|
149
|
+
imageData.data[i + 2] = Math.trunc(multiplied) - imageData.data[i] * 65536
|
|
150
|
+
- imageData.data[i + 1] * 256;
|
|
151
|
+
imageData.data[i + 3] = 255;
|
|
152
|
+
|
|
153
|
+
pixel += channelCount;
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
// console.timeEnd("heightmap generated in");
|
|
157
|
+
|
|
158
|
+
c!.putImageData(imageData, 0, 0);
|
|
159
|
+
const imageUrl = canvas.toDataURL('image/png');
|
|
160
|
+
// console.log('Heightmap generated.');
|
|
161
|
+
return imageUrl;
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
async getBitmap(
|
|
165
|
+
input: string | {
|
|
166
|
+
width: number,
|
|
167
|
+
height: number,
|
|
168
|
+
rasters: any[] },
|
|
169
|
+
options: GeoImageOptions,
|
|
170
|
+
) {
|
|
171
|
+
// console.time('bitmap-generated-in');
|
|
172
|
+
// const optionsLocal = { ...options };
|
|
173
|
+
const optionsLocal = { ...options };
|
|
174
|
+
|
|
175
|
+
let rasters = [];
|
|
176
|
+
let channels: number;
|
|
177
|
+
let width: number;
|
|
178
|
+
let height: number;
|
|
179
|
+
|
|
180
|
+
if (typeof (input) === 'string') {
|
|
181
|
+
await this.setUrl(input);
|
|
182
|
+
rasters = (await this.data!.readRasters()) as TypedArray[];
|
|
183
|
+
channels = rasters.length;
|
|
184
|
+
width = this.data!.getWidth();
|
|
185
|
+
height = this.data!.getHeight();
|
|
186
|
+
} else {
|
|
187
|
+
rasters = input.rasters;
|
|
188
|
+
channels = rasters.length;
|
|
189
|
+
width = input.width;
|
|
190
|
+
height = input.height;
|
|
191
|
+
}
|
|
192
|
+
// TO DO if alpha is set in options, then apply to entire image
|
|
193
|
+
|
|
194
|
+
const canvas = document.createElement('canvas');
|
|
195
|
+
canvas.width = width;
|
|
196
|
+
canvas.height = height;
|
|
197
|
+
const c = canvas.getContext('2d');
|
|
198
|
+
const imageData: ImageData = c!.createImageData(width, height);
|
|
199
|
+
|
|
200
|
+
let r; let g; let b; let
|
|
201
|
+
a;
|
|
202
|
+
const size = width * height * 4;
|
|
203
|
+
|
|
204
|
+
if (!options.noDataValue) {
|
|
205
|
+
console.log('Missing noData value. Raster might be displayed incorrectly.');
|
|
206
|
+
}
|
|
207
|
+
optionsLocal.unidentifiedColor = this.getColorFromChromaType(optionsLocal.unidentifiedColor);
|
|
208
|
+
optionsLocal.nullColor = this.getColorFromChromaType(optionsLocal.nullColor);
|
|
209
|
+
optionsLocal.clippedColor = this.getColorFromChromaType(optionsLocal.clippedColor);
|
|
210
|
+
optionsLocal.color = this.getColorFromChromaType(optionsLocal.color);
|
|
211
|
+
|
|
212
|
+
// console.log(rasters[0])
|
|
213
|
+
/* console.log("raster 0 length: " + rasters[0].length)
|
|
214
|
+
console.log("image width: " + width)
|
|
215
|
+
console.log("channels: " + channels)
|
|
216
|
+
console.log("format: " + rasters[0].length / (width * height))
|
|
217
|
+
*/
|
|
218
|
+
|
|
219
|
+
if (optionsLocal.useChannel == null) {
|
|
220
|
+
if (channels === 1) {
|
|
221
|
+
if (rasters[0].length / (width * height) === 1) {
|
|
222
|
+
const channel = rasters[0];
|
|
223
|
+
// AUTO RANGE
|
|
224
|
+
if (optionsLocal.useAutoRange) {
|
|
225
|
+
optionsLocal.colorScaleValueRange = this.getMinMax(channel, optionsLocal);
|
|
226
|
+
// console.log('data min: ' + optionsLocal.rangeMin + ', max: ' + optionsLocal.rangeMax);
|
|
227
|
+
}
|
|
228
|
+
// SINGLE CHANNEL
|
|
229
|
+
const colorData = this.getColorValue(channel, optionsLocal, size);
|
|
230
|
+
colorData.forEach((value, index) => {
|
|
231
|
+
imageData.data[index] = value;
|
|
232
|
+
});
|
|
233
|
+
}
|
|
234
|
+
if (rasters[0].length / (width * height) === 3) {
|
|
235
|
+
// console.log("geoImage: " + "RGB 1 array of length: " + rasters[0].length);
|
|
236
|
+
let pixel = 0;
|
|
237
|
+
for (let i = 0; i < size; i += 4) {
|
|
238
|
+
imageData.data[i] = rasters[0][pixel += 1];
|
|
239
|
+
imageData.data[i + 1] = rasters[0][pixel += 1];
|
|
240
|
+
imageData.data[i + 2] = rasters[0][pixel += 1];
|
|
241
|
+
imageData.data[i + 3] = optionsLocal.alpha!;
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
if (rasters[0].length / (width * height) === 4) {
|
|
245
|
+
// console.log("geoImage: " + "RGBA 1 array");
|
|
246
|
+
rasters[0].forEach((value, index) => {
|
|
247
|
+
imageData.data[index] = value;
|
|
248
|
+
});
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
if (channels === 3) {
|
|
252
|
+
// RGB
|
|
253
|
+
let pixel = 0;
|
|
254
|
+
for (let i = 0; i < size; i += 4) {
|
|
255
|
+
r = rasters[0][pixel];
|
|
256
|
+
g = rasters[1][pixel];
|
|
257
|
+
b = rasters[2][pixel];
|
|
258
|
+
a = optionsLocal.alpha!;
|
|
259
|
+
|
|
260
|
+
imageData.data[i] = r;
|
|
261
|
+
imageData.data[i + 1] = g;
|
|
262
|
+
imageData.data[i + 2] = b;
|
|
263
|
+
imageData.data[i + 3] = a;
|
|
264
|
+
|
|
265
|
+
pixel += 1;
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
if (channels === 4) {
|
|
269
|
+
// RGBA
|
|
270
|
+
let pixel = 0;
|
|
271
|
+
for (let i = 0; i < size; i += 4) {
|
|
272
|
+
r = rasters[0][pixel];
|
|
273
|
+
g = rasters[1][pixel];
|
|
274
|
+
b = rasters[2][pixel];
|
|
275
|
+
a = optionsLocal.alpha!;
|
|
276
|
+
|
|
277
|
+
imageData.data[i] = r;
|
|
278
|
+
imageData.data[i + 1] = g;
|
|
279
|
+
imageData.data[i + 2] = b;
|
|
280
|
+
imageData.data[i + 3] = a;
|
|
281
|
+
|
|
282
|
+
pixel += 1;
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
} else if (optionsLocal.useChannel <= optionsLocal.numOfChannels) {
|
|
286
|
+
let channel = rasters[0];
|
|
287
|
+
if (rasters[optionsLocal.useChannel]) {
|
|
288
|
+
channel = rasters[optionsLocal.useChannel];
|
|
289
|
+
}
|
|
290
|
+
// AUTO RANGE
|
|
291
|
+
if (optionsLocal.useAutoRange) {
|
|
292
|
+
optionsLocal.colorScaleValueRange = this.getMinMax(channel, optionsLocal);
|
|
293
|
+
// console.log('data min: ' + optionsLocal.rangeMin + ', max: ' + optionsLocal.rangeMax);
|
|
294
|
+
}
|
|
295
|
+
const numOfChannels = channel.length / (width * height);
|
|
296
|
+
const colorData = this.getColorValue(channel, optionsLocal, size, numOfChannels);
|
|
297
|
+
colorData.forEach((value, index) => {
|
|
298
|
+
imageData.data[index] = value;
|
|
299
|
+
});
|
|
300
|
+
} else {
|
|
301
|
+
// if user defined channel does not exist --> return greyscale image
|
|
302
|
+
console.log('Defined channel does not exist, displaying only grey values');
|
|
303
|
+
const defaultColorData = this.getDefaultColor(size, optionsLocal.nullColor);
|
|
304
|
+
defaultColorData.forEach((value, index) => {
|
|
305
|
+
imageData.data[index] = value;
|
|
306
|
+
});
|
|
307
|
+
}
|
|
308
|
+
// console.timeEnd('bitmap-generated-in');
|
|
309
|
+
|
|
310
|
+
c!.putImageData(imageData, 0, 0);
|
|
311
|
+
const imageUrl = canvas.toDataURL('image/png');
|
|
312
|
+
// console.log('Bitmap generated.');
|
|
313
|
+
return imageUrl;
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
getMinMax(array, options) {
|
|
317
|
+
let maxValue = options.maxValue ? options.maxValue : Number.MIN_VALUE;
|
|
318
|
+
let minValue = options.minValue ? options.minValue : Number.MAX_VALUE;
|
|
319
|
+
for (let idx = 0; idx < array.length; idx += 1) {
|
|
320
|
+
if (!options.noDataValue || array[idx] !== options.noDataValue) {
|
|
321
|
+
if (array[idx] > maxValue) maxValue = array[idx];
|
|
322
|
+
if (array[idx] < minValue) minValue = array[idx];
|
|
323
|
+
}
|
|
324
|
+
}
|
|
325
|
+
return [minValue, maxValue];
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
getColorValue(dataArray:[], options:GeoImageOptions, arrayLength:number, numOfChannels = 1) {
|
|
329
|
+
const colorScale = chroma.scale(options.colorScale).domain(options.colorScaleValueRange);
|
|
330
|
+
let pixel:number = options.useChannel === null ? 0 : options.useChannel;
|
|
331
|
+
const colorsArray = new Array(arrayLength);
|
|
332
|
+
|
|
333
|
+
// for useColorsBasedOnValues
|
|
334
|
+
const dataValues = options.colorsBasedOnValues ? options.colorsBasedOnValues.map(([first]) => first) : undefined;
|
|
335
|
+
const colorValues = options.colorsBasedOnValues ? options.colorsBasedOnValues.map(([, second]) => [...chroma(second).rgb(), 255]) : undefined;
|
|
336
|
+
|
|
337
|
+
for (let i = 0; i < arrayLength; i += 4) {
|
|
338
|
+
let pixelColor = options.nullColor;
|
|
339
|
+
if (!options.noDataValue || dataArray[pixel] !== options.noDataValue) {
|
|
340
|
+
if (
|
|
341
|
+
(options.clipLow != null && dataArray[pixel] <= options.clipLow)
|
|
342
|
+
|| (options.clipHigh != null && dataArray[pixel] >= options.clipHigh)
|
|
343
|
+
) {
|
|
344
|
+
pixelColor = options.clippedColor;
|
|
345
|
+
} else {
|
|
346
|
+
if (options.useHeatMap) {
|
|
347
|
+
// FIXME
|
|
348
|
+
// eslint-disable-next-line
|
|
349
|
+
pixelColor = [...colorScale(dataArray[pixel]).rgb(), 255];
|
|
350
|
+
}
|
|
351
|
+
if (options.useColorsBasedOnValues) {
|
|
352
|
+
const index = dataValues.indexOf(dataArray[pixel]);
|
|
353
|
+
if (index > -1) {
|
|
354
|
+
pixelColor = colorValues[index];
|
|
355
|
+
} else pixelColor = options.unidentifiedColor;
|
|
356
|
+
}
|
|
357
|
+
if (options.useSingleColor) {
|
|
358
|
+
// FIXME - Is this compatible with chroma.color?
|
|
359
|
+
pixelColor = options.color;
|
|
360
|
+
}
|
|
361
|
+
if (options.useDataForOpacity) {
|
|
362
|
+
// eslint-disable-next-line max-len
|
|
363
|
+
pixelColor[3] = this.scale(dataArray[pixel], options.colorScaleValueRange[0]!, options.colorScaleValueRange.slice(-1)[0]!, 0, 255);
|
|
364
|
+
}
|
|
365
|
+
}
|
|
366
|
+
}
|
|
367
|
+
// FIXME
|
|
368
|
+
// eslint-disable-next-line
|
|
369
|
+
[colorsArray[i], colorsArray[i + 1], colorsArray[i + 2], colorsArray[i + 3]] = pixelColor;
|
|
370
|
+
|
|
371
|
+
pixel += numOfChannels;
|
|
372
|
+
}
|
|
373
|
+
return colorsArray;
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
getDefaultColor(size, nullColor) {
|
|
377
|
+
const colorsArray = new Array(size);
|
|
378
|
+
for (let i = 0; i < size; i += 4) {
|
|
379
|
+
[colorsArray[i], colorsArray[i + 1], colorsArray[i + 2], colorsArray[i + 3]] = nullColor;
|
|
380
|
+
}
|
|
381
|
+
return colorsArray;
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
getColorFromChromaType(colorDefinition) {
|
|
385
|
+
if (!Array.isArray(colorDefinition) || colorDefinition.length !== 4) {
|
|
386
|
+
return [...chroma(colorDefinition).rgb(), 255];
|
|
387
|
+
}
|
|
388
|
+
return colorDefinition;
|
|
389
|
+
}
|
|
390
|
+
}
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import CogBitmapLayer from './cogbitmaplayer/CogBitmapLayer';
|
|
2
|
+
import CogTerrainLayer from './cogterrainlayer/CogTerrainLayer';
|
|
3
|
+
import cogtiles from './cogtiles/cogtiles';
|
|
4
|
+
import GeoImage from './geoimage/geoimage';
|
|
5
|
+
|
|
6
|
+
export default {
|
|
7
|
+
CogBitmapLayer,
|
|
8
|
+
CogTerrainLayer,
|
|
9
|
+
cogtiles,
|
|
10
|
+
GeoImage,
|
|
11
|
+
};
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
function isTileServiceUrl(url:string) {
|
|
2
|
+
if (url.includes('{x}') && url.includes('{y}') && url.includes('{z}')) {
|
|
3
|
+
return true;
|
|
4
|
+
}
|
|
5
|
+
return false;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
function isCogUrl(url:string) {
|
|
9
|
+
if (url.includes('.tif') || url.includes('.tiff') || url.includes('.TIF') || url.includes('.TIFF')) {
|
|
10
|
+
return true;
|
|
11
|
+
}
|
|
12
|
+
return false;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
function getTileUrl(service:string, x:number, y:number, z:number) {
|
|
16
|
+
const url = service.replace('{x}', String(x)).replace('{y}', String(y)).replace('{z}', String(z));
|
|
17
|
+
|
|
18
|
+
return url;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export { isTileServiceUrl, isCogUrl, getTileUrl };
|
package/tsconfig.json
ADDED
package/README.md
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
# geoimage
|
|
@@ -1,38 +0,0 @@
|
|
|
1
|
-
import { GeoTIFFImage } from 'geotiff';
|
|
2
|
-
import { IGeoImage } from './interface';
|
|
3
|
-
declare class GeoImage implements IGeoImage {
|
|
4
|
-
url: string;
|
|
5
|
-
origin: number[];
|
|
6
|
-
boundingBox: number[];
|
|
7
|
-
data: GeoTIFFImage | undefined;
|
|
8
|
-
useHeatMap: boolean;
|
|
9
|
-
useAutoRange: boolean;
|
|
10
|
-
useClip: boolean;
|
|
11
|
-
useDataForOpacity: boolean;
|
|
12
|
-
rangeMin: number;
|
|
13
|
-
rangeMax: number;
|
|
14
|
-
clipLow: number;
|
|
15
|
-
clipHigh: number;
|
|
16
|
-
multiplier: number;
|
|
17
|
-
color: number[];
|
|
18
|
-
alpha: number;
|
|
19
|
-
imageWidth: number;
|
|
20
|
-
imageHeight: number;
|
|
21
|
-
options: {};
|
|
22
|
-
useChannel: number;
|
|
23
|
-
scale: (num: number, inMin: number, inMax: number, outMin: number, outMax: number) => number;
|
|
24
|
-
getOrigin: () => number[];
|
|
25
|
-
getBoundingBox: () => number[];
|
|
26
|
-
setUrl(url: string): Promise<void>;
|
|
27
|
-
getHeightMap(input: any): Promise<string>;
|
|
28
|
-
getBitmap(input: any): Promise<string>;
|
|
29
|
-
setDataOpacity(toggle?: boolean): void;
|
|
30
|
-
setHeatMap(heat?: boolean): void;
|
|
31
|
-
setAutoRange(auto?: boolean): void;
|
|
32
|
-
setDataClip(low?: number, high?: number): void;
|
|
33
|
-
setDataRange(min?: number, max?: number): void;
|
|
34
|
-
setMultiplier(n?: number): void;
|
|
35
|
-
setColor(r?: number, g?: number, b?: number): void;
|
|
36
|
-
setOpacity(alpha?: number): void;
|
|
37
|
-
}
|
|
38
|
-
export default GeoImage;
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export { default } from './GeoImage';
|
|
@@ -1,32 +0,0 @@
|
|
|
1
|
-
import { GeoTIFFImage } from 'geotiff';
|
|
2
|
-
export interface IGeoImage {
|
|
3
|
-
url: string;
|
|
4
|
-
origin: number[];
|
|
5
|
-
boundingBox: number[];
|
|
6
|
-
data: GeoTIFFImage | undefined;
|
|
7
|
-
useHeatMap: boolean;
|
|
8
|
-
useAutoRange: boolean;
|
|
9
|
-
useClip: boolean;
|
|
10
|
-
useDataForOpacity: boolean;
|
|
11
|
-
rangeMin: number;
|
|
12
|
-
rangeMax: number;
|
|
13
|
-
clipLow: number;
|
|
14
|
-
clipHigh: number;
|
|
15
|
-
color: number[];
|
|
16
|
-
alpha: number;
|
|
17
|
-
imageWidth: number;
|
|
18
|
-
imageHeight: number;
|
|
19
|
-
setUrl(url: string): Promise<void>;
|
|
20
|
-
scale: (num: number, inMin: number, inMax: number, outMin: number, outMax: number) => number;
|
|
21
|
-
getOrigin: () => number[];
|
|
22
|
-
getBoundingBox: () => number[];
|
|
23
|
-
getBitmap(input: any): Promise<string>;
|
|
24
|
-
getHeightMap(input: any): Promise<string>;
|
|
25
|
-
setDataOpacity(toggle?: boolean): void;
|
|
26
|
-
setHeatMap(heat?: boolean): void;
|
|
27
|
-
setAutoRange(auto?: boolean): void;
|
|
28
|
-
setDataClip(low?: number, high?: number): void;
|
|
29
|
-
setDataRange(min?: number, max?: number): void;
|
|
30
|
-
setColor(r?: number, g?: number, b?: number): void;
|
|
31
|
-
setOpacity(alpha?: number): void;
|
|
32
|
-
}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export { default as GeoImage } from './geoImage';
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export { default as useGeoData } from './useGeoData';
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export { default } from './useGeoData';
|
package/dist/cjs/index.d.ts
DELETED
|
@@ -1,38 +0,0 @@
|
|
|
1
|
-
import { GeoTIFFImage } from 'geotiff';
|
|
2
|
-
import { IGeoImage } from './interface';
|
|
3
|
-
declare class GeoImage implements IGeoImage {
|
|
4
|
-
url: string;
|
|
5
|
-
origin: number[];
|
|
6
|
-
boundingBox: number[];
|
|
7
|
-
data: GeoTIFFImage | undefined;
|
|
8
|
-
useHeatMap: boolean;
|
|
9
|
-
useAutoRange: boolean;
|
|
10
|
-
useClip: boolean;
|
|
11
|
-
useDataForOpacity: boolean;
|
|
12
|
-
rangeMin: number;
|
|
13
|
-
rangeMax: number;
|
|
14
|
-
clipLow: number;
|
|
15
|
-
clipHigh: number;
|
|
16
|
-
multiplier: number;
|
|
17
|
-
color: number[];
|
|
18
|
-
alpha: number;
|
|
19
|
-
imageWidth: number;
|
|
20
|
-
imageHeight: number;
|
|
21
|
-
options: {};
|
|
22
|
-
useChannel: number;
|
|
23
|
-
scale: (num: number, inMin: number, inMax: number, outMin: number, outMax: number) => number;
|
|
24
|
-
getOrigin: () => number[];
|
|
25
|
-
getBoundingBox: () => number[];
|
|
26
|
-
setUrl(url: string): Promise<void>;
|
|
27
|
-
getHeightMap(input: any): Promise<string>;
|
|
28
|
-
getBitmap(input: any): Promise<string>;
|
|
29
|
-
setDataOpacity(toggle?: boolean): void;
|
|
30
|
-
setHeatMap(heat?: boolean): void;
|
|
31
|
-
setAutoRange(auto?: boolean): void;
|
|
32
|
-
setDataClip(low?: number, high?: number): void;
|
|
33
|
-
setDataRange(min?: number, max?: number): void;
|
|
34
|
-
setMultiplier(n?: number): void;
|
|
35
|
-
setColor(r?: number, g?: number, b?: number): void;
|
|
36
|
-
setOpacity(alpha?: number): void;
|
|
37
|
-
}
|
|
38
|
-
export default GeoImage;
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export { default } from './GeoImage';
|