GridDBを使用したニューヨークの犯罪データの地理空間分析

GridDB Community Edition 4.1以降のバージョンは、開発者がアプリケーションで時系列分析と地理空間分析の両方を組み合わせることができるGeometryデータタイプを備えています。 以前に GridDBでのジオメトリ値の使用と、ジオメトリデータを活用したアプリケーションのブログで、GridDBのジオメトリ機能について説明しました。

このブログでは、ニューヨーク市の過去の犯罪の通報データを用いて、Geometryデータタイプの実用的な使い方を説明します。 このデータで提供される犯罪レポートの緯度と経度を使って、GridDBで通報が発生したエリアを特定します。ニューヨーク市のオープンデータから犯罪データを取り込む方法を示した後、セントラルパークにおける犯罪件数が月ごとにどのように変わるかを調べます。また、個々の地区の通報件数を確認して、外部ポリゴンデータを読み込みます。

空間データポイントでGridDBのGeometryデータタイプを使用する主な目的は、ポイント、ポリライン(パス)、またはポリゴン(エリア)が交差する場所を検索できるようにすることです。 ポイント、ポリライン、およびポリゴンは、地図上のベクトルジオメトリオブジェクトを定義するマークアップ言語であるWell-known-text (WKT)を使用して定義されます。

データ取り込み

過去の犯罪データはニューヨーク市オープンデータから取得しました。 次の表は、CSVデータフィールドとGridDBスキーマの両方を示しています。

CSV Values GridDB Schema
  • CMPLNT_NUM (Unique ID)
  • CMPLNT_FR_DT (Complaint Date)
  • CMPLNT_FR_TM (Complaint Time)
  • CMPLNT_TO_DT
  • CMPLNT_TO_TM
  • ADDR_PCT_CD
  • RPT_DT
  • KY_CD
  • OFNS_DESC
  • PD_CD
  • PD_DESC
  • CRM_ATPT_CPTD_CD
  • LAW_CAT_CD
  • BORO_NM
  • LOC_OF_OCCUR_DESC
  • PREM_TYP_DESC
  • JURIS_DESC
  • JURISDICTION_CODE
  • PARKS_NM
  • HADEVELOPT
  • HOUSING_PSA
  • X_COORD_CD
  • Y_COORD_CD
  • SUSP_AGE_GROUP
  • SUSP_RACE
  • SUSP_SEX
  • TRANSIT_DISTRICT
  • Latitude (Floating Point Latitude)
  • Longitude (Floating Point Longitude)
  • Lat_Lon (Lat Lon WKT)
  • PATROL_BORO
  • STATION_NAME
  • VIC_AGE_GROUP
  • VIC_RACE
  • VIC_SEX
public class Complaint {
    int CMPLNT_NUM;
    Date CMPLNT_FR_DT;
    Date CMPLNT_TO_DT;
    int ADDR_PCT_CD;
    Date RPT_DT;
    int KY_CD;
    String OFNS_DESC;
    int PD_CD;
    String PD_DESC;
    String CRM_ATPT_CPTD_CD;
    String LAW_CAT_CD;
    String BORO_NM;
    String LOC_OF_OCCUR_DESC;
    String PREM_TYP_DESC;
    String JURIS_DESC;
    int JURISDICTION_CODE;
    String PARKS_NM;
    String HADEVELOPT;
    String HOUSING_PSA;
    int X_COORD_CD;
    int Y_COORD_CD;
    String SUSP_AGE_GROUP;
    String SUSP_RACE;
    String SUSP_SEX;
    int TRANSIT_DISTRICT;
    float Latitude;
    float Longitude;
    Geometry Lat_Lon;
    String PATROL_BORO;
    String STATION_NAME;
    String VIC_AGE_GROUP;
    String VIC_RACE;
    String VIC_SEX;
}

簡単にするために、データを複数のコンテナーに分割するのではなく、1つのコンテナーのみを使用しています。

CSVParserライブラリを使用してCSVを簡単に解析します。

   Iterable records = CSVFormat.RFC4180.withFirstRecordAsHeader().parse(in);
   for (CSVRecord record : records) {
        Complaint c = parseCsvRecord(record);
        if(c != null)
            col.put(c);
   }
   col.commit();

parseCsvRecord関数内でいくつかデータの変更をします。 まず、通報の時間の形式は標準設定されていませんが、簡単に解析できるようMM/DD/YYYYおよびHH:MM:SSの形式に変更します。

String dt[] = r.get("CMPLNT_FR_DT").split("/");
String tm[] = r.get("CMPLNT_FR_TM").split(":");
c.CMPLNT_FR_DT = new Date(Integer.parseInt(dt[2])-1900, Integer.parseInt(dt[0])-1, Integer.parseInt(dt[1]), Integer.parseInt(tm[0]), Integer.parseInt(tm[1]), Integer.parseInt(tm[2]));

未加工のCSVには、「緯度経度」で犯罪が発生したポイントのWKTテキストが含まれていますが、受け入れられるWKT形式はPOINT(x y)であり、緯度はY軸を示し、経度はX軸なので反転します。

c.Lat_Lon =   Geometry.valueOf("POINT("+c.Longitude+" "+c.Latitude+")");

