UP | HOME

Information and Computer Literacy

目次

1 Emacs 入門

1.1 特殊キーとキーバインド

1.1.1 特殊キー

Emacs では特殊キーと呼ばれるキーを活用する. ICL演習室に設置されている 109キーボード では,特殊キーは以下のように割当てられている.

特殊キー名省略表記キーボード上のキー
ControlC-Ctrl
MetaM-Alt
Supers-割当て無し
SpaceSPCSpace
ReturnRETEnter
TabTABTab
DeleteDELBackspace
EscESCEsc
fig/Emacs-109.png (著作権フリー画像)

1.1.2 キーバインド:同時押しと順次押しの組み合わせ

Emacs では,特殊キーとそれ以外のキーの組み合わせ(キーバインド)によって多彩な操作が可能である. 全てのキー操作は 同時押し順次押し で構成される.

同時押し
特殊キーとそれ以外のキーを 同時 に押すこと.ハイフン(-)で表現される.
C-g
Control と g を同時に押す
M-x
Meta と x を同時に押す
C-x-n
Control と Meta と n を同時に押す
順次押し
あるキーや同時押しの 後で, 別のキーや同時押しを入力すること.空白( )で区切って表現される.
C-x C-s
C-x の後で C-s を入力する
M-x info
M-x の後で info と入力する
C-x RET f
C-x, Return, f の順に入力する

1.2 基本的な操作

1.2.1 起動,キャンセル,そして終了

起動方法については口頭で説明.

C-g (keyboard-quit)
最初のうちは,Emacs の操作中に意図しない画面が出てきたりしてパニックになるかもしれない.そういう時は慌てず騒がず C-g を押そう.多くの場合,これで操作がキャンセルされる.それでも駄目な場合には,以下の C-x C-c を使ってEmacs を終了させて再起動てみよう.
C-x C-c (save-buffers-kill-terminal)
Emacs を終了させるには C-x C-c というキーバインドを利用する.保存されていない編集中のバッファは消えてしまう.そういうバッファがあれば,その都度,ミニバッファに以下のような警告が出る.
Save file /Users/foo/test.txt? (y, n, !, ., q, C-r, d or C-h)

ここで C-h を押せば,どのキーにどんな機能が対応しているかが一覧できる.

キー説明
SPC もしくは yこのバッファを保存
DEL もしくは nこのバッファは保存しない(編集は破棄される)(※)
RET もしくは q残り全てのバッファを保存しない(※)
!残り全てのバッファを保存する
C-rこのバッファを見る
dこのバッファの 差分 を見る
C-hヘルプを表示させる
※ 保存されないバッファがある場合は,さらに
Modified buffers exist; exit anyway? (yes or no)

(修正されたバッファが存在します.それでも終了しますか?)と表示される.ここで.yes RET と入力すれば,編集は破棄され,Emacs は終了する.no RET と入力すれば,終了がキャンセルされ,操作前の画面に戻る.

1.2.2 カーソルの操作,文字の入力・消去

Emacs では,十字キーではなく,キーバインドでもカーソルを操作できる.最初は十字キーの方が楽なように思うだろうが,慣れてくると十字キーに手を伸ばすのが億劫になってくるはずだ.最初の画面(eshell)で色々試してみよう.

キーバインド動作
C-f または <right> (forward-char)カーソルを右(前)へ
C-b または <left> (backward-char)カーソルを左(後)へ
C-n または <up> (next-line)カーソルを下(次の行)へ
C-p または <down> (previous-line)カーソルを上(前の行)へ
C-a (move-beginning-of-line)カーソルを行の先頭へ
C-e (move-end-of-line)カーソルを行の末尾へ
M-f または ESC <right> (forward-word)カーソルを1単語右(前)へ
M-b または ESC <left> (backward-word)カーソルを1単語左(後)へ
C-v (scroll-up-command)カーソルを1画面下(次の画面)へ
M-v (scroll-down-command)カーソルを1画面上(前の画面)へ
C-M-v (scroll-other-window)ウィンドウ分割時に他のウィンドウに対して C-v
C-M-S-v (scroll-other-window-down)ウィンドウ分割時に他のウィンドウに対して M-v
M-< (beginning-of-buffer)カーソルをバッファの先頭へ
M-> (end-of-buffer)カーソルをバッファの末尾へ
M-g (goto-line)指定した行へ移動.ミニバッファに Goto Line: と表示されるので,行番号を入力して RET すると移動.
C-q (quoted-insert)特殊文字を入力する(タブ文字を入力するには C-q TAB とする)
DEL (delete-backward-char)カーソルの の1文字を消去
C-d (delete-char)カーソルの の1文字を消去

1.2.3 ファイル操作 / バッファ操作

Emacs でウィンドウに表示されるのは,メモリ上にのみ存在する バッファ である.バッ ファはディスク上に実在する ファイル と明確に区別される必要がある.

C-x C-f で「ファイルを開く」とそのファイルの内容がバッファに読み込まれ,C-x C-wC-x C-s などで「ファイルを保存」すると,そのバッファの内容がファイルに 書き込まれる.

ウィンドウ上に表示されるバッファを切り替えるには C-x bC-x C-b を用いる.C-x の後に左右カーソルキーを入力する(C-x <left> / C-x <right>) ことでバッファを次々と切り替える機能も便利.バッファを閉じるには C-x k を用いる.

キーバインド動作
C-x C-f (find-file)ファイルを開く(ie. ファイルの内容をバッファに読み込む)
C-x C-w (write-file)ファイルを別名で保存する(ie. バッファの内容をファイルに書き込む). ミニバッファにファイル名を入力して RET を押すと保存する.
C-x C-s または s-s (save-buffer)ファイルを上書き保存する(ie. バッファの内容を開いたときのファイル名で保存)
C-x s (save-some-buffers)全てのファイルを上書き保存する
C-x b (switch-to-buffer)バッファを切り替える.ミニバッファ上で開きたいバッファ名を入力.TAB を押すと補完候補が一覧できる.
C-x k (kill-buffer)バッファを消去する.ミニバッファに Kill buffer (default foo): と消去するバッファ名を入力でき,RET を押すとそのバッファが消去される.デフォルトは呼び出し時にアクティブだったバッファ(※)
C-x <left> (previous-buffer)および C-x <left> (next-buffer)バッファリストにある前/次のバッファに切り替える.
C-x C-B (list-buffers)バッファリストを表示する.
※バッファが編集中で保存されていない場合にはミニバッファに
Buffer foo modified; kill anyway? (yes or no)

(foo という名前のバッファは保存されていません.それでも消去しますか?)と質問される.yes RET で編集を破棄してバッファを消去.no RET でバッファの消去をキャンセル(C-g でもキャンセル可能).

1.2.4 ウィンドウの分割

Emacs では,1つのフレームの中にいくつものウィンドウを作り,それぞれに別々のバッファを表示させられる. C-x 2 / C-x 3 で上下/左右に分割でき,C-x 0C-x 1 で分割を解除できる(C-x C-b などで意図せずにウィンドウが分割されたのを戻すのに頻繁に使う). ウィンドウを閉じてもバッファは消去されない.

キーバインド動作
C-x 2 (split-window-below)現在のウィンドウを上下に分割する.
C-x 3 (split-window-right)現在のウィンドウを左右に分割する.
C-x 1 (delete-other-windows)現在のウィンドウ以外を閉じる.
C-x 0 (delete-window)現在のウィンドウを閉じる.
C-x o (other-window)次のウィンドウにフォーカスを移動

1.2.5 マークとリージョン

Windows ではshift を押しながら上下左右で「範囲」を選択してコピー/カットできた. この機能は Emacs にも マークリージョン という(少し操作し辛い)形で存在する.

C-SPC でマークしてからカーソルを移動させると,マークと現在のカーソル位置までが 選択範囲(リージョン)となる.C-g とすればリージョンとマークを取り消せる. M-h で段落を選択, C-x h でバッファ全体を選択(narrowingされている場合は当該箇 所),C-x C-p でページ全体を選択.

M-w でリージョンをコピー,C-w でリージョンをカットできる.コピー/カットされた リージョンは,クリップボードに相当するキルリング(kill-ring)に保存され,C-y で直 前のキルリングの内容をヤンク(ペースト)できる.その際,C-y に続けて M-y を入力 することでキルリングを次々にさかのぼってヤンクできる.

リージョンを選択しておいて M-; とすると,当該リージョンをコメントアウトする.コ メントアウトされた領域を選択して M-; とするとコメント解除する.

キーバインド機能
C-SPC (set-mark-command)マーク
M-w (kill-ring-save)リージョンをキルリングにコピー
C-w (kill-region)リージョンをカット
C-y (yank)リージョンをヤンク(ペースト)
M-y (yank-pop)キルリングを次々さかのぼってヤンク
M-h (mark-paragraph)段落をマーク
C-x h (mark-whole-buffer)バッファをマーク
C-x C-p (mark-page)ページ全体をマーク

1.2.6 アンドゥ

直前の入力内容を取り消したい場合には C-/ (C-_ または C-x u でも可能).

1.2.7 検索・置換

C-s / C-r でバッファを順方向/逆方向に検索.C-M-s / C-M-r で正規表現による順方向/逆方向の検索. 検索された文字列を別の文字列で置換するには M-% / C-M-% (対話型の置換/正規表現置換) もしくは M-x replace-string / M-x replace-regexp (一括の置換/正規表現置換)を使う.

C-s (isearch-forward), 順方向にインクリメンタル検索.ミニバッファに I-search: [aa] と出るので,文字列を入力すると現在のカーソル位置以降で当該箇所にマッチする場所を表示.RET で移動,C-g でキャンセル.文字入力中に再び C-s を押すと,次の検索結果へジャンプ. C-r (isearch-backward), 逆方向にインクリメンタル検索.上述の検索をカーソル位置からバッファの先頭に向かって行う. C-M-s (isearch-forward-regexp), 順方向に正規表現検索.ミニバッファに Regexp I-search: [aa] と出るので,正規表現を入力すると,現在のカーソル位置以降で当該箇所にマッチする場所を表示. C-M-r (isearch-backward-regexp), 逆方向に正規表現検索.上述の正規表現検索をカーソル位置からバッファの先頭に向かって行う. M-% (query-replace), 対話型の置換.ミニバッファに Query replace (default *****): と出るので,検索する文字列, RET, 置換する文字列, RET と入力する.

2 文章構成方法

2.1 課題:「私の好きな競技のルールと魅力」

自分の好きな競技を他の人に紹介する文章を作成してみよう.とはいっても,次のような文章:

僕はサッカーが好きです.小さい頃から少年サッカーをやっていて,
今でも時々ベガルタ仙台の応援に行きます.
勝つとすごく盛り上がって楽しいです.皆さんもぜひスタジアムに足を運んで下さい.

ではなく,もう少し「大学生らしい」ものにしたい.

そこで,想定する読者と,その文章の目的を以下のように設定しよう:

その競技を知らない人・これから初めて観に行く人を読者として想定し,競技のルールの説明と絡めながら,その魅力を紹介する.

そして,この目的を達成する方法として,以下のようなアプローチを考える:

  • 競技のルールを客観的に説明する.特に,以下の2つの観点から解説する:
    競技の 勝敗を決定づける条件
    サッカーだったら,前後半45分づつプレイして得点数の多い方が勝ち.野球だったら9回裏表をプレイして得点数の多い方が勝ち.
    上記の条件で用いられる 基準の評価方法
    多くの場合,得点.サッカーだったら相手のゴールにボールが入ったら1点.野球だったら塁に出たバッターが本塁に戻ってきたら1点.
  • 上述の「勝敗の条件」や「基準の評価方法」と絡めながら,自分にとっての その競技の魅力 を伝える.

2.2 文章構成の基本

2.2.1 文章の材料・素材を集める

今回の課題の場合,勝敗の条件や基準の評価方法について書かなければならないので,もし自分がそれらについてあまり詳しくなければ,予め資料を集めておこう.

2.2.2 書きたいことを整理する

今回の課題の目的は「競技のルールの説明と絡めながら,競技の魅力を紹介する」ことである.言い換えれば,説明すべきルールは,伝えたい競技の魅力に関連するものに限られるのだ.例えば,野球を対象に「9回裏の逆転ホームラン」の魅力を伝えたい際に「マウンドの高さ」や「コールドゲーム」について解説する必要は無い.そこで,文章の構成に入る前に,

  • どんな魅力を伝えたいのか
  • その魅力に関連するルールは何か

といった観点で言いたいことを整理しておく必要がある.ここで言う整理とは,

  • どんな「ことがら」を書くのか(あるいは,書かないのか)
  • 書くと決めた「ことがら」同士がどのような関係にあるのか

といったことを明確にすることである.

この「書きたいことを整理する」というプロセスが,文章を構成する際に最も重要である.書きたいことが整理されていれば,論理的に無理のない文章がスラスラと構成できる.逆に,Wikipedia や Google などで調べたルールを適当にコピー&ペーストしてから,無理矢理に「こうしたルールがあるため,○○は魅力的である」と繋げようとすると必ず「綻(ほころ)び」が出てくる.そのため,こうした安易なやり方でスッキリとした文章が構成できることは,まず無い.

なお,「魅力に関連するルール」が何かを見極めることはなかなか難しいが,例えば,以下のようなものが該当するのではないだろうか.

伝えたい魅力を1つの文として構成した時に登場する用語に関連するルール
先の例では,各イニングが表と裏で構成されること,どのような状態を「ホームラン」と呼ぶのか,といったルール.
それ無くしては「(今回伝えたい)魅力が意味をなさない」ようなルール
先の例では,勝敗が9イニング終了時の得点で決定されることや,3アウトになるまではどれだけでも得点が入ること,など.

