Skip to content

Commit

Permalink
Merge pull request #20 from matafokka/development
Browse files Browse the repository at this point in the history
First stable release
  • Loading branch information
matafokka authored May 19, 2022
2 parents a6de08e + eebd463 commit 8dd65f5
Show file tree
Hide file tree
Showing 51 changed files with 2,675 additions and 1,274 deletions.
4 changes: 2 additions & 2 deletions DrawGeodesic.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ L.Geodesic.prototype.getActualLatLngs = function () {
// Writing these functions as class methods will result in an error being thrown. I don't know why.

function _createGeodesic (coords, opts = {}) {
let geodesic = L.geodesic(coords, {...opts, wrap: true});
let geodesic = new L.Geodesic(coords, {...opts, wrap: true});
geodesic.geom.geodesic.ellipsoid.a = 6378137;
geodesic.geom.geodesic.ellipsoid.b = 6378137;
geodesic.geom.geodesic.ellipsoid.f = 0;
Expand Down Expand Up @@ -125,7 +125,7 @@ L.Draw.Polyline = L.Draw.Feature.extend({
// also do not want to trigger any click handlers of objects we are clicking on
// while drawing.
if (!this._mouseMarker) {
this._mouseMarker = L.marker(this._map.getCenter(), {
this._mouseMarker = new L.Marker(this._map.getCenter(), {
icon: L.divIcon({
className: 'leaflet-mouse-marker',
iconAnchor: [20, 20],
Expand Down
109 changes: 61 additions & 48 deletions ESRIGridParser.js
Original file line number Diff line number Diff line change
@@ -1,37 +1,34 @@
const MathTools = require("./MathTools.js");
const proj4 = require("proj4");
const crs = L.CRS.EPSG3857;

/**
* A stateful ESRI Grid parser. Calculates min and max values for selected polygons in {@link L.ALS.SynthPolygonLayer}. Can parse huge files by chunks.
* A stateful ESRI Grid parser. Calculates min and max values for selected polygons in {@link L.ALS.SynthRectangleBaseLayer}. Can parse huge files by chunks.
*/
class ESRIGridParser {

/**
* Constructs a ESRI Grid parser.
*
* @param layer {L.ALS.SynthPolygonLayer} A layer to apply parsed values to.
* @param layer {L.ALS.SynthRectangleBaseLayer} A layer to apply parsed values to.
* @param projectionString {string} Proj4 projection string. If not given, WGS84 assumed.
*/
constructor(layer = undefined, projectionString = "") {

/**
* Layer to apply parsed values to
* @type {L.ALS.SynthPolygonLayer}
* @type {L.ALS.SynthRectangleBaseLayer}
*/
this.layer = layer
this.layer = layer;

if (projectionString === "")
projectionString = "WGS84";

/**
* Proj4 projection string
* @type {boolean}
* Proj4 projection object. Projects coordinates from WGS84 to grids projection.
* @type {Object|undefined}
*/
this.hasProj = (projectionString !== "");

if (this.hasProj)
/**
* Proj4 projection object. Projects coordinates from WGS84 to grids projection.
* @type {Object|undefined}
*/
this.projectionFromWGS = proj4("WGS84", projectionString);
this.projectionFromMerc = proj4("EPSG:3857", projectionString);

this.clearState();
}
Expand Down Expand Up @@ -119,15 +116,15 @@ class ESRIGridParser {
this.polygonsCoordinates = ESRIGridParser.getInitialData(this.layer);
}


/**
* Reads a chunk
* @param chunk {string} A chunk to read
*/
readChunk(chunk) {
for (let i = 0; i < chunk.length; i++) {
let symbol = chunk[i].toLowerCase();
let isSpace = (symbol === " "), isLineBreak = (symbol === "\n" || symbol === "\r");
let symbol = chunk[i].toLowerCase(),
isSpace = (symbol === " "),
isLineBreak = (symbol === "\n" || symbol === "\r");

// Skip multiple spaces and line breaks
let nameMap = [
Expand All @@ -151,8 +148,11 @@ class ESRIGridParser {
if (!this.allDEMParamsRead) {
// Read param name until we hit space
if (this.readingDEMParamName) {
if (symbol === " ")
if (isSpace) {
if (this.param === "")
continue;
this.readingDEMParamName = false;
}
// Stop reading when we hit minus or digit
else if (symbol === "-" || !isNaN(parseInt(symbol))) {
this.value = symbol;
Expand Down Expand Up @@ -199,12 +199,6 @@ class ESRIGridParser {
if (!this.DEMParams.nodata_value)
this.DEMParams.nodata_value = -9999;

/*let rect = L.rectangle([
[this.y, this.x],
[this.y - this.DEMParams.nrows * this.DEMParams.cellsize, this.x + this.DEMParams.ncols * this.DEMParams.cellsize]
], {color: "#ff7800", weight: 2});
this.layer.addLayers(rect);*/

this.DEMParamsCalculated = true;
}

Expand All @@ -219,17 +213,24 @@ class ESRIGridParser {
continue;
}

let oldPoint = [this.x, this.y];
let point = (this.hasProj) ? this.projectionFromWGS.inverse(oldPoint) : oldPoint;
let point = this.projectionFromMerc.inverse([this.x, this.y]);

for (let name in this.polygonsCoordinates) {
if (!MathTools.isPointInRectangle(point, this.polygonsCoordinates[name]))
let poly = this.polygonsCoordinates[name];

if (!MathTools[poly.length > 2 ? "isPointInPolygon" : "isPointInRectangle"](point, poly))
continue;

if (!this.polygonsStats[name])
this.polygonsStats[name] = ESRIGridParser.createStatsObject();

let stats = this.polygonsStats[name];
ESRIGridParser.addToStats(pixelValue, stats);

/*new L.CircleMarker(
crs.unproject(L.point(...point)),
{color: `rgb(${pixelValue},${pixelValue},${pixelValue})`, fillOpacity: 1, stroke: false}
).addTo(map);*/
}
} else if (!isSpace && !isLineBreak)
this.value += symbol;
Expand Down Expand Up @@ -260,7 +261,7 @@ class ESRIGridParser {
*
* This method is NOT thread-safe! Call it outside of your WebWorker and pass your layer as an argument.
*
* @param layer {L.ALS.SynthPolygonLayer} If you're not using it in a WebWorker, don't pass anything. Otherwise, pass your layer.
* @param layer {L.ALS.SynthRectangleBaseLayer} If you're not using it in a WebWorker, don't pass anything. Otherwise, pass your layer.
*/
copyStats(layer = undefined) {
let l = this.layer || layer;
Expand Down Expand Up @@ -311,38 +312,50 @@ class ESRIGridParser {

/**
* Generates initial parameters for the layer.
* @param layer {L.ALS.SynthPolygonLayer} Layer to copy data from
* @param layer {L.ALS.SynthPolygonBaseLayer} Layer to copy data from
*/
static getInitialData(layer) {
let polygonsCoordinates = {};
for (let name in layer.polygons) {
if (!layer.polygons.hasOwnProperty(name))
continue;
layer.forEachValidPolygon(polygon => {
polygon.tempDemName = L.ALS.Helpers.generateID();

let coords;

if (polygon instanceof L.Rectangle) {
let rect = polygon.getBounds();
coords = [rect.getNorthWest(), rect.getSouthEast()];
} else
coords = polygon.getLatLngs()[0];

let coordsCopy = [];
for (let coord of coords) {
let {x, y} = crs.project(coord);
coordsCopy.push([x, y]);
}

polygonsCoordinates[polygon.tempDemName] = coordsCopy;
});

let rect = layer.polygons[name].getBounds();
polygonsCoordinates[name] = [
[rect.getWest(), rect.getNorth()],
[rect.getEast(), rect.getSouth()]
];
}
return polygonsCoordinates;
}

/**
* Copies stats from any ESRIGridParser to a given layer
* @param layer {L.ALS.SynthPolygonLayer} Layer to copy stats to
* @param layer {L.ALS.SynthRectangleBaseLayer} Layer to copy stats to
* @param stats {Object} Stats from any ESRIGridParser
*/
static copyStats(layer, stats) {
for (let name in stats) {
let widgetable = layer.polygonsWidgets[name];
let s = stats[name];
s.mean = s.sum / s.count;
widgetable.getWidgetById("minHeight").setValue(s.min);
widgetable.getWidgetById("maxHeight").setValue(s.max);
widgetable.getWidgetById("meanHeight").setValue(s.mean);
}
layer.updateAll();
layer.forEachValidPolygon(polygon => {
let entry = stats[polygon.tempDemName];
if (!entry)
return;

entry.mean = entry.sum / entry.count;
polygon.widgetable.getWidgetById("minHeight").setValue(entry.min);
polygon.widgetable.getWidgetById("maxHeight").setValue(entry.max);
polygon.widgetable.getWidgetById("meanHeight").setValue(entry.mean);
});
layer.calculateParameters();
}

static createStatsObject() {
Expand Down
48 changes: 29 additions & 19 deletions GeoTIFFParser.js
Original file line number Diff line number Diff line change
Expand Up @@ -39,26 +39,28 @@ module.exports = async function (file, projectionString, initialData) {
projectionString = projInformation.proj4;
}

let projectionFromWGS = proj4("WGS84", projectionString);
let projectionFromMerc = proj4("EPSG:3857", projectionString);

for (let name in initialData) {
// Let's project each polygon to the image, get their intersection part and calculate statistics for it
// Project each polygon to the image, get their intersection part and calculate statistics for it
let polygon = initialData[name],
oldBbox = [
polygon[0], [polygon[1][0], polygon[0][1]], polygon[1], [polygon[0][0], polygon[1][1]], polygon[0]
],
newBbox = [];
isRect = polygon.length === 2,
coords = isRect ? [
polygon[0], [polygon[1][0], polygon[0][1]],
polygon[1], [polygon[0][0], polygon[1][1]]
] : polygon,
projPolygon = [];

for (let point of oldBbox)
newBbox.push(projectionFromWGS.forward(point));
for (let coord of coords)
projPolygon.push(projectionFromMerc.forward(coord));

newBbox = bboxPolygon(
bbox(
turfHelpers.polygon([newBbox])
)
);
projPolygon.push([...projPolygon[0]]); // Clone first coordinate to avoid floating point errors

let intersection = intersect(imagePolygon, newBbox);
let polygonBbox = bboxPolygon(bbox(
turfHelpers.polygon([projPolygon])
));

let intersection = intersect(imagePolygon, polygonBbox);
if (!intersection)
continue;

Expand All @@ -83,22 +85,24 @@ module.exports = async function (file, projectionString, initialData) {
stats = ESRIGridParser.createStatsObject(); // Stats for current polygon

for (currentY; currentY <= maxY; currentY++) {
let currentX = minX;
let raster = await image.readRasters({window: [minX, currentY, maxX, currentY + 1]});
let color0 = raster[0], // Raster is a TypedArray where elements are colors and their elements are pixel values of that color.
let currentX = minX,
raster = await image.readRasters({window: [minX, currentY, maxX, currentY + 1]}),
color0 = raster[0], // Raster is a TypedArray where elements are colors and their elements are pixel values of that color.
index = -1;

for (let pixel of color0) {
let crsX = leftX + currentX * xSize, crsY = topY + currentY * ySize;
if (projInformation) {
crsX *= projInformation.coordinatesConversionParameters.x;
crsY *= projInformation.coordinatesConversionParameters.y;
}

let point = projectionFromWGS.inverse([crsX, crsY]);
currentX++; // So we can continue without incrementing
index++;

if (!MathTools.isPointInRectangle(point, polygon))
let point = projectionFromMerc.inverse([crsX, crsY]);

if (!MathTools[isRect ? "isPointInRectangle" : "isPointInPolygon"](point, polygon))
continue;

let value = 0;
Expand All @@ -108,6 +112,12 @@ module.exports = async function (file, projectionString, initialData) {
let multipliedValue = value * zScale;
if (value === nodata || multipliedValue === nodata)
continue;

/*new L.CircleMarker(
L.CRS.EPSG3857.unproject(L.point(...point)),
{color: `rgb(${value},${value},${value})`, fillOpacity: 1, stroke: false}
).addTo(map);*/

ESRIGridParser.addToStats(multipliedValue, stats);
}
}
Expand Down
Loading

0 comments on commit 8dd65f5

Please sign in to comment.