comparison d3s_examples/python-neo4jrestclient/static/platin/js/GeoTemConfig.js @ 8:18ef6948d689

new d3s examples
author Dirk Wintergruen <dwinter@mpiwg-berlin.mpg.de>
date Thu, 01 Oct 2015 17:17:27 +0200
parents
children
comparison
equal deleted inserted replaced
7:45dad9e38c82 8:18ef6948d689
1 /*
2 * GeoTemConfig.js
3 *
4 * Copyright (c) 2012, Stefan Jänicke. 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 /**
23 * @class GeoTemConfig
24 * Global GeoTemCo Configuration File
25 * @author Stefan Jänicke (stjaenicke@informatik.uni-leipzig.de)
26 * @release 1.0
27 * @release date: 2012-07-27
28 * @version date: 2012-07-27
29 */
30
31
32 // credits: user76888, The Digital Gabeg (http://stackoverflow.com/questions/1539367)
33 $.fn.cleanWhitespace = function() {
34 textNodes = this.contents().filter( function() {
35 return (this.nodeType == 3 && !/\S/.test(this.nodeValue));
36 }).remove();
37 return this;
38 };
39
40 GeoTemConfig = {
41 debug : false, //show debug output (esp. regarding corrupt datasets)
42 incompleteData : true, // show/hide data with either temporal or spatial metadata
43 inverseFilter : true, // if inverse filtering is offered
44 mouseWheelZoom : true, // enable/disable zoom with mouse wheel on map & timeplot
45 language : 'en', // default language of GeoTemCo
46 allowFilter : true, // if filtering should be allowed
47 highlightEvents : true, // if updates after highlight events
48 selectionEvents : true, // if updates after selection events
49 tableExportDataset : true, // export dataset to KML
50 allowCustomColoring : false, // if DataObjects can have an own color (useful for weighted coloring)
51 allowUserShapeAndColorChange: false, // if the user can change the shapes and color of datasets
52 // this turns MapConfig.useGraphics auto-on, but uses circles as default
53 loadColorFromDataset : false, // if DataObject color should be loaded automatically (from column "color")
54 allowColumnRenaming : true,
55 //proxy : 'php/proxy.php?address=', //set this if a HTTP proxy shall be used (e.g. to bypass X-Domain problems)
56 //colors for several datasets; rgb1 will be used for selected objects, rgb0 for unselected
57 colors : [{
58 r1 : 255,
59 g1 : 101,
60 b1 : 0,
61 r0 : 253,
62 g0 : 229,
63 b0 : 205
64 }, {
65 r1 : 144,
66 g1 : 26,
67 b1 : 255,
68 r0 : 230,
69 g0 : 225,
70 b0 : 255
71 }, {
72 r1 : 0,
73 g1 : 217,
74 b1 : 0,
75 r0 : 213,
76 g0 : 255,
77 b0 : 213
78 }, {
79 r1 : 240,
80 g1 : 220,
81 b1 : 0,
82 r0 : 247,
83 g0 : 244,
84 b0 : 197
85 }]
86
87 }
88
89 GeoTemConfig.ie = false;
90 GeoTemConfig.ie8 = false;
91
92 GeoTemConfig.independentMapId = 0;
93 GeoTemConfig.independentTimeId = 0;
94
95 if (/MSIE (\d+\.\d+);/.test(navigator.userAgent)) {
96 GeoTemConfig.ie = true;
97 var ieversion = new Number(RegExp.$1);
98 if (ieversion == 8) {
99 GeoTemConfig.ie8 = true;
100 }
101 }
102
103 GeoTemConfig.getIndependentId = function(target){
104 if( target == 'map' ){
105 return ++GeoTemConfig.independentMapId;
106 }
107 if( target == 'time' ){
108 return ++GeoTemConfig.independentTimeId;
109 }
110 return 0;
111 };
112
113 GeoTemConfig.setHexColor = function(hex,index,fill){
114 var result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex);
115 if( fill ){
116 GeoTemConfig.colors[index].r0 = parseInt(result[1], 16);
117 GeoTemConfig.colors[index].g0 = parseInt(result[2], 16);
118 GeoTemConfig.colors[index].b0 = parseInt(result[3], 16);
119 }
120 else {
121 GeoTemConfig.colors[index].r1 = parseInt(result[1], 16);
122 GeoTemConfig.colors[index].g1 = parseInt(result[2], 16);
123 GeoTemConfig.colors[index].b1 = parseInt(result[3], 16);
124 }
125 }
126
127 GeoTemConfig.setRgbColor = function(r,g,b,index,fill){
128 if( fill ){
129 GeoTemConfig.colors[index].r0 = r;
130 GeoTemConfig.colors[index].g0 = g;
131 GeoTemConfig.colors[index].b0 = b;
132 }
133 else {
134 GeoTemConfig.colors[index].r1 = r;
135 GeoTemConfig.colors[index].g1 = g;
136 GeoTemConfig.colors[index].b1 = b;
137 }
138 }
139
140 GeoTemConfig.configure = function(urlPrefix) {
141 GeoTemConfig.urlPrefix = urlPrefix;
142 GeoTemConfig.path = GeoTemConfig.urlPrefix + "images/";
143 }
144
145 GeoTemConfig.applySettings = function(settings) {
146 $.extend(this, settings);
147 };
148
149 //Keeps track of how many colors where assigned yet.
150 GeoTemConfig.assignedColorCount = 0;
151 GeoTemConfig.getColor = function(id){
152 if (typeof GeoTemConfig.datasets[id].color === "undefined"){
153 var color;
154
155 while (true){
156 if( GeoTemConfig.colors.length <= GeoTemConfig.assignedColorCount ){
157 color = {
158 r1 : Math.floor((Math.random()*255)+1),
159 g1 : Math.floor((Math.random()*255)+1),
160 b1 : Math.floor((Math.random()*255)+1),
161 r0 : 230,
162 g0 : 230,
163 b0 : 230
164 };
165 } else
166 color = GeoTemConfig.colors[GeoTemConfig.assignedColorCount];
167
168 //make sure that no other dataset has this color
169 //TODO: one could also check that they are not too much alike
170 var found = false;
171 for (var i = 0; i < GeoTemConfig.datasets.length; i++){
172 var dataset = GeoTemConfig.datasets[i];
173
174 if (typeof dataset.color === "undefined")
175 continue;
176
177 if ( (dataset.color.r1 == color.r1) &&
178 (dataset.color.g1 == color.g1) &&
179 (dataset.color.b1 == color.b1) ){
180 found = true;
181 break;
182 }
183 }
184 if (found === true){
185 if( GeoTemConfig.colors.length <= GeoTemConfig.assignedColorCount ){
186 //next time skip over this color
187 GeoTemConfig.assignedColorCount++;
188 }
189 continue;
190 } else {
191 GeoTemConfig.colors.push(color);
192 break;
193 }
194 }
195 GeoTemConfig.datasets[id].color = color;
196
197 GeoTemConfig.assignedColorCount++;
198 }
199 return GeoTemConfig.datasets[id].color;
200 };
201
202 GeoTemConfig.getAverageDatasetColor = function(id, objects){
203 var c = new Object();
204 var datasetColor = GeoTemConfig.getColor(id);
205 c.r0 = datasetColor.r0;
206 c.g0 = datasetColor.g0;
207 c.b0 = datasetColor.b0;
208 c.r1 = datasetColor.r1;
209 c.g1 = datasetColor.g1;
210 c.b1 = datasetColor.b1;
211 if (!GeoTemConfig.allowCustomColoring)
212 return c;
213 if (objects.length == 0)
214 return c;
215 var avgColor = new Object();
216 avgColor.r0 = 0;
217 avgColor.g0 = 0;
218 avgColor.b0 = 0;
219 avgColor.r1 = 0;
220 avgColor.g1 = 0;
221 avgColor.b1 = 0;
222
223 $(objects).each(function(){
224 if (this.hasColorInformation){
225 avgColor.r0 += this.color.r0;
226 avgColor.g0 += this.color.g0;
227 avgColor.b0 += this.color.b0;
228 avgColor.r1 += this.color.r1;
229 avgColor.g1 += this.color.g1;
230 avgColor.b1 += this.color.b1;
231 } else {
232 avgColor.r0 += datasetColor.r0;
233 avgColor.g0 += datasetColor.g0;
234 avgColor.b0 += datasetColor.b0;
235 avgColor.r1 += datasetColor.r1;
236 avgColor.g1 += datasetColor.g1;
237 avgColor.b1 += datasetColor.b1;
238 }
239 });
240
241 c.r0 = Math.floor(avgColor.r0/objects.length);
242 c.g0 = Math.floor(avgColor.g0/objects.length);
243 c.b0 = Math.floor(avgColor.b0/objects.length);
244 c.r1 = Math.floor(avgColor.r1/objects.length);
245 c.g1 = Math.floor(avgColor.g1/objects.length);
246 c.b1 = Math.floor(avgColor.b1/objects.length);
247
248 return c;
249 };
250
251 GeoTemConfig.getString = function(field) {
252 if ( typeof Tooltips[GeoTemConfig.language] == 'undefined') {
253 GeoTemConfig.language = 'en';
254 }
255 return Tooltips[GeoTemConfig.language][field];
256 }
257 /**
258 * returns the actual mouse position
259 * @param {Event} e the mouseevent
260 * @return the top and left position on the screen
261 */
262 GeoTemConfig.getMousePosition = function(e) {
263 if (!e) {
264 e = window.event;
265 }
266 var body = (window.document.compatMode && window.document.compatMode == "CSS1Compat") ? window.document.documentElement : window.document.body;
267 return {
268 top : e.pageY ? e.pageY : e.clientY,
269 left : e.pageX ? e.pageX : e.clientX
270 };
271 }
272 /**
273 * returns the json object of the file from the given url
274 * @param {String} url the url of the file to load
275 * @return json object of given file
276 */
277 GeoTemConfig.getJson = function(url,asyncFunc) {
278 var async = false;
279 if( asyncFunc ){
280 async = true;
281 }
282
283 var data;
284 $.ajax({
285 url : url,
286 async : async,
287 dataType : 'json',
288 success : function(json) {
289 data = json;
290 if (async){
291 asyncFunc(data);
292 }
293 }
294 });
295
296 if (async){
297 return data;
298 }
299 }
300
301 GeoTemConfig.mergeObjects = function(set1, set2) {
302 var inside = [];
303 var newSet = [];
304 for (var i = 0; i < GeoTemConfig.datasets.length; i++){
305 inside.push([]);
306 newSet.push([]);
307 }
308 for (var i = 0; i < set1.length; i++) {
309 for (var j = 0; j < set1[i].length; j++) {
310 inside[i][set1[i][j].index] = true;
311 newSet[i].push(set1[i][j]);
312 }
313 }
314 for (var i = 0; i < set2.length; i++) {
315 for (var j = 0; j < set2[i].length; j++) {
316 if (!inside[i][set2[i][j].index]) {
317 newSet[i].push(set2[i][j]);
318 }
319 }
320 }
321 return newSet;
322 };
323
324 GeoTemConfig.datasets = [];
325
326 GeoTemConfig.addDataset = function(newDataset){
327 GeoTemConfig.datasets.push(newDataset);
328 Publisher.Publish('filterData', GeoTemConfig.datasets, null);
329 };
330
331 GeoTemConfig.addDatasets = function(newDatasets){
332 $(newDatasets).each(function(){
333 GeoTemConfig.datasets.push(this);
334 });
335 Publisher.Publish('filterData', GeoTemConfig.datasets, null);
336 };
337
338 GeoTemConfig.removeDataset = function(index){
339 GeoTemConfig.datasets.splice(index,1);
340 Publisher.Publish('filterData', GeoTemConfig.datasets, null);
341 };
342
343 GeoTemConfig.removeAllDatasets = function() {
344
345 if (GeoTemConfig.datasets.length > 0) {
346 GeoTemConfig.datasets.splice(0, GeoTemConfig.datasets.length);
347 Publisher.Publish('filterData', GeoTemConfig.datasets, null);
348 }
349 };
350
351 /**
352 * converts the csv-file into json-format
353 *
354 * @param {String}
355 * text
356 */
357 GeoTemConfig.convertCsv = function(text){
358 /* convert here from CSV to JSON */
359 var json = [];
360 /* define expected csv table headers (first line) */
361 var expectedHeaders = new Array("Name","Address","Description","Longitude","Latitude","TimeStamp","TimeSpan:begin","TimeSpan:end","weight");
362 /* convert csv string to array of arrays using ucsv library */
363 var csvArray = CSVToArray(text);
364 /* get real used table headers from csv file (first line) */
365 var usedHeaders = csvArray[0];
366 /* loop outer array, begin with second line */
367 for (var i = 1; i < csvArray.length; i++) {
368 var innerArray = csvArray[i];
369 var dataObject = new Object();
370 var tableContent = new Object();
371 /* exclude lines with no content */
372 var hasContent = false;
373 for (var j = 0; j < innerArray.length; j++) {
374 if (typeof innerArray[j] !== "undefined"){
375 if (typeof innerArray[j] === "string"){
376 if (innerArray[j].length > 0)
377 hasContent = true;
378 } else {
379 hasContent = true;
380 }
381 }
382
383 if (hasContent === true)
384 break;
385 }
386 if (hasContent === false)
387 continue;
388 /* loop inner array */
389 for (var j = 0; j < innerArray.length; j++) {
390 /* Name */
391 if (usedHeaders[j] == expectedHeaders[0]) {
392 dataObject["name"] = ""+innerArray[j];
393 tableContent["name"] = ""+innerArray[j];
394 }
395 /* Address */
396 else if (usedHeaders[j] == expectedHeaders[1]) {
397 dataObject["place"] = ""+innerArray[j];
398 tableContent["place"] = ""+innerArray[j];
399 }
400 /* Description */
401 else if (usedHeaders[j] == expectedHeaders[2]) {
402 dataObject["description"] = ""+innerArray[j];
403 tableContent["description"] = ""+innerArray[j];
404 }
405 /* TimeStamp */
406 else if (usedHeaders[j] == expectedHeaders[5]) {
407 dataObject["time"] = ""+innerArray[j];
408 }
409 /* TimeSpan:begin */
410 else if (usedHeaders[j] == expectedHeaders[6]) {
411 tableContent["TimeSpan:begin"] = ""+innerArray[j];
412 }
413 /* TimeSpan:end */
414 else if (usedHeaders[j] == expectedHeaders[7]) {
415 tableContent["TimeSpan:end"] = ""+innerArray[j];
416 }
417 /* weight */
418 else if (usedHeaders[j] == expectedHeaders[8]) {
419 dataObject["weight"] = ""+innerArray[j];
420 }
421 /* Longitude */
422 else if (usedHeaders[j] == expectedHeaders[3]) {
423 dataObject["lon"] = parseFloat(innerArray[j]);
424 }
425 /* Latitude */
426 else if (usedHeaders[j] == expectedHeaders[4]) {
427 dataObject["lat"] = parseFloat(innerArray[j]);
428 }
429 else {
430 var header = new String(usedHeaders[j]);
431 //remove leading and trailing Whitespace
432 header = $.trim(header);
433 tableContent[header] = ""+innerArray[j];
434 }
435 }
436
437 dataObject["tableContent"] = tableContent;
438
439 json.push(dataObject);
440 }
441
442 return json;
443 };
444
445 /**
446 * returns the xml dom object of the file from the given url
447 * @param {String} url the url of the file to load
448 * @return xml dom object of given file
449 */
450 GeoTemConfig.getKml = function(url,asyncFunc) {
451 var data;
452 var async = false;
453 if( asyncFunc ){
454 async = true;
455 }
456 $.ajax({
457 url : url,
458 async : async,
459 dataType : 'xml',
460 success : function(xml) {
461 if( asyncFunc ){
462 asyncFunc(xml);
463 }
464 else {
465 data = xml;
466 }
467 }
468 });
469 if( !async ){
470 return data;
471 }
472 }
473
474 /**
475 * returns an array of all xml dom object of the kmls
476 * found in the zip file from the given url
477 *
478 * can only be used with asyncFunc (because of browser
479 * constraints regarding arraybuffer)
480 *
481 * @param {String} url the url of the file to load
482 * @return xml dom object of given file
483 */
484 GeoTemConfig.getKmz = function(url,asyncFunc) {
485 var kmlDom = new Array();
486
487 var async = true;
488 if( !asyncFunc ){
489 //if no asyncFunc is given return an empty array
490 return kmlDom;
491 }
492
493 //use XMLHttpRequest as "arraybuffer" is not
494 //supported in jQuery native $.get
495 var req = new XMLHttpRequest();
496 req.open("GET",url,async);
497 req.responseType = "arraybuffer";
498 req.onload = function() {
499 var zip = new JSZip();
500 zip.load(req.response, {base64:false});
501 var kmlFiles = zip.file(new RegExp("kml$"));
502
503 $(kmlFiles).each(function(){
504 var kml = this;
505 if (kml.data != null) {
506 kmlDom.push($.parseXML(kml.data));
507 }
508 });
509
510 asyncFunc(kmlDom);
511 };
512 req.send();
513 };
514
515 /**
516 * returns the JSON "object"
517 * from the csv file from the given url
518 * @param {String} url the url of the file to load
519 * @return xml dom object of given file
520 */
521 GeoTemConfig.getCsv = function(url,asyncFunc) {
522 var async = false;
523 if( asyncFunc ){
524 async = true;
525 }
526
527 //use XMLHttpRequest as synchronous behaviour
528 //is not supported in jQuery native $.get
529 var req = new XMLHttpRequest();
530 req.open("GET",url,async);
531 //can only be set on asynchronous now
532 //req.responseType = "text";
533 var json;
534 req.onload = function() {
535 json = GeoTemConfig.convertCsv(req.response);
536 if( asyncFunc )
537 asyncFunc(json);
538 };
539 req.send();
540
541 if( !async ){
542 return json;
543 }
544 };
545
546 /**
547 * loads a binary file
548 * @param {String} url of the file to load
549 * @return binary data
550 */
551 GeoTemConfig.getBinary = function(url,asyncFunc) {
552 var async = true;
553
554 var req = new XMLHttpRequest();
555 req.open("GET",url,async);
556 req.responseType = "arraybuffer";
557
558 var binaryData;
559 req.onload = function() {
560 var arrayBuffer = req.response;
561 asyncFunc(arrayBuffer);
562 };
563 req.send();
564 };
565
566 /**
567 * returns a Date and a SimileAjax.DateTime granularity value for a given XML time
568 * @param {String} xmlTime the XML time as String
569 * @return JSON object with a Date and a SimileAjax.DateTime granularity
570 */
571 GeoTemConfig.getTimeData = function(xmlTime) {
572 if (!xmlTime)
573 return;
574 var dateData;
575 try {
576 var bc = false;
577 if (xmlTime.startsWith("-")) {
578 bc = true;
579 xmlTime = xmlTime.substring(1);
580 }
581 var timeSplit = xmlTime.split("T");
582 var timeData = timeSplit[0].split("-");
583 for (var i = 0; i < timeData.length; i++) {
584 parseInt(timeData[i]);
585 }
586 if (bc) {
587 timeData[0] = "-" + timeData[0];
588 }
589 if (timeSplit.length == 1) {
590 dateData = timeData;
591 } else {
592 var dayData;
593 if (timeSplit[1].indexOf("Z") != -1) {
594 dayData = timeSplit[1].substring(0, timeSplit[1].indexOf("Z") - 1).split(":");
595 } else {
596 dayData = timeSplit[1].substring(0, timeSplit[1].indexOf("+") - 1).split(":");
597 }
598 for (var i = 0; i < timeData.length; i++) {
599 parseInt(dayData[i]);
600 }
601 dateData = timeData.concat(dayData);
602 }
603 } catch (exception) {
604 return null;
605 }
606 var date, granularity;
607 if (dateData.length == 6) {
608 granularity = SimileAjax.DateTime.SECOND;
609 date = new Date(Date.UTC(dateData[0], dateData[1] - 1, dateData[2], dateData[3], dateData[4], dateData[5]));
610 } else if (dateData.length == 3) {
611 granularity = SimileAjax.DateTime.DAY;
612 date = new Date(Date.UTC(dateData[0], dateData[1] - 1, dateData[2]));
613 } else if (dateData.length == 2) {
614 granularity = SimileAjax.DateTime.MONTH;
615 date = new Date(Date.UTC(dateData[0], dateData[1] - 1, 1));
616 } else if (dateData.length == 1) {
617 granularity = SimileAjax.DateTime.YEAR;
618 date = new Date(Date.UTC(dateData[0], 0, 1));
619 }
620 if (timeData[0] && timeData[0] < 100) {
621 date.setFullYear(timeData[0]);
622 }
623
624 //check data validity;
625 var isValidDate = true;
626 if ( date instanceof Date ) {
627 if ( isNaN( date.getTime() ) )
628 isValidDate = false;
629 } else
630 isValidDate = false;
631
632 if (!isValidDate){
633 if ((GeoTemConfig.debug)&&(typeof console !== "undefined"))
634 console.error(xmlTime + " is no valid time format");
635 return null;
636 }
637
638 return {
639 date : date,
640 granularity : granularity
641 };
642 }
643 /**
644 * converts a JSON array into an array of data objects
645 * @param {JSON} JSON a JSON array of data items
646 * @return an array of data objects
647 */
648 GeoTemConfig.loadJson = function(JSON) {
649 var mapTimeObjects = [];
650 var runningIndex = 0;
651 for (var i in JSON ) {
652 try {
653 var item = JSON[i];
654 var index = item.index || item.id || runningIndex++;
655 var name = item.name || "";
656 var description = item.description || "";
657 var tableContent = item.tableContent || [];
658 var locations = [];
659 if (item.location instanceof Array) {
660 for (var j = 0; j < item.location.length; j++) {
661 var place = item.location[j].place || "unknown";
662 var lon = item.location[j].lon;
663 var lat = item.location[j].lat;
664 if ((typeof lon === "undefined" || typeof lat === "undefined" || isNaN(lon) || isNaN(lat) ) && !GeoTemConfig.incompleteData) {
665 throw "e";
666 }
667 locations.push({
668 longitude : lon,
669 latitude : lat,
670 place : place
671 });
672 }
673 } else {
674 var place = item.place || "unknown";
675 var lon = item.lon;
676 var lat = item.lat;
677 if ((typeof lon === "undefined" || typeof lat === "undefined" || isNaN(lon) || isNaN(lat) ) && !GeoTemConfig.incompleteData) {
678 throw "e";
679 }
680 locations.push({
681 longitude : lon,
682 latitude : lat,
683 place : place
684 });
685 }
686 var dates = [];
687 if (item.time instanceof Array) {
688 for (var j = 0; j < item.time.length; j++) {
689 var time = GeoTemConfig.getTimeData(item.time[j]);
690 if (time == null && !GeoTemConfig.incompleteData) {
691 throw "e";
692 }
693 dates.push(time);
694 }
695 } else {
696 var time = GeoTemConfig.getTimeData(item.time);
697 if (time == null && !GeoTemConfig.incompleteData) {
698 throw "e";
699 }
700 if (time != null) {
701 dates.push(time);
702 }
703 }
704 var weight = parseInt(item.weight) || 1;
705 //add all "other" attributes to table data
706 //this is a hack to allow "invalid" JSONs
707 var specialAttributes = ["id", "name", "description", "lon", "lat", "place", "time",
708 "tableContent", "location", "time"];
709 for (var attribute in item){
710 if ($.inArray(attribute, specialAttributes) == -1){
711 tableContent[attribute] = item[attribute];
712 }
713 }
714
715 var mapTimeObject = new DataObject(name, description, locations, dates, weight, tableContent);
716 mapTimeObject.setIndex(index);
717 mapTimeObjects.push(mapTimeObject);
718 } catch(e) {
719 continue;
720 }
721 }
722
723 if (GeoTemConfig.loadColorFromDataset)
724 GeoTemConfig.loadDataObjectColoring(mapTimeObjects);
725
726 return mapTimeObjects;
727 }
728 /**
729 * converts a KML dom into an array of data objects
730 * @param {XML dom} kml the XML dom for the KML file
731 * @return an array of data objects
732 */
733 GeoTemConfig.loadKml = function(kml) {
734 var mapObjects = [];
735 var elements = kml.getElementsByTagName("Placemark");
736 if (elements.length == 0) {
737 return [];
738 }
739 var index = 0;
740 var descriptionTableHeaders = [];
741 var xmlSerializer = new XMLSerializer();
742
743 for (var i = 0; i < elements.length; i++) {
744 var placemark = elements[i];
745 var name, description, place, granularity, lon, lat, tableContent = [], time = [], location = [];
746 var weight = 1;
747 var timeData = false, mapData = false;
748
749 try {
750 description = placemark.getElementsByTagName("description")[0].childNodes[0].nodeValue;
751
752 //cleanWhitespace removes non-sense text-nodes (space, tab)
753 //and is an addition to jquery defined above
754 try {
755 var descriptionDocument = $($.parseXML(description)).cleanWhitespace();
756
757 //check whether the description element contains a table
758 //if yes, this data will be loaded as separate columns
759 $(descriptionDocument).find("table").each(function(){
760 $(this).find("tr").each(
761 function() {
762 var isHeader = true;
763 var lastHeader = "";
764
765 $(this).find("td").each(
766 function() {
767 if (isHeader) {
768 lastHeader = $.trim($(this).text());
769 isHeader = false;
770 } else {
771 var value = "";
772
773 //if this td contains HTML, serialize all
774 //it's children (the "content"!)
775 $(this).children().each(
776 function() {
777 value += xmlSerializer.serializeToString(this);
778 }
779 );
780
781 //no HTML content (or no content at all)
782 if (value.length == 0)
783 value = $(this).text();
784 if (typeof value === "undefined")
785 value = "";
786
787 if ($.inArray(lastHeader, descriptionTableHeaders) === -1)
788 descriptionTableHeaders.push(lastHeader);
789
790 if (tableContent[lastHeader] != null)
791 //append if a field occures more than once
792 tableContent[lastHeader] += "\n" + value;
793 else
794 tableContent[lastHeader] = value;
795
796 isHeader = true;
797 }
798 }
799 );
800 }
801 );
802 });
803 } catch(e) {
804 //couldn't be parsed, so it contains no html table
805 //or is not in valid XHTML syntax
806 }
807
808 //check whether the description element contains content in the form of equations
809 //e.g. someDescriptor = someValue, where these eqations are separated by <br/>
810 //if yes, this data will be loaded as separate columns
811 var descriptionRows = description.replace(/<\s*br\s*[\/]*\s*>/g,"<br/>");
812 $(descriptionRows.split("<br/>")).each(function(){
813 var row = this;
814
815 if (typeof row === "undefined")
816 return;
817
818 var headerAndValue = row.split("=");
819 if (headerAndValue.length != 2)
820 return;
821
822 var header = $.trim(headerAndValue[0]);
823 var value = $.trim(headerAndValue[1]);
824
825 if ($.inArray(header, descriptionTableHeaders) === -1)
826 descriptionTableHeaders.push(header);
827
828 if (tableContent[header] != null)
829 //append if a field occures more than once
830 tableContent[header] += "\n" + value;
831 else
832 tableContent[header] = value;
833 });
834
835 tableContent["description"] = description;
836 } catch(e) {
837 description = "";
838 }
839
840 try {
841 name = placemark.getElementsByTagName("name")[0].childNodes[0].nodeValue;
842 tableContent["name"] = name;
843 } catch(e) {
844 if (typeof tableContent["name"] !== "undefined")
845 name = tableContent["name"];
846 else
847 name = "";
848 }
849
850 try {
851 place = placemark.getElementsByTagName("address")[0].childNodes[0].nodeValue;
852 tableContent["place"] = place;
853 } catch(e) {
854 if (typeof tableContent["place"] !== "undefined")
855 place = tableContent["place"];
856 else
857 place = "";
858 }
859
860 try {
861 var coordinates = placemark.getElementsByTagName("Point")[0].getElementsByTagName("coordinates")[0].childNodes[0].nodeValue;
862 var lonlat = coordinates.split(",");
863 lon = lonlat[0];
864 lat = lonlat[1];
865 if (lon == "" || lat == "" || isNaN(lon) || isNaN(lat)) {
866 throw "e";
867 }
868 location.push({
869 longitude : lon,
870 latitude : lat,
871 place : place
872 });
873 } catch(e) {
874 if (!GeoTemConfig.incompleteData) {
875 continue;
876 }
877 }
878
879 try {
880 var tuple = GeoTemConfig.getTimeData(placemark.getElementsByTagName("TimeStamp")[0].getElementsByTagName("when")[0].childNodes[0].nodeValue);
881 if (tuple != null) {
882 time.push(tuple);
883 timeData = true;
884 } else if (!GeoTemConfig.incompleteData) {
885 continue;
886 }
887 } catch(e) {
888 try {
889 if ( (typeof tableContent["TimeSpan:begin"] === "undefined") &&
890 (typeof tableContent["TimeSpan:end"] === "undefined") ){
891 var timeStart = $(placemark).find("TimeSpan begin").text();
892 var timeEnd = $(placemark).find("TimeSpan end").text();
893
894 if ( (timeStart != "") && (timeStart != "") ){
895 tableContent["TimeSpan:begin"] = timeStart;
896 tableContent["TimeSpan:end"] = timeEnd;
897
898 timeData = true;
899 }
900 }
901 } catch(e) {
902 if (!GeoTemConfig.incompleteData) {
903 continue;
904 }
905 }
906 }
907 var object = new DataObject(name, description, location, time, 1, tableContent);
908 object.setIndex(index);
909 index++;
910 mapObjects.push(object);
911 }
912
913 //make sure that all "description table" columns exists in all rows
914 if (descriptionTableHeaders.length > 0){
915 $(mapObjects).each(function(){
916 var object = this;
917 $(descriptionTableHeaders).each(function(){
918 if (typeof object.tableContent[this] === "undefined")
919 object.tableContent[this] = "";
920 });
921 });
922 }
923
924 if (GeoTemConfig.loadColorFromDataset)
925 GeoTemConfig.loadDataObjectColoring(mapObjects);
926
927 return mapObjects;
928 };
929
930 GeoTemConfig.createKMLfromDataset = function(index){
931 var kmlContent = "<?xml version=\"1.0\" encoding=\"UTF-8\"?><kml xmlns=\"http://www.opengis.net/kml/2.2\"><Document>";
932
933 //credits: Anatoly Mironov, http://stackoverflow.com/questions/2573521/how-do-i-output-an-iso-8601-formatted-string-in-javascript
934 function pad(number) {
935 var r = String(number);
936 if ( r.length === 1 ) {
937 r = '0' + r;
938 }
939 return r;
940 }
941
942 var dateToISOString = function(date, granularity) {
943 var ISOString = date.getFullYear();
944
945 if (granularity <= SimileAjax.DateTime.MONTH)
946 ISOString += '-' + pad( date.getMonth() + 1 );
947 if (granularity <= SimileAjax.DateTime.DAY)
948 ISOString += '-' + pad( date.getDate() );
949 if (granularity <= SimileAjax.DateTime.HOUR){
950 ISOString += 'T' + pad( date.getHours() );
951 if (granularity <= SimileAjax.DateTime.MINUTE)
952 ISOString += ':' + pad( date.getMinutes() );
953 if (granularity <= SimileAjax.DateTime.SECOND)
954 ISOString += ':' + pad( date.getSeconds() );
955 if (granularity <= SimileAjax.DateTime.MILLISECOND)
956 ISOString += '.' + String( (date.getMilliseconds()/1000).toFixed(3) ).slice( 2, 5 );
957 ISOString += 'Z';
958 }
959
960 return ISOString;
961 };
962
963 $(GeoTemConfig.datasets[index].objects).each(function(){
964 var name = this.name;
965 var description = this.description;
966 //TODO: allow multiple time/date
967 var place = this.getPlace(0,0);
968 var lat = this.getLatitude(0);
969 var lon = this.getLongitude(0);
970
971 var kmlEntry = "<Placemark>";
972
973 kmlEntry += "<name><![CDATA[" + name + "]]></name>";
974 kmlEntry += "<address><![CDATA[" + place + "]]></address>";
975 kmlEntry += "<description><![CDATA[" + description + "]]></description>";
976 kmlEntry += "<Point><coordinates>" + lon + "," + lat + "</coordinates></Point>";
977
978 if (this.isTemporal){
979 kmlEntry += "<TimeStamp><when>" + dateToISOString(this.getDate(0), this.getTimeGranularity(0)) + "</when></TimeStamp>";
980 } else if (this.isFuzzyTemporal){
981 kmlEntry += "<TimeSpan>"+
982 "<begin>" + dateToISOString(this.TimeSpanBegin.utc().toDate(), this.TimeSpanBeginGranularity) + "</begin>" +
983 "<end>" + dateToISOString(this.TimeSpanEnd.utc().toDate(), this.TimeSpanEndGranularity) + "</end>" +
984 "</TimeSpan>";
985 }
986
987 kmlEntry += "</Placemark>";
988
989 kmlContent += kmlEntry;
990 });
991
992 kmlContent += "</Document></kml>";
993
994 return(kmlContent);
995 };
996
997 GeoTemConfig.createCSVfromDataset = function(index){
998 var csvContent = "";
999 var header = ["name", "description", "weight"];
1000 var tableContent = [];
1001
1002 var firstDataObject = GeoTemConfig.datasets[index].objects[0];
1003
1004 for(var key in firstDataObject.tableContent){
1005 var found = false;
1006 $(header).each(function(index,val){
1007 if (val === key){
1008 found = true;
1009 return false;
1010 }
1011 });
1012 if (found === true)
1013 continue;
1014 else
1015 tableContent.push(key);
1016 }
1017
1018 var isFirst = true;
1019 $(header).each(function(key,val){
1020 if (isFirst){
1021 isFirst = false;
1022 } else {
1023 csvContent += ",";
1024 }
1025
1026 //Rename according to CSV import definition
1027 if (val === "name")
1028 val = "Name";
1029 else if (val === "description")
1030 val = "Description";
1031 csvContent += "\""+val+"\"";
1032 });
1033 $(tableContent).each(function(key,val){
1034 if (isFirst){
1035 isFirst = false;
1036 } else {
1037 csvContent += ",";
1038 }
1039 csvContent += "\""+val+"\"";
1040 });
1041 //Names according to CSV import definition
1042 csvContent += ",\"Address\",\"Latitude\",\"Longitude\",\"TimeStamp\",\"TimeSpan:begin\",\"TimeSpan:end\"";
1043 csvContent += "\n";
1044
1045 var isFirstRow = true;
1046 $(GeoTemConfig.datasets[index].objects).each(function(){
1047 var elem = this;
1048
1049 if (isFirstRow){
1050 isFirstRow = false;
1051 } else {
1052 csvContent += "\n";
1053 }
1054
1055 var isFirst = true;
1056 $(header).each(function(key,val){
1057 if (isFirst){
1058 isFirst = false;
1059 } else {
1060 csvContent += ",";
1061 }
1062 csvContent += "\""+elem[val]+"\"";
1063 });
1064 $(tableContent).each(function(key,val){
1065 if (isFirst){
1066 isFirst = false;
1067 } else {
1068 csvContent += ",";
1069 }
1070 csvContent += "\""+elem.tableContent[val]+"\"";
1071 });
1072
1073 csvContent += ",";
1074 csvContent += "\"";
1075 if (elem.isGeospatial){
1076 csvContent += elem.locations[0].place;
1077 }
1078 csvContent += "\"";
1079
1080 csvContent += ",";
1081 csvContent += "\"";
1082 if ( (elem.isGeospatial) && (typeof elem.getLatitude(0) !== "undefined") ){
1083 csvContent += elem.getLatitude(0);
1084 }
1085 csvContent += "\"";
1086
1087 csvContent += ",";
1088 csvContent += "\"";
1089 if ( (elem.isGeospatial) && (typeof elem.getLongitude(0) !== "undefined") ){
1090 csvContent += elem.getLongitude(0);
1091 }
1092 csvContent += "\"";
1093
1094 csvContent += ",";
1095 csvContent += "\"";
1096 if ( (elem.isTemporal) && (typeof elem.getDate(0) !== "undefined") ){
1097 //TODO: not supported in IE8 switch to moment.js
1098 csvContent += elem.getDate(0).toISOString();
1099 }
1100 csvContent += "\"";
1101
1102 csvContent += ",";
1103 if (elem.isFuzzyTemporal){
1104 //TODO: not supported in IE8 switch to moment.js
1105 csvContent += "\""+elem.TimeSpanBegin.format()+"\",\""+elem.TimeSpanEnd.format()+"\"";
1106 } else {
1107 csvContent += "\"\",\"\"";
1108 }
1109 });
1110
1111 return(csvContent);
1112 };
1113 /**
1114 * iterates over Datasets/DataObjects and loads color values
1115 * from the "color0" and "color1" elements, which contains RGB
1116 * values in hex (CSS style #RRGGBB)
1117 * @param {dataObjects} array of DataObjects
1118 */
1119 GeoTemConfig.loadDataObjectColoring = function(dataObjects) {
1120 $(dataObjects).each(function(){
1121 var r0,g0,b0,r1,g1,b1;
1122 if ( (typeof this.tableContent !== "undefined") &&
1123 (typeof this.tableContent["color0"] !== "undefined") ){
1124 var color = this.tableContent["color0"];
1125 if ( (color.indexOf("#") == 0) && (color.length == 7) ){
1126 r0 = parseInt("0x"+color.substr(1,2));
1127 g0 = parseInt("0x"+color.substr(3,2));
1128 b0 = parseInt("0x"+color.substr(5,2));
1129 }
1130 }
1131 if ( (typeof this.tableContent !== "undefined") &&
1132 (typeof this.tableContent["color1"] !== "undefined") ){
1133 var color = this.tableContent["color1"];
1134 if ( (color.indexOf("#") == 0) && (color.length == 7) ){
1135 r1 = parseInt("0x"+color.substr(1,2));
1136 g1 = parseInt("0x"+color.substr(3,2));
1137 b1 = parseInt("0x"+color.substr(5,2));
1138 }
1139 }
1140
1141 if ( (typeof r0 !== "undefined") && (typeof g0 !== "undefined") && (typeof b0 !== "undefined") &&
1142 (typeof r1 !== "undefined") && (typeof g1 !== "undefined") && (typeof b1 !== "undefined") ){
1143 this.setColor(r0,g0,b0,r1,g1,b1);
1144 delete this.tableContent["color0"];
1145 delete this.tableContent["color1"];
1146 } else {
1147 if ((GeoTemConfig.debug)&&(typeof console !== undefined))
1148 console.error("Object '" + this.name + "' has invalid color information");
1149 }
1150 });
1151 };
1152
1153 /**
1154 * renames (or copies, see below) a column of each DataObject in a Dataset
1155 * @param {Dataset} dataset the dataset where the rename should take place
1156 * @param {String} oldColumn name of column that will be renamed
1157 * @param {String} newColumn new name of column
1158 * @param {Boolean} keepOld keep old column (copy mode)
1159 * @return an array of data objects
1160 */
1161 GeoTemConfig.renameColumns = function(dataset, renames){
1162 if (renames.length===0){
1163 return;
1164 }
1165 for (var renCnt = 0; renCnt < renames.length; renCnt++){
1166 var oldColumn = renames[renCnt].oldColumn;
1167 var newColumn = renames[renCnt].newColumn;
1168
1169 var keepOld = renames[renCnt].keepOld;
1170 if (typeof keepOld === "undefined"){
1171 keepOld = true;
1172 }
1173 var oldColumObject = {};
1174 if (oldColumn.indexOf("[") != -1){
1175 oldColumObject.columnName = oldColumn.split("[")[0];
1176 var IndexAndAttribute = oldColumn.split("[")[1];
1177 if (IndexAndAttribute.indexOf("]") != -1){
1178 oldColumObject.type = 2;
1179 oldColumObject.arrayIndex = IndexAndAttribute.split("]")[0];
1180 var attribute = IndexAndAttribute.split("]")[1];
1181 if (attribute.length > 0){
1182 oldColumObject.type = 3;
1183 oldColumObject.attribute = attribute.split(".")[1];
1184 }
1185 }
1186 } else {
1187 oldColumObject.type = 1;
1188 oldColumObject.name = oldColumn;
1189 }
1190
1191 var newColumObject = {};
1192 if (newColumn.indexOf("[") != -1){
1193 newColumObject.name = newColumn.split("[")[0];
1194 var IndexAndAttribute = newColumn.split("[")[1];
1195 if (IndexAndAttribute.indexOf("]") != -1){
1196 newColumObject.type = 2;
1197 newColumObject.arrayIndex = IndexAndAttribute.split("]")[0];
1198 var attribute = IndexAndAttribute.split("]")[1];
1199 if (attribute.length > 0){
1200 newColumObject.type = 3;
1201 newColumObject.attribute = attribute.split(".")[1];
1202 }
1203 }
1204 } else {
1205 newColumObject.type = 1;
1206 newColumObject.name = newColumn;
1207 }
1208
1209 for (var i = 0; i < dataset.objects.length; i++){
1210 var dataObject = dataset.objects[i];
1211
1212 //get value from old column name
1213 var value;
1214 if (oldColumObject.type == 1){
1215 value = dataObject[oldColumObject.name];
1216 if (typeof value === "undefined"){
1217 value = dataObject.tableContent[oldColumObject.name];
1218 }
1219 if (!keepOld){
1220 delete dataObject.tableContent[oldColumObject.name];
1221 delete dataObject[oldColumObject.name];
1222 }
1223 } else if (oldColumObject.type == 2){
1224 value = dataObject[oldColumObject.name][oldColumObject.arrayIndex];
1225 if (!keepOld){
1226 delete dataObject[oldColumObject.name][oldColumObject.arrayIndex];
1227 }
1228 } else if (oldColumObject.type == 3){
1229 value = dataObject[oldColumObject.name][oldColumObject.arrayIndex][oldColumObject.attribute];
1230 if (!keepOld){
1231 delete dataObject[oldColumObject.name][oldColumObject.arrayIndex][oldColumObject.attribute];
1232 }
1233 }
1234
1235 //create new column
1236 if (newColumObject.type == 1){
1237 dataObject[newColumObject.name] = value;
1238 dataObject.tableContent[newColumObject.name] = value;
1239 } else if (newColumObject.type == 2){
1240 if (typeof dataObject[newColumObject.name] == "undefined"){
1241 dataObject[newColumObject.name] = [];
1242 }
1243 dataObject[newColumObject.name][newColumObject.arrayIndex] = value;
1244 } else if (newColumObject.type == 3){
1245 if (typeof dataObject[newColumObject.name] == "undefined"){
1246 dataObject[newColumObject.name] = [];
1247 }
1248 if (typeof dataObject[newColumObject.name][newColumObject.arrayIndex] == "undefined"){
1249 dataObject[newColumObject.name][newColumObject.arrayIndex] = {};
1250 }
1251 dataObject[newColumObject.name][newColumObject.arrayIndex][newColumObject.attribute] = value;
1252 }
1253 }
1254 }
1255
1256 //actually create new dataObjects
1257 for (var i = 0; i < dataset.objects.length; i++){
1258 var dataObject = dataset.objects[i];
1259 //save index
1260 var index = dataObject.index;
1261
1262 dataset.objects[i] = new DataObject(dataObject.name, dataObject.description, dataObject.locations,
1263 dataObject.dates, dataObject.weight, dataObject.tableContent, dataObject.projection);
1264 //set index
1265 dataset.objects[i].setIndex(index);
1266 }
1267 };