1 of 106

オープンデータ利用

静岡産業技術専門学校

2015年 4年生前期 水野信也

2 of 106

クラウド情報基盤研究室

データの循環から、

価値を生み出す

モバイル組み込み環境

IoT/M2M

・スマートフォン

・組み込みAndroid

・Arduino

・ウェアラブル

・動画

クラウド環境 ビッグデータ

・Paasの利用(GAE, AWS)

・各種Webシステム

データ

データ解析環境

・数学的アプローチ

・視覚化

・各種ツールの利用

各種APIの利用

DB

DB

DB

DB

DB

3 of 106

オープンデータとは?

「機械判読に適したデータ形式で、二次利用が可能な利用ルールで公開されたデータ」

「人手を多くかけずにデータの二次利用を可能とするもの」

→公的・公共機関のデータを利用し、新しいシステムを提案する

4 of 106

オープンデータ利用を

考えてみよう!

・利用するデータ

・提案する結果

例1

利用データ:AEDの設置場所

提案する結果:現在地から一番近いAEDの場所を提供

例2

利用するデータ:津波タワー

提案する結果:現在地から一番近い津波タワーの場所とルートを提供

5 of 106

オープンデータの種類

オープンデータ戦略の推進(総務省)

http://www.soumu.go.jp/menu_seisaku/ictseisaku/ictriyou/opendata/

ふじのくにオープンデータカタログ

http://www.pref.shizuoka.jp/kikaku/ki-330/opendata/

http://www.city.shizuoka.jp/186_000001.html (静岡市)

6 of 106

5/20 出席レポート

12:10までに、サイボウズ->情報理論(水野)

・利用するデータ

・提案する結果

を投稿しなさい。

→今回は静岡市のWiFiスポット一覧を利用する

7 of 106

本の紹介

[24時間365日] サーバ/インフラを支える技術 ‾スケーラビリティ、ハイパフォーマンス、省力運用 (WEB+DB PRESS plusシリーズ)

  • 出版社: 技術評論社 (2008/8/7)
  • ISBN-10: 4774135666
  • 価格:3,002円

8 of 106

本の紹介

スッキリわかるサーブレット&JSP入門

  • 出版社: インプレスジャパン (2014/5/7)
  • 言語: 日本語
  • ISBN-10: 4844335804
  • 値段:3,024円

9 of 106

5/27 静岡市WiFiスポット

静岡市オープンデータ

http://www.city.shizuoka.jp/186_000001.html

CSVファイルを手元に持ってくる

10 of 106

WiFiスポット検索システム

目的:現在地から、各WiFiスポットの距離、時間を計算し、一番近いWiFiスポットを提案する

フェーズ1:データをアップロードし、利用出来る形にする

フェーズ2:Webシステムとして利用出来るようにする

フェーズ3:モバイルと連携して利用出来るようにする

11 of 106

データベースの準備

phpMyAdmin

http://学籍番号.sangi01.net/phpMyAdmin/

ID j120XX

PASS j120XX

12 of 106

データベースの準備

テーブル3個

テーブル名:spots 「WiFiスポット情報」

(id,name,address,campany,latitude,longitude,created,modified)

テーブル名:places 「現在地情報」

(id,name,address,latitude,longitude,created,modified)

テーブル名:「distances」 現在地とWiFiスポットのパラメタ(距離、時間)

(id, spot_id,place_id,distance,duration,created,modified)

13 of 106

spotsテーブル作成

14 of 106

placesテーブル

15 of 106

distancesテーブル

16 of 106

Cakephpの環境を作成

学生ファイルサーバ->student->J4->授業資料->Opendata->cakephp-2.6.4.zip

をデスクトップにコピー

zipファイルをサーバにFTPでアップロード

FTP ホスト:学籍番号.sangi01.net

ID j120XX, PASS sangij120XX

でログインし、wwwの中に入れる

※接続モードを「アクティブ」に変更

17 of 106

SSHでログイン

puttyで接続

ホスト名:j120XX.sangi01.net

変換->文字コードをUTF-8に変更

開くを押す

ユーザ名:j120XX

パスワード:sangij120XX

で接続

18 of 106

cakephp設定

$ cd www

$ ls

[j11000@ip-10-126-24-180 ~]$ cd www

[j11000@ip-10-126-24-180 www]$ ls

README cakephp-2.6.4.zip

解凍

$ unzip cakephp-2.6.4.zip

