/bin/html/map/mapapi.renderer.js
JavaScript | 489 lines | 429 code | 34 blank | 26 comment | 105 complexity | 09177ee1dddd3f91d9ca5c59aa332a52 MD5 | raw file
1/** 2* License and Terms of Use 3* 4* Copyright (c) 2011 SignpostMarv 5* 6* Permission is hereby granted, free of charge, to any person obtaining a copy 7* of this software and associated documentation files (the "Software"), to deal 8* in the Software without restriction, including without limitation the rights 9* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10* copies of the Software, and to permit persons to whom the Software is 11* furnished to do so, subject to the following conditions: 12* 13* The above copyright notice and this permission notice shall be included in 14* all copies or substantial portions of the Software. 15* 16* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22* THE SOFTWARE. 23*/ 24(function(window, undefined){ 25 window['mapapi'] = window['mapapi'] || {}; 26 var 27 document = window['document'], 28 EventTarget = window['EventTarget'], 29 mapapi = window['mapapi'], 30 gridPoint = mapapi['gridPoint'], 31 bounds = mapapi['bounds'], 32 size = mapapi['size'], 33 empty = mapapi['utils']['empty'], 34 shape = mapapi['shape'] 35 ; 36 if(EventTarget == undefined){ 37 throw 'EventTarget not loaded'; 38 } 39 40 function each(array, cb){ 41 for(var i=0;i<array.length;++i){ 42 cb(array[i],i); 43 } 44 } 45 46 function dblclick_handler(e){ 47 var 48 obj = this, 49 point = e['pos'], 50 zoom = obj['zoom']() - 1 51 ; 52 if(this['smoothZoom']()){ 53 this['animate']({ 54 'zoom' : zoom, 55 'focus' : point 56 }, .5); 57 }else{ 58 obj['zoom'](zoom); 59 obj['focus'](point); 60 } 61 } 62 function dragpan(e){ 63 var 64 obj = this, 65 pos = e['to'] 66 ; 67 obj['focus'](pos); 68 } 69 70/** 71* @constructor 72*/ 73 function renderer(){ 74 var 75 obj = this 76 ; 77 EventTarget['call'](this); 78 79 obj['opts'] = {'shapes':new mapapi['shapeManager']}; 80 obj['_focus'] = new gridPoint(0,0); 81 } 82 83 renderer.prototype = new EventTarget(); 84 renderer.prototype['constructor'] = renderer; 85 renderer.prototype['browserSupported'] = false; 86 87 renderer.prototype['options'] = function(options){ 88 var 89 obj = this, 90 options = options || {}, 91 opts = obj['opts'], 92 container = options['container'], 93 hasFunc = ['minZoom', 'maxZoom', 'scrollWheelZoom', 'smoothZoom', 'draggable', 'dblclickZoom', 'zoom', 'focus', 'panUnitLR', 'panUnitUD'], 94 checkFunc 95 ; 96 97 options['minZoom'] = options['minZoom'] || 0; 98 options['maxZoom'] = options['maxZoom'] || 0; 99 options['panUnitLR'] = options['panUnitLR'] || 0; 100 options['panUnitUD'] = options['panUnitUD'] || 0; 101 102 for(var i=0;i<hasFunc.length;++i){ 103 var 104 checkFunc = hasFunc[i] 105 ; 106 if(options[checkFunc] != undefined){ 107 obj[checkFunc](options[checkFunc]); 108 } 109 } 110 111 obj['addListener']('drag', dragpan); 112 obj['addListener']('click', function(e){ 113 opts['shapes']['click'](e['pos']); 114 }); 115 116 if(container){ 117 if(!container['appendChild']){ 118 throw 'Container is invalid'; 119 }else{ 120 opts['container'] = container; 121 if(obj['contentNode']){ 122 obj['contentNode']['style']['width'] = '100%'; 123 obj['contentNode']['style']['height'] = '100%'; 124 empty(container)['appendChild'](obj['contentNode']); 125 } 126 } 127 } 128 } 129 130 renderer.prototype['minZoom'] = function(value){ 131 var 132 opts = this['opts'] 133 ; 134 if(value != undefined){ 135 opts['minZoom'] = Math.max(0, value); 136 } 137 return opts['minZoom']; 138 }; 139 140 renderer.prototype['maxZoom'] = function(value){ 141 var 142 opts = this['opts'] 143 ; 144 if(value != undefined){ 145 opts['maxZoom'] = Math.max(this.minZoom() + 1, value); 146 } 147 return opts['maxZoom']; 148 } 149 150 renderer.prototype['zoom'] = function(value){ 151 var 152 obj = this, 153 opts = obj['opts'] 154 ; 155 if(value != undefined){ 156 opts['zoom'] = Math.min(Math.max(value, obj['minZoom']()), obj['maxZoom']()); 157 } 158 return opts['zoom']; 159 } 160 161 renderer.prototype['panUnitUD'] = function(value){ 162 var 163 opts = this['opts'] 164 ; 165 if(value){ 166 opts['panUnitUD'] = Math.max(value, 1); 167 } 168 return opts['panUnitUD'] * Math.pow(2, this['zoom']()); 169 } 170 171 renderer.prototype['panUnitLR'] = function(value){ 172 var 173 opts = this['opts'] 174 ; 175 if(value){ 176 opts['panUnitLR'] = Math.max(value, 1); 177 } 178 return opts['panUnitLR'] * Math.pow(2, this['zoom']()); 179 } 180 181 renderer.prototype['panTo'] = function(pos, a){ 182 if(typeof pos == 'number' && typeof a == 'number'){ 183 pos = new gridPoint(pos, a); 184 }else if(typeof pos == 'object' && typeof pos['x'] == 'number' && typeof pos['y'] == 'number'){ 185 pos = new gridPoint(pos['x'], pos['y']); 186 } 187 if(pos instanceof gridPoint){ 188 if(this['bounds']()['isWithin'](pos)){ 189 this['animate']({ 190 'focus' : pos 191 }, .5); 192 }else{ 193 this['focus'](pos); 194 } 195 } 196 } 197 198 renderer.prototype['panUp'] = function(){ 199 var pos = this.focus(); 200 this.panTo(pos['x'], pos['y'] + this.panUnitUD()); 201 } 202 203 renderer.prototype['panDown'] = function(){ 204 var pos = this.focus(); 205 this.panTo(pos['x'], pos['y'] - this.panUnitUD()); 206 } 207 208 renderer.prototype['panLeft'] = function(){ 209 var pos = this.focus(); 210 this.panTo(pos['x'] - this.panUnitLR(), pos['y']); 211 } 212 213 renderer.prototype['panRight'] = function(){ 214 var pos = this.focus(); 215 this.panTo(pos['x'] + this.panUnitLR(), pos['y']); 216 } 217 218 renderer.prototype['scrollWheelZoom'] = function(flag){ 219 var 220 opts = this['opts'] 221 ; 222 if(flag != undefined){ 223 opts['scrollWheelZoom'] = !!flag; 224 } 225 return opts['scrollWheelZoom']; 226 } 227 228 renderer.prototype['smoothZoom'] = function(flag){ 229 var 230 obj = this, 231 opts = obj['opts'] 232 ; 233 if(flag != undefined){ 234 opts['smoothZoom'] = !!flag; 235 } 236 return opts['smoothZoom']; 237 } 238 239 renderer.prototype['draggable'] = function(flag){ 240 if(flag != undefined){ 241 if(flag){ // do stuff to make the map renderer draggable 242 return true; 243 }else{ // do stuff to make it non-draggable 244 return false; 245 } 246 } 247 return flag; // should return from other property 248 } 249 250 renderer.prototype['focus'] = function(pos, zoom, a){ // should return an instance of mapapi.gridPoint 251 if(typeof pos == 'number'){ 252 pos = new mapapi['gridPoint'](pos, zoom); 253 zoom = this['zoom'](); 254 } 255 if(zoom != undefined){ 256 this['zoom'](zoom); 257 } 258 var 259 opts = this['opts'] 260 ; 261 if(pos instanceof mapapi['gridPoint']){ // implementations should do something to update the renderer to the focal point 262 opts['focus'] = pos; 263 this['fire']('focus_changed', {'pos':pos, 'withinBounds' : this['bounds']()['isWithin'](pos)}); 264 } 265 return opts['focus']; 266 } 267 268 renderer.prototype['px2point'] = function(x, y){ 269 var 270 obj = this, 271 content = obj['contentNode'], 272 cWidth = content['width'], 273 cw2 = cWidth / 2.0, 274 cHeight = content['height'], 275 ch2 = cHeight / 2.0, 276 size = obj['tileSize'](), 277 distX = (x - cw2) / size['width'], 278 distY = ((cHeight - y) - ch2) / size['height'], 279 focus = obj['focus']()//, 280 mapX = focus['x'] + distX, 281 mapY = focus['y'] + distY 282 ; 283 return new gridPoint(mapX, mapY); 284 } 285 286 renderer.prototype['point2px'] = function(x, y){ 287 if(x instanceof gridPoint){ 288 y = x['y']; 289 x = x['x']; 290 } 291 var 292 content = this['contentNode'], 293 cWidth = content['clientWidth'], 294 cw2 = cWidth / 2.0, 295 cHeight = content['clientHeight'], 296 ch2 = cHeight / 2.0, 297 size = this['tileSize'](), 298 focus = this['focus'](), 299 fx = focus['x'], 300 fy = focus['y'], 301 diffX = (x - fx) * size['width'], 302 diffY = (y - fy) * size['height'] 303 ; 304 return {'x':cw2 + diffX, 'y':ch2 - diffY}; 305 } 306 307 renderer.prototype['dblclickZoom'] = function(flag){ 308 var 309 obj = this, 310 opts = obj['opts'] 311 ; 312 if(flag != undefined){ 313 opts['dblclickZoom'] = !!flag; 314 if(obj['contentNode']){ 315 if(!!flag){ 316 obj['addListener']('dblclick', dblclick_handler); 317 }else{ 318 obj['removeListener']('dblclick', dblclick_handler); 319 } 320 } 321 } 322 return opts['dblclickZoom']; // should return from other property 323 } 324 325 renderer.prototype['animate'] = function(opts, time){ 326 if(opts == undefined || (opts != undefined && typeof time != 'number')){ 327 return; 328 } 329 var 330 obj = this, 331 time = (!time || time < 0) ? 1 : time, 332 czoom = obj['zoom'](), 333 mnzm = obj['minZoom'](), 334 mxzm = obj['maxZoom'](), 335 cpos = obj['focus'](), 336 gridPoint = mapapi['gridPoint'], 337 zoom, 338 pos, 339 animateOrder 340 ; 341 if(typeof opts == 'number'){ 342 opts = {'zoom':opts}; 343 }else if(opts instanceof gridPoint || (typeof opts == 'object' && typeof opts['x'] == 'number' && typeof opts['y'] == 'number')){ 344 opts = {'focus':(opts instanceof gridPoint) ? opts : new gridPoint(opts['x'],opts['y'])}; 345 } 346 pos = animateOrder = !1; 347 if(opts['zoom'] != undefined){ 348 zoom = (typeof opts['zoom'] == 'number') ? opts['zoom'] : (opts['zoom'] * 1); 349 zoom = (zoom != czoom && zoom >= mnzm && zoom <= mxzm) ? zoom : undefined; 350 } 351 if(opts['focus'] instanceof gridPoint){ 352 pos = (opts['focus']['x'] != cpos['x'] || opts['focus']['y'] != cpos['y']) ? opts['focus'] : !1; 353 } 354 var 355 a = (zoom != undefined), 356 b = !!pos 357 ; 358 if(a || b){ 359 animateOrder = {}; 360 if(zoom != undefined){ 361 animateOrder['zoom'] = zoom; 362 animateOrder['fromZoom'] = czoom; 363 } 364 if(b){ 365 animateOrder['focus'] = pos; 366 animateOrder['fromFocus'] = cpos; 367 } 368 animateOrder['start'] = (new Date().getTime()) / 1000; 369 animateOrder['end'] = animateOrder['start'] + time; 370 obj['animateOrder'] = animateOrder; 371 } 372 } 373 renderer.prototype['doAnimation'] = function(){ 374 var 375 obj = this, 376 ao = obj['animateOrder'] 377 ; 378 if(!ao){ 379 return false; 380 } 381 var 382 a = ao['zoom'], 383 b = ao['fromZoom'], 384 c = ao['focus'], 385 d = ao['fromFocus'], 386 e = ao['start'], 387 f = ao['end'], 388 now,diff,g,h,i 389 ; 390 if(!!ao){ 391 now = (new Date().getTime()) / 1000; 392 diff = (now - e) / (f - e); 393 if(now >= ao['end']){ 394 if(!!c){ 395 obj['focus'](c, !!a ? a : undefined); 396 }else if(!!a){ 397 obj['zoom'](a); 398 } 399 obj['animateOrder'] = !1; 400 return true; 401 }else if(now > ao['start']){ 402 i = (a != undefined) ? (b + ((a - b) * diff)) : undefined; 403 if(!!c){ 404 g = !!c ? (d['x'] + ((c['x'] - d['x']) * diff)) : 0; 405 h = !!c ? (d['y'] + ((c['y'] - d['y']) * diff)) : 0; 406 obj['focus'](g, h, i); 407 }else if(i != undefined){ 408 obj['zoom'](i); 409 } 410 return true; 411 } 412 } 413 return false; 414 } 415 416 renderer.prototype['bounds'] = function(){ 417 var 418 obj = this, 419 content = obj['contentNode'], 420 zoom = obj['zoom'](), 421 zoom_a = .5 + (.5 * (1 - (zoom % 1))), 422 zoom_b = 1 << Math.floor(zoom), 423 focus = obj['focus'](), 424 cWidth = content['width'], 425 cHeight = content['height'], 426 tWidth = (obj.tileSource['size']['width'] * zoom_a) / zoom_b, 427 tHeight = (obj.tileSource['size']['height'] * zoom_a) / zoom_b, 428 wView = Math.ceil(cWidth / tWidth), 429 hView = Math.ceil(cHeight / tHeight), 430 wVhalf = Math.ceil(wView / 2.0), 431 hVhalf = Math.ceil(hView / 2.0) 432 ; 433 return new bounds({'x': focus['x'] - wVhalf, 'y': focus['y'] - hVhalf},{'x': focus['x'] + wVhalf, 'y': focus['y'] + hVhalf}); 434 } 435 436 renderer.prototype['tileSize'] = function(){ 437 var 438 obj = this, 439 zoom = obj['zoom'](), 440 zoom_a = .5 + (.5 * (1 - (zoom % 1))), 441 zoom_b = 1 << Math.floor(zoom), 442 tWidth = (obj['tileSource']['size']['width'] * zoom_a) / zoom_b, 443 tHeight = (obj['tileSource']['size']['height'] * zoom_a) / zoom_b 444 ; 445 return new size(tWidth, tHeight); 446 } 447 448 renderer.prototype['addShape'] = function(){ // bool return indicates whether shape was added 449 var 450 ret = false 451 ; 452 if(shape != undefined){ 453 for(var i=0;i<arguments.length;++i){ 454 if(arguments[i] instanceof shape && this['opts']['shapes']['indexOf'](arguments[i]) < 0){ 455 ret = true; 456 this['opts']['shapes']['push'](arguments[i]); 457 } 458 } 459 } 460 return ret; 461 } 462 463 renderer.prototype['removeShape'] = function(){ 464 var 465 ret = false 466 ; 467 if(shape != undefined){ 468 for(var i=0;i<arguments.length;++i){ 469 if(arguments[i] instanceof shape){ 470 var 471 pos = this['opts']['shapes']['indexOf'](arguments[i]) 472 ; 473 if(pos >= 0){ 474 ret = true; 475 this['opts']['shapes']['splice'](pos,1); 476 } 477 } 478 } 479 } 480 return ret; 481 } 482 483 renderer.prototype['shapes'] = function(){ 484 return this['opts']['shapes']; 485 } 486 487 mapapi['renderer'] = renderer; 488 mapapi['renderers'] = {}; 489})(window);