Virtual Earthを利用した地図アプリケーション(その1)

日経ソフトウェアの記事で、Visual Basicで地図アプリケーションを作るものがある(作ってビックリ!Visual Basic, こだかかおる、pp.130、2008年7月号)。
同じことを、Visual Basicの代わりにwxPythonでやってみる。Virtual EarthのAPIを使って、WebアプリではなくWindowsフォームアプリケーションを作成する。

世界中に地図情報を提供するサービスである、Microsoft Virtual EarthAPIは公開されている。wxPythonで作るので、Linuxでも使えるアプリになるはずだ。でもとりあえずWindowsでやってみる。

  • ポイント整理
    • Virtual Earth APIを使う。
    • Virtual Earth APIJavaScriptから使用するようになっている。
    • JavaScriptは今まで使ったことない。
    • VBではなく、wxPythonで作成する。

APIを呼び出しているファイルve.html(p.132のリスト1)は、JavaScriptを記述したHTMLファイルであり、これはこのまま使わせていただく。日経ソフトウェアのウェブページからダウンロードできたので、それを使う。
http://itpro.nikkeibp.co.jp/article/MAG/20071120/287618/?ST=nsw#200807のvb0807.lzhをダウンロードして展開。

とりあえず、このファイルを読み込んで、日本地図を表示するところまでwxPythonで作成した。
ソースは、_main_tree.pyとMainFrame.pyから成る。同じディレクトリにve.htmlを置いておく。

#!/bin/env python
# _main_tree.py

# coding: utf-8

import os, sys
import wx

import MainFrame

class Application(wx.App):
    def OnInit(self):
        frame = MainFrame.MainFrame(None, -1)
        frame.Show(True)
        self.SetTopWindow(frame)
        return True

def main():
    import time

    app = Application(0)
    print u"info> このアプリケーションの起動時刻は %sです。" % time.ctime(time.time())
    appl_boot_path = os.getcwd()
    print u"info> 起動ディレクトリは %sです。" % appl_boot_path
    app.MainLoop()

if __name__ == '__main__':
    main()
#eof

HTMLファイルを読み込むところは、wxPythonのデモコードのActiveX_IEHtmlWindow.pyを参考にした。Sizerの設定をうまくやらないと地図をパネルにうまく表示できない。

#!/bin/env python
# MainFrame.py
# coding: utf-8 

import sys, os
import wx

# from ActiveX_IEHtmlWindow.py
if wx.Platform == '__WXMSW__':
    import wx.lib.iewin as iewin