フォルダ名変更

$ mv cakephp-2.6.4 opendata

19 of 106

cakephp設定

$ cd opendata

opendata]$ chmod -R 777 lib/Cake/Cache/

opendata]$ cd app

app]$ chmod -R 777 tmp

ブラウザでアクセスして確認

http://j120XX.sangi01.net/opendata/

20 of 106

cakephp設定

app]$ vi Config/core.php

行番号を出す

: set number

行に移動(225行目に移動)

225G

これを適当な値に変更

保存して閉じる(Esc :wq)

21 of 106

cakephpの設定

ブラウザでアクセスするとNoticeが消えている

22 of 106

データベース設定

app]$ Console/cake bake

Name:

[default] >

Datasource: (Mysql/Postgres/Sqlite/Sqlserver)

[Mysql] >

Persistent Connection? (y/n)

[n] >

Database Host:

[localhost] >

Port?

[n] >

User:

[root] > j120XX

Password:

> j120XX

Database Name:

[cake] > j120XX

Table Prefix?

[n] >

Table encoding?

[n] > utf8

23 of 106

データベース設定

ブラウザで確認すると、緑が増えている

24 of 106

spotsテーブルのbake

app]$ Console/cake bake

Enter a number from the list above,

type in the name of another model, or 'q' to exit

[q] > 13(spotの番号)

Would you like to supply validation criteria

for the fields in your model? (y/n)

[y] >

この後はデフォルトでOK(Validationをつける)

25 of 106

stopsテーブル

Would you like to define model associations

(hasMany, hasOne, belongsTo, etc.)? (y/n)

[y] >

Spot hasMany Distance? (y/n)

[y] >

Would you like to define some additional model associations? (y/n)

[n] >

Look okay? (y/n)

[y] >

PHPUnit is not installed. Do you want to bake unit test files anyway? (y/n)

[y] > n

spotsテーブルのM(モデル)は終了

26 of 106

spotsテーブル(コントローラ)

What would you like to Bake? (D/M/V/C/P/F/T/Q)

> C

[q] > 13(spotの番号)

---------------------------------------------------------------

Baking SpotsController

---------------------------------------------------------------

Would you like to build your controller interactively? (y/n)

[y] >

27 of 106

spotsテーブル(コントローラ)

28 of 106

spotsテーブル(View)

What would you like to Bake? (D/M/V/C/P/F/T/Q)

> V

Enter a number from the list above,

type in the name of another controller, or 'q' to exit

[q] > 13(spotsの番号)

Would you like bake to build your views interactively?

Warning: Choosing no will overwrite Spots views if they exist. (y/n)

[n] >

29 of 106

spotsテーブル確認

30 of 106

placesテーブルも同様にbake

M -> C -> Vの順で実施

http://j120XX.sangi01.net/opendata/places/

にアクセス

31 of 106

distancesテーブルも同様にbake

M -> C -> Vの順で実施

http://j120XX.sangi01.net/opendata/distances/

にアクセス

Mのとき

A displayField could not be automatically detected

would you like to choose one? (y/n)

> n

nを入力

32 of 106

distancesテーブル確認

33 of 106

今日の課題

3つのテーブルをbakeし、サイボウズにURLを投稿すること

URLは

http://j120XX.sangi01.net/opendata/distances

を投稿すること

34 of 106

6/5 データのアップロード

SSHで接続、cd www/opendata/appに移動

SpotsテーブルにデータをCSVでアップロードを行う

http://j12XXX.sangi01.net/opendata/spots/csv

spots : モデル名(テーブル名)

csv:コントローラーの中の関数名

SpotsController->csv関数

表示:

View/Spots/csv.ctp (作成)

http://ホスト名/ルートフォルダ名/モデル名/アクション名

35 of 106

csv.ctp(表示)を作成

add.ctpをコピーして利用する

app]$ cp View/Spots/add.ctp View/Spots/csv.ctp

コントローラにcsvアクションを追加する

app]$ vi Controller/SpotsController.php

public function csv(){ //空の関数

}

保存 Esc + :wq

36 of 106

確認

http://j12XXX.sangi01.net/opendata/spots/csv

にアクセス

この画面の必要ないものを削除

app]$ vi View/Spots/csv.ctp

削除

2 <?php echo $this->Form->create('Spot'); ?>

6 echo $this->Form->input('name');

7 echo $this->Form->input('address');

8 echo $this->Form->input('campany');

