From b4f862a744fa7a9f156157d339cdae86cb326a0e Mon Sep 17 00:00:00 2001 From: Gareth Simons Date: Sat, 8 Jun 2024 21:22:11 +0100 Subject: [PATCH 1/5] itx hwy tags split opposing --- Cargo.lock | 20 +-- pdm.lock | 282 +++++++++++++++++---------------- pyproject.toml | 2 +- pysrc/cityseer/tools/graphs.py | 93 ++++++----- pysrc/cityseer/tools/io.py | 2 +- pysrc/cityseer/tools/mock.py | 2 +- pysrc/cityseer/tools/util.py | 2 +- 7 files changed, 212 insertions(+), 191 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 062f9818..798a6cdc 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -61,15 +61,15 @@ dependencies = [ [[package]] name = "crossbeam-utils" -version = "0.8.19" +version = "0.8.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "248e3bacc7dc6baa3b21e405ee045c3047101a49145e7e9eca583ab4c2ca5345" +checksum = "22ec99545bb0ed0ea7bb9b8e1e9122ea386ff8a48c0922e43f36d45ab09e0e80" [[package]] name = "either" -version = "1.11.0" +version = "1.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a47c1c47d2f5964e29c61246e81db715514cd532db6b5116a25ea3c03d6780a2" +checksum = "3dca9240753cf90908d7e4aac30f630662b02aebaa1b58a3cadabdb23385b58b" [[package]] name = "equivalent" @@ -124,9 +124,9 @@ checksum = "f958d3d68f4167080a18141e10381e7634563984a537f2a49a30fd8e53ac5767" [[package]] name = "libc" -version = "0.2.154" +version = "0.2.155" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ae743338b92ff9146ce83992f766a31066a91a8c84a45e0e9f21e7cf6de6d346" +checksum = "97b3888a4aecf77e811145cadf6eef5901f4782c53886191b2f693f24761847c" [[package]] name = "libm" @@ -227,9 +227,9 @@ checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" [[package]] name = "parking_lot" -version = "0.12.2" +version = "0.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7e4af0ca4f6caed20e900d564c242b8e5d4903fdacf31d3daf527b66fe6f42fb" +checksum = "f1bf18183cf54e8d6059647fc3063646a1801cf30896933ec2311622cc4b9a27" dependencies = [ "lock_api", "parking_lot_core", @@ -266,9 +266,9 @@ checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" [[package]] name = "proc-macro2" -version = "1.0.82" +version = "1.0.85" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ad3d49ab951a01fbaafe34f2ec74122942fe18a3f9814c3268f1bb72042131b" +checksum = "22244ce15aa966053a896d1accb3a6e68469b97c7f33f284b99f0d576879fc23" dependencies = [ "unicode-ident", ] diff --git a/pdm.lock b/pdm.lock index 6b9147fb..7c7a036c 100644 --- a/pdm.lock +++ b/pdm.lock @@ -111,7 +111,7 @@ files = [ [[package]] name = "astroid" -version = "3.1.0" +version = "3.2.2" requires_python = ">=3.8.0" summary = "An abstract syntax tree for Python with inference support." groups = ["dev"] @@ -119,8 +119,8 @@ dependencies = [ "typing-extensions>=4.0.0; python_version < \"3.11\"", ] files = [ - {file = "astroid-3.1.0-py3-none-any.whl", hash = "sha256:951798f922990137ac090c53af473db7ab4e70c770e6d7fae0cec59f74411819"}, - {file = "astroid-3.1.0.tar.gz", hash = "sha256:ac248253bfa4bd924a0de213707e7ebeeb3138abeb48d798784ead1e56d419d4"}, + {file = "astroid-3.2.2-py3-none-any.whl", hash = "sha256:e8a0083b4bb28fcffb6207a3bfc9e5d0a68be951dd7e336d5dcf639c682388c0"}, + {file = "astroid-3.2.2.tar.gz", hash = "sha256:8ead48e31b92b2e217b6c9733a21afafe479d52d6e164dd25fb1a770c7c3cf94"}, ] [[package]] @@ -459,43 +459,43 @@ files = [ [[package]] name = "coverage" -version = "7.5.1" +version = "7.5.3" requires_python = ">=3.8" summary = "Code coverage measurement for Python" groups = ["dev"] files = [ - {file = "coverage-7.5.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:c0884920835a033b78d1c73b6d3bbcda8161a900f38a488829a83982925f6c2e"}, - {file = "coverage-7.5.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:39afcd3d4339329c5f58de48a52f6e4e50f6578dd6099961cf22228feb25f38f"}, - {file = "coverage-7.5.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4a7b0ceee8147444347da6a66be737c9d78f3353b0681715b668b72e79203e4a"}, - {file = "coverage-7.5.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4a9ca3f2fae0088c3c71d743d85404cec8df9be818a005ea065495bedc33da35"}, - {file = "coverage-7.5.1-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5fd215c0c7d7aab005221608a3c2b46f58c0285a819565887ee0b718c052aa4e"}, - {file = "coverage-7.5.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:4bf0655ab60d754491004a5efd7f9cccefcc1081a74c9ef2da4735d6ee4a6223"}, - {file = "coverage-7.5.1-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:61c4bf1ba021817de12b813338c9be9f0ad5b1e781b9b340a6d29fc13e7c1b5e"}, - {file = "coverage-7.5.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:db66fc317a046556a96b453a58eced5024af4582a8dbdc0c23ca4dbc0d5b3146"}, - {file = "coverage-7.5.1-cp310-cp310-win32.whl", hash = "sha256:b016ea6b959d3b9556cb401c55a37547135a587db0115635a443b2ce8f1c7228"}, - {file = "coverage-7.5.1-cp310-cp310-win_amd64.whl", hash = "sha256:df4e745a81c110e7446b1cc8131bf986157770fa405fe90e15e850aaf7619bc8"}, - {file = "coverage-7.5.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:796a79f63eca8814ca3317a1ea443645c9ff0d18b188de470ed7ccd45ae79428"}, - {file = "coverage-7.5.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:4fc84a37bfd98db31beae3c2748811a3fa72bf2007ff7902f68746d9757f3746"}, - {file = "coverage-7.5.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6175d1a0559986c6ee3f7fccfc4a90ecd12ba0a383dcc2da30c2b9918d67d8a3"}, - {file = "coverage-7.5.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1fc81d5878cd6274ce971e0a3a18a8803c3fe25457165314271cf78e3aae3aa2"}, - {file = "coverage-7.5.1-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:556cf1a7cbc8028cb60e1ff0be806be2eded2daf8129b8811c63e2b9a6c43bca"}, - {file = "coverage-7.5.1-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:9981706d300c18d8b220995ad22627647be11a4276721c10911e0e9fa44c83e8"}, - {file = "coverage-7.5.1-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:d7fed867ee50edf1a0b4a11e8e5d0895150e572af1cd6d315d557758bfa9c057"}, - {file = "coverage-7.5.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:ef48e2707fb320c8f139424a596f5b69955a85b178f15af261bab871873bb987"}, - {file = "coverage-7.5.1-cp311-cp311-win32.whl", hash = "sha256:9314d5678dcc665330df5b69c1e726a0e49b27df0461c08ca12674bcc19ef136"}, - {file = "coverage-7.5.1-cp311-cp311-win_amd64.whl", hash = "sha256:5fa567e99765fe98f4e7d7394ce623e794d7cabb170f2ca2ac5a4174437e90dd"}, - {file = "coverage-7.5.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:b6cf3764c030e5338e7f61f95bd21147963cf6aa16e09d2f74f1fa52013c1206"}, - {file = "coverage-7.5.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:2ec92012fefebee89a6b9c79bc39051a6cb3891d562b9270ab10ecfdadbc0c34"}, - {file = "coverage-7.5.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:16db7f26000a07efcf6aea00316f6ac57e7d9a96501e990a36f40c965ec7a95d"}, - {file = "coverage-7.5.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:beccf7b8a10b09c4ae543582c1319c6df47d78fd732f854ac68d518ee1fb97fa"}, - {file = "coverage-7.5.1-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8748731ad392d736cc9ccac03c9845b13bb07d020a33423fa5b3a36521ac6e4e"}, - {file = "coverage-7.5.1-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:7352b9161b33fd0b643ccd1f21f3a3908daaddf414f1c6cb9d3a2fd618bf2572"}, - {file = "coverage-7.5.1-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:7a588d39e0925f6a2bff87154752481273cdb1736270642aeb3635cb9b4cad07"}, - {file = "coverage-7.5.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:68f962d9b72ce69ea8621f57551b2fa9c70509af757ee3b8105d4f51b92b41a7"}, - {file = "coverage-7.5.1-cp312-cp312-win32.whl", hash = "sha256:f152cbf5b88aaeb836127d920dd0f5e7edff5a66f10c079157306c4343d86c19"}, - {file = "coverage-7.5.1-cp312-cp312-win_amd64.whl", hash = "sha256:5a5740d1fb60ddf268a3811bcd353de34eb56dc24e8f52a7f05ee513b2d4f596"}, - {file = "coverage-7.5.1-pp38.pp39.pp310-none-any.whl", hash = "sha256:6537e7c10cc47c595828b8a8be04c72144725c383c4702703ff4e42e44577312"}, - {file = "coverage-7.5.1.tar.gz", hash = "sha256:54de9ef3a9da981f7af93eafde4ede199e0846cd819eb27c88e2b712aae9708c"}, + {file = "coverage-7.5.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:a6519d917abb15e12380406d721e37613e2a67d166f9fb7e5a8ce0375744cd45"}, + {file = "coverage-7.5.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:aea7da970f1feccf48be7335f8b2ca64baf9b589d79e05b9397a06696ce1a1ec"}, + {file = "coverage-7.5.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:923b7b1c717bd0f0f92d862d1ff51d9b2b55dbbd133e05680204465f454bb286"}, + {file = "coverage-7.5.3-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:62bda40da1e68898186f274f832ef3e759ce929da9a9fd9fcf265956de269dbc"}, + {file = "coverage-7.5.3-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d8b7339180d00de83e930358223c617cc343dd08e1aa5ec7b06c3a121aec4e1d"}, + {file = "coverage-7.5.3-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:25a5caf742c6195e08002d3b6c2dd6947e50efc5fc2c2205f61ecb47592d2d83"}, + {file = "coverage-7.5.3-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:05ac5f60faa0c704c0f7e6a5cbfd6f02101ed05e0aee4d2822637a9e672c998d"}, + {file = "coverage-7.5.3-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:239a4e75e09c2b12ea478d28815acf83334d32e722e7433471fbf641c606344c"}, + {file = "coverage-7.5.3-cp310-cp310-win32.whl", hash = "sha256:a5812840d1d00eafae6585aba38021f90a705a25b8216ec7f66aebe5b619fb84"}, + {file = "coverage-7.5.3-cp310-cp310-win_amd64.whl", hash = "sha256:33ca90a0eb29225f195e30684ba4a6db05dbef03c2ccd50b9077714c48153cac"}, + {file = "coverage-7.5.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:f81bc26d609bf0fbc622c7122ba6307993c83c795d2d6f6f6fd8c000a770d974"}, + {file = "coverage-7.5.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:7cec2af81f9e7569280822be68bd57e51b86d42e59ea30d10ebdbb22d2cb7232"}, + {file = "coverage-7.5.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:55f689f846661e3f26efa535071775d0483388a1ccfab899df72924805e9e7cd"}, + {file = "coverage-7.5.3-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:50084d3516aa263791198913a17354bd1dc627d3c1639209640b9cac3fef5807"}, + {file = "coverage-7.5.3-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:341dd8f61c26337c37988345ca5c8ccabeff33093a26953a1ac72e7d0103c4fb"}, + {file = "coverage-7.5.3-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:ab0b028165eea880af12f66086694768f2c3139b2c31ad5e032c8edbafca6ffc"}, + {file = "coverage-7.5.3-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:5bc5a8c87714b0c67cfeb4c7caa82b2d71e8864d1a46aa990b5588fa953673b8"}, + {file = "coverage-7.5.3-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:38a3b98dae8a7c9057bd91fbf3415c05e700a5114c5f1b5b0ea5f8f429ba6614"}, + {file = "coverage-7.5.3-cp311-cp311-win32.whl", hash = "sha256:fcf7d1d6f5da887ca04302db8e0e0cf56ce9a5e05f202720e49b3e8157ddb9a9"}, + {file = "coverage-7.5.3-cp311-cp311-win_amd64.whl", hash = "sha256:8c836309931839cca658a78a888dab9676b5c988d0dd34ca247f5f3e679f4e7a"}, + {file = "coverage-7.5.3-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:296a7d9bbc598e8744c00f7a6cecf1da9b30ae9ad51c566291ff1314e6cbbed8"}, + {file = "coverage-7.5.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:34d6d21d8795a97b14d503dcaf74226ae51eb1f2bd41015d3ef332a24d0a17b3"}, + {file = "coverage-7.5.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8e317953bb4c074c06c798a11dbdd2cf9979dbcaa8ccc0fa4701d80042d4ebf1"}, + {file = "coverage-7.5.3-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:705f3d7c2b098c40f5b81790a5fedb274113373d4d1a69e65f8b68b0cc26f6db"}, + {file = "coverage-7.5.3-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b1196e13c45e327d6cd0b6e471530a1882f1017eb83c6229fc613cd1a11b53cd"}, + {file = "coverage-7.5.3-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:015eddc5ccd5364dcb902eaecf9515636806fa1e0d5bef5769d06d0f31b54523"}, + {file = "coverage-7.5.3-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:fd27d8b49e574e50caa65196d908f80e4dff64d7e592d0c59788b45aad7e8b35"}, + {file = "coverage-7.5.3-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:33fc65740267222fc02975c061eb7167185fef4cc8f2770267ee8bf7d6a42f84"}, + {file = "coverage-7.5.3-cp312-cp312-win32.whl", hash = "sha256:7b2a19e13dfb5c8e145c7a6ea959485ee8e2204699903c88c7d25283584bfc08"}, + {file = "coverage-7.5.3-cp312-cp312-win_amd64.whl", hash = "sha256:0bbddc54bbacfc09b3edaec644d4ac90c08ee8ed4844b0f86227dcda2d428fcb"}, + {file = "coverage-7.5.3-pp38.pp39.pp310-none-any.whl", hash = "sha256:3538d8fb1ee9bdd2e2692b3b18c22bb1c19ffbefd06880f5ac496e42d7bb3884"}, + {file = "coverage-7.5.3.tar.gz", hash = "sha256:04aefca5190d1dc7a53a4c1a5a7f8568811306d7a8ee231c42fb69215571944f"}, ] [[package]] @@ -1387,7 +1387,7 @@ files = [ [[package]] name = "matplotlib" -version = "3.8.4" +version = "3.9.0" requires_python = ">=3.9" summary = "Python plotting package" groups = ["default"] @@ -1396,35 +1396,36 @@ dependencies = [ "cycler>=0.10", "fonttools>=4.22.0", "kiwisolver>=1.3.1", - "numpy>=1.21", + "numpy>=1.23", "packaging>=20.0", "pillow>=8", "pyparsing>=2.3.1", "python-dateutil>=2.7", ] files = [ - {file = "matplotlib-3.8.4-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:abc9d838f93583650c35eca41cfcec65b2e7cb50fd486da6f0c49b5e1ed23014"}, - {file = "matplotlib-3.8.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:8f65c9f002d281a6e904976007b2d46a1ee2bcea3a68a8c12dda24709ddc9106"}, - {file = "matplotlib-3.8.4-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ce1edd9f5383b504dbc26eeea404ed0a00656c526638129028b758fd43fc5f10"}, - {file = "matplotlib-3.8.4-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ecd79298550cba13a43c340581a3ec9c707bd895a6a061a78fa2524660482fc0"}, - {file = "matplotlib-3.8.4-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:90df07db7b599fe7035d2f74ab7e438b656528c68ba6bb59b7dc46af39ee48ef"}, - {file = "matplotlib-3.8.4-cp310-cp310-win_amd64.whl", hash = "sha256:ac24233e8f2939ac4fd2919eed1e9c0871eac8057666070e94cbf0b33dd9c338"}, - {file = "matplotlib-3.8.4-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:72f9322712e4562e792b2961971891b9fbbb0e525011e09ea0d1f416c4645661"}, - {file = "matplotlib-3.8.4-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:232ce322bfd020a434caaffbd9a95333f7c2491e59cfc014041d95e38ab90d1c"}, - {file = "matplotlib-3.8.4-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6addbd5b488aedb7f9bc19f91cd87ea476206f45d7116fcfe3d31416702a82fa"}, - {file = "matplotlib-3.8.4-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cc4ccdc64e3039fc303defd119658148f2349239871db72cd74e2eeaa9b80b71"}, - {file = "matplotlib-3.8.4-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:b7a2a253d3b36d90c8993b4620183b55665a429da8357a4f621e78cd48b2b30b"}, - {file = "matplotlib-3.8.4-cp311-cp311-win_amd64.whl", hash = "sha256:8080d5081a86e690d7688ffa542532e87f224c38a6ed71f8fbed34dd1d9fedae"}, - {file = "matplotlib-3.8.4-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:6485ac1f2e84676cff22e693eaa4fbed50ef5dc37173ce1f023daef4687df616"}, - {file = "matplotlib-3.8.4-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:c89ee9314ef48c72fe92ce55c4e95f2f39d70208f9f1d9db4e64079420d8d732"}, - {file = "matplotlib-3.8.4-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:50bac6e4d77e4262c4340d7a985c30912054745ec99756ce213bfbc3cb3808eb"}, - {file = "matplotlib-3.8.4-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f51c4c869d4b60d769f7b4406eec39596648d9d70246428745a681c327a8ad30"}, - {file = "matplotlib-3.8.4-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:b12ba985837e4899b762b81f5b2845bd1a28f4fdd1a126d9ace64e9c4eb2fb25"}, - {file = "matplotlib-3.8.4-cp312-cp312-win_amd64.whl", hash = "sha256:7a6769f58ce51791b4cb8b4d7642489df347697cd3e23d88266aaaee93b41d9a"}, - {file = "matplotlib-3.8.4-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:c7064120a59ce6f64103c9cefba8ffe6fba87f2c61d67c401186423c9a20fd35"}, - {file = "matplotlib-3.8.4-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a0e47eda4eb2614300fc7bb4657fced3e83d6334d03da2173b09e447418d499f"}, - {file = "matplotlib-3.8.4-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:493e9f6aa5819156b58fce42b296ea31969f2aab71c5b680b4ea7a3cb5c07d94"}, - {file = "matplotlib-3.8.4.tar.gz", hash = "sha256:8aac397d5e9ec158960e31c381c5ffc52ddd52bd9a47717e2a694038167dffea"}, + {file = "matplotlib-3.9.0-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:2bcee1dffaf60fe7656183ac2190bd630842ff87b3153afb3e384d966b57fe56"}, + {file = "matplotlib-3.9.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:3f988bafb0fa39d1074ddd5bacd958c853e11def40800c5824556eb630f94d3b"}, + {file = "matplotlib-3.9.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fe428e191ea016bb278758c8ee82a8129c51d81d8c4bc0846c09e7e8e9057241"}, + {file = "matplotlib-3.9.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:eaf3978060a106fab40c328778b148f590e27f6fa3cd15a19d6892575bce387d"}, + {file = "matplotlib-3.9.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:2e7f03e5cbbfacdd48c8ea394d365d91ee8f3cae7e6ec611409927b5ed997ee4"}, + {file = "matplotlib-3.9.0-cp310-cp310-win_amd64.whl", hash = "sha256:13beb4840317d45ffd4183a778685e215939be7b08616f431c7795276e067463"}, + {file = "matplotlib-3.9.0-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:063af8587fceeac13b0936c42a2b6c732c2ab1c98d38abc3337e430e1ff75e38"}, + {file = "matplotlib-3.9.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:9a2fa6d899e17ddca6d6526cf6e7ba677738bf2a6a9590d702c277204a7c6152"}, + {file = "matplotlib-3.9.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:550cdda3adbd596078cca7d13ed50b77879104e2e46392dcd7c75259d8f00e85"}, + {file = "matplotlib-3.9.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:76cce0f31b351e3551d1f3779420cf8f6ec0d4a8cf9c0237a3b549fd28eb4abb"}, + {file = "matplotlib-3.9.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:c53aeb514ccbbcbab55a27f912d79ea30ab21ee0531ee2c09f13800efb272674"}, + {file = "matplotlib-3.9.0-cp311-cp311-win_amd64.whl", hash = "sha256:a5be985db2596d761cdf0c2eaf52396f26e6a64ab46bd8cd810c48972349d1be"}, + {file = "matplotlib-3.9.0-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:c79f3a585f1368da6049318bdf1f85568d8d04b2e89fc24b7e02cc9b62017382"}, + {file = "matplotlib-3.9.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:bdd1ecbe268eb3e7653e04f451635f0fb0f77f07fd070242b44c076c9106da84"}, + {file = "matplotlib-3.9.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d38e85a1a6d732f645f1403ce5e6727fd9418cd4574521d5803d3d94911038e5"}, + {file = "matplotlib-3.9.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0a490715b3b9984fa609116481b22178348c1a220a4499cda79132000a79b4db"}, + {file = "matplotlib-3.9.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:8146ce83cbc5dc71c223a74a1996d446cd35cfb6a04b683e1446b7e6c73603b7"}, + {file = "matplotlib-3.9.0-cp312-cp312-win_amd64.whl", hash = "sha256:d91a4ffc587bacf5c4ce4ecfe4bcd23a4b675e76315f2866e588686cc97fccdf"}, + {file = "matplotlib-3.9.0-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:bd4f2831168afac55b881db82a7730992aa41c4f007f1913465fb182d6fb20c0"}, + {file = "matplotlib-3.9.0-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:290d304e59be2b33ef5c2d768d0237f5bd132986bdcc66f80bc9bcc300066a03"}, + {file = "matplotlib-3.9.0-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7ff2e239c26be4f24bfa45860c20ffccd118d270c5b5d081fa4ea409b5469fcd"}, + {file = "matplotlib-3.9.0-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:af4001b7cae70f7eaacfb063db605280058246de590fa7874f00f62259f2df7e"}, + {file = "matplotlib-3.9.0.tar.gz", hash = "sha256:e6d29ea6c19e34b30fb7d88b7081f869a03014f66fe06d62cc77d5a6ea88ed7a"}, ] [[package]] @@ -1443,27 +1444,27 @@ files = [ [[package]] name = "maturin" -version = "1.5.1" +version = "1.6.0" requires_python = ">=3.7" -summary = "Build and publish crates with pyo3, rust-cpython and cffi bindings as well as rust binaries as python packages" +summary = "Build and publish crates with pyo3, cffi and uniffi bindings as well as rust binaries as python packages" groups = ["dev"] dependencies = [ "tomli>=1.1.0; python_version < \"3.11\"", ] files = [ - {file = "maturin-1.5.1-py3-none-linux_armv6l.whl", hash = "sha256:589e9b7024007e130b136ba6f1c2c8393a87e42cf968d12852913ab1e3c69ed3"}, - {file = "maturin-1.5.1-py3-none-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:a1abda07093b3c8ef897626166c02ed64e3e446c48460b28efb51833abf89cbb"}, - {file = "maturin-1.5.1-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:48a1fbbdc2514525f27d6d339ab97b098ede28759f8593d110c89cc07bbe40ed"}, - {file = "maturin-1.5.1-py3-none-manylinux_2_12_i686.manylinux2010_i686.musllinux_1_1_i686.whl", hash = "sha256:96d96b1fa3a165db9ca539f764d31da8ebc92e31ca3a1dd6ccd50008d222bd96"}, - {file = "maturin-1.5.1-py3-none-manylinux_2_12_x86_64.manylinux2010_x86_64.musllinux_1_1_x86_64.whl", hash = "sha256:786bf36a98c4e27cbebb1dc8e432c1bcbbb59e7a9719592cbb89e46a0ccd5bcc"}, - {file = "maturin-1.5.1-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.musllinux_1_1_aarch64.whl", hash = "sha256:d821b37da759884ad09cfee4cd9deac10f4132744cc66e4d9190a1972233bc83"}, - {file = "maturin-1.5.1-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.musllinux_1_1_armv7l.whl", hash = "sha256:62133bf690555bbc8cc6b1c18a0c57b0ab2b4d68d3fcd320eb16df941563fe06"}, - {file = "maturin-1.5.1-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.musllinux_1_1_ppc64le.whl", hash = "sha256:6bff165252b1fcc887679ddf7b71b5cc024327ba96ea893133be38c0ed38f163"}, - {file = "maturin-1.5.1-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2c42a95466ffc3de0a3940cd20c57cf0c44fe5ea679375d73422afbb00236c64"}, - {file = "maturin-1.5.1-py3-none-win32.whl", hash = "sha256:d09538b4aa0da4b59fd47cb429003b45bfd5d801714adf1db2511bf8bdea532f"}, - {file = "maturin-1.5.1-py3-none-win_amd64.whl", hash = "sha256:a3db9054222ac79275e082b21cfd234b8e036714a4ff227a0a28f6a3ffa3744d"}, - {file = "maturin-1.5.1-py3-none-win_arm64.whl", hash = "sha256:acf528e51413f6ae489473d64116d8c83f140386349004949d29137c16a82193"}, - {file = "maturin-1.5.1.tar.gz", hash = "sha256:3dd834ece80edb866af18cbd4635e0ecac40139c726428d5f1849ae154b26dca"}, + {file = "maturin-1.6.0-py3-none-linux_armv6l.whl", hash = "sha256:d8620970bd0b6a0acb99dbd0b1c2ebb7a69909d25f6023bdff9635a39001aa51"}, + {file = "maturin-1.6.0-py3-none-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:bd85edcb1b8e2bcddc1b7d16ce58ce00a66aa80c422745c8ad9e132ac40d4b48"}, + {file = "maturin-1.6.0-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:337899784955934dd67b30497d1dd5fab22da89f60bb079dbaf2eaa446b97a10"}, + {file = "maturin-1.6.0-py3-none-manylinux_2_12_i686.manylinux2010_i686.musllinux_1_1_i686.whl", hash = "sha256:dbbbf25dc3c207b0a7bd4f3aea1df33d4f22b8508592796a6f36f4d8ed216db0"}, + {file = "maturin-1.6.0-py3-none-manylinux_2_12_x86_64.manylinux2010_x86_64.musllinux_1_1_x86_64.whl", hash = "sha256:d92b045e90ed919a8a2520dda64e3f384e5e746ea51e1498cc6ac3e9e5c76054"}, + {file = "maturin-1.6.0-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.musllinux_1_1_aarch64.whl", hash = "sha256:d67ca8dc7f3b2314bd3bf83c4de52645e220ee312fd526e53acc6a735f233fad"}, + {file = "maturin-1.6.0-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.musllinux_1_1_armv7l.whl", hash = "sha256:aa4eb7dca7d246b466392f21016f67ff09a9aff2305fa714ca25a2344e4639e7"}, + {file = "maturin-1.6.0-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.musllinux_1_1_ppc64le.whl", hash = "sha256:16ef860df20028618b5a064da06b02c1c47acba064a4d25aaf84662a459ec599"}, + {file = "maturin-1.6.0-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4e931c92037128ade49cd26dd040d9c46ad8092d8170cc44f5c3a0b4a052d576"}, + {file = "maturin-1.6.0-py3-none-win32.whl", hash = "sha256:c87d1a7596c42b589099adb831343a56e02373588366e4cede96cbdf8bd68f9d"}, + {file = "maturin-1.6.0-py3-none-win_amd64.whl", hash = "sha256:a2a2436628c36d98dabd79b52256df7e12fc4fd1b122984d9373fdf918fd4609"}, + {file = "maturin-1.6.0-py3-none-win_arm64.whl", hash = "sha256:50133965e52d8b5b969381fee3fde111ae2383905cdaba7650f256e08ccddcd4"}, + {file = "maturin-1.6.0.tar.gz", hash = "sha256:b955025c24c8babc808db49e0ff90db8b4b1320dcc16b14eb26132841737230d"}, ] [[package]] @@ -1768,17 +1769,18 @@ files = [ [[package]] name = "pandas-stubs" -version = "2.2.1.240316" +version = "2.2.2.240603" requires_python = ">=3.9" summary = "Type annotations for pandas" groups = ["dev"] dependencies = [ - "numpy>=1.26.0; python_version < \"3.13\"", + "numpy>=1.23.5; python_version >= \"3.9\" and python_version < \"3.12\"", + "numpy>=1.26.0; python_version >= \"3.12\" and python_version < \"3.13\"", "types-pytz>=2022.1.1", ] files = [ - {file = "pandas_stubs-2.2.1.240316-py3-none-any.whl", hash = "sha256:0126a26451a37cb893ea62357ca87ba3d181bd999ec8ba2ca5602e20207d6682"}, - {file = "pandas_stubs-2.2.1.240316.tar.gz", hash = "sha256:236a4f812fb6b1922e9607ff09e427f6d8540c421c9e5a40e3e4ddf7adac7f05"}, + {file = "pandas_stubs-2.2.2.240603-py3-none-any.whl", hash = "sha256:e08ce7f602a4da2bff5a67475ba881c39f2a4d4f7fccc1cba57c6f35a379c6c0"}, + {file = "pandas_stubs-2.2.2.240603.tar.gz", hash = "sha256:2dcc86e8fa6ea41535a4561c1f08b3942ba5267b464eff2e99caeee66f9e4cd1"}, ] [[package]] @@ -1816,7 +1818,7 @@ files = [ [[package]] name = "pdoc" -version = "14.4.0" +version = "14.5.0" requires_python = ">=3.8" summary = "API Documentation for Python Projects" groups = ["dev"] @@ -1826,8 +1828,8 @@ dependencies = [ "pygments>=2.12.0", ] files = [ - {file = "pdoc-14.4.0-py3-none-any.whl", hash = "sha256:6ea4fe07620b1f7601e2708a307a257636ec206e20b5611640b30f2e3cab47d6"}, - {file = "pdoc-14.4.0.tar.gz", hash = "sha256:c92edc425429ccbe287ace2a027953c24f13de53eab484c1a6d31ca72dd2fda9"}, + {file = "pdoc-14.5.0-py3-none-any.whl", hash = "sha256:9a8a84e19662610c0620fbe9f2e4174e3b090f8b601ed46348786ebb7517c508"}, + {file = "pdoc-14.5.0.tar.gz", hash = "sha256:79f534dc8a6494638dd6056b78e17a654df7ed34cc92646553ce3a7ba5a4fa4a"}, ] [[package]] @@ -1998,7 +2000,7 @@ files = [ [[package]] name = "pyarrow" -version = "16.0.0" +version = "16.1.0" requires_python = ">=3.8" summary = "Python library for Apache Arrow" groups = ["default"] @@ -2006,28 +2008,28 @@ dependencies = [ "numpy>=1.16.6", ] files = [ - {file = "pyarrow-16.0.0-cp310-cp310-macosx_10_15_x86_64.whl", hash = "sha256:22a1fdb1254e5095d629e29cd1ea98ed04b4bbfd8e42cc670a6b639ccc208b60"}, - {file = "pyarrow-16.0.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:574a00260a4ed9d118a14770edbd440b848fcae5a3024128be9d0274dbcaf858"}, - {file = "pyarrow-16.0.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c0815d0ddb733b8c1b53a05827a91f1b8bde6240f3b20bf9ba5d650eb9b89cdf"}, - {file = "pyarrow-16.0.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:df0080339387b5d30de31e0a149c0c11a827a10c82f0c67d9afae3981d1aabb7"}, - {file = "pyarrow-16.0.0-cp310-cp310-manylinux_2_28_aarch64.whl", hash = "sha256:edf38cce0bf0dcf726e074159c60516447e4474904c0033f018c1f33d7dac6c5"}, - {file = "pyarrow-16.0.0-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:91d28f9a40f1264eab2af7905a4d95320ac2f287891e9c8b0035f264fe3c3a4b"}, - {file = "pyarrow-16.0.0-cp310-cp310-win_amd64.whl", hash = "sha256:99af421ee451a78884d7faea23816c429e263bd3618b22d38e7992c9ce2a7ad9"}, - {file = "pyarrow-16.0.0-cp311-cp311-macosx_10_15_x86_64.whl", hash = "sha256:d22d0941e6c7bafddf5f4c0662e46f2075850f1c044bf1a03150dd9e189427ce"}, - {file = "pyarrow-16.0.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:266ddb7e823f03733c15adc8b5078db2df6980f9aa93d6bb57ece615df4e0ba7"}, - {file = "pyarrow-16.0.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5cc23090224b6594f5a92d26ad47465af47c1d9c079dd4a0061ae39551889efe"}, - {file = "pyarrow-16.0.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:56850a0afe9ef37249d5387355449c0f94d12ff7994af88f16803a26d38f2016"}, - {file = "pyarrow-16.0.0-cp311-cp311-manylinux_2_28_aarch64.whl", hash = "sha256:705db70d3e2293c2f6f8e84874b5b775f690465798f66e94bb2c07bab0a6bb55"}, - {file = "pyarrow-16.0.0-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:5448564754c154997bc09e95a44b81b9e31ae918a86c0fcb35c4aa4922756f55"}, - {file = "pyarrow-16.0.0-cp311-cp311-win_amd64.whl", hash = "sha256:729f7b262aa620c9df8b9967db96c1575e4cfc8c25d078a06968e527b8d6ec05"}, - {file = "pyarrow-16.0.0-cp312-cp312-macosx_10_15_x86_64.whl", hash = "sha256:fb8065dbc0d051bf2ae2453af0484d99a43135cadabacf0af588a3be81fbbb9b"}, - {file = "pyarrow-16.0.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:20ce707d9aa390593ea93218b19d0eadab56390311cb87aad32c9a869b0e958c"}, - {file = "pyarrow-16.0.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5823275c8addbbb50cd4e6a6839952682a33255b447277e37a6f518d6972f4e1"}, - {file = "pyarrow-16.0.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1ab8b9050752b16a8b53fcd9853bf07d8daf19093533e990085168f40c64d978"}, - {file = "pyarrow-16.0.0-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:42e56557bc7c5c10d3e42c3b32f6cff649a29d637e8f4e8b311d334cc4326730"}, - {file = "pyarrow-16.0.0-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:2a7abdee4a4a7cfa239e2e8d721224c4b34ffe69a0ca7981354fe03c1328789b"}, - {file = "pyarrow-16.0.0-cp312-cp312-win_amd64.whl", hash = "sha256:ef2f309b68396bcc5a354106741d333494d6a0d3e1951271849787109f0229a6"}, - {file = "pyarrow-16.0.0.tar.gz", hash = "sha256:59bb1f1edbbf4114c72415f039f1359f1a57d166a331c3229788ccbfbb31689a"}, + {file = "pyarrow-16.1.0-cp310-cp310-macosx_10_15_x86_64.whl", hash = "sha256:17e23b9a65a70cc733d8b738baa6ad3722298fa0c81d88f63ff94bf25eaa77b9"}, + {file = "pyarrow-16.1.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:4740cc41e2ba5d641071d0ab5e9ef9b5e6e8c7611351a5cb7c1d175eaf43674a"}, + {file = "pyarrow-16.1.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:98100e0268d04e0eec47b73f20b39c45b4006f3c4233719c3848aa27a03c1aef"}, + {file = "pyarrow-16.1.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f68f409e7b283c085f2da014f9ef81e885d90dcd733bd648cfba3ef265961848"}, + {file = "pyarrow-16.1.0-cp310-cp310-manylinux_2_28_aarch64.whl", hash = "sha256:a8914cd176f448e09746037b0c6b3a9d7688cef451ec5735094055116857580c"}, + {file = "pyarrow-16.1.0-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:48be160782c0556156d91adbdd5a4a7e719f8d407cb46ae3bb4eaee09b3111bd"}, + {file = "pyarrow-16.1.0-cp310-cp310-win_amd64.whl", hash = "sha256:9cf389d444b0f41d9fe1444b70650fea31e9d52cfcb5f818b7888b91b586efff"}, + {file = "pyarrow-16.1.0-cp311-cp311-macosx_10_15_x86_64.whl", hash = "sha256:d0ebea336b535b37eee9eee31761813086d33ed06de9ab6fc6aaa0bace7b250c"}, + {file = "pyarrow-16.1.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:2e73cfc4a99e796727919c5541c65bb88b973377501e39b9842ea71401ca6c1c"}, + {file = "pyarrow-16.1.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bf9251264247ecfe93e5f5a0cd43b8ae834f1e61d1abca22da55b20c788417f6"}, + {file = "pyarrow-16.1.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ddf5aace92d520d3d2a20031d8b0ec27b4395cab9f74e07cc95edf42a5cc0147"}, + {file = "pyarrow-16.1.0-cp311-cp311-manylinux_2_28_aarch64.whl", hash = "sha256:25233642583bf658f629eb230b9bb79d9af4d9f9229890b3c878699c82f7d11e"}, + {file = "pyarrow-16.1.0-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:a33a64576fddfbec0a44112eaf844c20853647ca833e9a647bfae0582b2ff94b"}, + {file = "pyarrow-16.1.0-cp311-cp311-win_amd64.whl", hash = "sha256:185d121b50836379fe012753cf15c4ba9638bda9645183ab36246923875f8d1b"}, + {file = "pyarrow-16.1.0-cp312-cp312-macosx_10_15_x86_64.whl", hash = "sha256:2e51ca1d6ed7f2e9d5c3c83decf27b0d17bb207a7dea986e8dc3e24f80ff7d6f"}, + {file = "pyarrow-16.1.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:06ebccb6f8cb7357de85f60d5da50e83507954af617d7b05f48af1621d331c9a"}, + {file = "pyarrow-16.1.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b04707f1979815f5e49824ce52d1dceb46e2f12909a48a6a753fe7cafbc44a0c"}, + {file = "pyarrow-16.1.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0d32000693deff8dc5df444b032b5985a48592c0697cb6e3071a5d59888714e2"}, + {file = "pyarrow-16.1.0-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:8785bb10d5d6fd5e15d718ee1d1f914fe768bf8b4d1e5e9bf253de8a26cb1628"}, + {file = "pyarrow-16.1.0-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:e1369af39587b794873b8a307cc6623a3b1194e69399af0efd05bb202195a5a7"}, + {file = "pyarrow-16.1.0-cp312-cp312-win_amd64.whl", hash = "sha256:febde33305f1498f6df85e8020bca496d0e9ebf2093bab9e0f65e2b4ae2b3444"}, + {file = "pyarrow-16.1.0.tar.gz", hash = "sha256:15fbb22ea96d11f0b5768504a3f961edab25eaf4197c341720c4a387f6c60315"}, ] [[package]] @@ -2068,12 +2070,12 @@ files = [ [[package]] name = "pylint" -version = "3.1.0" +version = "3.2.3" requires_python = ">=3.8.0" summary = "python code static checker" groups = ["dev"] dependencies = [ - "astroid<=3.2.0-dev0,>=3.1.0", + "astroid<=3.3.0-dev0,>=3.2.2", "colorama>=0.4.5; sys_platform == \"win32\"", "dill>=0.2; python_version < \"3.11\"", "dill>=0.3.6; python_version >= \"3.11\"", @@ -2085,8 +2087,8 @@ dependencies = [ "tomlkit>=0.10.1", ] files = [ - {file = "pylint-3.1.0-py3-none-any.whl", hash = "sha256:507a5b60953874766d8a366e8e8c7af63e058b26345cfcb5f91f89d987fd6b74"}, - {file = "pylint-3.1.0.tar.gz", hash = "sha256:6a69beb4a6f63debebaab0a3477ecd0f559aa726af4954fc948c51f7a2549e23"}, + {file = "pylint-3.2.3-py3-none-any.whl", hash = "sha256:b3d7d2708a3e04b4679e02d99e72329a8b7ee8afb8d04110682278781f889fa8"}, + {file = "pylint-3.2.3.tar.gz", hash = "sha256:02f6c562b215582386068d52a30f520d84fdbcf2a95fc7e855b816060d048b60"}, ] [[package]] @@ -2135,7 +2137,7 @@ files = [ [[package]] name = "pyright" -version = "1.1.362" +version = "1.1.366" requires_python = ">=3.7" summary = "Command line wrapper for pyright" groups = ["dev"] @@ -2143,13 +2145,13 @@ dependencies = [ "nodeenv>=1.6.0", ] files = [ - {file = "pyright-1.1.362-py3-none-any.whl", hash = "sha256:969957cff45154d8a45a4ab1dae5bdc8223d8bd3c64654fa608ab3194dfff319"}, - {file = "pyright-1.1.362.tar.gz", hash = "sha256:6a477e448d4a07a6a0eab58b2a15a1bbed031eb3169fa809edee79cca168d83a"}, + {file = "pyright-1.1.366-py3-none-any.whl", hash = "sha256:c09e73ccc894976bcd6d6a5784aa84d724dbd9ceb7b873b39d475ca61c2de071"}, + {file = "pyright-1.1.366.tar.gz", hash = "sha256:10e4d60be411f6d960cd39b0b58bf2ff76f2c83b9aeb102ffa9d9fda2e1303cb"}, ] [[package]] name = "pytest" -version = "8.2.0" +version = "8.2.2" requires_python = ">=3.8" summary = "pytest: simple powerful testing with Python" groups = ["dev"] @@ -2162,8 +2164,8 @@ dependencies = [ "tomli>=1; python_version < \"3.11\"", ] files = [ - {file = "pytest-8.2.0-py3-none-any.whl", hash = "sha256:1733f0620f6cda4095bbf0d9ff8022486e91892245bb9e7d5542c018f612f233"}, - {file = "pytest-8.2.0.tar.gz", hash = "sha256:d507d4482197eac0ba2bae2e9babf0672eb333017bcedaa5fb1a3d42c1174b3f"}, + {file = "pytest-8.2.2-py3-none-any.whl", hash = "sha256:c434598117762e2bd304e526244f67bf66bbd7b5d6cf22138be51ff661980343"}, + {file = "pytest-8.2.2.tar.gz", hash = "sha256:de4bb8104e201939ccdc688b27a89a7be2079b22e2bd2b07f806b6ba71117977"}, ] [[package]] @@ -2434,8 +2436,8 @@ files = [ [[package]] name = "requests" -version = "2.31.0" -requires_python = ">=3.7" +version = "2.32.3" +requires_python = ">=3.8" summary = "Python HTTP for Humans." groups = ["default", "dev"] dependencies = [ @@ -2445,8 +2447,8 @@ dependencies = [ "urllib3<3,>=1.21.1", ] files = [ - {file = "requests-2.31.0-py3-none-any.whl", hash = "sha256:58cd2187c01e70e6e26505bca751777aa9f2ee0b7f4300988b709f44e013003f"}, - {file = "requests-2.31.0.tar.gz", hash = "sha256:942c5a758f98d790eaed1a29cb6eefc7ffb0d1cf7af05c3d2791656dbd6ad1e1"}, + {file = "requests-2.32.3-py3-none-any.whl", hash = "sha256:70761cfe03c773ceb22aa2f671b4757976145175cdfca038c02654d061d6dcc6"}, + {file = "requests-2.32.3.tar.gz", hash = "sha256:55365417734eb18255590a9ff9eb97e9e1da868d4ccd6402399eaf68af20a760"}, ] [[package]] @@ -2558,7 +2560,7 @@ files = [ [[package]] name = "scikit-learn" -version = "1.4.2" +version = "1.5.0" requires_python = ">=3.9" summary = "A set of python modules for machine learning and data mining" groups = ["default", "dev"] @@ -2566,25 +2568,25 @@ dependencies = [ "joblib>=1.2.0", "numpy>=1.19.5", "scipy>=1.6.0", - "threadpoolctl>=2.0.0", + "threadpoolctl>=3.1.0", ] files = [ - {file = "scikit-learn-1.4.2.tar.gz", hash = "sha256:daa1c471d95bad080c6e44b4946c9390a4842adc3082572c20e4f8884e39e959"}, - {file = "scikit_learn-1.4.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:8539a41b3d6d1af82eb629f9c57f37428ff1481c1e34dddb3b9d7af8ede67ac5"}, - {file = "scikit_learn-1.4.2-cp310-cp310-macosx_12_0_arm64.whl", hash = "sha256:68b8404841f944a4a1459b07198fa2edd41a82f189b44f3e1d55c104dbc2e40c"}, - {file = "scikit_learn-1.4.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:81bf5d8bbe87643103334032dd82f7419bc8c8d02a763643a6b9a5c7288c5054"}, - {file = "scikit_learn-1.4.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:36f0ea5d0f693cb247a073d21a4123bdf4172e470e6d163c12b74cbb1536cf38"}, - {file = "scikit_learn-1.4.2-cp310-cp310-win_amd64.whl", hash = "sha256:87440e2e188c87db80ea4023440923dccbd56fbc2d557b18ced00fef79da0727"}, - {file = "scikit_learn-1.4.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:45dee87ac5309bb82e3ea633955030df9bbcb8d2cdb30383c6cd483691c546cc"}, - {file = "scikit_learn-1.4.2-cp311-cp311-macosx_12_0_arm64.whl", hash = "sha256:1d0b25d9c651fd050555aadd57431b53d4cf664e749069da77f3d52c5ad14b3b"}, - {file = "scikit_learn-1.4.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b0203c368058ab92efc6168a1507d388d41469c873e96ec220ca8e74079bf62e"}, - {file = "scikit_learn-1.4.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:44c62f2b124848a28fd695db5bc4da019287abf390bfce602ddc8aa1ec186aae"}, - {file = "scikit_learn-1.4.2-cp311-cp311-win_amd64.whl", hash = "sha256:5cd7b524115499b18b63f0c96f4224eb885564937a0b3477531b2b63ce331904"}, - {file = "scikit_learn-1.4.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:90378e1747949f90c8f385898fff35d73193dfcaec3dd75d6b542f90c4e89755"}, - {file = "scikit_learn-1.4.2-cp312-cp312-macosx_12_0_arm64.whl", hash = "sha256:ff4effe5a1d4e8fed260a83a163f7dbf4f6087b54528d8880bab1d1377bd78be"}, - {file = "scikit_learn-1.4.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:671e2f0c3f2c15409dae4f282a3a619601fa824d2c820e5b608d9d775f91780c"}, - {file = "scikit_learn-1.4.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d36d0bc983336bbc1be22f9b686b50c964f593c8a9a913a792442af9bf4f5e68"}, - {file = "scikit_learn-1.4.2-cp312-cp312-win_amd64.whl", hash = "sha256:d762070980c17ba3e9a4a1e043ba0518ce4c55152032f1af0ca6f39b376b5928"}, + {file = "scikit_learn-1.5.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:12e40ac48555e6b551f0a0a5743cc94cc5a765c9513fe708e01f0aa001da2801"}, + {file = "scikit_learn-1.5.0-cp310-cp310-macosx_12_0_arm64.whl", hash = "sha256:f405c4dae288f5f6553b10c4ac9ea7754d5180ec11e296464adb5d6ac68b6ef5"}, + {file = "scikit_learn-1.5.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:df8ccabbf583315f13160a4bb06037bde99ea7d8211a69787a6b7c5d4ebb6fc3"}, + {file = "scikit_learn-1.5.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2c75ea812cd83b1385bbfa94ae971f0d80adb338a9523f6bbcb5e0b0381151d4"}, + {file = "scikit_learn-1.5.0-cp310-cp310-win_amd64.whl", hash = "sha256:a90c5da84829a0b9b4bf00daf62754b2be741e66b5946911f5bdfaa869fcedd6"}, + {file = "scikit_learn-1.5.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:2a65af2d8a6cce4e163a7951a4cfbfa7fceb2d5c013a4b593686c7f16445cf9d"}, + {file = "scikit_learn-1.5.0-cp311-cp311-macosx_12_0_arm64.whl", hash = "sha256:4c0c56c3005f2ec1db3787aeaabefa96256580678cec783986836fc64f8ff622"}, + {file = "scikit_learn-1.5.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1f77547165c00625551e5c250cefa3f03f2fc92c5e18668abd90bfc4be2e0bff"}, + {file = "scikit_learn-1.5.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:118a8d229a41158c9f90093e46b3737120a165181a1b58c03461447aa4657415"}, + {file = "scikit_learn-1.5.0-cp311-cp311-win_amd64.whl", hash = "sha256:a03b09f9f7f09ffe8c5efffe2e9de1196c696d811be6798ad5eddf323c6f4d40"}, + {file = "scikit_learn-1.5.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:460806030c666addee1f074788b3978329a5bfdc9b7d63e7aad3f6d45c67a210"}, + {file = "scikit_learn-1.5.0-cp312-cp312-macosx_12_0_arm64.whl", hash = "sha256:1b94d6440603752b27842eda97f6395f570941857456c606eb1d638efdb38184"}, + {file = "scikit_learn-1.5.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d82c2e573f0f2f2f0be897e7a31fcf4e73869247738ab8c3ce7245549af58ab8"}, + {file = "scikit_learn-1.5.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a3a10e1d9e834e84d05e468ec501a356226338778769317ee0b84043c0d8fb06"}, + {file = "scikit_learn-1.5.0-cp312-cp312-win_amd64.whl", hash = "sha256:855fc5fa8ed9e4f08291203af3d3e5fbdc4737bd617a371559aaa2088166046e"}, + {file = "scikit_learn-1.5.0.tar.gz", hash = "sha256:789e3db01c750ed6d496fa2db7d50637857b451e57bcae863bff707c1247bef7"}, ] [[package]] @@ -2925,7 +2927,7 @@ files = [ [[package]] name = "types-requests" -version = "2.31.0.20240406" +version = "2.32.0.20240602" requires_python = ">=3.8" summary = "Typing stubs for requests" groups = ["dev"] @@ -2933,8 +2935,8 @@ dependencies = [ "urllib3>=2", ] files = [ - {file = "types-requests-2.31.0.20240406.tar.gz", hash = "sha256:4428df33c5503945c74b3f42e82b181e86ec7b724620419a2966e2de604ce1a1"}, - {file = "types_requests-2.31.0.20240406-py3-none-any.whl", hash = "sha256:6216cdac377c6b9a040ac1c0404f7284bd13199c0e1bb235f4324627e8898cf5"}, + {file = "types-requests-2.32.0.20240602.tar.gz", hash = "sha256:3f98d7bbd0dd94ebd10ff43a7fbe20c3b8528acace6d8efafef0b6a184793f06"}, + {file = "types_requests-2.32.0.20240602-py3-none-any.whl", hash = "sha256:ed3946063ea9fbc6b5fc0c44fa279188bae42d582cb63760be6cb4b9d06c3de8"}, ] [[package]] diff --git a/pyproject.toml b/pyproject.toml index a320c27f..c92a5715 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "cityseer" -version = '4.13.3' +version = '4.14.0b1' description = "Computational tools for network-based pedestrian-scale urban analysis" readme = "README.md" requires-python = ">=3.10, <3.13" diff --git a/pysrc/cityseer/tools/graphs.py b/pysrc/cityseer/tools/graphs.py index 26fde69c..3f9c2c7e 100644 --- a/pysrc/cityseer/tools/graphs.py +++ b/pysrc/cityseer/tools/graphs.py @@ -483,10 +483,10 @@ def nx_iron_edges( max_angle = util.measure_max_angle(edge_geom.coords) # if there is an angle greater than 95 then it is likely spurious if max_angle > 100: - edge_geom = edge_geom.simplify(10) + edge_geom = edge_geom.simplify(10) # type: ignore # don't apply to longer geoms, e.g. rural roads elif edge_geom.length > 150: - edge_geom = edge_geom.simplify(5) + edge_geom = edge_geom.simplify(5) # type: ignore # flatten if a relatively contained road but large angular change elif simple_geom.buffer(15).contains(edge_geom) and max_angle > 60: edge_geom = simple_geom @@ -743,7 +743,7 @@ def recursive_squash( # keep track of which nodes have been processed as part of recursion processed_nodes.add(nd_key) # get all other nodes within buffer distance - the self-node and previously processed nodes are also returned - j_hits: list[int] = nodes_tree.query(geometry.Point(x, y).buffer(buffer_dist)) + j_hits: list[int] = nodes_tree.query(geometry.Point(x, y).buffer(buffer_dist)) # type: ignore # review each node within the buffer j_nd_key: NodeKey for j_hit_idx in j_hits: @@ -811,6 +811,7 @@ def nx_split_opposing_geoms( buffer_dist: float = 12, merge_edges_by_midline: bool = True, contains_buffer_dist: int = 25, + skip_unmatched_osm_hwy_keys: bool = True, ) -> MultiGraph: """ Split edges opposite nodes on parallel edge segments if within a buffer distance. @@ -837,6 +838,9 @@ def nx_split_opposing_geoms( contains_buffer_dist: int The buffer distance to consider when checking if parallel edges sharing the same start and end nodes are sufficiently adjacent to be merged. + skip_unmatched_osm_hwy_keys: bool + `True` by default. When `True`, and if OpenStreetMap `highway` tags are avaiable, then the opposite edge will + only be split if opposing edges have intersecting `highway` tags. Returns ------- @@ -853,23 +857,23 @@ def make_edge_key(start_nd_key: NodeKey, end_nd_key: NodeKey, edge_idx: int) -> # recursive function for retrieving nested layers of successively replaced edges def recurse_child_keys( - start_nd_key: NodeKey, - end_nd_key: NodeKey, - edge_idx: int, - geom: geometry.LineString, + _start_nd_key: NodeKey, + _end_nd_key: NodeKey, + _edge_idx: int, + _edge_data: dict[Any, Any], current_edges: list[EdgeMapping], ): """ Recursively checks if an edge has been replaced by children, if so, use children instead. """ - edge_key = make_edge_key(start_nd_key, end_nd_key, edge_idx) + edge_key = make_edge_key(_start_nd_key, _end_nd_key, _edge_idx) # if an edge does not have children, add to current_edges and return if edge_key not in edge_children: - current_edges.append((start_nd_key, end_nd_key, edge_idx, geom)) + current_edges.append((_start_nd_key, _end_nd_key, _edge_idx, _edge_data)) # otherwise recursively drill-down until newest edges are found else: - for child_s, child_e, child_k, child_geom in edge_children[edge_key]: - recurse_child_keys(child_s, child_e, child_k, child_geom, current_edges) + for child_s, child_e, child_k, child_data in edge_children[edge_key]: + recurse_child_keys(child_s, child_e, child_k, child_data, current_edges) if not isinstance(nx_multigraph, nx.MultiGraph): raise TypeError("This method requires an undirected networkX MultiGraph.") @@ -890,7 +894,7 @@ def recurse_child_keys( # furthermore, successive iterations may remove old edges, so keep track of removed parent vs new child edges n_point = geometry.Point(nd_data["x"], nd_data["y"]) # spatial query from point returns all buffers with buffer_dist - edge_hits: list[int] = edges_tree.query(n_point.buffer(buffer_dist)) + edge_hits: list[int] = edges_tree.query(n_point.buffer(buffer_dist)) # type: ignore # extract the start node, end node, geom edges: list[EdgeMapping] = [] for edge_hit_idx in edge_hits: @@ -898,34 +902,50 @@ def recurse_child_keys( start_nd_key = edge_lookup["start_nd_key"] end_nd_key = edge_lookup["end_nd_key"] edge_idx = edge_lookup["edge_idx"] - edge_geom: geometry.LineString = nx_multigraph[start_nd_key][end_nd_key][edge_idx]["geom"] - edges.append((start_nd_key, end_nd_key, edge_idx, edge_geom)) - # check against removed edges + edge_data: dict[Any, Any] = nx_multigraph[start_nd_key][end_nd_key][edge_idx] + # don't add neighbouring edges + if nd_key in (start_nd_key, end_nd_key): + continue + edges.append((start_nd_key, end_nd_key, edge_idx, edge_data)) + # review gapped edges + # if already removed, get the new child edges current_edges: list[EdgeMapping] = [] - for start_nd_key, end_nd_key, edge_idx, edge_geom in edges: - recurse_child_keys(start_nd_key, end_nd_key, edge_idx, edge_geom, current_edges) - # get neighbouring nodes from new graph - neighbours: list[NodeKey] = list(_multi_graph.neighbors(nd_key)) - # abort if only direct neighbours - if len(current_edges) <= len(neighbours): - continue - # filter current_edges + for start_nd_key, end_nd_key, edge_idx, edge_data in edges: + recurse_child_keys(start_nd_key, end_nd_key, edge_idx, edge_data, current_edges) + # check that edges are within buffer gapped_edges: list[EdgeMapping] = [] - for start_nd_key, end_nd_key, edge_idx, edge_geom in current_edges: + for start_nd_key, end_nd_key, edge_idx, edge_data in current_edges: + edge_geom = edge_data["geom"] # check whether the geom is truly within the buffer distance if edge_geom.distance(n_point) > buffer_dist: continue - gapped_edges.append((start_nd_key, end_nd_key, edge_idx, edge_geom)) + # handle motorways + gapped_edges.append((start_nd_key, end_nd_key, edge_idx, edge_data)) # abort if no gapped edges if not gapped_edges: continue + # gather roadway descriptions + nb_hwy_keys = set() + for nb_nd_key in _multi_graph.neighbors(nd_key): + for edge_data in _multi_graph[nd_key][nb_nd_key].values(): + if "highways" in edge_data: + nb_hwy_keys.update(set(edge_data["highways"])) # prepare the root node's point geom n_geom = geometry.Point(nd_data["x"], nd_data["y"]) # iter gapped edges - for start_nd_key, end_nd_key, edge_idx, edge_geom in gapped_edges: + for start_nd_key, end_nd_key, edge_idx, edge_data in gapped_edges: + edge_geom = edge_data["geom"] # don't proceed with splits for short geoms if edge_geom.length < buffer_dist: continue + # get hwy keys + hwy_keys = set() + if "highways" in edge_data: + hwy_keys.update(edge_data["highways"]) + # check for itx + if skip_unmatched_osm_hwy_keys is True and nb_hwy_keys or hwy_keys: + if not nb_hwy_keys.intersection(hwy_keys): + continue # project a point and split the opposing geom # ops.nearest_points returns tuple of nearest from respective input geoms # want the nearest point on the line at index 1 @@ -941,7 +961,7 @@ def recurse_child_keys( continue new_edge_geom_a: geometry.LineString new_edge_geom_b: geometry.LineString - new_edge_geom_a, new_edge_geom_b = split_geoms.geoms + new_edge_geom_a, new_edge_geom_b = split_geoms.geoms # type: ignore # don't proceed with split for overly short split segments if new_edge_geom_a.length < buffer_dist or new_edge_geom_b.length < buffer_dist: continue @@ -952,7 +972,6 @@ def recurse_child_keys( # continue if a node already exists at this location if is_dupe: continue - edge_data: EdgeData = _multi_graph[start_nd_key][end_nd_key][edge_idx] edge_data_copy = {k: v for k, v in edge_data.items() if k != "geom"} _multi_graph.add_edge(start_nd_key, new_nd_name, **edge_data_copy) _multi_graph.add_edge(end_nd_key, new_nd_name, **edge_data_copy) @@ -1007,8 +1026,8 @@ def recurse_child_keys( # add the new edges to the edge_children dictionary edge_key = make_edge_key(start_nd_key, end_nd_key, edge_idx) edge_children[edge_key] = [ - (start_nd_key, new_nd_name, s_k, s_new_geom), - (end_nd_key, new_nd_name, e_k, e_new_geom), + (start_nd_key, new_nd_name, s_k, _multi_graph[start_nd_key][new_nd_name][s_k]), + (end_nd_key, new_nd_name, e_k, _multi_graph[end_nd_key][new_nd_name][e_k]), ] # drop the old edge from _multi_graph if _multi_graph.has_edge(start_nd_key, end_nd_key, edge_idx): @@ -1113,7 +1132,7 @@ def nx_decompose(nx_multigraph: MultiGraph, decompose_max: float) -> MultiGraph: for _ in range(cuts - 1): # create the split LineString geom for measuring the new length # switch back to shapely once bug resolved - line_segment: geometry.LineString = ops.substring(line_geom, step, step + step_size) + line_segment: geometry.LineString = ops.substring(line_geom, step, step + step_size) # type: ignore # get the x, y of the new end node x, y = line_segment.coords[-1] # add the new node and edge @@ -1142,7 +1161,7 @@ def nx_decompose(nx_multigraph: MultiGraph, decompose_max: float) -> MultiGraph: # set the last edge manually to avoid rounding errors at end of LineString # the nodes already exist, so just add edge # switch back to shapely once bug resolved - line_segment = ops.substring(line_geom, step, line_geom.length) + line_segment = ops.substring(line_geom, step, line_geom.length) # type: ignore edge_data_copy = {k: v for k, v in edge_data.items() if k != "geom"} g_multi_copy.add_edge(prior_node_id, end_nd_key, geom=line_segment, **edge_data_copy) @@ -1236,8 +1255,8 @@ def get_half_geoms(nx_multigraph_ref: MultiGraph, a_node: NodeKey, b_node: NodeK line_geom = geometry.LineString(line_geom_coords) # generate the two half geoms # switch back to shapely once bug resolved - a_half_geom: geometry.LineString = ops.substring(line_geom, 0.0, 0.5, normalized=True) - b_half_geom: geometry.LineString = ops.substring(line_geom, 0.5, 1.0, normalized=True) + a_half_geom: geometry.LineString = ops.substring(line_geom, 0.0, 0.5, normalized=True) # type: ignore + b_half_geom: geometry.LineString = ops.substring(line_geom, 0.5, 1.0, normalized=True) # type: ignore # check that nothing odd happened with new midpoint if not np.allclose( a_half_geom.coords[-1][:2], @@ -1325,7 +1344,7 @@ def prepare_dual_node_key(start_nd_key: NodeKey, end_nd_key: NodeKey, edge_idx: # get the near and far half geoms spoke_half_geom, _discard_geom = get_half_geoms(nx_multigraph, n_side, nb_nd_key, edge_idx) # weld the lines - merged_line: geometry.LineString = ops.linemerge([half_geom, spoke_half_geom]) + merged_line: geometry.LineString = ops.linemerge([half_geom, spoke_half_geom]) # type: ignore if merged_line.geom_type != "LineString": raise TypeError( f'Found {merged_line.geom_type} instead of "LineString" for new geom {merged_line.wkt}. ' @@ -1389,7 +1408,7 @@ def nx_weight_by_dissolved_edges( # find nearby edges edge_geom = edge_data["geom"] edge_geom_buff = edge_geom.buffer(dissolve_distance, cap_style=geometry.CAP_STYLE.square) - edges_hits: list[int] = edges_tree.query(edge_geom_buff) + edges_hits: list[int] = edges_tree.query(edge_geom_buff) # type: ignore for edge_hit_idx in edges_hits: edge_lookup = edge_lookups[edge_hit_idx] nearby_start_nd_key = edge_lookup["start_nd_key"] @@ -1465,7 +1484,7 @@ def nx_generate_vis_lines(nx_multigraph: MultiGraph) -> MultiGraph: util.align_linestring_coords(edge_geom.coords, (nd_data["x"], nd_data["y"])) ) edge_slice = ops.substring(edge_geom, 0, 0.5, normalized=True) - line_geoms.append(edge_slice) + line_geoms.append(edge_slice) # type: ignore # build line geom g_multi_copy.nodes[nd_key]["line_geom"] = geometry.MultiLineString(line_geoms) diff --git a/pysrc/cityseer/tools/io.py b/pysrc/cityseer/tools/io.py index 3a9628cf..7b2bf867 100644 --- a/pysrc/cityseer/tools/io.py +++ b/pysrc/cityseer/tools/io.py @@ -668,7 +668,7 @@ def nx_from_open_roads( ) # create the geometry geom = geometry.LineString(edge_data.geometry["coordinates"]) - geom: geometry.LineString = geom.simplify(5) + geom: geometry.LineString = geom.simplify(5) # type: ignore # do not add edges to clipped extents if start_nd not in g_multi or end_nd not in g_multi: n_dropped += 1 diff --git a/pysrc/cityseer/tools/mock.py b/pysrc/cityseer/tools/mock.py index 79bf0c82..60b8c65f 100644 --- a/pysrc/cityseer/tools/mock.py +++ b/pysrc/cityseer/tools/mock.py @@ -298,7 +298,7 @@ def mock_data_gdf(nx_multigraph: MultiGraph, length: int = 50, random_seed: int # last 5 datapoints are a cluster of nodes where the nodes share the same data_id for deduplication checks for idx, loc_idx in enumerate(range(length - 5, length)): data_gpd.loc[str(loc_idx), "data_id"] = length - 5 - data_gpd.loc[str(loc_idx), "geometry"] = geometry.Point(700100 + idx * 10, 5719100 + idx * 10) + data_gpd.loc[str(loc_idx), "geometry"] = geometry.Point(700100 + idx * 10, 5719100 + idx * 10) # type: ignore data_gpd = cast(gpd.GeoDataFrame, data_gpd) return data_gpd diff --git a/pysrc/cityseer/tools/util.py b/pysrc/cityseer/tools/util.py index 8aa03e07..93837420 100644 --- a/pysrc/cityseer/tools/util.py +++ b/pysrc/cityseer/tools/util.py @@ -35,7 +35,7 @@ NodeData = dict[str, Any] EdgeType = Union[tuple[NodeKey, NodeKey], tuple[NodeKey, NodeKey, int]] EdgeData = dict[str, Any] -EdgeMapping = tuple[NodeKey, NodeKey, int, geometry.LineString] +EdgeMapping = tuple[NodeKey, NodeKey, int, dict[Any, Any]] CoordsType = Union[tuple[float, float], tuple[float, float, float], npt.NDArray[np.float_]] ListCoordsType = Union[list[CoordsType], coords.CoordinateSequence] From 5b3b8d4ddcd040c3e61cfd9ea46884479513677e Mon Sep 17 00:00:00 2001 From: Gareth Simons Date: Sun, 9 Jun 2024 18:14:30 +0100 Subject: [PATCH 2/5] osm hwy tags iter --- pyproject.toml | 2 +- pysrc/cityseer/tools/graphs.py | 80 +++++++++++++++++++++++++++------- 2 files changed, 65 insertions(+), 17 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index c92a5715..f5dae33a 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "cityseer" -version = '4.14.0b1' +version = '4.14.0b2' description = "Computational tools for network-based pedestrian-scale urban analysis" readme = "README.md" requires-python = ">=3.10, <3.13" diff --git a/pysrc/cityseer/tools/graphs.py b/pysrc/cityseer/tools/graphs.py index 3f9c2c7e..ae815013 100644 --- a/pysrc/cityseer/tools/graphs.py +++ b/pysrc/cityseer/tools/graphs.py @@ -306,6 +306,7 @@ def nx_merge_parallel_edges( nx_multigraph: MultiGraph, merge_edges_by_midline: bool, contains_buffer_dist: int, + osm_hwy_target_tags: list[str] | None = None, ) -> MultiGraph: """ Check a MultiGraph for duplicate edges; which, if found, will be merged. @@ -329,6 +330,9 @@ def nx_merge_parallel_edges( contains_buffer_dist: int The buffer distance to consider when checking if parallel edges sharing the same start and end nodes are sufficiently adjacent to be merged. + osm_hwy_target_tags: list[str] + An optional list of OpenStreetMap target highway tags. If provided, only nodes with neighbouring edges + containing a tag matching one of the target OSM highway tags will be consolidated. Returns ------- @@ -344,6 +348,12 @@ def nx_merge_parallel_edges( # don't use copy() - add nodes only deduped_graph: MultiGraph = nx.MultiGraph() deduped_graph.add_nodes_from(nx_multigraph.nodes(data=True)) + # if using OSM tags heuristic + tags = set() + if osm_hwy_target_tags is not None: + if not isinstance(osm_hwy_target_tags, (list, set, tuple)): + raise ValueError("OSM target tags should be provided as a `list` of `str`.") + tags.update(osm_hwy_target_tags) # iter the edges start_nd_key: NodeKey end_nd_key: NodeKey @@ -370,9 +380,14 @@ def nx_merge_parallel_edges( # process longer geoms longer_geoms: list[geometry.LineString] = [] for edge_geom, edge_data in zip(edge_geoms, edges_data): + # get hwy keys + hwy_keys = set() + if "highways" in edge_data: + hwy_keys.update(edge_data["highways"]) # discard distinct edges where the buffer of the shorter contains the longer is_contained = shortest_geom.buffer(contains_buffer_dist).contains(edge_geom) - if is_contained: + # if controlling by tags: check for itx + if (tags and hwy_keys.intersection(tags) and is_contained) or (not tags and is_contained): edge_info.gather_edge_info(edge_data) longer_geoms.append(edge_geom) else: @@ -653,6 +668,16 @@ def _squash_adjacent( return nx_multigraph +def _gather_osm_hwy_tags(nx_multigraph: MultiGraph, nd_key: NodeKey): + # gather roadway descriptions + nb_hwy_keys = set() + for nb_nd_key in nx_multigraph.neighbors(nd_key): + for edge_data in nx_multigraph[nd_key][nb_nd_key].values(): + if "highways" in edge_data: + nb_hwy_keys.update(set(edge_data["highways"])) + return nb_hwy_keys + + def nx_consolidate_nodes( nx_multigraph: MultiGraph, buffer_dist: float = 12, @@ -661,6 +686,7 @@ def nx_consolidate_nodes( centroid_by_itx: bool = True, merge_edges_by_midline: bool = True, contains_buffer_dist: int = 25, + osm_hwy_target_tags: list[str] | None = None, ) -> MultiGraph: """ Consolidates nodes if they are within a buffer distance of each other. @@ -701,6 +727,9 @@ def nx_consolidate_nodes( contains_buffer_dist: int The buffer distance to consider when checking if parallel edges sharing the same start and end nodes are sufficiently adjacent to be merged. This is run after node consolidation has completed. + osm_hwy_target_tags: list[str] + An optional list of OpenStreetMap target highway tags. If provided, only nodes with neighbouring edges + containing a tag matching one of the target OSM highway tags will be consolidated. Returns ------- @@ -731,6 +760,12 @@ def nx_consolidate_nodes( logger.info("Consolidating nodes.") # keep track of removed nodes removed_nodes: set[NodeKey] = set() + # if using OSM tags heuristic + tags = set() + if osm_hwy_target_tags is not None: + if not isinstance(osm_hwy_target_tags, (list, set, tuple)): + raise ValueError("OSM target tags should be provided as a `list` of `str`.") + tags.update(osm_hwy_target_tags) def recursive_squash( nd_key: NodeKey, @@ -759,6 +794,9 @@ def recursive_squash( continue if neighbour_policy == "direct" and j_nd_key not in neighbours: continue + nb_hwy_keys = _gather_osm_hwy_tags(nx_multigraph, j_nd_key) + if tags and not nb_hwy_keys.intersection(tags): + continue # otherwise add the node node_group.add(j_nd_key) # if recursive, follow the chain @@ -781,6 +819,9 @@ def recursive_squash( # skip if already consolidated from an adjacent node, or if the node's degree doesn't meet min_node_degree if nd_key in removed_nodes: continue + nb_hwy_keys = _gather_osm_hwy_tags(nx_multigraph, nd_key) + if tags and not nb_hwy_keys.intersection(tags): + continue node_group = recursive_squash( nd_key, # node nd_key nd_data["x"], # x point for buffer @@ -801,7 +842,9 @@ def recursive_squash( # remove new filler nodes _multi_graph = nx_remove_filler_nodes(_multi_graph) # remove parallel edges resulting from squashing nodes - _multi_graph = nx_merge_parallel_edges(_multi_graph, merge_edges_by_midline, contains_buffer_dist) + _multi_graph = nx_merge_parallel_edges( + _multi_graph, merge_edges_by_midline, contains_buffer_dist, osm_hwy_target_tags + ) return _multi_graph @@ -811,7 +854,7 @@ def nx_split_opposing_geoms( buffer_dist: float = 12, merge_edges_by_midline: bool = True, contains_buffer_dist: int = 25, - skip_unmatched_osm_hwy_keys: bool = True, + osm_hwy_target_tags: list[str] | None = None, ) -> MultiGraph: """ Split edges opposite nodes on parallel edge segments if within a buffer distance. @@ -838,9 +881,9 @@ def nx_split_opposing_geoms( contains_buffer_dist: int The buffer distance to consider when checking if parallel edges sharing the same start and end nodes are sufficiently adjacent to be merged. - skip_unmatched_osm_hwy_keys: bool - `True` by default. When `True`, and if OpenStreetMap `highway` tags are avaiable, then the opposite edge will - only be split if opposing edges have intersecting `highway` tags. + osm_hwy_target_tags: list[str] + An optional list of OpenStreetMap target highway tags. If provided, only nodes with neighbouring edges + containing a tag matching one of the target OSM highway tags will be consolidated. Returns ------- @@ -878,6 +921,12 @@ def recurse_child_keys( if not isinstance(nx_multigraph, nx.MultiGraph): raise TypeError("This method requires an undirected networkX MultiGraph.") _multi_graph: MultiGraph = nx_multigraph.copy() + # if using OSM tags heuristic + tags = set() + if osm_hwy_target_tags is not None: + if not isinstance(osm_hwy_target_tags, (list, set, tuple)): + raise ValueError("OSM target tags should be provided as a `list` of `str`.") + tags.update(osm_hwy_target_tags) # create an edges STRtree (nodes and edges) edges_tree, edge_lookups = util.create_edges_strtree(_multi_graph) # iter @@ -889,6 +938,10 @@ def recurse_child_keys( # don't split opposing geoms from nodes of degree 1 if nx.degree(_multi_graph, nd_key) < 2: continue + # check tags + nb_hwy_keys = _gather_osm_hwy_tags(nx_multigraph, nd_key) + if tags and not nb_hwy_keys.intersection(tags): + continue # get all other edges within the buffer distance # the spatial index using bounding boxes, so further filtering is required (see further down) # furthermore, successive iterations may remove old edges, so keep track of removed parent vs new child edges @@ -924,12 +977,6 @@ def recurse_child_keys( # abort if no gapped edges if not gapped_edges: continue - # gather roadway descriptions - nb_hwy_keys = set() - for nb_nd_key in _multi_graph.neighbors(nd_key): - for edge_data in _multi_graph[nd_key][nb_nd_key].values(): - if "highways" in edge_data: - nb_hwy_keys.update(set(edge_data["highways"])) # prepare the root node's point geom n_geom = geometry.Point(nd_data["x"], nd_data["y"]) # iter gapped edges @@ -943,9 +990,8 @@ def recurse_child_keys( if "highways" in edge_data: hwy_keys.update(edge_data["highways"]) # check for itx - if skip_unmatched_osm_hwy_keys is True and nb_hwy_keys or hwy_keys: - if not nb_hwy_keys.intersection(hwy_keys): - continue + if tags and not hwy_keys.intersection(tags): + continue # project a point and split the opposing geom # ops.nearest_points returns tuple of nearest from respective input geoms # want the nearest point on the line at index 1 @@ -1033,7 +1079,9 @@ def recurse_child_keys( if _multi_graph.has_edge(start_nd_key, end_nd_key, edge_idx): _multi_graph.remove_edge(start_nd_key, end_nd_key, edge_idx) # squashing nodes can result in edge duplicates - deduped_graph = nx_merge_parallel_edges(_multi_graph, merge_edges_by_midline, contains_buffer_dist) + deduped_graph = nx_merge_parallel_edges( + _multi_graph, merge_edges_by_midline, contains_buffer_dist, osm_hwy_target_tags + ) return deduped_graph From 9b43df0b016de720321f9d67767e54e383e77f6e Mon Sep 17 00:00:00 2001 From: Gareth Simons Date: Sun, 9 Jun 2024 22:55:09 +0100 Subject: [PATCH 3/5] cleaning --- pyproject.toml | 2 +- pysrc/cityseer/tools/graphs.py | 28 +++++++++++++++++++++------- pysrc/cityseer/tools/util.py | 3 ++- 3 files changed, 24 insertions(+), 9 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index f5dae33a..c6f571bf 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "cityseer" -version = '4.14.0b2' +version = '4.14.0b3' description = "Computational tools for network-based pedestrian-scale urban analysis" readme = "README.md" requires-python = ">=3.10, <3.13" diff --git a/pysrc/cityseer/tools/graphs.py b/pysrc/cityseer/tools/graphs.py index ae815013..3df548e1 100644 --- a/pysrc/cityseer/tools/graphs.py +++ b/pysrc/cityseer/tools/graphs.py @@ -532,6 +532,9 @@ def _squash_adjacent( """ if not isinstance(nx_multigraph, nx.MultiGraph): raise TypeError("This method requires an undirected networkX MultiGraph (for multiple edges).") + # nothing to do for single node group + if len(node_group) < 2: + return nx_multigraph # remove any node keys no longer in the graph centroid_nodes_filter = [nd_key for nd_key in node_group if nd_key in nx_multigraph] # if using intersections, find straight-through routes and count @@ -929,6 +932,8 @@ def recurse_child_keys( tags.update(osm_hwy_target_tags) # create an edges STRtree (nodes and edges) edges_tree, edge_lookups = util.create_edges_strtree(_multi_graph) + # node groups + node_groups: list[set] = [] # iter logger.info("Splitting opposing edges.") # iterate origin graph (else node structure changes in place) @@ -972,19 +977,20 @@ def recurse_child_keys( # check whether the geom is truly within the buffer distance if edge_geom.distance(n_point) > buffer_dist: continue - # handle motorways gapped_edges.append((start_nd_key, end_nd_key, edge_idx, edge_data)) # abort if no gapped edges if not gapped_edges: continue # prepare the root node's point geom n_geom = geometry.Point(nd_data["x"], nd_data["y"]) + # nodes for squashing + node_group = set([nd_key]) # iter gapped edges for start_nd_key, end_nd_key, edge_idx, edge_data in gapped_edges: edge_geom = edge_data["geom"] - # don't proceed with splits for short geoms - if edge_geom.length < buffer_dist: - continue + # don't proceed with splits for short geoms?? + # if edge_geom.length < 5: + # continue # get hwy keys hwy_keys = set() if "highways" in edge_data: @@ -1008,9 +1014,6 @@ def recurse_child_keys( new_edge_geom_a: geometry.LineString new_edge_geom_b: geometry.LineString new_edge_geom_a, new_edge_geom_b = split_geoms.geoms # type: ignore - # don't proceed with split for overly short split segments - if new_edge_geom_a.length < buffer_dist or new_edge_geom_b.length < buffer_dist: - continue # add the new node and edges to _multi_graph (don't modify nx_multigraph because of iter in place) new_nd_name, is_dupe = util.add_node( _multi_graph, [start_nd_key, nd_key, end_nd_key], x=nearest_point.x, y=nearest_point.y @@ -1018,6 +1021,8 @@ def recurse_child_keys( # continue if a node already exists at this location if is_dupe: continue + node_group.update([new_nd_name]) + # copy edge data edge_data_copy = {k: v for k, v in edge_data.items() if k != "geom"} _multi_graph.add_edge(start_nd_key, new_nd_name, **edge_data_copy) _multi_graph.add_edge(end_nd_key, new_nd_name, **edge_data_copy) @@ -1078,6 +1083,15 @@ def recurse_child_keys( # drop the old edge from _multi_graph if _multi_graph.has_edge(start_nd_key, end_nd_key, edge_idx): _multi_graph.remove_edge(start_nd_key, end_nd_key, edge_idx) + node_groups.append(node_group) + # iter and squash + logger.info("Squashing opposing nodes") + for node_group in node_groups: + _multi_graph = _squash_adjacent( + _multi_graph, + node_group, + centroid_by_itx=False, + ) # squashing nodes can result in edge duplicates deduped_graph = nx_merge_parallel_edges( _multi_graph, merge_edges_by_midline, contains_buffer_dist, osm_hwy_target_tags diff --git a/pysrc/cityseer/tools/util.py b/pysrc/cityseer/tools/util.py index 93837420..730b69be 100644 --- a/pysrc/cityseer/tools/util.py +++ b/pysrc/cityseer/tools/util.py @@ -410,7 +410,8 @@ def add_node( """ # suggest a name based on the given names if len(nodes_names) == 1: - new_nd_name = str(nodes_names[0]) + # catch sets + new_nd_name = str(list(nodes_names)[0]) # if concatenating existing nodes, suggest a name based on a combination of existing names else: names = [] From a5c2580ab2a6d6bd9e04036057cf37266b64b039 Mon Sep 17 00:00:00 2001 From: Gareth Simons Date: Tue, 11 Jun 2024 18:36:33 +0100 Subject: [PATCH 4/5] fixes consolidation test --- pysrc/cityseer/tools/graphs.py | 1 + tests/tools/test_graphs.py | 19 +++++-------------- 2 files changed, 6 insertions(+), 14 deletions(-) diff --git a/pysrc/cityseer/tools/graphs.py b/pysrc/cityseer/tools/graphs.py index 3df548e1..3b41e300 100644 --- a/pysrc/cityseer/tools/graphs.py +++ b/pysrc/cityseer/tools/graphs.py @@ -989,6 +989,7 @@ def recurse_child_keys( for start_nd_key, end_nd_key, edge_idx, edge_data in gapped_edges: edge_geom = edge_data["geom"] # don't proceed with splits for short geoms?? + # not necessarily desirable behaviour # if edge_geom.length < 5: # continue # get hwy keys diff --git a/tests/tools/test_graphs.py b/tests/tools/test_graphs.py index d4cebe4e..3469ca4f 100644 --- a/tests/tools/test_graphs.py +++ b/tests/tools/test_graphs.py @@ -346,7 +346,7 @@ def test_nx_consolidate_nodes(parallel_segments_graph): """ """ # behaviour confirmed visually # from cityseer.tools import plot - # plot.plot_nx(G, labels=True, node_size=80, plot_geoms=True) + # plot.plot_nx(parallel_segments_graph, labels=True, node_size=80, plot_geoms=True) # set centroid_by_itx to False G_merged_spatial = graphs.nx_consolidate_nodes( parallel_segments_graph, buffer_dist=25, crawl=True, centroid_by_itx=False, merge_edges_by_midline=True @@ -367,12 +367,12 @@ def test_nx_consolidate_nodes(parallel_segments_graph): node_coords.append((d["x"], d["y"])) assert node_coords == [ (660, 660), - (620.0, 710.0), (660.0, 710.0), + (780.0, 710.0), + (620.0, 710.0), (710.0, 800.0), - (706.6666666666666, 713.3333333333334), + (710.0, 710.0), (710.0, 620.0), - (780.0, 710.0), (840.0, 710.0), ] edge_lens = [] @@ -380,16 +380,7 @@ def test_nx_consolidate_nodes(parallel_segments_graph): edge_lens.append(d["geom"].length) assert np.allclose( edge_lens, - [ - 50.0, - 40.0, - 46.78556282539396, - 86.73074554171788, - 93.39283817414604, - 73.40905181848417, - 60.0, - 147.70329614269008, - ], + [50.0, 40.0, 50.0, 70.0, 60.0, 147.70329614269008, 90.0, 90.0], ) From 5754619d9bc49f29cd377593d8322ced1182576e Mon Sep 17 00:00:00 2001 From: Gareth Simons Date: Mon, 17 Jun 2024 22:33:16 +0100 Subject: [PATCH 5/5] wraps alt clean --- pdm.lock | 11 ++++++- pyproject.toml | 3 +- pysrc/cityseer/tools/graphs.py | 5 +--- pysrc/cityseer/tools/io.py | 54 ++++++++++++++++------------------ 4 files changed, 39 insertions(+), 34 deletions(-) diff --git a/pdm.lock b/pdm.lock index 7c7a036c..9e47f115 100644 --- a/pdm.lock +++ b/pdm.lock @@ -5,7 +5,7 @@ groups = ["default", "dev"] strategy = ["cross_platform", "inherit_metadata"] lock_version = "4.4.1" -content_hash = "sha256:9cd8f5217f9f67fd5c6c02b678acb5f3b096d3d1035138003b7ff130c4f62d6c" +content_hash = "sha256:8766745ef09cd3bdc1efffaac3334bf6cc1336316cc01323d29f9f89fd26a446" [[package]] name = "affine" @@ -2984,6 +2984,15 @@ files = [ {file = "urllib3-2.2.1.tar.gz", hash = "sha256:d0570876c61ab9e520d776c38acbbb5b05a776d3f9ff98a5c8fd5162a444cf19"}, ] +[[package]] +name = "utm" +version = "0.7.0" +summary = "Bidirectional UTM-WGS84 converter for python" +groups = ["dev"] +files = [ + {file = "utm-0.7.0.tar.gz", hash = "sha256:3c9a3650e98bb6eecec535418d0dfd4db8f88c8ceaca112a0ff0787e116566e2"}, +] + [[package]] name = "wcwidth" version = "0.2.13" diff --git a/pyproject.toml b/pyproject.toml index c6f571bf..e32dd7f2 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "cityseer" -version = '4.14.0b3' +version = '4.14.0' description = "Computational tools for network-based pedestrian-scale urban analysis" readme = "README.md" requires-python = ">=3.10, <3.13" @@ -106,6 +106,7 @@ dev = [ "pdoc>=14.1.0", "docstring-parser>=0.15", "dominate>=2.8.0", + "utm>=0.7.0", ] [build-system] diff --git a/pysrc/cityseer/tools/graphs.py b/pysrc/cityseer/tools/graphs.py index 3b41e300..8d8d85db 100644 --- a/pysrc/cityseer/tools/graphs.py +++ b/pysrc/cityseer/tools/graphs.py @@ -503,9 +503,7 @@ def nx_iron_edges( elif edge_geom.length > 150: edge_geom = edge_geom.simplify(5) # type: ignore # flatten if a relatively contained road but large angular change - elif simple_geom.buffer(15).contains(edge_geom) and max_angle > 60: - edge_geom = simple_geom - elif simple_geom.buffer(7.5).contains(edge_geom) and max_angle > 30: + elif simple_geom.buffer(15).contains(edge_geom) and max_angle > 20: edge_geom = simple_geom g_multi_copy[start_nd_key][end_nd_key][edge_idx]["geom"] = edge_geom # straightening parallel edges can create duplicates @@ -545,7 +543,6 @@ def _squash_adjacent( crossings = 0 # compute node straight-through-angles nd_x_y = (nx_multigraph.nodes[nd_key]["x"], nx_multigraph.nodes[nd_key]["y"]) - crossings = 0 for nb_nd_key_a in nx.neighbors(nx_multigraph, nd_key): for nb_edge_data_a in nx_multigraph[nd_key][nb_nd_key_a].values(): geom_a = nb_edge_data_a["geom"] diff --git a/pysrc/cityseer/tools/io.py b/pysrc/cityseer/tools/io.py index 7b2bf867..ea63a749 100644 --- a/pysrc/cityseer/tools/io.py +++ b/pysrc/cityseer/tools/io.py @@ -328,8 +328,7 @@ def osm_graph_from_poly( ( way["highway"] ["area"!="yes"] - ["highway"!~"motorway|motorway_link|bus_guideway|busway|escape|raceway|proposed|planned|abandoned|platform|construction|emergency_bay|rest_area"] - ["tunnel"!="yes"] + ["highway"!~"bus_guideway|busway|escape|raceway|proposed|planned|abandoned|platform|construction|emergency_bay|rest_area"] ["footway"!="sidewalk"] ["service"!~"parking_aisle|driveway|drive-through|slipway"] ["amenity"!~"charging_station|parking|fuel|motorcycle_parking|parking_entrance|parking_space"] @@ -357,32 +356,31 @@ def osm_graph_from_poly( graph_crs = graphs.nx_remove_filler_nodes(graph_crs) if simplify: graph_crs = graphs.nx_remove_dangling_nodes(graph_crs, remove_disconnected=remove_disconnected) - graph_crs = graphs.nx_consolidate_nodes( - graph_crs, - buffer_dist=crawl_consolidate_dist, - crawl=True, - contains_buffer_dist=contains_buffer_dist, - ) - graph_crs = graphs.nx_split_opposing_geoms( - graph_crs, buffer_dist=parallel_consolidate_dist, contains_buffer_dist=contains_buffer_dist - ) - graph_crs = graphs.nx_consolidate_nodes( - graph_crs, - buffer_dist=parallel_consolidate_dist, - contains_buffer_dist=contains_buffer_dist, - ) - graph_crs = graphs.nx_remove_filler_nodes(graph_crs) - if iron_edges: - graph_crs = graphs.nx_iron_edges(graph_crs) - graph_crs = graphs.nx_split_opposing_geoms( - graph_crs, buffer_dist=parallel_consolidate_dist, contains_buffer_dist=contains_buffer_dist - ) - graph_crs = graphs.nx_consolidate_nodes( - graph_crs, - buffer_dist=parallel_consolidate_dist, - contains_buffer_dist=contains_buffer_dist, - ) - graph_crs = graphs.nx_remove_filler_nodes(graph_crs) + for hwy_keys, split_dist, consol_dist, cent_by_itx in [ + (["motorway"], 30, 15, False), + (["trunk"], parallel_consolidate_dist, crawl_consolidate_dist, True), + (["primary"], parallel_consolidate_dist, crawl_consolidate_dist, True), + (["secondary"], parallel_consolidate_dist, crawl_consolidate_dist, True), + (["tertiary"], parallel_consolidate_dist, crawl_consolidate_dist, True), + (["residential"], parallel_consolidate_dist, crawl_consolidate_dist, True), + (None, parallel_consolidate_dist, crawl_consolidate_dist, False), + ]: + contains_buffer_dist = max(split_dist, 25) + graph_crs = graphs.nx_split_opposing_geoms( + graph_crs, + buffer_dist=split_dist, + osm_hwy_target_tags=hwy_keys, + contains_buffer_dist=contains_buffer_dist, + ) + graph_crs = graphs.nx_consolidate_nodes( + graph_crs, + buffer_dist=consol_dist, + crawl=True, + osm_hwy_target_tags=hwy_keys, + centroid_by_itx=cent_by_itx, + contains_buffer_dist=contains_buffer_dist, + ) + graph_crs = graphs.nx_remove_filler_nodes(graph_crs) if iron_edges: graph_crs = graphs.nx_iron_edges(graph_crs)