Rev. 2.73

 

다음에서도 지도 API를 오픈했습니다. 경품 이벤트도 하는군요. 응모도 해볼 겸해서 지난번 "네이버 맵 API 사용해 보기"에서 만들었던 클래스를 이용하여 추가로 만들어 보았습니다. 이 클래스를 사용하면 다음과 네이버의 지도를 모두 다룰수 있습니다. 다음 예제를 보시죠.

Examples:

  new Maps('my-maps', {
    type: 'daum', // naver or daum 
    key: '1176f6b78db03f01382ee44afe2f82b98b8b899d', // API Key
    point: {x: 498637, y: 1116620, z: 3}, // coordinates and zoom level

    framesrc: 'robots.txt', // for validate url

    zoomwheel: true, // add whell zoom controll(default true)
    zoomslide: true, // add zoom controll(default true)
    minimap: true, // add index map controll(default true)

    // callback for customize
    callback: function(map, win) {
      win.DEvent.addListener(map, "move", function() { 
        var center = map.getCenter(); 
        debug.info("latitude : " + center.getLat() + "  longitude: " + center.getLng());
      });
    }
  });

type, key, point는 필수 옵션이며, 각각 API종류, API키, 좌표 및 줌레벨 값을 입력합니다. framesrc에 값을 넣게 되면, iframe을 사용해서 구동하게 됩니다. 이것은 특정 API에서 발생하는 Doctype으로 인한 문제 또는 스크립트 충돌 등을 해결하기 위한 것입니다. iframe 사용시 주의할 점은 임의의 파일을 호출할 수 있는 경로를 기입해야 합니다. 'about:blank'와 같이 지정되어버리면 location.hostname을 호출할 수 없어 맵 API의 URL 인증이 무효가 되어버리기 때문입니다.

이번 작업에서 발생한 가장 큰 문제는 maps.js가 들고있는 유틸리티 함수들이었습니다. 지도 API 시작하기 예제문서를 보면 DIV 요소에 지도를 추가하는 예제들이 있습니다. 이런 경우 루비온레일즈나 Prototype 라이브러리를 사용하는 사이트들에서는 충돌을 일으킬 가능성이 매우 높습니다. Prototype의 코드들을 상당수 오버라이딩 해버리기 때문이지요. 결국 다음 지도 API역시 iframe으로 처리해야 했습니다. 전역스러운(?) 네임스페이스의 소비를 개선하거나 충돌이 일어날만한 코드들은 우회하는 추가작업이 필요해 보입니다.

다음 지도 API의 장점은 구글맵의 좌표계인 WGS84, 네이버 지도의 좌표계인 KTM 등 자바스크립트 기반 좌표계변환기를 사용할 수 있어 여러 서비스간 호환성이 좋다는 점과, SVG를 사용하여 매우 빠른 사선 그리기와 마킹작업이 가능한 것을 들수 있겠습니다. 다음 번에는 구글과 야후 맵도 통합하고, API간의 좌표 변환기와, 마킹 컨트롤러 등을 추가해 보겠습니다. 요것 가지고 노는 재미가 점점 쏠쏠해 지는군요.

Downloads:

* original - maps.js (40.1kb)
* minified - maps.js (29.2kb)

Updates:

* 2008.11.18 IE에서 프레임이 생성되지 않던 문제를 수정하였습니다.
* 2008.11.19 KTM/WGS84간 좌표계 변환기를 추가하여 두 지도가 하나의 위치를 공유할 수 있습니다.
* 2008.11.20 WGS84에서 KTM 좌표계로 변환시 오차를 야매로(?) 수정하였습니다.
* 2008.12.01 구글맵 API가 추가되었습니다.
* 2008.12.02 구글맵과 네이버, 다음 지도간 줌레벨 변환기가 추가되었습니다.
* 2009.02.04 구글맵 역시 아이프레임을 사용하지 않고도 로드 할 수 있습니다.
* 2009.02.04 구글지도에 한글 서비스를 반영하였습니다.
* 2009.02.05 한국지역 외 상세 지도가 나오지 않는 현상을 수정하였습니다.
* 2009.02.08 다음지도의 기본 좌표계를 'congnamul'로 변경하였습니다.
* 2009.02.08 좌표계 변환기에 'congnamul' 변환식이 추가되었습니다.
* 2009.02.10 구글맵 로드시 GClientGeocoder(지역 찾기)클래스를 map 인스턴스에 포함합니다.
* 2009.02.10 길찾기와 관련된 Maps.Route 클래스가 추가되었습니다.
* 2009.02.11 구글어스의 헬퍼 클래스인 Maps.Earth가 추가되었습니다.

