こちらの記事で行政区域データを、行政区域コードや都道府県でまとめる作業を行いました。以下に、追加で確認したことを書き留めておきます。
SDO_UTIL.RECTIFY_GEOMETRYの結果
GeoJSONのデータを表JAR_JAPAN_REGION_ADMINSの列GEOMETRYに取り込んだ後に、SDO_UTIL.RECTIFY_GEOMETRYを実行しています。修正したジオメトリは列GEOM_Rへアップデートしています。
列GEOM_Rを確認すると、ジオメトリのSDO_GTYPEに2003(ポリゴン)だけではなく、2007(マルチポリゴン)も含まれています。確認に使用したコードは以下です。
set serveroutput on declare l_geom sdo_geometry; l_elem_cnt number; begin for c in ( select * from jar_japan_region_admins where geom_r is not null order by id ) loop l_geom := c.geom_r; l_elem_cnt := sdo_util.getnumelem(l_geom); if l_geom.sdo_gtype <> 2003 then dbms_output.put_line('id ' || c.id || ' is ' || l_geom.sdo_gtype || ', contains ' || l_elem_cnt || ' elements.'); end if; end loop; end; /
実行結果は以下のようになりました。
id 922 is 2007, contains 2 elements.
id 67318 is 2007, contains 2 elements.
id 78966 is 2007, contains 2 elements.
id 83266 is 2007, contains 2 elements.
id 94185 is 2007, contains 2 elements.
id 99633 is 2007, contains 2 elements.
id 100483 is 2007, contains 2 elements.
id 100866 is 2007, contains 2 elements.
id 101181 is 2007, contains 2 elements.
このままSDO_UTIL.SIMPLIFYを実行すると、含まれているポリゴンが非常に小さい場合、そのポリゴンが点または線のジオメトリに変換されます。結果としてOracle APEXのマップ・リージョンで表示されないジオメトリになります。
マルチポリゴンのジオメトリを分解し、別のIDを割り振って保存します。実行には以下のコードを使いました。
set serveroutput on declare l_geom sdo_geometry; l_elem_cnt number; l_geom_in sdo_geometry; l_max number; begin for c in ( select * from jar_japan_region_admins where geom_r is not null order by id ) loop -- rectifyされたジオメトリが処理の対象。 l_geom := c.geom_r; -- if l_geom.sdo_gtype in (2004,2007) then -- コレクション(2004)とマルチポリゴンが(2007)が修正の対象。 dbms_output.put_line('id ' || c.id || ' was converted into collection(2004) or multipolygon(2007). try to fix.'); l_elem_cnt := sdo_util.getnumelem(l_geom); select max(id) into l_max from jar_japan_region_admins; for i in 1..l_elem_cnt loop l_geom_in := sdo_util.extract(l_geom,i); if l_geom_in.sdo_gtype = 2003 then -- 含まれているポリゴンを新たなIDで追加する。 dbms_output.put_line('store inner polygon with new id ' || (l_max+i)); insert into jar_japan_region_admins( id, prefecture_name, branch_in_hokkaido, major_city, city_name, admin_code, geom_r, vertices) values( l_max+i, c.prefecture_name, c.branch_in_hokkaido, c.major_city, c.city_name, c.admin_code, l_geom_in, sdo_util.getnumvertices(l_geom_in)); else -- それ以外は削除される。 dbms_output.put_line('ignore inner geometry. ' || l_geom_in.sdo_gtype); end if; end loop; -- 元々のジオメトリは重複したデータになるためNULLにする。 dbms_output.put_line('nullify original rectified geometry.'); update jar_japan_region_admins set geom_r = null, vertices = null where id = c.id; end if; end loop; end; / commit;
実行結果は以下のようになりました。
id 922 was converted into collection(2004) or multipolygon(2007). try to fix.
store inner polygon with new id 121159
store inner polygon with new id 121160
nullify original rectified geometry.
id 67318 was converted into collection(2004) or multipolygon(2007). try to fix.
store inner polygon with new id 121161
store inner polygon with new id 121162
nullify original rectified geometry.
id 78966 was converted into collection(2004) or multipolygon(2007). try to fix.
store inner polygon with new id 121163
store inner polygon with new id 121164
nullify original rectified geometry.
id 83266 was converted into collection(2004) or multipolygon(2007). try to fix.
store inner polygon with new id 121165
store inner polygon with new id 121166
nullify original rectified geometry.
id 94185 was converted into collection(2004) or multipolygon(2007). try to fix.
store inner polygon with new id 121167
store inner polygon with new id 121168
nullify original rectified geometry.
id 99633 was converted into collection(2004) or multipolygon(2007). try to fix.
store inner polygon with new id 121169
store inner polygon with new id 121170
nullify original rectified geometry.
id 100483 was converted into collection(2004) or multipolygon(2007). try to fix.
store inner polygon with new id 121171
store inner polygon with new id 121172
nullify original rectified geometry.
id 100866 was converted into collection(2004) or multipolygon(2007). try to fix.
store inner polygon with new id 121173
store inner polygon with new id 121174
nullify original rectified geometry.
id 101181 was converted into collection(2004) or multipolygon(2007). try to fix.
store inner polygon with new id 121175
store inner polygon with new id 121176
nullify original rectified geometry.
含まれているジオメトリはすべて、表JAR_JAPAN_ADMIN_REGIONSに追記できました。
SDO_UTIL.SIMPLIFYの結果
SDO_UTIL.RECTIFY_GEOMETRYの結果を対象として、SDO_UTIL.SIMPLIFYを実行します。閾値が10m、許容差として0.05(5cm)を指定しています。
update jar_japan_region_admins set geom_s = sdo_util.simplify(geom_r,10,0.05); update jar_japan_region_admins set vert_s = sdo_util.getnumvertices(geom_s); commit;
列GEOM_Rに含まれているポリゴンが小さいと、結果として点(SDO_GTYPEが2001)、または線(SDO_GTYPEが2002)になります。この時点でポリゴンではなくなるため、これ以降の処理の対象にする必要がありません。また、SDO_UTIL.SIMPLIFYでもコレクション(2004)やマルチポリゴン(2007)となる場合があります。
以下のコードでジオメトリの確認を行います。
set serveroutput on declare l_geom sdo_geometry; l_elem_cnt number; l_point_cnt integer := 0; l_line_cnt integer := 0; begin for c in ( select * from jar_japan_region_admins where geom_s is not null order by id ) loop l_geom := c.geom_s; l_elem_cnt := sdo_util.getnumelem(l_geom); if l_geom.sdo_gtype = 2001 then l_point_cnt := l_point_cnt + 1; elsif l_geom.sdo_gtype = 2002 then l_line_cnt := l_line_cnt + 1; elsif l_geom.sdo_gtype = 2003 then -- polygon is valid continue; else dbms_output.put_line('id ' || c.id || ' is ' || l_geom.sdo_gtype || '. contains ' || l_elem_cnt || ' elements.'); end if; end loop; dbms_output.put_line('number of point exists. ' || l_point_cnt); dbms_output.put_line('number of line exists. ' || l_line_cnt); end; /
実行結果は以下のようになりました。
id 658 is 2007. contains 2 elements.
id 789 is 2007. contains 2 elements.
id 1286 is 2007. contains 4 elements.
[中略]
id 117307 is 2007. contains 3 elements.
id 119059 is 2004. contains 2 elements.
id 121174 is 2007. contains 2 elements.
number of point exists. 50702
number of line exists. 40409
多くのデータがコレクションまたはマルチポリゴンに変換されています。また、点や線になったポリゴンも多数あります。
列GEOM_Sで点や線になっているジオメトリをNULLに置き換えます。また、コレクションやマルチポリゴンは分解して、新たなIDで表JAR_JAPAN_REGION_ADMINSに保存します。実行のために以下のコードを使用しました。
set serveroutput on declare l_geom sdo_geometry; l_elem_cnt number; l_geom_in sdo_geometry; l_max number; begin for c in ( select * from jar_japan_region_admins where geom_s is not null order by id ) loop -- simplifyされたジオメトリが処理の対象。 l_geom := c.geom_s; -- if l_geom.sdo_gtype in (2001,2002) then /* 大量にあるので表示しない。 dbms_output.put_line('id ' || c.id || ' is converted into point(2001) or line(2002). nullfiy.'); */ update jar_japan_region_admins set geom_s = null, vert_s = null where id = c.id; elsif l_geom.sdo_gtype in (2004,2007) then -- コレクション(2004)とマルチポリゴンが(2007)が修正の対象。 dbms_output.put_line('id ' || c.id || ' was converted into collection(2004) or multipolygon(2007). try to fix.'); l_elem_cnt := sdo_util.getnumelem(l_geom); select max(id) into l_max from jar_japan_region_admins; for i in 1..l_elem_cnt loop l_geom_in := sdo_util.extract(l_geom,i); if l_geom_in.sdo_gtype in (2001,2002) then -- 点と線は削除する。 dbms_output.put_line('ignore inner geometry. ' || l_geom_in.sdo_gtype); else -- それ以外は追加する。 dbms_output.put_line('store inner polygon with new id ' || (l_max+i)); insert into jar_japan_region_admins( id, prefecture_name, branch_in_hokkaido, major_city, city_name, admin_code, geom_s, vert_s) values( l_max+i, c.prefecture_name, c.branch_in_hokkaido, c.major_city, c.city_name, c.admin_code, l_geom_in, sdo_util.getnumvertices(l_geom_in)); end if; end loop; -- 元々のジオメトリはNULLにする。 dbms_output.put_line('nullify original simplified geometry.'); update jar_japan_region_admins set geom_s = null, vert_s = null where id = c.id; end if; end loop; end; / commit;
再度、列GEOM_Sのチェックスクリプトを実行すると以下の結果になりました。
number of point exists. 0
number of line exists. 0
列GEOM_Sには、1行につき1つの単純なポリゴンが含まれています。
行政区域コード単位での集計
列GEOM_Sに含まれていた集計対象にならないジオメトリは、すでにNULLになっています。そのため面積を計算して、集計対象を制限する必要はありません。列GEOM_SがNULLの行を集計対象から外します。
MERGE文は以下になります。
merge into jar_japan_region_cities c using ( select prefecture_name, branch_in_hokkaido, major_city, city_name ,admin_code, geom_s geometry, vert_s vertices from jar_japan_region_admins where geom_s is not null and (prefecture_name, admin_code) in ( select prefecture_name, admin_code from jar_japan_region_admins group by prefecture_name, admin_code having count(*) = 1 ) ) a on (c.prefecture_name = a.prefecture_name and c.admin_code = a.admin_code) when matched then update set branch_in_hokkaido = a.branch_in_hokkaido ,major_city = a.major_city ,city_name = a.city_name ,geometry = a.geometry ,row_count = 1 ,vertices = a.vertices when not matched then insert (prefecture_name, branch_in_hokkaido, major_city, city_name ,admin_code, geometry, row_count, vertices) values (a.prefecture_name, a.branch_in_hokkaido, a.major_city, a.city_name ,a.admin_code, a.geometry, 1, a.vertices) ;
2つ以上のデータがある行政区域コードをまとめるために使用するコードは以下になります。
declare l_geom sdo_geometry; l_geom_a sdo_geometry; l_row_count number; l_vertices number; l_message varchar2(4000); begin delete from jar_validate_cities; commit; for c in ( -- まとめる数が少ない行政区分からひとつひとつまとめていく。 select prefecture_name, branch_in_hokkaido, major_city, city_name, admin_code from jar_japan_region_admins where geom_s is not null and (prefecture_name, admin_code) not in ( -- すでにまとめ済みの行政区分は除外する select prefecture_name, admin_code from jar_japan_region_cities ) group by prefecture_name, branch_in_hokkaido, major_city, city_name, admin_code order by count(*) asc ) loop l_geom := null; for r in ( select prefecture_name, admin_code, geom_s geometry, vert_s vertices from jar_japan_region_admins where prefecture_name = c.prefecture_name and admin_code = c.admin_code and geom_s is not null ) loop if l_geom is null then -- 最初の1行は初期化に使用する。 l_geom := r.geometry; l_row_count := 1; l_vertices := r.vertices; continue; end if; -- ポリゴン、行数、頂点の数を集計する if sdo_geom.relate(l_geom, 'disjoint', r.geometry, 0.05) = 'TRUE' then l_geom_a := sdo_util.append(l_geom, r.geometry); else l_geom_a := sdo_geom.sdo_union(l_geom, r.geometry, 0.05); end if; l_geom := l_geom_a; l_row_count := l_row_count + 1; l_vertices := l_vertices + r.vertices; end loop; -- 結果を保存する。途中で停止できるよう毎回commitする。 l_message := sdo_geom.validate_geometry_with_context(l_geom, 0.05); insert into jar_validate_cities(prefecture_name, branch_in_hokkaido, major_city, city_name, admin_code, message) values( c.prefecture_name, c.branch_in_hokkaido, c.major_city, c.city_name, c.admin_code, l_message); insert into jar_japan_region_cities (prefecture_name, branch_in_hokkaido, major_city, city_name ,admin_code, geometry, row_count, vertices) values( c.prefecture_name, c.branch_in_hokkaido, c.major_city, c.city_name, c.admin_code, l_geom, l_row_count, l_vertices); commit; end loop; end; /
集計の対象外になったデータは、SDO_UTIL.SIMPLIFYを使って閾値10m、許容差0.05(5cm)で簡略化した結果がポリゴンにならなかったもの、ということになります。
行政区域の隙間
以下の倍率程度であれば、行政区域が接する部分に隙間は見えません。
拡大すると、行政区域の接する部分に隙間があることがわかります。
単純にSDO_UTIL.SIMPLIFYを使用して頂点を間引く方法では、このような隙間が発生することは避けられないです。
ジオメトリのエラーを見つける
ジオメトリに限りませんが、エラーが発生していると開発者ツール・バーに通知されます。
エラーのアイコンをクリックすると、ブラウザのJavaScriptコンソールを開いて、エラーを確認してください。とあります。
簡略化されたデータの比較
SQL> select sum(vertices), sum(vert_s) from jar_japan_region_admins;
SUM(VERTICES) SUM(VERT_S)
------------- -----------
14748036 6734337
SQL> select sum(vertices), sum(vert_s) from jar_japan_region_admins;
SUM(VERTICES) SUM(VERT_S)
------------- -----------
14748036 1647760
完