9 echo $this->Form->input('latitude');

10 echo $this->Form->input('longitude');

13 <?php echo $this->Form->end(__('Submit')); ?>

37 of 106

csv.ctp

追加

echo $this->Form->create('Spot',array('action'=>'import','type'=>'file'));

echo $this->Form->input('CsvFile',array('label'=>'','type'=>'file'));

echo $this->Form->end('Upload');

create(‘Spot’ SpotsではなくSpot

38 of 106

確認

http://j12XXX.sangi01.net/opendata/spots/csv

39 of 106

保存が出来なくなった場合

swpファイルが出来ている可能性あり

ls -la 指定ディレクトリ

.swpファイルがあった場合

rm 指定ディレクトリ/ファイル名.swp

でswpファイルを削除

40 of 106

Spots->importアクションの作成

app]$ vi Controller/SpotsController.php

public function import(){

if($this->request->is('post')){

$up_file = $this->data['Spot’]['CsvFile']['tmp_name'];

$fileName = 'post.csv';

if(is_uploaded_file($up_file)){

move_uploaded_file($up_file, $fileName);

$this->Spot->loadCSV($fileName);

$this->Session->setFlash('Uploaded');

$this->redirect(array('action'=>'index'));

}

}

}

41 of 106

Spots->loadCSV関数作成

app]$ vi Model/Spot.php

loadCSV関数

42 of 106

アップロード

app]$ chmod 777 webroot/

ファイルを指定してアップロード

43 of 106

Spotsデータを地図表示

CakephpでGoogleMapを表示する

->GoogleMapHelperを利用

FTPで

www/opendata/app/View/Helper/ に保存

44 of 106

map関数の作成

app]$ vi Controller/SpotsController.php

public function map() {

$this->Spot->recursive = 0;

$this->paginate = array(

'conditions'=>array(),

'order' => array('id' => 'asc'),

'limit'=>200,

);

$this->set('spots', $this->Paginator->paginate());

}

45 of 106

map.ctpの作成

app]$ cp View/Spots/index.ctp View/Spots/map.ctp

app]$ vi View/Spots/map.ctp

1行目に追加

<?php echo $this->Html->script('http://maps.google.com/maps/api/js?sensor=true', false); ?>

を入れておく

46 of 106

マップ領域の作成

<h2><?php echo __('Spots'); ?></h2>の下に追加

<div id="map">

<?php echo $this->GoogleMap->map($map_options); ?>

</div>

47 of 106

map_optionsの作成:

<?php

$map_options = array(

'id' => 'map1',

'width' => '1200px',

'height' => '800px',

'zoom' => 13,

'type' => 'ROAD',

//'custom' => null,

'localize' => false,

'latitude' => 自分で指定,

'longitude' => 自分で指定,

'marker' => true,

'markerIcon' => 'http://google-maps-icons.googlecode.com/files/home.png',

'markerShadow' => 'http://google-maps-icons.googlecode.com/files/shadow.png',

'infoWindow' => true,

'windowText' => "自分で指定"

);

?>

中心、地図の設定を決める

48 of 106

マーカの作成

<?php $marker_index =1; ?>

<?php foreach ($spots as $spot): ?>

<?php

$marker_options = array(

'showWindow' => true,

'windowText' =>$spot['Spot']['id'].":".$spot['Spot']['name'],

'markerTitle' => 'Title',

'markerIcon' => 'http://chart.apis.google.com/chart?chst=d_map_pin_letter&chld='.$spot['Spot']['id'].'|ff7e73|000000',

'markerShadow' => 'http://labs.google.com/ridefinder/images/mm_20_purpleshadow.png'

);

echo $this->GoogleMap->addMarker('map1', $marker_index, array('latitude' => $spot['Spot']['latitude'], 'longitude' =>$spot['Spot']['longitude'] ),$marker_options);

$marker_index ++;

?>

<?php endforeach; ?>

49 of 106

50 of 106

確認

51 of 106

今日の課題

マーカ表示までのURLを投稿

http://j120XX.sangi01.net/opendata/spots/map/

をサイボウズに投稿

52 of 106

本の紹介

“0”からはじめる入門データ・サイエンティスト

  • 出版社: 秀和システム (2014/4/30)
  • 言語: 日本語
  • ISBN-10: 4798041084
  • ISBN-13: 978-4798041087
  • 発売日: 2014/4/30

53 of 106