2.3 LaTeX による文書作成

2.3.1 TeX ファイルの作成

  1. C-x C-f ~/report1.tex として report1.tex ファイルを(新たに)開く
  2. 以下をコピー(マウスの左ボタンを押しながら領域を選択→右クリックメニューから 「コピー」を選択)&ペースト(Emacs上で C-y )する.
    \documentclass[a4paper]{jarticle}
    \title{文章のタイトル}          % 適宜,変更して下さい
    \author{名前 {\tt(学籍番号)}} % 適宜,変更して下さい
    \begin{document}
    \maketitle
    % ↓↓↓ここから下に文章を挿入して下さい↓↓↓
    
    % ↑↑↑ここから上に文章を挿入して下さい↑↑↑
    \end{document}
    
  3. 文章のタイトル,名前,学籍番号を適当に編集する.
  4. % ↓↓↓ここから下にあなたの文章を挿入して下さい↓↓↓% ↑↑↑ここから上にあなたの文章を挿入して下さい↑↑↑ の間に,自分の文章を挿入する. 編集時には以下のコマンドが役に立つかもしれない:
    機能EmacsFirefox
    領域選択マウスの左ボタンを押しながらマウスを移動マウスの左ボタンを押しながらマウスを移動
    コピーM-w右クリックメニューから「コピー」
    カットC-w必要なし
    ペーストC-y必要なし
    過去のクリップボードをペーストM-y該当なし
  5. LaTeXのコマンドを使ってみよう LaTeXのコマンドは \ (バックスラッシュ)と {} (波括弧)で構成される.
    1. どこでも 空行を1つ入れる ことで,段落を変えることができる.
    2. 複数の段落のまとまりに名前をつけたい場合は,
      \section{節のタイトル}
      段落1
      
      \subsection{小節のタイトル}
      段落2
      
      段落3
      
      \subsection{小節のタイトル}
      段落4
      
      段落5
      

      とする.

    3. 箇条書きが必要な場合には itemize 環境を使う. 具体的には,\begin{itemize}\end{itemize} の間に \item で項目を記述する.
      \begin{itemize}
        \item 読売ジャイアンツ
        \item 東京ヤクルトスワローズ
        \item 横浜DeNaベイスターズ
        \item 中日ドラゴンズ
        \item 阪神タイガース
        \item 広島東洋カープ
      \end{itemize}
      

      \item の数は何個でも構わない.

      箇条書きは入れ子にもできる.

      \begin{itemize}
        \item セントラルリーグ
          \begin{itemize}
            \item 読売ジャイアンツ
            \item 東京ヤクルトスワローズ
            \item 横浜DeNaベイスターズ
            \item 中日ドラゴンズ
            \item 阪神タイガース
            \item 広島東洋カープ
          \end{itemize}
        \item パシフィックリーグ
          \begin{itemize}
            \item 北海道日本ハムファイターズ
            \item 東北楽天ゴールデンイーグルス
            \item 埼玉西武ライオンズ
            \item 千葉ロッテマリーンズ
            \item オリックス・バファローズ
            \item 福岡ソフトバンクホークス
          \end{itemize}
      \end{itemize}
      
    4. 番号つきの箇条書きが必要な場合は,
      \begin{enumerate}
      \item ピッチャー
      \item キャッチャー
      \item ファースト
      \item セカンド
      \item サード
      \item ショート
      \item レフト
      \item センター
      \item ライト
      \end{enumerate}
      

      とする.\item の数は何個でも構わない.

    5. 定義リストが必要な場合は,
      \begin{description}
              \item[先発] 最初に投げる投手
              \item[中継ぎ] (代打や不調などで)先発が降板した後に投げる投手
              \item[抑え] 勝利を確実なものにするために最後の1〜2回を投げる投手
      \end{description}
      

2.3.2 コンパイル

  • TeX ファイル → dvi ファイル
    1. Emacs上で M-! (Alt+shift+1)とすると,ミニバッファに Shell command: とプロンプトが出るので,
      platex report1.tex
      

      と入力して RET キーを押す.

    2. 問題が無ければ report1.dvi というファイルができるはずなので,C-x C-d RET としてディレクトリ一覧を表示させ,確認してみよう.
    3. ファイル名が report1.tex でない場合は,適切なファイル名を platex の後に記 述する.例えば,ファイル名が test.txt なら,以下のようにする.
      platex test.txt
      

      この場合は生成されるファイル名も test.dvi になる.

  • dvi ファイル → pdf ファイル
    1. 再び M-! として,Shell command: の後に
      dvipdfmx report1.dvi
      

      とする.

    2. 問題が無ければ report1.pdf というファイルができるはずなので,C-x C-d RET としてディレクトリ一覧を表示させ,確認してみよう.
    3. 同じく,dviファイル名が report1.dvi で無い場合には,適切なファイル名を与え る.例えば,ファイル名が test.dvi なら,以下のようにする.
      dvipdfmx test.dvi
      
  • PDFファイルの確認
    以下の方法で生成した PDF ファイルを確認できる.
    • メインメニューから「home」→「学籍番号」のフォルダを開いて,生成されたPDFファ イルをダブルクリックする
    • Adobe Acrobat を立ち上げ「ファイル」→「開く」から生成されたPDFファイルを選 択する

2.4 レポートの投稿

作成したレポートは,東北大学インターネットスクール(ISTU: Internet School of Tohoku University)から投稿する.

  1. ISTUサイトにログイン
  2. 右下のメニューから金曜日3時限 情報基礎B を選択する fig/subject_list.png
  3. レポート「私の好きな競技のルールと魅力」を選択する fig/contents_list.png
  4. レポートを提出する fig/submit.png
    • 「ファイルを選択」をクリックし,レポートPDFファイルを選択する
    • 右下の提出を押す
    • レポートを差し替えたい場合には「ファイルを選択」で新しいファイルを選択して「提出」
    • レポートを削除したい場合には「削除」を押して「提出」
  5. 投稿したレポートを確認する fig/report_check.png
    • レポートを投稿した画面に移動すると,投稿したファイルをダウンロードできる.
    • ファイルをダウンロードした後,Adobe Reader で開いて意図どおりのファイルが投 稿できているか確認すること.

3 プログラミング入門

3.1 何をするか?

まずは,次のリンクをクリックしてみて欲しい.何が見えるだろうか?: http://goo.gl/maps/OlzIc 最初は何だか判らないかもしれないので,適当にズームしたり移動させたりしてみて欲し い.例えば,自分の家の近く,仙台駅西側川内キャンパス周辺 を見てみよう.

これは,以下のようにして作成した:

  1. 国土交通省国土政策局国土数値情報ダウンロードサービス で提供している バス停留所データ から宮城県に存在するバス停をダウンロード
  2. ダウンロードしたデータを Google MapsGoogle Earth で利用できる KML(Keyhole Markup Language) という形式に変換
  3. 変換したファイルを Github が提供する Gist というコードをシェアするサービスにアップロード
  4. アップロードした KMLファイルへのリンクを Google Maps の検索クエリに指定

ただし,このままではあまりにバス停の数が多すぎるため,もう少し絞り込みたい. そこで,今回のプログラミングでは,以下の課題に取り組んでもらう:

  • 「自宅(もしくは宮城県内の適当な場所)の近くにあるバス停を Google Maps 上に表示 させる」

3.2 [6/21] Githubアカウントの登録

3.2.1 何のため?

Google Maps はローカルにあるファイルを読み込むことはできないので,読み込ませたい ファイルをインターネット上に公開しなければならない.

ファイルを公開する方法として Github, Bitbucket, Google Drive, Dropbox, CloudApp … など各種あるが,その中でも,Github は特別なソフトウェアを必要とせず,コピー&ペー ストだけでファイルをアップロードできる.

3.2.2 https://github.com へアクセス

登録画面が出るので,

  1. 希望するアカウント名(後から変更不可能)
  2. メールアドレス
  3. パスワード(授業でやったパスワードの作り方 を参照) を入力して「Sign up for free」をクリック fig/github_signup.png
  4. おしまい

3.3 [6/21] Gist への KMLファイルの投稿

3.3.1 何のため?

Github は,本来,git というバージョン管理システムで利用可能な外部リポジトリ である.そのため,ローカルのファイルをアップロードするには git コマンドを利用す る必要があるが,情報基盤センターの環境では提供されていない.

そこで,git ツールを使わずにブラウザ上でファイルの投稿・編集ができる Gist システ ム使ってファイルを投稿・公開する.

3.3.2 https://gist.github.com へアクセス

もしくは,Github のダッシュボード画面のメニューから Gist を選択: fig/github_dashboard.png

3.3.3 Gist を投稿する

空の gist 作成画面になるので,

  1. ファイルの説明: 教育情報基盤センター
  2. ファイル名: icl.kml
  3. ファイルの中身:
    <?xml version="1.0" encoding="UTF-8"?>
    <kml xmlns="http://www.opengis.net/kml/2.2">
      <Placemark>
        <name>東北大学 教育情報基盤センター</name>
        <description>いま私はここに居ます</description>
        <Point>
          <coordinates>140.850922, 38.260896</coordinates>
        </Point>
      </Placemark>
    </kml>
    

    を入力して「Create Public Gist」をクリック fig/first_gist.png

3.3.4 KMLファイルの中身の解説

ここで作ったファイルは KML (Keyhole Markup Language) という書式に基づいて作成 されている.その基本構造を順に解説していこう.

  1. 1行目〜2行目: xml の定義
    <?xml version="1.0" encoding="UTF-8"?>
    <kml xmlns="http://www.opengis.net/kml/2.2">
    

    第1行で,利用する XML(KMLを一般化したもの)のバージョン(1.0)を明示し, このファイルが utf-8 でエンコードされていることを明示している. 第2行では,ここで利用する KML が http://www.opengis.net/kml/2.2 で定義され る名前空間を利用することを示している.

  2. 3行目〜5行目: Placemark の設置
    <Placemark>
      <name>東北大学 教育情報基盤センター</name>
      <description>いま私はここに居ます</description>
    

    まず,<Placemark> によって,このあと </Placemark> で閉じられるまで, 新しい地図上のマップポイントを記述することを示している. 次に,<name>東北大学 教育情報基盤センター</name> および <description>いま私はここに居ます</description> により,この Placemark の名前と,クリックした時に表示される説明を設定している.

  3. 6行目〜8行目: 座標の設置
    <Point>
      <coordinates>140.850922, 38.260896</coordinates>
    </Point>
    

    <Point>〜</Point> タグ内に点の情報を書き込む.ここでは, <coordinate>経度(水平方向)・緯度(垂直方向)</coordinates> によって, この地点の緯度と経度を指定する.

  4. 9行目〜10行目: Placemark, kml タグを閉じる.
      </Placemark>
    </kml>
    

    これによって,めでたく Placemarkkml を終了できる.

3.4 [6/21] Google Maps に Gist にアップロードした KML ファイルへのリンクを渡す

3.4.1 何のため?

Goole Maps は,検索クエリに KML ファイルへのリンクを渡された場合,これを読み込 んで地図に重ねて表示する機能を持っている.ここで与えるリンクはインターネット上に 公開されている KML ファイルへのものでなければならない.そのため,上記の手順で作られた「Gist のページ」ではなく, 「アップロードしたファイル」へのリンクを渡す必要がある.

3.4.2 投稿した Gist ページを表示

https://gist.github.com へアクセスし,上述の手順で作った icl.kml のページを開 く.例えば,Gist ページの右上のメニューから自分の名前をクリックすれば,自分の 作った Gist 一覧が表示される. fig/gist_dashboard.png

3.4.3 投稿した KML ファイル の URL を獲得

Gist ページのファイル名の右に並ぶアイコンのうち一番右のもの(マウスを近づけると View Rawと表示される)の上で,右クリックメニューから「リンクのURLをコピー」を選択. fig/first_gist_URL.png

3.4.4 取得した URL を Google Maps に渡す

http://maps.google.com の検索窓にコピーした KML ファイルへのリンクをペースト fig/google_maps_init.png

3.4.5 こんな画面が出た?

fig/first_gist_result.png

3.5 [6/28] C++プログラミングの準備

3.5.1 何のため?

まず,Emacs で文脈に合わせて色がつくようにする.こうすることで打ち間違いが見付け やすくなる.次に,ファイルの中身をクリップボードにコピーするシェルスクリプトを登 録しておく.こうすることで,出力された KML のコピー&ペーストが容易になる. 最後に,プログラム用に ~/cpp というディレクトリを作る.こうすることで, ソース・ファイルやデータ・ファイルをまとめておける.

3.5.2 Emacs に色をつける

global-font-lock-mode を利用することで,開いたファイルの拡張子(TeXファイル なら .tex, C++ソースファイルなら .cpp)に応じて,文脈を理解して色をつけて くれる.

  1. ~/.emacs.el を作成する(既に存在するなら開く)

    Emacs 上で C-x C-f とする(C-x は Ctrlを押しながら x のコト)

    ミニバッファ(Emacs フレームの一番下にある1行だけのウィンドウ)に Find File: ~/ と表示されるので,~/ の後に .emacs.el と記述して RET (RET は enter キー) fig/emacs-find_file.png

  2. 開かれた ~/.emacs.el に以下を記述する
    (global-font-lock-mode t)
    

    fig/emacs-global-font-lock.png C-x C-s で保存する. fig/emacs-global-font-lock-saved.png

  3. 再起動する(C-x C-c で終了させ,再び開く). 再起動後に表示されるメッセージがほんのり赤くなっていたらOK.

