Skip to content

Commit c48bcf1

Browse files
committed
updated support for UTM and cogs
1 parent 9554e2a commit c48bcf1

6 files changed

+339
-154
lines changed

georaster-layer-for-leaflet.browserify.min.js

+121-76
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
1-
(function(){function r(e,n,t){function o(i,f){if(!n[i]){if(!e[i]){var c="function"==typeof require&&require;if(!f&&c)return c(i,!0);if(u)return u(i,!0);var a=new Error("Cannot find module '"+i+"'");throw a.code="MODULE_NOT_FOUND",a}var p=n[i]={exports:{}};e[i][0].call(p.exports,function(r){var n=e[i][1][r];return o(n||r)},p,p.exports,r,e,n,t)}return n[i].exports}for(var u="function"==typeof require&&require,i=0;i<t.length;i++)o(t[i]);return o}return r})()({1:[function(require,module,exports){
1+
(function(){function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o<r.length;o++)s(r[o]);return s}return e})()({1:[function(require,module,exports){
2+
/* todo - make sure proj4 calculations are being cached */
23
let chroma = require("chroma-js");
34

45
let L = window.L;
6+
let proj4 = window.proj4;
57

68
var GeoRasterLayer = L.GridLayer.extend({
79

@@ -36,18 +38,36 @@ var GeoRasterLayer = L.GridLayer.extend({
3638
this._ymin = georaster.ymin;
3739
this._xmax = georaster.xmax;
3840
this._ymax = georaster.ymax;
41+
this._projection = georaster.projection;
3942

4043
if (georaster.sourceType === 'url' && georaster.numberOfRasters === 1 && !options.pixelValueToColorFn) {
4144
// For COG, we can't determine a data min max for color scaling,
4245
// so pixelValueToColorFn is required.
4346
throw "pixelValueToColorFn is a required option for single-band rasters initialized via URL";
4447
}
4548

49+
const projstr = georaster.projection.toString();
4650
console.log("georaster.ymin:", georaster.ymin);
47-
let southWest = L.latLng(georaster.ymin, georaster.xmin);
48-
let northEast = L.latLng(georaster.ymax, georaster.xmax);
49-
this._bounds = L.latLngBounds(southWest, northEast);
50-
console.log("this._bounds:", this._bounds);
51+
if (projstr === '4326') {
52+
const southWest = L.latLng(georaster.ymin, georaster.xmin);
53+
const northEast = L.latLng(georaster.ymax, georaster.xmax);
54+
this._bounds = L.latLngBounds(southWest, northEast);
55+
} else if (projstr.startsWith("326") || projstr.startsWith("327")){
56+
/* using UTM Grid Projection */
57+
if (!proj4) {
58+
throw "proj4 must be found in the global scope in order to load a raster that uses a UTM projection";
59+
}
60+
const zone = projstr.substring(3);
61+
const hemisphere = projstr.startsWith("326") ? "N" : projstr.startsWith("327") ? "S" : '';
62+
this.projector = proj4(`+proj=utm +zone=${zone}${hemisphere === 'S' ? ' +south ' : ' '}+ellps=WGS84 +datum=WGS84 +units=m +no_defs`, 'EPSG:4326');
63+
const bottomLeft = this.projector.forward({x: georaster.xmin, y: georaster.ymin});
64+
const southWest = L.latLng(bottomLeft.y, bottomLeft.x);
65+
const topRight = this.projector.forward({x: georaster.xmax, y: georaster.ymax});
66+
const northEast = L.latLng(topRight.y, topRight.x);
67+
this._bounds = L.latLngBounds(southWest, northEast);
68+
} else {
69+
throw "georaster-layer-for-leaflet does not support rasters with the current georaster's projection";
70+
}
5171
options.bounds = this._bounds;
5272
L.setOptions(this, options);
5373

@@ -63,68 +83,84 @@ var GeoRasterLayer = L.GridLayer.extend({
6383
console.error("ERROR initializing GeoTIFFLayer", error);
6484
}
6585
},
66-
67-
getRasters: async function(
68-
map, tileNwPoint, height_of_rectangle_in_pixels, width_of_rectangle_in_pixels,
69-
coords, pixelHeight, pixelWidth, number_of_rectangles_across, number_of_rectangles_down, ymax, xmin) {
86+
87+
getRasters: function(options) {
88+
const {
89+
tileNwPoint,
90+
height_of_rectangle_in_pixels,
91+
width_of_rectangle_in_pixels,
92+
coords,
93+
number_of_rectangles_across,
94+
number_of_rectangles_down,
95+
ymax,
96+
xmin
97+
} = options;
98+
console.log("starting getRasters with options:", options);
7099
// called if georaster was constructed from URL and we need to get
71100
// data separately for each tile
72101
// aka "COG mode"
73102

74-
const raster_coords_for_tile_coords = function(h, w) {
75-
let y_center_in_map_pixels = tileNwPoint.y + (h + 0.5) * height_of_rectangle_in_pixels;
76-
let latWestPoint = L.point(tileNwPoint.x, y_center_in_map_pixels);
77-
let latWest = map.unproject(latWestPoint, coords.z);
78-
let { lat } = latWest;
79-
let y_in_tile_pixels = Math.round(h * height_of_rectangle_in_pixels);
80-
let y_in_raster_pixels = Math.floor( (ymax - lat) / pixelHeight );
103+
/*
104+
This function takes in coordinates in the rendered image tile and
105+
returns the y and x values in the original raster
106+
*/
107+
const raster_coords_for_tile_coords = (h, w) => {
81108

82-
let latLngPoint = L.point(tileNwPoint.x + (w + 0.5) * width_of_rectangle_in_pixels, y_center_in_map_pixels);
83-
let latLng = map.unproject(latLngPoint, coords.z);
84-
let { lng } = latLng;
85-
let x_in_raster_pixels = Math.floor( (lng - xmin) / pixelWidth );
109+
const x_center_in_map_pixels = tileNwPoint.x + (w + 0.5) * width_of_rectangle_in_pixels;
110+
const y_center_in_map_pixels = tileNwPoint.y + (h + 0.5) * height_of_rectangle_in_pixels;
86111

87-
return [y_in_raster_pixels, x_in_raster_pixels];
88-
}
112+
const mapPoint = L.point(x_center_in_map_pixels, y_center_in_map_pixels);
113+
console.log("mapPoint:", mapPoint);
89114

90-
let result = raster_coords_for_tile_coords(0, 0);
91-
let [ min_y, min_x ] = result;
92-
result = raster_coords_for_tile_coords(number_of_rectangles_down - 1, number_of_rectangles_across - 1);
93-
let [ max_y, max_x ] = result;
115+
const { lat, lng } = this._map.unproject(mapPoint, coords.z);
94116

95-
// careful not to flip min_y/max_y here
96-
let tile_values = await this.georaster.getValues(min_x, min_y, max_x, max_y, number_of_rectangles_across, number_of_rectangles_down);
97-
98-
let tile_values_2d = tile_values.map(valuesInOneDimension => {
99-
const valuesInTwoDimensions = [];
100-
const width = number_of_rectangles_across;
101-
const height = number_of_rectangles_down;
102-
for (let y = 0; y < height; y++) {
103-
const start = y * width;
104-
const end = start + width;
105-
valuesInTwoDimensions.push(valuesInOneDimension.slice(start, end));
117+
if (this.projection === 4326) {
118+
return {
119+
y: Math.floor( (ymax - lat) / this._pixelHeight),
120+
x: Math.floor( (lng - xmin) / this._pixelWidth )
121+
}
122+
} else if (this.projector) {
123+
/* source raster doesn't use latitude and longitude,
124+
so need to reproject point from lat/long to projection of raster
125+
*/
126+
const [x, y] = this.projector.inverse([lng, lat]);
127+
return {
128+
y: Math.floor( (ymax - y) / this._pixelHeight),
129+
x: Math.floor( (x - xmin) / this._pixelWidth)
130+
}
106131
}
107-
return valuesInTwoDimensions;
108-
});
132+
}
109133

110-
return tile_values_2d;
134+
// careful not to flip min_y/max_y here
135+
let topLeft = raster_coords_for_tile_coords(0, 0);
136+
let bottomRight = raster_coords_for_tile_coords(number_of_rectangles_down - 1, number_of_rectangles_across - 1);
137+
138+
const getValuesOptions = {
139+
bottom: bottomRight.y,
140+
height: number_of_rectangles_down,
141+
left: topLeft.x,
142+
right: bottomRight.x,
143+
top: topLeft.y,
144+
width: number_of_rectangles_across
145+
};
146+
console.log("getValuesOptions:", getValuesOptions);
147+
return this.georaster.getValues(getValuesOptions);
111148
},
112149

113150
createTile: function(coords, done) {
114151

115152
var error;
116153

117-
let debug_level = 0;
154+
let debug_level = 1;
118155

119156
if (debug_level >= 1) {
120-
var start_time = performance.now();
157+
var start_time = window.performance.now();
121158
var duration_reading_rasters = 0;
122159
var time_started_reading_rasters;
123160
var time_started_filling_rect;
124161
var duration_filling_rects = 0;
125162
}
126163

127-
128164
/*
129165
Unpacking values for use later.
130166
We do this in order to increase speed.
@@ -143,22 +179,20 @@ var GeoRasterLayer = L.GridLayer.extend({
143179
let xmax = this._xmax;
144180
let ymax = this._ymax;
145181

146-
//if (debug_level >= 1) console.group();
147-
148-
//if (debug_level >= 1) console.log("starting createTile with coords:", coords);
149-
182+
if (debug_level >= 1) {
183+
console.log("starting createTile with coords:", coords);
184+
}
150185

151186
// create a <canvas> element for drawing
152187
let tile = L.DomUtil.create('canvas', 'leaflet-tile');
153188
tile.height = this._tile_height;
154189
tile.width = this._tile_width;
155190

156-
157191
// get a canvas context and draw something on it using coords.x, coords.y and coords.z
158192
let context = tile.getContext('2d');
159193

160194
let bounds = this._tileCoordsToBounds(coords);
161-
//if (debug_level >= 1) console.log("bounds:", bounds);
195+
if (debug_level >= 1) console.log("bounds:", bounds);
162196

163197
let xmin_of_tile = bounds.getWest();
164198
let xmax_of_tile = bounds.getEast();
@@ -168,8 +202,25 @@ var GeoRasterLayer = L.GridLayer.extend({
168202

169203
let resolution = this.options.resolution;
170204

171-
let raster_pixels_across = Math.ceil((xmax_of_tile - xmin_of_tile) / pixelWidth);
172-
let raster_pixels_down = Math.ceil((ymax_of_tile - ymin_of_tile) / pixelHeight);
205+
console.log("this.projection:", this._projection);
206+
let raster_pixels_across, raster_pixels_down;
207+
if (this._projection === 4326) {
208+
// width of the tile in pixels from original raster
209+
raster_pixels_across = Math.ceil((xmax_of_tile - xmin_of_tile) / pixelWidth);
210+
raster_pixels_down = Math.ceil((ymax_of_tile - ymin_of_tile) / pixelHeight);
211+
} else if (this.projector) {
212+
/*
213+
1) convert top left to orginal values and top right to src values and divide by pixelWidth
214+
*/
215+
const topLeft = this.projector.inverse({ x: xmin_of_tile,y: ymax_of_tile });
216+
const topRight = this.projector.inverse({ x: xmax_of_tile,y: ymax_of_tile });
217+
const bottomLeft = this.projector.inverse({ x: xmin_of_tile, y: ymin_of_tile });
218+
const bottomRight = this.projector.inverse({ x: xmax_of_tile, y: ymin_of_tile });
219+
raster_pixels_across = Math.ceil(Math.max(topRight.x - topLeft.x, bottomRight.x - bottomLeft.x) / pixelWidth);
220+
raster_pixels_down = Math.ceil(Math.max(topLeft.y - bottomLeft.y, topRight.y - bottomRight.y) / pixelHeight);
221+
}
222+
223+
// just making sure that that resolution isn't higher than the actual number of pixels
173224
let number_of_rectangles_across = Math.min(resolution, raster_pixels_across);
174225
let number_of_rectangles_down = Math.min(resolution, raster_pixels_down);
175226

@@ -187,19 +238,21 @@ var GeoRasterLayer = L.GridLayer.extend({
187238

188239
//if (debug_level >= 1) console.log("ymax of raster:", ymax);
189240

190-
let number_of_pixels_per_rectangle = this._tile_width / 8;
191-
192241
let map = this._map;
193242
let tileSize = this.getTileSize();
243+
// this converts tile coordinates (like how many tiles down and right)
244+
// to pixels from left and top of tile pane
194245
let tileNwPoint = coords.scaleBy(tileSize);
195246

196247
// render asynchronously so tiles show up as they finish instead of all at once (which blocks the UI)
197248
setTimeout(async function () {
198249
let tile_rasters = null;
199250
if (!rasters) {
200-
tile_rasters = await this.getRasters(
201-
map, tileNwPoint, height_of_rectangle_in_pixels, width_of_rectangle_in_pixels, coords, pixelHeight, pixelWidth,
202-
number_of_rectangles_across, number_of_rectangles_down, ymax, xmin);
251+
tile_rasters = await this.getRasters({
252+
tileNwPoint, height_of_rectangle_in_pixels,
253+
width_of_rectangle_in_pixels, coords, pixelHeight, pixelWidth,
254+
number_of_rectangles_across, number_of_rectangles_down, ymax, xmin});
255+
console.log("tile_rasters:", tile_rasters);
203256
}
204257

205258
for (let h = 0; h < number_of_rectangles_down; h++) {
@@ -220,7 +273,7 @@ var GeoRasterLayer = L.GridLayer.extend({
220273
//if (debug_level >= 2) L.circleMarker([lat, lng], {color: "#00FF00"}).bindTooltip(h+","+w).addTo(this._map).openTooltip();
221274
let x_in_raster_pixels = Math.floor( (lng - xmin) / pixelWidth );
222275

223-
if (debug_level >= 1) time_started_reading_rasters = performance.now();
276+
if (debug_level >= 1) time_started_reading_rasters = window.performance.now();
224277
let values = null;
225278
if (tile_rasters) {
226279
// get value from array specific to this tile
@@ -229,7 +282,7 @@ var GeoRasterLayer = L.GridLayer.extend({
229282
// get value from array with data for entire raster
230283
values = rasters.map(raster => raster[y_in_raster_pixels][x_in_raster_pixels]);
231284
}
232-
if (debug_level >= 1) duration_reading_rasters += performance.now() - time_started_reading_rasters;
285+
if (debug_level >= 1) duration_reading_rasters += window.performance.now() - time_started_reading_rasters;
233286

234287
let color = null;
235288
if(this.options.pixelValueToColorFn) {
@@ -253,9 +306,9 @@ var GeoRasterLayer = L.GridLayer.extend({
253306
//context.fillStyle = this.getColor(color);
254307
if (color) {
255308
context.fillStyle = color;
256-
if (debug_level >= 1) time_started_filling_rect = performance.now();
309+
if (debug_level >= 1) time_started_filling_rect = window.performance.now();
257310
context.fillRect(Math.round(w * width_of_rectangle_in_pixels), y_in_tile_pixels, width_of_rectangle_in_pixels_int, height_of_rectangle_in_pixels_int);
258-
if (debug_level >= 1) duration_filling_rects += performance.now() - time_started_filling_rect;
311+
if (debug_level >= 1) duration_filling_rects += window.performance.now() - time_started_filling_rect;
259312
}
260313
//if (debug_level >= 2) console.log("filling:", [w * width_of_rectangle_in_pixels, rect_y_in_pixels, width_of_rectangle_in_pixels_int, height_of_rectangle_in_pixels_int]);
261314
//if (debug_level >= 2) console.log("with color:", color);
@@ -269,7 +322,7 @@ var GeoRasterLayer = L.GridLayer.extend({
269322

270323

271324
if (debug_level >= 1) {
272-
let duration = performance.now() - start_time;
325+
let duration = window.performance.now() - start_time;
273326
console.log("creating tile took ", duration, "milliseconds");
274327
console.log("took", duration_reading_rasters, "milliseconds to read rasters, which is ", Math.round(duration_reading_rasters / duration * 100), "percentage of the total time");
275328
console.log("took", duration_filling_rects, "milliseconds to fill rects, which is ", Math.round(duration_filling_rects / duration * 100), "percentage of the total time");
@@ -285,14 +338,14 @@ var GeoRasterLayer = L.GridLayer.extend({
285338

286339
// method from https://github.com/Leaflet/Leaflet/blob/bb1d94ac7f2716852213dd11563d89855f8d6bb1/src/layer/ImageOverlay.js
287340
getBounds: function () {
288-
return this._bounds;
341+
return this._bounds;
289342
},
290343

291344
getColor(name) {
292-
let d = document.createElement("div");
293-
d.style.color = name;
294-
document.body.appendChild(d)
295-
return window.getComputedStyle(d).color
345+
let d = document.createElement("div");
346+
d.style.color = name;
347+
document.body.appendChild(d)
348+
return window.getComputedStyle(d).color
296349
}
297350

298351
});
@@ -458,7 +511,7 @@ if (typeof window !== "undefined") {
458511
root.chroma = chroma;
459512
}
460513

461-
chroma.version = '1.4.1';
514+
chroma.version = '1.4.0';
462515

463516
_input = {};
464517

@@ -2424,7 +2477,7 @@ if (typeof window !== "undefined") {
24242477
if (bypassMap == null) {
24252478
bypassMap = false;
24262479
}
2427-
if (isNaN(val) || val === null) {
2480+
if (isNaN(val)) {
24282481
return _nacol;
24292482
}
24302483
if (!bypassMap) {
@@ -2664,14 +2717,6 @@ if (typeof window !== "undefined") {
26642717
return _gamma;
26652718
}
26662719
};
2667-
f.nodata = function(d) {
2668-
if (d != null) {
2669-
_nacol = chroma(d);
2670-
return f;
2671-
} else {
2672-
return _nacol;
2673-
}
2674-
};
26752720
return f;
26762721
};
26772722

georaster-layer-for-leaflet.bundle.js

+13-5
Original file line numberDiff line numberDiff line change
@@ -93,20 +93,28 @@ var GeoRasterLayer = L.GridLayer.extend({
9393

9494
var _result = result,
9595
_result2 = _slicedToArray(_result, 2),
96-
min_y = _result2[0],
97-
min_x = _result2[1];
96+
top = _result2[0],
97+
left = _result2[1];
9898

9999
result = raster_coords_for_tile_coords(number_of_rectangles_down - 1, number_of_rectangles_across - 1);
100100

101101
var _result3 = result,
102102
_result4 = _slicedToArray(_result3, 2),
103-
max_y = _result4[0],
104-
max_x = _result4[1];
103+
bottom = _result4[0],
104+
right = _result4[1];
105105

106106
// careful not to flip min_y/max_y here
107107

108108

109-
var tile_values = await this.georaster.getValues(min_x, min_y, max_x, max_y, number_of_rectangles_across, number_of_rectangles_down);
109+
var options = {
110+
bottom: bottom,
111+
height: number_of_rectangles_down,
112+
left: left,
113+
right: right,
114+
top: top,
115+
width: number_of_rectangles_across
116+
};
117+
var tile_values = await this.georaster.getValues(options);
110118

111119
var tile_values_2d = tile_values.map(function (valuesInOneDimension) {
112120
var valuesInTwoDimensions = [];

0 commit comments

Comments
 (0)