From 15228117de3eb86d7e67c04b8472ee3bbb8e80b5 Mon Sep 17 00:00:00 2001 From: David Levinsky Date: Mon, 23 Jul 2018 16:55:52 +0200 Subject: [PATCH 01/20] more measuring tools --- src/browser/browser.css | 26 ++++ src/browser/ui/control/measure.js | 227 ++++++++++++++++++++++++++---- 2 files changed, 229 insertions(+), 24 deletions(-) diff --git a/src/browser/browser.css b/src/browser/browser.css index 4340ad05..a4af428e 100755 --- a/src/browser/browser.css +++ b/src/browser/browser.css @@ -522,6 +522,17 @@ /*background-color: rgba(255, 255, 255, 0.9);*/ } +.vts-measure-tools div { + margin-right: 2px; +} + +.vts-measure-tools { + font-family: Verdana, Tahoma, Geneva, Arial, Sans-serif; + font-size: 12px; + margin-top: 2px; + /*background-color: rgba(255, 255, 255, 0.9);*/ +} + .vts-measure-tools-button { border-radius: 2px; border: 1px solid rgba(0, 0, 0, 0.7); @@ -536,6 +547,21 @@ border: 1px solid rgba(0, 102, 255, 1); } + +.vts-measure-compute { + position: absolute; + font-family: Verdana, Tahoma, Geneva, Arial, Sans-serif; + font-size: 12px; + top: 0px; + left: 0px; + display: none; + border-radius: 2px; + border: 2px solid rgba(255, 255, 255, 0.47); + border: solid 1px #000; + white-space: nowrap; + padding: 1px 3px; +} + /* * FULLSCREEN */ diff --git a/src/browser/ui/control/measure.js b/src/browser/ui/control/measure.js index aee2306b..f04e796f 100644 --- a/src/browser/ui/control/measure.js +++ b/src/browser/ui/control/measure.js @@ -1,8 +1,10 @@ import Dom_ from '../../utility/dom'; +import {utils as utils_} from '../../../core/utils/utils'; //get rid of compiler mess var dom = Dom_; +var utils = utils_; var UIControlMeasure = function(ui, visible, visibleLock) { @@ -22,16 +24,26 @@ var UIControlMeasure = function(ui, visible, visibleLock) { + '
' + '
' + '
' - + '' + + '' + '
' + '
' - + '
Clear
' + + '
Position
' + + '
Length
' + + '
Track Length
' + + '
Area
' + + '
Volume
' + + '
Clear Log
' + '
' + '
' + '
' + '
' + '
' + + + '
' + + '
Undo
' + + '
Compute
' + + '
' + ' ', visible, visibleLock); @@ -51,9 +63,27 @@ var UIControlMeasure = function(ui, visible, visibleLock) { clearButton.on('click', this.onClear.bind(this)); clearButton.on('dblclick', this.onDoNothing.bind(this)); + var toolButton = this.control.getElement('vts-measure-position'); + toolButton.on('click', this.onTool.bind(this, 0)); + toolButton.on('dblclick', this.onDoNothing.bind(this)); + toolButton = this.control.getElement('vts-measure-length'); + toolButton.on('click', this.onTool.bind(this, 1)); + toolButton.on('dblclick', this.onDoNothing.bind(this)); + toolButton = this.control.getElement('vts-measure-track'); + toolButton.on('click', this.onTool.bind(this, 2)); + toolButton.on('dblclick', this.onDoNothing.bind(this)); + toolButton = this.control.getElement('vts-measure-area'); + toolButton.on('click', this.onTool.bind(this, 3)); + toolButton.on('dblclick', this.onDoNothing.bind(this)); + toolButton = this.control.getElement('vts-measure-volume'); + toolButton.on('click', this.onTool.bind(this, 4)); + toolButton.on('dblclick', this.onDoNothing.bind(this)); + this.measuring = false; this.counter = 1; this.lastCoords = null; + this.navCoords = null; + this.tool = 0; this.listPanel = this.control.getElement('vts-measure-text-holder'); this.list = this.control.getElement('vts-measure-text-input'); @@ -69,6 +99,7 @@ var UIControlMeasure = function(ui, visible, visibleLock) { this.onMouseMoveCall = this.onMouseMove.bind(this); this.onMouseLeaveCall = this.onMouseLeave.bind(this); this.onMouseClickCall = this.onMouseClick.bind(this); + this.onMapUpdateCall = this.onMapUpdate.bind(this); this.update(); }; @@ -104,46 +135,82 @@ UIControlMeasure.prototype.onMouseClick = function(event) { var coords = event.getMouseCoords(); var clickCoords = map.getHitCoords(coords[0], coords[1], 'fix'); + map.redraw(); + if (!clickCoords) { return; } - clickCoords = map.convertCoordsFromNavToPublic(clickCoords, 'fix'); + var i, li, res, str, m2ft = 3.28084, km2mi = 0.621371; + var space = ' '; - var str = '#' + this.counter + ' ' + clickCoords[0].toFixed(7) + ', ' + clickCoords[1].toFixed(7) + ', ' + clickCoords[2].toFixed(2) + 'm'; + for (i = 0, li = ('' + this.counter).length; i < li; i++) { + space += ' '; + } - if (this.lastCoords) { - var res = map.getDistance(this.lastCoords, clickCoords, false, true); - var space = '\n '; + if (this.tool == 0) { + this.navCoords = clickCoords; + clickCoords = map.convertCoordsFromNavToPublic(clickCoords, 'fix'); - for (var i = 0, li = ('' + this.counter).length; i < li; i++) { - space += ' '; - } + str = '#' + this.counter + ' position: '; + str += '\n' + space + clickCoords[0].toFixed(7) + ', ' + clickCoords[1].toFixed(7) + ', ' + clickCoords[2].toFixed(2) + 'm/' + (clickCoords[2]*m2ft).toFixed(2) + 'ft'; + this.counter++; - str += space + 'great-circle distance: '; + } else if (this.tool == 1) { + if (!this.navCoords || this.navCoords.length == 2) { + this.navCoords = [clickCoords]; + clickCoords = map.convertCoordsFromNavToPublic(clickCoords, 'fix'); - if (res[0] > 100000) { - str += '' + (res[0]*0.001).toFixed(2) + 'km'; + str = '#' + this.counter + ' length: '; + str += '\n' + space + 'p1: ' + clickCoords[0].toFixed(7) + ', ' + clickCoords[1].toFixed(7) + ', ' + clickCoords[2].toFixed(2) + 'm/' + (clickCoords[2]*m2ft).toFixed(2) + 'ft'; } else { - str += '' + res[0].toFixed(2) + 'm'; + this.navCoords.push(clickCoords); + clickCoords = map.convertCoordsFromNavToPublic(clickCoords, 'fix'); + + str = space + 'p2: ' + clickCoords[0].toFixed(7) + ', ' + clickCoords[1].toFixed(7) + ', ' + clickCoords[2].toFixed(2) + 'm/' + (clickCoords[2]*m2ft).toFixed(2) + 'ft'; + + res = map.getDistance(this.lastCoords, clickCoords, false, true); + str += '\n' + space + 'great-circle distance: '; + + if (res[0] >= 100000) { + str += '' + (res[0]*0.001).toFixed(2) + 'km/' + (res[0]*0.001*km2mi).toFixed(2) + 'mi'; + } else { + str += '' + res[0].toFixed(2) + 'm/' + (res[0]*m2ft).toFixed(2) + 'ft'; + } + + str += '\n' + space + 'elevation difference: ' + (clickCoords[2] - this.lastCoords[2]).toFixed(2) + 'm/' + (((clickCoords[2] - this.lastCoords[2]))*m2ft).toFixed(2) + 'ft'; + str += '\n' + space + 'euclidean distance: '; + + if (res[2] >= 100000) { + str += '' + (res[2]*0.001).toFixed(2) + 'km/' + (res[2]*0.001*km2mi).toFixed(2) + 'mi'; + } else { + str += '' + res[2].toFixed(2) + 'm/' + (res[2]*m2ft).toFixed(2) + 'ft'; + } + + this.counter++; } + } else if (this.tool == 2) { + if (!this.navCoords) { + this.navCoords = [clickCoords]; + clickCoords = map.convertCoordsFromNavToPublic(clickCoords, 'fix'); - str += space + 'elevation difference: ' + (clickCoords[2] - this.lastCoords[2]).toFixed(2) + 'm'; - str += space + 'euclidean distance: '; - - if (res[2] > 100000) { - str += '' + (res[2]*0.001).toFixed(2) + 'km'; + str = '#' + this.counter + ' track length: '; + str += '\n' + space + 'p1: ' + clickCoords[0].toFixed(7) + ', ' + clickCoords[1].toFixed(7) + ', ' + clickCoords[2].toFixed(2) + 'm/' + (clickCoords[2]*m2ft).toFixed(2) + 'ft'; } else { - str += '' + res[2].toFixed(2) + 'm'; + this.navCoords.push(clickCoords); + clickCoords = map.convertCoordsFromNavToPublic(clickCoords, 'fix'); + + str = space + 'p' + this.navCoords.length + ': ' + clickCoords[0].toFixed(7) + ', ' + clickCoords[1].toFixed(7) + ', ' + clickCoords[2].toFixed(2) + 'm/' + (clickCoords[2]*m2ft).toFixed(2) + 'ft'; } } - this.counter++; this.lastCoords = clickCoords; - var listElement = this.list.getElement(); - listElement.value += str + '\n'; - listElement.scrollTop = listElement.scrollHeight; //scroll list to the last line + if (str) { + var listElement = this.list.getElement(); + listElement.value += str + '\n'; + listElement.scrollTop = listElement.scrollHeight; //scroll list to the last line + } }; UIControlMeasure.prototype.onMouseMove = function(event) { @@ -187,6 +254,7 @@ UIControlMeasure.prototype.onSwitch = function() { mapElement.on('mousemove', this.onMouseMoveCall); mapElement.on('mouseleave', this.onMouseLeaveCall); mapElement.on('click', this.onMouseClickCall); + this.browser.on('map-update', this.onMapUpdateCall); } else { this.buttonOn.setStyle('display', 'none'); @@ -195,19 +263,36 @@ UIControlMeasure.prototype.onSwitch = function() { mapElement.off('mousemove', this.onMouseMoveCall); mapElement.off('mouseleave', this.onMouseLeaveCall); mapElement.off('click', this.onMouseClickCall); + this.browser.off('map-update', this.onMapUpdateCall); } this.updateLink(); this.update(); }; +UIControlMeasure.prototype.onTool = function(tool) { + this.tool = tool; + this.navCoords = null; + + var map = this.browser.getMap(); + if (map) { + map.redraw(); + } +}; + UIControlMeasure.prototype.onClear = function() { this.counter = 1; this.lastCoords = null; + this.navCoords = null; var listElement = this.list.getElement(); listElement.value = ''; listElement.scrollTop = 0; + + var map = this.browser.getMap(); + if (map) { + map.redraw(); + } }; UIControlMeasure.prototype.update = function() { @@ -221,6 +306,100 @@ UIControlMeasure.prototype.update = function() { }; +UIControlMeasure.prototype.onMapUpdate = function() { + var map = this.browser.getMap(); + if (!map) { + return; + } + + var renderer = this.browser.getRenderer(); + + if (!this.circleImage) { + this.circleImage = utils.loadImage( + 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAABmJLR0QAAAAAAAD5Q7t/AAAACW9GRnMAAAAgAAAA4ACD+EAUAAAACXBIWXMAAAsTAAALEwEAmpwYAAAA/UlEQVRYw+2VPwqDMBTG3dz1Am56EnH2XLroETxGuwc3Z7cOdhY8QJpfSUBspUvStJAPPggvD973/uQligICAgL+DKViqygUV02hbaXLwJlio7gpyhNu2idzEXwwgfI8H+u6vnZdN/V9P3EuimLcCRlsiyArGcfxjWDLsmzyAGzc4aNFNDZ7/iw7AeQH4LNrh5WZYLgkJTaZCyHuVVVdkiSZ0zSdOWMzlaBFWkRrQ4A4Zk/A4wBie1MFYUMAz0wybCYAmR8FUAlzj6+2r18TgM2VAO8tOB1Cyk7mrofQ+zP0voheVjHtIBjDxjrmvCu7k1Xs/TP6ie84ICDAGR5uCYdPo0MWiAAAAABJRU5ErkJggg==', + //"http://maps.google.com/mapfiles/kml/shapes/placemarkcircle.png", + (function(){ + this.circleTexture = renderer.createTexture({ 'source': this.circleImage }); + }).bind(this) + ); + } + + if (!this.circleTexture) { + return; + } + + var i, li, coords, points; + + switch(this.tool) { + case 0: //point + + if (this.navCoords) { + coords = map.convertCoordsFromNavToCanvas(this.navCoords, "fix"); + + renderer.drawImage({ + rect : [coords[0]-12, coords[1]-12, 24, 24], + texture : this.circleTexture, + color : [255,0,0,255], //white point is multiplied by red color so resulting point will be red + depth : coords[2], + depthTest : false, + blend : true //point texture has alpha channel so blend is needed + }); + } + + break; + + case 1: //line + case 2: //track + + if (this.navCoords) { + points = []; + + for (i = 0, li = this.navCoords.length; i < li; i++) { + points.push(map.convertCoordsFromNavToCanvas(this.navCoords[i], "fix")); + } + + if (li > 1) { + renderer.drawLineString({ + points : points, + size : 5.0, + color : [0,0,0,255], + depthTest : false, + //depthTest : true, + //depthOffset : [-0.01,0,0], + blend : false + }); + + renderer.drawLineString({ + points : points, + size : 2.0, + color : [255,0,0,255], + depthTest : false, + //depthTest : true, + //depthOffset : [-0.01,0,0], + blend : false + }); + } + + for (i = 0; i < li; i++) { + coords = points[i]; + + renderer.drawImage({ + rect : [coords[0]-12, coords[1]-12, 24, 24], + texture : this.circleTexture, + color : [255,0,0,255], //white point is multiplied by red color so resulting point will be red + depth : coords[2], + depthTest : false, + blend : true //point texture has alpha channel so blend is needed + }); + } + } + + + } + + +}; + UIControlMeasure.prototype.updateLink = function() { /* var linkValue = this.browser.getLinkWithCurrentPos(); From 7d6813db3722640747f69764bc70c59f3c485e7c Mon Sep 17 00:00:00 2001 From: David Levinsky Date: Tue, 24 Jul 2018 08:44:42 +0200 Subject: [PATCH 02/20] measuring tool now supports length an track length including geodesic --- src/browser/browser.css | 8 +- src/browser/browser.js | 2 +- src/browser/interface.js | 4 +- src/browser/ui/control/measure.js | 213 ++++++++++++++++++++++++------ src/core/interface.js | 2 +- src/core/map/convert.js | 39 +++++- src/core/map/interface.js | 5 + 7 files changed, 226 insertions(+), 47 deletions(-) diff --git a/src/browser/browser.css b/src/browser/browser.css index a4af428e..fe19e0e8 100755 --- a/src/browser/browser.css +++ b/src/browser/browser.css @@ -556,10 +556,14 @@ left: 0px; display: none; border-radius: 2px; - border: 2px solid rgba(255, 255, 255, 0.47); + background-color: #aaa; border: solid 1px #000; white-space: nowrap; - padding: 1px 3px; + padding: 3px 1px 3px 3px; +} + +.vts-measure-compute div { + margin-right: 2px; } /* diff --git a/src/browser/browser.js b/src/browser/browser.js index 6190fcba..f732c9d1 100755 --- a/src/browser/browser.js +++ b/src/browser/browser.js @@ -108,7 +108,7 @@ Browser.prototype.getControlMode = function() { Browser.prototype.on = function(name, listener) { - this.core.on(name, listener); + return this.core.on(name, listener); }; diff --git a/src/browser/interface.js b/src/browser/interface.js index cc57a856..8eafc31e 100755 --- a/src/browser/interface.js +++ b/src/browser/interface.js @@ -95,8 +95,8 @@ BrowserInterface.prototype.destroyMap = function() { BrowserInterface.prototype.on = function(eventName, call) { if (this.killed) return; - this.core.on(eventName, call); - return this; + return this.core.on(eventName, call); + //return this; }; diff --git a/src/browser/ui/control/measure.js b/src/browser/ui/control/measure.js index f04e796f..d5fab795 100644 --- a/src/browser/ui/control/measure.js +++ b/src/browser/ui/control/measure.js @@ -24,7 +24,7 @@ var UIControlMeasure = function(ui, visible, visibleLock) { + '
' + '
' + '
' - + '' + + '' + '
' + '
' + '
Position
' @@ -33,6 +33,7 @@ var UIControlMeasure = function(ui, visible, visibleLock) { + '
Area
' + '
Volume
' + '
Clear Log
' + + '
Metric Units ON
' + '
' + '
' + '
' @@ -40,7 +41,7 @@ var UIControlMeasure = function(ui, visible, visibleLock) { + '
' + '
' - + '
' + + '
' + '
Undo
' + '
Compute
' + '
' @@ -58,6 +59,14 @@ var UIControlMeasure = function(ui, visible, visibleLock) { this.buttonOn.on('dblclick', this.onDoNothing.bind(this)); this.info = this.control.getElement('vts-measure-info'); + this.compute = this.control.getElement('vts-measure-buttons'); + + var computeButton = this.control.getElement('vts-measure-undo'); + computeButton.on('click', this.onCompute.bind(this, 0)); + computeButton.on('dblclick', this.onDoNothing.bind(this)); + computeButton = this.control.getElement('vts-measure-compute'); + computeButton.on('click', this.onCompute.bind(this, 1)); + computeButton.on('dblclick', this.onDoNothing.bind(this)); var clearButton = this.control.getElement('vts-measure-clear'); clearButton.on('click', this.onClear.bind(this)); @@ -78,12 +87,20 @@ var UIControlMeasure = function(ui, visible, visibleLock) { toolButton = this.control.getElement('vts-measure-volume'); toolButton.on('click', this.onTool.bind(this, 4)); toolButton.on('dblclick', this.onDoNothing.bind(this)); + toolButton = this.control.getElement('vts-measure-metric'); + toolButton.on('click', this.onTool.bind(this, 5)); + toolButton.on('dblclick', this.onDoNothing.bind(this)); + this.metricButton = toolButton; + this.measuring = false; this.counter = 1; + this.renderCounter = 1; this.lastCoords = null; this.navCoords = null; this.tool = 0; + this.metric = true; + this.mapUpdateDestructor = null; this.listPanel = this.control.getElement('vts-measure-text-holder'); this.list = this.control.getElement('vts-measure-text-input'); @@ -109,6 +126,7 @@ UIControlMeasure.prototype.onDoNothing = function(event) { dom.stopPropagation(event); }; + UIControlMeasure.prototype.onMouseLeave = function(event) { this.info.setStyle('display', 'none'); }; @@ -141,7 +159,7 @@ UIControlMeasure.prototype.onMouseClick = function(event) { return; } - var i, li, res, str, m2ft = 3.28084, km2mi = 0.621371; + var i, li, res, str; var space = ' '; for (i = 0, li = ('' + this.counter).length; i < li; i++) { @@ -153,7 +171,7 @@ UIControlMeasure.prototype.onMouseClick = function(event) { clickCoords = map.convertCoordsFromNavToPublic(clickCoords, 'fix'); str = '#' + this.counter + ' position: '; - str += '\n' + space + clickCoords[0].toFixed(7) + ', ' + clickCoords[1].toFixed(7) + ', ' + clickCoords[2].toFixed(2) + 'm/' + (clickCoords[2]*m2ft).toFixed(2) + 'ft'; + str += '\n' + space + clickCoords[0].toFixed(7) + ', ' + clickCoords[1].toFixed(7) + ', ' + this.getTextNumber(clickCoords[2]); this.counter++; } else if (this.tool == 1) { @@ -162,45 +180,38 @@ UIControlMeasure.prototype.onMouseClick = function(event) { clickCoords = map.convertCoordsFromNavToPublic(clickCoords, 'fix'); str = '#' + this.counter + ' length: '; - str += '\n' + space + 'p1: ' + clickCoords[0].toFixed(7) + ', ' + clickCoords[1].toFixed(7) + ', ' + clickCoords[2].toFixed(2) + 'm/' + (clickCoords[2]*m2ft).toFixed(2) + 'ft'; + str += '\n' + space + 'p1: ' + clickCoords[0].toFixed(7) + ', ' + clickCoords[1].toFixed(7) + ', ' + this.getTextNumber(clickCoords[2]); } else { this.navCoords.push(clickCoords); clickCoords = map.convertCoordsFromNavToPublic(clickCoords, 'fix'); - str = space + 'p2: ' + clickCoords[0].toFixed(7) + ', ' + clickCoords[1].toFixed(7) + ', ' + clickCoords[2].toFixed(2) + 'm/' + (clickCoords[2]*m2ft).toFixed(2) + 'ft'; + str = space + 'p2: ' + clickCoords[0].toFixed(7) + ', ' + clickCoords[1].toFixed(7) + ', ' + this.getTextNumber(clickCoords[2]); + str += '\n' + space + '------------------------'; res = map.getDistance(this.lastCoords, clickCoords, false, true); - str += '\n' + space + 'great-circle distance: '; - - if (res[0] >= 100000) { - str += '' + (res[0]*0.001).toFixed(2) + 'km/' + (res[0]*0.001*km2mi).toFixed(2) + 'mi'; - } else { - str += '' + res[0].toFixed(2) + 'm/' + (res[0]*m2ft).toFixed(2) + 'ft'; - } - - str += '\n' + space + 'elevation difference: ' + (clickCoords[2] - this.lastCoords[2]).toFixed(2) + 'm/' + (((clickCoords[2] - this.lastCoords[2]))*m2ft).toFixed(2) + 'ft'; - str += '\n' + space + 'euclidean distance: '; - - if (res[2] >= 100000) { - str += '' + (res[2]*0.001).toFixed(2) + 'km/' + (res[2]*0.001*km2mi).toFixed(2) + 'mi'; - } else { - str += '' + res[2].toFixed(2) + 'm/' + (res[2]*m2ft).toFixed(2) + 'ft'; - } + str += '\n' + space + 'great-circle distance: ' + this.getTextNumber(res[0]); + str += '\n' + space + 'euclidean distance: ' + this.getTextNumber(res[2]); + str += '\n' + space + 'elevation difference: ' + this.getTextNumber(clickCoords[2] - this.lastCoords[2]); + str += '\n' + space + 'azimuth: ' + res[1].toFixed(2) + ' deg'; this.counter++; } } else if (this.tool == 2) { + if (this.renderCounter != this.counter) { + this.renderCounter = this.counter; + this.onTool(2); + } + if (!this.navCoords) { this.navCoords = [clickCoords]; clickCoords = map.convertCoordsFromNavToPublic(clickCoords, 'fix'); - str = '#' + this.counter + ' track length: '; - str += '\n' + space + 'p1: ' + clickCoords[0].toFixed(7) + ', ' + clickCoords[1].toFixed(7) + ', ' + clickCoords[2].toFixed(2) + 'm/' + (clickCoords[2]*m2ft).toFixed(2) + 'ft'; + str += '\n' + space + 'p1: ' + clickCoords[0].toFixed(7) + ', ' + clickCoords[1].toFixed(7) + ', ' + this.getTextNumber(clickCoords[2]); } else { this.navCoords.push(clickCoords); clickCoords = map.convertCoordsFromNavToPublic(clickCoords, 'fix'); - str = space + 'p' + this.navCoords.length + ': ' + clickCoords[0].toFixed(7) + ', ' + clickCoords[1].toFixed(7) + ', ' + clickCoords[2].toFixed(2) + 'm/' + (clickCoords[2]*m2ft).toFixed(2) + 'ft'; + str = space + 'p' + this.navCoords.length + ': ' + clickCoords[0].toFixed(7) + ', ' + clickCoords[1].toFixed(7) + ', ' + this.getTextNumber(clickCoords[2]); } } @@ -213,6 +224,7 @@ UIControlMeasure.prototype.onMouseClick = function(event) { } }; + UIControlMeasure.prototype.onMouseMove = function(event) { var map = this.browser.getMap(); if (!map) { @@ -229,7 +241,7 @@ UIControlMeasure.prototype.onMouseMove = function(event) { clickCoords = map.convertCoordsFromNavToPublic(clickCoords, 'fix'); - var str = clickCoords[0].toFixed(7) + ', ' + clickCoords[1].toFixed(7) + ', ' + clickCoords[2].toFixed(2) + 'm'; + var str = clickCoords[0].toFixed(7) + ', ' + clickCoords[1].toFixed(7) + ', ' + this.getTextNumber(clickCoords[2]); coords[0] -= this.divRect.left; coords[1] -= this.divRect.top; @@ -240,6 +252,7 @@ UIControlMeasure.prototype.onMouseMove = function(event) { this.info.setHtml(str); }; + UIControlMeasure.prototype.onSwitch = function() { this.measuring = !this.measuring; @@ -254,7 +267,7 @@ UIControlMeasure.prototype.onSwitch = function() { mapElement.on('mousemove', this.onMouseMoveCall); mapElement.on('mouseleave', this.onMouseLeaveCall); mapElement.on('click', this.onMouseClickCall); - this.browser.on('map-update', this.onMapUpdateCall); + this.mapUpdateDestructor = this.browser.on('map-update', this.onMapUpdateCall); } else { this.buttonOn.setStyle('display', 'none'); @@ -263,23 +276,114 @@ UIControlMeasure.prototype.onSwitch = function() { mapElement.off('mousemove', this.onMouseMoveCall); mapElement.off('mouseleave', this.onMouseLeaveCall); mapElement.off('click', this.onMouseClickCall); - this.browser.off('map-update', this.onMapUpdateCall); + + if (this.mapUpdateDestructor) { + this.mapUpdateDestructor(); + } + } + + this.onTool(this.tool); + + this.compute.setStyle('display', 'none'); + + var map = this.browser.getMap(); + if (map) { + map.redraw(); } - this.updateLink(); this.update(); }; + UIControlMeasure.prototype.onTool = function(tool) { + if (tool == 5) { + this.metric = !this.metric; + this.metricButton.setHtml(this.metric ? 'Metric Units ON' : 'Metric Units OFF'); + return; + } + this.tool = tool; this.navCoords = null; + this.compute.setStyle('display', 'none'); + var map = this.browser.getMap(); if (map) { map.redraw(); } }; + +UIControlMeasure.prototype.onCompute = function(button) { + if (!this.navCoords) { + return; + } + + var map = this.browser.getMap(); + if (!map) { + return; + } + + var str; + + if (button == 0) { + this.navCoords.pop(); + } else { + if (this.tool == 2) { + + var distance = 0; + var distance2 = 0, i, li, coords, coords2, res; + var emin = Number.POSITIVE_INFINITY; + var emax = Number.NEGATIVE_INFINITY; + + var space = ' '; + + for (i = 0, li = ('' + this.counter).length; i < li; i++) { + space += ' '; + } + + str = space + '------------------------'; + + for (i = 0, li = this.navCoords.length; i < li; i++) { + coords = map.convertCoordsFromNavToPublic(this.navCoords[i], 'fix'); + + if (coords[2] > emax) { + emax = coords[2]; + } + + if (coords[2] < emin) { + emin = coords[2]; + } + } + + for (i = 0, li = this.navCoords.length - 1; i < li; i++) { + coords = map.convertCoordsFromNavToPublic(this.navCoords[i], 'fix'); + coords2 = map.convertCoordsFromNavToPublic(this.navCoords[i+1], 'fix'); + res = map.getDistance(coords, coords2, false, true); + distance += res[0]; + distance2 += res[2]; + } + + str += '\n' + space + 'great-circle distance: ' + this.getTextNumber(distance); + str += '\n' + space + 'euclidean distance: ' + this.getTextNumber(distance2); + str += '\n' + space + 'max elevation: ' + this.getTextNumber(emax); + str += '\n' + space + 'min elevation: ' + this.getTextNumber(emin); + str += '\n' + space + 'elevation difference: ' + this.getTextNumber(emax - emin); + + this.counter++; + } + } + + if (str) { + var listElement = this.list.getElement(); + listElement.value += str + '\n'; + listElement.scrollTop = listElement.scrollHeight; //scroll list to the last line + } + + map.redraw(); +}; + + UIControlMeasure.prototype.onClear = function() { this.counter = 1; this.lastCoords = null; @@ -289,12 +393,15 @@ UIControlMeasure.prototype.onClear = function() { listElement.value = ''; listElement.scrollTop = 0; + this.compute.setStyle('display', 'none'); + var map = this.browser.getMap(); if (map) { map.redraw(); } }; + UIControlMeasure.prototype.update = function() { //var button = this.control.getElement('vts-measure-button'); @@ -359,8 +466,20 @@ UIControlMeasure.prototype.onMapUpdate = function() { } if (li > 1) { + + var points2 = [], points3 = [], tmp; + + for (i = 0, li = this.navCoords.length - 1; i < li; i++) { + tmp = map.getGeodesicLinePoints(this.navCoords[i], this.navCoords[i+1]); + points2 = points2.concat(tmp); + } + + for (i = 0, li = points2.length; i < li; i++) { + points3.push(map.convertCoordsFromNavToCanvas(points2[i], "fix")); + } + renderer.drawLineString({ - points : points, + points : points3, size : 5.0, color : [0,0,0,255], depthTest : false, @@ -370,7 +489,7 @@ UIControlMeasure.prototype.onMapUpdate = function() { }); renderer.drawLineString({ - points : points, + points : points3, size : 2.0, color : [255,0,0,255], depthTest : false, @@ -380,7 +499,7 @@ UIControlMeasure.prototype.onMapUpdate = function() { }); } - for (i = 0; i < li; i++) { + for (i = 0, li = points.length; i < li; i++) { coords = points[i]; renderer.drawImage({ @@ -398,16 +517,32 @@ UIControlMeasure.prototype.onMapUpdate = function() { } -}; + if (this.tool == 2 && points) { + if (points.length < 2 || this.renderCounter != this.counter) { + this.compute.setStyle('display', 'none'); + return; + } + + coords = points[points.length - 1]; + coords[0] -= this.divRect.left; + coords[1] -= this.divRect.top; + + this.compute.setStyle('display', 'block'); + this.compute.setStyle('left', (coords[0]+20)+'px'); + this.compute.setStyle('top', (coords[1]+10)+'px'); + } -UIControlMeasure.prototype.updateLink = function() { - /* - var linkValue = this.browser.getLinkWithCurrentPos(); - if (this.list.getElement().value != linkValue) { - this.list.getElement().value = linkValue; - }*/ }; +UIControlMeasure.prototype.getTextNumber = function(value) { + var m2ft = 3.28084, km2mi = 0.621371; + + if (value >= 100000) { + return (this.metric) ? (value*0.001).toFixed(2) + 'km' : (value*0.001*km2mi).toFixed(2) + 'mi'; + } else { + return (this.metric) ? (value).toFixed(2) + 'm' : (value*m2ft).toFixed(2) + 'ft'; + } +}; export default UIControlMeasure; diff --git a/src/core/interface.js b/src/core/interface.js index 42ae10ed..5d79909e 100755 --- a/src/core/interface.js +++ b/src/core/interface.js @@ -81,7 +81,7 @@ CoreInterface.prototype.getProj4 = function() { CoreInterface.prototype.on = function(eventName, call) { if (!this.core) { return null; } - this.core.on(eventName, call); + return this.core.on(eventName, call); }; CoreInterface.prototype.callListener = function(name, event) { diff --git a/src/core/map/convert.js b/src/core/map/convert.js index 2e490433..59c2fa3d 100755 --- a/src/core/map/convert.js +++ b/src/core/map/convert.js @@ -34,8 +34,7 @@ MapConvert.prototype.movePositionCoordsTo = function(position, azimuth, distance position.setCoords2([coords[0] + (forward[0]*distance), coords[1] + (forward[1]*distance)]); } else { - var geod = new GeographicLib.Geodesic.Geodesic(navigationSrsInfo['a'], - (navigationSrsInfo['a'] / navigationSrsInfo['b']) - 1.0); + var geod = this.measure.getGeodesic(); var r = geod.Direct(coords[1], coords[0], azimuth, distance); position.setCoords2([r.lon2, r.lat2]); @@ -296,5 +295,41 @@ MapConvert.prototype.convertCoordsFromPhysToNav = function(coords, mode, lod) { return coords; }; +MapConvert.prototype.getGeodesicLinePoints = function(coords, coords2, height, density) { + var geod, r, length, azimuth, minStep, d; + var navigationSrsInfo = this.measure.navigationSrsInfo; + var dx = coords2[0] - coords[0]; + var dy = coords2[1] - coords[1]; + var dz = coords2[2] - coords[2]; + + if (this.isProjected) { + length = Math.sqrt(dx*dx + dy*dy + dz*dz); + minStep = 1000000; //just big number + } else { + geod = this.measure.getGeodesic(); + r = geod.Inverse(coords[1], coords[0], coords2[1], coords2[0]); + length = r.s12; + azimuth = r.azi1; + minStep = 10 * ((navigationSrsInfo['a'] * 2 * Math.PI) / 4007.5); //aprox 100km for earth + } + + var points = [coords]; + var distance = minStep; + + for (;distance < length; distance += minStep) { + d = distance / length; + + if (this.isProjected) { + points.push([ coords[0] + dx * d, coords[1] + dy * d, coords[2] + dz * d ]); + } else { + r = geod.Direct(coords[1], coords[0], azimuth, distance); + points.push([r.lon2, r.lat2, coords[2] + dz * d]); + } + } + + points.push(coords2); + + return points; +}; export default MapConvert; diff --git a/src/core/map/interface.js b/src/core/map/interface.js index 616dea9b..bcfec4b5 100755 --- a/src/core/map/interface.js +++ b/src/core/map/interface.js @@ -245,6 +245,11 @@ MapInterface.prototype.movePositionCoordsTo = function(position, azimuth, distan }; +MapInterface.prototype.getGeodesicLinePoints = function(coords, coords2, height, density) { + return this.map.convert.getGeodesicLinePoints(coords, coords2, height, density); +}; + + MapInterface.prototype.getSurfaceHeight = function(coords, precision) { return this.map.measure.getSurfaceHeight(coords, this.map.measure.getOptimalHeightLodBySampleSize(coords, precision)); }; From 269c9272aa9b835f288f78fdcf1f7c25642f3962 Mon Sep 17 00:00:00 2001 From: David Levinsky Date: Tue, 24 Jul 2018 10:13:42 +0200 Subject: [PATCH 03/20] fixed lines --- src/browser/ui/control/measure.js | 9 ++++++--- src/core/renderer/draw.js | 1 + 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/src/browser/ui/control/measure.js b/src/browser/ui/control/measure.js index d5fab795..127acec6 100644 --- a/src/browser/ui/control/measure.js +++ b/src/browser/ui/control/measure.js @@ -170,7 +170,8 @@ UIControlMeasure.prototype.onMouseClick = function(event) { this.navCoords = clickCoords; clickCoords = map.convertCoordsFromNavToPublic(clickCoords, 'fix'); - str = '#' + this.counter + ' position: '; + str = '------------------------------------------------------\n'; + str += '#' + this.counter + ' Position: '; str += '\n' + space + clickCoords[0].toFixed(7) + ', ' + clickCoords[1].toFixed(7) + ', ' + this.getTextNumber(clickCoords[2]); this.counter++; @@ -179,7 +180,8 @@ UIControlMeasure.prototype.onMouseClick = function(event) { this.navCoords = [clickCoords]; clickCoords = map.convertCoordsFromNavToPublic(clickCoords, 'fix'); - str = '#' + this.counter + ' length: '; + str = '------------------------------------------------------\n'; + str += '#' + this.counter + ' Length: '; str += '\n' + space + 'p1: ' + clickCoords[0].toFixed(7) + ', ' + clickCoords[1].toFixed(7) + ', ' + this.getTextNumber(clickCoords[2]); } else { this.navCoords.push(clickCoords); @@ -205,7 +207,8 @@ UIControlMeasure.prototype.onMouseClick = function(event) { if (!this.navCoords) { this.navCoords = [clickCoords]; clickCoords = map.convertCoordsFromNavToPublic(clickCoords, 'fix'); - str = '#' + this.counter + ' track length: '; + str = '------------------------------------------------------\n'; + str += '#' + this.counter + ' Track Length: '; str += '\n' + space + 'p1: ' + clickCoords[0].toFixed(7) + ', ' + clickCoords[1].toFixed(7) + ', ' + this.getTextNumber(clickCoords[2]); } else { this.navCoords.push(clickCoords); diff --git a/src/core/renderer/draw.js b/src/core/renderer/draw.js index e222c019..a13ffa3e 100755 --- a/src/core/renderer/draw.js +++ b/src/core/renderer/draw.js @@ -273,6 +273,7 @@ RendererDraw.prototype.drawLineString = function(points, screenSpace, size, colo gl.depthMask(false); } + gl.disable(gl.STENCIL_TEST); gl.disable(gl.CULL_FACE); } From 917dc80ab47787c61ba574a496c2755a8a9a40bd Mon Sep 17 00:00:00 2001 From: David Levinsky Date: Wed, 25 Jul 2018 17:38:45 +0200 Subject: [PATCH 04/20] basic area computation --- src/browser/ui/control/measure.js | 53 +++++++++++++++++++++++++++---- src/core/map/geodata-builder.js | 16 +++++++++- src/core/map/geodata-geometry.js | 49 +++++++++++++++++++++++++++- 3 files changed, 110 insertions(+), 8 deletions(-) diff --git a/src/browser/ui/control/measure.js b/src/browser/ui/control/measure.js index 127acec6..50cd0837 100644 --- a/src/browser/ui/control/measure.js +++ b/src/browser/ui/control/measure.js @@ -214,6 +214,24 @@ UIControlMeasure.prototype.onMouseClick = function(event) { this.navCoords.push(clickCoords); clickCoords = map.convertCoordsFromNavToPublic(clickCoords, 'fix'); + str = space + 'p' + this.navCoords.length + ': ' + clickCoords[0].toFixed(7) + ', ' + clickCoords[1].toFixed(7) + ', ' + this.getTextNumber(clickCoords[2]); + } + } else if (this.tool == 3) { + if (this.renderCounter != this.counter) { + this.renderCounter = this.counter; + this.onTool(3); + } + + if (!this.navCoords) { + this.navCoords = [clickCoords]; + clickCoords = map.convertCoordsFromNavToPublic(clickCoords, 'fix'); + str = '------------------------------------------------------\n'; + str += '#' + this.counter + ' Area: '; + str += '\n' + space + 'p1: ' + clickCoords[0].toFixed(7) + ', ' + clickCoords[1].toFixed(7) + ', ' + this.getTextNumber(clickCoords[2]); + } else { + this.navCoords.push(clickCoords); + clickCoords = map.convertCoordsFromNavToPublic(clickCoords, 'fix'); + str = space + 'p' + this.navCoords.length + ': ' + clickCoords[0].toFixed(7) + ', ' + clickCoords[1].toFixed(7) + ', ' + this.getTextNumber(clickCoords[2]); } } @@ -327,19 +345,19 @@ UIControlMeasure.prototype.onCompute = function(button) { return; } - var str; + var str, i, li, space; - if (button == 0) { + if (button == 0) { //undo button this.navCoords.pop(); - } else { + } else { //compute button if (this.tool == 2) { var distance = 0; - var distance2 = 0, i, li, coords, coords2, res; + var distance2 = 0, coords, coords2, res; var emin = Number.POSITIVE_INFINITY; var emax = Number.NEGATIVE_INFINITY; - var space = ' '; + space = ' '; for (i = 0, li = ('' + this.counter).length; i < li; i++) { space += ' '; @@ -375,6 +393,28 @@ UIControlMeasure.prototype.onCompute = function(button) { this.counter++; } + + if (this.tool == 3) { + var geodata = map.createGeodata(); + geodata.addPolygon3(this.navCoords, [], null, 'fix', {}, 'tmp-polygon'); + geodata.processHeights('node-by-lod', 62, (function(){ + + space = ' '; + + for (i = 0, li = ('' + this.counter).length; i < li; i++) { + space += ' '; + } + + str = space + '------------------------'; + + var poly = geodata.extractGeometry('tmp-polygon'); + + var area = poly.getSurfaceArea() + + str += '\n' + space + 'area: ' + area + 'm^2'; + + }).bind(this)); + } } if (str) { @@ -460,6 +500,7 @@ UIControlMeasure.prototype.onMapUpdate = function() { case 1: //line case 2: //track + case 3: //area if (this.navCoords) { points = []; @@ -520,7 +561,7 @@ UIControlMeasure.prototype.onMapUpdate = function() { } - if (this.tool == 2 && points) { + if ((this.tool == 2 || this.tool == 3) && points) { if (points.length < 2 || this.renderCounter != this.counter) { this.compute.setStyle('display', 'none'); return; diff --git a/src/core/map/geodata-builder.js b/src/core/map/geodata-builder.js index a8684581..b64fde82 100644 --- a/src/core/map/geodata-builder.js +++ b/src/core/map/geodata-builder.js @@ -1463,7 +1463,8 @@ MapGeodataBuilder.prototype.extractGeometry = function(id) { var group = this.groups[i]; var groupPoints = group.points; - var groupLines = group.lines, j, lj; + var groupLines = group.lines; + var groupPolygons = group.polygons, j, lj; //get group bbox for (j = 0, lj = groupPoints.length; j < lj; j++) { @@ -1477,6 +1478,12 @@ MapGeodataBuilder.prototype.extractGeometry = function(id) { feature = groupLines[j]; } } + + for (j = 0, lj = groupPolygons.length; j < lj; j++) { + if (groupPolygons[j].id == id) { + feature = groupPolygons[j]; + } + } } if (feature) { @@ -1529,6 +1536,13 @@ MapGeodataBuilder.prototype.extractGeometry = function(id) { } return new MapGeodataGeometry(this.map, {'type': 'line-geometry', 'id':feature.id, 'geometryBuffer': vertexBuffer, 'indicesBuffer': indexBuffer }); + } else if (feature.vertices) { + + /*feature.vertices = featureVertices; + feature.surface = featureSurface; + feature.borders = featureBorders;*/ + + return new MapGeodataGeometry(this.map, {'type': 'polygon-geometry', 'id':feature.id, 'geometryBuffer': feature.vertices, 'surface': feature.surface }); } return; diff --git a/src/core/map/geodata-geometry.js b/src/core/map/geodata-geometry.js index 1ba2adc2..708056b7 100644 --- a/src/core/map/geodata-geometry.js +++ b/src/core/map/geodata-geometry.js @@ -25,6 +25,9 @@ var MapGeodataGeometry = function(map, data) { break; case 'polygon-geometry': this.type = 3; + this.vertexBuffer = this.data.geometryBuffer; + this.surface = this.data.surface; + this.borders = this.data.borders; break; } }; @@ -42,12 +45,16 @@ MapGeodataGeometry.prototype.getElement = function(index) { switch(this.type) { case 1: return [v[i], v[i+1], v[i+2]]; //point case 2: return [[v[i], v[i+1], v[i+2]], [v[i+3], v[i+4], v[i+5]]]; //line + case 3: + var s = this.surface; + return [[v[s[i]], v[s[i+1]], v[s[i+2]]], [v[s[i+3]], v[s[i+4]], v[s[i+5]]], [v[s[i+6]], v[s[i+7]], v[s[i+8]]]]; //polygon } }; MapGeodataGeometry.prototype.getElements = function(pathIndex) { switch(this.type) { - case 1: return this.vertexBuffer.length / 3; //point + case 1: //point + case 3: return this.vertexBuffer.length / 3; //polygon case 2: //line pathIndex = pathIndex || 0; @@ -272,6 +279,46 @@ MapGeodataGeometry.prototype.getPathsCount = function() { }; +MapGeodataGeometry.prototype.getSurfaceArea = function() { + if (this.type != 3) { + return 0; + } + + if (!this.surfaceArea) { + var v = this.vertexBuffer, s = this.surface; + var p1, p2, p3, l1, l2, l3, dx, dy, dz, perimeter, area; + + this.surfaceArea = 0; + for (var i = 0, li = s.length; i < li; i+= 3) { + p1 = v[s[i]]; + p2 = v[s[i+1]]; + p3 = v[s[i+2]]; + + dx = p2[0] - p1[0]; + dy = p2[1] - p1[1]; + dz = p2[2] - p1[2]; + l1 = Math.sqrt(dx*dx + dy*dy + dz*dz); + + dx = p2[0] - p3[0]; + dy = p2[1] - p3[1]; + dz = p2[2] - p3[2]; + l2 = Math.sqrt(dx*dx + dy*dy + dz*dz); + + dx = p3[0] - p1[0]; + dy = p3[1] - p1[1]; + dz = p3[2] - p1[2]; + l3 = Math.sqrt(dx*dx + dy*dy + dz*dz); + + //Heron's formula + perimeter = (l1 + l2 + l3)/2; + area = Math.sqrt(perimeter*((perimeter-l1)*(perimeter-l2)*(perimeter-l3))); + + this.surfaceArea += area; + } + } + + return this.surfaceArea; +}; export default MapGeodataGeometry; From 9bfe8590196a75226d9c99bae3db455a33cbafbc Mon Sep 17 00:00:00 2001 From: David Levinsky Date: Fri, 27 Jul 2018 16:43:59 +0200 Subject: [PATCH 05/20] are report for non-metric units, fixed heigth measrure --- package.json | 2 +- src/browser/ui/control/measure.js | 37 ++++++++++++++++++++++++++++--- src/core/core.js | 2 +- src/core/map/measure.js | 16 +++++++++---- 4 files changed, 48 insertions(+), 9 deletions(-) diff --git a/package.json b/package.json index 1ce0f911..2beeb4b8 100755 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "vts-browser-js", - "version": "2.15.16", + "version": "2.15.17", "description": "JavaScript WebGL 3D maps rendering engine", "main": "src/browser/index.js", "scripts": { diff --git a/src/browser/ui/control/measure.js b/src/browser/ui/control/measure.js index 50cd0837..a52ec3c7 100644 --- a/src/browser/ui/control/measure.js +++ b/src/browser/ui/control/measure.js @@ -33,7 +33,7 @@ var UIControlMeasure = function(ui, visible, visibleLock) { + '
Area
' + '
Volume
' + '
Clear Log
' - + '
Metric Units ON
' + + '
Units: Meters
' + '
' + '' + '' @@ -319,7 +319,7 @@ UIControlMeasure.prototype.onSwitch = function() { UIControlMeasure.prototype.onTool = function(tool) { if (tool == 5) { this.metric = !this.metric; - this.metricButton.setHtml(this.metric ? 'Metric Units ON' : 'Metric Units OFF'); + this.metricButton.setHtml(this.metric ? 'Units: Meters' : 'Units: Feets'); return; } @@ -411,7 +411,34 @@ UIControlMeasure.prototype.onCompute = function(button) { var area = poly.getSurfaceArea() - str += '\n' + space + 'area: ' + area + 'm^2'; + if (this.metric) { + str += '\n' + space + 'area: ' + area.toFixed(2) + ' m\u00B2'; + + if (area > 100) { + str += '\n' + space + ' ' + (area/100).toFixed(2) + ' ares'; + } + + if (area > 10000) { + str += '\n' + space + ' ' + (area/10000).toFixed(2) + ' hectares'; + } + + if (area > 1000000) { + str += '\n' + space + ' ' + (area/1000000).toFixed(2) + ' km\u00B2'; + } + } else { + //str += '\n' + space + 'area: ' + (area / 0.09290304).toFixed(2) + ' ft²'; + str += '\n' + space + 'area: ' + (area / 0.83612736).toFixed(2) + ' yd\u00B2'; + + if ((area / 4046.8564224) >= 1) { + str += '\n' + space + ' ' + (area / 4046.8564224).toFixed(2) + ' acres'; + } + + if ((area / 2589988.110346) >= 1) { + str += '\n' + space + ' ' + (area / 2589988.110346).toFixed(2) + ' mi\u00B2'; + } + } + + this.counter++; }).bind(this)); } @@ -522,6 +549,10 @@ UIControlMeasure.prototype.onMapUpdate = function() { points3.push(map.convertCoordsFromNavToCanvas(points2[i], "fix")); } + if (this.tool == 3) { + points3.push(map.convertCoordsFromNavToCanvas(points2[0], "fix")); + } + renderer.drawLineString({ points : points3, size : 5.0, diff --git a/src/core/core.js b/src/core/core.js index 51ab191c..5a401f83 100755 --- a/src/core/core.js +++ b/src/core/core.js @@ -508,7 +508,7 @@ string getCoreVersion() */ function getCoreVersion(full) { - return (full ? 'Core: ' : '') + '2.15.16'; + return (full ? 'Core: ' : '') + '2.15.17'; } diff --git a/src/core/map/measure.js b/src/core/map/measure.js index 81227a98..12a72fbd 100755 --- a/src/core/map/measure.js +++ b/src/core/map/measure.js @@ -27,6 +27,12 @@ var MapMeasure = function(map) { MapMeasure.prototype.getSurfaceHeight = function(coords, lod, storeStats, node, nodeCoords, coordsArray, useNodeOnly) { + var tree = this.map.tree; + + if (tree.surfaceSequence.length == 0) { + return [0, true, true, null, null, null]; + } + if (!node) { var result = this.getSpatialDivisionNode(coords); node = result[0]; @@ -41,8 +47,6 @@ MapMeasure.prototype.getSurfaceHeight = function(coords, lod, storeStats, node, return this.getSurfaceHeightNodeOnly(null, lod + 8, storeStats, lod, null, node, nodeCoords, coordsArray); } - var tree = this.map.tree; - if (node != null && lod !== null) { var root = tree.findSurfaceTile(node.id); @@ -136,6 +140,12 @@ MapMeasure.prototype.getSurfaceHeight = function(coords, lod, storeStats, node, MapMeasure.prototype.getSurfaceHeightNodeOnly = function(coords, lod, storeStats, statsLod, deltaSample, node, nodeCoords, coordsArray) { var arrayRes, height, stats = this.map.stats; + + var tree = this.map.tree; + + if (tree.surfaceSequence.length == 0) { + return [0, true, true, null, null, null]; + } if (!deltaSample) { if (!node) { @@ -203,8 +213,6 @@ MapMeasure.prototype.getSurfaceHeightNodeOnly = function(coords, lod, storeStats //blend values } - var tree = this.map.tree; - if (node != null && lod !== null) { var root = tree.findSurfaceTile(node.id); From 54f4fbe37a7ca60200c02495d206bd42590446e6 Mon Sep 17 00:00:00 2001 From: David Levinsky Date: Fri, 27 Jul 2018 16:49:54 +0200 Subject: [PATCH 06/20] fixed height measure --- package.json | 2 +- src/core/core.js | 2 +- src/core/map/measure.js | 16 ++++++++++++---- 3 files changed, 14 insertions(+), 6 deletions(-) diff --git a/package.json b/package.json index 1ce0f911..2beeb4b8 100755 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "vts-browser-js", - "version": "2.15.16", + "version": "2.15.17", "description": "JavaScript WebGL 3D maps rendering engine", "main": "src/browser/index.js", "scripts": { diff --git a/src/core/core.js b/src/core/core.js index 51ab191c..5a401f83 100755 --- a/src/core/core.js +++ b/src/core/core.js @@ -508,7 +508,7 @@ string getCoreVersion() */ function getCoreVersion(full) { - return (full ? 'Core: ' : '') + '2.15.16'; + return (full ? 'Core: ' : '') + '2.15.17'; } diff --git a/src/core/map/measure.js b/src/core/map/measure.js index 81227a98..12a72fbd 100755 --- a/src/core/map/measure.js +++ b/src/core/map/measure.js @@ -27,6 +27,12 @@ var MapMeasure = function(map) { MapMeasure.prototype.getSurfaceHeight = function(coords, lod, storeStats, node, nodeCoords, coordsArray, useNodeOnly) { + var tree = this.map.tree; + + if (tree.surfaceSequence.length == 0) { + return [0, true, true, null, null, null]; + } + if (!node) { var result = this.getSpatialDivisionNode(coords); node = result[0]; @@ -41,8 +47,6 @@ MapMeasure.prototype.getSurfaceHeight = function(coords, lod, storeStats, node, return this.getSurfaceHeightNodeOnly(null, lod + 8, storeStats, lod, null, node, nodeCoords, coordsArray); } - var tree = this.map.tree; - if (node != null && lod !== null) { var root = tree.findSurfaceTile(node.id); @@ -136,6 +140,12 @@ MapMeasure.prototype.getSurfaceHeight = function(coords, lod, storeStats, node, MapMeasure.prototype.getSurfaceHeightNodeOnly = function(coords, lod, storeStats, statsLod, deltaSample, node, nodeCoords, coordsArray) { var arrayRes, height, stats = this.map.stats; + + var tree = this.map.tree; + + if (tree.surfaceSequence.length == 0) { + return [0, true, true, null, null, null]; + } if (!deltaSample) { if (!node) { @@ -203,8 +213,6 @@ MapMeasure.prototype.getSurfaceHeightNodeOnly = function(coords, lod, storeStats //blend values } - var tree = this.map.tree; - if (node != null && lod !== null) { var root = tree.findSurfaceTile(node.id); From fb677d83749691f63ce98c064f645ab7ee67ed1a Mon Sep 17 00:00:00 2001 From: David Levinsky Date: Mon, 6 Aug 2018 14:08:10 +0200 Subject: [PATCH 07/20] fixed glob-scr-count --- src/core/renderer/gmap.js | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/core/renderer/gmap.js b/src/core/renderer/gmap.js index 6dbcb2dc..23dda738 100644 --- a/src/core/renderer/gmap.js +++ b/src/core/renderer/gmap.js @@ -6,7 +6,7 @@ function processGMap(gpu, gl, renderer, screenPixelSize) { var ppi = 96 * (window.devicePixelRatio || 1); var screenLX = renderer.curSize[0]; var screenLY = renderer.curSize[1]; - var featureCount = (screenLX/ppi)*(screenLY/ppi)*featuresPerSquareInch; + var featureCount = Math.ceil((screenLX/ppi)*(screenLY/ppi)*featuresPerSquareInch); var i, li, top = renderer.config.mapFeaturesSortByTop; //get top features @@ -61,7 +61,7 @@ function processGMap(gpu, gl, renderer, screenPixelSize) { pp = feature[5]; - if (pp[0] < 0 || pp[0] >= screenLX || pp[1] < 0 || pp[1] >= screenLY) { + if (pp[0] < 30 || pp[0] >= (screenLX-30) || pp[1] < 30 || pp[1] >= (screenLY-30)) { featureCache[i] = null; continue; } @@ -137,6 +137,7 @@ function processGMap(gpu, gl, renderer, screenPixelSize) { c *= my; usedFeatures += a + b + c + d; + featureCount -= a + b + c + d; tileSize *= 2; } while(usedFeatures < featureCount2); From 532098ffa4b3e1d64b3c7b8d92280d7a0914c09b Mon Sep 17 00:00:00 2001 From: David Levinsky Date: Mon, 6 Aug 2018 14:32:38 +0200 Subject: [PATCH 08/20] fixed glob-scr-count --- src/core/renderer/gmap.js | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/core/renderer/gmap.js b/src/core/renderer/gmap.js index 6dbcb2dc..23dda738 100644 --- a/src/core/renderer/gmap.js +++ b/src/core/renderer/gmap.js @@ -6,7 +6,7 @@ function processGMap(gpu, gl, renderer, screenPixelSize) { var ppi = 96 * (window.devicePixelRatio || 1); var screenLX = renderer.curSize[0]; var screenLY = renderer.curSize[1]; - var featureCount = (screenLX/ppi)*(screenLY/ppi)*featuresPerSquareInch; + var featureCount = Math.ceil((screenLX/ppi)*(screenLY/ppi)*featuresPerSquareInch); var i, li, top = renderer.config.mapFeaturesSortByTop; //get top features @@ -61,7 +61,7 @@ function processGMap(gpu, gl, renderer, screenPixelSize) { pp = feature[5]; - if (pp[0] < 0 || pp[0] >= screenLX || pp[1] < 0 || pp[1] >= screenLY) { + if (pp[0] < 30 || pp[0] >= (screenLX-30) || pp[1] < 30 || pp[1] >= (screenLY-30)) { featureCache[i] = null; continue; } @@ -137,6 +137,7 @@ function processGMap(gpu, gl, renderer, screenPixelSize) { c *= my; usedFeatures += a + b + c + d; + featureCount -= a + b + c + d; tileSize *= 2; } while(usedFeatures < featureCount2); From 4f740809763e1b729ef5fd62c4477c69d2f042f5 Mon Sep 17 00:00:00 2001 From: David Levinsky Date: Thu, 9 Aug 2018 16:44:24 +0200 Subject: [PATCH 09/20] partial volume computation --- src/browser/ui/control/measure.js | 162 +++++++++++++++++++++++++++++- src/core/map/geodata-geometry.js | 5 +- src/core/map/interface.js | 9 +- 3 files changed, 167 insertions(+), 9 deletions(-) diff --git a/src/browser/ui/control/measure.js b/src/browser/ui/control/measure.js index a52ec3c7..e3ab8cc6 100644 --- a/src/browser/ui/control/measure.js +++ b/src/browser/ui/control/measure.js @@ -1,11 +1,12 @@ import Dom_ from '../../utility/dom'; import {utils as utils_} from '../../../core/utils/utils'; +import {vec3 as vec3_} from '../../../core/utils/matrix'; //get rid of compiler mess var dom = Dom_; var utils = utils_; - +var vec3 = vec3_; var UIControlMeasure = function(ui, visible, visibleLock) { this.ui = ui; @@ -216,17 +217,17 @@ UIControlMeasure.prototype.onMouseClick = function(event) { str = space + 'p' + this.navCoords.length + ': ' + clickCoords[0].toFixed(7) + ', ' + clickCoords[1].toFixed(7) + ', ' + this.getTextNumber(clickCoords[2]); } - } else if (this.tool == 3) { + } else if (this.tool == 3 || this.tool == 4) { if (this.renderCounter != this.counter) { this.renderCounter = this.counter; - this.onTool(3); + this.onTool(this.tool); } if (!this.navCoords) { this.navCoords = [clickCoords]; clickCoords = map.convertCoordsFromNavToPublic(clickCoords, 'fix'); str = '------------------------------------------------------\n'; - str += '#' + this.counter + ' Area: '; + str += '#' + this.counter + ((this.tool == 3) ? ' Area: ' : ' Volume: '); str += '\n' + space + 'p1: ' + clickCoords[0].toFixed(7) + ', ' + clickCoords[1].toFixed(7) + ', ' + this.getTextNumber(clickCoords[2]); } else { this.navCoords.push(clickCoords); @@ -442,6 +443,94 @@ UIControlMeasure.prototype.onCompute = function(button) { }).bind(this)); } + + if (this.tool == 4) { + var geodata = map.createGeodata(); + geodata.addPolygon(this.navCoords, [], null, 'fix', {}, 'tmp-polygon'); + geodata.processHeights('node-by-lod', 62, (function(){ + + if (this.navCoords.length) { + + space = ' '; + + for (i = 0, li = ('' + this.counter).length; i < li; i++) { + space += ' '; + } + + str = space + '------------------------'; + + var center = [0,0,0]; + + for (i = 0, li = this.navCoords.length; i < li; i++) { + coords = map.convertCoordsFromNavToPhys(this.navCoords[i], 'fix'); + center[0] += coords[0]; + center[1] += coords[1]; + center[2] += coords[2]; + } + + center[0] /= li; + center[1] /= li; + center[2] /= li; + + var radius = 0, dx, dy, dz, distance; + + for (i = 0, li = this.navCoords.length; i < li; i++) { + coords = map.convertCoordsFromNavToPhys(this.navCoords[i], 'fix'); + dx = (center[0] - coords[0]); + dy = (center[1] - coords[1]); + dz = (center[2] - coords[2]); + distance = Math.sqrt(dx*dx + dy*dy + dz*dz); + + if (distance > radius) { + radius = distance; + } + } + + + var poly = geodata.extractGeometry('tmp-polygon'); + + var faces = new Array(poly.getElements()); + + for (i = 0, li = faces.length; i < li; i++) { + faces[i] = poly.getElement(i); + } + + //TODO: build octree + //TODO: extract meshes and build octree + + var x, y, north, east; + + coords = map.convertCoordsFromPhysToNav(center, 'fix'); + + var ned = map.getNED(coords, false); + north = ned.direction; + east = ned.east; + + var steps = 5, l, sx, sy; + + for (y = -steps; y <= steps; y++) { + for (x = -steps; x <= steps; x++) { + + sx = (1.0 / steps) * x * radius; + sy = (1.0 / steps) * y * radius; + coords[0] = center[0] + north[0] * sy + east[0] * sx; + coords[1] = center[1] + north[1] * sy + east[1] * sx; + coords[2] = center[2] + north[2] * sy + east[2] * sx; + + res = this.hitFaces(coords, faces); + + } + + console.log("*"); + } + + } + + this.counter++; + + }).bind(this)); + } + } if (str) { @@ -453,6 +542,68 @@ UIControlMeasure.prototype.onCompute = function(button) { map.redraw(); }; +UIControlMeasure.prototype.hitFace = function(origin, dir, face) { + var EPSILON = 0.0000001; + var v1 = face[0]; + var v2 = face[1]; + var v3 = face[2]; + + var h = [0,0,0], q = [0,0,0], s; + var a,f,u,v; + var edge1 = [v2[0] - v1[0], v2[1] - v1[1], v2[2] - v1[2]]; + var edge2 = [v3[0] - v1[0], v3[1] - v1[1], v3[2] - v1[2]]; + + vec3.cross(dir, edge2, h); + a = vec3.dot(edge1, h); + + if (a > -EPSILON && a < EPSILON) { + return [false]; + } + + f = 1/a; + s = [origin[0] - v1[0], origin[1] - v1[1], origin[2] - v1[2]]; + u = f * (vec3.dot(s, h)); + + if (u < 0.0 || u > 1.0) { + return [false]; + } + + q = vec3.cross(s, edge1); + v = f * vec3.dot(dir, q); + if (v < 0.0 || u + v > 1.0) { + return [false]; + } + + // At this stage we can compute t to find out where the intersection point is on the line. + var t = f * vec3.dot(edge2, q); + //if (t > EPSILON) { // ray intersection + return [true, t]; //[origin[0] + dir[0] * t, origin[1] + dir[1] * t, origin[2] + dir[2] * t ]]; + //} else { // This means that there is a line intersection but not a ray intersection. + // return [false]; + //} +}; + + +UIControlMeasure.prototype.hitFaces = function(coords, faces) { + var dir = [0,0,0]; + vec3.normalize(coords, dir); // TODO: add support for projected systems + var hit = false, t = Number.POSITIVE_INFINITY; + + for (var i = 0, li = faces.length; i < li; i++) { + var res = this.hitFace(coords, dir, faces[i]); + + if (res[0]) { + hit = true; + + if (res[1] < t) { + t = res[1]; + } + } + } + + console.log(hit ? ("" + t.toFixed(2)) : ("N")); +}; + UIControlMeasure.prototype.onClear = function() { this.counter = 1; @@ -528,6 +679,7 @@ UIControlMeasure.prototype.onMapUpdate = function() { case 1: //line case 2: //track case 3: //area + case 4: //volume if (this.navCoords) { points = []; @@ -592,7 +744,7 @@ UIControlMeasure.prototype.onMapUpdate = function() { } - if ((this.tool == 2 || this.tool == 3) && points) { + if ((this.tool == 2 || this.tool == 3 || this.tool == 4) && points) { if (points.length < 2 || this.renderCounter != this.counter) { this.compute.setStyle('display', 'none'); return; diff --git a/src/core/map/geodata-geometry.js b/src/core/map/geodata-geometry.js index 708056b7..df3ceb3b 100644 --- a/src/core/map/geodata-geometry.js +++ b/src/core/map/geodata-geometry.js @@ -47,14 +47,15 @@ MapGeodataGeometry.prototype.getElement = function(index) { case 2: return [[v[i], v[i+1], v[i+2]], [v[i+3], v[i+4], v[i+5]]]; //line case 3: var s = this.surface; - return [[v[s[i]], v[s[i+1]], v[s[i+2]]], [v[s[i+3]], v[s[i+4]], v[s[i+5]]], [v[s[i+6]], v[s[i+7]], v[s[i+8]]]]; //polygon + var i1 = s[i], i2 = s[i+1], i3 = s[i+2]; + return [[v[i1][0], v[i1][1], v[i1][2]], [v[i2][0], v[i2][1], v[i2][2]], [v[i3][0], v[i3][1], v[i3][2]]]; //polygon } }; MapGeodataGeometry.prototype.getElements = function(pathIndex) { switch(this.type) { case 1: //point - case 3: return this.vertexBuffer.length / 3; //polygon + case 3: return this.surface.length / 3; //polygon case 2: //line pathIndex = pathIndex || 0; diff --git a/src/core/map/interface.js b/src/core/map/interface.js index bcfec4b5..bd767b59 100755 --- a/src/core/map/interface.js +++ b/src/core/map/interface.js @@ -265,8 +265,8 @@ MapInterface.prototype.getAzimuthCorrection = function(coords, coords2) { }; -MapInterface.prototype.getNED = function(coords) { - return this.map.measure.getNewNED(coords, true); +MapInterface.prototype.getNED = function(coords, onlyMatrix) { + return this.map.measure.getNewNED(coords, (onlyMatrix === false) ? false : true); }; @@ -400,6 +400,11 @@ MapInterface.prototype.renderToImage = function() { }; +MapInterface.prototype.getCurrentGeometry = function() { + return this.map.getCurrentGeometry(); +}; + + MapInterface.prototype.getStats = function(switches) { if (switches) { return { From f8dda2185a80f9d9bb40278f467f8c7d771bfb39 Mon Sep 17 00:00:00 2001 From: David Levinsky Date: Fri, 31 Aug 2018 13:12:10 +0200 Subject: [PATCH 10/20] basic geometry extraction --- src/browser/ui/control/measure.js | 3 ++ src/core/map/draw.js | 4 +- src/core/map/map.js | 10 +++++ src/core/map/surface-tree.js | 63 ++++++++++++++++++++++++++----- src/core/renderer/octree.js | 59 +++++++++++++++++++++++++++++ 5 files changed, 128 insertions(+), 11 deletions(-) create mode 100644 src/core/renderer/octree.js diff --git a/src/browser/ui/control/measure.js b/src/browser/ui/control/measure.js index e3ab8cc6..a3c9031b 100644 --- a/src/browser/ui/control/measure.js +++ b/src/browser/ui/control/measure.js @@ -498,6 +498,9 @@ UIControlMeasure.prototype.onCompute = function(button) { //TODO: build octree //TODO: extract meshes and build octree + var terrain = map.getCurrentGeometry(); + + var x, y, north, east; coords = map.convertCoordsFromPhysToNav(center, 'fix'); diff --git a/src/core/map/draw.js b/src/core/map/draw.js index 47fa8afe..2f4fca26 100755 --- a/src/core/map/draw.js +++ b/src/core/map/draw.js @@ -357,7 +357,7 @@ MapDraw.prototype.drawMap = function(skipFreeLayers) { } if (this.tree.surfaceSequence.length > 0) { - this.tree.draw(camInfo); + this.tree.draw(); } if (replay.storeTiles) { //used only in inspectors @@ -404,7 +404,7 @@ MapDraw.prototype.drawMap = function(skipFreeLayers) { if (layer.type == 'geodata') { this.drawMonoliticGeodata(layer); } else { - layer.tree.draw(camInfo); + layer.tree.draw(); } this.zbufferOffset = null; diff --git a/src/core/map/map.js b/src/core/map/map.js index 5ca1615c..5eebb256 100755 --- a/src/core/map/map.js +++ b/src/core/map/map.js @@ -1122,6 +1122,16 @@ Map.prototype.hitTestGeoLayers = function(screenX, screenY, mode) { } }; +Map.prototype.getCurrentGeometry = function() { + if (this.draw.tree.surfaceSequence.length > 0) { + this.draw.tree.draw(true); + var res = this.storedTilesRes; + this.storedTilesRes = []; + return res; + } + + return res; +}; Map.prototype.applyCredits = function(tile) { var value, value2; diff --git a/src/core/map/surface-tree.js b/src/core/map/surface-tree.js index ca483543..ab126518 100755 --- a/src/core/map/surface-tree.js +++ b/src/core/map/surface-tree.js @@ -125,7 +125,7 @@ MapSurfaceTree.prototype.findNavTile = function(id) { }; -MapSurfaceTree.prototype.draw = function() { +MapSurfaceTree.prototype.draw = function(storeTilesOnly) { this.cameraPos = [0,0,0]; this.worldPos = [0,0,0]; @@ -150,8 +150,8 @@ MapSurfaceTree.prototype.draw = function() { this.drawSurface([0,0,0]); if (periodicity.type == 'X') { - this.drawSurface([periodicity.period,0,0]); - this.drawSurface([-periodicity.period,0,0]); + this.drawSurface([periodicity.period,0,0], storeTilesOnly); + this.drawSurface([-periodicity.period,0,0], storeTilesOnly); } } else { @@ -164,9 +164,9 @@ MapSurfaceTree.prototype.draw = function() { } switch(mode) { - case 'topdown': this.drawSurface([0,0,0]); break; - case 'fit': this.drawSurfaceFit([0,0,0]); break; - case 'fitonly': this.drawSurfaceFitOnly([0,0,0]); break; + case 'topdown': this.drawSurface([0,0,0], storeTilesOnly); break; + case 'fit': this.drawSurfaceFit([0,0,0], storeTilesOnly); break; + case 'fitonly': this.drawSurfaceFitOnly([0,0,0], storeTilesOnly); break; } } }; @@ -219,7 +219,7 @@ MapSurfaceTree.prototype.logTileInfo = function(tile, node, cameraPos) { //loadmode = topdown -MapSurfaceTree.prototype.drawSurface = function() { +MapSurfaceTree.prototype.drawSurface = function(shift, storeTilesOnly) { this.counter++; var tile = this.surfaceTree; @@ -403,6 +403,11 @@ MapSurfaceTree.prototype.drawSurface = function() { processBufferIndex = newProcessBufferIndex; } while(processBufferIndex > 0); + + if (storeTilesOnly) { + this.storeDrawBufferGeometry(drawBufferIndex); + return; + } if (best2 > draw.bestMeshTexelSize) { draw.bestMeshTexelSize = best2; @@ -449,7 +454,7 @@ MapSurfaceTree.prototype.drawSurface = function() { //loadmode = fitonly -MapSurfaceTree.prototype.drawSurfaceFitOnly = function() { +MapSurfaceTree.prototype.drawSurfaceFitOnly = function(shift, storeTilesOnly) { this.counter++; // this.surfaceTracer.trace(this.surfaceTree);//this.rootId); @@ -622,6 +627,11 @@ MapSurfaceTree.prototype.drawSurfaceFitOnly = function() { } while(processBufferIndex > 0); + if (storeTilesOnly) { + this.storeDrawBufferGeometry(drawBufferIndex); + return; + } + var stats = map.stats; stats.usedNodes = usedNodes; @@ -658,7 +668,7 @@ MapSurfaceTree.prototype.drawSurfaceFitOnly = function() { //loadmode = fit -MapSurfaceTree.prototype.drawSurfaceFit = function() { +MapSurfaceTree.prototype.drawSurfaceFit = function(shift, storeTilesOnly) { this.counter++; var tile = this.surfaceTree; @@ -971,6 +981,11 @@ MapSurfaceTree.prototype.drawSurfaceFit = function() { } while(processBufferIndex > 0); + if (storeTilesOnly) { + this.storeDrawBufferGeometry(drawBufferIndex); + return; + } + var stats = map.stats; stats.usedNodes = usedNodes; @@ -1011,6 +1026,36 @@ MapSurfaceTree.prototype.drawSurfaceFit = function() { }; +MapSurfaceTree.prototype.storeDrawBufferGeometry = function(drawBufferIndex) { + var map = this.map; + var drawBuffer = map.draw.drawBuffer; + map.storedTilesRes = new Array(drawBufferIndex); + + for (var i = drawBufferIndex - 1; i >= 0; i--) { + var tile = drawBuffer[i]; + + if (tile.metanode && tile.surface && tile.metanode.hasGeometry() && + tile.surfaceMesh && tile.surfaceMesh.isReady(true, 0, true)) { + + var mesh = tile.surfaceMesh; + var submeshes = []; + + for (var j = 0, lj = mesh.submeshes.length; j < lj; j++) { + var submesh = mesh.submeshes[j]; + submeshes.push({ + "bbox": [submesh.bbox.min, submesh.bbox.max], + "vertices" : submesh.vertices }); + } + + map.storedTilesRes[i] = { + "id": tile.id.slice(), + "type": "mesh", + "submeshes": submeshes + }; + } + } +}; + MapSurfaceTree.prototype.traceHeight = function(tile, params, nodeOnly) { if (!tile) { return; diff --git a/src/core/renderer/octree.js b/src/core/renderer/octree.js new file mode 100644 index 00000000..e813bb8d --- /dev/null +++ b/src/core/renderer/octree.js @@ -0,0 +1,59 @@ + +var Octree = function() { + this.root = null; +}; + + +Octree.prototype.clear = function() { +}; + + +Octree.prototype.buildFromGeometry = function(data) { + if (!data) { + return; + } + + var i, li, j, lj, item, submeshes, submesh, bbox, + minX, minY, minZ, maxX, maxY, maxZ; + + //get gemetery bbox + for (i = 0, li = data.length; i < li; i++) { + item = data[i]; + if (item["type"] == "mesh") { + submeshes = item["submeshes"]; + + for (j = 0; j < lj; j++) { + submesh = submeshes[j]; + bbox = submesh["bbox"]; + + if (bbox.min[0] < minX) minX = bbox.min[0]; + if (bbox.min[1] < minY) minY = bbox.min[1]; + if (bbox.min[2] < minZ) minZ = bbox.min[2]; + + if (bbox.max[0] > maxX) maxX = bbox.max[0]; + if (bbox.max[1] > maxY) maxY = bbox.max[1]; + if (bbox.max[2] > maxZ) maxZ = bbox.max[2]; + } + } + } + + this.root = new OctreeNode([minX, minY, minZ], [maxX, maxY, maxZ]); +}; + + +var OctreeNode = function(min, max) { + this.min = min; + this.max = max; + this.children = []; + this.data = null; +}; + +Octree.prototype.add = function(data) { + +}; + +Octree.prototype.split = function(data) { + +}; + + From 5fc637ec2317d130aaa437218eac38288341e799 Mon Sep 17 00:00:00 2001 From: David Levinsky Date: Fri, 31 Aug 2018 16:52:06 +0200 Subject: [PATCH 11/20] octree builder and raycaster --- src/core/renderer/octree.js | 411 +++++++++++++++++++++++++++++++++++- 1 file changed, 403 insertions(+), 8 deletions(-) diff --git a/src/core/renderer/octree.js b/src/core/renderer/octree.js index e813bb8d..47980b59 100644 --- a/src/core/renderer/octree.js +++ b/src/core/renderer/octree.js @@ -1,6 +1,33 @@ var Octree = function() { this.root = null; + this.maxItemsPerNode = 20; + + /** + * A binary pattern that describes the standard octant layout: + * + * ```text + * 3____7 + * 2/___6/| + * | 1__|_5 + * 0/___4/ + * ``` + * + * This common layout is crucial for positional assumptions. + * + */ + + this.pattern = [ + new Uint8Array([0, 0, 0]), + new Uint8Array([0, 0, 1]), + new Uint8Array([0, 1, 0]), + new Uint8Array([0, 1, 1]), + + new Uint8Array([1, 0, 0]), + new Uint8Array([1, 0, 1]), + new Uint8Array([1, 1, 0]), + new Uint8Array([1, 1, 1]) + ]; }; @@ -13,14 +40,17 @@ Octree.prototype.buildFromGeometry = function(data) { return; } - var i, li, j, lj, item, submeshes, submesh, bbox, - minX, minY, minZ, maxX, maxY, maxZ; + var i, li, j, lj, k, lk, v, item, submeshes, submesh, bbox, index, + minX, minY, minZ, maxX, maxY, maxZ, geometry, vertices; + + minX = minY = minZ = Number.POSITIVE_INFINITY; + maxX = maxY = maxZ = Number.NEGATIVE_INFINITY; //get gemetery bbox for (i = 0, li = data.length; i < li; i++) { - item = data[i]; - if (item["type"] == "mesh") { - submeshes = item["submeshes"]; + geometry = data[i]; + if (geometry["type"] == "mesh") { + submeshes = geometry["submeshes"]; for (j = 0; j < lj; j++) { submesh = submeshes[j]; @@ -38,6 +68,44 @@ Octree.prototype.buildFromGeometry = function(data) { } this.root = new OctreeNode([minX, minY, minZ], [maxX, maxY, maxZ]); + + //get gemetery bbox + for (i = 0, li = data.length; i < li; i++) { + geometry = data[i]; + if (geometry["type"] == "mesh") { + submeshes = geometry["submeshes"]; + + for (j = 0, lj = submeshes.length; j < lj; j++) { + submesh = submeshes[j]; + + vertices = submesh["vertices"]; + + for (k = 0, lk = vertices.length; k < lk; k += 9) { + + minX = minY = minZ = Number.POSITIVE_INFINITY; + maxX = maxY = maxZ = Number.NEGATIVE_INFINITY; + + for (v = 0; v < 3; v ++) { + index = k + v; + + if (vertices[index] < minX) minX = vertices[index]; + if (vertices[index+1] < minY) minY = vertices[index+1]; + if (vertices[index+2] < minZ) minZ = vertices[index+2]; + + if (vertices[index] > maxX) maxX = vertices[index]; + if (vertices[index+1] > maxY) maxY = vertices[index+1]; + if (vertices[index+2] > maxZ) maxZ = vertices[index+2]; + } + + //this.root.add([minX, minY, minZ], [maxX, maxY, maxZ], [vertices, k]) + this.root.add([minX, minY, minZ, maxX, maxY, maxZ, vertices, k]) + } + + } + } + } + + }; @@ -45,15 +113,342 @@ var OctreeNode = function(min, max) { this.min = min; this.max = max; this.children = []; - this.data = null; + this.items = null; }; -Octree.prototype.add = function(data) { +OctreeNode.prototype.add = function(item, octree) { + this.items.push(item); + if (this.children.length >= octree.maxItemsPerNode) { + this.split(); + } }; -Octree.prototype.split = function(data) { +OctreeNode.prototype.split = function() { + var min = this.min; + var max = this.max; + var mid = [(max[0] + min[0]) * 0.5, (max[1] + min[1]) * 0.5, (max[2] + min[2]) * 0.5]; + var i, li, j; + + this.children = [ + null, null, + null, null, + null, null, + null, null + ]; + + for (i = 0; i < 8; i++) { + var combination = this.pattern[i]; + + this.children[i] = new OctreeNode( + [ + (combination[0] === 0) ? min.x : mid.x, + (combination[1] === 0) ? min.y : mid.y, + (combination[2] === 0) ? min.z : mid.z + ], + + [ + (combination[0] === 0) ? mid.x : max.x, + (combination[1] === 0) ? mid.y : max.y, + (combination[2] === 0) ? mid.z : max.z + ] + ); + } + + //distribute items + if (items) { + for (i = 0, li = items.length; i < li; i++) { + var item = items[i]; + + for (j = 0; j < 8; j++) { + var child = this.children[j]; + min = child.min; + max = child.max; + + if (item[0] < max[0] && item[3] > min[0] && + item[1] < max[1] && item[4] > min[1] && + item[2] < max[2] && item[5] > min[2]) { + + //collision detected, add item + child.add(item); + } + } + } + } }; +var OctreeRaycaster = function() { + + // A lookup-table containing octant ids. Used to determine the exit plane from an octant. + this.octantTable = [ + new Uint8Array([4, 2, 1]), + new Uint8Array([5, 3, 8]), + new Uint8Array([6, 8, 3]), + new Uint8Array([7, 8, 8]), + new Uint8Array([8, 6, 5]), + new Uint8Array([8, 7, 8]), + new Uint8Array([8, 8, 7]), + new Uint8Array([8, 8, 8]) + ]; + + // A byte that stores raycasting flags. + this.flags = 0; +}; + + +/** + * Finds the entry plane of the first octant that a ray travels through. + * + * Determining the first octant requires knowing which of the t0s is the + * largest. The tms of the other axes must also be compared against that + * largest t0. + * + * tx0, ty0,tz0 - Ray projection parameter. + * txm, tym, tzm - Ray projection parameter mean. + * returns - index of the first octant that the ray travels through. + */ + +OctreeRaycaster.prototype.findEntryOctant = function(tx0, ty0, tz0, txm, tym, tzm) { + var entry = 0; + + // Find the entry plane. + if(tx0 > ty0 && tx0 > tz0) { + + // YZ-plane. + if (tym < tx0) { + entry |= 2; + } + + if (tzm < tx0) { + entry |= 1; + } + + } else if (ty0 > tz0) { + + // XZ-plane. + if (txm < ty0) { + entry |= 4; + } + + if (tzm < ty0) { + entry |= 1; + } + + } else { + + // XY-plane. + if (txm < tz0) { + entry |= 4; + } + + if (tym < tz0) { + entry |= 2; + } + } + + return entry; +} + +/** + * Finds the next octant that intersects with the ray based on the exit plane of + * the current one. + * + * urrentOctant - The index of the current octant. + * tx1, ty1, tz1 - Ray projection parameter. + * returns - index of the next octant that the ray travels through. + */ + +OctreeRaycaster.prototype.findNextOctant = function(currentOctant, tx1, ty1, tz1) { + var min; + var exit = 0; + + // Find the exit plane. + if (tx1 < ty1) { + min = tx1; + exit = 0; // YZ-plane. + } else { + min = ty1; + exit = 1; // XZ-plane. + } + + if (tz1 < min) { + exit = 2; // XY-plane. + } + + return this.octantTable[currentOctant][exit]; +} + +/** + * Finds all octants that intersect with the given ray. + * + * @param {Octant} octant - The current octant. + * @param {Number} tx0 - Ray projection parameter. Initial tx0 = (minX - rayOriginX) / rayDirectionX. + * @param {Number} ty0 - Ray projection parameter. Initial ty0 = (minY - rayOriginY) / rayDirectionY. + * @param {Number} tz0 - Ray projection parameter. Initial tz0 = (minZ - rayOriginZ) / rayDirectionZ. + * @param {Number} tx1 - Ray projection parameter. Initial tx1 = (maxX - rayOriginX) / rayDirectionX. + * @param {Number} ty1 - Ray projection parameter. Initial ty1 = (maxY - rayOriginY) / rayDirectionY. + * @param {Number} tz1 - Ray projection parameter. Initial tz1 = (maxZ - rayOriginZ) / rayDirectionZ. + * @param {Array} intersects - An array to be filled with the intersecting octants. + */ + +OctreeRaycaster.prototype.raycastOctant = function(octant, tx0, ty0, tz0, tx1, ty1, tz1, intersects) { + var children = octant.children; + var currentOctant; + var txm, tym, tzm; + + if (tx1 >= 0.0 && ty1 >= 0.0 && tz1 >= 0.0) { + + if (children === null) { + + // Leaf. + intersects.push(octant); + + } else { + + // Compute means. + txm = 0.5 * (tx0 + tx1); + tym = 0.5 * (ty0 + ty1); + tzm = 0.5 * (tz0 + tz1); + + currentOctant = this.findEntryOctant(tx0, ty0, tz0, txm, tym, tzm); + + do { + + /* The possibilities for the next node are passed in the same respective + * order as the t-values. Hence, if the first value is found to be the + * greatest, the fourth one will be returned. If the second value is the + * greatest, the fifth one will be returned, etc. + */ + + switch(currentOctant) { + + case 0: + this.raycastOctant(children[this.flags], tx0, ty0, tz0, txm, tym, tzm, intersects); + currentOctant = this.findNextOctant(currentOctant, txm, tym, tzm); + break; + + case 1: + this.raycastOctant(children[this.flags ^ 1], tx0, ty0, tzm, txm, tym, tz1, intersects); + currentOctant = this.findNextOctant(currentOctant, txm, tym, tz1); + break; + + case 2: + this.raycastOctant(children[this.flags ^ 2], tx0, tym, tz0, txm, ty1, tzm, intersects); + currentOctant = this.findNextOctant(currentOctant, txm, ty1, tzm); + break; + + case 3: + this.raycastOctant(children[this.flags ^ 3], tx0, tym, tzm, txm, ty1, tz1, intersects); + currentOctant = this.findNextOctant(currentOctant, txm, ty1, tz1); + break; + + case 4: + this.raycastOctant(children[this.flags ^ 4], txm, ty0, tz0, tx1, tym, tzm, intersects); + currentOctant = this.findNextOctant(currentOctant, tx1, tym, tzm); + break; + + case 5: + this.raycastOctant(children[this.flags ^ 5], txm, ty0, tzm, tx1, tym, tz1, intersects); + currentOctant = this.findNextOctant(currentOctant, tx1, tym, tz1); + break; + + case 6: + this.raycastOctant(children[this.flags ^ 6], txm, tym, tz0, tx1, ty1, tzm, intersects); + currentOctant = this.findNextOctant(currentOctant, tx1, ty1, tzm); + break; + + case 7: + this.raycastOctant(children[this.flags ^ 7], txm, tym, tzm, tx1, ty1, tz1, intersects); + // Far top right octant. No other octants can be reached from here. + currentOctant = 8; + break; + + } + + } while(currentOctant < 8); + + } + + } + +} + +/** + * Finds the octants that intersect with the given ray. The intersecting + * octants are sorted by distance, closest first. + * + * @param {Octree} octree - An octree. + * @param {Array} intersects - A list to be filled with intersecting octants. + */ + +// https://github.com/vanruesc/sparse-octree/blob/master/src/core/OctreeRaycaster.js + +OctreeRaycaster.prototype.intersectOctree = function(rayPos, rayDir, octree, intersects) { + // Translate the octree extents to the scene origin. + var min = [0,0,0]; + var max = [octree.root.max[0] - octree.root.min[0], + octree.root.max[1] - octree.root.min[1], + octree.root.max[2] - octree.root.min[2]] + + var dimensions = [max[0], max[1], max[2]]; + var halfDimensions = [dimensions[0]*0.5, dimensions[1]*0.5, dimensions[2]*0.5]; + + var origin = [rayPos[0],rayPos[1],rayPos[2]]; + var direction = [rayDir[0], rayDir[1], rayDir[2]]; + + var invDirX, invDirY, invDirZ; + var tx0, tx1, ty0, ty1, tz0, tz1; + + var center = [(octree.root.max[0] + octree.root.min[0]) * 0.5, + (octree.root.max[1] + octree.root.min[1]) * 0.5, + (octree.root.max[2] + octree.root.min[2]) * 0.5] + + // Translate the ray to the center of the octree. + //origin.sub(octree.getCenter(v[2])).add(halfDimensions); + origin[0] = (origin[0] - center[0]) + halfDimensions[0]; + origin[1] = (origin[1] - center[1]) + halfDimensions[1]; + origin[2] = (origin[2] - center[2]) + halfDimensions[2]; + + // Reset all flags. + this.flags = 0; + + // Handle rays with negative directions. + if (direction[0] < 0.0) { + origin[0] = dimensions[0] - origin[0]; + direction[0] = -direction[0]; + this.flags |= 4; + } + + if (direction[1] < 0.0) { + origin[1] = dimensions[1] - origin[1]; + direction[1] = -direction[1]; + this.flags |= 2; + } + + if (direction[2] < 0.0) { + origin[2] = dimensions[2] - origin[2]; + direction[2] = -direction[2]; + this.flags |= 1; + } + + // Improve IEEE double stability. + invDirX = 1.0 / direction[0]; + invDirY = 1.0 / direction[1]; + invDirZ = 1.0 / direction[2]; + + // Project the ray to the root's boundaries. + tx0 = (min[0] - origin[0]) * invDirX; + tx1 = (max[0] - origin[0]) * invDirX; + ty0 = (min[1] - origin[1]) * invDirY; + ty1 = (max[1] - origin[1]) * invDirY; + tz0 = (min[2] - origin[2]) * invDirZ; + tz1 = (max[2] - origin[2]) * invDirZ; + + // Check if the ray hits the octree. + if (Math.max(Math.max(tx0, ty0), tz0) < Math.min(Math.min(tx1, ty1), tz1)) { + // Find the intersecting octants. + this.raycastOctant(octree.root, tx0, ty0, tz0, tx1, ty1, tz1, intersects); + } +} From 8f3ac3f436f5eb7aad842a99f66ae80d6f780732 Mon Sep 17 00:00:00 2001 From: David Levinsky Date: Fri, 31 Aug 2018 17:15:02 +0200 Subject: [PATCH 12/20] octree integration to renderer --- src/browser/ui/control/measure.js | 12 +++++++----- src/core/renderer/interface.js | 17 ++++++++++++++++- 2 files changed, 23 insertions(+), 6 deletions(-) diff --git a/src/browser/ui/control/measure.js b/src/browser/ui/control/measure.js index a3c9031b..3d665122 100644 --- a/src/browser/ui/control/measure.js +++ b/src/browser/ui/control/measure.js @@ -500,6 +500,8 @@ UIControlMeasure.prototype.onCompute = function(button) { var terrain = map.getCurrentGeometry(); + var renderer = this.browser.getRenderer(); + var octree = renderer.buildOctreeFromGeometry(terrain); var x, y, north, east; @@ -509,7 +511,7 @@ UIControlMeasure.prototype.onCompute = function(button) { north = ned.direction; east = ned.east; - var steps = 5, l, sx, sy; + var steps = 5, l, sx, sy, res2, dir = [0,0,0]; for (y = -steps; y <= steps; y++) { for (x = -steps; x <= steps; x++) { @@ -520,8 +522,10 @@ UIControlMeasure.prototype.onCompute = function(button) { coords[1] = center[1] + north[1] * sy + east[1] * sx; coords[2] = center[2] + north[2] * sy + east[2] * sx; - res = this.hitFaces(coords, faces); + vec3.normalize(coords, dir); // TODO: add support for projected systems + res = this.hitFaces(coords, dir, faces); + res2 = renderer.raycastOctreeGeometry(octree, coords, dir); } console.log("*"); @@ -587,9 +591,7 @@ UIControlMeasure.prototype.hitFace = function(origin, dir, face) { }; -UIControlMeasure.prototype.hitFaces = function(coords, faces) { - var dir = [0,0,0]; - vec3.normalize(coords, dir); // TODO: add support for projected systems +UIControlMeasure.prototype.hitFaces = function(coords, dir, faces) { var hit = false, t = Number.POSITIVE_INFINITY; for (var i = 0, li = faces.length; i < li; i++) { diff --git a/src/core/renderer/interface.js b/src/core/renderer/interface.js index 0af46484..559bcd20 100755 --- a/src/core/renderer/interface.js +++ b/src/core/renderer/interface.js @@ -2,12 +2,14 @@ import GpuTexture_ from './gpu/texture'; import GpuMesh_ from './gpu/mesh'; import GpuProgram_ from './gpu/program'; +import {Octree as Octree_, OctreeRaycaster as OctreeRaycaster_} from './octree.js'; //get rid of compiler mess var GpuTexture = GpuTexture_; var GpuMesh = GpuMesh_; var GpuProgram = GpuProgram_; - +var Octree = Octree_; +var OctreeRaycaster = OctreeRaycaster_; var RendererInterface = function(renderer) { this.renderer = renderer; @@ -407,6 +409,19 @@ RendererInterface.prototype.drawDebugText = function(options) { }; +RendererInterface.prototype.buildOctreeFromGeometry = function(geometry) { + var octree = new Octree(); + octree.buildFromGeometry(geometry); + return octree; +}; + + +RendererInterface.prototype.raycastOctreeGeometry = function(octree, rayPos, rayDir) { + var raycaster = new OctreeRaycaster(), intersects = []; + raycaster.intersectOctree(rayPos, rayDir, octree, intersects); + return intersects; +}; + RendererInterface.prototype.saveScreenshot = function(output, filename, filetype) { return this.renderer.saveScreenshot(output, filename, filetype); }; From 75ab95f7bed203cb5f454ead0135ad7e72d56b74 Mon Sep 17 00:00:00 2001 From: David Levinsky Date: Wed, 12 Sep 2018 09:06:53 +0200 Subject: [PATCH 13/20] fixed octree --- src/browser/ui/control/measure.js | 10 +++- src/core/map/surface-tree.js | 17 +++++-- src/core/renderer/octree.js | 81 +++++++++++++++++++++---------- 3 files changed, 78 insertions(+), 30 deletions(-) diff --git a/src/browser/ui/control/measure.js b/src/browser/ui/control/measure.js index 3d665122..fa07bfd2 100644 --- a/src/browser/ui/control/measure.js +++ b/src/browser/ui/control/measure.js @@ -525,7 +525,14 @@ UIControlMeasure.prototype.onCompute = function(button) { vec3.normalize(coords, dir); // TODO: add support for projected systems res = this.hitFaces(coords, dir, faces); - res2 = renderer.raycastOctreeGeometry(octree, coords, dir); + + if (res[0]) { + res2 = renderer.raycastOctreeGeometry(octree, coords, dir); + + if (res2 > 0) { + console.log("T" + JSON.stringify(res2)); + } + } } console.log("*"); @@ -607,6 +614,7 @@ UIControlMeasure.prototype.hitFaces = function(coords, dir, faces) { } console.log(hit ? ("" + t.toFixed(2)) : ("N")); + return [hit, t]; }; diff --git a/src/core/map/surface-tree.js b/src/core/map/surface-tree.js index ab126518..d6ec68ef 100755 --- a/src/core/map/surface-tree.js +++ b/src/core/map/surface-tree.js @@ -1041,10 +1041,21 @@ MapSurfaceTree.prototype.storeDrawBufferGeometry = function(drawBufferIndex) { var submeshes = []; for (var j = 0, lj = mesh.submeshes.length; j < lj; j++) { - var submesh = mesh.submeshes[j]; + var submesh = mesh.submeshes[j], + vertices = submesh.vertices.slice(), + min = submesh.bbox.min, + max = submesh.bbox.max, + delta = [max[0] - min[0], max[1] - min[1], max[2] - min[2]]; + + for (var k = 0, lk = vertices.length; k < lk; k+=3) { + vertices[k] = vertices[k]*delta[0] + min[0]; + vertices[k+1] = vertices[k+1]*delta[1] + min[1]; + vertices[k+2] = vertices[k+2]*delta[2] + min[2]; + } + submeshes.push({ - "bbox": [submesh.bbox.min, submesh.bbox.max], - "vertices" : submesh.vertices }); + "bbox": [min.slice(), max.slice()], + "vertices" : vertices }); } map.storedTilesRes[i] = { diff --git a/src/core/renderer/octree.js b/src/core/renderer/octree.js index 47980b59..93a1ab4e 100644 --- a/src/core/renderer/octree.js +++ b/src/core/renderer/octree.js @@ -52,17 +52,17 @@ Octree.prototype.buildFromGeometry = function(data) { if (geometry["type"] == "mesh") { submeshes = geometry["submeshes"]; - for (j = 0; j < lj; j++) { + for (j = 0, lj = submeshes.length; j < lj; j++) { submesh = submeshes[j]; bbox = submesh["bbox"]; - if (bbox.min[0] < minX) minX = bbox.min[0]; - if (bbox.min[1] < minY) minY = bbox.min[1]; - if (bbox.min[2] < minZ) minZ = bbox.min[2]; + if (bbox[0][0] < minX) minX = bbox[0][0]; + if (bbox[0][1] < minY) minY = bbox[0][1]; + if (bbox[0][2] < minZ) minZ = bbox[0][2]; - if (bbox.max[0] > maxX) maxX = bbox.max[0]; - if (bbox.max[1] > maxY) maxY = bbox.max[1]; - if (bbox.max[2] > maxZ) maxZ = bbox.max[2]; + if (bbox[1][0] > maxX) maxX = bbox[1][0]; + if (bbox[1][1] > maxY) maxY = bbox[1][1]; + if (bbox[1][2] > maxZ) maxZ = bbox[1][2]; } } } @@ -86,7 +86,7 @@ Octree.prototype.buildFromGeometry = function(data) { maxX = maxY = maxZ = Number.NEGATIVE_INFINITY; for (v = 0; v < 3; v ++) { - index = k + v; + index = k + v * 3; if (vertices[index] < minX) minX = vertices[index]; if (vertices[index+1] < minY) minY = vertices[index+1]; @@ -98,7 +98,7 @@ Octree.prototype.buildFromGeometry = function(data) { } //this.root.add([minX, minY, minZ], [maxX, maxY, maxZ], [vertices, k]) - this.root.add([minX, minY, minZ, maxX, maxY, maxZ, vertices, k]) + this.root.add([minX, minY, minZ, maxX, maxY, maxZ, vertices, k], this) } } @@ -112,23 +112,45 @@ Octree.prototype.buildFromGeometry = function(data) { var OctreeNode = function(min, max) { this.min = min; this.max = max; - this.children = []; + this.children = null; this.items = null; }; OctreeNode.prototype.add = function(item, octree) { + if (this.children) { + for (var i = 0; i < 8; i++) { + var child = this.children[i], + min = child.min, + max = child.max; + + if (item[0] < max[0] && item[3] > min[0] && + item[1] < max[1] && item[4] > min[1] && + item[2] < max[2] && item[5] > min[2]) { + + //collision detected, add item + child.add(item, octree); + } + } + + return; + } + + if (!this.items) { + this.items = []; + } + this.items.push(item); - if (this.children.length >= octree.maxItemsPerNode) { - this.split(); + if (this.items.length >= octree.maxItemsPerNode) { + this.split(octree); } }; -OctreeNode.prototype.split = function() { - var min = this.min; - var max = this.max; - var mid = [(max[0] + min[0]) * 0.5, (max[1] + min[1]) * 0.5, (max[2] + min[2]) * 0.5]; - var i, li, j; +OctreeNode.prototype.split = function(octree) { + var min = this.min, + max = this.max, + mid = [(max[0] + min[0]) * 0.5, (max[1] + min[1]) * 0.5, (max[2] + min[2]) * 0.5], + i, li, j; this.children = [ null, null, @@ -138,23 +160,25 @@ OctreeNode.prototype.split = function() { ]; for (i = 0; i < 8; i++) { - var combination = this.pattern[i]; + var combination = octree.pattern[i]; this.children[i] = new OctreeNode( [ - (combination[0] === 0) ? min.x : mid.x, - (combination[1] === 0) ? min.y : mid.y, - (combination[2] === 0) ? min.z : mid.z + (combination[0] === 0) ? min[0] : mid[0], + (combination[1] === 0) ? min[1] : mid[1], + (combination[2] === 0) ? min[2] : mid[2] ], [ - (combination[0] === 0) ? mid.x : max.x, - (combination[1] === 0) ? mid.y : max.y, - (combination[2] === 0) ? mid.z : max.z + (combination[0] === 0) ? mid[0] : max[0], + (combination[1] === 0) ? mid[1] : max[1], + (combination[2] === 0) ? mid[2] : max[2] ] ); } + var items = this.items; + //distribute items if (items) { for (i = 0, li = items.length; i < li; i++) { @@ -170,12 +194,13 @@ OctreeNode.prototype.split = function() { item[2] < max[2] && item[5] > min[2]) { //collision detected, add item - child.add(item); + child.add(item, octree); } } } } + this.items = null; }; @@ -300,7 +325,7 @@ OctreeRaycaster.prototype.raycastOctant = function(octant, tx0, ty0, tz0, tx1, t if (tx1 >= 0.0 && ty1 >= 0.0 && tz1 >= 0.0) { - if (children === null) { + if (!children) { // Leaf. intersects.push(octant); @@ -451,4 +476,8 @@ OctreeRaycaster.prototype.intersectOctree = function(rayPos, rayDir, octree, int // Find the intersecting octants. this.raycastOctant(octree.root, tx0, ty0, tz0, tx1, ty1, tz1, intersects); } + + var hits = []; } + +export {Octree, OctreeRaycaster}; From 6f5d91c67178c116209621d171ccc6ac644657bb Mon Sep 17 00:00:00 2001 From: David Levinsky Date: Thu, 13 Sep 2018 15:45:19 +0200 Subject: [PATCH 14/20] volume measure is working --- src/browser/ui/control/measure.js | 30 ++++++++-- src/core/renderer/interface.js | 2 +- src/core/renderer/octree.js | 96 +++++++++++++++++++++++++++---- 3 files changed, 109 insertions(+), 19 deletions(-) diff --git a/src/browser/ui/control/measure.js b/src/browser/ui/control/measure.js index fa07bfd2..5f055915 100644 --- a/src/browser/ui/control/measure.js +++ b/src/browser/ui/control/measure.js @@ -511,26 +511,40 @@ UIControlMeasure.prototype.onCompute = function(button) { north = ned.direction; east = ned.east; - var steps = 5, l, sx, sy, res2, dir = [0,0,0]; + var steps = 25, l, sx, sy, res2, dir = [0,0,0], delta; + var sampleArea = (1.0 / steps) * radius; + var volumeAbove = 0; + var volumeBelow = 0; for (y = -steps; y <= steps; y++) { for (x = -steps; x <= steps; x++) { sx = (1.0 / steps) * x * radius; sy = (1.0 / steps) * y * radius; - coords[0] = center[0] + north[0] * sy + east[0] * sx; - coords[1] = center[1] + north[1] * sy + east[1] * sx; - coords[2] = center[2] + north[2] * sy + east[2] * sx; + coords[0] = center[0] * 1.0001 + north[0] * sy + east[0] * sx; + coords[1] = center[1] * 1.0001 + north[1] * sy + east[1] * sx; + coords[2] = center[2] * 1.0001 + north[2] * sy + east[2] * sx; vec3.normalize(coords, dir); // TODO: add support for projected systems + dir[0] = -dir[0]; + dir[1] = -dir[1]; + dir[2] = -dir[2]; res = this.hitFaces(coords, dir, faces); if (res[0]) { res2 = renderer.raycastOctreeGeometry(octree, coords, dir); - if (res2 > 0) { - console.log("T" + JSON.stringify(res2)); + if (res2.length > 0) { + delta = (res[1] - res2[0]); + + if (delta >= 0) { + volumeAbove += delta; + } else { + volumeBelow += -delta; + } + + console.log("T" + JSON.stringify(res2)); } } } @@ -538,8 +552,12 @@ UIControlMeasure.prototype.onCompute = function(button) { console.log("*"); } + str += '\n' + space + 'volume above: ' + volumeAbove.toFixed(2) + ' m\u00B3'; + str += '\n' + space + 'volume below: ' + volumeBelow.toFixed(2) + ' m\u00B3'; + str += '\n' + space + 'volume combined: ' + (volumeAbove + volumeBelow).toFixed(2) + ' m\u00B3'; } + this.counter++; }).bind(this)); diff --git a/src/core/renderer/interface.js b/src/core/renderer/interface.js index 559bcd20..1af5f3c0 100755 --- a/src/core/renderer/interface.js +++ b/src/core/renderer/interface.js @@ -419,7 +419,7 @@ RendererInterface.prototype.buildOctreeFromGeometry = function(geometry) { RendererInterface.prototype.raycastOctreeGeometry = function(octree, rayPos, rayDir) { var raycaster = new OctreeRaycaster(), intersects = []; raycaster.intersectOctree(rayPos, rayDir, octree, intersects); - return intersects; + return raycaster.intersectOctants(rayPos, rayDir, intersects); }; RendererInterface.prototype.saveScreenshot = function(output, filename, filetype) { diff --git a/src/core/renderer/octree.js b/src/core/renderer/octree.js index 93a1ab4e..efff250e 100644 --- a/src/core/renderer/octree.js +++ b/src/core/renderer/octree.js @@ -1,4 +1,10 @@ +import {vec3 as vec3_} from '../utils/matrix'; + +//get rid of compiler mess +var vec3 = vec3_; + + var Octree = function() { this.root = null; this.maxItemsPerNode = 20; @@ -308,14 +314,15 @@ OctreeRaycaster.prototype.findNextOctant = function(currentOctant, tx1, ty1, tz1 /** * Finds all octants that intersect with the given ray. * - * @param {Octant} octant - The current octant. - * @param {Number} tx0 - Ray projection parameter. Initial tx0 = (minX - rayOriginX) / rayDirectionX. - * @param {Number} ty0 - Ray projection parameter. Initial ty0 = (minY - rayOriginY) / rayDirectionY. - * @param {Number} tz0 - Ray projection parameter. Initial tz0 = (minZ - rayOriginZ) / rayDirectionZ. - * @param {Number} tx1 - Ray projection parameter. Initial tx1 = (maxX - rayOriginX) / rayDirectionX. - * @param {Number} ty1 - Ray projection parameter. Initial ty1 = (maxY - rayOriginY) / rayDirectionY. - * @param {Number} tz1 - Ray projection parameter. Initial tz1 = (maxZ - rayOriginZ) / rayDirectionZ. - * @param {Array} intersects - An array to be filled with the intersecting octants. + * octant - The current octant. + * tx0 - Ray projection parameter. Initial tx0 = (minX - rayOriginX) / rayDirectionX. + * ty0 - Ray projection parameter. Initial ty0 = (minY - rayOriginY) / rayDirectionY. + * tz0 - Ray projection parameter. Initial tz0 = (minZ - rayOriginZ) / rayDirectionZ. + * tx1 - Ray projection parameter. Initial tx1 = (maxX - rayOriginX) / rayDirectionX. + * ty1 - Ray projection parameter. Initial ty1 = (maxY - rayOriginY) / rayDirectionY. + * tz1 - Ray projection parameter. Initial tz1 = (maxZ - rayOriginZ) / rayDirectionZ. + * intersects - An array to be filled with the intersecting octants. + * returns */ OctreeRaycaster.prototype.raycastOctant = function(octant, tx0, ty0, tz0, tx1, ty1, tz1, intersects) { @@ -328,7 +335,9 @@ OctreeRaycaster.prototype.raycastOctant = function(octant, tx0, ty0, tz0, tx1, t if (!children) { // Leaf. - intersects.push(octant); + if (octant.items) { + intersects.push(octant); + } } else { @@ -400,12 +409,53 @@ OctreeRaycaster.prototype.raycastOctant = function(octant, tx0, ty0, tz0, tx1, t } +OctreeRaycaster.prototype.hitFace = function(origin, dir, index, vertices) { + var EPSILON = 0.0000001, + v1 = [vertices[index], vertices[index+1], vertices[index+2]], + v2 = [vertices[index+3], vertices[index+4], vertices[index+5]], + v3 = [vertices[index+6], vertices[index+7], vertices[index+8]]; + + var h = [0,0,0], q = [0,0,0], s, a, f, u, v, + edge1 = [v2[0] - v1[0], v2[1] - v1[1], v2[2] - v1[2]], + edge2 = [v3[0] - v1[0], v3[1] - v1[1], v3[2] - v1[2]]; + + vec3.cross(dir, edge2, h); + a = vec3.dot(edge1, h); + + if (a > -EPSILON && a < EPSILON) { + return [false]; + } + + f = 1/a; + s = [origin[0] - v1[0], origin[1] - v1[1], origin[2] - v1[2]]; + u = f * (vec3.dot(s, h)); + + if (u < 0.0 || u > 1.0) { + return [false]; + } + + q = vec3.cross(s, edge1); + v = f * vec3.dot(dir, q); + if (v < 0.0 || u + v > 1.0) { + return [false]; + } + + // At this stage we can compute t to find out where the intersection point is on the line. + var t = f * vec3.dot(edge2, q); + //if (t > EPSILON) { // ray intersection + return [true, t]; //[origin[0] + dir[0] * t, origin[1] + dir[1] * t, origin[2] + dir[2] * t ]]; + //} else { // This means that there is a line intersection but not a ray intersection. + // return [false]; + //} +}; + + /** * Finds the octants that intersect with the given ray. The intersecting * octants are sorted by distance, closest first. * - * @param {Octree} octree - An octree. - * @param {Array} intersects - A list to be filled with intersecting octants. + * octree - An octree. + * intersects - A list to be filled with intersecting octants. */ // https://github.com/vanruesc/sparse-octree/blob/master/src/core/OctreeRaycaster.js @@ -476,8 +526,30 @@ OctreeRaycaster.prototype.intersectOctree = function(rayPos, rayDir, octree, int // Find the intersecting octants. this.raycastOctant(octree.root, tx0, ty0, tz0, tx1, ty1, tz1, intersects); } +}; +OctreeRaycaster.prototype.intersectOctants = function(rayPos, rayDir, octants) { var hits = []; -} + var t = Number.POSITIVE_INFINITY; + + for (var i = 0, li = octants.length; i < li; i++) { + var items = octants[i].items; + + for (var j = 0, lj = items.length; j < lj; j++) { + var item = items[j]; + var res = this.hitFace(rayPos, rayDir, item[7], item[6]); + + if (res[0] && res[1] < t) { + t = res[1]; + } + } + } + + if (t !== Number.POSITIVE_INFINITY) { + hits = [t]; + } + + return hits; +}; export {Octree, OctreeRaycaster}; From 6ddb63dc6f090894cb431d283897030c3a0ee6bd Mon Sep 17 00:00:00 2001 From: David Levinsky Date: Mon, 17 Sep 2018 17:53:16 +0200 Subject: [PATCH 15/20] fixed volume measurement --- src/browser/ui/control/measure.js | 4 +++- src/core/renderer/octree.js | 22 +++++++++++++++++----- 2 files changed, 20 insertions(+), 6 deletions(-) diff --git a/src/browser/ui/control/measure.js b/src/browser/ui/control/measure.js index 5f055915..1c837358 100644 --- a/src/browser/ui/control/measure.js +++ b/src/browser/ui/control/measure.js @@ -516,6 +516,8 @@ UIControlMeasure.prototype.onCompute = function(button) { var volumeAbove = 0; var volumeBelow = 0; + sampleArea *= sampleArea; + for (y = -steps; y <= steps; y++) { for (x = -steps; x <= steps; x++) { @@ -536,7 +538,7 @@ UIControlMeasure.prototype.onCompute = function(button) { res2 = renderer.raycastOctreeGeometry(octree, coords, dir); if (res2.length > 0) { - delta = (res[1] - res2[0]); + delta = (res[1] - res2[0]) * sampleArea; if (delta >= 0) { volumeAbove += delta; diff --git a/src/core/renderer/octree.js b/src/core/renderer/octree.js index efff250e..7670282d 100644 --- a/src/core/renderer/octree.js +++ b/src/core/renderer/octree.js @@ -8,6 +8,12 @@ var vec3 = vec3_; var Octree = function() { this.root = null; this.maxItemsPerNode = 20; + this.maxDepth = 20; + + this.depthCount = []; + for (var i = 0; i < 1000; i++) { + this.depthCount[i] = 0; + } /** * A binary pattern that describes the standard octant layout: @@ -122,8 +128,12 @@ var OctreeNode = function(min, max) { this.items = null; }; -OctreeNode.prototype.add = function(item, octree) { +OctreeNode.prototype.add = function(item, octree, depth) { if (this.children) { + if (!depth) { + depth = 0; + } + for (var i = 0; i < 8; i++) { var child = this.children[i], min = child.min, @@ -134,7 +144,7 @@ OctreeNode.prototype.add = function(item, octree) { item[2] < max[2] && item[5] > min[2]) { //collision detected, add item - child.add(item, octree); + child.add(item, octree, depth + 1); } } @@ -147,12 +157,12 @@ OctreeNode.prototype.add = function(item, octree) { this.items.push(item); - if (this.items.length >= octree.maxItemsPerNode) { - this.split(octree); + if (depth < octree.maxDepth && this.items.length >= octree.maxItemsPerNode) { + this.split(octree, depth + 1); } }; -OctreeNode.prototype.split = function(octree) { +OctreeNode.prototype.split = function(octree, depth) { var min = this.min, max = this.max, mid = [(max[0] + min[0]) * 0.5, (max[1] + min[1]) * 0.5, (max[2] + min[2]) * 0.5], @@ -165,6 +175,8 @@ OctreeNode.prototype.split = function(octree) { null, null ]; + this.depthCount[depth]++; + for (i = 0; i < 8; i++) { var combination = octree.pattern[i]; From 46028487a931a466b9ef55d29f502edc36a3d5c2 Mon Sep 17 00:00:00 2001 From: David Levinsky Date: Tue, 25 Sep 2018 17:59:14 +0200 Subject: [PATCH 16/20] surface area tiles extraction --- src/browser/ui/control/measure.js | 2 ++ src/core/map/interface.js | 5 +++ src/core/map/measure.js | 31 +++++++++++++++++ src/core/map/surface-tile.js | 11 ++++++ src/core/map/surface-tree.js | 57 +++++++++++++++++++++++++++++++ 5 files changed, 106 insertions(+) diff --git a/src/browser/ui/control/measure.js b/src/browser/ui/control/measure.js index 1c837358..1c2891b8 100644 --- a/src/browser/ui/control/measure.js +++ b/src/browser/ui/control/measure.js @@ -507,6 +507,8 @@ UIControlMeasure.prototype.onCompute = function(button) { coords = map.convertCoordsFromPhysToNav(center, 'fix'); + map.getSurfaceAreaTiles(coords, radius, 'lod', 21, false, false); + var ned = map.getNED(coords, false); north = ned.direction; east = ned.east; diff --git a/src/core/map/interface.js b/src/core/map/interface.js index bd767b59..936d661a 100755 --- a/src/core/map/interface.js +++ b/src/core/map/interface.js @@ -255,6 +255,11 @@ MapInterface.prototype.getSurfaceHeight = function(coords, precision) { }; +MapInterface.prototype.getSurfaceAreaTiles = function(coords, radius, mode, limit, loadMeshes, loadTextures) { + return this.map.measure.getSurfaceAreaTiles(coords, radius, mode, limit, loadMeshes, loadTextures); +}; + + MapInterface.prototype.getDistance = function(coords, coords2, includingHeights, usePublic) { return this.map.measure.getDistance(coords, coords2, includingHeights, usePublic); }; diff --git a/src/core/map/measure.js b/src/core/map/measure.js index 12a72fbd..dc9e4b1b 100755 --- a/src/core/map/measure.js +++ b/src/core/map/measure.js @@ -25,6 +25,37 @@ var MapMeasure = function(map) { this.maxDivisionNodeDepth = res[1]; }; +MapMeasure.prototype.getSurfaceAreaTiles = function(coords, radius, mode, limit, loadMeshes, loadTextures) { + var tree = this.map.tree; + + if (tree.surfaceSequence.length == 0) { + reurn [true, []]; + } + + var center = this.convert.convertCoords(coords, 'navigation', 'physical'); + var coneVec = [0,0,0]; + + vec3.normalize(center, coneVec); + + var distance = vec3.length(center); + var coneAngle = Math.atan(Math.tan(radius / distance)); + + tree.params = { + coneVec : coneVec, + coneAngle : coneAngle, + mode : mode, + limit : limit, + loaded : true, + areaTiles : [], + loadMeshes: (loadMeshes === true), + loadTextures: (loadTextures === true) + }; + + //priority = 0, noReadInly = false + tree.traceAreaTiles(tree.surfaceTree, 0, false); + + return [tree.params.loaded, tree.params.areaTiles]; +}; MapMeasure.prototype.getSurfaceHeight = function(coords, lod, storeStats, node, nodeCoords, coordsArray, useNodeOnly) { var tree = this.map.tree; diff --git a/src/core/map/surface-tile.js b/src/core/map/surface-tile.js index 089501af..b9155394 100755 --- a/src/core/map/surface-tile.js +++ b/src/core/map/surface-tile.js @@ -632,6 +632,17 @@ MapSurfaceTile.prototype.bboxVisible = function(id, bbox, cameraPos, node) { } }; +MapSurfaceTile.prototype.insideCone = function(coneVec, angle, node) { + + if (map.isGeocent && node.diskPos && node.diskNormal) { + var a = Math.acos(vec3.dot(rayVec, node.diskNormal)); + + return (a < angle + node.diskAngle2A) + } + + return true; +}; + MapSurfaceTile.prototype.getPixelSize = function(bbox, screenPixelSize, cameraPos, worldPos, returnDistance) { var min = bbox.min; diff --git a/src/core/map/surface-tree.js b/src/core/map/surface-tree.js index d6ec68ef..28af355f 100755 --- a/src/core/map/surface-tree.js +++ b/src/core/map/surface-tree.js @@ -1351,5 +1351,62 @@ MapSurfaceTree.prototype.getRenderedNodeById = function(id, drawCounter) { return; }; +MapSurfaceTree.prototype.traceAreaTiles = function(tile, priority, nodeReadyOnly) { + if (tile == null) { + return; + } + + if (!tile.isMetanodeReady(this, 0) || nodeReadyOnly) { + this.params.loaded = false; + return; + } + + tile.metanode.metatile.used(); + + if (tile.lastSurface && tile.lastSurface == tile.surface) { + tile.lastSurface = null; + tile.restoreLastState(); + //return; + } + + if (!tile.metanode.hasChildren()) { + console.log('(A)' + JSON.stringify(tile.id)); + + this.params.areaTiles.push(tile); + } else { + for (var i = 0; i < 4; i++) { + var child = tile.children[i]; + + if (child && child.metanode) { + if (child.insideCone(this.params.coneVec, this.params.coneAngle, child.metanode)) { + var fit = (this.params.mode == 'lod') ? (child.id[0] >= this.params.limit) : (child.metanode.pixelSize <= this.params.limit); + + if (fit) { + console.log('(A)' + JSON.stringify(child.id)); + + if (this.params.loadMeshes || this.params.loadTextures) { + + var tmp = this.config.mapNoTextures; + this.config.mapNoTextures = !this.params.loadTextures; + + //are resources ready? priority=0, preventRender=true, preventLoad=false, doNotCheckGpu=true + if (!this.map.draw.drawTiles.drawSurfaceTile(child, child.metanode, this.map.renderer.cameraPosition, child.texelSize, 0, true, false, true)) { + this.params.loaded = false; + } + + this.config.mapNoTextures = tmp; + } + + this.params.areaTiles.push(child); + } else { + this.traceAreaTiles(child, priority, nodeReadyOnly); + } + } + } + } + } +}; + + export default MapSurfaceTree; From e26550e296fa4d728bfef596d2b4dd6bd33f838c Mon Sep 17 00:00:00 2001 From: David Levinsky Date: Wed, 26 Sep 2018 16:59:54 +0200 Subject: [PATCH 17/20] area tiles loader --- src/browser/ui/control/measure.js | 86 ++++++++++++++++++------------- src/core/core.js | 21 ++++++-- src/core/map/interface.js | 13 ++++- src/core/map/measure.js | 2 +- src/core/map/surface-tile.js | 2 +- src/core/map/surface-tree.js | 51 +++++++++--------- 6 files changed, 106 insertions(+), 69 deletions(-) diff --git a/src/browser/ui/control/measure.js b/src/browser/ui/control/measure.js index 1c2891b8..1cb066f0 100644 --- a/src/browser/ui/control/measure.js +++ b/src/browser/ui/control/measure.js @@ -507,58 +507,72 @@ UIControlMeasure.prototype.onCompute = function(button) { coords = map.convertCoordsFromPhysToNav(center, 'fix'); - map.getSurfaceAreaTiles(coords, radius, 'lod', 21, false, false); + //map.getSurfaceAreaGeometry(coords, radius, 'lod', 21, false, false); - var ned = map.getNED(coords, false); - north = ned.direction; - east = ned.east; + var texelSize = radius * 0.0030; //0.15 texel size for 100m diameter - var steps = 25, l, sx, sy, res2, dir = [0,0,0], delta; - var sampleArea = (1.0 / steps) * radius; - var volumeAbove = 0; - var volumeBelow = 0; + //map.getSurfaceAreaGeometry(coords, radius, 'texelSize', texelSize, false, false); - sampleArea *= sampleArea; - for (y = -steps; y <= steps; y++) { - for (x = -steps; x <= steps; x++) { + var traceVolumeCall = (function(){ - sx = (1.0 / steps) * x * radius; - sy = (1.0 / steps) * y * radius; - coords[0] = center[0] * 1.0001 + north[0] * sy + east[0] * sx; - coords[1] = center[1] * 1.0001 + north[1] * sy + east[1] * sx; - coords[2] = center[2] * 1.0001 + north[2] * sy + east[2] * sx; + return; - vec3.normalize(coords, dir); // TODO: add support for projected systems - dir[0] = -dir[0]; - dir[1] = -dir[1]; - dir[2] = -dir[2]; + var ned = map.getNED(coords, false); + north = ned.direction; + east = ned.east; - res = this.hitFaces(coords, dir, faces); + var steps = 25, l, sx, sy, res2, dir = [0,0,0], delta; + var sampleArea = (1.0 / steps) * radius; + var volumeAbove = 0; + var volumeBelow = 0; - if (res[0]) { - res2 = renderer.raycastOctreeGeometry(octree, coords, dir); + sampleArea *= sampleArea; - if (res2.length > 0) { - delta = (res[1] - res2[0]) * sampleArea; + for (y = -steps; y <= steps; y++) { + for (x = -steps; x <= steps; x++) { - if (delta >= 0) { - volumeAbove += delta; - } else { - volumeBelow += -delta; - } + sx = (1.0 / steps) * x * radius; + sy = (1.0 / steps) * y * radius; + coords[0] = center[0] * 1.0001 + north[0] * sy + east[0] * sx; + coords[1] = center[1] * 1.0001 + north[1] * sy + east[1] * sx; + coords[2] = center[2] * 1.0001 + north[2] * sy + east[2] * sx; + + vec3.normalize(coords, dir); // TODO: add support for projected systems + dir[0] = -dir[0]; + dir[1] = -dir[1]; + dir[2] = -dir[2]; + + res = this.hitFaces(coords, dir, faces); + + if (res[0]) { + res2 = renderer.raycastOctreeGeometry(octree, coords, dir); + + if (res2.length > 0) { + delta = (res[1] - res2[0]) * sampleArea; + + if (delta >= 0) { + volumeAbove += delta; + } else { + volumeBelow += -delta; + } - console.log("T" + JSON.stringify(res2)); + console.log("T" + JSON.stringify(res2)); + } } } + + console.log("*"); } - console.log("*"); - } + str += '\n' + space + 'volume above: ' + volumeAbove.toFixed(2) + ' m\u00B3'; + str += '\n' + space + 'volume below: ' + volumeBelow.toFixed(2) + ' m\u00B3'; + str += '\n' + space + 'volume combined: ' + (volumeAbove + volumeBelow).toFixed(2) + ' m\u00B3'; + + }).bind(this); + + var destructor = map.getSurfaceAreaGeometry(coords, radius, 'texelSize', texelSize, traceVolumeCall, false, false); - str += '\n' + space + 'volume above: ' + volumeAbove.toFixed(2) + ' m\u00B3'; - str += '\n' + space + 'volume below: ' + volumeBelow.toFixed(2) + ' m\u00B3'; - str += '\n' + space + 'volume combined: ' + (volumeAbove + volumeBelow).toFixed(2) + ' m\u00B3'; } diff --git a/src/core/core.js b/src/core/core.js index 5a401f83..ffe42328 100755 --- a/src/core/core.js +++ b/src/core/core.js @@ -339,7 +339,7 @@ Core.prototype.setOption = function(/*key, value*/) { }; -Core.prototype.on = function(name, listener) { +Core.prototype.on = function(name, listener, wait, once) { if (this.killed) { // || this.renderer == null) { return; } @@ -349,17 +349,32 @@ Core.prototype.on = function(name, listener) { } this.listenerCounter++; - this.listeners.push({ name : name, listener : listener, id : this.listenerCounter }); + this.listeners.push({ name : name, listener : listener, id : this.listenerCounter, once: once, wait: wait ? wait : 0 }); return (function(id){ this.removeListener(id); }).bind(this, this.listenerCounter); }; +Core.prototype.once = function(name, listener, wait) { + this.on(name, listener, wait, true); +}; + + // private Core.prototype.callListener = function(name, event, log) { for (var i = 0; i < this.listeners.length; i++) { if (this.listeners[i].name == name) { - this.listeners[i].listener(event); + var listener = this.listeners[i]; + + if (listener.wait > 0) { + listener.wait--; + } else { + listener.listener(event); + if (listener.once) { + this.listeners.splice(i, 1); + i--; + } + } } } diff --git a/src/core/map/interface.js b/src/core/map/interface.js index 936d661a..82b0c3f3 100755 --- a/src/core/map/interface.js +++ b/src/core/map/interface.js @@ -255,8 +255,17 @@ MapInterface.prototype.getSurfaceHeight = function(coords, precision) { }; -MapInterface.prototype.getSurfaceAreaTiles = function(coords, radius, mode, limit, loadMeshes, loadTextures) { - return this.map.measure.getSurfaceAreaTiles(coords, radius, mode, limit, loadMeshes, loadTextures); +MapInterface.prototype.getSurfaceAreaGeometry = function(coords, radius, mode, limit, callback, loadTextures) { + var res = this.map.measure.getSurfaceAreaGeometry(coords, radius, mode, limit, true, loadTextures); + + console.log('getSurfaceAreaGeometry'); + + if (!res[0]) { + return this.map.core.once("map-update", this.getSurfaceAreaGeometry.bind(this, coords, radius, mode, limit, callback, loadTextures), 1); + } else { + callback(res[1]); + return (function(){}); + } }; diff --git a/src/core/map/measure.js b/src/core/map/measure.js index dc9e4b1b..64c9b6b9 100755 --- a/src/core/map/measure.js +++ b/src/core/map/measure.js @@ -25,7 +25,7 @@ var MapMeasure = function(map) { this.maxDivisionNodeDepth = res[1]; }; -MapMeasure.prototype.getSurfaceAreaTiles = function(coords, radius, mode, limit, loadMeshes, loadTextures) { +MapMeasure.prototype.getSurfaceAreaGeometry = function(coords, radius, mode, limit, loadMeshes, loadTextures) { var tree = this.map.tree; if (tree.surfaceSequence.length == 0) { diff --git a/src/core/map/surface-tile.js b/src/core/map/surface-tile.js index b9155394..fbd55fcc 100755 --- a/src/core/map/surface-tile.js +++ b/src/core/map/surface-tile.js @@ -309,7 +309,7 @@ MapSurfaceTile.prototype.isMetanodeReady = function(tree, priority, preventLoad) var ret = this.checkMetanode(tree, priority); if (!ret && !(this.metanode != null && this.lastMetanode)) { //metanode is not ready yet - return; + return false; } } diff --git a/src/core/map/surface-tree.js b/src/core/map/surface-tree.js index 28af355f..20330217 100755 --- a/src/core/map/surface-tree.js +++ b/src/core/map/surface-tree.js @@ -1369,40 +1369,39 @@ MapSurfaceTree.prototype.traceAreaTiles = function(tile, priority, nodeReadyOnly //return; } - if (!tile.metanode.hasChildren()) { + if (!tile.insideCone(this.params.coneVec, this.params.coneAngle, tile.metanode)) { + return; + } + + var fit = (this.params.mode == 'lod') ? (tile.id[0] >= this.params.limit) : (tile.metanode.pixelSize <= this.params.limit); + + if (fit) { console.log('(A)' + JSON.stringify(tile.id)); - this.params.areaTiles.push(tile); - } else { - for (var i = 0; i < 4; i++) { - var child = tile.children[i]; - - if (child && child.metanode) { - if (child.insideCone(this.params.coneVec, this.params.coneAngle, child.metanode)) { - var fit = (this.params.mode == 'lod') ? (child.id[0] >= this.params.limit) : (child.metanode.pixelSize <= this.params.limit); + if (this.params.loadMeshes || this.params.loadTextures) { - if (fit) { - console.log('(A)' + JSON.stringify(child.id)); + var tmp = this.config.mapNoTextures; + this.config.mapNoTextures = !this.params.loadTextures; - if (this.params.loadMeshes || this.params.loadTextures) { + //are resources ready? priority=0, preventRender=true, preventLoad=false, doNotCheckGpu=true + if (!this.map.draw.drawTiles.drawSurfaceTile(tile, tile.metanode, this.map.renderer.cameraPosition, tile.texelSize, 0, true, false, true)) { + this.params.loaded = false; + } - var tmp = this.config.mapNoTextures; - this.config.mapNoTextures = !this.params.loadTextures; + this.config.mapNoTextures = tmp; + } - //are resources ready? priority=0, preventRender=true, preventLoad=false, doNotCheckGpu=true - if (!this.map.draw.drawTiles.drawSurfaceTile(child, child.metanode, this.map.renderer.cameraPosition, child.texelSize, 0, true, false, true)) { - this.params.loaded = false; - } + this.params.areaTiles.push(tile); + return; + } - this.config.mapNoTextures = tmp; - } + if (!tile.metanode.hasChildren()) { + console.log('(A)' + JSON.stringify(tile.id)); - this.params.areaTiles.push(child); - } else { - this.traceAreaTiles(child, priority, nodeReadyOnly); - } - } - } + this.params.areaTiles.push(tile); + } else { + for (var i = 0; i < 4; i++) { + this.traceAreaTiles(tile.children[i], priority, nodeReadyOnly); } } }; From 887d9c19f5c12a68a0603e40d8887e6b91de44e6 Mon Sep 17 00:00:00 2001 From: David Levinsky Date: Mon, 1 Oct 2018 12:41:27 +0200 Subject: [PATCH 18/20] computed area used for volme computation --- src/browser/ui/control/measure.js | 74 +++++++++++++++++++++++-------- src/core/interface.js | 5 +++ src/core/map/draw-tiles.js | 4 ++ src/core/map/geodata-builder.js | 2 +- src/core/map/interface.js | 16 +++++-- src/core/map/surface-tile.js | 8 ++-- src/core/map/surface-tree.js | 52 ++++++++++++++-------- 7 files changed, 115 insertions(+), 46 deletions(-) diff --git a/src/browser/ui/control/measure.js b/src/browser/ui/control/measure.js index 1cb066f0..41fa7ce7 100644 --- a/src/browser/ui/control/measure.js +++ b/src/browser/ui/control/measure.js @@ -347,6 +347,7 @@ UIControlMeasure.prototype.onCompute = function(button) { } var str, i, li, space; + var listElement = this.list.getElement(); if (button == 0) { //undo button this.navCoords.pop(); @@ -498,10 +499,9 @@ UIControlMeasure.prototype.onCompute = function(button) { //TODO: build octree //TODO: extract meshes and build octree - var terrain = map.getCurrentGeometry(); - + //var terrain = map.getCurrentGeometry(); var renderer = this.browser.getRenderer(); - var octree = renderer.buildOctreeFromGeometry(terrain); + //var octree = renderer.buildOctreeFromGeometry(terrain); var x, y, north, east; @@ -512,11 +512,23 @@ UIControlMeasure.prototype.onCompute = function(button) { var texelSize = radius * 0.0030; //0.15 texel size for 100m diameter //map.getSurfaceAreaGeometry(coords, radius, 'texelSize', texelSize, false, false); + var core = this.browser.getCore(); + + var traceVolumeCall = (function(terrain){ - var traceVolumeCall = (function(){ + //str += '\n' + space + 'data loaded'; + str = listElement.value; + str = str.substr(0, str.lastIndexOf('loading data ...')); + str += 'computation progress: 0%'; - return; + if (!terrain) { + str += '\n some error ocurred. Try it again.'; + this.counter++; + return; + } + + var octree = renderer.buildOctreeFromGeometry(terrain); var ned = map.getNED(coords, false); north = ned.direction; @@ -529,7 +541,12 @@ UIControlMeasure.prototype.onCompute = function(button) { sampleArea *= sampleArea; - for (y = -steps; y <= steps; y++) { + var y = -steps; + + //for (y = -steps; y <= steps; y++) { + + var traceVolumeLine = (function(){ + for (x = -steps; x <= steps; x++) { sx = (1.0 / steps) * x * radius; @@ -557,26 +574,48 @@ UIControlMeasure.prototype.onCompute = function(button) { volumeBelow += -delta; } - console.log("T" + JSON.stringify(res2)); + ///console.log("T" + JSON.stringify(res2)); } } } - console.log("*"); - } + if (y < steps) { + str = str.substr(0, str.lastIndexOf('computation progress:')); + str += 'computation progress: ' + (((y + steps) / (steps*2))*100).toFixed(1) + ' %'; + } else { + str = str.substr(0, str.lastIndexOf('computation progress:')); + str += 'volume above: ' + volumeAbove.toFixed(2) + ' m\u00B3'; + str += '\n' + space + 'volume below: ' + volumeBelow.toFixed(2) + ' m\u00B3'; + str += '\n' + space + 'volume combined: ' + (volumeAbove + volumeBelow).toFixed(2) + ' m\u00B3' + '\n'; + this.counter++; + } - str += '\n' + space + 'volume above: ' + volumeAbove.toFixed(2) + ' m\u00B3'; - str += '\n' + space + 'volume below: ' + volumeBelow.toFixed(2) + ' m\u00B3'; - str += '\n' + space + 'volume combined: ' + (volumeAbove + volumeBelow).toFixed(2) + ' m\u00B3'; + if (str) { + listElement.value = str; + listElement.scrollTop = listElement.scrollHeight; //scroll list to the last line + } - }).bind(this); + if (y < steps) { + core.once('tick', traceVolumeLine, 1); + } - var destructor = map.getSurfaceAreaGeometry(coords, radius, 'texelSize', texelSize, traceVolumeCall, false, false); + y++; - } + //console.log("*"); + }).bind(this); + core.once('tick', traceVolumeLine, 1); - this.counter++; + }).bind(this); + + str += '\n' + space + 'loading data ...'; + listElement.value += str; + listElement.scrollTop = listElement.scrollHeight; //scroll list to the last line + str = null; + + var destructor = map.getSurfaceAreaGeometry(coords, radius, 'texelSize', texelSize, traceVolumeCall, false); + + } }).bind(this)); } @@ -584,7 +623,6 @@ UIControlMeasure.prototype.onCompute = function(button) { } if (str) { - var listElement = this.list.getElement(); listElement.value += str + '\n'; listElement.scrollTop = listElement.scrollHeight; //scroll list to the last line } @@ -649,7 +687,7 @@ UIControlMeasure.prototype.hitFaces = function(coords, dir, faces) { } } - console.log(hit ? ("" + t.toFixed(2)) : ("N")); + //console.log(hit ? ("" + t.toFixed(2)) : ("N")); return [hit, t]; }; diff --git a/src/core/interface.js b/src/core/interface.js index 5d79909e..eb8b8b8a 100755 --- a/src/core/interface.js +++ b/src/core/interface.js @@ -84,6 +84,11 @@ CoreInterface.prototype.on = function(eventName, call) { return this.core.on(eventName, call); }; +CoreInterface.prototype.once = function(eventName, call, wait) { + if (!this.core) { return null; } + return this.core.once(eventName, call, wait); +}; + CoreInterface.prototype.callListener = function(name, event) { if (!this.core) { return null; } this.core.callListener(name, event); diff --git a/src/core/map/draw-tiles.js b/src/core/map/draw-tiles.js index 2b6b13e5..50d556ba 100755 --- a/src/core/map/draw-tiles.js +++ b/src/core/map/draw-tiles.js @@ -736,6 +736,10 @@ MapDrawTiles.prototype.updateTileSurfaceBounds = function(tile, submesh, surface // tile = tile; //} + if (this.config.mapNoTextures) { + return; + } + //search map view if (surface.boundLayerSequence.length > 0) { if (fullUpdate) { diff --git a/src/core/map/geodata-builder.js b/src/core/map/geodata-builder.js index b64fde82..cf39b579 100644 --- a/src/core/map/geodata-builder.js +++ b/src/core/map/geodata-builder.js @@ -1043,7 +1043,7 @@ MapGeodataBuilder.prototype.addPolygon3 = function(shape, holes, middle, heightM ' p6:' + (Array.isArray(sbuffer2[l+5][1]) ? 'a' : '') + sbuffer2[l+5][1]);*/ } else if (l2 == l) { - console.log('l2'); + //console.log('l2'); //add new vertices to the buffer vbuffer[mm] = p5[0]; diff --git a/src/core/map/interface.js b/src/core/map/interface.js index 82b0c3f3..f2edd985 100755 --- a/src/core/map/interface.js +++ b/src/core/map/interface.js @@ -257,13 +257,21 @@ MapInterface.prototype.getSurfaceHeight = function(coords, precision) { MapInterface.prototype.getSurfaceAreaGeometry = function(coords, radius, mode, limit, callback, loadTextures) { var res = this.map.measure.getSurfaceAreaGeometry(coords, radius, mode, limit, true, loadTextures); - - console.log('getSurfaceAreaGeometry'); + //console.log('getSurfaceAreaGeometry'); if (!res[0]) { - return this.map.core.once("map-update", this.getSurfaceAreaGeometry.bind(this, coords, radius, mode, limit, callback, loadTextures), 1); + return this.map.core.once('map-update', this.getSurfaceAreaGeometry.bind(this, coords, radius, mode, limit, callback, loadTextures), 1); } else { - callback(res[1]); + var buffer = res[1], ret = [], map = this.map; + + if (map.tree) { + map.storedTilesRes = []; + map.tree.storeGeometry(buffer, buffer.length); + ret = map.storedTilesRes; + map.storedTilesRes = []; + } + + callback(ret); return (function(){}); } }; diff --git a/src/core/map/surface-tile.js b/src/core/map/surface-tile.js index fbd55fcc..755f9969 100755 --- a/src/core/map/surface-tile.js +++ b/src/core/map/surface-tile.js @@ -634,13 +634,13 @@ MapSurfaceTile.prototype.bboxVisible = function(id, bbox, cameraPos, node) { MapSurfaceTile.prototype.insideCone = function(coneVec, angle, node) { - if (map.isGeocent && node.diskPos && node.diskNormal) { - var a = Math.acos(vec3.dot(rayVec, node.diskNormal)); + if (this.map.isGeocent) { // && node.diskPos && node.diskNormal) { + var a = Math.acos(vec3.dot(coneVec, node.diskNormal)); - return (a < angle + node.diskAngle2A) + return (a < angle + node.diskAngle2A); } - return true; + return false; }; diff --git a/src/core/map/surface-tree.js b/src/core/map/surface-tree.js index 20330217..ab113b4c 100755 --- a/src/core/map/surface-tree.js +++ b/src/core/map/surface-tree.js @@ -1029,9 +1029,17 @@ MapSurfaceTree.prototype.drawSurfaceFit = function(shift, storeTilesOnly) { MapSurfaceTree.prototype.storeDrawBufferGeometry = function(drawBufferIndex) { var map = this.map; var drawBuffer = map.draw.drawBuffer; - map.storedTilesRes = new Array(drawBufferIndex); - for (var i = drawBufferIndex - 1; i >= 0; i--) { + this.storeGeometry(drawBuffer, drawBufferIndex); +}; + + +MapSurfaceTree.prototype.storeGeometry = function(array, length) { + var map = this.map; + var drawBuffer = array; + map.storedTilesRes = new Array(length); + + for (var i = length - 1; i >= 0; i--) { var tile = drawBuffer[i]; if (tile.metanode && tile.surface && tile.metanode.hasGeometry() && @@ -1351,6 +1359,23 @@ MapSurfaceTree.prototype.getRenderedNodeById = function(id, drawCounter) { return; }; + +MapSurfaceTree.prototype.chekTileMesh = function(tile) { + if (this.params.loadMeshes || this.params.loadTextures) { + + var tmp = this.config.mapNoTextures; + this.config.mapNoTextures = !this.params.loadTextures; + + //are resources ready? priority=0, preventRender=true, preventLoad=false, doNotCheckGpu=true + if (!this.map.draw.drawTiles.drawSurfaceTile(tile, tile.metanode, this.map.renderer.cameraPosition, tile.texelSize, 0, true, false, true)) { + this.params.loaded = false; + } + + this.config.mapNoTextures = tmp; + } +}; + + MapSurfaceTree.prototype.traceAreaTiles = function(tile, priority, nodeReadyOnly) { if (tile == null) { return; @@ -1358,6 +1383,8 @@ MapSurfaceTree.prototype.traceAreaTiles = function(tile, priority, nodeReadyOnly if (!tile.isMetanodeReady(this, 0) || nodeReadyOnly) { this.params.loaded = false; + //console.log('(L)' + JSON.stringify(tile.id)); + tile.isMetanodeReady(this, 0); return; } @@ -1376,28 +1403,15 @@ MapSurfaceTree.prototype.traceAreaTiles = function(tile, priority, nodeReadyOnly var fit = (this.params.mode == 'lod') ? (tile.id[0] >= this.params.limit) : (tile.metanode.pixelSize <= this.params.limit); if (fit) { - console.log('(A)' + JSON.stringify(tile.id)); - - if (this.params.loadMeshes || this.params.loadTextures) { - - var tmp = this.config.mapNoTextures; - this.config.mapNoTextures = !this.params.loadTextures; - - //are resources ready? priority=0, preventRender=true, preventLoad=false, doNotCheckGpu=true - if (!this.map.draw.drawTiles.drawSurfaceTile(tile, tile.metanode, this.map.renderer.cameraPosition, tile.texelSize, 0, true, false, true)) { - this.params.loaded = false; - } - - this.config.mapNoTextures = tmp; - } - + //console.log('(A)' + JSON.stringify(tile.id)); + this.chekTileMesh(tile); this.params.areaTiles.push(tile); return; } if (!tile.metanode.hasChildren()) { - console.log('(A)' + JSON.stringify(tile.id)); - + //console.log('(A)' + JSON.stringify(tile.id)); + this.chekTileMesh(tile); this.params.areaTiles.push(tile); } else { for (var i = 0; i < 4; i++) { From d313e6f6c957a5a0bd08299f7ef405824750d35b Mon Sep 17 00:00:00 2001 From: David Levinsky Date: Mon, 1 Oct 2018 16:30:33 +0200 Subject: [PATCH 19/20] added measure lite, imporoved measuring --- package.json | 2 +- src/browser/browser.css | 8 +- src/browser/browser.js | 17 +- src/browser/ui/control/loading.js | 2 + src/browser/ui/control/measure-lite.js | 236 +++++++++++++++++++++++++ src/browser/ui/control/measure.js | 127 +++++++------ src/browser/ui/ui.js | 5 +- src/core/core.js | 2 +- 8 files changed, 331 insertions(+), 68 deletions(-) create mode 100644 src/browser/ui/control/measure-lite.js diff --git a/package.json b/package.json index 2beeb4b8..3ac7bc84 100755 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "vts-browser-js", - "version": "2.15.17", + "version": "2.16.0", "description": "JavaScript WebGL 3D maps rendering engine", "main": "src/browser/index.js", "scripts": { diff --git a/src/browser/browser.css b/src/browser/browser.css index fe19e0e8..df33fbfc 100755 --- a/src/browser/browser.css +++ b/src/browser/browser.css @@ -542,12 +542,16 @@ cursor: pointer; } -.vts-measure-tools-button:hover { +.vts-measure-tools-button-selected { + border-radius: 2px; + display: inline-block; + padding: 0px 5px; + background-color: #fff; + cursor: pointer; color: rgba(0, 102, 255, 1); border: 1px solid rgba(0, 102, 255, 1); } - .vts-measure-compute { position: absolute; font-family: Verdana, Tahoma, Geneva, Arial, Sans-serif; diff --git a/src/browser/browser.js b/src/browser/browser.js index f732c9d1..4db9c115 100755 --- a/src/browser/browser.js +++ b/src/browser/browser.js @@ -310,6 +310,7 @@ Browser.prototype.initConfig = function() { controlSearchUrl : null, controlSearchFilter : true, controlMeasure : false, + controlMeasureLite : false, controlLink : false, controlGithub : false, controlScale : true, @@ -391,13 +392,14 @@ Browser.prototype.setConfigParam = function(key, value, ignoreCore) { case 'controlSearchFilter': this.config.controlSearchFilter = utils.validateBool(value, true); break; case 'controlSearchElement': this.config.controlSearchElement = value; this.updateUI(key); break; case 'controlSearchValue': this.config.controlSearchValue = /*utils.validateString(*/value/*, null)*/; this.updateUI(key); break; - case 'controlLink': this.config.controlLink = utils.validateBool(value, false); this.updateUI(key); break; - case 'controlGithub': this.config.controlGithub = utils.validateBool(value, false); this.updateUI(key); break; - case 'controlMeasure': this.config.controlMeasure = utils.validateBool(value, false); this.updateUI(key); break; - case 'controlLogo': this.config.controlLogo = utils.validateBool(value, false); this.updateUI(key); break; - case 'controlFullscreen': this.config.controlFullscreen = utils.validateBool(value, true); this.updateUI(key); break; - case 'controlCredits': this.config.controlCredits = utils.validateBool(value, true); this.updateUI(key); break; - case 'controlLoading': this.config.controlLoading = utils.validateBool(value, true); this.updateUI(key); break; + case 'controlLink': this.config.controlLink = utils.validateBool(value, false); this.updateUI(key); break; + case 'controlGithub': this.config.controlGithub = utils.validateBool(value, false); this.updateUI(key); break; + case 'controlMeasure': this.config.controlMeasure = utils.validateBool(value, false); this.updateUI(key); break; + case 'controlMeasureLite': this.config.controlMeasureLite = utils.validateBool(value, false); this.updateUI(key); break; + case 'controlLogo': this.config.controlLogo = utils.validateBool(value, false); this.updateUI(key); break; + case 'controlFullscreen': this.config.controlFullscreen = utils.validateBool(value, true); this.updateUI(key); break; + case 'controlCredits': this.config.controlCredits = utils.validateBool(value, true); this.updateUI(key); break; + case 'controlLoading': this.config.controlLoading = utils.validateBool(value, true); this.updateUI(key); break; case 'minViewExtent': this.config.minViewExtent = utils.validateNumber(value, 0.01, Number.MAXINTEGER, 100); break; case 'maxViewExtent': this.config.maxViewExtent = utils.validateNumber(value, 0.01, Number.MAXINTEGER, Number.MAXINTEGER); break; case 'sensitivity': this.config.sensitivity = utils.validateNumberArray(value, 3, [0,0,0], [10, 10, 10], [1, 0.12, 0.05]); break; @@ -486,6 +488,7 @@ Browser.prototype.getConfigParam = function(key) { case 'controlLink': return this.config.controlLink; case 'controlGithub': return this.config.controlGithub; case 'controlMeasure': return this.config.controlMeasure; + case 'controlMeasureLite': return this.config.controlMeasureLite; case 'controlLogo': return this.config.controlLogo; case 'controlFullscreen': return this.config.controlFullscreen; case 'controlCredits': return this.config.controlCredits; diff --git a/src/browser/ui/control/loading.js b/src/browser/ui/control/loading.js index efaab249..9563e662 100755 --- a/src/browser/ui/control/loading.js +++ b/src/browser/ui/control/loading.js @@ -39,6 +39,7 @@ UIControlLoading.prototype.show = function() { this.ui.setControlVisible('link', false); this.ui.setControlVisible('github', false); this.ui.setControlVisible('measure', false); + this.ui.setControlVisible('measure2', false); this.ui.setControlVisible('fullscreen', false); this.ui.setControlVisible('credits', false); this.ui.setControlVisible('loading', true); @@ -73,6 +74,7 @@ UIControlLoading.prototype.hide = function() { this.ui.setControlVisible('link', this.ui.config.controlLink, false); this.ui.setControlVisible('github', this.ui.config.controlGithub, false); this.ui.setControlVisible('measure', this.ui.config.controlMeasure, false); + this.ui.setControlVisible('measure2', this.ui.config.controlMeasureLite, false); this.ui.setControlVisible('fullscreen', this.ui.config.controlFullscreen, false); this.ui.setControlVisible('credits', this.ui.config.controlCredits, false); this.ui.setControlVisible('loading', false); diff --git a/src/browser/ui/control/measure-lite.js b/src/browser/ui/control/measure-lite.js new file mode 100644 index 00000000..a9dfdda8 --- /dev/null +++ b/src/browser/ui/control/measure-lite.js @@ -0,0 +1,236 @@ + +import Dom_ from '../../utility/dom'; +import {UIControlMeasureIcon as UIControlMeasureIcon_, UIControlMeasureIcon2 as UIControlMeasureIcon2_} from './measure'; + +//get rid of compiler mess +var dom = Dom_, + UIControlMeasureIcon = UIControlMeasureIcon_, + UIControlMeasureIcon2 = UIControlMeasureIcon2_; + + +var UIControlMeasureLite = function(ui, visible, visibleLock) { + this.ui = ui; + this.browser = ui.browser; + this.control = this.ui.addControl('measure2', + '
' + + + '' + + + '' + + + '
' + + '
' + + '
' + + '' + + '
' + + '
' + + '
Clear
' + + '
' + + '
' + + '
' + + + '
' + + '
' + + + '
', visible, visibleLock); + + this.div = this.control.getElement('vts-measure'); + + this.buttonOff = this.control.getElement('vts-measure-button'); + this.buttonOff.on('click', this.onSwitch.bind(this)); + this.buttonOff.on('dblclick', this.onDoNothing.bind(this)); + + this.buttonOn = this.control.getElement('vts-measure-button2'); + this.buttonOn.on('click', this.onSwitch.bind(this)); + this.buttonOn.on('dblclick', this.onDoNothing.bind(this)); + + this.info = this.control.getElement('vts-measure-info'); + + var clearButton = this.control.getElement('vts-measure-clear'); + clearButton.on('click', this.onClear.bind(this)); + clearButton.on('dblclick', this.onDoNothing.bind(this)); + + this.measuring = false; + this.counter = 1; + this.lastCoords = null; + + this.listPanel = this.control.getElement('vts-measure-text-holder'); + this.list = this.control.getElement('vts-measure-text-input'); + + if (this.measuring) { + this.buttonOn.setStyle('display', 'block'); + this.buttonOff.setStyle('display', 'none'); + } else { + this.buttonOn.setStyle('display', 'none'); + this.buttonOff.setStyle('display', 'block'); + } + + this.onMouseMoveCall = this.onMouseMove.bind(this); + this.onMouseLeaveCall = this.onMouseLeave.bind(this); + this.onMouseClickCall = this.onMouseClick.bind(this); + + this.update(); +}; + + +UIControlMeasureLite.prototype.onDoNothing = function(event) { + dom.stopPropagation(event); +}; + +UIControlMeasureLite.prototype.onMouseLeave = function(event) { + this.info.setStyle('display', 'none'); +}; + + +UIControlMeasureLite.prototype.onMouseClick = function(event) { + var map = this.browser.getMap(); + if (!map) { + return; + } + + var mapElement = this.ui.getMapElement(); + var state = mapElement.getDraggingState(); + + //if (state['dragging']) { //TODO: why does not work this parameter? Fix it once you have time + // return; + //} + var delta = state['absMoved']; + + if ((delta[0]+delta[1]) > 0) { + return; + } + + var coords = event.getMouseCoords(); + var clickCoords = map.getHitCoords(coords[0], coords[1], 'fix'); + + if (!clickCoords) { + return; + } + + clickCoords = map.convertCoordsFromNavToPublic(clickCoords, 'fix'); + + var str = '#' + this.counter + ' ' + clickCoords[0].toFixed(7) + ', ' + clickCoords[1].toFixed(7) + ', ' + clickCoords[2].toFixed(2) + 'm'; + + if (this.lastCoords) { + var res = map.getDistance(this.lastCoords, clickCoords, false, true); + var space = '\n '; + + for (var i = 0, li = ('' + this.counter).length; i < li; i++) { + space += ' '; + } + + str += space + 'great-circle distance: '; + + if (res[0] > 100000) { + str += '' + (res[0]*0.001).toFixed(2) + 'km'; + } else { + str += '' + res[0].toFixed(2) + 'm'; + } + + str += space + 'elevation difference: ' + (clickCoords[2] - this.lastCoords[2]).toFixed(2) + 'm'; + str += space + 'euclidean distance: '; + + if (res[2] > 100000) { + str += '' + (res[2]*0.001).toFixed(2) + 'km'; + } else { + str += '' + res[2].toFixed(2) + 'm'; + } + } + + this.counter++; + this.lastCoords = clickCoords; + + var listElement = this.list.getElement(); + listElement.value += str + '\n'; + listElement.scrollTop = listElement.scrollHeight; //scroll list to the last line +}; + +UIControlMeasureLite.prototype.onMouseMove = function(event) { + var map = this.browser.getMap(); + if (!map) { + return; + } + + var coords = event.getMouseCoords(); + var clickCoords = map.getHitCoords(coords[0], coords[1], 'fix'); + + if (!clickCoords) { + this.info.setStyle('display', 'none'); + return; + } + + clickCoords = map.convertCoordsFromNavToPublic(clickCoords, 'fix'); + + var str = clickCoords[0].toFixed(7) + ', ' + clickCoords[1].toFixed(7) + ', ' + clickCoords[2].toFixed(2) + 'm'; + + coords[0] -= this.divRect.left; + coords[1] -= this.divRect.top; + + this.info.setStyle('display', 'block'); + this.info.setStyle('left', (coords[0]+20)+'px'); + this.info.setStyle('top', (coords[1]+10)+'px'); + this.info.setHtml(str); +}; + +UIControlMeasureLite.prototype.onSwitch = function() { + this.measuring = !this.measuring; + + var mapElement = this.ui.getMapElement(); + + if (this.measuring) { + this.buttonOn.setStyle('display', 'block'); + this.buttonOff.setStyle('display', 'none'); + + this.divRect = this.div.getRect(); + + mapElement.on('mousemove', this.onMouseMoveCall); + mapElement.on('mouseleave', this.onMouseLeaveCall); + mapElement.on('click', this.onMouseClickCall); + + } else { + this.buttonOn.setStyle('display', 'none'); + this.buttonOff.setStyle('display', 'block'); + + mapElement.off('mousemove', this.onMouseMoveCall); + mapElement.off('mouseleave', this.onMouseLeaveCall); + mapElement.off('click', this.onMouseClickCall); + } + + this.updateLink(); + this.update(); +}; + +UIControlMeasureLite.prototype.onClear = function() { + this.counter = 1; + this.lastCoords = null; + + var listElement = this.list.getElement(); + listElement.value = ''; + listElement.scrollTop = 0; +}; + +UIControlMeasureLite.prototype.update = function() { + //var button = this.control.getElement('vts-measure-button'); + + var left = 10 + (this.ui.config.controlZoom ? 70 : 0) + + (this.ui.config.controlSpace ? 35 : 0); + + this.div.setStyle('left', left + 'px'); + this.listPanel.setStyle('display', this.measuring ? 'block' : 'none'); +}; + + +UIControlMeasureLite.prototype.updateLink = function() { + /* + var linkValue = this.browser.getLinkWithCurrentPos(); + if (this.list.getElement().value != linkValue) { + this.list.getElement().value = linkValue; + }*/ +}; + + +export default UIControlMeasureLite; diff --git a/src/browser/ui/control/measure.js b/src/browser/ui/control/measure.js index 41fa7ce7..102c8130 100644 --- a/src/browser/ui/control/measure.js +++ b/src/browser/ui/control/measure.js @@ -4,9 +4,12 @@ import {utils as utils_} from '../../../core/utils/utils'; import {vec3 as vec3_} from '../../../core/utils/matrix'; //get rid of compiler mess -var dom = Dom_; -var utils = utils_; -var vec3 = vec3_; +var dom = Dom_, + utils = utils_, + vec3 = vec3_, + UIControlMeasureIcon = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABkAAAAZCAYAAADE6YVjAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAA2lpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuNS1jMDIxIDc5LjE1NDkxMSwgMjAxMy8xMC8yOS0xMTo0NzoxNiAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wTU09Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9tbS8iIHhtbG5zOnN0UmVmPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvc1R5cGUvUmVzb3VyY2VSZWYjIiB4bWxuczp4bXA9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC8iIHhtcE1NOk9yaWdpbmFsRG9jdW1lbnRJRD0ieG1wLmRpZDpkNjI1MjFjMi1mYzE5LTcyNDUtOTI5My1kNTU3MmE5N2E1MjgiIHhtcE1NOkRvY3VtZW50SUQ9InhtcC5kaWQ6ODJGRUI2NzE2NzkwMTFFN0EzRUZFNzQ1NEFCMkVFQUQiIHhtcE1NOkluc3RhbmNlSUQ9InhtcC5paWQ6ODJGRUI2NzA2NzkwMTFFN0EzRUZFNzQ1NEFCMkVFQUQiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIENDIChXaW5kb3dzKSI+IDx4bXBNTTpEZXJpdmVkRnJvbSBzdFJlZjppbnN0YW5jZUlEPSJ4bXAuaWlkOjRBMjkwN0JENjc4QzExRTc5QTQwRjk4NjQzOEI4RDczIiBzdFJlZjpkb2N1bWVudElEPSJ4bXAuZGlkOjRBMjkwN0JFNjc4QzExRTc5QTQwRjk4NjQzOEI4RDczIi8+IDwvcmRmOkRlc2NyaXB0aW9uPiA8L3JkZjpSREY+IDwveDp4bXBtZXRhPiA8P3hwYWNrZXQgZW5kPSJyIj8+k3ySjQAAAdJJREFUeNrclk0oBGEYgHemjZOLm8tSyt9BqT2I1O7JSdSSkpsDxZGLi5MTjn5K4SDh4OfiaPfkIDebOPiPUqKUiLKet96pzzT7MztxMPX0le+b95n3Z2ZZmUwm9NuXHfqLK1smsVhsED5hKkhswc4mYJmHVxgNIvIslyE4hnLYDSqycwiaoCGVSnUFFdk5BH2wKHtBRbYKqlVwbQhGoNU5GETkZHKpggjMqaBFhaGgIltv/mKphRuQss1oZkPuG4oRWTLHlmU5fSlhOYNKSEK7xIUugj+6hmSHpROm2RvL9Q7arqf80IykdDEdgll4IWi/Bm+ECT8Z2R7l+DBKV6N/PoRnEbBuwIKf0nm+8S7RGuzDLaxLMuw/5OuR+bn60ROPt9/pUUQnUEa6A56SyeSWeTYejzs9GkC8ZIpySjxEy1APbdCM6MAlGmeZdIahYIlLVAU9kIA7KZFxrJrA5+bU8RBjBf+eGD26gk0oI4AItiU4dMOqHh+GTylbQT3JU7oj2IMLmNBJrIATKIUoD5L2LfEQJbRPvXAP0iPZj5J92vNl9Fk6mSCZsBV4dwuyflb8XJrRqX6C3iDsFhSdiXOzZlSnwxD2yuBHJv/iX6JvAQYAPSICqA82OnoAAAAASUVORK5CYII=', + UIControlMeasureIcon2 = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABkAAAAZCAYAAADE6YVjAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAyFpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuNS1jMDIxIDc5LjE1NDkxMSwgMjAxMy8xMC8yOS0xMTo0NzoxNiAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RSZWY9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZVJlZiMiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIENDIChXaW5kb3dzKSIgeG1wTU06SW5zdGFuY2VJRD0ieG1wLmlpZDo2Nzg3NzczMzY3OEMxMUU3QjU2QUUxNTNCNzc4MzVBQiIgeG1wTU06RG9jdW1lbnRJRD0ieG1wLmRpZDo2Nzg3NzczNDY3OEMxMUU3QjU2QUUxNTNCNzc4MzVBQiI+IDx4bXBNTTpEZXJpdmVkRnJvbSBzdFJlZjppbnN0YW5jZUlEPSJ4bXAuaWlkOjY3ODc3NzMxNjc4QzExRTdCNTZBRTE1M0I3NzgzNUFCIiBzdFJlZjpkb2N1bWVudElEPSJ4bXAuZGlkOjY3ODc3NzMyNjc4QzExRTdCNTZBRTE1M0I3NzgzNUFCIi8+IDwvcmRmOkRlc2NyaXB0aW9uPiA8L3JkZjpSREY+IDwveDp4bXBtZXRhPiA8P3hwYWNrZXQgZW5kPSJyIj8+owD8TQAAAepJREFUeNrcls8rBGEYx993lIMfxR4kF/+AKMqBg7K1ajelLEWK025tKOTnjVykuAklNgcHuzmQg4MiTtz4Aza3TeGActDr+45nZt41s7tmBwdbn2l35933M9/3eead5UII9tsvjf3FK2uSiGgBKdDtZW6JllXA2DG4A3teRM7LZQl2QRuY9irScghGQRXb5KteRVoOQSMY0895FGkk8JHgSBH0gllzpAeRkeSRBAEwQIIZUJMxukARly3GOZdpivA5DnpAM2iQ5zFx3KE55DIugT6cT+a7PTTlKt9xHAT74AzU6vWJiEVQ4iWR9uXHhkgu3QhYB/JK3zBZE6WoBkE3Ivt9YolOQBd9uwJedQFjC+A8XyJ1J3G+4zNFV+CCmmMejINnY8sQG0wVzZnFlnW2Fd55ezGaIUAtPgzqwAsmv8mYMCLW8DaqdyY1g73w+RMFQRh0glse1RtDHRvDcdJp6XInsScKU3u3gnukSfCoOcoH0YPa3jif/P7zxEqUoPqUkWBZ398YqwdTNHqLdu+YuyTONToEl+AaTIAhUE61qwTtuJC0e4klkssQAn7QTy3+BA5AhRQgfdoovHvJp6iYli5EdTqlBKZA7a7CJFaiHdABUqBUFfyMxBJt06PBrwpskn/xl+hDgAEAH0j0b9rsgVUAAAAASUVORK5CYII='; + var UIControlMeasure = function(ui, visible, visibleLock) { this.ui = ui; @@ -16,11 +19,11 @@ var UIControlMeasure = function(ui, visible, visibleLock) { + '' + + ' src="' + UIControlMeasureIcon + '">' + '' + + ' src="' + UIControlMeasureIcon2 + '">' + '
' + '
' @@ -73,24 +76,31 @@ var UIControlMeasure = function(ui, visible, visibleLock) { clearButton.on('click', this.onClear.bind(this)); clearButton.on('dblclick', this.onDoNothing.bind(this)); + this.toolButtons = []; var toolButton = this.control.getElement('vts-measure-position'); toolButton.on('click', this.onTool.bind(this, 0)); toolButton.on('dblclick', this.onDoNothing.bind(this)); + this.toolButtons.push(toolButton); toolButton = this.control.getElement('vts-measure-length'); toolButton.on('click', this.onTool.bind(this, 1)); toolButton.on('dblclick', this.onDoNothing.bind(this)); + this.toolButtons.push(toolButton); toolButton = this.control.getElement('vts-measure-track'); toolButton.on('click', this.onTool.bind(this, 2)); toolButton.on('dblclick', this.onDoNothing.bind(this)); + this.toolButtons.push(toolButton); toolButton = this.control.getElement('vts-measure-area'); toolButton.on('click', this.onTool.bind(this, 3)); toolButton.on('dblclick', this.onDoNothing.bind(this)); + this.toolButtons.push(toolButton); toolButton = this.control.getElement('vts-measure-volume'); toolButton.on('click', this.onTool.bind(this, 4)); toolButton.on('dblclick', this.onDoNothing.bind(this)); + this.toolButtons.push(toolButton); toolButton = this.control.getElement('vts-measure-metric'); toolButton.on('click', this.onTool.bind(this, 5)); toolButton.on('dblclick', this.onDoNothing.bind(this)); + this.toolButtons.push(toolButton); this.metricButton = toolButton; @@ -329,6 +339,12 @@ UIControlMeasure.prototype.onTool = function(tool) { this.compute.setStyle('display', 'none'); + for (var i = 0; i < 5; i++) { + this.toolButtons[i].setClass('vts-measure-tools-button'); + } + + this.toolButtons[tool].setClass('vts-measure-tools-button-selected'); + var map = this.browser.getMap(); if (map) { map.redraw(); @@ -446,47 +462,53 @@ UIControlMeasure.prototype.onCompute = function(button) { } if (this.tool == 4) { - var geodata = map.createGeodata(); - geodata.addPolygon(this.navCoords, [], null, 'fix', {}, 'tmp-polygon'); - geodata.processHeights('node-by-lod', 62, (function(){ - if (this.navCoords.length) { + var center = [0,0,0]; - space = ' '; + for (i = 0, li = this.navCoords.length; i < li; i++) { + coords = map.convertCoordsFromNavToPhys(this.navCoords[i], 'fix'); + center[0] += coords[0]; + center[1] += coords[1]; + center[2] += coords[2]; + } - for (i = 0, li = ('' + this.counter).length; i < li; i++) { - space += ' '; + center[0] /= li; + center[1] /= li; + center[2] /= li; + + var radius = 0, dx, dy, dz, distance; + + for (i = 0, li = this.navCoords.length; i < li; i++) { + coords = map.convertCoordsFromNavToPhys(this.navCoords[i], 'fix'); + dx = (center[0] - coords[0]); + dy = (center[1] - coords[1]); + dz = (center[2] - coords[2]); + distance = Math.sqrt(dx*dx + dy*dy + dz*dz); + + if (distance > radius) { + radius = distance; } + } - str = space + '------------------------'; + var geodata = map.createGeodata(); - var center = [0,0,0]; + if (radius > 30000) { + geodata.addPolygon3(this.navCoords, [], null, 'fix', {}, 'tmp-polygon'); + } else { + geodata.addPolygon(this.navCoords, [], null, 'fix', {}, 'tmp-polygon'); + } - for (i = 0, li = this.navCoords.length; i < li; i++) { - coords = map.convertCoordsFromNavToPhys(this.navCoords[i], 'fix'); - center[0] += coords[0]; - center[1] += coords[1]; - center[2] += coords[2]; - } + geodata.processHeights('node-by-lod', 62, (function(){ - center[0] /= li; - center[1] /= li; - center[2] /= li; + if (this.navCoords.length) { - var radius = 0, dx, dy, dz, distance; + space = ' '; - for (i = 0, li = this.navCoords.length; i < li; i++) { - coords = map.convertCoordsFromNavToPhys(this.navCoords[i], 'fix'); - dx = (center[0] - coords[0]); - dy = (center[1] - coords[1]); - dz = (center[2] - coords[2]); - distance = Math.sqrt(dx*dx + dy*dy + dz*dz); - - if (distance > radius) { - radius = distance; - } + for (i = 0, li = ('' + this.counter).length; i < li; i++) { + space += ' '; } + str = space + '------------------------'; var poly = geodata.extractGeometry('tmp-polygon'); @@ -496,35 +518,22 @@ UIControlMeasure.prototype.onCompute = function(button) { faces[i] = poly.getElement(i); } - //TODO: build octree - //TODO: extract meshes and build octree - - //var terrain = map.getCurrentGeometry(); var renderer = this.browser.getRenderer(); - //var octree = renderer.buildOctreeFromGeometry(terrain); - var x, y, north, east; coords = map.convertCoordsFromPhysToNav(center, 'fix'); - //map.getSurfaceAreaGeometry(coords, radius, 'lod', 21, false, false); - var texelSize = radius * 0.0030; //0.15 texel size for 100m diameter - - //map.getSurfaceAreaGeometry(coords, radius, 'texelSize', texelSize, false, false); var core = this.browser.getCore(); - var traceVolumeCall = (function(terrain){ - //str += '\n' + space + 'data loaded'; str = listElement.value; str = str.substr(0, str.lastIndexOf('loading data ...')); str += 'computation progress: 0%'; if (!terrain) { str += '\n some error ocurred. Try it again.'; - this.counter++; return; } @@ -543,8 +552,6 @@ UIControlMeasure.prototype.onCompute = function(button) { var y = -steps; - //for (y = -steps; y <= steps; y++) { - var traceVolumeLine = (function(){ for (x = -steps; x <= steps; x++) { @@ -584,10 +591,17 @@ UIControlMeasure.prototype.onCompute = function(button) { str += 'computation progress: ' + (((y + steps) / (steps*2))*100).toFixed(1) + ' %'; } else { str = str.substr(0, str.lastIndexOf('computation progress:')); - str += 'volume above: ' + volumeAbove.toFixed(2) + ' m\u00B3'; - str += '\n' + space + 'volume below: ' + volumeBelow.toFixed(2) + ' m\u00B3'; - str += '\n' + space + 'volume combined: ' + (volumeAbove + volumeBelow).toFixed(2) + ' m\u00B3' + '\n'; - this.counter++; + + if (this.metric) { + str += 'volume above: ' + volumeAbove.toFixed(2) + ' m\u00B3'; + str += '\n' + space + 'volume below: ' + volumeBelow.toFixed(2) + ' m\u00B3'; + str += '\n' + space + 'volume combined: ' + (volumeAbove + volumeBelow).toFixed(2) + ' m\u00B3' + '\n'; + } else { + var yd2m = 0.764554857984; + str += 'volume above: ' + (volumeAbove/yd2m).toFixed(2) + ' yd\u00B3'; + str += '\n' + space + 'volume below: ' + (volumeBelow/yd2m).toFixed(2) + ' yd\u00B3'; + str += '\n' + space + 'volume combined: ' + ((volumeAbove + volumeBelow)/yd2m).toFixed(2) + ' yd\u00B3' + '\n'; + } } if (str) { @@ -618,8 +632,9 @@ UIControlMeasure.prototype.onCompute = function(button) { } }).bind(this)); - } + this.counter++; + } } if (str) { @@ -788,7 +803,7 @@ UIControlMeasure.prototype.onMapUpdate = function() { points3.push(map.convertCoordsFromNavToCanvas(points2[i], "fix")); } - if (this.tool == 3) { + if (this.tool == 3 || this.tool == 4) { points3.push(map.convertCoordsFromNavToCanvas(points2[0], "fix")); } @@ -858,5 +873,5 @@ UIControlMeasure.prototype.getTextNumber = function(value) { } }; -export default UIControlMeasure; +export {UIControlMeasure , UIControlMeasureIcon, UIControlMeasureIcon2}; diff --git a/src/browser/ui/ui.js b/src/browser/ui/ui.js index ada14aa7..d45f4469 100755 --- a/src/browser/ui/ui.js +++ b/src/browser/ui/ui.js @@ -19,11 +19,12 @@ import UIControlSpace_ from './control/space'; import UIControlSearch_ from './control/search'; import UIControlLink_ from './control/link'; import UIControlGithub_ from './control/github'; -import UIControlMeasure_ from './control/measure'; import UIControlLayers_ from './control/layers'; import UIControlFallback_ from './control/fallback'; import UIControlPopup_ from './control/popup'; import UIControlLoading_ from './control/loading'; +import { UIControlMeasure as UIControlMeasure_ } from './control/measure'; +import UIControlMeasureLite_ from './control/measure-lite'; //get rid of compiler mess var UIControlCompass = UIControlCompass_; @@ -35,6 +36,7 @@ var UIControlSearch = UIControlSearch_; var UIControlLink = UIControlLink_; var UIControlGithub = UIControlGithub_; var UIControlMeasure = UIControlMeasure_; +var UIControlMeasureLite = UIControlMeasureLite_; var UIControlLayers = UIControlLayers_; var UIControlFallback = UIControlFallback_; var UIControlPopup = UIControlPopup_; @@ -81,6 +83,7 @@ UI.prototype.init = function() { this.link = new UIControlLink(this, (!loading && this.config.controlLink), loading); this.github = new UIControlGithub(this, (!loading && this.config.controlGithub), loading); this.measure = new UIControlMeasure(this, (!loading && this.config.controlMeasure), loading); + this.measure2 = new UIControlMeasureLite(this, (!loading && this.config.controlMeasureLite), loading); //this.navigator = new UIControlNavigation(this, this.config.controlNavigator); this.layers = new UIControlLayers(this, (!loading && this.config.controlLayers), loading); this.fallback = new UIControlFallback(this); diff --git a/src/core/core.js b/src/core/core.js index ffe42328..d7762e06 100755 --- a/src/core/core.js +++ b/src/core/core.js @@ -523,7 +523,7 @@ string getCoreVersion() */ function getCoreVersion(full) { - return (full ? 'Core: ' : '') + '2.15.17'; + return (full ? 'Core: ' : '') + '2.16.0'; } From 62705c781ec018fbbcbce7047a7c4aa76c187c2d Mon Sep 17 00:00:00 2001 From: David Levinsky Date: Mon, 1 Oct 2018 16:47:15 +0200 Subject: [PATCH 20/20] fixed multipoint feature in geojson --- src/core/map/geodata-import/geojson.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/core/map/geodata-import/geojson.js b/src/core/map/geodata-import/geojson.js index ed1d8ad4..fb0e6b30 100644 --- a/src/core/map/geodata-import/geojson.js +++ b/src/core/map/geodata-import/geojson.js @@ -21,7 +21,7 @@ MapGeodataImportGeoJSON.prototype.processGeometry = function(geometry, feature) break; case 'MultiPoint': - this.builder.addPointArray(cords, this.heightMode, feature['properties'], feature['properties'] ? feature['properties']['id'] : null, this.srs); + this.builder.addPointArray(coords, this.heightMode, feature['properties'], feature['properties'] ? feature['properties']['id'] : null, this.srs); break; case 'LineString':