Repository: rapidsai/cuspatial (language: python)

Issue:
[BUG]: cannot construct a GeoSeries
### Version

24.10

### On which installation method(s) does this occur?

Conda

### Describe the issue

Some `cuspatial` operations that construct a `cudf.Series` are now failing like this:

> E   TypeError: Use cudf.Series._from_data for constructing a Series from ColumnAccessor or a ColumnBase

and this:

> AttributeError("'GeoSeries' object has no attribute '_data'") raised in repr()

See the summary of test failures at https://github.com/rapidsai/cuspatial/actions/runs/10287466179/job/28471526539?pr=1430.

### Minimum reproducible example

Seeing this on CI targeting `branch-24.10`. For example, from #1430: https://github.com/rapidsai/cuspatial/actions/runs/10287466179/job/28471526539?pr=1430

This does not appear to be limited to a specific subset of Python version, CPU architecture, or CUDA version.

With latest 24.10 nightly conda packages of `cudf` and `cuspatial`, this is sufficient to reproduce the first error:

```python
import cuspatial
from shapely.geometry import Polygon
cuspatial.GeoSeries([Polygon([(0, 0), (0, 1), (1, 1), (1, 0)])])
```

I haven't yet found a reproducible example for the `repr()` error, because even this empty construction fails with the first error:

```python
import cuspatial
g = cuspatial.GeoSeries([])
```

### Relevant log output

_No response_

### Environment details

<details><summary>output of print_env.sh (click me)</summary>

```shell
ACTIONS_ID_TOKEN_REQUEST_TOKEN=***
AWS_ACCESS_KEY_ID=***
AWS_DEFAULT_REGION=us-east-2
AWS_REGION=us-east-2
AWS_SECRET_ACCESS_KEY=***
AWS_SESSION_TOKEN=***
CI=true
CONDA_DEFAULT_ENV=test
CONDA_EXE=/opt/conda/bin/conda
CONDA_PREFIX=/opt/conda/envs/test
CONDA_PROMPT_MODIFIER=(test) 
CONDA_PYTHON_EXE=/opt/conda/bin/python
CONDA_SHLVL=1
CPL_ZIP_ENCODING=UTF-8
CUDA_VERSION=11.8.0
CUSPATIAL_HOME=/__w/cuspatial/cuspatial
GDAL_DATA=/opt/conda/envs/test/share/gdal
GDAL_DRIVER_PATH=/opt/conda/envs/test/lib/gdalplugins
GH_TOKEN=***
GITHUB_ACTION=__run_2
GITHUB_ACTIONS=true
GITHUB_ACTION_REF=
GITHUB_ACTION_REPOSITORY=
GITHUB_ACTOR=copy-pr-bot[bot]
GITHUB_ACTOR_ID=138252374
GITHUB_API_URL=https://api.github.com
GITHUB_BASE_REF=
GITHUB_ENV=/__w/_temp/_runner_file_commands/set_env_422e29da-92da-4226-ae2d-52b6c2cbe2f1
GITHUB_EVENT_NAME=push
GITHUB_EVENT_PATH=/github/workflow/event.json
GITHUB_GRAPHQL_URL=https://api.github.com/graphql
GITHUB_HEAD_REF=
GITHUB_JOB=tests
GITHUB_OUTPUT=/__w/_temp/_runner_file_commands/set_output_422e29da-92da-4226-ae2d-52b6c2cbe2f1
GITHUB_PATH=/__w/_temp/_runner_file_commands/add_path_422e29da-92da-4226-ae2d-52b6c2cbe2f1
GITHUB_REF=refs/heads/pull-request/1430
GITHUB_REF_NAME=pull-request/1430
GITHUB_REF_PROTECTED=false
GITHUB_REF_TYPE=branch
GITHUB_REPOSITORY=rapidsai/cuspatial
GITHUB_REPOSITORY_ID=199666905
GITHUB_REPOSITORY_OWNER=rapidsai
GITHUB_REPOSITORY_OWNER_ID=43887749
GITHUB_RETENTION_DAYS=90
GITHUB_RUN_ATTEMPT=1
GITHUB_RUN_ID=10287466179
GITHUB_RUN_NUMBER=1980
GITHUB_SERVER_URL=https://github.com
GITHUB_SHA=8f4be5d89b817fae026b8f63327a24e471ddc8a6
GITHUB_STATE=/__w/_temp/_runner_file_commands/save_state_422e29da-92da-4226-ae2d-52b6c2cbe2f1
GITHUB_STEP_SUMMARY=/__w/_temp/_runner_file_commands/step_summary_422e29da-92da-4226-ae2d-52b6c2cbe2f1
GITHUB_TRIGGERING_ACTOR=copy-pr-bot[bot]
GITHUB_WORKFLOW=pr
GITHUB_WORKFLOW_REF=rapidsai/cuspatial/.github/workflows/pr.yaml@refs/heads/pull-request/1430
GITHUB_WORKFLOW_SHA=8f4be5d89b817fae026b8f63327a24e471ddc8a6
GITHUB_WORKSPACE=/__w/cuspatial/cuspatial
GSETTINGS_SCHEMA_DIR=/opt/conda/envs/test/share/glib-2.0/schemas
GSETTINGS_SCHEMA_DIR_CONDA_BACKUP=
HOME=/github/home
HOSTNAME=aac1a87a372b
LD_LIBRARY_PATH=/usr/local/nvidia/lib:/usr/local/nvidia/lib64
NVARCH=x86_64
NVIDIA_DRIVER_CAPABILITIES=compute,utility
NVIDIA_REQUIRE_CUDA=cuda>=11.8 brand=tesla,driver>=470,driver<471 brand=unknown,driver>=470,driver<471 brand=nvidia,driver>=470,driver<471 brand=nvidiartx,driver>=470,driver<471 brand=geforce,driver>=470,driver<471 brand=geforcertx,driver>=470,driver<471 brand=quadro,driver>=470,driver<471 brand=quadrortx,driver>=470,driver<471 brand=titan,driver>=470,driver<471 brand=titanrtx,driver>=470,driver<471
NVIDIA_VISIBLE_DEVICES=GPU-c1e748cb-4c41-e95b-a6b3-e0214cbaf23c
NV_CUDA_CUDART_VERSION=11.8.89-1
PATH=/opt/conda/envs/test/bin:/opt/conda/condabin:/opt/conda/bin:/usr/local/nvidia/bin:/usr/local/cuda/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
PROJ_DATA=/opt/conda/envs/test/share/proj
PROJ_NETWORK=ON
***
PYTHON_VERSION=3.9
RAPIDS_ARTIFACTS_DIR=/__w/cuspatial/cuspatial/artifacts
RAPIDS_BUILD_TYPE=pull-request
RAPIDS_CONDA_BLD_OUTPUT_DIR=/tmp/conda-bld-output
RAPIDS_CONDA_BLD_ROOT_DIR=/tmp/conda-bld-workspace
RAPIDS_COVERAGE_DIR=/__w/cuspatial/cuspatial/coverage-results
RAPIDS_CUDA_VERSION=11.8.0
RAPIDS_NIGHTLY_DATE=
RAPIDS_PY_VERSION=3.9
RAPIDS_REF_NAME=pull-request/1430
RAPIDS_REPOSITORY=rapidsai/cuspatial
RAPIDS_SHA=8f4be5d89b817fae026b8f63327a24e471ddc8a6
RAPIDS_TESTS_DIR=/__w/cuspatial/cuspatial/test-results
RUNNER_ARCH=X64
RUNNER_ENVIRONMENT=self-hosted
RUNNER_NAME=linux-amd64-gpu-v100-earliest-1-bbxjq-runner-bg79m
RUNNER_OS=Linux
RUNNER_TEMP=/__w/_temp
RUNNER_TOOL_CACHE=/__w/_tool
RUNNER_WORKSPACE=/__w/cuspatial
SHLVL=3
XML_CATALOG_FILES=file:///opt/conda/envs/test/etc/xml/catalog file:///etc/xml/catalog
_=/usr/bin/env
_CE_CONDA=
_CE_M=

[32mRAPIDS logger[0m » [08/07/24 16:08:03]
[32m┌──────────────────┐[0m
[32m|    Print arch    |[0m
[32m└──────────────────┘[0m

x86_64

[32mRAPIDS logger[0m » [08/07/24 16:08:03]
[32m┌────────────────────────────┐[0m
[32m|    Check python version    |[0m
[32m└────────────────────────────┘[0m

Python 3.9.19

[32mRAPIDS logger[0m » [08/07/24 16:08:03]
[32m┌───────────────────────────────┐[0m
[32m|    Check conda environment    |[0m
[32m└───────────────────────────────┘[0m


     active environment : test
    active env location : /opt/conda/envs/test
            shell level : 1
       user config file : /github/home/.condarc
 populated config files : /opt/conda/.condarc
          conda version : 24.7.1
    conda-build version : 24.5.1
         python version : 3.9.19.final.0
                 solver : libmamba (default)
       virtual packages : __archspec=1=broadwell
                          __conda=24.7.1=0
                          __cuda=11.8=0
                          __glibc=2.28=0
                          __linux=5.4.0=0
                          __unix=0=0
       base environment : /opt/conda  (writable)
      conda av data dir : /opt/conda/etc/conda
  conda av metadata url : None
           channel URLs : https://conda.anaconda.org/rapidsai/linux-64
                          https://conda.anaconda.org/rapidsai/noarch
                          https://conda.anaconda.org/rapidsai-nightly/linux-64
                          https://conda.anaconda.org/rapidsai-nightly/noarch
                          https://conda.anaconda.org/dask/label/dev/linux-64
                          https://conda.anaconda.org/dask/label/dev/noarch
                          https://conda.anaconda.org/pytorch/linux-64
                          https://conda.anaconda.org/pytorch/noarch
                          https://conda.anaconda.org/conda-forge/linux-64
                          https://conda.anaconda.org/conda-forge/noarch
                          https://conda.anaconda.org/nvidia/linux-64
                          https://conda.anaconda.org/nvidia/noarch
          package cache : /opt/conda/pkgs
                          /github/home/.conda/pkgs
       envs directories : /opt/conda/envs
                          /github/home/.conda/envs
               platform : linux-64
             user-agent : conda/24.7.1 requests/2.32.3 CPython/3.9.19 Linux/5.4.0-190-generic rocky/8.10 glibc/2.28 solver/libmamba conda-libmamba-solver/24.7.0 libmambapy/1.5.8
                UID:GID : 0:0
             netrc file : None
            offline mode : False
 
 
 ==> /opt/conda/.condarc <==
 auto_update_conda: False
 channels:
   - rapidsai
   - rapidsai-nightly
   - dask/label/dev
   - pytorch
   - conda-forge
   - nvidia
 always_yes: True
 number_channel_notices: 0
 conda-build:
   set_build_id: False
   root_dir: /tmp/conda-bld-workspace
   output_folder: /tmp/conda-bld-output
 
 ==> envvars <==
 allow_softlinks: False
 
 # packages in environment at /opt/conda/envs/test:
 #
 # Name                    Version                   Build  Channel
 _libgcc_mutex             0.1                 conda_forge    conda-forge
 _openmp_mutex             4.5                       2_gnu    conda-forge
 aws-c-auth                0.7.22              hbd3ac97_10    conda-forge
 aws-c-cal                 0.7.1                h87b94db_1    conda-forge
 aws-c-common              0.9.23               h4ab18f5_0    conda-forge
 aws-c-compression         0.2.18               he027950_7    conda-forge
 aws-c-event-stream        0.4.2               h7671281_15    conda-forge
 aws-c-http                0.8.2                he17ee6b_6    conda-forge
 aws-c-io                  0.14.10              h826b7d6_1    conda-forge
 aws-c-mqtt                0.10.4               hcd6a914_8    conda-forge
 aws-c-s3                  0.6.0                h365ddd8_2    conda-forge
 aws-c-sdkutils            0.1.16               he027950_3    conda-forge
 aws-checksums             0.1.18               he027950_7    conda-forge
 aws-crt-cpp               0.27.3               hda66527_2    conda-forge
 aws-sdk-cpp               1.11.329             h46c3b66_9    conda-forge
 azure-core-cpp            1.13.0               h935415a_0    conda-forge
 azure-identity-cpp        1.8.0                hd126650_2    conda-forge
 azure-storage-blobs-cpp   12.12.0              hd2e3451_0    conda-forge
 azure-storage-common-cpp  12.7.0               h10ac4d7_1    conda-forge
 azure-storage-files-datalake-cpp 12.11.0              h325d260_1    conda-forge
 blosc                     1.21.6               hef167b5_0    conda-forge
 branca                    0.7.2              pyhd8ed1ab_0    conda-forge
 brotli                    1.1.0                hd590300_1    conda-forge
 brotli-bin                1.1.0                hd590300_1    conda-forge
 brotli-python             1.1.0            py39h3d6467e_1    conda-forge
 bzip2                     1.0.8                h4bc722e_7    conda-forge
 c-ares                    1.32.3               h4bc722e_0    conda-forge
 ca-certificates           2024.7.4             hbcca054_0    conda-forge
 cachetools                5.4.0              pyhd8ed1ab_0    conda-forge
 cairo                     1.18.0               h3faef2a_0    conda-forge
 certifi                   2024.7.4           pyhd8ed1ab_0    conda-forge
 cffi                      1.17.0           py39h49a4b6b_0    conda-forge
 cfitsio                   4.3.1                hbdc6101_0    conda-forge
 charset-normalizer        3.3.2              pyhd8ed1ab_0    conda-forge
 colorama                  0.4.6              pyhd8ed1ab_0    conda-forge
 contourpy                 1.2.1            py39h7633fee_0    conda-forge
 coverage                  7.6.1            py39hcd6043d_0    conda-forge
 cubinlinker               0.3.0            py39hac6bf05_1    rapidsai
 cuda-python               11.8.3           py39h6461835_2    conda-forge
 cuda-version              11.8                 h70ddcb2_3    conda-forge
 cudatoolkit               11.8.0              h4ba93d1_13    conda-forge
 cudf                      24.10.00a150    cuda11_py39_240807_g6b0bff4b09_150    rapidsai-nightly
 cuproj                    24.10.00a24     cuda11_py39_240807_g8f4be5d8_24    file:///tmp/python_channel
 cupy                      13.2.0           py39h8710ada_1    conda-forge
 cupy-core                 13.2.0           py39h4d139e5_1    conda-forge
 cuspatial                 24.10.00a24     cuda11_py39_240807_g8f4be5d8_24    file:///tmp/python_channel
 cycler                    0.12.1             pyhd8ed1ab_0    conda-forge
 dlpack                    0.8                  h59595ed_3    conda-forge
 exceptiongroup            1.2.2              pyhd8ed1ab_0    conda-forge
 execnet                   2.1.1              pyhd8ed1ab_0    conda-forge
 expat                     2.6.2                h59595ed_0    conda-forge
 fastrlock                 0.8.2            py39h3d6467e_2    conda-forge
 fmt                       10.2.1               h00ab1b0_0    conda-forge
 folium                    0.17.0             pyhd8ed1ab_0    conda-forge
 font-ttf-dejavu-sans-mono 2.37                 hab24e00_0    conda-forge
 font-ttf-inconsolata      3.000                h77eed37_0    conda-forge
 font-ttf-source-code-pro  2.038                h77eed37_0    conda-forge
 font-ttf-ubuntu           0.83                 h77eed37_2    conda-forge
 fontconfig                2.14.2               h14ed4e7_0    conda-forge
 fonts-conda-ecosystem     1                             0    conda-forge
 fonts-conda-forge         1                             0    conda-forge
 fonttools                 4.53.1           py39hcd6043d_0    conda-forge
 freetype                  2.12.1               h267a509_2    conda-forge
 freexl                    2.0.0                h743c826_0    conda-forge
 fsspec                    2024.6.1           pyhff2d567_0    conda-forge
 gdal                      3.8.1            py39h14df8fe_3    conda-forge
 geopandas                 1.0.1              pyhd8ed1ab_0    conda-forge
 geopandas-base            1.0.1              pyha770c72_0    conda-forge
 geos                      3.12.1               h59595ed_0    conda-forge
 geotiff                   1.7.1               hf074850_14    conda-forge
 gettext                   0.22.5               h59595ed_2    conda-forge
 gettext-tools             0.22.5               h59595ed_2    conda-forge
 gflags                    2.2.2             he1b5a44_1004    conda-forge
 giflib                    5.2.2                hd590300_0    conda-forge
 glog                      0.7.1                hbabe93e_0    conda-forge
 h2                        4.1.0              pyhd8ed1ab_0    conda-forge
 hdf4                      4.2.15               h2a13503_7    conda-forge
 hdf5                      1.14.3          nompi_hdf9ad27_105    conda-forge
 hpack                     4.0.0              pyh9f0ad1d_0    conda-forge
 hyperframe                6.0.1              pyhd8ed1ab_0    conda-forge
 icu                       73.2                 h59595ed_0    conda-forge
 idna                      3.7                pyhd8ed1ab_0    conda-forge
 importlib-resources       6.4.0              pyhd8ed1ab_0    conda-forge
 importlib_resources       6.4.0              pyhd8ed1ab_0    conda-forge
 iniconfig                 2.0.0              pyhd8ed1ab_0    conda-forge
 jinja2                    3.1.4              pyhd8ed1ab_0    conda-forge
 joblib                    1.4.2              pyhd8ed1ab_0    conda-forge
 json-c                    0.17                 h1220068_1    conda-forge
 kealib                    1.5.3                hee9dde6_1    conda-forge
 keyutils                  1.6.1                h166bdaf_0    conda-forge
 kiwisolver                1.4.5            py39h7633fee_1    conda-forge
 krb5                      1.21.3               h659f571_0    conda-forge
 lcms2                     2.16                 hb7c19ff_0    conda-forge
 ld_impl_linux-64          2.40                 hf3520f5_7    conda-forge
 lerc                      4.0.0                h27087fc_0    conda-forge
 libabseil                 20240116.2      cxx17_he02047a_1    conda-forge
 libaec                    1.1.3                h59595ed_0    conda-forge
 libarchive                3.7.4                hfca40fe_0    conda-forge
 libarrow                  16.1.0          h4b47046_16_cpu    conda-forge
 libarrow-acero            16.1.0          he02047a_16_cpu    conda-forge
 libarrow-dataset          16.1.0          he02047a_16_cpu    conda-forge
 libarrow-substrait        16.1.0          hc9a23c6_16_cpu    conda-forge
 libasprintf               0.22.5               h661eb56_2    conda-forge
 libasprintf-devel         0.22.5               h661eb56_2    conda-forge
 libblas                   3.9.0           23_linux64_openblas    conda-forge
 libbrotlicommon           1.1.0                hd590300_1    conda-forge
 libbrotlidec              1.1.0                hd590300_1    conda-forge
 libbrotlienc              1.1.0                hd590300_1    conda-forge
 libcblas                  3.9.0           23_linux64_openblas    conda-forge
 libcrc32c                 1.1.2                h9c3ff4c_0    conda-forge
 libcudf                   24.10.00a150    cuda11_240807_g6b0bff4b09_150    rapidsai-nightly
 libcufile                 1.4.0.31                      0    nvidia
 libcufile-dev             1.4.0.31                      0    nvidia
 libcurl                   8.9.1                hdb1bdb2_0    conda-forge
 libcuspatial              24.10.00a24     cuda11_240807_g8f4be5d8_24    file:///tmp/cpp_channel
 libdeflate                1.19                 hd590300_0    conda-forge
 libedit                   3.1.20191231         he28a2e2_2    conda-forge
 libev                     4.33                 hd590300_2    conda-forge
 libevent                  2.1.12               hf998b51_1    conda-forge
 libexpat                  2.6.2                h59595ed_0    conda-forge
 libffi                    3.4.2                h7f98852_5    conda-forge
 libgcc-ng                 14.1.0               h77fa898_0    conda-forge
 libgdal                   3.8.1                h4b8bffa_3    conda-forge
 libgettextpo              0.22.5               h59595ed_2    conda-forge
 libgettextpo-devel        0.22.5               h59595ed_2    conda-forge
 libgfortran-ng            14.1.0               h69a702a_0    conda-forge
 libgfortran5              14.1.0               hc5f4f2c_0    conda-forge
 libglib                   2.78.4               h783c2da_0    conda-forge
 libgomp                   14.1.0               h77fa898_0    conda-forge
 libgoogle-cloud           2.26.0               h26d7fe4_0    conda-forge
 libgoogle-cloud-storage   2.26.0               ha262f82_0    conda-forge
 libgrpc                   1.62.2               h15f2491_0    conda-forge
 libiconv                  1.17                 hd590300_2    conda-forge
 libjpeg-turbo             3.0.0                hd590300_1    conda-forge
 libkml                    1.3.0             hbbc8833_1020    conda-forge
 libkvikio                 24.10.00a       cuda11_240807_g865c881_22    rapidsai-nightly
 liblapack                 3.9.0           23_linux64_openblas    conda-forge
 libllvm14                 14.0.6               hcd5def8_4    conda-forge
 libnetcdf                 4.9.2           nompi_h135f659_114    conda-forge
 libnghttp2                1.58.0               h47da74e_1    conda-forge
 libnsl                    2.0.1                hd590300_0    conda-forge
 libopenblas               0.3.27          pthreads_hac2b453_1    conda-forge
 libparquet                16.1.0          h9e5060d_16_cpu    conda-forge
 libpng                    1.6.43               h2797004_0    conda-forge
 libpq                     16.3                 ha72fbe1_0    conda-forge
 libprotobuf               4.25.3               h08a7969_0    conda-forge
 libre2-11                 2023.09.01           h5a48ba9_2    conda-forge
 librmm                    24.10.00a18     cuda11_240807_g47899659_18    rapidsai-nightly
 librttopo                 1.1.0               h8917695_15    conda-forge
 libspatialite             5.1.0                h72606ae_3    conda-forge
 libsqlite                 3.46.0               hde9e2c9_0    conda-forge
 libssh2                   1.11.0               h0841786_0    conda-forge
 libstdcxx-ng              14.1.0               hc0a3c3a_0    conda-forge
 libthrift                 0.19.0               hb90f79a_1    conda-forge
 libtiff                   4.6.0                ha9c0a0a_2    conda-forge
 libutf8proc               2.8.0                h166bdaf_0    conda-forge
 libuuid                   2.38.1               h0b41bf4_0    conda-forge
 libwebp-base              1.4.0                hd590300_0    conda-forge
 libxcb                    1.15                 h0b41bf4_0    conda-forge
 libxcrypt                 4.4.36               hd590300_1    conda-forge
 libxml2                   2.12.7               h4c95cb1_3    conda-forge
 libzip                    1.10.1               h2629f0a_3    conda-forge
 libzlib                   1.3.1                h4ab18f5_1    conda-forge
 llvmlite                  0.43.0           py39h81bab63_0    conda-forge
 lz4-c                     1.9.4                hcb278e6_0    conda-forge
 lzo                       2.10              hd590300_1001    conda-forge
 mapclassify               2.7.0              pyhd8ed1ab_0    conda-forge
 markdown-it-py            3.0.0              pyhd8ed1ab_0    conda-forge
 markupsafe                2.1.5            py39hd1e30aa_0    conda-forge
 matplotlib-base           3.9.1            py39h0565ad7_2    conda-forge
 mdurl                     0.1.2              pyhd8ed1ab_0    conda-forge
 minizip                   4.0.7                h401b404_0    conda-forge
 munkres                   1.1.4              pyh9f0ad1d_0    conda-forge
 ncurses                   6.5                  h59595ed_0    conda-forge
 networkx                  3.2.1              pyhd8ed1ab_0    conda-forge
 nspr                      4.35                 h27087fc_0    conda-forge
 nss                       3.103                h593d115_0    conda-forge
 numba                     0.60.0           py39h0320e7d_0    conda-forge
 numpy                     1.26.4           py39h474f0d3_0    conda-forge
 nvcomp                    3.0.6                h56904bc_0    conda-forge
 nvtx                      0.2.10           py39hd1e30aa_0    conda-forge
 openjpeg                  2.5.2                h488ebb8_0    conda-forge
 openssl                   3.3.1                h4bc722e_2    conda-forge
 orc                       2.0.1                h17fec99_1    conda-forge
 packaging                 24.1               pyhd8ed1ab_0    conda-forge
 pandas                    2.2.2            py39hfc16268_1    conda-forge
 pcre2                     10.42                hcad00b1_0    conda-forge
 pillow                    10.3.0           py39h90c7501_0    conda-forge
 pip                       24.2               pyhd8ed1ab_0    conda-forge
 pixman                    0.43.2               h59595ed_0    conda-forge
 pluggy                    1.5.0              pyhd8ed1ab_0    conda-forge
 poppler                   23.12.0              h590f24d_0    conda-forge
 poppler-data              0.4.12               hd8ed1ab_0    conda-forge
 postgresql                16.3                 h8e811e2_0    conda-forge
 proj                      9.3.0                h1d62c97_2    conda-forge
 pthread-stubs             0.4               h36c2ea0_1001    conda-forge
 ptxcompiler               0.8.1            py39h5232d60_2    conda-forge
 pyarrow                   16.1.0           py39he8689d4_5    conda-forge
 pyarrow-core              16.1.0          py39hb8ba4b6_5_cpu    conda-forge
 pycparser                 2.22               pyhd8ed1ab_0    conda-forge
 pygments                  2.18.0             pyhd8ed1ab_0    conda-forge
 pyogrio                   0.7.2            py39hcfcd403_1    conda-forge
 pyparsing                 3.1.2              pyhd8ed1ab_0    conda-forge
 pyproj                    3.6.1            py39hce394fd_4    conda-forge
 pysocks                   1.7.1              pyha2e5f31_6    conda-forge
 pytest                    8.3.2              pyhd8ed1ab_0    conda-forge
 pytest-cov                5.0.0              pyhd8ed1ab_0    conda-forge
 pytest-xdist              3.6.1              pyhd8ed1ab_0    conda-forge
 python                    3.9.19          h0755675_0_cpython    conda-forge
 python-dateutil           2.9.0              pyhd8ed1ab_0    conda-forge
 python-tzdata             2024.1             pyhd8ed1ab_0    conda-forge
 python_abi                3.9                      4_cp39    conda-forge
 pytz                      2024.1             pyhd8ed1ab_0    conda-forge
 qhull                     2020.2               h434a139_5    conda-forge
 re2                       2023.09.01           h7f4b329_2    conda-forge
 readline                  8.2                  h8228510_1    conda-forge
 requests                  2.32.3             pyhd8ed1ab_0    conda-forge
 rich                      13.7.1             pyhd8ed1ab_0    conda-forge
 rmm                       24.10.00a18     cuda11_py39_240807_g47899659_18    rapidsai-nightly
 s2n                       1.4.17               he19d79f_0    conda-forge
 scikit-learn              1.5.1            py39hf7b0125_0    conda-forge
 scipy                     1.13.1           py39haf93ffa_0    conda-forge
 setuptools                72.1.0             pyhd8ed1ab_0    conda-forge
 shapely                   2.0.4            py39h5a575da_1    conda-forge
 six                       1.16.0             pyh6c4a22f_0    conda-forge
 snappy                    1.2.1                ha2e4443_0    conda-forge
 spdlog                    1.12.0               hd2e6256_2    conda-forge
 sqlite                    3.46.0               h6d4b2fc_0    conda-forge
 threadpoolctl             3.5.0              pyhc1e730c_0    conda-forge
 tiledb                    2.18.2               h99f50a1_1    conda-forge
 tk                        8.6.13          noxft_h4845f30_101    conda-forge
 toml                      0.10.2             pyhd8ed1ab_0    conda-forge
 tomli                     2.0.1              pyhd8ed1ab_0    conda-forge
 typing_extensions         4.12.2             pyha770c72_0    conda-forge
 tzcode                    2024a                h3f72095_0    conda-forge
 tzdata                    2024a                h0c530f3_0    conda-forge
 unicodedata2              15.1.0           py39hd1e30aa_0    conda-forge
 uriparser                 0.9.8                hac33072_0    conda-forge
 urllib3                   2.2.2              pyhd8ed1ab_1    conda-forge
 wheel                     0.44.0             pyhd8ed1ab_0    conda-forge
 xerces-c                  3.2.5                hac6953d_0    conda-forge
 xorg-kbproto              1.0.7             h7f98852_1002    conda-forge
 xorg-libice               1.1.1                hd590300_0    conda-forge
 xorg-libsm                1.2.4                h7391055_0    conda-forge
 xorg-libx11               1.8.9                h8ee46fc_0    conda-forge
 xorg-libxau               1.0.11               hd590300_0    conda-forge
 xorg-libxdmcp             1.1.3                h7f98852_0    conda-forge
 xorg-libxext              1.3.4                h0b41bf4_2    conda-forge
 xorg-libxrender           0.9.11               hd590300_0    conda-forge
 xorg-renderproto          0.11.1            h7f98852_1002    conda-forge
 xorg-xextproto            7.3.0             h0b41bf4_1003    conda-forge
 xorg-xproto               7.0.31            h7f98852_1007    conda-forge
 xyzservices               2024.6.0           pyhd8ed1ab_0    conda-forge
 xz                        5.2.6                h166bdaf_0    conda-forge
 zipp                      3.19.2             pyhd8ed1ab_0    conda-forge
 zlib                      1.3.1                h4ab18f5_1    conda-forge
 zstandard                 0.23.0           py39h623c9ba_0    conda-forge
 zstd                      1.5.6                ha6fb4c9_0    conda-forge
```