3.5.3 ファイルの中身をクリップボードにコピーするシェルスクリプトを登録する

端末などで使われる bash というシェルは,起動時に ~/.bashrc を参照し,そこに登録 されているエイリアス(コマンドの別名)や関数を読み込んでくれる.このファイルに,引 数として与えられたファイルの中身をクリップボードに取り込むスクリプト klip を登 録しておけば,以降のプログラムの実行結果を簡単にクリップボードに送れるようになる.

  1. ~/.bashrc を開く

    Emacs 上で C-x C-f とするとミニバッファに Find File: ~/ と表示されるので, ~/ の後に .bashrc と記述して RET を入力する.こうすることで, ~/.bashrc が存在すればそれが開かれ,存在しなければ新規に作成される. なお,ICL演習室の環境で =~/.bashrc= がデフォルトで存在するかどうかは未確認.

  2. ~/.bashrc を編集し,保存する

    開かれた ~/.bashrc の最後に以下の一行を追記する:

    klip() { dcop klipper klipper setClipboardContents "$(ls $1)"; }
    

    開き波括弧 { の後ろと閉じ波括弧 } の前には 半角スペースが必要 である.

    Emacs 上ではこのように表示される(当該箇所以外の ~/.bashrc の内容がこれと同じ である保証はないことに注意されたい). fig/emacs-bashrc.png

  3. 端末を起動する

    メニューから「システム」→「端末」を選択する.もしくは,デスクトップ上の 「端末」アイコンをダブルクリックする fig/menu-terminal.png

    もしすでに「端末」を開いていたら,端末に

    $source ~/.bashrc
    

    と記述して RET を入力する.

  4. 端末に以下を入力する
    klip ~/.bashrc
    

    これで ~/.bashrc の中身がクリップボードに取り込まれる. fig/shell-klip-test.png

  5. Klipper を開いてクリップボードの中身を確認する

    画面右下にあるクリップボードアイコン をクリックすると,クリップボードの履歴が表示される. ~/.bashrc の最初の数行が表示されていれば OK. fig/Klipper.png

3.5.4 cpp ディレクトリを作る

そこにプログラム課題用のファイルをまとめておくために, ホームディレクトリ ~ の下に ~/cpp というフォルダを作る.

  1. Emacs のどの画面からでもよいので, M-x make-directory と入力する(M-x は Alt を押しながら x のこと).

    まず,M-x (Alt を押しながら x) と入力すると,ミニバッファに「 M-x 」と表示さ れ,フォーカスがミニバッファに移動する: fig/emacs-M-x.png 次に,そのまま make-directory と入力して RET と入力 fig/emacs-M-x-make-directory.png

    なお,ここで,M-x make-directory と入力するとき, make-d まで入力して TAB キーを押すと make-directory まで補完してくれる. fig/emacs-M-x-make-d-TAB.png

  2. ミニバッファに Make directory: ~/ と表示されるので,その後に cpp と入力し て RET を入力する. fig/emacs-M-x-make-directory-cpp.png これで,~/cpp というフォルダが作成される.
  3. ホームフォルダを表示させて確認する.

    メニューから「ホーム」を選択 fig/menu-home.png

    ホームフォルダ内に「cpp」というフォルダが出来ているのを確認する. fig/menu-home-check.png

3.6 [6/28] C++で標準出力を行うプログラムを作って実行してみる

3.6.1 何のため?

C++ を用いて,標準出力に "Hello world" と表示させるプログラム(sample1)を作 る.これによって,以下を理解する.

  • C++ の基本構造と標準出力および << 演算子の使い方
  • コンパイルの方法とエラー・メッセージに対処する方法

3.6.2 ~/cpp/sample1.cpp を編集

  1. 新しく ~/cpp/sample1.cpp を作成

    Emacs を起動して C-x C-f とすると Find file: ~/ と表示されるので,その後に cpp/sample1.cpp と入力して RET

  2. sample1.cpp を編集 以下を入力する.ただし,行番号は除くこと.なお,// 以降(赤く表示されている部分)は,ソースコードを読み易くするためのコメントなので,必ずしも入力する必要は無い.
    1:  #include <iostream>             // 標準入出力用ライブラリを読み込む
    2:  using namespace std;            // 名前空間 std をデフォルトで利用する
    3:  
    4:  // メインルーチン
    5:  int main ( void )               // 引数無しで整数(int)型の戻り値を返す main 関数を定義
    6:  { 
    7:    cout << "Hello world" << endl; // 標準出力 cout に Hello world という文字列と改行を送る
    8:    return 0;                      // 正常終了を表す 0 を返す
    9:  }
    

    このプログラムの各行の解説は後に回して,ここでは,注意すべき点を述べよう.

    1. コメント文以外は全て半角である.Anthy が OFF になっていることを確認するこ と.
    2. 1行目の #include の後の <iostream> は一対の山括弧(「<」「>」, JIS配列 では Shift+「,」, Shift+「.」で入力できる)で囲まれている.
    3. 2行目の最後の「;」(セミコロン,JIS配列の「l」の右隣)を忘れないこと.同様に,7行目,8行目もセミコ ロンで終わる必要がある.
    4. 5行目の main の後の ( void ) は一対の丸括弧(「(」「)」, JIS配列では Shift+「8」, Shift+「9」で入力できる)で囲まれている.
    5. 6行目の行頭は「{」(開き波括弧, JIS配列では Enter の左隣),9行目の行頭は「}」 (閉じ波括弧, JIS配列ではEnterの左隣)である.
    6. 7行目の "Hello world" は,ダブルクォート( " , JIS配列では Shift+「2」で入力できる)で囲まれている.
    7. 7行目と8行目はインデント(これらの行のどこかにカーソルを置いて TAB を入力)すると読み易い.
  3. 編集が終わったら C-x C-s で保存するのを忘れないように.保存すると,Emacsのス テータスバーの左端が「**」から「–」に変わる.

    適切に入力されたときの Emacs の画面は下記のような色で表示されているはずだ: fig/emacs-sample1.png

3.6.3 端末の起動

コンパイルや実行は Emacs 上でも可能だが,今回は,様々な処理が可能な「端末」を使っ てみる.

  1. pwd : 現在のフォルダを確認する

    端末の起動時のフォルダはホームフォルダ(~/)である.現在のフォルダを確認する には pwd (print working directory)というコマンドを使う. 端末には bash3.2$ と表示されているので,その後に続けて pwd とタイプして RET を入力すると,

    $pwd
    /home/******
    

    と,ホームフォルダが表示されるはず(******のところには学籍番号が表示されるはず).

  2. cp : cpp フォルダに移動する

    フォルダの移動には cd (change directory)というコマンドを使う.

    $ cd cpp
    

    というように,コマンド名 cd の後に,半角スペースを1つ空けて移動するフォルダ 名(cpp)をタイプして RET を入力する.

    再び pwd を使って移動できているかを確認してみよう.

    $ pwd
    /home/******/cpp
    

    と表示されれば OK.

  3. ls : フォルダの一覧を確認する.

    現在のフォルダ(~/cpp)の中身を確認してみよう.フォルダ内のファイル一覧を取得 するには ls というコマンドを使う.ls とタイプして RET を入力する.

    $ ls
    sample1.cpp
    

    と表示されただろうか.

    以上の一連の処理を行なったときの端末は次のように表示されているだろう: fig/shell-cd-pwd.png

3.6.4 コンパイル

C++ 言語で書かれたテキストファイル(sample1.cpp)を,計算機で実行可能なバイナリ ファイルに変換する(コンパイル する)には g++ というコマンドを使う.

  1. g++ を用いてソースファイル sample1.cpp から実行可能ファイル sample1.out へ変換する(コンパイルする)

    端末に以下をタイプして RET を入力する.

    $ g++ sample1.cpp -o sample1.out
    

    このコマンドは,以下のように4つの部分から構成されている:

    1) g++
    コマンド本体
    2) sample1.cpp
    C++言語で書かれたソースファイルの名前
    3) -o
    出力ファイル名を指定するオプション
    4) sample1.out
    出力ファイル名

    sample1.cpp に問題が無ければ,これによってソースファイルが解析され, sample1.out という実行ファイルが生成される.

  2. sample1.out が生成されたことを確認

    ls コマンドを作って sample1.out が生成されたことを確認.

    以上の一連の処理を行なったときの端末は次のように表示されているだろう: fig/shell-g++.png

3.6.5 実行

上記のようにして生成された実行可能ファイル sample1.out を実行してみよう. 端末で ./sample1.out とタイプして RET を入力すると,

$./sample1.out
Hello world

と表示され,再び bash3.2$ と表示されて入力待ち状態になっただろうか?

ここで, sample1.out の前に ./ をつけるのは,対象となる実行ファイルが現在のフォルダ( . で表される)にあることを明示するためである.

3.6.6 ソースコードの解説

sample1.cpp の構造と,各行の意味を解説しよう.

第1行
標準出力 cout を使うためのライブラリ <iostream> を読み込む
#include <iostream>             // 標準入出力用ライブラリを読み込む

ライブラリとは,一連の目的の下に定数や関数の定義を集めたもの.「車輪 の再発明」を避けられる.標準ライブラリは,その中でも特に標準的に利用 されるもの.

#include <ライブラリ名> とすることで,標準ライブラリを読み 込める.よく使う標準ライブラリには,例えば,以下のようなものがある:

<iostream>標準入出力用のライブラリ.おそらく最も利用頻度が高い.
<fstream>ファイル入出力用のライブラリ
<iomanip>入出力制御用のライブラリ
<string>高機能な文字列型の string 型用のライブラリ
<cmath>数学定数などのライブラリ
第2行
デフォルトの名前空間 std を用いる
using namespace std;            // 名前空間 std をデフォルトで利用する

C++ では,変数名や関数名の衝突を避けるために「名前空間」を用いる.同じ 名前の友人を区別するためにつける名字やニックネーム(例:「長屋」の八っつ あん)のようなものだと思えばいい.標準出力関係で頻繁に用いる coutendl などは std という名前空間の下で定義されているため,本来は std::coutstd::endl といったように「名字」をつける必要がある. using namespace std; と宣言することで「このスコープでは std という名前 空間を使いますよ」ということを指定している.

第3行
空行

C++ では空の行は読み飛ばされる.プログラムを読み易くするための空行は 奨励される.

第4行
コメント文

C++では // 以降は全てコメント文と見なし,無視される.

第5行〜第6行
main 関数を宣言
// メインルーチン
int main ( void ) // 引数無しで整数(int)型の戻り値を返す main 関数を定義
{

C++では main という名前の関数から処理が始まる.一般に,関数は

戻り値の型 関数名 ( 引数 )
{
  関数の定義
}

という構造をしている.この行は,main を, 引数無し(void)で int という整数型の戻り値を返す関数として定義している.

なお,第6行の冒頭では開き波括弧「{」によって関数本体の宣言が始まるこ とを伝えている.

第7行
標準出力に "Hello world" という文字列と改行(endl)を渡す
cout << "Hello world" << endl; // 標準出力 cout に Hello world という文字列と改行を送る

この行は sample1.cpp で唯一「処理」に相当することをしている.この行 は,空白で区切られた以下の5つの部分で構成されている:

  1. cout : 標準出力ストリーム.
  2. << : 挿入演算子.左辺のストリーム(この場合 cout )に右辺の要素(この場合 "Hello world" )を出力する.
  3. "Hello world" : 文字列定数."(ダブルクォーテーション)で囲まれた文字は文字列定数として扱われる.
  4. << : 再び挿入演算子.左辺のストリーム(この場合, "Hello world" を出力し終えた cout ストリーム)に右辺の要素(この場合 endl)を出力する.
  5. endl: 改行を表す定数.

従って,この行は「 cout"Hello world" と改行をこの順に出力せよ」という命令を表している.

第8行
正常終了であることを関数を呼び出した側に伝える
return 0;                      // 正常終了を表す 0 を返す

return とすることで現在の関数を抜け出し,呼び出し側へ移動する.この とき空白の後に戻り値を指定すれば,呼び出し側へその値を渡すことができ る.

第9行
関数 main が終わることを宣言.
}

C++の関数の定義は閉じ波括弧「}」で終了する必要がある.

3.6.7 コンパイルエラーとデバッグ

