diff --git a/README.md b/README.md index 5919f56..88cf499 100644 --- a/README.md +++ b/README.md @@ -65,34 +65,36 @@ of those flags here. #### TypeScript -If you use JupyterLab to develop then you can watch the source directory and run JupyterLab at the same time in different -terminals to watch for changes in the extension's source and automatically rebuild the widget. +The TypeScript code for the visualizations can be found in the [TileDB-Viz](https://github.com/TileDB-Inc/TileDB-Viz) package. After making changes in TileDB-Viz build the package with: -```bash -# Watch the source directory in one terminal, automatically rebuilding when needed -yarn run watch -# Run JupyterLab in another terminal -jupyter lab -``` +`yarn build` -After a change wait for the build to finish and then refresh your browser and the changes should take effect. +To then see these changes in TileDB-PyBabylonJS run: -To add a TypeScript package use [yarn](https://classic.yarnpkg.com/lang/en/docs/cli/add/): +`yarn add file:/path/to/TileDB-Viz/packages/core` -```bash -yarn add -yarn add --dev -``` +`yarn build` + +And restart the notebook kernel. #### Python -If you make a change to the python code then you will need to restart the notebook kernel to have it take effect. +When you make a change to the Python code rebuild the package and restart the notebook kernel to see your changes. ## Usage -Jupyter notebooks are provided in the [Examples](https://github.com/TileDB-Inc/TileDB-PyBabylonJS/tree/main/examples). +### Point clouds + +Jupyter notebooks are provided in the [Examples folder](https://github.com/TileDB-Inc/TileDB-PyBabylonJS/tree/main/examples) for the following visualizations: -Create a default visualization from a local sparse array containing LiDAR data by specifying the bounding box (`bbox`) of the slice of the data in the array uri: +* [Point cloud](/examples/point_cloud.ipynb) +* [Point cloud with a time slider](/examples/point-cloud-time.ipynb) +* [Point cloud with a classes slider](/examples/point-cloud-classes.ipynb) +* [Point cloud with a Mapbox base map](/examples/point-cloud-topo.ipynb) +* [Point cloud with gltf models](/examples/point-cloud-gltf.ipynb) +* [MBRS of a point cloud](/examples/mbrs.ipynb) + +Display a point cloud visualization from a local sparse array by specifying the bounding box of a slice of the data: ```python from pybabylonjs import Show as show @@ -103,14 +105,13 @@ bbox = { 'Z': [406.14, 615.26] } +lidar_array = "autzen-classified" + show.point_cloud(source="local", - mode="default", - uri="./data/autzen", - bbox=bbox) + uri=lidar_array, + bbox = bbox) ``` -This creates an interactive visualization in a notebook widget of which the below is a screenshot: - To add a slider over `GpsTime` change the `mode` to `time`: diff --git a/examples/mbrs.ipynb b/examples/mbrs.ipynb index 6321b9b..3bf9e6d 100644 --- a/examples/mbrs.ipynb +++ b/examples/mbrs.ipynb @@ -9,21 +9,10 @@ "source": [ "import os\n", "import pdal\n", - "import pybabylonjs\n", "from pybabylonjs import Show as show\n", "import tiledb" ] }, - { - "cell_type": "code", - "execution_count": null, - "id": "ee8f4ea7-1b51-43d9-ab4d-189149f40831", - "metadata": {}, - "outputs": [], - "source": [ - "pybabylonjs.__version__" - ] - }, { "cell_type": "markdown", "id": "376c7606-efde-45b5-b512-39b6987fbbf2", @@ -73,7 +62,7 @@ "id": "8fce1435-d6b6-4a9d-b961-c36416375a0e", "metadata": {}, "source": [ - "### MBRS\n", + "### MBRS visualization\n", "\n", "Detailed information about the fragments in the array is read with:" ] diff --git a/examples/point-cloud-classes.ipynb b/examples/point-cloud-classes.ipynb new file mode 100644 index 0000000..12ed5ef --- /dev/null +++ b/examples/point-cloud-classes.ipynb @@ -0,0 +1,207 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "d4f53ca1-2726-4c0c-af0b-d39831237087", + "metadata": {}, + "source": [ + "# Point Cloud Data Visualization with a classes slider\n", + "\n", + "* Download point cloud data\n", + "* Create a sparse TileDB array\n", + "* Visualize the point cloud with a classes slider\n", + "* Customize with optional parameters" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "c33b9bc9-cb24-4064-b8ec-61b1b332a266", + "metadata": {}, + "outputs": [], + "source": [ + "import requests\n", + "\n", + "import pdal\n", + "from pybabylonjs import Show as show\n", + "import tiledb" + ] + }, + { + "cell_type": "markdown", + "id": "fb67c477-5b04-4fd8-bec7-a286e7ae92a2", + "metadata": {}, + "source": [ + "## Optional: create a sparse TileDB array from a LAZ file" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "f9eb5905-32a5-49e6-9b96-d89a701f91f9", + "metadata": {}, + "outputs": [], + "source": [ + "!wget -nc \"https://github.com/PDAL/data/blob/master/autzen/autzen-classified.laz?raw=true\" -O \"autzen-classified.laz\"" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d878a610-ad6f-4796-bf25-1c2f1c4d31c9", + "metadata": {}, + "outputs": [], + "source": [ + "pipeline = (\n", + " pdal.Reader(\"autzen-classified.laz\") |\n", + " pdal.Filter.stats() |\n", + " pdal.Writer.tiledb(array_name=\"autzen-classified\",chunk_size=100000)\n", + ")\n", + "\n", + "count = pipeline.execute() " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "77cd7fd4-9e77-4307-bb7a-c828a9395530", + "metadata": {}, + "outputs": [], + "source": [ + "lidar_array = \"autzen-classified\"" + ] + }, + { + "cell_type": "markdown", + "id": "300faf9b-716e-4c3c-af00-86d9cf118b39", + "metadata": {}, + "source": [ + "To load and display a slice of the data a bounding box with the minimum and maximum values of X, Y and Z are needed:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "7aa48a9d-9aba-4919-8ed6-3fea268b5bf4", + "metadata": {}, + "outputs": [], + "source": [ + "bbox = {\n", + " 'X': [636800, 637800],\n", + " 'Y': [851000, 853000],\n", + " 'Z': [406.14, 615.26]\n", + "}" + ] + }, + { + "cell_type": "markdown", + "id": "e179da13-10d4-42c6-ad8e-7850339eee2e", + "metadata": {}, + "source": [ + "### Visualize the point cloud with a classes slider\n", + "\n", + "*The below example loads the data from a local array, but a time slider can be added to point clouds from all data sources: `local`, `cloud` and `dict`.*\n", + "\n", + "Point cloud data from the local array can be loaded and displayed with the below, where `uri` is the location of the array:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "29b8b806-b785-4784-a802-d5d7d735a12a", + "metadata": {}, + "outputs": [], + "source": [ + "show.point_cloud(source=\"local\",\n", + " mode=\"default\",\n", + " uri=lidar_array,\n", + " bbox = bbox)" + ] + }, + { + "cell_type": "markdown", + "id": "e06eaa47-7277-419f-b204-69c8418628ee", + "metadata": {}, + "source": [ + "To add a time slider change the mode from `default` to `classes` and add the `classes` variable that contains the names for each class. Also make sure there is an attribute `Classification` in the array:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "95fc9ca9-19b5-4872-9369-3473bd286442", + "metadata": {}, + "outputs": [], + "source": [ + "classes = {\n", + " 'numbers': [0,2,5,6,9,15,17,19,64,65,66,67,68,69,70,71,72,73,74,75,76,77],\n", + " 'names': ['None','Ground','Vegetation','Building','Water','Transmission Tower',\n", + " 'Bridge Deck','Overhead Structure','Wire','Car','Truck','Boat',\n", + " 'Barrier','Railroad car','Elevated Walkway','Covered Walkway',\n", + " 'Pier/Dock','Fence','Tower','Crane','Silo/Storage Tank','Bridge Structure']\n", + "}\n", + "\n", + "show.point_cloud(source=\"local\",\n", + " mode=\"classes\",\n", + " uri=lidar_array,\n", + " bbox = bbox,\n", + " classes=classes)" + ] + }, + { + "cell_type": "markdown", + "id": "435491c9-1093-4c6a-b67f-6f35047e7b15", + "metadata": {}, + "source": [ + "## Customize with optional parameters" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "c54a3e7a-7edf-4412-a018-b570481f9381", + "metadata": {}, + "outputs": [], + "source": [ + "show.point_cloud(source=\"local\",\n", + " mode=\"classes\",\n", + " uri=lidar_array,\n", + " bbox = bbox,\n", + " classes=classes,\n", + " width=1200,\n", + " height=800,\n", + " z_scale=1.5,\n", + " color_scheme=\"blue\")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "3f29c476-0dfa-43e8-8eb2-9b680a04da4d", + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.7.10" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/examples/gltf.ipynb b/examples/point-cloud-gltf.ipynb similarity index 51% rename from examples/gltf.ipynb rename to examples/point-cloud-gltf.ipynb index a774b4a..652363a 100644 --- a/examples/gltf.ipynb +++ b/examples/point-cloud-gltf.ipynb @@ -2,21 +2,20 @@ "cells": [ { "cell_type": "markdown", - "id": "b69d7104-541f-474d-b30d-06a83c4d120e", + "id": "e89ada4d-bb1b-49ff-9d7e-4a5c2e386ab2", "metadata": {}, "source": [ - "# GLTF -> TileDB demo\n", - "\n", - "We will start by importing the required libraries;" + "# Point Cloud Data Visualization with GLTF models" ] }, { "cell_type": "code", - "execution_count": 1, - "id": "c9da5366-077c-40c8-b136-816ee9ddab9c", + "execution_count": null, + "id": "25862f91-ff48-454e-9dde-05d67ba8a68d", "metadata": {}, "outputs": [], "source": [ + "import math\n", "import json\n", "import numpy as np\n", "from pybabylonjs import Show as show\n", @@ -25,16 +24,16 @@ }, { "cell_type": "markdown", - "id": "570ce59b-1968-439b-ae7e-f9a4840031dd", + "id": "48e95c4f-bc26-4d67-803c-93c8f1724330", "metadata": {}, "source": [ - "The BabylonJS viewer reads GLTF so we will read the FBX file as a GLTF model" + "Load the gltf model:" ] }, { "cell_type": "code", - "execution_count": 2, - "id": "b73aebb0-fed5-4b62-b87a-9d2c9d94bf0f", + "execution_count": null, + "id": "296015d6-b5d6-498e-945d-c2ec0ff131bb", "metadata": {}, "outputs": [], "source": [ @@ -44,20 +43,19 @@ }, { "cell_type": "markdown", - "id": "f4839324-1a52-408f-a25b-1771235a38ec", + "id": "f4e616ab-baad-450d-b96e-ba9be6f8ddf6", "metadata": {}, "source": [ - "Create a point cloud ground surface for this building" + "Create a point cloud ground surface for this building:" ] }, { "cell_type": "code", - "execution_count": 3, - "id": "8530408b-6d1c-4ca8-97ce-be8dc98b2450", + "execution_count": null, + "id": "a58649e9-18a2-4127-afd5-dca5c3884701", "metadata": {}, "outputs": [], "source": [ - "# create surface for model\n", "(minx, maxx), (miny, maxy), (minz, maxz) = ((-80, 80), (-150, 150), (-30, 40))\n", "extent = 50.\n", "num_vals = 1000\n", @@ -78,33 +76,36 @@ }, { "cell_type": "markdown", - "id": "99f1d235-a073-47b8-9ae6-a0b5a1730cc0", + "id": "8094f79b-38dc-4483-9a17-aa5e21926501", "metadata": {}, "source": [ - "And render;" + "**And render:**\n", + "\n", + "Hotkeys to interact with the point cloud:\n", + "\n", + "* [C] switch between two camera options\n", + "* [W] forward\n", + "* [D] backward\n", + "* [A] left\n", + "* [S] right\n", + "* [E] up\n", + "* [Q] down\n", + "\n", + "Hotkeys to interact with the mesh:\n", + "* click on a mesh to select it\n", + "* click anywhere else to deselect\n", + "* [F] to focus on selected mesh\n", + "* [R] to toggle selected mesh wireframe\n", + "* [=] to perform a basic clash detection\n", + "* [-] to return to the original point cloud" ] }, { "cell_type": "code", - "execution_count": 6, - "id": "36c4d496-2bf8-40e0-92ca-944d08c9c191", + "execution_count": null, + "id": "2ae70fbe-5c4d-4ca9-b127-2e1b9aaed71c", "metadata": {}, - "outputs": [ - { - "data": { - "application/vnd.jupyter.widget-view+json": { - "model_id": "6b5bff1e44fe43439047bf2e681eb211", - "version_major": 2, - "version_minor": 0 - }, - "text/plain": [ - "BabylonPointCloud(value={'inspector': False, 'width': 800, 'height': 600, 'z_scale': 1, 'wheel_precision': -1,…" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], + "outputs": [], "source": [ "show.point_cloud(source='dict',\n", " mode=\"gltf\",\n", @@ -113,10 +114,42 @@ " point_size = 5)" ] }, + { + "cell_type": "markdown", + "id": "90e2fd04-bbe8-4d14-a840-ee3172b18860", + "metadata": {}, + "source": [ + "Move the point cloud with:\n", + "* `point_shift`\n", + "\n", + "Move,rotate and scale the mesh with:\n", + "* `mesh_shift`\n", + "* `mesh_rotation`\n", + "* `mesh_scale`" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b8fde9a1-bf0e-4deb-9530-77f870165715", + "metadata": {}, + "outputs": [], + "source": [ + "show.point_cloud(source='dict',\n", + " mode=\"gltf\",\n", + " data=data,\n", + " gltf_data = gltf_data,\n", + " point_size = 5,\n", + " point_shift = [50,0,10],\n", + " mesh_shift = [200, 0, 100],\n", + " mesh_rotation = [0, math.pi/2, 0],\n", + " mesh_scale = [4, 2, 3])" + ] + }, { "cell_type": "code", "execution_count": null, - "id": "8df1d99b-c32b-4622-995b-fa45e4ad7162", + "id": "3c2bba7c-a4f6-4697-8b2a-0a05063de0c9", "metadata": {}, "outputs": [], "source": [] diff --git a/examples/point-cloud-time.ipynb b/examples/point-cloud-time.ipynb new file mode 100644 index 0000000..7e5ddbd --- /dev/null +++ b/examples/point-cloud-time.ipynb @@ -0,0 +1,198 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "d4f53ca1-2726-4c0c-af0b-d39831237087", + "metadata": {}, + "source": [ + "# Point Cloud Data Visualization with a time slider\n", + "\n", + "* Download point cloud data\n", + "* Create a sparse TileDB array\n", + "* Visualize the point cloud with a time slider\n", + "* Customize with optional parameters" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "c33b9bc9-cb24-4064-b8ec-61b1b332a266", + "metadata": {}, + "outputs": [], + "source": [ + "import requests\n", + "\n", + "import pdal\n", + "from pybabylonjs import Show as show\n", + "import tiledb" + ] + }, + { + "cell_type": "markdown", + "id": "fb67c477-5b04-4fd8-bec7-a286e7ae92a2", + "metadata": {}, + "source": [ + "## Optional: create a sparse TileDB array from a LAZ file" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "f9eb5905-32a5-49e6-9b96-d89a701f91f9", + "metadata": {}, + "outputs": [], + "source": [ + "!wget -nc \"https://github.com/PDAL/data/blob/master/autzen/autzen-classified.laz?raw=true\" -O \"autzen-classified.laz\"" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d878a610-ad6f-4796-bf25-1c2f1c4d31c9", + "metadata": {}, + "outputs": [], + "source": [ + "pipeline = (\n", + " pdal.Reader(\"autzen-classified.laz\") |\n", + " pdal.Filter.stats() |\n", + " pdal.Writer.tiledb(array_name=\"autzen-classified\",chunk_size=100000)\n", + ")\n", + "\n", + "count = pipeline.execute() " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "77cd7fd4-9e77-4307-bb7a-c828a9395530", + "metadata": {}, + "outputs": [], + "source": [ + "lidar_array = \"autzen-classified\"" + ] + }, + { + "cell_type": "markdown", + "id": "300faf9b-716e-4c3c-af00-86d9cf118b39", + "metadata": {}, + "source": [ + "To load and display a slice of the data a bounding box with the minimum and maximum values of X, Y and Z are needed:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "7aa48a9d-9aba-4919-8ed6-3fea268b5bf4", + "metadata": {}, + "outputs": [], + "source": [ + "bbox = {\n", + " 'X': [636800, 637800],\n", + " 'Y': [851000, 853000],\n", + " 'Z': [406.14, 615.26]\n", + "}" + ] + }, + { + "cell_type": "markdown", + "id": "e179da13-10d4-42c6-ad8e-7850339eee2e", + "metadata": {}, + "source": [ + "### Visualize the point cloud with a time slider\n", + "\n", + "*The below example loads the data from a local array, but a time slider can be added to point clouds from all data sources: `local`, `cloud` and `dict`.*\n", + "\n", + "Point cloud data from the local array can be loaded and displayed with the below, where `uri` is the location of the array:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "29b8b806-b785-4784-a802-d5d7d735a12a", + "metadata": {}, + "outputs": [], + "source": [ + "show.point_cloud(source=\"local\",\n", + " mode=\"default\",\n", + " uri=lidar_array,\n", + " bbox = bbox)" + ] + }, + { + "cell_type": "markdown", + "id": "e06eaa47-7277-419f-b204-69c8418628ee", + "metadata": {}, + "source": [ + "To add a time slider change the mode from `default` to `time`, make sure there is an attribute `GpsTime` in the array and add the following parameters:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "95fc9ca9-19b5-4872-9369-3473bd286442", + "metadata": {}, + "outputs": [], + "source": [ + "show.point_cloud(source=\"local\",\n", + " mode=\"time\",\n", + " uri=lidar_array,\n", + " bbox = bbox)" + ] + }, + { + "cell_type": "markdown", + "id": "435491c9-1093-4c6a-b67f-6f35047e7b15", + "metadata": {}, + "source": [ + "## Customize with optional parameters" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "c54a3e7a-7edf-4412-a018-b570481f9381", + "metadata": {}, + "outputs": [], + "source": [ + "show.point_cloud(source=\"local\",\n", + " mode=\"time\",\n", + " uri=lidar_array,\n", + " bbox = bbox,\n", + " width=1200,\n", + " height=800,\n", + " z_scale=1.5,\n", + " point_size=3,\n", + " color_scheme=\"blue\")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "3f29c476-0dfa-43e8-8eb2-9b680a04da4d", + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.7.10" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/examples/point-cloud-topo.ipynb b/examples/point-cloud-topo.ipynb new file mode 100644 index 0000000..7cc316f --- /dev/null +++ b/examples/point-cloud-topo.ipynb @@ -0,0 +1,222 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "2ac32241-fbe7-468f-944c-f29da504d93a", + "metadata": {}, + "source": [ + "# Point Cloud Data Visualization combined with a Mapbox base map\n", + "\n", + "* Download point cloud data\n", + "* Create a sparse TileDB array\n", + "* Visualize the point cloud with a Mapbox base layer\n", + "* Customize with optional parameters" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "10acb771-d7c5-44ab-820c-81a87ddb35ce", + "metadata": {}, + "outputs": [], + "source": [ + "import requests\n", + "\n", + "import pdal\n", + "from pybabylonjs import Show as show\n", + "import tiledb" + ] + }, + { + "cell_type": "markdown", + "id": "fbe1ba46-6111-44e5-a079-014d98233a93", + "metadata": {}, + "source": [ + "## Optional: create a sparse TileDB array from a LAZ file" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "a4ae70fc-2509-4372-9838-696d5141552b", + "metadata": {}, + "outputs": [], + "source": [ + "!wget -nc \"https://github.com/PDAL/data/blob/master/autzen/autzen-classified.laz?raw=true\" -O \"autzen-classified.laz\"" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "76e9a3e3-569c-40dd-a14a-ceb24398478a", + "metadata": {}, + "outputs": [], + "source": [ + "pipeline = (\n", + " pdal.Reader(\"autzen-classified.laz\") |\n", + " pdal.Filter.stats() |\n", + " pdal.Writer.tiledb(array_name=\"autzen-classified\",chunk_size=100000)\n", + ")\n", + "\n", + "count = pipeline.execute() " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d0fc4bc6-4439-48ba-814b-cce29bda435f", + "metadata": {}, + "outputs": [], + "source": [ + "lidar_array = \"autzen-classified\"" + ] + }, + { + "cell_type": "markdown", + "id": "a983e294-84a7-4a47-9cfc-5a40faf4e96e", + "metadata": {}, + "source": [ + "To load and display a slice of the data a bounding box with the minimum and maximum values of X, Y and Z are needed:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "96783257-26c9-4b01-9d8d-1e1778ed0aca", + "metadata": {}, + "outputs": [], + "source": [ + "bbox = {\n", + " 'X': [636800, 637800],\n", + " 'Y': [851000, 853000],\n", + " 'Z': [406.14, 615.26]\n", + "}" + ] + }, + { + "cell_type": "markdown", + "id": "b66ed4cc-77e3-4912-82f1-8a718e65e349", + "metadata": {}, + "source": [ + "### Visualize the point cloud with a Mapbox base layer\n", + "\n", + "*The below example loads the data from a local array, but a mapbox layer can be added to point clouds from all data sources: `local`, `cloud` and `dict`.*\n", + "\n", + "Point cloud data from the local array can be loaded and displayed with the below, where `uri` is the location of the array:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "fc25b96b-36c6-4c67-aecc-b42586a8d528", + "metadata": {}, + "outputs": [], + "source": [ + "show.point_cloud(source=\"local\",\n", + " mode=\"default\",\n", + " uri=lidar_array,\n", + " bbox = bbox)" + ] + }, + { + "cell_type": "markdown", + "id": "9027019d-8a1e-4b0d-a562-5307268fe925", + "metadata": {}, + "source": [ + "To add a base map, a Mapbox token is needed that you can get by [signing up here for free](https://www.mapbox.com). Paste the mapbox token below (`***`):" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "3d450a7d-ad23-4b98-9013-2ccb3418a180", + "metadata": {}, + "outputs": [], + "source": [ + "mbtoken = \"***\"" + ] + }, + { + "cell_type": "markdown", + "id": "f66cc94e-a9b8-450b-8b4b-56a9dbfd2a16", + "metadata": {}, + "source": [ + "Now display the point cloud with a Mapbox layer by changing the `mode` to `topo` and adding the following parameters:\n", + "\n", + "* `mbtoken`\n", + "* `mbstyle` is the [style of the map](https://docs.mapbox.com/api/maps/styles/) to use\n", + "* `crs` is the coordinate reference system of the point cloud that is needed to convert the bounding box to latitude and longitude\n", + "* `topo_offset` is the height (Z) of where you want to place the map relative to the point cloud" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "807c4ab4-6420-4760-bf4c-c648ed29ce44", + "metadata": {}, + "outputs": [], + "source": [ + "show.point_cloud(source=\"local\",\n", + " mode=\"topo\",\n", + " uri=lidar_array,\n", + " bbox=bbox,\n", + " topo_offset = 10,\n", + " mbtoken = mbtoken,\n", + " mbstyle = \"streets-v11\",\n", + " crs = \"EPSG:2994\")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "3c2bba7c-a4f6-4697-8b2a-0a05063de0c9", + "metadata": {}, + "outputs": [], + "source": [ + "show.point_cloud(source=\"local\",\n", + " mode=\"topo\",\n", + " uri=lidar_array,\n", + " bbox=bbox,\n", + " topo_offset = 20,\n", + " mbtoken = mbtoken,\n", + " mbstyle = \"satellite-v9\",\n", + " crs = \"EPSG:2994\",\n", + " width=1200,\n", + " height=800,\n", + " z_scale=1.5,\n", + " point_size=2,\n", + " show_fraction=4,\n", + " color_scheme=\"blue\")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "bc67b92e-3d9e-4a25-b8dd-b2a9eb19c18d", + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.7.10" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/examples/point_cloud.ipynb b/examples/point_cloud.ipynb index 0137139..3d4eedb 100644 --- a/examples/point_cloud.ipynb +++ b/examples/point_cloud.ipynb @@ -1,5 +1,22 @@ { "cells": [ + { + "cell_type": "markdown", + "id": "8e320b80-f162-4dad-8f5b-17032c782daf", + "metadata": {}, + "source": [ + "# Point Cloud Data Visualization\n", + "\n", + "* Download point cloud data\n", + "* Create a sparse TileDB array\n", + "* Visualize the point cloud from a\n", + " * local array\n", + " * cloud array\n", + " * dictionary\n", + "* Customize with optional parameters\n", + "* Navigate the point cloud" + ] + }, { "cell_type": "code", "execution_count": null, @@ -7,36 +24,20 @@ "metadata": {}, "outputs": [], "source": [ - "import os\n", - "import json\n", "import requests\n", "\n", "import pandas as pd\n", - "import rasterio\n", - "from rasterio.coords import BoundingBox\n", - "from rasterio.warp import transform_bounds\n", "import pdal\n", - "import pybabylonjs\n", "from pybabylonjs import Show as show\n", "import tiledb" ] }, - { - "cell_type": "code", - "execution_count": null, - "id": "00dac7f4-ce98-4ecd-b2dc-050749fb92f9", - "metadata": {}, - "outputs": [], - "source": [ - "pybabylonjs.__version__" - ] - }, { "cell_type": "markdown", "id": "cc1939af-2756-4186-8622-4f30b5b26d49", "metadata": {}, "source": [ - "## Create a sparse TileDB array from a LAZ file" + "## Optional: create a sparse TileDB array from a LAZ file" ] }, { @@ -65,22 +66,52 @@ "count = pipeline.execute() " ] }, + { + "cell_type": "markdown", + "id": "22aa9555-41c0-491d-8f36-1ecaf1c21122", + "metadata": {}, + "source": [ + "## Data sources\n", + "\n", + "The point cloud data can be loaded in three different ways that are specfied by the `source` parameter:\n", + "* a locally stored TileDB array (`source = \"local\"`)\n", + "* a TileDB Cloud array (`source = \"cloud\"`)\n", + "* a dictionary (`source = \"dict\"`)\n", + "\n", + "To load and display a slice of the data a bounding box with the minimum and maximum values of X, Y and Z are needed:" + ] + }, { "cell_type": "code", "execution_count": null, - "id": "39fef3ab-8669-4897-a3fb-73ee3bf12b4a", + "id": "fec9e371-519c-44d5-95b0-7bc2f3093a1c", "metadata": {}, "outputs": [], "source": [ - "lidar_array = \"autzen-classified\"" + "bbox = {\n", + " 'X': [636800, 637800],\n", + " 'Y': [851000, 853000],\n", + " 'Z': [406.14, 615.26]\n", + "}" ] }, { "cell_type": "markdown", - "id": "22aa9555-41c0-491d-8f36-1ecaf1c21122", + "id": "8ef43d2c-9214-4fd1-bddc-04afc9e144e3", "metadata": {}, "source": [ - "## Default point cloud" + "### Local array\n", + "Point cloud data in a local array is loaded and displayed with the below, where `uri` is the location of the array:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "39fef3ab-8669-4897-a3fb-73ee3bf12b4a", + "metadata": {}, + "outputs": [], + "source": [ + "lidar_array = \"autzen-classified\"" ] }, { @@ -90,89 +121,221 @@ "metadata": {}, "outputs": [], "source": [ - "bbox = {\n", - " 'X': [636800, 637800],\n", - " 'Y': [851000, 853000],\n", - " 'Z': [406.14, 615.26]\n", - "}\n", - "\n", "show.point_cloud(source=\"local\",\n", - " mode=\"default\",\n", " uri=lidar_array,\n", - " bbox=bbox)" + " bbox = bbox)" + ] + }, + { + "cell_type": "markdown", + "id": "a149a0e0-c9ac-4b99-b830-4c54e85248fe", + "metadata": {}, + "source": [ + "### Cloud array\n", + "To display point cloud data from a TileDB cloud array a TileDB `token` is needed: \n", + "* [Sign up for a free TileDB account](https://cloud.tiledb.com/auth/signup)\n", + "* [Create a token as described here](https://docs.tiledb.com/cloud/how-to/account/create-api-tokens)\n", + "* Paste the token into the below cell (`***`)" ] }, { "cell_type": "code", "execution_count": null, - "id": "0411028b-7a61-4f5c-b1f1-76ab9c1977aa", + "id": "c2b6d89a-bafc-42b5-8e8d-6069cc187a3b", "metadata": {}, "outputs": [], "source": [ - "# optional parameters\n", + "token = \"***\"" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "6b2f4d85-9704-45c0-888d-5bf94b6a3989", + "metadata": {}, + "outputs": [], + "source": [ + "show.point_cloud(source=\"cloud\",\n", + " uri = \"tiledb://TileDB-Inc/autzen_classified_tiledb\",\n", + " token=token,\n", + " bbox=bbox)" + ] + }, + { + "cell_type": "markdown", + "id": "321874d3-6119-4edc-8250-64fe3e10be4f", + "metadata": {}, + "source": [ + "### Data from a dictionary\n", + "Alternatively data can be loaded into a dictionary first and then displayed. \n", "\n", - "show.point_cloud(source=\"local\",\n", - " mode=\"default\",\n", - " uri=lidar_array,\n", - " bbox=bbox,\n", - " inspector=False,\n", - " width=900,\n", - " height=500,\n", - " z_scale=2,\n", - " wheel_precision=-1,\n", - " point_size=2\n", - " )" + "Load the data directly into a dictionary from the local array:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "67b71dbc-d2be-4b2f-9972-f4719a33ea82", + "metadata": {}, + "outputs": [], + "source": [ + "with tiledb.open(lidar_array) as arr:\n", + " data = arr.query(attrs=[\"Red\", \"Green\", \"Blue\"], dims=[\"X\", \"Y\", \"Z\"])[\n", + " bbox[\"X\"][0] : bbox[\"X\"][1],\n", + " bbox[\"Y\"][0] : bbox[\"Y\"][1],\n", + " bbox[\"Z\"][0] : bbox[\"Z\"][1],\n", + " ]" ] }, { "cell_type": "markdown", - "id": "89ef6545-2b5d-4573-8230-03a59e054023", + "id": "6953f74c-2cc9-4786-be41-f05ff0da14fc", "metadata": {}, "source": [ - "## Point cloud with a time slider" + "Or first load the data into a pandas DataFrame when for example pre-processing of the data is needed:" ] }, { "cell_type": "code", "execution_count": null, - "id": "1b111330-4a66-44b9-8e7b-8c4043cb6994", + "id": "4bf0e649-5997-4b44-b6f9-f39b9400c6b3", "metadata": {}, "outputs": [], "source": [ - "show.point_cloud(source=\"local\",\n", - " mode=\"time\",\n", - " uri=lidar_array,\n", - " bbox=bbox)" + "with tiledb.open(lidar_array) as arr:\n", + " df = pd.DataFrame(arr[\n", + " bbox[\"X\"][0] : bbox[\"X\"][1],\n", + " bbox[\"Y\"][0] : bbox[\"Y\"][1],\n", + " bbox[\"Z\"][0] : bbox[\"Z\"][1]])" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "4b024dc4-030b-4054-b2a9-a936077f4e74", + "metadata": {}, + "outputs": [], + "source": [ + "df" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b1636835-9527-4fc1-ad57-6ac4f3e2407f", + "metadata": {}, + "outputs": [], + "source": [ + "data = {\n", + " 'X': df['X'],\n", + " 'Y': df['Y'],\n", + " 'Z': df['Z'],\n", + " 'Red': df['Red'],\n", + " 'Green': df['Green'],\n", + " 'Blue': df['Blue']\n", + "}" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "a4bcf7ed-af2b-447a-ae3a-66cc67db1a84", + "metadata": {}, + "outputs": [], + "source": [ + "show.point_cloud(source=\"dict\",\n", + " data=data)" ] }, { "cell_type": "markdown", - "id": "0d5cb85d-bf73-4d93-8112-c92e491453e8", + "id": "efebcc6b-af38-4988-bddc-343d551e3a54", + "metadata": {}, + "source": [ + "## Optional parameters\n", + "\n", + "There are several parameters to change the way the visualization looks that are available for all three data sources:\n", + "\n", + "* `width` and `height` define the size of the display canvas (defaults are 800 and 600)\n", + "* `z_scale` scales the Z values of the coordinates of all points (default is 1)\n", + "* `point_size` sets the size of the points (default is 1)\n", + "* `color_scheme` sets the background color scheme, choose from \"dark\" (default), \"light\" and \"blue\"\n", + "* `rgb_max` is the maximum RGB value used to scale all values between 0 and 1, when not provided it will be calculated from the data\n", + "* `show_fraction` can be used to display a fraction of the points, showing every *nth* point from the loaded data\n", + "\n", + "Two examples:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "950da2b4-359a-42ed-97ec-3873eecdebc0", "metadata": {}, + "outputs": [], "source": [ - "## Point cloud with a classification slider" + "show.point_cloud(source=\"dict\",\n", + " data=data,\n", + " width=400,\n", + " height=300,\n", + " z_scale=3,\n", + " rgb_max=63744,\n", + " color_scheme=\"light\")" ] }, { "cell_type": "code", "execution_count": null, - "id": "8d0e893c-df37-42da-874d-221b3c76a2d7", + "id": "0411028b-7a61-4f5c-b1f1-76ab9c1977aa", "metadata": {}, "outputs": [], "source": [ - "classes = {\n", - " 'numbers': [0,2,5,6,9,15,17,19,64,65,66,67,68,69,70,71,72,73,74,75,76,77],\n", - " 'names': ['None','Ground','Vegetation','Building','Water','Transmission Tower',\n", - " 'Bridge Deck','Overhead Structure','Wire','Car','Truck','Boat',\n", - " 'Barrier','Railroad car','Elevated Walkway','Covered Walkway',\n", - " 'Pier/Dock','Fence','Tower','Crane','Silo/Storage Tank','Bridge Structure']\n", - "}\n", + "show.point_cloud(source=\"dict\",\n", + " data=data,\n", + " bbox=bbox,\n", + " inspector=False,\n", + " width=1200,\n", + " height=800,\n", + " z_scale=1.5,\n", + " point_size=2,\n", + " show_fraction=10,\n", + " color_scheme=\"blue\"\n", + " )" + ] + }, + { + "cell_type": "markdown", + "id": "88419d21-bee0-487c-baf5-baadf7ceb1f6", + "metadata": {}, + "source": [ + "## Navigating the point cloud\n", "\n", - "show.point_cloud(source=\"local\",\n", - " mode=\"classes\",\n", - " uri=lidar_array,\n", - " bbox=bbox,\n", - " classes=classes)" + "There are two ways to view the point cloud. Use the mouse to zoom in and out and drag the points around. Control the free camera with these hotkeys:\n", + "\n", + "* [C] switch between the two camera options \n", + "* [W] forward\n", + "* [D] backward\n", + "* [A] left\n", + "* [S] right \n", + "* [E] up \n", + "* [Q] down \n", + "\n", + "These parameters control the behaviour of the camera:\n", + "\n", + "* `wheel_precision` controls the speed of zooming in and out (default is -1)\n", + "* `move_speed` controls how fast the camera moves through the point cloud (default is -1)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "dd69ede2-2880-4b2f-a911-0541b7d99ef3", + "metadata": {}, + "outputs": [], + "source": [ + "show.point_cloud(source=\"dict\",\n", + " data=data,\n", + " move_speed=10,\n", + " wheel_precision=-10)" ] }, { diff --git a/examples/pointcloud.png b/examples/pointcloud.png index 02d0af6..2dbfa8c 100644 Binary files a/examples/pointcloud.png and b/examples/pointcloud.png differ