Skip to content

Commit

Permalink
Tiled Image parameters update (#62)
Browse files Browse the repository at this point in the history
* initial changes - wip

* image viewer added - wip

* add image viewer

* Removed unses polyfill plugin, update tiled image parameters

* Updated dependencies

* Bump TileDB Viz version to 1.0.2-alpha.12

* lint fix

* Pin capnp-ts to 0.4.0

* Remove packageManager entry since jlab v3 uses yarn v1

---------

Co-authored-by: Margriet Groenendijk <[email protected]>
Co-authored-by: SarantopoulosKon <[email protected]>
  • Loading branch information
3 people authored Nov 17, 2023
1 parent 020a961 commit 838316b
Show file tree
Hide file tree
Showing 11 changed files with 4,126 additions and 5,417 deletions.
3 changes: 2 additions & 1 deletion .yarnrc.yml
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
enableImmutableInstalls: false
nodeLinker: node-modules

nodeLinker: node-modules
5 changes: 4 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@
"dependencies": {
"@jupyter-widgets/base": "^2 || ^3 || ^4 || ^5 || ^6",
"@jupyterlab/application": "^3 || ^4",
"@tiledb-inc/viz-core": "^1.0.2-alpha.2"
"@tiledb-inc/viz-core": "1.0.2-alpha.12"
},
"devDependencies": {
"@jupyterlab/builder": "^3 || ^4",
Expand Down Expand Up @@ -86,6 +86,9 @@
"webpack": "^5.88.1",
"webpack-cli": "^5.1.4"
},
"resolutions": {
"capnp-ts": "0.4.0"
},
"jupyterlab": {
"extension": "lib/plugin",
"outputDir": "pybabylonjs/labextension",
Expand Down
106 changes: 54 additions & 52 deletions pybabylonjs/args.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,19 +15,13 @@
"inspector": None,
"color_scheme": None,
"z_scale": None,
"topo_offset": None,
"classes": {"numbers": [], "names": []},
"time_offset": None,
"point_shift": [None, None, None],
"rgb_max": None,
"bbox": None,
"name_space": None,
"group_name": None,
"array_name": None,
"token": None,
"tiledb_env": None,
"mbtoken": None,
"mbstyle": None,
"crs": None,
"buffer_size": None,
"streaming": None,
Expand All @@ -46,45 +40,65 @@
"worker_pool_size": None,
}

IMAGE_ARGS_DEFAULTS = {
"width": None,
"height": None,
"wheel_precision": None, # used? in base class?
"move_speed": None, # used?
"name_space": None,
"array_name": None,
"group_name": None,
"geometry_array_name": None,
"base_group": None,
"token": None,
"tiledb_env": None,
"default_channels": None,
}

def check_point_cloud_args(source, mode, point_cloud_args_in):
if mode == "time":
raise ValueError("This mode will be implemented soon")
if mode == "classes":
raise ValueError("This mode will be implemented soon")
if not "classes" in point_cloud_args_in:
raise ValueError(
"The classes containing numbers and names is not specified"
)
elif mode == "topo":
raise ValueError("This mode will be implemented soon")
if not "mbtoken" in point_cloud_args_in:
raise ValueError("The Mapbox token is not specified")
if not "crs" in point_cloud_args_in:
raise ValueError(
"The crs (coordinate reference system) of the data is not specified"
)
if not "bbox" in point_cloud_args_in:
raise ValueError("The bbox is not specified")

def in_pixels(h, default):
if h is None:
return default
if isinstance(h, str):
if "px" in h:
return h
return h + "px"
if isinstance(h, int):
return str(h) + "px"
if isinstance(h, float):
return str(int(h)) + "px"


def check_image_args(image_args_in):
image_args = {}
for key in IMAGE_ARGS_DEFAULTS.keys():
if key in image_args_in:
if key is not None:
image_args[key] = image_args_in.pop(key)

image_args["height"] = in_pixels(image_args.get("height"), "500px")
image_args["width"] = in_pixels(image_args.get("width"), "700px")

if not "token" in image_args:
try:
token = os.getenv("TILEDB_REST_TOKEN")
except:
if token == None:
raise ValueError(
"The TileDB Cloud token needed to access the array is not specified or cannot be accessed"
)
image_args = {**image_args, "token": token}

return image_args


def check_point_cloud_args(source, streaming, point_cloud_args_in):
point_cloud_args = {}
for key in POINT_CLOUD_ARGS_DEFAULTS.keys():
if key in point_cloud_args_in:
if key is not None:
point_cloud_args[key] = point_cloud_args_in.pop(key)

def in_pixels(h, default):
if h is None:
return default
if isinstance(h, str):
if "px" in h:
return h
return h + "px"
if isinstance(h, int):
return str(h) + "px"
if isinstance(h, float):
return str(int(h)) + "px"

point_cloud_args["height"] = in_pixels(point_cloud_args.get("height"), "500px")
point_cloud_args["width"] = in_pixels(point_cloud_args.get("width"), "700px")

Expand All @@ -101,7 +115,7 @@ def in_pixels(h, default):
return point_cloud_args


def check_point_cloud_data_dict(mode, data):
def check_point_cloud_data_dict(data):
for var in ["X", "Y", "Z", "Red", "Green", "Blue"]:
if not var in data:
raise ValueError("Data dictionary does not contain " + var)
Expand All @@ -115,28 +129,16 @@ def check_point_cloud_data_dict(mode, data):
):
raise ValueError("Attributes in data dictionary do not have the same length.")

if mode == "time":
if not "GpsTime" in data:
raise ValueError("Data dictionary does not contain 'GpsTime'")

