QGIS3.32を利用してLASからCOPCとSTAC仕様に基づいたVirtual Point Clouds（仮想点群）を生成する

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/203944/4c4aa295-3b79-7207-86fd-a61f149302f2.png)

QGIS3.32（2023/07時点最新リリース）ではクラウドファウンディングにより得た資金でLutraConsulting社が点群データ処理を大幅に改良し、超パワーアップしました！

[Native point cloud processing in QGIS](https://www.lutraconsulting.co.uk/blog/2023/05/30/pointcloud-qgis-processing/)

詳しくは上の記事を見てほしいですが、本当にいろんな機能が追加されているので、列挙します。

- LAS ⇆ LAZの変換
- グリッド化、ラスターへのエクスポート
- ベクトルデータ（ポイント）へのエクスポート
- 投影法割り当て
- **仮想点群の構築**
- ベクターレイヤーでのクリップ
- **COPCの作成**
- メタデータの表示
- 点群データのマージ
- 点群データの投影変換
- 点群データの間引き
- 点群データからのポリゴン生成
- 点群データの境界作成
- 点群データから密度ラスターの作成
- 点群データの式によるフィルタリング

恐ろしいですね。

上記機能は、元々PythonなどからOpen3D・PyntcloudやPDALを利用することで自動化することなど出来ましたが、これがGUIでサクッとできるようになったのは本当にすごいと思います。

今回は、この中で個人的に激推しである「仮想点群（VPC）の構築」と「COPCの作成」について触れていきたいと思います。

## データをダウンロード

フリーな点群データといえば「VIRTUAL SHIZUOKA」か「オープンナガサキ」が有名ですが、今回は静岡の方を使っていきましょう。

[VIRTUAL SHIZUOKA 静岡県 中・西部 点群データ](https://www.geospatial.jp/ckan/dataset/virtual-shizuoka-mw)から「LPデータ オリジナルデータ」をいくつか取得していきましょう。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/203944/dc75890a-1eeb-9164-9143-f3e9b5edd35b.png)

静岡市周辺でいくつかデータをダウンロードします。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/203944/2010464a-a945-99cf-64a3-054a3cd3a34a.png)

urlリストはこちら。

```text
https://gic-shizuoka.s3.ap-northeast-1.amazonaws.com/2022/p/LP/LAS/08nd7793.zip
https://gic-shizuoka.s3.ap-northeast-1.amazonaws.com/2022/p/LP/LAS/08nd8703.zip
https://gic-shizuoka.s3.ap-northeast-1.amazonaws.com/2022/p/LP/LAS/08nd8713.zip
https://gic-shizuoka.s3.ap-northeast-1.amazonaws.com/2022/p/LP/LAS/08nd8714.zip
https://gic-shizuoka.s3.ap-northeast-1.amazonaws.com/2022/p/LP/LAS/08nd8704.zip
https://gic-shizuoka.s3.ap-northeast-1.amazonaws.com/2022/p/LP/LAS/08nd7794.zip
https://gic-shizuoka.s3.ap-northeast-1.amazonaws.com/2022/p/LP/LAS/08nd7795.zip
https://gic-shizuoka.s3.ap-northeast-1.amazonaws.com/2022/p/LP/LAS/08nd8705.zip
https://gic-shizuoka.s3.ap-northeast-1.amazonaws.com/2022/p/LP/LAS/08nd8715.zip
```

解答すると以下のようになりました。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/203944/ad15e879-e311-d544-77ff-468c9fd8dc54.png)

280MB × 9ファイルですので、大体2.5GB程度ですね。
さほど大きくはないです。

## 投影法の設定

QGIS3.32を開き、静岡県なので、プロジェクトの投影法は事前に「EPSG:6676」に設定した上で、点群データをQGISに読み込ませておきましょう。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/203944/20f41f77-4ca0-c35a-9767-4cc45eb54bc3.png)

これだけで点群データが表示されると思いますが、点群データ自体にも投影法の設定も行ってあげましょう。

適当なレイヤのプロパティを見るとCRSが「不明」になっていると思います。
（日本で公開されているデータはほぼ全てCRSの設定がなされていません。）

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/203944/4cecf022-31e6-7c05-7b56-cc1050c4e445.png)

レイヤプロパティを開いて、CRSを「EPSG:6676」に設定してあげましょう。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/203944/18f9596c-8eb6-d807-df6a-68744b5b3f27.png)

## 仮想点群の作成

プロセシングツールボックスの検索窓にVPCと入力すると「仮想点群（VPC）を作成」が表示されると思います。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/203944/34957859-87c5-c746-9dca-0d4dd782a956.png)

レイヤを全て選択します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/203944/4bbcacb4-9029-bfe9-f90e-8561a89b18f1.png)

各種パラメータは以下のような意味です。

- 境界ポリゴンを計算: データの正確な境界を表示できるようになる
- 統計量を計算: 属性の値の範囲計算する
- 概観点群をビルド: 元のデータの1000個ごとの点のみを使用しCOPCを生成する

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/203944/cf1a7b2a-e964-0d10-0ba3-d1e8ff7cf69d.png)

今回はお試しなので、全てのオプションをオフにしておきます。
ただし、仮想点群はファイルとして出力するようにしておきましょう。

一瞬で処理が完了すると思います。

その後、作成された仮想点群にもCRSを設定しましょう。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/203944/cecc6fe9-8192-26d0-f10c-27a4c8fbfc7d.png)

現在は入力された点群データがCOPCではない場合は、点群の範囲のみが表示されるようですので、元データをCOPCに変換していきましょう。

