From 6b711a6df20c4ed8d6b93af822dacb4a7794d425 Mon Sep 17 00:00:00 2001 From: David Levinsky Date: Mon, 27 Mar 2017 16:20:56 +0200 Subject: [PATCH] Support for hover and click events in geodata (#3) * click and hover events in geodata implemented --- .gitignore | 0 src/core/core.js | 8 +- src/core/map/draw.js | 12 +-- .../geodata-processor/worker-linestring.js | 3 +- src/core/map/geodata-processor/worker-main.js | 83 ++++++++++--------- .../map/geodata-processor/worker-style.js | 8 +- src/core/map/interface.js | 4 +- src/core/map/map.js | 56 +++++++++---- src/core/renderer/draw.js | 17 +++- src/core/renderer/gpu/group.js | 33 ++++++-- src/core/renderer/renderer.js | 5 +- 11 files changed, 150 insertions(+), 79 deletions(-) mode change 100644 => 100755 .gitignore diff --git a/.gitignore b/.gitignore old mode 100644 new mode 100755 diff --git a/src/core/core.js b/src/core/core.js index 9b967590..be732b4f 100755 --- a/src/core/core.js +++ b/src/core/core.js @@ -241,12 +241,16 @@ Melown.Core.prototype.on = function(name_, listener_) { // private -Melown.Core.prototype.callListener = function(name_, event_) { +Melown.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_); } } + + if (log_) { + console.log("event " + name_ + ": " + JSON.stringify(event_)); + } }; // private @@ -268,7 +272,7 @@ string getCoreVersion() */ Melown.getCoreVersion = function(full_) { - return (full_ ? "Core: " : "") + "1.94"; + return (full_ ? "Core: " : "") + "1.95"; }; diff --git a/src/core/map/draw.js b/src/core/map/draw.js index 4203fb12..1e27aa57 100755 --- a/src/core/map/draw.js +++ b/src/core/map/draw.js @@ -11,7 +11,7 @@ Melown.Map.prototype.draw = function(skipFreeLayers_, projected_, camInfo_) { var cameraPos_ = this.cameraPosition_; if (this.freeLayersHaveGeodata_ && this.drawChannel_ == 0) { - this.renderer_.drawGpuJobs(); + this.renderer_.clearJobBuffer(); } if (this.drawEarth_) { @@ -99,8 +99,8 @@ Melown.Map.prototype.draw = function(skipFreeLayers_, projected_, camInfo_) { if ((this.replay_.drawFreeTiles_ && this.replay_.drawnFreeTiles_) || (this.replay_.drawLoaded_ && this.replay_.loaded_)) { - if (this.freeLayersHaveGeodata_) { - this.renderer_.clearJobBuffer(); + if (this.freeLayersHaveGeodata_ && this.drawChannel_ == 0) { + this.renderer_.drawGpuJobs(); } } @@ -150,7 +150,7 @@ Melown.Map.prototype.draw = function(skipFreeLayers_, projected_, camInfo_) { for (var i = 0, li = this.freeLayerSequence_.length; i < li; i++) { var layer_ = this.freeLayerSequence_[i]; if (layer_.ready_ && layer_.tree_ && - (!layer_.geodata_ || (layer_.stylesheet_ && layer_.stylesheet_.isReady())) ) { + (!layer_.geodata_ || (layer_.stylesheet_ && layer_.stylesheet_.isReady())) && this.drawChannel_ == 0) { if (layer_.type_ == "geodata") { this.drawMonoliticGeodata(layer_); @@ -303,8 +303,8 @@ Melown.Map.prototype.draw = function(skipFreeLayers_, projected_, camInfo_) { } }*/ - if (this.freeLayersHaveGeodata_) { - this.renderer_.clearJobBuffer(); + if (this.freeLayersHaveGeodata_ && this.drawChannel_ == 0) { + this.renderer_.drawGpuJobs(); } } } diff --git a/src/core/map/geodata-processor/worker-linestring.js b/src/core/map/geodata-processor/worker-linestring.js index e09960ec..142eb8f1 100755 --- a/src/core/map/geodata-processor/worker-linestring.js +++ b/src/core/map/geodata-processor/worker-linestring.js @@ -532,7 +532,8 @@ var processLineStringPass = function(lineString_, lod_, style_, zIndex_, eventIn type_: messageData_["type"], color_ : lineColor_, zIndex_ : zIndex_, - zOffset_ : zbufferOffset_ + zOffset_ : zbufferOffset_, + state_ : hitState_ }); if (normalBuffer_) { diff --git a/src/core/map/geodata-processor/worker-main.js b/src/core/map/geodata-processor/worker-main.js index c99fc08a..12cc2d8c 100755 --- a/src/core/map/geodata-processor/worker-main.js +++ b/src/core/map/geodata-processor/worker-main.js @@ -31,32 +31,27 @@ var processLayerFeaturePass = function(type_, feature_, lod_, layer_, zIndex_, e }; -var processLayerFeature = function(type_, feature_, lod_, layer_, featureIndex_) { - //var layer_ = getLayer(feature_["style"], type_, featureIndex_); - var visible_ = getLayerPropertyValue(layer_, "visible", feature_, lod_); - var zIndex_ = getLayerPropertyValue(layer_, "z-index", feature_, lod_); - - if (visible_ == false) { - return; - } - - var eventInfo_ = feature_.properties_; +var processFeature = function(type_, feature_, lod_, featureIndex_, featureType_, group_) { + + //loop layers + for (var key_ in stylesheetLayers_) { + var layer_ = stylesheetLayers_[key_]; + var filter_ = getLayerPropertyValue(layer_, "filter", feature_, lod_); - var hoverLayerId_ = getLayerPropertyValue(layer_, "hover-style", feature_, lod_); - var hoverlayer_ = (hoverLayerId_ != "") ? getLayer(hoverLayerId_, type_, featureIndex_) : null; + feature_.properties_ = feature_["properties"] || {}; - if (hoverlayer_ != null) { - hitState_ = 1; - processLayerFeaturePass(type_, feature_, lod_, layer_, zIndex_, eventInfo_); - hitState_ = 2; - processLayerFeaturePass(type_, feature_, lod_, hoverlayer_, zIndex_, eventInfo_); - } else { - hitState_ = 0; - processLayerFeaturePass(type_, feature_, lod_, layer_, zIndex_, eventInfo_); + if (feature_["id"]) { + feature_.properties_["#id"] = feature_["id"]; + } + + if (!filter_ || getFilterResult(filter_, feature_, featureType_, group_)) { + processLayerFeature(type_, feature_, lod_, layer_, featureIndex_); + } } +} - - var multiPass_ = getLayerPropertyValue(layer_, "next-pass", feature_, lod_); +var processLayerFeatureMultipass = function(type_, feature_, lod_, layer_, featureIndex_, eventInfo_) { + var multiPass_ = getLayerPropertyValue(layer_, "next-pass", feature_, lod_); if (multiPass_ != null) { for (var i = 0, li = multiPass_.length; i < li; i++) { @@ -69,37 +64,51 @@ var processLayerFeature = function(type_, feature_, lod_, layer_, featureIndex_) continue; } - hoverLayerId_ = getLayerPropertyValue(layer_, "hover-style", feature_, lod_); + hoverLayerId_ = getLayerPropertyValue(layer_, "hover-layer", feature_, lod_); hoverlayer_ = (hoverLayerId_ != "") ? getLayer(hoverLayerId_, type_, featureIndex_) : null; if (hoverlayer_ != null) { + var lastHitState_ = hitState_; hitState_ = 1; processLayerFeaturePass(type_, feature_, lod_, layer_, zIndex_, eventInfo_); hitState_ = 2; processLayerFeaturePass(type_, feature_, lod_, hoverlayer_, zIndex_, eventInfo_); + hitState_ = lastHitState_; } else { - hitState_ = 0; + //hitState_ = 0; processLayerFeaturePass(type_, feature_, lod_, layer_, zIndex_, eventInfo_); } } } - }; -var processFeature = function(type_, feature_, lod_, featureIndex_, featureType_, group_) { - - //loop layers - for (var key_ in stylesheetLayers_) { - var layer_ = stylesheetLayers_[key_]; - var filter_ = getLayerPropertyValue(layer_, "filter", feature_, lod_); - feature_.properties_ = feature_["properties"] || {}; - - if (!filter_ || getFilterResult(filter_, feature_, featureType_, group_)) { - processLayerFeature(type_, feature_, lod_, layer_, featureIndex_); - } +var processLayerFeature = function(type_, feature_, lod_, layer_, featureIndex_) { + //var layer_ = getLayer(feature_["style"], type_, featureIndex_); + var visible_ = getLayerPropertyValue(layer_, "visible", feature_, lod_); + var zIndex_ = getLayerPropertyValue(layer_, "z-index", feature_, lod_); + + if (visible_ == false) { + return; + } + + var eventInfo_ = feature_.properties_; + + var hoverLayerId_ = getLayerPropertyValue(layer_, "hover-layer", feature_, lod_); + var hoverlayer_ = (hoverLayerId_ != "") ? getLayer(hoverLayerId_, type_, featureIndex_) : null; + + if (hoverlayer_ != null) { + hitState_ = 1; + processLayerFeaturePass(type_, feature_, lod_, layer_, zIndex_, eventInfo_); + processLayerFeatureMultipass(type_, feature_, lod_, layer_, featureIndex_, eventInfo_); + hitState_ = 2; + processLayerFeaturePass(type_, feature_, lod_, hoverlayer_, zIndex_, eventInfo_); + processLayerFeatureMultipass(type_, feature_, lod_, hoverlayer_, featureIndex_, eventInfo_); + } else { + hitState_ = 0; + processLayerFeaturePass(type_, feature_, lod_, layer_, zIndex_, eventInfo_); + processLayerFeatureMultipass(type_, feature_, lod_, layer_, featureIndex_, eventInfo_); } - }; diff --git a/src/core/map/geodata-processor/worker-style.js b/src/core/map/geodata-processor/worker-style.js index 10c5943f..34ef15f8 100755 --- a/src/core/map/geodata-processor/worker-style.js +++ b/src/core/map/geodata-processor/worker-style.js @@ -183,7 +183,7 @@ var inheritLayer = function(layerId_, layer_, layerData_, stylesheetLayersData_, //do we need inherite Layer? if (layerData_["inherit"] != null) { //get inherited Layer - var LayerToInherit_ = stylesheetLayersData_["Layers"][layerData_["inherit"]]; + var LayerToInherit_ = stylesheetLayersData_["layers"][layerData_["inherit"]]; if (LayerToInherit_ != null) { @@ -379,7 +379,7 @@ var validateValue = function(layerId_, key_, value_, type_, arrayLength_, min_, typeof valueItem_[0] == "number" && typeof valueItem_[1] == "string") { - if (stylesheetLayersData_["Layers"][valueItem_[1]] == null) { + if (stylesheetLayersData_["layers"][valueItem_[1]] == null) { } @@ -551,7 +551,7 @@ var validateLayerPropertyValue = function(layerId_, key_, value_) { case "zbuffer-offset": return validateValue(layerId_, key_, value_, "object", 3, 0, Number.MAX_VALUE); break; case "hover-event": return validateValue(layerId_, key_, value_, "boolean"); break; - case "hover-style": return validateValue(layerId_, key_, value_, "string"); break; + case "hover-layer": return validateValue(layerId_, key_, value_, "string"); break; case "enter-event": return validateValue(layerId_, key_, value_, "boolean"); break; case "leave-event": return validateValue(layerId_, key_, value_, "boolean"); break; case "click-event": return validateValue(layerId_, key_, value_, "boolean"); break; @@ -617,7 +617,7 @@ var getDefaultLayerPropertyValue = function(key_) { case "zbuffer-offset": return [0,0,0]; case "hover-event": return false; - case "hover-style": return ""; + case "hover-layer": return ""; case "enter-event": return false; case "leave-event": return false; case "click-event": return false; diff --git a/src/core/map/interface.js b/src/core/map/interface.js index 138c63d8..e29dc484 100755 --- a/src/core/map/interface.js +++ b/src/core/map/interface.js @@ -376,11 +376,11 @@ Melown.MapInterface.prototype.getStats = function() { }; Melown.MapInterface.prototype.click = function(screenX_, screenY_, state_) { - map_.click(screenX_, screenY_, state_); + this.map_.click(screenX_, screenY_, state_); }; Melown.MapInterface.prototype.hover = function(screenX_, screenY_, persistent_, state_) { - map_.hover(screenX_, screenY_, persistent_, state_); + this.map_.hover(screenX_, screenY_, persistent_, state_); }; Melown.MapPositionInterface = Melown.MapPosition; diff --git a/src/core/map/map.js b/src/core/map/map.js index a6561bc0..0fd16017 100755 --- a/src/core/map/map.js +++ b/src/core/map/map.js @@ -936,25 +936,46 @@ Melown.Map.prototype.getHitCoords = function(screenX_, screenY_, mode_, lod_) { Melown.Map.prototype.hitTestGeoLayers = function(screenX_, screenY_, mode_) { if (this.geoHitMapDirty_) { if (this.freeLayersHaveGeodata_) { + this.renderer_.gpu_.setState(this.drawTileState_); this.renderer_.switchToFramebuffer("geo"); + //this.renderer_.onlyHitLayers_ = true; this.renderer_.drawGpuJobs(); + //this.renderer_.onlyHitLayers_ = false; this.renderer_.switchToFramebuffer("base"); this.geoHitMapDirty_ = false; } } + if (!this.freeLayersHaveGeodata_) { + this.lastHoverFeature_ = null; + this.lastHoverFeatureId_ = null; + this.hoverFeature_ = null; + this.hoverFeatureId_ = null; + + return [null, false, []]; + } + var res_ = this.renderer_.hitTestGeoLayers(screenX_, screenY_, mode_); if (res_[0]) { //do we hit something? //console.log(JSON.stringify([id_, JSON.stringify(this.hoverFeatureList_[id_])])); + + var id_ = (res_[1]) + (res_[2]<<8); + var elementId_ = (res_[3]) + (res_[4]<<8); + + var feature_ = this.hoverFeatureList_[id_]; if (mode_ == "hover") { this.lastHoverFeature_ = this.hoverFeature_; this.lastHoverFeatureId_ = this.hoverFeatureId_; - this.hoverFeature_ = null; - this.hoverFeatureId_ = null; - this.hoverFeature_ = this.hoverFeatureList_[id_]; - this.hoverFeatureId_ = (this.hoverFeature_ != null) ? this.hoverFeature_[0]["id"] : null; + + if (feature_ && feature_[3] == true) { + this.hoverFeature_ = feature_; + this.hoverFeatureId_ = (feature_ != null) ? feature_[0]["#id"] : null; + } else { + this.hoverFeature_ = null; + this.hoverFeatureId_ = null; + } var relatedEvents_ = []; @@ -971,18 +992,17 @@ Melown.Map.prototype.hitTestGeoLayers = function(screenX_, screenY_, mode_) { } if (this.hoverFeature_ != null && this.hoverFeature_[3] == true) { - return [this.hoverFeature_, surfaceHit_, relatedEvents_]; + return [this.hoverFeature_, true, relatedEvents_]; } else { return [null, false, relatedEvents_]; } } if (mode_ == "click") { - var feature_ = this.hoverFeatureList_[id_]; //this.hoverFeatureId_ = (this.hoverFeature_ != null) ? this.hoverFeature_["id"] : null; - if (feature_ != null && this.hoverFeature_ != null && this.hoverFeature_[2] == true) { - return [feature_, surfaceHit_, []]; + if (feature_ != null && feature_[2] == true) { + return [feature_, true, []]; } else { return [null, false, []]; } @@ -1032,6 +1052,10 @@ Melown.Map.prototype.drawMap = function() { this.renderer_.dirty_ = true; this.renderer_.drawFog_ = this.drawFog_; + this.renderer_.hoverFeatureCounter_ = 0; + this.renderer_.hoverFeatureList_ = this.hoverFeatureList_; + this.renderer_.hoverFeature_ = this.hoverFeature_; + this.renderer_.cameraPosition_ = this.cameraPosition_; this.renderer_.cameraOrientation_ = this.position_.getOrientation(); this.renderer_.cameraTiltFator_ = Math.cos(Melown.radians(this.renderer_.cameraOrientation_[1])); @@ -1180,8 +1204,8 @@ Melown.Map.prototype.update = function() { var result_ = this.hitTestGeoLayers(this.hoverEvent_[0], this.hoverEvent_[1], "hover"); if (result_[1] == true && result_[0] != null) { - this.core_.callListener("geo-feature-hover", {"feature": result_[0][0], "screen-pos":this.project(result_[0][1]), - "scene-pos":result_[0][1], "state": this.hoverEvent_[3] }); + this.core_.callListener("geo-feature-hover", {"feature": result_[0][0], "canvas-coords":this.renderer_.project2(result_[0][1], this.camera_.getMvpMatrix()), + "camera-coords":result_[0][1], "state": this.hoverEvent_[3] }, true); } var relatedEvents_ = result_[2]; @@ -1192,13 +1216,13 @@ Melown.Map.prototype.update = function() { switch(event_[0]) { case "enter": - this.core_.callListener("geo-feature-enter", {"feature": event_[0], "screen-pos":this.project(event_[1]), - "scene-pos":event_[1], "state": this.hoverEvent_[3] }); + this.core_.callListener("geo-feature-enter", {"feature": event_[1][0], "canvas-coords":this.renderer_.project2(event_[1][1], this.camera_.getMvpMatrix()), + "camera-coords":event_[1][1], "state": this.hoverEvent_[3] }, true); break; case "leave": - this.core_.callListener("geo-feature-leave", {"feature":event_[0], "screen-pos":this.project(event_[1]), - "scene-pos":event_[1], "state": this.hoverEvent_[3] }); + this.core_.callListener("geo-feature-leave", {"feature":event_[1][0], "canvas-coords":this.renderer_.project2(event_[1][1], this.camera_.getMvpMatrix()), + "camera-coords":event_[1][1], "state": this.hoverEvent_[3] }, true); break; } } @@ -1214,8 +1238,8 @@ Melown.Map.prototype.update = function() { var result_ = this.hitTestGeoLayers(this.clickEvent_[0], this.clickEvent_[1], "click"); if (result_[1] == true && result_[0] != null) { - this.core_.callListener("geo-feature-click", {"feature": result_[0][0], "screen-pos":this.project(result_[0][1]), - "scene-pos":result_[0][1], "state": this.hoverEvent_[3] }); + this.core_.callListener("geo-feature-click", {"feature": result_[0][0], "canvas-coords":this.renderer_.project2(result_[0][1], this.camera_.getMvpMatrix()), + "camera-coords":result_[0][1], "state": this.clickEvent_[2] }, true); } this.clickEvent_ = null; diff --git a/src/core/renderer/draw.js b/src/core/renderer/draw.js index 8da8c7a0..b82e0249 100755 --- a/src/core/renderer/draw.js +++ b/src/core/renderer/draw.js @@ -600,6 +600,8 @@ Melown.Renderer.prototype.drawGpuJobs = function() { Melown.StencilLineState_ = this.gpu_.createState({blend_:true, stencil_:true, culling_: false}); Melown.LineLabelState_ = this.gpu_.createState({blend_:true, culling_: false}); + Melown.StencilLineHitState_ = this.gpu_.createState({blend_:false, stencil_:true, culling_: false}); + Melown.LineLabelHitState_ = this.gpu_.createState({blend_:false, culling_: false}); var screenPixelSize_ = [1.0/this.curSize_[0], 1.0/this.curSize_[1]]; @@ -629,9 +631,18 @@ Melown.Renderer.prototype.drawGpuJobs = function() { } } - for (var j = 0; j < lj; j++) { - Melown.drawGpuJob(gpu_, gl_, this, buffer_[j], screenPixelSize_); - //buffer_[j] = null; + + if (this.onlyHitLayers_) { + for (var j = 0; j < lj; j++) { + if (buffer_[j].hitable_) { + Melown.drawGpuJob(gpu_, gl_, this, buffer_[j], screenPixelSize_); + } + } + } else { + for (var j = 0; j < lj; j++) { + Melown.drawGpuJob(gpu_, gl_, this, buffer_[j], screenPixelSize_); + //buffer_[j] = null; + } } //this.jobZBufferSize_[i] = 0; diff --git a/src/core/renderer/gpu/group.js b/src/core/renderer/gpu/group.js index f3a3a5b8..06040f2a 100755 --- a/src/core/renderer/gpu/group.js +++ b/src/core/renderer/gpu/group.js @@ -380,18 +380,18 @@ Melown.drawGpuJob = function(gpu_, gl_, renderer_, job_, screenPixelSize_) { } if (job_.state_ != 0) { - var id_ = job_.eventInfo_["id"]; + var id_ = job_.eventInfo_["#id"]; if (id_ != null && renderer_.hoverFeature_ != null) { if (job_.state_ == 1){ // 1 = no hover state - if (renderer_.hoverFeature_[0]["id"] == id_) { //are we hovering over feature? + if (renderer_.hoverFeature_[0]["#id"] == id_) { //are we hovering over feature? return; } } else { // 2 = hover state - if (renderer_.hoverFeature_[0]["id"] != id_) { //are we hovering over feature? + if (renderer_.hoverFeature_[0]["#id"] != id_) { //are we hovering over feature? return; } @@ -409,13 +409,19 @@ Melown.drawGpuJob = function(gpu_, gl_, renderer_, job_, screenPixelSize_) { if (hitmapRender_) { var c = renderer_.hoverFeatureCounter_; - color_ = [(c&255)/255, ((c>>8)&255)/255, ((c>>16)&255)/255, 1]; + //color_ = [(c&255)/255, ((c>>8)&255)/255, ((c>>16)&255)/255, 1]; + color_ = [(c&255)/255, ((c>>8)&255)/255, 0, 0]; renderer_.hoverFeatureList_[c] = [job_.eventInfo_, job_.center_, job_.clickEvent_, job_.hoverEvent_, job_.enterEvent_, job_.leaveEvent_]; renderer_.hoverFeatureCounter_++; } switch(job_.type_) { case "flat-line": + if (hitmapRender_) { + gpu_.setState(Melown.StencilLineHitState_); + } else { + gpu_.setState(Melown.StencilLineState_); + } gpu_.setState(Melown.StencilLineState_); var prog_ = renderer_.progLine_; @@ -438,8 +444,12 @@ Melown.drawGpuJob = function(gpu_, gl_, renderer_, job_, screenPixelSize_) { case "flat-tline": case "pixel-line": case "pixel-tline": - - gpu_.setState(Melown.StencilLineState_); + if (hitmapRender_) { + gpu_.setState(Melown.StencilLineHitState_); + } else { + gpu_.setState(Melown.StencilLineState_); + } + var prog_ = job_.program_; var texture_ = null; var textureParams_ = [0,0,0,0]; @@ -505,13 +515,17 @@ Melown.drawGpuJob = function(gpu_, gl_, renderer_, job_, screenPixelSize_) { break; case "line-label": + if (hitmapRender_) { + gpu_.setState(Melown.LineLabelHitState_); + } else { + gpu_.setState(Melown.LineLabelState_); + } var texture_ = hitmapRender_ ? renderer_.whiteTexture_ : renderer_.font_.texture_; //var yaw_ = Melown.radians(renderer_.cameraOrientation_[0]); //var forward_ = [-Math.sin(yaw_), Math.cos(yaw_), 0, 0]; - gpu_.setState(Melown.LineLabelState_); var prog_ = renderer_.progText_; gpu_.bindTexture(texture_); @@ -541,6 +555,11 @@ Melown.drawGpuJob = function(gpu_, gl_, renderer_, job_, screenPixelSize_) { case "icon": case "label": + if (hitmapRender_) { + gpu_.setState(Melown.LineLabelHitState_); + } else { + gpu_.setState(Melown.LineLabelState_); + } var texture_ = hitmapRender_ ? renderer_.whiteTexture_ : job_.texture_; diff --git a/src/core/renderer/renderer.js b/src/core/renderer/renderer.js index af6395fe..4b3a21c2 100755 --- a/src/core/renderer/renderer.js +++ b/src/core/renderer/renderer.js @@ -33,6 +33,9 @@ Melown.Renderer = function(core_, div_, onUpdate_, onResize_, config_) { this.onResizeCall_ = onResize_; this.math_ = Melown.Math; + this.hoverFeatureCounter_ = 0; + this.hoverFeatureList_ = []; + this.touchSurfaceEvent_ = []; var rect_ = this.div_.getBoundingClientRect(); @@ -410,7 +413,7 @@ Melown.Renderer.prototype.switchToFramebuffer = function(type_) { this.onlyHitLayers_ = true; this.gpu_.clear(); - this.updateCamera(); + this.camera_.update(); break; } };