Mercurial > hg > STI-GWT
view war/scripts/sti/STIMap.js @ 75:d69066d16e45 trimmed_data
load overlays from data-file
author | Sebastian Kruse <skruse@mpiwg-berlin.mpg.de> |
---|---|
date | Tue, 15 Jan 2013 16:32:25 +0100 |
parents | eac73bf1ce6e |
children | 7b65d95735d2 |
line wrap: on
line source
/** * 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.overlayLayers; 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(){ this.overlayLayers = new Array(); //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: google.maps.MapTypeId.TERRAIN, 'sphericalMercator': true} ) ); this.baseLayers.push( new OpenLayers.Layer.Google( 'Google Streets', { 'sphericalMercator': true } ) ); this.baseLayers.push( new OpenLayers.Layer.Google( 'Google Satellite', { type: google.maps.MapTypeId.SATELLITE, 'sphericalMercator': true } ) ); this.baseLayers.push( new OpenLayers.Layer.Google( 'Google Hybrid', { type: google.maps.MapTypeId.HYBRID, '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 } ) ); } /* Was commented out, no idea, why? */ 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"; cv.className = "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); /* OLD GRADIENTS */ /* gradient.addColorStop(0, '#8bafd8'); * gradient.addColorStop(1, '#355272'); */ /* new gradients: dark DARIAH theme (2012-07-19 SUBGOE Stefan E. Funk) */ gradient.addColorStop(0, '#68635d'); gradient.addColorStop(1, '#46413a'); 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]); }, setOverlay: function(index){ if (this.overlayLayers[index] instanceof OpenLayers.Layer.Vector) { this.openlayersMap.removeLayer(this.overlayLayers[index]); this.overlayLayers[index] = null; } else { var newLayer = new OpenLayers.Layer.Vector("KML", { projection: this.openlayersMap.displayProjection, strategies: [new OpenLayers.Strategy.Fixed()], protocol: new OpenLayers.Protocol.HTTP({ url: this.core.overlays[index].url, format: new OpenLayers.Format.KML({ extractStyles: true, extractAttributes: true }) }) }); this.openlayersMap.addLayer(newLayer); this.overlayLayers[index] = newLayer; } }, 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); } }