</details>

### Other/Misc.

_No response_
[BUG]: conda devcontainers build failing: "No module named 'libcudf'"
### Version

24.10

### On which installation method(s) does this occur?

_No response_

### Describe the issue

See https://github.com/rapidsai/cuspatial/pull/1454#issuecomment-2332722073.

Since #1450 was merged, conda devcontainers build (but not pip devcontainers) here have been failing like this at the step where they build `cuproj`:

```text
      File "/home/coder/cuspatial/python/libcuspatial/libcuspatial/__init__.py", line 16, in <module>
  
        from libcuspatial.load import load_library
  
      File "/home/coder/cuspatial/python/libcuspatial/libcuspatial/load.py", line 19, in <module>
  
        import libcudf
  
    ModuleNotFoundError: No module named 'libcudf'
  
    error: subprocess-exited-with-error
    
    × Building editable for cuproj-cu12 (pyproject.toml) did not run successfully.
    │ exit code: 1
    ╰─> See above for output.
    
    note: This error originates from a subprocess, and is likely not a problem with pip.
  
    full command: /home/coder/.conda/envs/rapids/bin/python /home/coder/.conda/envs/rapids/lib/python3.11/site-packages/pip/_vendor/pyproject_hooks/_in_process/_in_process.py build_editable /tmp/tmp3owulgd3
    cwd: /home/coder/cuspatial/python/cuproj
    Building editable for cuproj-cu12 (pyproject.toml): finished with status 'error'
  
    ERROR: Failed building editable for cuproj-cu12
  
  Failed to build cuproj-cu12
```

