1 2 /** 3 * @name CeL map function 4 * @fileoverview 5 * 本檔案包含了 map 的 functions。 6 * @since 7 */ 8 9 10 if (typeof CeL === 'function'){ 11 12 /** 13 * 本 module 之 name(id),<span style="text-decoration:line-through;">不設定時會從呼叫時之 path 取得</span>。 14 * @type String 15 * @constant 16 * @inner 17 * @ignore 18 */ 19 var module_name = 'net.map'; 20 21 //=================================================== 22 /** 23 * 若欲 include 整個 module 時,需囊括之 code。 24 * @type Function 25 * @param {Function} library_namespace namespace of library 26 * @param load_arguments 呼叫時之 argument(s) 27 * @return 28 * @name CeL.net.map 29 * @constant 30 * @inner 31 * @ignore 32 */ 33 var code_for_including = function(library_namespace, load_arguments) { 34 35 36 var _module_name = module_name; 37 //requires 38 if (library_namespace.use( [ 'data', 'net.web' ], module_name)) 39 return; 40 // module_name 會被重設 41 module_name = _module_name; 42 43 var XML_node = library_namespace.net.web.XML_node, 44 set_attribute = library_namespace.net.web.set_attribute, 45 remove_all_child = library_namespace.net.web.remove_all_child, 46 set_class = library_namespace.net.web.set_class, 47 split_String_to_Object = library_namespace.data.split_String_to_Object; 48 49 50 /** 51 * null module constructor 52 * @class map 的 functions 53 */ 54 CeL.net.map 55 = function() { 56 // null module constructor 57 }; 58 59 /** 60 * for JSDT: 有 prototype 才會將之當作 Class 61 */ 62 CeL.net.map 63 .prototype = { 64 }; 65 66 67 68 69 70 71 72 73 // init function 74 75 var SL=new Debug.log,sl=function(){SL.log.apply(SL,arguments);},err=function(){SL.err.apply(SL,arguments);},warn=function(){SL.warn.apply(SL,arguments);}; 76 77 var gMap,mapO,mData,lostItem,gLocal,dLoc={tw:[23.7,121]}; // 台灣: 23.7,121 78 79 80 addLoad(function(){ 81 var b=SL.setBoard('log'); 82 if(window.location.protocol=='file:'&&b)b.style.display='block'; // 強制顯示 log 83 84 if(init&&init.run)sl('Page loaded. Prepare to initial..'); 85 86 if(typeof GLatLng=='undefined'){sl('GMap does not loaded.');return;} 87 88 for(var i in dLoc) 89 dLoc[i]=new GLatLng(dLoc[i][0],dLoc[i][1]); 90 91 setTimeout('init(0);',0); 92 }); 93 94 init.run=1; 95 function init(i){ 96 if(!arguments.callee.run)return; 97 98 var m=0,n; 99 switch(i){ 100 case m++: 101 setSize(); 102 if(typeof preLoadMap=='function')preLoadMap(); 103 break; 104 case m++: 105 catchFile.sn='map-files'; 106 catchFile.f=function(url,success,captureId){ 107 sl('Capture '+(success?'succeeded':'failed')+': <a href="'+url+'">'+url+'</a>'); 108 }; 109 catchFile([ 110 'map.js', 111 'map.css' 112 ]); 113 break; 114 case m++: 115 initMap(); 116 initSearch(); 117 if(window.location.protocol=='file:')setTimeout('mapO.removeTM();',3000); // 3000: 適当。隨 client 而有不同。 118 break; 119 case m++: 120 readLoc(); 121 break; 122 case m++: 123 loadMapData(); 124 break; 125 case m++: 126 placeMapItem(); 127 break; 128 case m++: 129 if(typeof additionalFunc=='function')additionalFunc(); 130 break; 131 } 132 m=['Starting initial process. Catch files..','Catch done. Initial all components..','Map loaded. Loading address records..','address loaded. Loading map data..','map data loaded. placing map items..','placing done. Do additional works..','Initial done.']; 133 sl('init: '+m[i]); 134 if(arguments.callee.run && ++i<m.length) 135 setTimeout('init('+i+');',1); 136 } 137 138 function setOverviewMap(i){ 139 var m=mapO.overviewMap,n; 140 if(m && (n=m.getOverviewMap())){ 141 // 因為有時來不及反應,所以放這邊。 142 if(!i)n.addControl(new GMenuMapTypeControl(1)),i=1,setTimeout('setOverviewMap(1);',500); 143 else m.hide(); 144 }else /*m.show(),*/setTimeout('setOverviewMap();',500); 145 } 146 147 148 function initSearch(){ 149 if(typeof google!='undefined' && google.load)google.load("search","1",{callback:function(){ 150 gLocal=new getSearch(function(r){ 151 google.search.LocalSearch.resizeStaticMapUrl(r,100,140); 152 mapO.setLatLng(r.address||r.titleNoFormatting,r.lat,r.lng); 153 sA(r.address,r.titleNoFormatting,r.phone.join('<br/>')+'<br/>'+r.address); 154 // http://code.google.com/apis/ajaxsearch/documentation/reference.html#_class_GlocalResult 155 var h='<div style="background-color:#fef;margin-left:3em;margin-right:3em;padding-top:.5em;padding-bottom:.5em;font-size:.8em;clear:both;margin-bottom:40px;"><b onclick="sA(\''+UnicodeToHTML(r.address,2)+'\',\''+UnicodeToHTML(r.titleNoFormatting,2)+'\',\''+UnicodeToHTML(r.phone.join('<br/>')+'<br/>'+r.address,2)+'\');" title="'+r.titleNoFormatting+'" style="color:#94e;cursor:pointer;"><img style="margin-top:-.5em;float:left;margin-right:1em;" src="'+r.staticMapUrl+'"/>'+r.title 156 +'</b><br/>'+r.address+'<br/>'+r.phone.join('<br/>')+(r.phone.length?'<br/>':'')/*+r.listingType+'<br/>'*/+r.content+'<br/>('+r.lat+','+r.lng+') <a href="'+r.url+'" target="_blank">Use Google Maps</a></div>'; 157 if(sA2.c)sA2.c.innerHTML+=h;else sl(h); 158 },'Local'); 159 sl('initSearch: local search initialed.'); 160 }}); 161 else sl('initSearch: Can not initial local search. Please load API.'); 162 } 163 164 165 var _map_tmp_message; 166 initMap.flag={backgroundColor:'#DDE'}; 167 function initMap(){ 168 var a,m,i,_f=arguments.callee; 169 mapO=new gMap('map_canvas',dLoc.tw,_f.flag); 170 //if(!mapO)return 1; 171 172 //mapO.geocoder.setBaseCountryCode('TW'); 173 sl('initMap: set geocoder country code: '+mapO.geocoder.getBaseCountryCode()); 174 175 // 要先 show 才能得到 getOverviewMap() 176 if(m=mapO.overviewMap) 177 // IE7 上 .hide() 時 .show() 會出錯 178 // 2008/9/6 22:37:33 IE6 也會出錯了 179 if(navigator.userAgent.indexOf('MSIE')==-1) 180 m.show(),setOverviewMap(); 181 182 // small mark template http://econym.googlepages.com/custom.htm http://mapki.com/wiki/Available_Images http://econym.googlepages.com/geicons.htm http://code.google.com/apis/maps/documentation/overlays.html#Icons_overview 183 // iconSize 的處理還是有問題。 184 mapO.icon(_f.iconOption||{ 185 shadow:'http://labs.google.com/ridefinder/images/mm_20_shadow.png', 186 iconSize:new GSize(12,20), 187 shadowSize:new GSize(22,20), 188 iconAnchor:new GPoint(6,20), 189 infoWindowAnchor:new GPoint(5,1) 190 },1); 191 mapO.icon(_f.iconArray||[ 192 {image:'http://labs.google.com/ridefinder/images/mm_20_green.png'},//http://www.google.com/intl/en_us/mapfiles/ms/micons/blue-dot.png//{shadow:'http://maps.google.com/mapfiles/kml/pal5/icon14s.png',image:'http://maps.google.com/mapfiles/kml/pal5/icon14.png'},// 193 {image:'http://labs.google.com/ridefinder/images/mm_20_yellow.png'}, 194 {image:'http://labs.google.com/ridefinder/images/mm_20_green.png'},//{icon:G_DEFAULT_ICON}, 195 {image:'http://labs.google.com/ridefinder/images/mm_20_orange.png'},//'http://maps.google.com/mapfiles/arrow.png', 196 '' 197 ]); 198 mapO.runAfterAdd=function(o){getO.add(o.address,o.name);showML.sel(o);showML();}; 199 mapO.runOnClick=function(o){showML.sel(o);showML();}; 200 mapO.runAfterRemove=function(o){if(showML.sel()==o)showML.sel(null,1);showML();}; 201 mapO.notFound=function(a,d){sl('<em>沒找到 ['+a+'] '+d.name+'</em>');return false;}; 202 // 在 unload 的時候呼叫 GUnload 以避免瀏覽器 memory leak。 203 addListener(0,'unload',function(){try{GUnload();}catch(e){}}); 204 205 a=function(htm,t,js,ico){ 206 return '<div class="cm_line" title="'+t+'" onclick="'+js+'" onmouseover="this.className=\'cm_line_mo\';" onmouseout="this.className=\'cm_line\';"><img style="height:1em;width:1em;" src="'+(ico||'http://www.google.com/mapfiles/markerTransparent.png')+'"/> '+htm+'</div>'; 207 }; 208 if(m=mapO.map) 209 // use google's message 210 i=function(i){try{ 211 eval('_map_tmp_message=p('+i+');',m); 212 var a=_map_tmp_message; 213 //sl('setContextMenu '+i+': '+a); 214 return a&&a.length<8?a:''; 215 }catch(e){}}, 216 mapO.setContextMenu([ 217 a(i(10985)||'Zoom in','將地圖於此放大','mapO.showContextMenu(0),mapO.map.zoomIn(mapO.clickLatLng,true);','http://www.google.com/mapfiles/zoom-plus.png') 218 ,a(i(10986)||'Zoom out','將地圖於此縮小','mapO.showContextMenu(0),mapO.map.zoomOut(mapO.clickLatLng,true);','http://www.google.com/mapfiles/zoom-minus.png') 219 ,a(/*i(11047)||*/'Set Center','將地圖於此置中','mapO.showContextMenu(0),mapO.setCenter(mapO.clickLatLng);','http://www.google.com/mapfiles/center.png') 220 ,'<hr/>' 221 ,a('Search near','搜尋附近','mapO.showContextMenu(0),showNeighbor(mapO.clickLatLng);','http://maps.google.com/mapfiles/kml/pal2/icon13.png') 222 ].join('')), 223 GEvent.addListener(m,"click",function(){ 224 mapO.showContextMenu(0); 225 }),GEvent.addListener(m,"dragstart",function(){ 226 mapO.showContextMenu(0); 227 }); 228 } 229 230 231 function readLoc(){ 232 if(loadMapData.dataF)catchFile([loadMapData.dataF,mapO.locFP=loadMapData.dataF+'.adr.csv']); 233 mapO.readLoc(); 234 } 235 236 237 //loadMapData.baseD=''; 238 // 預設可使用之模板 239 loadMapData.getHTM=function(d,type){ 240 var l=mData[d.name||d.address],a=[mapO.getPoint(d,type)],r=[]; 241 if(d.address||'')a.unshift(d.address); 242 l=l?l.link:''; 243 if(!loadMapData.baseD)loadMapData.baseD=''; 244 if(d.name) // 不能用 this.baseD 245 r.push('<em>'+(l?'<a href="'+loadMapData.baseD+l+'" target="_blank">'+d.name+'</a>':d.name)+'</em>'); 246 if(d.dscr)r.push(d.dscr); 247 r.push('<div class="adr">'+a.join('<br/>')+'</div>'); 248 if(typeof d.getLength=='function')r.push('length: '+(d.getLength()/1000).toFixed(3)+'km'); 249 if(typeof d.getArea=='function')r.push('面積: '+d.getArea().toFixed(3)+'m²'); 250 //if(showNeighbor.pointer&&d.getLatLng)r.push('距離 '+showNeighbor.pointer.distanceFrom(d.getLatLng())); 251 //r.push(,'<hr class="sp"/><span onclick="">search near</span>'); 252 return r.join('<br/>').replace(/(<\/(div|p)>)\s*<br\/?>/g,'$1'); 253 }; 254 loadMapData.lessItems=3; 255 loadMapData.forEach=function(mapData,index){}; 256 /* 257 loadMapData.zIndexP=function(){ 258 sl('click '+this.name); 259 return -1; 260 }; 261 */ 262 function loadMapData(){ 263 /* load: 這裡假設 data 中標題可作為id(獨一無二) 264 map data: [標題 敘述(description) link _data_] 265 _data_: search string/kind additional description/data of different kind of data 266 search string/additional description (用括弧框起來的) 267 'marker' lat lng 268 'polyline' points levels 269 'polygon' points levels 270 271 mData[標題||address]={}; 272 */ 273 mData={}; 274 var i=0,m,t,_f=arguments.callee; 275 _f.run=1; 276 277 try{t=getU(_f.dataF);}catch(e){sl('Get data file ['+_f.dataF+'] error: '+e.message);} 278 if(t)t=parseCSV(t);//t.replace(/\r+/g,'').replace(/\n+$/,'').replace(/^\n+/,'').split('\n'); 279 else t=[],showML.write('No list got of ['+_f.dataF+'].'); 280 281 showML.write('總共 '+t.length+' 筆資料正處理中,請稍候…'); 282 sl('loadMapData: Found '+t.length+' lines. Parsing.. (<em onclick="if(loadMapData.run)loadMapData.run=0,sl(\'User stopped.\');" style="cursor:pointer;">stop</em>)'); 283 for(;i<t.length&&_f.run;i++){ 284 //sl(t[i][7]); 285 //if(t[i] && (m=t[i].replace(/\\n/g,'<br/>').split('\t')).length>_f.lessItems) 286 if(t[i].length>_f.lessItems) 287 _f.forEach(t[i],i); 288 } 289 _f.run=0; 290 } 291 292 293 placeMapItem.done=function(i){ 294 //sl('placing done.'); 295 showML.refresh=1,showML(); 296 // workaround 權宜之計: iconSize 的處理還是有問題。 297 setTimeout('if(mapO)mapO.iconA[2].image=G_DEFAULT_ICON.image;',500); 298 }; 299 placeMapItem.step=function(i){ 300 var _t=this; 301 if(_t.run&&i<_t.stepA.length&&(!_t.loadMax||i<_t.loadMax)) 302 mapO.add(mData[_t.stepA[i++]]),setTimeout('placeMapItem.step('+i+');',0/*i>40?400:i+i*i/4*/); 303 else if(_t.done)_t.done(i); 304 }; 305 placeMapItem.loadMax=80; 306 function placeMapItem(){ 307 var _f=arguments.callee,i; 308 if(_f.noPlace){ // 一開始不列出點 309 if(typeof _f.noPlace=='function')_f.noPlace(); 310 return; 311 } 312 313 lostItem={}; 314 _f.run=1; 315 //showML(); 316 showML.refresh=0; 317 if(_f.step){ 318 _f.stepA=[]; 319 for(i in mData) 320 lostItem[i]=mData[i],_f.stepA.push(i);//,mapO.add(mData[i]); 321 setTimeout('placeMapItem.step(0);',0); 322 }else{ 323 for(i in mData)mapO.add(mData[i]); 324 if(_f.done)_f.done(); 325 } 326 } 327 328 329 330 // interface 331 332 // 2008/9/6 15:7:34 333 //setSize.size=[880,320,240]; // width, height, menuWidth 334 setSize.size='95%,70%,25%'.split(','); // width, height, menuWidth 335 setSize.setContainer=function(){ 336 try{ 337 this.menuC=(this.menu=document.getElementById('markerList')).parentNode; // menuC: menu container 338 return this.container=document.getElementById('map_canvas').parentNode; 339 }catch(e){} 340 }; 341 function setSize(){ 342 var _f=arguments.callee,s=_f.size,_s=s.join('\0').split('\0'),m,a=function(i,k){ 343 if(!isNaN(s[i]))s[i]+='px'; 344 if(typeof _s[i]=='string'&&(m=_s[i].match(/^(\d+)%$/))) 345 k=k?'Height':'Width', 346 _s[i]=Math.floor((window.innerHeight?window['inner'+k]:document.documentElement&&document.documentElement.clientHeight?document.documentElement['client'+k]:document.body['offset'+k])*m[1]/100); 347 }; 348 349 /* 350 // from getWinStatus() 351 with(document.body.style)width=height='100%'; 352 sl('setSize: window.inner: '+window.innerWidth+','+window.innerHeight); 353 sl('setSize: window.page: '+window.pageXOffset+','+window.pageYOffset); 354 sl('setSize: screen: '+screen.width+','+screen.height); 355 sl('setSize: document.documentElement.client: '+document.documentElement.clientWidth+','+document.documentElement.clientHeight); 356 sl('setSize: document.body.scroll: '+document.body.scrollWidth+','+document.body.scrollHeight); 357 sl('setSize: document.body.offset: '+document.body.offsetWidth+','+document.body.offsetHeight); 358 sl('setSize: document.body.client: '+document.body.clientWidth+','+document.body.clientHeight); 359 */ 360 361 a(0); 362 a(1,1); 363 a(2); 364 //sl('setSize: '+_s); 365 366 a=_f.container; 367 if(!a&&!(a=_f.setContainer()))return; 368 369 with(a.style)width=s[0],height=s[1]; 370 with(_f.menuC.style)width=s[2],height=s[1]; 371 372 _f.menu.style.height=(_s[1]-_f.menu.offsetTop)+'px'; 373 374 //sl('setSize: '+a.offsetWidth+'-'+_f.menuC.offsetWidth+'='+(a.offsetWidth-_f.menuC.offsetWidth)); 375 //sl('setSize: '+_s[0]+'-'+_s[2]+'='+(_s[0]-_s[2])); 376 initMap.flag.size=[ 377 a.offsetWidth-_f.menuC.offsetWidth-(navigator.userAgent.indexOf('MSIE')==-1?13:1), // 13: 和 scrollbar 有關嗎?? 378 (navigator.userAgent.indexOf('MSIE')==-1?_s[1]:_s[1]) // _s[1] 不能改成 a.offsetHeight 379 ]; 380 } 381 382 383 384 showML.refresh=1; 385 showML.closeMark='×'; 386 showML.indexA='iA'; 387 showML.selClass='sel'; 388 showML.specialKind='sp'; 389 showML.isSelR=new RegExp('(^|\\s+)'+showML.selClass); 390 //showML.selO=null; 391 showML.sel=function(o,removed){ 392 var _t=this; 393 if(typeof o!='undefined'){ 394 // GMarker.setImage(src) 395 if(!removed&&_t.selO&&_t.selO.setImage) 396 try{_t.selO.setImage(mapO.icon().image);}catch(e){} // 用 try 是因為有時被刪了可是卻還是在(動作太快?),這時要 setImage 會出錯。 397 _t.selO=o; 398 if(o&&o.setImage)o.setImage(mapO.icon(showSP.selectedI).image); 399 } 400 return _t.selO; 401 }; 402 showML.getName=function(o){ 403 var name=o.parentNode.attributes.getNamedItem(this.indexA),type; 404 if(name&&(name=name.nodeValue)&&(type=name.match(/^([a-z]+),(.+)$/i))){ 405 //sl('showML.getName: ['+type[1]+'] '+type[2]+'('+getO.alias(type[2])+','+mapO.getO(type[1],getO.alias(type[2]))+')'); 406 //name=,type=type[1]; 407 return [getO.alias(type[2]),type[1]]//[name,type]; 408 } 409 }; 410 showML.write=function(t){ 411 try{document.getElementById('markerList').innerHTML=t;}catch(e){} 412 }; 413 function showML(o){ 414 if(!mapO)return; // 尚未 initial? 415 416 var _f=arguments.callee; 417 if(o){ 418 var name,type; 419 if(type=_f.getName(o)){ 420 name=type[0],type=type[1]; 421 if(o.className=='closeMark') 422 mapO.remove(name,type); 423 else{ 424 showML.sel(mapO.getO(type,name));//.setImage(mapO.icon(2).image); 425 mapO.show(name,type); 426 o=o.parentNode; 427 for(var i=0,LI=o.parentNode.parentNode.getElementsByTagName('li');i<LI.length;i++) 428 if(_f.isSelR.test(LI[i].className))LI[i].className=LI[i].className.replace(_f.isSelR,''); 429 o.className+=' '+_f.selClass; 430 } 431 } 432 return false; 433 } 434 435 if(!_f.refresh)return; 436 //alert(mapO.getOArray('type').join('\n')); 437 438 var t=['<ol>'],a,i,j,id,OK={}; 439 for(j in mapO.supportKind()){ 440 a=mapO.getO(j); 441 for(i in a){ 442 //sl(a[i].name+','+a[i].address); 443 if(a[i].name in lostItem)delete lostItem[a[i].name]; 444 else if(('address' in a[i]) && (a[i].address in lostItem))delete lostItem[a[i].address]; 445 446 id=typeof a[i].getLatLng=='function'?a[i].getLatLng():a[i].name; 447 if(!(id in OK)) 448 OK[id]=1, 449 t.push('<li '+_f.indexA+'="'+j+','+i+'" class="'+(j=='marker'?'':_f.specialKind)+' '+(_f.sel()==a[i]?_f.selClass:'')+'"' 450 +'><a href="#" title="'+UnicodeToHTML(( 451 ((a[i].name+'').indexOf(id)!=-1||(a[i].dscr+'').indexOf(id)!=-1?'':id+'\n') 452 +(a[i].dscr||i) 453 +(showNeighbor.pointer&&typeof a[i].getLatLng=='function'?'\n距離定點 '+(a[i].getLatLng().distanceFrom(showNeighbor.pointer.getLatLng())/1000||0).toFixed(3)+' km':'') 454 ).replace(/<br\/?>/gi,'\n').replace(/\n{2,}/g,'\n'),2)+'" onclick="return showML(this);" onmouseover="showSP(this,1);" onmouseout="showSP(this);">' 455 +(a[i].name||i).replace(/<[^>]+>/g,'') 456 +'</a> [<a href="#" class="closeMark" onclick="return showML(this);" title="關閉">'+_f.closeMark+'</a>]</li>');// title="remove marker" 457 } 458 } 459 for(i in lostItem)t.push('<li class="lost"><a href="#" onclick="mData['+dQuote(i,0,"'")+'].show=1;mapO.add(mData['+dQuote(i,0,"'")+']);return false;" title="'+UnicodeToHTML(mData[i].name||mData[i].type||'')+'">'+i+'</a></li>'); 460 t.push('</ol>'); 461 //sl(UnicodeToHTML(t.join('')),1); 462 _f.write(t.join('')); 463 if(typeof dealLink=='function')setTimeout('dealLink(0,1);',0); 464 return false; 465 } 466 467 468 // show spot 469 showSP.defaultI=0; // default icon index 470 showSP.selectedI=2; // selected icon index 471 function showSP(o,i){ 472 var _f=arguments.callee; 473 if(o){ 474 var name,type; 475 if(type=showML.getName(o)){ 476 name=type[0],type=type[1]; 477 o=mapO.getO(type,name); 478 if(o&&o.setImage&&o!=showML.sel()) 479 //sl('showSP: set icon of ['+o.name+'] to '+i), 480 o.setImage(mapO.icon(i).image); 481 } 482 return false; 483 } 484 485 } 486 487 // 應用 488 489 function removeAll(){ 490 showML.refresh=0,lostItem={}; 491 if(mapO)mapO.removeAll(); // 尚未 initial? 492 if(showML)showML.sel(null,1),showML.refresh=1,showML(); 493 } 494 495 496 // 不能造成 type 改變! 497 function normalize_address(t) { 498 var _f = arguments.callee, a = '0123456789'; 499 if (!_f.dc) { 500 _f.nr = new RegExp('[' + a + ']', 'g'); 501 _f.dc = {}; 502 for ( var i = 0; i < a.length; i++) 503 _f.dc[a.charAt(i)] = i; 504 } 505 return t.replace(/[\s ]+/g, '').replace(_f.nr, function($0) { 506 return _f.dc[$0]; 507 }) 508 //.replace(/號([^樓]+樓|之.{1,2})$/,'號'); 509 ; 510 }; 511 512 /* 513 http://www.post.gov.tw/post/internet/f_searchzone/sz_a_b_ta.jsp#a 514 縣市 515 鄉鎮市區 【市】為縣轄市 516 村里 直轄市:台北市、高雄市及省轄市:基隆市、新竹市、台中市、嘉義市、台南市以外地區由於路名結構變化較大,有些地區沒有路、街,僅有「村」、「里」名稱 517 鄰 518 路街 路名中不可使用(全、半形)阿拉伯數字,必須一律使用中文數字,如中正二路等,惟門牌號不必輸入。 新村、山莊、新城、工業區等與街、路同級之名稱 519 段 520 巷 文字巷 521 弄 522 號 523 \d(之\d)樓(之\d) 524 525 縣市鄉鎮區村里鄰路街段巷弄號樓 526 527 TODO: 528 806台灣高雄市前鎮區光華二路413號 529 530 */ 531 CeL.net.map 532 . 533 /** 534 * 解析地址 535 * @param {String} address 地址 536 * @returns 537 * @memberOf CeL.net.map 538 */ 539 parse_address = function (address) { 540 var a = {}, i, v, w = '縣市鄉鎮區村里鄰路街段巷弄號樓', 541 r = new RegExp('[^' + w + ']+([' + w + '])', 'g'); 542 543 var _ = address.replace(/[ \s]+/g, '').replace(/號([^樓F]{1,6})F$/, '號$1樓') 544 .replace(/^(\d{3,5})?[台臺]灣/, '$1').replace(/^\d+/, function($0) { 545 r.zip = $0; 546 return ''; 547 }).replace(r, function($0, $1) { 548 v = $0; 549 if (/縣市/.test(i = $1) && !r.zip) 550 v.replace(/^\d{3}\d{2}?}/, function($0) { 551 r.zip = $0; 552 return ''; 553 }); 554 a[i] = v; 555 //sl(v); 556 return ''; 557 }); 558 // sl('['+address+'] --- ['+_+']'); 559 return _ ? 0 : address; 560 }; 561 562 563 // 地址轉 index 564 getO.n={}; // name(address) to mapO.getO's name 565 getO.add=function(alias,name,type){ 566 if(alias&&name&&alias!=name){ 567 if(type&&mapO.getO(type,alias)&&!mapO.getO(type,name)){ 568 var t=name;name=alias,alias=t; 569 } 570 //sl('getO.add: ['+alias+'] set to ['+name+']'), 571 this.n[alias]=name; 572 } 573 }; 574 getO.alias=function(name){return this.n[name]||name;}; 575 function getO(name){ 576 return mapO.getO(arguments.callee.n[name]||name); 577 } 578 579 580 581 582 /* search address 583 sA(index of mData) 584 sA({type(where):'',name:'',description:''}) 585 sA(type(where),name,description) 586 */ 587 function sA(i,a,b){ 588 if(!mData)return; // 尚未 initial? 589 if(!(i in mData)||arguments.length>1) 590 //sl('sA: search '+i), 591 mapO.searchPoint.apply(mapO,arguments); 592 else 593 //sl('sA: call mData['+i+']('+mData[i].type+')'), 594 mapO.searchPoint(mData[i]); 595 return false; 596 } 597 598 // 先找尋現有資料 599 function sA2(adr,noFit){ 600 var _f=arguments.callee; 601 602 if(!noFit){ 603 showFit(adr 604 ,function(k,o){return k&&(o.name+o.description).indexOf(k)!=-1;} 605 ,function(k){_f(adr,1);} 606 ); 607 return; 608 } 609 610 if(typeof adr_to_mData!='object'||mapO.LatLngR.test(adr))return sA(adr); 611 612 //return sA(adr_to_mData[adr]||adr); 613 614 615 if(!_f.c)_f.c=document.getElementById('guess'); 616 617 if(!(adr in adr_to_mData)&&(adr.toLowerCase() in adr_to_mData)) 618 //sl('sA2: ['+adr+']→['+adr.toLowerCase()+']'), 619 adr=adr.toLowerCase(); 620 if(adr_to_mData[adr] in mData){ 621 var a=adr_to_mData[adr]; 622 mData[a].show=1; // 置中 623 sA(a); 624 if(showML.selO)mapO.showWindow(showML.selO); 625 }else if(!_f.c)sA(adr); 626 else mapO.getLocations(adr,function(r){ 627 var cM='<div style="float:right;cursor:pointer;" title="close" onclick="sA2.c.style.display=\'none\';">[×]</div>'; 628 if(gLocal){gLocal.searcher.setCenterPoint(showNeighbor.pointer.getLatLng()||mapO.setCenter()||'台灣');gLocal.s(adr);} 629 if(!r.length){_f.c.innerHTML=cM+'使用 GClientGeocoder.getLocations 沒找到 [<span style="color:#e23;">'+adr+'</span>]:<br/>'+mapO.GeoStatus(mapO.getLocations.errno),_f.c.style.display='block';return;} 630 if(r.length==1&&adr==r[0][2]){sA(adr);if(showML.selO)mapO.showWindow(showML.selO);return;} 631 var i=0,t=[cM+'對於 [<span style="color:#e23;">'+adr+'</span>],您是不是指:<ol>']; 632 for(;i<r.length;i++) 633 if(r[i][0]) 634 //sl('sA2: '+r[i]), 635 t.push('<li><span title="'+r[i][0]+','+r[i][1]+'">〒</span> <span class="point" onclick="sA(this.title);if(showML.selO)mapO.showWindow(showML.selO);return false;" title="'+r[i][2]+'">'+r[i][2]+'</span></li>'); 636 t.push('</ol>'); 637 _f.c.innerHTML=t.join(''); 638 _f.c.style.display='block'; 639 });//sA(adr); 640 return false; 641 } 642 643 644 645 /* 646 從 mData show 符合條件的 647 */ 648 showFit.showZoom=40; // 這以下就 zoom 649 showFit.limit=80; // 最多取點數 650 function showFit(k,func,notFound){ 651 if(typeof k=='undefined'||!func)return; 652 removeAll(); 653 showML.write('頁面資料讀取中,請稍候…'); 654 655 var i,p=[],b,_f=arguments.callee; 656 for(i in mData) 657 if(func(k,b=mData[i],i) && (b=mapO.searchPoint.call(mapO,b))) // 確定有找到才 c++ 658 if(p.push(b), _f.limit&&p.length>_f.limit)break; 659 660 if(p.length<_f.showZoom) 661 if(p.length){ 662 // zoom 663 b=new GLatLngBounds(); 664 for(i=0;i<p.length;i++) 665 b.extend(p[i]); 666 mapO.setCenter({p:b.getCenter(),m:'pan',z:mapO.map.getBoundsZoomLevel(b)}); 667 }else if(notFound)notFound(k); 668 669 return p; 670 } 671 672 673 674 /* 只顯示附近的spot 675 showNeighbor('高雄市苓雅區'); 676 677 bug: 678 679 TODO: 680 setZoom: getBoundsZoomLevel 681 addSelf: 加入此點,使之受到管控。預設 false 682 不在管控內 683 */ 684 showNeighbor.pointer=null; // default GMarker 685 showNeighbor.addPointer=function(a){ 686 var _m=mapO.map,m=mapO.addMarker( 687 new GLatLng(22.62,120.33)//dLoc.tw 688 ,{draggable:true,title:'請拖曳我,以找出鄰近的點。',icon:mapO.icon(3)} 689 ); 690 GEvent.addListener(m,"dragend",function(){ 691 showNeighbor(m.getLatLng()); 692 }); 693 694 if(this.l)GEvent.removeListener(this.l); 695 this.l=GEvent.addListener(_m,"click",function(overlay,point){ 696 var i,p=[5,10,15,20,30,50],s='<br/><a href="#" onclick="return showNeighbor.byD();" title="search near">搜尋附近</a>'; 697 for(i=0;i<p.length;i++)s+=' <a href="#" onclick="return showNeighbor.byD('+p[i]+');">'+p[i]+'</a>'; 698 s+=' km'; 699 if(overlay==m)m.openInfoWindowHtml('經緯度: '+m.getLatLng()+s); 700 else if(!overlay&&point) 701 p=m.getLatLng(),m.setLatLng(point), 702 m.openInfoWindowHtml(point+'<br/>from: '+p+',<br/>distance: '+(p.distanceFrom(point)/1000).toFixed(3)+'km'+s); 703 }); 704 705 return this.pointer=m; 706 }; 707 showNeighbor.arg={ // 傳給 .getNeighbor() 的參數 708 //f:'n[i]=n[i][1][2];', // 不能改變結構!因為需要 getBoundsZoomLevel 709 d:30, 710 c:9 711 }; 712 showNeighbor.forEach=function(a){ 713 //sl('showNeighbor.forEach: ['+(typeof a[1][2])+'] '+a[1][2]); 714 sA(a[1][2]); 715 }; 716 showNeighbor.byD=function(d){ 717 var _t=this; 718 if(d)_t.arg.d=d; 719 if(_t.pointer)_t(_t.pointer.getLatLng()); 720 return false; 721 }; 722 showNeighbor.notFound=function(address,address_not_found){ 723 showML.write('抱歉,找不到 <em>'+address+'</em>'+(address_not_found?'':' 附近的點')+'。'); 724 }; 725 function showNeighbor(l,f){//(location | [location, address], addSelf) 726 if(!l)return; 727 showML.write('頁面資料讀取中,請稍候…'); 728 var _f=arguments.callee,i,o,adr; 729 if(l instanceof Array)adr=l[1],l=l[0]; 730 //sl('showNeighbor: ['+l+','+adr+'] '+(l instanceof GLatLng)+', '+mapO.getLatLng(l+'')); 731 if(!(l instanceof GLatLng)) 732 // 無此資料。嘗試取得 loc.. 733 return mapO.getLatLng(l+'',[function(p){_f([p,adr||l]);},function(p){if(p)return _f([p,adr||l]);_f.notFound&&_f.notFound(adr||l,1);}]); 734 o=mapO.getNeighbor(l,_f.arg); 735 if(o.length){ 736 removeAll(); 737 var bounds=new GLatLngBounds(); 738 bounds.extend(l); 739 for(i=0;i<o.length;i++){ 740 if(!isNaN(o[i][1][1])) 741 //sl('Add to bound: '+o[i][1][0]+','+o[i][1][1]), 742 bounds.extend(new GLatLng(o[i][1][0],o[i][1][1])); 743 //sl('add ['+o[i][1][2]+'] by '+(_f.forEach+'').slice(0,20)+'..'); 744 _f.forEach(o[i]); 745 } 746 if(!mapO.searchPoint.show) // 若設了 searchPoint.show,還是會被其他的奪去… 747 mapO.setCenter({p:bounds.getCenter(),m:'pan',z:mapO.map.getBoundsZoomLevel(bounds)}); 748 }else _f.notFound(l); 749 if(_f.pointer)_f.pointer.setLatLng(l),_f.pointer.openInfoWindowHtml('搜尋 '+(adr?'<em title="'+l+'">'+adr+'</em> ':l)+' 四周 '+_f.arg.d+' km<br/>找到 '+o.length+'/(最多 '+_f.arg.c+') 個點。'); 750 //sl('showNeighbor: search around '+l+' (四周 '+_f.arg.d+' km) get '+o.length+'/(max '+_f.arg.c+') results.'); 751 } 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 // =================================================== 767 /* 768 ** use Yahoo! Map to get position of a address 769 770 _=this 771 772 TODO: 773 774 775 HISTORY: 776 2008/7/31 19:56:29 create 777 778 779 http://tw.developer.yahoo.com/maps/ 780 http://developer.yahoo.com/maps/ajax/V3.8/index.html 781 */ 782 var 783 getLatLon= 784 785 (function(){ 786 787 var 788 789 // class private ----------------------------------- 790 791 // class name 792 n='getLatLon', 793 794 // running now 795 r=0, 796 797 // interval/timeout seed 798 s=''+Math.random()*1e12, 799 800 801 // { address: [function(lat, lng, address), not found function(address)], .. } 802 o={}, 803 804 // queue: [ adr, .. ] 805 q=[], 806 807 808 // map object 809 m, 810 811 // initial 812 i=function(){ 813 if(typeof YMap!='function'){ 814 //sl(n+': Please include YMap first!'); 815 return 1; 816 } 817 818 var o=document.createElement('div'); 819 document.body.appendChild(o); 820 o.style.width=o.style.height='1px'; // =0 會造成 .getArea() 出問題 821 YEvent.Capture(m=new YMap(o),EventsList.onEndGeoCode,c); 822 o.style.visibility='hidden'; 823 //o.style.display='none'; // 會造成 .getArea() 出問題 824 825 //s=Math.random()*1e12+''; 826 }, 827 828 829 // do query 830 d=function(){ 831 //sl(n+'.do query: '+q[0]); 832 if(!q||!q.length)r=0; 833 else m.geoCodeAddress(q.shift()); 834 }, 835 836 837 // catch function 838 c=function(r){ 839 var a=r.Address,f; 840 841 if(f=_.interval) 842 setTimeout(n+'("'+s+'");',f); 843 else d(); 844 845 if(a in o) 846 if(f=o[a],r.success)f[0](r.GeoPoint.Lat,r.GeoPoint.Lon,a); 847 else f[1]&&f[1](a); 848 delete o[a]; 849 }, 850 851 852 // instance constructor --------------------------- 853 _=function(a,f,nf){ // address, function, not found function 854 if(a===s)return d(); 855 856 if(!a||!f || !m&&i())return 1; 857 858 o[a+='']=[f,nf]; 859 860 //sl(n+': ('+q.length+')'+[a,f,nf]); 861 q.push(a); 862 863 if(!r)r=1,d(); 864 }; 865 866 867 // class public interface --------------------------- 868 869 // interval (ms) 870 _.interval=0; 871 872 873 // class constructor --------------------------- 874 875 i(); 876 877 878 return _; 879 })(); // (function(){ 880 881 // =================================================== 882 883 884 /* old 885 // use Yahoo! Map 886 887 // interval 888 //getLatLon.t=200; 889 890 // { address: [function, not found function], .. } 891 getLatLon.o={}; 892 893 // queue: [ adr, .. ] 894 getLatLon.q=[]; 895 896 // initial 897 getLatLon.i=function(){ 898 if(typeof YMap!='function'){ 899 //sl('getLatLon: Please include YMap first!'); 900 return 1; 901 } 902 var c=document.createElement('div'); 903 document.body.appendChild(c); 904 c.style.width=c.style.height=0; 905 YEvent.Capture(this.m=new YMap(c),EventsList.onEndGeoCode,this.c); 906 c.style.display='none'; 907 this.T=Math.random()*1e12+''; 908 }; 909 910 // catch function 911 getLatLon.c=function(r){ 912 var t=getLatLon; 913 if(t.t)setTimeout('getLatLon.d("'+t.T+'");',t.t);else getLatLon.d(); 914 var f=t.o; 915 if(r.Address in f) 916 if(f=f[r.Address],r.success)f[0](r.GeoPoint.Lat,r.GeoPoint.Lon,r.Address); 917 else f[1]&&f[1](r.Address); 918 delete t.o[r.Address]; 919 } 920 921 // do query 922 getLatLon.d=function(){ 923 var _f=arguments.callee,_t=this,a,f,n; 924 //sl('getLatLon.d: '+_t.q[0]); 925 if(!_t.q||!_t.q.length)_t.r=0; 926 else _t.m.geoCodeAddress(_t.q.shift()); 927 }; 928 929 930 // running 931 //getLatLon.r=0; 932 933 function getLatLon(adr,f,nf){ 934 var _f=arguments.callee; 935 if(adr===_f.T)return _f.d(); 936 if(!adr||!f || !_f.m&&_f.i())return; 937 938 if(!_f.q)_f.q=[]; 939 //sl('getLatLon: ('+_f.q.length+')'+[adr,f,nf]); 940 _f.o[adr]=[f,nf]; 941 _f.q.push(adr); 942 943 if(!_f.r){_f.r=1;_f.d();} 944 } 945 */ 946 947 948 949 950 951 952 953 954 // =================================================== 955 956 /* 957 main map function 958 959 _=this 960 961 TODO: 962 963 */ 964 965 //var 966 gMap= 967 968 (function(){ 969 970 // class private ----------------------------------- 971 var 972 973 // class interface ----------------------------------- 974 _=function(){ 975 // Dynamic Loading http://code.google.com/apis/ajax/documentation/#Dynamic 976 //if(typeof GMap=='undefined')google.load("maps","2",{language:"ja_JP",callback:mapsLoaded}); 977 978 // init member 979 var _t=this,i; 980 // initial instance object 981 _t.locArray=[],_t.locArray2=[],_t.locArray_u={},_t.locArray2_u={},_t.iconA=[],_t.iconO={},_t.dMarkerO={}; 982 _t.kinds={marker:GMarker,polyline:GPolyline,polygon:GPolygon,xml:GGeoXml}; // If this failed, maybe GMap didn't loaded? 983 for(i in _t.kinds) 984 if(i in _t)throw 'Error: ['+i+'] is already a member of me!'; 985 else _t[i]={}; 986 987 988 // 調整 GLatLng 的顯示 989 GLatLng.prototype.toS=function(p){ 990 if(!p)p=_t.precision||0; 991 return Number(this.lat()).toFixed(p)+','+Number(this.lng()).toFixed(p); 992 }; 993 GLatLng.prototype.toString=function(p){return '('+this.toS(p)+')';}; 994 995 996 /* 997 http://blog.wctang.info/2007/07/use-google-map-api-without-api-key.html 998 Geocode 查詢每天有 50000 次的限制 使用 Geocoder 就是要連到 Google 去做查詢,而現在 Google 在做 Geocode 查詢時會在 Server 端做 API key 的檢查,這個就躲不掉了 999 http://blog.wctang.info/2007/07/use-google-map-geocoder-without-api-key.html 1000 1001 1002 */ 1003 if(GBrowserIsCompatible()&&!_t.geocoder) 1004 with(_t.geocoder=new GClientGeocoder()) 1005 setCache(null), // disable cache, 因為找到的都被管控了。 1006 setBaseCountryCode('tw'); // 語系 1007 1008 //_t.readLoc(); 1009 _t.initMap.apply(_t,arguments);//return _t.initMap.apply(_t,arguments); 1010 }; 1011 1012 1013 // class public ----------------------------------- 1014 1015 1016 // prototype ----------------------------------- 1017 _.prototype={ 1018 1019 // 這些函數可重寫 1020 notFound:function(address,data){ 1021 return 1;//throw 'Address ['+address+'] not found!'; 1022 }, 1023 // 增加 overlay 後 1024 runAfterAdd:function(obj,type,data,name){}, 1025 // 移除 overlay 後 1026 runAfterRemove:function(obj,type){}, 1027 // 按 overlay 時 1028 runOnClick:function(obj,type){}, 1029 1030 precision:6, // 精度,算到小數點下第幾位。GMap 2008:6 1031 1032 defaultZoom:14, // 預設縮放 1033 1034 /* 1035 map, // GMap obj 1036 1037 TODO: 1038 use GMarkerManager, http://code.google.com/apis/maps/documentation/overlays.html#Marker_Manager 1039 1040 1041 //handle array: 1042 marker={'lat,lng':GMarker}, // GMarker 地圖標記 1043 polyline={points:GPolyline}, // GPolyline 折線 1044 polygon={points:GPolygon}, // GPolygon 多邊形 1045 xml={URL:GGeoXml}, // GGeoXml: xml/kml 1046 */ 1047 kinds:{}, 1048 supportKind:function(k){ 1049 return k?k in this.kinds:this.kinds; 1050 }, 1051 1052 getKind:function(o){ 1053 for(var i in this.kinds) 1054 if(o instanceof this.kinds[i])return i; 1055 }, 1056 1057 1058 // 讀入先前 catch 的經緯度,存loc而不必每次search 1059 readLoc:function(){ 1060 var _t=this,t,i=0,l,a; 1061 if(!_t.locFP)_t.locFP='map_loc.dat'; // 紀錄 LatLng/地址 可供 searchPoint() 使用 1062 // GDownloadUrl(url,callback) 1063 try{t=getU(_t.locFP);}catch(e){} 1064 _t.adr_to_loc={}; 1065 if(!t||!(t=t.replace(/\r/g,'').split('\n')).length)return; 1066 1067 sl('Get '+t.length+' catched address records from ['+_t.locFP+'].'); 1068 for(;i<t.length;i++) 1069 if((a=t[i].split(' ')).length>1 && (l=a[0].split(',')).length==2) 1070 _t.adr_to_loc[a[1]]=new GLatLng(l[0],l[1]) 1071 //,sl('readLoc: ['+a[1]+'] '+_t.adr_to_loc[a[1]]) 1072 ; 1073 else sl('readLoc: error data: '+t[i]); 1074 }, 1075 1076 // ** important ** 這邊不能作 object 之 initialization,否則因為 object 只會 copy reference,因此 new 時東西會一樣。initialization 得在 _() 中作! 1077 // locArray[]=[lat,lng,adr] sort by lat 給 writeLoc() & getNeighbor() 用,僅包含需要 search 的。 1078 locArray:[], 1079 // 寫入 catched 的經緯度 1080 writeLoc:function(s){ 1081 var _t=this,i,t=[],l,a=_t.precision,b,c; 1082 //sl('writeLoc: We will write data to ['+_t.locFP+'].'); 1083 if(!_t.locFP)return; 1084 1085 for(i in _t.adr_to_loc) 1086 if(l=_t.adr_to_loc[i])try{ 1087 if(isNaN(b=Number(l.lat()))||isNaN(c=Number(l.lng())))throw new Error(1,'經緯度非數字'); 1088 t.push([b.toFixed(a),c.toFixed(a),i]); 1089 }catch(e){sl('writeLoc: Error: '+e.message+': ['+l+'] '+i+', ('+l.lat()+','+l.lng()+')');} 1090 1091 sl('writeLoc: '+_t.locArray.length+'→'+t.length); 1092 // 不相同時才作處理 1093 if(t.length!=_t.locArray.length){ 1094 t.sort(function(l,r){return l[0]-r[0]||l[1]-r[1];}); 1095 for(a=_t.locArray=[],b=_t.locArray_u={},c=[],i=0;i<t.length;i++) 1096 if(l=t[i]){ 1097 if((l[0]+','+l[1]) in b)sl('writeLoc: 重複住址@ '+l[0]+','+l[1]+': '+b[l[0]+','+l[1]][2]+' , '+l[2]); 1098 a.push(b[l[0]+','+l[1]]=l); 1099 c.push(l[0]+','+l[1]+' '+l[2]); 1100 //sl('writeLoc: '+i+'/'+t.length+' '+c[c.length-1]); // 多!! 1101 } 1102 //sl('writeLoc: '+(typeof simpleWrite)+','+_t.locFP); 1103 if(typeof simpleWrite=='function'){ 1104 c=c.join('\n'); 1105 if(typeof simpleRead=='function' && simpleRead(_t.locFP,'utf-8')==c) 1106 sl('writeLoc: 欲寫入之內容('+c.length+' chars)與標的檔相同。檔案並未變更。'); 1107 else 1108 //sl('writeLoc: Write '+c.length+' chars to ['+_t.locFP+'].'), 1109 simpleWrite(_t.locFP,c,'utf-8'); 1110 }//else sl('writeLoc: <em>Warning: function.js is not included?</em>'); 1111 } 1112 if(s)sl('<textarea>'+t.join('\n')+'</textarea>'); 1113 return t; 1114 }, 1115 // locArray2[]=[lat,lng,adr] sort by lat 給 getNeighbor() 用,包括所有不需要 search 的地址。 1116 locArray2:[], 1117 locArray_u:{},locArray2_u:{}, // 預防重複: locArray_u['lat,lng']=obj of locArray or locArray2 1118 /* 取得鄰近的地點: 經緯度, 最大距離(km) http://blog.ben.idv.tw/2007/06/blog-post.html http://hk.geocities.com/hk_weather/big5/others/calculators.html http://blog.xuite.net/joy715/blog/9285691 http://iask.sina.com.cn/b/6263160.html 1119 mapO.getNeighbor([22.620096,120.333381],"sl(n[i][1][2]);"); 1120 return: 1121 f.d: 距離(km) 1122 f.c: 最多取用點數,<=0:全取,未設:default 1123 f.s: 最後時選取與否的篩選設置之函數 傳回數值越大越後面 1124 f.D: 計算距離之函數,將用來比較 1125 default [ [較準確的距離, [lat,lng,adr]],.. ] 1126 求得較大概的距離(以距離平方比計算,比較快) 1127 [ [距離, [lat,lng,adr]],.. ] f.D=function(p,l){var a=l.lat()-p[0],b=l.lng()-p[1];return a*a+b*b;} 1128 1129 f.f: 對選出之 spot 作最後處置之函數 1130 default [ [較準確的距離, [lat,lng,adr]],.. ] 1131 傳回地址 [ adr1, adr2,.. ] f.f= 'n[i]=n[i][1][2];' 1132 傳回 obj [ [lat,lng,adr],.. ] f.f= 'n[i]=n[i][1];' 1133 求得較準確的距離 [ [距離, [lat,lng,adr]],.. ] f.f= 'n[i][1]=l.distanceFrom(n[i][1]);' 1134 */ 1135 getNeighbor:function(l,f){ 1136 var _t=this,lat,lng,i,n=[],p=function(A){ 1137 // 計算最接近上限mLat之loc 1138 // c: 誤差 1139 var i=0,j=A.length,a,b,c=f._d,mLat=lat-c; 1140 if(!j)return; 1141 //sl(mLat+'~'+(lat+c)); 1142 do{ 1143 //sl(Math.floor((i+j)/2)+'/'+A.length+','+A[a=Math.floor((i+j)/2)]); 1144 b=A[a=Math.floor((i+j)/2)][0]; 1145 if(b>mLat)j=a;else if(b<mLat)i=a; 1146 }while(i<j-1&&Math.abs(b-mLat)>c); 1147 //sl('start: from ['+a+'/'+A.length+'] '+A[a].join(':')+' to '+(lat+c));var tt=[]; 1148 for(i=a,mLat=lat+c,a=lng-c,b=lng+c;i<A.length&&A[i][0]<mLat;i++) 1149 if(/*tt.push('- '+A[i]+': '+a+','+c+','+b),*/c=A[i][1],a<c&&c<b) 1150 //n.push([j=f.D(A[i],l),A[i]]),sl('distance: '+j.toFixed(2)+' to '+A[i]); 1151 n.push([f.D(A[i],l),A[i]]); 1152 //sl(tt.join('<br/>')); 1153 }; 1154 if(typeof l=='string')l=l.split(','); 1155 //if(typeof l=='function'&&l.lat&&l.lng)lat=typeof l.lat=='function'?l.lat():l.lat,lng=typeof l.lng=='function'?l.lng():l.lng; 1156 if(l instanceof GLatLng)lat=l.lat(),lng=l.lng(); 1157 else lat=l[0],lng=l[1],l=new GLatLng(lat,lng); 1158 // 這邊起 l 為原始點之 GLatLng 1159 if(typeof f!='object') 1160 f=isNaN(f)?{f:f}:{d:f}; 1161 f._d=(f.d>0?f.d:20)/111; // 1度的實際長度~111公里。 1162 if(!f.D)f.D=function(p,l){ 1163 //var a=lat-p[0],b=lng-p[1];return a*a+b*b; // 大概的,比較快。 1164 return l.distanceFrom(new GLatLng(p[0],p[1])); // real distance 1165 }; 1166 1167 //sl(lng+', '+lat+'; '+f._d); 1168 _t.writeLoc(); 1169 p(_t.locArray); 1170 _t.locArray2.sort(function(l,r){return l[0]-r[0]||l[1]-r[1];}); 1171 //sl('['+_t.locArray2.length+']<br/>* '+_t.locArray2.join('<br/>* ')); 1172 p(_t.locArray2); 1173 //sl('Get '+n.length+' records near ('+lat+','+lng+').'); 1174 1175 // 由近至遠 sort 1176 if(typeof f.s=='undefined')f.s=function(l,r){return l[0]-r[0];}; // l, r: [distance by f.D,[lat,lng,adr]] 1177 if(f.s)n.sort(f.s); 1178 //for(i=0;i<n.length;i++)sl('getNeighbor '+i+': '+n[i][0]+' '+n[i][1][2]); 1179 if(typeof f.c=='undefined'||f.c&&!isNaN(f.c)&&n.length>f.c) 1180 n=n.slice(0,f.c>0?f.c:9); // 預設取 9 個 1181 1182 if(typeof f.f=='string')f.f=new Function('n','i','l',f.f); 1183 //sl('Run: [~'+n.length+'] by '+f.f); 1184 if(typeof f.f=='function') 1185 for(i=0;i<n.length;i++)f.f(n,i,l); 1186 1187 return n; 1188 }, 1189 /* 1190 http://econym.googlepages.com/geomulti.htm 1191 http://econym.googlepages.com/didyoumean.htm 1192 enum GGeoStatusCode http://code.google.com/intl/zh-CN/apis/maps/documentation/reference.html#GGeoStatusCode 1193 */ 1194 GeoStatus:function(c){ 1195 var m=this.GeoStatusM; 1196 if(!m){ 1197 this.GeoStatusM=m={ 1198 G_GEO_SERVER_ERROR:'伺服器錯誤。', 1199 G_GEO_MISSING_QUERY:'輸入空地址。', 1200 G_GEO_UNKNOWN_ADDRESS:'找不到指定地址的對應地理位置。可能地址比較新,無法解析地址,地址不正確,或者缺少該地址的數據。', 1201 G_GEO_UNAVAILABLE_ADDRESS:'由於合法性或合同原因,無法返回給定地址的地理位置信息。', 1202 G_GEO_BAD_KEY:'給定的密鑰無效或與給定的 host ('+window.location.host+') 不匹配。', 1203 G_GEO_TOO_MANY_QUERIES:'給定的密鑰超出了 24 小時的請求限制。', 1204 //404:'沒找到網頁', 1205 403:'Probably an incorrect error caused by a bug in the handling of invalid JSON', 1206 G_GEO_SUCCESS:'查詢成功' 1207 }; 1208 var i,v; 1209 for(i in m)try{ 1210 if(!isNaN(v=eval(i))&&v!=i)m[v]=m[i]; 1211 }catch(e){} 1212 } 1213 return m[c]||''; 1214 }, 1215 /* mapO.getLocations('taiwan',function(r){}); 1216 arguments: 1217 address, function([[lat,lng,adr], ..]), data object 1218 address, [deal function, (預設0: 傳入 [[lat,lng,adr], ..], 1: 傳入 GClientGeocoder.getLocations)], data object 1219 1220 http://code.google.com/apis/maps/documentation/services.html#Geocoding_Structured 1221 http://code.google.com/apis/kml/documentation/kmlreference.html#placemark 1222 http://www.step1.cn/googleapi/map/kml.htm#Placemark 1223 1224 to use: 1225 mapO.getLocations('宿舍',function(r){for(var i=0;i<r.length;i++)sl(i+'/'+r.length+' '+r[i]);}); 1226 */ 1227 getLocations:function(adr,func,d){ 1228 var _t=this,_f=arguments.callee,a 1229 // country code 1230 ,cc={TW:'台灣',US:'United States',JP:'日本',CN:'中国',KR:'大韓民國',KP:'朝鮮',UK:'United Kingdom'} 1231 ,ga=function(p){ // 從 Placemark 得到住址(address)資料 1232 var c,a,b; 1233 if(c=p.AddressDetails.Country){ 1234 b=c.CountryNameCode; 1235 a=b in cc?[cc[b]]:[]; 1236 if(c=c.AdministrativeArea){ 1237 if(b=c.AdministrativeAreaName)a.push(b); 1238 if(c=c.SubAdministrativeArea){ 1239 if(b=c.SubAdministrativeAreaName)a.push(b); 1240 if(c=c.Locality){ 1241 if(b=c.PostalCode)a.unshift('['+b.PostalCodeNumber+']'); 1242 if(b=c.LocalityName)a.push(b); 1243 if((c=c.Thoroughfare)&&(b=c.ThoroughfareName)) 1244 a.push(b); 1245 } 1246 } 1247 } 1248 a=a.join(' '); 1249 } 1250 return p.address?p.address+' ('+a+')':a; 1251 }, 1252 // 預設代為處理函數組 1253 f=[ 1254 // 代為處理傳入 [lat,lng,adr] 1255 function(r){ 1256 _f.errno=r.Status.code; 1257 if(r instanceof GLatLng) 1258 r=[[r.lat(),r.lng(),adr]]; 1259 else if(r.Status.code==G_GEO_SUCCESS){ 1260 //sl('getLocations: Get '+r.Placemark.length+' place(s) of ['+r.name+'].'); 1261 //sl('getLocations: [0]: '+r.Placemark[0].name); 1262 var i=0,n=r.name,p=r.Placemark,l,a; 1263 for(r=[];i<p.length;i++) 1264 a=ga(p[i])||n+'('+i+')', 1265 //sl('getLocations: ('+p[i].Point.coordinates+') '+a), 1266 l=p[i].Point.coordinates,r.push([l[1],l[0],a]), 1267 _t.adr_to_loc[a]=new GLatLng(l[1],l[0]); 1268 }else r=[]; 1269 return func(r); 1270 } 1271 , 1272 // 代為處理傳入 Placemark 1273 function(r){ 1274 _f.errno=r.Status.code; 1275 if(r instanceof GLatLng) 1276 r={Status:{code:G_GEO_SUCCESS},Placemark:[{Point:{coordinates:[r[0],r[1]]}}]}; 1277 else if(r.Status.code==G_GEO_SUCCESS){ 1278 //sl('getLocations: find '+r.Placemark.length+' records, '+r.Placemark[0].Point.coordinates+': '+r.Placemark[0].address); 1279 //if(r.Placemark.length==1){var l=r.Placemark[0].Point.coordinates;_t.adr_to_loc[r.Placemark[0].address]=new GLatLng(l[1],l[0]);} 1280 for(var i=0,n=r.name,p=r.Placemark,l;i<p.length;i++)l=p[i].Point.coordinates,_t.adr_to_loc[ga(p[i])||n+'('+i+')']=new GLatLng(l[1],l[0]); 1281 }//else sl('getLocations: search ['+adr+'] fault: ['+r.Status.code+'] '+_t.GeoStatus(r.Status.code)); 1282 return func(r); 1283 } 1284 ]; 1285 1286 if(func instanceof Array) 1287 a=func[1], 1288 f= typeof a=='function'?a 1289 :typeof a=='number' && f[a]? f[a] 1290 :f[0], 1291 func=func[0]; 1292 else f=f[0]; 1293 1294 return _t.getLatLng(adr+='',[f,function(){return _t.geocoder.getLocations(adr,f);}],d); 1295 }, 1296 1297 // 直接手動設定 1298 setLatLng:function(adr,lat,lng){ 1299 if(!(lat instanceof GLatLng))lat=new GLatLng(lat,lng); 1300 this.adr_to_loc[adr]=lat; 1301 sl('setLatLng: '+lat+' '+adr); 1302 return lat; 1303 }, 1304 1305 // 經緯度 RegExp 1306 LatLngR:/^\s*(\d{1,3}(\.\d+)?)\s*,\s*(\d{1,3}(\.\d+)?)\s*$/, 1307 1308 /* 未設定 func 則僅回傳 catched 的位置 1309 You can define your method by .f(adr,c), for example: search by specified SQL server. 1310 1311 TODO: 1312 繼承 Geocoding cache 1313 */ 1314 getLatLng:function(adr,func,d){ // string address, (function(GLatLng) | [func:function(GLatLng), deal_func:function(GLatLng, address)]), data object 1315 //if(!adr)return; 1316 var _t=this,m,a,f,c=function(p){ 1317 if(!p)sl('getLatLng: Search failed: '+adr); 1318 if(p)_t.adr_to_loc[adr]=p; 1319 return func(p,adr)||p; 1320 }; 1321 1322 if(func instanceof Array) 1323 f=func[1],func=func[0]; 1324 1325 //sl('getLatLng: search '+adr); 1326 // 檢測是否為經緯度 1327 if(m=adr.match(_t.LatLngR)){ 1328 if(a=(d&&d.description||adr).replace(/<[/]?([bh]r|p)[^>]*>/ig,'\n').match(/[^\r\n]+[市區街路][^\r\n]+/)) 1329 //sl('('+m[1]+','+m[3]+') ['+a[0]+']'), 1330 if(!((m[1]+','+m[3]) in _t.locArray2_u))_t.locArray2.push(_t.locArray2_u[m[1]+','+m[3]]=[m[1],m[3],a[0]]); 1331 m=new GLatLng(m[1],m[3]); 1332 return func?func(m):m; 1333 // 搜尋已知地址 1334 }else if((m=_t.adr_to_loc) && (m=m[adr]))//sl('getLatLng: deal adr_to_loc['+adr+']='+m[adr]+' get '+(func?c(m):m)/*+' by '+func*/), 1335 return func?c(m):m; 1336 // 搜尋未知地址 1337 else if(func) 1338 //sl('<em>Not catched: '+adr+'</em>'), 1339 return _t.geocoder.getLatLng(adr,typeof f=='function'?function(m){f(m,adr);}:c); // 原來需要用 arguments.callee.f,但若已經用 var 定義則可直接使用。 1340 }, 1341 1342 /* 1343 d={ 1344 name:'', // 這邊 name 被當作 id, title 1345 description:'HTML', 1346 type:'', 1347 data:['',''] 1348 //選用 optional: 1349 htm:'HTML' / function(obj){return 'HTML';}, 1350 //尚未用到︰ 1351 link:'', 1352 }; 1353 */ 1354 add:function(d,force){ 1355 if(!d||typeof d!='object')return this; 1356 var _t=this,o=_t.supportKind(d.type); 1357 if(!o){_t.searchPoint(d);return this;} 1358 1359 //if(!(d.type in _t))_t[d.type]={}; 1360 var _S=_t[d.type],_m=this.map,a; 1361 //if(typeof _S!='object')sl('add: typeof ['+d.type+'] = '+(typeof _S)); 1362 if((d.name in _S)&&!force)return this; // 已存在 1363 1364 o=_t.kinds[d.type]; 1365 if(d.type=='marker'){ 1366 o=new o(new GLatLng(d.data[0],d.data[1]),_t.getMarkerO(d)); 1367 //if(d.zIndexP)o._zIndexProcess=o._zIndexProcess,o.zIndexProcess=d.zIndexP; // ** 可以利用 zIndexP 來在 infowindow is opened 時設定 z-index.. 沒用 @ 2008/6/30 19:43:43 1368 }else a={points:d.data[0],levels:d.data[1],numLevels:4,zoomFactor:16}, 1369 o=new o.fromEncoded(d.type=='polyline'?a:{polylines:[a],fill:true,outline:true});//geodesic:true Geodesic means 'along great circle' 1370 1371 _t._add(o,d); 1372 1373 return this; 1374 }, 1375 searchPoint:function(adr,name,description){ 1376 var _f=arguments.callee,_t=this,type='marker',_M=_t[type],_m=_t.map,latlng,d; 1377 if(typeof adr=='object')d=adr,adr=adr.type;else d={type:adr,show:_f.show}; // 預設 searchPoint.show 1378 if(!d.name)d.name=name||adr; 1379 if(typeof d.description=='undefined') 1380 if(description)d.description=description; 1381 else if(d.name!=adr)d.description=adr; 1382 if(!d.type)return; 1383 if(isNaN(d.retry)&&!_t.geocoder.getCache())d.retry=2; // 找不到時重試次數 1384 1385 try{ 1386 if(adr in _M) 1387 //sl('We already have ['+adr+']'), 1388 _t.show(_M[adr],type); 1389 else _t.getLatLng(adr+='',function(point){ 1390 if(!point){ 1391 if(d.retry){ 1392 //sl('try once more('+d.retry+'): ['+adr+']'); 1393 d.retry--,_f.call(_t,d);return false; 1394 }else{ 1395 //sl('searchPoint: not found function: '+_t.notFound); 1396 return _t.notFound(adr,d); 1397 } 1398 }else{ 1399 if(adr in _M)return; // 可能經過太久才被 load? 1400 1401 // ** 注意:這邊沒設 _M[adr]=_M[point.toUrlValue(_t.precision)] 1402 var p=new GMarker(latlng=point,_t.getMarkerO(d)); 1403 if(!d.name)d.name=p+''; 1404 //sl('found '+point+' '+d.name+', icon: '+p.getIcon().iconSize); 1405 //point='loc: '+point; 1406 if(typeof d.description=='undefined')d.description=point; 1407 else if(d.description==adr)d.description+='<br/>'+point; 1408 //_M=_M[adr]; 1409 if(!('address' in p))p.address=adr;else throw 'GMarker.address was used: ['+p.address+']!'; 1410 //sl('Last add '+adr+'..'); 1411 _t._add(p,d,type); 1412 } 1413 },d); 1414 }catch(e){sl('searchPoint: Error: '+adr+', '+_M+': '+e.message);}//throw e; 1415 return latlng; 1416 }, 1417 // private: 註冊 o 成為內容 d={},並設定 click 等 event 1418 _add:function(o,d,type){ 1419 if(!o)return; 1420 if(!d)d=o; 1421 if(!type)type=d.type; 1422 var _t=this,_S=_t[type],_m=this.map; 1423 1424 //if(d.name)_S[d.name]=o; // 或許已經設定過了,這邊就需要跳過。 1425 if(((d.name||d)+'') in _S) 1426 sl('_add: Warning: Type '+type+' 已存在 ['+(d.name||d)+']'+(_S[d.name||d].getLatLng?' '+_S[d.name||d].getLatLng():'')+('address' in _S[d.name||d]?' '+_S[d.name||d].address:'')+'!' 1427 //+'<br/>_add: 將以 ['+(o.name||d.name||d)+']'+(o.getLatLng?' '+o.getLatLng():'')+(o.address?' '+o.address:'')+' 覆寫。' 1428 ); 1429 _S[d.name||d]=o; // 必設!! 1430 1431 if(_m)_m.addOverlay(o);//_t._addOverlay(o);// 1432 //if(o.getIcon)sl('_add: show '+o.getIcon().iconSize+' '+o.getIcon().image); 1433 o.name=d.name,o.dscr=d.description; // GMarker 中這兩個本來就有被用,偵測也只會發現已使用。 1434 // another way to add tooltip: GControlPosition 1435 if(!('sHtm' in o)){ 1436 if('htm' in d)o.sHtm=typeof d.htm=='function'?d.htm.call(d,o,type):d.htm; 1437 else o.sHtm=(d.name?'<em>'+d.name+'</em>'+(d.description?'<br/>':''):'')+(d.description||''); 1438 }else throw '['+type+'].sHtm was used: ['+o.sHtm+']!'; 1439 if(!('sHtmF' in o)){ 1440 if('htmF' in d) 1441 o.sHtmF=typeof d.htmF=='function'?d.htmF.call(d,o,type):d.htmF; 1442 }else throw '['+type+'].sHtmF was used: ['+o.sHtm+']!'; 1443 // openInfoWindowTabs: http://www.geocodezip.com/mapXmlTabsPlus.asp 1444 GEvent.addListener(o,"click",function(e){ 1445 _t.showWindow(o); 1446 _t.runOnClick(o,type,e&&e.target||window.event&&window.event.srcElement); 1447 }); 1448 if(d.show)_t.show(o,type); 1449 _t.runAfterAdd(o,type,d,d.name||d); 1450 }, 1451 _addOverlay:function(o){ 1452 var _t=this,_m=_t.map; 1453 if(!_t._aa)_t._aa=[]; 1454 if(o){_t._aa.push(o);return;} 1455 var i; 1456 while(i=_t._aa.shift()) 1457 _m.addOverlay(i); 1458 }, 1459 1460 1461 // icon setup 1462 defaultIconIndex:0, 1463 iconA:[], 1464 iconO:{}, 1465 icon:function(index){ 1466 var _t=this; 1467 if(index instanceof Array){ 1468 // 設定 icon 1469 _t.iconA=[]; 1470 for(var a,i=0,p,u;i<index.length;i++) 1471 if(u=index[i]){ 1472 if(typeof u=='string')u={image:u}; 1473 if(u instanceof Object){ 1474 _t.iconA.push(a=new GIcon(u.icon||_t.iconA[_t.defaultIconIndex]||G_DEFAULT_ICON)); 1475 //a=new GIcon(u.icon||_t.iconA[_t.defaultIconIndex]||G_DEFAULT_ICON); 1476 for(p in _t.iconO) // default first 1477 //sl('icon: set icon['+(_t.iconA.length-1)+'].'+p+'='+_t.iconO[p]), 1478 a[p]=_t.iconO[p]; 1479 if('temp' in u){ // template 2 1480 for(p in u.temp) 1481 //sl('icon: template set icon['+(_t.iconA.length-1)+'].'+p+'='+u.temp[p]), 1482 a[p]=u.temp[p]; 1483 delete u.temp; 1484 } 1485 for(p in u) // user set last 1486 //sl('icon: specified set icon['+(_t.iconA.length-1)+'].'+p+'='+u[p]), 1487 a[p]=u[p]; 1488 //_t.iconA.push(new GIcon(a)); 1489 }else _t.iconA.push(u); 1490 } 1491 return _t.iconA.length; 1492 } 1493 1494 if(index instanceof Object){ 1495 //sl('icon: set default icon option'); 1496 for(i in index) 1497 //sl('icon: set default ['+i+']=['+index[i]+']'), 1498 _t.iconO[i]=index[i]; 1499 return; 1500 } 1501 1502 // return icon[index] 1503 if(isNaN(index)||index<0||index>=_t.iconA.length)index=_t.defaultIconIndex; 1504 //sl('icon: return icon['+index+'] '+(_t.iconA[index]?_t.iconA[index].iconSize+' '+_t.iconA[index].image:'G_DEFAULT_ICON')); 1505 return _t.iconA[index]||G_DEFAULT_ICON; 1506 }, 1507 1508 dMarkerO:{}, // default marker option 1509 getMarkerO:function(mo,setMO){ // setMO: set default, 1: add, 2:reset 1510 var _t=this,i,a={icon:1,title:1,zIndexProcess:1,draggable:1}; // class GMarkerOptions 1511 _t.dMarkerO.icon=_t.icon(); 1512 if(setMO){ 1513 if(setMO==2)_t.dMarkerO={}; 1514 setMO=_t.dMarkerO; 1515 }else{ 1516 // 複製一份 1517 setMO={}; 1518 for(i in _t.dMarkerO) 1519 //sl('getMarkerO: from default ['+i+']=['+_t.dMarkerO[i]+']'), 1520 setMO[i]=_t.dMarkerO[i]; 1521 } 1522 if(mo instanceof Object) 1523 for(i in a) 1524 if(a[i]&&typeof mo[i]!='undefined') 1525 //sl('getMarkerO: set ['+i+']=['+mo[i]+']'), 1526 setMO[i]=mo[i]; 1527 //sl('getMarkerO: icon: '+setMO.icon.iconSize+' '+setMO.icon.image); 1528 return setMO; 1529 }, 1530 1531 /* 增加自己控制的 marker,會自動顯現,但不會列入管控,得自己設定。 1532 usage: 1533 mapO.addMarker(dLoc.tw,{draggable:true}); 1534 */ 1535 addMarker:function(loc,opt){ 1536 var _t=this,_m=_t.map,m; 1537 if(_m){ 1538 if(loc instanceof Array)loc=new GLatLng(loc[0],loc[1]); 1539 m=new GMarker(loc,_t.getMarkerO(opt)); 1540 //sl('addMarker icon: '+_t.getMarkerO(opt).icon.iconSize+' '+_t.getMarkerO(opt).icon.image); 1541 //sl('addMarker iconSize: '+_t.getMarkerO(opt).iconSize); 1542 _m.addOverlay(m); 1543 } 1544 return m; 1545 }, 1546 1547 1548 // f={p:position, m:method(pan/panBy/set), z:zoom} 1549 setCenter:function(f){ 1550 var _m=this.map; 1551 if(f instanceof GLatLng||!(f instanceof Object))f={p:f}; 1552 1553 //sl('setCenter: setZoom ['+(f.z||null)+'] @ '+f.p+' by method ['+(f.m||'setCenter')+'].'); 1554 if(!isNaN(f.z))_m.setZoom(f.z); 1555 if(f.p){ 1556 if(f.p instanceof Array)f.p=new GLatLng(f.p[0],f.p[1]); 1557 if(f.m=='pan')_m.panTo(f.p); 1558 else if(f.m=='panBy')_m.panBy(f.p); 1559 else _m.setCenter(f.p); 1560 } 1561 1562 return _m.getCenter(); 1563 }, 1564 1565 // zoom above 19 You can set zoom up to 30 by using setCenter() not by setZoom() or zoomIn() firefox: 45.1238,-123.1138 http://esa.ilmari.googlepages.com/highres.htm 1566 /* 1567 eval('err_noImage=p(10121);',mapO.map); 1568 sl(err_noImage); 1569 */ 1570 zoom:function(z){ 1571 var _t=this,_m=_t.map,m; 1572 if(typeof z=='string'&&(m=z.match(/^[+-]/)))z=_m.getZoom()+(m[0]=='+'?z:-z); 1573 if(z)_m.setZoom(z);//try{_m.setCenter(_m.getCenter(),z);}catch(e){} // 中文中, enableContinuousZoom()? 這麼搞會出錯 1574 return _m.getZoom(); 1575 }, 1576 // show, or focus. f={noCenter:false, redraw: false} 1577 show:function(name,type,f){ 1578 var _t=this,_S=_t[type],_m=_t.map,inC; // inC: in control 1579 if(typeof name=='string') 1580 if(name in _S)_S=_S[name],inC=1; 1581 else _S=0; 1582 else _S=name; 1583 if(typeof _S!='object'||!_S)return; 1584 1585 if(_S.isHidden&&_S.isHidden())_S.show(); 1586 if(typeof f!='object')f={noCenter:f}; // default: don't set to center 1587 if(!f.noCenter){ 1588 var p=_t.getPoint(_S,type); 1589 //sl('show: center= '+p); 1590 if(_m){ 1591 _m.setCenter(p); 1592 if(_m.getZoom()<9)_m.setZoom(_t.defaultZoom); 1593 if(inC)_t.showWindow(_S,p); // 未管控就 showWindow 會有奇妙的結果。 1594 } 1595 } 1596 if(f.redraw && _S.redraw)_S.redraw(true); // Front/back order of markers can be messed simply by moving them in south-north direction. (v1) http://koti.mbnet.fi/ojalesa/exam/anim_v2.html 1597 1598 return _t; 1599 }, 1600 1601 // show HTML window (obj, point) o.sHtmF=show HTML flag: {maxContent:'', ..}: see class GInfoWindowOptions 1602 showWindow:function(o,p){ 1603 //sl('showWindow: '+(p||o.openInfoWindowHtml)); 1604 if(typeof o.openInfoWindowHtml=='function') 1605 o.openInfoWindowHtml(o.sHtm,o.sHtmF); // enableMaximize() 1606 else this.map.openInfoWindowHtml(p||this.getPoint(o),o.sHtm,o.sHtmF); 1607 }, 1608 1609 // get the GLatLng of the object 1610 getPoint:function(o,type){ 1611 if(!type)type=this.getKind(o); 1612 //sl('getPoint: ['+type+']'+o.name); 1613 if(type=='marker'&&typeof o.getLatLng=='function')return o.getLatLng(); 1614 1615 if(typeof o.getBounds=='function') 1616 return o.getBounds().getCenter(); 1617 1618 if(typeof o.getCenter=='function') 1619 return o.getCenter(); 1620 1621 if(typeof o.getVertexCount=='function') 1622 return o.getVertex(Math.floor(o.getVertexCount()/2)); 1623 }, 1624 1625 getZoom:function(o,type){ 1626 if(!type)type=this.getKind(o); 1627 //sl('getPoint: ['+type+']'+o.name); 1628 1629 if(typeof o.getBounds=='function') 1630 return this.getBoundsZoomLevel(o.getBounds()); // 得到適當的 zoom 1631 1632 //if(type=='marker')return _t.defaultZoom; 1633 return _t.defaultZoom; 1634 }, 1635 1636 1637 initMap:function(id,latlng,f){ // container, center, other initial setting flags 1638 var _t=this,_m,a; 1639 // 檢查當前瀏覽器是否支持地圖 API 庫 1640 if(GBrowserIsCompatible()){ 1641 // 指定GMap使用的圖層 @ id 1642 if(typeof id=='string')id=document.getElementById(id); 1643 if(!id)return _t; 1644 _t.canvas=id; // container object 1645 1646 if(!f)f={}; 1647 if(!f.size) 1648 f.size=f.x&&f.y?[f.x,f.y]:[640,320]; 1649 if(f.size instanceof Array)f.size=new GSize(f.size[0],f.size[1]); 1650 1651 _m=_t.map=new GMap2(id,f);//=new google.maps.Map2(); 1652 // 設定中心點座標 1653 _m.setCenter(latlng||new GLatLng(0,0),7); // default center. 1654 //_m.setMapType(G_HYBRID_MAP); 1655 _m.addMapType(G_PHYSICAL_MAP); // 地形圖 1656 _m.addMapType(G_SATELLITE_3D_MAP); // with the Google Earth Browser Plug-in 1657 1658 // 控制元件 客制化: http://julian.norway.idv.tw/index.php/archives/322 1659 //_m.addControl((new GHierarchicalMapTypeControl()).addRelationship(G_SATELLITE_MAP, G_HYBRID_MAP, "Labels", true)); 1660 //_m.removeMapType(G_HYBRID_MAP); 1661 _m.addControl(a=_t.overviewMap=new GOverviewMapControl(new GSize(_m.getSize().width/2.5,_m.getSize().height/2))); // 可折疊的縮小圖 1662 a.hide(); // show(), hide(). 1663 //a.getOverviewMap().addControl(new GMenuMapTypeControl(1)); must use setTimeout: getOverviewMap() is not available until after the module has loaded. 1664 _m.addControl(new GLargeMapControl()); // 加入地圖縮放工具 1665 _m.addControl(new GMenuMapTypeControl(1));//GMapTypeControl(1) // 切換地圖型態的按鈕 1666 _m.addControl(new GScaleControl()); // 地圖比例尺 1667 _m.enableScrollWheelZoom(); 1668 _m.enableContinuousZoom(); // 平滑放大 1669 1670 GEvent.addListener(_m,'mouseover',function(){_m.showControls();}); 1671 GEvent.addListener(_m,'mouseout',function(){_m.hideControls();}); 1672 }else{ 1673 sl('<em>抱歉,您的瀏覽器不支援 Google Maps!</em>'); 1674 } 1675 return _t; 1676 }, 1677 1678 1679 1680 /* 移除所有管控項 1681 c.f., this.map.clearOverlays() 1682 */ 1683 removeAll:function(type){ 1684 var _t=this,i,o; 1685 if(!type)for(i in _t.kinds) 1686 arguments.callee.call(this,i); 1687 else{ 1688 //sl('removeAll: ('+(typeof type)+') ['+type+'], '+(typeof _t[type])); 1689 //o=[];for(i in _t[type])o.push(i);for(i=0;i<o.length;i++)_t.remove(o[i],type); 1690 for(i in _t[type])_t.remove(i,type); 1691 //_t[type]={}; 1692 } 1693 }, 1694 1695 remove:function(n,type){ 1696 var _S=this[type]; 1697 if(n in _S){ 1698 //sl('remove '+type+' ['+n+']: '+(_S[n].name||_S[n].address||_S[n].dscr)); 1699 this.map.removeOverlay(_S[n]); 1700 delete _S[n]; 1701 this.runAfterRemove(n,type); 1702 } 1703 return this; 1704 }, 1705 1706 // http://econym.googlepages.com/example_context.htm 1707 setContextMenu:function(o){ 1708 var _t=this,_m=_t.map,h; 1709 if(!_m)return; 1710 1711 if(typeof o!='object'){ 1712 h=o; 1713 o=document.createElement('div'); 1714 o.className='gMap_contextMenu'; 1715 o.innerHTML=h; 1716 } 1717 1718 if(_t.contextMenu) 1719 _t.contextMenu.replaceNode(o); 1720 else _m.getContainer().appendChild(_t.contextMenu=o) 1721 ,GEvent.addListener(_m,'singlerightclick',function(p,t){ 1722 _t.clickLatLng=_m.fromContainerPixelToLatLng(_t.clickPoint=p); 1723 var x=p.x,y=p.y,w=_m.getSize().width-o.offsetWidth,h=_m.getSize().height-o.offsetHeight; 1724 if(x>w&&w>0)x=w; 1725 if(y>h&&h>0)y=h; 1726 (new GControlPosition(G_ANCHOR_TOP_LEFT,new GSize(x,y))).apply(o); 1727 _t.showContextMenu(1); 1728 }); 1729 1730 _t.showContextMenu(0); 1731 return o; 1732 }, 1733 showContextMenu:function(v){ 1734 var o=this.contextMenu; 1735 if(o)o.style.visibility=v||typeof v=='undefined'?'visible':'hidden'; 1736 }, 1737 1738 // get overlay 1739 getO:function(type,name){ 1740 var s=this[type]; 1741 return name?name in s?s[name]:null:s; 1742 }, 1743 1744 // get name of the type 1745 getOArray:function(type){ 1746 var i,a=[],o=this[type]; 1747 if(o)for(i in o)a.push(i); 1748 return a; 1749 }, 1750 1751 1752 /* 1753 var i,t=[],o; 1754 o=GGeoXml.prototype;//GMap2.prototype 1755 sl('['+(typeof o)+'] '+(o+'').replace(/\n/g,'<br/>')+'<hr/>',1);for(i in o)t.push('['+(typeof o[i])+'] '+i);sl(t.sort().join('<br/>')); 1756 1757 TODO: 1758 GEvent.addListener(map,"addoverlay",function(overlay){if(overlay.name){}}); 1759 */ 1760 loadXML:function(URL){ 1761 var _t=this,x=new GGeoXml(URL); 1762 // .getDefaultCenter(), .getDefaultBounds() 可能是 null 1763 _t.setCenter({p:x.getDefaultCenter(),z:x.getDefaultBounds(),m:'pan'}); 1764 _t.map.addOverlay(x); 1765 return _t.xml[URL]=x; 1766 }, 1767 1768 //resize map 1769 resize:function(x,y){ 1770 with(this.map.getContainer().style) 1771 width=x+'px',height=y+'px'; 1772 }, 1773 1774 // 去除商標, Copyright message 1775 removeTM:function(l){ 1776 var a=this.canvas; 1777 if(!a)return; 1778 a=a.getElementsByTagName('a'),i=a.length,t=1; 1779 //sl('removeTM: '+UnicodeToHTML(document.getElementById('map_canvas').innerHTML)); 1780 for(;i>0&&(t||l);){ 1781 i--; 1782 // http://www.google.com/intl/en_ALL/help/terms_maps.html 1783 if(t && a[i].href.indexOf('terms_maps')!=-1 && a[i].parentNode.tagName.toLowerCase()=='div'){ 1784 //sl('removeTM: remove copyright: '+a[i].href); 1785 //sl('removeTM: remove copyright: '+UnicodeToHTML(a[i].parentNode.innerHTML)); 1786 removeNode(a[i].parentNode,1); // 連這div都刪除會有奇怪現象發生 1787 t=0; 1788 }else if(l && a[i].innerHTML.indexOf('poweredby.png')!=0){ 1789 //sl('removeTM: remove logo: '+UnicodeToHTML(a[i].parentNode.innerHTML)); 1790 removeNode(a[i].parentNode,1); 1791 l=0; 1792 } 1793 } 1794 } 1795 1796 }; // _.prototype= 1797 1798 1799 return _; 1800 })(); // (function(){ 1801 1802 // =================================================== 1803 1804 1805 /* 2008/9-10/1 1806 搜尋用代理工具 1807 1808 usage: 1809 google.load("search","1",{language:"ja_JP",callback:loadSearch}); 1810 function loadSearch(){ 1811 gSearch=new getSearch(function(r,p){ 1812 sl('<a href="'+r.unescapedUrl+'">'+r.title+'</a><br/><div style="margin-left:3em;font-size:.8em;">'+r.content+'</div>'); 1813 }); 1814 } 1815 1816 1817 TODO: 1818 Yahoo! Search BOSS http://developer.yahoo.com/search/boss/ 1819 1820 LocalSearch: 1821 http://www.google.com/uds/samples/apidocs/static-tiles.html 1822 http://code.google.com/apis/ajaxsearch/documentation/reference.html#_class_GlocalSearch 1823 1824 */ 1825 function getSearch(fn,kind){ // deal function, kind: Web/Local 1826 if(!kind)kind='Web'; 1827 var _t=this,_s=typeof google!='undefined'?google.search:0; 1828 if(!_s||!_s[kind+'Search'])return; 1829 _s=_t.searcher=new _s[kind+'Search'](); 1830 1831 if(kind=='Local'){ 1832 //sl('Set center: '+'Taiwan'); 1833 _s.setCenterPoint('台灣');//Taiwan 1834 _s.setResultSetSize(google.search.Search.LARGE_RESULTSET); 1835 //_s.setCenterPoint("93108"); 1836 }else{ 1837 _s.setNoHtmlGeneration(); 1838 //.addSearcher(_s,(new google.search.SearcherOptions()).setExpandMode(google.search.SearchControl.EXPAND_MODE_OPEN)); 1839 } 1840 1841 _s.setResultSetSize(google.search.Search.LARGE_RESULTSET); 1842 1843 _s.setSearchCompleteCallback(_t,_t.searchComplete[kind],[_s]); 1844 if(fn)_t.sf=fn; 1845 return _t; 1846 } 1847 getSearch.prototype={ 1848 1849 // country translate 1850 countryT:{Taiwan:'台灣'}, 1851 1852 searchComplete:{ 1853 Local:function(searcher){ 1854 var r=searcher.results,i=0,a,b,j; 1855 if(r&&r.length>0)for(;i<r.length;i++){ 1856 o=r[i],a=o.country; 1857 if(a in this.countryT)a=this.countryT[a]; 1858 o.address=a+o.region+o.city+o.streetAddress; 1859 o.phone=[]; 1860 if(a=o.phoneNumbers) 1861 for(j=0;j<a.length;j++) 1862 o.phone.push((a[j].type?a[j].type+': ':'')+a[j].number); 1863 this.sf(o); 1864 } 1865 /* 1866 var imageUrl = GlocalSearch.computeStaticMapUrl(searcher.results, 350, 400); 1867 document.getElementById("resultsImg").src = imageUrl; 1868 */ 1869 }, 1870 Web:function(searcher){ 1871 var s=searcher,p=s.cursor.currentPageIndex,i=0,r=s.results; 1872 //sl('<hr/>page '+p+':'); 1873 if(r&&r.length)for(;i<r.length;i++) 1874 this.sf(r[i],p); 1875 s.gotoPage(p+1); // 這會一直執行到不能執行為止。(2008/7: 0-3) 1876 } 1877 1878 }, // searchComplete 1879 1880 1881 // deal function 1882 sf:function(r,p){ 1883 }, 1884 1885 s:function(w){ 1886 //sl('getSearch: search ['+w+']'); 1887 if(w)this.searcher.execute(w); 1888 } 1889 1890 }; 1891 1892 1893 1894 1895 1896 1897 1898 1899 return ( 1900 CeL.net.map 1901 ); 1902 }; 1903 1904 //=================================================== 1905 1906 CeL.setup_module(module_name, code_for_including); 1907 1908 }; 1909