Information and Computer Literacy
目次
- 1 Emacs 入門
- 2 文章構成方法
- 3 プログラミング入門
- 3.1 何をするか?
- 3.2 [6/21] Githubアカウントの登録
- 3.3 [6/21] Gist への KMLファイルの投稿
- 3.4 [6/21] Google Maps に Gist にアップロードした KML ファイルへのリンクを渡す
- 3.5 [6/28] C++プログラミングの準備
- 3.6 [6/28] C++で標準出力を行うプログラムを作って実行してみる
- 3.7 [7/5] C++でKMLを出力し,Google Map に渡してみる(レポート課題あり)
- 3.8 [7/12] C++で標準入力を受けとるプログラムを作って実行してみる
- 3.9 [7/19] ファイル上のバス停データを KML で出力するプログラムを C++ で作ってみる
- 3.10 [7/26] 自宅の近くのバス停を表示させてみる(レポート課題あり)
- 4 参考文献
1 Emacs 入門
1.1 特殊キーとキーバインド
1.1.1 特殊キー
Emacs では特殊キーと呼ばれるキーを活用する. ICL演習室に設置されている 109キーボード では,特殊キーは以下のように割当てられている.
特殊キー名 | 省略表記 | キーボード上のキー |
---|---|---|
Control | C- | Ctrl |
Meta | M- | Alt |
Super | s- | 割当て無し |
Space | SPC | Space |
Return | RET | Enter |
Tab | TAB | Tab |
Delete | DEL | Backspace |
Esc | ESC | Esc |

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-w
や C-x C-s
などで「ファイルを保存」すると,そのバッファの内容がファイルに
書き込まれる.
ウィンドウ上に表示されるバッファを切り替えるには C-x b
や C-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 0
や C-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 ファイルの作成
C-x C-f ~/report1.tex
としてreport1.tex
ファイルを(新たに)開く- 以下をコピー(マウスの左ボタンを押しながら領域を選択→右クリックメニューから
「コピー」を選択)&ペースト(Emacs上で
C-y
)する.\documentclass[a4paper]{jarticle} \title{文章のタイトル} % 適宜,変更して下さい \author{名前 {\tt(学籍番号)}} % 適宜,変更して下さい \begin{document} \maketitle % ↓↓↓ここから下に文章を挿入して下さい↓↓↓ % ↑↑↑ここから上に文章を挿入して下さい↑↑↑ \end{document}
- 文章のタイトル,名前,学籍番号を適当に編集する.
% ↓↓↓ここから下にあなたの文章を挿入して下さい↓↓↓
と% ↑↑↑ここから上にあなたの文章を挿入して下さい↑↑↑
の間に,自分の文章を挿入する. 編集時には以下のコマンドが役に立つかもしれない:機能 Emacs Firefox 領域選択 マウスの左ボタンを押しながらマウスを移動 マウスの左ボタンを押しながらマウスを移動 コピー M-w
右クリックメニューから「コピー」 カット C-w
必要なし ペースト C-y
必要なし 過去のクリップボードをペースト M-y
該当なし - LaTeXのコマンドを使ってみよう
LaTeXのコマンドは
\
(バックスラッシュ)と{}
(波括弧)で構成される.- どこでも 空行を1つ入れる ことで,段落を変えることができる.
- 複数の段落のまとまりに名前をつけたい場合は,
\section{節のタイトル} 段落1 \subsection{小節のタイトル} 段落2 段落3 \subsection{小節のタイトル} 段落4 段落5
とする.
- 箇条書きが必要な場合には
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}
- 番号つきの箇条書きが必要な場合は,
\begin{enumerate} \item ピッチャー \item キャッチャー \item ファースト \item セカンド \item サード \item ショート \item レフト \item センター \item ライト \end{enumerate}
とする.
\item
の数は何個でも構わない. - 定義リストが必要な場合は,
\begin{description} \item[先発] 最初に投げる投手 \item[中継ぎ] (代打や不調などで)先発が降板した後に投げる投手 \item[抑え] 勝利を確実なものにするために最後の1〜2回を投げる投手 \end{description}
2.3.2 コンパイル
- TeX ファイル → dvi ファイル
- Emacs上で
M-!
(Alt+shift+1
)とすると,ミニバッファにShell command:
とプロンプトが出るので,platex report1.tex
と入力して
RET
キーを押す. - 問題が無ければ
report1.dvi
というファイルができるはずなので,C-x C-d RET
としてディレクトリ一覧を表示させ,確認してみよう. - ファイル名が
report1.tex
でない場合は,適切なファイル名をplatex
の後に記 述する.例えば,ファイル名がtest.txt
なら,以下のようにする.platex test.txt
この場合は生成されるファイル名も
test.dvi
になる.
- Emacs上で
- dvi ファイル → pdf ファイル
- 再び
M-!
として,Shell command:
の後にdvipdfmx report1.dvi
とする.
- 問題が無ければ
report1.pdf
というファイルができるはずなので,C-x C-d RET
としてディレクトリ一覧を表示させ,確認してみよう. - 同じく,dviファイル名が
report1.dvi
で無い場合には,適切なファイル名を与え る.例えば,ファイル名がtest.dvi
なら,以下のようにする.dvipdfmx test.dvi
- 再び
- PDFファイルの確認
以下の方法で生成した PDF ファイルを確認できる.- メインメニューから「home」→「学籍番号」のフォルダを開いて,生成されたPDFファ イルをダブルクリックする
- Adobe Acrobat を立ち上げ「ファイル」→「開く」から生成されたPDFファイルを選 択する
2.4 レポートの投稿
作成したレポートは,東北大学インターネットスクール(ISTU: Internet School of Tohoku University)から投稿する.
- ISTUサイトにログイン
- https://xapp.istu.jp/istu/package/auth/view/Login.html から学籍番号とパスワード(ICL演習室と同じもの)でログインする
- 右下のメニューから金曜日3時限 情報基礎B を選択する
- レポート「私の好きな競技のルールと魅力」を選択する
- レポートを提出する
- 「ファイルを選択」をクリックし,レポートPDFファイルを選択する
- 右下の提出を押す
- レポートを差し替えたい場合には「ファイルを選択」で新しいファイルを選択して「提出」
- レポートを削除したい場合には「削除」を押して「提出」
- 投稿したレポートを確認する
- レポートを投稿した画面に移動すると,投稿したファイルをダウンロードできる.
- ファイルをダウンロードした後,Adobe Reader で開いて意図どおりのファイルが投 稿できているか確認すること.
3 プログラミング入門
3.1 何をするか?
まずは,次のリンクをクリックしてみて欲しい.何が見えるだろうか?: http://goo.gl/maps/OlzIc 最初は何だか判らないかもしれないので,適当にズームしたり移動させたりしてみて欲し い.例えば,自分の家の近く,仙台駅西側 や川内キャンパス周辺 を見てみよう.
これは,以下のようにして作成した:
- 国土交通省国土政策局 が 国土数値情報ダウンロードサービス で提供している バス停留所データ から宮城県に存在するバス停をダウンロード
- ダウンロードしたデータを Google Maps や Google Earth で利用できる KML(Keyhole Markup Language) という形式に変換
- 変換したファイルを Github が提供する Gist というコードをシェアするサービスにアップロード
- アップロードした KMLファイルへのリンクを Google Maps の検索クエリに指定
ただし,このままではあまりにバス停の数が多すぎるため,もう少し絞り込みたい. そこで,今回のプログラミングでは,以下の課題に取り組んでもらう:
- 「自宅(もしくは宮城県内の適当な場所)の近くにあるバス停を Google Maps 上に表示 させる」
3.2 [6/21] Githubアカウントの登録
3.2.1 何のため?
3.2.2 https://github.com へアクセス
登録画面が出るので,
- 希望するアカウント名(後から変更不可能)
- メールアドレス
- パスワード(授業でやったパスワードの作り方 を参照)
を入力して「Sign up for free」をクリック
- おしまい
3.3 [6/21] Gist への KMLファイルの投稿
3.3.1 何のため?
3.3.2 https://gist.github.com へアクセス
もしくは,Github のダッシュボード画面のメニューから Gist を選択:
3.3.3 Gist を投稿する
空の gist 作成画面になるので,
- ファイルの説明: 教育情報基盤センター
- ファイル名: icl.kml
- ファイルの中身:
<?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」をクリック
3.3.4 KMLファイルの中身の解説
ここで作ったファイルは KML (Keyhole Markup Language) という書式に基づいて作成 されている.その基本構造を順に解説していこう.
- 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 で定義され る名前空間を利用することを示している.
- 3行目〜5行目: Placemark の設置
<Placemark> <name>東北大学 教育情報基盤センター</name> <description>いま私はここに居ます</description>
まず,
<Placemark>
によって,このあと</Placemark>
で閉じられるまで, 新しい地図上のマップポイントを記述することを示している. 次に,<name>東北大学 教育情報基盤センター</name>
および<description>いま私はここに居ます</description>
により,この Placemark の名前と,クリックした時に表示される説明を設定している. - 6行目〜8行目: 座標の設置
<Point> <coordinates>140.850922, 38.260896</coordinates> </Point>
<Point>〜</Point>
タグ内に点の情報を書き込む.ここでは,<coordinate>経度(水平方向)・緯度(垂直方向)</coordinates>
によって, この地点の緯度と経度を指定する. - 9行目〜10行目: Placemark, kml タグを閉じる.
</Placemark> </kml>
これによって,めでたく
Placemark
とkml
を終了できる.
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 一覧が表示される.
3.4.3 投稿した KML ファイル の URL を獲得
Gist ページのファイル名の右に並ぶアイコンのうち一番右のもの(マウスを近づけると
View Rawと表示される)の上で,右クリックメニューから「リンクのURLをコピー」を選択.
3.4.4 取得した URL を Google Maps に渡す
http://maps.google.com の検索窓にコピーした KML ファイルへのリンクをペースト
3.4.5 こんな画面が出た?
3.5 [6/28] C++プログラミングの準備
3.5.1 何のため?
まず,Emacs で文脈に合わせて色がつくようにする.こうすることで打ち間違いが見付け
やすくなる.次に,ファイルの中身をクリップボードにコピーするシェルスクリプトを登
録しておく.こうすることで,出力された KML のコピー&ペーストが容易になる.
最後に,プログラム用に ~/cpp
というディレクトリを作る.こうすることで,
ソース・ファイルやデータ・ファイルをまとめておける.
3.5.2 Emacs に色をつける
global-font-lock-mode
を利用することで,開いたファイルの拡張子(TeXファイル
なら .tex
, C++ソースファイルなら .cpp
)に応じて,文脈を理解して色をつけて
くれる.
~/.emacs.el
を作成する(既に存在するなら開く)Emacs 上で
C-x C-f
とする(C-x
は Ctrlを押しながらx
のコト)ミニバッファ(Emacs フレームの一番下にある1行だけのウィンドウ)に
Find File: ~/
と表示されるので,~/
の後に.emacs.el
と記述してRET
(RET
は enter キー)- 開かれた
~/.emacs.el
に以下を記述する(global-font-lock-mode t)
C-x C-s
で保存する. - 再起動する(
C-x C-c
で終了させ,再び開く). 再起動後に表示されるメッセージがほんのり赤くなっていたらOK.
3.5.3 ファイルの中身をクリップボードにコピーするシェルスクリプトを登録する
端末などで使われる bash というシェルは,起動時に ~/.bashrc
を参照し,そこに登録
されているエイリアス(コマンドの別名)や関数を読み込んでくれる.このファイルに,引
数として与えられたファイルの中身をクリップボードに取り込むスクリプト klip
を登
録しておけば,以降のプログラムの実行結果を簡単にクリップボードに送れるようになる.
~/.bashrc
を開くEmacs 上で
C-x C-f
とするとミニバッファにFind File: ~/
と表示されるので,~/
の後に.bashrc
と記述してRET
を入力する.こうすることで,~/.bashrc
が存在すればそれが開かれ,存在しなければ新規に作成される. なお,ICL演習室の環境で =~/.bashrc= がデフォルトで存在するかどうかは未確認.~/.bashrc
を編集し,保存する開かれた
~/.bashrc
の最後に以下の一行を追記する:klip() { dcop klipper klipper setClipboardContents "$(ls $1)"; }
開き波括弧
{
の後ろと閉じ波括弧}
の前には 半角スペースが必要 である.Emacs 上ではこのように表示される(当該箇所以外の
~/.bashrc
の内容がこれと同じ である保証はないことに注意されたい).- 端末を起動する
メニューから「システム」→「端末」を選択する.もしくは,デスクトップ上の 「端末」アイコンをダブルクリックする
もしすでに「端末」を開いていたら,端末に
$source ~/.bashrc
と記述して
RET
を入力する. - 端末に以下を入力する
klip ~/.bashrc
これで
~/.bashrc
の中身がクリップボードに取り込まれる. - Klipper を開いてクリップボードの中身を確認する
画面右下にあるクリップボードアイコン
をクリックすると,クリップボードの履歴が表示される.
~/.bashrc
の最初の数行が表示されていれば OK.
3.5.4 cpp ディレクトリを作る
そこにプログラム課題用のファイルをまとめておくために,
ホームディレクトリ ~
の下に ~/cpp
というフォルダを作る.
- Emacs のどの画面からでもよいので,
M-x make-directory
と入力する(M-x
は Alt を押しながらx
のこと).まず,
M-x
(Alt を押しながらx
) と入力すると,ミニバッファに「M-x
」と表示さ れ,フォーカスがミニバッファに移動する:次に,そのまま
make-directory
と入力してRET
と入力なお,ここで,
M-x make-directory
と入力するとき,make-d
まで入力してTAB
キーを押すとmake-directory
まで補完してくれる. - ミニバッファに
Make directory: ~/
と表示されるので,その後にcpp
と入力し てRET
を入力する.これで,
~/cpp
というフォルダが作成される. - ホームフォルダを表示させて確認する.
メニューから「ホーム」を選択
ホームフォルダ内に「cpp」というフォルダが出来ているのを確認する.
3.6 [6/28] C++で標準出力を行うプログラムを作って実行してみる
3.6.1 何のため?
C++ を用いて,標準出力に "Hello world" と表示させるプログラム(sample1
)を作
る.これによって,以下を理解する.
- C++ の基本構造と標準出力および
<<
演算子の使い方 - コンパイルの方法とエラー・メッセージに対処する方法
3.6.2 ~/cpp/sample1.cpp
を編集
- 新しく
~/cpp/sample1.cpp
を作成Emacs を起動して
C-x C-f
とするとFind file: ~/
と表示されるので,その後にcpp/sample1.cpp
と入力して RET 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: }
このプログラムの各行の解説は後に回して,ここでは,注意すべき点を述べよう.
- コメント文以外は全て半角である.Anthy が OFF になっていることを確認するこ と.
- 1行目の
#include
の後の<iostream>
は一対の山括弧(「<」「>」, JIS配列 では Shift+「,」, Shift+「.」で入力できる)で囲まれている. - 2行目の最後の「;」(セミコロン,JIS配列の「l」の右隣)を忘れないこと.同様に,7行目,8行目もセミコ ロンで終わる必要がある.
- 5行目の
main
の後の( void )
は一対の丸括弧(「(」「)」, JIS配列では Shift+「8」, Shift+「9」で入力できる)で囲まれている. - 6行目の行頭は「{」(開き波括弧, JIS配列では Enter の左隣),9行目の行頭は「}」 (閉じ波括弧, JIS配列ではEnterの左隣)である.
- 7行目の
"Hello world" は,ダブルクォート(" , JIS配列では Shift+「2」で入力できる)で囲まれている. - 7行目と8行目はインデント(これらの行のどこかにカーソルを置いて
TAB
を入力)すると読み易い.
- 編集が終わったら
C-x C-s
で保存するのを忘れないように.保存すると,Emacsのス テータスバーの左端が「**」から「–」に変わる.適切に入力されたときの Emacs の画面は下記のような色で表示されているはずだ:
3.6.3 端末の起動
コンパイルや実行は Emacs 上でも可能だが,今回は,様々な処理が可能な「端末」を使っ てみる.
pwd
: 現在のフォルダを確認する端末の起動時のフォルダはホームフォルダ(
~/
)である.現在のフォルダを確認する にはpwd
(print working directory)というコマンドを使う. 端末にはbash3.2$
と表示されているので,その後に続けてpwd
とタイプして RET を入力すると,$pwd /home/******
と,ホームフォルダが表示されるはず(******のところには学籍番号が表示されるはず).
cp
:cpp
フォルダに移動するフォルダの移動には
cd
(change directory)というコマンドを使う.$ cd cpp
というように,コマンド名
cd
の後に,半角スペースを1つ空けて移動するフォルダ 名(cpp
)をタイプして RET を入力する.再び
pwd
を使って移動できているかを確認してみよう.$ pwd /home/******/cpp
と表示されれば OK.
ls
: フォルダの一覧を確認する.現在のフォルダ(
~/cpp
)の中身を確認してみよう.フォルダ内のファイル一覧を取得 するにはls
というコマンドを使う.ls
とタイプして RET を入力する.$ ls sample1.cpp
と表示されただろうか.
以上の一連の処理を行なったときの端末は次のように表示されているだろう:
3.6.4 コンパイル
C++ 言語で書かれたテキストファイル(sample1.cpp
)を,計算機で実行可能なバイナリ
ファイルに変換する(コンパイル する)には g++
というコマンドを使う.
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
という実行ファイルが生成される.sample1.out
が生成されたことを確認ls
コマンドを作ってsample1.out
が生成されたことを確認.以上の一連の処理を行なったときの端末は次のように表示されているだろう:
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++ では,変数名や関数名の衝突を避けるために「名前空間」を用いる.同じ 名前の友人を区別するためにつける名字やニックネーム(例:「長屋」の八っつ あん)のようなものだと思えばいい.標準出力関係で頻繁に用いる
cout
やendl
などはstd
という名前空間の下で定義されているため,本来はstd::cout
やstd::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つの部分で構成されている:cout
: 標準出力ストリーム.<<
: 挿入演算子.左辺のストリーム(この場合cout
)に右辺の要素(この場合"Hello world" )を出力する.-
"Hello world" : 文字列定数."(ダブルクォーテーション)で囲まれた文字は文字列定数として扱われる. <<
: 再び挿入演算子.左辺のストリーム(この場合,"Hello world" を出力し終えたcout
ストリーム)に右辺の要素(この場合endl
)を出力する.endl
: 改行を表す定数.
従って,この行は「
cout
に"Hello world" と改行をこの順に出力せよ」という命令を表している. - 第8行
- 正常終了であることを関数を呼び出した側に伝える
return 0; // 正常終了を表す 0 を返す
return
とすることで現在の関数を抜け出し,呼び出し側へ移動する.この とき空白の後に戻り値を指定すれば,呼び出し側へその値を渡すことができ る. - 第9行
- 関数
main
が終わることを宣言.}
C++の関数の定義は閉じ波括弧「}」で終了する必要がある.
3.6.7 コンパイルエラーとデバッグ
g++
はデフォルトでは「うまくいった時には何も表示しない」という「不言実行」仕様
で設計されている.そのため「 g++
がお返事した時」は何かがうまくいっていないので
ある.以下では,どんな場合にどのようなエラーが表示され,どう対処すべきかの例をい
くつか挙げよう.
- 行番号までコピーしてしまった場合
不注意な学生は「あ,またコピペでいいのかな」と,ブラウザ上に表示されたソースコー ドをそのままコピー&ペーストしているかもしれない.このとき,Emacs 上では以下のよ うに表示される:色が崩れていないので,一見よさそうに見えるが,これをコンパイルすると次のように, エラー・メッセージが表示される:
ここでは,3つのエラー・メッセージが表示されている:
sample1.cpp:1: error: stray '#' in program
sample1.cpp:1: error: expected unqualified-id before numeric constant
sample1.cpp:3: error: expected unqualified-id before numeric constant
それぞれのエラー・メッセージは,
ファイル名:行番号: error: エラーの内容
という構造で表示されている.すなわち,それぞれのエラー・メッセージは,以下のこと を表している:
sample1.cpp
の 1行目 でstray '#' in program
というエラーが起きましたここで,
stray '#' in program
は「適切な位置でないところに '#
' という文字がありま すよ」という意味.この1行目の最初の文字が#
で始まるべきところ,1:
という文字で始まっているため,#
が登場したところで構文解析できなくなっ ている.sample1.cpp
の 1行目 でexpected unqualified-id before numeric constant
というエラーが起きましたここで,
expected unqualified-id before numeric constant
は「数値定数の前に 何かが必要なはずです」という意味(多分).1行目の最初の1
を数値と見なすと, その前に代入や宣言が無いので構文解析できなくなっている.なお,2行目について何も言及が無いのは問題が無いからではなくて,2行目まで含め て構文解析してみたところ,少なくともこういうエラーが起きたので,これ以上解析 できないことを伝えている.
sample1.cpp
の 3行目 でexpected unqualified-id before numeric constant
というエラーが起きました上と同様.4行目以降について何も言及が無いのは問題が無いからではないことに注意.
しかるべき対処方法は,当然,各行の先頭から「行番号」+「:」を取り除くことである.
- 1行目の頭の
#
を忘れた場合
このとき,Emacs 上では以下のように,1行目がぜんぶ緑色で表示される:これに気づかずコンパイルすると,以下のエラーメッセージが表示される(後で説明し易 くするため,行番号をつけ足してある):
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つ:sample1.cpp
の 1行目 でexpected constructor, destructor, or type conversion before '<' token
(< という記号(トークン)の前に,コンストラクタ, デストラクタもしくは型変換があるはずです)というエラーsample1.cpp
の 7行目 で'cout' was not declared in this scope (このスコープ内では 'cout
' は定義されていません)というエラーsample1.cpp
の 7行目 で'endl' was not declared in this scope (このスコープ内では 'endl
' は定義されていません)というエラー
問題があるのは1行目なのに,7行目までエラーが出ている.これは,本来,1行目の
#include <iostream>
で
cout
やendl
の「定義」を読み込むところでエラーが生じているため 「得体の知れないcout
やendl
は使えません」と返されている.
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つの部分から構成されている:
mv
コマンドa.out
変更前のファイル名sample1.out
変更後のファイル名
以上の一連の処理を行なったときの端末は次のように表示されているだろう:
p
- クイズ:どこが間違っている?
問題:次のプログラム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を出力するプログラムを作成し,コンパイルして実行してみる
- 新しいファイル
~/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: }
- コンパイルして実行してみる
端末上で以下のように入力する(# 以降の赤字になっている部分はコメントなので入力 する必要なし)
$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
)型の変数latitude
とlongitude
を定義し,それぞれ を定数で初期化.// 緯度・経度を格納するための倍精度実数(double)型の変数を定義 double latitude = 38.2523; // 緯度 double longitude = 140.856; // 経度
「変数」とは,数値や文字列などのデータを格納しておく「箱」のようなものだ.それぞれの箱 には,入れられるデータの種類(整数,実数,文字列など)が決まっており,これを「型」 と呼ぶ.整数を格納するための型は
int
型,実数を格納するための型はdouble
型で ある.それぞれの変数には,型とは別に,名前がついている.変数の名前は型とは無関係に, 自由につけて良い.変数名には英字・数字および
_
(アンダーバー)が利用できる. 大文字と小文字は区別されるので,longitude
とLongitude
は全く別物として扱 われる.変数に値を格納するためには,まず,変数を定義しなければならない. 変数の定義は,
型 変数名;
とする.同じ型の複数の変数を定義する時には,
型 変数名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つの方法がある:
- 端末上で領域を選択して右クリックメニューからコピーする.
直感的だが,領域の選択を間違う可能性があったり,出力される結果が大きくなる と選択するのが大変になる.左ボタンを押しながらマウスを動かすことで領域を選択 できる.選択が終わったら右クリックメニューから「コピー」を選択する.
- シェルのパイプ機能とシェルスクリプト
klip
を使ってクリップボードへ実行結果を送るシンプルなコマンドで実現できる.マウス操作が不要なので人為的ミスをしにくい.端末上で
./sample1-2.out | klip
というように,実行ファイル
./sample1-2.out
の後に縦棒「|
」と シェルスクリプトklip
を記述することで,./sample1-2.out
の実行結果を (Klipper)が管理するクリップボードに取り込める.
3.7.5 クリップボード上の KML を Gist に投稿し,そのリンクを Google Maps に渡す
- Gist に KML を投稿する
https://gist.github.com にログインすると Gist 投稿画面になるので, = クリップボードの中身を貼り付ける
- ファイル名を
castle.kml
などとする(ファイル名は自由につけてよいが,拡張子 は =.kml=にしておく必要がある) - ファイルの説明を記述する
- 「Create Secret Gist」もしくは「Create Public Gist」をクリックして Gist を投 稿する
- ファイル名を
- Gist のリンクを Google Maps に渡す
- 投稿した Gist のページに移動し,View Raw アイコン
の上で右クリック:
- メニューから「リンクのURLをコピー」を選択
- 投稿した Gist のページに移動し,View Raw アイコン
- Google Maps に KML のリンクを渡す
- http://maps.google.com にアクセスし,検索窓にコピーした URL を貼り付ける.
- http://maps.google.com にアクセスし,検索窓にコピーした URL を貼り付ける.
- こんな画面になった?
3.7.6 レポート課題
sample1-2.cpp
をもとにして,- 緯度: 35.6332, 経度: 139.881 に目印(
<Placemark>
)を置く - 目印の説明(
<description>
タグ)として,上記の座標 に加えて,学籍番号と氏名 を記載する. - 目印の名前(
<name>
タグ)を(「仙台城跡」ではなく),当該座標に適したものにす る
ような KML を出力するプログラムを C++ を用いて作成し,そのソースファイルを
report1-2.cpp
という名前で提出せよ.- 緯度: 35.6332, 経度: 139.881 に目印(
- 出力された KML を
report1-2.kml
という名前で Gist にアップロードし,そのリ ンクを Google Maps に渡し,意図通りに表示されているかを確認せよ. 以下の方法を参考に,表示させた地図へのリンク をコピーし,提出せよ.
- 演習課題のヒント
sample1-2.cpp
をreport1-2.cpp
という別名で保存する.これを実行するには いく通りもの方法がある.例えば,- Emacs 上で以下を入力する
C-x C-w ~/cpp/report1-2.cpp
- 端末上で以下を入力する
$cp ~/cpp/sample1-2.cpp ~/cpp/report1-2.cpp
- Cent OS のメニューから「ホーム」を選び,右クリックメニューを使ってコピー, 貼り付けする(名前が重複するときはファイル名を入力するようになっている).
などがある.
- Emacs 上で以下を入力する
- 第8〜9行目の緯度と経度を,与えられたものに修正する.
- 第19行目の
<name>〜</name>
の中身を適切なものに修正する. - 第21〜24行目に,以下のような1行をつけ加える:
- 修正前
-
<< " <description>\n" << " 緯度:" << latitude << " 経度:" << longitude << "\n" << " </description>" << endl
- 修正後
-
<< " <description>\n" << " 緯度:" << latitude << " 経度:" << longitude << "\n" << " 学籍番号:xxxxxxxx 名前:oooooooo" // ←この行を追加 << " </description>" << endl
- 「端末」上で
report1-2.cpp
をコンパイルし,実行する$ cd ~/cpp # ディレクトリを ~/cpp に移動する $ g++ report1-2.cpp -o report1-2.out # コンパイルする $ ./report1-2.out # 実行する : : (実行結果として KML が表示される) : :
- 出力された KML をクリップボードにコピーする
$ ./report1-2.out | klip
- http://gist.github.com にアクセスし,コピーした KML を貼り付け,
ファイル名を
report1-2.kml
として「Create Privae(もしくはPublic) Gist」とし て新しい gist を作成する. - 作成された gist の raw ファイルの URL を取得し,Google Maps (http://maps.google.co.jp) に渡す.
- 適切な目印が表示されたことを確認したら,課題を提出する.
- 課題の提出方法
C++ソースファイルおよび Google Maps へのリンクの提出は 東北大学インターネット スクール(ISTU: https://xapp.istu.jp) から行う.- ISTU にアクセスし,「情報基礎B」のレポート「指定された座標に目印を置くKMLを出 力するプログラムを C++ で作成し,目印をGoogle Maps に表示させる」を選択すると, ファイルとテキストを投稿する画面が現れる.
- ファイル名の横の「参照」をクリックし,自分が作った
report1-2.cpp
を選択する. - Google Maps への URL (
https://maps.google.com/maps?q=http://...
で始まるも の) を取得し,「解答」というテキストエリアに貼り付ける - 準備ができたら,画面右下の「提出」ボタンをクリック
3.8 [7/12] C++で標準入力を受けとるプログラムを作って実行してみる
3.8.1 何のため?
ここでは,
- 標準入力から緯度と経度を受けとってそれを表示するプログラム(
sample2
) - 受け取った緯度と経度が宮城県内に存在するかどうかを表示するプログラム(
sample3
)
を作る.これによって,
- 入力ストリームから
>>
演算子を用いてデータを取得する方法 setf
やprecision
を使って特定の書式でデータを出力する方法if
文を用いたプログラムの「分岐」の方法
を学ぶ.
3.8.2 標準入力から情報を受けとるプログラムを作成し,コンパイルして実行してみる
- 新しいファイル
~/cpp/sample2.cpp
を作成する.Emacs 上で以下を入力して
~/cpp
フォルダの下にsample2.cpp
というファイル を作ろう.C-x C-f ~/cpp/sample2.cpp RET
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: }
- 端末上で以下のように入力してコンパイルし,実行する.
$ cd ~/cpp # ディレクトリを cpp に移動(既に移動済みなら省略可能) $ g++ sample2.cpp -o sample2.out # コンパイルする $ ./sample2.out # 実行する
このとき,端末には下記のように表示され,入力を待つ状態になる:
ここで,宮城県の県庁所在地の緯度・経度を,半角スペースで区切って下記のように タイプ:
38.268839 140.872103
して
RET
を入力すると,緯度:38.2688 経度:140.872
と表示される.緯度に関しては小数点以下第5桁以降が,経度に関しては第4桁以降が 表示されていないが,これに関しては,次の項で修正しよう.
なお,緯度・経度は,改行で区切ってもよいが,コンマ(,)などで区切ってはいけない.
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
)型の変数latitude
とlongitude
を定義する(初期化はしない).double latitude, longitude; // 2つの倍精度実数型の変数 latitude, longitude を定義
- 第9行
- 標準出力
cout
に入力を促すメッセージを表示.cout << "緯度と経度を入力して下さい" << endl; // 標準出力 cout に緯度・経度の入力を促すメッセージを表示
cout
のような出力ストリームに文字や数値を送るには,<<
という演算子を用いる.出力ストリームへは,いくつでも,どんな型のデータでも送ることができる.
cout << [データ1] << [データ2] << ... << [データn];
というように,
<<
を間に入れることで,この順に,いくつでもデータを出力させられ る.ここで[データ1]や[データ2]などの位置に指定できるのは,以下の3つの いずれかである.- (既に定義されている) 変数
- (
123
や "Hello world
" や "d34a0rtt
" のような) 定数 endl
(改行)のような マニピュレータ
- 第10行
- 標準入力
cin
からlatitude
およびlongitude
の値をこの順に取得す る.cin >> latitude >> longitude; // 標準入力から latitude と longitude の値を獲得
標準入力とは,通常は,コンソール(端末のこと)からのキーボード入力を 指す.
cin
のような入力ストリームからデータを取得するには,>>
という抽出演算子を用いる(出力のときとは向きが逆なことに注意され たい).挿入演算子と同様に,抽出演算子を用いれば,いくつでも,どんな型のデー タでも入力ストリームから取得できる.
cin >> [データ1] >> [データ2] >> ... >> [データn];
ただし,挿入演算子とは異なり,[データ1], [データ2]などの位置には,変 数しか指定できない.
- 第13行
- 入力された緯度と経度を出力する.
cout << "緯度:" << latitude << " 経度:" << longitude << endl; // 文字列と組み合わせて表示
ここでは,標準出力
cout
に,以下の5つのデータを順に送っている.- "
緯度:
"という文字列 latitude
という変数- "
経度:
"という文字列 longitude
という変数endl
というマニピュレータ
ただし,2 と 4 の変数については,変数の名前(
latitude, longitude
) ではなく,変数に格納された数値が展開されて標準出力に送られる. - "
- 第14行
main
関数の終わりを宣言.}
3.8.4 表示桁数を修正してみる
上述の方法では,緯度が小数点以下第4桁まで(経度は第3桁まで)しか表示されない.これ
を修正するために,出力ストリーム・クラスが備える setf
と precision
という関数
を使う(同時に,これらを使うのに必要な <iomanip>
というライブラリを読み込む).
~/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
というファ イルが生成された.- ソース・ファイルの修正
~/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: }
- コンパイルして実行する
端末上で以下のように入力する:
$ g++ sample2-2.cpp -o sample2-2.out $ ./sample2-2.out 緯度と経度を入力して下さい # 入力待ち 38.268839 140.872103 # 緯度と経度を入力して Enter 緯度:38.268839 経度:140.872103 # 小数点以下6桁までが表示される
- 変更した部分の解説
追加した各行がどのような意味を持つのかを解説しよう.
- 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 以下
を同時に満足するとき,その点は宮城県内にあると判定することにしよう.
~/cpp/sample2-2.cpp
を別名(sample3.cpp
)で保存する.C-x C-w ~/cpp/sample3.cpp RET
- ソース・ファイルの修正
~/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: }
- コンパイルして実行する
端末上で以下のように入力する:
$ g++ sample3.cpp -o sample3.out $ ./sample3.out 緯度と経度を入力して下さい # 入力待ち
何度か実行して緯度や経度を入力してみよう.緯度・経度だけでなく,宮城県内かど うかも表示してくれる.少々弱気な表現になっている理由は「補足:正確に宮城県内/ 外を判定するには?」に後述したので読んでみて欲しい.
- 補足:正確に宮城県内/外を判定するには?
県境はメルカトル図法上の長方形にはなっていないので,当然,上記の条件 だけでは宮城県の内外は判定できない(「おそらく」という弱気なメッセージ なっているのはそのためである).例えば,天童市(北緯: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行
if
〜else
文による分岐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つにでも当ては まる ときには「範囲外です」と表示させ,そうでなければ「宮城県内 です」と表示させている.
- 緯度(
latitude
)が南端(lat_min
)より小さい - 緯度(
latitude
)が北端(lat_max
)より大きい - 経度(
longitude
)が西端(lng_min
)より小さい - 経度(
longitude
)が東端(lng_max
)より大きい
このように「ある条件が満足されるときには A という処理を行い 満足されないときには B という処理を行う」などと条件次第で処理 を分けることを 分岐 と呼ぶ.
C++ で上記のような分岐を実現するには,
if
〜else
という命令 を使う.その基本構造は,以下の通りである.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つの条件:
latitude < lat_min
latitude > lat_max
longitude < lng_min
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.out
やsample4.out
の実行結果がのように文字化けしていたり,
sample3.cpp
コンパイル時にのようなエラーが出たら,それは 文字コード の問題である.
- このファイルの文字コードは?
Emacs 上では,開いているファイルの文字コードはウィンドウの左下のステータスバーに表示されている.ここには「E」「S」「J」の3つのアルファベットのいずれかが表示されている.それぞれ, 以下の文字コードを表している.
記号 表示イメージ 文字コード E EUC-JP S Shift-JIS J UTF-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 になったら完成.
3.9 [7/19] ファイル上のバス停データを KML で出力するプログラムを C++ で作ってみる
3.9.1 何のため?
ここでは,ファイル上にあるバス停データを KML で出力するプログラムを C++ で作って みる.これにより,
- ファイル入力ストリームを開く方法
- 入力ストリームから
getline
を使って CSV 形式のデータを読み込む方法 while
文を使った繰り返しの方法
を学ぶ.
3.9.2 クリップボードにコピーするシェルスクリプトを変更
7月4日以前の設定では,日本語を含む処理結果を適切に表示できない.そこで,
~/.bashrc
の中身を以下のように修正する(cat
を lv
に変更する):
- 修正前
-
klip() { dcop klipper klipper setClipboardContents "$(cat $1)"; }
- 修正後
-
klip() { dcop klipper klipper setClipboardContents "$(lv $1)"; }
修正し終わったら保存し,端末上で
source ~/.bashrc
とする.
3.9.3 バス停データをダウンロードする
Firefox からダウンロードする.
- Firefox で以下の URL にアクセスする:
https://raw.github.com/nagae/2013ICL-B/master/bus-short.csv
- 右クリック・メニューで「名前を付けて保存」
- 保存先を
~/cpp
に指定する - 中身を確認する
less
だと日本語が化けるのでlv
コマンドを使う$ lv bus-short.csv
lv
モードでは,各キーに以下のような機能が割り当てられている. #+ATTRHTML:border="2" rules="all"キー 機能 カーソル上下 行単位でスクロール F
/B
ページ単位でスクロール q
lv
モードを終了
3.9.4 ファイルからデータを読み込んで表示させるプログラム(sample4.cpp
)を作成する
- 新しいファイル
~/cpp/samplel4.cpp
を作成する.Emacs で以下のように入力する.C-x C-f ~/cpp/sample4.cpp RET
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: }
- コンパイルして実行する.
端末上で以下のように入力する.
$cd ~/cpp # ~/cpp フォルダに移動 $g++ sample4.cpp -o sample4.out # sample4.cpp をコンパイル(sample4.outを生成) $./sample4.out # sample4.out を実行
こんな風に,読み取ったデータを加工して表示させられる.
3.9.5 ファイルから読み込んだデータを KML で出力させるプログラム(sample5.cpp
)を作成する
sample4.cpp
のデータを表示させる部分(第24〜30行)を KML 形式で出力するように修
正し,KML を使うために必要なヘッダをフッタを出力する命令を付け加えれば,バス停デー
タを KML 方式で出力できる.
~/cpp/sample4.cpp
を別名(~/sample5.cpp
)で保存するsample4.cpp
が開かれている Emacs のバッファ上でC-x C-w ~/cpp/sample5.cpp RET
とすれば,
sample5.cpp
という名前で保存できる.- ソース・ファイルの修正
~/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;
- ファイルを保存,コンパイル,そして実行
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
をタイプ). - クリップボード上の KML を Gist に投稿し,そのリンクを Google Maps に渡す
Gist への投稿方法や,そのリンクを Google Maps に渡す方法については こちら を参照(リンク先から戻るには
b
をタイプ).適切な KML が渡されれば,以下のように表示されるはず.
3.10 [7/26] 自宅の近くのバス停を表示させてみる(レポート課題あり)
3.10.1 何のため?
- ここまでやってきたことの集大成として,当初目的である「自宅周辺のバス停を Google Maps に表示させる」
3.10.2 ファイルから読み込んだバス停のうち,特定の範囲内にあるものだけを KML形式で出力させるプログラム(sample6.cpp
)を作成する
sample5.cpp
の一部を修正し,仙台駅を基準として緯度・経度が±0.05 の範囲内にあ
るものだけを KML 形式で出力するプログラムを作成する.
~/cpp/sample5.cpp
を別名(~/sample6.cpp
)で保存するsample5.cpp
が開かれている Emacs のバッファ上でC-x C-w ~/cpp/sample6.cpp RET
とすれば,
sample6.cpp
という名前で保存できる.- ソース・ファイルの修正
~/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行目
- 表示範囲に入っているものだけを出力.
if
〜else
型の構文については,緯度・経度に応じた分岐 を行うプログラム 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; }
- ファイルを保存,コンパイル,そして実行
sample6.cpp
の編集が終わったら保存(C-x C-s
)し,端末で以下のように入力す る:$cd ~/cpp # ~/cpp フォルダへ移動(既に移動済みなら不要) $g++ sample6.cpp -o sample6.out # sample6.cpp をコンパイル(sample6.outを生成) $./sample6.out # 実行結果を確認する
日本語がうまく表示されない場合には「文字化けを解消するには?」を参照のこと(リン ク先から戻るには
b
をタイプせよ). - 出力された KML をGist に貼り付けて
sample6.kml
という名前でアップロードし,Google Maps に読 み込ませてみる.- 出力結果をクリップボードにコピーする方法はこちらを参照(リンク先から戻るには
b
をタイプ). - Gist への投稿方法や,そのリンクを Google Maps に渡す方法については こちら を参照(リンク先から戻るには
b
をタイプ).
適切な KML が渡されれば,以下のように表示されるはず.
- 出力結果をクリップボードにコピーする方法はこちらを参照(リンク先から戻るには
3.10.3 表示範囲も表示させるプログラム(sample7.cpp
)を作成する
sample6.cpp
の出力結果だけでは,適切なバス停が抽出できているかどうか判りにくい
ので,これをさらに修正して,特定の緯度・経度を中心とした矩形範囲を表示させる KML
も出力させるプログラム sample7.cpp
を作成する.
~/cpp/sample6.cpp
を別名(~/sample7.cpp
)で保存するsample6.cpp
が開かれている Emacs のバッファ上でC-x C-w ~/cpp/sample7.cpp RET
とすれば,
sample7.cpp
という名前で保存できる.- ソース・ファイルの修正
~/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> を参照.
- ファイルを保存,コンパイル,そして実行
sample7.cpp
の編集が終わったら保存(C-x C-s
)し,端末で以下のように入力す る:$cd ~/cpp # ~/cpp フォルダへ移動(既に移動済みなら不要) $g++ sample7.cpp -o sample7.out # sample7.cpp をコンパイル(sample7.outを生成) $./sample7.out # 実行結果を確認する
日本語がうまく表示されない場合には「文字化けを解消するには?」を参照のこと(リン ク先から戻るには
b
をタイプせよ). - 出力された KML をGist に貼り付けて
sample7.kml
という名前でアップロードし,Google Maps に読 み込ませてみる.- 出力結果をクリップボードにコピーする方法はこちらを参照(リンク先から戻るには
b
をタイプ). - Gist への投稿方法や,そのリンクを Google Maps に渡す方法については こちら を参照(リンク先から戻るには
b
をタイプ).
適切な KML が渡されれば,以下のように表示されるはず.
- 出力結果をクリップボードにコピーする方法はこちらを参照(リンク先から戻るには
3.10.4 完全版バス停データをダウンロードする
Firefox からダウンロードする.
- Firefox で以下の URL にアクセスする:
https://raw.github.com/nagae/2013ICL-B/master/bus.csv
- 右クリック・メニューで「名前を付けて保存」
- 保存先を
~/cpp
に指定する - 中身を確認する
3.10.5 完全版バス停データを用いて仙台駅周辺のバス停を表示させるプログラム(sample8.cpp
)を作成する
~/cpp/sample7.cpp
を別名(~/sample8.cpp
)で保存するsample7.cpp
が開かれている Emacs のバッファ上でC-x C-w ~/cpp/sample8.cpp RET
とすれば,
sample8.cpp
という名前で保存できる.- ソース・ファイルの修正
~/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; // 表示する緯度・経度の範囲
- ファイルを保存,コンパイル,そして実行
sample8.cpp
の編集が終わったら保存(C-x C-s
)し,端末で以下のように入力す る:$cd ~/cpp # ~/cpp フォルダへ移動(既に移動済みなら不要) $g++ sample8.cpp -o sample8.out # sample8.cpp をコンパイル(sample8.outを生成) $./sample8.out # 実行結果を確認する
日本語がうまく表示されない場合には「文字化けを解消するには?」を参照のこと(リン ク先から戻るには
b
をタイプせよ). - 出力された KML をGist に貼り付けて
sample8.kml
という名前でアップロードし,Google Maps に読 み込ませてみる.- 出力結果をクリップボードにコピーする方法はこちらを参照(リンク先から戻るには
b
をタイプ). - Gist への投稿方法や,そのリンクを Google Maps に渡す方法については こちら を参照(リンク先から戻るには
b
をタイプ).
適切な KML が渡されれば,以下のように表示されるはず.
- 出力結果をクリップボードにコピーする方法はこちらを参照(リンク先から戻るには
3.10.6 レポート課題
sample8.cpp
をもとにして,以下の全てを表示させる KML を出力するプログラムを C++ を用い て作成,そのソースファイルをfinal_report.cpp
という名前で提出せよ:- 自宅を基準として,緯度・経度が
±0.01=〜=±0.05
程度の範囲(出力されるバス 停の数に応じて適宜調整すること)を四角い枠で表示する.ただし,プライバシーが気になる人は,自宅の近くの適当な座標 を中心としてよい. - 完全版バス停データ(
bus.csv
)の中から,上記の範囲に入るバス停を表示する.
- 自宅を基準として,緯度・経度が
- 出力された KML を
final_report.kml
という名前で Gist にアップロードし,その リンクを Google Maps に渡して意図通りに表示されているかを確認せよ. 適切に表示されたら,その Google Maps へのリンクを提出せよ.
課題の提出方法については こちら を参照 (リンク先から戻るには b
をタイプ)
4 参考文献
- 大竹智也, Emacs 実践入門, 技術評論社, 2012.
- 酒井聡樹, 100ページの文章術, 共立出版, 2011.