初めに
こんにちは、株式会社BFT名古屋支店新人エンジニアのないとうです。
今回はAPI GatewayとLambdaを用いて、DynamoDBから情報を取得する方法について紹介したいと思います。
前提条件
・DynamoDBにデータが保存されている
・DynamoDBのデータを取得できるLambda実行ロールが作成されている
システムについて
システム概要
今回使用するDynamoDBのテーブルはパーティションキーを画像ファイル名、ソートキーを日付、それ以外のデータとして緯度、経度、画像サイズ(縦)、画像サイズ(横)、画像の状態が保存されています。
LambdaはPython3.9で作成していきます。
そしてシステムの要件として、
①座標範囲を入力してLambdaに送信する
②座標範囲内のデータをDBから取得する
③近い座標のデータは1つにまとめる
以上の3つがあります。
①座標範囲を入力してLambdaに送信する
①を実装する方法としてgetメソッドを用いることとしました。
getメソッドは単純なもので【URL】?【変数名】=【値】と記載することで?以降の内容を送信することができる通信方式です。
今回は【変数名】を【minlat】,【minlng】,【maxlat】,【maxlng】として【値】を入力することでLambdaに情報を送信します。
Lambdaに送信された情報はPythonの場合、以下のようにevent['queryStringParameters']配列で受け取ります。
minlat = Decimal(event['queryStringParameters']['minlat']) maxlat = Decimal(event['queryStringParameters']['maxlat']) minlng = Decimal(event['queryStringParameters']['minlng']) maxlng = Decimal(event['queryStringParameters']['maxlng'])
getメソッドの場合文字列で送信されるので、数値変換もデータを受け取った時に行っています。
②座標範囲内のデータをDBから取得する
DynamoDBのテーブルからデータを取得する方法としてPythonでは、getitem,scan,queryがあります。
今回は範囲内のデータを全て取得する必要があるので、データの全取得ができるscanを用いることに決定しました。
以下のようにbetweenで緯度(lat)と経度(lng)でデータを絞り込み、取得します。
resp = table.scan(FilterExpression=Key('lat').between(minlat,maxlat) & Key('lng').between(minlng,maxlng) )
③近い座標のデータは1つにまとめる
google mapsでは最大ズームにした際のスケールバーが5mです。
ですので、今回1つにまとめるデータの範囲は直径5mほどの円にしたいと考えています。
日本のある北緯35度近辺で経度が1度ずれた場合に約90kmほど移動するので、
5mの範囲とするために度を範囲としました。
範囲内のデータについては以下のようにまとめるようにしています。
1.DBから取得したデータは辞書型として{Key1:[data1],Key2:[data2],・・・}の形としてリストに追加する
2.範囲内だった場合はそれぞれのKeyごとのリストにdataを追加する
3.範囲外だった場合は1と同様にデータをリストに追加する
4.DBからデータが取得できない場合は空のリストを返す
例(data1とdata2が範囲内に入っており、data3が範囲外だった場合) [ { Key1:[data1,data2], Key2:[data1,data2], ・ ・ ・ }, { Key1:[data3], Key2:[data3], ・ ・ ・ } ]
このようなデータ構造にするためには次のようプログラムを作る必要があります。
item=resp["Items"] if(len(item)>=1): result=[] data={'filename':[item[0]['filename']],'time':[item[0]['time']],'lat':[str(item[0]['lat'])],'lng':[str(item[0]['lng'])],'status':[item[0]['status']],'w':[str(item[0]['w'])],'h':[str(item[0]['h'])]} result.append(data) i=1 j=0 while i < len(item): while j < len(result): if float(result[j]['lat'][0])-0.00003<item[i]['lat']<float(result[j]['lat'][0])+0.00003 and float(result[j]['lng'][0])-0.00003<item[i]['lng']<float(result[j]['lng'][0])+0.00003: result[j]['filename'].append(item[i]['filename']) result[j]['time'].append(item[i]['time']) result[j]['lat'].append(str(item[i]['lat'])) result[j]['lng'].append(str(item[i]['lng'])) result[j]['status'].append(item[i]['status']) result[j]['w'].append(str(item[i]['w'])) result[j]['h'].append(str(item[i]['h'])) j=0 break j=j+1 if j==len(result): data={'filename':[item[i]['filename']],'time':[item[i]['time']],'lat':[str(item[i]['lat'])],'lng':[str(item[i]['lng'])],'status':[item[i]['status']],'w':[str(item[i]['w'])],'h':[str(item[i]['h'])]} result.append(data) j=0 break i=i+1 else: result=[]
システムの実装
システム構成
今回作成するシステムは次のような手順で動作するようにします。
①座標範囲を入力してAPI Gatewayにアクセスする
②API GatewayがLambdaを呼び出す
③Lambdaが座標範囲に応じたデータを取得する
④API GatewayがLambdaのデータを出力する
Lambdaの作成
1.【1から作成】を選択し【関数名】を入力、【ランタイム】に「Python3.9」を選択する。
2.Lambdaの実行ロールをアタッチして関数を作成する。
3.プログラム作成
Lambdaのプログラムを以下のように作成する。
import json import boto3 from boto3.dynamodb.conditions import Key from decimal import Decimal TABLE_NAME='CDN_DIS_EXIFdata' dynamodb = boto3.resource('dynamodb') def lambda_handler(event, context): table = dynamodb.Table(TABLE_NAME) #①座標範囲を入力してLambdaに送信する minlat = Decimal(event['queryStringParameters']['minlat']) maxlat = Decimal(event['queryStringParameters']['maxlat']) minlng = Decimal(event['queryStringParameters']['minlng']) maxlng = Decimal(event['queryStringParameters']['maxlng']) #②座標範囲内のデータをDBから取得する resp = table.scan(FilterExpression=Key('lat').between(minlat,maxlat) & Key('lng').between(minlng,maxlng) ) item=resp["Items"] #③近い座標のデータは1つにまとめる if(len(item)>=1): result=[] data={'filename':[item[0]['filename']],'time':[item[0]['time']],'lat':[str(item[0]['lat'])],'lng':[str(item[0]['lng'])],'status':[item[0]['status']],'w':[str(item[0]['w'])],'h':[str(item[0]['h'])]} result.append(data) i=1 j=0 while i < len(item): while j < len(result): if float(result[j]['lat'][0])-0.00003<item[i]['lat']<float(result[j]['lat'][0])+0.00003 and float(result[j]['lng'][0])-0.00003<item[i]['lng']<float(result[j]['lng'][0])+0.00003: result[j]['filename'].append(item[i]['filename']) result[j]['time'].append(item[i]['time']) result[j]['lat'].append(str(item[i]['lat'])) result[j]['lng'].append(str(item[i]['lng'])) result[j]['status'].append(item[i]['status']) result[j]['w'].append(str(item[i]['w'])) result[j]['h'].append(str(item[i]['h'])) j=0 break j=j+1 if j==len(result): data={'filename':[item[i]['filename']],'time':[item[i]['time']],'lat':[str(item[i]['lat'])],'lng':[str(item[i]['lng'])],'status':[item[i]['status']],'w':[str(item[i]['w'])],'h':[str(item[i]['h'])]} result.append(data) j=0 break i=i+1 else: result=[] return{ 'statusCode': 200, 'body': json.dumps(result) }
API Gatewayの作成と設定
1.REST APIの【構築】をクリックしAPIを作成する。
2.getメソッドを作成し以下の設定値を設定し保存する
【統合タイプ】:「Lambda関数」
【Lambdaプロシキ統合を使用】:チェック
【Lambdaリージョン】:「ap-northeast-1」
【Lambda関数名】:「先ほど作成したLambda関数名」
【デフォルトタイムアウトを使用】:チェック
3.【メソッドレスポンス】の【200のレスポンス本文】に「application/json」を追加する
4.APIをデプロイする
機能の確認
ブラウザで以下のように入力しデータが出力されることが確認されれば完了
【APIのURL】?minlat=【数値】,minlng=【数値】,maxlat=【数値】,maxlng=【数値】
終わりに
今回はDynamo DBから情報を取得する方法について紹介しました。
scanを用いてデータを選別しましたが、他にも方法はあると思うので調べてみたいと思います。
LambdaとAPI Gatewayの作成について詳しくはこちら→【AWS】【API Gateway】【Lambda】API GatewayとLambdaでS3の画像を表示する - BFT名古屋 TECH BLOG
それでは。
おまけ
GPSの誤差について
GPSの誤差は10mほどだといわれています。
しかし、SONYのホームページを見てみると
「受信状態が良好の状態で約2m程度の誤差が、受信状態が悪い状態では数百mの誤差が発生する場合があります。」
と記載があり、かなり誤差が発生する可能性があるのでGPSの座標を使う場合は注意が必要です。