今後の予定

6/12 2コマ(レイアウト、パラメタ取得)

6/19 2コマ(解析手法)

7/3 2コマ(テーマ提出期限)

7/6〜8 テーマ課題制作

7/9 最終課題提出(プレゼン10分/人)4コマ

7/9 までの結果で評価

テーマ:各自考えておく

・オープンデータの利用(出典を明らかにする)

・APIの利用

・解析・計算、テーブル2個以上

54 of 106

6/12 レイアウト変更

www/opendata/app/View/Layout/

j12000.ctp (テンプレート)を置く

自分の学籍番号にファイル名を変更しておく

www/opendata/app/webroot/css/

cake.j12000.css をおく

自分の学籍番号にファイル名を変更しておく

2つのファイルをFTPでアップロード

55 of 106

j120XX.ctpを編集

$ cd www/opendata/app

$ vi View/Layout/j120XX.ctp

cake.j12XXX を自分の学籍番号(cssのファイル名)に変更

フッターも自分の情報に変更

56 of 106

文字化けした時の対処法

viの文字コードを変更

コマンドモードで

:set encoding=utf-8

を発行

57 of 106

全体にレイアウトを適用

$ vi Controller/AppController.php

クラス直下に

public $layout = "j120xx”;

58 of 106

確認

全ページにレイアウトが適用される

59 of 106

メニューの追加

テーブルに対してメニューを作成

・テーブル名:spots 「WiFiスポット情報」

・テーブル名:places 「現在地情報」

・テーブル名:distances 「現在地とWiFiスポットのパラメタ(距離、時間)」

・WiFiスポット情報:一覧、追加

(View、Edit、Deleteはメニューにいれない)

・現在地情報:一覧、追加

・現在地とスポットの距離・時間:一覧、追加

 

60 of 106

メニュー作成

http://cssmenumaker.com/

で作成して、ダウンロード

自分で好きなメニューを持ってくる

メニューサンプル

61 of 106

メニュの反映

styles.css を app/webroot/css

にFTPでアップロード

$ vi View/Layout/j12XXX.ctp

メニュー用のcssを取り込み

62 of 106

メニューの追加

<div id='cssmenu'>

</div>  </body>の直前までをコピーして

<div id="content">

の上にはりつける

63 of 106

メニュー確認

リンクが飛ぶように確認

64 of 106

パラメータ取得の準備

Placesテーブル(現在地情報)に情報を追加すると、

distancesテーブルにplacesとspotsの組み合わせが登録されるようにする

準備

places/add.ctpを開き、latitude、longitudeの項目を削除

$ vi View/Places/add.ctp

65 of 106

現在地情報を入力

名前と住所を入力

->緯度、経度を取得

->distancesテーブルに、spotsテーブルとの組み合わせを追加

66 of 106

緯度、経度取得関数

getLatLonElv関数

これをAppController.phpに追加

$ vi Controller/AppController.php

67 of 106

緯度、経度の取得

$ vi Controller/PlacesController.php

add関数に追加

$result = $this->getLatLonElv($this->request->data['Place']['address']);

$this->request->data['Place']['latitude'] = $result[0];

$this->request->data['Place']['longitude'] = $result[1];

68 of 106

緯度、経度の動作確認

名前、住所をいれると緯度経度が取れる事を確認

69 of 106

今日の課題

http://j120XX.sangi01.net/opendata/places

のURLを投稿

・オリジナルレイアウト

・メニュー

・placesの緯度経度取得

70 of 106

6/19 Distancesテーブルへ格納

Placesテーブルにデータが追加されととき、PlacesテーブルとSpotsテーブルの組み合わせをDistancesテーブルへ格納

このときは(距離、時間は取得しない)

$ cd www/opendata/app

$ vi Controller/PlacesController.php

71 of 106

Spotsテーブルの情報を持ってくる

Placesモデルで別モデル(Spotsモデル、Distanceモデル)を使えるようにする

App::import('Model','Spot');

App::import('Model','Distance');

$this->Spot = new Spot;

$this->Distance = new Distance;

72 of 106

Spotsテーブルの情報

//Spots全件持ってくる

$spots = $this->Spot->find('all');

//SpotsとPlaceの組み合わせをDistanceに入れる

foreach($spots as $spot){

$data =array(

'spot_id' => $spot['Spot']['id'],

'place_id' => $this->Place->getLastInsertID()

);

$this->Distance->create();

$this->Distance->save($data);

}