各管区内の犯罪件数

ニューヨーク市のオープンデータは、こちらから入手可能な個々の警察管区のWKTポリゴンも提供しています。 通報データと同様に、CSVParserを使用して簡単にロードできますが、各地区は複数のポリゴンで構成され、WKT MULTIPOLYGONタイプを使用しているため、MULTIPOLYGONを単純なPOLYGONに分割するにはさらに処理が必要です。

String polys[] = record.get("the_geom").split("\\),");
int count=0;
for(int i=0; i < polys.length; i++) {
    String subpoly = polys[i].replace("MULTIPOLYGON (", "").replace(")))", ")");
    query = col.query("select * where ST_MBRIntersects(Lat_Lon, ST_GeomFromText('POLYGON"+subpoly+")') )");
    rs = query.fetch(false);
    count =+ rs.size();
}

結果は次のとおりです。

第1地区 : 243         第52地区 : 888
第5地区 : 177         第60地区 : 185
第6地区 : 216         第61地区 : n/a
第71地区 : 227        第62地区 : 210
第72地区 : 262        第63地区 : 324
第7地区 : 132         第66地区 : 261
第9地区 : 233         第68地区 : 233
第22地区 : 345        第69地区 : 220
第10地区 : 203        第70地区 : 369
第13地区 : 400        第76地区 : 135
第14地区 : 428        第77地区 : 334
第17地区 : 174        第78地区 : 211
第20地区 : 132        第81地区 : 12
第18地区 : 379        第83地区 : 498
第19地区 : 225        第84地区 : 175
第23地区 : 225        第88地区 : 174
第24地区 : 147        第90地区 : 290
第25地区 : 336        第94地区 : 102
第79地区 : 266        第100地区 : 8
第26地区 : 217        第101地区 : 0
第28地区 : 213        第102地区 : 283
第30地区 : 206        第103地区 : 367
第32地区 : 361        第104地区 : 511
第73地区 : 410        第105地区 : 481
第33地区 : 152        第106地区 : 227
第34地区 : 224        第107地区 : 294
第75地区 : 529        第108地区 : 262
第40地区 : 444        第109地区 : 299
第41地区 : 304        第110地区 : 431
第42地区 : 487        第111地区 : 138
第43地区 : 408        第112地区 : 178
第48地区 : 495        第113地区 : n/a
第44地区 : 559        第114地区 : 28
第45地区 : 323        第115地区 : 246
第46地区 : 400        第120地区 : 228
第47地区 : 441        第121地区 : 194
第49地区 : 267        第122地区 : 217
第50地区 : 219        第123地区 : 83
第67地区 : 504

セントラルパーク

まず最初の地理空間分析として、セントラルパークでの犯罪件数を月ごとに調べ、犯罪の通報件数が気温に応じて増減するかどうかを確認します。 セントラルパークは南北軸に位置合わせされていないため、基本的な地理空間分析クエリのように境界ボックス(where lat > min && lat < max && lon > min && lon < max) を単純に使用することはできません。

代わりに、セントラルパークの各コーナーのポリゴンを作成し、そのポリゴンと交差する犯罪の通報件数を照会します。
セントラルパークのポイントを見つけてWKTオブジェクトを構築するには、Wicketを使うと便利です。

Using Wicket To Build Geospatial Analysis Queries

これで、TQLクエリを作成できるようになりました。

String CentralParkWKT = "POLYGON((-73.97308900174315 40.764422448981996,-73.98192956265623 40.76812781417226,-73.9584064734938 40.80087951931638,-73.94982340464614 40.797240957024385,-73.97308900174315 40.764422448981996))";

for(int month=0; month <= 11; month++) {
    int count=0;
    for (int year=108; year <= 118; year++) {
        Date start = new Date(year, month, 1);
        Date end = new Date(year, month+1, 1);

	Query query = col.query("select * where ST_MBRIntersects(Lat_Lon, ST_GeomFromText('"+CentralParkWKT+"')) and CMPLNT_FR_DT >= TO_TIMESTAMP_MS("+start.getTime()+")  and CMPLNT_FR_DT < TO_TIMESTAMP_MS("+ end.getTime()+") ");
        RowSet rs = query.fetch(false);
        count += rs.size(); 
    }
    System.out.println(month+": "+count);
}

では、先ほどの質問、気温が変化に伴い犯罪の件数は変化するか、の答えはどうだったでしょうか?

1月: 30
2月: 23
3月: 36
4月: 33
5月: 29
6月: 19
7月: 26
8月: 49
9月: 25
10月: 23
11月: 26
12月: 18

12月と8月の数字はこの仮説を支持しますが、1月と6月の数字は支持しないため、この仮説は事実ではないことが分かりました。

データをより詳しく見たり、完全なコードを確認したりする場合は、こちらからデータを入手することができます。

ブログの内容について疑問や質問がある場合は Q&A サイトである Stack Overflow に質問を投稿しましょう。 GridDB 開発者やエンジニアから速やかな回答が得られるようにするためにも "griddb" タグをつけることをお忘れなく。 https://stackoverflow.com/questions/ask?tags=griddb

Leave a Reply

Your email address will not be published. Required fields are marked *