Comments

얼마전 구글에서 배포한 크롬 브라우저를 테스트 해보았습니다. 엘리먼트 드로잉 속도는 정말 쾌적하더군요. 하지만 매우 심각한 오류가 유발 될 소지가 있어 이를 해결하고자 제가 처한 환경에서 여러가지 삽질을 해봅니다. 우선 조취해야 한 것은, navigator.userAgent 정보가 AppleWebKit(사파리)과 거의 흡사하여 사파리에 대한 예외처리로 집입할 우려가 있어 아래와 같은 코드를 추가했습니다.

크롬 브라우저 및 사파리 브라우저 발견 스크립트 추가 및 오버라이딩 :

Prototype.Browser.Chrome = !!navigator.userAgent.match('Chrome');
Prototype.Browser.WebKit = !Prototype.Browser.Chrome && Prototype.Browser.WebKit;

위와 같은 조치만으로 다수의 오류를 잡을 수 있었습니다. 특히 LazyLoader와 같은 동적 자바스크립트 호출을 사용한다면 무한 루프로 진입하게 되는 현상을 방지할 수 있습니다. 두번째 문제로는 크롬 브라우저가 특정 사이트에 처음 액세스 하는 경우(케시되기 전)에는 정상적으로 작동합니다만, 케시되고 난 후에 Prorotype이 먹통이되는 사태가 발생합니다.

chrome.jpg
정상적인 초기화
chrome-error.jpg
초기화 오류
typeof Element.prototype.setStyle; // "undefined"
typeof HTMLElement.prototype.setStyle; // "undefined"
typeof HTMLDivElement.prototype.setStyle; // "undefined"
typeof document.createElement('div').setStyle; // "function"

한참동안 꼼수를 부려봤지만, 답은 안나오고 하는 수 없이 브라우저 이슈로 넘겼습니다.(콘솔창만 열면 오류가 발생하는 것으로 보입니다.) 구글이 자바스크립트가 통용되는 버전을 만들어 줄 것이라 믿어 의심치 않습니다.

Comments


Dion Almaer씨의 구글 AJAX Language API를 이용한 다국어 번역 북마크릿을 보고 한글로 번역되는 것도 있으면 좋겠다 싶어 만들어 보았습니다. 구글의 AJAX Language API는 총 39개국의 언어를 지원하며, 그중에서도 한국을 포함한 23개국 언어간 쌍방향 번역기능이 포함된 쓸만한 번역 엔진입니다. 번역하고자 하는 문자 영역을 드래그하여 선택한 후 북마크릿을 클릭하면 위 화면과 같은 번역 결과물을 출력합니다. 파이어폭스2, 3 RC에서 테스트 되었으며, 아쉽게도 IE계열 브라우저는 지원하지 않습니다. 자세한 특징은 다음과 같습니다.

  • 39개국 언어를 지원하는 구글 AJAX Language API를 사용함
  • 영어, 일본어, 프랑스어, 독일어, 중국어 등 총 23개국 언어 번역 지원
  • 번역 대상의 언어를 자동검출함으로 별도로 지정할 필요가 없음
  • 어느 사이트에서나 잘 보일수 있는 출력화면 최적화
  • 빠른 속도를 위해 별도의 자바스크립트 라이브러리(프레임웍)을 사용치 않음
  • 512바이트 단위로 분할 전송하여 장문 번역 가능(추가)
  • 장문인 경우 단어 단위로 계산하여 오역 발생 확률 감소(추가)
  • 줄바꿈 인식 및 HTML코드 탈출(추가)
  • 창의 크기가 윈도 크기를 넘긴 경우 스크롤바 활성(추가)
  • 상황별 오류 출력(추가)
  • 상황별 처리중 알리미 출력(추가)
  • 창 드래그 앤 드롭(추가 예정)

다국어 번역 북마크릿 - 이 링크를 브라우저의 북마크 도구모음에 추가하거나 북마크에 추가 하십시오.