## LASをCOPCに変換

COPCと検索すると作成アルゴリズムが表示されます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/203944/d8db2361-33e2-9ea6-514a-b5de9888fde2.png)

対象9レイヤを全て選択しましょう。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/203944/6fdd5d8e-d73f-d310-9ca0-f1ca36d17045.png)

その後、適当なフォルダを選択して「実行」します。

※それなりに時間がかかります。

作成されたCOPCを読み込んでみましょう。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/203944/4c4aa295-3b79-7207-86fd-a61f149302f2.png)

高速に描画されるようになっているはずです。
LASと同様にCRSの設定も行っていきましょう。

その後、作成したVPCをレイヤーに追加し、ズームしていくと、分類された点群データが表示されます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/203944/73374a10-ad9b-43bf-3ad6-56830d461cf1.png)

（スタイル設定などで分類する前の生データなどの表示も行えると思いますが、今回は試していません。）

## .vpcを確認してみる

最後に、生成されたデータを確認してみましょう。

```json
{
  "type": "FeatureCollection",
  "features": [
    {
      "type": "Feature",
      "stac_version": "1.0.0",
      "stac_extensions": [
        "https://stac-extensions.github.io/pointcloud/v1.0.0/schema.json",
        "https://stac-extensions.github.io/projection/v1.1.0/schema.json"
      ],
      "id": "08nd7793.copc",
      "geometry": {
        "coordinates": [
          [
            [
              -10800.0,
              -114000.0,
              9.79
            ],
            [
              -10800.0,
              -113700.0,
              9.79
            ],
            [
              -10400.0,
              -113700.0,
              109.67
            ],
            [
              -10400.0,
              -114000.0,
              109.67
            ],
            [
              -10800.0,
              -114000.0,
              9.79
            ]
          ]
        ],
        "type": "Polygon"
      },
      "bbox": [
        -10800.0,
        -114000.0,
        9.79,
        -10400.0,
        -113700.0,
        109.67
      ],
      "properties": {
        "datetime": "0001-01-01T00:00:00Z",
        "pc:count": 7787332,
        "pc:encoding": "?",
        "pc:schemas": [
          {
            "name": "X",
            "size": 8,
            "type": "floating"
          },
          {
            "name": "Y",
            "size": 8,
            "type": "floating"
          },
          {
            "name": "Z",
            "size": 8,
            "type": "floating"
          },
          {
            "name": "Intensity",
            "size": 2,
            "type": "unsigned"
          },
          {
            "name": "ReturnNumber",
            "size": 1,
            "type": "unsigned"
          },
          {
            "name": "NumberOfReturns",
            "size": 1,
            "type": "unsigned"
          },
          {
            "name": "ScanDirectionFlag",
            "size": 1,
            "type": "unsigned"
          },
          {
            "name": "EdgeOfFlightLine",
            "size": 1,
            "type": "unsigned"
          },
          {
            "name": "Classification",
            "size": 1,
            "type": "unsigned"
          },
          {
            "name": "ScanAngleRank",
            "size": 4,
            "type": "floating"
          },
          {
            "name": "UserData",
            "size": 1,
            "type": "unsigned"
          },
          {
            "name": "PointSourceId",
            "size": 2,
            "type": "unsigned"
          },
          {
            "name": "GpsTime",
            "size": 8,
            "type": "floating"
          },
          {
            "name": "ScanChannel",
            "size": 1,
            "type": "unsigned"
          },
          {
            "name": "ClassFlags",
            "size": 1,
            "type": "unsigned"
          },
          {
            "name": "Red",
            "size": 2,
            "type": "unsigned"
          },
          {
            "name": "Green",
            "size": 2,
            "type": "unsigned"
          },
          {
            "name": "Blue",
            "size": 2,
            "type": "unsigned"
          }
        ],
        "pc:type": "lidar",
        "proj:bbox": [
          -10800.0,
          -114000.0,
          9.79,
          -10400.0,
          -113700.0,
          109.67
        ],
        "proj:geometry": {
          "coordinates": [
            [
              [
                -10800.0,
                -114000.0,
                9.79
              ],
              [
                -10800.0,
                -113700.0,
                9.79
              ],
              [
                -10400.0,
                -113700.0,
                109.67
              ],
              [
                -10400.0,
                -114000.0,
                109.67
              ],
              [
                -10800.0,
                -114000.0,
                9.79
              ]
            ]
          ],
          "type": "Polygon"
        },
        "proj:wkt2": ""
      },
      "links": [],
      "assets": {
        "data": {
          "href": "./08nd7793.copc.laz",
          "roles": [
            "data"
          ]
        }
      }
    },
...
  ]
}
```

データは上記のようなGeoJSONになっています。

ただし、これはただのGeoJSONではなく、「STAC Item」という仕様に則ったGeoJSONになっています。

STACについては以下の記事などが参考になります。

- [AWSのオープンデータとPythonを使って、宇宙から地球を覗いてみよう！](https://qiita.com/nokonoko_1203/items/a9bfe50150c397da5e3e)
- [静的な STAC カタログを stac-ruby でつくる](https://qiita.com/sankichi92/items/b3376af497639319a745)
- [SpatioTemporal Asset Catalogとは](https://zenn.dev/yonda/articles/0e5188d55f8492)

## おわりに

今回はQGISでCOPCへの変換と仮想点群の生成を行っていきました！

QGISの3.32では今回紹介した以外にも以下のような点群操作ができるようになっているので、皆さん使ってみてください！