Mercurial > hg > STI-GWT
diff war/scripts/sti/STIMap.js @ 3:cf06b77a8bbd
Committed branch of the e4D repos sti-gwt branch 16384.
git-svn-id: http://dev.dariah.eu/svn/repos/eu.dariah.de/ap1/sti-gwt-dariah-geobrowser@36 f2b5be40-def6-11e0-8a09-b3c1cc336c6b
author | StefanFunk <StefanFunk@f2b5be40-def6-11e0-8a09-b3c1cc336c6b> |
---|---|
date | Tue, 17 Jul 2012 13:34:40 +0000 |
parents | |
children | 517a6422d1bd |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/war/scripts/sti/STIMap.js Tue Jul 17 13:34:40 2012 +0000 @@ -0,0 +1,1372 @@ +/** + * defines the map component of the Spatio Temporal Interface. + * it builds a map context with the OpenLayers JavaScript Framework + * @param {STICore} core the sti core component, the map component has to deal with + * @param {String} window the div id for the window div for the container of the map widget + * @param {String} container the div id for the container of the map widget + * + * @constructor + */ +function STIMap(core,window,container){ + + this.core = core; + this.window = window; + this.container = container; + this.openlayersMap; + this.baseLayers; + this.objectLayer; + this.drilldownLayer; + this.connectionLayer; + + this.drawPolygon; + this.drawCircle; + this.selectCountry; + this.dragArea; + this.selectFeature; + this.navigation; + + this.polygon; + + this.selectElementsPlace; + this.pointToAdd; + this.displayPointSet; + + this.popup; + this.lastHovered; + + this.xShift; + this.yShift; + this.showConnections; + this.connections; + + this.mapMouseMove; + this.addCrossImage; + + this.initialize(); + +} + +STIMap.prototype = { + + /** + * initializes the map for the Spatio Temporal Interface. + * it includes setting up all layers of the map and defines all map specific interaction possibilities + */ + initialize: function(){ + + //OpenLayers.ProxyHost = "/cgi-bin/proxy.cgi?url="; + var map = this; + + this.pointSelected = false; + this.showConnections = false; + this.labelDivs = []; + this.polygons = []; + + var window = document.getElementById(this.window); + + this.toolbar = document.createElement("div"); + this.toolbar.setAttribute('class','mapToolbar'); + window.appendChild(this.toolbar); + + this.drag = document.createElement("div"); + this.drag.title = "Drag Area: drag a selection area and with left mouse-button"; + this.drag.setAttribute('class','dragRange'); + this.toolbar.appendChild(this.drag); + this.drag.onclick = function(evt){ + if( map.activeControl == "drag" ){ + map.deactivate("drag"); + } + else { + map.deactivate(map.activControl); + map.activate("drag"); + } + } + + var zoom = document.createElement("div"); + zoom.title = "Zoom into selection. To undo, go a step back in the History."; + zoom.setAttribute('class','zoomRange'); + this.toolbar.appendChild(zoom); + zoom.onclick = function(){ + map.core.refine(); + } + + var cancel = document.createElement("div"); + cancel.title = "Clear Selection"; + cancel.setAttribute('class','cancelRange'); + this.toolbar.appendChild(cancel); + cancel.onclick = function(){ + map.core.reset(); + } + + this.controlLockDiv = document.createElement("div"); + this.controlLockDiv.setAttribute('class','controlLock'); + window.appendChild(this.controlLockDiv); + + this.leftTagCloudDiv = document.createElement("div"); + this.leftTagCloudDiv.setAttribute('class','tagCloudDiv'); + window.appendChild(this.leftTagCloudDiv); + + this.rightTagCloudDiv = document.createElement("div"); + this.rightTagCloudDiv.setAttribute('class','tagCloudDiv'); + window.appendChild(this.rightTagCloudDiv); + + this.pointClickDiv = document.createElement("div"); + this.pointClickDiv.setAttribute('class','pointClickDiv'); + window.appendChild(this.pointClickDiv); + + var pointClickDivBackground = document.createElement("div"); + pointClickDivBackground.setAttribute('class','pointClickDivBackground'); + this.pointClickDiv.appendChild(pointClickDivBackground); + + this.objectLayer = new OpenLayers.Layer.Vector("Data Objects", { + projection: "EPSG:4326" + }); + this.connectionLayer = new OpenLayers.Layer.Vector("Connections", { + projection: "EPSG:4326" + }); + this.drilldownLayer = new OpenLayers.Layer.Vector("Drilldown", { + projection: "EPSG:4326" + }); + this.parseBaseLayers("layers.xml"); + + this.navigation = new OpenLayers.Control.Navigation({ + zoomWheelEnabled: true + }); + this.navigation.defaultDblClick = function(evt){ + var newCenter = this.map.getLonLatFromViewPortPx(evt.xy); + this.map.setCenter(newCenter, this.map.zoom + 1); + map.drawObjectLayer(true); + setMapZoom(this.map.zoom/this.map.numZoomLevels); + } + this.navigation.wheelUp = function(evt){ + this.wheelChange(evt, 1); + map.drawObjectLayer(true); + setMapZoom(this.map.zoom/this.map.numZoomLevels); + } + this.navigation.wheelDown = function(evt){ + this.wheelChange(evt, -1); + map.drawObjectLayer(true); + setMapZoom(this.map.zoom/this.map.numZoomLevels); + } + + var options = { + controls: [this.navigation, new OpenLayers.Control.ScaleLine()], + projection: new OpenLayers.Projection("EPSG:900913"), + displayProjection: new OpenLayers.Projection("EPSG:4326"), + units: "m", + minZoomLevel: 1, + maxZoomLevel: 17, + numZoomLevels: 17, + maxResolution: 78271.51695, + maxExtent: new OpenLayers.Bounds(-20037508.34, -20037508.34, 20037508.34, 20037508.34) + }; + this.openlayersMap = new OpenLayers.Map(this.container, options); + for (var i = 0; i < this.baseLayers.length; i++) + this.openlayersMap.addLayers([this.baseLayers[i]]); + this.openlayersMap.fractionalZoom = false; + this.activeControl = "navigate"; + + var bounds = new OpenLayers.Bounds(boundaries.minLon, boundaries.minLat, boundaries.maxLon, boundaries.maxLat); + var projectionBounds = bounds.transform(this.openlayersMap.displayProjection, this.openlayersMap.projection); + this.openlayersMap.zoomToExtent(projectionBounds); + this.openlayersMap.addLayers([this.connectionLayer, this.objectLayer, this.drilldownLayer]); + + // places the toolbar inside the window + var placeToolbar = function(){ + map.toolbar.style.visibility = "visible"; + var left = 0; + var top = 0; + if( map.polygons.length > 0 ){ + map.drag.style.visibility = "visible"; + for (var i = 0; i < map.polygons.length; i++){ + for (var j = 0; j < map.polygons[i].components.length; j++){ + var vertices = map.polygons[i].components[j].getVertices(); + for (var k = 0; k < vertices.length; k++){ + var lonlat = new OpenLayers.LonLat( vertices[k].x, vertices[k].y ); + var pixel = map.openlayersMap.getPixelFromLonLat(lonlat); + if( pixel.x > left ){ + left = pixel.x; + top = pixel.y; + } + } + } + } + map.toolbar.style.width = "69px"; + map.toolbar.style.left = left+"px"; + map.toolbar.style.top = (top-map.toolbar.offsetHeight/2)+"px"; + } + else { + map.drag.style.visibility = "hidden"; + map.toolbar.style.width = "47px"; + map.toolbar.style.left = (map.pointClickDiv.offsetLeft + map.pointClickDiv.offsetWidth - map.toolbar.offsetWidth)+"px"; + map.toolbar.style.top = map.pointClickDiv.offsetTop+"px"; + } + } + + this.openlayersMap.div.onmousedown = function(){ + map.toolbar.style.visibility = "hidden"; + map.drag.style.visibility = "hidden"; + } + this.openlayersMap.div.onmouseup = function(){ + if( map.polygons.length > 0 ){ + placeToolbar(); + } + } + + // manages selection of elements if a polygon was drawn + var drawnPolygonHandler = function(polygon){ + if( map.displayPointSet == undefined ){ + return; + } + map.polygon = polygon; + var polygonArea; + if (polygon instanceof OpenLayers.Geometry.Polygon) + polygonArea = new OpenLayers.Geometry.MultiPolygon([polygon]); + else + if (polygon instanceof OpenLayers.Geometry.MultiPolygon) + polygonArea = polygon; + var points = map.displayPointSet[Math.floor(map.openlayersMap.getZoom()+0.05)]; + var polygons = polygonArea.components; + var innerPoints = []; + for (var i = 0; i < points.length; i++) + for (var j = 0; j < polygons.length; j++) + if (polygons[j].containsPoint(points[i].pointFeature.geometry)) { + innerPoints.push(points[i]); + continue; + } + map.updateByPlace(innerPoints, 0); + map.drilldownLayer.addFeatures([new OpenLayers.Feature.Vector(polygon)]); + map.polygons = polygons; + placeToolbar(); + switchToNavigation(); + } + + // resets the core + var snapper = function(){ + map.core.reset(); + } + + if( this.core.props.polygonSelect ){ + this.drawPolygon = new OpenLayers.Control.DrawFeature(map.drilldownLayer, OpenLayers.Handler.Polygon, { + displayClass: "olControlDrawFeaturePolygon", + title: "Polygon Drilldown", + callbacks: { + "done": drawnPolygonHandler, + "create": snapper + } + }); + this.openlayersMap.addControl(this.drawPolygon); + } + + if( this.core.props.circleSelect ){ + this.drawCircle = new OpenLayers.Control.DrawFeature(map.drilldownLayer, OpenLayers.Handler.RegularPolygon, { + displayClass: "olControlDrawFeaturePolygon", + title: "Cirlce Drilldown", + handlerOptions: { + sides: 40 + }, + callbacks: { + "done": drawnPolygonHandler, + "create": snapper + } + }); + this.openlayersMap.addControl(this.drawCircle); + } + + if( this.core.props.polygonSelect || this.core.props.circleSelect ){ + this.dragArea = new OpenLayers.Control.DragFeature(map.drilldownLayer, { + onStart: function(){ + map.toolbar.style.visibility = "hidden"; + }, + onComplete: function(feature){ + drawnPolygonHandler(feature.geometry); + } + }); + this.openlayersMap.addControl(this.dragArea); + } + + if( this.core.props.historicMaps && this.core.props.countrySelect ){ + this.selectCountry = new OpenLayers.Control.GetFeature({ + protocol: OpenLayers.Protocol.WFS.fromWMSLayer(map.openlayersMap.baseLayer) + }); + this.selectCountry.events.register("featureselected", this, function(e){ + if (map.pointSelected){ + map.pointSelected = false; + } + else { + drawnPolygonHandler(e.feature.geometry); + } + }); + this.selectCountry.events.register("featureunselected", this, function(e){ + snapper(); + }); + this.openlayersMap.addControl(this.selectCountry); + } + + // changes selection between labels (click, hover) + var changeLabelSelection = function(point,label,update){ + if( update && map.lastLabel.div == label.div ){ + return; + } + var k = point.search; + var color0 = 'rgb('+colors[k].r0+','+colors[k].g0+','+colors[k].b0+')'; + var color1 = colors[k].hex; + if( update ){ + map.lastLabel.div.style.color = color0; + map.lastLabel.div.style.textShadow = "0 0 0.4em black, 0 0 0.4em black, 0 0 0.4em black, 0 0 0.4em "+color1; + map.lastLabel.div.style.textDecoration = "none"; + map.lastLabel.selected = false; + } + map.lastLabel = label; + label.selected = true; + label.div.style.color = color1; + label.div.style.textDecoration = "underline"; + label.div.style.textShadow = "0 0 0.1em white, 0 0 0.1em white, 0 0 0.1em white, 0 0 0.1em "+color1; +// label.div.style.textShadow = "0 0 0.4em black, 0 0 0.4em black, 0 0 0.4em black, 0 0 0.4em "+color1; + label.div.style.filter = "glow(color="+color1+", strength=3) DropShadow(Color=#292929, OffX=1, OffY=-1, Positive=1) DropShadow(Color=#292929, OffX=-1, OffY=-1, Positive=1) DropShadow(Color=#292929, OffX=1, OffY=1, Positive=1) DropShadow(Color=#292929, OffX=-1, OffY=1, Positive=1) blur(add=false, direction=0, strength=1) blur(add=false, direction=90, strength=1) blur(add=false, direction=180, strength=1) blur(add=false, direction=270, strength=1)"; + map.updateByPlaceLabel(point,label.elements,0); + } + + var getLevelOfDetail = function(){ + var zoom = map.openlayersMap.getZoom(); + if( zoom <= 1 ){ + return 0; + } + else if( zoom <= 3 ){ + return 1; + } + else if( zoom <= 8 ){ + return 2; + } + else { + return 3; + } + } + + // calculates the tag cloud + var calculateTagCloud = function(){ + var elements = map.lastHovered.elements; + var labels = []; + var levelOfDetail = getLevelOfDetail(); + for( var i=0; i<elements.length; i++ ){ + var found = false; + var label = elements[i].getPlace(levelOfDetail); + if( label == "" ){ + label = "unknown"; + } + for( var j=0; j<labels.length; j++ ){ + if( labels[j].place == label ){ + labels[j].elements.push(elements[i]); + found = true; + break; + } + } + if( !found ){ + labels.push( { place: label, elements: new Array(elements[i]) } ); + } + } + var sortBySize = function(label1, label2){ + if (label1.elements.length > label2.elements.length){ + return -1; + } + return 1; + } + labels.sort(sortBySize); + if( labels.length+1 > tagCloudLabels ){ + var c = []; + for( var i=tagCloudLabels-2; i<labels.length; i++ ){ + c = c.concat(labels[i].elements); + } + labels = labels.slice(0,tagCloudLabels-2); + labels.push( { place: "others", elements: c } ); + } + if( labels.length > 1 ){ + labels.push( { place: "all", elements: elements } ); + } + else if( labels[0].place == "unknown" ){ + labels[0].place = "all"; + } + map.labels = labels; + + var k = map.lastHovered.search; + var color = 'rgb('+colors[k].r0+','+colors[k].g0+','+colors[k].b0+')'; + var shadow = colors[k].hex;0 + var clickFunction = function(point,label){ + label.div.onclick = function(){ + if( map.pointSelected ){ + changeLabelSelection(point,label,true); + } + } + label.div.onmouseover = function(){ + if( map.pointSelected && !label.selected ){ + label.div.style.textShadow = "0 -1px "+shadow+", 1px 0 "+shadow+", 0 1px "+shadow+", -1px 0 "+shadow; + label.div.style.filter = "glow(color="+shadow+", strength=2) blur(add=false, direction=135, strength=1) blur(add=false, direction=45, strength=1)"; + map.updateByPlaceLabel(point,label.elements,1); + } + } + label.div.onmouseout = function(){ + if( map.pointSelected && !label.selected ){ + label.div.style.textShadow = "0 0 0.4em black, 0 0 0.4em black, 0 0 0.4em black, 0 0 0.4em "+shadow; + label.div.style.filter = "glow(color="+shadow+", strength=3) DropShadow(Color=#292929, OffX=1, OffY=-1, Positive=1) DropShadow(Color=#292929, OffX=-1, OffY=-1, Positive=1) DropShadow(Color=#292929, OffX=1, OffY=1, Positive=1) DropShadow(Color=#292929, OffX=-1, OffY=1, Positive=1) blur(add=false, direction=0, strength=1) blur(add=false, direction=90, strength=1) blur(add=false, direction=180, strength=1) blur(add=false, direction=270, strength=1)"; + map.updateByPlaceLabel(point,label.elements,2); + } + } + } + for( var i=0; i<map.labels.length; i++ ){ + var l = map.labels[i]; + l.selected = false; + var div = document.createElement("div"); + div.setAttribute('class','tagCloudItem'); + div.style.color = color; + var fs = 2*l.elements.length/1000; + if( l.place == "all" ){ + fs = 0; + } + if( fs > 2 ){ + + + fs = 2; + } + + + + + + window.appendChild(div); + div.style.fontSize = (1+fs)+"em"; + div.style.textShadow = "0 0 0.4em black, 0 0 0.4em black, 0 0 0.4em black, 0 0 0.4em "+shadow; + div.style.filter = "glow(color="+shadow+", strength=3) DropShadow(Color=#292929, OffX=1, OffY=-1, Positive=1) DropShadow(Color=#292929, OffX=-1, OffY=-1, Positive=1) DropShadow(Color=#292929, OffX=1, OffY=1, Positive=1) DropShadow(Color=#292929, OffX=-1, OffY=1, Positive=1) blur(add=false, direction=0, strength=1) blur(add=false, direction=90, strength=1) blur(add=false, direction=180, strength=1) blur(add=false, direction=270, strength=1)"; + div.innerHTML = l.place + "<span style='font-size:"+(1-fs/(1+fs))+"em'> (" + l.elements.length + ")</span>"; + l.div = div; + var point = map.lastHovered; + clickFunction(point,l); + } + var createDiv = function( mod, div ){ + var height = 0; + var width = 0; + for( var i=0; i<map.labels.length; i++ ){ + if( i%2 == mod ){ + height += map.labels[i].div.offsetHeight; + if( map.labels[i].div.offsetWidth > width ){ + width = map.labels[i].div.offsetWidth; + } + if( i>1 ){ + height += 5; + } + } + } + div.style.width = width+"px"; + div.style.height = height+"px"; + height = 0; + for( var i=0; i<map.labels.length; i++ ){ + if( i%2 == mod ){ + var h = map.labels[i].div.offsetHeight; + div.appendChild(map.labels[i].div); + if( mod == 0 ){ + map.labels[i].div.style.right = "0px"; + } + else { + map.labels[i].div.style.left = "0px"; + } + map.labels[i].div.style.top = height+"px"; + height += h+5; + } + } + } + map.leftTagCloudDiv.innerHTML = ""; + map.rightTagCloudDiv.innerHTML = ""; + createDiv(0,map.leftTagCloudDiv); + createDiv(1,map.rightTagCloudDiv); + map.placeTagCloud(map.lastHovered); + } + + // manages hover selection of point objects + var hoverSelect = function(event){ + if( map.pointSelected ){ + return; + } + map.core.undoHover(true); + var index = event.feature.index; + map.lastHovered = map.displayPointSet[Math.floor(map.openlayersMap.getZoom()+0.05)][index]; + calculateTagCloud(); + map.updateByPlace([map.lastHovered], 1); + }; + var hoverUnselect = function(event){ + map.hoverUnselect(); + }; + var highlightCtrl = new OpenLayers.Control.SelectFeature(this.objectLayer, { + hover: true, + highlightOnly: true, + renderIntent: "temporary", + eventListeners: { + featurehighlighted: hoverSelect, + featureunhighlighted: hoverUnselect + } + }); + this.openlayersMap.addControl(highlightCtrl); + highlightCtrl.activate(); + + this.selectFeature = new OpenLayers.Control.SelectFeature(this.objectLayer); + + // manages click selection of point objects + var onFeatureSelect = function(event){ + if( map.pointSelected ){ + return; + } + hoverUnselect(); + map.openlayersMap.setCenter(event.feature.geometry.getBounds().getCenterLonLat()); + var index = event.feature.index; + var point = map.displayPointSet[Math.floor(map.openlayersMap.getZoom()+0.05)][index]; + map.placeTagCloud(point); + map.pointClickDiv.style.visibility = "visible"; + map.polygons = []; + placeToolbar(); + changeLabelSelection(point,map.labels[map.labels.length-1],false); + } + this.objectLayer.events.on({ "featureselected": onFeatureSelect }); + this.openlayersMap.addControl(this.selectFeature); + this.selectFeature.activate(); + + if( this.core.props.addElements ){ + this.addCrossImage = document.createElement("img"); + this.addCrossImage.src = "images/cross.png"; + this.addCrossImage.setAttribute('class','addCross'); + this.addCrossImage.style.visibility = "hidden"; + window.appendChild(this.addCrossImage); + + this.addCrossImage.onclick = function(e){ + var mousePos = getMousePosition(e); + var pixel = new OpenLayers.Pixel( + mousePos.left - window.offsetLeft, + mousePos.top - window.offsetTop + ); + var position = map.openlayersMap.getLonLatFromPixel(pixel); + var point = new OpenLayers.Geometry.Point( + position.lon, + position.lat + ); + if (map.pointToAdd != null){ + map.drilldownLayer.removeFeatures([map.pointToAdd]); + } + map.pointToAdd = new OpenLayers.Feature.Vector(point); + map.drilldownLayer.addFeatures([map.pointToAdd]); + point.transform(map.openlayersMap.projection, map.openlayersMap.displayProjection); + setAddElementContext(point.x,point.y,mousePos.left,mousePos.top,window.offsetWidth,window.offsetHeight); + } + + // manages movement of the add cross + this.mapMouseMove = function(e){ + var mousePos = getMousePosition(e); + var left = mousePos.left - window.offsetLeft - 7; + var top = mousePos.top - window.offsetTop - 6; + var visibility = map.addCrossImage.style.visibility; + if( left < 0 || left+14 > window.offsetWidth || top < 0 || top+12 > window.offsetHeight ){ + if( visibility == "visible" ){ + map.addCrossImage.style.visibility = "hidden"; + } + } + else { + if( visibility == "hidden" ){ + map.addCrossImage.style.visibility = "visible"; + } + map.addCrossImage.style.left = left+"px"; + map.addCrossImage.style.top = top+"px"; + } + } + } + + if( this.core.props.historicMaps ){ + this.setCanvas(); + } + + }, + + /** + * parses all base layers in a given xmlFile and initializes google and osm layers + * @param {String} xmlFile the name of the file to parse + */ + parseBaseLayers: function(xmlFile){ + this.baseLayers = []; + if( this.core.props.historicMaps ){ + var xmlhttp = false; + if (!xmlhttp) + try { + xmlhttp = new XMLHttpRequest(); + } + catch (e) { + xmlhttp = false; + } + if (typeof ActiveXObject != "undefined") { + if (!xmlhttp) + try { + xmlhttp = new ActiveXObject("MSXML2.XMLHTTP"); + } + catch (e) { + xmlhttp = false; + } + if (!xmlhttp) + try { + xmlhttp = new ActiveXObject("Microsoft.XMLHTTP"); + } + catch (e) { + xmlhttp = false; + } + } + if (!xmlhttp) + try { + xmlhttp = createRequest(); + } + catch (e) { + xmlhttp = false; + } + + xmlhttp.open("GET", xmlFile, false); + xmlhttp.send(""); + xmlDoc = xmlhttp.responseXML; + + var wmsLayers = xmlDoc.getElementsByTagName("wms"); + for (i = 0; i < wmsLayers.length; i++) { + var name = wmsLayers[i].getElementsByTagName("name")[0].childNodes[0].nodeValue; + var server = wmsLayers[i].getElementsByTagName("server")[0].childNodes[0].nodeValue; + var layer = wmsLayers[i].getElementsByTagName("layer")[0].childNodes[0].nodeValue; + var format = wmsLayers[i].getElementsByTagName("format")[0].childNodes[0].nodeValue; + var transparency = wmsLayers[i].getElementsByTagName("transparency")[0].childNodes[0].nodeValue; + var layer = new OpenLayers.Layer.WMS(name, server, { + layers: layer, + format: format, + transparent: transparency + }, { + isBaseLayer: true + }); + this.baseLayers.push(layer); + } + } + + + if( this.core.props.googleMaps ){ + this.baseLayers.push( new OpenLayers.Layer.Google("Google Physical", {type: G_PHYSICAL_MAP, 'sphericalMercator': true} ) ); + this.baseLayers.push( new OpenLayers.Layer.Google( 'Google Streets', { 'sphericalMercator': true } ) ); + this.baseLayers.push( new OpenLayers.Layer.Google( 'Google Satellite', { type: G_SATELLITE_MAP, 'sphericalMercator': true } ) ); + this.baseLayers.push( new OpenLayers.Layer.Google( 'Google Hybrid', { type: G_HYBRID_MAP, 'sphericalMercator': true } ) ); + } + + if( this.core.props.bingMaps ){ + this.baseLayers.push( new OpenLayers.Layer.VirtualEarth("Bing Streets", { type: VEMapStyle.Shaded, 'sphericalMercator': true } ) ); + this.baseLayers.push( new OpenLayers.Layer.VirtualEarth("Bing Aerial", { type: VEMapStyle.Aerial, 'sphericalMercator': true } ) ); + this.baseLayers.push( new OpenLayers.Layer.VirtualEarth("Bing Hybrid", { type: VEMapStyle.Hybrid, 'sphericalMercator': true } ) ); + } + /* + if( this.core.props.osmMaps ){ + this.baseLayers.push( new OpenLayers.Layer.OSM( 'Open Street Map' ) ); + this.baseLayers.push( new OpenLayers.Layer.OSM( 'OSM Tiles@Home', 'http://tah.openstreetmap.org/Tiles/tile/${z}/${x}/${y}.png' ) ); + } + */ + }, + + /** + * sets the background canvas of the map window (or resets it after resizing the browser window) + */ + setCanvas: function(){ + var mapWindow = document.getElementById(this.window); + var cv = document.getElementById("mapCanvas"); + if (cv == null) { + cv = document.createElement("canvas"); + cv.id = "mapCanvas"; + mapWindow.appendChild(cv); + } + cv.width = document.getElementById(this.container).clientWidth; + cv.height = document.getElementById(this.container).clientHeight; + if (!cv.getContext && G_vmlCanvasManager) + cv = G_vmlCanvasManager.initElement(cv); + var ctx = cv.getContext('2d'); + var gradient = ctx.createLinearGradient(0, 0, 0, cv.height); + gradient.addColorStop(0, '#8bafd8'); + gradient.addColorStop(1, '#355272'); + ctx.fillStyle = gradient; + ctx.fillRect(0, 0, cv.width, cv.height); + }, + + /** + * draws the object layer. + * @param {boolean} zoom if there was a zoom; if not, the new boundary of the map is calculated + */ + drawObjectLayer: function(zoom){ + if( this.displayPointSet == undefined ){ + return; + } + this.hoverUnselect(); + this.objectLayer.removeAllFeatures(); + if (!zoom) { + var minLat, maxLat, minLon, maxLon; + var points = this.displayPointSet[this.openlayersMap.getNumZoomLevels() - 1]; + for (var i = 0; i < points.length; i++) { + var point = points[i]; + if (!minLon || point.originX < minLon) + minLon = point.originX; + if (!maxLon || point.originX > maxLon) + maxLon = point.originX; + if (!minLat || point.originY < minLat) + minLat = point.originY; + if (!maxLat || point.originY > maxLat) + maxLat = point.originY; + } + if (minLon == maxLon && minLat == maxLat) { + this.openlayersMap.setCenter(new OpenLayers.LonLat(minLon, minLat)); + } + else { + var gapX = 0.1 * ( maxLon - minLon ); + var gapY = 0.1 * ( maxLat - minLat ); + this.openlayersMap.zoomToExtent(new OpenLayers.Bounds(minLon-gapX, minLat-gapY, maxLon+gapX, maxLat+gapY)); + this.openlayersMap.zoomTo(Math.floor(this.openlayersMap.getZoom()+0.05)); + } + setMapZoom(this.openlayersMap.getZoom()/this.openlayersMap.numZoomLevels); + if( this.openlayersMap.getZoom() == this.openlayersMap.numZoomLevels - 1 ){ + this.openlayersMap.zoomTo(2); + setMapZoom(2/this.openlayersMap.numZoomLevels); + } + } + + var points = this.displayPointSet[Math.floor(this.openlayersMap.getZoom()+0.05)]; + for (var i = 0; i < points.length; i++) { + var resolution = this.openlayersMap.getResolution(); + var p = points[i]; + var x = p.originX + resolution * p.shiftX; + var y = p.originY + resolution * p.shiftY; + p.pointFeature.geometry.x = x; + p.pointFeature.geometry.y = y; + p.olPointFeature.geometry.x = x; + p.olPointFeature.geometry.y = y; + this.objectLayer.addFeatures([p.pointFeature]); + this.objectLayer.addFeatures([p.olPointFeature]); + } + + var dist = function(p1,p2){ + return Math.sqrt( (p1.x-p2.x)*(p1.x-p2.x) + (p1.y-p2.y)*(p1.y-p2.y) ); + } + + for (var i = 0; i < this.connections.length; i++) + this.connections[i] = []; + var slices = this.core.timeplot.getSlices(); + for (var i = 0; i < slices.length; i++) { + for (var j = 0; j < slices[i].elements.length; j++) { + var e = slices[i].elements[j]; + if (e.length == 0) + continue; + var points = []; + for (var k = 0; k < e.length; k++) { + var point = e[k].pointobjects[Math.floor(this.openlayersMap.getZoom())].pointFeature.geometry; + if( points.indexOf(point) == -1 ){ + points.push(point); + } + } + var matrix = new AdjMatrix(points.length); + for (var k = 0; k < points.length-1; k++) { + for (var l = k+1; l < points.length; l++) { + matrix.setEdge(k,l,dist(points[k],points[l])); + } + } + var tree = Prim(matrix); + var lines = []; + for( var z=0; z<tree.length; z++ ){ + lines.push(new OpenLayers.Geometry.LineString(new Array(points[tree[z].v1],points[tree[z].v2]))); + } + this.connections[j].push({ + first: e[0].pointobjects[Math.floor(this.openlayersMap.getZoom()+0.05)].pointFeature.geometry, + last: e[e.length-1].pointobjects[Math.floor(this.openlayersMap.getZoom()+0.05)].pointFeature.geometry, + lines: lines, + time: slices[i].date + }); + } + } + this.updateMap(); + }, + + /** + * initializes the object layer. + * all point representations for all zoom levels are calculated and initialized + * @param {DataSet[]} dataSets the datasets which contain all objects to display on the map + */ + initObjectLayer: function(dataSets){ + + this.clearMap(); + + var map = this; + + var getPointCluster = function(dataSets){ + + maximumRadius = minimumRadius; + var zoomLevels = map.openlayersMap.getNumZoomLevels(); + var getMaxRadius = function(size){ + var exponent = 0; + while (Math.pow(classBase, exponent) < size) + exponent++; + return minimumRadius + exponent; + } + + var dd = new HierarchicalClustering(-20037508.34,-20037508.34,20037508.34,20037508.34); + for (var i = 0; i < dataSets.length; i++){ + for (var j = 0; j < dataSets[i].objects.length; j++) { + var p = new OpenLayers.Geometry.Point(dataSets[i].objects[j].longitude, dataSets[i].objects[j].latitude, null); + p.transform(map.openlayersMap.displayProjection, map.openlayersMap.projection); + var point = new Vertex(Math.floor(p.x), Math.floor(p.y)); + var objects = []; + for( var k=0; k<dataSets.length; k++ ){ + objects.push([]); + } + objects[i].push(dataSets[i].objects[j]); + point.setElements(objects); + dd.add(point); + } + var r = getMaxRadius(dataSets[i].objects.length); + if( r > maximumRadius ){ + maximumRadius = r; + maximumPoints = dataSets[i].objects.length; + } + } + + var displayPoints = []; + for (var i = 0; i < zoomLevels; i++) { + var points = []; + var resolution = map.openlayersMap.getResolutionForZoom(zoomLevels - i - 1); + dd.mergeForResolution(resolution); + for (var j = 0; j < dd.vertices.length; j++) { + var point = dd.vertices[j]; + if( !point.legal ){ + continue; + } + var balls = []; + for (var k = 0; k < point.elements.length; k++){ + if (point.elements[k].length > 0){ + balls.push({ + search: k, + elements: point.elements[k], + radius: point.radii[k] + }); + } + } + var orderBalls = function( b1, b2 ){ + if ( b1.radius > b2.radius ){ + return -1; + } + if ( b2.radius > b1.radius ){ + return 1; + } + return 0; + } + if (balls.length == 1) { + points.push(new DisplayPointObject(point.x, point.y, 0, 0, balls[0].elements, balls[0].radius, balls[0].search)); + } + else + if (balls.length == 2) { + var r1 = balls[0].radius; + var r2 = balls[1].radius; + points.push(new DisplayPointObject(point.x, point.y, -1*r2, 0, balls[0].elements, r1, balls[0].search)); + points.push(new DisplayPointObject(point.x, point.y, r1, 0, balls[1].elements, r2, balls[1].search)); + } + else + if (balls.length == 3) { + var r1 = balls[0].radius; + var r2 = balls[1].radius; + var r3 = balls[2].radius; + var d = (2 / 3 * Math.sqrt(3) - 1) / 2; + points.push(new DisplayPointObject(point.x, point.y, -2*d*r1-r1, 0, balls[0].elements, r1, balls[0].search)); + points.push(new DisplayPointObject(point.x, point.y, r2/2, r2, balls[1].elements, r2, balls[1].search)); + points.push(new DisplayPointObject(point.x, point.y, r3/2, -1*r3, balls[2].elements, r3, balls[2].search)); + } + else + if (balls.length == 4) { + balls.sort(orderBalls); + var r1 = balls[0].radius; + var r2 = balls[1].radius; + var r3 = balls[2].radius; + var r4 = balls[3].radius; + var d = (Math.sqrt(2) - 1)*r2; + points.push(new DisplayPointObject(point.x, point.y, -1*d-r2, 0, balls[0].elements, r1, balls[0].search)); + points.push(new DisplayPointObject(point.x, point.y, r1-r2, -1*d-r4, balls[3].elements, r4, balls[3].search)); + points.push(new DisplayPointObject(point.x, point.y, r1-r2, d+r3, balls[2].elements, r3, balls[2].search)); + points.push(new DisplayPointObject(point.x, point.y, d+r1, 0, balls[1].elements, r2, balls[1].search)); + } + } + displayPoints.push(points); + } + return displayPoints.reverse(); + + } + + this.connections = []; + for (var i = 0; i < dataSets.length; i++) { + this.connections.push([]); + for (var j = 0; j < dataSets[i].objects.length; j++){ + dataSets[i].objects[j].pointobjects = []; + } + } + + this.displayPointSet = getPointCluster(dataSets); + + if( this.connections.length == 0 || this.displayPointSet.length == 0 ){ + return; + } + + for (var i = 0; i < this.displayPointSet.length; i++) + for (var j = 0; j < this.displayPointSet[i].length; j++) { + var point = this.displayPointSet[i][j]; + for (var k = 0; k < point.elements.length; k++) + point.elements[k].pointobjects.push(point); + var style = { + fillColor: 'rgb(' + colors[point.search].r0 + ',' + colors[point.search].g0 + ',' + colors[point.search].b0 + ')', + fillOpacity: 1, + strokeWidth: 1, + strokeColor: 'rgb(' + colors[point.search].r1 + ',' + colors[point.search].g1 + ',' + colors[point.search].b1 + ')', + stroke: false, + pointRadius: point.radius + }; + var pointGeometry = new OpenLayers.Geometry.Point(point.originX, point.originY, null); + var pointFeature = new OpenLayers.Feature.Vector(pointGeometry); + pointFeature.style = style; + pointFeature.index = j; + point.setPointFeature(pointFeature); + var olStyle = { + fillColor: 'rgb(' + colors[point.search].r1 + ',' + colors[point.search].g1 + ',' + colors[point.search].b1 + ')', + fillOpacity: 1, + stroke: false, + pointRadius: 0 + }; + var olPointGeometry = new OpenLayers.Geometry.Point(point.originX, point.originY, null); + var olPointFeature = new OpenLayers.Feature.Vector(olPointGeometry); + olPointFeature.style = olStyle; + olPointFeature.index = j; + point.setOlPointFeature(olPointFeature); + } + + this.drawObjectLayer(false); + + }, + + /** + * hides all tagCloud and pointclick divs + */ + setVisibility: function(visibility){ + this.toolbar.style.visibility = visibility; + this.leftTagCloudDiv.style.visibility = visibility; + this.rightTagCloudDiv.style.visibility = visibility; + this.pointClickDiv.style.visibility = visibility; + }, + + /** + * resets the map by destroying all additional elements except the point objects, which are replaced + */ + resetMap: function(){ + this.connectionLayer.destroyFeatures(); + this.drilldownLayer.destroyFeatures(); + this.controlLockDiv.style.visibility = "hidden"; + this.selectFeature.unselectAll(); + this.resetPoints(); + this.deactivate("drag"); + this.drag.style.visibility = "hidden"; + this.setVisibility("hidden"); + this.pointSelected = false; + this.polygons = []; + }, + + /** + * resets the map by destroying all elements + */ + clearMap: function(){ + this.connectionLayer.destroyFeatures(); + this.drilldownLayer.destroyFeatures(); + this.objectLayer.destroyFeatures(); + delete this.displayPointSet; + this.displayPointSet = undefined; + }, + + /** + * updates the proportional selection status of a point object + * @param {PointObject} point the point to update + * @param {OpenLayers.Geometry.Polygon} polygon the actual displayed map polygon + */ + updatePoint: function(point,polygon){ + var pFull = 0; + var pRest = 0; + for (var j = 0; j < point.elements.length; j++) { + var o = point.elements[j]; + if (o.percentage == 1 || ( o.hoverSelect && this.lastHovered == undefined ) ) { + pFull++; + } + else { + pRest += o.percentage; + } + } + var drawOl = false; + var draw = false; + var pf = point.pointFeature; + var olf = point.olPointFeature; + if (pFull == 0 && olf.style.pointRadius != 0) { + olf.style.pointRadius = 0; + drawOl = true; + } + else if (pFull > 0) { + /* + var noer = pf.style.pointRadius - minimumRadius; + var olpr = Math.floor(pFull / point.elements.length * noer) + minimumRadius; + if (olpr != olf.style.pointRadius) { + olf.style.pointRadius = olpr; + drawOl = true; + } + */ + olf.style.pointRadius = getRadius(pFull); + drawOl = true; + } + if (point.percentage != pRest) { + point.percentage = pRest; + draw = true; + var p; + if (pRest == 0){ + p = 0; + } + else { + p = pRest / (point.elements.length - pFull); + } + var s = point.search; + var r = colors[s].r0 + Math.round(p * (colors[s].r1 - colors[s].r0)); + var g = colors[s].g0 + Math.round(p * (colors[s].g1 - colors[s].g0)); + var b = colors[s].b0 + Math.round(p * (colors[s].b1 - colors[s].b0)); + point.pointFeature.style.fillColor = 'rgb(' + r + ',' + g + ',' + b + ')'; + } + if (polygon.containsPoint(point.pointFeature.geometry)) { + if (draw || drawOl) { + this.objectLayer.drawFeature(point.pointFeature); + this.objectLayer.drawFeature(point.olPointFeature); + } + } + }, + + /** + * updates the the object layer of the map after selections had been executed in timeplot or table or zoom level has changed + */ + updateMap: function(){ + var points = this.displayPointSet[Math.floor(this.openlayersMap.getZoom()+0.05)]; + var polygon = this.openlayersMap.getExtent().toGeometry(); + for (var i = 0; i < points.length; i++) { + this.updatePoint(points[i],polygon); + } + this.displayConnections(); + }, + + /** + * resets the point objects depending on the actual zoom level in basic style + */ + resetPoints: function(){ + if( this.displayPointSet != undefined ){ + if( this.displayPointSet.length == 0 ){ + return; + } + var points = this.displayPointSet[Math.floor(this.openlayersMap.getZoom()+0.05)]; + for (var i = 0; i < points.length; i++) { + var j = points[i].search; + points[i].pointFeature.style.fillColor = 'rgb(' + colors[j].r0 + ',' + colors[j].g0 + ',' + colors[j].b0 + ')'; + points[i].olPointFeature.style.pointRadius = 0; + } + this.objectLayer.redraw(); + } + }, + + /** + * updates the data objects percentages after a selection on the map had been performed + * @param {PointObject[]} pointObjects the point objects that corresponds to the selection + * @param {boolean} hover if it was a hover selection + */ + updateByPlace: function(pointObjects, hover){ + if (hover == 0) { + this.core.reset(); + } + for (var i = 0; i < pointObjects.length; i++) { + var s = pointObjects[i].search; + var c = colors[s].hex; + if (hover == 1) { + pointObjects[i].pointFeature.style.stroke = true; + for (var j = 0; j < pointObjects[i].elements.length; j++) { + pointObjects[i].elements[j].setHover(true); + } + } + else + if (hover == 2) { + pointObjects[i].pointFeature.style.stroke = false; + for (var j = 0; j < pointObjects[i].elements.length; j++) { + pointObjects[i].elements[j].setHover(false); + } + } + else { + pointObjects[i].pointFeature.style.fillColor = c; + pointObjects[i].pointFeature.style.stroke = false; + for (var j = 0; j < pointObjects[i].elements.length; j++) { + pointObjects[i].elements[j].setHover(false); + pointObjects[i].elements[j].setPercentage(1); + } + } + this.objectLayer.drawFeature(pointObjects[i].pointFeature); + this.objectLayer.drawFeature(pointObjects[i].olPointFeature); + } + if( hover == 0 ){ + this.core.updateTimeAndTable(false); + } + else { + this.core.updateTimeAndTable(true); + } + }, + + /** + * updates the data objects percentages after a selection on the map by clicking on a place label + * @param {PointObject} point the point object that corresponds to the label selection + * @param {DataObject[]} elements the data objects corresponding to the selected label + * @param {boolean} hover if it was a hover selection + */ + updateByPlaceLabel: function(point,elements,hover){ + if( hover == 0 ){ + this.core.reset(); + } + this.controlLockDiv.style.visibility = "visible"; + for (var j = 0; j < elements.length; j++) { + if( hover == 0 ){ + elements[j].setPercentage(1); + } + else if( hover == 1 ){ + elements[j].hoverSelect = true; + } + else if( hover == 2 ){ + elements[j].hoverSelect = false; + } + } + this.setVisibility("visible"); + this.pointSelected = true; + this.updatePoint(point,this.openlayersMap.getExtent().toGeometry()); + if( hover == 0 ){ + this.core.updateTimeAndTable(false); + } + else { + this.core.updateTimeAndTable(true); + } + }, + + /** + * displays connections between data objects + */ + displayConnections: function(){ + var ltm = this.core.timeplot.leftFlagTime; + var rtm = this.core.timeplot.rightFlagTime; + if (ltm == undefined || ltm == null){ + return; + } + else { + ltm = ltm.getTime(); + rtm = rtm.getTime(); + } + this.connectionLayer.destroyFeatures(); + if (this.showConnections) { + for (var i = 0; i < this.connections.length; i++) { + var c = colors[i]; + var style = { + strokeColor: 'rgb(' + c.r1 + ',' + c.g1 + ',' + c.b1 + ')', + strokeOpacity: 0.5, + strokeWidth: 3 + }; + var pointsToConnect = []; + var last = undefined; + for (var j = 0; j < this.connections[i].length; j++) { + var c = this.connections[i][j]; + var ct = c.time.getTime(); + if (ct >= ltm && ct <= rtm) { + if( last != undefined ){ + var line = new OpenLayers.Geometry.LineString(new Array(last,c.first)); + this.connectionLayer.addFeatures([new OpenLayers.Feature.Vector(line, null, style)]); + } + for( var k=0; k<c.lines.length; k++ ){ + this.connectionLayer.addFeatures([new OpenLayers.Feature.Vector(c.lines[k], null, style)]); + } + last = c.last; + } + } + } + this.connectionLayer.redraw(); + } + }, + + /** + * causes deselection of a hovered point object + */ + hoverUnselect: function(){ + if( this.pointSelected ){ + return; + } + if (this.lastHovered != undefined) { + this.leftTagCloudDiv.style.visibility = "hidden"; + this.rightTagCloudDiv.style.visibility = "hidden"; + this.updateByPlace([this.lastHovered], 2); + this.lastHovered = undefined; + } + }, + + /** + * places the tagCloud divs inside the map window + * @param {PointObject} point the point object that corresponds to the tagCloud + */ + placeTagCloud: function( point ){ + var lonlat = new OpenLayers.LonLat( point.pointFeature.geometry.x, point.pointFeature.geometry.y ); + var pixel = this.openlayersMap.getPixelFromLonLat(lonlat); + var radius = point.pointFeature.style.pointRadius; + var window = document.getElementById(this.window); + var lw = this.leftTagCloudDiv.offsetWidth; + var rw = this.rightTagCloudDiv.offsetWidth; + var lp = false; + var rp = false; + if( pixel.x - radius - lw -5 < 0 ){ + lp = true; + if( pixel.x + radius + lw + rw + 10 > window.offsetWidth ){ + rp = true; + } + } + else if( pixel.x + radius + rw + 5 > window.offsetWidth ){ + rp = true; + if( pixel.x - radius - lw - rw - 10 < 0 ){ + lp = true; + } + } + if( !lp && !rp ){ + this.leftTagCloudDiv.style.left = (pixel.x-radius-lw-5)+"px"; + this.rightTagCloudDiv.style.left = (pixel.x+radius+5)+"px"; + } + else if( lp && !rp ){ + this.leftTagCloudDiv.style.left = (pixel.x+radius+5)+"px"; + this.rightTagCloudDiv.style.left = (pixel.x+radius+lw+10)+"px"; + } + else if( !lp && rp ){ + this.leftTagCloudDiv.style.left = (pixel.x-radius-lw-rw-10)+"px"; + this.rightTagCloudDiv.style.left = (pixel.x-radius-rw-5)+"px"; + } + var lh = this.leftTagCloudDiv.offsetHeight; + var rh = this.rightTagCloudDiv.offsetHeight; + var lt = pixel.y - lh/2; + var rt = pixel.y - rh/2; + if( lt < 0 ){ + lt = 0; + } + else if( lt + lh > window.offsetHeight ){ + lt = window.offsetHeight - lh; + } + if( rt < 0 ){ + rt = 0; + } + else if( rt + rh > window.offsetHeight ){ + rt = window.offsetHeight - rh; + } + this.leftTagCloudDiv.style.top = lt+"px"; + this.rightTagCloudDiv.style.top = rt+"px"; + this.leftTagCloudDiv.style.visibility = "visible"; + this.rightTagCloudDiv.style.visibility = "visible"; + + this.pointClickDiv.style.height = (lh+45)+"px"; + this.pointClickDiv.style.width = (lw+rw+2*radius+70)+"px"; + this.pointClickDiv.style.left = (this.leftTagCloudDiv.offsetLeft-30)+"px"; + this.pointClickDiv.style.top = (lt-30)+"px"; + }, + + /** + * activates a specific map control + * @param {String} status the identifier of the control to activate + */ + activate: function(status){ + this.activeControl = status; + if( status == "drag" ){ + this.dragArea.activate(); + this.drilldownLayer.setZIndex(parseInt(this.objectLayer.getZIndex())+1); + this.drag.style.backgroundImage = "url(images/dragger-click.png)"; + } + else if( status == "polygon" ){ + this.drawPolygon.activate(); + } + else if( status == "circle" ){ + this.drawCircle.activate(); + } + else if( status == "country" ){ + this.selectCountry.activate(); + } + else if( status == "add" ){ + document.onmousemove = this.mapMouseMove; + } + }, + + /** + * deactivates a specific map control + * @param {String} status the identifier of the control to deactivate + */ + deactivate: function(status){ + if( status == "drag" ){ + this.dragArea.deactivate(); + this.drilldownLayer.setZIndex(parseInt(this.objectLayer.getZIndex())-1); + this.drag.style.backgroundImage = "url(images/dragger.png)"; + } + else if( status == "polygon" ){ + this.drawPolygon.deactivate(); + } + else if( status == "circle" ){ + this.drawCircle.deactivate(); + } + else if( status == "country" ){ + this.selectCountry.deactivate(); + } + else if( status == "add" ){ + document.onmousemove = null; + this.addCrossImage.style.visibility = "hidden"; + } + }, + + /** + * inverts the show connection status + * @return boolean value if connections are enabled or not + */ + connectionsClick: function(){ + this.showConnections = !this.showConnections; + this.displayConnections(); + return this.showConnections; + }, + + /** + * performs a zoom on the map + * @param {int} delta the change of zoom levels + */ + zoom: function( delta ){ + if( this.pointSelected ){ + return false; + } + var zoom = delta*this.openlayersMap.numZoomLevels; + if( this.openlayersMap.baseLayer instanceof OpenLayers.Layer.WMS ){ + this.openlayersMap.zoomTo(zoom); + } + else { + this.openlayersMap.zoomTo(Math.round(zoom)); + + } + this.drawObjectLayer(true); + return true; + }, + + setMap: function(index){ + if (this.baseLayers[index] instanceof OpenLayers.Layer.WMS) { + this.openlayersMap.fractionalZoom = true; + this.selectCountry.protocol = OpenLayers.Protocol.WFS.fromWMSLayer(this.baseLayers[index]); + } + else { + this.openlayersMap.fractionalZoom = false; + } + this.openlayersMap.zoomTo(Math.floor(this.openlayersMap.getZoom()+0.05)); + this.openlayersMap.setBaseLayer(this.baseLayers[index]); + }, + + configure: function(zoom,cLon,cLat,mapId){ + this.openlayersMap.zoomTo(zoom); + this.drawObjectLayer(true); + this.setMap(mapId); + this.openlayersMap.setCenter(new OpenLayers.LonLat(cLon,cLat)); + setMapZoom(this.openlayersMap.zoom/this.openlayersMap.numZoomLevels); + } + +}