class MainFrame(wx.Frame):       
    def __init__(self, id, title):
        width, height = 850, 600
        wx.Frame.__init__(self, id, title=u"Virtual Earth with wxPython",
                          size=wx.Size(width, height))

        self.current = os.path.join(os.getcwd(), "ve.html")

        # status bar
        sb = self.CreateStatusBar(2)
        sb.SetStatusWidths([-1, 150])
        sb.SetStatusText(self.current)

        # widgets on a panel
        Pan = wx.Panel(self, -1)

        self.ie = iewin.IEHtmlWindow(Pan, -1, style=wx.NO_FULL_REPAINT_ON_RESIZE)
        self.ie.LoadUrl(self.current)

        BtnRoad = wx.Button(Pan, -1, u"道路")
        BtnAerial = wx.Button(Pan, -1, u"航空写真")
        BtnHybrid = wx.Button(Pan, -1, u"ハイブリッド")
        BtnBirdseye = wx.Button(Pan, -1, u"概観図")
        BtnZoomOut = wx.Button(Pan, wx.ID_ZOOM_OUT)
        BtnZoomIn  = wx.Button(Pan, wx.ID_ZOOM_IN)
        BtnGetRoute = wx.Button(Pan, -1, "Get Route")

        BtnRoad.Bind(wx.EVT_BUTTON, self.OnRoad)
        BtnAerial.Bind(wx.EVT_BUTTON, self.OnAerial)
        BtnHybrid.Bind(wx.EVT_BUTTON, self.OnHybrid)
        BtnBirdseye.Bind(wx.EVT_BUTTON, self.OnBirdseye)
        BtnZoomOut.Bind(wx.EVT_BUTTON, self.OnZoomOut)
        BtnZoomIn.Bind(wx.EVT_BUTTON, self.OnZoomIn)
        BtnGetRoute.Bind(wx.EVT_BUTTON, self.OnGetRoute)

        StTxt1 = wx.StaticText(Pan, -1, u"緯度:")
        StTxt2 = wx.StaticText(Pan, -1, u"経度:")
        StTxt3 = wx.StaticText(Pan, -1, u"ルート探索:")
        TxtLatitude = wx.TextCtrl(Pan, -1, "  ")
        TxtLongitude = wx.TextCtrl(Pan, -1, "  ")
        TxtFrom = wx.TextCtrl(Pan, -1, "", size=(300,-1))
        TxtTo = wx.TextCtrl(Pan, -1, "", size=(300,-1))

        # sizer
        Sizer0 = wx.BoxSizer(wx.VERTICAL)
        Sizer1 = wx.BoxSizer(wx.HORIZONTAL)
        Sizer3 = wx.BoxSizer(wx.HORIZONTAL)

        Sizer1.Add(BtnRoad, 0, wx.FIXED_MINSIZE|wx.LEFT, 2)
        Sizer1.Add(BtnAerial, 0, wx.FIXED_MINSIZE|wx.LEFT, 2)
        Sizer1.Add(BtnHybrid, 0, wx.FIXED_MINSIZE|wx.LEFT, 2)
        Sizer1.Add(BtnBirdseye, 0, wx.FIXED_MINSIZE|wx.LEFT, 2)
        Sizer1.Add(BtnZoomOut, 0, wx.FIXED_MINSIZE|wx.LEFT, 40)
        Sizer1.Add(BtnZoomIn, 0, wx.FIXED_MINSIZE|wx.LEFT, 2)
        Sizer1.Add(StTxt1, 0, wx.FIXED_MINSIZE|wx.LEFT, 20)
        Sizer1.Add(TxtLatitude, 0, wx.FIXED_MINSIZE|wx.LEFT, 2)
        Sizer1.Add(StTxt2, 0, wx.FIXED_MINSIZE|wx.LEFT, 10)
        Sizer1.Add(TxtLongitude, 0, wx.FIXED_MINSIZE|wx.LEFT, 2)

        Sizer3.Add(StTxt3, 0, wx.FIXED_MINSIZE|wx.LEFT, 2)
        Sizer3.Add(TxtFrom, 0, wx.FIXED_MINSIZE|wx.LEFT, 10)
        Sizer3.Add(TxtTo, 0, wx.FIXED_MINSIZE|wx.LEFT, 10)
        Sizer3.Add(BtnGetRoute, 0, wx.FIXED_MINSIZE|wx.LEFT, 40)

        Sizer0.Add(Sizer1, 0, wx.ALL, 10)
        Sizer0.Add(self.ie, 1, wx.EXPAND)  # this is a point!
        Sizer0.Add(Sizer3, 0, wx.ALL, 10)

        Pan.SetSizer(Sizer0)

    def mapStyle():
        pass

    def OnRoad(self, event):
        self.mapStyle('r')

    def OnAerial(self, event):
        self.mapStyle('a')

    def OnHybrid(self, event):
        self.mapStyle('h')

    def OnBirdseye(self, event):
        self.mapStyle('o')

    def OnZoomOut(self, event):
        pass

    def OnZoomIn(self, event):
        pass

    def OnGetRoute(self, event):
        pass

_main_tree.pyをダブルクリックすれば実行され、下の画面が開く。

ここまでは良い。ボタンを押したときのイベントの実行をどうするか次に考える。

  • 問題点
    • HTMLファイル中に定義されたJavaScriptの関数(この場合、ve.htmlの中のsetMapStyleという関数)をファイルの外から呼ぶにはどうしたらいいのか?