g++ はデフォルトでは「うまくいった時には何も表示しない」という「不言実行」仕様 で設計されている.そのため「 g++ がお返事した時」は何かがうまくいっていないので ある.以下では,どんな場合にどのようなエラーが表示され,どう対処すべきかの例をい くつか挙げよう.

  • 行番号までコピーしてしまった場合
    不注意な学生は「あ,またコピペでいいのかな」と,ブラウザ上に表示されたソースコー ドをそのままコピー&ペーストしているかもしれない.このとき,Emacs 上では以下のよ うに表示される: fig/emacs-sample1-naive_copy.png

    色が崩れていないので,一見よさそうに見えるが,これをコンパイルすると次のように, エラー・メッセージが表示される: fig/shell-g++-naive_copy.png

    ここでは,3つのエラー・メッセージが表示されている:

    1. sample1.cpp:1: error: stray '#' in program
    2. sample1.cpp:1: error: expected unqualified-id before numeric constant
    3. sample1.cpp:3: error: expected unqualified-id before numeric constant

    それぞれのエラー・メッセージは,

    ファイル名:行番号: error: エラーの内容
    

    という構造で表示されている.すなわち,それぞれのエラー・メッセージは,以下のこと を表している:

    1. sample1.cpp1行目stray '#' in program というエラーが起きました

      ここで,stray '#' in program は「適切な位置でないところに '#' という文字がありま すよ」という意味.この1行目の最初の文字が # で始まるべきところ, 1: という文字で始まっているため,# が登場したところで構文解析できなくなっ ている.

    2. sample1.cpp1行目expected unqualified-id before numeric constant というエラーが起きました

      ここで,expected unqualified-id before numeric constant は「数値定数の前に 何かが必要なはずです」という意味(多分).1行目の最初の 1 を数値と見なすと, その前に代入や宣言が無いので構文解析できなくなっている.

      なお,2行目について何も言及が無いのは問題が無いからではなくて,2行目まで含め て構文解析してみたところ,少なくともこういうエラーが起きたので,これ以上解析 できないことを伝えている.

    3. sample1.cpp3行目expected unqualified-id before numeric constant というエラーが起きました

      上と同様.4行目以降について何も言及が無いのは問題が無いからではないことに注意.

    しかるべき対処方法は,当然,各行の先頭から「行番号」+「:」を取り除くことである.

  • 1行目の頭の # を忘れた場合
    このとき,Emacs 上では以下のように,1行目がぜんぶ緑色で表示される: fig/emacs-sample1-forgot_sharp.png

    これに気づかずコンパイルすると,以下のエラーメッセージが表示される(後で説明し易 くするため,行番号をつけ足してある):

    1:  sample1.cpp:1: error: expected constructor, destructor, or type conversion before '<' token
    2:  sample1.cpp: In function 'int main()':
    3:  sample1.cpp:7: error: 'cout' was not declared in this scope
    4:  sample1.cpp:7: error: 'endl' was not declared in this scope
    

    このうち,2行目は,続く3行目と4行目のエラーが int main() という関数内で見つかっ ていることを表しているので,実際に報告されているエラーは以下の3つ:

    1. sample1.cpp1行目expected constructor, destructor, or type conversion before '<' token (< という記号(トークン)の前に,コンストラクタ, デストラクタもしくは型変換があるはずです)というエラー
    2. sample1.cpp7行目'cout' was not declared in this scope (このスコープ内では 'cout' は定義されていません)というエラー
    3. sample1.cpp7行目'endl' was not declared in this scope (このスコープ内では 'endl' は定義されていません)というエラー

    問題があるのは1行目なのに,7行目までエラーが出ている.これは,本来,1行目の

    #include <iostream>
    

    coutendl の「定義」を読み込むところでエラーが生じているため 「得体の知れない coutendl は使えません」と返されている.

  • g++ コマンドを間違った場合
    端末上で g++ コマンドを間違って読んでもエラーが生じる.例えば,次の例:
    $g++ sample.cpp -o sample1.out
    g++: sample.cpp: そのようなファイルやディレクトリはありません
    g++: no input files
    

    では,入力ファイル名として sample1.cpp を渡すべきところを sample.cpp とミス タイプしたため「そのようなファイルやディレクトリはありません」と返されている.

    エラー・メッセージは表示されないが,意図しない動作をすることもある.例えば, -o 以下を入力するのを失念して

    $g++ sample1.cpp
    

    とした場合,エラー・メッセージは表示されない.これは,g++-o オプションを 与えられなかった場合には,生成する実行可能ファイル名とに自動的に a.out を採用 するように設計されているためである.

    実際,ls コマンドで一覧を表示させると a.out というファイルができていることを 確認できるだろう:

    $ls
    a.out sample1.cpp
    

    そのにも関わらず ./sample1.out を実行しようとすると,

    $./sample1.out
    bash: ./sample1.out: そのようなファイルやディレクトリはありません.
    

    とエラーが表示される.

    これを解決するには,端末上で mv (move)コマンドを使ってファイル名を a.out か ら sample1.out に変更してやればよい.

    $mv a.out sample1.out
    

    このコマンドは以下の3つの部分から構成されている:

    1. mv コマンド
    2. a.out 変更前のファイル名
    3. sample1.out 変更後のファイル名

    以上の一連の処理を行なったときの端末は次のように表示されているだろう: fig/shell-g++-without-option.pngp

  • クイズ:どこが間違っている?
    問題:次のプログラム
    1:  #include <iostream>             // 標準入出力用ライブラリを読み込む
    2:  using namespace std;            // 名前空間 std をデフォルトで利用する
    3:  
    4:  // メインルーチン
    5:  int main ( void ) // 引数無しで整数(int)型の戻り値を返す main 関数を定義
    6:  { 
    7:    cout << "Hello world" << endl  // 標準出力 cout に Hello world という文字列と改行を送る
    8:    return 0;                      // 正常終了を表す 0 を返す
    9:  }
    

    g++ でコンパイルすると以下のエラー・メッセージが表示される.

    sample1.cpp: In function 'int main()':
    sample1.cpp:8: error: expected `;' before 'return'
    

    このプログラムのどこをどう直せばよいだろうか?

3.7 [7/5] C++でKMLを出力し,Google Map に渡してみる(レポート課題あり)

3.7.1 何のため?

C++ で KML を出力し,Google Map に表示させてみる. まず,C++ で KML を標準出力に表示させるプログラムを作成する. 次に,「準備」の章で ~/.bashrc に登録したシェルスクリプト klip を使って, 出力された KML を Klipper と呼ばれるクリップボード用のソフトウェアに保存する. 最後に,クリップボードに保存された KML を Gist に貼り付け,その URL を Google Map に渡してみる.

3.7.2 標準出力にKMLを出力するプログラムを作成し,コンパイルして実行してみる

  1. 新しいファイル ~/cpp/sample1-2.cpp を作成する

    Emacs 上で C-x C-f と入力すると,ミニバッファに Find file: と表示されるので, その部分を ~/cpp/sample1-2.cpp となるように修正して RET を入力する.

    なお,Fild file:= の後には,C-x C-f を呼び出す時に開いていたファイルと同じフォ ルダが表示される.例えば,Emacs の起動直後や ~/.bashrc の編集後に C-x C-f とすると Fild file: ~/ と表示されるし,~/cpp/sample1.cpp を編集した 後で C-x C-f とした時には Find file: ~/cpp と表示される.前者の場合は ~/ の後に cpp/sample1-2.cpp と入力,後者の場合は ~/cpp/ の後ろに sample1-2.cpp と入力して RET を入力すればよい.

    開いた sample1-2.cpp に以下を記述し,C-x C-s で保存する.

     1:  #include <iostream>             // 標準入出力用ライブラリを読み込む
     2:  using namespace std;            // 名前空間 std をデフォルトで利用する
     3:  
     4:  // メインルーチン
     5:  int main ( void )               // 引数無しで整数(int)型の戻り値を返す main 関数を定義
     6:  {
     7:    // 緯度・経度を格納するための倍精度実数(double)型の変数を定義
     8:    double latitude = 38.2523;    // 緯度
     9:    double longitude = 140.856;   // 経度
    10:  
    11:    // 標準出力(cout)に KML を表示
    12:    // KMLを使うためのヘッダを出力
    13:    cout << "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
    14:         << "<kml xmlns=\"http://www.opengis.net/kml/2.2\">" << endl;
    15:  
    16:    // 地図上の目印を<Placemark>〜</Placemark>の間に定義
    17:    cout << "  <Placemark>\n"
    18:      // 目印の名前を<name>〜</name>の間に記述
    19:         << "    <name>仙台城跡</name>\n"
    20:      // 目印の説明として緯度と経度を<description>〜</description>の間に記述
    21:         << "    <description>\n"
    22:         << "    緯度:" << latitude << " 経度:" << longitude << "\n"
    23:         << "    </description>" << endl
    24:      // 目印の構成要素として1つの点(マーク)を<Point>〜</Point>の間に定義
    25:      // この点の座標を<coordinates>〜</coordinates>の間に経度,緯度,標高の順に記述
    26:         << "    <Point><coordinates>\n"
    27:         << "      " << longitude << "," << latitude << ",0\n"
    28:         << "    </coordinates></Point>\n"
    29:         << "  </Placemark>" << endl;
    30:  
    31:    // KMLを使うためのフッタを出力
    32:    cout << "</kml>" << endl;
    33:  }
    
  2. コンパイルして実行してみる

    端末上で以下のように入力する(# 以降の赤字になっている部分はコメントなので入力 する必要なし)

    $cd ~/cpp                           # cpp フォルダへ移動
    $g++ sample1-2.cpp -o sample1-2.out # コンパイル
    $./sample1-2.out                    # 実行すると,KMLが表示される     
    

    以下が出力されれば OK.

    <?xml version="1.0" encoding="UTF-8"?>
    <kml xmlns="http://www.opengis.net/kml/2.2">
      <Placemark>
        <name>仙台城跡</name>
        <description>
        緯度:38.2523 経度:140.856
        </description>
        <Point><coordinates>
          140.856,38.25230
        </coordinates></Point>
      </Placemark>
    </kml>
    

3.7.3 C++ ソースコードの解説

sample1-2.cpp の構造と,各行の意味を解説しよう.

第1〜第2行

標準出力 cout を使うためのライブラリ <iostream> を読込み,デフォルトの名前 空間として std を用いる.

#include <qiostream>             // 標準入出力用ライブラリを読み込む
using namespace std;            // 名前空間 std をデフォルトで利用する

この2行は,どんな時にも記述する「おまじない」のようなものだと思ってもらっていい.

第4〜第5行
main 関数を宣言.
// メインルーチン
int main ( void )               // 引数無しで整数(int)型の戻り値を返す main 関数を定義
{
第8〜第9行
2つの倍精度実数(double)型の変数 latitudelongitude を定義し,それぞれ を定数で初期化.
// 緯度・経度を格納するための倍精度実数(double)型の変数を定義
double latitude = 38.2523;    // 緯度
double longitude = 140.856;   // 経度

「変数」とは,数値や文字列などのデータを格納しておく「箱」のようなものだ.それぞれの箱 には,入れられるデータの種類(整数,実数,文字列など)が決まっており,これを「型」 と呼ぶ.整数を格納するための型は int 型,実数を格納するための型は double 型で ある.

それぞれの変数には,型とは別に,名前がついている.変数の名前は型とは無関係に, 自由につけて良い.変数名には英字・数字および _ (アンダーバー)が利用できる. 大文字と小文字は区別されるので,longitudeLongitude は全く別物として扱 われる.

変数に値を格納するためには,まず,変数を定義しなければならない. 変数の定義は,

型 変数名;

とする.同じ型の複数の変数を定義する時には,

型 変数名1, 変数名2;

と「, 」で区切って変数名を並べてよい.

ある変数に値を格納( 代入 という)するには,

変数名 = 値; 

とする.ここで, = は「その左側の変数の中身を,右側の値で書き換える」という処理を行う演算子である. 右側に変数や式があれば,それを評価したものを「値」として採用する. なお,この例のように,変数を定義すると同時に値を代入することを 初期化 といい, その(プログラム上の)挙動は,厳密には,代入とは異なる.

第13〜14行
標準出力 cout に KML を使うためのヘッダを表示する.
cout << "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
       << "<kml xmlns=\"http://www.opengis.net/kml/2.2\">" << endl;                

KML の基本構造は以下のような形をしているため,ここでは,その最初の2 行(ヘッダと呼ぶ)を出力している.最後の </kml> は,後述するように第 32行で出力する.

<?xml version="1.0" encoding="UTF-8"?>
<kml xmlns="http://www.opengis.net/kml/2.2">

KML の中身

</kml>

なお,KML の中身は,全て,

<タグ名>内容</タグ名>

というタグで構成されている.

第17〜第29行
<Placemark>〜</Placemark> タグの内部を標準出力に表示する.
cout << "  <Placemark>\n"
     << "    <name>仙台城址</name>\n"
     << "    <description>\n"
     << "    緯度:" << latitude << " 経度:" << longitude << "\n" // 変数 latitude, longitude を利用
     << "    </description>" << endl
     << "    <Point><coordinates>\n"
     << "      " << longitude << "," << latitude << "0\n" // 変数 latitude, longitude を利用
     << "    </coordinates></Point>\n"
     << "  </Placemark>" << endl;

KML では,1つの目印について,1組の <Placemark>〜</Placemark> を記述する.Placemark タグの中身としては,以下のような ものがある

タグ内容記述例
<name>目印の名前<name>仙台城跡</name>
<description>目印についての説明<description>緯度:38.2523 経度:140.856</description>
<Point>地点.必ず <coordinate> タグを内包する<Point><coordinates>140.856,38.25230,0</coordinates></Point>
<coodinates>座標.経度(x),緯度(y),高度(z)の順にコンマで区切って入力<coordinates>140.856,38.25230,0</coordinates>

この例では,<description> タグと <coodinate> タグの中への出 力として緯度・経度(38.2523, 140.856)の値を直接記述する代わり に,これらを格納した変数 latitude , longitude をストリーム 挿入演算子 << で連結している.

こうしておけば,別の緯度と経度を与えたくなったとき(例えば,演 習課題を実施するとき)には,第8〜第9行目の値だけを変更すればよ い.2回以上つかいまわす値は,変数に格納する のが鉄則である.

第32行
KML のフッタを出力する.
cout << "</kml>" << endl;

3.7.4 出力された KML をクリップボードにコピーする

sample1-2.out によって出力された KML を Gist にアップロードするためには,これ を一旦クリップボードにコピーする必要がある.これには以下の2つの方法がある:

  1. 端末上で領域を選択して右クリックメニューからコピーする.

    直感的だが,領域の選択を間違う可能性があったり,出力される結果が大きくなる と選択するのが大変になる.左ボタンを押しながらマウスを動かすことで領域を選択 できる.選択が終わったら右クリックメニューから「コピー」を選択する. fig/shell-sample1_2-mouse.png

  2. シェルのパイプ機能とシェルスクリプト klip を使ってクリップボードへ実行結果を送る

    シンプルなコマンドで実現できる.マウス操作が不要なので人為的ミスをしにくい.端末上で

    ./sample1-2.out | klip
    

    というように,実行ファイル ./sample1-2.out の後に縦棒「 | 」と シェルスクリプト klip を記述することで,./sample1-2.out の実行結果を (Klipper)が管理するクリップボードに取り込める. fig/shell-sample1_2.png

3.7.5 クリップボード上の KML を Gist に投稿し,そのリンクを Google Maps に渡す

  1. Gist に KML を投稿する

    https://gist.github.com にログインすると Gist 投稿画面になるので, = クリップボードの中身を貼り付ける

    • ファイル名を castle.kml などとする(ファイル名は自由につけてよいが,拡張子 は =.kml=にしておく必要がある)
    • ファイルの説明を記述する
    • 「Create Secret Gist」もしくは「Create Public Gist」をクリックして Gist を投 稿する
  2. Gist のリンクを Google Maps に渡す
    • 投稿した Gist のページに移動し,View Raw アイコン の上で右クリック: fig/gist-castle-rawfile.png
    • メニューから「リンクのURLをコピー」を選択
  3. Google Maps に KML のリンクを渡す
  4. こんな画面になった? fig/google-maps-castle.png

3.7.6 レポート課題

  1. sample1-2.cpp をもとにして,
    • 緯度: 35.6332, 経度: 139.881 に目印(<Placemark>)を置く
    • 目印の説明(<description> タグ)として,上記の座標 に加えて,学籍番号と氏名 を記載する.
    • 目印の名前(<name> タグ)を(「仙台城跡」ではなく),当該座標に適したものにす る

    ような KML を出力するプログラムを C++ を用いて作成し,そのソースファイルを report1-2.cpp という名前で提出せよ.

  2. 出力された KML を report1-2.kml という名前で Gist にアップロードし,そのリ ンクを Google Maps に渡し,意図通りに表示されているかを確認せよ. 以下の方法を参考に,表示させた地図へのリンク をコピーし,提出せよ. fig/report1-2.png
  • 演習課題のヒント
    1. sample1-2.cppreport1-2.cpp という別名で保存する.これを実行するには いく通りもの方法がある.例えば,
      1. Emacs 上で以下を入力する
        C-x C-w ~/cpp/report1-2.cpp
        
      2. 端末上で以下を入力する
        $cp ~/cpp/sample1-2.cpp ~/cpp/report1-2.cpp
        
      3. Cent OS のメニューから「ホーム」を選び,右クリックメニューを使ってコピー, 貼り付けする(名前が重複するときはファイル名を入力するようになっている).

      などがある.

    2. 第8〜9行目の緯度と経度を,与えられたものに修正する.
    3. 第19行目の <name>〜</name> の中身を適切なものに修正する.
    4. 第21〜24行目に,以下のような1行をつけ加える:
      修正前
      << "    <description>\n"
      << "    緯度:" << latitude << " 経度:" << longitude << "\n"
      << "    </description>" << endl
      
      修正後
      << "    <description>\n"
      << "    緯度:" << latitude << " 経度:" << longitude << "\n"
      << "    学籍番号:xxxxxxxx 名前:oooooooo" // ←この行を追加
      << "    </description>" << endl
      
    5. 「端末」上で report1-2.cpp をコンパイルし,実行する
      $ cd ~/cpp                      # ディレクトリを ~/cpp に移動する
      $ g++ report1-2.cpp -o report1-2.out # コンパイルする
      $ ./report1-2.out                    # 実行する
          :
          :
        (実行結果として KML が表示される)
          :
          :
      
    6. 出力された KML をクリップボードにコピーする
      $ ./report1-2.out | klip
      
    7. http://gist.github.com にアクセスし,コピーした KML を貼り付け, ファイル名を report1-2.kml として「Create Privae(もしくはPublic) Gist」とし て新しい gist を作成する.
    8. 作成された gist の raw ファイルの URL を取得し,Google Maps (http://maps.google.co.jp) に渡す.
    9. 適切な目印が表示されたことを確認したら,課題を提出する.
  • 課題の提出方法
    C++ソースファイルおよび Google Maps へのリンクの提出は 東北大学インターネット スクール(ISTU: https://xapp.istu.jp) から行う.
    1. ISTU にアクセスし,「情報基礎B」のレポート「指定された座標に目印を置くKMLを出 力するプログラムを C++ で作成し,目印をGoogle Maps に表示させる」を選択すると, ファイルとテキストを投稿する画面が現れる.
    2. ファイル名の横の「参照」をクリックし,自分が作った report1-2.cpp を選択する.
    3. Google Maps への URL (https://maps.google.com/maps?q=http://... で始まるも の) を取得し,「解答」というテキストエリアに貼り付ける
    4. 準備ができたら,画面右下の「提出」ボタンをクリック

    fig/report1-2-submit.png

3.8 [7/12] C++で標準入力を受けとるプログラムを作って実行してみる

3.8.1 何のため?

ここでは,

  • 標準入力から緯度と経度を受けとってそれを表示するプログラム(sample2)
  • 受け取った緯度と経度が宮城県内に存在するかどうかを表示するプログラム(sample3)

を作る.これによって,

  • 入力ストリームから >> 演算子を用いてデータを取得する方法
  • setfprecision を使って特定の書式でデータを出力する方法
  • if 文を用いたプログラムの「分岐」の方法

を学ぶ.

3.8.2 標準入力から情報を受けとるプログラムを作成し,コンパイルして実行してみる

  1. 新しいファイル ~/cpp/sample2.cpp を作成する.

    Emacs 上で以下を入力して ~/cpp フォルダの下に sample2.cpp というファイル を作ろう.

    C-x C-f ~/cpp/sample2.cpp RET
    
  2. sample2.cpp ファイルを以下のように編集し,保存(C-x C-s)する.
     1:  #include <iostream>             // 標準入出力用ライブラリを読み込む
     2:  using namespace std;            // 名前空間 std をデフォルトで利用する
     3:  
     4:  // メインルーチン
     5:  int main ( void ) // 引数無しで整数(int)型の戻り値を返す main 関数を定義
     6:  {
     7:    // 標準入力(cin)から緯度と経度を獲得
     8:    double latitude, longitude; // 2つの倍精度実数型の変数 latitude, longitude を定義
     9:    cout << "緯度と経度を入力して下さい" << endl; // 標準出力 cout に緯度・経度の入力を促すメッセージを表示
    10:    cin >> latitude >> longitude; // 標準入力から latitude と longitude の値を獲得
    11:  
    12:    // 標準出力(cout)に緯度と経度を表示
    13:    cout << "緯度:" << latitude << " 経度:" << longitude << endl; // 文字列と組み合わせて表示
    14:  }
    
  3. 端末上で以下のように入力してコンパイルし,実行する.
    $ cd ~/cpp                      # ディレクトリを cpp に移動(既に移動済みなら省略可能)
    $ g++ sample2.cpp -o sample2.out # コンパイルする
    $ ./sample2.out                  # 実行する
    

    このとき,端末には下記のように表示され,入力を待つ状態になる: fig/shell-sample2-prompt.png

    ここで,宮城県の県庁所在地の緯度・経度を,半角スペースで区切って下記のように タイプ:

    38.268839 140.872103
    

    して RET を入力すると,

    緯度:38.2688 経度:140.872
    

    と表示される.緯度に関しては小数点以下第5桁以降が,経度に関しては第4桁以降が 表示されていないが,これに関しては,次の項で修正しよう.

    なお,緯度・経度は,改行で区切ってもよいが,コンマ(,)などで区切ってはいけない. fig/shell-sample2.png

3.8.3 ソースコードの解説

sample2.cpp の構造と,各行の意味を解説しよう.

第1〜第2行
標準出力 cout や標準入力 cin を使うためのライブラリ <iostream> を読込み, デフォルトの名前空間として std を用いる.
#include <iostream>             // 標準入出力用ライブラリを読み込む
using namespace std;            // 名前空間 std をデフォルトで利用する
第4〜第6行
main 関数を宣言
// メインルーチン
int main ( void ) // 引数無しで整数(int)型の戻り値を返す main 関数を定義
{
第8行
入力された緯度,経度を格納しておくための2つの倍精度実数(double)型の変数 latitudelongitude を定義する(初期化はしない).
double latitude, longitude; // 2つの倍精度実数型の変数 latitude, longitude を定義  
第9行
標準出力 cout に入力を促すメッセージを表示.
cout << "緯度と経度を入力して下さい" << endl; // 標準出力 cout に緯度・経度の入力を促すメッセージを表示

cout のような出力ストリームに文字や数値を送るには,<< という演算子を用いる.

出力ストリームへは,いくつでも,どんな型のデータでも送ることができる.

cout << [データ1] << [データ2] << ... << [データn]; 

というように,<< を間に入れることで,この順に,いくつでもデータを出力させられ る.ここで[データ1]や[データ2]などの位置に指定できるのは,以下の3つの いずれかである.

  1. (既に定義されている) 変数
  2. (123 や "Hello world" や "d34a0rtt" のような) 定数
  3. endl (改行)のような マニピュレータ
第10行
標準入力 cin から latitude および longitude の値をこの順に取得す る.
cin >> latitude >> longitude; // 標準入力から latitude と longitude の値を獲得             

標準入力とは,通常は,コンソール(端末のこと)からのキーボード入力を 指す.cin のような入力ストリームからデータを取得するには, >> という抽出演算子を用いる(出力のときとは向きが逆なことに注意され たい).

挿入演算子と同様に,抽出演算子を用いれば,いくつでも,どんな型のデー タでも入力ストリームから取得できる.

cin >> [データ1] >> [データ2] >> ... >> [データn];

ただし,挿入演算子とは異なり,[データ1], [データ2]などの位置には,変 数しか指定できない.

第13行
入力された緯度と経度を出力する.
cout << "緯度:" << latitude << " 経度:" << longitude << endl; // 文字列と組み合わせて表示

ここでは,標準出力 cout に,以下の5つのデータを順に送っている.

  1. "緯度:"という文字列
  2. latitude という変数
  3. "経度:"という文字列
  4. longitude という変数
  5. endl というマニピュレータ

ただし,2 と 4 の変数については,変数の名前(latitude, longitude) ではなく,変数に格納された数値が展開されて標準出力に送られる.

第14行
main 関数の終わりを宣言.
}

3.8.4 表示桁数を修正してみる

上述の方法では,緯度が小数点以下第4桁まで(経度は第3桁まで)しか表示されない.これ を修正するために,出力ストリーム・クラスが備える setfprecision という関数 を使う(同時に,これらを使うのに必要な <iomanip> というライブラリを読み込む).

  1. ~/cpp/sample2.cpp を別名(sample2-2.cpp)で保存する.

    sample2.cpp が開かれているバッファ上で C-x C-w と入力すると,ミニバッファ に

    Write File: ~/cpp/
    

    と表示されるので,~/cpp/ の後に sample2-2.cpp とタイプして RET を入力す る.これで,sample2.cpp と全く同じ内容を持つ ~/cpp/sample2-2.cpp というファ イルが生成された.

  2. ソース・ファイルの修正

    ~/cpp/sample2-2.cpp に以下の2つの行を追加する

    13行目
    cout.setf(ios::fixed);        // 固定小数表記を指定
    
    14行目
    cout.precision(6);            // 小数点以下の桁数を指定
    

    なお,14行目と15行目はインデント(行のどこかで TAB を入力)しておくと見易い.

    これによって,以下の内容を持つ ~/cpp/sample2-2.cpp が作成できる.

     1:  #include <iostream>             // 標準入出力用ライブラリを読み込む
     2:  using namespace std;            // 名前空間 std をデフォルトで利用する
     3:  
     4:  // メインルーチン
     5:  int main ( void ) // 引数無しで整数(int)型の戻り値を返す main 関数を定義
     6:  {
     7:    // 標準入力(cin)から緯度と経度を獲得
     8:    double latitude, longitude; // 2つの倍精度実数型の変数 latitude, longitude を定義
     9:    cout << "緯度と経度を入力して下さい" << endl; // 標準出力 cout に緯度・経度の入力を促すメッセージを表示
    10:    cin >> latitude >> longitude; // 標準入力から latitude と longitude の値を獲得
    11:  
    12:    // 標準出力(cout)に緯度と経度を表示
    13:    cout.setf(ios::fixed);        // 固定小数表記を指定
    14:    cout.precision(6);            // 小数点以下の桁数を指定
    15:    cout << "緯度:" << latitude << " 経度:" << longitude << endl; // 文字列と組み合わせて表示
    16:  }
    
  3. コンパイルして実行する

    端末上で以下のように入力する:

    $ g++ sample2-2.cpp -o sample2-2.out
    $ ./sample2-2.out
    緯度と経度を入力して下さい      # 入力待ち
    38.268839 140.872103            # 緯度と経度を入力して Enter
    緯度:38.268839 経度:140.872103  # 小数点以下6桁までが表示される
    
  4. 変更した部分の解説

    追加した各行がどのような意味を持つのかを解説しよう.

    13行目
    標準出力 cout の表示として固定小数方式を指定
    cout.setf(ios::fixed);        // 固定小数表記を指定
    

    cout のメンバ関数 setf に引数 ios::fixed を与えて呼ぶことで, 小数点の位置を固定した表示方式を指定している.

    14行目
    標準出力 cout の表示精度を小数点以下6桁に指定
    cout.precision(6);            // 小数点以下の桁数を指定
    

    cout のメンバ関数 precision に引数 6 を与えて呼ぶことで,小数 点以下6桁まで表示するように指定している.

3.8.5 入力された緯度・経度が宮城県内にあるか否かを判断して表示させる

今度は,入力された緯度・経度だけではなく,その座標が宮城県内にある か否かも表示させてみよう.

国土地理院 が提供する 都道府県の庁舎及び東西南北端の経緯度 のページから, 宮城県の東西南北端の緯度と経度(世界測地系)は,それぞれ,以下のように得られる.

  • 南端: 37.773333 (北緯 37度46分24秒)
  • 北端: 39.002778 (北緯 39度00分10秒)
  • 西端: 140.275000 (東経 140度16分30秒)
  • 東端: 141.675278 (東経 141度40分31秒)

なお,「37度46分24秒」という60進数から「37.773333」という10進数へは以下の式で変換できる.

10進数 = (度) + (分÷60) + (秒÷3600)

これより,緯度・経度が以下の2つの条件:

  • 緯度が 37.773333 以上かつ 39.002778 以下
  • 経度が 140.275000 以上かつ 141.67528 以下

を同時に満足するとき,その点は宮城県内にあると判定することにしよう.

  1. ~/cpp/sample2-2.cpp を別名(sample3.cpp)で保存する.
    C-x C-w ~/cpp/sample3.cpp RET
    
  2. ソース・ファイルの修正

    ~/cpp/sample3.cpp を以下のように修正する(17行目〜32行目を追加):

     1:  #include <iostream>             // 標準入出力用ライブラリを読み込む
     2:  using namespace std;            // 名前空間 std をデフォルトで利用する
     3:  
     4:  // メインルーチン
     5:  int main ( void ) // 引数無しで整数(int)型の戻り値を返す main 関数を定義
     6:  {
     7:    // 標準入力(cin)から緯度と経度を獲得
     8:    double latitude, longitude; // 2つの倍精度実数型の変数 latitude, longitude を定義
     9:    cout << "緯度と経度を入力して下さい" << endl; // 標準出力 cout に緯度・経度の入力を促すメッセージを表示
    10:    cin >> latitude >> longitude; // 標準入力から latitude と longitude の値を獲得
    11:  
    12:    // 標準出力(cout)に緯度と経度を表示
    13:    cout.setf(ios::fixed);        // 固定小数表記を指定
    14:    cout.precision(6);            // 小数点以下の桁数を指定
    15:    cout << "緯度:" << latitude << " 経度:" << longitude << endl; // 文字列と組み合わせて表示
    16:  
    17:    // 宮城県内かどうかを判別
    18:    double lat_min =  37.77333, lat_max =  39.002778; // 南端の緯度(緯度の最小値)および北端の緯度(緯度の最大値)
    19:    double lng_min = 140.27500, lng_max = 141.675278; // 西端の経度(経度の最小値)および東端の経度(経度の最大値)
    20:  
    21:    if ( latitude < lat_min       // 「緯度 latitude が最小値 lat_min より小さい」
    22:         || latitude > lat_max    // または「緯度 latitude が最小値 lat_max より大きい」
    23:         || longitude < lng_min   // または「経度 longitude が最小値 lng_min より小さい」
    24:         || longitude > lng_max ) // または「経度 longitude が最小値 lng_max より大きい」
    25:                                  // ならば「この点は宮城県外である」と判定される
    26:      {                  
    27:        cout << "緯度,経度もしくはその両方が範囲外です" << endl;
    28:      }
    29:    else                          // 以上の条件のどれにも該当しないなら「宮城県内である」と判定される
    30:      { 
    31:        cout << "この点は(おそらく)宮城県内です" << endl;
    32:      }
    33:  }
    
  3. コンパイルして実行する

    端末上で以下のように入力する:

    $ g++ sample3.cpp -o sample3.out
    $ ./sample3.out
    緯度と経度を入力して下さい      # 入力待ち
    

    何度か実行して緯度や経度を入力してみよう.緯度・経度だけでなく,宮城県内かど うかも表示してくれる.少々弱気な表現になっている理由は「補足:正確に宮城県内/ 外を判定するには?」に後述したので読んでみて欲しい. fig/shell-sample3.png

  • 補足:正確に宮城県内/外を判定するには?
    県境はメルカトル図法上の長方形にはなっていないので,当然,上記の条件 だけでは宮城県の内外は判定できない(「おそらく」という弱気なメッセージ なっているのはそのためである).例えば,天童市(北緯:38.361042, 東 経:140.379181)は上記の緯度・経度内に入るが,明らかに宮城県ではない.

    こうした厳密な判断には,Google Map の Geocoding API などが活用できる. 例えば,以下のフォームは,Geocoding API と Javascript を用いて, 入力された緯度と経度から県名を取得する.

    緯度: 経度:
    県名: 住所:
    ボタンを押すとここに 地図が表示されます

3.8.6 sample3.cpp の解説

ここでは,sample3.cpp のうち,sample2-2.cpp と異なる部分を中心に, 各行の意味を解説しよう.

第18〜第19行
4つの倍精度実数(double)型の変数を定義し,宮城県の南端・北端 / 西端・東端で初期化する.
double lat_min =  37.77333, lat_max =  39.002778; // 南端の緯度(緯度の最小値)および北端の緯度(緯度の最大値)
double lng_min = 140.27500, lng_max = 141.675278; // 西端の経度(経度の最小値)および東端の                  

このように変数をまとめて定義しておくことで,分析対象を 「宮城県」ではなく「東京都」「愛知県」とした時に,この部分だけ を変更すればよく,より保守性が高いといえる.

第21〜32行
ifelse 文による分岐
if ( latitude < lat_min       // 「緯度 latitude が最小値 lat_min より小さい」
     || latitude > lat_max    // または「緯度 latitude が最小値 lat_max より大きい」
     || longitude < lng_min   // または「経度 longitude が最小値 lng_min より小さい」
     || longitude > lng_max ) // または「経度 longitude が最小値 lng_max より大きい」
  // ならば「この点は宮城県外である」と判定される
  {                  
    cout << "緯度,経度もしくはその両方が範囲外です" << endl;
  }
 else                          // 以上の条件のどれにも該当しないなら「宮城県内である」と判定される
   { 
     cout << "この点は(おそらく)宮城県内です" << endl;
   }

ここでは,入力された緯度と経度が以下の いずれか1つにでも当ては まる ときには「範囲外です」と表示させ,そうでなければ「宮城県内 です」と表示させている.

  1. 緯度(latitude)が南端(lat_min)より小さい
  2. 緯度(latitude)が北端(lat_max)より大きい
  3. 経度(longitude)が西端(lng_min)より小さい
  4. 経度(longitude)が東端(lng_max)より大きい

このように「ある条件が満足されるときには A という処理を行い 満足されないときには B という処理を行う」などと条件次第で処理 を分けることを 分岐 と呼ぶ.

C++ で上記のような分岐を実現するには,ifelse という命令 を使う.その基本構造は,以下の通りである.

if ( 条件 )
  {
    条件が満足されるときの処理
  }
else
  {
    条件が満足されないときの処理
  }

sample3.cpp では,まず,第21〜24行に if の宣言に続けて括弧 内に条件を記載している:

if ( latitude < lat_min       // 「緯度 latitude が最小値 lat_min より小さい」
     || latitude > lat_max    // または「緯度 latitude が最小値 lat_max より大きい」
     || longitude < lng_min   // または「経度 longitude が最小値 lng_min より小さい」
     || longitude > lng_max ) // または「経度 longitude が最小値 lng_max より大きい」                 

ここでは,上述した4つの条件:

  1. latitude < lat_min
  2. latitude > lat_max
  3. longitude < lng_min
  4. longitude > lng_max

を論理和演算子 || で連結することで, 「 latitude < lat_min または latitude>lat_max または longitude < lng_min または longitude>lng_max 」という条 件を表現している.

続く第26〜第28行は,上記の条件が満足される(つまり,緯度・経度が 宮城県外である)ときの処理を与えている.

{                  
  cout << "緯度,経度もしくはその両方が範囲外です" << endl;
}

最後に,第29〜第32行では,else 命令に続けて,上記の条件が満足されない(つまり,緯度・ 経度が宮城県内である)ときの処理を与えている.

else                          // 以上の条件のどれにも該当しないなら「宮城県内である」と判定される
  { 
    cout << "この点は(おそらく)宮城県内です" << endl;
  }                 

3.8.7 文字化けを解消するには?

  • どんな症状?
    sample3.outsample4.out の実行結果が

    fig/encoding_error_shift_jis.png

    fig/encoding_error_utf-8_console.png

    のように文字化けしていたり,sample3.cpp コンパイル時に

    fig/encoding_error_compile.png

    のようなエラーが出たら,それは 文字コード の問題である.

  • このファイルの文字コードは?
    Emacs 上では,開いているファイルの文字コードはウィンドウの左下のステータスバーに表示されている. fig/emacs_encode.png ここには「E」「S」「J」の3つのアルファベットのいずれかが表示されている.それぞれ, 以下の文字コードを表している.
    記号表示イメージ文字コード
    EEUC-JP
    SShift-JIS
    JUTF-8

    ICL演習室の端末では,日本語を含むファイルは EUC-JP(euc-jp-unix)の文字コードで 保存されていなくてはならない.そのため,この部分に「S」や「J」と表示されていたら, 文字コードを変更する必要がある.

  • 文字コードを変換するには?
    Emacs上で自由に文字コードを変換できる(ただし,そのためのキーバインドが妙に複雑だが). 文字コードを EUC-JP(euc-jp-unix)に変換するには, Emacs 上で
    C-x RET f
    

    と入力する(Ctrl-x, enter キー,f の順に入力)と,ミニバッファに

    Coding system for visited file (default, nil): 
    

    と表示されるので,euc-jp-unix と記述して RET を入力する.ステータスバーの表示が E になったら完成. fig/emacs_encode_change.png

3.9 [7/19] ファイル上のバス停データを KML で出力するプログラムを C++ で作ってみる

3.9.1 何のため?

ここでは,ファイル上にあるバス停データを KML で出力するプログラムを C++ で作って みる.これにより,

  • ファイル入力ストリームを開く方法
  • 入力ストリームから getline を使って CSV 形式のデータを読み込む方法
  • while 文を使った繰り返しの方法

を学ぶ.

3.9.2 クリップボードにコピーするシェルスクリプトを変更

7月4日以前の設定では,日本語を含む処理結果を適切に表示できない.そこで, ~/.bashrc の中身を以下のように修正する(catlv に変更する):

修正前
klip() { dcop klipper klipper setClipboardContents "$(cat $1)"; }
修正後
klip() { dcop klipper klipper setClipboardContents "$(lv $1)"; }

修正し終わったら保存し,端末上で

source ~/.bashrc

とする.

3.9.3 バス停データをダウンロードする

Firefox からダウンロードする.

  1. Firefox で以下の URL にアクセスする:
    https://raw.github.com/nagae/2013ICL-B/master/bus-short.csv
    
  2. 右クリック・メニューで「名前を付けて保存」 fig/save_csv.png
  3. 保存先を ~/cpp に指定する fig/save_in_specific_folder.png
  4. 中身を確認する

    less だと日本語が化けるので lv コマンドを使う

    $ lv bus-short.csv
    

    lv モードでは,各キーに以下のような機能が割り当てられている. #+ATTRHTML:border="2" rules="all"

    キー機能
    カーソル上下行単位でスクロール
    F / Bページ単位でスクロール
    qlv モードを終了
    fig/lv-result.png

3.9.4 ファイルからデータを読み込んで表示させるプログラム(sample4.cpp)を作成する

  1. 新しいファイル ~/cpp/samplel4.cpp を作成する.Emacs で以下のように入力する.
    C-x C-f ~/cpp/sample4.cpp RET
    
  2. sample4.cpp ファイルを以下のように編集し,保存(C-x C-s)する.
     1:  #include <iostream>             // 標準入出力用ライブラリを読み込む
     2:  #include <iomanip>              // 入出力表示精度を指定するためのライブラリを読み込む
     3:  #include <fstream>              // ファイル入出力ストリーム用ライブラリを読み込む
     4:  
     5:  using namespace std;            // 名前空間 std をデフォルトで利用する
     6:  
     7:  // メインルーチン
     8:  int main ( void ) { // 引数無しで整数(int)型の戻り値を返す main 関数を定義
     9:    ifstream fin("bus-short.csv"); // ファイル入力ストリーム
    10:    while ( !fin.eof() ) {         // fos がファイルの最後を指さない限り,処理を繰り返す
    11:      // ファイル入力ストリームからデータを読み込む
    12:      int ID;                     // バス停ID用の整数型の変数を定義
    13:      double latitude, longitude; // 緯度・経度用の2つの倍精度実数型の変数を定義
    14:      string name;                // バス停の名前
    15:      fin >> ID >> latitude >> longitude; // ID, 緯度,経度の順に空白で区切られたデータを読み込む
    16:      // バス停の名前は getline 関数を使って取得する.
    17:      // >> 演算子を使うと,バス停名に空白が入っているとき,そこでデータが終わりだと思ってしまう.
    18:      // geline 関数を使うことで,その行の最後までをひとまとまり文字列として name に読み込める
    19:      getline( fin, name );
    20:      // ただし,getline 関数を使うと,経度とバス停名を区切っていた空白もデータとして読み込んでしまうので,
    21:      // 最初の1文字を無視する
    22:      name = name.substr(1);
    23:  
    24:      // 標準出力(cout)に読み込んだデータを加工して表示する
    25:      cout.setf(ios::fixed);      // 固定小数表記を指定
    26:      cout.precision(8);          // 小数点以下の桁数を指定
    27:      cout << "ID:"       << ID
    28:           << " 緯度:"    << latitude
    29:           << " 経度:"    << longitude
    30:           << " バス停名:" << name << endl;
    31:    }
    32:  }
    
  3. コンパイルして実行する.

    端末上で以下のように入力する.

    $cd ~/cpp                       # ~/cpp フォルダに移動
    $g++ sample4.cpp -o sample4.out # sample4.cpp をコンパイル(sample4.outを生成)
    $./sample4.out                  # sample4.out を実行
    

    こんな風に,読み取ったデータを加工して表示させられる. fig/sample4-result.png

3.9.5 ファイルから読み込んだデータを KML で出力させるプログラム(sample5.cpp)を作成する

sample4.cpp のデータを表示させる部分(第24〜30行)を KML 形式で出力するように修 正し,KML を使うために必要なヘッダをフッタを出力する命令を付け加えれば,バス停デー タを KML 方式で出力できる.

  1. ~/cpp/sample4.cpp を別名(~/sample5.cpp)で保存する

    sample4.cpp が開かれている Emacs のバッファ上で

    C-x C-w ~/cpp/sample5.cpp RET
    

    とすれば,sample5.cpp という名前で保存できる.

  2. ソース・ファイルの修正 ~/cpp/sample5.cpp の中身を以下のように修正する.
     1:  #include <iostream>             // 標準入出力用ライブラリを読み込む
     2:  #include <iomanip>              // 入出力表示精度を指定するためのライブラリを読み込む
     3:  #include <fstream>              // ファイル入出力ストリーム用ライブラリを読み込む
     4:  
     5:  using namespace std;            // 名前空間 std をデフォルトで利用する
     6:  
     7:  // メインルーチン
     8:  int main ( void ) { // 引数無しで整数(int)型の戻り値を返す main 関数を定義
     9:    ifstream fin("bus-short.csv"); // ファイル入力ストリーム
    10:  
    11:    // KMLを使うためのヘッダを出力
    12:    cout << "<?xml version=\"1.0\" encoding=\"UTF-8\"?>" << endl;
    13:    cout << "<kml xmlns=\"http://www.opengis.net/kml/2.2\">" << endl;
    14:    cout << "  <Folder>" << endl;
    15:  
    16:    // ファイルからデータを読み込んで表示させる
    17:    while ( !fin.eof() ) {         // fos がファイルの最後を指さない限り,処理を繰り返す
    18:      // ファイル入力ストリームからデータを読み込む
    19:      int ID;                     // バス停ID用の整数型の変数を定義
    20:      double latitude, longitude; // 緯度・経度用の2つの倍精度実数型の変数を定義
    21:      string name;                // バス停の名前
    22:      fin >> ID >> latitude >> longitude; // ID, 緯度,経度の順に空白で区切られたデータを読み込む
    23:      // バス停の名前は getline 関数を使って取得する.
    24:      // >> 演算子を使うと,バス停名に空白が入っているとき,そこでデータが終わりだと思ってしまう.
    25:      // geline 関数を使うことで,その行の最後までをひとまとまり文字列として name に読み込める
    26:      getline( fin, name );
    27:      // ただし,getline 関数を使うと,経度とバス停名を区切っていた空白もデータとして読み込んでしまうので,
    28:      // 最初の1文字を無視する
    29:      name = name.substr(1);
    30:  
    31:      // 標準出力(cout)に読み込んだデータを加工して表示する
    32:      cout.setf(ios::fixed);      // 固定小数表記を指定
    33:      cout.precision(8);          // 小数点以下の桁数を指定
    34:      // Placemark タグを出力
    35:      cout << "<Placemark>"
    36:           << "<Point><coordinates>" // 座標を出力
    37:           << longitude << "," << latitude << ",0"
    38:           << "</coordinates></Point>"
    39:           << "<name>" << name << "</name>" // バス停の名前を出力
    40:           << "<description>[" << ID << "]" << name << "</description>" // 説明にはバス停IDも記述
    41:           << "</Placemark>" << endl;
    42:    }
    43:  
    44:    // KMLを使うためのヘッダを出力
    45:    cout << "</Folder></kml>" << endl;
    46:  }
    

    sample4.cpp からの変更点は下記の通り:

    第11〜14行目
    KML を使うためのヘッダを出力
    // KMLを使うためのヘッダを出力
    cout << "<?xml version=\"1.0\" encoding=\"UTF-8\"?>" << endl;
    cout << "<kml xmlns=\"http://www.opengis.net/kml/2.2\">" << endl;
    cout << "  <Folder>" << endl;
    
    第35〜41行目
    バス停データを KML 方式で出力
    // Placemark タグを出力
    cout << "<Placemark>"
         << "<Point><coordinates>" // 座標を出力
         << longitude << "," << latitude << ",0"
         << "</coordinates></Point>"
         << "<name>" << name << "</name>" // バス停の名前を出力
         << "<description>[" << ID << "]" << name << "</description>" // 説明にはバス停IDも記述
         << "</Placemark>" << endl;
    
    第44〜45行目
    KML を使うためのフッタを出力
    // KMLを使うためのヘッダを出力
    cout << "</Folder></kml>" << endl;                      
    
  3. ファイルを保存,コンパイル,そして実行

    sample5.cpp の編集が終わったら保存(C-x C-s)し,端末で以下のように入力す る:

    $cd ~/cpp                       # ~/cpp フォルダへ移動(既に移動済みなら不要)
    $g++ sample5.cpp -o sample5.out # sample5.cpp をコンパイル(sample5.outを生成)
    $./sample5.out                  # 実行結果を確認する   
    

    日本語がうまく表示されない場合には「文字化けを解消するには?」を参照のこと(リン ク先から戻るには b をタイプせよ).

    適切な結果が表示されたのを確認したら,実行結果をクリップボードにコピーしよう. マウスで選択してコピーするのものいいが,端末からシェルスクリプト klip とパ イプ(|)を使って

    $./sample5.out | klip           # sample5.out を実行し,出力結果をクリップボードにコピー
    

    とする方が手っ取り早い.詳細については こちらを参照(リンク先から戻るには b をタイプ).

  4. クリップボード上の KML を Gist に投稿し,そのリンクを Google Maps に渡す

    Gist への投稿方法や,そのリンクを Google Maps に渡す方法については こちら を参照(リンク先から戻るには b をタイプ).

    適切な KML が渡されれば,以下のように表示されるはず. fig/sample5-result.png

3.10 [7/26] 自宅の近くのバス停を表示させてみる(レポート課題あり)

3.10.1 何のため?

  • ここまでやってきたことの集大成として,当初目的である「自宅周辺のバス停を Google Maps に表示させる」

3.10.2 ファイルから読み込んだバス停のうち,特定の範囲内にあるものだけを KML形式で出力させるプログラム(sample6.cpp)を作成する

sample5.cpp の一部を修正し,仙台駅を基準として緯度・経度が±0.05 の範囲内にあ るものだけを KML 形式で出力するプログラムを作成する.

  1. ~/cpp/sample5.cpp を別名(~/sample6.cpp)で保存する

    sample5.cpp が開かれている Emacs のバッファ上で

    C-x C-w ~/cpp/sample6.cpp RET
    

    とすれば,sample6.cpp という名前で保存できる.

  2. ソース・ファイルの修正 ~/cpp/sample6.cpp の中身を以下のように修正する.
     1:  #include <iostream>             // 標準入出力用ライブラリを読み込む
     2:  #include <iomanip>              // 入出力表示精度を指定するためのライブラリを読み込む
     3:  #include <fstream>              // ファイル入出力ストリーム用ライブラリを読み込む
     4:  
     5:  using namespace std;            // 名前空間 std をデフォルトで利用する
     6:  
     7:  // メインルーチン
     8:  int main ( void ) { // 引数無しで整数(int)型の戻り値を返す main 関数を定義
     9:    ifstream fin("bus-short.csv"); // ファイル入力ストリーム
    10:  
    11:    // 表示範囲を設定
    12:    double target_lat = 38.260297, target_lng =140.88204; // 目標(仙台駅)の緯度・経度
    13:    double range = 0.05;                                   // 表示する緯度・経度の範囲
    14:    double lat_min = target_lat - range;                  // 表示範囲の西端(経度の最小値)
    15:    double lat_max = target_lat + range;                  // 表示範囲の東端(経度の最大値)
    16:    double lng_min = target_lng - range;                  // 表示範囲の南端(緯度の最小値)
    17:    double lng_max = target_lng + range;                  // 表示範囲の北端(緯度の最大値)
    18:  
    19:    // KMLを使うためのヘッダを出力
    20:    cout << "<?xml version=\"1.0\" encoding=\"UTF-8\"?>" << endl;
    21:    cout << "<kml xmlns=\"http://www.opengis.net/kml/2.2\">" << endl;
    22:    cout << "  <Folder>" << endl;
    23:  
    24:    // ファイルからデータを読み込んで表示させる
    25:    while ( !fin.eof() ) {        // fin がファイルの最後を指さない限り,処理を繰り返す
    26:      // ファイル入力ストリームからデータを読み込む
    27:      int ID;                     // バス停ID用の整数型の変数を定義
    28:      double latitude, longitude; // 緯度・経度用の2つの倍精度実数型の変数を定義
    29:      string name;                // バス停の名前
    30:      fin >> ID >> latitude >> longitude; // ID, 緯度,経度の順に空白で区切られたデータを読み込む
    31:      // バス停の名前は getline 関数を使って取得する.
    32:      // >> 演算子を使うと,バス停名に空白が入っているとき,そこでデータが終わりだと思ってしまう.
    33:      // geline 関数を使うことで,その行の最後までをひとまとまり文字列として name に読み込める
    34:      getline( fin, name );
    35:      // ただし,getline 関数を使うと,経度とバス停名を区切っていた空白もデータとして読み込んでしまうので,
    36:      // 最初の1文字を無視する
    37:      name = name.substr(1);
    38:  
    39:      if ( latitude < lat_min     // 「緯度 latitude が最小値 lat_min より小さい」
    40:           || latitude > lat_max  // または「緯度 latitude が最小値 lat_max より大きい」
    41:           || longitude < lng_min // または「経度 longitude が最小値 lng_min より小さい」
    42:           || longitude > lng_max ) // または「経度 longitude が最小値 lng_max より大きい」
    43:                                  // ならば何もしない.
    44:        {
    45:          // 何もしない
    46:        }
    47:      else                        // 以上の条件のどれにも該当しないなら KML を出力する
    48:        {
    49:          // 標準出力(cout)に読み込んだデータを加工して表示する
    50:          cout.setf(ios::fixed);  // 固定小数表記を指定
    51:          cout.precision(8);      // 小数点以下の桁数を指定
    52:          // Placemark タグを出力
    53:          cout << "<Placemark>"
    54:               << "<Point><coordinates>" // 座標を出力
    55:               << longitude << "," << latitude << ",0"
    56:               << "</coordinates></Point>"
    57:               << "<name>" << name << "</name>" // バス停の名前を出力
    58:               << "<description>[" << ID << "]" << name << "</description>" // 説明にはバス停IDも記述
    59:               << "</Placemark>" << endl;
    60:        }
    61:    }
    62:  
    63:    // KMLを使うためのフッタを出力
    64:    cout << "</Folder></kml>" << endl;
    65:  }
    

    sample5.cpp からの変更点は下記の通り:

    第11〜17行目
    表示範囲を設定し,変数に格納する
    // 表示範囲を設定
    double target_lat = 38.260297, target_lng =140.88204; // 目標(仙台駅)の緯度・経度
    double range = 0.05;                                   // 表示する緯度・経度の範囲
    double lat_min = target_lat - range;                  // 表示範囲の西端(経度の最小値)
    double lat_max = target_lat + range;                  // 表示範囲の東端(経度の最大値)
    double lng_min = target_lng - range;                  // 表示範囲の南端(緯度の最小値)
    double lng_max = target_lng + range;                  // 表示範囲の北端(緯度の最大値)
    
    第39〜61行目
    表示範囲に入っているものだけを出力. ifelse 型の構文については,緯度・経度に応じた分岐 を行うプログラム sample3.cpp の解説 を参照せよ.

    ここでは,バス停の緯度・経度が if の後の条件を満足する(つまり,表示範囲に入っ ていない)なら何も表示させず,条件を満足しない(表示範囲内)な らば,else ブロックの中で sample5.cpp と同じものを表示させている.

    実際, sample5.cpp の第31〜41行と,sample6.cpp の第49〜59行は 全く同じである.

    if ( latitude < lat_min     // 「緯度 latitude が最小値 lat_min より小さい」
         || latitude > lat_max  // または「緯度 latitude が最小値 lat_max より大きい」
         || longitude < lng_min // または「経度 longitude が最小値 lng_min より小さい」
         || longitude > lng_max ) // または「経度 longitude が最小値 lng_max より大きい」
      // ならば何もしない.
      {
        // 何もしない
      }
    else                        // 以上の条件のどれにも該当しないなら KML を出力する
      {
        // 標準出力(cout)に読み込んだデータを加工して表示する
        cout.setf(ios::fixed);  // 固定小数表記を指定
        cout.precision(8);      // 小数点以下の桁数を指定
        // Placemark タグを出力
        cout << "<Placemark>"
             << "<Point><coordinates>" // 座標を出力
             << longitude << "," << latitude << ",0"
             << "</coordinates></Point>"
             << "<name>" << name << "</name>" // バス停の名前を出力
             << "<description>[" << ID << "]" << name << "</description>" // 説明にはバス停IDも記述
             << "</Placemark>" << endl;
      }                     
    
  3. ファイルを保存,コンパイル,そして実行 sample6.cpp の編集が終わったら保存(C-x C-s)し,端末で以下のように入力す る:
    $cd ~/cpp                       # ~/cpp フォルダへ移動(既に移動済みなら不要)
    $g++ sample6.cpp -o sample6.out # sample6.cpp をコンパイル(sample6.outを生成)
    $./sample6.out                  # 実行結果を確認する
    

    日本語がうまく表示されない場合には「文字化けを解消するには?」を参照のこと(リン ク先から戻るには b をタイプせよ).

  4. 出力された KML をGist に貼り付けて sample6.kml という名前でアップロードし,Google Maps に読 み込ませてみる.
    • 出力結果をクリップボードにコピーする方法はこちらを参照(リンク先から戻るには b をタイプ).
    • Gist への投稿方法や,そのリンクを Google Maps に渡す方法については こちら を参照(リンク先から戻るには b をタイプ).

    適切な KML が渡されれば,以下のように表示されるはず. fig/sample6-result.png

3.10.3 表示範囲も表示させるプログラム(sample7.cpp)を作成する

sample6.cpp の出力結果だけでは,適切なバス停が抽出できているかどうか判りにくい ので,これをさらに修正して,特定の緯度・経度を中心とした矩形範囲を表示させる KML も出力させるプログラム sample7.cpp を作成する.

  1. ~/cpp/sample6.cpp を別名(~/sample7.cpp)で保存する

    sample6.cpp が開かれている Emacs のバッファ上で

    C-x C-w ~/cpp/sample7.cpp RET
    

    とすれば,sample7.cpp という名前で保存できる.

  2. ソース・ファイルの修正 ~/cpp/sample7.cpp の中身を以下のように修正する.
     1:  #include <iostream>             // 標準入出力用ライブラリを読み込む
     2:  #include <iomanip>              // 入出力表示精度を指定するためのライブラリを読み込む
     3:  #include <fstream>              // ファイル入出力ストリーム用ライブラリを読み込む
     4:  
     5:  using namespace std;            // 名前空間 std をデフォルトで利用する
     6:  
     7:  // メインルーチン
     8:  int main ( void ) { // 引数無しで整数(int)型の戻り値を返す main 関数を定義
     9:    ifstream fin("bus-short.csv"); // ファイル入力ストリーム
    10:  
    11:    double target_lat = 38.260297, target_lng =140.88204; // 目標(仙台駅)の緯度・経度
    12:    double range = 0.05;                                  // 表示する緯度・経度の範囲
    13:    double lat_min = target_lat - range;                  // 表示範囲の西端(経度の最小値)
    14:    double lat_max = target_lat + range;                  // 表示範囲の東端(経度の最大値)
    15:    double lng_min = target_lng - range;                  // 表示範囲の南端(緯度の最小値)
    16:    double lng_max = target_lng + range;                  // 表示範囲の北端(緯度の最大値)
    17:  
    18:    // KMLを使うためのヘッダを出力
    19:    cout << "<?xml version=\"1.0\" encoding=\"UTF-8\"?>" << endl;
    20:    cout << "<kml xmlns=\"http://www.opengis.net/kml/2.2\">" << endl;
    21:    cout << "  <Folder>" << endl;
    22:  
    23:    // 領域を四角で囲む<Placemark>タグを出力
    24:    cout << "    <Placemark>" << endl;
    25:    cout << "      <Style><LineStyle><color>7f0000ff</color><width>4</width></LineStyle></Style>" << endl;
    26:    cout << "      <LineString><coordinates>" << endl;
    27:    cout << "      " << lng_min << "," << lat_min << ",0" << endl;
    28:    cout << "      " << lng_min << "," << lat_max << ",0" << endl;
    29:    cout << "      " << lng_max << "," << lat_max << ",0" << endl;
    30:    cout << "      " << lng_max << "," << lat_min << ",0" << endl;
    31:    cout << "      " << lng_min << "," << lat_min << ",0" << endl;
    32:    cout << "      </coordinates></LineString>" << endl;
    33:    cout << "    </Placemark>" << endl;
    34:  
    35:    // ファイルからデータを読み込んで表示させる
    36:    while ( !fin.eof() ) {        // fin がファイルの最後を指さない限り,処理を繰り返す
    37:      // ファイル入力ストリームからデータを読み込む
    38:      int ID;                     // バス停ID用の整数型の変数を定義
    39:      double latitude, longitude; // 緯度・経度用の2つの倍精度実数型の変数を定義
    40:      string name;                // バス停の名前
    41:      fin >> ID >> latitude >> longitude; // ID, 緯度,経度の順に空白で区切られたデータを読み込む
    42:      // バス停の名前は getline 関数を使って取得する.
    43:      // >> 演算子を使うと,バス停名に空白が入っているとき,そこでデータが終わりだと思ってしまう.
    44:      // geline 関数を使うことで,その行の最後までをひとまとまり文字列として name に読み込める
    45:      getline( fin, name );
    46:      // ただし,getline 関数を使うと,経度とバス停名を区切っていた空白もデータとして読み込んでしまうので,
    47:      // 最初の1文字を無視する
    48:      name = name.substr(1);
    49:  
    50:      if ( latitude < lat_min     // 「緯度 latitude が最小値 lat_min より小さい」
    51:           || latitude > lat_max  // または「緯度 latitude が最小値 lat_max より大きい」
    52:           || longitude < lng_min // または「経度 longitude が最小値 lng_min より小さい」
    53:           || longitude > lng_max ) // または「経度 longitude が最小値 lng_max より大きい」
    54:                                  // ならば何もしない.
    55:        {
    56:          // 何もしない
    57:        }
    58:      else                        // 以上の条件のどれにも該当しないなら KML を出力する
    59:        {
    60:          // 標準出力(cout)に読み込んだデータを加工して表示する
    61:          cout.setf(ios::fixed);  // 固定小数表記を指定
    62:          cout.precision(8);      // 小数点以下の桁数を指定
    63:          // Placemark タグを出力
    64:          cout << "<Placemark>"
    65:               << "<Point><coordinates>" // 座標を出力
    66:               << longitude << "," << latitude << ",0"
    67:               << "</coordinates></Point>"
    68:               << "<name>" << name << "</name>" // バス停の名前を出力
    69:               << "<description>[" << ID << "]" << name << "</description>" // 説明にはバス停IDも記述
    70:               << "</Placemark>" << endl;
    71:        }
    72:    }
    73:  
    74:    // KMLを使うためのフッタを出力
    75:    cout << "</Folder></kml>" << endl;
    76:  }
    

    sample6.cpp からの変更点は下記の通り:

    第23〜33行目
    領域を四角で囲む <Placemark> タグを出力
    // 領域を四角で囲む<Placemark>タグを出力
    cout << "    <Placemark>" << endl;
    cout << "      <Style><LineStyle><color>7f0000ff</color><width>4</width></LineStyle></Style>" << endl;
    cout << "      <LineString><coordinates>" << endl;
    cout << "      " << lng_min << "," << lat_min << ",0" << endl;
    cout << "      " << lng_min << "," << lat_max << ",0" << endl;
    cout << "      " << lng_max << "," << lat_max << ",0" << endl;
    cout << "      " << lng_max << "," << lat_min << ",0" << endl;
    cout << "      " << lng_min << "," << lat_min << ",0" << endl;
    cout << "      </coordinates></LineString>" << endl;
    cout << "    </Placemark>" << endl;
    

    <Placemark>〜</Placemark> タグの中身は,以下の2つで構成さ れている.

    まず,<Style><LineStyle>〜</LineStyle></Style> タグ内で,囲む線の透明度,色,幅を指定する部分:

    cout << "      <Style><LineStyle><color>7f0000ff</color><width>4</width></LineStyle></Style>" << endl;
    

    では,<color>〜</color> タグ内の8桁の16進数で透明度(7f)・ 青(00)・緑(00)・赤(ff)を表現し,<width>〜</width> タグ内の数値(=4)で線の幅を指定している.色の指定に関するより詳しい解説については KMLリファレンス<ColorStyle> を参照.

    次に,<LineString><coordinates>〜 </coordinates></LineString> タグ内で,連結すべき点の座標を与え ている.具体的には,1) 南西端(lng_min,lat_min), 2) 南東端 (lng_min,lat_max), 3) 北東端(lng_max,lat_max), 4)北西端 (lng_max,lat_min), 5) 南西端(lng_min,lat_min) を順に結 ぶような線を記述させている.複数の点を結ぶ線に関するより詳 しい解説は KML リファレンス<LineString> を参照.

  3. ファイルを保存,コンパイル,そして実行 sample7.cpp の編集が終わったら保存(C-x C-s)し,端末で以下のように入力す る:
    $cd ~/cpp                       # ~/cpp フォルダへ移動(既に移動済みなら不要)
    $g++ sample7.cpp -o sample7.out # sample7.cpp をコンパイル(sample7.outを生成)
    $./sample7.out                  # 実行結果を確認する
    

    日本語がうまく表示されない場合には「文字化けを解消するには?」を参照のこと(リン ク先から戻るには b をタイプせよ).

  4. 出力された KML をGist に貼り付けて sample7.kml という名前でアップロードし,Google Maps に読 み込ませてみる.
    • 出力結果をクリップボードにコピーする方法はこちらを参照(リンク先から戻るには b をタイプ).
    • Gist への投稿方法や,そのリンクを Google Maps に渡す方法については こちら を参照(リンク先から戻るには b をタイプ).

    適切な KML が渡されれば,以下のように表示されるはず. fig/sample7-result.png

3.10.4 完全版バス停データをダウンロードする

Firefox からダウンロードする.

  1. Firefox で以下の URL にアクセスする:
    https://raw.github.com/nagae/2013ICL-B/master/bus.csv
    
  2. 右クリック・メニューで「名前を付けて保存」 fig/save_long_csv.png
  3. 保存先を ~/cpp に指定する fig/save_long_in_specific_folder.png
  4. 中身を確認する fig/lv-long-result.png

3.10.5 完全版バス停データを用いて仙台駅周辺のバス停を表示させるプログラム(sample8.cpp)を作成する

  1. ~/cpp/sample7.cpp を別名(~/sample8.cpp)で保存する

    sample7.cpp が開かれている Emacs のバッファ上で

    C-x C-w ~/cpp/sample8.cpp RET
    

    とすれば,sample8.cpp という名前で保存できる.

  2. ソース・ファイルの修正

    ~/cpp/sample8.cpp の第9行と第12行を,それぞれ,以下のように修正する.

    第9行
    入力ストリームとしてファイル名を bus.csv に変更
    修正前
    ifstream fin("bus-short.csv"); // ファイル入力ストリーム
    
    修正後
    ifstream fin("bus.csv"); // ファイル入力ストリーム
    
    第12行
    表示する緯度・経度の範囲を ±0.05 から ±0.01 に変更(0.05のままだ と表示されるバス停が多すぎる)
    修正前
    double range = 0.05;                                  // 表示する緯度・経度の範囲
    
    修正後
    double range = 0.01;                                  // 表示する緯度・経度の範囲
    
  3. ファイルを保存,コンパイル,そして実行 sample8.cpp の編集が終わったら保存(C-x C-s)し,端末で以下のように入力す る:
    $cd ~/cpp                       # ~/cpp フォルダへ移動(既に移動済みなら不要)
    $g++ sample8.cpp -o sample8.out # sample8.cpp をコンパイル(sample8.outを生成)
    $./sample8.out                  # 実行結果を確認する
    

    日本語がうまく表示されない場合には「文字化けを解消するには?」を参照のこと(リン ク先から戻るには b をタイプせよ).

  4. 出力された KML をGist に貼り付けて sample8.kml という名前でアップロードし,Google Maps に読 み込ませてみる.
    • 出力結果をクリップボードにコピーする方法はこちらを参照(リンク先から戻るには b をタイプ).
    • Gist への投稿方法や,そのリンクを Google Maps に渡す方法については こちら を参照(リンク先から戻るには b をタイプ).

    適切な KML が渡されれば,以下のように表示されるはず. fig/sample8-result.png

3.10.6 レポート課題

  1. sample8.cpp をもとにして,以下の全てを表示させる KML を出力するプログラムを C++ を用い て作成,そのソースファイルを final_report.cpp という名前で提出せよ:
    • 自宅を基準として,緯度・経度が ±0.01=〜=±0.05 程度の範囲(出力されるバス 停の数に応じて適宜調整すること)を四角い枠で表示する.ただし,プライバシーが気になる人は,自宅の近くの適当な座標 を中心としてよい.
    • 完全版バス停データ(bus.csv)の中から,上記の範囲に入るバス停を表示する.
  2. 出力された KML を final_report.kml という名前で Gist にアップロードし,その リンクを Google Maps に渡して意図通りに表示されているかを確認せよ. 適切に表示されたら,その Google Maps へのリンクを提出せよ.

課題の提出方法については こちら を参照 (リンク先から戻るには b をタイプ)

4 参考文献

  • 大竹智也, Emacs 実践入門, 技術評論社, 2012.
  • 酒井聡樹, 100ページの文章術, 共立出版, 2011.

日付: 2013-07-19T12:34+0900

著者: Takeshi Nagae

Org version 7.9.3f with Emacs version 24

Validate XHTML 1.0