生成AI観光ガイド地図アプリ
このチュートリアルでは、地図の中心周辺の観光名所をGoogle Gemini APIに問い合わせて地図上に表示し、マーカーをクリックするとその場所の説明を表示するアプリを作ります。
生成AI観光名所地図アプリでは観光名所をGeminiで検索してその場所を地図上にマーカーで表示しましたが、結構知らない場所も表示されるので、どんなところかわかると便利だろうと思い、マーカーをクリックするとその名前と住所に加えてその場所の説明も表示するようにしました。
この機能追加のために、アプリ本体とGASスクリプトを変更しています。
Google Gemini APIの呼び出しにはGoogle Apps Script(GAS)を使います。
Google AI Studio で作るGemini APIは課金方法を登録しなくても以下の範囲ならば無料で使えます。
- 1 分あたりのリクエスト数: 5
- 1 日あたりのリクエスト数: 250,000
- 1 分あたりのトークン数(入力): 100
Google Apps Scriptの設定
Google Apps Scriptに設定方法などはここを参照してください。
GASスクリプトの変更
200文字以内の説明も返すようにマニュアルでスクリプトの中のプロンプトを変更します。
観光地検索API (Google Apps Script)
/**
* App InventorからのGETリクエストを処理するメイン関数
* @param {object} e - App Inventorから渡されるイベントオブジェクト
* @returns {ContentService.TextOutput} - 観光地情報のJSONまたはエラーメッセージ
*/
function doGet(e) {
// レスポンスのMIMEタイプをJSONに設定
const response = ContentService.createTextOutput();
response.setMimeType(ContentService.MimeType.JSON);
try {
// クエリパラメータから緯度、経度、半径を取得
const lat = e.parameter.latitude;
const lon = e.parameter.longitude;
const radius = e.parameter.radius;
// パラメータが不足している場合はエラーを返す
if (!lat || !lon || !radius) {
response.setContent(JSON.stringify({
error: "パラメータが不足しています。'latitude', 'longitude', 'radius'が必要です。"
}));
return response;
}
// Gemini APIを呼び出して観光地情報を取得
const touristSpots = getTouristSpots(lat, lon, radius);
// 取得した情報をJSON形式で返す
response.setContent(JSON.stringify(touristSpots));
} catch (error) {
// エラーが発生した場合、エラーメッセージをJSONで返す
response.setContent(JSON.stringify({
error: "データの取得中にエラーが発生しました。",
details: error.message
}));
}
return response;
}
/**
* Gemini APIを呼び出して、指定された座標周辺の観光地情報を取得する
* @param {number} latitude - 中心の緯度
* @param {number} longitude - 中心の経度
* @param {number} radius - 検索半径(メートル)
* @returns {Array<Object>} - 観光地のリスト
*/
function getTouristSpots(latitude, longitude, radius) {
// スクリプトプロパティからAPIキーを読み込む
const API_KEY = PropertiesService.getScriptProperties().getProperty('GEMINI_API_KEY');
if (!API_KEY) {
throw new Error("APIキーがスクリプトプロパティに設定されていません。");
}
const API_URL = `https://generativelanguage.googleapis.com/v1beta/models/gemini-2.5-flash:generateContent?key=${API_KEY}`;
// Geminiに渡すプロンプト(指示文)- 緯度経度が不要なことを明記
const prompt = `
あなたは優秀なローカルツアーガイドです。
以下の中心座標と半径の範囲内にある観光地を最大5件まで探してください。
結果は必ず指定されたJSON形式で、名前と住所と200文字以内の説明のみを含めてください。緯度と経度は不要です。
中心緯度: ${latitude}
中心経度: ${longitude}
半径: ${radius}メートル
`;
// Gemini APIに送信するデータ(ペイロード)- responseSchemaから緯度経度を削除
const payload = {
"contents": [{
"parts": [{
"text": prompt
}]
}],
"generationConfig": {
"responseMimeType": "application/json",
"responseSchema": {
"type": "ARRAY",
"items": {
"type": "OBJECT",
"properties": {
"name": { "type": "STRING", "description": "観光地の名前" },
"address": { "type": "STRING", "description": "観光地の住所" },
"desc": { "type": "STRING", "description": "観光地の説明" }
},
"required": ["name", "address"]
}
}
}
};
// APIリクエストのオプション
const options = {
'method': 'post',
'contentType': 'application/json',
'payload': JSON.stringify(payload),
'muteHttpExceptions': true // HTTPエラー時もレスポンスを返す
};
// APIを呼び出す
const response = UrlFetchApp.fetch(API_URL, options);
const responseCode = response.getResponseCode();
const responseBody = response.getContentText();
if (responseCode !== 200) {
throw new Error(`APIリクエストに失敗しました。ステータスコード: ${responseCode}, レスポンス: ${responseBody}`);
}
// レスポンスからJSON部分を抽出してパースする
const result = JSON.parse(responseBody);
const jsonText = result.candidates[0].content.parts[0].text;
return JSON.parse(jsonText);
}
App Inventorアプリ
ページの最後にあるダウンロードセクションからソースコードをダウンロードできます。
[プロジェクト]メニューから[新規プロジェクトを始める]を選択し、"AITravelGuide"と名前を付けます。