73 of 106

動作確認

まず、Placesテーブルを空にする

Placesテーブル選択->操作->テーブルを空にする(Truncate)

同じようにDistancesテーブルも空にしておく

両テーブルにレコード無しの状態で、Placesテーブルに一見追加

->distancesテーブルに199件の組み合わせが入る

74 of 106

Distancesテーブルにレコード追加

75 of 106

distancesテーブルでデータ取得

1回につき、5件のデータを取得できるようにする

5件ずつ選択できるように、チェックボックスを作成

$ cp View/Distances/index.ctp View/Distances/choice.ctp

$ vi Controller/DistancesController.php

choice関数を作成

76 of 106

choice関数

index関数をコピー

77 of 106

choice.ctpを編集

$ vi View/Distances/choice.ctp

追加

<?php echo $this->Form->create('Distance',array('url'=> array('controller' => 'distances','action' => 'choiceIndex'))); ?>

78 of 106

choice.ctpを編集

<th>check</th>

を追加

79 of 106

choice.ctpを編集

追加

<td>

<?php

echo $this->Form->input('selectDistances.', array(

'type' => 'checkbox',

'value' => $distance['Distance']['id'],

'checked' => true,

'div' => false

));

?>&nbsp;</td>

80 of 106

choice.ctpを編集

81 of 106

submitボタン

<?php echo $this->Form->end('Submit'); ?>

82 of 106

チェックボックスを確認

83 of 106

choice関数を編集

distanceまたはdurationが空のものを5件ずつページング

$ vi Controller/DistancesController.php

$this->paginate = array(

'conditions'=>array(

'OR' => array(

array('distance' => ''),

array('duration' => '')

)

),

'order' => array('id' => 'asc'),

'limit'=>5,

);

84 of 106

choice関数

85 of 106

86 of 106

受け取り:choiceIndex関数

$ vi Controller/DistancesController.php

public function choiceIndex(){

$parameters = $this->Distance->find('all',array('conditions' => array('Distance.id' => $this->data['Distance']['selectDistances'])));

foreach($parameters as $parameter){

$result = $this->getDirection($parameter['Place']['latitude'],$parameter['Place']['longitude'],$parameter['Spot']['latitude'],$parameter['Spot']['longitude']);

$data = array(

'id' => $parameter['Distance']['id'],

'distance' => $result[0],

'duration' => $result[1]

);

$this->Distance->save($data);

}

return $this->redirect(array('action' => 'choice'));

}

87 of 106

距離、時間を取得する関数

$ vi Controller/AppController.php

public function getDirection($orglat, $orglon, $dstlat, $dstlon){

$datas = file_get_contents("http://maps.googleapis.com/maps/api/directions/json?origin=$orglat,$orglon&destination=$dstlat,$dstlon&sensor=false");

$directions = json_decode($datas);

$direction = $directions->routes[0]->legs[0];

$distance = $direction->distance->value;

$duration = $direction->duration->value;

$result = array($distance, $duration);

return $result;

}

88 of 106

確認

/distances/choce

でsubmitを押すと距離、時間が取得できる

89 of 106

6/19 課題

5件ずつ距離、時間を取得してDistancesテーブルに格納する(全件とらなくてよい)

メニューに

Spotsテーブル->CSVアップロード

(csv.ctpへのリンク)

Distancesテーブル->パラメータ取得

(choice.ctpへのリンク)

http://j120XX.sangi01.net/opendata/distances/

をサイボウズに投稿

90 of 106

7/3 Place->Viewの情報

現在のViewはパラメータ情報を持っている

Viewの下に関連情報が表示されている->これを利用

91 of 106

7/3 最短距離にあるWiFiSpotを表示

Placeテーブル->Viewに最短距離にあるWiFiSpot情報を表示する

$ vi Controller/PlacesController.php

Viewのfind文を変更

$place = $this->Place->find('first', array($options,'recursive'=>2));

recursive=>2 とすると関連・関連テーブル情報まで取得

Viewのfind �文

92 of 106

recursive=>2での情報

print_r($place);

spot_idの関連テーブル(spotテーブル)の情報も取得

Place->Viewを確認

93 of 106

View アクション

