#!/usr/bin/env python3
# -*- coding: utf-8 -*-
#
#*******************************************************************************
#
#  Copyright 2026 RIEGL Laser Measurement Systems
#
#  Licensed under the Apache License, Version 2.0 (the "License");
#  you may not use this file except in compliance with the License.
#  You may obtain a copy of the License at
#
#      http://www.apache.org/licenses/LICENSE-2.0
#
#  Unless required by applicable law or agreed to in writing, software
#  distributed under the License is distributed on an "AS IS" BASIS,
#  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
#  See the License for the specific language governing permissions and
#  limitations under the License.
#
#  SPDX-License-Identifier: Apache-2.0
#
#*******************************************************************************
#
"""
rdb-example-03-select-points.py

This example shows different ways to open an existing database and query
some points.
"""

import riegl.rdb


def example_a():
    """
    Load points that meet filter criteria and print them point-wise.
    To load all points instead, simply remove the filter string or
    specify 'None' (the constant, not the string "None").
    """
    print("Example A:")
    with riegl.rdb.rdb_open("pointcloud.rdbx") as rdb:
        for point in rdb.points(
            "(riegl.xyz[2] > 5) && (riegl.reflectance > 35)"
        ):
            print(f"{point['riegl.id']}, {point['riegl.xyz']}, {point['riegl.reflectance']}")


def example_b():
    """
    Similar to example A, but instead of loading all point attributes
    we specify the names of the point attributes to load and print.
    """
    print("Example B:")
    with riegl.rdb.rdb_open("pointcloud.rdbx") as rdb:
        for point in rdb.points(
            "(riegl.xyz[2] > 5) && (riegl.reflectance > 35)",
            ["riegl.id", "riegl.xyz", "riegl.reflectance"]
        ):
            print(f"{point['riegl.id']}, {point['riegl.xyz']}, {point['riegl.reflectance']}")


def example_c():
    """
    Similar to example A, but instead of loading point-by-point, we
    load points chunk-wise and access the attributes via arrays.
    Please note that we use select() instead of points() this time.
    """
    print("Example C:")
    with riegl.rdb.rdb_open("pointcloud.rdbx") as rdb:
        for points in rdb.select(
            "(riegl.xyz[2] > 5) && (riegl.reflectance > 35)",
            chunk_size=100000  # optional: number of points to load in one step
        ):
            print(points["riegl.id"])  # this time, the attribute is not a
            print(points["riegl.xyz"])  # single value but an array of values
            print(points["riegl.reflectance"])


def example_d():
    """
    This example shows the most flexible way as known from the RDB C++ API.
    This time we manually create buffers for the point attributes read from
    the point cloud and iterate the point cloud chunk-wise.
    """
    print("Example D:")
    with riegl.rdb.rdb_open("pointcloud.rdbx") as rdb:
        # number of points to load in one step
        chunk_size = 100 * 1000

        # create attribute buffers (array) for point coordinates and reflectance
        buffer_xyz = riegl.rdb.AttributeBuffer(
            rdb.point_attributes["riegl.xyz"], chunk_size
        )
        buffer_reflectance = riegl.rdb.AttributeBuffer(
            rdb.point_attributes["riegl.reflectance"], chunk_size
        )

        # load all points chunk-wise
        with rdb.select() as query:
            # tell the query where to store the point attribute data to
            query.bind(buffer_xyz)
            query.bind(buffer_reflectance)

            point_count = 1  # actual number of points not yet known
            while point_count > 0:
                point_count = query.next(chunk_size)
                # Please note, that the buffer array length is not modified
                # by query.next(). So the arrays will always have a length
                # of 'chunk_size' but only the first 'point_count' elements
                # actually contain data.
                for i in range(point_count):
                    print(f"{buffer_xyz[i]}, {buffer_reflectance[i]}")


def example_e():
    """
    This example shows how to read points into a Pandas DataFrame.

    Please note that 'attributes' must be specified as a tuple or list.
    Do not forget to add a comma at the end if the tuple contains only
    one character string.
    """
    print("Example E:")
    frame = riegl.rdb.rdb_to_pandas(
        pointcloud=riegl.rdb.rdb_open("pointcloud.rdbx"),
        attributes=("riegl.id", "riegl.xyz", "riegl.reflectance"),
        selection="riegl.id <= 10"
    )
    print(frame.dtypes)
    print(frame)


if __name__ == "__main__":
    example_a()
    example_b()
    example_c()
    example_d()
    example_e()