i = np.argsort(data["GpsTime"])
for key in ["Red", "Green", "Blue", "GpsTime", "X", "Y", "Z"]:
data[key] = data[key][i]

elif mode == "classes":
if not "Classification" in data:
raise ValueError("Data dictionary does not contain 'Classification'")

return data


def check_point_cloud_data_local(mode, uri, point_cloud_args):
def check_point_cloud_data_local(uri, point_cloud_args):
if os.path.isdir(uri) == False:
raise ValueError("uri: " + uri + " does not exist.")
if not "bbox" in point_cloud_args:
raise ValueError("The bbox for slicing data from the array is not specified")

data = create_point_cloud(mode, uri, point_cloud_args["bbox"])
data = create_point_cloud(uri, point_cloud_args["bbox"])

return data

Expand Down
6 changes: 3 additions & 3 deletions pybabylonjs/babylonjs.py
Original file line number Diff line number Diff line change
Expand Up @@ -46,8 +46,8 @@ class BabylonMBRS(BabylonBase):

@register
class BabylonImage(BabylonBase):
"""Ground surface as 2D array with BabylonJS"""
"""Images with BabylonJS"""

_model_name = Unicode("BabylonImageModel").tag(sync=True)
_view_name = Unicode("BabylonImageView").tag(sync=True)
_model_name = Unicode("BabylonTileImageModel").tag(sync=True)
_view_name = Unicode("BabylonTileImageView").tag(sync=True)
value = Dict().tag(sync=True)
110 changes: 2 additions & 108 deletions pybabylonjs/data.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,20 +2,12 @@
# Licensed under the MIT License.
"""Functions to format data from the arrays to be used in the visualization."""

import io
import numpy as np
import pandas as pd
import cv2
import tiledb


def create_point_cloud(mode, array_uri: str, bbox):
if mode == "time":
attrs = ["Red", "Green", "Blue", "GpsTime"]
elif mode == "classes":
attrs = ["Red", "Green", "Blue", "Classification"]
else:
attrs = ["Red", "Green", "Blue"]
def create_point_cloud(array_uri: str, bbox):
attrs = ["Red", "Green", "Blue"]

with tiledb.open(array_uri) as arr:
data = arr.query(attrs=attrs, dims=["X", "Y", "Z"])[
Expand All @@ -24,75 +16,9 @@ def create_point_cloud(mode, array_uri: str, bbox):
bbox["Z"][0] : bbox["Z"][1],
]

if mode == "time":
i = np.argsort(data["GpsTime"])
for key in ["Red", "Green", "Blue", "GpsTime", "X", "Y", "Z"]:
data[key] = data[key][i]

return data


def create_mapbox_image(data: dict, point_cloud_args):
"""Create a Dict with an additional topographic image from mapbox
Parameters:
"""
import requests
from rasterio.coords import BoundingBox
from rasterio.warp import transform_bounds

mbtoken = point_cloud_args["mbtoken"]
style_id = point_cloud_args["mbstyle"]
data_crs = point_cloud_args["crs"]
bbox_in = point_cloud_args["bbox"]

dst_crs = {"init": "EPSG:4326"} # lat/lon

if bbox_in:
bbox = BoundingBox(
bbox_in["X"][0], bbox_in["Y"][0], bbox_in["X"][1], bbox_in["Y"][1]
)
else:
bbox = BoundingBox(
data["X"].min(), data["Y"].min(), data["X"].max(), data["Y"].max()
)

dst_bbox = transform_bounds(data_crs, dst_crs, *bbox)

w = bbox[2] - bbox[0]
h = bbox[3] - bbox[1]

if w > h:
ww = 1280
hh = int(h / w * 1280)
elif h > w:
hh = 1280
ww = int(w / h * 1280)

f = requests.get(
(
"https://api.mapbox.com/styles/v1/mapbox/"
+ style_id
+ "/static/["
+ str(dst_bbox[0])
+ ","
+ str(dst_bbox[1])
+ ","
+ str(dst_bbox[2])
+ ","
+ str(dst_bbox[3])
+ "]/"
+ str(ww)
+ "x"
+ str(hh)
+ "?access_token="
+ mbtoken
)
)

return f.content


def create_mbrs(array_uri: str):
"""Create a Dict to be passed on to BabylonMBRS to create MBRS outlines."""
fragments_info = tiledb.array_fragments(array_uri, include_mbrs=True)
Expand Down Expand Up @@ -137,35 +63,3 @@ def create_mbrs(array_uri: str):
]

return dict(extents=extents, data=data)


def create_image(array_uri: str, **kwargs):
"""Create a Dict to be passed on to BabylonGround containing images as blobs.
Parameters:
array_uri: uri of the dense array
attribute: the attribute to load from the array
xy_bbox: ranges of x and y to slice data on [x1,x2,y1,y2]
band: band number to slice from the array
scale_factor: factor to scale the values in the image
"""

def numpy_to_binary(arr):
is_success, buffer = cv2.imencode(".png", arr)
io_buf = io.BytesIO(buffer)
return io_buf.read()

bbox = kwargs["xy_bbox"]
band = kwargs["band"]
image_type = kwargs["image_type"]
sar_scale = kwargs["sar_scale_factor"]

with tiledb.open(array_uri, "r") as arr:
img = arr[band, bbox[0] : bbox[1], bbox[2] : bbox[3]][kwargs["attribute"]]

if image_type == "sar":
img = 20 * np.log10(img * sar_scale)
img = ((img - np.min(img)) / (np.max(img) - np.min(img))) * 255
binary_image = numpy_to_binary(img)

return dict(data=binary_image)
Loading

0 comments on commit 838316b

Please sign in to comment.