public function view($id = null) {

if (!$this->Place->exists($id)) {

throw new NotFoundException(__('Invalid place'));

}

$options = array('conditions' => array('Place.' . $this->Place->primaryKey => $id));

$place = $this->Place->find('first', array($options,'recursive'=>2));

print_r($place);

//ここまでがテーブルからの情報取得

94 of 106

View アクション 続き

最短距離の計算

$min_index = 0; //最短のWiFiSpotのID

$min_distance = 10000000; //最短距離

foreach ($place['Distance'] as $distance){

if($min_distance > $distance['distance']){

$min_distance = $distance['distance'];

$min_index = $distance['id'];

$min_latitude = $distance['Spot']['latitude'];

$min_longitude = $distance['Spot']['longitude'];

$min_name = $distance['Spot']['name'];

}

}

//最短距離のWiFiSpotが確定

95 of 106

View アクション 続き

最短距離WiFiSpot情報を配列に格納

$min = array('min_index' => $min_index, 'min_distance' => $min_distance, 'min_latitude' => $min_latitude, 'min_longitude' => $min_longitude, 'min_name' => $min_name);

//minとplaceという変数でViewに引き渡す

$this->set('min', $min);

$this->set('place', $place);

96 of 106

Viewアクション全体

97 of 106

$vi View/Place/view.ctp

1行目

<?php echo $this->Html->script('http://maps.google.com/maps/api/js?sensor=true', false); ?>

98 of 106

Place->view.ctp

地図を表示

<?php

$latitude = $place['Place']['latitude'];

$longitude = $place['Place']['longitude'];

$windowText = $place['Place']['name'];

$map_options = array(

'id' => 'map1',

'width' => '1200px',

'height' => '800px',

'zoom' => 13,

'type' => 'ROAD',

//'custom' => null,

'localize' => false,

'latitude' => $latitude,

'longitude' => $longitude,

'marker' => true,

'markerIcon' => 'http://google-maps-icons.googlecode.com/files/home.png',

'markerShadow' => 'http://google-maps-icons.googlecode.com/files/shadow.png',

'infoWindow' => true,

'windowText' => $windowText

);

?>

<div id="map">

<?php echo $this->GoogleMap->map($map_options); ?>

</div>

$vi View/Place/view.ctp

基本情報の下、アクションの上に地図を表示

99 of 106

マーカー追加

<?php

$marker_options = array(

'showWindow' => true,

'windowText' =>$min['min_name'].":".$min['min_distance']."m",

'markerTitle' => 'Title',

'markerIcon' => 'http://chart.apis.google.com/chart?chst=d_map_pin_letter&chld='.$min['min_index'].'|ff7e73|000000',

'markerShadow' => 'http://labs.google.com/ridefinder/images/mm_20_purpleshadow.png'

);

echo $this->GoogleMap->addMarker('map1', 1, array('latitude' => $min['min_latitude'], 'longitude' =>$min['min_longitude'] ),$marker_options);

?>

100 of 106

ルート表示

<div id="directions01">

<?php

echo $this->GoogleMap->getDirections("map1", "directions1", array(

"from" => array("latitude" => $place['Place']['latitude'], "longitude" => $place['Place']['longitude']),

"to" => array("latitude" => $min['min_latitude'], "longitude" => $min['min_longitude'])

), array(

"travelMode" => "DRIVING",

"directionsDiv" => "directions01",

));

?>

</div>

101 of 106

完成

地図上にマーカ、ルート案内を出す

http://j11000.sangi01.net/opendata/places/view/1

102 of 106

課題提出について

課題0

授業でやった内容

課題1

メニュー->WiFiスポット情報->一覧

アクション->Viewにマップを追加(マーカ付き)

課題2

メニュー->現在地とスポットの距離時間->一覧

アクション->Viewにマップを追加(マーカ、ルート付き)

103 of 106

課題1(view.ctpのみ変更でOK)

Viewをクリック

ViewにMapが表示される

104 of 106

課題2(view.ctpのみ変更でOK)

現在地とスポットの距離時間->一覧->Viewを表示

ルートが表示される

105 of 106

課題提出

すべての課題を実施し、7月6日(月)朝9:30までにサイボウズにURLを提出

提出URL

http://j12XXX.sangi01.net/opendata/spots

106 of 106

最終課題テーマ提出

[最終課題内容提出]

タイトル:******を利用したXXXXXXXXXXXサイトの構築

(********はオープンデータ名、XXXXXXXXXXXは求める内容)

利用データURL:

概要:200字程度

提出期限 7月6日11:00まで

サイボウズに投稿すること