From b4e5c3cb2d60de273bdab77a5cd86ebdd46ee2c0 Mon Sep 17 00:00:00 2001 From: Tom Gaskins Date: Sat, 7 Nov 2015 19:30:27 -0800 Subject: [PATCH] Added data lookup to USGSSlabs app. --- apps/USGSSlabs/DataGrid.js | 155 +++++++++++++++++++++++++++++++----- apps/USGSSlabs/USGSSlabs.js | 138 ++++++++++---------------------- 2 files changed, 176 insertions(+), 117 deletions(-) diff --git a/apps/USGSSlabs/DataGrid.js b/apps/USGSSlabs/DataGrid.js index a1e094ff..517aacf2 100644 --- a/apps/USGSSlabs/DataGrid.js +++ b/apps/USGSSlabs/DataGrid.js @@ -8,14 +8,15 @@ define(function () { "use strict"; - var DataGrid = function (data, width) { - var latitude, longitude, altitude; + var DataGrid = function (data) { + var latitude, longitude, altitude, firstLatitude = -1e5, lineCounter = 0, indexMap = []; - this.width = width; + this.width = 0; this.positions = []; - this.minLon = this.minLat = -Number.MAX_VALUE; - this.maxLon = this.maxLat = Number.MAX_VALUE; + this.minLon = this.minLat = Number.MAX_VALUE; + this.maxLon = this.maxLat = -Number.MAX_VALUE; + // Read the data but retain only the positions that have altitude values. var lines = data.split("\n"); for (var i = 0; i < lines.length; i++) { var rawPosition = lines[i].trim().split("\t"); @@ -23,34 +24,144 @@ define(function () { continue; } + longitude = parseFloat(rawPosition[0]); + latitude = parseFloat(rawPosition[1]); + + if (longitude > 180) { + longitude -= 360; + } + + if (longitude < this.minLon) + this.minLon = longitude; + if (longitude > this.maxLon) + this.maxLon = longitude; + if (latitude < this.minLat) + this.minLat = latitude; + if (latitude > this.maxLat) + this.maxLat = latitude; + + // Recognize when the first row of the grid ends and use that to calculate the grid width. + if (firstLatitude === -1e5) { // identify the first latitude in the grid + firstLatitude = latitude; + } else if (latitude !== firstLatitude && this.width === 0) { + // We've reached the first position of the second row, so now we know the grid width. + this.width = lineCounter; + } + if (rawPosition[2] != "NaN") { - longitude = parseFloat(rawPosition[0]); - latitude = parseFloat(rawPosition[1]); altitude = parseFloat(rawPosition[2]); + // Keep a map that relates the original ordinal position in the data to the retained positions list. + indexMap[lineCounter] = this.positions.length; + this.positions.push(new WorldWind.Position(latitude, longitude, altitude * 1000)); + } - if (longitude > 180) { - longitude -= 360; - } + ++lineCounter; + } - if (longitude < this.minLon) - this.minLon = longitude; - if (longitude > this.maxLon) - this.maxLon = longitude; - if (latitude < this.minLat) - this.minLat = latitude; - if (latitude > this.MinLat) - this.maxLat = latitude; + this.height = lineCounter / this.width; + this.deltaLat = (this.maxLat - this.minLat) / (this.height - 1); + this.deltaLon = (this.maxLon - this.minLon) / (this.width - 1); + this.indexMap = indexMap; + }; - positions.push(new WorldWind.Position(latitude, longitude, altitude * 1000)); - } else { - positions.push(null); + DataGrid.prototype.makeGridIndices = function () { + // Create all the triangles formed by the original grid. + + var gridIndices = [], i = 0, width = this.width, height = this.height; + + for (var r = 0; r < height - 1; r++) { + for (var c = 0; c < width - 1; c++) { + var k = r * width + c; + + // lower left triangle + gridIndices[i++] = k; + gridIndices[i++] = k + 1; + gridIndices[i++] = k + width; + + // upper right triangle + gridIndices[i++] = k + 1; + gridIndices[i++] = k + 1 + width; + gridIndices[i++] = k + width; } } - this.height = this.positions.length / this.width; + return gridIndices; + }; + + DataGrid.prototype.findTriangles = function () { + // Create triangles from the retained positions. + + var gridIndices = this.makeGridIndices(), // get all the triangles in the original grid + indexMap = this.indexMap, // maps original grid indices to the indices in the retained-positions array + mappedIndices = [], // collects the triangle definitions + ia, ib, ic, iaMapped, ibMapped, icMapped; + + for (var i = 0; i < gridIndices.length; i += 3) { // for all original triangles + // Determine the triangle's indices in the retained-positions array. + ia = gridIndices[i]; + ib = gridIndices[i + 1]; + ic = gridIndices[i + 2]; + + // Determine whether those original positions had data associated with them. If they did not then + // they will not have an entry in the index map. + iaMapped = indexMap[ia]; + ibMapped = indexMap[ib]; + icMapped = indexMap[ic]; + + // If all three triangle vertices have data associated, save the triangle, using the mapped indices. + if (iaMapped && ibMapped && icMapped) { + mappedIndices.push(iaMapped); + mappedIndices.push(ibMapped); + mappedIndices.push(icMapped); + } + } + + return mappedIndices; }; DataGrid.prototype.lookupValue = function (latitude, longitude) { + // Look up a value from the grid using bilinear interpolation. + + // Determine the four corner indices of the original grid. + var colNW = Math.floor((this.width - 1) * (longitude - this.minLon) / (this.maxLon - this.minLon)), + rowNW = Math.floor((this.height - 1) * (this.maxLat - latitude) / (this.maxLat - this.minLat)); + + if (colNW === this.width - 1) { + --colNW; + } + if (rowNW === this.height - 1) { + --rowNW; + } + + var nwIndex = rowNW * this.width + colNW, + neIndex = nwIndex + 1, + swIndex = nwIndex + this.width, + seIndex = swIndex + 1; + + // Map the grid indices to the retained-positions indices. + var indexMap = this.indexMap; + swIndex = indexMap[swIndex]; + seIndex = indexMap[seIndex]; + nwIndex = indexMap[nwIndex]; + neIndex = indexMap[neIndex]; + + // If all four corners have values, interpolate the values over the grid cell. + if (swIndex && seIndex && nwIndex && neIndex) { + var a = this.positions[swIndex], aa = a.altitude, + b = this.positions[seIndex], bb = b.altitude, + c = this.positions[nwIndex], cc = c.altitude, + d = this.positions[neIndex], dd = d.altitude; + + var s = (longitude - a.longitude) / this.deltaLon, + t = (latitude - a.latitude) / this.deltaLat; + var lower = (1 - s) * aa + s * bb, + upper = (1 - s) * cc + s * dd, + result = (1 - t) * lower + t * upper; + + return result; + } else { + return null; + } }; return DataGrid; diff --git a/apps/USGSSlabs/USGSSlabs.js b/apps/USGSSlabs/USGSSlabs.js index 7f46c19d..858497da 100644 --- a/apps/USGSSlabs/USGSSlabs.js +++ b/apps/USGSSlabs/USGSSlabs.js @@ -9,12 +9,14 @@ define(['../../src/WorldWind', '../util/GoToBox', '../util/LayersPanel', '../util/ProjectionMenu', - '../util/TerrainOpacityController'], + '../util/TerrainOpacityController', + 'DataGrid'], function (ww, GoToBox, LayersPanel, ProjectionMenu, - TerrainOpacityController) { + TerrainOpacityController, + DataGrid) { "use strict"; var USGSSlabs = function () { @@ -57,23 +59,46 @@ define(['../../src/WorldWind', this.projectionMenu = new ProjectionMenu(this.wwd); this.terrainOpacityController = new TerrainOpacityController(this.wwd); + this.screenText = new WorldWind.ScreenText( + new WorldWind.Offset(WorldWind.OFFSET_PIXELS, 0, WorldWind.OFFSET_PIXELS, 0), "Upper Left"); + var textAttributes = new WorldWind.TextAttributes(textAttributes); + // Use offset to position the lower left corner of the text string at the shape's screen location. + textAttributes.offset = new WorldWind.Offset(WorldWind.OFFSET_FRACTION, 0, WorldWind.OFFSET_FRACTION, 0); + this.screenText.attributes = textAttributes; + this.textLayer = new WorldWind.RenderableLayer(); + this.textLayer.hide = true; + this.textLayer.enabled = false; + this.textLayer.addRenderable(this.screenText); + this.wwd.addLayer(this.textLayer); + this.layersPanel.synchronizeLayerList(); this.loadSlabData("CAS", "cascadia_slab1.0_clip.xyz", 401, WorldWind.Color.YELLOW); - //this.loadSlabData("SOL", "sol_slab1.0_clip.xyz", 1001, WorldWind.Color.YELLOW); - //this.loadSlabData("MEX", "mex_slab1.0_clip.xyz", 1251, WorldWind.Color.CYAN); + this.loadSlabData("SOL", "sol_slab1.0_clip.xyz", 1001, WorldWind.Color.YELLOW); + this.loadSlabData("MEX", "mex_slab1.0_clip.xyz", 1251, WorldWind.Color.CYAN); //this.loadSlabData("ALU", "alu_slab1.0_clip.xyz", 2451, WorldWind.Color.MAGENTA); - var wwd = this.wwd; - var handlePick = function (o) { - var pickPoint = wwd.canvasCoordinates(o.clientX, o.clientY); + var handlePick = (function (o) { + var pickPoint = this.wwd.canvasCoordinates(o.clientX, o.clientY); - var pickList = wwd.pick(pickPoint); + this.textLayer.enabled = false; + this.wwd.redraw(); + + var pickList = this.wwd.pick(pickPoint); if (pickList.objects.length > 0) { for (var p = 0; p < pickList.objects.length; p++) { var pickedObject = pickList.objects[p]; if (pickedObject.userObject instanceof WorldWind.TriangleMesh) { if (pickedObject.position) { + var latitude = pickedObject.position.latitude, + longitude = pickedObject.position.longitude, + altitude = pickedObject.userObject.dataGrid.lookupValue(latitude, longitude); + if (altitude !== null) { + this.screenText.screenOffset.x = pickPoint[0]; + this.screenText.screenOffset.y = this.wwd.viewport.width - pickPoint[1]; + this.screenText.text = Math.floor(Math.abs(altitude) / 1000).toString() + " Km"; + this.textLayer.enabled = true; + } //console.log("PO: " + pickedObject.position.toString() + " " + pickedObject.isOnTop); //console.log("TN: " + pickList.terrainObject().position.toString() + // " " + pickList.terrainObject().isOnTop); @@ -81,13 +106,13 @@ define(['../../src/WorldWind', } } } - }; + }).bind(this); // Listen for mouse moves and highlight the placemarks that the cursor rolls over. - wwd.addEventListener("mousemove", handlePick); + this.wwd.addEventListener("mousemove", handlePick); // Listen for taps on mobile devices and highlight the placemarks that the user taps. - var tapRecognizer = new WorldWind.TapRecognizer(wwd, handlePick); + var tapRecognizer = new WorldWind.TapRecognizer(this.wwd, handlePick); }; USGSSlabs.prototype.loadSlabData = function (name, dataFile, width, color) { @@ -100,7 +125,8 @@ define(['../../src/WorldWind', xhr.onreadystatechange = (function () { if (xhr.readyState === 4) { if (xhr.status === 200) { - this.parse(name, width, color, xhr.responseText); + var dataGrid = new DataGrid(xhr.responseText); + this.addGridToWorldWindow(name, dataGrid, color); } else { Logger.log(Logger.LEVEL_WARNING, @@ -120,9 +146,7 @@ define(['../../src/WorldWind', xhr.send(null); }; - USGSSlabs.prototype.parse = function (name, width, color, responseText) { - var lines = responseText.split("\n"); - + USGSSlabs.prototype.addGridToWorldWindow = function (name, dataGrid, color) { var meshLayer = new WorldWind.RenderableLayer(); meshLayer.displayName = name; this.wwd.addLayer(meshLayer); @@ -136,16 +160,14 @@ define(['../../src/WorldWind', var highlightAttributes = new WorldWind.ShapeAttributes(meshAttributes); highlightAttributes.outlineColor = WorldWind.Color.WHITE; - var positions = this.makePositionList(lines); - var gridIndices = this.makeGridIndices(width, positions.numOriginalPositions / width); - var indices = this.findTriangles(gridIndices, positions.indexMap); - var splitShapes = WorldWind.TriangleMesh.split(positions.positions, indices, null, null); + var indices = dataGrid.findTriangles(); + var splitShapes = WorldWind.TriangleMesh.split(dataGrid.positions, indices, null, null); for (var i = 0; i < splitShapes.length; i++) { var mesh = new WorldWind.TriangleMesh(splitShapes[i].positions, splitShapes[i].indices, meshAttributes); - //mesh.altitudeScale = 100; - mesh.altitudeMode = WorldWind.RELATIVE_TO_GROUND; + mesh.altitudeMode = WorldWind.ABSOLUTE; mesh.highlightAttributes = highlightAttributes; + mesh.dataGrid = dataGrid; meshLayer.addRenderable(mesh); } @@ -153,79 +175,5 @@ define(['../../src/WorldWind', this.wwd.redraw(); }; - USGSSlabs.prototype.makeGridIndices = function (width, height) { - var indices = [], i = 0; - - for (var r = 0; r < height - 1; r++) { - for (var c = 0; c < width - 1; c++) { - var k = r * width + c; - - indices[i++] = k; - indices[i++] = k + 1; - indices[i++] = k + width; - indices[i++] = k + 1; - indices[i++] = k + 1 + width; - indices[i++] = k + width; - } - } - - return indices; - }; - - USGSSlabs.prototype.makePositionList = function (lines) { - var positions = [], - indices = [], - originalIndex = 0, - latitude, longitude, altitude; - - for (var i = 0; i < lines.length; i++) { - var rawPosition = lines[i].trim().split("\t"); - if (rawPosition.length != 3) { - continue; - } - - if (rawPosition[2] != "NaN") { - indices[originalIndex] = positions.length; - - longitude = parseFloat(rawPosition[0]); - latitude = parseFloat(rawPosition[1]); - altitude = parseFloat(rawPosition[2]); - - if (longitude > 180) { - longitude -= 360; - } - - positions.push(new WorldWind.Position(latitude, longitude, altitude * 1000)); - } - - ++originalIndex; - } - - return {positions: positions, indexMap: indices, numOriginalPositions: originalIndex}; - }; - - USGSSlabs.prototype.findTriangles = function (gridIndices, indexMap) { - var mappedIndices = [], - ia, ib, ic, iaMapped, ibMapped, icMapped; - - for (var i = 0; i < gridIndices.length; i += 3) { - ia = gridIndices[i]; - ib = gridIndices[i + 1]; - ic = gridIndices[i + 2]; - - iaMapped = indexMap[ia]; - ibMapped = indexMap[ib]; - icMapped = indexMap[ic]; - - if (iaMapped && ibMapped && icMapped) { - mappedIndices.push(iaMapped); - mappedIndices.push(ibMapped); - mappedIndices.push(icMapped); - } - } - - return mappedIndices; - }; - return USGSSlabs; }); \ No newline at end of file