([build link](https://github.com/rapidsai/cuspatial/actions/runs/10713942205/job/29754070823?pr=1454))

### Minimum reproducible example

_No response_

### Relevant log output

_No response_

### Environment details

_No response_

### Other/Misc.

After https://github.com/rapidsai/devcontainers/pull/387 was merged, I re-ran the **pip**, but not **conda** devcontainers jobs on #1450.

That might explain how this wasn't caught in CI before.


Discussion / hints:

Fix patch:
diff --git a/.devcontainer/cuda11.8-conda/devcontainer.json b/.devcontainer/cuda11.8-conda/devcontainer.json
index a50f61dac..646640085 100644
--- a/.devcontainer/cuda11.8-conda/devcontainer.json
+++ b/.devcontainer/cuda11.8-conda/devcontainer.json
@@ -5,17 +5,17 @@
     "args": {
       "CUDA": "11.8",
       "PYTHON_PACKAGE_MANAGER": "conda",
-      "BASE": "rapidsai/devcontainers:24.08-cpp-cuda11.8-mambaforge-ubuntu22.04"
+      "BASE": "rapidsai/devcontainers:24.10-cpp-cuda11.8-mambaforge-ubuntu22.04"
     }
   },
   "runArgs": [
     "--rm",
     "--name",
-    "${localEnv:USER:anon}-rapids-${localWorkspaceFolderBasename}-24.08-cuda11.8-conda"
+    "${localEnv:USER:anon}-rapids-${localWorkspaceFolderBasename}-24.10-cuda11.8-conda"
   ],
   "hostRequirements": {"gpu": "optional"},
   "features": {
-    "ghcr.io/rapidsai/devcontainers/features/rapids-build-utils:24.8": {}
+    "ghcr.io/rapidsai/devcontainers/features/rapids-build-utils:24.10": {}
   },
   "overrideFeatureInstallOrder": [
     "ghcr.io/rapidsai/devcontainers/features/rapids-build-utils"
diff --git a/.devcontainer/cuda11.8-pip/devcontainer.json b/.devcontainer/cuda11.8-pip/devcontainer.json
index 11b8ad305..b29f6a10e 100644
--- a/.devcontainer/cuda11.8-pip/devcontainer.json
+++ b/.devcontainer/cuda11.8-pip/devcontainer.json
@@ -5,17 +5,17 @@
     "args": {
       "CUDA": "11.8",
       "PYTHON_PACKAGE_MANAGER": "pip",
-      "BASE": "rapidsai/devcontainers:24.08-cpp-cuda11.8-ubuntu22.04"
+      "BASE": "rapidsai/devcontainers:24.10-cpp-cuda11.8-ubuntu22.04"
     }
   },
   "runArgs": [
     "--rm",
     "--name",
-    "${localEnv:USER:anon}-rapids-${localWorkspaceFolderBasename}-24.08-cuda11.8-pip"
+    "${localEnv:USER:anon}-rapids-${localWorkspaceFolderBasename}-24.10-cuda11.8-pip"
   ],
   "hostRequirements": {"gpu": "optional"},
   "features": {
-    "ghcr.io/rapidsai/devcontainers/features/rapids-build-utils:24.8": {}
+    "ghcr.io/rapidsai/devcontainers/features/rapids-build-utils:24.10": {}
   },
   "overrideFeatureInstallOrder": [
     "ghcr.io/rapidsai/devcontainers/features/rapids-build-utils"
diff --git a/.devcontainer/cuda12.5-conda/devcontainer.json b/.devcontainer/cuda12.5-conda/devcontainer.json
index 33328e98f..7b5a6efaa 100644
--- a/.devcontainer/cuda12.5-conda/devcontainer.json
+++ b/.devcontainer/cuda12.5-conda/devcontainer.json
@@ -5,17 +5,17 @@
     "args": {
       "CUDA": "12.5",
       "PYTHON_PACKAGE_MANAGER": "conda",
-      "BASE": "rapidsai/devcontainers:24.08-cpp-mambaforge-ubuntu22.04"
+      "BASE": "rapidsai/devcontainers:24.10-cpp-mambaforge-ubuntu22.04"
     }
   },
   "runArgs": [
     "--rm",
     "--name",
-    "${localEnv:USER:anon}-rapids-${localWorkspaceFolderBasename}-24.08-cuda12.5-conda"
+    "${localEnv:USER:anon}-rapids-${localWorkspaceFolderBasename}-24.10-cuda12.5-conda"
   ],
   "hostRequirements": {"gpu": "optional"},
   "features": {
-    "ghcr.io/rapidsai/devcontainers/features/rapids-build-utils:24.8": {}
+    "ghcr.io/rapidsai/devcontainers/features/rapids-build-utils:24.10": {}
   },
   "overrideFeatureInstallOrder": [
     "ghcr.io/rapidsai/devcontainers/features/rapids-build-utils"
diff --git a/.devcontainer/cuda12.5-pip/devcontainer.json b/.devcontainer/cuda12.5-pip/devcontainer.json
index 5dca1ccc5..1d3a82749 100644
--- a/.devcontainer/cuda12.5-pip/devcontainer.json
+++ b/.devcontainer/cuda12.5-pip/devcontainer.json
@@ -5,17 +5,17 @@
     "args": {
       "CUDA": "12.5",
       "PYTHON_PACKAGE_MANAGER": "pip",
-      "BASE": "rapidsai/devcontainers:24.08-cpp-cuda12.5-ubuntu22.04"
+      "BASE": "rapidsai/devcontainers:24.10-cpp-cuda12.5-ubuntu22.04"
     }
   },
   "runArgs": [
     "--rm",
     "--name",
-    "${localEnv:USER:anon}-rapids-${localWorkspaceFolderBasename}-24.08-cuda12.5-pip"
+    "${localEnv:USER:anon}-rapids-${localWorkspaceFolderBasename}-24.10-cuda12.5-pip"
   ],
   "hostRequirements": {"gpu": "optional"},
   "features": {
-    "ghcr.io/rapidsai/devcontainers/features/rapids-build-utils:24.8": {}
+    "ghcr.io/rapidsai/devcontainers/features/rapids-build-utils:24.10": {}
   },
   "overrideFeatureInstallOrder": [
     "ghcr.io/rapidsai/devcontainers/features/rapids-build-utils"
diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml
index b5e2dbb44..d86122f5a 100644
--- a/.github/workflows/build.yaml
+++ b/.github/workflows/build.yaml
@@ -28,7 +28,7 @@ concurrency:
 jobs:
   cpp-build:
     secrets: inherit
-    uses: rapidsai/shared-workflows/.github/workflows/conda-cpp-build.yaml@branch-24.08
+    uses: rapidsai/shared-workflows/.github/workflows/conda-cpp-build.yaml@branch-24.10
     with:
       build_type: ${{ inputs.build_type || 'branch' }}
       branch: ${{ inputs.branch }}
@@ -37,7 +37,7 @@ jobs:
   python-build:
     needs: [cpp-build]
     secrets: inherit
-    uses: rapidsai/shared-workflows/.github/workflows/conda-python-build.yaml@branch-24.08
+    uses: rapidsai/shared-workflows/.github/workflows/conda-python-build.yaml@branch-24.10
     with:
       build_type: ${{ inputs.build_type || 'branch' }}
       branch: ${{ inputs.branch }}
@@ -46,7 +46,7 @@ jobs:
   upload-conda:
     needs: [cpp-build, python-build]
     secrets: inherit
-    uses: rapidsai/shared-workflows/.github/workflows/conda-upload-packages.yaml@branch-24.08
+    uses: rapidsai/shared-workflows/.github/workflows/conda-upload-packages.yaml@branch-24.10
     with:
       build_type: ${{ inputs.build_type || 'branch' }}
       branch: ${{ inputs.branch }}
@@ -56,7 +56,7 @@ jobs:
     if: github.ref_type == 'branch'
     needs: python-build
     secrets: inherit
-    uses: rapidsai/shared-workflows/.github/workflows/custom-job.yaml@branch-24.08
+    uses: rapidsai/shared-workflows/.github/workflows/custom-job.yaml@branch-24.10
     with:
       arch: "amd64"
       branch: ${{ inputs.branch }}
@@ -66,9 +66,32 @@ jobs:
       node_type: "gpu-v100-latest-1"
       run_script: "ci/build_docs.sh"
       sha: ${{ inputs.sha }}
+  wheel-build-libcuspatial:
+    secrets: inherit
+    uses: rapidsai/shared-workflows/.github/workflows/wheels-build.yaml@branch-24.10
+    with:
+      # build for every combination of arch and CUDA version, but only for the latest Python
+      matrix_filter: group_by([.ARCH, (.CUDA_VER|split(".")|map(tonumber)|.[0])]) | map(max_by(.PY_VER|split(".")|map(tonumber)))
+      build_type: ${{ inputs.build_type || 'branch' }}
+      branch: ${{ inputs.branch }}
+      sha: ${{ inputs.sha }}
+      date: ${{ inputs.date }}
+      script: ci/build_wheel_libcuspatial.sh
+  wheel-publish-libcuspatial:
+    needs: wheel-build-libcuspatial
+    secrets: inherit
+    uses: rapidsai/shared-workflows/.github/workflows/wheels-publish.yaml@branch-24.10
+    with:
+      build_type: ${{ inputs.build_type || 'branch' }}
+      branch: ${{ inputs.branch }}
+      sha: ${{ inputs.sha }}
+      date: ${{ inputs.date }}
+      package-name: libcuspatial
+      package-type: cpp
   wheel-build-cuspatial:
+    needs: wheel-publish-libcuspatial
     secrets: inherit
-    uses: rapidsai/shared-workflows/.github/workflows/wheels-build.yaml@branch-24.08
+    uses: rapidsai/shared-workflows/.github/workflows/wheels-build.yaml@branch-24.10
     with:
       build_type: ${{ inputs.build_type || 'branch' }}
       branch: ${{ inputs.branch }}
@@ -78,16 +101,17 @@ jobs:
   wheel-publish-cuspatial:
     needs: wheel-build-cuspatial
     secrets: inherit
-    uses: rapidsai/shared-workflows/.github/workflows/wheels-publish.yaml@branch-24.08
+    uses: rapidsai/shared-workflows/.github/workflows/wheels-publish.yaml@branch-24.10
     with:
       build_type: ${{ inputs.build_type || 'branch' }}
       branch: ${{ inputs.branch }}
       sha: ${{ inputs.sha }}
       date: ${{ inputs.date }}
       package-name: cuspatial
+      package-type: python
   wheel-build-cuproj:
     secrets: inherit
-    uses: rapidsai/shared-workflows/.github/workflows/wheels-build.yaml@branch-24.08
+    uses: rapidsai/shared-workflows/.github/workflows/wheels-build.yaml@branch-24.10
     with:
       build_type: ${{ inputs.build_type || 'branch' }}
       branch: ${{ inputs.branch }}
@@ -97,10 +121,11 @@ jobs:
   wheel-publish-cuproj:
     needs: wheel-build-cuproj
     secrets: inherit
-    uses: rapidsai/shared-workflows/.github/workflows/wheels-publish.yaml@branch-24.08
+    uses: rapidsai/shared-workflows/.github/workflows/wheels-publish.yaml@branch-24.10
     with:
       build_type: ${{ inputs.build_type || 'branch' }}
       branch: ${{ inputs.branch }}
       sha: ${{ inputs.sha }}
       date: ${{ inputs.date }}
       package-name: cuproj
+      package-type: python
diff --git a/.github/workflows/pr.yaml b/.github/workflows/pr.yaml
index 2c76c0a45..94617d953 100644
--- a/.github/workflows/pr.yaml
+++ b/.github/workflows/pr.yaml
@@ -14,51 +14,60 @@ jobs:
     needs:
       - checks
       - conda-cpp-build
+      - conda-cpp-checks
       - conda-cpp-tests
       - conda-python-build
       - conda-python-tests
       - conda-notebook-tests
       - docs-build
       - wheel-build-cuspatial
+      - wheel-build-libcuspatial
       - wheel-tests-cuspatial
       - wheel-build-cuproj
       - wheel-tests-cuproj
       - devcontainer
     secrets: inherit
-    uses: rapidsai/shared-workflows/.github/workflows/pr-builder.yaml@branch-24.08
+    uses: rapidsai/shared-workflows/.github/workflows/pr-builder.yaml@branch-24.10
   checks:
     secrets: inherit
-    uses: rapidsai/shared-workflows/.github/workflows/checks.yaml@branch-24.08
+    uses: rapidsai/shared-workflows/.github/workflows/checks.yaml@branch-24.10
     with:
       enable_check_generated_files: false
   conda-cpp-build:
     needs: checks
     secrets: inherit
-    uses: rapidsai/shared-workflows/.github/workflows/conda-cpp-build.yaml@branch-24.08
+    uses: rapidsai/shared-workflows/.github/workflows/conda-cpp-build.yaml@branch-24.10
     with:
       build_type: pull-request
+  conda-cpp-checks:
+    needs: conda-cpp-build
+    secrets: inherit
+    uses: rapidsai/shared-workflows/.github/workflows/conda-cpp-post-build-checks.yaml@branch-24.10
+    with:
+      build_type: pull-request
+      enable_check_symbols: true
   conda-cpp-tests:
     needs: conda-cpp-build
     secrets: inherit
-    uses: rapidsai/shared-workflows/.github/workflows/conda-cpp-tests.yaml@branch-24.08
+    uses: rapidsai/shared-workflows/.github/workflows/conda-cpp-tests.yaml@branch-24.10
     with:
       build_type: pull-request
   conda-python-build:
     needs: conda-cpp-build
     secrets: inherit
-    uses: rapidsai/shared-workflows/.github/workflows/conda-python-build.yaml@branch-24.08
+    uses: rapidsai/shared-workflows/.github/workflows/conda-python-build.yaml@branch-24.10
     with:
       build_type: pull-request
   conda-python-tests:
     needs: conda-python-build
     secrets: inherit
-    uses: rapidsai/shared-workflows/.github/workflows/conda-python-tests.yaml@branch-24.08
+    uses: rapidsai/shared-workflows/.github/workflows/conda-python-tests.yaml@branch-24.10
     with:
       build_type: pull-request
   conda-notebook-tests:
     needs: conda-python-build
     secrets: inherit
-    uses: rapidsai/shared-workflows/.github/workflows/custom-job.yaml@branch-24.08
+    uses: rapidsai/shared-workflows/.github/workflows/custom-job.yaml@branch-24.10
     with:
       build_type: pull-request
       node_type: "gpu-v100-latest-1"
@@ -68,44 +77,53 @@ jobs:
   docs-build:
     needs: conda-python-build
     secrets: inherit
-    uses: rapidsai/shared-workflows/.github/workflows/custom-job.yaml@branch-24.08
+    uses: rapidsai/shared-workflows/.github/workflows/custom-job.yaml@branch-24.10
     with:
       build_type: pull-request
       node_type: "gpu-v100-latest-1"
       arch: "amd64"
       container_image: "rapidsai/ci-conda:latest"
       run_script: "ci/build_docs.sh"
-  wheel-build-cuspatial:
+  wheel-build-libcuspatial:
     needs: checks
     secrets: inherit
-    uses: rapidsai/shared-workflows/.github/workflows/wheels-build.yaml@branch-24.08
+    uses: rapidsai/shared-workflows/.github/workflows/wheels-build.yaml@branch-24.10
+    with:
+      # build for every combination of arch and CUDA version, but only for the latest Python
+      matrix_filter: group_by([.ARCH, (.CUDA_VER|split(".")|map(tonumber)|.[0])]) | map(max_by(.PY_VER|split(".")|map(tonumber)))
+      build_type: pull-request
+      script: ci/build_wheel_libcuspatial.sh
+  wheel-build-cuspatial:
+    needs: [checks, wheel-build-libcuspatial]
+    secrets: inherit
+    uses: rapidsai/shared-workflows/.github/workflows/wheels-build.yaml@branch-24.10
     with:
       build_type: pull-request
       script: ci/build_wheel_cuspatial.sh
   wheel-tests-cuspatial:
     needs: wheel-build-cuspatial
     secrets: inherit
-    uses: rapidsai/shared-workflows/.github/workflows/wheels-test.yaml@branch-24.08
+    uses: rapidsai/shared-workflows/.github/workflows/wheels-test.yaml@branch-24.10
     with:
       build_type: pull-request
       script: ci/test_wheel_cuspatial.sh
   wheel-build-cuproj:
     needs: checks
     secrets: inherit
-    uses: rapidsai/shared-workflows/.github/workflows/wheels-build.yaml@branch-24.08
+    uses: rapidsai/shared-workflows/.github/workflows/wheels-build.yaml@branch-24.10
     with:
       build_type: pull-request
       script: ci/build_wheel_cuproj.sh
   wheel-tests-cuproj:
     needs: [wheel-build-cuspatial, wheel-build-cuproj]
     secrets: inherit
-    uses: rapidsai/shared-workflows/.github/workflows/wheels-test.yaml@branch-24.08
+    uses: rapidsai/shared-workflows/.github/workflows/wheels-test.yaml@branch-24.10
     with:
       build_type: pull-request
       script: ci/test_wheel_cuproj.sh
   devcontainer:
     secrets: inherit
-    uses: rapidsai/shared-workflows/.github/workflows/build-in-devcontainer.yaml@branch-24.08
+    uses: rapidsai/shared-workflows/.github/workflows/build-in-devcontainer.yaml@branch-24.10
     with:
       arch: '["amd64"]'
       cuda: '["12.5"]'
diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml
index f91a2e09b..3949b4c3d 100644
--- a/.pre-commit-config.yaml
+++ b/.pre-commit-config.yaml
@@ -18,7 +18,7 @@ repos:
               # Explicitly specify the pyproject.toml at the repo root, not per-project.
                 args: ["--config", "pyproject.toml"]
       - repo: https://github.com/PyCQA/flake8
-        rev: 5.0.4
+        rev: 7.1.1
         hooks:
               - id: flake8
                 args: ["--config=.flake8"]
@@ -43,7 +43,7 @@ repos:
               args: ["--config pyproject.toml"]
               additional_dependencies: ["tomli"]
       - repo: https://github.com/rapidsai/pre-commit-hooks
-        rev: v0.2.0
+        rev: v0.4.0
         hooks:
             - id: verify-copyright
             - id: verify-alpha-spec
diff --git a/CHANGELOG.md b/CHANGELOG.md
index faa934484..abe227bae 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,3 +1,48 @@
+# cuspatial 24.10.00 (9 Oct 2024)
+
+## 🚨 Breaking Changes
+
+- Disallow GeoSeries from accepting a column in favor of `_from_column` ([#1434](https://github.com/rapidsai/cuspatial/pull/1434)) [@mroeschke](https://github.com/mroeschke)
+
+## 🐛 Bug Fixes
+
+- re-enable mixing libcuspatial wheels with libcudf conda packages ([#1456](https://github.com/rapidsai/cuspatial/pull/1456)) [@jameslamb](https://github.com/jameslamb)
+- fix wheel tests on Rocky Linux ([#1452](https://github.com/rapidsai/cuspatial/pull/1452)) [@jameslamb](https://github.com/jameslamb)
+- Add data=None to ListColumn constructor ([#1442](https://github.com/rapidsai/cuspatial/pull/1442)) [@mroeschke](https://github.com/mroeschke)
+- Remove unused shuffle_order in GeoColumn ([#1439](https://github.com/rapidsai/cuspatial/pull/1439)) [@mroeschke](https://github.com/mroeschke)
+- Fix _recombine_columns when either geocolumn or non-geocolumn was empty ([#1438](https://github.com/rapidsai/cuspatial/pull/1438)) [@mroeschke](https://github.com/mroeschke)
+- Internal linkage kernels ([#1436](https://github.com/rapidsai/cuspatial/pull/1436)) [@robertmaynard](https://github.com/robertmaynard)
+- Disallow GeoSeries from accepting a column in favor of `_from_column` ([#1434](https://github.com/rapidsai/cuspatial/pull/1434)) [@mroeschke](https://github.com/mroeschke)
+
+## 📖 Documentation
+
+- Recommending `miniforge` for conda install ([#1457](https://github.com/rapidsai/cuspatial/pull/1457)) [@mmccarty](https://github.com/mmccarty)
+
+## 🛠️ Improvements
+
+- xfail Python tests dependent on invalid Polygons ([#1467](https://github.com/rapidsai/cuspatial/pull/1467)) [@mroeschke](https://github.com/mroeschke)
+- Switch `traceback` to `native` in pytests ([#1464](https://github.com/rapidsai/cuspatial/pull/1464)) [@galipremsagar](https://github.com/galipremsagar)
+- Update update-version.sh to use packaging lib ([#1462](https://github.com/rapidsai/cuspatial/pull/1462)) [@AyodeAwe](https://github.com/AyodeAwe)
+- Use CI workflow branch &#39;branch-24.10&#39; again ([#1459](https://github.com/rapidsai/cuspatial/pull/1459)) [@jameslamb](https://github.com/jameslamb)
+- Update flake8 to 7.1.1. ([#1458](https://github.com/rapidsai/cuspatial/pull/1458)) [@bdice](https://github.com/bdice)
+- Add support for Python 3.12, pin proj back to 9.3.x, require geopandas&gt;=1.0 ([#1453](https://github.com/rapidsai/cuspatial/pull/1453)) [@jameslamb](https://github.com/jameslamb)
+- rearrange dependencies.yaml, fix development instructions ([#1451](https://github.com/rapidsai/cuspatial/pull/1451)) [@jameslamb](https://github.com/jameslamb)
+- Distribute libcuspatial wheels ([#1450](https://github.com/rapidsai/cuspatial/pull/1450)) [@jameslamb](https://github.com/jameslamb)
+- cuproj: remove pin on &#39;proj&#39; in conda packages ([#1449](https://github.com/rapidsai/cuspatial/pull/1449)) [@jameslamb](https://github.com/jameslamb)
+- cuproj: depend on librmm, not rmm ([#1448](https://github.com/rapidsai/cuspatial/pull/1448)) [@jameslamb](https://github.com/jameslamb)
+- wheels: dynamically load `libcudf.so` from `libcudf` wheel ([#1447](https://github.com/rapidsai/cuspatial/pull/1447)) [@jameslamb](https://github.com/jameslamb)
+- Update rapidsai/pre-commit-hooks ([#1445](https://github.com/rapidsai/cuspatial/pull/1445)) [@KyleFromNVIDIA](https://github.com/KyleFromNVIDIA)
+- Drop Python 3.9 support ([#1443](https://github.com/rapidsai/cuspatial/pull/1443)) [@jameslamb](https://github.com/jameslamb)
+- Remove NumPy &lt;2 pin, remove proj pin ([#1441](https://github.com/rapidsai/cuspatial/pull/1441)) [@seberg](https://github.com/seberg)
+- Update cuspatial to use pylibcudf ([#1440](https://github.com/rapidsai/cuspatial/pull/1440)) [@vyasr](https://github.com/vyasr)
+- Update pre-commit hooks ([#1435](https://github.com/rapidsai/cuspatial/pull/1435)) [@KyleFromNVIDIA](https://github.com/KyleFromNVIDIA)
+- Improve update-version.sh ([#1432](https://github.com/rapidsai/cuspatial/pull/1432)) [@bdice](https://github.com/bdice)
+- Use tool.scikit-build.cmake.version, set scikit-build-core minimum-version ([#1430](https://github.com/rapidsai/cuspatial/pull/1430)) [@jameslamb](https://github.com/jameslamb)
+- Pass columns instead of Series to `cudf.DataFrame` in split-combine workflow ([#1429](https://github.com/rapidsai/cuspatial/pull/1429)) [@mroeschke](https://github.com/mroeschke)
+- Address cudf.DataFrame.insert API change ([#1428](https://github.com/rapidsai/cuspatial/pull/1428)) [@mroeschke](https://github.com/mroeschke)
+- ensure notebook-testing script fails when notebooks fail ([#1424](https://github.com/rapidsai/cuspatial/pull/1424)) [@jameslamb](https://github.com/jameslamb)
+- Forward-merge branch-24.08 into branch-24.10 ([#1410](https://github.com/rapidsai/cuspatial/pull/1410)) [@jakirkham](https://github.com/jakirkham)
+
 # cuspatial 24.08.00 (7 Aug 2024)
 
 ## 🐛 Bug Fixes
diff --git a/README.md b/README.md
index 52202b950..6086a7c9e 100644
--- a/README.md
+++ b/README.md
@@ -113,19 +113,19 @@ An example command from the Release Selector:
 docker run --gpus all --pull always --rm -it \
     --shm-size=1g --ulimit memlock=-1 --ulimit stack=67108864 \
     -p 8888:8888 -p 8787:8787 -p 8786:8786 \
-    nvcr.io/nvidia/rapidsai/notebooks:24.08-cuda11.8-py3.10
+    nvcr.io/nvidia/rapidsai/notebooks:24.10-cuda11.8-py3.10
 ```
 
 ### Install with Conda
 
 To install via conda:
-> **Note** cuSpatial is supported only on Linux or [through WSL](https://rapids.ai/wsl2.html), and with Python versions 3.9, 3.10, and 3.11.
+> **Note** cuSpatial is supported only on Linux or [through WSL](https://rapids.ai/wsl2.html), and with Python versions 3.10, 3.11, and 3.12.
 
 cuSpatial can be installed with conda (miniconda, or the full Anaconda distribution) from the rapidsai channel:
 
 ```shell
 conda install -c rapidsai -c conda-forge -c nvidia \
-    cuspatial=24.08 python=3.11 cudatoolkit=11.8
+    cuspatial=24.10 python=3.12 cudatoolkit=11.8
 ```
 We also provide nightly Conda packages built from the HEAD of our latest development branch.
 
@@ -134,7 +134,7 @@ See the [RAPIDS installation documentation](https://docs.rapids.ai/install) for
 ### Install with pip
 
 To install via pip:
-> **Note** cuSpatial is supported only on Linux or [through WSL](https://rapids.ai/wsl2.html), and with Python versions 3.9, 3.10, and 3.11.
+> **Note** cuSpatial is supported only on Linux or [through WSL](https://rapids.ai/wsl2.html), and with Python versions 3.10, 3.11, and 3.12.
 
 The cuSpatial pip packages can be installed from NVIDIA's PyPI index. pip installations require using the matching wheel to the system's installed CUDA toolkit.
 - For CUDA 11 toolkits, install the `-cu11` wheels
@@ -145,35 +145,6 @@ pip install cuspatial-cu12 --extra-index-url=https://pypi.nvidia.com
 pip install cuspatial-cu11 --extra-index-url=https://pypi.nvidia.com
 ```
 
-#### Troubleshooting Fiona/GDAL versions
-
-cuSpatial depends on [`geopandas`](https://github.com/geopandas/geopandas), which uses [`fiona >= 1.8.19`](https://pypi.org/project/Fiona/), to read common GIS formats with GDAL.
-
-Fiona requires GDAL is already present on your system, but its minimum required version may be newer than the version of GDAL in your OS's package manager.
-
-Fiona checks the GDAL version at install time and fails with an error like this if a compatible version of GDAL isn't installed:
-```
-ERROR: GDAL >= 3.2 is required for fiona. Please upgrade GDAL.
-```
-
-There are two ways to fix this:
-
-1. Install a version of GDAL that meets fiona's minimum required version
-  * Ubuntu users can install a newer GDAL with the [UbuntuGIS PPA](https://wiki.ubuntu.com/UbuntuGIS):
-    ```shell
-    sudo -y add-apt-repository ppa:ubuntugis/ppa
-    sudo apt install libgdal-dev
-    ```
-2. Pin fiona's version to a range that's compatible with your version of `libgdal-dev`
-  * For Ubuntu20.04 ([GDAL v3.0.4](https://packages.ubuntu.com/focal/libgdal-dev)):
-    ```shell
-    pip install --no-binary fiona --extra-index-url=https://pypi.nvidia.com cuspatial-cu12 'fiona>=1.8.19,<1.9'
-    ```
-  * For Ubuntu22.04 ([GDAL v3.4.1](https://packages.ubuntu.com/jammy/libgdal-dev)):
-    ```shell
-    pip install --no-binary fiona --extra-index-url=https://pypi.nvidia.com cuspatial-cu12 'fiona>=1.9'
-    ```
-
 ### Build/Install from source
 
 To build and install cuSpatial from source please see the [build documentation](https://docs.rapids.ai/api/cuspatial/stable/developer_guide/build.html).
@@ -184,7 +155,7 @@ To build and install cuSpatial from source please see the [build documentation](
 If you find cuSpatial useful in your published work, please consider citing the repository.
 
 ```bibtex
-@misc{cuspatial:24.08,
+@misc{cuspatial:24.10,
     author = {{NVIDIA Corporation}},
     title = {cuSpatial: GPU-Accelerated Geospatial and Spatiotemporal Algorithms},
     year = {2023},
diff --git a/VERSION b/VERSION
index ec8489fda..7c7ba0443 100644
--- a/VERSION
+++ b/VERSION
@@ -1,1 +1,1 @@
-24.08.00
+24.10.00
diff --git a/build.sh b/build.sh
index ffe97da9a..77b8fa3eb 100755
--- a/build.sh
+++ b/build.sh
@@ -124,11 +124,6 @@ if hasArg benchmarks; then
     BUILD_BENCHMARKS=ON
 fi
 
-# Append `-DFIND_CUSPATIAL_CPP=ON` to EXTRA_CMAKE_ARGS unless a user specified the option.
-if [[ "${EXTRA_CMAKE_ARGS}" != *"DFIND_CUSPATIAL_CPP"* ]]; then
-    EXTRA_CMAKE_ARGS="${EXTRA_CMAKE_ARGS} -DFIND_CUSPATIAL_CPP=ON"
-fi
-
 # Append `-DFIND_CUPROJ_CPP=ON` to EXTRA_CMAKE_ARGS unless a user specified the option.
 if [[ "${EXTRA_CMAKE_ARGS}" != *"DFIND_CUPROJ_CPP"* ]]; then
     EXTRA_CMAKE_ARGS="${EXTRA_CMAKE_ARGS} -DFIND_CUPROJ_CPP=ON"
diff --git a/ci/build_docs.sh b/ci/build_docs.sh
index 84d0bad0f..80b7c6ca7 100755
--- a/ci/build_docs.sh
+++ b/ci/build_docs.sh
@@ -23,7 +23,7 @@ rapids-print-env
 
 export RAPIDS_VERSION="$(rapids-version)"
 export RAPIDS_VERSION_MAJOR_MINOR="$(rapids-version-major-minor)"
-export RAPIDS_VERSION_NUMBER="24.08"
+export RAPIDS_VERSION_NUMBER="24.10"
 export RAPIDS_DOCS_DIR="$(mktemp -d)"
 
 rapids-logger "Build cuSpatial CPP docs"
diff --git a/ci/build_wheel.sh b/ci/build_wheel.sh
index 5e6eb944e..75328d272 100755
--- a/ci/build_wheel.sh
+++ b/ci/build_wheel.sh
@@ -5,6 +5,26 @@ set -euo pipefail
 
 package_name=$1
 package_dir=$2
+package_type=$3
+
+# The 'libcuspatial' wheel should package 'libcuspatial.so', and all others
+# should exclude it (they dynamically load it if they need it).
+#
+# Capturing that here in argument-parsing to allow this build_wheel.sh
+# script to be re-used by all wheel builds in the project.
+case "${package_dir}" in
+  python/libcuspatial)
+    EXCLUDE_ARGS=(
+      --exclude "libcudf.so"
+    )
+  ;;
+  *)
+    EXCLUDE_ARGS=(
+      --exclude "libcudf.so"
+      --exclude "libcuspatial.so"
+    )
+  ;;
+esac
 
 source rapids-configure-sccache
 source rapids-date-string
@@ -18,6 +38,9 @@ cd "${package_dir}"
 python -m pip wheel . -w dist -vvv --no-deps --disable-pip-version-check
 
 mkdir -p final_dist
-python -m auditwheel repair -w final_dist dist/*
+python -m auditwheel repair \
+    "${EXCLUDE_ARGS[@]}" \
+    -w final_dist \
+    dist/*
 
-RAPIDS_PY_WHEEL_NAME="${package_name}_${RAPIDS_PY_CUDA_SUFFIX}" rapids-upload-wheels-to-s3 final_dist
+RAPIDS_PY_WHEEL_NAME="${package_name}_${RAPIDS_PY_CUDA_SUFFIX}" rapids-upload-wheels-to-s3 "${package_type}" final_dist
diff --git a/ci/build_wheel_cuproj.sh b/ci/build_wheel_cuproj.sh
index 961f4e363..6001e44e9 100755
--- a/ci/build_wheel_cuproj.sh
+++ b/ci/build_wheel_cuproj.sh
@@ -1,6 +1,6 @@
 #!/bin/bash
-# Copyright (c) 2023, NVIDIA CORPORATION.
+# Copyright (c) 2023-2024, NVIDIA CORPORATION.
 
 set -euo pipefail
 
-ci/build_wheel.sh cuproj python/cuproj
+ci/build_wheel.sh cuproj python/cuproj python
diff --git a/ci/build_wheel_cuspatial.sh b/ci/build_wheel_cuspatial.sh
index 932665e27..e581a3e76 100755
--- a/ci/build_wheel_cuspatial.sh
+++ b/ci/build_wheel_cuspatial.sh
@@ -1,8 +1,17 @@
 #!/bin/bash
-# Copyright (c) 2023, NVIDIA CORPORATION.
+# Copyright (c) 2023-2024, NVIDIA CORPORATION.
 
 set -euo pipefail
 
-export SKBUILD_CMAKE_ARGS="-DUSE_LIBARROW_FROM_PYARROW=ON"
+RAPIDS_PY_CUDA_SUFFIX="$(rapids-wheel-ctk-name-gen ${RAPIDS_CUDA_VERSION})"
 
-ci/build_wheel.sh cuspatial python/cuspatial
+# Downloads libcuspatial wheel from this current build,
+# then ensures 'cuspatial' wheel builds always use the 'libcuspatial' just built in the same CI run.
+#
+# Using env variable PIP_CONSTRAINT is necessary to ensure the constraints
+# are used when creating the isolated build environment.
+RAPIDS_PY_WHEEL_NAME="libcuspatial_${RAPIDS_PY_CUDA_SUFFIX}" rapids-download-wheels-from-s3 cpp /tmp/libcuspatial_dist
+echo "libcuspatial-${RAPIDS_PY_CUDA_SUFFIX} @ file://$(echo /tmp/libcuspatial_dist/libcuspatial_*.whl)" > /tmp/constraints.txt
+export PIP_CONSTRAINT="/tmp/constraints.txt"
+
+ci/build_wheel.sh cuspatial python/cuspatial python
diff --git a/ci/build_wheel_libcuspatial.sh b/ci/build_wheel_libcuspatial.sh
new file mode 100755
index 000000000..742a58caa
--- /dev/null
+++ b/ci/build_wheel_libcuspatial.sh
@@ -0,0 +1,6 @@
+#!/bin/bash
+# Copyright (c) 2024, NVIDIA CORPORATION.
+
+set -euo pipefail
+
+ci/build_wheel.sh libcuspatial python/libcuspatial cpp
diff --git a/ci/release/update-version.sh b/ci/release/update-version.sh
index d017e1123..03c05e9c4 100755
--- a/ci/release/update-version.sh
+++ b/ci/release/update-version.sh
@@ -18,11 +18,14 @@ CURRENT_MINOR=$(echo $CURRENT_TAG | awk '{split($0, a, "."); print a[2]}')
 CURRENT_PATCH=$(echo $CURRENT_TAG | awk '{split($0, a, "."); print a[3]}')
 CURRENT_SHORT_TAG=${CURRENT_MAJOR}.${CURRENT_MINOR}
 
-#Get <major>.<minor> for next version
+# Get <major>.<minor> for next version
 NEXT_MAJOR=$(echo $NEXT_FULL_TAG | awk '{split($0, a, "."); print a[1]}')
 NEXT_MINOR=$(echo $NEXT_FULL_TAG | awk '{split($0, a, "."); print a[2]}')
 NEXT_SHORT_TAG=${NEXT_MAJOR}.${NEXT_MINOR}
 
+# Need to distutils-normalize the original version
+NEXT_SHORT_TAG_PEP440=$(python -c "from packaging.version import Version; print(Version('${NEXT_SHORT_TAG}'))")
+
 echo "Preparing release $CURRENT_TAG => $NEXT_FULL_TAG"
 
 # Inplace sed replace; workaround for Linux and Mac
@@ -39,10 +42,8 @@ for FILE in .github/workflows/*.yaml; do
 done
 sed_runner "s/RAPIDS_VERSION_NUMBER=\".*/RAPIDS_VERSION_NUMBER=\"${NEXT_SHORT_TAG}\"/g" ci/build_docs.sh
 
-# Need to distutils-normalize the original version
-NEXT_SHORT_TAG_PEP440=$(python -c "from setuptools.extern import packaging; print(packaging.version.Version('${NEXT_SHORT_TAG}'))")
-
 DEPENDENCIES=(
+  pylibcudf
   cudf
   cuml
   cuspatial
@@ -54,23 +55,21 @@ DEPENDENCIES=(
   cuspatial
   cuproj
 )
-
 for DEP in "${DEPENDENCIES[@]}"; do
   for FILE in dependencies.yaml conda/environments/*.yaml; do
     sed_runner "/-.* ${DEP}\(-cu[[:digit:]]\{2\}\)\{0,1\}==/ s/==.*/==${NEXT_SHORT_TAG_PEP440}.*,>=0.0.0a0/g" "${FILE}"
   done
-  sed_runner "s/${DEP}\(-cu[[:digit:]]\{2\}\)\{0,1\}==.*\",/${DEP}==${NEXT_SHORT_TAG_PEP440}.*,>=0.0.0a0\",/g" python/cuspatial/pyproject.toml
-  sed_runner "s/${DEP}\(-cu[[:digit:]]\{2\}\)\{0,1\}==.*\",/${DEP}==${NEXT_SHORT_TAG_PEP440}.*,>=0.0.0a0\",/g" python/cuproj/pyproject.toml
+  for FILE in python/*/pyproject.toml; do
+    sed_runner "/\"${DEP}==/ s/==.*\"/==${NEXT_SHORT_TAG_PEP440}.*,>=0.0.0a0\"/g" "${FILE}"
+  done
 done
 
-# Dependency versions in dependencies.yaml
-sed_runner "/-cu[0-9]\{2\}==/ s/==.*/==${NEXT_SHORT_TAG_PEP440}.*/g" dependencies.yaml
-
-# Version in cuspatial_api_examples.ipynb
+# Versions in cuspatial_api_examples.ipynb
 sed_runner "s/rapids-[0-9]*\.[0-9]*/rapids-${NEXT_SHORT_TAG}/g" docs/source/user_guide/cuspatial_api_examples.ipynb
 sed_runner "s/cuproj=[0-9]*\.[0-9]*/cuproj=${NEXT_SHORT_TAG}/g" docs/source/user_guide/cuspatial_api_examples.ipynb
 sed_runner "s/cuspatial=[0-9]*\.[0-9]*/cuspatial=${NEXT_SHORT_TAG}/g" docs/source/user_guide/cuspatial_api_examples.ipynb
-# Version in cuproj_api_examples.ipynb
+
+# Versions in cuproj_api_examples.ipynb
 sed_runner "s/rapids-[0-9]*\.[0-9]*/rapids-${NEXT_SHORT_TAG}/g" docs/cuproj/source/user_guide/cuproj_api_examples.ipynb
 sed_runner "s/cuproj=[0-9]*\.[0-9]*/cuproj-${NEXT_SHORT_TAG}/g" docs/cuproj/source/user_guide/cuproj_api_examples.ipynb
 sed_runner "s/cuspatial=[0-9]*\.[0-9]*/cuspatial=${NEXT_SHORT_TAG}/g" docs/cuproj/source/user_guide/cuproj_api_examples.ipynb
diff --git a/conda/environments/all_cuda-118_arch-x86_64.yaml b/conda/environments/all_cuda-118_arch-x86_64.yaml
index 513f2b28b..6526e9d5f 100644
--- a/conda/environments/all_cuda-118_arch-x86_64.yaml
+++ b/conda/environments/all_cuda-118_arch-x86_64.yaml
@@ -11,27 +11,24 @@ dependencies:
 - cmake>=3.26.4,!=3.30.0
 - cuda-version=11.8
 - cudatoolkit
-- cudf==24.8.*,>=0.0.0a0
-- cuml==24.8.*,>=0.0.0a0
+- cudf==24.10.*,>=0.0.0a0
+- cuml==24.10.*,>=0.0.0a0
 - cupy>=12.0.0
 - curl
 - cxx-compiler
 - cython>=3.0.0
 - doxygen
 - gcc_linux-64=11.*
-- geopandas<1
-- geopandas>=0.11.0
+- geopandas>=1.0.0
 - ipython
 - ipywidgets
-- libcudf==24.8.*,>=0.0.0a0
-- libcuspatial-tests==24.8.*
-- libcuspatial==24.8.*
-- librmm==24.8.*,>=0.0.0a0
+- libcudf==24.10.*,>=0.0.0a0
+- librmm==24.10.*,>=0.0.0a0
 - myst-parser
 - nbsphinx
 - ninja
 - notebook
-- numpy>=1.23,<2.0a0
+- numpy>=1.23,<3.0a0
 - numpydoc
 - nvcc_linux-64=11.8
 - osmnx>=1.9.3
@@ -39,13 +36,14 @@ dependencies:
 - proj
 - pydata-sphinx-theme!=0.14.2
 - pydeck
+- pylibcudf==24.10.*,>=0.0.0a0
 - pytest
 - pytest-cov
 - pytest-xdist
-- python>=3.9,<3.12
+- python>=3.10,<3.13
 - rapids-build-backend>=0.3.0,<0.4.0.dev0
-- rmm==24.8.*,>=0.0.0a0
-- scikit-build-core>=0.7.0
+- rmm==24.10.*,>=0.0.0a0
+- scikit-build-core>=0.10.0
 - scikit-image
 - shapely
 - sphinx<6
diff --git a/conda/environments/all_cuda-125_arch-x86_64.yaml b/conda/environments/all_cuda-125_arch-x86_64.yaml
index 4c245f627..9d0e65000 100644
--- a/conda/environments/all_cuda-125_arch-x86_64.yaml
+++ b/conda/environments/all_cuda-125_arch-x86_64.yaml
@@ -14,40 +14,38 @@ dependencies:
 - cuda-nvcc
 - cuda-nvrtc-dev
 - cuda-version=12.5
-- cudf==24.8.*,>=0.0.0a0
-- cuml==24.8.*,>=0.0.0a0
+- cudf==24.10.*,>=0.0.0a0
+- cuml==24.10.*,>=0.0.0a0
 - cupy>=12.0.0
 - curl
 - cxx-compiler
 - cython>=3.0.0
 - doxygen
 - gcc_linux-64=11.*
-- geopandas<1
-- geopandas>=0.11.0
+- geopandas>=1.0.0
 - ipython
 - ipywidgets
-- libcudf==24.8.*,>=0.0.0a0
-- libcuspatial-tests==24.8.*
-- libcuspatial==24.8.*
-- librmm==24.8.*,>=0.0.0a0
+- libcudf==24.10.*,>=0.0.0a0
+- librmm==24.10.*,>=0.0.0a0
 - myst-parser
 - nbsphinx
 - ninja
 - notebook
-- numpy>=1.23,<2.0a0
+- numpy>=1.23,<3.0a0
 - numpydoc
 - osmnx>=1.9.3
 - pre-commit
 - proj
 - pydata-sphinx-theme!=0.14.2
 - pydeck
+- pylibcudf==24.10.*,>=0.0.0a0
 - pytest
 - pytest-cov
 - pytest-xdist
-- python>=3.9,<3.12
+- python>=3.10,<3.13
 - rapids-build-backend>=0.3.0,<0.4.0.dev0
-- rmm==24.8.*,>=0.0.0a0
-- scikit-build-core>=0.7.0
+- rmm==24.10.*,>=0.0.0a0
+- scikit-build-core>=0.10.0
 - scikit-image
 - shapely
 - sphinx<6
diff --git a/conda/recipes/cuproj/conda_build_config.yaml b/conda/recipes/cuproj/conda_build_config.yaml
index 2186d11ca..001878ff2 100644
--- a/conda/recipes/cuproj/conda_build_config.yaml
+++ b/conda/recipes/cuproj/conda_build_config.yaml
@@ -18,7 +18,3 @@ c_stdlib_version:
 
 cmake_version:
   - ">=3.26.4,!=3.30.0"
-
-# Workaround until proj 9.3.1 migration completes
-proj:
-  - "9.3.0"
diff --git a/conda/recipes/cuproj/meta.yaml b/conda/recipes/cuproj/meta.yaml
index 516ea4a5a..29d590bc0 100644
--- a/conda/recipes/cuproj/meta.yaml
+++ b/conda/recipes/cuproj/meta.yaml
@@ -62,8 +62,8 @@ requirements:
     - cython >=3.0.0
     - python
     - rapids-build-backend >=0.3.0,<0.4.0.dev0
-    - rmm ={{ minor_version }}
-    - scikit-build-core >=0.7.0
+    - librmm ={{ minor_version }}
+    - scikit-build-core >=0.10.0
     - proj
     - sqlite
   run:
@@ -74,17 +74,11 @@ requirements:
     {% endif %}
     - {{ pin_compatible('cuda-version', max_pin='x', min_pin='x') }}
     - python
-    - rmm ={{ minor_version }}
     - cupy>=12.0.0
 
 test:            # [linux64]
   imports:       # [linux64]
     - cuproj     # [linux64]
-  requires:
-    - cupy>=12.0.0
-    - cuspatial ={{ minor_version }}
-    - rmm ={{ minor_version }}
-
 
 about:
   home: https://rapids.ai/
diff --git a/conda/recipes/cuspatial/meta.yaml b/conda/recipes/cuspatial/meta.yaml
index c06b4fa06..9f37d47e9 100644
--- a/conda/recipes/cuspatial/meta.yaml
+++ b/conda/recipes/cuspatial/meta.yaml
@@ -60,12 +60,13 @@ requirements:
     {% endif %}
     - cuda-version ={{ cuda_version }}
     - cudf ={{ minor_version }}
+    - pylibcudf ={{ minor_version }}
     - cython >=3.0.0
     - libcuspatial ={{ version }}
     - python
     - rapids-build-backend >=0.3.0,<0.4.0.dev0
     - rmm ={{ minor_version }}
-    - scikit-build-core >=0.7.0
+    - scikit-build-core >=0.10.0
   run:
     {% if cuda_major == "11" %}
     - cudatoolkit
@@ -74,8 +75,9 @@ requirements:
     {% endif %}
     - {{ pin_compatible('cuda-version', max_pin='x', min_pin='x') }}
     - cudf ={{ minor_version }}
-    - geopandas >=0.11.0
-    - numpy >=1.23,<2.0a0
+    - pylibcudf ={{ minor_version }}
+    - geopandas >=1.0.0
+    - numpy >=1.23,<3.0a0
     - python
     - rmm ={{ minor_version }}
 
diff --git a/cpp/cmake/thirdparty/get_ranger.cmake b/cpp/cmake/thirdparty/get_ranger.cmake
index 563e4120a..c57131f3a 100644
--- a/cpp/cmake/thirdparty/get_ranger.cmake
+++ b/cpp/cmake/thirdparty/get_ranger.cmake
@@ -1,5 +1,5 @@
 #=============================================================================
-# Copyright (c) 2023, NVIDIA CORPORATION.
+# Copyright (c) 2023-2024, NVIDIA CORPORATION.
 #
 # Licensed under the Apache License, Version 2.0 (the "License");
 # you may not use this file except in compliance with the License.
@@ -26,8 +26,6 @@ function(find_and_configure_ranger)
     rapids_cpm_find(
       ranger 00.01.00
       GLOBAL_TARGETS "${global_targets}"
-      BUILD_EXPORT_SET cuspatial-exports
-      INSTALL_EXPORT_SET cuspatial-exports
       CPM_ARGS
       GIT_REPOSITORY https://github.com/harrism/ranger.git
       GIT_TAG main
diff --git a/cpp/include/cuspatial/cuda_utils.hpp b/cpp/include/cuspatial/cuda_utils.hpp
index f46fddfcb..28353ad2e 100644
--- a/cpp/include/cuspatial/cuda_utils.hpp
+++ b/cpp/include/cuspatial/cuda_utils.hpp
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2022, NVIDIA CORPORATION.
+ * Copyright (c) 2022-2024, NVIDIA CORPORATION.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -19,8 +19,10 @@
 
 #ifdef __CUDACC__
 #define CUSPATIAL_HOST_DEVICE __host__ __device__
+#define CUSPATIAL_KERNEL      __global__ static
 #else
 #define CUSPATIAL_HOST_DEVICE
+#define CUSPATIAL_KERNEL
 #endif
 
 /**
diff --git a/cpp/include/cuspatial/detail/distance/hausdorff.cuh b/cpp/include/cuspatial/detail/distance/hausdorff.cuh
index aa955a7d6..aa3484fa7 100644
--- a/cpp/include/cuspatial/detail/distance/hausdorff.cuh
+++ b/cpp/include/cuspatial/detail/distance/hausdorff.cuh
@@ -82,7 +82,7 @@ constexpr auto magnitude_squared(T a, T b)
  * @param results directed Hausdorff distances computed by kernel
  */
 template <typename T, typename Index, typename PointIt, typename OffsetIt, typename OutputIt>
-__global__ void kernel_hausdorff(
+CUSPATIAL_KERNEL void kernel_hausdorff(
   Index num_points, PointIt points, Index num_spaces, OffsetIt space_offsets, OutputIt results)
 {
   using Point = typename std::iterator_traits<PointIt>::value_type;
diff --git a/cpp/include/cuspatial/detail/find/find_and_combine_segment.cuh b/cpp/include/cuspatial/detail/find/find_and_combine_segment.cuh
index 2cdbfd425..ca86096be 100644
--- a/cpp/include/cuspatial/detail/find/find_and_combine_segment.cuh
+++ b/cpp/include/cuspatial/detail/find/find_and_combine_segment.cuh
@@ -48,9 +48,9 @@ namespace detail {
  * 2. Repeat 1 until all mergeable group is processed.
  */
 template <typename OffsetRange, typename SegmentRange, typename OutputIt>
-void __global__ simple_find_and_combine_segments_kernel(OffsetRange offsets,
-                                                        SegmentRange segments,
-                                                        OutputIt merged_flag)
+CUSPATIAL_KERNEL void simple_find_and_combine_segments_kernel(OffsetRange offsets,
+                                                              SegmentRange segments,
+                                                              OutputIt merged_flag)
 {
   for (auto pair_idx : ranger::grid_stride_range(offsets.size() - 1)) {
     // Zero-initialize flags for all segments in current space.
diff --git a/cpp/include/cuspatial/detail/find/find_duplicate_points.cuh b/cpp/include/cuspatial/detail/find/find_duplicate_points.cuh
index de18bb927..f78424ebe 100644
--- a/cpp/include/cuspatial/detail/find/find_duplicate_points.cuh
+++ b/cpp/include/cuspatial/detail/find/find_duplicate_points.cuh
@@ -34,8 +34,8 @@ namespace detail {
  * @brief Kernel to compute duplicate points in each multipoint. Naive N^2 algorithm.
  */
 template <typename MultiPointRange, typename OutputIt>
-void __global__ find_duplicate_points_kernel_simple(MultiPointRange multipoints,
-                                                    OutputIt duplicate_flags)
+CUSPATIAL_KERNEL void find_duplicate_points_kernel_simple(MultiPointRange multipoints,
+                                                          OutputIt duplicate_flags)
 {
   for (auto idx : ranger::grid_stride_range(multipoints.size())) {
     auto multipoint    = multipoints[idx];
diff --git a/cpp/include/cuspatial/detail/intersection/linestring_intersection_count.cuh b/cpp/include/cuspatial/detail/intersection/linestring_intersection_count.cuh
index b164fa729..19009c06c 100644
--- a/cpp/include/cuspatial/detail/intersection/linestring_intersection_count.cuh
+++ b/cpp/include/cuspatial/detail/intersection/linestring_intersection_count.cuh
@@ -34,10 +34,11 @@ template <typename MultiLinestringRange1,
           typename MultiLinestringRange2,
           typename OutputIt1,
           typename OutputIt2>
-__global__ void count_intersection_and_overlaps_simple(MultiLinestringRange1 multilinestrings1,
-                                                       MultiLinestringRange2 multilinestrings2,
-                                                       OutputIt1 point_count_it,
-                                                       OutputIt2 segment_count_it)
+CUSPATIAL_KERNEL void count_intersection_and_overlaps_simple(
+  MultiLinestringRange1 multilinestrings1,
+  MultiLinestringRange2 multilinestrings2,
+  OutputIt1 point_count_it,
+  OutputIt2 segment_count_it)
 {
   using T = typename MultiLinestringRange1::element_t;
   for (auto idx : ranger::grid_stride_range(multilinestrings1.num_points())) {
diff --git a/cpp/include/cuspatial/detail/intersection/linestring_intersection_with_duplicates.cuh b/cpp/include/cuspatial/detail/intersection/linestring_intersection_with_duplicates.cuh
index f1a365555..5d2165437 100644
--- a/cpp/include/cuspatial/detail/intersection/linestring_intersection_with_duplicates.cuh
+++ b/cpp/include/cuspatial/detail/intersection/linestring_intersection_with_duplicates.cuh
@@ -394,17 +394,18 @@ template <typename MultiLinestringRange1,
           typename IdRanges,
           typename OutputIt1,
           typename OutputIt2>
-void __global__ pairwise_linestring_intersection_simple(MultiLinestringRange1 multilinestrings1,
-                                                        MultiLinestringRange2 multilinestrings2,
-                                                        TempIt1 n_points_stored,
-                                                        TempIt2 n_segments_stored,
-                                                        Offsets1 num_points_offsets_first,
-                                                        Offsets2 num_segments_offsets_first,
-                                                        Offsets3 num_points_per_pair_first,
-                                                        IdRanges point_ids_range,
-                                                        IdRanges segment_ids_range,
-                                                        OutputIt1 points_first,
-                                                        OutputIt2 segments_first)
+CUSPATIAL_KERNEL void pairwise_linestring_intersection_simple(
+  MultiLinestringRange1 multilinestrings1,
+  MultiLinestringRange2 multilinestrings2,
+  TempIt1 n_points_stored,
+  TempIt2 n_segments_stored,
+  Offsets1 num_points_offsets_first,
+  Offsets2 num_segments_offsets_first,
+  Offsets3 num_points_per_pair_first,
+  IdRanges point_ids_range,
+  IdRanges segment_ids_range,
+  OutputIt1 points_first,
+  OutputIt2 segments_first)
 {
   using T       = typename MultiLinestringRange1::element_t;
   using types_t = uint8_t;
diff --git a/cpp/include/cuspatial/detail/kernel/pairwise_distance.cuh b/cpp/include/cuspatial/detail/kernel/pairwise_distance.cuh
index 9786681d9..e5eaf7259 100644
--- a/cpp/include/cuspatial/detail/kernel/pairwise_distance.cuh
+++ b/cpp/include/cuspatial/detail/kernel/pairwise_distance.cuh
@@ -48,10 +48,10 @@ namespace detail {
  * @note This kernel does not compute pairs that contains empty geometry.
  */
 template <class MultiLinestringRange1, class MultiLinestringRange2, class OutputIt>
-__global__ void linestring_distance(MultiLinestringRange1 multilinestrings1,
-                                    MultiLinestringRange2 multilinestrings2,
-                                    thrust::optional<uint8_t*> intersects,
-                                    OutputIt distances_first)
+CUSPATIAL_KERNEL void linestring_distance(MultiLinestringRange1 multilinestrings1,
+                                          MultiLinestringRange2 multilinestrings2,
+                                          thrust::optional<uint8_t*> intersects,
+                                          OutputIt distances_first)
 {
   using T = typename MultiLinestringRange1::element_t;
 
@@ -91,10 +91,10 @@ __global__ void linestring_distance(MultiLinestringRange1 multilinestrings1,
  * set to nullopt, no distance computation will be bypassed.
  */
 template <class MultiPointRange, class MultiLinestringRange, class OutputIterator>
-__global__ void point_linestring_distance(MultiPointRange multipoints,
-                                          MultiLinestringRange multilinestrings,
-                                          thrust::optional<uint8_t*> intersects,
-                                          OutputIterator distances)
+CUSPATIAL_KERNEL void point_linestring_distance(MultiPointRange multipoints,
+                                                MultiLinestringRange multilinestrings,
+                                                thrust::optional<uint8_t*> intersects,
+                                                OutputIterator distances)
 {
   using T = typename MultiPointRange::element_t;
 
diff --git a/cpp/include/cuspatial/detail/pairwise_multipoint_equals_count.cuh b/cpp/include/cuspatial/detail/pairwise_multipoint_equals_count.cuh
index 141a85d61..157c32485 100644
--- a/cpp/include/cuspatial/detail/pairwise_multipoint_equals_count.cuh
+++ b/cpp/include/cuspatial/detail/pairwise_multipoint_equals_count.cuh
@@ -43,9 +43,9 @@ namespace cuspatial {
 namespace detail {
 
 template <class MultiPointRangeA, class MultiPointRangeB, class OutputIt>
-void __global__ pairwise_multipoint_equals_count_kernel(MultiPointRangeA lhs,
-                                                        MultiPointRangeB rhs,
-                                                        OutputIt output)
+CUSPATIAL_KERNEL void pairwise_multipoint_equals_count_kernel(MultiPointRangeA lhs,
+                                                              MultiPointRangeB rhs,
+                                                              OutputIt output)
 {
   using T = typename MultiPointRangeA::point_t::value_type;
 
diff --git a/cpp/include/cuspatial/detail/point_linestring_nearest_points.cuh b/cpp/include/cuspatial/detail/point_linestring_nearest_points.cuh
index f085204eb..13270a7cc 100644
--- a/cpp/include/cuspatial/detail/point_linestring_nearest_points.cuh
+++ b/cpp/include/cuspatial/detail/point_linestring_nearest_points.cuh
@@ -54,18 +54,18 @@ template <class Vec2dItA,
           class OffsetIteratorB,
           class OffsetIteratorC,
           class OutputIt>
-void __global__
-pairwise_point_linestring_nearest_points_kernel(OffsetIteratorA points_geometry_offsets_first,
-                                                OffsetIteratorA points_geometry_offsets_last,
-                                                Vec2dItA points_first,
-                                                Vec2dItA points_last,
-                                                OffsetIteratorB linestring_geometry_offsets_first,
-                                                OffsetIteratorB linestring_geometry_offsets_last,
-                                                OffsetIteratorC linestring_part_offsets_first,
-                                                OffsetIteratorC linestring_part_offsets_last,
-                                                Vec2dItB linestring_points_first,
-                                                Vec2dItB linestring_points_last,
-                                                OutputIt output_first)
+CUSPATIAL_KERNEL void pairwise_point_linestring_nearest_points_kernel(
+  OffsetIteratorA points_geometry_offsets_first,
+  OffsetIteratorA points_geometry_offsets_last,
+  Vec2dItA points_first,
+  Vec2dItA points_last,
+  OffsetIteratorB linestring_geometry_offsets_first,
+  OffsetIteratorB linestring_geometry_offsets_last,
+  OffsetIteratorC linestring_part_offsets_first,
+  OffsetIteratorC linestring_part_offsets_last,
+  Vec2dItB linestring_points_first,
+  Vec2dItB linestring_points_last,
+  OutputIt output_first)
 {
   using T         = iterator_vec_base_type<Vec2dItA>;
   using IndexType = iterator_value_type<OffsetIteratorA>;
diff --git a/dependencies.yaml b/dependencies.yaml
index 764c000fd..8e4c0964e 100644
--- a/dependencies.yaml
+++ b/dependencies.yaml
@@ -17,9 +17,10 @@ files:
       - depends_on_cudf
       - depends_on_cuml
       - depends_on_cupy
+      - depends_on_libcudf
+      - depends_on_librmm
       - rapids_build_skbuild
       - run_python_cuspatial
-      - test_libcuspatial
       - test_notebooks
       - test_python_cuspatial
       - test_python_cuproj
@@ -28,24 +29,27 @@ files:
     output: none
     includes:
       - cuda_version
+      - depends_on_libcuspatial
       - test_libcuspatial
   test_python:
     output: none
     includes:
       - cuda_version
+      - depends_on_cuproj
+      - depends_on_cuspatial
       - py_version
       - test_python_cuspatial
       - test_python_cuproj
-      - test_cuspatial
   test_notebooks:
     output: none
     includes:
       - cuda_version
       - depends_on_cuml
+      - depends_on_cuproj
+      - depends_on_cuspatial
       - test_notebooks
       - notebooks
       - py_version
-      - test_cuspatial
   checks:
     output: none
     includes:
@@ -55,9 +59,35 @@ files:
     output: none
     includes:
       - cuda_version
+      - depends_on_cuspatial
+      - depends_on_cuproj
       - docs
       - py_version
-      - test_cuspatial
+  py_build_libcuspatial:
+    output: [pyproject]
+    pyproject_dir: python/libcuspatial
+    extras:
+      table: build-system
+    includes:
+      - rapids_build_skbuild
+  py_rapids_build_libcuspatial:
+    output: [pyproject]
+    pyproject_dir: python/libcuspatial
+    extras:
+      table: tool.rapids-build-backend
+      key: requires
+    includes:
+      - build_cpp
+      - build_wheels
+      - depends_on_libcudf
+      - depends_on_librmm
+  py_run_libcuspatial:
+    output: [pyproject]
+    pyproject_dir: python/libcuspatial
+    extras:
+      table: project
+    includes:
+      - depends_on_libcudf
   py_build_cuspatial:
     output: [pyproject]
     pyproject_dir: python/cuspatial
@@ -77,14 +107,19 @@ files:
       - build_cpp
       - build_python
       - build_wheels
+      - depends_on_libcudf
+      - depends_on_libcuspatial
+      - depends_on_librmm
   py_run_cuspatial:
     output: [pyproject]
     pyproject_dir: python/cuspatial
     extras:
       table: project
     includes:
-      - depends_on_rmm
       - depends_on_cudf
+      - depends_on_libcudf
+      - depends_on_libcuspatial
+      - depends_on_rmm
       - run_python_cuspatial
   py_test_cuspatial:
     output: [pyproject]
@@ -108,17 +143,16 @@ files:
       table: tool.rapids-build-backend
       key: requires
     includes:
-      - depends_on_rmm
       - build_cpp_cuproj
       - build_python
       - build_wheels
+      - depends_on_librmm
   py_run_cuproj:
     output: [pyproject]
     pyproject_dir: python/cuproj
     extras:
       table: project
     includes:
-      - depends_on_rmm
       - depends_on_cupy
   py_test_cuproj:
     output: [pyproject]
@@ -129,7 +163,6 @@ files:
     includes:
       - test_python_cuproj
       - depends_on_cuspatial
-      - test_cuspatial
 
 channels:
   - rapidsai
@@ -141,14 +174,12 @@ dependencies:
     common:
       - output_types: [conda, requirements, pyproject]
         packages:
-          - ninja
-          - cmake>=3.26.4,!=3.30.0
+          - &ninja ninja
+          - &cmake cmake>=3.26.4,!=3.30.0
       - output_types: conda
         packages:
           - c-compiler
           - cxx-compiler
-          - libcudf==24.8.*,>=0.0.0a0
-          - librmm==24.8.*,>=0.0.0a0
           - proj
           - sqlite
     specific:
@@ -184,13 +215,12 @@ dependencies:
     common:
       - output_types: [conda, requirements, pyproject]
         packages:
-          - ninja
-          - cmake>=3.26.4,!=3.30.0
+          - *ninja
+          - *cmake
       - output_types: conda
         packages:
           - c-compiler
           - cxx-compiler
-          - librmm==24.8.*,>=0.0.0a0
           - proj
           - sqlite
     specific:
@@ -252,10 +282,10 @@ dependencies:
           - &rapids_build_backend rapids-build-backend>=0.3.0,<0.4.0.dev0
       - output_types: conda
         packages:
-          - scikit-build-core>=0.7.0
+          - scikit-build-core>=0.10.0
       - output_types: [requirements, pyproject]
         packages:
-          - scikit-build-core[pyproject]>=0.7.0
+          - scikit-build-core[pyproject]>=0.10.0
   cuda_version:
     specific:
       - output_types: conda
@@ -343,9 +373,6 @@ dependencies:
   # TODO: Remove geopandas.dataset usage in cuspatial_api_examples.ipynb
   test_notebooks:
     common:
-      - output_types: [conda, requirements, pyproject]
-        packages:
-          - geopandas<1
       - output_types: conda
         packages:
           - osmnx>=1.9.3
@@ -353,10 +380,6 @@ dependencies:
     specific:
       - output_types: conda
         matrices:
-          - matrix:
-              py: "3.9"
-            packages:
-              - python=3.9
           - matrix:
               py: "3.10"
             packages:
@@ -365,15 +388,19 @@ dependencies:
               py: "3.11"
             packages:
               - python=3.11
+          - matrix:
+              py: "3.12"
+            packages:
+              - python=3.12
           - matrix:
             packages:
-              - python>=3.9,<3.12
+              - python>=3.10,<3.13
   run_python_cuspatial:
     common:
       - output_types: [conda, requirements, pyproject]
         packages:
-          - geopandas>=0.11.0
-          - &numpy numpy>=1.23,<2.0a0
+          - &geopandas geopandas>=1.0.0
+          - &numpy numpy>=1.23,<3.0a0
   test_python_cuspatial:
     common:
       - output_types: [conda, requirements, pyproject]
@@ -389,7 +416,7 @@ dependencies:
           - pytest
           - pytest-cov
           - pytest-xdist
-          - geopandas>=0.11.0
+          - *geopandas
       - output_types: [requirements, pyproject]
         packages:
           - pyproj>=3.6.0,<3.7a0
@@ -398,7 +425,7 @@ dependencies:
     common:
       - output_types: conda
         packages:
-          - &rmm_unsuffixed rmm==24.8.*,>=0.0.0a0
+          - &rmm_unsuffixed rmm==24.10.*,>=0.0.0a0
       - output_types: requirements
         packages:
           # pip recognizes the index as a global option for the requirements.txt file
@@ -411,19 +438,20 @@ dependencies:
               cuda: "12.*"
               cuda_suffixed: "true"
             packages:
-              - rmm-cu12==24.8.*,>=0.0.0a0
+              - rmm-cu12==24.10.*,>=0.0.0a0
           - matrix:
               cuda: "11.*"
               cuda_suffixed: "true"
             packages:
-              - rmm-cu11==24.8.*,>=0.0.0a0
+              - rmm-cu11==24.10.*,>=0.0.0a0
           - {matrix: null, packages: [*rmm_unsuffixed]}
 
   depends_on_cudf:
     common:
       - output_types: conda
         packages:
-          - &cudf_unsuffixed cudf==24.8.*,>=0.0.0a0
+          - &cudf_unsuffixed cudf==24.10.*,>=0.0.0a0
+          - &pylibcudf_unsuffixed pylibcudf==24.10.*,>=0.0.0a0
       - output_types: requirements
         packages:
           # pip recognizes the index as a global option for the requirements.txt file
@@ -436,19 +464,46 @@ dependencies:
               cuda: "12.*"
               cuda_suffixed: "true"
             packages:
-              - cudf-cu12==24.8.*,>=0.0.0a0
+              - cudf-cu12==24.10.*,>=0.0.0a0
+              - pylibcudf-cu12==24.10.*,>=0.0.0a0
           - matrix:
               cuda: "11.*"
               cuda_suffixed: "true"
             packages:
-              - cudf-cu11==24.8.*,>=0.0.0a0
+              - cudf-cu11==24.10.*,>=0.0.0a0
+              - pylibcudf-cu11==24.10.*,>=0.0.0a0
           - {matrix: null, packages: [*cudf_unsuffixed]}
 
+  depends_on_libcudf:
+    common:
+      - output_types: conda
+        packages:
+          - &libcudf_unsuffixed libcudf==24.10.*,>=0.0.0a0
+      - output_types: requirements
+        packages:
+          # pip recognizes the index as a global option for the requirements.txt file
+          - --extra-index-url=https://pypi.nvidia.com
+          - --extra-index-url=https://pypi.anaconda.org/rapidsai-wheels-nightly/simple
+    specific:
+      - output_types: [requirements, pyproject]
+        matrices:
+          - matrix:
+              cuda: "12.*"
+              cuda_suffixed: "true"
+            packages:
+              - libcudf-cu12==24.10.*,>=0.0.0a0
+          - matrix:
+              cuda: "11.*"
+              cuda_suffixed: "true"
+            packages:
+              - libcudf-cu11==24.10.*,>=0.0.0a0
+          - {matrix: null, packages: [*libcudf_unsuffixed]}
+
   depends_on_cuml:
     common:
       - output_types: conda
         packages:
-          - &cuml_unsuffixed cuml==24.8.*,>=0.0.0a0
+          - &cuml_unsuffixed cuml==24.10.*,>=0.0.0a0
       - output_types: requirements
         packages:
           # pip recognizes the index as a global option for the requirements.txt file
@@ -461,19 +516,19 @@ dependencies:
               cuda: "12.*"
               cuda_suffixed: "true"
             packages:
-              - cuml-cu12==24.8.*,>=0.0.0a0
+              - cuml-cu12==24.10.*,>=0.0.0a0
           - matrix:
               cuda: "11.*"
               cuda_suffixed: "true"
             packages:
-              - cuml-cu11==24.8.*,>=0.0.0a0
+              - cuml-cu11==24.10.*,>=0.0.0a0
           - {matrix: null, packages: [*cuml_unsuffixed]}
 
   depends_on_cuspatial:
     common:
       - output_types: conda
         packages:
-          - &cuspatial_unsuffixed cuspatial==24.8.*,>=0.0.0a0
+          - &cuspatial_unsuffixed cuspatial==24.10.*,>=0.0.0a0
       - output_types: requirements
         packages:
           # pip recognizes the index as a global option for the requirements.txt file
@@ -486,14 +541,39 @@ dependencies:
               cuda: "12.*"
               cuda_suffixed: "true"
             packages:
-              - cuspatial-cu12==24.8.*,>=0.0.0a0
+              - cuspatial-cu12==24.10.*,>=0.0.0a0
           - matrix:
               cuda: "11.*"
               cuda_suffixed: "true"
             packages:
-              - cuspatial-cu11==24.8.*,>=0.0.0a0
+              - cuspatial-cu11==24.10.*,>=0.0.0a0
           - {matrix: null, packages: [*cuspatial_unsuffixed]}
 
+  depends_on_cuproj:
+    common:
+      - output_types: conda
+        packages:
+          - &cuproj_unsuffixed cuproj==24.10.*,>=0.0.0a0
+      - output_types: requirements
+        packages:
+          # pip recognizes the index as a global option for the requirements.txt file
+          - --extra-index-url=https://pypi.nvidia.com
+          - --extra-index-url=https://pypi.anaconda.org/rapidsai-wheels-nightly/simple
+    specific:
+      - output_types: [requirements, pyproject]
+        matrices:
+          - matrix:
+              cuda: "12.*"
+              cuda_suffixed: "true"
+            packages:
+              - cuproj-cu12==24.10.*,>=0.0.0a0
+          - matrix:
+              cuda: "11.*"
+              cuda_suffixed: "true"
+            packages:
+              - cuproj-cu11==24.10.*,>=0.0.0a0
+          - {matrix: null, packages: [*cuproj_unsuffixed]}
+
   depends_on_cupy:
     common:
       - output_types: conda
@@ -515,12 +595,53 @@ dependencies:
     common:
       - output_types: conda
         packages:
-          - libcuspatial==24.8.*
-          - libcuspatial-tests==24.8.*
-  test_cuspatial:
+          - libcuspatial-tests==24.10.*,>=0.0.0a0
+  depends_on_libcuspatial:
     common:
       - output_types: conda
         packages:
-          - libcuspatial==24.8.*
-          - cuspatial==24.8.*,>=0.0.0a0
-          - cuproj==24.8.*,>=0.0.0a0
+          - &libcuspatial_unsuffixed libcuspatial==24.10.*,>=0.0.0a0
+      - output_types: requirements
+        packages:
+          # pip recognizes the index as a global option for the requirements.txt file
+          - --extra-index-url=https://pypi.nvidia.com
+          - --extra-index-url=https://pypi.anaconda.org/rapidsai-wheels-nightly/simple
+    specific:
+      - output_types: [requirements, pyproject]
+        matrices:
+          - matrix:
+              cuda: "12.*"
+              cuda_suffixed: "true"
+            packages:
+              - libcuspatial-cu12==24.10.*,>=0.0.0a0
+          - matrix:
+              cuda: "11.*"
+              cuda_suffixed: "true"
+            packages:
+              - libcuspatial-cu11==24.10.*,>=0.0.0a0
+          - {matrix: null, packages: [*libcuspatial_unsuffixed]}
+
+  depends_on_librmm:
+    common:
+      - output_types: conda
+        packages:
+          - &librmm_unsuffixed librmm==24.10.*,>=0.0.0a0
+      - output_types: requirements
+        packages:
+          # pip recognizes the index as a global option for the requirements.txt file
+          - --extra-index-url=https://pypi.nvidia.com
+          - --extra-index-url=https://pypi.anaconda.org/rapidsai-wheels-nightly/simple
+    specific:
+      - output_types: [requirements, pyproject]
+        matrices:
+          - matrix:
+              cuda: "12.*"
+              cuda_suffixed: "true"
+            packages:
+              - librmm-cu12==24.10.*,>=0.0.0a0
+          - matrix:
+              cuda: "11.*"
+              cuda_suffixed: "true"
+            packages:
+              - librmm-cu11==24.10.*,>=0.0.0a0
+          - {matrix: null, packages: [*librmm_unsuffixed]}
diff --git a/docs/cuproj/source/user_guide/cuproj_api_examples.ipynb b/docs/cuproj/source/user_guide/cuproj_api_examples.ipynb
index 1250daf54..2c78ac8bb 100644
--- a/docs/cuproj/source/user_guide/cuproj_api_examples.ipynb
+++ b/docs/cuproj/source/user_guide/cuproj_api_examples.ipynb
@@ -45,7 +45,7 @@
    "metadata": {},
    "outputs": [],
    "source": [
-    "# !conda create -n rapids-24.08 --solver=libmamba -c rapidsai -c conda-forge -c nvidia \\ \n",
+    "# !conda create -n rapids-24.10 --solver=libmamba -c rapidsai -c conda-forge -c nvidia \\ \n",
     "#     cuproj-23.12 python=3.10 cuda-version=12.0"
    ]
   },
diff --git a/docs/source/developer_guide/build.md b/docs/source/developer_guide/build.md
index d20e27b3d..8683d1461 100644
--- a/docs/source/developer_guide/build.md
+++ b/docs/source/developer_guide/build.md
@@ -4,7 +4,7 @@
 
 - gcc >= 7.5
 - cmake >= 3.26.4
-- miniconda
+- miniforge
 
 ## Fetch cuSpatial repository
 
diff --git a/docs/source/user_guide/cuspatial_api_examples.ipynb b/docs/source/user_guide/cuspatial_api_examples.ipynb
index 90ac97083..0a34ea08a 100644
--- a/docs/source/user_guide/cuspatial_api_examples.ipynb
+++ b/docs/source/user_guide/cuspatial_api_examples.ipynb
@@ -57,8 +57,8 @@
    "metadata": {},
    "outputs": [],
    "source": [
-    "# !conda create -n rapids-24.08 -c rapidsai -c conda-forge -c nvidia \\ \n",
-    "#     cuspatial=24.08 python=3.9 cudatoolkit=11.5 "
+    "# !conda create -n rapids-24.10 -c rapidsai -c conda-forge -c nvidia \\ \n",
+    "#     cuspatial=24.10 python=3.9 cudatoolkit=11.5 "
    ]
   },
   {
diff --git a/notebooks/nyc_taxi_years_correlation.ipynb b/notebooks/nyc_taxi_years_correlation.ipynb
index d13ce0d3b..d1c377e6c 100644
--- a/notebooks/nyc_taxi_years_correlation.ipynb
+++ b/notebooks/nyc_taxi_years_correlation.ipynb
@@ -130,8 +130,8 @@
     "taxi_zone_rings = cuspatial.GeoSeries.from_polygons_xy(\n",
     "    polygons_xy=taxi_zones.polygons.xy,\n",
     "    ring_offset=taxi_zones.polygons.ring_offset,\n",
-    "    part_offsets=taxi_zones.polygons.part_offset,\n",
-    "    geometry_offsets=cudf.Series(range(len(taxi_zones.polygons.part_offset)))\n",
+    "    part_offset=taxi_zones.polygons.part_offset,\n",
+    "    geometry_offset=cudf.Series(range(len(taxi_zones.polygons.part_offset)))\n",
     ")"
    ]
   },
diff --git a/pyproject.toml b/pyproject.toml
index 9b073cb98..0de7433bd 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -1,6 +1,8 @@
+# Copyright (c) 2019-2024, NVIDIA CORPORATION.
+
 [tool.black]
 line-length = 79
-target-version = ["py39"]
+target-version = ["py310"]
 include = '\.py?$'
 force-exclude = '''
 /(
diff --git a/python/cuproj/pyproject.toml b/python/cuproj/pyproject.toml
index eca654f73..f7e920543 100644
--- a/python/cuproj/pyproject.toml
+++ b/python/cuproj/pyproject.toml
@@ -16,7 +16,7 @@
 build-backend = "rapids_build_backend.build"
 requires = [
     "rapids-build-backend>=0.3.0,<0.4.0.dev0",
-    "scikit-build-core[pyproject]>=0.7.0",
+    "scikit-build-core[pyproject]>=0.10.0",
 ] # This list was generated by `rapids-dependency-file-generator`. To make changes, edit ../../dependencies.yaml and run `rapids-dependency-file-generator`.
 
 [project]
@@ -26,10 +26,9 @@ description = "cuProj: GPU-Accelerated Coordinate Projection"
 readme = { file = "README.md", content-type = "text/markdown" }
 authors = [{ name = "NVIDIA Corporation" }]
 license = { text = "Apache 2.0" }
-requires-python = ">=3.9"
+requires-python = ">=3.10"
 dependencies = [
     "cupy-cuda11x>=12.0.0",
-    "rmm==24.8.*,>=0.0.0a0",
 ] # This list was generated by `rapids-dependency-file-generator`. To make changes, edit ../../dependencies.yaml and run `rapids-dependency-file-generator`.
 classifiers = [
     "Intended Audience :: Developers",
@@ -37,16 +36,16 @@ classifiers = [
     "Topic :: Scientific/Engineering",
     "License :: OSI Approved :: Apache Software License",
     "Programming Language :: Python",
-    "Programming Language :: Python :: 3.9",
     "Programming Language :: Python :: 3.10",
     "Programming Language :: Python :: 3.11",
+    "Programming Language :: Python :: 3.12",
 ]
 
 [project.optional-dependencies]
 test = [
-    "cuspatial==24.8.*,>=0.0.0a0",
-    "geopandas>=0.11.0",
-    "numpy>=1.23,<2.0a0",
+    "cuspatial==24.10.*,>=0.0.0a0",
+    "geopandas>=1.0.0",
+    "numpy>=1.23,<3.0a0",
     "pyproj>=3.6.0,<3.7a0",
     "pytest",
     "pytest-cov",
@@ -65,7 +64,7 @@ force_grid_wrap = 0
 combine_as_imports = true
 order_by_type = true
 known_dask = ["dask", "distributed", "dask_cuda"]
-known_rapids = ["rmm", "cudf"]
+known_rapids = ["rmm", "cudf", "pylibcudf"]
 known_first_party = ["cuspatial", "cuproj"]
 default_section = "THIRDPARTY"
 sections = [
@@ -95,7 +94,8 @@ skip = [
 [tool.scikit-build]
 build-dir = "build/{wheel_tag}"
 cmake.build-type = "Release"
-cmake.minimum-version = "3.26.4"
+cmake.version = "CMakeLists.txt"
+minimum-version = "build-system.requires"
 ninja.make-fallback = true
 sdist.exclude = ["*tests*"]
 sdist.reproducible = true
@@ -122,7 +122,7 @@ matrix-entry = "cuda_suffixed=true"
 requires = [
     "cmake>=3.26.4,!=3.30.0",
     "cython>=3.0.0",
+    "librmm==24.10.*,>=0.0.0a0",
     "ninja",
-    "rmm==24.8.*,>=0.0.0a0",
     "wheel",
 ] # This list was generated by `rapids-dependency-file-generator`. To make changes, edit ../../dependencies.yaml and run `rapids-dependency-file-generator`.
diff --git a/python/cuspatial/CMakeLists.txt b/python/cuspatial/CMakeLists.txt
index e3678c311..c3dc00c8a 100644
--- a/python/cuspatial/CMakeLists.txt
+++ b/python/cuspatial/CMakeLists.txt
@@ -23,42 +23,9 @@ project(
   VERSION "${RAPIDS_VERSION}"
   LANGUAGES CXX CUDA)
 
-option(FIND_CUSPATIAL_CPP "Search for existing cuspatial C++ installations before defaulting to local files"
-       OFF)
-
-# If the user requested it we attempt to find cuspatial.
-if(FIND_CUSPATIAL_CPP)
-  find_package(cuspatial "${RAPIDS_VERSION}")
-else()
-  set(cuspatial_FOUND OFF)
-endif()
-
-if(NOT cuspatial_FOUND)
-  set(BUILD_TESTS OFF)
-  set(BUILD_BENCHMARKS OFF)
-  set(CUDA_STATIC_RUNTIME ON)
-  set(CUSPATIAL_USE_CUDF_STATIC ON)
-  set(CUSPATIAL_EXCLUDE_CUDF_FROM_ALL ON)
-
-  add_subdirectory(../../cpp cuspatial-cpp EXCLUDE_FROM_ALL)
-
-  set(cython_lib_dir cuspatial)
-  include(cmake/Modules/WheelHelpers.cmake)
-  # TODO: This install is currently overzealous. We should only install the libraries that are
-  # downloaded by CPM during the build, not libraries that were found on the system.  However, in
-  # practice this would only be a problem if libcudf was not found but some of the
-  # dependencies were, and we have no real use cases where that happens.
-  install_aliased_imported_targets(
-    TARGETS cuspatial arrow_shared nvcomp::nvcomp nvcomp::nvcomp_gdeflate nvcomp::nvcomp_bitcomp
-    DESTINATION ${cython_lib_dir}
-  )
-endif()
+find_package(cuspatial "${RAPIDS_VERSION}" REQUIRED)
 
 include(rapids-cython-core)
 rapids_cython_init()
 
 add_subdirectory(cuspatial/_lib)
-
-if(DEFINED cython_lib_dir)
-  rapids_cython_add_rpath_entries(TARGET cuspatial PATHS "${cython_lib_dir}")
-endif()
diff --git a/python/cuspatial/cmake/Modules/WheelHelpers.cmake b/python/cuspatial/cmake/Modules/WheelHelpers.cmake
deleted file mode 100644
index 278d6751c..000000000
--- a/python/cuspatial/cmake/Modules/WheelHelpers.cmake
+++ /dev/null
@@ -1,59 +0,0 @@
-# =============================================================================
-# Copyright (c) 2022-2023, NVIDIA CORPORATION.
-#
-# 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.
-# =============================================================================
-include_guard(GLOBAL)
-
-# Making libraries available inside wheels by installing the associated targets.
-function(install_aliased_imported_targets)
-  list(APPEND CMAKE_MESSAGE_CONTEXT "install_aliased_imported_targets")
-
-  set(options "")
-  set(one_value "DESTINATION")
-  set(multi_value "TARGETS")
-  cmake_parse_arguments(_ "${options}" "${one_value}" "${multi_value}" ${ARGN})
-
-  message(VERBOSE "Installing targets '${__TARGETS}' into lib_dir '${__DESTINATION}'")
-
-  foreach(target IN LISTS __TARGETS)
-
-    if(NOT TARGET ${target})
-      message(VERBOSE "No target named ${target}")
-      continue()
-    endif()
-
-    get_target_property(alias_target ${target} ALIASED_TARGET)
-    if(alias_target)
-      set(target ${alias_target})
-    endif()
-
-    get_target_property(is_imported ${target} IMPORTED)
-    if(NOT is_imported)
-      # If the target isn't imported, install it into the wheel
-      install(TARGETS ${target} DESTINATION ${__DESTINATION})
-      message(VERBOSE "install(TARGETS ${target} DESTINATION ${__DESTINATION})")
-    else()
-      # If the target is imported, make sure it's global
-      get_target_property(type ${target} TYPE)
-      if(${type} STREQUAL "UNKNOWN_LIBRARY")
-        install(FILES $<TARGET_FILE:${target}> DESTINATION ${__DESTINATION})
-        message(VERBOSE "install(FILES $<TARGET_FILE:${target}> DESTINATION ${__DESTINATION})")
-      else()
-        install(IMPORTED_RUNTIME_ARTIFACTS ${target} DESTINATION ${__DESTINATION})
-        message(
-          VERBOSE
-          "install(IMPORTED_RUNTIME_ARTIFACTS $<TARGET_FILE:${target}> DESTINATION ${__DESTINATION})"
-        )
-      endif()
-    endif()
-  endforeach()
-endfunction()
diff --git a/python/cuspatial/cuspatial/__init__.py b/python/cuspatial/cuspatial/__init__.py
index 069b5454d..89c193819 100644
--- a/python/cuspatial/cuspatial/__init__.py
+++ b/python/cuspatial/cuspatial/__init__.py
@@ -1,4 +1,16 @@
-# Copyright (c) 2023, NVIDIA CORPORATION.
+# Copyright (c) 2023-2024, NVIDIA CORPORATION.
+
+# If libcuspatial was installed as a wheel, we must request it to load the library
+# symbols. Otherwise, we assume that the library was installed in a system path that ld
+# can find.
+try:
+    import libcuspatial
+except ModuleNotFoundError:
+    pass
+else:
+    libcuspatial.load_library()
+    del libcuspatial
+
 
 from ._version import __git_commit__, __version__
 from .core.geodataframe import GeoDataFrame
diff --git a/python/cuspatial/cuspatial/_lib/cpp/column/geometry_column_view.pxd b/python/cuspatial/cuspatial/_lib/cpp/column/geometry_column_view.pxd
index 2b867a069..2c2568e7b 100644
--- a/python/cuspatial/cuspatial/_lib/cpp/column/geometry_column_view.pxd
+++ b/python/cuspatial/cuspatial/_lib/cpp/column/geometry_column_view.pxd
@@ -1,8 +1,9 @@
-# Copyright (c) 2023, NVIDIA CORPORATION.
+# Copyright (c) 2023-2024, NVIDIA CORPORATION.
 
 from libcpp.memory cimport unique_ptr
 
-from cudf._lib.column cimport column, column_view
+from pylibcudf.libcudf.column.column cimport column
+from pylibcudf.libcudf.column.column_view cimport column_view
 
 from cuspatial._lib.cpp.types cimport collection_type_id, geometry_type_id
 
diff --git a/python/cuspatial/cuspatial/_lib/cpp/distance.pxd b/python/cuspatial/cuspatial/_lib/cpp/distance.pxd
index 98925a516..855c8b527 100644
--- a/python/cuspatial/cuspatial/_lib/cpp/distance.pxd
+++ b/python/cuspatial/cuspatial/_lib/cpp/distance.pxd
@@ -3,9 +3,9 @@
 from libcpp.memory cimport unique_ptr
 from libcpp.utility cimport pair
 
-from cudf._lib.pylibcudf.libcudf.column.column cimport column
-from cudf._lib.pylibcudf.libcudf.column.column_view cimport column_view
-from cudf._lib.pylibcudf.libcudf.table.table_view cimport table_view
+from pylibcudf.libcudf.column.column cimport column
+from pylibcudf.libcudf.column.column_view cimport column_view
+from pylibcudf.libcudf.table.table_view cimport table_view
 
 from cuspatial._lib.cpp.column.geometry_column_view cimport (
     geometry_column_view,
diff --git a/python/cuspatial/cuspatial/_lib/cpp/distance/polygon_distance.pxd b/python/cuspatial/cuspatial/_lib/cpp/distance/polygon_distance.pxd
index 3a92adae3..2c9ca9826 100644
--- a/python/cuspatial/cuspatial/_lib/cpp/distance/polygon_distance.pxd
+++ b/python/cuspatial/cuspatial/_lib/cpp/distance/polygon_distance.pxd
@@ -2,7 +2,7 @@
 
 from libcpp.memory cimport unique_ptr
 
-from cudf._lib.pylibcudf.libcudf.column.column cimport column
+from pylibcudf.libcudf.column.column cimport column
 
 from cuspatial._lib.cpp.column.geometry_column_view cimport (
     geometry_column_view,
diff --git a/python/cuspatial/cuspatial/_lib/cpp/linestring_bounding_boxes.pxd b/python/cuspatial/cuspatial/_lib/cpp/linestring_bounding_boxes.pxd
index 8622a417c..6d95074af 100644
--- a/python/cuspatial/cuspatial/_lib/cpp/linestring_bounding_boxes.pxd
+++ b/python/cuspatial/cuspatial/_lib/cpp/linestring_bounding_boxes.pxd
@@ -2,8 +2,8 @@
 
 from libcpp.memory cimport unique_ptr
 
-from cudf._lib.pylibcudf.libcudf.column.column_view cimport column_view
-from cudf._lib.pylibcudf.libcudf.table.table cimport table
+from pylibcudf.libcudf.column.column_view cimport column_view
+from pylibcudf.libcudf.table.table cimport table
 
 
 cdef extern from "cuspatial/bounding_boxes.hpp" \
diff --git a/python/cuspatial/cuspatial/_lib/cpp/linestring_intersection.pxd b/python/cuspatial/cuspatial/_lib/cpp/linestring_intersection.pxd
index 0478c87de..e6a3a93f7 100644
--- a/python/cuspatial/cuspatial/_lib/cpp/linestring_intersection.pxd
+++ b/python/cuspatial/cuspatial/_lib/cpp/linestring_intersection.pxd
@@ -1,8 +1,9 @@
-# Copyright (c) 2023, NVIDIA CORPORATION.
+# Copyright (c) 2023-2024, NVIDIA CORPORATION.
 
 from libcpp.memory cimport unique_ptr
 
-from cudf._lib.column cimport column, column_view
+from pylibcudf.libcudf.column.column cimport column
+from pylibcudf.libcudf.column.column_view cimport column_view
 
 from cuspatial._lib.cpp.column.geometry_column_view cimport (
     geometry_column_view,
diff --git a/python/cuspatial/cuspatial/_lib/cpp/nearest_points.pxd b/python/cuspatial/cuspatial/_lib/cpp/nearest_points.pxd
index 826505c30..3151be093 100644
--- a/python/cuspatial/cuspatial/_lib/cpp/nearest_points.pxd
+++ b/python/cuspatial/cuspatial/_lib/cpp/nearest_points.pxd
@@ -1,8 +1,9 @@
-# Copyright (c) 2022-2023, NVIDIA CORPORATION.
+# Copyright (c) 2022-2024, NVIDIA CORPORATION.
 
 from libcpp.memory cimport unique_ptr
 
-from cudf._lib.column cimport column, column_view
+from pylibcudf.libcudf.column.column cimport column
+from pylibcudf.libcudf.column.column_view cimport column_view
 
 from cuspatial._lib.cpp.optional cimport optional
 
diff --git a/python/cuspatial/cuspatial/_lib/cpp/pairwise_multipoint_equals_count.pxd b/python/cuspatial/cuspatial/_lib/cpp/pairwise_multipoint_equals_count.pxd
index 5af573bd0..463c8998b 100644
--- a/python/cuspatial/cuspatial/_lib/cpp/pairwise_multipoint_equals_count.pxd
+++ b/python/cuspatial/cuspatial/_lib/cpp/pairwise_multipoint_equals_count.pxd
@@ -1,8 +1,9 @@
-# Copyright (c) 2023, NVIDIA CORPORATION.
+# Copyright (c) 2023-2024, NVIDIA CORPORATION.
 
 from libcpp.memory cimport unique_ptr
 
-from cudf._lib.column cimport column, column_view
+from pylibcudf.libcudf.column.column cimport column
+from pylibcudf.libcudf.column.column_view cimport column_view
 
 from cuspatial._lib.cpp.column.geometry_column_view cimport (
     geometry_column_view,
diff --git a/python/cuspatial/cuspatial/_lib/cpp/pairwise_point_in_polygon.pxd b/python/cuspatial/cuspatial/_lib/cpp/pairwise_point_in_polygon.pxd
index a9f1f7622..07e8d1d92 100644
--- a/python/cuspatial/cuspatial/_lib/cpp/pairwise_point_in_polygon.pxd
+++ b/python/cuspatial/cuspatial/_lib/cpp/pairwise_point_in_polygon.pxd
@@ -1,8 +1,9 @@
-# Copyright (c) 2022-2023, NVIDIA CORPORATION.
+# Copyright (c) 2022-2024, NVIDIA CORPORATION.
 
 from libcpp.memory cimport unique_ptr
 
-from cudf._lib.column cimport column, column_view
+from pylibcudf.libcudf.column.column cimport column
+from pylibcudf.libcudf.column.column_view cimport column_view
 
 
 cdef extern from "cuspatial/point_in_polygon.hpp" \
diff --git a/python/cuspatial/cuspatial/_lib/cpp/point_in_polygon.pxd b/python/cuspatial/cuspatial/_lib/cpp/point_in_polygon.pxd
index 94facd936..6bda46812 100644
--- a/python/cuspatial/cuspatial/_lib/cpp/point_in_polygon.pxd
+++ b/python/cuspatial/cuspatial/_lib/cpp/point_in_polygon.pxd
@@ -1,8 +1,9 @@
-# Copyright (c) 2020, NVIDIA CORPORATION.
+# Copyright (c) 2020-2024, NVIDIA CORPORATION.
 
 from libcpp.memory cimport unique_ptr
 
-from cudf._lib.column cimport column, column_view
+from pylibcudf.libcudf.column.column cimport column
+from pylibcudf.libcudf.column.column_view cimport column_view
 
 
 cdef extern from "cuspatial/point_in_polygon.hpp" namespace "cuspatial" nogil:
diff --git a/python/cuspatial/cuspatial/_lib/cpp/points_in_range.pxd b/python/cuspatial/cuspatial/_lib/cpp/points_in_range.pxd
index d03b3a133..d5b150ed0 100644
--- a/python/cuspatial/cuspatial/_lib/cpp/points_in_range.pxd
+++ b/python/cuspatial/cuspatial/_lib/cpp/points_in_range.pxd
@@ -2,8 +2,10 @@
 
 from libcpp.memory cimport unique_ptr
 
-from cudf._lib.column cimport column, column_view
-from cudf._lib.pylibcudf.libcudf.table.table cimport table, table_view
+from pylibcudf.libcudf.column.column cimport column
+from pylibcudf.libcudf.column.column_view cimport column_view
+from pylibcudf.libcudf.table.table cimport table
+from pylibcudf.libcudf.table.table_view cimport table_view
 
 
 cdef extern from "cuspatial/points_in_range.hpp" namespace "cuspatial" nogil:
diff --git a/python/cuspatial/cuspatial/_lib/cpp/polygon_bounding_boxes.pxd b/python/cuspatial/cuspatial/_lib/cpp/polygon_bounding_boxes.pxd
index 45910626b..72bb23e53 100644
--- a/python/cuspatial/cuspatial/_lib/cpp/polygon_bounding_boxes.pxd
+++ b/python/cuspatial/cuspatial/_lib/cpp/polygon_bounding_boxes.pxd
@@ -2,8 +2,8 @@
 
 from libcpp.memory cimport unique_ptr
 
-from cudf._lib.pylibcudf.libcudf.column.column_view cimport column_view
-from cudf._lib.pylibcudf.libcudf.table.table cimport table
+from pylibcudf.libcudf.column.column_view cimport column_view
+from pylibcudf.libcudf.table.table cimport table
 
 
 cdef extern from "cuspatial/bounding_boxes.hpp" \
diff --git a/python/cuspatial/cuspatial/_lib/cpp/projection.pxd b/python/cuspatial/cuspatial/_lib/cpp/projection.pxd
index 724269486..23a95fc81 100644
--- a/python/cuspatial/cuspatial/_lib/cpp/projection.pxd
+++ b/python/cuspatial/cuspatial/_lib/cpp/projection.pxd
@@ -3,8 +3,8 @@
 from libcpp.memory cimport unique_ptr
 from libcpp.pair cimport pair
 
-from cudf._lib.pylibcudf.libcudf.column.column cimport column
-from cudf._lib.pylibcudf.libcudf.column.column_view cimport column_view
+from pylibcudf.libcudf.column.column cimport column
+from pylibcudf.libcudf.column.column_view cimport column_view
 
 
 cdef extern from "cuspatial/projection.hpp" namespace "cuspatial" \
diff --git a/python/cuspatial/cuspatial/_lib/cpp/quadtree.pxd b/python/cuspatial/cuspatial/_lib/cpp/quadtree.pxd
index db2dabaca..f3554f5ff 100644
--- a/python/cuspatial/cuspatial/_lib/cpp/quadtree.pxd
+++ b/python/cuspatial/cuspatial/_lib/cpp/quadtree.pxd
@@ -4,10 +4,10 @@ from libc.stdint cimport int8_t
 from libcpp.memory cimport unique_ptr
 from libcpp.pair cimport pair
 
-from cudf._lib.pylibcudf.libcudf.column.column cimport column
-from cudf._lib.pylibcudf.libcudf.column.column_view cimport column_view
-from cudf._lib.pylibcudf.libcudf.table.table cimport table
-from cudf._lib.pylibcudf.libcudf.types cimport size_type
+from pylibcudf.libcudf.column.column cimport column
+from pylibcudf.libcudf.column.column_view cimport column_view
+from pylibcudf.libcudf.table.table cimport table
+from pylibcudf.libcudf.types cimport size_type
 
 
 cdef extern from "cuspatial/point_quadtree.hpp" namespace "cuspatial" nogil:
diff --git a/python/cuspatial/cuspatial/_lib/cpp/spatial_join.pxd b/python/cuspatial/cuspatial/_lib/cpp/spatial_join.pxd
index ab96cf53c..2aa024819 100644
--- a/python/cuspatial/cuspatial/_lib/cpp/spatial_join.pxd
+++ b/python/cuspatial/cuspatial/_lib/cpp/spatial_join.pxd
@@ -3,8 +3,9 @@
 from libc.stdint cimport int8_t
 from libcpp.memory cimport unique_ptr
 
-from cudf._lib.column cimport column_view
-from cudf._lib.pylibcudf.libcudf.table.table cimport table, table_view
+from pylibcudf.libcudf.column.column_view cimport column_view
+from pylibcudf.libcudf.table.table cimport table
+from pylibcudf.libcudf.table.table_view cimport table_view
 
 
 cdef extern from "cuspatial/spatial_join.hpp" namespace "cuspatial" nogil:
diff --git a/python/cuspatial/cuspatial/_lib/cpp/trajectory.pxd b/python/cuspatial/cuspatial/_lib/cpp/trajectory.pxd
index 5940349cc..d15a71c5b 100644
--- a/python/cuspatial/cuspatial/_lib/cpp/trajectory.pxd
+++ b/python/cuspatial/cuspatial/_lib/cpp/trajectory.pxd
@@ -3,10 +3,10 @@
 from libcpp.memory cimport unique_ptr
 from libcpp.pair cimport pair
 
-from cudf._lib.pylibcudf.libcudf.column.column cimport column
-from cudf._lib.pylibcudf.libcudf.column.column_view cimport column_view
-from cudf._lib.pylibcudf.libcudf.table.table cimport table
-from cudf._lib.pylibcudf.libcudf.types cimport size_type
+from pylibcudf.libcudf.column.column cimport column
+from pylibcudf.libcudf.column.column_view cimport column_view
+from pylibcudf.libcudf.table.table cimport table
+from pylibcudf.libcudf.types cimport size_type
 
 
 cdef extern from "cuspatial/trajectory.hpp" namespace "cuspatial" nogil:
diff --git a/python/cuspatial/cuspatial/_lib/distance.pyx b/python/cuspatial/cuspatial/_lib/distance.pyx
index 90e7b5ac0..0295dce92 100644
--- a/python/cuspatial/cuspatial/_lib/distance.pyx
+++ b/python/cuspatial/cuspatial/_lib/distance.pyx
@@ -4,10 +4,10 @@ from libcpp.memory cimport make_shared, shared_ptr, unique_ptr
 from libcpp.utility cimport move, pair
 
 from cudf._lib.column cimport Column
-from cudf._lib.pylibcudf.libcudf.column.column cimport column
-from cudf._lib.pylibcudf.libcudf.column.column_view cimport column_view
-from cudf._lib.pylibcudf.libcudf.table.table_view cimport table_view
 from cudf._lib.utils cimport columns_from_table_view
+from pylibcudf.libcudf.column.column cimport column
+from pylibcudf.libcudf.column.column_view cimport column_view
+from pylibcudf.libcudf.table.table_view cimport table_view
 
 from cuspatial._lib.cpp.column.geometry_column_view cimport (
     geometry_column_view,
diff --git a/python/cuspatial/cuspatial/_lib/linestring_bounding_boxes.pyx b/python/cuspatial/cuspatial/_lib/linestring_bounding_boxes.pyx
index 4023f4965..0badef0d4 100644
--- a/python/cuspatial/cuspatial/_lib/linestring_bounding_boxes.pyx
+++ b/python/cuspatial/cuspatial/_lib/linestring_bounding_boxes.pyx
@@ -4,9 +4,9 @@ from libcpp.memory cimport unique_ptr
 from libcpp.utility cimport move
 
 from cudf._lib.column cimport Column
-from cudf._lib.pylibcudf.libcudf.column.column_view cimport column_view
-from cudf._lib.pylibcudf.libcudf.table.table cimport table
 from cudf._lib.utils cimport columns_from_unique_ptr
+from pylibcudf.libcudf.column.column_view cimport column_view
+from pylibcudf.libcudf.table.table cimport table
 
 from cuspatial._lib.cpp.linestring_bounding_boxes cimport (
     linestring_bounding_boxes as cpp_linestring_bounding_boxes,
diff --git a/python/cuspatial/cuspatial/_lib/nearest_points.pyx b/python/cuspatial/cuspatial/_lib/nearest_points.pyx
index da24357d3..6f64dba13 100644
--- a/python/cuspatial/cuspatial/_lib/nearest_points.pyx
+++ b/python/cuspatial/cuspatial/_lib/nearest_points.pyx
@@ -3,7 +3,7 @@
 from libcpp.utility cimport move
 
 from cudf._lib.column cimport Column
-from cudf._lib.pylibcudf.libcudf.column.column_view cimport column_view
+from pylibcudf.libcudf.column.column_view cimport column_view
 
 from cuspatial._lib.cpp.nearest_points cimport (
     pairwise_point_linestring_nearest_points as c_func,
diff --git a/python/cuspatial/cuspatial/_lib/pairwise_multipoint_equals_count.pyx b/python/cuspatial/cuspatial/_lib/pairwise_multipoint_equals_count.pyx
index aea144568..cca06afcc 100644
--- a/python/cuspatial/cuspatial/_lib/pairwise_multipoint_equals_count.pyx
+++ b/python/cuspatial/cuspatial/_lib/pairwise_multipoint_equals_count.pyx
@@ -1,9 +1,10 @@
-# Copyright (c) 2023, NVIDIA CORPORATION.
+# Copyright (c) 2023-2024, NVIDIA CORPORATION.
 
 from libcpp.memory cimport make_shared, shared_ptr, unique_ptr
 from libcpp.utility cimport move
 
-from cudf._lib.column cimport Column, column
+from cudf._lib.column cimport Column
+from pylibcudf.libcudf.column.column cimport column
 
 from cuspatial._lib.cpp.column.geometry_column_view cimport (
     geometry_column_view,
diff --git a/python/cuspatial/cuspatial/_lib/pairwise_point_in_polygon.pyx b/python/cuspatial/cuspatial/_lib/pairwise_point_in_polygon.pyx
index 8ce2c952e..84b442823 100644
--- a/python/cuspatial/cuspatial/_lib/pairwise_point_in_polygon.pyx
+++ b/python/cuspatial/cuspatial/_lib/pairwise_point_in_polygon.pyx
@@ -1,9 +1,11 @@
-# Copyright (c) 2022, NVIDIA CORPORATION.
+# Copyright (c) 2022-2024, NVIDIA CORPORATION.
 
 from libcpp.memory cimport unique_ptr
 from libcpp.utility cimport move
 
-from cudf._lib.column cimport Column, column, column_view
+from cudf._lib.column cimport Column
+from pylibcudf.libcudf.column.column cimport column
+from pylibcudf.libcudf.column.column_view cimport column_view
 
 from cuspatial._lib.cpp.pairwise_point_in_polygon cimport (
     pairwise_point_in_polygon as cpp_pairwise_point_in_polygon,
diff --git a/python/cuspatial/cuspatial/_lib/point_in_polygon.pyx b/python/cuspatial/cuspatial/_lib/point_in_polygon.pyx
index 1b585614d..833e87cc3 100644
--- a/python/cuspatial/cuspatial/_lib/point_in_polygon.pyx
+++ b/python/cuspatial/cuspatial/_lib/point_in_polygon.pyx
@@ -1,9 +1,11 @@
-# Copyright (c) 2020, NVIDIA CORPORATION.
+# Copyright (c) 2020-2024, NVIDIA CORPORATION.
 
 from libcpp.memory cimport unique_ptr
 from libcpp.utility cimport move
 
-from cudf._lib.column cimport Column, column, column_view
+from cudf._lib.column cimport Column
+from pylibcudf.libcudf.column.column cimport column
+from pylibcudf.libcudf.column.column_view cimport column_view
 
 from cuspatial._lib.cpp.point_in_polygon cimport (
     point_in_polygon as cpp_point_in_polygon,
diff --git a/python/cuspatial/cuspatial/_lib/points_in_range.pyx b/python/cuspatial/cuspatial/_lib/points_in_range.pyx
index 4c25c2dbf..833dfbcfa 100644
--- a/python/cuspatial/cuspatial/_lib/points_in_range.pyx
+++ b/python/cuspatial/cuspatial/_lib/points_in_range.pyx
@@ -3,9 +3,10 @@
 from libcpp.memory cimport unique_ptr
 from libcpp.utility cimport move
 
-from cudf._lib.column cimport Column, column_view
-from cudf._lib.pylibcudf.libcudf.table.table cimport table
+from cudf._lib.column cimport Column
 from cudf._lib.utils cimport data_from_unique_ptr
+from pylibcudf.libcudf.column.column_view cimport column_view
+from pylibcudf.libcudf.table.table cimport table
 
 from cuspatial._lib.cpp.points_in_range cimport (
     points_in_range as cpp_points_in_range,
diff --git a/python/cuspatial/cuspatial/_lib/polygon_bounding_boxes.pyx b/python/cuspatial/cuspatial/_lib/polygon_bounding_boxes.pyx
index 0f963fd5a..bd01a08f5 100644
--- a/python/cuspatial/cuspatial/_lib/polygon_bounding_boxes.pyx
+++ b/python/cuspatial/cuspatial/_lib/polygon_bounding_boxes.pyx
@@ -4,9 +4,9 @@ from libcpp.memory cimport unique_ptr
 from libcpp.utility cimport move
 
 from cudf._lib.column cimport Column
-from cudf._lib.pylibcudf.libcudf.column.column_view cimport column_view
-from cudf._lib.pylibcudf.libcudf.table.table cimport table
 from cudf._lib.utils cimport columns_from_unique_ptr
+from pylibcudf.libcudf.column.column_view cimport column_view
+from pylibcudf.libcudf.table.table cimport table
 
 from cuspatial._lib.cpp.polygon_bounding_boxes cimport (
     polygon_bounding_boxes as cpp_polygon_bounding_boxes,
diff --git a/python/cuspatial/cuspatial/_lib/quadtree.pyx b/python/cuspatial/cuspatial/_lib/quadtree.pyx
index eb548a882..4f1a8395b 100644
--- a/python/cuspatial/cuspatial/_lib/quadtree.pyx
+++ b/python/cuspatial/cuspatial/_lib/quadtree.pyx
@@ -6,11 +6,11 @@ from libcpp.pair cimport pair
 from libcpp.utility cimport move
 
 from cudf._lib.column cimport Column
-from cudf._lib.pylibcudf.libcudf.column.column cimport column
-from cudf._lib.pylibcudf.libcudf.column.column_view cimport column_view
-from cudf._lib.pylibcudf.libcudf.table.table cimport table
-from cudf._lib.pylibcudf.libcudf.types cimport size_type
 from cudf._lib.utils cimport data_from_unique_ptr
+from pylibcudf.libcudf.column.column cimport column
+from pylibcudf.libcudf.column.column_view cimport column_view
+from pylibcudf.libcudf.table.table cimport table
+from pylibcudf.libcudf.types cimport size_type
 
 from cuspatial._lib.cpp.quadtree cimport (
     quadtree_on_points as cpp_quadtree_on_points,
diff --git a/python/cuspatial/cuspatial/_lib/spatial.pyx b/python/cuspatial/cuspatial/_lib/spatial.pyx
index 6698da904..0afe2484e 100644
--- a/python/cuspatial/cuspatial/_lib/spatial.pyx
+++ b/python/cuspatial/cuspatial/_lib/spatial.pyx
@@ -5,8 +5,8 @@ from libcpp.pair cimport pair
 from libcpp.utility cimport move
 
 from cudf._lib.column cimport Column
-from cudf._lib.pylibcudf.libcudf.column.column cimport column
-from cudf._lib.pylibcudf.libcudf.column.column_view cimport column_view
+from pylibcudf.libcudf.column.column cimport column
+from pylibcudf.libcudf.column.column_view cimport column_view
 
 from cuspatial._lib.cpp.projection cimport (
     sinusoidal_projection as cpp_sinusoidal_projection,
diff --git a/python/cuspatial/cuspatial/_lib/spatial_join.pyx b/python/cuspatial/cuspatial/_lib/spatial_join.pyx
index 045014aba..04e62eff9 100644
--- a/python/cuspatial/cuspatial/_lib/spatial_join.pyx
+++ b/python/cuspatial/cuspatial/_lib/spatial_join.pyx
@@ -4,9 +4,10 @@ from libc.stdint cimport int8_t
 from libcpp.memory cimport unique_ptr
 from libcpp.utility cimport move
 
-from cudf._lib.column cimport Column, column_view
-from cudf._lib.pylibcudf.libcudf.table.table cimport table, table_view
+from cudf._lib.column cimport Column
 from cudf._lib.utils cimport data_from_unique_ptr, table_view_from_table
+from pylibcudf.libcudf.column.column_view cimport column_view
+from pylibcudf.libcudf.table.table cimport table, table_view
 
 from cuspatial._lib.cpp.spatial_join cimport (
     join_quadtree_and_bounding_boxes as cpp_join_quadtree_and_bounding_boxes,
diff --git a/python/cuspatial/cuspatial/_lib/trajectory.pyx b/python/cuspatial/cuspatial/_lib/trajectory.pyx
index df29dc05a..8c183f5f4 100644
--- a/python/cuspatial/cuspatial/_lib/trajectory.pyx
+++ b/python/cuspatial/cuspatial/_lib/trajectory.pyx
@@ -5,11 +5,11 @@ from libcpp.pair cimport pair
 from libcpp.utility cimport move
 
 from cudf._lib.column cimport Column
-from cudf._lib.pylibcudf.libcudf.column.column cimport column
-from cudf._lib.pylibcudf.libcudf.column.column_view cimport column_view
-from cudf._lib.pylibcudf.libcudf.table.table cimport table
-from cudf._lib.pylibcudf.libcudf.types cimport size_type
 from cudf._lib.utils cimport data_from_unique_ptr
+from pylibcudf.libcudf.column.column cimport column
+from pylibcudf.libcudf.column.column_view cimport column_view
+from pylibcudf.libcudf.table.table cimport table
+from pylibcudf.libcudf.types cimport size_type
 
 from cuspatial._lib.cpp.trajectory cimport (
     derive_trajectories as cpp_derive_trajectories,
diff --git a/python/cuspatial/cuspatial/_lib/utils.pxd b/python/cuspatial/cuspatial/_lib/utils.pxd
index ad1714016..5dff013cb 100644
--- a/python/cuspatial/cuspatial/_lib/utils.pxd
+++ b/python/cuspatial/cuspatial/_lib/utils.pxd
@@ -1,5 +1,5 @@
 # Copyright (c) 2022-2024, NVIDIA CORPORATION.
-from cudf._lib.pylibcudf.libcudf.column.column_view cimport column_view
+from pylibcudf.libcudf.column.column_view cimport column_view
 
 from cuspatial._lib.cpp.optional cimport nullopt, optional
 
diff --git a/python/cuspatial/cuspatial/_lib/utils.pyx b/python/cuspatial/cuspatial/_lib/utils.pyx
index 4aea62863..9bc0b01ab 100644
--- a/python/cuspatial/cuspatial/_lib/utils.pyx
+++ b/python/cuspatial/cuspatial/_lib/utils.pyx
@@ -1,6 +1,6 @@
 # Copyright (c) 2022-2024, NVIDIA CORPORATION.
 from cudf._lib.column cimport Column
-from cudf._lib.pylibcudf.libcudf.column.column_view cimport column_view
+from pylibcudf.libcudf.column.column_view cimport column_view
 
 from cuspatial._lib.cpp.optional cimport nullopt, optional
 
diff --git a/python/cuspatial/cuspatial/core/_column/geocolumn.py b/python/cuspatial/cuspatial/core/_column/geocolumn.py
index 5ad1ffb55..52b9f3381 100644
--- a/python/cuspatial/cuspatial/core/_column/geocolumn.py
+++ b/python/cuspatial/cuspatial/core/_column/geocolumn.py
@@ -1,8 +1,9 @@
-# Copyright (c) 2021-2023 NVIDIA CORPORATION
+# Copyright (c) 2021-2024, NVIDIA CORPORATION
+from __future__ import annotations
 
 from enum import Enum
 from functools import cached_property
-from typing import Tuple, TypeVar
+from typing import TypeVar
 
 import cupy as cp
 import pyarrow as pa
@@ -41,9 +42,8 @@ class GeoColumn(ColumnBase):
 
     def __init__(
         self,
-        data: Tuple,
+        data: tuple[cudf.Series, cudf.Series, cudf.Series, cudf.Series],
         meta: GeoMeta = None,
-        shuffle_order: cudf.Index = None,
     ):
         if (
             isinstance(data[0], cudf.Series)
@@ -61,7 +61,7 @@ def __init__(
             self.polygons = data[3]
             self.polygons.name = "polygons"
         else:
-            raise TypeError("All four Tuple arguments must be cudf.ListSeries")
+            raise TypeError("All four Tuple arguments must be cudf.Series")
         super().__init__(None, size=len(self), dtype="geometry")
 
     def to_arrow(self):
@@ -153,14 +153,14 @@ def _from_points_xy(cls, points_xy: ColumnBase):
         coord_dtype = points_xy.dtype
         return cls(
             (
-                cudf.Series(point_col),
-                cudf.Series(
+                cudf.Series._from_column(point_col),
+                cudf.Series._from_column(
                     empty_geometry_column(Feature_Enum.MULTIPOINT, coord_dtype)
                 ),
-                cudf.Series(
+                cudf.Series._from_column(
                     empty_geometry_column(Feature_Enum.LINESTRING, coord_dtype)
                 ),
-                cudf.Series(
+                cudf.Series._from_column(
                     empty_geometry_column(Feature_Enum.POLYGON, coord_dtype)
                 ),
             ),
@@ -180,6 +180,7 @@ def _from_multipoints_xy(
 
         multi_elements = _xy_as_variable_sized_list(multipoints_xy)
         multipoint_col = ListColumn(
+            data=None,
             dtype=cudf.ListDtype(multi_elements.dtype),
             size=len(geometry_offsets) - 1,
             children=(geometry_offsets, multi_elements),
@@ -205,14 +206,14 @@ def _from_multipoints_xy(
 
         return cls(
             (
-                cudf.Series(
+                cudf.Series._from_column(
                     empty_geometry_column(Feature_Enum.POINT, coord_dtype)
                 ),
-                cudf.Series(multipoint_col),
-                cudf.Series(
+                cudf.Series._from_column(multipoint_col),
+                cudf.Series._from_column(
                     empty_geometry_column(Feature_Enum.LINESTRING, coord_dtype)
                 ),
-                cudf.Series(
+                cudf.Series._from_column(
                     empty_geometry_column(Feature_Enum.POLYGON, coord_dtype)
                 ),
             ),
@@ -235,11 +236,13 @@ def _from_linestrings_xy(
 
         parts_elements = _xy_as_variable_sized_list(linestrings_xy)
         parts_col = ListColumn(
+            data=None,
             dtype=cudf.ListDtype(parts_elements.dtype),
             size=len(part_offsets) - 1,
             children=(part_offsets, parts_elements),
         )
         linestrings_col = ListColumn(
+            data=None,
             dtype=cudf.ListDtype(parts_col.dtype),
             size=len(geometry_offsets) - 1,
             children=(geometry_offsets, parts_col),
@@ -265,14 +268,14 @@ def _from_linestrings_xy(
 
         return cls(
             (
-                cudf.Series(
+                cudf.Series._from_column(
                     empty_geometry_column(Feature_Enum.POINT, coord_dtype)
                 ),
-                cudf.Series(
+                cudf.Series._from_column(
                     empty_geometry_column(Feature_Enum.MULTIPOINT, coord_dtype)
                 ),
-                cudf.Series(linestrings_col),
-                cudf.Series(
+                cudf.Series._from_column(linestrings_col),
+                cudf.Series._from_column(
                     empty_geometry_column(Feature_Enum.POLYGON, coord_dtype)
                 ),
             ),
@@ -296,16 +299,19 @@ def _from_polygons_xy(
 
         ring_elements = _xy_as_variable_sized_list(polygons_xy)
         rings_col = ListColumn(
+            data=None,
             dtype=cudf.ListDtype(ring_elements.dtype),
             size=len(ring_offsets) - 1,
             children=(ring_offsets, ring_elements),
         )
         parts_col = ListColumn(
+            data=None,
             dtype=cudf.ListDtype(rings_col.dtype),
             size=len(part_offsets) - 1,
             children=(part_offsets, rings_col),
         )
         polygons_col = ListColumn(
+            data=None,
             dtype=cudf.ListDtype(parts_col.dtype),
             size=len(geometry_offsets) - 1,
             children=(geometry_offsets, parts_col),
@@ -331,16 +337,16 @@ def _from_polygons_xy(
 
         return cls(
             (
-                cudf.Series(
+                cudf.Series._from_column(
                     empty_geometry_column(Feature_Enum.POINT, coord_dtype)
                 ),
-                cudf.Series(
+                cudf.Series._from_column(
                     empty_geometry_column(Feature_Enum.MULTIPOINT, coord_dtype)
                 ),
-                cudf.Series(
+                cudf.Series._from_column(
                     empty_geometry_column(Feature_Enum.LINESTRING, coord_dtype)
                 ),
-                cudf.Series(polygons_col),
+                cudf.Series._from_column(polygons_col),
             ),
             meta,
         )
@@ -369,5 +375,8 @@ def _xy_as_variable_sized_list(xy: ColumnBase):
     num_points = len(xy) // 2
     indices = as_column(range(0, num_points * 2 + 1, 2), dtype="int32")
     return ListColumn(
-        dtype=cudf.ListDtype(xy.dtype), size=num_points, children=(indices, xy)
+        data=None,
+        dtype=cudf.ListDtype(xy.dtype),
+        size=num_points,
+        children=(indices, xy),
     )
diff --git a/python/cuspatial/cuspatial/core/_column/geometa.py b/python/cuspatial/cuspatial/core/_column/geometa.py
index 9f794ab16..2158b02b2 100644
--- a/python/cuspatial/cuspatial/core/_column/geometa.py
+++ b/python/cuspatial/cuspatial/core/_column/geometa.py
@@ -1,12 +1,13 @@
-# Copyright (c) 2021-2022 NVIDIA CORPORATION
+# Copyright (c) 2021-2024, NVIDIA CORPORATION
 
 # This allows GeoMeta as its own init type
 from __future__ import annotations
 
 from enum import Enum
-from typing import Union
+from typing import Literal, Union
 
 import cudf
+import cudf.core.column
 
 
 # This causes arrow to encode NONE as =255, which I'll accept now
@@ -26,12 +27,33 @@ class GeoMeta:
     GeoSeries if necessary.
     """
 
-    def __init__(self, meta: Union[GeoMeta, dict]):
+    def __init__(
+        self,
+        meta: Union[
+            GeoMeta,
+            dict[
+                Literal["input_types", "union_offsets"],
+                cudf.core.column.ColumnBase,
+            ],
+        ],
+    ):
         if isinstance(meta, dict):
-            self.input_types = cudf.Series(meta["input_types"], dtype="int8")
-            self.union_offsets = cudf.Series(
-                meta["union_offsets"], dtype="int32"
-            )
+            meta_it = meta["input_types"]
+            if isinstance(meta_it, cudf.core.column.ColumnBase):
+                self.input_types = cudf.Series._from_column(meta_it).astype(
+                    "int8"
+                )
+            else:
+                # Could be Series from GeoSeries.__getitem__
+                self.input_types = cudf.Series(meta_it, dtype="int8")
+            meta_uo = meta["union_offsets"]
+            if isinstance(meta_uo, cudf.core.column.ColumnBase):
+                self.union_offsets = cudf.Series._from_column(meta_uo).astype(
+                    "int32"
+                )
+            else:
+                # Could be Series from GeoSeries.__getitem__
+                self.union_offsets = cudf.Series(meta_uo, dtype="int32")
         else:
             self.input_types = cudf.Series(meta.input_types, dtype="int8")
             self.union_offsets = cudf.Series(meta.union_offsets, dtype="int32")
diff --git a/python/cuspatial/cuspatial/core/binops/distance_dispatch.py b/python/cuspatial/cuspatial/core/binops/distance_dispatch.py
index db2e6339b..d55822ab8 100644
--- a/python/cuspatial/cuspatial/core/binops/distance_dispatch.py
+++ b/python/cuspatial/cuspatial/core/binops/distance_dispatch.py
@@ -1,3 +1,5 @@
+# Copyright (c) 2024, NVIDIA CORPORATION
+
 import cudf
 from cudf.core.column import as_column
 
@@ -200,4 +202,4 @@ def __call__(self):
         # If `align==False`, geopandas preserves lhs index.
         index = None if self._align else self._res_index
 
-        return cudf.Series(result, index=index, nan_as_null=False)
+        return cudf.Series._from_column(result, index=index)
diff --git a/python/cuspatial/cuspatial/core/binops/equals_count.py b/python/cuspatial/cuspatial/core/binops/equals_count.py
index 83cd97b9e..b86358f48 100644
--- a/python/cuspatial/cuspatial/core/binops/equals_count.py
+++ b/python/cuspatial/cuspatial/core/binops/equals_count.py
@@ -1,4 +1,4 @@
-# Copyright (c) 2023, NVIDIA CORPORATION.
+# Copyright (c) 2023-2024, NVIDIA CORPORATION.
 
 import cudf
 
@@ -76,4 +76,4 @@ def pairwise_multipoint_equals_count(lhs, rhs):
     rhs_column = rhs._column.mpoints._column
     result = c_pairwise_multipoint_equals_count(lhs_column, rhs_column)
 
-    return cudf.Series(result)
+    return cudf.Series._from_column(result)
diff --git a/python/cuspatial/cuspatial/core/binops/intersection.py b/python/cuspatial/cuspatial/core/binops/intersection.py
index 06e23a4e8..62eac4227 100644
--- a/python/cuspatial/cuspatial/core/binops/intersection.py
+++ b/python/cuspatial/cuspatial/core/binops/intersection.py
@@ -1,4 +1,4 @@
-# Copyright (c) 2023, NVIDIA CORPORATION.
+# Copyright (c) 2023-2024, NVIDIA CORPORATION.
 
 from typing import TYPE_CHECKING
 
@@ -86,6 +86,7 @@ def pairwise_linestring_intersection(
     # Organize the look back ids into list column
     (lhs_linestring_id, lhs_segment_id, rhs_linestring_id, rhs_segment_id,) = [
         ListColumn(
+            data=None,
             dtype=cudf.ListDtype(id_.dtype),
             size=len(geometry_collection_offset) - 1,
             children=(geometry_collection_offset, id_),
@@ -94,6 +95,7 @@ def pairwise_linestring_intersection(
     ]
 
     linestring_column = ListColumn(
+        data=None,
         dtype=cudf.ListDtype(segments.dtype),
         size=segments.size,
         children=(
@@ -109,15 +111,15 @@ def pairwise_linestring_intersection(
     )
     from cuspatial.core.geoseries import GeoSeries
 
-    geometries = GeoSeries(
+    geometries = GeoSeries._from_column(
         GeoColumn(
             (
-                cudf.Series(points),
-                cudf.Series(
+                cudf.Series._from_column(points),
+                cudf.Series._from_column(
                     empty_geometry_column(Feature_Enum.MULTIPOINT, coord_dtype)
                 ),
-                cudf.Series(linestring_column),
-                cudf.Series(
+                cudf.Series._from_column(linestring_column),
+                cudf.Series._from_column(
                     empty_geometry_column(Feature_Enum.POLYGON, coord_dtype)
                 ),
             ),
diff --git a/python/cuspatial/cuspatial/core/binpreds/contains.py b/python/cuspatial/cuspatial/core/binpreds/contains.py
index c66afd4ed..f73b2f159 100644
--- a/python/cuspatial/cuspatial/core/binpreds/contains.py
+++ b/python/cuspatial/cuspatial/core/binpreds/contains.py
@@ -155,7 +155,7 @@ def _pairwise_contains_properly(points, polygons):
     # point) pair where the point is contained properly by the polygon. We can
     # use this to create a dataframe with only (polygon, point) pairs that
     # satisfy the relationship.
-    pip_result = cudf.Series(result_column, dtype="bool")
+    pip_result = cudf.Series._from_column(result_column).astype("bool")
     trues = pip_result[pip_result].index
     true_pairs = cudf.DataFrame(
         {
diff --git a/python/cuspatial/cuspatial/core/binpreds/contains_geometry_processor.py b/python/cuspatial/cuspatial/core/binpreds/contains_geometry_processor.py
index 8ff5dd659..add5f6cfd 100644
--- a/python/cuspatial/cuspatial/core/binpreds/contains_geometry_processor.py
+++ b/python/cuspatial/cuspatial/core/binpreds/contains_geometry_processor.py
@@ -62,7 +62,9 @@ def _preprocess_multipoint_rhs(self, lhs, rhs):
         point_indices = geom.point_indices()
         from cuspatial.core.geoseries import GeoSeries
 
-        final_rhs = GeoSeries(GeoColumn._from_points_xy(xy_points._column))
+        final_rhs = GeoSeries._from_column(
+            GeoColumn._from_points_xy(xy_points._column)
+        )
         preprocess_result = PreprocessorResult(
             lhs, rhs, final_rhs, point_indices
         )
diff --git a/python/cuspatial/cuspatial/core/binpreds/feature_contains.py b/python/cuspatial/cuspatial/core/binpreds/feature_contains.py
index 0a4824dd2..8bf915cd4 100644
--- a/python/cuspatial/cuspatial/core/binpreds/feature_contains.py
+++ b/python/cuspatial/cuspatial/core/binpreds/feature_contains.py
@@ -1,4 +1,4 @@
-# Copyright (c) 2023, NVIDIA CORPORATION.
+# Copyright (c) 2023-2024, NVIDIA CORPORATION.
 
 from typing import TypeVar
 
@@ -71,7 +71,7 @@ def _intersection_results_for_contains_polygon(self, lhs, rhs):
         if len(pli_features) == 0:
             return _zero_series(len(lhs))
 
-        pli_offsets = cudf.Series(pli[0])
+        pli_offsets = cudf.Series._from_column(pli[0])
 
         # Convert the pli to multipoints for equality checking
         multipoints = _points_and_lines_to_multipoints(
diff --git a/python/cuspatial/cuspatial/core/binpreds/feature_equals.py b/python/cuspatial/cuspatial/core/binpreds/feature_equals.py
index 0bf109980..37d9a9310 100644
--- a/python/cuspatial/cuspatial/core/binpreds/feature_equals.py
+++ b/python/cuspatial/cuspatial/core/binpreds/feature_equals.py
@@ -1,4 +1,4 @@
-# Copyright (c) 2023, NVIDIA CORPORATION.
+# Copyright (c) 2023-2024, NVIDIA CORPORATION.
 
 from __future__ import annotations
 
@@ -236,7 +236,7 @@ def _preprocess(self, lhs: "GeoSeries", rhs: "GeoSeries"):
             lhs, rhs, PreprocessorResult(None, rhs.point_indices)
         )
 
-    def _vertices_equals(self, lhs: Series, rhs: Series):
+    def _vertices_equals(self, lhs: Series, rhs: Series) -> Series:
         """Compute the equals relationship between interleaved xy
         coordinate buffers."""
         if not isinstance(lhs, Series):
@@ -246,7 +246,7 @@ def _vertices_equals(self, lhs: Series, rhs: Series):
         length = min(len(lhs), len(rhs))
         a = lhs[:length:2]._column == rhs[:length:2]._column
         b = rhs[1:length:2]._column == lhs[1:length:2]._column
-        return a & b
+        return Series._from_column(a & b)
 
     def _compute_predicate(self, lhs, rhs, preprocessor_result):
         """Perform the binary predicate operation on the input GeoSeries.
diff --git a/python/cuspatial/cuspatial/core/binpreds/feature_intersects.py b/python/cuspatial/cuspatial/core/binpreds/feature_intersects.py
index 25c463b7c..6d33be4b5 100644
--- a/python/cuspatial/cuspatial/core/binpreds/feature_intersects.py
+++ b/python/cuspatial/cuspatial/core/binpreds/feature_intersects.py
@@ -1,4 +1,4 @@
-# Copyright (c) 2023, NVIDIA CORPORATION.
+# Copyright (c) 2023-2024, NVIDIA CORPORATION.
 
 
 import cupy as cp
@@ -70,7 +70,7 @@ def _get_intersecting_geometry_indices(self, lhs, op_result):
         a set of lengths from the returned offsets buffer, then
         returns an integer index for all of the offset sizes that
         are larger than 0."""
-        is_offsets = cudf.Series(op_result.result[0])
+        is_offsets = cudf.Series._from_column(op_result.result[0])
         is_sizes = is_offsets[1:].reset_index(drop=True) - is_offsets[
             :-1
         ].reset_index(drop=True)
diff --git a/python/cuspatial/cuspatial/core/binpreds/feature_touches.py b/python/cuspatial/cuspatial/core/binpreds/feature_touches.py
index 3071fdf7a..ea1387951 100644
--- a/python/cuspatial/cuspatial/core/binpreds/feature_touches.py
+++ b/python/cuspatial/cuspatial/core/binpreds/feature_touches.py
@@ -1,4 +1,4 @@
-# Copyright (c) 2023, NVIDIA CORPORATION.
+# Copyright (c) 2023-2024, NVIDIA CORPORATION.
 
 import cupy as cp
 
@@ -66,7 +66,7 @@ def _preprocess(self, lhs, rhs):
         # First compute pli which will contain points for line crossings and
         # linestrings for overlapping segments.
         pli = _basic_intersects_pli(lhs, rhs)
-        offsets = cudf.Series(pli[0])
+        offsets = cudf.Series._from_column(pli[0])
         pli_geometry_count = offsets[1:].reset_index(drop=True) - offsets[
             :-1
         ].reset_index(drop=True)
diff --git a/python/cuspatial/cuspatial/core/geodataframe.py b/python/cuspatial/cuspatial/core/geodataframe.py
index 90dcaf24e..7d34ae4ed 100644
--- a/python/cuspatial/cuspatial/core/geodataframe.py
+++ b/python/cuspatial/cuspatial/core/geodataframe.py
@@ -134,7 +134,6 @@ def _copy_type_metadata(
                         other_col.polygons,
                     ),
                     other_col._meta,
-                    cudf.Index(col),
                 )
                 type_copied._data.set_by_label(name, col, validate=False)
 
@@ -161,18 +160,29 @@ def _split_out_geometry_columns(
 
     def _recombine_columns(
         self, geo_columns: GeoDataFrame, data_columns: cudf.DataFrame
-    ) -> dict[Any, GeoSeries | cudf.Series]:
+    ) -> dict[Any, GeoColumn | cudf.core.column.ColumnBase]:
         """
         Combine a GeoDataFrame of only geometry columns with a DataFrame
         of non-geometry columns in the same order as the columns in `self`
+
+        The output is meant for GeoDataFrame._from_data.
         """
+        if not (
+            geo_columns.empty
+            or data_columns.empty
+            or geo_columns.index.equals(data_columns.index)
+        ):
+            raise ValueError("geo_columns.index must equal data_columns.index")
+
         columns_mask = self.columns
-        geocolumn_mask = (
-            isinstance(self[col], GeoSeries) for col in columns_mask
-        )
+        col_is_geo = (isinstance(self[col], GeoSeries) for col in columns_mask)
         return {
-            name: (geo_columns[name] if mask else data_columns[name])
-            for name, mask in zip(columns_mask, geocolumn_mask)
+            name: (
+                geo_columns[name]._column
+                if is_geo
+                else data_columns[name]._column
+            )
+            for name, is_geo in zip(columns_mask, col_is_geo)
         }
 
     def _slice(self: T, arg: slice) -> T:
@@ -184,20 +194,23 @@ def _slice(self: T, arg: slice) -> T:
             {name: geo_columns[name].iloc[arg] for name in geo_columns.columns}
         )
         sliced_data_columns = data_columns._slice(arg)
-        result = self._recombine_columns(
-            sliced_geo_columns, sliced_data_columns
+        return self._from_data(
+            self._recombine_columns(sliced_geo_columns, sliced_data_columns),
+            index=sliced_data_columns.index,
         )
-        return self.__class__(result)
 
     def _apply_boolean_mask(self, mask: BooleanMask, keep_index=True) -> T:
         geo_columns, data_columns = self._split_out_geometry_columns()
         data = data_columns._apply_boolean_mask(mask, keep_index)
 
         geo = GeoDataFrame(
-            {name: geo_columns[name][mask.column] for name in geo_columns}
+            {
+                name: geo_columns[name][cudf.Index._from_column(mask.column)]
+                for name in geo_columns
+            }
         )
 
-        res = self.__class__(self._recombine_columns(geo, data))
+        res = self._from_data(self._recombine_columns(geo, data))
         if keep_index:
             res.index = data.index
         return res
@@ -215,14 +228,14 @@ def _gather(self, gather_map: GatherMap, keep_index=True):
             for geo in geo_data.keys()
         }
         geo_gathered = GeoDataFrame(gathered)
+        # _gather may have discarded the index, so re-add
+        cudf_gathered.index = geo_gathered.index
 
         # combine
-        result = GeoDataFrame(
-            self._recombine_columns(geo_gathered, cudf_gathered)
+        return GeoDataFrame._from_data(
+            self._recombine_columns(geo_gathered, cudf_gathered),
+            index=geo_gathered.index,
         )
-        result.index = geo_gathered.index
-        # return
-        return result
 
     def reset_index(
         self, level=None, drop=False, inplace=False, col_level=0, col_fill=""
@@ -270,7 +283,7 @@ def reset_index(
         if not drop:
             if not isinstance(cudf_data.index, cudf.MultiIndex):
                 recombiner.insert(
-                    loc=0, name="index", value=cudf_reindexed["index"]
+                    loc=0, column="index", value=cudf_reindexed["index"]
                 )
             # If the index is a MultiIndex, we need to insert the
             # individual levels into the GeoDataFrame.
@@ -288,7 +301,7 @@ def reset_index(
                 for n, name in enumerate(levels):
                     recombiner.insert(
                         loc=n,
-                        name=name,
+                        column=name,
                         value=cudf_reindexed[name].reset_index(drop=True),
                     )
                 recombiner.index = cudf_reindexed.index
@@ -301,11 +314,10 @@ def reset_index(
             # Reset the index of the GeoDataFrame to match the
             # cudf DataFrame and recombine.
             geo_data.index = cudf_reindexed.index
-            result = GeoDataFrame(
-                recombiner._recombine_columns(geo_data, cudf_reindexed)
+            return GeoDataFrame._from_data(
+                recombiner._recombine_columns(geo_data, cudf_reindexed),
+                index=cudf_reindexed.index,
             )
-            result.index = geo_data.index
-            return result
 
 
 class _GeoSeriesUtility:
@@ -313,9 +325,9 @@ class _GeoSeriesUtility:
     def _from_data(cls, new_data, name=None, index=None):
         new_column = new_data.columns[0]
         if is_geometry_type(new_column):
-            return GeoSeries(new_column, name=name, index=index)
+            return GeoSeries._from_column(new_column, name=name, index=index)
         else:
-            return cudf.Series(new_column, name=name, index=index)
+            return cudf.Series._from_column(new_column, name=name, index=index)
 
 
 def is_geometry_type(obj):
diff --git a/python/cuspatial/cuspatial/core/geoseries.py b/python/cuspatial/cuspatial/core/geoseries.py
index 10addfec0..b8d7c4945 100644
--- a/python/cuspatial/cuspatial/core/geoseries.py
+++ b/python/cuspatial/cuspatial/core/geoseries.py
@@ -77,7 +77,7 @@ def __init__(
         data: Optional[
             Union[gpd.GeoSeries, Tuple, T, pd.Series, GeoColumn, list]
         ],
-        index: Union[cudf.Index, pd.Index] = None,
+        index: Union[cudf.Index, pd.Index, None] = None,
         dtype=None,
         name=None,
         nan_as_null=True,
@@ -86,29 +86,29 @@ def __init__(
         if data is None or isinstance(data, (pd.Series, list)):
             data = gpGeoSeries(data)
         # Create column
-        if isinstance(data, GeoColumn):
-            column = data
-        elif isinstance(data, GeoSeries):
-            column = data._column
+        if isinstance(data, GeoSeries):
+            gser = type(self)._from_column(
+                data._column, index=data.index, name=data.name
+            )
         elif isinstance(data, gpGeoSeries):
             from cuspatial.io.geopandas_reader import GeoPandasReader
 
             adapter = GeoPandasReader(data)
             pandas_meta = GeoMeta(adapter.get_geopandas_meta())
-            column = GeoColumn(adapter._get_geotuple(), pandas_meta)
+            geocolumn = GeoColumn(adapter._get_geotuple(), pandas_meta)
+            gser = type(self)._from_column(
+                geocolumn, index=cudf.Index(data.index), name=data.name
+            )
         else:
             raise TypeError(
                 f"Incompatible object passed to GeoSeries ctor {type(data)}"
             )
         # Condition index
-        if isinstance(data, (gpGeoSeries, GeoSeries)):
-            if index is None:
-                index = data.index
-        if index is None:
-            index = cudf.RangeIndex(0, len(column))
-        super().__init__(
-            column, index, dtype=dtype, name=name, nan_as_null=nan_as_null
-        )
+        if index is not None:
+            gser.index = cudf.Index(index)
+        if name is not None:
+            gser.name = name
+        super().__init__(gser, dtype=dtype, nan_as_null=nan_as_null)
 
     @property
     def feature_types(self):
@@ -435,9 +435,11 @@ def __getitem__(self, indexes):
             )
 
             if isinstance(indexes, Integral):
-                return GeoSeries(column, name=self._sr.name).to_shapely()
+                return GeoSeries._from_column(
+                    column, name=self._sr.name
+                ).to_shapely()
             else:
-                return GeoSeries(
+                return GeoSeries._from_column(
                     column, index=self._sr.index[indexes], name=self._sr.name
                 )
 
@@ -454,7 +456,7 @@ def from_arrow(union):
                 "union_offsets": union.offsets,
             },
         )
-        return GeoSeries(column)
+        return GeoSeries._from_column(column)
 
     @property
     def loc(self):
@@ -636,7 +638,7 @@ def to_arrow(self):
 
         return pa.UnionArray.from_dense(
             self._column._meta.input_types.to_arrow(),
-            self._column._meta.union_offsets.to_arrow(),
+            self._column._meta.union_offsets.astype("int32").to_arrow(),
             [
                 arrow_points,
                 arrow_mpoints,
@@ -681,7 +683,7 @@ def _align_to_index(
                 "union_offsets": aligned_union_offsets,
             },
         )
-        return GeoSeries(column)
+        return GeoSeries._from_column(column)
 
     @classmethod
     def from_points_xy(cls, points_xy):
@@ -700,7 +702,7 @@ def from_points_xy(cls, points_xy):
         """
         coords_dtype = _check_coords_dtype(points_xy)
 
-        return cls(
+        return cls._from_column(
             GeoColumn._from_points_xy(as_column(points_xy, dtype=coords_dtype))
         )
 
@@ -735,7 +737,7 @@ def from_multipoints_xy(cls, multipoints_xy, geometry_offset):
         dtype: geometry
         """
         coords_dtype = coords_dtype = _check_coords_dtype(multipoints_xy)
-        return cls(
+        return cls._from_column(
             GeoColumn._from_multipoints_xy(
                 as_column(multipoints_xy, dtype=coords_dtype),
                 as_column(geometry_offset, dtype="int32"),
@@ -781,7 +783,7 @@ def from_linestrings_xy(
         dtype: geometry
         """
         coords_dtype = _check_coords_dtype(linestrings_xy)
-        return cls(
+        return cls._from_column(
             GeoColumn._from_linestrings_xy(
                 as_column(linestrings_xy, dtype=coords_dtype),
                 as_column(part_offset, dtype="int32"),
@@ -831,7 +833,7 @@ def from_polygons_xy(
         dtype: geometry
         """
         coords_dtype = _check_coords_dtype(polygons_xy)
-        return cls(
+        return cls._from_column(
             GeoColumn._from_polygons_xy(
                 as_column(polygons_xy, dtype=coords_dtype),
                 as_column(ring_offset, dtype="int32"),
diff --git a/python/cuspatial/cuspatial/core/spatial/distance.py b/python/cuspatial/cuspatial/core/spatial/distance.py
index f9a0b1d6b..0a95baeba 100644
--- a/python/cuspatial/cuspatial/core/spatial/distance.py
+++ b/python/cuspatial/cuspatial/core/spatial/distance.py
@@ -1,4 +1,4 @@
-# Copyright (c) 2022-2023, NVIDIA CORPORATION.
+# Copyright (c) 2022-2024, NVIDIA CORPORATION.
 
 import cudf
 from cudf import DataFrame, Series
@@ -217,15 +217,13 @@ def pairwise_point_distance(points1: GeoSeries, points2: GeoSeries):
         rhs_point_collection_type,
     ) = _extract_point_column_and_collection_type(points2)
 
-    return Series._from_data(
-        {
-            None: cpp_pairwise_point_distance(
-                lhs_point_collection_type,
-                rhs_point_collection_type,
-                lhs_column,
-                rhs_column,
-            )
-        }
+    return Series._from_column(
+        cpp_pairwise_point_distance(
+            lhs_point_collection_type,
+            rhs_point_collection_type,
+            lhs_column,
+            rhs_column,
+        )
     )
 
 
@@ -293,13 +291,11 @@ def pairwise_linestring_distance(
     if len(multilinestrings1) == 0:
         return cudf.Series(dtype="float64")
 
-    return Series._from_data(
-        {
-            None: cpp_pairwise_linestring_distance(
-                multilinestrings1.lines.column(),
-                multilinestrings2.lines.column(),
-            )
-        }
+    return Series._from_column(
+        cpp_pairwise_linestring_distance(
+            multilinestrings1.lines.column(),
+            multilinestrings2.lines.column(),
+        )
     )
 
 
@@ -583,8 +579,8 @@ def pairwise_linestring_polygon_distance(
     linestrings_column = linestrings.lines.column()
     polygon_column = polygons.polygons.column()
 
-    return Series._from_data(
-        {None: c_pairwise_line_poly_dist(linestrings_column, polygon_column)}
+    return Series._from_column(
+        c_pairwise_line_poly_dist(linestrings_column, polygon_column)
     )
 
 
diff --git a/python/cuspatial/cuspatial/core/spatial/indexing.py b/python/cuspatial/cuspatial/core/spatial/indexing.py
index 52d63e274..a7116d435 100644
--- a/python/cuspatial/cuspatial/core/spatial/indexing.py
+++ b/python/cuspatial/cuspatial/core/spatial/indexing.py
@@ -1,4 +1,4 @@
-# Copyright (c) 2022-2023, NVIDIA CORPORATION.
+# Copyright (c) 2022-2024, NVIDIA CORPORATION.
 
 import warnings
 
@@ -187,4 +187,4 @@ def quadtree_on_points(
         max_depth,
         max_size,
     )
-    return Series(key_to_point), DataFrame._from_data(*quadtree)
+    return Series._from_column(key_to_point), DataFrame._from_data(*quadtree)
diff --git a/python/cuspatial/cuspatial/core/trajectory.py b/python/cuspatial/cuspatial/core/trajectory.py
index be779313f..27fdb227c 100644
--- a/python/cuspatial/cuspatial/core/trajectory.py
+++ b/python/cuspatial/cuspatial/core/trajectory.py
@@ -1,4 +1,4 @@
-# Copyright (c) 2019-2022, NVIDIA CORPORATION.
+# Copyright (c) 2019-2024, NVIDIA CORPORATION.
 
 import numpy as np
 
@@ -71,7 +71,7 @@ def derive_trajectories(object_ids, points: GeoSeries, timestamps):
     objects, traj_offsets = cpp_derive_trajectories(
         object_ids, xs, ys, timestamps
     )
-    return DataFrame._from_data(*objects), Series(data=traj_offsets)
+    return DataFrame._from_data(*objects), Series._from_column(traj_offsets)
 
 
 def trajectory_bounding_boxes(num_trajectories, object_ids, points: GeoSeries):
diff --git a/python/cuspatial/cuspatial/io/geopandas_reader.py b/python/cuspatial/cuspatial/io/geopandas_reader.py
index cca86448d..a4fc8091e 100644
--- a/python/cuspatial/cuspatial/io/geopandas_reader.py
+++ b/python/cuspatial/cuspatial/io/geopandas_reader.py
@@ -1,4 +1,8 @@
-# Copyright (c) 2020-2022 NVIDIA CORPORATION.
+# Copyright (c) 2020-2024, NVIDIA CORPORATION.
+
+from __future__ import annotations
+
+from typing import Literal
 
 from geopandas import GeoSeries as gpGeoSeries
 from shapely.geometry import (
@@ -12,6 +16,7 @@
 )
 
 import cudf
+import cudf.core.column
 
 from cuspatial.core._column.geometa import Feature_Enum
 from cuspatial.io import pygeoarrow
@@ -119,7 +124,11 @@ def _get_geotuple(self) -> cudf.Series:
             polygons,
         )
 
-    def get_geopandas_meta(self) -> dict:
+    def get_geopandas_meta(
+        self,
+    ) -> dict[
+        Literal["input_types", "union_offsets"], cudf.core.column.ColumnBase
+    ]:
         """
         Returns the metadata that was created converting the GeoSeries into
         GeoArrow format. The metadata essentially contains the object order
@@ -129,6 +138,6 @@ def get_geopandas_meta(self) -> dict:
         """
         buffers = self.buffers
         return {
-            "input_types": buffers.type_codes,
-            "union_offsets": buffers.offsets,
+            "input_types": cudf.core.column.as_column(buffers.type_codes),
+            "union_offsets": cudf.core.column.as_column(buffers.offsets),
         }
diff --git a/python/cuspatial/cuspatial/utils/binpred_utils.py b/python/cuspatial/cuspatial/utils/binpred_utils.py
index c8e2804eb..6a299840f 100644
--- a/python/cuspatial/cuspatial/utils/binpred_utils.py
+++ b/python/cuspatial/cuspatial/utils/binpred_utils.py
@@ -4,6 +4,7 @@
 import numpy as np
 
 import cudf
+import cudf.core.column
 
 import cuspatial
 from cuspatial.core._column.geocolumn import ColumnType
@@ -425,7 +426,13 @@ def _pli_features_rebuild_offsets(pli, features):
     in_sizes = (
         features.sizes if len(features) > 0 else _zero_series(len(pli[0]) - 1)
     )
-    offsets = cudf.Series(pli[0])
+    if isinstance(pli[0], cudf.core.column.ColumnBase):
+        offsets = cudf.Series._from_column(pli[0])
+    else:
+        # TODO: Can be removed if pairwise_linestring_intersection
+        # always returns a cudf.Series
+        # in the first element
+        offsets = cudf.Series(pli[0])
     offset_sizes = offsets[1:].reset_index(drop=True) - offsets[
         :-1
     ].reset_index(drop=True)
diff --git a/python/cuspatial/pyproject.toml b/python/cuspatial/pyproject.toml
index d8461c722..94b401472 100644
--- a/python/cuspatial/pyproject.toml
+++ b/python/cuspatial/pyproject.toml
@@ -16,7 +16,7 @@
 build-backend = "rapids_build_backend.build"
 requires = [
     "rapids-build-backend>=0.3.0,<0.4.0.dev0",
-    "scikit-build-core[pyproject]>=0.7.0",
+    "scikit-build-core[pyproject]>=0.10.0",
 ] # This list was generated by `rapids-dependency-file-generator`. To make changes, edit ../../dependencies.yaml and run `rapids-dependency-file-generator`.
 
 [project]
@@ -28,12 +28,14 @@ authors = [
     { name = "NVIDIA Corporation" },
 ]
 license = { text = "Apache 2.0" }
-requires-python = ">=3.9"
+requires-python = ">=3.10"
 dependencies = [
-    "cudf==24.8.*,>=0.0.0a0",
-    "geopandas>=0.11.0",
-    "numpy>=1.23,<2.0a0",
-    "rmm==24.8.*,>=0.0.0a0",
+    "cudf==24.10.*,>=0.0.0a0",
+    "geopandas>=1.0.0",
+    "libcudf==24.10.*,>=0.0.0a0",
+    "libcuspatial==24.10.*,>=0.0.0a0",
+    "numpy>=1.23,<3.0a0",
+    "rmm==24.10.*,>=0.0.0a0",
 ] # This list was generated by `rapids-dependency-file-generator`. To make changes, edit ../../dependencies.yaml and run `rapids-dependency-file-generator`.
 classifiers = [
     "Intended Audience :: Developers",
@@ -41,9 +43,9 @@ classifiers = [
     "Topic :: Scientific/Engineering",
     "License :: OSI Approved :: Apache Software License",
     "Programming Language :: Python",
-    "Programming Language :: Python :: 3.9",
     "Programming Language :: Python :: 3.10",
     "Programming Language :: Python :: 3.11",
+    "Programming Language :: Python :: 3.12",
 ]
 
 [project.optional-dependencies]
@@ -72,6 +74,8 @@ known_dask = [
 known_rapids = [
     "rmm",
     "cudf",
+    "libcudf",
+    "pylibcudf",
 ]
 known_first_party = [
     "cuspatial",
@@ -104,7 +108,8 @@ skip = [
 [tool.scikit-build]
 build-dir = "build/{wheel_tag}"
 cmake.build-type = "Release"
-cmake.minimum-version = "3.26.4"
+cmake.version = "CMakeLists.txt"
+minimum-version = "build-system.requires"
 ninja.make-fallback = true
 sdist.exclude = ["*tests*"]
 sdist.reproducible = true
@@ -131,9 +136,12 @@ dependencies-file = "../../dependencies.yaml"
 matrix-entry = "cuda_suffixed=true"
 requires = [
     "cmake>=3.26.4,!=3.30.0",
-    "cudf==24.8.*,>=0.0.0a0",
+    "cudf==24.10.*,>=0.0.0a0",
     "cython>=3.0.0",
+    "libcudf==24.10.*,>=0.0.0a0",
+    "libcuspatial==24.10.*,>=0.0.0a0",
+    "librmm==24.10.*,>=0.0.0a0",
     "ninja",
-    "rmm==24.8.*,>=0.0.0a0",
+    "rmm==24.10.*,>=0.0.0a0",
     "wheel",
 ] # This list was generated by `rapids-dependency-file-generator`. To make changes, edit ../../dependencies.yaml and run `rapids-dependency-file-generator`.
diff --git a/python/libcuspatial/CMakeLists.txt b/python/libcuspatial/CMakeLists.txt
new file mode 100644
index 000000000..86c3cd1ef
--- /dev/null
+++ b/python/libcuspatial/CMakeLists.txt
@@ -0,0 +1,42 @@
+# =============================================================================
+# Copyright (c) 2024, NVIDIA CORPORATION.
+#
+# 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.
+# =============================================================================
+
+cmake_minimum_required(VERSION 3.26.4 FATAL_ERROR)
+
+include(../../rapids_config.cmake)
+
+project(
+  libcuspatial-python
+  VERSION "${RAPIDS_VERSION}"
+  LANGUAGES CXX
+)
+
+# Check if cuspatial is already available. If so, it is the user's responsibility to ensure that the
+# CMake package is also available at build time of the Python cuspatial package.
+find_package(cuspatial "${RAPIDS_VERSION}")
+if(cuspatial_FOUND)
+  return()
+endif()
+
+unset(cuspatial_FOUND)
+
+# set options for building libcuspatial
+set(BUILD_SHARED_LIBS ON)
+set(BUILD_TESTS OFF)
+set(BUILD_BENCHMARKS OFF)
+set(CUDA_STATIC_RUNTIME ON)
+set(CUSPATIAL_USE_CUDF_STATIC OFF)
+set(CUSPATIAL_EXCLUDE_CUDF_FROM_ALL ON)
+
+add_subdirectory(../../cpp cuspatial-cpp)
diff --git a/python/libcuspatial/LICENSE b/python/libcuspatial/LICENSE
new file mode 120000
index 000000000..30cff7403
--- /dev/null
+++ b/python/libcuspatial/LICENSE
@@ -0,0 +1,1 @@
+../../LICENSE
\ No newline at end of file
diff --git a/python/libcuspatial/README.md b/python/libcuspatial/README.md
new file mode 120000
index 000000000..fe8400541
--- /dev/null
+++ b/python/libcuspatial/README.md
@@ -0,0 +1,1 @@
+../../README.md
\ No newline at end of file
diff --git a/python/libcuspatial/libcuspatial/VERSION b/python/libcuspatial/libcuspatial/VERSION
new file mode 120000
index 000000000..d62dc733e
--- /dev/null
+++ b/python/libcuspatial/libcuspatial/VERSION
@@ -0,0 +1,1 @@
+../../../VERSION
\ No newline at end of file
diff --git a/python/libcuspatial/libcuspatial/__init__.py b/python/libcuspatial/libcuspatial/__init__.py
new file mode 100644
index 000000000..49b02bb14
--- /dev/null
+++ b/python/libcuspatial/libcuspatial/__init__.py
@@ -0,0 +1,16 @@
+# Copyright (c) 2024, NVIDIA CORPORATION.
+#
+# 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.
+
+from libcuspatial._version import __git_commit__, __version__
+from libcuspatial.load import load_library
diff --git a/python/libcuspatial/libcuspatial/_version.py b/python/libcuspatial/libcuspatial/_version.py
new file mode 100644
index 000000000..826e84a67
--- /dev/null
+++ b/python/libcuspatial/libcuspatial/_version.py
@@ -0,0 +1,32 @@
+# Copyright (c) 2023-2024, NVIDIA CORPORATION.
+#
+# 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.
+import importlib.resources
+
+__version__ = (
+    importlib.resources.files(__package__)
+    .joinpath("VERSION")
+    .read_text()
+    .strip()
+)
+try:
+    __git_commit__ = (
+        importlib.resources.files(__package__)
+        .joinpath("GIT_COMMIT")
+        .read_text()
+        .strip()
+    )
+except FileNotFoundError:
+    __git_commit__ = ""
+
+__all__ = ["__git_commit__", "__version__"]
diff --git a/python/libcuspatial/libcuspatial/load.py b/python/libcuspatial/libcuspatial/load.py
new file mode 100644
index 000000000..7842de9a0
--- /dev/null
+++ b/python/libcuspatial/libcuspatial/load.py
@@ -0,0 +1,64 @@
+# Copyright (c) 2024, NVIDIA CORPORATION.
+#
+# 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.
+#
+
+import ctypes
+import os
+
+
+def load_library():
+    try:
+        # libcudf must be loaded before libcuspatial because libcuspatial
+        # references its symbols
+        import libcudf
+        libcudf.load_library()
+    except ModuleNotFoundError:
+        # 'libcuspatial' has a runtime dependency on 'libcudf'. However,
+        # that dependency might be satisfied by the 'libcudf' conda package
+        # (which does not have any Python modules), instead of the
+        # 'libcudf' wheel.
+        #
+        # In that situation, assume that 'libcudf.so' is in a place where
+        # the loader can find it.
+        pass
+
+    # Dynamically load libcuspatial.so. Prefer a system library if one is
+    # present to avoid clobbering symbols that other packages might expect,
+    # but if no other library is present use the one in the wheel.
+    libcuspatial_lib = None
+    try:
+        libcuspatial_lib = ctypes.CDLL("libcuspatial.so", ctypes.RTLD_GLOBAL)
+    except OSError:
+        # If neither of these directories contain the library, we assume we are
+        # in an environment where the C++ library is already installed
+        # somewhere else and the CMake build of the libcuspatial Python package
+        # was a no-op.
+        #
+        # Note that this approach won't work for real editable installs of the
+        # libcuspatial package. scikit-build-core has limited support for
+        # importlib.resources so there isn't a clean way to support that case
+        # yet.
+        for lib_dir in ("lib", "lib64"):
+            if os.path.isfile(
+                lib := os.path.join(
+                    os.path.dirname(__file__), lib_dir, "libcuspatial.so"
+                )
+            ):
+                libcuspatial_lib = ctypes.CDLL(lib, ctypes.RTLD_GLOBAL)
+                break
+
+    # The caller almost never needs to do anything with this library, but no
+    # harm in offering the option since this object at least provides a handle
+    # to inspect where the library was loaded from.
+    return libcuspatial_lib
diff --git a/python/libcuspatial/pyproject.toml b/python/libcuspatial/pyproject.toml
new file mode 100644
index 000000000..ab64d4cb6
--- /dev/null
+++ b/python/libcuspatial/pyproject.toml
@@ -0,0 +1,77 @@
+# Copyright (c) 2024, NVIDIA CORPORATION.
+#
+# 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.
+
+[build-system]
+build-backend = "rapids_build_backend.build"
+requires = [
+    "rapids-build-backend>=0.3.0,<0.4.0.dev0",
+    "scikit-build-core[pyproject]>=0.10.0",
+] # This list was generated by `rapids-dependency-file-generator`. To make changes, edit ../../dependencies.yaml and run `rapids-dependency-file-generator`.
+
+[project]
+name = "libcuspatial"
+dynamic = ["version"]
+description = "cuSpatial: GPU-Accelerated Spatial and Trajectory Data Management and Analytics Library (C++)"
+readme = { file = "README.md", content-type = "text/markdown" }
+authors = [
+    { name = "NVIDIA Corporation" },
+]
+license = { text = "Apache 2.0" }
+requires-python = ">=3.10"
+classifiers = [
+    "Intended Audience :: Developers",
+    "Topic :: Database",
+    "Topic :: Scientific/Engineering",
+    "License :: OSI Approved :: Apache Software License",
+    "Programming Language :: Python",
+    "Programming Language :: Python :: 3.10",
+    "Programming Language :: Python :: 3.11",
+]
+dependencies = [
+    "libcudf==24.10.*,>=0.0.0a0",
+] # This list was generated by `rapids-dependency-file-generator`. To make changes, edit ../../dependencies.yaml and run `rapids-dependency-file-generator`.
+
+[project.urls]
+Homepage = "https://github.com/rapidsai/cuspatial"
+
+[project.entry-points."cmake.prefix"]
+libcuspatial = "libcuspatial"
+
+[tool.scikit-build]
+build-dir = "build/{wheel_tag}"
+cmake.build-type = "Release"
+cmake.version = "CMakeLists.txt"
+minimum-version = "build-system.requires"
+ninja.make-fallback = true
+sdist.reproducible = true
+wheel.packages = ["libcuspatial"]
+wheel.install-dir = "libcuspatial"
+wheel.py-api = "py3"
+
+[tool.scikit-build.metadata.version]
+provider = "scikit_build_core.metadata.regex"
+input = "libcuspatial/VERSION"
+regex = "(?P<value>.*)"
+
+[tool.rapids-build-backend]
+build-backend = "scikit_build_core.build"
+dependencies-file = "../../dependencies.yaml"
+matrix-entry = "cuda_suffixed=true"
+requires = [
+    "cmake>=3.26.4,!=3.30.0",
+    "libcudf==24.10.*,>=0.0.0a0",
+    "librmm==24.10.*,>=0.0.0a0",
+    "ninja",
+    "wheel",
+] # This list was generated by `rapids-dependency-file-generator`. To make changes, edit ../../dependencies.yaml and run `rapids-dependency-file-generator`.


Test patch:
diff --git a/.github/workflows/test.yaml b/.github/workflows/test.yaml
index f00545fd9..493eca446 100644
--- a/.github/workflows/test.yaml
+++ b/.github/workflows/test.yaml
@@ -14,9 +14,18 @@ on:
         type: string
 
 jobs:
+  conda-cpp-checks:
+    secrets: inherit
+    uses: rapidsai/shared-workflows/.github/workflows/conda-cpp-post-build-checks.yaml@branch-24.10
+    with:
+      build_type: nightly
+      branch: ${{ inputs.branch }}
+      date: ${{ inputs.date }}
+      sha: ${{ inputs.sha }}
+      enable_check_symbols: true
   conda-cpp-tests:
     secrets: inherit
-    uses: rapidsai/shared-workflows/.github/workflows/conda-cpp-tests.yaml@branch-24.08
+    uses: rapidsai/shared-workflows/.github/workflows/conda-cpp-tests.yaml@branch-24.10
     with:
       build_type: nightly
       branch: ${{ inputs.branch }}
@@ -24,7 +33,7 @@ jobs:
       sha: ${{ inputs.sha }}
   conda-python-tests:
     secrets: inherit
-    uses: rapidsai/shared-workflows/.github/workflows/conda-python-tests.yaml@branch-24.08
+    uses: rapidsai/shared-workflows/.github/workflows/conda-python-tests.yaml@branch-24.10
     with:
       build_type: nightly
       branch: ${{ inputs.branch }}
@@ -32,7 +41,7 @@ jobs:
       sha: ${{ inputs.sha }}
   wheel-tests-cuspatial:
     secrets: inherit
-    uses: rapidsai/shared-workflows/.github/workflows/wheels-test.yaml@branch-24.08
+    uses: rapidsai/shared-workflows/.github/workflows/wheels-test.yaml@branch-24.10
     with:
       build_type: nightly
       branch: ${{ inputs.branch }}
@@ -41,7 +50,7 @@ jobs:
       script: ci/test_wheel_cuspatial.sh
   wheel-tests-cuproj:
     secrets: inherit
-    uses: rapidsai/shared-workflows/.github/workflows/wheels-test.yaml@branch-24.08
+    uses: rapidsai/shared-workflows/.github/workflows/wheels-test.yaml@branch-24.10
     with:
       build_type: nightly
       branch: ${{ inputs.branch }}
diff --git a/ci/test_notebooks.sh b/ci/test_notebooks.sh
index 44353684a..c6cba4a15 100755
--- a/ci/test_notebooks.sh
+++ b/ci/test_notebooks.sh
@@ -1,7 +1,7 @@
 #!/bin/bash
 # Copyright (c) 2020-2024, NVIDIA CORPORATION.
 
-set -euo pipefail
+set -eEuo pipefail
 
 . /opt/conda/etc/profile.d/conda.sh
 
diff --git a/ci/test_wheel_cuproj.sh b/ci/test_wheel_cuproj.sh
index 9451190e0..abd20ebf1 100755
--- a/ci/test_wheel_cuproj.sh
+++ b/ci/test_wheel_cuproj.sh
@@ -5,19 +5,17 @@ set -eou pipefail
 
 mkdir -p ./dist
 RAPIDS_PY_CUDA_SUFFIX="$(rapids-wheel-ctk-name-gen ${RAPIDS_CUDA_VERSION})"
-RAPIDS_PY_WHEEL_NAME="cuproj_${RAPIDS_PY_CUDA_SUFFIX}" rapids-download-wheels-from-s3 ./dist
 
-# Install additional dependencies
-apt update
-DEBIAN_FRONTEND=noninteractive apt install -y --no-install-recommends libgdal-dev
-python -m pip install --no-binary fiona 'fiona>=1.8.19,<1.9'
-
-# Download the cuspatial built in the previous step
-RAPIDS_PY_WHEEL_NAME="cuspatial_${RAPIDS_PY_CUDA_SUFFIX}" rapids-download-wheels-from-s3 ./local-cuspatial-dep
-python -m pip install --no-deps ./local-cuspatial-dep/cuspatial*.whl
+# Download the cuproj and cuspatial built in the previous step
+RAPIDS_PY_WHEEL_NAME="cuproj_${RAPIDS_PY_CUDA_SUFFIX}" rapids-download-wheels-from-s3 python ./dist
+RAPIDS_PY_WHEEL_NAME="cuspatial_${RAPIDS_PY_CUDA_SUFFIX}" rapids-download-wheels-from-s3 python ./dist
+RAPIDS_PY_WHEEL_NAME="libcuspatial_${RAPIDS_PY_CUDA_SUFFIX}" rapids-download-wheels-from-s3 cpp ./dist
 
 # echo to expand wildcard before adding `[extra]` requires for pip
-python -m pip install $(echo ./dist/cuproj*.whl)[test]
+python -m pip install \
+  "$(echo ./dist/cuspatial*.whl)" \
+  "$(echo ./dist/cuproj*.whl)[test]" \
+  "$(echo ./dist/libcuspatial*.whl)"
 
 rapids-logger "pytest cuproj"
 pushd python/cuproj/cuproj
diff --git a/ci/test_wheel_cuspatial.sh b/ci/test_wheel_cuspatial.sh
index aa36681bc..a352e1bfd 100755
--- a/ci/test_wheel_cuspatial.sh
+++ b/ci/test_wheel_cuspatial.sh
@@ -5,15 +5,15 @@ set -eou pipefail
 
 mkdir -p ./dist
 RAPIDS_PY_CUDA_SUFFIX="$(rapids-wheel-ctk-name-gen ${RAPIDS_CUDA_VERSION})"
-RAPIDS_PY_WHEEL_NAME="cuspatial_${RAPIDS_PY_CUDA_SUFFIX}" rapids-download-wheels-from-s3 ./dist
 
-# Install additional dependencies
-apt update
-DEBIAN_FRONTEND=noninteractive apt install -y --no-install-recommends libgdal-dev
-python -m pip install --no-binary fiona 'fiona>=1.8.19,<1.9'
+# Download the cuspatial and libcuspatial built in the previous step
+RAPIDS_PY_WHEEL_NAME="cuspatial_${RAPIDS_PY_CUDA_SUFFIX}" rapids-download-wheels-from-s3 python ./dist
+RAPIDS_PY_WHEEL_NAME="libcuspatial_${RAPIDS_PY_CUDA_SUFFIX}" rapids-download-wheels-from-s3 cpp ./dist
 
 # echo to expand wildcard before adding `[extra]` requires for pip
-python -m pip install $(echo ./dist/cuspatial*.whl)[test]
+python -m pip install \
+  "$(echo ./dist/cuspatial*.whl)[test]" \
+  "$(echo ./dist/libcuspatial*.whl)"
 
 rapids-logger "pytest cuspatial"
 pushd python/cuspatial/cuspatial
diff --git a/cpp/include/cuspatial_test/geometry_generator.cuh b/cpp/include/cuspatial_test/geometry_generator.cuh
index 8c17ff291..fbf6f02d6 100644
--- a/cpp/include/cuspatial_test/geometry_generator.cuh
+++ b/cpp/include/cuspatial_test/geometry_generator.cuh
@@ -194,8 +194,8 @@ ring_centroid_displacement(vec_2d<T> centroid, std::size_t ring_local_idx, T rad
  * @param params Parameters to generate the mulitpolygons
  */
 template <typename T, typename MultipolygonRange>
-void __global__ generate_multipolygon_array_coordinates(MultipolygonRange multipolygons,
-                                                        multipolygon_generator_parameter<T> params)
+CUSPATIAL_KERNEL void generate_multipolygon_array_coordinates(
+  MultipolygonRange multipolygons, multipolygon_generator_parameter<T> params)
 {
   for (auto idx : ranger::grid_stride_range(multipolygons.num_points())) {
     auto ring_idx     = multipolygons.ring_idx_from_point_idx(idx);
diff --git a/cpp/tests/operators/linestrings_test.cu b/cpp/tests/operators/linestrings_test.cu
index 7216f276a..2fa44cb43 100644
--- a/cpp/tests/operators/linestrings_test.cu
+++ b/cpp/tests/operators/linestrings_test.cu
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2022-2023, NVIDIA CORPORATION.
+ * Copyright (c) 2022-2024, NVIDIA CORPORATION.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -69,10 +69,10 @@ segment<T> __device__ order_end_points(segment<T> const& seg)
 }
 
 template <typename T, typename Point, typename Segment>
-__global__ void compute_intersection(segment<T> ab,
-                                     segment<T> cd,
-                                     Point point_out,
-                                     Segment segment_out)
+CUSPATIAL_KERNEL void compute_intersection(segment<T> ab,
+                                           segment<T> cd,
+                                           Point point_out,
+                                           Segment segment_out)
 {
   auto [p, s]    = detail::segment_intersection(ab, cd);
   point_out[0]   = p;
diff --git a/cpp/tests/range/multilinestring_range_test.cu b/cpp/tests/range/multilinestring_range_test.cu
index 4938e0df4..b4d1fe6de 100644
--- a/cpp/tests/range/multilinestring_range_test.cu
+++ b/cpp/tests/range/multilinestring_range_test.cu
@@ -38,7 +38,9 @@ using namespace cuspatial;
 using namespace cuspatial::test;
 
 template <typename MultiLineStringRange, typename OutputIt>
-void __global__ array_access_tester(MultiLineStringRange mls, std::size_t i, OutputIt output_points)
+CUSPATIAL_KERNEL void array_access_tester(MultiLineStringRange mls,
+                                          std::size_t i,
+                                          OutputIt output_points)
 {
   thrust::copy(thrust::seq, mls[i].point_begin(), mls[i].point_end(), output_points);
 }
diff --git a/cpp/tests/range/multipoint_range_test.cu b/cpp/tests/range/multipoint_range_test.cu
index 501ddd6cf..3e7546bc5 100644
--- a/cpp/tests/range/multipoint_range_test.cu
+++ b/cpp/tests/range/multipoint_range_test.cu
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2023, NVIDIA CORPORATION.
+ * Copyright (c) 2023-2024, NVIDIA CORPORATION.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -39,16 +39,18 @@ using namespace cuspatial;
 using namespace cuspatial::test;
 
 template <typename MultiPointRange, typename OutputIt>
-void __global__ array_access_tester(MultiPointRange multipoints,
-                                    std::size_t i,
-                                    OutputIt output_points)
+CUSPATIAL_KERNEL void array_access_tester(MultiPointRange multipoints,
+                                          std::size_t i,
+                                          OutputIt output_points)
 {
   using T = typename MultiPointRange::element_t;
   thrust::copy(thrust::seq, multipoints[i].begin(), multipoints[i].end(), output_points);
 }
 
 template <typename MultiPointRange, typename OutputIt>
-void __global__ point_accessor_tester(MultiPointRange multipoints, std::size_t i, OutputIt point)
+CUSPATIAL_KERNEL void point_accessor_tester(MultiPointRange multipoints,
+                                            std::size_t i,
+                                            OutputIt point)
 {
   using T  = typename MultiPointRange::element_t;
   point[0] = multipoints.point(i);
diff --git a/cpp/tests/range/multipolygon_range_test.cu b/cpp/tests/range/multipolygon_range_test.cu
index 1e93ef136..f39ffb7e7 100644
--- a/cpp/tests/range/multipolygon_range_test.cu
+++ b/cpp/tests/range/multipolygon_range_test.cu
@@ -648,7 +648,9 @@ TYPED_TEST(MultipolygonRangeTest, MultipolygonAsMultiPoint3)
 }
 
 template <typename MultiPolygonRange, typename PointOutputIt>
-__global__ void array_access_tester(MultiPolygonRange rng, std::size_t i, PointOutputIt output)
+CUSPATIAL_KERNEL void array_access_tester(MultiPolygonRange rng,
+                                          std::size_t i,
+                                          PointOutputIt output)
 {
   thrust::copy(thrust::seq, rng[i].point_begin(), rng[i].point_end(), output);
 }
diff --git a/python/cuproj/cuproj/tests/pytest.ini b/python/cuproj/cuproj/tests/pytest.ini
new file mode 100644
index 000000000..bf70c06f8
--- /dev/null
+++ b/python/cuproj/cuproj/tests/pytest.ini
@@ -0,0 +1,5 @@
+# Copyright (c) 2024, NVIDIA CORPORATION.
+
+[pytest]
+addopts = --tb=native
+
diff --git a/python/cuspatial/cuspatial/tests/conftest.py b/python/cuspatial/cuspatial/tests/conftest.py
index caa53dcea..b71b08128 100644
--- a/python/cuspatial/cuspatial/tests/conftest.py
+++ b/python/cuspatial/cuspatial/tests/conftest.py
@@ -29,6 +29,8 @@ def gs():
     g8 = Polygon(
         ((35, 36), (37, 38), (39, 40), (41, 42)),
     )
+    # TODO: g9, g10, g11 are invalid
+    # https://github.com/libgeos/geos/issues/1177
     g9 = MultiPolygon(
         [
             (
diff --git a/python/cuspatial/cuspatial/tests/pytest.ini b/python/cuspatial/cuspatial/tests/pytest.ini
new file mode 100644
index 000000000..bf70c06f8
--- /dev/null
+++ b/python/cuspatial/cuspatial/tests/pytest.ini
@@ -0,0 +1,5 @@
+# Copyright (c) 2024, NVIDIA CORPORATION.
+
+[pytest]
+addopts = --tb=native
+
diff --git a/python/cuspatial/cuspatial/tests/test_cudf_integration.py b/python/cuspatial/cuspatial/tests/test_cudf_integration.py
index 0457f0992..41214e816 100644
--- a/python/cuspatial/cuspatial/tests/test_cudf_integration.py
+++ b/python/cuspatial/cuspatial/tests/test_cudf_integration.py
@@ -1,11 +1,18 @@
-# Copyright (c) 2022 NVIDIA CORPORATION.
+# Copyright (c) 2022-2024, NVIDIA CORPORATION.
 import geopandas as gpd
 import numpy as np
 import pandas as pd
+import pytest
 
 import cuspatial
 
+reason = (
+    "gs fixture contains invalid Polygons/MultiPolygons: "
+    "https://github.com/libgeos/geos/issues/1177"
+)
 
+
+@pytest.mark.xfail(reason=reason)
 def test_sort_index_series(gs):
     gs.index = np.random.permutation(len(gs))
     cugs = cuspatial.from_geopandas(gs)
@@ -14,6 +21,7 @@ def test_sort_index_series(gs):
     gpd.testing.assert_geoseries_equal(got, expected)
 
 
+@pytest.mark.xfail(reason=reason)
 def test_sort_index_dataframe(gpdf):
     gpdf.index = np.random.permutation(len(gpdf))
     cugpdf = cuspatial.from_geopandas(gpdf)
@@ -22,6 +30,7 @@ def test_sort_index_dataframe(gpdf):
     gpd.testing.assert_geodataframe_equal(got, expected)
 
 
+@pytest.mark.xfail(reason=reason)
 def test_sort_values(gpdf):
     cugpdf = cuspatial.from_geopandas(gpdf)
     expected = gpdf.sort_values("random")
diff --git a/python/cuspatial/cuspatial/tests/test_from_geopandas.py b/python/cuspatial/cuspatial/tests/test_from_geopandas.py
index 74675512c..1c33e216a 100644
--- a/python/cuspatial/cuspatial/tests/test_from_geopandas.py
+++ b/python/cuspatial/cuspatial/tests/test_from_geopandas.py
@@ -1,4 +1,4 @@
-# Copyright (c) 2020-2021, NVIDIA CORPORATION.
+# Copyright (c) 2020-2024, NVIDIA CORPORATION.
 import geopandas as gpd
 import pandas as pd
 from shapely.geometry import (
@@ -63,7 +63,9 @@ def test_from_geopandas_multipoint():
         cudf.Series([1.0, 2.0, 3.0, 4.0], dtype="float64"),
     )
     cudf.testing.assert_series_equal(
-        cudf.Series(cugs._column.mpoints._column.base_children[0]),
+        cudf.Series._from_column(
+            cugs._column.mpoints._column.base_children[0]
+        ),
         cudf.Series([0, 2], dtype="int32"),
     )
 
@@ -75,7 +77,7 @@ def test_from_geopandas_linestring():
         cugs.lines.xy, cudf.Series([4.0, 3.0, 2.0, 1.0], dtype="float64")
     )
     cudf.testing.assert_series_equal(
-        cudf.Series(cugs._column.lines._column.base_children[0]),
+        cudf.Series._from_column(cugs._column.lines._column.base_children[0]),
         cudf.Series([0, 1], dtype="int32"),
     )
 
@@ -95,7 +97,7 @@ def test_from_geopandas_multilinestring():
         cudf.Series([1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0], dtype="float64"),
     )
     cudf.testing.assert_series_equal(
-        cudf.Series(cugs._column.lines._column.base_children[0]),
+        cudf.Series._from_column(cugs._column.lines._column.base_children[0]),
         cudf.Series([0, 2], dtype="int32"),
     )
 
@@ -112,11 +114,13 @@ def test_from_geopandas_polygon():
         cudf.Series([0.0, 0.0, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0], dtype="float64"),
     )
     cudf.testing.assert_series_equal(
-        cudf.Series(cugs._column.polygons._column.base_children[0]),
+        cudf.Series._from_column(
+            cugs._column.polygons._column.base_children[0]
+        ),
         cudf.Series([0, 1], dtype="int32"),
     )
     cudf.testing.assert_series_equal(
-        cudf.Series(
+        cudf.Series._from_column(
             cugs._column.polygons._column.base_children[1].base_children[0]
         ),
         cudf.Series([0, 1], dtype="int32"),
@@ -156,11 +160,13 @@ def test_from_geopandas_polygon_hole():
         ),
     )
     cudf.testing.assert_series_equal(
-        cudf.Series(cugs._column.polygons._column.base_children[0]),
+        cudf.Series._from_column(
+            cugs._column.polygons._column.base_children[0]
+        ),
         cudf.Series([0, 1], dtype="int32"),
     )
     cudf.testing.assert_series_equal(
-        cudf.Series(
+        cudf.Series._from_column(
             cugs._column.polygons._column.base_children[1].base_children[0]
         ),
         cudf.Series([0, 2], dtype="int32"),
@@ -204,11 +210,13 @@ def test_from_geopandas_multipolygon():
         ),
     )
     cudf.testing.assert_series_equal(
-        cudf.Series(cugs._column.polygons._column.base_children[0]),
+        cudf.Series._from_column(
+            cugs._column.polygons._column.base_children[0]
+        ),
         cudf.Series([0, 1], dtype="int32"),
     )
     cudf.testing.assert_series_equal(
-        cudf.Series(
+        cudf.Series._from_column(
             cugs._column.polygons._column.base_children[1].base_children[0]
         ),
         cudf.Series([0, 2], dtype="int32"),
diff --git a/python/cuspatial/cuspatial/tests/test_geodataframe.py b/python/cuspatial/cuspatial/tests/test_geodataframe.py
index dee2bfeb4..f834b35e5 100644
--- a/python/cuspatial/cuspatial/tests/test_geodataframe.py
+++ b/python/cuspatial/cuspatial/tests/test_geodataframe.py
@@ -66,7 +66,7 @@ def generator(size, has_z=False):
 
 
 def assert_eq_point(p1, p2):
-    assert type(p1) == type(p2)
+    assert type(p1) is type(p2)
     assert p1.x == p2.x
     assert p1.y == p2.y
     assert p1.has_z == p2.has_z
@@ -76,7 +76,7 @@ def assert_eq_point(p1, p2):
 
 
 def assert_eq_multipoint(p1, p2):
-    assert type(p1) == type(p2)
+    assert type(p1) is type(p2)
     assert len(p1) == len(p2)
     for i in range(len(p1)):
         assert_eq_point(p1[i], p2[i])
@@ -93,8 +93,7 @@ def assert_eq_multipolygon(p1, p2):
 
 
 def assert_eq_geo_df(geo1, geo2):
-    if type(geo1) != type(geo2):
-        assert TypeError
+    assert type(geo1) is type(geo2)
     assert geo1.columns.equals(geo2.columns)
     for col in geo1.columns:
         if geo1[col].dtype == "geometry":
@@ -112,7 +111,7 @@ def test_select_multiple_columns(gpdf):
 
 def test_type_persistence(gpdf):
     cugpdf = cuspatial.from_geopandas(gpdf)
-    assert type(cugpdf["geometry"]) == cuspatial.GeoSeries
+    assert type(cugpdf["geometry"]) is cuspatial.GeoSeries
 
 
 def test_interleaved_point(gpdf, polys):
@@ -462,10 +461,16 @@ def test_reset_index(level, drop, inplace, col_level, col_fill):
 def test_cudf_dataframe_init():
     df = cudf.DataFrame({"a": [1, 2, 3], "b": [4, 5, 6]})
     gdf = cuspatial.GeoDataFrame(df)
-    assert_eq_geo_df(gdf.to_pandas(), df.to_pandas())
+    assert_eq_geo_df(gdf.to_pandas(), gpd.GeoDataFrame(df.to_pandas()))
 
 
-def test_apply_boolean_mask(gpdf, mask_factory):
+def test_apply_boolean_mask(gpdf, mask_factory, request):
+    if "MaskNone" not in request.node.callspec.id:
+        reason = (
+            "gs fixture contains invalid Polygons/MultiPolygons: "
+            "https://github.com/libgeos/geos/issues/1177"
+        )
+        request.applymarker(pytest.mark.xfail(reason=reason))
     mask = mask_factory(len(gpdf))
 
     expected = gpdf[mask]
diff --git a/python/cuspatial/cuspatial/tests/test_geoseries.py b/python/cuspatial/cuspatial/tests/test_geoseries.py
index 35007c57d..56f61158e 100644
--- a/python/cuspatial/cuspatial/tests/test_geoseries.py
+++ b/python/cuspatial/cuspatial/tests/test_geoseries.py
@@ -87,7 +87,7 @@ def generator(size: Integral, obj_type: Example_Feature_Enum = None):
 
 
 def assert_eq_point(p1, p2):
-    assert type(p1) == type(p2)
+    assert type(p1) is type(p2)
     assert p1.x == p2.x
     assert p1.y == p2.y
     assert p1.has_z == p2.has_z
@@ -97,14 +97,14 @@ def assert_eq_point(p1, p2):
 
 
 def assert_eq_multipoint(p1, p2):
-    assert type(p1) == type(p2)
+    assert type(p1) is type(p2)
     assert len(p1) == len(p2)
     for i in range(len(p1)):
         assert_eq_point(p1[i], p2[i])
 
 
 def assert_eq_linestring(p1, p2):
-    assert type(p1) == type(p2)
+    assert type(p1) is type(p2)
     assert p1 == p2
 
 
@@ -124,8 +124,8 @@ def assert_eq_multipolygon(p1, p2):
 
 
 def assert_eq_geo(geo1, geo2):
-    if type(geo1) != type(geo2):
-        assert TypeError
+    if type(geo1) is not type(geo2):
+        raise TypeError
     result = geo1.equals(geo2)
     if isinstance(result, bool):
         assert result