acj 0.1.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (50) hide show
  1. acj-0.1.0/.github/workflows/wheels.yml +53 -0
  2. acj-0.1.0/.gitignore +53 -0
  3. acj-0.1.0/CMakeLists.txt +80 -0
  4. acj-0.1.0/Dockerfile +62 -0
  5. acj-0.1.0/Makefile +79 -0
  6. acj-0.1.0/PKG-INFO +16 -0
  7. acj-0.1.0/README.md +122 -0
  8. acj-0.1.0/examples/example_acj.py +108 -0
  9. acj-0.1.0/examples/example_crime_osm.py +250 -0
  10. acj-0.1.0/examples/example_crime_visualization.py +254 -0
  11. acj-0.1.0/examples/example_minkowski_osm.py +92 -0
  12. acj-0.1.0/examples/example_multimodal_evaluation.py +83 -0
  13. acj-0.1.0/examples/example_realtime.py +205 -0
  14. acj-0.1.0/examples/example_simplification.py +125 -0
  15. acj-0.1.0/examples/example_simplification_osm.py +246 -0
  16. acj-0.1.0/examples/example_simplification_visual.py +208 -0
  17. acj-0.1.0/flake.lock +27 -0
  18. acj-0.1.0/flake.nix +57 -0
  19. acj-0.1.0/pyproject.toml +33 -0
  20. acj-0.1.0/src/acj/__init__.py +46 -0
  21. acj-0.1.0/src/acj/algorithms/__init__.py +0 -0
  22. acj-0.1.0/src/acj/algorithms/graph.py +472 -0
  23. acj-0.1.0/src/acj/algorithms/map_index.py +204 -0
  24. acj-0.1.0/src/acj/algorithms/minkowski.py +91 -0
  25. acj-0.1.0/src/acj/core/CMakeLists.txt +42 -0
  26. acj-0.1.0/src/acj/core/__init__.py +3 -0
  27. acj-0.1.0/src/acj/core/network.py +105 -0
  28. acj-0.1.0/src/acj/core/semantics.py +51 -0
  29. acj-0.1.0/src/acj/core/src/bindings.cpp +65 -0
  30. acj-0.1.0/src/acj/core/src/graph_simplify.cpp +987 -0
  31. acj-0.1.0/src/acj/core/src/graph_simplify.hpp +43 -0
  32. acj-0.1.0/src/acj/core/src/spatial_index.cpp +147 -0
  33. acj-0.1.0/src/acj/core/src/spatial_index.hpp +17 -0
  34. acj-0.1.0/src/acj/core/src/types.hpp +81 -0
  35. acj-0.1.0/src/acj/data/__init__.py +0 -0
  36. acj-0.1.0/src/acj/data/io.py +219 -0
  37. acj-0.1.0/src/acj/evaluation/__init__.py +0 -0
  38. acj-0.1.0/src/acj/evaluation/base.py +28 -0
  39. acj-0.1.0/src/acj/evaluation/evaluators.py +53 -0
  40. acj-0.1.0/src/acj/evaluation/metrics.py +51 -0
  41. acj-0.1.0/src/acj/utils/__init__.py +0 -0
  42. acj-0.1.0/src/acj/utils/render.py +394 -0
  43. acj-0.1.0/tests/__init__.py +0 -0
  44. acj-0.1.0/tests/test_data.py +44 -0
  45. acj-0.1.0/tests/test_evaluation.py +45 -0
  46. acj-0.1.0/tests/test_network.py +34 -0
  47. acj-0.1.0/tests/test_parsers.py +55 -0
  48. acj-0.1.0/tests/test_semantics.py +58 -0
  49. acj-0.1.0/tests/test_simplification.py +81 -0
  50. acj-0.1.0/tests/test_spatial_index.py +82 -0