if (!window.apiLoaded) {
  window.apiLoaded = function(text) {
    var link = false, next = null, language = 'ko';
    if (!text) {
      text = window.getSelection().toString().replace(/&/g, '&');
      text = text.replace(/</g, '&lt;').replace(/>/g, '&gt;').replace(/\n/g, '<br/>');
    } else link = true;

    if (text.length > 511) {
      var count = 0, words = text.split(' ');
      for (var i = 0; i < words.length; i++) {
        var l = words[i].length + 1;
        if (count + l > 500) {
          if (count > 511) count -= words[i-1].length + 1;
          break;
        }
        count += l;
      }
      next = text.substring(count + 1, text.length);
      text = text.substring(0, count);
    }

    var win = document.getElementById('__translationWindow');
    var img = document.getElementById('__translationWindowIcon');
    if (!link && win && win.style.display != 'none')
      win.childNodes[1].innerHTML = 'Translating...';
    else if (!img) {
      img = document.createElement('img');
      img.id = '__translationWindowIcon';
      img.src = "http://www.google.com/favicon.ico";
      img.setAttribute('style', 'top:10px;right:10px;position:fixed;z-index:999998');
      document.body.appendChild(img);
    } else img.style.display = 'block';

    var getLang = function(lang) {
      for (var l in google.language.Languages)
        if (google.language.Languages[l] == lang) return l.toLowerCase();
      return lang;
    };

    var error = function(text, lang) {
      if (lang) lang = 'guessing the language: ' + getLang(lang);
      text = 'No translation found for "' + text + '" ' + lang;
      return translationWindow(text, null, link, img, win);
    };

    if (!text) return error('');
    google.load('language', '1', {'callback': function() {
      google.language.detect(text, function(d) {
        if (!d.error && d.language) {
          google.language.translate(text, d.language, language, function(t) {
            if (t.translation) {
              translationWindow(t.translation, getLang(d.language), link, img, win);
              if (next) apiLoaded(next);
            } else error(text, language);
          });
        } else error(text, '');
      });
    }});
  };
}

if (!window.translationWindow) {
  window.translationWindow = function(translation, language, link, img, win) {
    img.style.display = 'none';
    if (!win) {
      var ws = [
        'font-family:AppleGothic,Malgun Gothic,Gulim,Dotum', 'background:#eee',
        'width:300px', 'opacity:0.9', 'padding:1px', 'text-align:left', 'color:#333',
        'top:10px', 'right:10px', 'font-size:12px', 'overflow:auto', 'position:fixed',
        'border:3px solid #aaa', 'line-height:1.5', 'z-index:999999', 'display:block'
      ], ds = ['background:#bbb','padding:2px 4px'];

      win = document.createElement('div');
      win.id = '__translationWindow';
      win.onmouseover = function() { win.style.opacity='1'; };
      win.onmouseout  = function() { win.style.opacity='0.9'; };
      win.setAttribute('style', ws.join(';'));
      win.innerHTML = [
        '<div style="font-size:13px;font-weight:bold;margin-bottom:3px;'+ds.join(';')+'">', 
          '<img style="vertical-align:text-bottom;" src="' + img.src + '"/>',
          'Google Translate',
        '</div>',
        '<span></span>',
        '<div style="text-align:right;font-size:11px;margin-top:3px;'+ds.join(';')+'">',
        '</div>'].join('');

      var a = document.createElement('a');
      a.setAttribute('style', 'position:absolute;top:2px;right:7px;cursor:pointer;');
      a.innerHTML = 'x';
      a.onclick = function(){ win.style.display = 'none'; };
      win.firstChild.appendChild(a);

      document.body.appendChild(win);
    } else win.style.display = 'block';

    var span = win.childNodes[1];
    if (!link){
      if (!language) translation = '<span style="color:#f30">' + translation + "</span>";
      else win.lastChild.innerHTML = 'detected as ' + language;
      span.innerHTML = translation;
      win.style.bottom = '';
    } else {      
      span.innerHTML += ' ' + translation;
      if (window.innerHeight < win.offsetHeight) win.style.bottom = '10px';
    }
    return true;
  };
}

if (!window.google || (google && !google.language)) {
  var script = document.createElement('script');
  script.src = 'http://www.google.com/jsapi?callback=apiLoaded';
  script.type = 'text/javascript';
  document.getElementsByTagName('body')[0].appendChild(script);
} else apiLoaded();

void(0);

Javascript to URL Bookmarklet Encoder

만들다 보니 자바스크립트 코드를 URL 형태로 만들어주는 녀석이 필요해서 간단한 엔코더도 만들었습니다. 상단의 TEXTAREA에 자바스크립트 코드를 넣고 버튼을 누르면 북마크릿에 사용할 수 있는 URL형태로 변환해 줍니다. 좌측 버튼은 최소한의 코드 변환을 실행하고 우측 버튼은 웹 표준에 부합하는 링크용 코드로 변환합니다.

Comments