mirror of
				https://github.com/jiawanlong/Cesium-Examples.git
				synced 2025-11-04 09:14:17 +00:00 
			
		
		
		
	
		
			
	
	
		
			2449 lines
		
	
	
		
			75 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
		
		
			
		
	
	
			2449 lines
		
	
	
		
			75 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
| 
								 | 
							
								/**
							 | 
						||
| 
								 | 
							
								 * Copyright (C) 2015 OSM Buildings, Jan Marsch
							 | 
						||
| 
								 | 
							
								 * A JavaScript library for visualizing building geometry on interactive maps.
							 | 
						||
| 
								 | 
							
								 * @osmbuildings, http://osmbuildings.org
							 | 
						||
| 
								 | 
							
								 */
							 | 
						||
| 
								 | 
							
								//****** file: prefix.js ******
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								(function (global) {
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    'use strict';
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								//****** file: shortcuts.js ******
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								// object access shortcuts
							 | 
						||
| 
								 | 
							
								    var
							 | 
						||
| 
								 | 
							
								        m = Math,
							 | 
						||
| 
								 | 
							
								        exp = m.exp,
							 | 
						||
| 
								 | 
							
								        log = m.log,
							 | 
						||
| 
								 | 
							
								        sin = m.sin,
							 | 
						||
| 
								 | 
							
								        cos = m.cos,
							 | 
						||
| 
								 | 
							
								        tan = m.tan,
							 | 
						||
| 
								 | 
							
								        atan = m.atan,
							 | 
						||
| 
								 | 
							
								        atan2 = m.atan2,
							 | 
						||
| 
								 | 
							
								        min = m.min,
							 | 
						||
| 
								 | 
							
								        max = m.max,
							 | 
						||
| 
								 | 
							
								        sqrt = m.sqrt,
							 | 
						||
| 
								 | 
							
								        ceil = m.ceil,
							 | 
						||
| 
								 | 
							
								        floor = m.floor,
							 | 
						||
| 
								 | 
							
								        round = m.round,
							 | 
						||
| 
								 | 
							
								        pow = m.pow;
							 | 
						||
| 
								 | 
							
								    var mapObj;
							 | 
						||
| 
								 | 
							
								// polyfills
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    var
							 | 
						||
| 
								 | 
							
								        Int32Array = Int32Array || Array,
							 | 
						||
| 
								 | 
							
								        Uint8Array = Uint8Array || Array;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    var IS_IOS = /iP(ad|hone|od)/g.test(navigator.userAgent);
							 | 
						||
| 
								 | 
							
								    var IS_MSIE = !!~navigator.userAgent.indexOf('Trident');
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    var requestAnimFrame = (global.requestAnimationFrame && !IS_IOS && !IS_MSIE) ?
							 | 
						||
| 
								 | 
							
								        global.requestAnimationFrame : function (callback) {
							 | 
						||
| 
								 | 
							
								            callback();
							 | 
						||
| 
								 | 
							
								        };
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								//****** file: Color.debug.js ******
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    var Color = (function (window) {
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        var w3cColors = {
							 | 
						||
| 
								 | 
							
								            aqua: '#00ffff',
							 | 
						||
| 
								 | 
							
								            black: '#000000',
							 | 
						||
| 
								 | 
							
								            blue: '#0000ff',
							 | 
						||
| 
								 | 
							
								            fuchsia: '#ff00ff',
							 | 
						||
| 
								 | 
							
								            gray: '#808080',
							 | 
						||
| 
								 | 
							
								            grey: '#808080',
							 | 
						||
| 
								 | 
							
								            green: '#008000',
							 | 
						||
| 
								 | 
							
								            lime: '#00ff00',
							 | 
						||
| 
								 | 
							
								            maroon: '#800000',
							 | 
						||
| 
								 | 
							
								            navy: '#000080',
							 | 
						||
| 
								 | 
							
								            olive: '#808000',
							 | 
						||
| 
								 | 
							
								            orange: '#ffa500',
							 | 
						||
| 
								 | 
							
								            purple: '#800080',
							 | 
						||
| 
								 | 
							
								            red: '#ff0000',
							 | 
						||
| 
								 | 
							
								            silver: '#c0c0c0',
							 | 
						||
| 
								 | 
							
								            teal: '#008080',
							 | 
						||
| 
								 | 
							
								            white: '#ffffff',
							 | 
						||
| 
								 | 
							
								            yellow: '#ffff00'
							 | 
						||
| 
								 | 
							
								        };
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        function hue2rgb(p, q, t) {
							 | 
						||
| 
								 | 
							
								            if (t < 0) t += 1;
							 | 
						||
| 
								 | 
							
								            if (t > 1) t -= 1;
							 | 
						||
| 
								 | 
							
								            if (t < 1 / 6) return p + (q - p) * 6 * t;
							 | 
						||
| 
								 | 
							
								            if (t < 1 / 2) return q;
							 | 
						||
| 
								 | 
							
								            if (t < 2 / 3) return p + (q - p) * (2 / 3 - t) * 6;
							 | 
						||
| 
								 | 
							
								            return p;
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        function clamp(v, max) {
							 | 
						||
| 
								 | 
							
								            return Math.min(max, Math.max(0, v));
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        var Color = function (h, s, l, a) {
							 | 
						||
| 
								 | 
							
								            this.H = h;
							 | 
						||
| 
								 | 
							
								            this.S = s;
							 | 
						||
| 
								 | 
							
								            this.L = l;
							 | 
						||
| 
								 | 
							
								            this.A = a;
							 | 
						||
| 
								 | 
							
								        };
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        /*
							 | 
						||
| 
								 | 
							
								         * str can be in any of these:
							 | 
						||
| 
								 | 
							
								         * #0099ff rgb(64, 128, 255) rgba(64, 128, 255, 0.5)
							 | 
						||
| 
								 | 
							
								         */
							 | 
						||
| 
								 | 
							
								        Color.parse = function (str) {
							 | 
						||
| 
								 | 
							
								            var
							 | 
						||
| 
								 | 
							
								                r = 0, g = 0, b = 0, a = 1,
							 | 
						||
| 
								 | 
							
								                m;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								            str = ('' + str).toLowerCase();
							 | 
						||
| 
								 | 
							
								            str = w3cColors[str] || str;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								            if ((m = str.match(/^#(\w{2})(\w{2})(\w{2})$/))) {
							 | 
						||
| 
								 | 
							
								                r = parseInt(m[1], 16);
							 | 
						||
| 
								 | 
							
								                g = parseInt(m[2], 16);
							 | 
						||
| 
								 | 
							
								                b = parseInt(m[3], 16);
							 | 
						||
| 
								 | 
							
								            } else if ((m = str.match(/rgba?\((\d+)\D+(\d+)\D+(\d+)(\D+([\d.]+))?\)/))) {
							 | 
						||
| 
								 | 
							
								                r = parseInt(m[1], 10);
							 | 
						||
| 
								 | 
							
								                g = parseInt(m[2], 10);
							 | 
						||
| 
								 | 
							
								                b = parseInt(m[3], 10);
							 | 
						||
| 
								 | 
							
								                a = m[4] ? parseFloat(m[5]) : 1;
							 | 
						||
| 
								 | 
							
								            } else {
							 | 
						||
| 
								 | 
							
								                return;
							 | 
						||
| 
								 | 
							
								            }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								            return this.fromRGBA(r, g, b, a);
							 | 
						||
| 
								 | 
							
								        };
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        Color.fromRGBA = function (r, g, b, a) {
							 | 
						||
| 
								 | 
							
								            if (typeof r === 'object') {
							 | 
						||
| 
								 | 
							
								                g = r.g / 255;
							 | 
						||
| 
								 | 
							
								                b = r.b / 255;
							 | 
						||
| 
								 | 
							
								                a = r.a;
							 | 
						||
| 
								 | 
							
								                r = r.r / 255;
							 | 
						||
| 
								 | 
							
								            } else {
							 | 
						||
| 
								 | 
							
								                r /= 255;
							 | 
						||
| 
								 | 
							
								                g /= 255;
							 | 
						||
| 
								 | 
							
								                b /= 255;
							 | 
						||
| 
								 | 
							
								            }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								            var
							 | 
						||
| 
								 | 
							
								                max = Math.max(r, g, b),
							 | 
						||
| 
								 | 
							
								                min = Math.min(r, g, b),
							 | 
						||
| 
								 | 
							
								                h, s, l = (max + min) / 2,
							 | 
						||
| 
								 | 
							
								                d = max - min;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								            if (!d) {
							 | 
						||
| 
								 | 
							
								                h = s = 0; // achromatic
							 | 
						||
| 
								 | 
							
								            } else {
							 | 
						||
| 
								 | 
							
								                s = l > 0.5 ? d / (2 - max - min) : d / (max + min);
							 | 
						||
| 
								 | 
							
								                switch (max) {
							 | 
						||
| 
								 | 
							
								                    case r:
							 | 
						||
| 
								 | 
							
								                        h = (g - b) / d + (g < b ? 6 : 0);
							 | 
						||
| 
								 | 
							
								                        break;
							 | 
						||
| 
								 | 
							
								                    case g:
							 | 
						||
| 
								 | 
							
								                        h = (b - r) / d + 2;
							 | 
						||
| 
								 | 
							
								                        break;
							 | 
						||
| 
								 | 
							
								                    case b:
							 | 
						||
| 
								 | 
							
								                        h = (r - g) / d + 4;
							 | 
						||
| 
								 | 
							
								                        break;
							 | 
						||
| 
								 | 
							
								                }
							 | 
						||
| 
								 | 
							
								                h *= 60;
							 | 
						||
| 
								 | 
							
								            }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								            return new Color(h, s, l, a);
							 | 
						||
| 
								 | 
							
								        };
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        Color.prototype = {
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								            toRGBA: function () {
							 | 
						||
| 
								 | 
							
								                var
							 | 
						||
| 
								 | 
							
								                    h = clamp(this.H, 360),
							 | 
						||
| 
								 | 
							
								                    s = clamp(this.S, 1),
							 | 
						||
| 
								 | 
							
								                    l = clamp(this.L, 1),
							 | 
						||
| 
								 | 
							
								                    rgba = {a: clamp(this.A, 1)};
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								                // achromatic
							 | 
						||
| 
								 | 
							
								                if (s === 0) {
							 | 
						||
| 
								 | 
							
								                    rgba.r = l;
							 | 
						||
| 
								 | 
							
								                    rgba.g = l;
							 | 
						||
| 
								 | 
							
								                    rgba.b = l;
							 | 
						||
| 
								 | 
							
								                } else {
							 | 
						||
| 
								 | 
							
								                    var
							 | 
						||
| 
								 | 
							
								                        q = l < 0.5 ? l * (1 + s) : l + s - l * s,
							 | 
						||
| 
								 | 
							
								                        p = 2 * l - q;
							 | 
						||
| 
								 | 
							
								                    h /= 360;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								                    rgba.r = hue2rgb(p, q, h + 1 / 3);
							 | 
						||
| 
								 | 
							
								                    rgba.g = hue2rgb(p, q, h);
							 | 
						||
| 
								 | 
							
								                    rgba.b = hue2rgb(p, q, h - 1 / 3);
							 | 
						||
| 
								 | 
							
								                }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								                return {
							 | 
						||
| 
								 | 
							
								                    r: Math.round(rgba.r * 255),
							 | 
						||
| 
								 | 
							
								                    g: Math.round(rgba.g * 255),
							 | 
						||
| 
								 | 
							
								                    b: Math.round(rgba.b * 255),
							 | 
						||
| 
								 | 
							
								                    a: rgba.a
							 | 
						||
| 
								 | 
							
								                };
							 | 
						||
| 
								 | 
							
								            },
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								            toString: function () {
							 | 
						||
| 
								 | 
							
								                var rgba = this.toRGBA();
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								                if (rgba.a === 1) {
							 | 
						||
| 
								 | 
							
								                    return '#' + ((1 << 24) + (rgba.r << 16) + (rgba.g << 8) + rgba.b).toString(16).slice(1, 7);
							 | 
						||
| 
								 | 
							
								                }
							 | 
						||
| 
								 | 
							
								                return 'rgba(' + [rgba.r, rgba.g, rgba.b, rgba.a.toFixed(2)].join(',') + ')';
							 | 
						||
| 
								 | 
							
								            },
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								            hue: function (h) {
							 | 
						||
| 
								 | 
							
								                return new Color(this.H * h, this.S, this.L, this.A);
							 | 
						||
| 
								 | 
							
								            },
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								            saturation: function (s) {
							 | 
						||
| 
								 | 
							
								                return new Color(this.H, this.S * s, this.L, this.A);
							 | 
						||
| 
								 | 
							
								            },
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								            lightness: function (l) {
							 | 
						||
| 
								 | 
							
								                return new Color(this.H, this.S, this.L * l, this.A);
							 | 
						||
| 
								 | 
							
								            },
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								            alpha: function (a) {
							 | 
						||
| 
								 | 
							
								                return new Color(this.H, this.S, this.L, this.A * a);
							 | 
						||
| 
								 | 
							
								            }
							 | 
						||
| 
								 | 
							
								        };
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        return Color;
							 | 
						||
| 
								 | 
							
								    }(this));
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								//****** file: SunPosition.js ******
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								// calculations are based on http://aa.quae.nl/en/reken/zonpositie.html
							 | 
						||
| 
								 | 
							
								// code credits to Vladimir Agafonkin (@mourner)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    var getSunPosition = (function () {
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        var m = Math,
							 | 
						||
| 
								 | 
							
								            PI = m.PI,
							 | 
						||
| 
								 | 
							
								            sin = m.sin,
							 | 
						||
| 
								 | 
							
								            cos = m.cos,
							 | 
						||
| 
								 | 
							
								            tan = m.tan,
							 | 
						||
| 
								 | 
							
								            asin = m.asin,
							 | 
						||
| 
								 | 
							
								            atan = m.atan2;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        var rad = PI / 180,
							 | 
						||
| 
								 | 
							
								            dayMs = 1000 * 60 * 60 * 24,
							 | 
						||
| 
								 | 
							
								            J1970 = 2440588,
							 | 
						||
| 
								 | 
							
								            J2000 = 2451545,
							 | 
						||
| 
								 | 
							
								            e = rad * 23.4397; // obliquity of the Earth
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        function toJulian(date) {
							 | 
						||
| 
								 | 
							
								            return date.valueOf() / dayMs - 0.5 + J1970;
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        function toDays(date) {
							 | 
						||
| 
								 | 
							
								            return toJulian(date) - J2000;
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        function getRightAscension(l, b) {
							 | 
						||
| 
								 | 
							
								            return atan(sin(l) * cos(e) - tan(b) * sin(e), cos(l));
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        function getDeclination(l, b) {
							 | 
						||
| 
								 | 
							
								            return asin(sin(b) * cos(e) + cos(b) * sin(e) * sin(l));
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        function getAzimuth(H, phi, dec) {
							 | 
						||
| 
								 | 
							
								            return atan(sin(H), cos(H) * sin(phi) - tan(dec) * cos(phi));
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        function getAltitude(H, phi, dec) {
							 | 
						||
| 
								 | 
							
								            return asin(sin(phi) * sin(dec) + cos(phi) * cos(dec) * cos(H));
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        function getSiderealTime(d, lw) {
							 | 
						||
| 
								 | 
							
								            return rad * (280.16 + 360.9856235 * d) - lw;
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        function getSolarMeanAnomaly(d) {
							 | 
						||
| 
								 | 
							
								            return rad * (357.5291 + 0.98560028 * d);
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        function getEquationOfCenter(M) {
							 | 
						||
| 
								 | 
							
								            return rad * (1.9148 * sin(M) + 0.0200 * sin(2 * M) + 0.0003 * sin(3 * M));
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        function getEclipticLongitude(M, C) {
							 | 
						||
| 
								 | 
							
								            var P = rad * 102.9372; // perihelion of the Earth
							 | 
						||
| 
								 | 
							
								            return M + C + P + PI;
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        return function getSunPosition(date, lat, lon) {
							 | 
						||
| 
								 | 
							
								            var lw = rad * -lon,
							 | 
						||
| 
								 | 
							
								                phi = rad * lat,
							 | 
						||
| 
								 | 
							
								                d = toDays(date),
							 | 
						||
| 
								 | 
							
								                M = getSolarMeanAnomaly(d),
							 | 
						||
| 
								 | 
							
								                C = getEquationOfCenter(M),
							 | 
						||
| 
								 | 
							
								                L = getEclipticLongitude(M, C),
							 | 
						||
| 
								 | 
							
								                D = getDeclination(L, 0),
							 | 
						||
| 
								 | 
							
								                A = getRightAscension(L, 0),
							 | 
						||
| 
								 | 
							
								                t = getSiderealTime(d, lw),
							 | 
						||
| 
								 | 
							
								                H = t - A;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								            return {
							 | 
						||
| 
								 | 
							
								                altitude: getAltitude(H, phi, D),
							 | 
						||
| 
								 | 
							
								                azimuth: getAzimuth(H, phi, D) - PI / 2 // origin: north
							 | 
						||
| 
								 | 
							
								            };
							 | 
						||
| 
								 | 
							
								        };
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    }());
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								//****** file: GeoJSON.js ******
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    var GeoJSON = (function () {
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        var METERS_PER_LEVEL = 3;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        var materialColors = {
							 | 
						||
| 
								 | 
							
								            brick: '#cc7755',
							 | 
						||
| 
								 | 
							
								            bronze: '#ffeecc',
							 | 
						||
| 
								 | 
							
								            canvas: '#fff8f0',
							 | 
						||
| 
								 | 
							
								            concrete: '#999999',
							 | 
						||
| 
								 | 
							
								            copper: '#a0e0d0',
							 | 
						||
| 
								 | 
							
								            glass: '#e8f8f8',
							 | 
						||
| 
								 | 
							
								            gold: '#ffcc00',
							 | 
						||
| 
								 | 
							
								            plants: '#009933',
							 | 
						||
| 
								 | 
							
								            metal: '#aaaaaa',
							 | 
						||
| 
								 | 
							
								            panel: '#fff8f0',
							 | 
						||
| 
								 | 
							
								            plaster: '#999999',
							 | 
						||
| 
								 | 
							
								            roof_tiles: '#f08060',
							 | 
						||
| 
								 | 
							
								            silver: '#cccccc',
							 | 
						||
| 
								 | 
							
								            slate: '#666666',
							 | 
						||
| 
								 | 
							
								            stone: '#996666',
							 | 
						||
| 
								 | 
							
								            tar_paper: '#333333',
							 | 
						||
| 
								 | 
							
								            wood: '#deb887'
							 | 
						||
| 
								 | 
							
								        };
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        var baseMaterials = {
							 | 
						||
| 
								 | 
							
								            asphalt: 'tar_paper',
							 | 
						||
| 
								 | 
							
								            bitumen: 'tar_paper',
							 | 
						||
| 
								 | 
							
								            block: 'stone',
							 | 
						||
| 
								 | 
							
								            bricks: 'brick',
							 | 
						||
| 
								 | 
							
								            glas: 'glass',
							 | 
						||
| 
								 | 
							
								            glassfront: 'glass',
							 | 
						||
| 
								 | 
							
								            grass: 'plants',
							 | 
						||
| 
								 | 
							
								            masonry: 'stone',
							 | 
						||
| 
								 | 
							
								            granite: 'stone',
							 | 
						||
| 
								 | 
							
								            panels: 'panel',
							 | 
						||
| 
								 | 
							
								            paving_stones: 'stone',
							 | 
						||
| 
								 | 
							
								            plastered: 'plaster',
							 | 
						||
| 
								 | 
							
								            rooftiles: 'roof_tiles',
							 | 
						||
| 
								 | 
							
								            roofingfelt: 'tar_paper',
							 | 
						||
| 
								 | 
							
								            sandstone: 'stone',
							 | 
						||
| 
								 | 
							
								            sheet: 'canvas',
							 | 
						||
| 
								 | 
							
								            sheets: 'canvas',
							 | 
						||
| 
								 | 
							
								            shingle: 'tar_paper',
							 | 
						||
| 
								 | 
							
								            shingles: 'tar_paper',
							 | 
						||
| 
								 | 
							
								            slates: 'slate',
							 | 
						||
| 
								 | 
							
								            steel: 'metal',
							 | 
						||
| 
								 | 
							
								            tar: 'tar_paper',
							 | 
						||
| 
								 | 
							
								            tent: 'canvas',
							 | 
						||
| 
								 | 
							
								            thatch: 'plants',
							 | 
						||
| 
								 | 
							
								            tile: 'roof_tiles',
							 | 
						||
| 
								 | 
							
								            tiles: 'roof_tiles'
							 | 
						||
| 
								 | 
							
								        };
							 | 
						||
| 
								 | 
							
								        // cardboard
							 | 
						||
| 
								 | 
							
								        // eternit
							 | 
						||
| 
								 | 
							
								        // limestone
							 | 
						||
| 
								 | 
							
								        // straw
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        function getMaterialColor(str) {
							 | 
						||
| 
								 | 
							
								            str = str.toLowerCase();
							 | 
						||
| 
								 | 
							
								            if (str[0] === '#') {
							 | 
						||
| 
								 | 
							
								                return str;
							 | 
						||
| 
								 | 
							
								            }
							 | 
						||
| 
								 | 
							
								            return materialColors[baseMaterials[str] || str] || null;
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        var WINDING_CLOCKWISE = 'CW';
							 | 
						||
| 
								 | 
							
								        var WINDING_COUNTER_CLOCKWISE = 'CCW';
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        // detect winding direction: clockwise or counter clockwise
							 | 
						||
| 
								 | 
							
								        function getWinding(points) {
							 | 
						||
| 
								 | 
							
								            var x1, y1, x2, y2,
							 | 
						||
| 
								 | 
							
								                a = 0,
							 | 
						||
| 
								 | 
							
								                i, il;
							 | 
						||
| 
								 | 
							
								            for (i = 0, il = points.length - 3; i < il; i += 2) {
							 | 
						||
| 
								 | 
							
								                x1 = points[i];
							 | 
						||
| 
								 | 
							
								                y1 = points[i + 1];
							 | 
						||
| 
								 | 
							
								                x2 = points[i + 2];
							 | 
						||
| 
								 | 
							
								                y2 = points[i + 3];
							 | 
						||
| 
								 | 
							
								                a += x1 * y2 - x2 * y1;
							 | 
						||
| 
								 | 
							
								            }
							 | 
						||
| 
								 | 
							
								            return (a / 2) > 0 ? WINDING_CLOCKWISE : WINDING_COUNTER_CLOCKWISE;
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        // enforce a polygon winding direcetion. Needed for proper backface culling.
							 | 
						||
| 
								 | 
							
								        function makeWinding(points, direction) {
							 | 
						||
| 
								 | 
							
								            var winding = getWinding(points);
							 | 
						||
| 
								 | 
							
								            if (winding === direction) {
							 | 
						||
| 
								 | 
							
								                return points;
							 | 
						||
| 
								 | 
							
								            }
							 | 
						||
| 
								 | 
							
								            var revPoints = [];
							 | 
						||
| 
								 | 
							
								            for (var i = points.length - 2; i >= 0; i -= 2) {
							 | 
						||
| 
								 | 
							
								                revPoints.push(points[i], points[i + 1]);
							 | 
						||
| 
								 | 
							
								            }
							 | 
						||
| 
								 | 
							
								            return revPoints;
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        function alignProperties(prop) {
							 | 
						||
| 
								 | 
							
								            var item = {};
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								            prop = prop || {};
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								            item.height = prop.height || (prop.levels ? prop.levels * METERS_PER_LEVEL : DEFAULT_HEIGHT);
							 | 
						||
| 
								 | 
							
								            item.minHeight = prop.minHeight || (prop.minLevel ? prop.minLevel * METERS_PER_LEVEL : 0);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								            var wallColor = prop.material ? getMaterialColor(prop.material) : (prop.wallColor || prop.color);
							 | 
						||
| 
								 | 
							
								            if (wallColor) {
							 | 
						||
| 
								 | 
							
								                item.wallColor = wallColor;
							 | 
						||
| 
								 | 
							
								            }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								            var roofColor = prop.roofMaterial ? getMaterialColor(prop.roofMaterial) : prop.roofColor;
							 | 
						||
| 
								 | 
							
								            if (roofColor) {
							 | 
						||
| 
								 | 
							
								                item.roofColor = roofColor;
							 | 
						||
| 
								 | 
							
								            }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								            switch (prop.shape) {
							 | 
						||
| 
								 | 
							
								                case 'cylinder':
							 | 
						||
| 
								 | 
							
								                case 'cone':
							 | 
						||
| 
								 | 
							
								                case 'dome':
							 | 
						||
| 
								 | 
							
								                case 'sphere':
							 | 
						||
| 
								 | 
							
								                    item.shape = prop.shape;
							 | 
						||
| 
								 | 
							
								                    item.isRotational = true;
							 | 
						||
| 
								 | 
							
								                    break;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								                case 'pyramid':
							 | 
						||
| 
								 | 
							
								                    item.shape = prop.shape;
							 | 
						||
| 
								 | 
							
								                    break;
							 | 
						||
| 
								 | 
							
								            }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								            switch (prop.roofShape) {
							 | 
						||
| 
								 | 
							
								                case 'cone':
							 | 
						||
| 
								 | 
							
								                case 'dome':
							 | 
						||
| 
								 | 
							
								                    item.roofShape = prop.roofShape;
							 | 
						||
| 
								 | 
							
								                    item.isRotational = true;
							 | 
						||
| 
								 | 
							
								                    break;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								                case 'pyramid':
							 | 
						||
| 
								 | 
							
								                    item.roofShape = prop.roofShape;
							 | 
						||
| 
								 | 
							
								                    break;
							 | 
						||
| 
								 | 
							
								            }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								            if (item.roofShape && prop.roofHeight) {
							 | 
						||
| 
								 | 
							
								                item.roofHeight = prop.roofHeight;
							 | 
						||
| 
								 | 
							
								                item.height = max(0, item.height - item.roofHeight);
							 | 
						||
| 
								 | 
							
								            } else {
							 | 
						||
| 
								 | 
							
								                item.roofHeight = 0;
							 | 
						||
| 
								 | 
							
								            }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								            return item;
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        function getGeometries(geometry) {
							 | 
						||
| 
								 | 
							
								            var
							 | 
						||
| 
								 | 
							
								                i, il, polygon,
							 | 
						||
| 
								 | 
							
								                geometries = [], sub;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								            switch (geometry.type) {
							 | 
						||
| 
								 | 
							
								                case 'GeometryCollection':
							 | 
						||
| 
								 | 
							
								                    geometries = [];
							 | 
						||
| 
								 | 
							
								                    for (i = 0, il = geometry.geometries.length; i < il; i++) {
							 | 
						||
| 
								 | 
							
								                        if ((sub = getGeometries(geometry.geometries[i]))) {
							 | 
						||
| 
								 | 
							
								                            geometries.push.apply(geometries, sub);
							 | 
						||
| 
								 | 
							
								                        }
							 | 
						||
| 
								 | 
							
								                    }
							 | 
						||
| 
								 | 
							
								                    return geometries;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								                case 'MultiPolygon':
							 | 
						||
| 
								 | 
							
								                    geometries = [];
							 | 
						||
| 
								 | 
							
								                    for (i = 0, il = geometry.coordinates.length; i < il; i++) {
							 | 
						||
| 
								 | 
							
								                        if ((sub = getGeometries({type: 'Polygon', coordinates: geometry.coordinates[i]}))) {
							 | 
						||
| 
								 | 
							
								                            geometries.push.apply(geometries, sub);
							 | 
						||
| 
								 | 
							
								                        }
							 | 
						||
| 
								 | 
							
								                    }
							 | 
						||
| 
								 | 
							
								                    return geometries;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								                case 'Polygon':
							 | 
						||
| 
								 | 
							
								                    polygon = geometry.coordinates;
							 | 
						||
| 
								 | 
							
								                    break;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								                default:
							 | 
						||
| 
								 | 
							
								                    return [];
							 | 
						||
| 
								 | 
							
								            }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								            var
							 | 
						||
| 
								 | 
							
								                j, jl,
							 | 
						||
| 
								 | 
							
								                p, lat = 1, lon = 0,
							 | 
						||
| 
								 | 
							
								                outer = [], inner = [];
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								            p = polygon[0];
							 | 
						||
| 
								 | 
							
								            for (i = 0, il = p.length; i < il; i++) {
							 | 
						||
| 
								 | 
							
								                outer.push(p[i][lat], p[i][lon]);
							 | 
						||
| 
								 | 
							
								            }
							 | 
						||
| 
								 | 
							
								            outer = makeWinding(outer, WINDING_CLOCKWISE);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								            for (i = 0, il = polygon.length - 1; i < il; i++) {
							 | 
						||
| 
								 | 
							
								                p = polygon[i + 1];
							 | 
						||
| 
								 | 
							
								                inner[i] = [];
							 | 
						||
| 
								 | 
							
								                for (j = 0, jl = p.length; j < jl; j++) {
							 | 
						||
| 
								 | 
							
								                    inner[i].push(p[j][lat], p[j][lon]);
							 | 
						||
| 
								 | 
							
								                }
							 | 
						||
| 
								 | 
							
								                inner[i] = makeWinding(inner[i], WINDING_COUNTER_CLOCKWISE);
							 | 
						||
| 
								 | 
							
								            }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								            return [{
							 | 
						||
| 
								 | 
							
								                outer: outer,
							 | 
						||
| 
								 | 
							
								                inner: inner.length ? inner : null
							 | 
						||
| 
								 | 
							
								            }];
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        function clone(obj) {
							 | 
						||
| 
								 | 
							
								            var res = {};
							 | 
						||
| 
								 | 
							
								            for (var p in obj) {
							 | 
						||
| 
								 | 
							
								                if (obj.hasOwnProperty(p)) {
							 | 
						||
| 
								 | 
							
								                    res[p] = obj[p];
							 | 
						||
| 
								 | 
							
								                }
							 | 
						||
| 
								 | 
							
								            }
							 | 
						||
| 
								 | 
							
								            return res;
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        return {
							 | 
						||
| 
								 | 
							
								            read: function (geojson) {
							 | 
						||
| 
								 | 
							
								                if (!geojson || geojson.type !== 'FeatureCollection') {
							 | 
						||
| 
								 | 
							
								                    return [];
							 | 
						||
| 
								 | 
							
								                }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								                var
							 | 
						||
| 
								 | 
							
								                    collection = geojson.features,
							 | 
						||
| 
								 | 
							
								                    i, il, j, jl,
							 | 
						||
| 
								 | 
							
								                    res = [],
							 | 
						||
| 
								 | 
							
								                    feature,
							 | 
						||
| 
								 | 
							
								                    geometries,
							 | 
						||
| 
								 | 
							
								                    baseItem, item;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								                for (i = 0, il = collection.length; i < il; i++) {
							 | 
						||
| 
								 | 
							
								                    feature = collection[i];
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								                    if (feature.type !== 'Feature' || onEach(feature) === false) {
							 | 
						||
| 
								 | 
							
								                        continue;
							 | 
						||
| 
								 | 
							
								                    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								                    baseItem = alignProperties(feature.properties);
							 | 
						||
| 
								 | 
							
								                    geometries = getGeometries(feature.geometry);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								                    for (j = 0, jl = geometries.length; j < jl; j++) {
							 | 
						||
| 
								 | 
							
								                        item = clone(baseItem);
							 | 
						||
| 
								 | 
							
								                        item.footprint = geometries[j].outer;
							 | 
						||
| 
								 | 
							
								                        if (item.isRotational) {
							 | 
						||
| 
								 | 
							
								                            item.radius = getLonDelta(item.footprint);
							 | 
						||
| 
								 | 
							
								                        }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								                        if (geometries[j].inner) {
							 | 
						||
| 
								 | 
							
								                            item.holes = geometries[j].inner;
							 | 
						||
| 
								 | 
							
								                        }
							 | 
						||
| 
								 | 
							
								                        if (feature.id || feature.properties.id) {
							 | 
						||
| 
								 | 
							
								                            item.id = feature.id || feature.properties.id;
							 | 
						||
| 
								 | 
							
								                        }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								                        if (feature.properties.relationId) {
							 | 
						||
| 
								 | 
							
								                            item.relationId = feature.properties.relationId;
							 | 
						||
| 
								 | 
							
								                        }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								                        res.push(item); // TODO: clone base properties!
							 | 
						||
| 
								 | 
							
								                    }
							 | 
						||
| 
								 | 
							
								                }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								                return res;
							 | 
						||
| 
								 | 
							
								            }
							 | 
						||
| 
								 | 
							
								        };
							 | 
						||
| 
								 | 
							
								    }());
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								//****** file: variables.js ******
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    var
							 | 
						||
| 
								 | 
							
								        VERSION = '0.2.2b',
							 | 
						||
| 
								 | 
							
								        ATTRIBUTION = '© <a href="http://osmbuildings.org">OSM Buildings</a>',
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        DATA_SRC = 'http://{s}.data.osmbuildings.org/0.2/{k}/tile/{z}/{x}/{y}.json',  ////////////////geojson 数据
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        PI = Math.PI,
							 | 
						||
| 
								 | 
							
								        HALF_PI = PI / 2,
							 | 
						||
| 
								 | 
							
								        QUARTER_PI = PI / 4,
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        MAP_TILE_SIZE = 256,    // map tile size in pixels
							 | 
						||
| 
								 | 
							
								        DATA_TILE_SIZE = 0.0075, // data tile size in geo coordinates, smaller: less data to load but more requests
							 | 
						||
| 
								 | 
							
								        ZOOM, MAP_SIZE,
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        MIN_ZOOM = 8,
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        LAT = 'latitude', LON = 'longitude',
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        TRUE = true, FALSE = false,
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        WIDTH = 0, HEIGHT = 0,
							 | 
						||
| 
								 | 
							
								        CENTER_X = 0, CENTER_Y = 0,
							 | 
						||
| 
								 | 
							
								        ORIGIN_X = 0, ORIGIN_Y = 0,
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        WALL_COLOR = Color.parse('rgba(200, 190, 180)'),
							 | 
						||
| 
								 | 
							
								        ALT_COLOR = WALL_COLOR.lightness(0.8),
							 | 
						||
| 
								 | 
							
								        ROOF_COLOR = WALL_COLOR.lightness(1.2),
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        WALL_COLOR_STR = '' + WALL_COLOR,
							 | 
						||
| 
								 | 
							
								        ALT_COLOR_STR = '' + ALT_COLOR,
							 | 
						||
| 
								 | 
							
								        ROOF_COLOR_STR = '' + ROOF_COLOR,
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        PIXEL_PER_DEG = 0,
							 | 
						||
| 
								 | 
							
								        ZOOM_FACTOR = 1,
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        MAX_HEIGHT, // taller buildings will be cut to this
							 | 
						||
| 
								 | 
							
								        DEFAULT_HEIGHT = 5,
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        CAM_X, CAM_Y, CAM_Z = 450,
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        isZooming;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								//****** file: geometry.js ******
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    function getDistance(p1, p2) {
							 | 
						||
| 
								 | 
							
								        var
							 | 
						||
| 
								 | 
							
								            dx = p1.x - p2.x,
							 | 
						||
| 
								 | 
							
								            dy = p1.y - p2.y;
							 | 
						||
| 
								 | 
							
								        return dx * dx + dy * dy;
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    //是否可旋转
							 | 
						||
| 
								 | 
							
								    function isRotational(polygon) {
							 | 
						||
| 
								 | 
							
								        var length = polygon.length;
							 | 
						||
| 
								 | 
							
								        if (length < 16) {
							 | 
						||
| 
								 | 
							
								            return false;
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        var i;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        var minX = Infinity, maxX = -Infinity, minY = Infinity, maxY = -Infinity;
							 | 
						||
| 
								 | 
							
								        for (i = 0; i < length - 1; i += 2) {
							 | 
						||
| 
								 | 
							
								            minX = Math.min(minX, polygon[i]);
							 | 
						||
| 
								 | 
							
								            maxX = Math.max(maxX, polygon[i]);
							 | 
						||
| 
								 | 
							
								            minY = Math.min(minY, polygon[i + 1]);
							 | 
						||
| 
								 | 
							
								            maxY = Math.max(maxY, polygon[i + 1]);
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        var
							 | 
						||
| 
								 | 
							
								            width = maxX - minX,
							 | 
						||
| 
								 | 
							
								            height = (maxY - minY),
							 | 
						||
| 
								 | 
							
								            ratio = width / height;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        if (ratio < 0.85 || ratio > 1.15) {
							 | 
						||
| 
								 | 
							
								            return false;
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        var
							 | 
						||
| 
								 | 
							
								            center = {x: minX + width / 2, y: minY + height / 2},
							 | 
						||
| 
								 | 
							
								            radius = (width + height) / 4,
							 | 
						||
| 
								 | 
							
								            sqRadius = radius * radius;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        for (i = 0; i < length - 1; i += 2) {
							 | 
						||
| 
								 | 
							
								            var dist = getDistance({x: polygon[i], y: polygon[i + 1]}, center);
							 | 
						||
| 
								 | 
							
								            if (dist / sqRadius < 0.8 || dist / sqRadius > 1.2) {
							 | 
						||
| 
								 | 
							
								                return false;
							 | 
						||
| 
								 | 
							
								            }
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        return true;
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    function getSquareSegmentDistance(px, py, p1x, p1y, p2x, p2y) {
							 | 
						||
| 
								 | 
							
								        var
							 | 
						||
| 
								 | 
							
								            dx = p2x - p1x,
							 | 
						||
| 
								 | 
							
								            dy = p2y - p1y,
							 | 
						||
| 
								 | 
							
								            t;
							 | 
						||
| 
								 | 
							
								        if (dx !== 0 || dy !== 0) {
							 | 
						||
| 
								 | 
							
								            t = ((px - p1x) * dx + (py - p1y) * dy) / (dx * dx + dy * dy);
							 | 
						||
| 
								 | 
							
								            if (t > 1) {
							 | 
						||
| 
								 | 
							
								                p1x = p2x;
							 | 
						||
| 
								 | 
							
								                p1y = p2y;
							 | 
						||
| 
								 | 
							
								            } else if (t > 0) {
							 | 
						||
| 
								 | 
							
								                p1x += dx * t;
							 | 
						||
| 
								 | 
							
								                p1y += dy * t;
							 | 
						||
| 
								 | 
							
								            }
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								        dx = px - p1x;
							 | 
						||
| 
								 | 
							
								        dy = py - p1y;
							 | 
						||
| 
								 | 
							
								        return dx * dx + dy * dy;
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    function simplifyPolygon(buffer) {
							 | 
						||
| 
								 | 
							
								        var
							 | 
						||
| 
								 | 
							
								            sqTolerance = 2,
							 | 
						||
| 
								 | 
							
								            len = buffer.length / 2,
							 | 
						||
| 
								 | 
							
								            markers = new Uint8Array(len),
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								            first = 0, last = len - 1,
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								            i,
							 | 
						||
| 
								 | 
							
								            maxSqDist,
							 | 
						||
| 
								 | 
							
								            sqDist,
							 | 
						||
| 
								 | 
							
								            index,
							 | 
						||
| 
								 | 
							
								            firstStack = [], lastStack = [],
							 | 
						||
| 
								 | 
							
								            newBuffer = [];
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        markers[first] = markers[last] = 1;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        while (last) {
							 | 
						||
| 
								 | 
							
								            maxSqDist = 0;
							 | 
						||
| 
								 | 
							
								            for (i = first + 1; i < last; i++) {
							 | 
						||
| 
								 | 
							
								                sqDist = getSquareSegmentDistance(
							 | 
						||
| 
								 | 
							
								                    buffer[i * 2], buffer[i * 2 + 1],
							 | 
						||
| 
								 | 
							
								                    buffer[first * 2], buffer[first * 2 + 1],
							 | 
						||
| 
								 | 
							
								                    buffer[last * 2], buffer[last * 2 + 1]
							 | 
						||
| 
								 | 
							
								                );
							 | 
						||
| 
								 | 
							
								                if (sqDist > maxSqDist) {
							 | 
						||
| 
								 | 
							
								                    index = i;
							 | 
						||
| 
								 | 
							
								                    maxSqDist = sqDist;
							 | 
						||
| 
								 | 
							
								                }
							 | 
						||
| 
								 | 
							
								            }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								            if (maxSqDist > sqTolerance) {
							 | 
						||
| 
								 | 
							
								                markers[index] = 1;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								                firstStack.push(first);
							 | 
						||
| 
								 | 
							
								                lastStack.push(index);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								                firstStack.push(index);
							 | 
						||
| 
								 | 
							
								                lastStack.push(last);
							 | 
						||
| 
								 | 
							
								            }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								            first = firstStack.pop();
							 | 
						||
| 
								 | 
							
								            last = lastStack.pop();
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        for (i = 0; i < len; i++) {
							 | 
						||
| 
								 | 
							
								            if (markers[i]) {
							 | 
						||
| 
								 | 
							
								                newBuffer.push(buffer[i * 2], buffer[i * 2 + 1]);
							 | 
						||
| 
								 | 
							
								            }
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        return newBuffer;
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    function getCenter(footprint) {
							 | 
						||
| 
								 | 
							
								        var minX = Infinity, maxX = -Infinity, minY = Infinity, maxY = -Infinity;
							 | 
						||
| 
								 | 
							
								        for (var i = 0, il = footprint.length - 3; i < il; i += 2) {
							 | 
						||
| 
								 | 
							
								            minX = min(minX, footprint[i]);
							 | 
						||
| 
								 | 
							
								            maxX = max(maxX, footprint[i]);
							 | 
						||
| 
								 | 
							
								            minY = min(minY, footprint[i + 1]);
							 | 
						||
| 
								 | 
							
								            maxY = max(maxY, footprint[i + 1]);
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								        return {x: minX + (maxX - minX) / 2 << 0, y: minY + (maxY - minY) / 2 << 0};
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    var EARTH_RADIUS = 6378137;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    function getLonDelta(footprint) {
							 | 
						||
| 
								 | 
							
								        var minLon = 180, maxLon = -180;
							 | 
						||
| 
								 | 
							
								        for (var i = 0, il = footprint.length; i < il; i += 2) {
							 | 
						||
| 
								 | 
							
								            minLon = min(minLon, footprint[i + 1]);
							 | 
						||
| 
								 | 
							
								            maxLon = max(maxLon, footprint[i + 1]);
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								        return (maxLon - minLon) / 2;
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								//****** file: functions.js ******
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    function rad(deg) {
							 | 
						||
| 
								 | 
							
								        return deg * PI / 180;
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    function deg(rad) {
							 | 
						||
| 
								 | 
							
								        return rad / PI * 180;
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    function pixelToGeo(x, y) {
							 | 
						||
| 
								 | 
							
								        var res = {};
							 | 
						||
| 
								 | 
							
								        x /= MAP_SIZE;
							 | 
						||
| 
								 | 
							
								        y /= MAP_SIZE;
							 | 
						||
| 
								 | 
							
								        res[LAT] = y <= 0 ? 90 : y >= 1 ? -90 : deg(2 * atan(exp(PI * (1 - 2 * y))) - HALF_PI);
							 | 
						||
| 
								 | 
							
								        res[LON] = (x === 1 ? 1 : (x % 1 + 1) % 1) * 360 - 180;
							 | 
						||
| 
								 | 
							
								        return res;
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    function geoToPixel(lat, lon) {
							 | 
						||
| 
								 | 
							
								        var latLngProjection = isLatLngProjection();
							 | 
						||
| 
								 | 
							
								        if (latLngProjection) {
							 | 
						||
| 
								 | 
							
								            var latitude = ((90 - lat) / 180),
							 | 
						||
| 
								 | 
							
								                longitude = lon / 360 + 0.5;
							 | 
						||
| 
								 | 
							
								            return {
							 | 
						||
| 
								 | 
							
								                x: (longitude * MAP_SIZE) << 0,
							 | 
						||
| 
								 | 
							
								                y: (latitude * MAP_SIZE / 2) << 0
							 | 
						||
| 
								 | 
							
								            };
							 | 
						||
| 
								 | 
							
								        } else {
							 | 
						||
| 
								 | 
							
								            var latitude = min(1, max(0, 0.5 - (log(tan(QUARTER_PI + HALF_PI * lat / 180)) / PI) / 2)),
							 | 
						||
| 
								 | 
							
								                longitude = lon / 360 + 0.5;
							 | 
						||
| 
								 | 
							
								            return {
							 | 
						||
| 
								 | 
							
								                x: (longitude * MAP_SIZE) << 0,
							 | 
						||
| 
								 | 
							
								                y: (latitude * MAP_SIZE) << 0
							 | 
						||
| 
								 | 
							
								            };
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    function isLatLngProjection() {
							 | 
						||
| 
								 | 
							
								        var code = mapObj.getProjection();
							 | 
						||
| 
								 | 
							
								        if (code.indexOf('3857') > -1) {
							 | 
						||
| 
								 | 
							
								            return false;
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								        if (code.indexOf('4326') > -1||code.indexOf('4490') > -1) {
							 | 
						||
| 
								 | 
							
								            return true;
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        console.log('Only support EPSG:3857 and EPSG:4326(4490) projection!');
							 | 
						||
| 
								 | 
							
								        return false;
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    function fromRange(sVal, sMin, sMax, dMin, dMax) {
							 | 
						||
| 
								 | 
							
								        sVal = min(max(sVal, sMin), sMax);
							 | 
						||
| 
								 | 
							
								        var rel = (sVal - sMin) / (sMax - sMin),
							 | 
						||
| 
								 | 
							
								            range = dMax - dMin;
							 | 
						||
| 
								 | 
							
								        return min(max(dMin + rel * range, dMin), dMax);
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    function isVisible(polygon) {
							 | 
						||
| 
								 | 
							
								        var
							 | 
						||
| 
								 | 
							
								            maxX = WIDTH + ORIGIN_X,
							 | 
						||
| 
								 | 
							
								            maxY = HEIGHT + ORIGIN_Y;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        // TODO: checking footprint is sufficient for visibility - NOT VALID FOR SHADOWS!
							 | 
						||
| 
								 | 
							
								        for (var i = 0, il = polygon.length - 3; i < il; i += 2) {
							 | 
						||
| 
								 | 
							
								            if (polygon[i] > ORIGIN_X && polygon[i] < maxX && polygon[i + 1] > ORIGIN_Y && polygon[i + 1] < maxY) {
							 | 
						||
| 
								 | 
							
								                return true;
							 | 
						||
| 
								 | 
							
								            }
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								        return false;
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								//数据请求  原来是在线OSMBuilding OSM与上请求 Builldings Geojson 数据
							 | 
						||
| 
								 | 
							
								//****** file: Request.js ******
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    var Request = (function () {
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        //var cacheData = {};
							 | 
						||
| 
								 | 
							
								        //var cacheIndex = [];
							 | 
						||
| 
								 | 
							
								        //var cacheSize = 0;
							 | 
						||
| 
								 | 
							
								        //var maxCacheSize = 1024*1024 * 5; // 5MB
							 | 
						||
| 
								 | 
							
								        //
							 | 
						||
| 
								 | 
							
								        //function xhr(url, callback) {
							 | 
						||
| 
								 | 
							
								        //    if (cacheData[url]) {
							 | 
						||
| 
								 | 
							
								        //        if (callback) {
							 | 
						||
| 
								 | 
							
								        //            callback(cacheData[url]);
							 | 
						||
| 
								 | 
							
								        //        }
							 | 
						||
| 
								 | 
							
								        //        return;
							 | 
						||
| 
								 | 
							
								        //    }
							 | 
						||
| 
								 | 
							
								        //
							 | 
						||
| 
								 | 
							
								        //    var req = new XMLHttpRequest();
							 | 
						||
| 
								 | 
							
								        //
							 | 
						||
| 
								 | 
							
								        //    req.onreadystatechange = function() {
							 | 
						||
| 
								 | 
							
								        //        if (req.readyState !== 4) {
							 | 
						||
| 
								 | 
							
								        //            return;
							 | 
						||
| 
								 | 
							
								        //        }
							 | 
						||
| 
								 | 
							
								        //        if (!req.status || req.status < 200 || req.status > 299) {
							 | 
						||
| 
								 | 
							
								        //            return;
							 | 
						||
| 
								 | 
							
								        //        }
							 | 
						||
| 
								 | 
							
								        //        if (callback && req.responseText) {
							 | 
						||
| 
								 | 
							
								        //            var responseText = req.responseText;
							 | 
						||
| 
								 | 
							
								        //
							 | 
						||
| 
								 | 
							
								        //            cacheData[url] = responseText;
							 | 
						||
| 
								 | 
							
								        //            cacheIndex.push({ url: url, size: responseText.length });
							 | 
						||
| 
								 | 
							
								        //            cacheSize += responseText.length;
							 | 
						||
| 
								 | 
							
								        //
							 | 
						||
| 
								 | 
							
								        //            callback(responseText);
							 | 
						||
| 
								 | 
							
								        //
							 | 
						||
| 
								 | 
							
								        //            while (cacheSize > maxCacheSize) {
							 | 
						||
| 
								 | 
							
								        //                var item = cacheIndex.shift();
							 | 
						||
| 
								 | 
							
								        //                cacheSize -= item.size;
							 | 
						||
| 
								 | 
							
								        //                delete cacheData[item.url];
							 | 
						||
| 
								 | 
							
								        //            }
							 | 
						||
| 
								 | 
							
								        //        }
							 | 
						||
| 
								 | 
							
								        //    };
							 | 
						||
| 
								 | 
							
								        //
							 | 
						||
| 
								 | 
							
								        //    req.open('GET', url);
							 | 
						||
| 
								 | 
							
								        //    req.send(null);
							 | 
						||
| 
								 | 
							
								        //
							 | 
						||
| 
								 | 
							
								        //    return req;
							 | 
						||
| 
								 | 
							
								        //}
							 | 
						||
| 
								 | 
							
								        //
							 | 
						||
| 
								 | 
							
								        //return {
							 | 
						||
| 
								 | 
							
								        //    loadJSON: function(url, callback) {
							 | 
						||
| 
								 | 
							
								        //        return xhr(url, function(responseText) {
							 | 
						||
| 
								 | 
							
								        //            var json;
							 | 
						||
| 
								 | 
							
								        //            try {
							 | 
						||
| 
								 | 
							
								        //                json = JSON.parse(responseText);
							 | 
						||
| 
								 | 
							
								        //            } catch(ex) {}
							 | 
						||
| 
								 | 
							
								        //            callback(json);
							 | 
						||
| 
								 | 
							
								        //        });
							 | 
						||
| 
								 | 
							
								        //    }
							 | 
						||
| 
								 | 
							
								        //};
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    }());
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								//****** file: Data.js ******
							 | 
						||
| 
								 | 
							
								    var Data = {
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        loadedItems: {}, // maintain a list of cached items in order to avoid duplicates on tile borders
							 | 
						||
| 
								 | 
							
								        items: [],
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        getPixelFootprint: function (buffer) {
							 | 
						||
| 
								 | 
							
								            var footprint = new Int32Array(buffer.length),
							 | 
						||
| 
								 | 
							
								                px;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								            for (var i = 0, il = buffer.length - 1; i < il; i += 2) {
							 | 
						||
| 
								 | 
							
								                px = geoToPixel(buffer[i], buffer[i + 1]);
							 | 
						||
| 
								 | 
							
								                footprint[i] = px.x;
							 | 
						||
| 
								 | 
							
								                footprint[i + 1] = px.y;
							 | 
						||
| 
								 | 
							
								            }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								            footprint = simplifyPolygon(footprint);
							 | 
						||
| 
								 | 
							
								            if (footprint.length < 8) { // 3 points & end==start (*2)
							 | 
						||
| 
								 | 
							
								                return;
							 | 
						||
| 
								 | 
							
								            }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								            return footprint;
							 | 
						||
| 
								 | 
							
								        },
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        resetItems: function () {
							 | 
						||
| 
								 | 
							
								            this.items = [];
							 | 
						||
| 
								 | 
							
								            this.loadedItems = {};
							 | 
						||
| 
								 | 
							
								            HitAreas.reset();
							 | 
						||
| 
								 | 
							
								        },
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        addRenderItems: function (data, allAreNew) {
							 | 
						||
| 
								 | 
							
								            var item, scaledItem, id;
							 | 
						||
| 
								 | 
							
								            var geojson = GeoJSON.read(data);
							 | 
						||
| 
								 | 
							
								            for (var i = 0, il = geojson.length; i < il; i++) {
							 | 
						||
| 
								 | 
							
								                item = geojson[i];
							 | 
						||
| 
								 | 
							
								                id = item.id || [item.footprint[0], item.footprint[1], item.height, item.minHeight].join(',');
							 | 
						||
| 
								 | 
							
								                if (!this.loadedItems[id]) {
							 | 
						||
| 
								 | 
							
								                    if ((scaledItem = this.scale(item))) {
							 | 
						||
| 
								 | 
							
								                        scaledItem.scale = allAreNew ? 0 : 1;
							 | 
						||
| 
								 | 
							
								                        this.items.push(scaledItem);
							 | 
						||
| 
								 | 
							
								                        this.loadedItems[id] = 1;
							 | 
						||
| 
								 | 
							
								                    }
							 | 
						||
| 
								 | 
							
								                }
							 | 
						||
| 
								 | 
							
								            }
							 | 
						||
| 
								 | 
							
								            fadeIn();
							 | 
						||
| 
								 | 
							
								        },
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        scale: function (item) {
							 | 
						||
| 
								 | 
							
								            var
							 | 
						||
| 
								 | 
							
								                res = {},
							 | 
						||
| 
								 | 
							
								                // TODO: calculate this on zoom change only
							 | 
						||
| 
								 | 
							
								                zoomScale = 6 / pow(2, ZOOM - MIN_ZOOM); // TODO: consider using HEIGHT / (global.devicePixelRatio || 1)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								            if (item.id) {
							 | 
						||
| 
								 | 
							
								                res.id = item.id;
							 | 
						||
| 
								 | 
							
								            }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								            res.height = min(item.height / zoomScale, MAX_HEIGHT);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								            res.minHeight = isNaN(item.minHeight) ? 0 : item.minHeight / zoomScale;
							 | 
						||
| 
								 | 
							
								            if (res.minHeight > MAX_HEIGHT) {
							 | 
						||
| 
								 | 
							
								                return;
							 | 
						||
| 
								 | 
							
								            }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								            res.footprint = this.getPixelFootprint(item.footprint);
							 | 
						||
| 
								 | 
							
								            if (!res.footprint) {
							 | 
						||
| 
								 | 
							
								                return;
							 | 
						||
| 
								 | 
							
								            }
							 | 
						||
| 
								 | 
							
								            res.center = getCenter(res.footprint);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								            if (item.radius) {
							 | 
						||
| 
								 | 
							
								                res.radius = item.radius * PIXEL_PER_DEG;
							 | 
						||
| 
								 | 
							
								            }
							 | 
						||
| 
								 | 
							
								            if (item.shape) {
							 | 
						||
| 
								 | 
							
								                res.shape = item.shape;
							 | 
						||
| 
								 | 
							
								            }
							 | 
						||
| 
								 | 
							
								            if (item.roofShape) {
							 | 
						||
| 
								 | 
							
								                res.roofShape = item.roofShape;
							 | 
						||
| 
								 | 
							
								            }
							 | 
						||
| 
								 | 
							
								            if ((res.roofShape === 'cone' || res.roofShape === 'dome') && !res.shape && isRotational(res.footprint)) {
							 | 
						||
| 
								 | 
							
								                res.shape = 'cylinder';
							 | 
						||
| 
								 | 
							
								            }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								            if (item.holes) {
							 | 
						||
| 
								 | 
							
								                res.holes = [];
							 | 
						||
| 
								 | 
							
								                var innerFootprint;
							 | 
						||
| 
								 | 
							
								                for (var i = 0, il = item.holes.length; i < il; i++) {
							 | 
						||
| 
								 | 
							
								                    // TODO: simplify
							 | 
						||
| 
								 | 
							
								                    if ((innerFootprint = this.getPixelFootprint(item.holes[i]))) {
							 | 
						||
| 
								 | 
							
								                        res.holes.push(innerFootprint);
							 | 
						||
| 
								 | 
							
								                    }
							 | 
						||
| 
								 | 
							
								                }
							 | 
						||
| 
								 | 
							
								            }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								            var color;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								            if (item.wallColor) {
							 | 
						||
| 
								 | 
							
								                if ((color = Color.parse(item.wallColor))) {
							 | 
						||
| 
								 | 
							
								                    color = color.alpha(ZOOM_FACTOR);
							 | 
						||
| 
								 | 
							
								                    res.altColor = '' + color.lightness(0.8);
							 | 
						||
| 
								 | 
							
								                    res.wallColor = '' + color;
							 | 
						||
| 
								 | 
							
								                }
							 | 
						||
| 
								 | 
							
								            }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								            if (item.roofColor) {
							 | 
						||
| 
								 | 
							
								                if ((color = Color.parse(item.roofColor))) {
							 | 
						||
| 
								 | 
							
								                    res.roofColor = '' + color.alpha(ZOOM_FACTOR);
							 | 
						||
| 
								 | 
							
								                }
							 | 
						||
| 
								 | 
							
								            }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								            if (item.relationId) {
							 | 
						||
| 
								 | 
							
								                res.relationId = item.relationId;
							 | 
						||
| 
								 | 
							
								            }
							 | 
						||
| 
								 | 
							
								            res.hitColor = HitAreas.idToColor(item.relationId || item.id);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								            res.roofHeight = isNaN(item.roofHeight) ? 0 : item.roofHeight / zoomScale;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								            if (res.height + res.roofHeight <= res.minHeight) {
							 | 
						||
| 
								 | 
							
								                return;
							 | 
						||
| 
								 | 
							
								            }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								            return res;
							 | 
						||
| 
								 | 
							
								        },
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        set: function (data) {
							 | 
						||
| 
								 | 
							
								            this.isStatic = true;
							 | 
						||
| 
								 | 
							
								            this.resetItems();
							 | 
						||
| 
								 | 
							
								            this._staticData = data;
							 | 
						||
| 
								 | 
							
								            this.addRenderItems(this._staticData, true);
							 | 
						||
| 
								 | 
							
								        },
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        load: function (src, key) {
							 | 
						||
| 
								 | 
							
								            this.src = src || DATA_SRC.replace('{k}', (key || 'anonymous'));
							 | 
						||
| 
								 | 
							
								            this.update();
							 | 
						||
| 
								 | 
							
								        },
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        update: function () {
							 | 
						||
| 
								 | 
							
								            this.resetItems();
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								            if (ZOOM < MIN_ZOOM) {
							 | 
						||
| 
								 | 
							
								                return;
							 | 
						||
| 
								 | 
							
								            }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								            if (this.isStatic && this._staticData) {
							 | 
						||
| 
								 | 
							
								                this.addRenderItems(this._staticData);
							 | 
						||
| 
								 | 
							
								                return;
							 | 
						||
| 
								 | 
							
								            }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								            if (!this.src) {
							 | 
						||
| 
								 | 
							
								                return;
							 | 
						||
| 
								 | 
							
								            }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								            var
							 | 
						||
| 
								 | 
							
								                tileZoom = 13,
							 | 
						||
| 
								 | 
							
								                tileSize = 256,
							 | 
						||
| 
								 | 
							
								                zoomedTileSize = ZOOM > tileZoom ? tileSize << (ZOOM - tileZoom) : tileSize >> (tileZoom - ZOOM),
							 | 
						||
| 
								 | 
							
								                minX = ORIGIN_X / zoomedTileSize << 0,
							 | 
						||
| 
								 | 
							
								                minY = ORIGIN_Y / zoomedTileSize << 0,
							 | 
						||
| 
								 | 
							
								                maxX = ceil((ORIGIN_X + WIDTH) / zoomedTileSize),
							 | 
						||
| 
								 | 
							
								                maxY = ceil((ORIGIN_Y + HEIGHT) / zoomedTileSize),
							 | 
						||
| 
								 | 
							
								                x, y;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								            var scope = this;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								            function callback(json) {
							 | 
						||
| 
								 | 
							
								                scope.addRenderItems(json);
							 | 
						||
| 
								 | 
							
								            }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								            for (y = minY; y <= maxY; y++) {
							 | 
						||
| 
								 | 
							
								                for (x = minX; x <= maxX; x++) {
							 | 
						||
| 
								 | 
							
								                    this.loadTile(x, y, tileZoom, callback);
							 | 
						||
| 
								 | 
							
								                }
							 | 
						||
| 
								 | 
							
								            }
							 | 
						||
| 
								 | 
							
								        },
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        loadTile: function (x, y, zoom, callback) {
							 | 
						||
| 
								 | 
							
								            var s = 'abcd'[(x + y) % 4];
							 | 
						||
| 
								 | 
							
								            var url = this.src.replace('{s}', s).replace('{x}', x).replace('{y}', y).replace('{z}', zoom);
							 | 
						||
| 
								 | 
							
								            //return Request.loadJSON(url, callback);
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								    };
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								//块状体
							 | 
						||
| 
								 | 
							
								//****** file: Block.js ******
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    var Block = {
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        draw: function (context, polygon, innerPolygons, height, minHeight, color, altColor, roofColor) {
							 | 
						||
| 
								 | 
							
								            var
							 | 
						||
| 
								 | 
							
								                i, il,
							 | 
						||
| 
								 | 
							
								                roof = this._extrude(context, polygon, height, minHeight, color, altColor),
							 | 
						||
| 
								 | 
							
								                innerRoofs = [];
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								            if (innerPolygons) {
							 | 
						||
| 
								 | 
							
								                for (i = 0, il = innerPolygons.length; i < il; i++) {
							 | 
						||
| 
								 | 
							
								                    innerRoofs[i] = this._extrude(context, innerPolygons[i], height, minHeight, color, altColor);
							 | 
						||
| 
								 | 
							
								                }
							 | 
						||
| 
								 | 
							
								            }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								            context.fillStyle = roofColor;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								            context.beginPath();
							 | 
						||
| 
								 | 
							
								            this._ring(context, roof);
							 | 
						||
| 
								 | 
							
								            if (innerPolygons) {
							 | 
						||
| 
								 | 
							
								                for (i = 0, il = innerRoofs.length; i < il; i++) {
							 | 
						||
| 
								 | 
							
								                    this._ring(context, innerRoofs[i]);
							 | 
						||
| 
								 | 
							
								                }
							 | 
						||
| 
								 | 
							
								            }
							 | 
						||
| 
								 | 
							
								            context.closePath();
							 | 
						||
| 
								 | 
							
								            context.stroke();
							 | 
						||
| 
								 | 
							
								            context.fill();
							 | 
						||
| 
								 | 
							
								        },
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        _extrude: function (context, polygon, height, minHeight, color, altColor) {
							 | 
						||
| 
								 | 
							
								            var
							 | 
						||
| 
								 | 
							
								                scale = CAM_Z / (CAM_Z - height),
							 | 
						||
| 
								 | 
							
								                minScale = CAM_Z / (CAM_Z - minHeight),
							 | 
						||
| 
								 | 
							
								                a = {x: 0, y: 0},
							 | 
						||
| 
								 | 
							
								                b = {x: 0, y: 0},
							 | 
						||
| 
								 | 
							
								                _a, _b,
							 | 
						||
| 
								 | 
							
								                roof = [];
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								            for (var i = 0, il = polygon.length - 3; i < il; i += 2) {
							 | 
						||
| 
								 | 
							
								                a.x = polygon[i] - ORIGIN_X;
							 | 
						||
| 
								 | 
							
								                a.y = polygon[i + 1] - ORIGIN_Y;
							 | 
						||
| 
								 | 
							
								                b.x = polygon[i + 2] - ORIGIN_X;
							 | 
						||
| 
								 | 
							
								                b.y = polygon[i + 3] - ORIGIN_Y;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								                _a = Buildings.project(a, scale);
							 | 
						||
| 
								 | 
							
								                _b = Buildings.project(b, scale);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								                if (minHeight) {
							 | 
						||
| 
								 | 
							
								                    a = Buildings.project(a, minScale);
							 | 
						||
| 
								 | 
							
								                    b = Buildings.project(b, minScale);
							 | 
						||
| 
								 | 
							
								                }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								                // backface culling check
							 | 
						||
| 
								 | 
							
								                if ((b.x - a.x) * (_a.y - a.y) > (_a.x - a.x) * (b.y - a.y)) {
							 | 
						||
| 
								 | 
							
								                    // depending on direction, set wall shading
							 | 
						||
| 
								 | 
							
								                    if ((a.x < b.x && a.y < b.y) || (a.x > b.x && a.y > b.y)) {
							 | 
						||
| 
								 | 
							
								                        context.fillStyle = altColor;
							 | 
						||
| 
								 | 
							
								                    } else {
							 | 
						||
| 
								 | 
							
								                        context.fillStyle = color;
							 | 
						||
| 
								 | 
							
								                    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								                    context.beginPath();
							 | 
						||
| 
								 | 
							
								                    this._ring(context, [
							 | 
						||
| 
								 | 
							
								                        b.x, b.y,
							 | 
						||
| 
								 | 
							
								                        a.x, a.y,
							 | 
						||
| 
								 | 
							
								                        _a.x, _a.y,
							 | 
						||
| 
								 | 
							
								                        _b.x, _b.y
							 | 
						||
| 
								 | 
							
								                    ]);
							 | 
						||
| 
								 | 
							
								                    context.closePath();
							 | 
						||
| 
								 | 
							
								                    context.fill();
							 | 
						||
| 
								 | 
							
								                }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								                roof[i] = _a.x;
							 | 
						||
| 
								 | 
							
								                roof[i + 1] = _a.y;
							 | 
						||
| 
								 | 
							
								            }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								            return roof;
							 | 
						||
| 
								 | 
							
								        },
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        _ring: function (context, polygon) {
							 | 
						||
| 
								 | 
							
								            context.moveTo(polygon[0], polygon[1]);
							 | 
						||
| 
								 | 
							
								            for (var i = 2, il = polygon.length - 1; i < il; i += 2) {
							 | 
						||
| 
								 | 
							
								                context.lineTo(polygon[i], polygon[i + 1]);
							 | 
						||
| 
								 | 
							
								            }
							 | 
						||
| 
								 | 
							
								        },
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        simplified: function (context, polygon, innerPolygons) {
							 | 
						||
| 
								 | 
							
								            context.beginPath();
							 | 
						||
| 
								 | 
							
								            this._ringAbs(context, polygon);
							 | 
						||
| 
								 | 
							
								            if (innerPolygons) {
							 | 
						||
| 
								 | 
							
								                for (var i = 0, il = innerPolygons.length; i < il; i++) {
							 | 
						||
| 
								 | 
							
								                    this._ringAbs(context, innerPolygons[i]);
							 | 
						||
| 
								 | 
							
								                }
							 | 
						||
| 
								 | 
							
								            }
							 | 
						||
| 
								 | 
							
								            context.closePath();
							 | 
						||
| 
								 | 
							
								            context.stroke();
							 | 
						||
| 
								 | 
							
								            context.fill();
							 | 
						||
| 
								 | 
							
								        },
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        _ringAbs: function (context, polygon) {
							 | 
						||
| 
								 | 
							
								            context.moveTo(polygon[0] - ORIGIN_X, polygon[1] - ORIGIN_Y);
							 | 
						||
| 
								 | 
							
								            for (var i = 2, il = polygon.length - 1; i < il; i += 2) {
							 | 
						||
| 
								 | 
							
								                context.lineTo(polygon[i] - ORIGIN_X, polygon[i + 1] - ORIGIN_Y);
							 | 
						||
| 
								 | 
							
								            }
							 | 
						||
| 
								 | 
							
								        },
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        shadow: function (context, polygon, innerPolygons, height, minHeight) {
							 | 
						||
| 
								 | 
							
								            var
							 | 
						||
| 
								 | 
							
								                mode = null,
							 | 
						||
| 
								 | 
							
								                a = {x: 0, y: 0},
							 | 
						||
| 
								 | 
							
								                b = {x: 0, y: 0},
							 | 
						||
| 
								 | 
							
								                _a, _b;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								            for (var i = 0, il = polygon.length - 3; i < il; i += 2) {
							 | 
						||
| 
								 | 
							
								                a.x = polygon[i] - ORIGIN_X;
							 | 
						||
| 
								 | 
							
								                a.y = polygon[i + 1] - ORIGIN_Y;
							 | 
						||
| 
								 | 
							
								                b.x = polygon[i + 2] - ORIGIN_X;
							 | 
						||
| 
								 | 
							
								                b.y = polygon[i + 3] - ORIGIN_Y;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								                _a = Shadows.project(a, height);
							 | 
						||
| 
								 | 
							
								                _b = Shadows.project(b, height);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								                if (minHeight) {
							 | 
						||
| 
								 | 
							
								                    a = Shadows.project(a, minHeight);
							 | 
						||
| 
								 | 
							
								                    b = Shadows.project(b, minHeight);
							 | 
						||
| 
								 | 
							
								                }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								                // mode 0: floor edges, mode 1: roof edges
							 | 
						||
| 
								 | 
							
								                if ((b.x - a.x) * (_a.y - a.y) > (_a.x - a.x) * (b.y - a.y)) {
							 | 
						||
| 
								 | 
							
								                    if (mode === 1) {
							 | 
						||
| 
								 | 
							
								                        context.lineTo(a.x, a.y);
							 | 
						||
| 
								 | 
							
								                    }
							 | 
						||
| 
								 | 
							
								                    mode = 0;
							 | 
						||
| 
								 | 
							
								                    if (!i) {
							 | 
						||
| 
								 | 
							
								                        context.moveTo(a.x, a.y);
							 | 
						||
| 
								 | 
							
								                    }
							 | 
						||
| 
								 | 
							
								                    context.lineTo(b.x, b.y);
							 | 
						||
| 
								 | 
							
								                } else {
							 | 
						||
| 
								 | 
							
								                    if (mode === 0) {
							 | 
						||
| 
								 | 
							
								                        context.lineTo(_a.x, _a.y);
							 | 
						||
| 
								 | 
							
								                    }
							 | 
						||
| 
								 | 
							
								                    mode = 1;
							 | 
						||
| 
								 | 
							
								                    if (!i) {
							 | 
						||
| 
								 | 
							
								                        context.moveTo(_a.x, _a.y);
							 | 
						||
| 
								 | 
							
								                    }
							 | 
						||
| 
								 | 
							
								                    context.lineTo(_b.x, _b.y);
							 | 
						||
| 
								 | 
							
								                }
							 | 
						||
| 
								 | 
							
								            }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								            if (innerPolygons) {
							 | 
						||
| 
								 | 
							
								                for (i = 0, il = innerPolygons.length; i < il; i++) {
							 | 
						||
| 
								 | 
							
								                    this._ringAbs(context, innerPolygons[i]);
							 | 
						||
| 
								 | 
							
								                }
							 | 
						||
| 
								 | 
							
								            }
							 | 
						||
| 
								 | 
							
								        },
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        shadowMask: function (context, polygon, innerPolygons) {
							 | 
						||
| 
								 | 
							
								            this._ringAbs(context, polygon);
							 | 
						||
| 
								 | 
							
								            if (innerPolygons) {
							 | 
						||
| 
								 | 
							
								                for (var i = 0, il = innerPolygons.length; i < il; i++) {
							 | 
						||
| 
								 | 
							
								                    this._ringAbs(context, innerPolygons[i]);
							 | 
						||
| 
								 | 
							
								                }
							 | 
						||
| 
								 | 
							
								            }
							 | 
						||
| 
								 | 
							
								        },
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        hitArea: function (context, polygon, innerPolygons, height, minHeight, color) {
							 | 
						||
| 
								 | 
							
								            var
							 | 
						||
| 
								 | 
							
								                mode = null,
							 | 
						||
| 
								 | 
							
								                a = {x: 0, y: 0},
							 | 
						||
| 
								 | 
							
								                b = {x: 0, y: 0},
							 | 
						||
| 
								 | 
							
								                scale = CAM_Z / (CAM_Z - height),
							 | 
						||
| 
								 | 
							
								                minScale = CAM_Z / (CAM_Z - minHeight),
							 | 
						||
| 
								 | 
							
								                _a, _b;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								            context.fillStyle = color;
							 | 
						||
| 
								 | 
							
								            context.beginPath();
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								            for (var i = 0, il = polygon.length - 3; i < il; i += 2) {
							 | 
						||
| 
								 | 
							
								                a.x = polygon[i] - ORIGIN_X;
							 | 
						||
| 
								 | 
							
								                a.y = polygon[i + 1] - ORIGIN_Y;
							 | 
						||
| 
								 | 
							
								                b.x = polygon[i + 2] - ORIGIN_X;
							 | 
						||
| 
								 | 
							
								                b.y = polygon[i + 3] - ORIGIN_Y;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								                _a = Buildings.project(a, scale);
							 | 
						||
| 
								 | 
							
								                _b = Buildings.project(b, scale);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								                if (minHeight) {
							 | 
						||
| 
								 | 
							
								                    a = Buildings.project(a, minScale);
							 | 
						||
| 
								 | 
							
								                    b = Buildings.project(b, minScale);
							 | 
						||
| 
								 | 
							
								                }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								                // mode 0: floor edges, mode 1: roof edges
							 | 
						||
| 
								 | 
							
								                if ((b.x - a.x) * (_a.y - a.y) > (_a.x - a.x) * (b.y - a.y)) {
							 | 
						||
| 
								 | 
							
								                    if (mode === 1) { // mode is initially undefined
							 | 
						||
| 
								 | 
							
								                        context.lineTo(a.x, a.y);
							 | 
						||
| 
								 | 
							
								                    }
							 | 
						||
| 
								 | 
							
								                    mode = 0;
							 | 
						||
| 
								 | 
							
								                    if (!i) {
							 | 
						||
| 
								 | 
							
								                        context.moveTo(a.x, a.y);
							 | 
						||
| 
								 | 
							
								                    }
							 | 
						||
| 
								 | 
							
								                    context.lineTo(b.x, b.y);
							 | 
						||
| 
								 | 
							
								                } else {
							 | 
						||
| 
								 | 
							
								                    if (mode === 0) { // mode is initially undefined
							 | 
						||
| 
								 | 
							
								                        context.lineTo(_a.x, _a.y);
							 | 
						||
| 
								 | 
							
								                    }
							 | 
						||
| 
								 | 
							
								                    mode = 1;
							 | 
						||
| 
								 | 
							
								                    if (!i) {
							 | 
						||
| 
								 | 
							
								                        context.moveTo(_a.x, _a.y);
							 | 
						||
| 
								 | 
							
								                    }
							 | 
						||
| 
								 | 
							
								                    context.lineTo(_b.x, _b.y);
							 | 
						||
| 
								 | 
							
								                }
							 | 
						||
| 
								 | 
							
								            }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								            context.closePath();
							 | 
						||
| 
								 | 
							
								            context.fill();
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    };
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								//圆柱体
							 | 
						||
| 
								 | 
							
								//****** file: Cylinder.js ******
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    var Cylinder = {
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        draw: function (context, center, radius, topRadius, height, minHeight, color, altColor, roofColor) {
							 | 
						||
| 
								 | 
							
								            var
							 | 
						||
| 
								 | 
							
								                c = {x: center.x - ORIGIN_X, y: center.y - ORIGIN_Y},
							 | 
						||
| 
								 | 
							
								                scale = CAM_Z / (CAM_Z - height),
							 | 
						||
| 
								 | 
							
								                minScale = CAM_Z / (CAM_Z - minHeight),
							 | 
						||
| 
								 | 
							
								                apex = Buildings.project(c, scale),
							 | 
						||
| 
								 | 
							
								                a1, a2;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								            topRadius *= scale;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								            if (minHeight) {
							 | 
						||
| 
								 | 
							
								                c = Buildings.project(c, minScale);
							 | 
						||
| 
								 | 
							
								                radius = radius * minScale;
							 | 
						||
| 
								 | 
							
								            }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								            // common tangents for ground and roof circle
							 | 
						||
| 
								 | 
							
								            var tangents = this._tangents(c, radius, apex, topRadius);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								            // no tangents? top circle is inside bottom circle
							 | 
						||
| 
								 | 
							
								            if (!tangents) {
							 | 
						||
| 
								 | 
							
								                a1 = 1.5 * PI;
							 | 
						||
| 
								 | 
							
								                a2 = 1.5 * PI;
							 | 
						||
| 
								 | 
							
								            } else {
							 | 
						||
| 
								 | 
							
								                a1 = atan2(tangents[0].y1 - c.y, tangents[0].x1 - c.x);
							 | 
						||
| 
								 | 
							
								                a2 = atan2(tangents[1].y1 - c.y, tangents[1].x1 - c.x);
							 | 
						||
| 
								 | 
							
								            }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								            context.fillStyle = color;
							 | 
						||
| 
								 | 
							
								            context.beginPath();
							 | 
						||
| 
								 | 
							
								            context.arc(apex.x, apex.y, topRadius, HALF_PI, a1, true);
							 | 
						||
| 
								 | 
							
								            context.arc(c.x, c.y, radius, a1, HALF_PI);
							 | 
						||
| 
								 | 
							
								            context.closePath();
							 | 
						||
| 
								 | 
							
								            context.fill();
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								            context.fillStyle = altColor;
							 | 
						||
| 
								 | 
							
								            context.beginPath();
							 | 
						||
| 
								 | 
							
								            context.arc(apex.x, apex.y, topRadius, a2, HALF_PI, true);
							 | 
						||
| 
								 | 
							
								            context.arc(c.x, c.y, radius, HALF_PI, a2);
							 | 
						||
| 
								 | 
							
								            context.closePath();
							 | 
						||
| 
								 | 
							
								            context.fill();
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								            context.fillStyle = roofColor;
							 | 
						||
| 
								 | 
							
								            this._circle(context, apex, topRadius);
							 | 
						||
| 
								 | 
							
								        },
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        simplified: function (context, center, radius) {
							 | 
						||
| 
								 | 
							
								            this._circle(context, {x: center.x - ORIGIN_X, y: center.y - ORIGIN_Y}, radius);
							 | 
						||
| 
								 | 
							
								        },
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        shadow: function (context, center, radius, topRadius, height, minHeight) {
							 | 
						||
| 
								 | 
							
								            var
							 | 
						||
| 
								 | 
							
								                c = {x: center.x - ORIGIN_X, y: center.y - ORIGIN_Y},
							 | 
						||
| 
								 | 
							
								                apex = Shadows.project(c, height),
							 | 
						||
| 
								 | 
							
								                p1, p2;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								            if (minHeight) {
							 | 
						||
| 
								 | 
							
								                c = Shadows.project(c, minHeight);
							 | 
						||
| 
								 | 
							
								            }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								            // common tangents for ground and roof circle
							 | 
						||
| 
								 | 
							
								            var tangents = this._tangents(c, radius, apex, topRadius);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								            // TODO: no tangents? roof overlaps everything near cam position
							 | 
						||
| 
								 | 
							
								            if (tangents) {
							 | 
						||
| 
								 | 
							
								                p1 = atan2(tangents[0].y1 - c.y, tangents[0].x1 - c.x);
							 | 
						||
| 
								 | 
							
								                p2 = atan2(tangents[1].y1 - c.y, tangents[1].x1 - c.x);
							 | 
						||
| 
								 | 
							
								                context.moveTo(tangents[1].x2, tangents[1].y2);
							 | 
						||
| 
								 | 
							
								                context.arc(apex.x, apex.y, topRadius, p2, p1);
							 | 
						||
| 
								 | 
							
								                context.arc(c.x, c.y, radius, p1, p2);
							 | 
						||
| 
								 | 
							
								            } else {
							 | 
						||
| 
								 | 
							
								                context.moveTo(c.x + radius, c.y);
							 | 
						||
| 
								 | 
							
								                context.arc(c.x, c.y, radius, 0, 2 * PI);
							 | 
						||
| 
								 | 
							
								            }
							 | 
						||
| 
								 | 
							
								        },
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        shadowMask: function (context, center, radius) {
							 | 
						||
| 
								 | 
							
								            var c = {x: center.x - ORIGIN_X, y: center.y - ORIGIN_Y};
							 | 
						||
| 
								 | 
							
								            context.moveTo(c.x + radius, c.y);
							 | 
						||
| 
								 | 
							
								            context.arc(c.x, c.y, radius, 0, PI * 2);
							 | 
						||
| 
								 | 
							
								        },
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        hitArea: function (context, center, radius, topRadius, height, minHeight, color) {
							 | 
						||
| 
								 | 
							
								            var
							 | 
						||
| 
								 | 
							
								                c = {x: center.x - ORIGIN_X, y: center.y - ORIGIN_Y},
							 | 
						||
| 
								 | 
							
								                scale = CAM_Z / (CAM_Z - height),
							 | 
						||
| 
								 | 
							
								                minScale = CAM_Z / (CAM_Z - minHeight),
							 | 
						||
| 
								 | 
							
								                apex = Buildings.project(c, scale),
							 | 
						||
| 
								 | 
							
								                p1, p2;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								            topRadius *= scale;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								            if (minHeight) {
							 | 
						||
| 
								 | 
							
								                c = Buildings.project(c, minScale);
							 | 
						||
| 
								 | 
							
								                radius = radius * minScale;
							 | 
						||
| 
								 | 
							
								            }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								            // common tangents for ground and roof circle
							 | 
						||
| 
								 | 
							
								            var tangents = this._tangents(c, radius, apex, topRadius);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								            context.fillStyle = color;
							 | 
						||
| 
								 | 
							
								            context.beginPath();
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								            // TODO: no tangents? roof overlaps everything near cam position
							 | 
						||
| 
								 | 
							
								            if (tangents) {
							 | 
						||
| 
								 | 
							
								                p1 = atan2(tangents[0].y1 - c.y, tangents[0].x1 - c.x);
							 | 
						||
| 
								 | 
							
								                p2 = atan2(tangents[1].y1 - c.y, tangents[1].x1 - c.x);
							 | 
						||
| 
								 | 
							
								                context.moveTo(tangents[1].x2, tangents[1].y2);
							 | 
						||
| 
								 | 
							
								                context.arc(apex.x, apex.y, topRadius, p2, p1);
							 | 
						||
| 
								 | 
							
								                context.arc(c.x, c.y, radius, p1, p2);
							 | 
						||
| 
								 | 
							
								            } else {
							 | 
						||
| 
								 | 
							
								                context.moveTo(c.x + radius, c.y);
							 | 
						||
| 
								 | 
							
								                context.arc(c.x, c.y, radius, 0, 2 * PI);
							 | 
						||
| 
								 | 
							
								            }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								            context.closePath();
							 | 
						||
| 
								 | 
							
								            context.fill();
							 | 
						||
| 
								 | 
							
								        },
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        _circle: function (context, center, radius) {
							 | 
						||
| 
								 | 
							
								            context.beginPath();
							 | 
						||
| 
								 | 
							
								            context.arc(center.x, center.y, radius, 0, PI * 2);
							 | 
						||
| 
								 | 
							
								            context.stroke();
							 | 
						||
| 
								 | 
							
								            context.fill();
							 | 
						||
| 
								 | 
							
								        },
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        // http://en.wikibooks.org/wiki/Algorithm_Implementation/Geometry/Tangents_between_two_circles
							 | 
						||
| 
								 | 
							
								        _tangents: function (c1, r1, c2, r2) {
							 | 
						||
| 
								 | 
							
								            var
							 | 
						||
| 
								 | 
							
								                dx = c1.x - c2.x,
							 | 
						||
| 
								 | 
							
								                dy = c1.y - c2.y,
							 | 
						||
| 
								 | 
							
								                dr = r1 - r2,
							 | 
						||
| 
								 | 
							
								                sqdist = (dx * dx) + (dy * dy);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								            if (sqdist <= dr * dr) {
							 | 
						||
| 
								 | 
							
								                return;
							 | 
						||
| 
								 | 
							
								            }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								            var dist = sqrt(sqdist),
							 | 
						||
| 
								 | 
							
								                vx = -dx / dist,
							 | 
						||
| 
								 | 
							
								                vy = -dy / dist,
							 | 
						||
| 
								 | 
							
								                c = dr / dist,
							 | 
						||
| 
								 | 
							
								                res = [],
							 | 
						||
| 
								 | 
							
								                h, nx, ny;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								            // Let A, B be the centers, and C, D be points at which the tangent
							 | 
						||
| 
								 | 
							
								            // touches first and second circle, and n be the normal vector to it.
							 | 
						||
| 
								 | 
							
								            //
							 | 
						||
| 
								 | 
							
								            // We have the system:
							 | 
						||
| 
								 | 
							
								            //   n * n = 1    (n is a unit vector)
							 | 
						||
| 
								 | 
							
								            //   C = A + r1 * n
							 | 
						||
| 
								 | 
							
								            //   D = B + r2 * n
							 | 
						||
| 
								 | 
							
								            //   n * CD = 0   (common orthogonality)
							 | 
						||
| 
								 | 
							
								            //
							 | 
						||
| 
								 | 
							
								            // n * CD = n * (AB + r2*n - r1*n) = AB*n - (r1 -/+ r2) = 0,  <=>
							 | 
						||
| 
								 | 
							
								            // AB * n = (r1 -/+ r2), <=>
							 | 
						||
| 
								 | 
							
								            // v * n = (r1 -/+ r2) / d,  where v = AB/|AB| = AB/d
							 | 
						||
| 
								 | 
							
								            // This is a linear equation in unknown vector n.
							 | 
						||
| 
								 | 
							
								            // Now we're just intersecting a line with a circle: v*n=c, n*n=1
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								            h = sqrt(max(0, 1 - c * c));
							 | 
						||
| 
								 | 
							
								            for (var sign = 1; sign >= -1; sign -= 2) {
							 | 
						||
| 
								 | 
							
								                nx = vx * c - sign * h * vy;
							 | 
						||
| 
								 | 
							
								                ny = vy * c + sign * h * vx;
							 | 
						||
| 
								 | 
							
								                res.push({
							 | 
						||
| 
								 | 
							
								                    x1: c1.x + r1 * nx << 0,
							 | 
						||
| 
								 | 
							
								                    y1: c1.y + r1 * ny << 0,
							 | 
						||
| 
								 | 
							
								                    x2: c2.x + r2 * nx << 0,
							 | 
						||
| 
								 | 
							
								                    y2: c2.y + r2 * ny << 0
							 | 
						||
| 
								 | 
							
								                });
							 | 
						||
| 
								 | 
							
								            }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								            return res;
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								    };
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								//椎体
							 | 
						||
| 
								 | 
							
								//****** file: Pyramid.js ******
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    var Pyramid = {
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        draw: function (context, polygon, center, height, minHeight, color, altColor) {
							 | 
						||
| 
								 | 
							
								            var
							 | 
						||
| 
								 | 
							
								                c = {x: center.x - ORIGIN_X, y: center.y - ORIGIN_Y},
							 | 
						||
| 
								 | 
							
								                scale = CAM_Z / (CAM_Z - height),
							 | 
						||
| 
								 | 
							
								                minScale = CAM_Z / (CAM_Z - minHeight),
							 | 
						||
| 
								 | 
							
								                apex = Buildings.project(c, scale),
							 | 
						||
| 
								 | 
							
								                a = {x: 0, y: 0},
							 | 
						||
| 
								 | 
							
								                b = {x: 0, y: 0};
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								            for (var i = 0, il = polygon.length - 3; i < il; i += 2) {
							 | 
						||
| 
								 | 
							
								                a.x = polygon[i] - ORIGIN_X;
							 | 
						||
| 
								 | 
							
								                a.y = polygon[i + 1] - ORIGIN_Y;
							 | 
						||
| 
								 | 
							
								                b.x = polygon[i + 2] - ORIGIN_X;
							 | 
						||
| 
								 | 
							
								                b.y = polygon[i + 3] - ORIGIN_Y;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								                if (minHeight) {
							 | 
						||
| 
								 | 
							
								                    a = Buildings.project(a, minScale);
							 | 
						||
| 
								 | 
							
								                    b = Buildings.project(b, minScale);
							 | 
						||
| 
								 | 
							
								                }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								                // backface culling check
							 | 
						||
| 
								 | 
							
								                if ((b.x - a.x) * (apex.y - a.y) > (apex.x - a.x) * (b.y - a.y)) {
							 | 
						||
| 
								 | 
							
								                    // depending on direction, set shading
							 | 
						||
| 
								 | 
							
								                    if ((a.x < b.x && a.y < b.y) || (a.x > b.x && a.y > b.y)) {
							 | 
						||
| 
								 | 
							
								                        context.fillStyle = altColor;
							 | 
						||
| 
								 | 
							
								                    } else {
							 | 
						||
| 
								 | 
							
								                        context.fillStyle = color;
							 | 
						||
| 
								 | 
							
								                    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								                    context.beginPath();
							 | 
						||
| 
								 | 
							
								                    this._triangle(context, a, b, apex);
							 | 
						||
| 
								 | 
							
								                    context.closePath();
							 | 
						||
| 
								 | 
							
								                    context.fill();
							 | 
						||
| 
								 | 
							
								                }
							 | 
						||
| 
								 | 
							
								            }
							 | 
						||
| 
								 | 
							
								        },
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        _triangle: function (context, a, b, c) {
							 | 
						||
| 
								 | 
							
								            context.moveTo(a.x, a.y);
							 | 
						||
| 
								 | 
							
								            context.lineTo(b.x, b.y);
							 | 
						||
| 
								 | 
							
								            context.lineTo(c.x, c.y);
							 | 
						||
| 
								 | 
							
								        },
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        _ring: function (context, polygon) {
							 | 
						||
| 
								 | 
							
								            context.moveTo(polygon[0] - ORIGIN_X, polygon[1] - ORIGIN_Y);
							 | 
						||
| 
								 | 
							
								            for (var i = 2, il = polygon.length - 1; i < il; i += 2) {
							 | 
						||
| 
								 | 
							
								                context.lineTo(polygon[i] - ORIGIN_X, polygon[i + 1] - ORIGIN_Y);
							 | 
						||
| 
								 | 
							
								            }
							 | 
						||
| 
								 | 
							
								        },
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        shadow: function (context, polygon, center, height, minHeight) {
							 | 
						||
| 
								 | 
							
								            var
							 | 
						||
| 
								 | 
							
								                a = {x: 0, y: 0},
							 | 
						||
| 
								 | 
							
								                b = {x: 0, y: 0},
							 | 
						||
| 
								 | 
							
								                c = {x: center.x - ORIGIN_X, y: center.y - ORIGIN_Y},
							 | 
						||
| 
								 | 
							
								                apex = Shadows.project(c, height);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								            for (var i = 0, il = polygon.length - 3; i < il; i += 2) {
							 | 
						||
| 
								 | 
							
								                a.x = polygon[i] - ORIGIN_X;
							 | 
						||
| 
								 | 
							
								                a.y = polygon[i + 1] - ORIGIN_Y;
							 | 
						||
| 
								 | 
							
								                b.x = polygon[i + 2] - ORIGIN_X;
							 | 
						||
| 
								 | 
							
								                b.y = polygon[i + 3] - ORIGIN_Y;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								                if (minHeight) {
							 | 
						||
| 
								 | 
							
								                    a = Shadows.project(a, minHeight);
							 | 
						||
| 
								 | 
							
								                    b = Shadows.project(b, minHeight);
							 | 
						||
| 
								 | 
							
								                }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								                // backface culling check
							 | 
						||
| 
								 | 
							
								                if ((b.x - a.x) * (apex.y - a.y) > (apex.x - a.x) * (b.y - a.y)) {
							 | 
						||
| 
								 | 
							
								                    // depending on direction, set shading
							 | 
						||
| 
								 | 
							
								                    this._triangle(context, a, b, apex);
							 | 
						||
| 
								 | 
							
								                }
							 | 
						||
| 
								 | 
							
								            }
							 | 
						||
| 
								 | 
							
								        },
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        shadowMask: function (context, polygon) {
							 | 
						||
| 
								 | 
							
								            this._ring(context, polygon);
							 | 
						||
| 
								 | 
							
								        },
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        hitArea: function (context, polygon, center, height, minHeight, color) {
							 | 
						||
| 
								 | 
							
								            var
							 | 
						||
| 
								 | 
							
								                c = {x: center.x - ORIGIN_X, y: center.y - ORIGIN_Y},
							 | 
						||
| 
								 | 
							
								                scale = CAM_Z / (CAM_Z - height),
							 | 
						||
| 
								 | 
							
								                minScale = CAM_Z / (CAM_Z - minHeight),
							 | 
						||
| 
								 | 
							
								                apex = Buildings.project(c, scale),
							 | 
						||
| 
								 | 
							
								                a = {x: 0, y: 0},
							 | 
						||
| 
								 | 
							
								                b = {x: 0, y: 0};
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								            context.fillStyle = color;
							 | 
						||
| 
								 | 
							
								            context.beginPath();
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								            for (var i = 0, il = polygon.length - 3; i < il; i += 2) {
							 | 
						||
| 
								 | 
							
								                a.x = polygon[i] - ORIGIN_X;
							 | 
						||
| 
								 | 
							
								                a.y = polygon[i + 1] - ORIGIN_Y;
							 | 
						||
| 
								 | 
							
								                b.x = polygon[i + 2] - ORIGIN_X;
							 | 
						||
| 
								 | 
							
								                b.y = polygon[i + 3] - ORIGIN_Y;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								                if (minHeight) {
							 | 
						||
| 
								 | 
							
								                    a = Buildings.project(a, minScale);
							 | 
						||
| 
								 | 
							
								                    b = Buildings.project(b, minScale);
							 | 
						||
| 
								 | 
							
								                }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								                // backface culling check
							 | 
						||
| 
								 | 
							
								                if ((b.x - a.x) * (apex.y - a.y) > (apex.x - a.x) * (b.y - a.y)) {
							 | 
						||
| 
								 | 
							
								                    this._triangle(context, a, b, apex);
							 | 
						||
| 
								 | 
							
								                }
							 | 
						||
| 
								 | 
							
								            }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								            context.closePath();
							 | 
						||
| 
								 | 
							
								            context.fill();
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								    };
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								//****** file: Buildings.js ******
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    var Buildings = {
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        project: function (p, m) {
							 | 
						||
| 
								 | 
							
								            return {
							 | 
						||
| 
								 | 
							
								                x: (p.x - CAM_X) * m + CAM_X << 0,
							 | 
						||
| 
								 | 
							
								                y: (p.y - CAM_Y) * m + CAM_Y << 0
							 | 
						||
| 
								 | 
							
								            };
							 | 
						||
| 
								 | 
							
								        },
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        render: function () {
							 | 
						||
| 
								 | 
							
								            var context = this.context;
							 | 
						||
| 
								 | 
							
								            context.clearRect(0, 0, WIDTH, HEIGHT);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								            // show on high zoom levels only and avoid rendering during zoom
							 | 
						||
| 
								 | 
							
								            if (ZOOM < MIN_ZOOM || isZooming) {
							 | 
						||
| 
								 | 
							
								                return;
							 | 
						||
| 
								 | 
							
								            }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								            var
							 | 
						||
| 
								 | 
							
								                item,
							 | 
						||
| 
								 | 
							
								                h, mh,
							 | 
						||
| 
								 | 
							
								                sortCam = {x: CAM_X + ORIGIN_X, y: CAM_Y + ORIGIN_Y},
							 | 
						||
| 
								 | 
							
								                footprint,
							 | 
						||
| 
								 | 
							
								                wallColor, altColor, roofColor,
							 | 
						||
| 
								 | 
							
								                dataItems = Data.items;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								            dataItems.sort(function (a, b) {
							 | 
						||
| 
								 | 
							
								                return (a.minHeight - b.minHeight) || getDistance(b.center, sortCam) - getDistance(a.center, sortCam) || (b.height - a.height);
							 | 
						||
| 
								 | 
							
								            });
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								            for (var i = 0, il = dataItems.length; i < il; i++) {
							 | 
						||
| 
								 | 
							
								                item = dataItems[i];
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								                if (Simplified.isSimple(item)) {
							 | 
						||
| 
								 | 
							
								                    continue;
							 | 
						||
| 
								 | 
							
								                }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								                footprint = item.footprint;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								                if (!isVisible(footprint)) {
							 | 
						||
| 
								 | 
							
								                    continue;
							 | 
						||
| 
								 | 
							
								                }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								                // when fading in, use a dynamic height
							 | 
						||
| 
								 | 
							
								                h = item.scale < 1 ? item.height * item.scale : item.height;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								                mh = 0;
							 | 
						||
| 
								 | 
							
								                if (item.minHeight) {
							 | 
						||
| 
								 | 
							
								                    mh = item.scale < 1 ? item.minHeight * item.scale : item.minHeight;
							 | 
						||
| 
								 | 
							
								                }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								                wallColor = item.wallColor || WALL_COLOR_STR;
							 | 
						||
| 
								 | 
							
								                altColor = item.altColor || ALT_COLOR_STR;
							 | 
						||
| 
								 | 
							
								                roofColor = item.roofColor || ROOF_COLOR_STR;
							 | 
						||
| 
								 | 
							
								                context.strokeStyle = altColor;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								                switch (item.shape) {
							 | 
						||
| 
								 | 
							
								                    case 'cylinder':
							 | 
						||
| 
								 | 
							
								                        Cylinder.draw(context, item.center, item.radius, item.radius, h, mh, wallColor, altColor, roofColor);
							 | 
						||
| 
								 | 
							
								                        break;
							 | 
						||
| 
								 | 
							
								                    case 'cone':
							 | 
						||
| 
								 | 
							
								                        Cylinder.draw(context, item.center, item.radius, 0, h, mh, wallColor, altColor);
							 | 
						||
| 
								 | 
							
								                        break;
							 | 
						||
| 
								 | 
							
								                    case 'dome':
							 | 
						||
| 
								 | 
							
								                        Cylinder.draw(context, item.center, item.radius, item.radius / 2, h, mh, wallColor, altColor);
							 | 
						||
| 
								 | 
							
								                        break;
							 | 
						||
| 
								 | 
							
								                    case 'sphere':
							 | 
						||
| 
								 | 
							
								                        Cylinder.draw(context, item.center, item.radius, item.radius, h, mh, wallColor, altColor, roofColor);
							 | 
						||
| 
								 | 
							
								                        break;
							 | 
						||
| 
								 | 
							
								                    case 'pyramid':
							 | 
						||
| 
								 | 
							
								                        Pyramid.draw(context, footprint, item.center, h, mh, wallColor, altColor);
							 | 
						||
| 
								 | 
							
								                        break;
							 | 
						||
| 
								 | 
							
								                    default:
							 | 
						||
| 
								 | 
							
								                        Block.draw(context, footprint, item.holes, h, mh, wallColor, altColor, roofColor);
							 | 
						||
| 
								 | 
							
								                }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								                switch (item.roofShape) {
							 | 
						||
| 
								 | 
							
								                    case 'cone':
							 | 
						||
| 
								 | 
							
								                        Cylinder.draw(context, item.center, item.radius, 0, h + item.roofHeight, h, roofColor, '' + Color.parse(roofColor).lightness(0.9));
							 | 
						||
| 
								 | 
							
								                        break;
							 | 
						||
| 
								 | 
							
								                    case 'dome':
							 | 
						||
| 
								 | 
							
								                        Cylinder.draw(context, item.center, item.radius, item.radius / 2, h + item.roofHeight, h, roofColor, '' + Color.parse(roofColor).lightness(0.9));
							 | 
						||
| 
								 | 
							
								                        break;
							 | 
						||
| 
								 | 
							
								                    case 'pyramid':
							 | 
						||
| 
								 | 
							
								                        Pyramid.draw(context, footprint, item.center, h + item.roofHeight, h, roofColor, Color.parse(roofColor).lightness(0.9));
							 | 
						||
| 
								 | 
							
								                        break;
							 | 
						||
| 
								 | 
							
								                }
							 | 
						||
| 
								 | 
							
								            }
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								    };
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								//****** file: Simplified.js ******
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    var Simplified = {
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        maxZoom: MIN_ZOOM + 2,
							 | 
						||
| 
								 | 
							
								        maxHeight: 5,
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        isSimple: function (item) {
							 | 
						||
| 
								 | 
							
								            return (ZOOM <= this.maxZoom && item.height + item.roofHeight < this.maxHeight);
							 | 
						||
| 
								 | 
							
								        },
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        render: function () {
							 | 
						||
| 
								 | 
							
								            var context = this.context;
							 | 
						||
| 
								 | 
							
								            context.clearRect(0, 0, WIDTH, HEIGHT);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								            // show on high zoom levels only and avoid rendering during zoom
							 | 
						||
| 
								 | 
							
								            if (ZOOM < MIN_ZOOM || isZooming || ZOOM > this.maxZoom) {
							 | 
						||
| 
								 | 
							
								                return;
							 | 
						||
| 
								 | 
							
								            }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								            var
							 | 
						||
| 
								 | 
							
								                item,
							 | 
						||
| 
								 | 
							
								                footprint,
							 | 
						||
| 
								 | 
							
								                dataItems = Data.items;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								            for (var i = 0, il = dataItems.length; i < il; i++) {
							 | 
						||
| 
								 | 
							
								                item = dataItems[i];
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								                if (item.height >= this.maxHeight) {
							 | 
						||
| 
								 | 
							
								                    continue;
							 | 
						||
| 
								 | 
							
								                }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								                footprint = item.footprint;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								                if (!isVisible(footprint)) {
							 | 
						||
| 
								 | 
							
								                    continue;
							 | 
						||
| 
								 | 
							
								                }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								                context.strokeStyle = item.altColor || ALT_COLOR_STR;
							 | 
						||
| 
								 | 
							
								                context.fillStyle = item.roofColor || ROOF_COLOR_STR;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								                switch (item.shape) {
							 | 
						||
| 
								 | 
							
								                    case 'cylinder':
							 | 
						||
| 
								 | 
							
								                    case 'cone':
							 | 
						||
| 
								 | 
							
								                    case 'dome':
							 | 
						||
| 
								 | 
							
								                    case 'sphere':
							 | 
						||
| 
								 | 
							
								                        Cylinder.simplified(context, item.center, item.radius);
							 | 
						||
| 
								 | 
							
								                        break;
							 | 
						||
| 
								 | 
							
								                    default:
							 | 
						||
| 
								 | 
							
								                        Block.simplified(context, footprint, item.holes);
							 | 
						||
| 
								 | 
							
								                }
							 | 
						||
| 
								 | 
							
								            }
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								    };
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								//****** file: Shadows.js ******
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    var Shadows = {
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        enabled: true,
							 | 
						||
| 
								 | 
							
								        color: '#666666',
							 | 
						||
| 
								 | 
							
								        blurColor: '#000000',
							 | 
						||
| 
								 | 
							
								        blurSize: 15,
							 | 
						||
| 
								 | 
							
								        date: new Date(),
							 | 
						||
| 
								 | 
							
								        direction: {x: 0, y: 0},
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        project: function (p, h) {
							 | 
						||
| 
								 | 
							
								            return {
							 | 
						||
| 
								 | 
							
								                x: p.x + this.direction.x * h,
							 | 
						||
| 
								 | 
							
								                y: p.y + this.direction.y * h
							 | 
						||
| 
								 | 
							
								            };
							 | 
						||
| 
								 | 
							
								        },
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        render: function () {
							 | 
						||
| 
								 | 
							
								            var
							 | 
						||
| 
								 | 
							
								                context = this.context,
							 | 
						||
| 
								 | 
							
								                screenCenter, sun, length, alpha;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								            context.clearRect(0, 0, WIDTH, HEIGHT);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								            // show on high zoom levels only and avoid rendering during zoom
							 | 
						||
| 
								 | 
							
								            if (!this.enabled || ZOOM < MIN_ZOOM || isZooming) {
							 | 
						||
| 
								 | 
							
								                return;
							 | 
						||
| 
								 | 
							
								            }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								            // TODO: calculate this just on demand
							 | 
						||
| 
								 | 
							
								            screenCenter = pixelToGeo(CENTER_X + ORIGIN_X, CENTER_Y + ORIGIN_Y);
							 | 
						||
| 
								 | 
							
								            sun = getSunPosition(this.date, screenCenter.latitude, screenCenter.longitude);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								            if (sun.altitude <= 0) {
							 | 
						||
| 
								 | 
							
								                return;
							 | 
						||
| 
								 | 
							
								            }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								            length = 1 / tan(sun.altitude);
							 | 
						||
| 
								 | 
							
								            alpha = length < 5 ? 0.75 : 1 / length * 5;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								            this.direction.x = cos(sun.azimuth) * length;
							 | 
						||
| 
								 | 
							
								            this.direction.y = sin(sun.azimuth) * length;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								            var
							 | 
						||
| 
								 | 
							
								                i, il,
							 | 
						||
| 
								 | 
							
								                item,
							 | 
						||
| 
								 | 
							
								                h, mh,
							 | 
						||
| 
								 | 
							
								                footprint,
							 | 
						||
| 
								 | 
							
								                dataItems = Data.items;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								            context.canvas.style.opacity = alpha / (ZOOM_FACTOR * 2);
							 | 
						||
| 
								 | 
							
								            context.shadowColor = this.blurColor;
							 | 
						||
| 
								 | 
							
								            context.shadowBlur = this.blurSize * (ZOOM_FACTOR / 2);
							 | 
						||
| 
								 | 
							
								            context.fillStyle = this.color;
							 | 
						||
| 
								 | 
							
								            context.beginPath();
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								            for (i = 0, il = dataItems.length; i < il; i++) {
							 | 
						||
| 
								 | 
							
								                item = dataItems[i];
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								                footprint = item.footprint;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								                if (!isVisible(footprint)) {
							 | 
						||
| 
								 | 
							
								                    continue;
							 | 
						||
| 
								 | 
							
								                }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								                // when fading in, use a dynamic height
							 | 
						||
| 
								 | 
							
								                h = item.scale < 1 ? item.height * item.scale : item.height;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								                mh = 0;
							 | 
						||
| 
								 | 
							
								                if (item.minHeight) {
							 | 
						||
| 
								 | 
							
								                    mh = item.scale < 1 ? item.minHeight * item.scale : item.minHeight;
							 | 
						||
| 
								 | 
							
								                }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								                switch (item.shape) {
							 | 
						||
| 
								 | 
							
								                    case 'cylinder':
							 | 
						||
| 
								 | 
							
								                        Cylinder.shadow(context, item.center, item.radius, item.radius, h, mh);
							 | 
						||
| 
								 | 
							
								                        break;
							 | 
						||
| 
								 | 
							
								                    case 'cone':
							 | 
						||
| 
								 | 
							
								                        Cylinder.shadow(context, item.center, item.radius, 0, h, mh);
							 | 
						||
| 
								 | 
							
								                        break;
							 | 
						||
| 
								 | 
							
								                    case 'dome':
							 | 
						||
| 
								 | 
							
								                        Cylinder.shadow(context, item.center, item.radius, item.radius / 2, h, mh);
							 | 
						||
| 
								 | 
							
								                        break;
							 | 
						||
| 
								 | 
							
								                    case 'sphere':
							 | 
						||
| 
								 | 
							
								                        Cylinder.shadow(context, item.center, item.radius, item.radius, h, mh);
							 | 
						||
| 
								 | 
							
								                        break;
							 | 
						||
| 
								 | 
							
								                    case 'pyramid':
							 | 
						||
| 
								 | 
							
								                        Pyramid.shadow(context, footprint, item.center, h, mh);
							 | 
						||
| 
								 | 
							
								                        break;
							 | 
						||
| 
								 | 
							
								                    default:
							 | 
						||
| 
								 | 
							
								                        Block.shadow(context, footprint, item.holes, h, mh);
							 | 
						||
| 
								 | 
							
								                }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								                switch (item.roofShape) {
							 | 
						||
| 
								 | 
							
								                    case 'cone':
							 | 
						||
| 
								 | 
							
								                        Cylinder.shadow(context, item.center, item.radius, 0, h + item.roofHeight, h);
							 | 
						||
| 
								 | 
							
								                        break;
							 | 
						||
| 
								 | 
							
								                    case 'dome':
							 | 
						||
| 
								 | 
							
								                        Cylinder.shadow(context, item.center, item.radius, item.radius / 2, h + item.roofHeight, h);
							 | 
						||
| 
								 | 
							
								                        break;
							 | 
						||
| 
								 | 
							
								                    case 'pyramid':
							 | 
						||
| 
								 | 
							
								                        Pyramid.shadow(context, footprint, item.center, h + item.roofHeight, h);
							 | 
						||
| 
								 | 
							
								                        break;
							 | 
						||
| 
								 | 
							
								                }
							 | 
						||
| 
								 | 
							
								            }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								            context.closePath();
							 | 
						||
| 
								 | 
							
								            context.fill();
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								            context.shadowBlur = null;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								            // now draw all the footprints as negative clipping mask
							 | 
						||
| 
								 | 
							
								            context.globalCompositeOperation = 'destination-out';
							 | 
						||
| 
								 | 
							
								            context.beginPath();
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								            for (i = 0, il = dataItems.length; i < il; i++) {
							 | 
						||
| 
								 | 
							
								                item = dataItems[i];
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								                footprint = item.footprint;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								                if (!isVisible(footprint)) {
							 | 
						||
| 
								 | 
							
								                    continue;
							 | 
						||
| 
								 | 
							
								                }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								                // if object is hovered, there is no need to clip it's footprint
							 | 
						||
| 
								 | 
							
								                if (item.minHeight) {
							 | 
						||
| 
								 | 
							
								                    continue;
							 | 
						||
| 
								 | 
							
								                }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								                switch (item.shape) {
							 | 
						||
| 
								 | 
							
								                    case 'cylinder':
							 | 
						||
| 
								 | 
							
								                    case 'cone':
							 | 
						||
| 
								 | 
							
								                    case 'dome':
							 | 
						||
| 
								 | 
							
								                        Cylinder.shadowMask(context, item.center, item.radius);
							 | 
						||
| 
								 | 
							
								                        break;
							 | 
						||
| 
								 | 
							
								                    default:
							 | 
						||
| 
								 | 
							
								                        Block.shadowMask(context, footprint, item.holes);
							 | 
						||
| 
								 | 
							
								                }
							 | 
						||
| 
								 | 
							
								            }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								            context.fillStyle = '#00ff00';
							 | 
						||
| 
								 | 
							
								            context.fill();
							 | 
						||
| 
								 | 
							
								            context.globalCompositeOperation = 'source-over';
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								    };
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								//****** file: HitAreas.js ******
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    var HitAreas = {
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        _idMapping: [null],
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        reset: function () {
							 | 
						||
| 
								 | 
							
								            this._idMapping = [null];
							 | 
						||
| 
								 | 
							
								        },
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        render: function () {
							 | 
						||
| 
								 | 
							
								            if (this._timer) {
							 | 
						||
| 
								 | 
							
								                return;
							 | 
						||
| 
								 | 
							
								            }
							 | 
						||
| 
								 | 
							
								            var self = this;
							 | 
						||
| 
								 | 
							
								            this._timer = setTimeout(function () {
							 | 
						||
| 
								 | 
							
								                self._timer = null;
							 | 
						||
| 
								 | 
							
								                self._render();
							 | 
						||
| 
								 | 
							
								            }, 500);
							 | 
						||
| 
								 | 
							
								        },
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        _render: function () {
							 | 
						||
| 
								 | 
							
								            var context = this.context;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								            context.clearRect(0, 0, WIDTH, HEIGHT);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								            // show on high zoom levels only and avoid rendering during zoom
							 | 
						||
| 
								 | 
							
								            if (ZOOM < MIN_ZOOM || isZooming) {
							 | 
						||
| 
								 | 
							
								                return;
							 | 
						||
| 
								 | 
							
								            }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								            var
							 | 
						||
| 
								 | 
							
								                item,
							 | 
						||
| 
								 | 
							
								                h, mh,
							 | 
						||
| 
								 | 
							
								                sortCam = {x: CAM_X + ORIGIN_X, y: CAM_Y + ORIGIN_Y},
							 | 
						||
| 
								 | 
							
								                footprint,
							 | 
						||
| 
								 | 
							
								                color,
							 | 
						||
| 
								 | 
							
								                dataItems = Data.items;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								            dataItems.sort(function (a, b) {
							 | 
						||
| 
								 | 
							
								                return (a.minHeight - b.minHeight) || getDistance(b.center, sortCam) - getDistance(a.center, sortCam) || (b.height - a.height);
							 | 
						||
| 
								 | 
							
								            });
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								            for (var i = 0, il = dataItems.length; i < il; i++) {
							 | 
						||
| 
								 | 
							
								                item = dataItems[i];
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								                if (!(color = item.hitColor)) {
							 | 
						||
| 
								 | 
							
								                    continue;
							 | 
						||
| 
								 | 
							
								                }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								                footprint = item.footprint;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								                if (!isVisible(footprint)) {
							 | 
						||
| 
								 | 
							
								                    continue;
							 | 
						||
| 
								 | 
							
								                }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								                h = item.height;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								                mh = 0;
							 | 
						||
| 
								 | 
							
								                if (item.minHeight) {
							 | 
						||
| 
								 | 
							
								                    mh = item.minHeight;
							 | 
						||
| 
								 | 
							
								                }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								                switch (item.shape) {
							 | 
						||
| 
								 | 
							
								                    case 'cylinder':
							 | 
						||
| 
								 | 
							
								                        Cylinder.hitArea(context, item.center, item.radius, item.radius, h, mh, color);
							 | 
						||
| 
								 | 
							
								                        break;
							 | 
						||
| 
								 | 
							
								                    case 'cone':
							 | 
						||
| 
								 | 
							
								                        Cylinder.hitArea(context, item.center, item.radius, 0, h, mh, color);
							 | 
						||
| 
								 | 
							
								                        break;
							 | 
						||
| 
								 | 
							
								                    case 'dome':
							 | 
						||
| 
								 | 
							
								                        Cylinder.hitArea(context, item.center, item.radius, item.radius / 2, h, mh, color);
							 | 
						||
| 
								 | 
							
								                        break;
							 | 
						||
| 
								 | 
							
								                    case 'sphere':
							 | 
						||
| 
								 | 
							
								                        Cylinder.hitArea(context, item.center, item.radius, item.radius, h, mh, color);
							 | 
						||
| 
								 | 
							
								                        break;
							 | 
						||
| 
								 | 
							
								                    case 'pyramid':
							 | 
						||
| 
								 | 
							
								                        Pyramid.hitArea(context, footprint, item.center, h, mh, color);
							 | 
						||
| 
								 | 
							
								                        break;
							 | 
						||
| 
								 | 
							
								                    default:
							 | 
						||
| 
								 | 
							
								                        Block.hitArea(context, footprint, item.holes, h, mh, color);
							 | 
						||
| 
								 | 
							
								                }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								                switch (item.roofShape) {
							 | 
						||
| 
								 | 
							
								                    case 'cone':
							 | 
						||
| 
								 | 
							
								                        Cylinder.hitArea(context, item.center, item.radius, 0, h + item.roofHeight, h, color);
							 | 
						||
| 
								 | 
							
								                        break;
							 | 
						||
| 
								 | 
							
								                    case 'dome':
							 | 
						||
| 
								 | 
							
								                        Cylinder.hitArea(context, item.center, item.radius, item.radius / 2, h + item.roofHeight, h, color);
							 | 
						||
| 
								 | 
							
								                        break;
							 | 
						||
| 
								 | 
							
								                    case 'pyramid':
							 | 
						||
| 
								 | 
							
								                        Pyramid.hitArea(context, footprint, item.center, h + item.roofHeight, h, color);
							 | 
						||
| 
								 | 
							
								                        break;
							 | 
						||
| 
								 | 
							
								                }
							 | 
						||
| 
								 | 
							
								            }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								            this._imageData = this.context.getImageData(0, 0, WIDTH, HEIGHT).data;
							 | 
						||
| 
								 | 
							
								        },
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        getIdFromXY: function (x, y) {
							 | 
						||
| 
								 | 
							
								            var imageData = this._imageData;
							 | 
						||
| 
								 | 
							
								            if (!imageData) {
							 | 
						||
| 
								 | 
							
								                return;
							 | 
						||
| 
								 | 
							
								            }
							 | 
						||
| 
								 | 
							
								            var pos = 4 * ((y | 0) * WIDTH + (x | 0));
							 | 
						||
| 
								 | 
							
								            var index = imageData[pos] | (imageData[pos + 1] << 8) | (imageData[pos + 2] << 16);
							 | 
						||
| 
								 | 
							
								            return this._idMapping[index];
							 | 
						||
| 
								 | 
							
								        },
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        idToColor: function (id) {
							 | 
						||
| 
								 | 
							
								            var index = this._idMapping.indexOf(id);
							 | 
						||
| 
								 | 
							
								            if (index === -1) {
							 | 
						||
| 
								 | 
							
								                this._idMapping.push(id);
							 | 
						||
| 
								 | 
							
								                index = this._idMapping.length - 1;
							 | 
						||
| 
								 | 
							
								            }
							 | 
						||
| 
								 | 
							
								            var r = index & 0xff;
							 | 
						||
| 
								 | 
							
								            var g = (index >> 8) & 0xff;
							 | 
						||
| 
								 | 
							
								            var b = (index >> 16) & 0xff;
							 | 
						||
| 
								 | 
							
								            return 'rgb(' + [r, g, b].join(',') + ')';
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								    };
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								//****** file: Debug.js ******
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    var Debug = {
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        point: function (x, y, color, size) {
							 | 
						||
| 
								 | 
							
								            var context = this.context;
							 | 
						||
| 
								 | 
							
								            context.fillStyle = color || '#ffcc00';
							 | 
						||
| 
								 | 
							
								            context.beginPath();
							 | 
						||
| 
								 | 
							
								            context.arc(x, y, size || 3, 0, 2 * PI);
							 | 
						||
| 
								 | 
							
								            context.closePath();
							 | 
						||
| 
								 | 
							
								            context.fill();
							 | 
						||
| 
								 | 
							
								        },
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        line: function (ax, ay, bx, by, color) {
							 | 
						||
| 
								 | 
							
								            var context = this.context;
							 | 
						||
| 
								 | 
							
								            context.strokeStyle = color || '#ffcc00';
							 | 
						||
| 
								 | 
							
								            context.beginPath();
							 | 
						||
| 
								 | 
							
								            context.moveTo(ax, ay);
							 | 
						||
| 
								 | 
							
								            context.lineTo(bx, by);
							 | 
						||
| 
								 | 
							
								            context.closePath();
							 | 
						||
| 
								 | 
							
								            context.stroke();
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								    };
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								//****** file: Layers.js ******
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    var animTimer;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    function fadeIn() {
							 | 
						||
| 
								 | 
							
								        if (animTimer) {
							 | 
						||
| 
								 | 
							
								            return;
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        animTimer = setInterval(function () {
							 | 
						||
| 
								 | 
							
								            var dataItems = Data.items,
							 | 
						||
| 
								 | 
							
								                isNeeded = false;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								            for (var i = 0, il = dataItems.length; i < il; i++) {
							 | 
						||
| 
								 | 
							
								                if (dataItems[i].scale < 1) {
							 | 
						||
| 
								 | 
							
								                    dataItems[i].scale += 0.5 * 0.2; // amount*easing
							 | 
						||
| 
								 | 
							
								                    if (dataItems[i].scale > 1) {
							 | 
						||
| 
								 | 
							
								                        dataItems[i].scale = 1;
							 | 
						||
| 
								 | 
							
								                    }
							 | 
						||
| 
								 | 
							
								                    isNeeded = true;
							 | 
						||
| 
								 | 
							
								                }
							 | 
						||
| 
								 | 
							
								            }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								            Layers.render();
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								            if (!isNeeded) {
							 | 
						||
| 
								 | 
							
								                clearInterval(animTimer);
							 | 
						||
| 
								 | 
							
								                animTimer = null;
							 | 
						||
| 
								 | 
							
								            }
							 | 
						||
| 
								 | 
							
								        }, 33);
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    var Layers = {
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        container: document.createElement('DIV'),
							 | 
						||
| 
								 | 
							
								        items: [],
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        init: function () {
							 | 
						||
| 
								 | 
							
								            this.container.style.pointerEvents = 'none';
							 | 
						||
| 
								 | 
							
								            this.container.style.position = 'absolute';
							 | 
						||
| 
								 | 
							
								            this.container.style.left = 0;
							 | 
						||
| 
								 | 
							
								            this.container.style.top = 0;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								            // TODO: improve this to .setContext(context)
							 | 
						||
| 
								 | 
							
								            Shadows.context = this.createContext(this.container);
							 | 
						||
| 
								 | 
							
								            Simplified.context = this.createContext(this.container);
							 | 
						||
| 
								 | 
							
								            Buildings.context = this.createContext(this.container);
							 | 
						||
| 
								 | 
							
								            HitAreas.context = this.createContext();
							 | 
						||
| 
								 | 
							
								//    Debug.context      = this.createContext(this.container);
							 | 
						||
| 
								 | 
							
								        },
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        render: function (quick) {
							 | 
						||
| 
								 | 
							
								            requestAnimFrame(function () {
							 | 
						||
| 
								 | 
							
								                if (!quick) {
							 | 
						||
| 
								 | 
							
								                    Shadows.render();
							 | 
						||
| 
								 | 
							
								                    Simplified.render();
							 | 
						||
| 
								 | 
							
								                    HitAreas.render();
							 | 
						||
| 
								 | 
							
								                }
							 | 
						||
| 
								 | 
							
								                Buildings.render();
							 | 
						||
| 
								 | 
							
								            });
							 | 
						||
| 
								 | 
							
								        },
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        createContext: function (container) {
							 | 
						||
| 
								 | 
							
								            var canvas = document.createElement('CANVAS');
							 | 
						||
| 
								 | 
							
								            canvas.style.transform = 'translate3d(0, 0, 0)'; // turn on hw acceleration
							 | 
						||
| 
								 | 
							
								            canvas.style.imageRendering = 'optimizeSpeed';
							 | 
						||
| 
								 | 
							
								            canvas.style.position = 'absolute';
							 | 
						||
| 
								 | 
							
								            canvas.style.left = 0;
							 | 
						||
| 
								 | 
							
								            canvas.style.top = 0;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								            var context = canvas.getContext('2d');
							 | 
						||
| 
								 | 
							
								            context.lineCap = 'round';
							 | 
						||
| 
								 | 
							
								            context.lineJoin = 'round';
							 | 
						||
| 
								 | 
							
								            context.lineWidth = 1;
							 | 
						||
| 
								 | 
							
								            context.imageSmoothingEnabled = false;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								            this.items.push(canvas);
							 | 
						||
| 
								 | 
							
								            if (container) {
							 | 
						||
| 
								 | 
							
								                container.appendChild(canvas);
							 | 
						||
| 
								 | 
							
								            }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								            return context;
							 | 
						||
| 
								 | 
							
								        },
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        appendTo: function (parentNode) {
							 | 
						||
| 
								 | 
							
								            parentNode.appendChild(this.container);
							 | 
						||
| 
								 | 
							
								        },
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        remove: function () {
							 | 
						||
| 
								 | 
							
								            this.container.parentNode.removeChild(this.container);
							 | 
						||
| 
								 | 
							
								        },
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        setSize: function (width, height) {
							 | 
						||
| 
								 | 
							
								            for (var i = 0, il = this.items.length; i < il; i++) {
							 | 
						||
| 
								 | 
							
								                this.items[i].width = width;
							 | 
						||
| 
								 | 
							
								                this.items[i].height = height;
							 | 
						||
| 
								 | 
							
								            }
							 | 
						||
| 
								 | 
							
								        },
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        // usually called after move: container jumps by move delta, cam is reset
							 | 
						||
| 
								 | 
							
								        setPosition: function (x, y) {
							 | 
						||
| 
								 | 
							
								            this.container.style.left = x + 'px';
							 | 
						||
| 
								 | 
							
								            this.container.style.top = y + 'px';
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								    };
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    Layers.init();
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								//****** file: adapter.js ******
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    function setOrigin(origin) {
							 | 
						||
| 
								 | 
							
								        ORIGIN_X = origin.x;
							 | 
						||
| 
								 | 
							
								        ORIGIN_Y = origin.y;
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    function moveCam(offset) {
							 | 
						||
| 
								 | 
							
								        CAM_X = CENTER_X + offset.x;
							 | 
						||
| 
								 | 
							
								        CAM_Y = HEIGHT + offset.y;
							 | 
						||
| 
								 | 
							
								        Layers.render(true);
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    function setSize(size) {
							 | 
						||
| 
								 | 
							
								        WIDTH = size.width;
							 | 
						||
| 
								 | 
							
								        HEIGHT = size.height;
							 | 
						||
| 
								 | 
							
								        CENTER_X = WIDTH / 2 << 0;
							 | 
						||
| 
								 | 
							
								        CENTER_Y = HEIGHT / 2 << 0;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        CAM_X = CENTER_X;
							 | 
						||
| 
								 | 
							
								        CAM_Y = HEIGHT;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        Layers.setSize(WIDTH, HEIGHT);
							 | 
						||
| 
								 | 
							
								        MAX_HEIGHT = CAM_Z - 50;
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    function setZoom(z) {
							 | 
						||
| 
								 | 
							
								        ZOOM = z;
							 | 
						||
| 
								 | 
							
								        if (isLatLngProjection()) {
							 | 
						||
| 
								 | 
							
								            MAP_SIZE = 360 / mapObj.resolution;
							 | 
						||
| 
								 | 
							
								        } else {
							 | 
						||
| 
								 | 
							
								            MAP_SIZE = MAP_TILE_SIZE << ZOOM;
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        var center = pixelToGeo(ORIGIN_X + CENTER_X, ORIGIN_Y + CENTER_Y);
							 | 
						||
| 
								 | 
							
								        var a = geoToPixel(center.latitude, 0);
							 | 
						||
| 
								 | 
							
								        var b = geoToPixel(center.latitude, 1);
							 | 
						||
| 
								 | 
							
								        PIXEL_PER_DEG = b.x - a.x;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        ZOOM_FACTOR = pow(0.95, ZOOM - MIN_ZOOM);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        WALL_COLOR_STR = '' + WALL_COLOR.alpha(ZOOM_FACTOR);
							 | 
						||
| 
								 | 
							
								        ALT_COLOR_STR = '' + ALT_COLOR.alpha(ZOOM_FACTOR);
							 | 
						||
| 
								 | 
							
								        ROOF_COLOR_STR = '' + ROOF_COLOR.alpha(ZOOM_FACTOR);
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    function onResize(e) {
							 | 
						||
| 
								 | 
							
								        setSize(e);
							 | 
						||
| 
								 | 
							
								        Layers.render();
							 | 
						||
| 
								 | 
							
								        Data.update();
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    function onMoveEnd(e) {
							 | 
						||
| 
								 | 
							
								        Layers.render();
							 | 
						||
| 
								 | 
							
								        Data.update(); // => fadeIn() => Layers.render()
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    function onZoomStart() {
							 | 
						||
| 
								 | 
							
								        isZooming = true;
							 | 
						||
| 
								 | 
							
								// effectively clears because of isZooming flag
							 | 
						||
| 
								 | 
							
								// TODO: introduce explicit clear()
							 | 
						||
| 
								 | 
							
								        Layers.render();
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    function onZoomEnd(e) {
							 | 
						||
| 
								 | 
							
								        isZooming = false;
							 | 
						||
| 
								 | 
							
								        setZoom(e.zoom);
							 | 
						||
| 
								 | 
							
								        Data.update(); // => fadeIn()
							 | 
						||
| 
								 | 
							
								        Layers.render();
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								//****** file: SuperMap.js ******
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								// based on a pull request from Jérémy Judéaux (https://github.com/Volune)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    var parent = SuperMap.Layer.prototype;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    var transformExtent = function (extent) {
							 | 
						||
| 
								 | 
							
								        if (isLatLngProjection()) {
							 | 
						||
| 
								 | 
							
								            return {
							 | 
						||
| 
								 | 
							
								                left: -180,
							 | 
						||
| 
								 | 
							
								                bottom: -90,
							 | 
						||
| 
								 | 
							
								                right: 180,
							 | 
						||
| 
								 | 
							
								                top: 90
							 | 
						||
| 
								 | 
							
								            }
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								        return extent;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    };
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    var osmb = function (map) {
							 | 
						||
| 
								 | 
							
								        this.offset = {x: 0, y: 0}; // cumulative cam offset during moveBy()
							 | 
						||
| 
								 | 
							
								        bindMap(map);
							 | 
						||
| 
								 | 
							
								        parent.initialize.call(this, this.name, {projection: 'EPSG:900913'});
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        if (map) {
							 | 
						||
| 
								 | 
							
								            map.addLayer(this);
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								    };
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    //bind map to global
							 | 
						||
| 
								 | 
							
								    function bindMap(map) {
							 | 
						||
| 
								 | 
							
								        mapObj = map;
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    var proto = osmb.prototype = new SuperMap.Layer();
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    proto.name = 'OSM Buildings';
							 | 
						||
| 
								 | 
							
								    proto.attribution = ATTRIBUTION;
							 | 
						||
| 
								 | 
							
								    proto.isBaseLayer = false;
							 | 
						||
| 
								 | 
							
								    proto.alwaysInRange = true;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    proto.addTo = function (map) {
							 | 
						||
| 
								 | 
							
								        this.setMap(map);
							 | 
						||
| 
								 | 
							
								        return this;
							 | 
						||
| 
								 | 
							
								    };
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    proto.setOrigin = function () {
							 | 
						||
| 
								 | 
							
								        var map = this.map,
							 | 
						||
| 
								 | 
							
								            origin = map.getLonLatFromPixel(new SuperMap.Pixel(0, 0)),
							 | 
						||
| 
								 | 
							
								            res = map.resolution;
							 | 
						||
| 
								 | 
							
								        var ext = transformExtent(this.maxExtent);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        var x = (origin.lon - ext.left) / res << 0,
							 | 
						||
| 
								 | 
							
								            y = (ext.top - origin.lat) / res << 0;
							 | 
						||
| 
								 | 
							
								        setOrigin({x: x, y: y});
							 | 
						||
| 
								 | 
							
								    };
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    proto.setMap = function (map) {
							 | 
						||
| 
								 | 
							
								        if (!this.map) {
							 | 
						||
| 
								 | 
							
								            parent.setMap.call(this, map);
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								        Layers.appendTo(this.div);
							 | 
						||
| 
								 | 
							
								        setSize({width: map.size.w, height: map.size.h});
							 | 
						||
| 
								 | 
							
								        setZoom(map.zoom);
							 | 
						||
| 
								 | 
							
								        this.setOrigin();
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        var layerProjection = this.projection;
							 | 
						||
| 
								 | 
							
								        map.events.register('click', map, function (e) {
							 | 
						||
| 
								 | 
							
								            var id = HitAreas.getIdFromXY(e.xy.x, e.xy.y);
							 | 
						||
| 
								 | 
							
								            if (id) {
							 | 
						||
| 
								 | 
							
								                var geo = map.getLonLatFromPixel(e.xy).transform(layerProjection, this.projection);
							 | 
						||
| 
								 | 
							
								                onClick({feature: id, lat: geo.lat, lon: geo.lon});
							 | 
						||
| 
								 | 
							
								            }
							 | 
						||
| 
								 | 
							
								        });
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        Data.update();
							 | 
						||
| 
								 | 
							
								    };
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    proto.removeMap = function (map) {
							 | 
						||
| 
								 | 
							
								        Layers.remove();
							 | 
						||
| 
								 | 
							
								        parent.removeMap.call(this, map);
							 | 
						||
| 
								 | 
							
								        this.map = null;
							 | 
						||
| 
								 | 
							
								    };
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    proto.onMapResize = function () {
							 | 
						||
| 
								 | 
							
								        var map = this.map;
							 | 
						||
| 
								 | 
							
								        parent.onMapResize.call(this);
							 | 
						||
| 
								 | 
							
								        onResize({width: map.size.w, height: map.size.h});
							 | 
						||
| 
								 | 
							
								    };
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    proto.moveTo = function (bounds, zoomChanged, isDragging) {
							 | 
						||
| 
								 | 
							
								        var
							 | 
						||
| 
								 | 
							
								            map = this.map,
							 | 
						||
| 
								 | 
							
								            res = parent.moveTo.call(this, bounds, zoomChanged, isDragging);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        if (!isDragging) {
							 | 
						||
| 
								 | 
							
								            var
							 | 
						||
| 
								 | 
							
								                offsetLeft = parseInt(map.layerContainerDiv.style.left, 10),
							 | 
						||
| 
								 | 
							
								                offsetTop = parseInt(map.layerContainerDiv.style.top, 10);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								            this.div.style.left = -offsetLeft + 'px';
							 | 
						||
| 
								 | 
							
								            this.div.style.top = -offsetTop + 'px';
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        this.setOrigin();
							 | 
						||
| 
								 | 
							
								        this.offset.x = 0;
							 | 
						||
| 
								 | 
							
								        this.offset.y = 0;
							 | 
						||
| 
								 | 
							
								        moveCam(this.offset);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        if (zoomChanged) {
							 | 
						||
| 
								 | 
							
								            onZoomEnd({zoom: map.zoom});
							 | 
						||
| 
								 | 
							
								        } else {
							 | 
						||
| 
								 | 
							
								            onMoveEnd();
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        return res;
							 | 
						||
| 
								 | 
							
								    };
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    proto.moveByPx = function (dx, dy) {
							 | 
						||
| 
								 | 
							
								        this.offset.x += dx;
							 | 
						||
| 
								 | 
							
								        this.offset.y += dy;
							 | 
						||
| 
								 | 
							
								        var res = parent.moveByPx.call(this, dx, dy);
							 | 
						||
| 
								 | 
							
								        moveCam(this.offset);
							 | 
						||
| 
								 | 
							
								        return res;
							 | 
						||
| 
								 | 
							
								    };
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								//****** file: public.js ******
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    proto.style = function (style) {
							 | 
						||
| 
								 | 
							
								        style = style || {};
							 | 
						||
| 
								 | 
							
								        var color;
							 | 
						||
| 
								 | 
							
								        if ((color = style.color || style.wallColor)) {
							 | 
						||
| 
								 | 
							
								            WALL_COLOR = Color.parse(color);
							 | 
						||
| 
								 | 
							
								            WALL_COLOR_STR = '' + WALL_COLOR.alpha(ZOOM_FACTOR);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								            ALT_COLOR = WALL_COLOR.lightness(0.8);
							 | 
						||
| 
								 | 
							
								            ALT_COLOR_STR = '' + ALT_COLOR.alpha(ZOOM_FACTOR);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								            ROOF_COLOR = WALL_COLOR.lightness(1.2);
							 | 
						||
| 
								 | 
							
								            ROOF_COLOR_STR = '' + ROOF_COLOR.alpha(ZOOM_FACTOR);
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        if (style.roofColor) {
							 | 
						||
| 
								 | 
							
								            ROOF_COLOR = Color.parse(style.roofColor);
							 | 
						||
| 
								 | 
							
								            ROOF_COLOR_STR = '' + ROOF_COLOR.alpha(ZOOM_FACTOR);
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        if (style.shadows !== undefined) {
							 | 
						||
| 
								 | 
							
								            Shadows.enabled = !!style.shadows;
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        Layers.render();
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        return this;
							 | 
						||
| 
								 | 
							
								    };
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    proto.date = function (date) {
							 | 
						||
| 
								 | 
							
								        Shadows.date = date;
							 | 
						||
| 
								 | 
							
								        Shadows.render();
							 | 
						||
| 
								 | 
							
								        return this;
							 | 
						||
| 
								 | 
							
								    };
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    proto.load = function (url) {
							 | 
						||
| 
								 | 
							
								        Data.load(url);
							 | 
						||
| 
								 | 
							
								        return this;
							 | 
						||
| 
								 | 
							
								    };
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    proto.set = function (data) {
							 | 
						||
| 
								 | 
							
								        Data.set(data);
							 | 
						||
| 
								 | 
							
								        return this;
							 | 
						||
| 
								 | 
							
								    };
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    var onEach = function () {
							 | 
						||
| 
								 | 
							
								    };
							 | 
						||
| 
								 | 
							
								    proto.each = function (handler) {
							 | 
						||
| 
								 | 
							
								        onEach = function (payload) {
							 | 
						||
| 
								 | 
							
								            return handler(payload);
							 | 
						||
| 
								 | 
							
								        };
							 | 
						||
| 
								 | 
							
								        return this;
							 | 
						||
| 
								 | 
							
								    };
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    var onClick = function () {
							 | 
						||
| 
								 | 
							
								    };
							 | 
						||
| 
								 | 
							
								    proto.click = function (handler) {
							 | 
						||
| 
								 | 
							
								        onClick = function (payload) {
							 | 
						||
| 
								 | 
							
								            return handler(payload);
							 | 
						||
| 
								 | 
							
								        };
							 | 
						||
| 
								 | 
							
								        return this;
							 | 
						||
| 
								 | 
							
								    };
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    osmb.VERSION = VERSION;
							 | 
						||
| 
								 | 
							
								    osmb.ATTRIBUTION = ATTRIBUTION;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								//****** file: suffix.js ******
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    global.OSMBuildings = osmb;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								}(this));
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 |