@@ -0,0 +1,53 @@
1
+ name: Build Wheels
2
+
3
+ on:
4
+ push:
5
+ branches:
6
+ - main
7
+ - develop
8
+ tags:
9
+ - 'v*'
10
+ pull_request:
11
+
12
+ jobs:
13
+ build_wheels:
14
+ name: Build wheels on ${{ matrix.os }}
15
+ runs-on: ${{ matrix.os }}
16
+ strategy:
17
+ fail-fast: false
18
+ matrix:
19
+ os: [ubuntu-latest, windows-latest, macos-latest]
20
+
21
+ steps:
22
+ - uses: actions/checkout@v4
23
+
24
+ - name: Set up Python
25
+ uses: actions/setup-python@v5
26
+ with:
27
+ python-version: '3.10'
28
+
29
+ # cibuildwheel automatically downloads the cibuildwheel docker images for linux
30
+ # and runs the build process across different python versions.
31
+ - name: Cache vcpkg installed packages
32
+ uses: actions/cache@v4
33
+ if: runner.os == 'Windows'
34
+ with:
35
+ path: C:\vcpkg\installed
36
+ key: ${{ runner.os }}-vcpkg-boost-full
37
+ restore-keys: |
38
+ ${{ runner.os }}-vcpkg-
39
+
40
+ - name: Build wheels
41
+ uses: pypa/cibuildwheel@v2.22.0
42
+ env:
43
+ CIBW_MANYLINUX_X86_64_IMAGE: manylinux_2_28
44
+ CIBW_BEFORE_ALL_LINUX: "dnf install -y gmp-devel mpfr-devel boost-devel"
45
+ CIBW_BEFORE_ALL_MACOS: "brew install gmp mpfr boost"
46
+ CIBW_ENVIRONMENT_MACOS: 'MACOSX_DEPLOYMENT_TARGET="15.0"'
47
+ CIBW_BEFORE_ALL_WINDOWS: "vcpkg install boost:x64-windows gmp:x64-windows mpfr:x64-windows"
48
+ CIBW_SKIP: "cp36-* cp37-* cp38-* pp* *-musllinux_* *i686* *win32*" # Omitir versiones muy viejas o 32 bits
49
+
50
+ - uses: actions/upload-artifact@v4
51
+ with:
52
+ name: cibw-wheels-${{ matrix.os }}-${{ strategy.job-index }}
53
+ path: ./wheelhouse/*.whl
acj-0.1.0/.gitignore ADDED
@@ -0,0 +1,53 @@
1
+ # Build artifacts
2
+ build/
3
+ *.so
4
+ *.o
5
+ *.a
6
+ *.dylib
7
+
8
+ # Python cache
9
+ __pycache__/
10
+ *.py[cod]
11
+ *$py.class
12
+ *.egg-info/
13
+ dist/
14
+ .eggs/
15
+
16
+ # Testing
17
+ .pytest_cache/
18
+ .coverage
19
+ htmlcov/
20
+
21
+ # IDEs
22
+ .vscode/
23
+ .idea/
24
+ *.swp
25
+ *.swo
26
+ *~
27
+ .env
28
+
29
+ # OS
30
+ .DS_Store
31
+ Thumbs.db
32
+
33
+ # CMake
34
+ CMakeCache.txt
35
+ CMakeFiles/
36
+ cmake_install.cmake
37
+ build/Makefile
38
+ *.cmake
39
+
40
+ # Keep only the main CMakeLists.txt files and root Makefile
41
+ !CMakeLists.txt
42
+ !acj/core/CMakeLists.txt
43
+ !Makefile
44
+
45
+ # ACJ specific
46
+ cache/
47
+ output/
48
+ *.tar.xz
49
+
50
+ # NixOs / direnv
51
+ .direnv/
52
+ .venv/
53
+ .envrc
@@ -0,0 +1,80 @@
1
+ cmake_minimum_required(VERSION 3.15)
2
+ project(acj_project LANGUAGES CXX)
3
+
4
+ set(CMAKE_CXX_STANDARD 17)
5
+ set(CMAKE_CXX_STANDARD_REQUIRED ON)
6
+
7
+ if(NOT CMAKE_BUILD_TYPE)
8
+ set(CMAKE_BUILD_TYPE Release)
9
+ endif()
10
+
11
+ find_package(Python3 COMPONENTS Interpreter Development.Module REQUIRED)
12
+ find_package(pybind11 CONFIG REQUIRED)
13
+
14
+ # Use FetchContent to extract the local CGAL tarball during build
15
+ include(FetchContent)
16
+ FetchContent_Declare(
17
+ CGAL
18
+ URL https://github.com/CGAL/cgal/releases/download/v6.0.1/CGAL-6.0.1.tar.xz
19
+ )
20
+ FetchContent_Populate(CGAL)
21
+
22
+ # PATCH CGAL BUG in AABB_traits_2.h
23
+ file(READ "${cgal_SOURCE_DIR}/include/CGAL/AABB_traits_2.h" AABB_TRAITS_CONTENT)
24
+ string(REPLACE "bb,true)?" "bb)?" AABB_TRAITS_CONTENT "${AABB_TRAITS_CONTENT}")
25
+ file(WRITE "${cgal_SOURCE_DIR}/include/CGAL/AABB_traits_2.h" "${AABB_TRAITS_CONTENT}")
26
+ # CGAL is header-only from 5.0+, just add the include directories
27
+ add_library(CGAL::CGAL INTERFACE IMPORTED)
28
+ target_include_directories(CGAL::CGAL INTERFACE "${cgal_SOURCE_DIR}/include")
29
+
30
+ # Find Boost (strictly required by CGAL)
31
+ if(NOT WIN32)
32
+ find_package(Boost REQUIRED)
33
+ endif()
34
+
35
+ if(WIN32)
36
+ # === WINDOWS LOGIC ===
37
+ # Include vcpkg globally for gmp/mpfr and EXACTLY inject Boost include path like the experiment!
38
+ include_directories("C:/vcpkg/installed/x64-windows/include")
39
+
40
+ find_library(GMP_LIBRARY gmp PATHS "C:/vcpkg/installed/x64-windows/lib" NO_DEFAULT_PATH)
41
+ find_library(MPFR_LIBRARY mpfr PATHS "C:/vcpkg/installed/x64-windows/lib" NO_DEFAULT_PATH)
42
+ if(GMP_LIBRARY AND MPFR_LIBRARY)
43
+ target_link_libraries(CGAL::CGAL INTERFACE ${GMP_LIBRARY} ${MPFR_LIBRARY})
44
+ endif()
45
+
46
+ elseif(APPLE)
47
+ # === MACOS LOGIC ===
48
+ if(TARGET Boost::headers)
49
+ target_link_libraries(CGAL::CGAL INTERFACE Boost::headers)
50
+ elseif(TARGET Boost::boost)
51
+ target_link_libraries(CGAL::CGAL INTERFACE Boost::boost)
52
+ elseif(Boost_INCLUDE_DIRS)
53
+ target_include_directories(CGAL::CGAL INTERFACE "${Boost_INCLUDE_DIRS}")
54
+ endif()
55
+
56
+ find_library(GMP_LIBRARY gmp)
57
+ find_library(MPFR_LIBRARY mpfr)
58
+ if(GMP_LIBRARY AND MPFR_LIBRARY)
59
+ target_link_libraries(CGAL::CGAL INTERFACE ${GMP_LIBRARY} ${MPFR_LIBRARY})
60
+ endif()
61
+
62
+ else()
63
+ # === LINUX (UBUNTU) LOGIC ===
64
+ if(TARGET Boost::headers)
65
+ target_link_libraries(CGAL::CGAL INTERFACE Boost::headers)
66
+ elseif(TARGET Boost::boost)
67
+ target_link_libraries(CGAL::CGAL INTERFACE Boost::boost)
68
+ elseif(Boost_INCLUDE_DIRS)
69
+ target_include_directories(CGAL::CGAL INTERFACE "${Boost_INCLUDE_DIRS}")
70
+ endif()
71
+
72
+ find_library(GMP_LIBRARY gmp)
73
+ find_library(MPFR_LIBRARY mpfr)
74
+ if(GMP_LIBRARY AND MPFR_LIBRARY)
75
+ target_link_libraries(CGAL::CGAL INTERFACE ${GMP_LIBRARY} ${MPFR_LIBRARY})
76
+ endif()
77
+ endif()
78
+
79
+ add_compile_definitions(CGAL_DO_NOT_USE_BOOST_MP)
80
+ add_subdirectory(src/acj/core)
acj-0.1.0/Dockerfile ADDED
@@ -0,0 +1,62 @@
1
+ FROM ubuntu:jammy
2
+
3
+ ARG DEBIAN_FRONTEND=noninteractive
4
+ ARG uid
5
+ ARG gid
6
+ ARG user
7
+ ARG group
8
+
9
+ RUN apt-get update
10
+ RUN apt-get install -y tzdata
11
+ RUN apt-get upgrade -y
12
+
13
+ RUN echo 'alias ll="ls -l --color -a"' >> /root/.bashrc
14
+
15
+ RUN apt-get install -y python3 python3-pip python3-dev
16
+ RUN apt-get install -y build-essential cmake git pkg-config
17
+ RUN apt-get install -y pybind11-dev
18
+
19
+ RUN apt-get install -y xz-utils
20
+ RUN apt-get install -y g++
21
+ RUN apt-get install -y libboost-all-dev
22
+ RUN apt-get install -y libgmp-dev
23
+ RUN apt-get install -y libmpfr-dev
24
+
25
+ RUN apt-get install -y libcgal-dev
26
+
27
+ RUN mkdir -p /tmp/cgal
28
+ COPY CGAL-6.0.1.tar.xz /tmp/cgal/
29
+ RUN cd /tmp/cgal && tar -xf CGAL-6.0.1.tar.xz
30
+ RUN cd /tmp/cgal/CGAL-6.0.1 && mkdir -p build
31
+ RUN cd /tmp/cgal/CGAL-6.0.1/build && cmake ..
32
+ RUN cd /tmp/cgal/CGAL-6.0.1/build && make install
33
+ RUN rm -rf /tmp/cgal
34
+
35
+ RUN apt-get install -y libspatialindex-dev libgeos-dev libproj-dev
36
+
37
+ RUN apt-get install -y libgl1-mesa-glx libglib2.0-0 libsm6 libxrender1 libxext6 libfontconfig1
38
+ RUN apt-get install -y python3-pyqt5 python3-pyqt5.qtopengl
39
+
40
+ RUN rm -rf /var/lib/apt/lists/*
41
+
42
+ RUN mkdir -p /workspace
43
+
44
+ RUN groupadd -g ${gid} ${group} || true
45
+ RUN useradd -m -u ${uid} -g ${gid} -s /bin/bash ${user} || true
46
+
47
+ RUN echo 'alias ll="ls -l --color -a"' >> /home/${user}/.bashrc || true
48
+
49
+ RUN chown -R ${uid}:${gid} /workspace
50
+
51
+ USER ${user}
52
+ WORKDIR /workspace
53
+
54
+ COPY --chown=${user}:${group} . /workspace/
55
+
56
+ RUN pip3 install -r requirements.txt
57
+
58
+ RUN mkdir -p build
59
+ WORKDIR /workspace/build
60
+ RUN cmake .. && make -j$(nproc)
61
+
62
+ WORKDIR /workspace
acj-0.1.0/Makefile ADDED
@@ -0,0 +1,79 @@
1
+ .PHONY: build shell shell-user test example example-realtime example-simplification example-simplification-visual example-crime example-crime-osm example-simplification-osm example-minkowski-osm clean clean-all help
2
+
3
+ .DEFAULT_GOAL := help
4
+
5
+ help:
6
+ @echo "ACJ Library - Makefile Commands"
7
+ @echo "================================"
8
+ @echo ""
9
+ @echo "Main commands:"
10
+ @echo " make build - Build Docker image with all dependencies"
11
+ @echo " make test - Run ACJ library tests"
12
+ @echo ""
13
+ @echo "Examples with synthetic data:"
14
+ @echo " make example - Basic ACJ example"
15
+ @echo " make example-simplification-visual - Graph simplification demo (synthetic)"
16
+ @echo " make example-crime - Crime heatmap demo (synthetic)"
17
+ @echo ""
18
+ @echo "Examples with real OSM data (configurable city):"
19
+ @echo " make example-realtime - Real-time crime heatmap"
20
+ @echo " make example-simplification - Graph simplification comparison"
21
+ @echo " make example-minkowski-osm - Minkowski simplification (configurable location)"
22
+ @echo " make example-crime-osm - Crime heatmap (configurable location)"
23
+ @echo " make example-simplification-osm - Simplification (configurable location)"
24
+ @echo ""
25
+ @echo "Cleanup commands:"
26
+ @echo " make clean - Clean build artifacts"
27
+ @echo " make clean-all - Clean everything including Docker cache"
28
+ @echo ""
29
+ @echo "Development commands:"
30
+ @echo " make shell - Open Docker shell as root"
31
+ @echo " make shell-user - Open Docker shell as user"
32
+
33
+ # Build Docker image
34
+ build:
35
+ docker build -f Dockerfile -t ubuntu-acj:1 --build-arg uid="$(shell id -u)" --build-arg gid="$(shell id -g)" --build-arg user=dockeruser --build-arg group=dockergroup .
36
+
37
+ # Interactive shells
38
+ shell:
39
+ docker run -v $(shell pwd):/workspace -w /workspace -it ubuntu-acj:1 /bin/bash
40
+
41
+ shell-user:
42
+ docker run --user $(shell id -u):$(shell id -g) -v $(shell pwd):/workspace -w /workspace -it ubuntu-acj:1 /bin/bash
43
+
44
+ # ACJ Library commands (Actualizado para Arquitectura src/)
45
+ test: ## Run ACJ library test suite
46
+ docker run --user $(shell id -u):$(shell id -g) -v $(shell pwd):/workspace ubuntu-acj:1 sh -c "cd /workspace && mkdir -p build && cd build && cmake .. && make -j\$$(nproc) && cd .. && PYTHONPATH=/workspace/build:/workspace/src python3 -m pytest tests/ -v"
47
+
48
+ example: ## Run basic ACJ library example
49
+ docker run --user $(shell id -u):$(shell id -g) -v $(shell pwd):/workspace ubuntu-acj:1 sh -c "cd /workspace && mkdir -p build && cd build && cmake .. && make -j\$$(nproc) && cd .. && PYTHONPATH=/workspace/build:/workspace/src python3 examples/example_acj.py"
50
+
51
+ example-realtime: ## Run real-time interactive visualization with VisPy
52
+ docker run --user $(shell id -u):$(shell id -g) -v $(shell pwd):/workspace -v /tmp/.X11-unix:/tmp/.X11-unix -e DISPLAY=$$DISPLAY ubuntu-acj:1 sh -c "cd /workspace && mkdir -p build && cd build && cmake .. && make -j\$$(nproc) && cd .. && PYTHONPATH=/workspace/build:/workspace/src python3 examples/example_realtime.py"
53
+
54
+ example-simplification: ## Run graph simplification comparison
55
+ docker run --user $(shell id -u):$(shell id -g) -v $(shell pwd):/workspace -v /tmp/.X11-unix:/tmp/.X11-unix -e DISPLAY=$$DISPLAY ubuntu-acj:1 sh -c "cd /workspace && mkdir -p build && cd build && cmake .. && make -j\$$(nproc) && cd .. && PYTHONPATH=/workspace/build:/workspace/src python3 examples/example_simplification.py"
56
+
57
+ example-simplification-visual: ## Run visual graph simplification demo with interactive comparison
58
+ docker run --user $(shell id -u):$(shell id -g) -v $(shell pwd):/workspace -v /tmp/.X11-unix:/tmp/.X11-unix -e DISPLAY=$$DISPLAY ubuntu-acj:1 sh -c "cd /workspace && mkdir -p build && cd build && cmake .. && make -j\$$(nproc) && cd .. && PYTHONPATH=/workspace/build:/workspace/src python3 examples/example_simplification_visual.py"
59
+
60
+ example-crime: ## Run crime heatmap visualization demo with interactive comparison
61
+ docker run --user $(shell id -u):$(shell id -g) -v $(shell pwd):/workspace -v /tmp/.X11-unix:/tmp/.X11-unix -e DISPLAY=$$DISPLAY ubuntu-acj:1 sh -c "cd /workspace && mkdir -p build && cd build && cmake .. && make -j\$$(nproc) && cd .. && PYTHONPATH=/workspace/build:/workspace/src python3 examples/example_crime_visualization.py"
62
+
63
+ example-crime-osm: ## Run crime heatmap with real OSM data (configurable location)
64
+ docker run --user $(shell id -u):$(shell id -g) -v $(shell pwd):/workspace -v /tmp/.X11-unix:/tmp/.X11-unix -e DISPLAY=$$DISPLAY ubuntu-acj:1 sh -c "cd /workspace && mkdir -p build && cd build && cmake .. && make -j\$$(nproc) && cd .. && PYTHONPATH=/workspace/build:/workspace/src python3 examples/example_crime_osm.py"
65
+
66
+ example-simplification-osm: ## Run simplification comparison with real OSM data (configurable location)
67
+ docker run --user $(shell id -u):$(shell id -g) -v $(shell pwd):/workspace -v /tmp/.X11-unix:/tmp/.X11-unix -e DISPLAY=$$DISPLAY ubuntu-acj:1 sh -c "cd /workspace && mkdir -p build && cd build && cmake .. && make -j\$$(nproc) && cd .. && PYTHONPATH=/workspace/build:/workspace/src python3 examples/example_simplification_osm.py"
68
+
69
+ example-minkowski-osm: ## Run Minkowski simplification with real OSM data (configurable location)
70
+ docker run --user $(shell id -u):$(shell id -g) -v $(shell pwd):/workspace -v /tmp/.X11-unix:/tmp/.X11-unix -e DISPLAY=$$DISPLAY ubuntu-acj:1 sh -c "cd /workspace && mkdir -p build && cd build && cmake .. && make -j\$$(nproc) && cd .. && PYTHONPATH=/workspace/build:/workspace/src python3 examples/example_minkowski_osm.py"
71
+
72
+
73
+ # Cleanup
74
+ clean: ## Clean build artifacts
75
+ rm -rf build/ cache/ output/
76
+
77
+ clean-all: ## Clean everything including Docker cache
78
+ rm -rf build/ cache/ output/
79
+ docker rmi ubuntu-acj:1 || true
acj-0.1.0/PKG-INFO ADDED
@@ -0,0 +1,16 @@
1
+ Metadata-Version: 2.2
2
+ Name: acj
3
+ Version: 0.1.0
4
+ Summary: Simplificacion de grafos urbanos y analisis de criminalidad
5
+ Author-Email: Alejandro <alejandro.calizaya@utec.edu.pe>, Cesar <cesar.perales@utec.edu.pe>, Jerimy <jerimy.sandoval@utec.edu.pe>
6
+ Requires-Python: >=3.10
7
+ Requires-Dist: pybind11
8
+ Requires-Dist: numpy
9
+ Requires-Dist: pandas
10
+ Requires-Dist: osmnx
11
+ Requires-Dist: vispy
12
+ Requires-Dist: PyQt5
13
+ Requires-Dist: pytest
14
+ Requires-Dist: geopandas
15
+ Requires-Dist: networkx
16
+
acj-0.1.0/README.md ADDED
@@ -0,0 +1,122 @@
1
+ # ACJ: Urban Graph Acceleration & Simplification Framework
2
+
3
+ ![Python](https://img.shields.io/badge/Python-3.10+-blue.svg?logo=python&logoColor=white)
4
+ ![C++](https://img.shields.io/badge/C++-17-00599C.svg?logo=c%2B%2B&logoColor=white)
5
+ ![CMake](https://img.shields.io/badge/CMake-3.15+-064F8C.svg?logo=cmake&logoColor=white)
6
+ ![License](https://img.shields.io/badge/License-MIT-green.svg)
7
+
8
+ **ACJ** is a high-performance hybrid framework (C++/Python) designed for the semantic and topological simplification of large-scale urban networks. It safely decouples complex geometric operations from semantic data attributes, ensuring that metadata (e.g., speed limits, road names) survives massive graph reductions.
9
+
10
+ ## System Architecture
11
+
12
+ The core philosophy of ACJ relies on treating `UrbanNetwork` as an invariant Registry/Proxy. Heavy geometric simplifications are sent to a highly optimized C++ core using CGAL. The core returns the simplified topology alongside strict lineage maps, which Python then uses to reconstruct and resolve semantic collisions.
13
+
14
+ ```mermaid
15
+ graph TD
16
+ subgraph Inputs ["Multimodal Inputs"]
17
+ A1[OSMnx]
18
+ A2[DataFrames]
19
+ A3[Shapefiles]
20
+ end
21
+
22
+ A1 -->|from_networkx| B
23
+ A2 -->|from_dataframe| B
24
+ A3 -->|Parsers| B
25
+
26
+ subgraph Python ["Python Layer"]
27
+ B[UrbanNetwork <br/> Registry/Proxy]
28
+ E[Semantic Resolution <br/> Metadata Fusion]
29
+ F[Metrics & Evaluator]
30
+ end
31
+
32
+ subgraph Cpp ["C++ Core (CGAL/pybind11)"]
33
+ C[Raw Topology <br/> V, E matrices]
34
+ D[Geometric & Topological <br/> Simplification Algorithms]
35
+ end
36
+
37
+ B -.->|Passes Topology| C
38
+ C --> D
39
+ D -.->|SimplificationResult <br/> Topology + Lineage Maps| E
40
+ B -.->|Passes Metadata| E
41
+
42
+ E -->|Resolved UrbanNetwork| F
43
+ ```
44
+
45
+ ## Installation Guide
46
+
47
+ ### Prerequisites
48
+ You need a system with C++17 support, CMake, and the following libraries:
49
+ - **CGAL** (Computational Geometry Algorithms Library)
50
+ - **Boost**
51
+ - **pybind11**
52
+
53
+ On Ubuntu/Debian:
54
+ ```bash
55
+ sudo apt-get update
56
+ sudo apt-get install cmake libcgal-dev libboost-all-dev
57
+ ```
58
+ On Arch Linux:
59
+ ```bash
60
+ sudo pacman -S cmake cgal boost pybind11
61
+ ```
62
+
63
+ ### Building the Package
64
+ We highly recommend setting up a virtual environment:
65
+
66
+ ```bash
67
+ python -m venv venv
68
+ source venv/bin/activate
69
+ pip install -U pip setuptools wheel
70
+ ```
71
+
72
+ Install the package in editable mode (which will trigger CMake build):
73
+
74
+ ```bash
75
+ pip install -e .
76
+ ```
77
+
78
+ ## Quick Start (E2E Pipeline)
79
+
80
+ This demonstrates the end-to-end pipeline: extracting raw data, parsing, executing a highly optimized topological simplification, and calculating compression metrics.
81
+
82
+ ```python
83
+ import osmnx as ox
84
+ from acj import UrbanNetwork, ACJTopologicalEvaluator
85
+ from acj import CompressionRatioMetric, SemanticSpeedDistortionMetric
86
+
87
+ # 1. Ingest Data (Raw Graph)
88
+ G = ox.graph_from_place("Barranco, Lima, Peru", network_type="drive")
89
+
90
+ # 2. Parse into Multimodal Registry
91
+ network = UrbanNetwork.from_networkx(G)
92
+ print("Initial:", network)
93
+
94
+ # 3. Setup Metrics and Evaluator
95
+ metrics = [CompressionRatioMetric(), SemanticSpeedDistortionMetric()]
96
+ evaluator = ACJTopologicalEvaluator(network, metrics)
97
+
98
+ # 4. Evaluate (C++ Topo Simplification + Semantic Resolution)
99
+ results = evaluator.evaluate()
100
+
101
+ print("Results:", results)
102
+ print("Simplified:", evaluator.simplified_network)
103
+ ```
104
+
105
+ ## Module Structure (API Reference)
106
+
107
+ ### `acj.core` (The Heart of the Framework)
108
+ This module acts as the central orchestrator for data decoupling.
109
+ - **`UrbanNetwork`**: The fundamental data structure of the library. It stores raw network topologies in heavily optimized Pandas DataFrames alongside dictionaries of semantic metadata. Supports multimodal ingestion from `NetworkX`, `osmnx`, or arbitrary DataFrames.
110
+ - **`resolve_semantics()`**: The pure Python metadata fusion engine. When a graph is collapsed by C++, this function takes the topological lineage map and automatically resolves semantic collisions (e.g., averaging max speeds or concatenating street names) to keep information loss at 0.
111
+
112
+ ### `acj.algorithms` (Python Wrappers & Indexing)
113
+ - **`graph` / `minkowski`**: Wrappers for invoking our low-level C++ simplification procedures. Here you'll find interfaces for topological simplifications, geometric clustering, Minkowski-sum reductions, and more.
114
+ - **`MapIndex`**: An advanced spatial querying interface wrapping CGAL spatial trees, enabling fast nearest-neighbor point-to-graph assignments (essential for crime mapping or event correlation on large networks).
115
+
116
+ ### `acj.data` (Data Binding & IO)
117
+ - **`SimplificationResult`**: The critical struct generated and bound directly from pybind11. It holds the new `GraphData` topology alongside `.node_lineage` and `.edge_lineage` dictionaries linking new entities back to their original IDs.
118
+ - **`GraphData`**: Internal lightweight wrapper handling node/edge validations before passing arrays down to C++.
119
+
120
+ ### `acj.evaluation` (Research & Metrics Engine)
121
+ - **`BaseEvaluator` / `ACJTopologicalEvaluator`**: Automates the simplification lifecycle. These evaluators accept a raw `UrbanNetwork`, inject it into C++ for reduction, apply the semantic resolution layer, and systematically test the results against predefined metrics.
122
+ - **`CompressionRatioMetric` / `SemanticSpeedDistortionMetric`**: Pluggable metrics allowing researchers to quantitatively evaluate exactly how much data is compressed and what percentage of the semantic truth (like speed distributions) is distorted during network abstraction.
@@ -0,0 +1,108 @@
1
+ """
2
+ Example usage of the ACJ library.
3
+
4
+ This example demonstrates how to:
5
+ 1. Load graph data from pandas DataFrames
6
+ 2. Create a MapIndex for spatial queries
7
+ 3. Assign points to nearest graph endpoints (nodes)
8
+ """
9
+
10
+ import sys
11
+ import os
12
+ sys.path.insert(0, os.path.dirname(os.path.dirname(__file__)))
13
+
14
+ import acj
15
+ import pandas as pd
16
+ import numpy as np
17
+
18
+
19
+ def main():
20
+ print("=" * 70)
21
+ print("ACJ Library - Example Usage")
22
+ print("=" * 70)
23
+ print()
24
+
25
+ # Step 1: Create sample graph data (simple street network)
26
+ print("[1/4] Creating sample graph data...")
27
+ print()
28
+
29
+ nodes = pd.DataFrame({
30
+ 'node_id': [0, 1, 2, 3, 4],
31
+ 'x': [0.0, 100.0, 200.0, 300.0, 150.0],
32
+ 'y': [0.0, 0.0, 100.0, 0.0, 100.0]
33
+ })
34
+
35
+ segments = pd.DataFrame({
36
+ 'segment_id': [0, 1, 2, 3],
37
+ 'node_start': [0, 1, 2, 3],
38
+ 'node_end': [1, 2, 4, 4],
39
+ 'x1': [0.0, 100.0, 200.0, 300.0],
40
+ 'y1': [0.0, 0.0, 100.0, 0.0],
41
+ 'x2': [100.0, 200.0, 150.0, 150.0],
42
+ 'y2': [0.0, 100.0, 100.0, 100.0]
43
+ })
44
+
45
+ print(f" Nodes: {len(nodes)}")
46
+ print(f" Segments: {len(segments)}")
47
+ print()
48
+
49
+ # Step 2: Load graph using ACJ
50
+ print("[2/4] Loading graph data...")
51
+ graph = acj.load_graph(nodes, segments)
52
+ print(f" Graph loaded: {graph}")
53
+ print()
54
+
55
+ # Step 3: Create MapIndex
56
+ print("[3/4] Building spatial index...")
57
+ map_index = acj.MapIndex(graph)
58
+ print(f" MapIndex created: {map_index}")
59
+ print()
60
+
61
+ # Step 4: Create sample points (e.g., crime locations)
62
+ print("[4/4] Assigning sample points to nearest nodes...")
63
+ print()
64
+
65
+ points = pd.DataFrame({
66
+ 'point_id': [0, 1, 2, 3, 4],
67
+ 'x': [10.0, 105.0, 195.0, 290.0, 155.0],
68
+ 'y': [5.0, 10.0, 95.0, 5.0, 90.0],
69
+ 'crime_type': ['robbery', 'assault', 'theft', 'vandalism', 'burglary']
70
+ })
71
+
72
+ print("Sample points to assign:")
73
+ print(points)
74
+ print()
75
+
76
+ # Perform assignment
77
+ print("Performing endpoint assignment...")
78
+ assignments = map_index.assign_to_endpoints(points)
79
+
80
+ print()
81
+ print("Assignment results:")
82
+ print(assignments[['point_id', 'crime_type', 'assigned_node_id', 'distance']])
83
+ print()
84
+
85
+ # Summary statistics
86
+ print("=" * 70)
87
+ print("Summary Statistics")
88
+ print("=" * 70)
89
+ print(f"Total points assigned: {len(assignments)}")
90
+ print(f"Average distance: {assignments['distance'].mean():.2f} meters")
91
+ print(f"Max distance: {assignments['distance'].max():.2f} meters")
92
+ print(f"Min distance: {assignments['distance'].min():.2f} meters")
93
+ print()
94
+
95
+ # Distribution by node
96
+ print("Points per node:")
97
+ node_counts = assignments['assigned_node_id'].value_counts().sort_index()
98
+ for node_id, count in node_counts.items():
99
+ print(f" Node {node_id}: {count} points")
100
+ print()
101
+
102
+ print("=" * 70)
103
+ print("Example completed successfully!")
104
+ print("=" * 70)
105
+
106
+
107
+ if __name__ == "__main__":
108
+ main()