comparison lib/GeoTemCo/js/LineOverlay/LineOverlayWidget.js @ 0:b57c7821382f

initial
author Dirk Wintergruen <dwinter@mpiwg-berlin.mpg.de>
date Thu, 28 May 2015 10:28:12 +0200
parents
children
comparison
equal deleted inserted replaced
-1:000000000000 0:b57c7821382f
1 /*
2 * LineOverlayWidget.js
3 *
4 * Copyright (c) 2013, Sebastian Kruse. All rights reserved.
5 *
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 3 of the License, or (at your option) any later version.
10 *
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
15 *
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
19 * MA 02110-1301 USA
20 */
21
22 //calculate angle between line and x-axis
23 //credits: geometricnet (http://geometricnet.sourceforge.net/examples/directions.html)
24 bearing = function(x1,y1,x2,y2) {
25 b_x = 0;
26 b_y = 1;
27 a_x = x2 - x1;
28 a_y = y2 - y1;
29 angle_rad = Math.acos((a_x*b_x+a_y*b_y)/Math.sqrt(a_x*a_x+a_y*a_y)) ;
30 angle = 360/(2*Math.PI)*angle_rad;
31 if (a_x < 0) {
32 return 360 - angle;
33 } else {
34 return angle;
35 }
36 };
37
38 /**
39 * @class LineOverlayWidget
40 * Implementation for the widget interactions of an overlay showing lines between points
41 * @author Sebastian Kruse (skruse@mpiwg-berlin.mpg.de)
42 *
43 * @param {WidgetWrapper} core wrapper for interaction to other widgets
44 * @param {JSON} options user specified configuration that overwrites options in OverlayloaderConfig.js
45 */
46 LineOverlayWidget = function (core, options) {
47
48 this.core = core;
49 this.core.setWidget(this);
50
51 this.options = (new LineOverlayConfig(options)).options;
52
53 this.attachedMapWidgets = new Array();
54
55 this.lineOverlay = new LineOverlay(this);
56 this.lines = [];
57 this.multiLineFeature;
58
59 this.selected = [];
60 }
61
62 /**
63 * @param {Number} dataSet number of dataSet in dataSet array
64 * @param {Number} objectID number of DataObject in objects array
65 */
66
67 function Line(objectStart, objectEnd ) {
68 this.objectStart = objectStart;
69 this.objectEnd = objectEnd;
70 }
71
72 LineOverlayWidget.prototype = {
73
74 initWidget : function() {
75 var lineOverlayWidget = this;
76 this.drawLines();
77 },
78
79 highlightChanged : function(objects) {
80 if( !GeoTemConfig.highlightEvents ){
81 return;
82 }
83 this.drawLines(GeoTemConfig.mergeObjects(objects,this.selected));
84 },
85
86 selectionChanged : function(selection) {
87 if( !GeoTemConfig.selectionEvents ){
88 return;
89 }
90 if (selection.valid())
91 this.selected = selection.objects;
92 else
93 this.selected = [];
94
95 this.drawLines(this.selected);
96 },
97
98 triggerHighlight : function(item) {
99 },
100
101 tableSelection : function() {
102 },
103
104 deselection : function() {
105 },
106
107 filtering : function() {
108 },
109
110 inverseFiltering : function() {
111 },
112
113 triggerRefining : function() {
114 },
115
116 reset : function() {
117 },
118
119 //identical to the function in PieChartWidget
120 //here cause widgets may be used independed of each other
121 getElementData : function(dataObject, watchedColumn, selectionFunction) {
122 var columnData;
123 if (watchedColumn.indexOf("[") === -1){
124 columnData = dataObject[watchedColumn];
125 if (typeof columnData === "undefined"){
126 columnData = dataObject.tableContent[watchedColumn];
127 };
128 } else {
129 try {
130 var columnName = watchedColumn.split("[")[0];
131 var IndexAndAttribute = watchedColumn.split("[")[1];
132 if (IndexAndAttribute.indexOf("]") != -1){
133 var arrayIndex = IndexAndAttribute.split("]")[0];
134 var attribute = IndexAndAttribute.split("]")[1];
135
136 if (typeof attribute === "undefined")
137 columnData = dataObject[columnName][arrayIndex];
138 else{
139 attribute = attribute.split(".")[1];
140 columnData = dataObject[columnName][arrayIndex][attribute];
141 }
142 }
143 } catch(e) {
144 if (typeof console !== undefined)
145 console.error(e);
146
147 delete columnData;
148 }
149 }
150
151 if ( (typeof columnData !== "undefined") && (typeof selectionFunction !== "undefined") )
152 columnData = selectionFunction(columnData);
153
154 return(columnData);
155 },
156
157 matchColumns : function(dataSet1, columnName1, dataSet2, columnName2) {
158 var lineOverlayWidget = this;
159 lineOverlayWidget.lines;
160 $(GeoTemConfig.datasets[dataSet1].objects).each(function(){
161 var object1 = this;
162 var data1 = lineOverlayWidget.getElementData(object1, columnName1);
163 //split because there could be multiple comma separated values
164 data1 = data1.split(",");
165
166 $(GeoTemConfig.datasets[dataSet2].objects).each(function(){
167 var object2 = this;
168 //avoid reflexive and double entries
169 if ((columnName1 === columnName2)&&(dataSet1 === dataSet2)&&(object1.index<=object2.index))
170 return;
171 var data2 = lineOverlayWidget.getElementData(object2, columnName2);
172 //split because there could be multiple comma separated values
173 data2 = data2.split(",");
174
175 //check if at least one pair matches
176 for(var i = 0; i < data1.length; i++ ){
177 var firstVal = data1[i];
178 if (data2.indexOf(firstVal) !== -1){
179 lineOverlayWidget.lines.push(new Line(object1, object2));
180 break;
181 }
182 }
183 });
184 });
185 },
186
187 getXYofObject : function(cs,dataObject){
188 //iterata over datasets
189 var x,y;
190 var found = false;
191 $(cs).each(function(){
192 //iterate over circles
193 $(this).each(function(){
194 var circle = this;
195 //iterata over objects in this circle;
196 var index = $.inArray(dataObject,circle.elements);
197 if (index !== -1){
198 x = circle.feature.geometry.x;
199 y = circle.feature.geometry.y;
200 found = true;
201 return false;
202 }
203 });
204 //break loop
205 if (found === true)
206 return false;
207 });
208
209 return ({x:x,y:y});
210 },
211
212 /**
213 * @param {DataObjects[][]} objects set of objects to limit to
214 */
215 drawLines : function(objects) {
216 var flatObjects = [];
217 if ( (typeof objects !== "undefined") &&
218 (objects instanceof Array) &&
219 (objects.length > 0) ) {
220 $(objects).each(function(){
221 $.merge(flatObjects, this);
222 });
223 }
224 var lineOverlayWidget = this;
225
226 $(lineOverlayWidget.attachedMapWidgets).each(function(){
227 var mapWidget = this.mapWidget;
228 var lineLayer = this.lineLayer;
229
230 var map = mapWidget.openlayersMap;
231 var cs = mapWidget.mds.getObjectsByZoom();
232
233 mapWidget.openlayersMap.setLayerIndex(lineLayer, 99);
234
235 lineLayer.removeAllFeatures();
236
237 var lineElements = [];
238
239 var checkIfLineInPreset = function(){return false;};
240 if (lineOverlayWidget.options.showLines === "inbound"){
241 checkIfLineInPreset = function(objectStart,objectEnd,flatObjects){
242 return ($.inArray(objectEnd, flatObjects) === -1);
243 };
244 } else if (lineOverlayWidget.options.showLines === "outbound"){
245 checkIfLineInPreset = function(objectStart,objectEnd,flatObjects){
246 return ($.inArray(objectStart, flatObjects) === -1);
247 };
248 } else /*if (lineOverlayWidget.options.showLines === "both")*/{
249 checkIfLineInPreset = function(objectStart,objectEnd,flatObjects){
250 return ( ($.inArray(objectStart, flatObjects) === -1) &&
251 ($.inArray(objectEnd, flatObjects) === -1) );
252 };
253 }
254
255 $(lineOverlayWidget.lines).each(function(){
256 var line = this;
257
258 if ((lineOverlayWidget.options.onlyShowSelectedOrHighlighted === true) || (flatObjects.length > 0)){
259 //if objects are limited, check whether start or end are within
260 if (checkIfLineInPreset(line.objectStart, line.objectEnd, flatObjects))
261 return;
262 }
263 //get XY-val of start Object
264 var xyStart = lineOverlayWidget.getXYofObject(cs, line.objectStart);
265 //continue if no valid XY-coords where found
266 if ( (typeof xyStart.x === "undefined") && (typeof xyStart.y === "undefined") )
267 return;
268 var xyEnd = lineOverlayWidget.getXYofObject(cs, line.objectEnd);
269 //continue if no valid XY-coords where found
270 if ( (typeof xyEnd.x === "undefined") && (typeof xyEnd.y === "undefined") )
271 return;
272
273 //do not draw 0-length lines (from same circle)
274 if ( (xyStart.x === xyEnd.x) && (xyStart.y === xyEnd.y) )
275 return;
276
277 var points = new Array(
278 new OpenLayers.Geometry.Point(xyStart.x, xyStart.y),
279 new OpenLayers.Geometry.Point(xyEnd.x, xyEnd.y)
280 );
281
282 var line = new OpenLayers.Geometry.LineString(points);
283
284 //Only draw each line once. Unfortunately this check is faster
285 //than drawing multiple lines.
286 var found = false;
287 $(lineElements).each(function(){
288 var checkLine = this.line;
289 if (( (checkLine.components[0].x === line.components[0].x) &&
290 (checkLine.components[0].y === line.components[0].y) &&
291 (checkLine.components[1].x === line.components[1].x) &&
292 (checkLine.components[1].y === line.components[1].y) ) ||
293 // if lines are "directional" (arrows) the opposite one isn't the same anymore!
294 ( (lineOverlayWidget.options.showArrows === false) &&
295 (checkLine.components[0].x === line.components[1].x) &&
296 (checkLine.components[0].y === line.components[1].y) &&
297 (checkLine.components[1].x === line.components[0].x) &&
298 (checkLine.components[1].y === line.components[0].y) ) ){
299 found = true;
300 //increase width of this line
301 this.width++;
302 //and don't draw it again
303 return false;
304 }
305 });
306
307 if (found === true)
308 return;
309
310 lineElements.push({line:line,width:1});
311 });
312
313 $(lineElements).each(function(){
314 var line = this.line;
315 var width = this.width;
316
317 if (lineOverlayWidget.options.showArrows === true){
318 var xyStart = line.components[0];
319 var xyEnd = line.components[1];
320 var arrowFeature = new OpenLayers.Feature.Vector(
321 new OpenLayers.Geometry.Point(xyEnd.x-((xyEnd.x-xyStart.x)*0.03), xyEnd.y-((xyEnd.y-xyStart.y)*0.03)),
322 {
323 type: "triangle",
324 angle: bearing(xyStart.x,xyStart.y,xyEnd.x,xyEnd.y),
325 width: width+1
326 }
327 );
328 lineLayer.addFeatures(arrowFeature);
329 }
330
331 var lineFeature = new OpenLayers.Feature.Vector(line,{width:width});
332 lineLayer.addFeatures(lineFeature);
333 });
334 });
335 },
336
337 attachMapWidget : function(mapWidget) {
338 var styles = new OpenLayers.StyleMap({
339 "default": {
340 graphicName: "${type}",
341 rotation: "${angle}",
342 pointRadius: "${width}",
343 strokeColor: '#0000ff',
344 strokeOpacity: 0.5,
345 strokeWidth: "${width}",
346 fillOpacity: 1
347 }
348 });
349
350 var lineOverlayWidget = this;
351 var lineLayer = new OpenLayers.Layer.Vector("Line Layer", {
352 styleMap: styles,
353 isBaseLayer:false
354 });
355 mapWidget.openlayersMap.addLayer(lineLayer);
356 mapWidget.openlayersMap.setLayerIndex(lineLayer, 99);
357 this.attachedMapWidgets.push({mapWidget:mapWidget,lineLayer:lineLayer});
358 //register zoom event
359 mapWidget.openlayersMap.events.register("zoomend", lineOverlayWidget, function(){
360 this.drawLines(this.selected);
361 });
362 }
363 };