From cc6acf3fe9281c5410be9ef6b41b5b29a2fac930 Mon Sep 17 00:00:00 2001 From: Hans Vanrompay Date: Tue, 10 Nov 2026 10:43:12 +0100 Subject: [PATCH 01/12] working on feedback --- CHANGELOG.md | 1 + docs/cookbook/examples/job_splitting.ipynb | 808 ++++++++++++++++++ docs/cookbook/job_manager.rst | 37 + openeo/extra/job_management/__init__.py | 3 +- openeo/extra/job_management/_job_splitting.py | 123 +++ .../job_management/test_job_splitting.py | 141 +++ 6 files changed, 1112 insertions(+), 1 deletion(-) create mode 100644 docs/cookbook/examples/job_splitting.ipynb diff --git a/CHANGELOG.md b/CHANGELOG.md index 743f5bf29..760df2b9b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -21,6 +21,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [0.54.0] - 2026-10-28 +- Add public `split_points` helper in `openeo.extra.job_management` for point-based job splitting, including optional tile-grid co-grouping. ### Changed diff --git a/docs/cookbook/examples/job_splitting.ipynb b/docs/cookbook/examples/job_splitting.ipynb new file mode 100644 index 000000000..084b7791d --- /dev/null +++ b/docs/cookbook/examples/job_splitting.ipynb @@ -0,0 +1,808 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "cdc93935", + "metadata": {}, + "source": [ + "# Internal walkthrough of `split_area` and `split_points`\n", + "\n", + "This notebook is not customer-facing. It is a developer guide to understand the\n", + "runtime behavior of the splitter logic in\n", + "`openeo.extra.job_management._job_splitting`.\n", + "\n", + "It is configured to import from the local repository checkout (workspace source),\n", + "not from any preinstalled `openeo` package.\n", + "\n", + "What this notebook covers:\n", + "\n", + "1. `split_area` size-based path and custom-grid path.\n", + "2. `split_points` default quadtree path.\n", + "3. Quadtree no-progress fallback (coincident points -> plain chunking).\n", + "4. Row semantics (`Point` vs `MultiPoint` rows).\n", + "5. Tile-grid path (`gpd.sjoin`) with boundary and unmatched-point behavior.\n", + "6. CRS reprojection behavior in tile-grid mode.\n", + "7. Validation error cases.\n", + "\n", + "All cells are safe to run locally without connecting to an openEO backend." + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "id": "6893a1ed", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Using local openeo from: C:\\Git_projects\\openeo-python-client\\openeo\\__init__.py\n" + ] + } + ], + "source": [ + "import sys\n", + "from pathlib import Path\n", + "\n", + "import geopandas as gpd\n", + "import matplotlib.pyplot as plt\n", + "import numpy as np\n", + "import shapely.geometry\n", + "\n", + "# Force imports to come from this local repository checkout, not a preinstalled package.\n", + "repo_root = Path.cwd().resolve()\n", + "while not (repo_root / \"openeo\").exists() and repo_root != repo_root.parent:\n", + " repo_root = repo_root.parent\n", + "\n", + "if not (repo_root / \"openeo\").exists():\n", + " raise RuntimeError(\"Could not locate local repository root containing 'openeo/'\")\n", + "\n", + "repo_root_str = str(repo_root)\n", + "if repo_root_str in sys.path:\n", + " sys.path.remove(repo_root_str)\n", + "sys.path.insert(0, repo_root_str)\n", + "\n", + "import openeo\n", + "from openeo.extra.job_management import split_area, split_points\n", + "from openeo.extra.job_management._job_splitting import JobSplittingFailure\n", + "\n", + "print(\"Using local openeo from:\", Path(openeo.__file__).resolve())\n", + "\n", + "plt.rcParams[\"figure.figsize\"] = (7, 6)\n", + "\n", + "\n", + "def plot_groups(groups, title, ax=None, markersize=20):\n", + " if ax is None:\n", + " fig, ax = plt.subplots()\n", + " cmap = plt.get_cmap(\"tab20\")\n", + " for i, group in enumerate(groups):\n", + " group.plot(ax=ax, color=cmap(i % 20), markersize=markersize, label=f\"g{i} (n={len(group)})\")\n", + " ax.set_title(title)\n", + " ax.legend(loc=\"center left\", bbox_to_anchor=(1.0, 0.5))\n", + " return ax\n", + "\n", + "\n", + "def print_group_sizes(groups, label):\n", + " print(f\"{label}: {len(groups)} groups\")\n", + " print(\"sizes:\", [len(g) for g in groups])" + ] + }, + { + "cell_type": "markdown", + "id": "23370d5d", + "metadata": {}, + "source": [ + "## Case 1: `split_area` size-based path\n", + "\n", + "This follows the `split_area(..., projection=..., tile_size=...)` branch.\n", + "The AOI is reprojected to the tiling CRS and split into square tiles." + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "id": "dc5e80d5", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "tile CRS: EPSG:3857\n", + "tile count: 25\n" + ] + }, + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
geometry
0POLYGON ((405750.167 6621293.723, 405750.167 6...
1POLYGON ((405750.167 6626293.723, 405750.167 6...
2POLYGON ((405750.167 6631293.723, 405750.167 6...
3POLYGON ((405750.167 6636293.723, 405750.167 6...
4POLYGON ((405750.167 6641293.723, 405750.167 6...
\n", + "
" + ], + "text/plain": [ + " geometry\n", + "0 POLYGON ((405750.167 6621293.723, 405750.167 6...\n", + "1 POLYGON ((405750.167 6626293.723, 405750.167 6...\n", + "2 POLYGON ((405750.167 6631293.723, 405750.167 6...\n", + "3 POLYGON ((405750.167 6636293.723, 405750.167 6...\n", + "4 POLYGON ((405750.167 6641293.723, 405750.167 6..." + ] + }, + "execution_count": 4, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "aoi = {\"west\": 3.60, \"south\": 51.0, \"east\": 3.80, \"north\": 51.12, \"crs\": \"EPSG:4326\"}\n", + "\n", + "tiles = split_area(aoi=aoi, tile_size=5_000, projection=\"EPSG:3857\")\n", + "print(\"tile CRS:\", tiles.crs)\n", + "print(\"tile count:\", len(tiles))\n", + "tiles.head()" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "id": "8dae3d94", + "metadata": {}, + "outputs": [ + { + "data": { + "image/png": "iVBORw0KGgoAAAANSUhEUgAAAikAAAIQCAYAAABE5v57AAAAOnRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjEwLjksIGh0dHBzOi8vbWF0cGxvdGxpYi5vcmcvJkbTWQAAAAlwSFlzAAAPYQAAD2EBqD+naQAARKJJREFUeJzt3X98zfX///H72WZns9nYbGwz2/wmZFmpCGU1vUkqicivflOKd+ojSSQiSb3fIZLkZ4Te+kFpSHojeic/8yOW/M6vDWnazvP7R9+dt2M/7MS78xy36+VyLpzX6/l8vZ6v83jtnPteP84cxhgjAAAAy/j5egAAAAAFIaQAAAArEVIAAICVCCkAAMBKhBQAAGAlQgoAALASIQUAAFiJkAIAAKxESAEAAFYipCCfjIwMORwOvfvuu+5pL7zwghwOh+8GZTGHw6EXXnjhL11nt27dFBoa+peu82LwxWtVlLx9fdSoURd1ud7Ux7bXRJISExPVrVs39/Nly5bJ4XBo2bJlPhvT/9q7774rh8OhjIwMXw8FZyGk4E8bNmyYPvzwQ18PAzivTz/91Log4Gv//ve/9cILL+j48eO+HkqJ9fTTT8vhcOiee+4pst2mTZvUuXNnxcXFyel0KjY2Vp06ddKmTZvytc0LS2vXrv1fDbtEIaSgWJ577jmdPn3aYxoh5Q+nT5/Wc8895+thoAiffvqpBg8e7OthWOXf//63Bg8eXGBI2bp1qyZOnPjXD6oEMcZo5syZSkxM1EcffaQTJ04U2G7evHm66qqrlJ6eru7du2vs2LG6//77tXTpUl111VWaP3/+XzzykiXA1wNAyRAQEKCAAN/vLi6XS2fOnFFQUJCvh+Jm01iAi8HpdPp6CMWWk5Mjl8ulwMDAv3S9y5Yt0549e7RkyRKlpaVp3rx56tq1q0ebH3/8Uffdd5+qVKmi5cuXKyoqyj3viSee0A033KD77rtP69evV5UqVf7S8ZcUHEm5RJw4cUJPPvmkEhMT5XQ6FR0drZtvvln/+c9/3G2aN2+uunXr6ttvv9X111+v4OBgJSUlafz48edd/rnXpDgcDp06dUpTpkyRw+GQw+HwOId9PqNGjdL111+vyMhIBQcHq2HDhvrggw/ytXM4HHrsscc0ffp0XXHFFXI6nVq0aJEkae/everRo4cqVKggp9OpK664Qu+8845H/zNnzuj5559Xw4YNFR4erpCQEN1www1aunRpsca5du1apaWlqXz58u7Xq0ePHvnGmHcqIe8ah8IeZ1u9erVatmyp8PBwlS5dWs2aNdPXX39d3JdQkrRz506lpaUpJCREsbGxGjJkiM79w+bFfa0XL16sJk2aqGzZsgoNDVXNmjX17LPPerTJzs7WoEGDVK1aNTmdTsXHx+vpp59WdnZ2vnZ9+vRRVFSUypQpozZt2mjPnj3F2qa86x/ef/99Pfvss6pYsaJCQkLUpk0b/fzzzx5tv/rqK919992qXLmyezx9+vTxOOrXrVs3vfnmm5JUaC0kacKECapataqcTqeuvvpqrVmzxmP+77//rh9++EH79+8v1nZIxavPubp166bExMR80wu7LmzatGlq2LChgoODFRERoQ4dOuR7nQpaVr9+/SRJSUlJ7tck73qMc69JKUxx9uHivDcV19nXEI0ZM8Zdr82bN0uSfvjhB7Vr104REREKCgpSSkqKFixYkG85mzZt0k033aTg4GBVqlRJQ4cOlcvl8mos06dPV506dXTjjTcqNTVV06dPz9fmlVde0a+//qoJEyZ4BBRJKl++vN566y2dOnVKI0eO9GrdlxPf/2qMi+KRRx7RBx98oMcee0x16tTRkSNHtGLFCm3ZskVXXXWVu92xY8f0t7/9Te3bt1fHjh01e/ZsPfroowoMDMz34VuUqVOn6oEHHtA111yjhx56SJJUtWrVYvd//fXX1aZNG3Xq1ElnzpzRrFmzdPfdd+vjjz9Wq1atPNouWbJEs2fP1mOPPaby5csrMTFRBw8e1LXXXusOMVFRUVq4cKHuv/9+ZWVl6cknn5QkZWVl6e2331bHjh314IMP6sSJE5o0aZLS0tL0zTffqEGDBoWO8dChQ7rlllsUFRWl//u//1PZsmWVkZGhefPmFdonKipKU6dO9Zj2+++/q0+fPh6/6S1ZskS33nqrGjZsqEGDBsnPz0+TJ0/WTTfdpK+++krXXHPNeV/D3NxctWzZUtdee61GjhypRYsWadCgQcrJydGQIUO8eq03bdqk1q1bq379+hoyZIicTqd27Njh8YHjcrnUpk0brVixQg899JBq166tDRs26LXXXtO2bds8Tv098MADmjZtmu69915df/31WrJkSb66ns9LL70kh8OhZ555RocOHdKYMWOUmpqqdevWKTg4WJI0Z84c/frrr3r00UcVGRmpb775Rv/4xz+0Z88ezZkzR5L08MMPa9++fVq8eHG+2uSZMWOGTpw4oYcfflgOh0MjR47UnXfeqZ07d6pUqVKS/gjFtWvXVteuXT0uKr/Q+lyIl156SQMHDlT79u31wAMP6JdfftE//vEPNW3aVN99953Kli1bYL8777xT27Zt08yZM/Xaa6+pfPnykpTvg7Qoxd2Hi/ve5I3Jkyfrt99+00MPPSSn06mIiAht2rRJjRs3VlxcnP7v//5PISEhmj17ttq2bau5c+fqjjvukCQdOHBAN954o3JyctztJkyY4N6niiM7O1tz587V3//+d0lSx44d1b17dx04cEAVK1Z0t/voo4+UmJioG264ocDlNG3aVImJifrkk0/+1OtwWTC4JISHh5tevXoV2aZZs2ZGknn11Vfd07Kzs02DBg1MdHS0OXPmjDHGmF27dhlJZvLkye52gwYNMufuLiEhIaZr165/ary//vqrx/MzZ86YunXrmptuusljuiTj5+dnNm3a5DH9/vvvNzExMebw4cMe0zt06GDCw8Pdy8/JyTHZ2dkebY4dO2YqVKhgevToUeQY58+fbySZNWvWFNlOkhk0aFCh83v27Gn8/f3NkiVLjDHGuFwuU716dZOWlmZcLpe73a+//mqSkpLMzTffXOT6jDGma9euRpJ5/PHH3dNcLpdp1aqVCQwMNL/88ovHcs9W0Gv92muvGUke/c41depU4+fnZ7766iuP6ePHjzeSzNdff22MMWbdunVGkunZs6dHu3vvvfe8r5UxxixdutRIMnFxcSYrK8s9ffbs2UaSef311wvdNmOMGT58uHE4HOann35yT+vVq1e+/deY/+7rkZGR5ujRo+7p//rXv4wk89FHH+VrW5x93pv6nPuadO3a1SQkJORb5rk/gxkZGcbf39+89NJLHu02bNhgAgIC8k0/1yuvvGIkmV27duWbl5CQ4LGdeTVZunSpe1uKuw8X572puPJqEBYWZg4dOuQxr0WLFqZevXrmt99+c09zuVzm+uuvN9WrV3dPe/LJJ40ks3r1ave0Q4cOmfDw8EJfj3N98MEHRpLZvn27McaYrKwsExQUZF577TV3m+PHjxtJ5vbbby9yWW3atDGS3Pv65MmTi/W+c7ngdM8lomzZslq9erX27dtXZLuAgAA9/PDD7ueBgYF6+OGHdejQIX377bf/62G6nf1by7Fjx5SZmakbbrihwEPAzZo1U506ddzPjTGaO3eubrvtNhljdPjwYfcjLS1NmZmZ7uX4+/u7j2C4XC4dPXpUOTk5SklJOe/h5rzfQj/++GP9/vvvf2o733vvPY0dO1YjR47UjTfeKElat26dtm/frnvvvVdHjhxxj/3UqVNq0aKFli9fXuxDz4899pj7/3lHlc6cOaMvvvjCPb04r3Xetv7rX/8qdN1z5sxR7dq1VatWLY/X/KabbpIk9ym0Tz/9VJLUu3dvj/55R7eKq0uXLipTpoz7ebt27RQTE+Ne/rnbdurUKR0+fFjXX3+9jDH67rvvir2ue+65R+XKlXM/z/vNd+fOne5piYmJMsYU6yhKnuLU58+aN2+eXC6X2rdv71GPihUrqnr16sU+pflneLMPF/e9yRt33XWXx1Gfo0ePasmSJWrfvr1OnDjhHs+RI0eUlpam7du3a+/evZL+2D+vvfZaj6OVUVFR6tSpU7HXP336dKWkpKhatWqSpDJlyqhVq1Yep3zyLqQ9ex8uSN78rKysYq//cnJZhpTly5frtttuU2xsrBwOx5+6Q8UYo1GjRqlGjRpyOp2Ki4vTSy+9dPEHW0wjR47Uxo0bFR8fr2uuuUYvvPCCxxtsntjYWIWEhHhMq1GjhiT9pd8P8PHHH+vaa69VUFCQIiIiFBUVpXHjxikzMzNf26SkJI/nv/zyi44fP+4+z3v2o3v37pL+OFWTZ8qUKapfv76CgoIUGRmpqKgoffLJJwWu62zNmjXTXXfdpcGDB6t8+fK6/fbbNXny5HzXXxRm3bp1euSRR9SxY0f17dvXPX379u2SpK5du+Yb/9tvv63s7GxlZmbqzJkzOnDggMcjNzfXvRw/P798F9sVVMvivNb33HOPGjdurAceeEAVKlRQhw4dNHv2bI/Asn37dm3atCnfmPPWmfea//TTT/Lz88t3+q9mzZrFet3yVK9e3eO5w+FQtWrVPLZt9+7d6tatmyIiIhQaGqqoqCg1a9ZMks5b37NVrlzZ43leYDl27JhXYz5bcevzZ23fvl3GGFWvXj1fTbZs2eLxM3CxFXcflor/3uSNc98TduzYIWOMBg4cmG88gwYNkuS5f567b0nF3z+PHz+uTz/9VM2aNdOOHTvcj8aNG2vt2rXatm2bpP+Gj8Lu+slT3DBzubosr0k5deqUrrzySvXo0UN33nnnn1rGE088oc8//1yjRo1SvXr1dPToUR09evQij7T42rdvrxtuuEHz58/X559/rldeeUUjRozQvHnzdOutt/psXAX56quv1KZNGzVt2lRjx45VTEyMSpUqpcmTJ2vGjBn52p97rjjvg7Nz5875rqbPU79+fUl/XFTYrVs3tW3bVv369VN0dLT8/f01fPhw/fjjj0WO0+Fw6IMPPtCqVav00Ucf6bPPPlOPHj306quvatWqVUV+WdexY8d01113qUaNGnr77bcLHP8rr7xS6DUxoaGh+vrrr91HX/Ls2rWrwIsqC1Pc1zo4OFjLly/X0qVL9cknn2jRokV6//33ddNNN+nzzz+Xv7+/XC6X6tWrp9GjRxe4rvj4+GKP62LIzc3VzTffrKNHj+qZZ55RrVq1FBISor1796pbt25eXQjp7+9f4HRznotc/xcK+9LEswOq9Md+5HA4tHDhwgLH/7/8sr/i7sPS/+a9qbD3hKeeekppaWkF9sk76nGh5syZo+zsbL366qt69dVX882fPn26Bg8erPDwcMXExGj9+vVFLm/9+vWKi4tTWFjYRRnfpeayDCm33nprkT8c2dnZGjBggGbOnKnjx4+rbt26GjFihJo3by5J2rJli8aNG6eNGze60/e5yd4XYmJi1LNnT/Xs2VOHDh3SVVddpZdeesljW/ft26dTp055HE3JS/7efPhJhb+Zns/cuXMVFBSkzz77zONWx8mTJxerf94dI7m5uUpNTS2y7QcffKAqVapo3rx5HuPN++2qOK699lpde+21eumllzRjxgx16tRJs2bN0gMPPFBge5fLpU6dOun48eP64osvVLp0aY/5eUcYwsLCihz/lVdeqcWLF3tMO/uiPJfLpZ07d7p/O5fy19Kb19rPz08tWrRQixYtNHr0aA0bNkwDBgzQ0qVLlZqaqqpVq+r7779XixYtiqx9QkKCXC6XfvzxR4/fTrdu3Vpon4Lk/baexxijHTt2uAPohg0btG3bNk2ZMkVdunRxtzv3NZP+/L56IYpTn4KUK1euwO8u+emnnzyeV61aVcYYJSUleayjuC7kNSnuPpynOO9NFyLviFWpUqXOO56EhIR8+5ZU/P1z+vTpqlu3boHvIW+99ZZmzJjh/k6e1q1ba+LEiVqxYoWaNGmSr/1XX32ljIwMj1Pw8HRZnu45n8cee0wrV67UrFmztH79et19991q2bKle8f+6KOPVKVKFX388cdKSkpSYmKiHnjgAZ8dScnNzc13aDs6OlqxsbH5Tk3k5OTorbfecj8/c+aM3nrrLUVFRalhw4ZerTckJORPfVulv7+/HA6Hx2+GGRkZxT7t5u/vr7vuuktz587Vxo0b883/5ZdfPNpKnr8Rr169WitXrjzveo4dO5bvN+m83xqLOuUzePBgffbZZ5o5c2aB4bVhw4aqWrWqRo0apZMnTxY6/nLlyik1NdXjce53svzzn/90/98Yo3/+858qVaqUWrRoIan4r3VB++6529q+fXvt3bu3wC/5On36tE6dOiVJ7g+eN954w6PNmDFj8vUrynvvvedxqPyDDz7Q/v373csvqLbGGL3++uv5lpUXyi/k21X/zC3I56tPQapWrarMzEyP38D379+f70u/7rzzTvn7+2vw4MH59lNjjI4cOVLk2C7kNSnuPuzNe9OFiI6OVvPmzfXWW28VWJ+z3xP+9re/adWqVfrmm2885hd0C/G5fv75Zy1fvlzt27dXu3bt8j26d++uHTt2aPXq1ZKkfv36KTg4WA8//HC+ehw9elSPPPKISpcu7b4dHPldlkdSirJ7925NnjxZu3fvVmxsrKQ/DiEuWrRIkydP1rBhw7Rz50799NNPmjNnjt577z3l5uaqT58+ateunZYsWfKXj/nEiROqVKmS2rVrpyuvvFKhoaH64osvtGbNmnyHI2NjYzVixAhlZGSoRo0aev/997Vu3TpNmDDBfatlcTVs2FBffPGFRo8erdjYWCUlJalRo0bn7deqVSuNHj1aLVu21L333qtDhw7pzTffVLVq1c57aDTPyy+/rKVLl6pRo0Z68MEHVadOHR09elT/+c9/9MUXX7g/dFu3bq158+bpjjvuUKtWrbRr1y6NHz9ederUKfDN9WxTpkzR2LFjdccdd6hq1ao6ceKEJk6cqLCwMP3tb38rsM+GDRv04osvqmnTpjp06JCmTZvmMb9z587y8/PT22+/rVtvvVVXXHGFunfvrri4OO3du1dLly5VWFiYPvroo/O+BkFBQVq0aJG6du2qRo0aaeHChfrkk0/07LPPui8qLO5rPWTIEC1fvlytWrVSQkKCDh06pLFjx6pSpUru3wDvu+8+zZ49W4888oiWLl2qxo0bKzc3Vz/88INmz56tzz77TCkpKWrQoIE6duyosWPHKjMzU9dff73S09O1Y8eO827T2SIiItSkSRN1795dBw8e1JgxY1StWjU9+OCDkqRatWqpatWqeuqpp7R3716FhYVp7ty5BV5HkhfAe/furbS0NPn7+6tDhw5ejcfbW5CLU5+CdOjQQc8884zuuOMO9e7dW7/++qvGjRunGjVqeFzsXLVqVQ0dOlT9+/dXRkaG2rZtqzJlymjXrl2aP3++HnroIT311FOFrifvNRkwYIA6dOigUqVK6bbbbst3zVpBirsPF/e9admyZbrxxhs1aNCgP/3nC9588001adJE9erV04MPPqgqVaro4MGDWrlypfbs2aPvv/9e0h9fZT916lS1bNlSTzzxhPsW5ISEhPO+/8yYMUPGGLVp06bA+X/7298UEBCg6dOnq1GjRqpevbqmTJmiTp06qV69err//vuVlJSkjIwMTZo0SYcPH9bMmTO9+vqGy85ffDeRdSSZ+fPnu59//PHHRpIJCQnxeAQEBJj27dsbY4x58MEHjSSzdetWd79vv/3WSDI//PDDX70JJjs72/Tr189ceeWVpkyZMiYkJMRceeWVZuzYsR7tmjVrZq644gqzdu1ac91115mgoCCTkJBg/vnPf3q0K+4tyD/88INp2rSpCQ4OLvatmXkmTZpkqlevbpxOp6lVq5aZPHlygeuQVOjtiwcPHjS9evUy8fHxplSpUqZixYqmRYsWZsKECe42LpfLDBs2zCQkJBin02mSk5PNxx9/XOhtnmf7z3/+Yzp27GgqV65snE6niY6ONq1btzZr167NN8a8W0jzbtUs7HG27777ztx5550mMjLSOJ1Ok5CQYNq3b2/S09PP+/p17drVhISEmB9//NHccsstpnTp0qZChQpm0KBBJjc316NtcV7r9PR0c/vtt5vY2FgTGBhoYmNjTceOHc22bds8lnXmzBkzYsQIc8UVVxin02nKlStnGjZsaAYPHmwyMzPd7U6fPm169+5tIiMjTUhIiLntttvMzz//7NUtyDNnzjT9+/c30dHRJjg42LRq1crjtmJjjNm8ebNJTU01oaGhpnz58ubBBx8033//fb79Nycnxzz++OMmKirKOBwO97bn7euvvPJKvnGcO1Zvb0Eubn0Kek0+//xzU7duXRMYGGhq1qxppk2bVuDPhzHGzJ071zRp0sT9XlWrVi3Tq1cvj/enwrz44osmLi7O+Pn5edx+e75bkPOcbx8u7nvTRx99ZCSZ8ePHFzneoupljDE//vij6dKli6lYsaIpVaqUiYuLM61btzYffPCBR7v169ebZs2amaCgIBMXF2defPFFM2nSpPPeglyvXj1TuXLlIsfYvHlzEx0dbX7//XeP9XXs2NHExMS436s6duxoNmzYkK8/tyB7chjjgyvDLOJwODR//ny1bdtWkvT++++7//DTuRejhYaGqmLFiho0aJCGDRvmcVvq6dOnVbp0aX3++ee6+eab/8pNKLbmzZvr8OHDBZ4iAWyR91v1nDlz1K5dO18PB3+Bp59+WjNnztSOHTtK1Ffy43+P0z3nSE5OVm5urg4dOlTotwQ2btxYOTk5+vHHH92H6fIuiEtISPjLxgoAl4KlS5dq4MCBBBTkc1mGlJMnT3qcH9+1a5fWrVuniIgI1ahRQ506dVKXLl306quvKjk5Wb/88ovS09NVv359tWrVSqmpqbrqqqvUo0cPjRkzRi6XS7169dLNN9/8p66yv1Tk5uZ6XKBWkNDQ0P/prZEASp5z/04SkOeyvLtn7dq1Sk5OVnJysiSpb9++Sk5O1vPPPy/pj9szu3Tpor///e+qWbOm2rZtqzVr1ri/8MnPz08fffSRypcvr6ZNm6pVq1aqXbu2Zs2a5bNtssHPP/+smJiYIh+jRo3y9TABACXEZX9NCi6e3377TStWrCiyTZUqVfiT5ACAYiGkAAAAK12Wp3sAAID9LpsLZ10ul/bt26cyZcr45CuyAQDAH9+IfOLECcXGxsrPr+hjJZdNSNm3b99f/gfQAABAwX7++WdVqlSpyDaXTUjJ+zPYP//8M39tEgAAH8nKylJ8fLz7c7kol01IyTvFExYWRkgBAMDHinPpBRfOAgAAKxFSAACAlQgpAADASoQUAABgJUIKAACwEiEFAABYiZACAACsREgBAABWIqQAAAArEVIAAICVCCkAAMBKhBQAAGAlQgoAALASIQUAAFiJkAIAAKxESAEAAFYipAAAACsF+HoAJV3WokXa99rrcp065euhQJLLSH4OX48CErWwCbWwR0mrhV9IiGL7PKmwlmk+WT8h5QLte+11mZ8yVIL2uUuav68HADdqYQ9qYY+SVgtz+Bfte20MIaWkcp06JYck4/CTIiN9PZzLWnaOSyd/y1FoUICcAZzJ9CVqYQ9qYY8SV4sjR+QwLp+eKSCkXCyRkaqzYrmvR3FZ+/C7vXry/XUac08DtU2O8/VwLmvUwh7Uwh4lrRabmzSVDv/i0zGUgCgHAAAuR4QUAABgJUIKAACwEiEFAABYiZACAACsREgBAABWIqQAAAArEVIAAICVCCkAAMBKhBQAAGAlQgoAALASIQUAAFiJkAIAAKxESAEAAFYipAAAACsRUgAAgJUIKQAAwEqEFAAAYCVCCgAAsBIhBQAAWImQAgAArBTg6wFcKrJzXPrwu72+HsZlbe1PR//4N+Ooj0cCamEPamGPklaLhByXgnw8Bocxxvh4DH+JrKwshYeHKzMzU2FhYRdtuRsbN5X/kV90OChc97UceNGWCwCAL01d9KLK/5ap3Mgo1f16+UVbrjefxxxJuUB+jj/+DQ0K0Jh7Gvh0LJe7tRlHNW31bnW+trJSEiJ8PZzLGrWwB7WwR0mrReiyAOm3/37O+QIh5SJxBvipbXKcr4dx2Zu2erdSEiKohQWohT2ohT1KUi02B/j+slXfjwAAAKAAhBQAAGAlQgoAALASIQUAAFiJkAIAAKxESAEAAFYipAAAACsRUgAAgJUIKQAAwEqEFAAAYCVCCgAAsBIhBQAAWImQAgAArERIAQAAViKkAAAAKxFSAACAlQgpAADASoQUAABgJa9Dyt69e9W5c2dFRkYqODhY9erV09q1a4vsk52drQEDBighIUFOp1OJiYl65513Cmw7a9YsORwOtW3b1mO6MUbPP/+8YmJiFBwcrNTUVG3fvt3b4QMAgBIiwJvGx44dU+PGjXXjjTdq4cKFioqK0vbt21WuXLki+7Vv314HDx7UpEmTVK1aNe3fv18ulytfu4yMDD311FO64YYb8s0bOXKk3njjDU2ZMkVJSUkaOHCg0tLStHnzZgUFBXmzGQAAoATwKqSMGDFC8fHxmjx5sntaUlJSkX0WLVqkL7/8Ujt37lRERIQkKTExMV+73NxcderUSYMHD9ZXX32l48ePu+cZYzRmzBg999xzuv322yVJ7733nipUqKAPP/xQHTp08GYzAABACeDV6Z4FCxYoJSVFd999t6Kjo5WcnKyJEycWq8/IkSMVFxenGjVq6KmnntLp06c92g0ZMkTR0dG6//778y1j165dOnDggFJTU93TwsPD1ahRI61cudKbTQAAACWEV0dSdu7cqXHjxqlv37569tlntWbNGvXu3VuBgYHq2rVroX1WrFihoKAgzZ8/X4cPH1bPnj115MgR9xGZFStWaNKkSVq3bl2Byzhw4IAkqUKFCh7TK1So4J53ruzsbGVnZ7ufZ2VlebOpAADAx7wKKS6XSykpKRo2bJgkKTk5WRs3btT48eMLDSkul0sOh0PTp09XeHi4JGn06NFq166dxo4dq5ycHN13332aOHGiypcvf4Gb81/Dhw/X4MGDL9ryAADAX8urkBITE6M6dep4TKtdu7bmzp1bZJ+4uDh3QMnrY4zRnj17dOrUKWVkZOi2225zz8+7qDYgIEBbt25VxYoVJUkHDx5UTEyMu93BgwfVoEGDAtfbv39/9e3b1/08KytL8fHxxd9YAADgU16FlMaNG2vr1q0e07Zt26aEhIQi+8yZM0cnT55UaGiou4+fn58qVaokh8OhDRs2ePR57rnndOLECb3++uuKj49XqVKlVLFiRaWnp7tDSVZWllavXq1HH320wPU6nU45nU5vNg8AAFjEqwtn+/Tpo1WrVmnYsGHasWOHZsyYoQkTJqhXr17uNv3791eXLl3cz++9915FRkaqe/fu2rx5s5YvX65+/fqpR48eCg4OVlBQkOrWrevxKFu2rMqUKaO6desqMDBQDodDTz75pIYOHaoFCxZow4YN6tKli2JjY/N9nwoAALg0eHUk5eqrr9b8+fPVv39/DRkyRElJSRozZow6derkbrN//37t3r3b/Tw0NFSLFy/W448/rpSUFEVGRqp9+/YaOnSoVwN9+umnderUKT300EM6fvy4mjRpokWLFvEdKQAAXKK8CimS1Lp1a7Vu3brQ+e+++26+abVq1dLixYuLvY6CluFwODRkyBANGTKk2MsBAAAlF3+7BwAAWImQAgAArERIAQAAViKkAAAAKxFSAACAlQgpAADASoQUAABgJUIKAACwEiEFAABYiZACAACsREgBAABWIqQAAAArEVIAAICVCCkAAMBKhBQAAGAlQgoAALASIQUAAFiJkAIAAKxESAEAAFYipAAAACsRUgAAgJUIKQAAwEqEFAAAYCVCCgAAsBIhBQAAWImQAgAArERIAQAAViKkAAAAKxFSAACAlQgpAADASoQUAABgJUIKAACwEiEFAABYiZAC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AFiJkAIAAKxESAEAAFYipAAAACsRUgAAgJUIKQAAwEqEFAAAYCVCCgAAsBIhBQAAWImQAgAArERIAQAAViKkAAAAKxFSAACAlQgpAADASoQUAABgJUIKAACwEiEFAABYiZACAACsREgBAABWIqQAAAArEVIAAICVCCkAAMBKhBQAAGAlQgoAALASIQUAAFiJkAIAAKxESAEAAFYipAAAACsRUgAAgJUIKQAAwEqEFAAAYCVCCgAAsBIhBQAAWImQAgAArERIAQAAViKkAAAAKxFSAACAlQJ8PYBLRXaOSx9+t9fXw7isrf3p6B//Zhz18UhALexBLexR0mqRkONSkI/H4DDGGB+P4S+RlZWl8PBwZWZmKiws7KItd2PjpvI/8osOB4XrvpYDL9pyAQDwpamLXlT53zKVGxmlul8vv2jL9ebzmCMpF8jP8ce/oUEBGnNPA5+O5XK3NuOopq3erc7XVlZKQoSvh3NZoxb2oBb2KGm1CF0WIP323885XyCkXCTOAD+1TY7z9TAue9NW71ZKQgS1sAC1sAe1sEdJqsXmAN9ftur7EQAAABSAkAIAAKxESAEAAFYipAAAACsRUgAAgJUIKQAAwEqEFAAAYCVCCgAAsBIhBQAAWImQAgAArERIAQAAViKkAAAAKxFSAACAlQgpAADASoQUAABgJUIKAACwEiEFAABYiZACAACsREgBAABW8jqk7N27V507d1ZkZKSCg4NVr149rV27tsg+2dnZGjBggBISEuR0OpWYmKh33nnHPX/ixIm64YYbVK5cOZUrV06pqan65ptvPJbRrVs3ORwOj0fLli29HT4AACghArxpfOzYMTVu3Fg33nijFi5cqKioKG3fvl3lypUrsl/79u118OBBTZo0SdWqVdP+/fvlcrnc85ctW6aOHTvq+uuvV1BQkEaMGKFbbrlFmzZtUlxcnLtdy5YtNXnyZPdzp9PpzfABAEAJ4lVIGTFihOLj4z2CQlJSUpF9Fi1apC+//FI7d+5URESEJCkxMdGjzfTp0z2ev/3225o7d67S09PVpUsX93Sn06mKFSt6M2QAAFBCeXW6Z8GCBUpJSdHdd9+t6OhoJScna+LEicXqM3LkSMXFxalGjRp66qmndPr06UL7/Prrr/r999/doSbPsmXLFB0drZo1a+rRRx/VkSNHCl1Gdna2srKyPB4AAKDk8Cqk7Ny5U+PGjVP16tX12Wef6dFHH1Xv3r01ZcqUIvusWLFCGzdu1Pz58zVmzBh98MEH6tmzZ6F9nnnmGcXGxio1NdU9rWXLlnrvvfeUnp6uESNG6Msvv9Stt96q3NzcApcxfPhwhYeHux/x8fHebCoAAPAxr073uFwupaSkaNiwYZKk5ORkbdy4UePHj1fXrl0L7eNwODR9+nSFh4dLkkaPHq127dpp7NixCg4O9mj/8ssva9asWVq2bJmCgoLc0zt06OD+f7169VS/fn1VrVpVy5YtU4sWLfKtt3///urbt6/7eVZWFkEFAIASxKsjKTExMapTp47HtNq1a2v37t1F9omLi3MHlLw+xhjt2bPHo+2oUaP08ssv6/PPP1f9+vWLHEuVKlVUvnx57dixo8D5TqdTYWFhHg8AAFByeBVSGjdurK1bt3pM27ZtmxISEorss2/fPp08edKjj5+fnypVquSeNnLkSL344otatGiRUlJSzjuWPXv26MiRI4qJifFmEwAAQAnhVUjp06ePVq1apWHDhmnHjh2aMWOGJkyYoF69ernb9O/f3+OOnHvvvVeRkZHq3r27Nm/erOXLl6tfv37q0aOH+1TPiBEjNHDgQL3zzjtKTEzUgQMHdODAAXewOXnypPr166dVq1YpIyND6enpuv3221WtWjWlpaVdjNcBAABYxquQcvXVV2v+/PmaOXOm6tatqxdffFFjxoxRp06d3G3279/vcfonNDRUixcv1vHjx5WSkqJOnTrptttu0xtvvOFuM27cOJ05c0bt2rVTTEyM+zFq1ChJkr+/v9avX682bdqoRo0auv/++9WwYUN99dVXfFcKAACXKK8unJWk1q1bq3Xr1oXOf/fdd/NNq1WrlhYvXlxon4yMjCLXGRwcrM8++6y4QwQAAJcA/nYPAACwEiEFAABYiZACAACsREgBAABWIqQAAAArEVIAAICVCCkAAMBKhBQAAGAlQgoAALASIQUAAFiJkAIAAKxESAEAAFYipAAAACsRUgAAgJUIKQAAwEqEFAAAYCVCCgAAsBIhBQAAWImQAgAArERIAQAAViKkAAAAKxFSAACAlQgpAADASoQUAABgJUIKAACwEiEFAABYiZACAACsREgBAABWIqQAAAArEVIAAICVCCkAAMBKhBQAAGAlQgoAALASIQUAAFiJkAIAAKxESAEA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", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "ax = tiles.plot(facecolor=\"none\", edgecolor=\"tab:blue\")\n", + "aoi_poly = shapely.geometry.box(aoi[\"west\"], aoi[\"south\"], aoi[\"east\"], aoi[\"north\"])\n", + "aoi_gdf = gpd.GeoDataFrame(geometry=[aoi_poly], crs=\"EPSG:4326\")\n", + "aoi_gdf.to_crs(tiles.crs).plot(ax=ax, facecolor=\"none\", edgecolor=\"tab:red\", linewidth=2)\n", + "ax.set_title(\"split_area size-based path: blue tiles, red AOI\")\n", + "plt.show()" + ] + }, + { + "cell_type": "markdown", + "id": "18b53e23", + "metadata": {}, + "source": [ + "## Case 2: `split_points` default quadtree path\n", + "\n", + "This follows the `tile_grid is None` branch:\n", + "\n", + "- centroids are computed\n", + "- recursive quadtree split is applied\n", + "- groups are capped by `max_points`" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "id": "485a4ff8", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "input rows: 300\n", + "default quadtree: 8 groups\n", + "sizes: [63, 38, 28, 21, 32, 24, 43, 51]\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "C:\\Git_projects\\openeo-python-client\\openeo\\extra\\job_management\\_job_splitting.py:481: UserWarning: Geometry is in a geographic CRS. Results from 'centroid' are likely incorrect. Use 'GeoSeries.to_crs()' to re-project geometries to a projected CRS before this operation.\n", + "\n", + " centroids = points.geometry.centroid\n" + ] + } + ], + "source": [ + "rng = np.random.default_rng(42)\n", + "\n", + "cluster_a = rng.normal(loc=(3.65, 51.03), scale=0.01, size=(150, 2))\n", + "cluster_b = rng.normal(loc=(3.75, 51.09), scale=0.01, size=(150, 2))\n", + "coords = np.vstack([cluster_a, cluster_b])\n", + "\n", + "points = gpd.GeoDataFrame(\n", + " geometry=[shapely.geometry.Point(xy) for xy in coords],\n", + " crs=\"EPSG:4326\",\n", + ")\n", + "\n", + "groups = split_points(points, max_points=100)\n", + "print(\"input rows:\", len(points))\n", + "print_group_sizes(groups, \"default quadtree\")" + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "id": "a23d5bf1", + "metadata": {}, + "outputs": [ + { + "data": { + "image/png": "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4+3u06ZNQ0FBAX766Sd4e3tbKmqz2pN/9uzZyM3NNXnPBgQEYOnSpThx4oQlYwNoX3aNRoNhw4bZ5Pv1rgdlr6+vR319vdXer2TDrN2+lCA+Pl5OnToler1ecnNzJT4+XlQqlfz4448iIjJ79myJj4837i8uLhY3NzdZuHCh5OXlydGjR8XHx0c++OCDB34PS572skT+zMxMcXR0lHXr1klBQYF89dVX0qlTJ/nyyy9tPvvcuXOle/fuxlvdDx06JF27dpVly5ZZNftdo0aNkpiYmGa/5saNG8XT01MOHz4subm58sILL1jkVndzZ6+rq5OJEydKYGCgZGdnm9zefPeInC3nb46lTntZIvuhQ4fEyclJdu/eLQUFBbJt2zZRq9Xy888/23z2p556SkJDQyU1NVWKiopk79694uzsLDt27DBrdrIvLD8dYP78+aLT6USj0Ui3bt0kKirK+GYWaXxzzp071+Q1v/76q0RERIhWq5Xg4GBZt25di6ezLFl+LJX/yJEjMmDAANFqtdK/f3/ZvXu3XWSvrq6WxYsXS1BQkDg7O0twcLCsWrXK7L+EHyb7xYsXBYDJvqYMBoOsXr1afH19RavVSlRUlOTl5Zk1tyWy6/V6AdDsSE1Ntfn8zbFU+bFU9j179kifPn3E2dlZBg0aJElJSXaRvby8XObNmycBAQHi7Ows/fr1k82bNxsvPCdlUomY+bG0RERERDaM1/wQERGRorD8EBERkaKw/BAREZGisPwQERGRorD8EBERkaKw/BAREZGisPwQERGRorD8EBERkaKw/BAREZGisPwQERGRorD8EBERkaKw/BAREZGi/B9Zs7NEVQGz5AAAAABJRU5ErkJggg==", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "plot_groups(groups, \"split_points default quadtree\")\n", + "plt.show()" + ] + }, + { + "cell_type": "markdown", + "id": "b81f052b", + "metadata": {}, + "source": [ + "## Case 3: tile-grid path (`gpd.sjoin`) and why `centroids_gdf` matters\n", + "\n", + "This follows the `tile_grid is not None` branch:\n", + "\n", + "- use centroid geometries (`centroids_gdf`)\n", + "- optional CRS reprojection of centroids to tile-grid CRS\n", + "- spatial join: each centroid gets a matching tile (`index_right`)\n", + "- de-duplicate centroids that match multiple tiles on boundaries\n", + "- split each tile group separately\n", + "\n", + "We will inspect the join table directly before calling `split_points`." + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "id": "b271d9c6", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "joined rows: 300\n", + "input rows: 300\n", + "rows with no tile match (index_right is NaN): 0\n" + ] + }, + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
geometryindex_right
0POINT (406655.351 6624761.497)5
1POINT (407151.54 6628267.088)6
2POINT (404144.259 6624297.52)0
3POINT (406458.453 6626042.353)5
4POINT (406297.438 6625092.318)5
\n", + "
" + ], + "text/plain": [ + " geometry index_right\n", + "0 POINT (406655.351 6624761.497) 5\n", + "1 POINT (407151.54 6628267.088) 6\n", + "2 POINT (404144.259 6624297.52) 0\n", + "3 POINT (406458.453 6626042.353) 5\n", + "4 POINT (406297.438 6625092.318) 5" + ] + }, + "execution_count": 11, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# Reuse 'tiles' from Case 1 (EPSG:3857)\n", + "points_3857 = points.to_crs(tiles.crs)\n", + "centroids_gdf = gpd.GeoDataFrame(geometry=points_3857.geometry.centroid, crs=points_3857.crs)\n", + "\n", + "joined = gpd.sjoin(\n", + " centroids_gdf,\n", + " tiles[[\"geometry\"]].reset_index(drop=True),\n", + " how=\"left\",\n", + " predicate=\"intersects\",\n", + ")\n", + "\n", + "print(\"joined rows:\", len(joined))\n", + "print(\"input rows:\", len(points_3857))\n", + "print(\"rows with no tile match (index_right is NaN):\", joined[\"index_right\"].isna().sum())\n", + "joined.head()" + ] + }, + { + "cell_type": "markdown", + "id": "fe2b9477", + "metadata": {}, + "source": [ + "### Debug view: centroid-to-tile assignment before splitting\n", + "\n", + "This plot visualizes the exact `gpd.sjoin` output used by the tile-grid branch:\n", + "\n", + "- each centroid is colored by `index_right` (the matched tile index)\n", + "- centroids with no tile match are highlighted separately\n", + "\n", + "If this looks wrong, the resulting split groups will also be wrong." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "342185d1", + "metadata": {}, + "outputs": [], + "source": [ + "# Visual check of tile assignment from sjoin (`index_right`)\n", + "joined_plot = joined.copy()\n", + "joined_plot[\"tile_label\"] = joined_plot[\"index_right\"].apply(\n", + " lambda v: \"unmatched\" if (v is None or (isinstance(v, float) and np.isnan(v))) else f\"tile_{int(v)}\"\n", + ")\n", + "\n", + "ax = tiles.plot(facecolor=\"none\", edgecolor=\"black\", linewidth=0.8)\n", + "\n", + "for label, sub in joined_plot.groupby(\"tile_label\"):\n", + " color = \"red\" if label == \"unmatched\" else None\n", + " sub.plot(ax=ax, markersize=18, label=f\"{label} (n={len(sub)})\", color=color)\n", + "\n", + "ax.set_title(\"Centroid -> tile assignment (from sjoin)\")\n", + "ax.legend(loc=\"center left\", bbox_to_anchor=(1.0, 0.5))\n", + "plt.show()" + ] + }, + { + "cell_type": "code", + "execution_count": 16, + "id": "d437520d", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "tile-grid split: 16 groups\n", + "sizes: [25, 17, 45, 39, 16, 6, 1, 1, 9, 7, 38, 40, 18, 27, 9, 2]\n" + ] + }, + { + "data": { + "image/png": "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FjTkv//9L06fPo39+/fj33//xRNPPIGHHnqICxJ+/PFHBAcH4/DhwwgKCkJgYCCef/55mpHtQGprayGTyXSOeXt7QywW670NXlNTg08//ZT7vKqqCp9++im8vLwaDKrqEwqFKC0tbfS4v//+e51cyr///htnz57lAm9fX19ERERg165dOve5dOkSfv31Vzz88MONvndd2mC4/nMpKSnRm9mMiIgAgAbTC4KCguDn54ctW7aguroa0dHRADQBbmpqKr777jsMHjy4wdqnxsbWVE3p19vbGyNGjMCnn36KvLw8vfN37twxuy/tjO4nn3yic/zDDz+0eFx1ab+H6pY7A4CtW7c2qV9CCGmKTjkja0pWVhbi4uKQlZUFsVgMQPN235EjRxAXF4e3334baWlpyMzMxLfffovdu3ejtrYWixcvxtSpU/H777+38jMgzUEul8Pf3x9Tp05F//794eTkhN9++w3nzp3Dpk2bdNqKxWJs2LABGRkZ6NmzJ77++mskJydjx44dRss1GTNw4ED89ttv2Lx5M8RiMYKCgnDfffeZfX1ISAiGDh2K//znP1CpVNi6dSs8PDywbNkyrs17772H8ePH4/7778ecOXO48lsuLi7NtnVrREQEbGxssGHDBshkMggEAjz44IPYt28fPvnkEzz22GPo3r075HI5PvvsMzg7O5sVRD/wwAPYv38/wsPDudzVAQMGQCgU4vr163jmmWca7EP7x8XChQsxbtw42NjY4KmnnmraE4bx5+zt7W3W9R9//DGGDh2K8PBwvPDCCwgODkZ+fj5Onz6N7Oxs/PPPP2b1M3DgQEyZMgVbt25FUVERV35L+y5BY2eOIyIi8PTTT+OTTz6BTCbDkCFDcOzYsSbP9BJCSFNQIFvPxYsXUVtbyy3W0VKpVPDw8ACgyWdTqVTYvXs3127nzp0YOHAgUlJSaPFDB+Do6IgXX3wRv/76Kw4ePAi1Wo2QkBB88skn+M9//qPT1s3NDbt27cKCBQvw2WefoUuXLvjoo4/wwgsvWHzfzZs3Y+7cudz2oDNmzLAokH3uuefA4/GwdetWFBQUICoqCh999BF8fX25NqNHj8aRI0ewevVqrFq1Cra2thg+fDg2bNjQbCkyPj4+2L59O9avX485c+agtrYWf/zxB4YPH46///4b+/fvR35+PlxcXBAVFYW9e/eadW9t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6oXAwEDccMMNOHXqlOq+oqIiTJ06Ff7+/oiKisKiRYtgNptVbb799luMGDECvr6+GDRoENasWWP3/Ndeew39+/eHn58fRo8ejZ9++kn1emvGcr5o61xIkmT3sXHjRlUbzoV7HM3FW2+9hQkTJiA4OBiSJKGystLuPr1ej1mzZiE4OBihoaG48847cfbsWVWb/fv34/LLL4efnx/69u2L559/3q6fDz74ABdffDH8/PyQkpKCTz/9VPW6EAJLlixBTEwMdDodJk2ahIKCgg753nuSts5D//797f5OLF++XNWG8+Ce5nOh1+uxcOFCJCUlQafTIT4+Hvfffz+qqqpU9/F3BZEHER7mp59+Ev379xdDhw4VDzzwgHL97rvvFn379hVff/21+Pnnn8Wll14qLrvsMuV1s9kshgwZIiZNmiT27dsnPv30UxERESEWL16stDly5Ijw9/cXDz30kMjLyxOvvvqq8PLyEp9//rnSZuPGjcLHx0f861//Er/++quYN2+eCA0NFadOnWr1WM4XbZ0LIYQAIFavXi1KSkqUj9raWuV1zoV7nM3Fiy++KJ599lnx7LPPCgCioqLC7t4pU6aIYcOGiR9//FFkZWWJQYMGiVtvvVV5vaqqSvTu3VvMmjVLHDhwQGzYsEHodDrx5ptvKm127twpvLy8xPPPPy/y8vLEX//6V+Ht7S1yc3OVNsuXLxchISHiP//5j/jll1/EtddeKwYMGKCad0/Xnnno16+fePrpp1V/J86ePau8znlwj6O5yM3NFddff73IzMwUhw8fFl9//bVITEwUN9xwg3Iff1cQeRaPCmSNRqNITEwU27ZtE+PHj1f+51RZWSm8vb3FBx98oLT97bffBACxa9cuIYQQn376qdBoNKK0tFRp88Ybb4jg4GBRX18vhBDikUceEZdcconqmTfffLPIyMhQvk5LSxP33nuv8nVjY6OIjY0Vzz77bKvHcj5oz1wIIQeyW7Zscdo/56L1nM2FrW+++cZhAJWXlycAiD179ijXPvvsMyFJkjh58qQQQojXX39dhIWFKX9PhBDi0UcfFUlJScrXN910k5g6daqq79GjR4sFCxYIIYSwWCwiOjpa/P3vf1der6ysFL6+vmLDhg1t/t57kvbMgxByIPviiy867Z/z0HqtmQurTZs2CR8fH2EymYQQ/F1B5Gk8KrXg3nvvxdSpUzFp0iTV9ezsbJhMJtX1iy++GPHx8di1axcAYNeuXUhJSUHv3r2VNhkZGTAYDPj111+VNs37zsjIUPpoaGhAdna2qo1Go8GkSZOUNq0Zy/mgPXNh20dERATS0tLwr3/9C8KmpDHnovWczUVr7Nq1C6GhoRg1apRybdKkSdBoNNi9e7fSZty4cfDx8VHaZGRkID8/HxUVFUobV/N19OhRlJaWqtqEhIRg9OjR581ctGcerJYvX45evXohNTUVf//731VvZ3MeWs+duaiqqkJwcDC0Wi0A/q4g8jTa7h5Aa23cuBF79+7Fnj177F4rLS2Fj48PQkNDVdd79+6N0tJSpY3t/5isr1tfc9XGYDCgtrYWFRUVaGxsdNjm4MGDrR6Lp2vvXADA008/jSuvvBL+/v748ssvcc899+Ds2bO4//77lX44Fy1zNRetUVpaiqioKNU1rVaL8PBw1d+LAQMGqNrY/t0JCwtzOl+2fdje56iNJ2vvPADA/fffjxEjRiA8PBw//PADFi9ejJKSEqxcuRIA56G13JmL8vJyLFu2DPPnz1eu8XcFkWfxiED2999/xwMPPIBt27bBz8+vu4dzQeuoufi///s/5c+pqamorq7G3//+dyWQpZbx70XP0FHz8NBDDyl/Hjp0KHx8fLBgwQI8++yzPA61ldyZC4PBgKlTpyI5ORlPPvlk1wyQiDqcR6QWZGdno6ysDCNGjIBWq4VWq8WOHTvwyiuvQKvVonfv3mhoaLDbCXzq1ClER0cDAKKjo+12g1q/bqlNcHAwdDodIiIi4OXl5bCNbR8tjcWTdcRcODJ69GicOHEC9fX1ADgXrdHSXDQ2NrbYR3R0NMrKylTXzGYz9Hp9h/zdsX3d9j5HbTxVR8yDI6NHj4bZbMaxY8cAcB5ao7VzYTQaMWXKFAQFBWHLli3w9vZW+uDvCiLP4hGB7MSJE5Gbm4ucnBzlY9SoUZg1a5byZ29vb3z99dfKPfn5+SgqKsKYMWMAAGPGjEFubq7ql/a2bdsQHByM5ORkpY1tH9Y21j58fHwwcuRIVRuLxYKvv/5aaTNy5MgWx+LJOmIuHMnJyUFYWJiy8sS5aFlLc+Hl5dViH2PGjEFlZSWys7OVa9u3b4fFYsHo0aOVNt999x1MJpPSZtu2bUhKSkJYWJjSxtV8DRgwANHR0ao2BoMBu3fv9vi56Ih5cCQnJwcajUZJ/eA8tKw1c2EwGDB58mT4+PggMzPTbuWWvyuIPEx37zZrq+Y7Ue+++24RHx8vtm/fLn7++WcxZswYMWbMGOV1a0mVyZMni5ycHPH555+LyMhIhyVVFi1aJH777Tfx2muvOSyp4uvrK9asWSPy8vLE/PnzRWhoqGqHa0tjOd+4OxeZmZni7bffFrm5uaKgoEC8/vrrwt/fXyxZskRpw7lom+ZzUVJSIvbt2yfefvttAUB89913Yt++feLMmTNKmylTpojU1FSxe/du8f3334vExERV+a3KykrRu3dvMXv2bHHgwAGxceNG4e/vb1f2SavVihUrVojffvtNLF261GHZp9DQUPHxxx+L/fv3ixkzZpyXZZ+EcH8efvjhB/Hiiy+KnJwcUVhYKN5//30RGRkpbr/9dqUPzkPb2M5FVVWVGD16tEhJSRGHDx9WlTozm81CCP6uIPI0500gW1tbK+655x4RFhYm/P39xR//+EdRUlKiuufYsWPi6quvFjqdTkRERIj//d//VUquWH3zzTdi+PDhwsfHRwwcOFCsXr3a7tmvvvqqiI+PFz4+PiItLU38+OOPqtdbM5bzibtz8dlnn4nhw4eLwMBAERAQIIYNGyZWrVolGhsbVf1yLtzXfC6WLl0qANh92P4sz5w5I2699VYRGBgogoODxdy5c4XRaFT1+8svv4ixY8cKX19fERcXJ5YvX2737E2bNomLLrpI+Pj4iEsuuUT897//Vb1usVjE//3f/4nevXsLX19fMXHiRJGfn9+h339P4e48ZGdni9GjR4uQkBDh5+cnBg8eLP72t7+Juro6Vb+cB/fZzoW1/Jmjj6NHjyr38HcFkeeQhLCpeURERERE5CE8IkeWiIiIiKg5BrJERERE5JEYyBIRERGRR2IgS0REREQeiYEsEREREXkkBrJERERE5JEYyBIRERGRR2IgS0REREQeiYEsEREREXkkBrJERERE5JEYyBIRERGRR2IgS0REREQe6f8DJnJ6fMhwzO4AAAAASUVORK5CYII=", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "tile_groups = split_points(points_3857, max_points=50, tile_grid=tiles)\n", + "print_group_sizes(tile_groups, \"tile-grid split\")\n", + "\n", + "ax = tiles.plot(facecolor=\"none\", edgecolor=\"lightgrey\")\n", + "plot_groups(tile_groups, \"split_points with tile_grid\", ax=ax, markersize=14)\n", + "plt.show()" + ] + }, + { + "cell_type": "markdown", + "id": "d5b055e4", + "metadata": {}, + "source": [ + "## Case 4: quadtree no-progress fallback (`any(len(q) == len(idx))`)\n", + "\n", + "When all points coincide (or all land in one quadrant), recursive quartering\n", + "cannot reduce the set size. The implementation then falls back to plain\n", + "chunking by `max_points` to guarantee termination." + ] + }, + { + "cell_type": "code", + "execution_count": 17, + "id": "3350f55c", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "coincident fallback: 3 groups\n", + "sizes: [10, 10, 5]\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "C:\\Git_projects\\openeo-python-client\\openeo\\extra\\job_management\\_job_splitting.py:481: UserWarning: Geometry is in a geographic CRS. Results from 'centroid' are likely incorrect. Use 'GeoSeries.to_crs()' to re-project geometries to a projected CRS before this operation.\n", + "\n", + " centroids = points.geometry.centroid\n" + ] + } + ], + "source": [ + "coincident = gpd.GeoDataFrame(\n", + " geometry=[shapely.geometry.Point(0, 0)] * 25,\n", + " crs=\"EPSG:4326\",\n", + ")\n", + "coincident_groups = split_points(coincident, max_points=10)\n", + "print_group_sizes(coincident_groups, \"coincident fallback\")" + ] + }, + { + "cell_type": "markdown", + "id": "3a4fa0f8", + "metadata": {}, + "source": [ + "## Case 5: row semantics (`Point` and `MultiPoint`)\n", + "\n", + "`split_points` counts rows (features), not embedded coordinates.\n", + "A single `MultiPoint` row is still one feature for `max_points`." + ] + }, + { + "cell_type": "code", + "execution_count": 19, + "id": "f5d1b42e", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "row count: 2\n", + "group row counts: [2]\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "C:\\Git_projects\\openeo-python-client\\openeo\\extra\\job_management\\_job_splitting.py:481: UserWarning: Geometry is in a geographic CRS. Results from 'centroid' are likely incorrect. Use 'GeoSeries.to_crs()' to re-project geometries to a projected CRS before this operation.\n", + "\n", + " centroids = points.geometry.centroid\n" + ] + } + ], + "source": [ + "mixed = gpd.GeoDataFrame(\n", + " geometry=[\n", + " shapely.geometry.MultiPoint([(0, 0), (0.1, 0.1), (0.2, 0.2)]),\n", + " shapely.geometry.Point(0.5, 0.5),\n", + " ],\n", + " crs=\"EPSG:4326\",\n", + ")\n", + "mixed_groups = split_points(mixed, max_points=10)\n", + "print(\"row count:\", len(mixed))\n", + "print(\"group row counts:\", [len(g) for g in mixed_groups])" + ] + }, + { + "cell_type": "markdown", + "id": "322f339d", + "metadata": {}, + "source": [ + "## Case 6: centroid on shared tile boundary (duplicate join rows)\n", + "\n", + "A point exactly on a shared edge can intersect more than one tile, producing\n", + "multiple rows in `sjoin`. The implementation then keeps only the first match\n", + "for that centroid index." + ] + }, + { + "cell_type": "code", + "execution_count": 20, + "id": "e7856a74", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "raw sjoin rows: 3\n", + " geometry index_right\n", + "0 POINT (1 0.5) 0\n", + "0 POINT (1 0.5) 1\n", + "1 POINT (0.5 0.5) 0\n", + "rows after dedup-by-left-index: 2\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "C:\\Users\\VROMPAYH\\AppData\\Local\\Temp\\ipykernel_31420\\1828992808.py:18: UserWarning: Geometry is in a geographic CRS. Results from 'centroid' are likely incorrect. Use 'GeoSeries.to_crs()' to re-project geometries to a projected CRS before this operation.\n", + "\n", + " gpd.GeoDataFrame(geometry=boundary_points.geometry.centroid, crs=boundary_points.crs),\n" + ] + } + ], + "source": [ + "boundary_tiles = gpd.GeoDataFrame(\n", + " geometry=[\n", + " shapely.geometry.box(0, 0, 1, 1),\n", + " shapely.geometry.box(1, 0, 2, 1),\n", + " ],\n", + " crs=\"EPSG:4326\",\n", + ")\n", + "\n", + "boundary_points = gpd.GeoDataFrame(\n", + " geometry=[\n", + " shapely.geometry.Point(1, 0.5), # exactly on shared boundary x=1\n", + " shapely.geometry.Point(0.5, 0.5),\n", + " ],\n", + " crs=\"EPSG:4326\",\n", + ")\n", + "\n", + "raw_join = gpd.sjoin(\n", + " gpd.GeoDataFrame(geometry=boundary_points.geometry.centroid, crs=boundary_points.crs),\n", + " boundary_tiles[[\"geometry\"]].reset_index(drop=True),\n", + " how=\"left\",\n", + " predicate=\"intersects\",\n", + ")\n", + "\n", + "print(\"raw sjoin rows:\", len(raw_join))\n", + "print(raw_join)\n", + "print(\"rows after dedup-by-left-index:\", len(raw_join.loc[~raw_join.index.duplicated(keep=\"first\")]))" + ] + }, + { + "cell_type": "markdown", + "id": "1282f69d", + "metadata": {}, + "source": [ + "## Case 7: unmatched points (`index_right = NaN`) and CRS reprojection\n", + "\n", + "This demonstrates two behaviors:\n", + "\n", + "- points outside all tiles are still kept (left join) and split separately\n", + "- when point CRS differs from tile-grid CRS, centroids are reprojected before\n", + " the spatial join" + ] + }, + { + "cell_type": "code", + "execution_count": 21, + "id": "55554db2", + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "C:\\Git_projects\\openeo-python-client\\openeo\\extra\\job_management\\_job_splitting.py:481: UserWarning: Geometry is in a geographic CRS. Results from 'centroid' are likely incorrect. Use 'GeoSeries.to_crs()' to re-project geometries to a projected CRS before this operation.\n", + "\n", + " centroids = points.geometry.centroid\n" + ] + }, + { + "ename": "ValueError", + "evalue": "Categorical categories cannot be null", + "output_type": "error", + "traceback": [ + "\u001b[31m---------------------------------------------------------------------------\u001b[39m", + "\u001b[31mValueError\u001b[39m Traceback (most recent call last)", + "\u001b[36mCell\u001b[39m\u001b[36m \u001b[39m\u001b[32mIn[21]\u001b[39m\u001b[32m, line 13\u001b[39m\n\u001b[32m 9\u001b[39m geometry=[shapely.geometry.Point(\u001b[32m0.1\u001b[39m, \u001b[32m0.1\u001b[39m), shapely.geometry.Point(\u001b[32m50\u001b[39m, \u001b[32m50\u001b[39m)],\n\u001b[32m 10\u001b[39m crs=\u001b[33m\"EPSG:4326\"\u001b[39m,\n\u001b[32m 11\u001b[39m )\n\u001b[32m 12\u001b[39m \n\u001b[32m---> \u001b[39m\u001b[32m13\u001b[39m mixed_groups = split_points(points_wgs84, max_points=\u001b[32m10\u001b[39m, tile_grid=wm_tile_grid)\n\u001b[32m 14\u001b[39m print_group_sizes(mixed_groups, \u001b[33m\"CRS mismatch + unmatched point\"\u001b[39m)\n\u001b[32m 15\u001b[39m \u001b[38;5;28;01mfor\u001b[39;00m i, g \u001b[38;5;28;01min\u001b[39;00m enumerate(mixed_groups):\n\u001b[32m 16\u001b[39m print(f\"group {i} bounds:\", g.total_bounds)\n", + "\u001b[36mFile \u001b[39m\u001b[32mC:\\Git_projects\\openeo-python-client\\openeo\\extra\\job_management\\_job_splitting.py:496\u001b[39m, in \u001b[36msplit_points\u001b[39m\u001b[34m(points, max_points, tile_grid)\u001b[39m\n\u001b[32m 493\u001b[39m \u001b[38;5;66;03m# A centroid on a shared tile edge can match multiple tiles: keep just the first match.\u001b[39;00m\n\u001b[32m 494\u001b[39m joined = joined.loc[~joined.index.duplicated(keep=\u001b[33m\"\u001b[39m\u001b[33mfirst\u001b[39m\u001b[33m\"\u001b[39m)]\n\u001b[32m--> \u001b[39m\u001b[32m496\u001b[39m \u001b[38;5;28;01mfor\u001b[39;00m _, idx \u001b[38;5;129;01min\u001b[39;00m \u001b[30;43mjoined\u001b[39;49m\u001b[30;43m.\u001b[39;49m\u001b[30;43mgroupby\u001b[39;49m\u001b[30;43m(\u001b[39;49m\u001b[30;43m\"\u001b[39;49m\u001b[30;43mindex_right\u001b[39;49m\u001b[30;43m\"\u001b[39;49m\u001b[30;43m,\u001b[39;49m\u001b[30;43m \u001b[39;49m\u001b[30;43mdropna\u001b[39;49m\u001b[30;43m=\u001b[39;49m\u001b[30;43;01mFalse\u001b[39;49;00m\u001b[30;43m)\u001b[39;49m\u001b[30;43m.\u001b[39;49m\u001b[30;43mgroups\u001b[39;49m.items():\n\u001b[32m 497\u001b[39m _quadtree_split_indices(centroids, idx, max_points, index_groups)\n\u001b[32m 499\u001b[39m \u001b[38;5;28;01mreturn\u001b[39;00m [points.loc[idx].reset_index(drop=\u001b[38;5;28;01mTrue\u001b[39;00m) \u001b[38;5;28;01mfor\u001b[39;00m idx \u001b[38;5;129;01min\u001b[39;00m index_groups]\n", + "\u001b[36mFile \u001b[39m\u001b[32m~\\AppData\\Local\\anaconda3\\envs\\openeopythonclient\\Lib\\site-packages\\pandas\\core\\groupby\\groupby.py:853\u001b[39m, in \u001b[36mBaseGroupBy.groups\u001b[39m\u001b[34m(self)\u001b[39m\n\u001b[32m 807\u001b[39m \u001b[38;5;129m@final\u001b[39m\n\u001b[32m 808\u001b[39m \u001b[38;5;129m@property\u001b[39m\n\u001b[32m 809\u001b[39m \u001b[38;5;28;01mdef\u001b[39;00m\u001b[38;5;250m \u001b[39m\u001b[34mgroups\u001b[39m(\u001b[38;5;28mself\u001b[39m) -> \u001b[38;5;28mdict\u001b[39m[Hashable, np.ndarray]:\n\u001b[32m 810\u001b[39m \u001b[38;5;250m \u001b[39m\u001b[33;03m\"\"\"\u001b[39;00m\n\u001b[32m 811\u001b[39m \u001b[33;03m Dict {group name -> group labels}.\u001b[39;00m\n\u001b[32m 812\u001b[39m \n\u001b[32m (...)\u001b[39m\u001b[32m 851\u001b[39m \u001b[33;03m {Timestamp('2023-01-01 00:00:00'): 2, Timestamp('2023-02-01 00:00:00'): 4}\u001b[39;00m\n\u001b[32m 852\u001b[39m \u001b[33;03m \"\"\"\u001b[39;00m\n\u001b[32m--> \u001b[39m\u001b[32m853\u001b[39m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[30;43mself\u001b[39;49m\u001b[30;43m.\u001b[39;49m\u001b[30;43m_grouper\u001b[39;49m\u001b[30;43m.\u001b[39;49m\u001b[30;43mgroups\u001b[39;49m\n", + "\u001b[36mFile \u001b[39m\u001b[32mpandas/_libs/properties.pyx:36\u001b[39m, in \u001b[36mpandas._libs.properties.CachedProperty.__get__\u001b[39m\u001b[34m()\u001b[39m\n\u001b[32m---> \u001b[39m\u001b[32m36\u001b[39m \u001b[33m'Could not get source, probably due dynamically evaluated source code.'\u001b[39m\n", + "\u001b[36mFile \u001b[39m\u001b[32m~\\AppData\\Local\\anaconda3\\envs\\openeopythonclient\\Lib\\site-packages\\pandas\\core\\groupby\\ops.py:726\u001b[39m, in \u001b[36mBaseGrouper.groups\u001b[39m\u001b[34m(self)\u001b[39m\n\u001b[32m 724\u001b[39m \u001b[38;5;250m\u001b[39m\u001b[33;03m\"\"\"dict {group name -> group labels}\"\"\"\u001b[39;00m\n\u001b[32m 725\u001b[39m \u001b[38;5;28;01mif\u001b[39;00m \u001b[38;5;28mlen\u001b[39m(\u001b[38;5;28mself\u001b[39m.groupings) == \u001b[32m1\u001b[39m:\n\u001b[32m--> \u001b[39m\u001b[32m726\u001b[39m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[30;43mself\u001b[39;49m\u001b[30;43m.\u001b[39;49m\u001b[30;43mgroupings\u001b[39;49m\u001b[30;43m[\u001b[39;49m\u001b[30;43m0\u001b[39;49m\u001b[30;43m]\u001b[39;49m\u001b[30;43m.\u001b[39;49m\u001b[30;43mgroups\u001b[39;49m\n\u001b[32m 727\u001b[39m \u001b[38;5;28;01melse\u001b[39;00m:\n\u001b[32m 728\u001b[39m to_groupby = []\n", + "\u001b[36mFile \u001b[39m\u001b[32mpandas/_libs/properties.pyx:36\u001b[39m, in \u001b[36mpandas._libs.properties.CachedProperty.__get__\u001b[39m\u001b[34m()\u001b[39m\n\u001b[32m---> \u001b[39m\u001b[32m36\u001b[39m \u001b[33m'Could not get source, probably due dynamically evaluated source code.'\u001b[39m\n", + "\u001b[36mFile \u001b[39m\u001b[32m~\\AppData\\Local\\anaconda3\\envs\\openeopythonclient\\Lib\\site-packages\\pandas\\core\\groupby\\grouper.py:842\u001b[39m, in \u001b[36mGrouping.groups\u001b[39m\u001b[34m(self)\u001b[39m\n\u001b[32m 840\u001b[39m \u001b[38;5;129m@cache_readonly\u001b[39m\n\u001b[32m 841\u001b[39m \u001b[38;5;28;01mdef\u001b[39;00m\u001b[38;5;250m \u001b[39m\u001b[34mgroups\u001b[39m(\u001b[38;5;28mself\u001b[39m) -> \u001b[38;5;28mdict\u001b[39m[Hashable, np.ndarray]:\n\u001b[32m--> \u001b[39m\u001b[32m842\u001b[39m cats = \u001b[30;43mCategorical\u001b[39;49m\u001b[30;43m.\u001b[39;49m\u001b[30;43mfrom_codes\u001b[39;49m\u001b[30;43m(\u001b[39;49m\u001b[30;43mself\u001b[39;49m\u001b[30;43m.\u001b[39;49m\u001b[30;43mcodes\u001b[39;49m\u001b[30;43m,\u001b[39;49m\u001b[30;43m \u001b[39;49m\u001b[30;43mself\u001b[39;49m\u001b[30;43m.\u001b[39;49m\u001b[30;43m_group_index\u001b[39;49m\u001b[30;43m,\u001b[39;49m\u001b[30;43m \u001b[39;49m\u001b[30;43mvalidate\u001b[39;49m\u001b[30;43m=\u001b[39;49m\u001b[30;43;01mFalse\u001b[39;49;00m\u001b[30;43m)\u001b[39;49m\n\u001b[32m 843\u001b[39m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[38;5;28mself\u001b[39m._index.groupby(cats)\n", + "\u001b[36mFile \u001b[39m\u001b[32m~\\AppData\\Local\\anaconda3\\envs\\openeopythonclient\\Lib\\site-packages\\pandas\\core\\arrays\\categorical.py:746\u001b[39m, in \u001b[36mCategorical.from_codes\u001b[39m\u001b[34m(cls, codes, categories, ordered, dtype, validate)\u001b[39m\n\u001b[32m 693\u001b[39m \u001b[38;5;129m@classmethod\u001b[39m\n\u001b[32m 694\u001b[39m \u001b[38;5;28;01mdef\u001b[39;00m\u001b[38;5;250m \u001b[39m\u001b[34mfrom_codes\u001b[39m(\n\u001b[32m 695\u001b[39m \u001b[38;5;28mcls\u001b[39m,\n\u001b[32m (...)\u001b[39m\u001b[32m 700\u001b[39m validate: \u001b[38;5;28mbool\u001b[39m = \u001b[38;5;28;01mTrue\u001b[39;00m,\n\u001b[32m 701\u001b[39m ) -> Self:\n\u001b[32m 702\u001b[39m \u001b[38;5;250m \u001b[39m\u001b[33;03m\"\"\"\u001b[39;00m\n\u001b[32m 703\u001b[39m \u001b[33;03m Make a Categorical type from codes and categories or dtype.\u001b[39;00m\n\u001b[32m 704\u001b[39m \n\u001b[32m (...)\u001b[39m\u001b[32m 744\u001b[39m \u001b[33;03m Categories (2, object): ['a' < 'b']\u001b[39;00m\n\u001b[32m 745\u001b[39m \u001b[33;03m \"\"\"\u001b[39;00m\n\u001b[32m--> \u001b[39m\u001b[32m746\u001b[39m dtype = \u001b[30;43mCategoricalDtype\u001b[39;49m\u001b[30;43m.\u001b[39;49m\u001b[30;43m_from_values_or_dtype\u001b[39;49m\u001b[30;43m(\u001b[39;49m\n\u001b[32m 747\u001b[39m \u001b[30;43m \u001b[39;49m\u001b[30;43mcategories\u001b[39;49m\u001b[30;43m=\u001b[39;49m\u001b[30;43mcategories\u001b[39;49m\u001b[30;43m,\u001b[39;49m\u001b[30;43m \u001b[39;49m\u001b[30;43mordered\u001b[39;49m\u001b[30;43m=\u001b[39;49m\u001b[30;43mordered\u001b[39;49m\u001b[30;43m,\u001b[39;49m\u001b[30;43m \u001b[39;49m\u001b[30;43mdtype\u001b[39;49m\u001b[30;43m=\u001b[39;49m\u001b[30;43mdtype\u001b[39;49m\n\u001b[32m 748\u001b[39m \u001b[30;43m \u001b[39;49m\u001b[30;43m)\u001b[39;49m\n\u001b[32m 749\u001b[39m \u001b[38;5;28;01mif\u001b[39;00m dtype.categories \u001b[38;5;129;01mis\u001b[39;00m \u001b[38;5;28;01mNone\u001b[39;00m:\n\u001b[32m 750\u001b[39m msg = (\n\u001b[32m 751\u001b[39m \u001b[33m\"\u001b[39m\u001b[33mThe categories must be provided in \u001b[39m\u001b[33m'\u001b[39m\u001b[33mcategories\u001b[39m\u001b[33m'\u001b[39m\u001b[33m or \u001b[39m\u001b[33m\"\u001b[39m\n\u001b[32m 752\u001b[39m \u001b[33m\"\u001b[39m\u001b[33m'\u001b[39m\u001b[33mdtype\u001b[39m\u001b[33m'\u001b[39m\u001b[33m. Both were None.\u001b[39m\u001b[33m\"\u001b[39m\n\u001b[32m 753\u001b[39m )\n", + "\u001b[36mFile \u001b[39m\u001b[32m~\\AppData\\Local\\anaconda3\\envs\\openeopythonclient\\Lib\\site-packages\\pandas\\core\\dtypes\\dtypes.py:338\u001b[39m, in \u001b[36mCategoricalDtype._from_values_or_dtype\u001b[39m\u001b[34m(cls, values, categories, ordered, dtype)\u001b[39m\n\u001b[32m 331\u001b[39m dtype = values.dtype._from_categorical_dtype(\n\u001b[32m 332\u001b[39m values.dtype, categories, ordered\n\u001b[32m 333\u001b[39m )\n\u001b[32m 334\u001b[39m \u001b[38;5;28;01melse\u001b[39;00m:\n\u001b[32m 335\u001b[39m \u001b[38;5;66;03m# If dtype=None and values is not categorical, create a new dtype.\u001b[39;00m\n\u001b[32m 336\u001b[39m \u001b[38;5;66;03m# Note: This could potentially have categories=None and\u001b[39;00m\n\u001b[32m 337\u001b[39m \u001b[38;5;66;03m# ordered=None.\u001b[39;00m\n\u001b[32m--> \u001b[39m\u001b[32m338\u001b[39m dtype = \u001b[30;43mCategoricalDtype\u001b[39;49m\u001b[30;43m(\u001b[39;49m\u001b[30;43mcategories\u001b[39;49m\u001b[30;43m,\u001b[39;49m\u001b[30;43m \u001b[39;49m\u001b[30;43mordered\u001b[39;49m\u001b[30;43m)\u001b[39;49m\n\u001b[32m 340\u001b[39m \u001b[38;5;28;01mreturn\u001b[39;00m cast(CategoricalDtype, dtype)\n", + "\u001b[36mFile \u001b[39m\u001b[32m~\\AppData\\Local\\anaconda3\\envs\\openeopythonclient\\Lib\\site-packages\\pandas\\core\\dtypes\\dtypes.py:221\u001b[39m, in \u001b[36mCategoricalDtype.__init__\u001b[39m\u001b[34m(self, categories, ordered)\u001b[39m\n\u001b[32m 220\u001b[39m \u001b[38;5;28;01mdef\u001b[39;00m\u001b[38;5;250m \u001b[39m\u001b[34m__init__\u001b[39m(\u001b[38;5;28mself\u001b[39m, categories=\u001b[38;5;28;01mNone\u001b[39;00m, ordered: Ordered = \u001b[38;5;28;01mFalse\u001b[39;00m) -> \u001b[38;5;28;01mNone\u001b[39;00m:\n\u001b[32m--> \u001b[39m\u001b[32m221\u001b[39m \u001b[30;43mself\u001b[39;49m\u001b[30;43m.\u001b[39;49m\u001b[30;43m_finalize\u001b[39;49m\u001b[30;43m(\u001b[39;49m\u001b[30;43mcategories\u001b[39;49m\u001b[30;43m,\u001b[39;49m\u001b[30;43m \u001b[39;49m\u001b[30;43mordered\u001b[39;49m\u001b[30;43m,\u001b[39;49m\u001b[30;43m \u001b[39;49m\u001b[30;43mfastpath\u001b[39;49m\u001b[30;43m=\u001b[39;49m\u001b[30;43;01mFalse\u001b[39;49;00m\u001b[30;43m)\u001b[39;49m\n", + "\u001b[36mFile \u001b[39m\u001b[32m~\\AppData\\Local\\anaconda3\\envs\\openeopythonclient\\Lib\\site-packages\\pandas\\core\\dtypes\\dtypes.py:378\u001b[39m, in \u001b[36mCategoricalDtype._finalize\u001b[39m\u001b[34m(self, categories, ordered, fastpath)\u001b[39m\n\u001b[32m 375\u001b[39m \u001b[38;5;28mself\u001b[39m.validate_ordered(ordered)\n\u001b[32m 377\u001b[39m \u001b[38;5;28;01mif\u001b[39;00m categories \u001b[38;5;129;01mis\u001b[39;00m \u001b[38;5;129;01mnot\u001b[39;00m \u001b[38;5;28;01mNone\u001b[39;00m:\n\u001b[32m--> \u001b[39m\u001b[32m378\u001b[39m categories = \u001b[30;43mself\u001b[39;49m\u001b[30;43m.\u001b[39;49m\u001b[30;43mvalidate_categories\u001b[39;49m\u001b[30;43m(\u001b[39;49m\u001b[30;43mcategories\u001b[39;49m\u001b[30;43m,\u001b[39;49m\u001b[30;43m \u001b[39;49m\u001b[30;43mfastpath\u001b[39;49m\u001b[30;43m=\u001b[39;49m\u001b[30;43mfastpath\u001b[39;49m\u001b[30;43m)\u001b[39;49m\n\u001b[32m 380\u001b[39m \u001b[38;5;28mself\u001b[39m._categories = categories\n\u001b[32m 381\u001b[39m \u001b[38;5;28mself\u001b[39m._ordered = ordered\n", + "\u001b[36mFile \u001b[39m\u001b[32m~\\AppData\\Local\\anaconda3\\envs\\openeopythonclient\\Lib\\site-packages\\pandas\\core\\dtypes\\dtypes.py:576\u001b[39m, in \u001b[36mCategoricalDtype.validate_categories\u001b[39m\u001b[34m(categories, fastpath)\u001b[39m\n\u001b[32m 574\u001b[39m \u001b[38;5;28;01mif\u001b[39;00m \u001b[38;5;129;01mnot\u001b[39;00m fastpath:\n\u001b[32m 575\u001b[39m \u001b[38;5;28;01mif\u001b[39;00m categories.hasnans:\n\u001b[32m--> \u001b[39m\u001b[32m576\u001b[39m \u001b[38;5;28;01mraise\u001b[39;00m \u001b[38;5;167;01mValueError\u001b[39;00m(\u001b[33m\"\u001b[39m\u001b[33mCategorical categories cannot be null\u001b[39m\u001b[33m\"\u001b[39m)\n\u001b[32m 578\u001b[39m \u001b[38;5;28;01mif\u001b[39;00m \u001b[38;5;129;01mnot\u001b[39;00m categories.is_unique:\n\u001b[32m 579\u001b[39m \u001b[38;5;28;01mraise\u001b[39;00m \u001b[38;5;167;01mValueError\u001b[39;00m(\u001b[33m\"\u001b[39m\u001b[33mCategorical categories must be unique\u001b[39m\u001b[33m\"\u001b[39m)\n", + "\u001b[31mValueError\u001b[39m: Categorical categories cannot be null" + ] + } + ], + "source": [ + "# Tile grid in EPSG:3857\n", + "wm_tile_grid = gpd.GeoDataFrame(\n", + " geometry=[shapely.geometry.box(-111_320, -111_325, 111_320, 111_325)],\n", + " crs=\"EPSG:3857\",\n", + ")\n", + "\n", + "# Points in EPSG:4326 (one near origin, one far away)\n", + "points_wgs84 = gpd.GeoDataFrame(\n", + " geometry=[shapely.geometry.Point(0.1, 0.1), shapely.geometry.Point(50, 50)],\n", + " crs=\"EPSG:4326\",\n", + ")\n", + "\n", + "mixed_groups = split_points(points_wgs84, max_points=10, tile_grid=wm_tile_grid)\n", + "print_group_sizes(mixed_groups, \"CRS mismatch + unmatched point\")\n", + "for i, g in enumerate(mixed_groups):\n", + " print(f\"group {i} bounds:\", g.total_bounds)" + ] + }, + { + "cell_type": "markdown", + "id": "0c5c8f9b", + "metadata": {}, + "source": [ + "## Case 8: validation errors (guard clauses)\n", + "\n", + "These are the explicit checks in `split_points` and `split_area`.\n", + "Run this cell to see the exact exceptions raised by invalid inputs." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "3c4452e0", + "metadata": {}, + "outputs": [], + "source": [ + "def show_error(label, fn):\n", + " try:\n", + " fn()\n", + " except Exception as e:\n", + " print(f\"[{label}] {type(e).__name__}: {e}\")\n", + "\n", + "show_error(\n", + " \"split_points missing geometry column\",\n", + " lambda: split_points(gpd.GeoDataFrame({\"foo\": [1, 2]})),\n", + ")\n", + "\n", + "show_error(\n", + " \"split_points missing CRS\",\n", + " lambda: split_points(gpd.GeoDataFrame(geometry=[shapely.geometry.Point(0, 0)])),\n", + ")\n", + "\n", + "show_error(\n", + " \"split_points non-positive max_points\",\n", + " lambda: split_points(gpd.GeoDataFrame(geometry=[shapely.geometry.Point(0, 0)], crs=\"EPSG:4326\"), max_points=0),\n", + ")\n", + "\n", + "show_error(\n", + " \"split_points tile_grid missing CRS\",\n", + " lambda: split_points(\n", + " gpd.GeoDataFrame(geometry=[shapely.geometry.Point(0, 0)], crs=\"EPSG:4326\"),\n", + " tile_grid=gpd.GeoDataFrame(geometry=[shapely.geometry.box(0, 0, 1, 1)]),\n", + " ),\n", + ")\n", + "\n", + "show_error(\n", + " \"split_area conflicting args\",\n", + " lambda: split_area(\n", + " shapely.geometry.box(0, 0, 1, 1),\n", + " projection=\"EPSG:4326\",\n", + " tile_size=1.0,\n", + " tile_grid=gpd.GeoDataFrame(geometry=[shapely.geometry.box(0, 0, 1, 1)], crs=\"EPSG:4326\"),\n", + " ),\n", + ")\n", + "\n", + "show_error(\n", + " \"split_area missing projection\",\n", + " lambda: split_area({\"west\": 0.0, \"south\": 0.0, \"east\": 1.0, \"north\": 1.0, \"crs\": \"EPSG:4326\"}, tile_size=1.0),\n", + ")\n", + "\n", + "show_error(\n", + " \"split_area invalid AOI type\",\n", + " lambda: split_area(\"not_a_geometry\", projection=\"EPSG:4326\", tile_size=1.0),\n", + ")" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "openeopythonclient", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.12.13" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/docs/cookbook/job_manager.rst b/docs/cookbook/job_manager.rst index 20b76f912..1bc9685fa 100644 --- a/docs/cookbook/job_manager.rst +++ b/docs/cookbook/job_manager.rst @@ -302,6 +302,43 @@ or any other custom partitioning, you can pass a pre-computed # Only tiles intersecting the AOI are returned gdf = split_area(aoi=extent , tile_grid=tile_grid) +Splitting point-based extractions +---------------------------------- + +When the work to split is a set of sample locations (points) rather than a +single large area, e.g. for point-based extraction jobs, +:py:func:`~openeo.extra.job_management._job_splitting.split_points` groups +geometries into batches of at most ``max_points`` rows. +Each row is treated as a single feature to group, so a row can be a single +``Point`` or a multi-part geometry (e.g. ``MultiPoint``) representing a +cluster of locations that must stay together. +By default, groups are formed with a spatial quadtree split so that each +batch stays geographically clustered: + +.. code-block:: python + + import geopandas as gpd + from openeo.extra.job_management import split_points + + points = gpd.read_file("my_sample_locations.gpkg") + batches = split_points(points, max_points=500) + # batches is a list of GeoDataFrames, each with at most 500 rows + +Optionally, a ``tile_grid`` (e.g. a satellite tiling grid) can be passed so +that geometries are first grouped by the tile their centroid falls in. This +guarantees a batch never spans multiple tiles, which avoids batch jobs +having to load multiple source products for a single extraction job: + +.. code-block:: python + + tile_grid = gpd.read_parquet("my_tile_grid.parquet") + batches = split_points(points, max_points=500, tile_grid=tile_grid) + +.. tip:: + + See the `job splitting notebook `_ + for a visual, hands-on walkthrough of ``split_area`` and ``split_points``. + Customizing Job Handling ======================== diff --git a/openeo/extra/job_management/__init__.py b/openeo/extra/job_management/__init__.py index 02806531e..2020b28a4 100644 --- a/openeo/extra/job_management/__init__.py +++ b/openeo/extra/job_management/__init__.py @@ -6,7 +6,7 @@ create_job_db, get_job_db, ) -from openeo.extra.job_management._job_splitting import split_area +from openeo.extra.job_management._job_splitting import split_area, split_points from openeo.extra.job_management._manager import MultiBackendJobManager from openeo.extra.job_management.process_based import ProcessBasedJobCreator @@ -19,5 +19,6 @@ "create_job_db", "get_job_db", "split_area", + "split_points", "MultiBackendJobManager", ] diff --git a/openeo/extra/job_management/_job_splitting.py b/openeo/extra/job_management/_job_splitting.py index 9cc854c17..9d6641f89 100644 --- a/openeo/extra/job_management/_job_splitting.py +++ b/openeo/extra/job_management/_job_splitting.py @@ -374,3 +374,126 @@ def split_area( grid = _SizeBasedTileGrid(epsg=normalize_crs(projection), size=tile_size) return grid.get_tiles(aoi) + + +def _quadtree_split_indices( + centroids: gpd.GeoSeries, + idx, + max_points: int, + out: List, +) -> None: + """ + Recursively quarter the bounding box of ``centroids.loc[idx]`` until each + resulting group of index labels holds at most *max_points* entries. + Appends the index-label groups (not the geometries themselves) to *out*. + """ + if len(idx) <= max_points: + out.append(idx) + return + + sub_centroids = centroids.loc[idx] + minx, miny, maxx, maxy = sub_centroids.total_bounds + midx, midy = (minx + maxx) / 2, (miny + maxy) / 2 + quadrants = [ + idx[(sub_centroids.x <= midx) & (sub_centroids.y <= midy)], + idx[(sub_centroids.x <= midx) & (sub_centroids.y > midy)], + idx[(sub_centroids.x > midx) & (sub_centroids.y <= midy)], + idx[(sub_centroids.x > midx) & (sub_centroids.y > midy)], + ] + if any(len(q) == len(idx) for q in quadrants): + # Points coincide (or all fall in one quadrant): quartering the + # bounding box doesn't help, so fall back to plain chunking. + for i in range(0, len(idx), max_points): + out.append(idx[i : i + max_points]) + return + + for quadrant in quadrants: + if len(quadrant) > 0: + _quadtree_split_indices(centroids, quadrant, max_points, out) + + +def split_points( + points: gpd.GeoDataFrame, + *, + max_points: int = 500, + tile_grid: Optional[gpd.GeoDataFrame] = None, +) -> List[gpd.GeoDataFrame]: + """ + Split a GeoDataFrame of geometries (typically points, e.g. sample + locations for point-based extraction) into groups of at most + *max_points* each. + + Each row is treated as a single "feature" to group, regardless of whether + its geometry is a single ``Point`` or a multi-part geometry (e.g. + ``MultiPoint``) representing a cluster of locations that must stay + together: *max_points* limits the number of rows (features) per group, + not the number of individual coordinates. To limit individual + coordinates instead, explode multi-part geometries into separate rows + first, e.g. with ``points.explode(index_parts=False)``. + + By default, grouping is done with a recursive quadtree split on the + geometries' centroids: the bounding box of a (sub)set is quartered and + each quarter is split again until it holds at most *max_points* + geometries. This keeps each resulting group spatially clustered, which is + useful for batch jobs doing point extractions, as opposed to a naive + split on row order. + + If *tile_grid* is given (e.g. a Sentinel-2 MGRS/UTM tile grid), geometries + are first grouped by the tile their centroid falls in, so that a group + never spans multiple tiles. This avoids batch jobs having to load + multiple (possibly non-overlapping) source products for a single query. + Tiles that end up with more than *max_points* geometries are further + split with the quadtree approach described above. Geometries that don't + fall inside any tile are grouped (and split) separately. + + :param points: GeoDataFrame with a ``geometry`` column (any geometry type; + centroids are used for grouping) and a CRS set. + :param max_points: maximum number of geometries per group. + :param tile_grid: optional GeoDataFrame of tile polygons (with CRS set) + to co-join geometries by before splitting, e.g. a satellite tiling + grid. When given, geometries are only grouped together if their + centroids fall in the same tile. + :return: list of GeoDataFrames, each with at most *max_points* rows. + :raises JobSplittingFailure: if the GeoDataFrame has no geometry column + or CRS, or if *max_points* is not positive. + """ + if "geometry" not in points.columns: + raise JobSplittingFailure("The GeoDataFrame must contain a 'geometry' column.") + if points.crs is None: + raise JobSplittingFailure("The GeoDataFrame must have a CRS set.") + if max_points <= 0: + raise JobSplittingFailure(f"'max_points' must be positive, got {max_points!r}.") + if tile_grid is not None and tile_grid.crs is None: + raise JobSplittingFailure("The 'tile_grid' GeoDataFrame must have a CRS set.") + + points_epsg = points.crs.to_epsg() + if points_epsg is not None: + _TileGridInterface._check_antimeridian_crossing(points.total_bounds, points_epsg) + + if tile_grid is not None: + tile_epsg = tile_grid.crs.to_epsg() + if tile_epsg is not None: + _TileGridInterface._check_antimeridian_crossing(tile_grid.total_bounds, tile_epsg) + + if points.empty: + return [] + + centroids = points.geometry.centroid + index_groups: List = [] + + if tile_grid is None: + _quadtree_split_indices(centroids, points.index, max_points, index_groups) + else: + centroids_gdf = gpd.GeoDataFrame(geometry=centroids, crs=points.crs) + if centroids_gdf.crs != tile_grid.crs: + centroids_gdf = centroids_gdf.to_crs(tile_grid.crs) + joined = gpd.sjoin( + centroids_gdf, tile_grid[["geometry"]].reset_index(drop=True), how="left", predicate="intersects" + ) + # A centroid on a shared tile edge can match multiple tiles: keep just the first match. + joined = joined.loc[~joined.index.duplicated(keep="first")] + + for _, idx in joined.groupby("index_right", dropna=False).groups.items(): + _quadtree_split_indices(centroids, idx, max_points, index_groups) + + return [points.loc[idx].reset_index(drop=True) for idx in index_groups] diff --git a/tests/extra/job_management/test_job_splitting.py b/tests/extra/job_management/test_job_splitting.py index f8f1ab227..5ebabbf4b 100644 --- a/tests/extra/job_management/test_job_splitting.py +++ b/tests/extra/job_management/test_job_splitting.py @@ -8,6 +8,7 @@ _SizeBasedTileGrid, _TileGridInterface, split_area, + split_points, ) from openeo.util import BBoxDict @@ -386,3 +387,143 @@ def test_antimeridian_not_checked_for_projected_crs(self): aoi = {"west": 18_000_000.0, "south": 0.0, "east": 19_000_000.0, "north": 1_000_000.0, "crs": "EPSG:3857"} result = split_area(aoi, projection="EPSG:3857", tile_size=1_000_000.0) assert len(result) >= 1 + + +class TestSplitPoints: + def _grid_points(self, n_side: int) -> gpd.GeoDataFrame: + points = [shapely.geometry.Point(x, y) for x in range(n_side) for y in range(n_side)] + return gpd.GeoDataFrame(geometry=points, crs="EPSG:4326") + + def test_single_group_when_under_limit(self): + gdf = self._grid_points(5) # 25 points + result = split_points(gdf, max_points=100) + assert len(result) == 1 + assert len(result[0]) == 25 + + def test_splits_into_groups_of_max_points(self): + gdf = self._grid_points(10) # 100 points + result = split_points(gdf, max_points=30) + assert sum(len(g) for g in result) == 100 + assert all(len(g) <= 30 for g in result) + assert len(result) > 1 + + def test_groups_are_spatially_clustered(self): + """Points on the left half and right half should end up in different groups.""" + left = [shapely.geometry.Point(x, 0) for x in range(10)] + right = [shapely.geometry.Point(x + 100, 0) for x in range(10)] + gdf = gpd.GeoDataFrame(geometry=left + right, crs="EPSG:4326") + result = split_points(gdf, max_points=10) + assert len(result) == 2 + for group in result: + xs = [geom.x for geom in group.geometry] + assert max(xs) - min(xs) < 50 + + def test_default_max_points(self): + gdf = self._grid_points(3) + result = split_points(gdf) + assert len(result) == 1 + + def test_empty_geodataframe(self): + gdf = gpd.GeoDataFrame(geometry=[], crs="EPSG:4326") + assert split_points(gdf) == [] + + def test_coincident_points_fall_back_to_chunking(self): + """Points that all share the same location can't be split by quadtree; fall back to plain chunking.""" + gdf = gpd.GeoDataFrame(geometry=[shapely.geometry.Point(0, 0)] * 25, crs="EPSG:4326") + result = split_points(gdf, max_points=10) + assert sum(len(g) for g in result) == 25 + assert all(len(g) <= 10 for g in result) + + def test_missing_geometry_column_raises(self): + gdf = gpd.GeoDataFrame({"foo": [1, 2]}) + with pytest.raises(JobSplittingFailure, match="geometry"): + split_points(gdf) + + def test_missing_crs_raises(self): + gdf = gpd.GeoDataFrame(geometry=[shapely.geometry.Point(0, 0)]) + with pytest.raises(JobSplittingFailure, match="CRS"): + split_points(gdf) + + def test_non_positive_max_points_raises(self): + gdf = self._grid_points(2) + with pytest.raises(JobSplittingFailure, match="max_points"): + split_points(gdf, max_points=0) + + def test_preserves_extra_columns(self): + gdf = gpd.GeoDataFrame( + {"id": [1, 2, 3], "geometry": [shapely.geometry.Point(x, 0) for x in range(3)]}, + crs="EPSG:4326", + ) + result = split_points(gdf, max_points=10) + assert len(result) == 1 + assert list(result[0]["id"]) == [1, 2, 3] + + def test_multipoint_rows_treated_as_single_feature(self): + """A row with a MultiPoint geometry counts as one feature towards max_points, not one per coordinate.""" + gdf = gpd.GeoDataFrame( + geometry=[ + shapely.geometry.MultiPoint([(0, 0), (0.1, 0.1), (0.2, 0.2)]), # 3 coordinates, 1 feature + shapely.geometry.Point(0.5, 0.5), + ], + crs="EPSG:4326", + ) + result = split_points(gdf, max_points=10) + assert len(result) == 1 + assert len(result[0]) == 2 # 2 rows/features, regardless of coordinate count + + def test_tile_grid_groups_never_span_multiple_tiles(self): + tile_grid = gpd.GeoDataFrame( + {"tile": ["A", "B"]}, + geometry=[shapely.geometry.box(0, 0, 1, 1), shapely.geometry.box(1, 0, 2, 1)], + crs="EPSG:4326", + ) + points = [shapely.geometry.Point(0.1 * i, 0.5) for i in range(5)] # tile A + points += [shapely.geometry.Point(1 + 0.1 * i, 0.5) for i in range(5)] # tile B + gdf = gpd.GeoDataFrame(geometry=points, crs="EPSG:4326") + + result = split_points(gdf, max_points=100, tile_grid=tile_grid) + assert len(result) == 2 + for group in result: + xs = [geom.x for geom in group.geometry] + assert (max(xs) < 1) or (min(xs) >= 1) + + def test_tile_grid_oversized_tile_is_further_split(self): + tile_grid = gpd.GeoDataFrame(geometry=[shapely.geometry.box(0, 0, 10, 10)], crs="EPSG:4326") + points = [shapely.geometry.Point(x, y) for x in range(10) for y in range(10)] # 100 points, 1 tile + gdf = gpd.GeoDataFrame(geometry=points, crs="EPSG:4326") + + result = split_points(gdf, max_points=30, tile_grid=tile_grid) + assert sum(len(g) for g in result) == 100 + assert all(len(g) <= 30 for g in result) + assert len(result) > 1 + + def test_tile_grid_unmatched_points_grouped_separately(self): + tile_grid = gpd.GeoDataFrame(geometry=[shapely.geometry.box(0, 0, 1, 1)], crs="EPSG:4326") + gdf = gpd.GeoDataFrame( + geometry=[shapely.geometry.Point(0.5, 0.5), shapely.geometry.Point(50, 50)], + crs="EPSG:4326", + ) + result = split_points(gdf, max_points=10, tile_grid=tile_grid) + assert len(result) == 2 + assert sum(len(g) for g in result) == 2 + + def test_tile_grid_reprojects_when_crs_differs(self): + tile_grid = gpd.GeoDataFrame( + geometry=[shapely.geometry.box(-111_320, -111_325, 111_320, 111_325)], crs="EPSG:3857" + ) + gdf = gpd.GeoDataFrame(geometry=[shapely.geometry.Point(0.1, 0.1)], crs="EPSG:4326") + result = split_points(gdf, max_points=10, tile_grid=tile_grid) + assert len(result) == 1 + assert len(result[0]) == 1 + + def test_tile_grid_missing_crs_raises(self): + tile_grid = gpd.GeoDataFrame(geometry=[shapely.geometry.box(0, 0, 1, 1)]) + gdf = self._grid_points(2) + with pytest.raises(JobSplittingFailure, match="tile_grid.*CRS"): + split_points(gdf, tile_grid=tile_grid) + + def test_antimeridian_crossing_points_raises(self): + """Geographic points with longitude outside [-180, 180] are rejected to avoid antimeridian issues.""" + gdf = gpd.GeoDataFrame(geometry=[shapely.geometry.Point(190.0, 0.0)], crs="EPSG:4326") + with pytest.raises(JobSplittingFailure, match="antimeridian"): + split_points(gdf, max_points=10) From 7f239957e8a9b09ba6df8b7aba06acce4eb3eb39 Mon Sep 17 00:00:00 2001 From: Hans Vanrompay Date: Fri, 9 Oct 2026 10:26:05 +0200 Subject: [PATCH 02/12] fix --- docs/cookbook/examples/job_splitting.ipynb | 61 +++++++++++++------ openeo/extra/job_management/_job_splitting.py | 6 +- .../job_management/test_job_splitting.py | 3 +- 3 files changed, 50 insertions(+), 20 deletions(-) diff --git a/docs/cookbook/examples/job_splitting.ipynb b/docs/cookbook/examples/job_splitting.ipynb index 084b7791d..c8f860448 100644 --- a/docs/cookbook/examples/job_splitting.ipynb +++ b/docs/cookbook/examples/job_splitting.ipynb @@ -29,7 +29,7 @@ }, { "cell_type": "code", - "execution_count": 3, + "execution_count": 22, "id": "6893a1ed", "metadata": {}, "outputs": [ @@ -101,7 +101,7 @@ }, { "cell_type": "code", - "execution_count": 4, + "execution_count": 23, "id": "dc5e80d5", "metadata": {}, "outputs": [ @@ -171,7 +171,7 @@ "4 POLYGON ((405750.167 6641293.723, 405750.167 6..." ] }, - "execution_count": 4, + "execution_count": 23, "metadata": {}, "output_type": "execute_result" } @@ -187,7 +187,7 @@ }, { "cell_type": "code", - "execution_count": 5, + "execution_count": 24, "id": "8dae3d94", "metadata": {}, "outputs": [ @@ -227,7 +227,7 @@ }, { "cell_type": "code", - "execution_count": 8, + "execution_count": 32, "id": "485a4ff8", "metadata": {}, "outputs": [ @@ -269,7 +269,7 @@ }, { "cell_type": "code", - "execution_count": 9, + "execution_count": 33, "id": "a23d5bf1", "metadata": {}, "outputs": [ @@ -309,7 +309,7 @@ }, { "cell_type": "code", - "execution_count": 11, + "execution_count": 34, "id": "b271d9c6", "metadata": {}, "outputs": [ @@ -386,7 +386,7 @@ "4 POINT (406297.438 6625092.318) 5" ] }, - "execution_count": 11, + "execution_count": 34, "metadata": {}, "output_type": "execute_result" } @@ -426,10 +426,21 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 35, "id": "342185d1", "metadata": {}, - "outputs": [], + "outputs": [ + { + "data": { + "image/png": "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", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], "source": [ "# Visual check of tile assignment from sjoin (`index_right`)\n", "joined_plot = joined.copy()\n", @@ -450,7 +461,7 @@ }, { "cell_type": "code", - "execution_count": 16, + "execution_count": 36, "id": "d437520d", "metadata": {}, "outputs": [ @@ -496,7 +507,7 @@ }, { "cell_type": "code", - "execution_count": 17, + "execution_count": 37, "id": "3350f55c", "metadata": {}, "outputs": [ @@ -540,7 +551,7 @@ }, { "cell_type": "code", - "execution_count": 19, + "execution_count": 38, "id": "f5d1b42e", "metadata": {}, "outputs": [ @@ -589,7 +600,7 @@ }, { "cell_type": "code", - "execution_count": 20, + "execution_count": 39, "id": "e7856a74", "metadata": {}, "outputs": [ @@ -660,7 +671,7 @@ }, { "cell_type": "code", - "execution_count": 21, + "execution_count": 40, "id": "55554db2", "metadata": {}, "outputs": [ @@ -680,7 +691,7 @@ "traceback": [ "\u001b[31m---------------------------------------------------------------------------\u001b[39m", "\u001b[31mValueError\u001b[39m Traceback (most recent call last)", - "\u001b[36mCell\u001b[39m\u001b[36m \u001b[39m\u001b[32mIn[21]\u001b[39m\u001b[32m, line 13\u001b[39m\n\u001b[32m 9\u001b[39m geometry=[shapely.geometry.Point(\u001b[32m0.1\u001b[39m, \u001b[32m0.1\u001b[39m), shapely.geometry.Point(\u001b[32m50\u001b[39m, \u001b[32m50\u001b[39m)],\n\u001b[32m 10\u001b[39m crs=\u001b[33m\"EPSG:4326\"\u001b[39m,\n\u001b[32m 11\u001b[39m )\n\u001b[32m 12\u001b[39m \n\u001b[32m---> \u001b[39m\u001b[32m13\u001b[39m mixed_groups = split_points(points_wgs84, max_points=\u001b[32m10\u001b[39m, tile_grid=wm_tile_grid)\n\u001b[32m 14\u001b[39m print_group_sizes(mixed_groups, \u001b[33m\"CRS mismatch + unmatched point\"\u001b[39m)\n\u001b[32m 15\u001b[39m \u001b[38;5;28;01mfor\u001b[39;00m i, g \u001b[38;5;28;01min\u001b[39;00m enumerate(mixed_groups):\n\u001b[32m 16\u001b[39m print(f\"group {i} bounds:\", g.total_bounds)\n", + "\u001b[36mCell\u001b[39m\u001b[36m \u001b[39m\u001b[32mIn[40]\u001b[39m\u001b[32m, line 13\u001b[39m\n\u001b[32m 9\u001b[39m geometry=[shapely.geometry.Point(\u001b[32m0.1\u001b[39m, \u001b[32m0.1\u001b[39m), shapely.geometry.Point(\u001b[32m50\u001b[39m, \u001b[32m50\u001b[39m)],\n\u001b[32m 10\u001b[39m crs=\u001b[33m\"EPSG:4326\"\u001b[39m,\n\u001b[32m 11\u001b[39m )\n\u001b[32m 12\u001b[39m \n\u001b[32m---> \u001b[39m\u001b[32m13\u001b[39m mixed_groups = split_points(points_wgs84, max_points=\u001b[32m10\u001b[39m, tile_grid=wm_tile_grid)\n\u001b[32m 14\u001b[39m print_group_sizes(mixed_groups, \u001b[33m\"CRS mismatch + unmatched point\"\u001b[39m)\n\u001b[32m 15\u001b[39m \u001b[38;5;28;01mfor\u001b[39;00m i, g \u001b[38;5;28;01min\u001b[39;00m enumerate(mixed_groups):\n\u001b[32m 16\u001b[39m print(f\"group {i} bounds:\", g.total_bounds)\n", "\u001b[36mFile \u001b[39m\u001b[32mC:\\Git_projects\\openeo-python-client\\openeo\\extra\\job_management\\_job_splitting.py:496\u001b[39m, in \u001b[36msplit_points\u001b[39m\u001b[34m(points, max_points, tile_grid)\u001b[39m\n\u001b[32m 493\u001b[39m \u001b[38;5;66;03m# A centroid on a shared tile edge can match multiple tiles: keep just the first match.\u001b[39;00m\n\u001b[32m 494\u001b[39m joined = joined.loc[~joined.index.duplicated(keep=\u001b[33m\"\u001b[39m\u001b[33mfirst\u001b[39m\u001b[33m\"\u001b[39m)]\n\u001b[32m--> \u001b[39m\u001b[32m496\u001b[39m \u001b[38;5;28;01mfor\u001b[39;00m _, idx \u001b[38;5;129;01min\u001b[39;00m \u001b[30;43mjoined\u001b[39;49m\u001b[30;43m.\u001b[39;49m\u001b[30;43mgroupby\u001b[39;49m\u001b[30;43m(\u001b[39;49m\u001b[30;43m\"\u001b[39;49m\u001b[30;43mindex_right\u001b[39;49m\u001b[30;43m\"\u001b[39;49m\u001b[30;43m,\u001b[39;49m\u001b[30;43m \u001b[39;49m\u001b[30;43mdropna\u001b[39;49m\u001b[30;43m=\u001b[39;49m\u001b[30;43;01mFalse\u001b[39;49;00m\u001b[30;43m)\u001b[39;49m\u001b[30;43m.\u001b[39;49m\u001b[30;43mgroups\u001b[39;49m.items():\n\u001b[32m 497\u001b[39m _quadtree_split_indices(centroids, idx, max_points, index_groups)\n\u001b[32m 499\u001b[39m \u001b[38;5;28;01mreturn\u001b[39;00m [points.loc[idx].reset_index(drop=\u001b[38;5;28;01mTrue\u001b[39;00m) \u001b[38;5;28;01mfor\u001b[39;00m idx \u001b[38;5;129;01min\u001b[39;00m index_groups]\n", "\u001b[36mFile \u001b[39m\u001b[32m~\\AppData\\Local\\anaconda3\\envs\\openeopythonclient\\Lib\\site-packages\\pandas\\core\\groupby\\groupby.py:853\u001b[39m, in \u001b[36mBaseGroupBy.groups\u001b[39m\u001b[34m(self)\u001b[39m\n\u001b[32m 807\u001b[39m \u001b[38;5;129m@final\u001b[39m\n\u001b[32m 808\u001b[39m \u001b[38;5;129m@property\u001b[39m\n\u001b[32m 809\u001b[39m \u001b[38;5;28;01mdef\u001b[39;00m\u001b[38;5;250m \u001b[39m\u001b[34mgroups\u001b[39m(\u001b[38;5;28mself\u001b[39m) -> \u001b[38;5;28mdict\u001b[39m[Hashable, np.ndarray]:\n\u001b[32m 810\u001b[39m \u001b[38;5;250m \u001b[39m\u001b[33;03m\"\"\"\u001b[39;00m\n\u001b[32m 811\u001b[39m \u001b[33;03m Dict {group name -> group labels}.\u001b[39;00m\n\u001b[32m 812\u001b[39m \n\u001b[32m (...)\u001b[39m\u001b[32m 851\u001b[39m \u001b[33;03m {Timestamp('2023-01-01 00:00:00'): 2, Timestamp('2023-02-01 00:00:00'): 4}\u001b[39;00m\n\u001b[32m 852\u001b[39m \u001b[33;03m \"\"\"\u001b[39;00m\n\u001b[32m--> \u001b[39m\u001b[32m853\u001b[39m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[30;43mself\u001b[39;49m\u001b[30;43m.\u001b[39;49m\u001b[30;43m_grouper\u001b[39;49m\u001b[30;43m.\u001b[39;49m\u001b[30;43mgroups\u001b[39;49m\n", "\u001b[36mFile \u001b[39m\u001b[32mpandas/_libs/properties.pyx:36\u001b[39m, in \u001b[36mpandas._libs.properties.CachedProperty.__get__\u001b[39m\u001b[34m()\u001b[39m\n\u001b[32m---> \u001b[39m\u001b[32m36\u001b[39m \u001b[33m'Could not get source, probably due dynamically evaluated source code.'\u001b[39m\n", @@ -728,10 +739,24 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 41, "id": "3c4452e0", "metadata": {}, - "outputs": [], + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "[split_points missing geometry column] JobSplittingFailure: The GeoDataFrame must contain a 'geometry' column.\n", + "[split_points missing CRS] JobSplittingFailure: The GeoDataFrame must have a CRS set.\n", + "[split_points non-positive max_points] JobSplittingFailure: 'max_points' must be positive, got 0.\n", + "[split_points tile_grid missing CRS] JobSplittingFailure: The 'tile_grid' GeoDataFrame must have a CRS set.\n", + "[split_area conflicting args] JobSplittingFailure: Cannot combine 'tile_grid' with 'projection' or 'tile_size'. Either pass a _TileGridInterface, or pass projection + tile_size.\n", + "[split_area missing projection] JobSplittingFailure: 'projection' is required when using size-based tiling.\n", + "[split_area invalid AOI type] JobSplittingFailure: Expected a bounding-box dict, Polygon, or MultiPolygon, got str.\n" + ] + } + ], "source": [ "def show_error(label, fn):\n", " try:\n", diff --git a/openeo/extra/job_management/_job_splitting.py b/openeo/extra/job_management/_job_splitting.py index 9d6641f89..598e8bdd6 100644 --- a/openeo/extra/job_management/_job_splitting.py +++ b/openeo/extra/job_management/_job_splitting.py @@ -493,7 +493,11 @@ def split_points( # A centroid on a shared tile edge can match multiple tiles: keep just the first match. joined = joined.loc[~joined.index.duplicated(keep="first")] - for _, idx in joined.groupby("index_right", dropna=False).groups.items(): + # Group using an explicit key to avoid pandas categorical/NaN edge cases + # when unmatched centroids (index_right = NaN) are present. + joined["_tile_group_key"] = joined["index_right"].where(joined["index_right"].notna(), "__unmatched__") + + for _, idx in joined.groupby("_tile_group_key").groups.items(): _quadtree_split_indices(centroids, idx, max_points, index_groups) return [points.loc[idx].reset_index(drop=True) for idx in index_groups] diff --git a/tests/extra/job_management/test_job_splitting.py b/tests/extra/job_management/test_job_splitting.py index 5ebabbf4b..30ae2af90 100644 --- a/tests/extra/job_management/test_job_splitting.py +++ b/tests/extra/job_management/test_job_splitting.py @@ -478,7 +478,8 @@ def test_tile_grid_groups_never_span_multiple_tiles(self): crs="EPSG:4326", ) points = [shapely.geometry.Point(0.1 * i, 0.5) for i in range(5)] # tile A - points += [shapely.geometry.Point(1 + 0.1 * i, 0.5) for i in range(5)] # tile B + # Start at x=1.1 to avoid the x=1.0 shared-edge ambiguity. + points += [shapely.geometry.Point(1.1 + 0.1 * i, 0.5) for i in range(5)] # tile B gdf = gpd.GeoDataFrame(geometry=points, crs="EPSG:4326") result = split_points(gdf, max_points=100, tile_grid=tile_grid) From acf68ca5225179064b940340a7fb261e1f042db1 Mon Sep 17 00:00:00 2001 From: Hans Vanrompay Date: Fri, 9 Oct 2026 10:37:31 +0200 Subject: [PATCH 03/12] clean up docs --- docs/cookbook/examples/job_splitting.ipynb | 833 --------------------- docs/cookbook/job_manager.rst | 5 - 2 files changed, 838 deletions(-) delete mode 100644 docs/cookbook/examples/job_splitting.ipynb diff --git a/docs/cookbook/examples/job_splitting.ipynb b/docs/cookbook/examples/job_splitting.ipynb deleted file mode 100644 index c8f860448..000000000 --- a/docs/cookbook/examples/job_splitting.ipynb +++ /dev/null @@ -1,833 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "id": "cdc93935", - "metadata": {}, - "source": [ - "# Internal walkthrough of `split_area` and `split_points`\n", - "\n", - "This notebook is not customer-facing. It is a developer guide to understand the\n", - "runtime behavior of the splitter logic in\n", - "`openeo.extra.job_management._job_splitting`.\n", - "\n", - "It is configured to import from the local repository checkout (workspace source),\n", - "not from any preinstalled `openeo` package.\n", - "\n", - "What this notebook covers:\n", - "\n", - "1. `split_area` size-based path and custom-grid path.\n", - "2. `split_points` default quadtree path.\n", - "3. Quadtree no-progress fallback (coincident points -> plain chunking).\n", - "4. Row semantics (`Point` vs `MultiPoint` rows).\n", - "5. Tile-grid path (`gpd.sjoin`) with boundary and unmatched-point behavior.\n", - "6. CRS reprojection behavior in tile-grid mode.\n", - "7. Validation error cases.\n", - "\n", - "All cells are safe to run locally without connecting to an openEO backend." - ] - }, - { - "cell_type": "code", - "execution_count": 22, - "id": "6893a1ed", - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Using local openeo from: C:\\Git_projects\\openeo-python-client\\openeo\\__init__.py\n" - ] - } - ], - "source": [ - "import sys\n", - "from pathlib import Path\n", - "\n", - "import geopandas as gpd\n", - "import matplotlib.pyplot as plt\n", - "import numpy as np\n", - "import shapely.geometry\n", - "\n", - "# Force imports to come from this local repository checkout, not a preinstalled package.\n", - "repo_root = Path.cwd().resolve()\n", - "while not (repo_root / \"openeo\").exists() and repo_root != repo_root.parent:\n", - " repo_root = repo_root.parent\n", - "\n", - "if not (repo_root / \"openeo\").exists():\n", - " raise RuntimeError(\"Could not locate local repository root containing 'openeo/'\")\n", - "\n", - "repo_root_str = str(repo_root)\n", - "if repo_root_str in sys.path:\n", - " sys.path.remove(repo_root_str)\n", - "sys.path.insert(0, repo_root_str)\n", - "\n", - "import openeo\n", - "from openeo.extra.job_management import split_area, split_points\n", - "from openeo.extra.job_management._job_splitting import JobSplittingFailure\n", - "\n", - "print(\"Using local openeo from:\", Path(openeo.__file__).resolve())\n", - "\n", - "plt.rcParams[\"figure.figsize\"] = (7, 6)\n", - "\n", - "\n", - "def plot_groups(groups, title, ax=None, markersize=20):\n", - " if ax is None:\n", - " fig, ax = plt.subplots()\n", - " cmap = plt.get_cmap(\"tab20\")\n", - " for i, group in enumerate(groups):\n", - " group.plot(ax=ax, color=cmap(i % 20), markersize=markersize, label=f\"g{i} (n={len(group)})\")\n", - " ax.set_title(title)\n", - " ax.legend(loc=\"center left\", bbox_to_anchor=(1.0, 0.5))\n", - " return ax\n", - "\n", - "\n", - "def print_group_sizes(groups, label):\n", - " print(f\"{label}: {len(groups)} groups\")\n", - " print(\"sizes:\", [len(g) for g in groups])" - ] - }, - { - "cell_type": "markdown", - "id": "23370d5d", - "metadata": {}, - "source": [ - "## Case 1: `split_area` size-based path\n", - "\n", - "This follows the `split_area(..., projection=..., tile_size=...)` branch.\n", - "The AOI is reprojected to the tiling CRS and split into square tiles." - ] - }, - { - "cell_type": "code", - "execution_count": 23, - "id": "dc5e80d5", - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "tile CRS: EPSG:3857\n", - "tile count: 25\n" - ] - }, - { - "data": { - "text/html": [ - "
\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
geometry
0POLYGON ((405750.167 6621293.723, 405750.167 6...
1POLYGON ((405750.167 6626293.723, 405750.167 6...
2POLYGON ((405750.167 6631293.723, 405750.167 6...
3POLYGON ((405750.167 6636293.723, 405750.167 6...
4POLYGON ((405750.167 6641293.723, 405750.167 6...
\n", - "
" - ], - "text/plain": [ - " geometry\n", - "0 POLYGON ((405750.167 6621293.723, 405750.167 6...\n", - "1 POLYGON ((405750.167 6626293.723, 405750.167 6...\n", - "2 POLYGON ((405750.167 6631293.723, 405750.167 6...\n", - "3 POLYGON ((405750.167 6636293.723, 405750.167 6...\n", - "4 POLYGON ((405750.167 6641293.723, 405750.167 6..." - ] - }, - "execution_count": 23, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "aoi = {\"west\": 3.60, \"south\": 51.0, \"east\": 3.80, \"north\": 51.12, \"crs\": \"EPSG:4326\"}\n", - "\n", - "tiles = split_area(aoi=aoi, tile_size=5_000, projection=\"EPSG:3857\")\n", - "print(\"tile CRS:\", tiles.crs)\n", - "print(\"tile count:\", len(tiles))\n", - "tiles.head()" - ] - }, - { - "cell_type": "code", - "execution_count": 24, - "id": "8dae3d94", - "metadata": {}, - "outputs": [ - { - "data": { - "image/png": "iVBORw0KGgoAAAANSUhEUgAAAikAAAIQCAYAAABE5v57AAAAOnRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjEwLjksIGh0dHBzOi8vbWF0cGxvdGxpYi5vcmcvJkbTWQAAAAlwSFlzAAAPYQAAD2EBqD+naQAARKJJREFUeJzt3X98zfX///H72WZns9nYbGwz2/wmZFmpCGU1vUkqicivflOKd+ojSSQiSb3fIZLkZ4Te+kFpSHojeic/8yOW/M6vDWnazvP7R9+dt2M/7MS78xy36+VyLpzX6/l8vZ6v83jtnPteP84cxhgjAAAAy/j5egAAAAAFIaQAAAArEVIAAICVCCkAAMBKhBQAAGAlQgoAALASIQUAAFiJkAIAAKxESAEAAFYipCCfjIwMORwOvfvuu+5pL7zwghwOh+8GZTGHw6EXXnjhL11nt27dFBoa+peu82LwxWtVlLx9fdSoURd1ud7Ux7bXRJISExPVrVs39/Nly5bJ4XBo2bJlPhvT/9q7774rh8OhjIwMXw8FZyGk4E8bNmyYPvzwQ18PAzivTz/91Log4Gv//ve/9cILL+j48eO+HkqJ9fTTT8vhcOiee+4pst2mTZvUuXNnxcXFyel0KjY2Vp06ddKmTZvytc0LS2vXrv1fDbtEIaSgWJ577jmdPn3aYxoh5Q+nT5/Wc8895+thoAiffvqpBg8e7OthWOXf//63Bg8eXGBI2bp1qyZOnPjXD6oEMcZo5syZSkxM1EcffaQTJ04U2G7evHm66qqrlJ6eru7du2vs2LG6//77tXTpUl111VWaP3/+XzzykiXA1wNAyRAQEKCAAN/vLi6XS2fOnFFQUJCvh+Jm01iAi8HpdPp6CMWWk5Mjl8ulwMDAv3S9y5Yt0549e7RkyRKlpaVp3rx56tq1q0ebH3/8Uffdd5+qVKmi5cuXKyoqyj3viSee0A033KD77rtP69evV5UqVf7S8ZcUHEm5RJw4cUJPPvmkEhMT5XQ6FR0drZtvvln/+c9/3G2aN2+uunXr6ttvv9X111+v4OBgJSUlafz48edd/rnXpDgcDp06dUpTpkyRw+GQw+HwOId9PqNGjdL111+vyMhIBQcHq2HDhvrggw/ytXM4HHrsscc0ffp0XXHFFXI6nVq0aJEkae/everRo4cqVKggp9OpK664Qu+8845H/zNnzuj5559Xw4YNFR4erpCQEN1www1aunRpsca5du1apaWlqXz58u7Xq0ePHvnGmHcqIe8ah8IeZ1u9erVatmyp8PBwlS5dWs2aNdPXX39d3JdQkrRz506lpaUpJCREsbGxGjJkiM79w+bFfa0XL16sJk2aqGzZsgoNDVXNmjX17LPPerTJzs7WoEGDVK1aNTmdTsXHx+vpp59WdnZ2vnZ9+vRRVFSUypQpozZt2mjPnj3F2qa86x/ef/99Pfvss6pYsaJCQkLUpk0b/fzzzx5tv/rqK919992qXLmyezx9+vTxOOrXrVs3vfnmm5JUaC0kacKECapataqcTqeuvvpqrVmzxmP+77//rh9++EH79+8v1nZIxavPubp166bExMR80wu7LmzatGlq2LChgoODFRERoQ4dOuR7nQpaVr9+/SRJSUlJ7tck73qMc69JKUxx9uHivDcV19nXEI0ZM8Zdr82bN0uSfvjhB7Vr104REREKCgpSSkqKFixYkG85mzZt0k033aTg4GBVqlRJQ4cOlcvl8mos06dPV506dXTjjTcqNTVV06dPz9fmlVde0a+//qoJEyZ4BBRJKl++vN566y2dOnVKI0eO9GrdlxPf/2qMi+KRRx7RBx98oMcee0x16tTRkSNHtGLFCm3ZskVXXXWVu92xY8f0t7/9Te3bt1fHjh01e/ZsPfroowoMDMz34VuUqVOn6oEHHtA111yjhx56SJJUtWrVYvd//fXX1aZNG3Xq1ElnzpzRrFmzdPfdd+vjjz9Wq1atPNouWbJEs2fP1mOPPaby5csrMTFRBw8e1LXXXusOMVFRUVq4cKHuv/9+ZWVl6cknn5QkZWVl6e2331bHjh314IMP6sSJE5o0aZLS0tL0zTffqEGDBoWO8dChQ7rlllsUFRWl//u//1PZsmWVkZGhefPmFdonKipKU6dO9Zj2+++/q0+fPh6/6S1ZskS33nqrGjZsqEGDBsnPz0+TJ0/WTTfdpK+++krXXHPNeV/D3NxctWzZUtdee61GjhypRYsWadCgQcrJydGQIUO8eq03bdqk1q1bq379+hoyZIicTqd27Njh8YHjcrnUpk0brVixQg899JBq166tDRs26LXXXtO2bds8Tv098MADmjZtmu69915df/31WrJkSb66ns9LL70kh8OhZ555RocOHdKYMWOUmpqqdevWKTg4WJI0Z84c/frrr3r00UcVGRmpb775Rv/4xz+0Z88ezZkzR5L08MMPa9++fVq8eHG+2uSZMWOGTpw4oYcfflgOh0MjR47UnXfeqZ07d6pUqVKS/gjFtWvXVteuXT0uKr/Q+lyIl156SQMHDlT79u31wAMP6JdfftE//vEPNW3aVN99953Kli1bYL8777xT27Zt08yZM/Xaa6+pfPnykpTvg7Qoxd2Hi/ve5I3Jkyfrt99+00MPPSSn06mIiAht2rRJjRs3VlxcnP7v//5PISEhmj17ttq2bau5c+fqjjvukCQdOHBAN954o3JyctztJkyY4N6niiM7O1tz587V3//+d0lSx44d1b17dx04cEAVK1Z0t/voo4+UmJioG264ocDlNG3aVImJifrkk0/+1OtwWTC4JISHh5tevXoV2aZZs2ZGknn11Vfd07Kzs02DBg1MdHS0OXPmjDHGmF27dhlJZvLkye52gwYNMufuLiEhIaZr165/ary//vqrx/MzZ86YunXrmptuusljuiTj5+dnNm3a5DH9/vvvNzExMebw4cMe0zt06GDCw8Pdy8/JyTHZ2dkebY4dO2YqVKhgevToUeQY58+fbySZNWvWFNlOkhk0aFCh83v27Gn8/f3NkiVLjDHGuFwuU716dZOWlmZcLpe73a+//mqSkpLMzTffXOT6jDGma9euRpJ5/PHH3dNcLpdp1aqVCQwMNL/88ovHcs9W0Gv92muvGUke/c41depU4+fnZ7766iuP6ePHjzeSzNdff22MMWbdunVGkunZs6dHu3vvvfe8r5UxxixdutRIMnFxcSYrK8s9ffbs2UaSef311wvdNmOMGT58uHE4HOann35yT+vVq1e+/deY/+7rkZGR5ujRo+7p//rXv4wk89FHH+VrW5x93pv6nPuadO3a1SQkJORb5rk/gxkZGcbf39+89NJLHu02bNhgAgIC8k0/1yuvvGIkmV27duWbl5CQ4LGdeTVZunSpe1uKuw8X572puPJqEBYWZg4dOuQxr0WLFqZevXrmt99+c09zuVzm+uuvN9WrV3dPe/LJJ40ks3r1ave0Q4cOmfDw8EJfj3N98MEHRpLZvn27McaYrKwsExQUZF577TV3m+PHjxtJ5vbbby9yWW3atDGS3Pv65MmTi/W+c7ngdM8lomzZslq9erX27dtXZLuAgAA9/PDD7ueBgYF6+OGHdejQIX377bf/62G6nf1by7Fjx5SZmakbbrihwEPAzZo1U506ddzPjTGaO3eubrvtNhljdPjwYfcjLS1NmZmZ7uX4+/u7j2C4XC4dPXpUOTk5SklJOe/h5rzfQj/++GP9/vvvf2o733vvPY0dO1YjR47UjTfeKElat26dtm/frnvvvVdHjhxxj/3UqVNq0aKFli9fXuxDz4899pj7/3lHlc6cOaMvvvjCPb04r3Xetv7rX/8qdN1z5sxR7dq1VatWLY/X/KabbpIk9ym0Tz/9VJLUu3dvj/55R7eKq0uXLipTpoz7ebt27RQTE+Ne/rnbdurUKR0+fFjXX3+9jDH67rvvir2ue+65R+XKlXM/z/vNd+fOne5piYmJMsYU6yhKnuLU58+aN2+eXC6X2rdv71GPihUrqnr16sU+pflneLMPF/e9yRt33XWXx1Gfo0ePasmSJWrfvr1OnDjhHs+RI0eUlpam7du3a+/evZL+2D+vvfZaj6OVUVFR6tSpU7HXP336dKWkpKhatWqSpDJlyqhVq1Yep3zyLqQ9ex8uSN78rKysYq//cnJZhpTly5frtttuU2xsrBwOx5+6Q8UYo1GjRqlGjRpyOp2Ki4vTSy+9dPEHW0wjR47Uxo0bFR8fr2uuuUYvvPCCxxtsntjYWIWEhHhMq1GjhiT9pd8P8PHHH+vaa69VUFCQIiIiFBUVpXHjxikzMzNf26SkJI/nv/zyi44fP+4+z3v2o3v37pL+OFWTZ8qUKapfv76CgoIUGRmpqKgoffLJJwWu62zNmjXTXXfdpcGDB6t8+fK6/fbbNXny5HzXXxRm3bp1euSRR9SxY0f17dvXPX379u2SpK5du+Yb/9tvv63s7GxlZmbqzJkzOnDggMcjNzfXvRw/P798F9sVVMvivNb33HOPGjdurAceeEAVKlRQhw4dNHv2bI/Asn37dm3atCnfmPPWmfea//TTT/Lz88t3+q9mzZrFet3yVK9e3eO5w+FQtWrVPLZt9+7d6tatmyIiIhQaGqqoqCg1a9ZMks5b37NVrlzZ43leYDl27JhXYz5bcevzZ23fvl3GGFWvXj1fTbZs2eLxM3CxFXcflor/3uSNc98TduzYIWOMBg4cmG88gwYNkuS5f567b0nF3z+PHz+uTz/9VM2aNdOOHTvcj8aNG2vt2rXatm2bpP+Gj8Lu+slT3DBzubosr0k5deqUrrzySvXo0UN33nnnn1rGE088oc8//1yjRo1SvXr1dPToUR09evQij7T42rdvrxtuuEHz58/X559/rldeeUUjRozQvHnzdOutt/psXAX56quv1KZNGzVt2lRjx45VTEyMSpUqpcmTJ2vGjBn52p97rjjvg7Nz5875rqbPU79+fUl/XFTYrVs3tW3bVv369VN0dLT8/f01fPhw/fjjj0WO0+Fw6IMPPtCqVav00Ucf6bPPPlOPHj306quvatWqVUV+WdexY8d01113qUaNGnr77bcLHP8rr7xS6DUxoaGh+vrrr91HX/Ls2rWrwIsqC1Pc1zo4OFjLly/X0qVL9cknn2jRokV6//33ddNNN+nzzz+Xv7+/XC6X6tWrp9GjRxe4rvj4+GKP62LIzc3VzTffrKNHj+qZZ55RrVq1FBISor1796pbt25eXQjp7+9f4HRznotc/xcK+9LEswOq9Md+5HA4tHDhwgLH/7/8sr/i7sPS/+a9qbD3hKeeekppaWkF9sk76nGh5syZo+zsbL366qt69dVX882fPn26Bg8erPDwcMXExGj9+vVFLm/9+vWKi4tTWFjYRRnfpeayDCm33nprkT8c2dnZGjBggGbOnKnjx4+rbt26GjFihJo3by5J2rJli8aNG6eNGze60/e5yd4XYmJi1LNnT/Xs2VOHDh3SVVddpZdeesljW/ft26dTp055HE3JS/7efPhJhb+Zns/cuXMVFBSkzz77zONWx8mTJxerf94dI7m5uUpNTS2y7QcffKAqVapo3rx5HuPN++2qOK699lpde+21eumllzRjxgx16tRJs2bN0gMPPFBge5fLpU6dOun48eP64osvVLp0aY/5eUcYwsLCihz/lVdeqcWLF3tMO/uiPJfLpZ07d7p/O5fy19Kb19rPz08tWrRQixYtNHr0aA0bNkwDBgzQ0qVLlZqaqqpVq+r7779XixYtiqx9QkKCXC6XfvzxR4/fTrdu3Vpon4Lk/baexxijHTt2uAPohg0btG3bNk2ZMkVdunRxtzv3NZP+/L56IYpTn4KUK1euwO8u+emnnzyeV61aVcYYJSUleayjuC7kNSnuPpynOO9NFyLviFWpUqXOO56EhIR8+5ZU/P1z+vTpqlu3boHvIW+99ZZmzJjh/k6e1q1ba+LEiVqxYoWaNGmSr/1XX32ljIwMj1Pw8HRZnu45n8cee0wrV67UrFmztH79et19991q2bKle8f+6KOPVKVKFX388cdKSkpSYmKiHnjgAZ8dScnNzc13aDs6OlqxsbH5Tk3k5OTorbfecj8/c+aM3nrrLUVFRalhw4ZerTckJORPfVulv7+/HA6Hx2+GGRkZxT7t5u/vr7vuuktz587Vxo0b883/5ZdfPNpKnr8Rr169WitXrjzveo4dO5bvN+m83xqLOuUzePBgffbZZ5o5c2aB4bVhw4aqWrWqRo0apZMnTxY6/nLlyik1NdXjce53svzzn/90/98Yo3/+858qVaqUWrRoIan4r3VB++6529q+fXvt3bu3wC/5On36tE6dOiVJ7g+eN954w6PNmDFj8vUrynvvvedxqPyDDz7Q/v373csvqLbGGL3++uv5lpUXyi/k21X/zC3I56tPQapWrarMzEyP38D379+f70u/7rzzTvn7+2vw4MH59lNjjI4cOVLk2C7kNSnuPuzNe9OFiI6OVvPmzfXWW28VWJ+z3xP+9re/adWqVfrmm2885hd0C/G5fv75Zy1fvlzt27dXu3bt8j26d++uHTt2aPXq1ZKkfv36KTg4WA8//HC+ehw9elSPPPKISpcu7b4dHPldlkdSirJ7925NnjxZu3fvVmxsrKQ/DiEuWrRIkydP1rBhw7Rz50799NNPmjNnjt577z3l5uaqT58+ateunZYsWfKXj/nEiROqVKmS2rVrpyuvvFKhoaH64osvtGbNmnyHI2NjYzVixAhlZGSoRo0aev/997Vu3TpNmDDBfatlcTVs2FBffPGFRo8erdjYWCUlJalRo0bn7deqVSuNHj1aLVu21L333qtDhw7pzTffVLVq1c57aDTPyy+/rKVLl6pRo0Z68MEHVadOHR09elT/+c9/9MUXX7g/dFu3bq158+bpjjvuUKtWrbRr1y6NHz9ederUKfDN9WxTpkzR2LFjdccdd6hq1ao6ceKEJk6cqLCwMP3tb38rsM+GDRv04osvqmnTpjp06JCmTZvmMb9z587y8/PT22+/rVtvvVVXXHGFunfvrri4OO3du1dLly5VWFiYPvroo/O+BkFBQVq0aJG6du2qRo0aaeHChfrkk0/07LPPui8qLO5rPWTIEC1fvlytWrVSQkKCDh06pLFjx6pSpUru3wDvu+8+zZ49W4888oiWLl2qxo0bKzc3Vz/88INmz56tzz77TCkpKWrQoIE6duyosWPHKjMzU9dff73S09O1Y8eO827T2SIiItSkSRN1795dBw8e1JgxY1StWjU9+OCDkqRatWqpatWqeuqpp7R3716FhYVp7ty5BV5HkhfAe/furbS0NPn7+6tDhw5ejcfbW5CLU5+CdOjQQc8884zuuOMO9e7dW7/++qvGjRunGjVqeFzsXLVqVQ0dOlT9+/dXRkaG2rZtqzJlymjXrl2aP3++HnroIT311FOFrifvNRkwYIA6dOigUqVK6bbbbst3zVpBirsPF/e9admyZbrxxhs1aNCgP/3nC9588001adJE9erV04MPPqgqVaro4MGDWrlypfbs2aPvv/9e0h9fZT916lS1bNlSTzzxhPsW5ISEhPO+/8yYMUPGGLVp06bA+X/7298UEBCg6dOnq1GjRqpevbqmTJmiTp06qV69err//vuVlJSkjIwMTZo0SYcPH9bMmTO9+vqGy85ffDeRdSSZ+fPnu59//PHHRpIJCQnxeAQEBJj27dsbY4x58MEHjSSzdetWd79vv/3WSDI//PDDX70JJjs72/Tr189ceeWVpkyZMiYkJMRceeWVZuzYsR7tmjVrZq644gqzdu1ac91115mgoCCTkJBg/vnPf3q0K+4tyD/88INp2rSpCQ4OLvatmXkmTZpkqlevbpxOp6lVq5aZPHlygeuQVOjtiwcPHjS9evUy8fHxplSpUqZixYqmRYsWZsKECe42LpfLDBs2zCQkJBin02mSk5PNxx9/XOhtnmf7z3/+Yzp27GgqV65snE6niY6ONq1btzZr167NN8a8W0jzbtUs7HG27777ztx5550mMjLSOJ1Ok5CQYNq3b2/S09PP+/p17drVhISEmB9//NHccsstpnTp0qZChQpm0KBBJjc316NtcV7r9PR0c/vtt5vY2FgTGBhoYmNjTceOHc22bds8lnXmzBkzYsQIc8UVVxin02nKlStnGjZsaAYPHmwyMzPd7U6fPm169+5tIiMjTUhIiLntttvMzz//7NUtyDNnzjT9+/c30dHRJjg42LRq1crjtmJjjNm8ebNJTU01oaGhpnz58ubBBx8033//fb79Nycnxzz++OMmKirKOBwO97bn7euvvPJKvnGcO1Zvb0Eubn0Kek0+//xzU7duXRMYGGhq1qxp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gFJTU93TwsPD1ahRI61cudKbTQAAACWEV0dSdu7cqXHjxqlv37569tlntWbNGvXu3VuBgYHq2rVroX1WrFihoKAgzZ8/X4cPH1bPnj115MgR9xGZFStWaNKkSVq3bl2Byzhw4IAkqUKFCh7TK1So4J53ruzsbGVnZ7ufZ2VlebOpAADAx7wKKS6XSykpKRo2bJgkKTk5WRs3btT48eMLDSkul0sOh0PTp09XeHi4JGn06NFq166dxo4dq5ycHN13332aOHGiypcvf4Gb81/Dhw/X4MGDL9ryAADAX8urkBITE6M6dep4TKtdu7bmzp1bZJ+4uDh3QMnrY4zRnj17dOrUKWVkZOi2225zz8+7qDYgIEBbt25VxYoVJUkHDx5UTEyMu93BgwfVoEGDAtfbv39/9e3b1/08KytL8fHxxd9YAADgU16FlMaNG2vr1q0e07Zt26aEhIQi+8yZM0cnT55UaGiou4+fn58qVaokh8OhDRs2ePR57rnndOLECb3++uuKj49XqVKlVLFiRaWnp7tDSVZWllavXq1HH320wPU6nU45nU5vNg8AAFjEqwtn+/Tpo1WrVmnYsGHasWOHZsyYoQkTJqhXr17uNv3791eXLl3cz++9915FRkaqe/fu2rx5s5YvX65+/fqpR48eCg4OVlBQkOrWrevxKFu2rMqUKaO6desqMDBQDodDTz75pIYOHaoFCxZow4YN6tKli2JjY/N9nwoAALg0eHUk5eqrr9b8+fPVv39/DRkyRElJSRozZow6derkbrN//37t3r3b/Tw0NFSLFy/W448/rpSUFEVGRqp9+/YaOnSoVwN9+umnderUKT300EM6fvy4mjRpokWLFvEdKQAAXKK8CimS1Lp1a7Vu3brQ+e+++26+abVq1dLixYuLvY6CluFwODRkyBANGTKk2MsBAAAlF3+7BwAAWImQAgAArERIAQAAViKkAAAAKxFSAACAlQgpAADASoQUAABgJUIKAACwEiEFAABYiZACAACsREgBAABWIqQAAAArEVIAAICVCCkAAMBKhBQAAGAlQgoAALASIQUAAFiJkAIAAKxESAEAAFYipAAAACsRUgAAgJUIKQAAwEqEFAAAYCVCCgAAsBIhBQAAWImQAgAArERIAQAAViKkAAAAKxFSAACAlQgpAADASoQUAABgJUIKAACwEiEFAABYiZAC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AFiJkAIAAKxESAEAAFYipAAAACsRUgAAgJUIKQAAwEqEFAAAYCVCCgAAsBIhBQAAWImQAgAArERIAQAAViKkAAAAKxFSAACAlQgpAADASoQUAABgJUIKAACwEiEFAABYiZACAACsREgBAABWIqQAAAArEVIAAICVCCkAAMBKhBQAAGAlQgoAALASIQUAAFiJkAIAAKxESAEAAFYipAAAACsRUgAAgJUIKQAAwEqEFAAAYCVCCgAAsBIhBQAAWImQAgAArERIAQAAViKkAAAAKxFSAACAlQJ8PYBLRXaOSx9+t9fXw7isrf3p6B//Zhz18UhALexBLexR0mqRkONSkI/H4DDGGB+P4S+RlZWl8PBwZWZmKiws7KItd2PjpvI/8osOB4XrvpYDL9pyAQDwpamLXlT53zKVGxmlul8vv2jL9ebzmCMpF8jP8ce/oUEBGnNPA5+O5XK3NuOopq3erc7XVlZKQoSvh3NZoxb2oBb2KGm1CF0WIP323885XyCkXCTOAD+1TY7z9TAue9NW71ZKQgS1sAC1sAe1sEdJqsXmAN9ftur7EQAAABSAkAIAAKxESAEAAFYipAAAACsRUgAAgJUIKQAAwEqEFAAAYCVCCgAAsBIhBQAAWImQAgAArERIAQAAViKkAAAAKxFSAACAlQgpAADASoQUAABgJUIKAACwEiEFAABYiZACAACsREgBAABW8jqk7N27V507d1ZkZKSCg4NVr149rV27tsg+2dnZGjBggBISEuR0OpWYmKh33nnHPX/ixIm64YYbVK5cOZUrV06pqan65ptvPJbRrVs3ORwOj0fLli29HT4AACghArxpfOzYMTVu3Fg33nijFi5cqKioKG3fvl3lypUrsl/79u118OBBTZo0SdWqVdP+/fvlcrnc85ctW6aOHTvq+uuvV1BQkEaMGKFbbrlFmzZtUlxcnLtdy5YtNXnyZPdzp9PpzfABAEAJ4lVIGTFihOLj4z2CQlJSUpF9Fi1apC+//FI7d+5URESEJCkxMdGjzfTp0z2ev/3225o7d67S09PVpUsX93Sn06mKFSt6M2QAAFBCeXW6Z8GCBUpJSdHdd9+t6OhoJScna+LEicXqM3LkSMXFxalGjRp66qmndPr06UL7/Prrr/r999/doSbPsmXLFB0drZo1a+rRRx/VkSNHCl1Gdna2srKyPB4AAKDk8Cqk7Ny5U+PGjVP16tX12Wef6dFHH1Xv3r01ZcqUIvusWLFCGzdu1Pz58zVmzBh98MEH6tmzZ6F9nnnmGcXGxio1NdU9rWXLlnrvvfeUnp6uESNG6Msvv9Stt96q3NzcApcxfPhwhYeHux/x8fHebCoAAPAxr073uFwupaSkaNiwYZKk5ORkbdy4UePHj1fXrl0L7eNwODR9+nSFh4dLkkaPHq127dpp7NixCg4O9mj/8ssva9asWVq2bJmCgoLc0zt06OD+f7169VS/fn1VrVpVy5YtU4sWLfKtt3///urbt6/7eVZWFkEFAIASxKsjKTExMapTp47HtNq1a2v37t1F9omLi3MHlLw+xhjt2bPHo+2oUaP08ssv6/PPP1f9+vWLHEuVKlVUvnx57dixo8D5TqdTYWFhHg8AAFByeBVSGjdurK1bt3pM27ZtmxISEorss2/fPp08edKjj5+fnypVquSeNnLkSL344otatGiRUlJSzjuWPXv26MiRI4qJifFmEwAAQAnhVUjp06ePVq1apWHDhmnHjh2aMWOGJkyYoF69ernb9O/f3+OOnHvvvVeRkZHq3r27Nm/erOXLl6tfv37q0aOH+1TPiBEjNHDgQL3zzjtKTEzUgQMHdODAAXewOXnypPr166dVq1YpIyND6enpuv3221WtWjWlpaVdjNcBAABYxquQcvXVV2v+/PmaOXOm6tatqxdffFFjxoxRp06d3G3279/vcfonNDRUixcv1vHjx5WSkqJOnTrptttu0xtvvOFuM27cOJ05c0bt2rVTTEyM+zFq1ChJkr+/v9avX682bdqoRo0auv/++9WwYUN99dVXfFcKAACXKK8unJWk1q1bq3Xr1oXOf/fdd/NNq1WrlhYvXlxon4yMjCLXGRwcrM8++6y4QwQAAJcA/nYPAACwEiEFAABYiZACAACsREgBAABWIqQAAAArEVIAAICVCCkAAMBKhBQAAGAlQgoAALASIQUAAFiJkAIAAKxESAEAAFYipAAAACsRUgAAgJUIKQAAwEqEFAAAYCVCCgAAsBIhBQAAWImQAgAArERIAQAAViKkAAAAKxFSAACAlQgpAADASoQUAABgJUIKAACwEiEFAABYiZACAACsREgBAABWIqQAAAArEVIAAICVCCkAAMBKhBQAAGAlQgoAALASIQUAAFiJkAIAAKxESAEA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", - "text/plain": [ - "
" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "ax = tiles.plot(facecolor=\"none\", edgecolor=\"tab:blue\")\n", - "aoi_poly = shapely.geometry.box(aoi[\"west\"], aoi[\"south\"], aoi[\"east\"], aoi[\"north\"])\n", - "aoi_gdf = gpd.GeoDataFrame(geometry=[aoi_poly], crs=\"EPSG:4326\")\n", - "aoi_gdf.to_crs(tiles.crs).plot(ax=ax, facecolor=\"none\", edgecolor=\"tab:red\", linewidth=2)\n", - "ax.set_title(\"split_area size-based path: blue tiles, red AOI\")\n", - "plt.show()" - ] - }, - { - "cell_type": "markdown", - "id": "18b53e23", - "metadata": {}, - "source": [ - "## Case 2: `split_points` default quadtree path\n", - "\n", - "This follows the `tile_grid is None` branch:\n", - "\n", - "- centroids are computed\n", - "- recursive quadtree split is applied\n", - "- groups are capped by `max_points`" - ] - }, - { - "cell_type": "code", - "execution_count": 32, - "id": "485a4ff8", - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "input rows: 300\n", - "default quadtree: 8 groups\n", - "sizes: [63, 38, 28, 21, 32, 24, 43, 51]\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "C:\\Git_projects\\openeo-python-client\\openeo\\extra\\job_management\\_job_splitting.py:481: UserWarning: Geometry is in a geographic CRS. Results from 'centroid' are likely incorrect. Use 'GeoSeries.to_crs()' to re-project geometries to a projected CRS before this operation.\n", - "\n", - " centroids = points.geometry.centroid\n" - ] - } - ], - "source": [ - "rng = np.random.default_rng(42)\n", - "\n", - "cluster_a = rng.normal(loc=(3.65, 51.03), scale=0.01, size=(150, 2))\n", - "cluster_b = rng.normal(loc=(3.75, 51.09), scale=0.01, size=(150, 2))\n", - "coords = np.vstack([cluster_a, cluster_b])\n", - "\n", - "points = gpd.GeoDataFrame(\n", - " geometry=[shapely.geometry.Point(xy) for xy in coords],\n", - " crs=\"EPSG:4326\",\n", - ")\n", - "\n", - "groups = split_points(points, max_points=100)\n", - "print(\"input rows:\", len(points))\n", - "print_group_sizes(groups, \"default quadtree\")" - ] - }, - { - "cell_type": "code", - "execution_count": 33, - "id": "a23d5bf1", - "metadata": {}, - "outputs": [ - { - "data": { - "image/png": "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4+3u06ZNQ0FBAX766Sd4e3tbKmqz2pN/9uzZyM3NNXnPBgQEYOnSpThx4oQlYwNoX3aNRoNhw4bZ5Pv1rgdlr6+vR319vdXer2TDrN2+lCA+Pl5OnToler1ecnNzJT4+XlQqlfz4448iIjJ79myJj4837i8uLhY3NzdZuHCh5OXlydGjR8XHx0c++OCDB34PS572skT+zMxMcXR0lHXr1klBQYF89dVX0qlTJ/nyyy9tPvvcuXOle/fuxlvdDx06JF27dpVly5ZZNftdo0aNkpiYmGa/5saNG8XT01MOHz4subm58sILL1jkVndzZ6+rq5OJEydKYGCgZGdnm9zefPeInC3nb46lTntZIvuhQ4fEyclJdu/eLQUFBbJt2zZRq9Xy888/23z2p556SkJDQyU1NVWKiopk79694uzsLDt27DBrdrIvLD8dYP78+aLT6USj0Ui3bt0kKirK+GYWaXxzzp071+Q1v/76q0RERIhWq5Xg4GBZt25di6ezLFl+LJX/yJEjMmDAANFqtdK/f3/ZvXu3XWSvrq6WxYsXS1BQkDg7O0twcLCsWrXK7L+EHyb7xYsXBYDJvqYMBoOsXr1afH19RavVSlRUlOTl5Zk1tyWy6/V6AdDsSE1Ntfn8zbFU+bFU9j179kifPn3E2dlZBg0aJElJSXaRvby8XObNmycBAQHi7Ows/fr1k82bNxsvPCdlUomY+bG0RERERDaM1/wQERGRorD8EBERkaKw/BAREZGisPwQERGRorD8EBERkaKw/BAREZGisPwQERGRorD8EBERkaKw/BAREZGisPwQERGRorD8EBERkaKw/BAREZGi/B9Zs7NEVQGz5AAAAABJRU5ErkJggg==", - "text/plain": [ - "
" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "plot_groups(groups, \"split_points default quadtree\")\n", - "plt.show()" - ] - }, - { - "cell_type": "markdown", - "id": "b81f052b", - "metadata": {}, - "source": [ - "## Case 3: tile-grid path (`gpd.sjoin`) and why `centroids_gdf` matters\n", - "\n", - "This follows the `tile_grid is not None` branch:\n", - "\n", - "- use centroid geometries (`centroids_gdf`)\n", - "- optional CRS reprojection of centroids to tile-grid CRS\n", - "- spatial join: each centroid gets a matching tile (`index_right`)\n", - "- de-duplicate centroids that match multiple tiles on boundaries\n", - "- split each tile group separately\n", - "\n", - "We will inspect the join table directly before calling `split_points`." - ] - }, - { - "cell_type": "code", - "execution_count": 34, - "id": "b271d9c6", - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "joined rows: 300\n", - "input rows: 300\n", - "rows with no tile match (index_right is NaN): 0\n" - ] - }, - { - "data": { - "text/html": [ - "
\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
geometryindex_right
0POINT (406655.351 6624761.497)5
1POINT (407151.54 6628267.088)6
2POINT (404144.259 6624297.52)0
3POINT (406458.453 6626042.353)5
4POINT (406297.438 6625092.318)5
\n", - "
" - ], - "text/plain": [ - " geometry index_right\n", - "0 POINT (406655.351 6624761.497) 5\n", - "1 POINT (407151.54 6628267.088) 6\n", - "2 POINT (404144.259 6624297.52) 0\n", - "3 POINT (406458.453 6626042.353) 5\n", - "4 POINT (406297.438 6625092.318) 5" - ] - }, - "execution_count": 34, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "# Reuse 'tiles' from Case 1 (EPSG:3857)\n", - "points_3857 = points.to_crs(tiles.crs)\n", - "centroids_gdf = gpd.GeoDataFrame(geometry=points_3857.geometry.centroid, crs=points_3857.crs)\n", - "\n", - "joined = gpd.sjoin(\n", - " centroids_gdf,\n", - " tiles[[\"geometry\"]].reset_index(drop=True),\n", - " how=\"left\",\n", - " predicate=\"intersects\",\n", - ")\n", - "\n", - "print(\"joined rows:\", len(joined))\n", - "print(\"input rows:\", len(points_3857))\n", - "print(\"rows with no tile match (index_right is NaN):\", joined[\"index_right\"].isna().sum())\n", - "joined.head()" - ] - }, - { - "cell_type": "markdown", - "id": "fe2b9477", - "metadata": {}, - "source": [ - "### Debug view: centroid-to-tile assignment before splitting\n", - "\n", - "This plot visualizes the exact `gpd.sjoin` output used by the tile-grid branch:\n", - "\n", - "- each centroid is colored by `index_right` (the matched tile index)\n", - "- centroids with no tile match are highlighted separately\n", - "\n", - "If this looks wrong, the resulting split groups will also be wrong." - ] - }, - { - "cell_type": "code", - "execution_count": 35, - "id": "342185d1", - "metadata": {}, - "outputs": [ - { - "data": { - "image/png": "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", - "text/plain": [ - "
" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "# Visual check of tile assignment from sjoin (`index_right`)\n", - "joined_plot = joined.copy()\n", - "joined_plot[\"tile_label\"] = joined_plot[\"index_right\"].apply(\n", - " lambda v: \"unmatched\" if (v is None or (isinstance(v, float) and np.isnan(v))) else f\"tile_{int(v)}\"\n", - ")\n", - "\n", - "ax = tiles.plot(facecolor=\"none\", edgecolor=\"black\", linewidth=0.8)\n", - "\n", - "for label, sub in joined_plot.groupby(\"tile_label\"):\n", - " color = \"red\" if label == \"unmatched\" else None\n", - " sub.plot(ax=ax, markersize=18, label=f\"{label} (n={len(sub)})\", color=color)\n", - "\n", - "ax.set_title(\"Centroid -> tile assignment (from sjoin)\")\n", - "ax.legend(loc=\"center left\", bbox_to_anchor=(1.0, 0.5))\n", - "plt.show()" - ] - }, - { - "cell_type": "code", - "execution_count": 36, - "id": "d437520d", - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "tile-grid split: 16 groups\n", - "sizes: [25, 17, 45, 39, 16, 6, 1, 1, 9, 7, 38, 40, 18, 27, 9, 2]\n" - ] - }, - { - "data": { - "image/png": "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FjTkv//9L06fPo39+/fj33//xRNPPIGHHnqICxJ+/PFHBAcH4/DhwwgKCkJgYCCef/55mpHtQGprayGTyXSOeXt7QywW670NXlNTg08//ZT7vKqqCp9++im8vLwaDKrqEwqFKC0tbfS4v//+e51cyr///htnz57lAm9fX19ERERg165dOve5dOkSfv31Vzz88MONvndd2mC4/nMpKSnRm9mMiIgAgAbTC4KCguDn54ctW7aguroa0dHRADQBbmpqKr777jsMHjy4wdqnxsbWVE3p19vbGyNGjMCnn36KvLw8vfN37twxuy/tjO4nn3yic/zDDz+0eFx1ab+H6pY7A4CtW7c2qV9CCGmKTjkja0pWVhbi4uKQlZUFsVgMQPN235EjRxAXF4e3334baWlpyMzMxLfffovdu3ejtrYWixcvxtSpU/H777+38jMgzUEul8Pf3x9Tp05F//794eTkhN9++w3nzp3Dpk2bdNqKxWJs2LABGRkZ6NmzJ77++mskJydjx44dRss1GTNw4ED89ttv2Lx5M8RiMYKCgnDfffeZfX1ISAiGDh2K//znP1CpVNi6dSs8PDywbNkyrs17772H8ePH4/7778ecOXO48lsuLi7NtnVrREQEbGxssGHDBshkMggEAjz44IPYt28fPvnkEzz22GPo3r075HI5PvvsMzg7O5sVRD/wwAPYv38/wsPDudzVAQMGQCgU4vr163jmmWca7EP7x8XChQsxbtw42NjY4KmnnmraE4bx5+zt7W3W9R9//DGGDh2K8PBwvPDCCwgODkZ+fj5Onz6N7Oxs/PPPP2b1M3DgQEyZMgVbt25FUVERV35L+y5BY2eOIyIi8PTTT+OTTz6BTCbDkCFDcOzYsSbP9BJCSFNQIFvPxYsXUVtbyy3W0VKpVPDw8ACgyWdTqVTYvXs3127nzp0YOHAgUlJSaPFDB+Do6IgXX3wRv/76Kw4ePAi1Wo2QkBB88skn+M9//qPT1s3NDbt27cKCBQvw2WefoUuXLvjoo4/wwgsvWHzfzZs3Y+7cudz2oDNmzLAokH3uuefA4/GwdetWFBQUICoqCh999BF8fX25NqNHj8aRI0ewevVqrFq1Cra2thg+fDg2bNjQbCkyPj4+2L59O9avX485c+agtrYWf/zxB4YPH46///4b+/fvR35+PlxcXBAVFYW9e/eadW9t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6oXAwEDccMMNOHXqlOq+oqIiTJ06Ff7+/oiKisKiRYtgNptVbb799luMGDECvr6+GDRoENasWWP3/Ndeew39+/eHn58fRo8ejZ9++kn1emvGcr5o61xIkmT3sXHjRlUbzoV7HM3FW2+9hQkTJiA4OBiSJKGystLuPr1ej1mzZiE4OBihoaG48847cfbsWVWb/fv34/LLL4efnx/69u2L559/3q6fDz74ABdffDH8/PyQkpKCTz/9VPW6EAJLlixBTEwMdDodJk2ahIKCgg753nuSts5D//797f5OLF++XNWG8+Ce5nOh1+uxcOFCJCUlQafTIT4+Hvfffz+qqqpU9/F3BZEHER7mp59+Ev379xdDhw4VDzzwgHL97rvvFn379hVff/21+Pnnn8Wll14qLrvsMuV1s9kshgwZIiZNmiT27dsnPv30UxERESEWL16stDly5Ijw9/cXDz30kMjLyxOvvvqq8PLyEp9//rnSZuPGjcLHx0f861//Er/++quYN2+eCA0NFadOnWr1WM4XbZ0LIYQAIFavXi1KSkqUj9raWuV1zoV7nM3Fiy++KJ599lnx7LPPCgCioqLC7t4pU6aIYcOGiR9//FFkZWWJQYMGiVtvvVV5vaqqSvTu3VvMmjVLHDhwQGzYsEHodDrx5ptvKm127twpvLy8xPPPPy/y8vLEX//6V+Ht7S1yc3OVNsuXLxchISHiP//5j/jll1/EtddeKwYMGKCad0/Xnnno16+fePrpp1V/J86ePau8znlwj6O5yM3NFddff73IzMwUhw8fFl9//bVITEwUN9xwg3Iff1cQeRaPCmSNRqNITEwU27ZtE+PHj1f+51RZWSm8vb3FBx98oLT97bffBACxa9cuIYQQn376qdBoNKK0tFRp88Ybb4jg4GBRX18vhBDikUceEZdcconqmTfffLPIyMhQvk5LSxP33nuv8nVjY6OIjY0Vzz77bKvHcj5oz1wIIQeyW7Zscdo/56L1nM2FrW+++cZhAJWXlycAiD179ijXPvvsMyFJkjh58qQQQojXX39dhIWFKX9PhBDi0UcfFUlJScrXN910k5g6daqq79GjR4sFCxYIIYSwWCwiOjpa/P3vf1der6ysFL6+vmLDhg1t/t57kvbMgxByIPviiy867Z/z0HqtmQurTZs2CR8fH2EymYQQ/F1B5Gk8KrXg3nvvxdSpUzFp0iTV9ezsbJhMJtX1iy++GPHx8di1axcAYNeuXUhJSUHv3r2VNhkZGTAYDPj111+VNs37zsjIUPpoaGhAdna2qo1Go8GkSZOUNq0Zy/mgPXNh20dERATS0tLwr3/9C8KmpDHnovWczUVr7Nq1C6GhoRg1apRybdKkSdBoNNi9e7fSZty4cfDx8VHaZGRkID8/HxUVFUobV/N19OhRlJaWqtqEhIRg9OjR581ctGcerJYvX45evXohNTUVf//731VvZ3MeWs+duaiqqkJwcDC0Wi0A/q4g8jTa7h5Aa23cuBF79+7Fnj177F4rLS2Fj48PQkNDVdd79+6N0tJSpY3t/5isr1tfc9XGYDCgtrYWFRUVaGxsdNjm4MGDrR6Lp2vvXADA008/jSuvvBL+/v748ssvcc899+Ds2bO4//77lX44Fy1zNRetUVpaiqioKNU1rVaL8PBw1d+LAQMGqNrY/t0JCwtzOl+2fdje56iNJ2vvPADA/fffjxEjRiA8PBw//PADFi9ejJKSEqxcuRIA56G13JmL8vJyLFu2DPPnz1eu8XcFkWfxiED2999/xwMPPIBt27bBz8+vu4dzQeuoufi///s/5c+pqamorq7G3//+dyWQpZbx70XP0FHz8NBDDyl/Hjp0KHx8fLBgwQI8++yzPA61ldyZC4PBgKlTpyI5ORlPPvlk1wyQiDqcR6QWZGdno6ysDCNGjIBWq4VWq8WOHTvwyiuvQKvVonfv3mhoaLDbCXzq1ClER0cDAKKjo+12g1q/bqlNcHAwdDodIiIi4OXl5bCNbR8tjcWTdcRcODJ69GicOHEC9fX1ADgXrdHSXDQ2NrbYR3R0NMrKylTXzGYz9Hp9h/zdsX3d9j5HbTxVR8yDI6NHj4bZbMaxY8cAcB5ao7VzYTQaMWXKFAQFBWHLli3w9vZW+uDvCiLP4hGB7MSJE5Gbm4ucnBzlY9SoUZg1a5byZ29vb3z99dfKPfn5+SgqKsKYMWMAAGPGjEFubq7ql/a2bdsQHByM5ORkpY1tH9Y21j58fHwwcuRIVRuLxYKvv/5aaTNy5MgWx+LJOmIuHMnJyUFYWJiy8sS5aFlLc+Hl5dViH2PGjEFlZSWys7OVa9u3b4fFYsHo0aOVNt999x1MJpPSZtu2bUhKSkJYWJjSxtV8DRgwANHR0ao2BoMBu3fv9vi56Ih5cCQnJwcajUZJ/eA8tKw1c2EwGDB58mT4+PggMzPTbuWWvyuIPEx37zZrq+Y7Ue+++24RHx8vtm/fLn7++WcxZswYMWbMGOV1a0mVyZMni5ycHPH555+LyMhIhyVVFi1aJH777Tfx2muvOSyp4uvrK9asWSPy8vLE/PnzRWhoqGqHa0tjOd+4OxeZmZni7bffFrm5uaKgoEC8/vrrwt/fXyxZskRpw7lom+ZzUVJSIvbt2yfefvttAUB89913Yt++feLMmTNKmylTpojU1FSxe/du8f3334vExERV+a3KykrRu3dvMXv2bHHgwAGxceNG4e/vb1f2SavVihUrVojffvtNLF261GHZp9DQUPHxxx+L/fv3ixkzZpyXZZ+EcH8efvjhB/Hiiy+KnJwcUVhYKN5//30RGRkpbr/9dqUPzkPb2M5FVVWVGD16tEhJSRGHDx9WlTozm81CCP6uIPI0500gW1tbK+655x4RFhYm/P39xR//+EdRUlKiuufYsWPi6quvFjqdTkRERIj//d//VUquWH3zzTdi+PDhwsfHRwwcOFCsXr3a7tmvvvqqiI+PFz4+PiItLU38+OOPqtdbM5bzibtz8dlnn4nhw4eLwMBAERAQIIYNGyZWrVolGhsbVf1yLtzXfC6WLl0qANh92P4sz5w5I2699VYRGBgogoODxdy5c4XRaFT1+8svv4ixY8cKX19fERcXJ5YvX2737E2bNomLLrpI+Pj4iEsuuUT897//Vb1usVjE//3f/4nevXsLX19fMXHiRJGfn9+h339P4e48ZGdni9GjR4uQkBDh5+cnBg8eLP72t7+Juro6Vb+cB/fZzoW1/Jmjj6NHjyr38HcFkeeQhLCpeURERERE5CE8IkeWiIiIiKg5BrJERERE5JEYyBIRERGRR2IgS0REREQeiYEsEREREXkkBrJERERE5JEYyBIRERGRR2IgS0REREQeiYEsEREREXkkBrJERERE5JEYyBIRERGRR2IgS0REREQe6f8DJnJ6fMhwzO4AAAAASUVORK5CYII=", - "text/plain": [ - "
" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "tile_groups = split_points(points_3857, max_points=50, tile_grid=tiles)\n", - "print_group_sizes(tile_groups, \"tile-grid split\")\n", - "\n", - "ax = tiles.plot(facecolor=\"none\", edgecolor=\"lightgrey\")\n", - "plot_groups(tile_groups, \"split_points with tile_grid\", ax=ax, markersize=14)\n", - "plt.show()" - ] - }, - { - "cell_type": "markdown", - "id": "d5b055e4", - "metadata": {}, - "source": [ - "## Case 4: quadtree no-progress fallback (`any(len(q) == len(idx))`)\n", - "\n", - "When all points coincide (or all land in one quadrant), recursive quartering\n", - "cannot reduce the set size. The implementation then falls back to plain\n", - "chunking by `max_points` to guarantee termination." - ] - }, - { - "cell_type": "code", - "execution_count": 37, - "id": "3350f55c", - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "coincident fallback: 3 groups\n", - "sizes: [10, 10, 5]\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "C:\\Git_projects\\openeo-python-client\\openeo\\extra\\job_management\\_job_splitting.py:481: UserWarning: Geometry is in a geographic CRS. Results from 'centroid' are likely incorrect. Use 'GeoSeries.to_crs()' to re-project geometries to a projected CRS before this operation.\n", - "\n", - " centroids = points.geometry.centroid\n" - ] - } - ], - "source": [ - "coincident = gpd.GeoDataFrame(\n", - " geometry=[shapely.geometry.Point(0, 0)] * 25,\n", - " crs=\"EPSG:4326\",\n", - ")\n", - "coincident_groups = split_points(coincident, max_points=10)\n", - "print_group_sizes(coincident_groups, \"coincident fallback\")" - ] - }, - { - "cell_type": "markdown", - "id": "3a4fa0f8", - "metadata": {}, - "source": [ - "## Case 5: row semantics (`Point` and `MultiPoint`)\n", - "\n", - "`split_points` counts rows (features), not embedded coordinates.\n", - "A single `MultiPoint` row is still one feature for `max_points`." - ] - }, - { - "cell_type": "code", - "execution_count": 38, - "id": "f5d1b42e", - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "row count: 2\n", - "group row counts: [2]\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "C:\\Git_projects\\openeo-python-client\\openeo\\extra\\job_management\\_job_splitting.py:481: UserWarning: Geometry is in a geographic CRS. Results from 'centroid' are likely incorrect. Use 'GeoSeries.to_crs()' to re-project geometries to a projected CRS before this operation.\n", - "\n", - " centroids = points.geometry.centroid\n" - ] - } - ], - "source": [ - "mixed = gpd.GeoDataFrame(\n", - " geometry=[\n", - " shapely.geometry.MultiPoint([(0, 0), (0.1, 0.1), (0.2, 0.2)]),\n", - " shapely.geometry.Point(0.5, 0.5),\n", - " ],\n", - " crs=\"EPSG:4326\",\n", - ")\n", - "mixed_groups = split_points(mixed, max_points=10)\n", - "print(\"row count:\", len(mixed))\n", - "print(\"group row counts:\", [len(g) for g in mixed_groups])" - ] - }, - { - "cell_type": "markdown", - "id": "322f339d", - "metadata": {}, - "source": [ - "## Case 6: centroid on shared tile boundary (duplicate join rows)\n", - "\n", - "A point exactly on a shared edge can intersect more than one tile, producing\n", - "multiple rows in `sjoin`. The implementation then keeps only the first match\n", - "for that centroid index." - ] - }, - { - "cell_type": "code", - "execution_count": 39, - "id": "e7856a74", - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "raw sjoin rows: 3\n", - " geometry index_right\n", - "0 POINT (1 0.5) 0\n", - "0 POINT (1 0.5) 1\n", - "1 POINT (0.5 0.5) 0\n", - "rows after dedup-by-left-index: 2\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "C:\\Users\\VROMPAYH\\AppData\\Local\\Temp\\ipykernel_31420\\1828992808.py:18: UserWarning: Geometry is in a geographic CRS. Results from 'centroid' are likely incorrect. Use 'GeoSeries.to_crs()' to re-project geometries to a projected CRS before this operation.\n", - "\n", - " gpd.GeoDataFrame(geometry=boundary_points.geometry.centroid, crs=boundary_points.crs),\n" - ] - } - ], - "source": [ - "boundary_tiles = gpd.GeoDataFrame(\n", - " geometry=[\n", - " shapely.geometry.box(0, 0, 1, 1),\n", - " shapely.geometry.box(1, 0, 2, 1),\n", - " ],\n", - " crs=\"EPSG:4326\",\n", - ")\n", - "\n", - "boundary_points = gpd.GeoDataFrame(\n", - " geometry=[\n", - " shapely.geometry.Point(1, 0.5), # exactly on shared boundary x=1\n", - " shapely.geometry.Point(0.5, 0.5),\n", - " ],\n", - " crs=\"EPSG:4326\",\n", - ")\n", - "\n", - "raw_join = gpd.sjoin(\n", - " gpd.GeoDataFrame(geometry=boundary_points.geometry.centroid, crs=boundary_points.crs),\n", - " boundary_tiles[[\"geometry\"]].reset_index(drop=True),\n", - " how=\"left\",\n", - " predicate=\"intersects\",\n", - ")\n", - "\n", - "print(\"raw sjoin rows:\", len(raw_join))\n", - "print(raw_join)\n", - "print(\"rows after dedup-by-left-index:\", len(raw_join.loc[~raw_join.index.duplicated(keep=\"first\")]))" - ] - }, - { - "cell_type": "markdown", - "id": "1282f69d", - "metadata": {}, - "source": [ - "## Case 7: unmatched points (`index_right = NaN`) and CRS reprojection\n", - "\n", - "This demonstrates two behaviors:\n", - "\n", - "- points outside all tiles are still kept (left join) and split separately\n", - "- when point CRS differs from tile-grid CRS, centroids are reprojected before\n", - " the spatial join" - ] - }, - { - "cell_type": "code", - "execution_count": 40, - "id": "55554db2", - "metadata": {}, - "outputs": [ - { - "name": "stderr", - "output_type": "stream", - "text": [ - "C:\\Git_projects\\openeo-python-client\\openeo\\extra\\job_management\\_job_splitting.py:481: UserWarning: Geometry is in a geographic CRS. Results from 'centroid' are likely incorrect. Use 'GeoSeries.to_crs()' to re-project geometries to a projected CRS before this operation.\n", - "\n", - " centroids = points.geometry.centroid\n" - ] - }, - { - "ename": "ValueError", - "evalue": "Categorical categories cannot be null", - "output_type": "error", - "traceback": [ - "\u001b[31m---------------------------------------------------------------------------\u001b[39m", - "\u001b[31mValueError\u001b[39m Traceback (most recent call last)", - "\u001b[36mCell\u001b[39m\u001b[36m \u001b[39m\u001b[32mIn[40]\u001b[39m\u001b[32m, line 13\u001b[39m\n\u001b[32m 9\u001b[39m geometry=[shapely.geometry.Point(\u001b[32m0.1\u001b[39m, \u001b[32m0.1\u001b[39m), shapely.geometry.Point(\u001b[32m50\u001b[39m, \u001b[32m50\u001b[39m)],\n\u001b[32m 10\u001b[39m crs=\u001b[33m\"EPSG:4326\"\u001b[39m,\n\u001b[32m 11\u001b[39m )\n\u001b[32m 12\u001b[39m \n\u001b[32m---> \u001b[39m\u001b[32m13\u001b[39m mixed_groups = split_points(points_wgs84, max_points=\u001b[32m10\u001b[39m, tile_grid=wm_tile_grid)\n\u001b[32m 14\u001b[39m print_group_sizes(mixed_groups, \u001b[33m\"CRS mismatch + unmatched point\"\u001b[39m)\n\u001b[32m 15\u001b[39m \u001b[38;5;28;01mfor\u001b[39;00m i, g \u001b[38;5;28;01min\u001b[39;00m enumerate(mixed_groups):\n\u001b[32m 16\u001b[39m print(f\"group {i} bounds:\", g.total_bounds)\n", - "\u001b[36mFile \u001b[39m\u001b[32mC:\\Git_projects\\openeo-python-client\\openeo\\extra\\job_management\\_job_splitting.py:496\u001b[39m, in \u001b[36msplit_points\u001b[39m\u001b[34m(points, max_points, tile_grid)\u001b[39m\n\u001b[32m 493\u001b[39m \u001b[38;5;66;03m# A centroid on a shared tile edge can match multiple tiles: keep just the first match.\u001b[39;00m\n\u001b[32m 494\u001b[39m joined = joined.loc[~joined.index.duplicated(keep=\u001b[33m\"\u001b[39m\u001b[33mfirst\u001b[39m\u001b[33m\"\u001b[39m)]\n\u001b[32m--> \u001b[39m\u001b[32m496\u001b[39m \u001b[38;5;28;01mfor\u001b[39;00m _, idx \u001b[38;5;129;01min\u001b[39;00m \u001b[30;43mjoined\u001b[39;49m\u001b[30;43m.\u001b[39;49m\u001b[30;43mgroupby\u001b[39;49m\u001b[30;43m(\u001b[39;49m\u001b[30;43m\"\u001b[39;49m\u001b[30;43mindex_right\u001b[39;49m\u001b[30;43m\"\u001b[39;49m\u001b[30;43m,\u001b[39;49m\u001b[30;43m \u001b[39;49m\u001b[30;43mdropna\u001b[39;49m\u001b[30;43m=\u001b[39;49m\u001b[30;43;01mFalse\u001b[39;49;00m\u001b[30;43m)\u001b[39;49m\u001b[30;43m.\u001b[39;49m\u001b[30;43mgroups\u001b[39;49m.items():\n\u001b[32m 497\u001b[39m _quadtree_split_indices(centroids, idx, max_points, index_groups)\n\u001b[32m 499\u001b[39m \u001b[38;5;28;01mreturn\u001b[39;00m [points.loc[idx].reset_index(drop=\u001b[38;5;28;01mTrue\u001b[39;00m) \u001b[38;5;28;01mfor\u001b[39;00m idx \u001b[38;5;129;01min\u001b[39;00m index_groups]\n", - "\u001b[36mFile \u001b[39m\u001b[32m~\\AppData\\Local\\anaconda3\\envs\\openeopythonclient\\Lib\\site-packages\\pandas\\core\\groupby\\groupby.py:853\u001b[39m, in \u001b[36mBaseGroupBy.groups\u001b[39m\u001b[34m(self)\u001b[39m\n\u001b[32m 807\u001b[39m \u001b[38;5;129m@final\u001b[39m\n\u001b[32m 808\u001b[39m \u001b[38;5;129m@property\u001b[39m\n\u001b[32m 809\u001b[39m \u001b[38;5;28;01mdef\u001b[39;00m\u001b[38;5;250m \u001b[39m\u001b[34mgroups\u001b[39m(\u001b[38;5;28mself\u001b[39m) -> \u001b[38;5;28mdict\u001b[39m[Hashable, np.ndarray]:\n\u001b[32m 810\u001b[39m \u001b[38;5;250m \u001b[39m\u001b[33;03m\"\"\"\u001b[39;00m\n\u001b[32m 811\u001b[39m \u001b[33;03m Dict {group name -> group labels}.\u001b[39;00m\n\u001b[32m 812\u001b[39m \n\u001b[32m (...)\u001b[39m\u001b[32m 851\u001b[39m \u001b[33;03m {Timestamp('2023-01-01 00:00:00'): 2, Timestamp('2023-02-01 00:00:00'): 4}\u001b[39;00m\n\u001b[32m 852\u001b[39m \u001b[33;03m \"\"\"\u001b[39;00m\n\u001b[32m--> \u001b[39m\u001b[32m853\u001b[39m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[30;43mself\u001b[39;49m\u001b[30;43m.\u001b[39;49m\u001b[30;43m_grouper\u001b[39;49m\u001b[30;43m.\u001b[39;49m\u001b[30;43mgroups\u001b[39;49m\n", - "\u001b[36mFile \u001b[39m\u001b[32mpandas/_libs/properties.pyx:36\u001b[39m, in \u001b[36mpandas._libs.properties.CachedProperty.__get__\u001b[39m\u001b[34m()\u001b[39m\n\u001b[32m---> \u001b[39m\u001b[32m36\u001b[39m \u001b[33m'Could not get source, probably due dynamically evaluated source code.'\u001b[39m\n", - "\u001b[36mFile \u001b[39m\u001b[32m~\\AppData\\Local\\anaconda3\\envs\\openeopythonclient\\Lib\\site-packages\\pandas\\core\\groupby\\ops.py:726\u001b[39m, in \u001b[36mBaseGrouper.groups\u001b[39m\u001b[34m(self)\u001b[39m\n\u001b[32m 724\u001b[39m \u001b[38;5;250m\u001b[39m\u001b[33;03m\"\"\"dict {group name -> group labels}\"\"\"\u001b[39;00m\n\u001b[32m 725\u001b[39m \u001b[38;5;28;01mif\u001b[39;00m \u001b[38;5;28mlen\u001b[39m(\u001b[38;5;28mself\u001b[39m.groupings) == \u001b[32m1\u001b[39m:\n\u001b[32m--> \u001b[39m\u001b[32m726\u001b[39m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[30;43mself\u001b[39;49m\u001b[30;43m.\u001b[39;49m\u001b[30;43mgroupings\u001b[39;49m\u001b[30;43m[\u001b[39;49m\u001b[30;43m0\u001b[39;49m\u001b[30;43m]\u001b[39;49m\u001b[30;43m.\u001b[39;49m\u001b[30;43mgroups\u001b[39;49m\n\u001b[32m 727\u001b[39m \u001b[38;5;28;01melse\u001b[39;00m:\n\u001b[32m 728\u001b[39m to_groupby = []\n", - "\u001b[36mFile \u001b[39m\u001b[32mpandas/_libs/properties.pyx:36\u001b[39m, in \u001b[36mpandas._libs.properties.CachedProperty.__get__\u001b[39m\u001b[34m()\u001b[39m\n\u001b[32m---> \u001b[39m\u001b[32m36\u001b[39m \u001b[33m'Could not get source, probably due dynamically evaluated source code.'\u001b[39m\n", - "\u001b[36mFile \u001b[39m\u001b[32m~\\AppData\\Local\\anaconda3\\envs\\openeopythonclient\\Lib\\site-packages\\pandas\\core\\groupby\\grouper.py:842\u001b[39m, in \u001b[36mGrouping.groups\u001b[39m\u001b[34m(self)\u001b[39m\n\u001b[32m 840\u001b[39m \u001b[38;5;129m@cache_readonly\u001b[39m\n\u001b[32m 841\u001b[39m \u001b[38;5;28;01mdef\u001b[39;00m\u001b[38;5;250m \u001b[39m\u001b[34mgroups\u001b[39m(\u001b[38;5;28mself\u001b[39m) -> \u001b[38;5;28mdict\u001b[39m[Hashable, np.ndarray]:\n\u001b[32m--> \u001b[39m\u001b[32m842\u001b[39m cats = \u001b[30;43mCategorical\u001b[39;49m\u001b[30;43m.\u001b[39;49m\u001b[30;43mfrom_codes\u001b[39;49m\u001b[30;43m(\u001b[39;49m\u001b[30;43mself\u001b[39;49m\u001b[30;43m.\u001b[39;49m\u001b[30;43mcodes\u001b[39;49m\u001b[30;43m,\u001b[39;49m\u001b[30;43m \u001b[39;49m\u001b[30;43mself\u001b[39;49m\u001b[30;43m.\u001b[39;49m\u001b[30;43m_group_index\u001b[39;49m\u001b[30;43m,\u001b[39;49m\u001b[30;43m \u001b[39;49m\u001b[30;43mvalidate\u001b[39;49m\u001b[30;43m=\u001b[39;49m\u001b[30;43;01mFalse\u001b[39;49;00m\u001b[30;43m)\u001b[39;49m\n\u001b[32m 843\u001b[39m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[38;5;28mself\u001b[39m._index.groupby(cats)\n", - "\u001b[36mFile \u001b[39m\u001b[32m~\\AppData\\Local\\anaconda3\\envs\\openeopythonclient\\Lib\\site-packages\\pandas\\core\\arrays\\categorical.py:746\u001b[39m, in \u001b[36mCategorical.from_codes\u001b[39m\u001b[34m(cls, codes, categories, ordered, dtype, validate)\u001b[39m\n\u001b[32m 693\u001b[39m \u001b[38;5;129m@classmethod\u001b[39m\n\u001b[32m 694\u001b[39m \u001b[38;5;28;01mdef\u001b[39;00m\u001b[38;5;250m \u001b[39m\u001b[34mfrom_codes\u001b[39m(\n\u001b[32m 695\u001b[39m \u001b[38;5;28mcls\u001b[39m,\n\u001b[32m (...)\u001b[39m\u001b[32m 700\u001b[39m validate: \u001b[38;5;28mbool\u001b[39m = \u001b[38;5;28;01mTrue\u001b[39;00m,\n\u001b[32m 701\u001b[39m ) -> Self:\n\u001b[32m 702\u001b[39m \u001b[38;5;250m \u001b[39m\u001b[33;03m\"\"\"\u001b[39;00m\n\u001b[32m 703\u001b[39m \u001b[33;03m Make a Categorical type from codes and categories or dtype.\u001b[39;00m\n\u001b[32m 704\u001b[39m \n\u001b[32m (...)\u001b[39m\u001b[32m 744\u001b[39m \u001b[33;03m Categories (2, object): ['a' < 'b']\u001b[39;00m\n\u001b[32m 745\u001b[39m \u001b[33;03m \"\"\"\u001b[39;00m\n\u001b[32m--> \u001b[39m\u001b[32m746\u001b[39m dtype = \u001b[30;43mCategoricalDtype\u001b[39;49m\u001b[30;43m.\u001b[39;49m\u001b[30;43m_from_values_or_dtype\u001b[39;49m\u001b[30;43m(\u001b[39;49m\n\u001b[32m 747\u001b[39m \u001b[30;43m \u001b[39;49m\u001b[30;43mcategories\u001b[39;49m\u001b[30;43m=\u001b[39;49m\u001b[30;43mcategories\u001b[39;49m\u001b[30;43m,\u001b[39;49m\u001b[30;43m \u001b[39;49m\u001b[30;43mordered\u001b[39;49m\u001b[30;43m=\u001b[39;49m\u001b[30;43mordered\u001b[39;49m\u001b[30;43m,\u001b[39;49m\u001b[30;43m \u001b[39;49m\u001b[30;43mdtype\u001b[39;49m\u001b[30;43m=\u001b[39;49m\u001b[30;43mdtype\u001b[39;49m\n\u001b[32m 748\u001b[39m \u001b[30;43m \u001b[39;49m\u001b[30;43m)\u001b[39;49m\n\u001b[32m 749\u001b[39m \u001b[38;5;28;01mif\u001b[39;00m dtype.categories \u001b[38;5;129;01mis\u001b[39;00m \u001b[38;5;28;01mNone\u001b[39;00m:\n\u001b[32m 750\u001b[39m msg = (\n\u001b[32m 751\u001b[39m \u001b[33m\"\u001b[39m\u001b[33mThe categories must be provided in \u001b[39m\u001b[33m'\u001b[39m\u001b[33mcategories\u001b[39m\u001b[33m'\u001b[39m\u001b[33m or \u001b[39m\u001b[33m\"\u001b[39m\n\u001b[32m 752\u001b[39m \u001b[33m\"\u001b[39m\u001b[33m'\u001b[39m\u001b[33mdtype\u001b[39m\u001b[33m'\u001b[39m\u001b[33m. Both were None.\u001b[39m\u001b[33m\"\u001b[39m\n\u001b[32m 753\u001b[39m )\n", - "\u001b[36mFile \u001b[39m\u001b[32m~\\AppData\\Local\\anaconda3\\envs\\openeopythonclient\\Lib\\site-packages\\pandas\\core\\dtypes\\dtypes.py:338\u001b[39m, in \u001b[36mCategoricalDtype._from_values_or_dtype\u001b[39m\u001b[34m(cls, values, categories, ordered, dtype)\u001b[39m\n\u001b[32m 331\u001b[39m dtype = values.dtype._from_categorical_dtype(\n\u001b[32m 332\u001b[39m values.dtype, categories, ordered\n\u001b[32m 333\u001b[39m )\n\u001b[32m 334\u001b[39m \u001b[38;5;28;01melse\u001b[39;00m:\n\u001b[32m 335\u001b[39m \u001b[38;5;66;03m# If dtype=None and values is not categorical, create a new dtype.\u001b[39;00m\n\u001b[32m 336\u001b[39m \u001b[38;5;66;03m# Note: This could potentially have categories=None and\u001b[39;00m\n\u001b[32m 337\u001b[39m \u001b[38;5;66;03m# ordered=None.\u001b[39;00m\n\u001b[32m--> \u001b[39m\u001b[32m338\u001b[39m dtype = \u001b[30;43mCategoricalDtype\u001b[39;49m\u001b[30;43m(\u001b[39;49m\u001b[30;43mcategories\u001b[39;49m\u001b[30;43m,\u001b[39;49m\u001b[30;43m \u001b[39;49m\u001b[30;43mordered\u001b[39;49m\u001b[30;43m)\u001b[39;49m\n\u001b[32m 340\u001b[39m \u001b[38;5;28;01mreturn\u001b[39;00m cast(CategoricalDtype, dtype)\n", - "\u001b[36mFile \u001b[39m\u001b[32m~\\AppData\\Local\\anaconda3\\envs\\openeopythonclient\\Lib\\site-packages\\pandas\\core\\dtypes\\dtypes.py:221\u001b[39m, in \u001b[36mCategoricalDtype.__init__\u001b[39m\u001b[34m(self, categories, ordered)\u001b[39m\n\u001b[32m 220\u001b[39m \u001b[38;5;28;01mdef\u001b[39;00m\u001b[38;5;250m \u001b[39m\u001b[34m__init__\u001b[39m(\u001b[38;5;28mself\u001b[39m, categories=\u001b[38;5;28;01mNone\u001b[39;00m, ordered: Ordered = \u001b[38;5;28;01mFalse\u001b[39;00m) -> \u001b[38;5;28;01mNone\u001b[39;00m:\n\u001b[32m--> \u001b[39m\u001b[32m221\u001b[39m \u001b[30;43mself\u001b[39;49m\u001b[30;43m.\u001b[39;49m\u001b[30;43m_finalize\u001b[39;49m\u001b[30;43m(\u001b[39;49m\u001b[30;43mcategories\u001b[39;49m\u001b[30;43m,\u001b[39;49m\u001b[30;43m \u001b[39;49m\u001b[30;43mordered\u001b[39;49m\u001b[30;43m,\u001b[39;49m\u001b[30;43m \u001b[39;49m\u001b[30;43mfastpath\u001b[39;49m\u001b[30;43m=\u001b[39;49m\u001b[30;43;01mFalse\u001b[39;49;00m\u001b[30;43m)\u001b[39;49m\n", - "\u001b[36mFile \u001b[39m\u001b[32m~\\AppData\\Local\\anaconda3\\envs\\openeopythonclient\\Lib\\site-packages\\pandas\\core\\dtypes\\dtypes.py:378\u001b[39m, in \u001b[36mCategoricalDtype._finalize\u001b[39m\u001b[34m(self, categories, ordered, fastpath)\u001b[39m\n\u001b[32m 375\u001b[39m \u001b[38;5;28mself\u001b[39m.validate_ordered(ordered)\n\u001b[32m 377\u001b[39m \u001b[38;5;28;01mif\u001b[39;00m categories \u001b[38;5;129;01mis\u001b[39;00m \u001b[38;5;129;01mnot\u001b[39;00m \u001b[38;5;28;01mNone\u001b[39;00m:\n\u001b[32m--> \u001b[39m\u001b[32m378\u001b[39m categories = \u001b[30;43mself\u001b[39;49m\u001b[30;43m.\u001b[39;49m\u001b[30;43mvalidate_categories\u001b[39;49m\u001b[30;43m(\u001b[39;49m\u001b[30;43mcategories\u001b[39;49m\u001b[30;43m,\u001b[39;49m\u001b[30;43m \u001b[39;49m\u001b[30;43mfastpath\u001b[39;49m\u001b[30;43m=\u001b[39;49m\u001b[30;43mfastpath\u001b[39;49m\u001b[30;43m)\u001b[39;49m\n\u001b[32m 380\u001b[39m \u001b[38;5;28mself\u001b[39m._categories = categories\n\u001b[32m 381\u001b[39m \u001b[38;5;28mself\u001b[39m._ordered = ordered\n", - "\u001b[36mFile \u001b[39m\u001b[32m~\\AppData\\Local\\anaconda3\\envs\\openeopythonclient\\Lib\\site-packages\\pandas\\core\\dtypes\\dtypes.py:576\u001b[39m, in \u001b[36mCategoricalDtype.validate_categories\u001b[39m\u001b[34m(categories, fastpath)\u001b[39m\n\u001b[32m 574\u001b[39m \u001b[38;5;28;01mif\u001b[39;00m \u001b[38;5;129;01mnot\u001b[39;00m fastpath:\n\u001b[32m 575\u001b[39m \u001b[38;5;28;01mif\u001b[39;00m categories.hasnans:\n\u001b[32m--> \u001b[39m\u001b[32m576\u001b[39m \u001b[38;5;28;01mraise\u001b[39;00m \u001b[38;5;167;01mValueError\u001b[39;00m(\u001b[33m\"\u001b[39m\u001b[33mCategorical categories cannot be null\u001b[39m\u001b[33m\"\u001b[39m)\n\u001b[32m 578\u001b[39m \u001b[38;5;28;01mif\u001b[39;00m \u001b[38;5;129;01mnot\u001b[39;00m categories.is_unique:\n\u001b[32m 579\u001b[39m \u001b[38;5;28;01mraise\u001b[39;00m \u001b[38;5;167;01mValueError\u001b[39;00m(\u001b[33m\"\u001b[39m\u001b[33mCategorical categories must be unique\u001b[39m\u001b[33m\"\u001b[39m)\n", - "\u001b[31mValueError\u001b[39m: Categorical categories cannot be null" - ] - } - ], - "source": [ - "# Tile grid in EPSG:3857\n", - "wm_tile_grid = gpd.GeoDataFrame(\n", - " geometry=[shapely.geometry.box(-111_320, -111_325, 111_320, 111_325)],\n", - " crs=\"EPSG:3857\",\n", - ")\n", - "\n", - "# Points in EPSG:4326 (one near origin, one far away)\n", - "points_wgs84 = gpd.GeoDataFrame(\n", - " geometry=[shapely.geometry.Point(0.1, 0.1), shapely.geometry.Point(50, 50)],\n", - " crs=\"EPSG:4326\",\n", - ")\n", - "\n", - "mixed_groups = split_points(points_wgs84, max_points=10, tile_grid=wm_tile_grid)\n", - "print_group_sizes(mixed_groups, \"CRS mismatch + unmatched point\")\n", - "for i, g in enumerate(mixed_groups):\n", - " print(f\"group {i} bounds:\", g.total_bounds)" - ] - }, - { - "cell_type": "markdown", - "id": "0c5c8f9b", - "metadata": {}, - "source": [ - "## Case 8: validation errors (guard clauses)\n", - "\n", - "These are the explicit checks in `split_points` and `split_area`.\n", - "Run this cell to see the exact exceptions raised by invalid inputs." - ] - }, - { - "cell_type": "code", - "execution_count": 41, - "id": "3c4452e0", - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "[split_points missing geometry column] JobSplittingFailure: The GeoDataFrame must contain a 'geometry' column.\n", - "[split_points missing CRS] JobSplittingFailure: The GeoDataFrame must have a CRS set.\n", - "[split_points non-positive max_points] JobSplittingFailure: 'max_points' must be positive, got 0.\n", - "[split_points tile_grid missing CRS] JobSplittingFailure: The 'tile_grid' GeoDataFrame must have a CRS set.\n", - "[split_area conflicting args] JobSplittingFailure: Cannot combine 'tile_grid' with 'projection' or 'tile_size'. Either pass a _TileGridInterface, or pass projection + tile_size.\n", - "[split_area missing projection] JobSplittingFailure: 'projection' is required when using size-based tiling.\n", - "[split_area invalid AOI type] JobSplittingFailure: Expected a bounding-box dict, Polygon, or MultiPolygon, got str.\n" - ] - } - ], - "source": [ - "def show_error(label, fn):\n", - " try:\n", - " fn()\n", - " except Exception as e:\n", - " print(f\"[{label}] {type(e).__name__}: {e}\")\n", - "\n", - "show_error(\n", - " \"split_points missing geometry column\",\n", - " lambda: split_points(gpd.GeoDataFrame({\"foo\": [1, 2]})),\n", - ")\n", - "\n", - "show_error(\n", - " \"split_points missing CRS\",\n", - " lambda: split_points(gpd.GeoDataFrame(geometry=[shapely.geometry.Point(0, 0)])),\n", - ")\n", - "\n", - "show_error(\n", - " \"split_points non-positive max_points\",\n", - " lambda: split_points(gpd.GeoDataFrame(geometry=[shapely.geometry.Point(0, 0)], crs=\"EPSG:4326\"), max_points=0),\n", - ")\n", - "\n", - "show_error(\n", - " \"split_points tile_grid missing CRS\",\n", - " lambda: split_points(\n", - " gpd.GeoDataFrame(geometry=[shapely.geometry.Point(0, 0)], crs=\"EPSG:4326\"),\n", - " tile_grid=gpd.GeoDataFrame(geometry=[shapely.geometry.box(0, 0, 1, 1)]),\n", - " ),\n", - ")\n", - "\n", - "show_error(\n", - " \"split_area conflicting args\",\n", - " lambda: split_area(\n", - " shapely.geometry.box(0, 0, 1, 1),\n", - " projection=\"EPSG:4326\",\n", - " tile_size=1.0,\n", - " tile_grid=gpd.GeoDataFrame(geometry=[shapely.geometry.box(0, 0, 1, 1)], crs=\"EPSG:4326\"),\n", - " ),\n", - ")\n", - "\n", - "show_error(\n", - " \"split_area missing projection\",\n", - " lambda: split_area({\"west\": 0.0, \"south\": 0.0, \"east\": 1.0, \"north\": 1.0, \"crs\": \"EPSG:4326\"}, tile_size=1.0),\n", - ")\n", - "\n", - "show_error(\n", - " \"split_area invalid AOI type\",\n", - " lambda: split_area(\"not_a_geometry\", projection=\"EPSG:4326\", tile_size=1.0),\n", - ")" - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "openeopythonclient", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.12.13" - } - }, - "nbformat": 4, - "nbformat_minor": 5 -} diff --git a/docs/cookbook/job_manager.rst b/docs/cookbook/job_manager.rst index 1bc9685fa..60e11b7b8 100644 --- a/docs/cookbook/job_manager.rst +++ b/docs/cookbook/job_manager.rst @@ -334,11 +334,6 @@ having to load multiple source products for a single extraction job: tile_grid = gpd.read_parquet("my_tile_grid.parquet") batches = split_points(points, max_points=500, tile_grid=tile_grid) -.. tip:: - - See the `job splitting notebook `_ - for a visual, hands-on walkthrough of ``split_area`` and ``split_points``. - Customizing Job Handling ======================== From eac9dfa9c076061cb1415f7ab00a3512ec38eab2 Mon Sep 17 00:00:00 2001 From: Hans Vanrompay Date: Tue, 10 Nov 2026 10:46:09 +0100 Subject: [PATCH 04/12] rebase --- CHANGELOG.md | 4 + docs/cookbook/job_manager.rst | 42 +++++- openeo/extra/job_management/_job_splitting.py | 131 ++++++++++++------ .../job_management/test_job_splitting.py | 92 +++++++++++- 4 files changed, 218 insertions(+), 51 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 760df2b9b..40fbd9ac8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Added +<<<<<<< HEAD - Richer (HTML based) representation of cube dimension metadata in Jupyter context ([#827](https://github.com/Open-EO/openeo-python-client/issues/827)) ### Changed @@ -22,6 +23,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [0.54.0] - 2026-10-28 - Add public `split_points` helper in `openeo.extra.job_management` for point-based job splitting, including optional tile-grid co-grouping. +======= +- Add public `split_points` helper in `openeo.extra.job_management` for point-based job splitting, including optional co-grouping by tile grid or by `tile_size` + `projection`. +>>>>>>> 3796ed2b (review) ### Changed diff --git a/docs/cookbook/job_manager.rst b/docs/cookbook/job_manager.rst index 60e11b7b8..ee1c751be 100644 --- a/docs/cookbook/job_manager.rst +++ b/docs/cookbook/job_manager.rst @@ -202,6 +202,22 @@ to resume where you left off. Parquet support requires the ``pyarrow`` package (see :ref:`optional dependencies `). +SQLite (experimental) +------ + +A SQLite database file (e.g. ``jobs.sqlite`` or ``jobs.db``) only needs the Python standard library. +It updates individual rows in a transaction instead of rewriting the whole file, +which is more robust against interruptions and faster for large job databases: + +.. code-block:: python + + job_db = create_job_db("jobs.sqlite", df=df) + +.. note:: + + Avoid placing a SQLite job database on a network file system (NFS, SMB, ...), + as file locking is unreliable there. + STAC API (experimental) ----------------------- @@ -308,10 +324,14 @@ Splitting point-based extractions When the work to split is a set of sample locations (points) rather than a single large area, e.g. for point-based extraction jobs, :py:func:`~openeo.extra.job_management._job_splitting.split_points` groups -geometries into batches of at most ``max_points`` rows. +geometries into batches, optionally of at most ``max_points`` rows. Each row is treated as a single feature to group, so a row can be a single -``Point`` or a multi-part geometry (e.g. ``MultiPoint``) representing a +``Point`` or a ``MultiPoint`` representing a cluster of locations that must stay together. +Other geometry types are rejected. +Besides a GeoDataFrame, a list of shapely points or a single ``MultiPoint`` +(one row per point) is accepted. Like with ``split_area``, points without +CRS information are assumed to be in the CRS of the tile grid (if any). By default, groups are formed with a spatial quadtree split so that each batch stays geographically clustered: @@ -334,6 +354,22 @@ having to load multiple source products for a single extraction job: tile_grid = gpd.read_parquet("my_tile_grid.parquet") batches = split_points(points, max_points=500, tile_grid=tile_grid) +Alternatively, like with ``split_area``, a regular grid can be specified +through ``tile_size`` and ``projection``: + +.. code-block:: python + + # group points per 50x50 km tile in EPSG:3857 + batches = split_points(points, max_points=500, tile_size=50_000, projection="EPSG:3857") + +Without ``max_points`` (default ``None``), there is no size limit and +points are simply grouped per tile: + +.. code-block:: python + + # one batch per tile + batches = split_points(points, tile_grid=tile_grid) + Customizing Job Handling ======================== @@ -575,6 +611,8 @@ Job Database .. autoclass:: openeo.extra.job_management.ParquetJobDatabase +.. autoclass:: openeo.extra.job_management.sqlite_job_db.SqliteJobDatabase + .. autofunction:: openeo.extra.job_management.create_job_db .. autofunction:: openeo.extra.job_management.get_job_db diff --git a/openeo/extra/job_management/_job_splitting.py b/openeo/extra/job_management/_job_splitting.py index 598e8bdd6..4d0ffc9ad 100644 --- a/openeo/extra/job_management/_job_splitting.py +++ b/openeo/extra/job_management/_job_splitting.py @@ -6,7 +6,7 @@ import geopandas as gpd import shapely import shapely.geometry.base -from shapely.geometry import MultiPolygon, Polygon +from shapely.geometry import MultiPoint, MultiPolygon, Point, Polygon from openeo.util import BBoxDict, normalize_crs @@ -379,7 +379,7 @@ def split_area( def _quadtree_split_indices( centroids: gpd.GeoSeries, idx, - max_points: int, + max_points: Optional[int], out: List, ) -> None: """ @@ -387,7 +387,7 @@ def _quadtree_split_indices( resulting group of index labels holds at most *max_points* entries. Appends the index-label groups (not the geometries themselves) to *out*. """ - if len(idx) <= max_points: + if max_points is None or len(idx) <= max_points: out.append(idx) return @@ -413,15 +413,16 @@ def _quadtree_split_indices( def split_points( - points: gpd.GeoDataFrame, + points: Union[gpd.GeoDataFrame, List[Union[Point, MultiPoint]], MultiPoint], *, - max_points: int = 500, - tile_grid: Optional[gpd.GeoDataFrame] = None, + max_points: Optional[int] = None, + projection: Optional[str] = None, + tile_size: Optional[float] = None, + tile_grid: Optional[Union[_TileGridInterface, gpd.GeoDataFrame]] = None, ) -> List[gpd.GeoDataFrame]: """ - Split a GeoDataFrame of geometries (typically points, e.g. sample - locations for point-based extraction) into groups of at most - *max_points* each. + Split point geometries (e.g. sample locations for point-based extraction) + into groups of at most *max_points* each. Each row is treated as a single "feature" to group, regardless of whether its geometry is a single ``Point`` or a multi-part geometry (e.g. @@ -442,38 +443,77 @@ def split_points( are first grouped by the tile their centroid falls in, so that a group never spans multiple tiles. This avoids batch jobs having to load multiple (possibly non-overlapping) source products for a single query. + Alternatively, *tile_size* and *projection* can be given to group + geometries in a regular grid of square tiles (anchored at the south-west + corner of the points' extent), analogous to :func:`split_area`. + Tiles that end up with more than *max_points* geometries are further split with the quadtree approach described above. Geometries that don't fall inside any tile are grouped (and split) separately. - :param points: GeoDataFrame with a ``geometry`` column (any geometry type; - centroids are used for grouping) and a CRS set. + :param points: a GeoDataFrame with a ``geometry`` column of ``Point`` + and/or ``MultiPoint`` geometries, a list of such shapely geometries, + or a single :class:`~shapely.geometry.MultiPoint` + (which is exploded into one row per point). :param max_points: maximum number of geometries per group. - :param tile_grid: optional GeoDataFrame of tile polygons (with CRS set) - to co-join geometries by before splitting, e.g. a satellite tiling - grid. When given, geometries are only grouped together if their - centroids fall in the same tile. + ``None`` (default) means no limit: geometries are then only grouped + per tile (or all in a single group when no tile grid is given). + :param projection: EPSG string (e.g. ``"EPSG:3857"``) of the regular grid + to group by. Must be combined with *tile_size*. + :param tile_size: tile edge length in the unit of *projection*. + Must be combined with *projection*. + :param tile_grid: optional :class:`_TileGridInterface` instance or + GeoDataFrame of tile polygons (with CRS set) to co-join geometries by + before splitting, e.g. a satellite tiling grid. When given, geometries + are only grouped together if their centroids fall in the same tile. + Mutually exclusive with *projection* / *tile_size*. :return: list of GeoDataFrames, each with at most *max_points* rows. - :raises JobSplittingFailure: if the GeoDataFrame has no geometry column - or CRS, or if *max_points* is not positive. + :raises JobSplittingFailure: on invalid input (e.g. non-point geometries, + non-positive *max_points*, contradictory grid arguments). + + Like with :func:`split_area`, points without CRS information + (e.g. a list of shapely geometries) are assumed to be in the tile grid's CRS. + Without tile grid, no CRS is needed and the returned groups have no CRS either. """ + if isinstance(points, MultiPoint): + points = list(points.geoms) + if isinstance(points, list): + if not all(isinstance(p, (Point, MultiPoint)) for p in points): + raise JobSplittingFailure("All geometries in 'points' must be Point or MultiPoint instances.") + points = gpd.GeoDataFrame(geometry=points) + elif not isinstance(points, gpd.GeoDataFrame): + raise JobSplittingFailure( + f"Expected a GeoDataFrame, list of points, or MultiPoint, got {type(points).__name__}." + ) + if "geometry" not in points.columns: raise JobSplittingFailure("The GeoDataFrame must contain a 'geometry' column.") - if points.crs is None: - raise JobSplittingFailure("The GeoDataFrame must have a CRS set.") - if max_points <= 0: + if not points.geom_type.isin(["Point", "MultiPoint"]).all(): + raise JobSplittingFailure("All geometries must be of type Point or MultiPoint.") + if max_points is not None and max_points <= 0: raise JobSplittingFailure(f"'max_points' must be positive, got {max_points!r}.") - if tile_grid is not None and tile_grid.crs is None: - raise JobSplittingFailure("The 'tile_grid' GeoDataFrame must have a CRS set.") - - points_epsg = points.crs.to_epsg() - if points_epsg is not None: - _TileGridInterface._check_antimeridian_crossing(points.total_bounds, points_epsg) if tile_grid is not None: - tile_epsg = tile_grid.crs.to_epsg() - if tile_epsg is not None: - _TileGridInterface._check_antimeridian_crossing(tile_grid.total_bounds, tile_epsg) + if projection is not None or tile_size is not None: + raise JobSplittingFailure( + "Cannot combine 'tile_grid' with 'projection' or 'tile_size'. " + "Either pass a tile_grid, or pass projection + tile_size." + ) + if isinstance(tile_grid, gpd.GeoDataFrame): + tile_grid = _PredefinedTileGrid(tiles=tile_grid) + elif projection is not None or tile_size is not None: + if projection is None or tile_size is None: + raise JobSplittingFailure("'projection' and 'tile_size' must be provided together.") + tile_grid = _SizeBasedTileGrid.from_size_projection(size=tile_size, projection=projection) + + if points.crs is None and tile_grid is not None: + _log.warning( + "Points have no CRS information; assuming they are already in the tile grid's CRS (EPSG:%d).", + tile_grid.crs, + ) + points = points.set_crs(f"EPSG:{tile_grid.crs}") + if points.crs is not None and points.crs.to_epsg() is not None: + _TileGridInterface._check_antimeridian_crossing(points.total_bounds, points.crs.to_epsg()) if points.empty: return [] @@ -484,20 +524,25 @@ def split_points( if tile_grid is None: _quadtree_split_indices(centroids, points.index, max_points, index_groups) else: - centroids_gdf = gpd.GeoDataFrame(geometry=centroids, crs=points.crs) - if centroids_gdf.crs != tile_grid.crs: - centroids_gdf = centroids_gdf.to_crs(tile_grid.crs) - joined = gpd.sjoin( - centroids_gdf, tile_grid[["geometry"]].reset_index(drop=True), how="left", predicate="intersects" - ) - # A centroid on a shared tile edge can match multiple tiles: keep just the first match. - joined = joined.loc[~joined.index.duplicated(keep="first")] - - # Group using an explicit key to avoid pandas categorical/NaN edge cases - # when unmatched centroids (index_right = NaN) are present. - joined["_tile_group_key"] = joined["index_right"].where(joined["index_right"].notna(), "__unmatched__") - - for _, idx in joined.groupby("_tile_group_key").groups.items(): + grid_centroids = centroids.to_crs(epsg=tile_grid.crs) + if isinstance(tile_grid, _SizeBasedTileGrid): + # Regular grid anchored at the south-west corner of the points' extent. + minx, miny, _, _ = grid_centroids.total_bounds + keys = [(grid_centroids.x - minx) // tile_grid.size, (grid_centroids.y - miny) // tile_grid.size] + elif isinstance(tile_grid, _PredefinedTileGrid): + joined = gpd.sjoin( + gpd.GeoDataFrame(geometry=grid_centroids), + tile_grid._gdf[["geometry"]].reset_index(drop=True), + how="left", + predicate="intersects", + ) + # A centroid on a shared tile edge can match multiple tiles: keep just the first match. + joined = joined.loc[~joined.index.duplicated(keep="first")] + # Unmatched centroids (index_right = NaN) get their own group. + keys = joined["index_right"].fillna(-1) + else: + raise JobSplittingFailure(f"Unsupported tile grid type: {type(tile_grid).__name__}.") + for idx in points.index.to_series().groupby(keys).groups.values(): _quadtree_split_indices(centroids, idx, max_points, index_groups) return [points.loc[idx].reset_index(drop=True) for idx in index_groups] diff --git a/tests/extra/job_management/test_job_splitting.py b/tests/extra/job_management/test_job_splitting.py index 30ae2af90..3ecebec8e 100644 --- a/tests/extra/job_management/test_job_splitting.py +++ b/tests/extra/job_management/test_job_splitting.py @@ -418,10 +418,16 @@ def test_groups_are_spatially_clustered(self): xs = [geom.x for geom in group.geometry] assert max(xs) - min(xs) < 50 - def test_default_max_points(self): - gdf = self._grid_points(3) + def test_default_max_points_is_unlimited(self): + gdf = self._grid_points(30) # 900 points result = split_points(gdf) assert len(result) == 1 + assert len(result[0]) == 900 + + def test_default_max_points_groups_per_tile(self): + gdf = self._grid_points(30) + result = split_points(gdf, tile_size=15, projection="EPSG:4326") + assert sorted(len(g) for g in result) == [225, 225, 225, 225] def test_empty_geodataframe(self): gdf = gpd.GeoDataFrame(geometry=[], crs="EPSG:4326") @@ -439,10 +445,11 @@ def test_missing_geometry_column_raises(self): with pytest.raises(JobSplittingFailure, match="geometry"): split_points(gdf) - def test_missing_crs_raises(self): + def test_missing_crs_without_grid(self): gdf = gpd.GeoDataFrame(geometry=[shapely.geometry.Point(0, 0)]) - with pytest.raises(JobSplittingFailure, match="CRS"): - split_points(gdf) + result = split_points(gdf) + assert len(result) == 1 + assert result[0].crs is None def test_non_positive_max_points_raises(self): gdf = self._grid_points(2) @@ -520,9 +527,82 @@ def test_tile_grid_reprojects_when_crs_differs(self): def test_tile_grid_missing_crs_raises(self): tile_grid = gpd.GeoDataFrame(geometry=[shapely.geometry.box(0, 0, 1, 1)]) gdf = self._grid_points(2) - with pytest.raises(JobSplittingFailure, match="tile_grid.*CRS"): + with pytest.raises(JobSplittingFailure, match="CRS"): split_points(gdf, tile_grid=tile_grid) + def test_list_of_points(self): + points = [shapely.geometry.Point(x, 0) for x in range(5)] + result = split_points(points, max_points=10) + assert len(result) == 1 + assert len(result[0]) == 5 + + def test_multipoint_input_is_exploded(self): + multipoint = shapely.geometry.MultiPoint([(x, 0) for x in range(5)]) + result = split_points(multipoint, max_points=2) + assert sum(len(g) for g in result) == 5 + assert all(len(g) <= 2 for g in result) + + def test_points_without_crs_assume_grid_crs(self, caplog): + points = [shapely.geometry.Point(100_000 * x, 0) for x in range(3)] + result = split_points(points, max_points=10, tile_size=150_000, projection="EPSG:3857") + assert [len(g) for g in result] == [2, 1] + assert all(g.crs.to_epsg() == 3857 for g in result) + assert "assuming they are already in the tile grid's CRS (EPSG:3857)" in caplog.text + + def test_tile_grid_interface_instance(self): + gdf = gpd.GeoDataFrame( + geometry=[shapely.geometry.Point(0.5, 0.5), shapely.geometry.Point(1.5, 0.5)], crs="EPSG:4326" + ) + tile_grid = _PredefinedTileGrid( + tiles=[shapely.geometry.box(0, 0, 1, 1), shapely.geometry.box(1, 0, 2, 1)], crs=4326 + ) + assert len(split_points(gdf, max_points=10, tile_grid=tile_grid)) == 2 + tile_grid = _SizeBasedTileGrid(epsg=4326, size=1.0) + assert len(split_points(gdf, max_points=10, tile_grid=tile_grid)) == 2 + + def test_list_with_non_points_raises(self): + with pytest.raises(JobSplittingFailure, match="Point or MultiPoint"): + split_points([shapely.geometry.box(0, 0, 1, 1)]) + + def test_non_point_geometries_raise(self): + gdf = gpd.GeoDataFrame(geometry=[shapely.geometry.box(0, 0, 1, 1)], crs="EPSG:4326") + with pytest.raises(JobSplittingFailure, match="Point or MultiPoint"): + split_points(gdf) + + def test_tile_size_projection_groups_by_regular_grid(self): + points = [shapely.geometry.Point(0.1 * i, 0.5) for i in range(5)] + points += [shapely.geometry.Point(1.1 + 0.1 * i, 0.5) for i in range(5)] + gdf = gpd.GeoDataFrame(geometry=points, crs="EPSG:4326") + result = split_points(gdf, max_points=100, tile_size=1.0, projection="EPSG:4326") + assert len(result) == 2 + for group in result: + xs = [geom.x for geom in group.geometry] + assert (max(xs) < 1) or (min(xs) >= 1) + + def test_tile_size_projection_reprojects(self): + # ~0.1 degree apart near the equator is ~11 km in EPSG:3857: 50 km tiles keep them together + gdf = gpd.GeoDataFrame( + geometry=[shapely.geometry.Point(0.0, 0.0), shapely.geometry.Point(0.1, 0.0)], crs="EPSG:4326" + ) + result = split_points(gdf, max_points=10, tile_size=50_000, projection="EPSG:3857") + assert len(result) == 1 + result = split_points(gdf, max_points=10, tile_size=5_000, projection="EPSG:3857") + assert len(result) == 2 + + @pytest.mark.parametrize( + "kwargs", + [ + {"tile_size": 1.0}, + {"projection": "EPSG:4326"}, + {"tile_size": 1.0, "projection": "EPSG:4326", "tile_grid": "grid"}, + ], + ) + def test_invalid_grid_argument_combinations_raise(self, kwargs): + if kwargs.get("tile_grid") == "grid": + kwargs["tile_grid"] = gpd.GeoDataFrame(geometry=[shapely.geometry.box(0, 0, 1, 1)], crs="EPSG:4326") + with pytest.raises(JobSplittingFailure): + split_points(self._grid_points(2), **kwargs) + def test_antimeridian_crossing_points_raises(self): """Geographic points with longitude outside [-180, 180] are rejected to avoid antimeridian issues.""" gdf = gpd.GeoDataFrame(geometry=[shapely.geometry.Point(190.0, 0.0)], crs="EPSG:4326") From 4678eb90c15c3231ed5aeef87c0f3a8e35c81580 Mon Sep 17 00:00:00 2001 From: Hans Vanrompay Date: Thu, 29 Oct 2026 10:24:04 +0100 Subject: [PATCH 05/12] exploring adding polygons to scope --- docs/cookbook/job_manager.rst | 70 ++-- openeo/extra/job_management/__init__.py | 4 +- openeo/extra/job_management/_job_splitting.py | 366 +++++++++++------- .../job_management/test_job_splitting.py | 280 +++++++++++--- 4 files changed, 487 insertions(+), 233 deletions(-) diff --git a/docs/cookbook/job_manager.rst b/docs/cookbook/job_manager.rst index ee1c751be..433b892d6 100644 --- a/docs/cookbook/job_manager.rst +++ b/docs/cookbook/job_manager.rst @@ -263,7 +263,7 @@ Example: splitting a bounding box into 10x10 km tiles in EPSG:3857: # Split into 10x10 km tiles in EPSG:3857 gdf = split_area( - aoi=extent, + spatial_extent=extent, tile_size=10_000, # tile size in meters (units of the projection) projection="EPSG:3857", # projection for tiling ) @@ -316,59 +316,69 @@ or any other custom partitioning, you can pass a pre-computed tile_grid = gpd.read_parquet("my_tile_grid.parquet") # Only tiles intersecting the AOI are returned - gdf = split_area(aoi=extent , tile_grid=tile_grid) - -Splitting point-based extractions ----------------------------------- - -When the work to split is a set of sample locations (points) rather than a -single large area, e.g. for point-based extraction jobs, -:py:func:`~openeo.extra.job_management._job_splitting.split_points` groups -geometries into batches, optionally of at most ``max_points`` rows. -Each row is treated as a single feature to group, so a row can be a single -``Point`` or a ``MultiPoint`` representing a -cluster of locations that must stay together. -Other geometry types are rejected. -Besides a GeoDataFrame, a list of shapely points or a single ``MultiPoint`` -(one row per point) is accepted. Like with ``split_area``, points without + gdf = split_area(spatial_extent=extent, tile_grid=tile_grid) + +Grouping geometries (e.g. point-based extractions) +-------------------------------------------------- + +When the work to split is a set of geometries (e.g. sample locations) +rather than a single large area, e.g. for point-based extraction jobs, +:py:func:`~openeo.extra.job_management._job_splitting.group_geometries` groups +the rows of a GeoDataFrame into batches, optionally of at most ``max_geometries`` rows. +Each row is treated as a single feature to group, based on its representative point, +regardless of geometry type (e.g. ``Point``, ``MultiPoint``, ``Polygon``). +Null or empty geometries are not allowed. +Geometries are not split themselves: use ``split_area`` for a large area of interest. +Like with ``split_area``, geometries without CRS information are assumed to be in the CRS of the tile grid (if any). -By default, groups are formed with a spatial quadtree split so that each -batch stays geographically clustered: +Groups are formed by recursive bisection at the (weighted) median along the longer axis, +so that batches stay geographically clustered and balanced. +The returned GeoDataFrames keep the original index, to link back to the input rows: .. code-block:: python import geopandas as gpd - from openeo.extra.job_management import split_points + from openeo.extra.job_management import group_geometries points = gpd.read_file("my_sample_locations.gpkg") - batches = split_points(points, max_points=500) + batches = group_geometries(points, max_geometries=500) # batches is a list of GeoDataFrames, each with at most 500 rows +Batches can also be limited by a ``weight`` (a column name, a pandas Series, +or ``"area"`` for the geometry area in m², at least 100 m² per feature) +with ``max_weight``, and by the bounding box edge length of a batch with ``max_extent`` +(in units of the CRS of the geometries, or of the tile grid if any): + +.. code-block:: python + + batches = group_geometries(polygons, weight="area", max_weight=1e9, max_extent=50_000) + Optionally, a ``tile_grid`` (e.g. a satellite tiling grid) can be passed so -that geometries are first grouped by the tile their centroid falls in. This -guarantees a batch never spans multiple tiles, which avoids batch jobs -having to load multiple source products for a single extraction job: +that each feature is first assigned to a single tile (the one with the largest overlap) +and batches are formed per tile. Polygons crossing tile edges may still touch +neighbouring tiles, but points are guaranteed to stay within one tile: .. code-block:: python tile_grid = gpd.read_parquet("my_tile_grid.parquet") - batches = split_points(points, max_points=500, tile_grid=tile_grid) + batches = group_geometries(points, max_geometries=500, tile_grid=tile_grid) -Alternatively, like with ``split_area``, a regular grid can be specified -through ``tile_size`` and ``projection``: +Alternatively, a regular grid can be specified through ``tile_size`` and ``projection``. +This grid is anchored at the origin of the projection, +so the grouping of existing features does not change when other features are added: .. code-block:: python # group points per 50x50 km tile in EPSG:3857 - batches = split_points(points, max_points=500, tile_size=50_000, projection="EPSG:3857") + batches = group_geometries(points, max_geometries=500, tile_size=50_000, projection="EPSG:3857") -Without ``max_points`` (default ``None``), there is no size limit and -points are simply grouped per tile: +Without ``max_geometries`` (default ``None``), there is no size limit and +geometries are simply grouped per tile: .. code-block:: python # one batch per tile - batches = split_points(points, tile_grid=tile_grid) + batches = group_geometries(points, tile_grid=tile_grid) Customizing Job Handling ======================== diff --git a/openeo/extra/job_management/__init__.py b/openeo/extra/job_management/__init__.py index 2020b28a4..6e2ac44e8 100644 --- a/openeo/extra/job_management/__init__.py +++ b/openeo/extra/job_management/__init__.py @@ -6,7 +6,7 @@ create_job_db, get_job_db, ) -from openeo.extra.job_management._job_splitting import split_area, split_points +from openeo.extra.job_management._job_splitting import group_geometries, split_area from openeo.extra.job_management._manager import MultiBackendJobManager from openeo.extra.job_management.process_based import ProcessBasedJobCreator @@ -19,6 +19,6 @@ "create_job_db", "get_job_db", "split_area", - "split_points", + "group_geometries", "MultiBackendJobManager", ] diff --git a/openeo/extra/job_management/_job_splitting.py b/openeo/extra/job_management/_job_splitting.py index 4d0ffc9ad..9a389431b 100644 --- a/openeo/extra/job_management/_job_splitting.py +++ b/openeo/extra/job_management/_job_splitting.py @@ -4,14 +4,21 @@ from typing import Dict, List, Optional, Tuple, Union import geopandas as gpd +import numpy as np +import pandas as pd import shapely import shapely.geometry.base -from shapely.geometry import MultiPoint, MultiPolygon, Point, Polygon +from shapely.geometry import MultiPolygon, Polygon from openeo.util import BBoxDict, normalize_crs _log = logging.getLogger(__name__) +# Minimum area weight (m²) of a feature when weighting by "area": roughly one 10 m Sentinel-2 pixel, +# so that points and lines are not "free". +_MIN_AREA_WEIGHT = 100.0 + + class JobSplittingFailure(Exception): pass @@ -72,25 +79,6 @@ def _parse_input_geometry( "Invalid bounding box: west must not equal east (zero-width bounding box)." ) - # if bbox["west"] > bbox["east"]: - # # In a geographic CRS this indicates an antimeridian crossing; - # # split into two polygons on each side of the ±180° meridian. - # if source_epsg is not None and _is_geographic_crs(source_epsg): - # _log.info( - # "Bounding box crosses the antimeridian (west=%s > east=%s); " - # "splitting into two polygons.", - # bbox["west"], - # bbox["east"], - # ) - # west_part = shapely.geometry.box(bbox["west"], bbox["south"], 180.0, bbox["north"]) - # east_part = shapely.geometry.box(-180.0, bbox["south"], bbox["east"], bbox["north"]) - # return MultiPolygon([west_part, east_part]), source_epsg - # raise JobSplittingFailure( - # "Invalid bounding box: west must be less than east. " - # "Antimeridian-crossing bounding boxes are only supported " - # "for geographic CRSs (e.g. EPSG:4326) with an explicit 'crs' field." - # ) - return bbox.as_geometry(), source_epsg elif isinstance(geometry, (Polygon, MultiPolygon)): return geometry, None @@ -323,7 +311,7 @@ def get_tiles(self, geometry: Union[Dict, Polygon, MultiPolygon]) -> gpd.GeoData def split_area( - aoi: Union[Dict, MultiPolygon, Polygon], + spatial_extent: Union[Dict, MultiPolygon, Polygon], *, projection: Optional[str] = None, tile_size: Optional[float] = None, @@ -341,7 +329,7 @@ def split_area( (e.g. :class:`_PredefinedTileGrid`) or a :class:`~geopandas.GeoDataFrame` as *tile_grid*. - :param aoi: area of interest as a bounding-box dict + :param spatial_extent: area of interest as a bounding-box dict (keys ``west``, ``south``, ``east``, ``north``, optionally ``crs``), a :class:`~shapely.geometry.Polygon`, or a :class:`~shapely.geometry.MultiPolygon`. @@ -363,7 +351,7 @@ def split_area( ) if isinstance(tile_grid, gpd.GeoDataFrame): tile_grid = _PredefinedTileGrid(tiles=tile_grid) - return tile_grid.get_tiles(aoi) + return tile_grid.get_tiles(spatial_extent) # --- Size-based splitting path --- if tile_size is None: @@ -373,125 +361,197 @@ def split_area( raise JobSplittingFailure("'projection' is required when using size-based tiling.") grid = _SizeBasedTileGrid(epsg=normalize_crs(projection), size=tile_size) - return grid.get_tiles(aoi) - - -def _quadtree_split_indices( - centroids: gpd.GeoSeries, - idx, - max_points: Optional[int], - out: List, -) -> None: + return grid.get_tiles(spatial_extent) + + +def _resolve_weights( + geometries: gpd.GeoDataFrame, + weight: Union[None, str, pd.Series], + max_weight: Optional[float], +) -> np.ndarray: + """Resolve the *weight* argument of :func:`group_geometries` to a validated float array (one value per row).""" + if weight is None: + return np.ones(len(geometries)) + + if isinstance(weight, str): + if weight in geometries.columns: + values = geometries[weight] + elif weight == "area": + if geometries.crs is None: + raise JobSplittingFailure("weight='area' requires geometries with a CRS.") + values = np.maximum(geometries.geometry.to_crs("EPSG:6933").area.to_numpy(), _MIN_AREA_WEIGHT) + else: + raise JobSplittingFailure(f"Weight column {weight!r} not found in the GeoDataFrame.") + elif isinstance(weight, pd.Series): + if not weight.index.equals(geometries.index): + if not (weight.index.is_unique and geometries.index.is_unique): + raise JobSplittingFailure("'weight' Series must be aligned with the index of 'geometries'.") + weight = weight.reindex(geometries.index) + values = weight + else: + raise JobSplittingFailure(f"'weight' must be None, a column name or a pandas Series, got {weight!r}.") + + try: + values = np.asarray(values, dtype=float) + except (TypeError, ValueError) as e: + raise JobSplittingFailure("Weights must be numeric.") from e + if not np.isfinite(values).all() or (values < 0).any(): + raise JobSplittingFailure("Weights must be finite and non-negative.") + return values + + +def _predefined_tile_keys(tile_grid: "_PredefinedTileGrid", geoms: np.ndarray) -> np.ndarray: + """For each geometry (in the grid CRS), the position of the intersecting tile with largest overlap (-1: none).""" + tiles = tile_grid._gdf.geometry.to_numpy() + tile_idx, geom_idx = shapely.STRtree(tiles).query(geoms, predicate="intersects")[::-1] + areas = shapely.area(shapely.intersection(geoms[geom_idx], tiles[tile_idx])) + # Sort per geometry by decreasing overlap area, then tile position; take the first of each geometry. + order = np.lexsort((tile_idx, -areas, geom_idx)) + geom_idx, tile_idx = geom_idx[order], tile_idx[order] + first = np.ones(len(geom_idx), dtype=bool) + first[1:] = geom_idx[1:] != geom_idx[:-1] + keys = np.full(len(geoms), -1) + keys[geom_idx[first]] = tile_idx[first] + return keys + + +def _bisect_group( + positions: np.ndarray, + *, + xs: np.ndarray, + ys: np.ndarray, + weights: np.ndarray, + bounds: np.ndarray, + max_geometries: Optional[int], + max_weight: Optional[float], + max_extent: Optional[float], +) -> List[np.ndarray]: """ - Recursively quarter the bounding box of ``centroids.loc[idx]`` until each - resulting group of index labels holds at most *max_points* entries. - Appends the index-label groups (not the geometries themselves) to *out*. + Recursively bisect a group of rows (given as positions) at the weighted median of their locations, + along the longer axis of the locations' spread, until limits are satisfied. """ - if max_points is None or len(idx) <= max_points: - out.append(idx) - return - - sub_centroids = centroids.loc[idx] - minx, miny, maxx, maxy = sub_centroids.total_bounds - midx, midy = (minx + maxx) / 2, (miny + maxy) / 2 - quadrants = [ - idx[(sub_centroids.x <= midx) & (sub_centroids.y <= midy)], - idx[(sub_centroids.x <= midx) & (sub_centroids.y > midy)], - idx[(sub_centroids.x > midx) & (sub_centroids.y <= midy)], - idx[(sub_centroids.x > midx) & (sub_centroids.y > midy)], - ] - if any(len(q) == len(idx) for q in quadrants): - # Points coincide (or all fall in one quadrant): quartering the - # bounding box doesn't help, so fall back to plain chunking. - for i in range(0, len(idx), max_points): - out.append(idx[i : i + max_points]) - return - - for quadrant in quadrants: - if len(quadrant) > 0: - _quadtree_split_indices(centroids, quadrant, max_points, out) - - -def split_points( - points: Union[gpd.GeoDataFrame, List[Union[Point, MultiPoint]], MultiPoint], + result = [] + stack = [positions] + while stack: + pos = stack.pop() + if len(pos) == 1: + if max_weight is not None and weights[pos[0]] > max_weight: + _log.warning( + "Feature at position %d has weight %s exceeding 'max_weight' %s: putting it in its own group.", + pos[0], + weights[pos[0]], + max_weight, + ) + result.append(pos) + continue + fits = ( + (max_geometries is None or len(pos) <= max_geometries) + and (max_weight is None or weights[pos].sum() <= max_weight) + and ( + max_extent is None + or max( + bounds[pos, 2].max() - bounds[pos, 0].min(), + bounds[pos, 3].max() - bounds[pos, 1].min(), + ) + <= max_extent + ) + ) + if fits: + result.append(pos) + continue + px, py = xs[pos], ys[pos] + axis = px if np.ptp(px) >= np.ptp(py) else py + pos = pos[np.argsort(axis, kind="stable")] + cumulative = np.cumsum(weights[pos]) + cut = int(np.clip(np.searchsorted(cumulative, cumulative[-1] / 2, side="right"), 1, len(pos) - 1)) + stack.append(pos[cut:]) + stack.append(pos[:cut]) + return result + + +def group_geometries( + geometries: gpd.GeoDataFrame, *, - max_points: Optional[int] = None, + max_geometries: Optional[int] = None, + weight: Union[None, str, pd.Series] = None, + max_weight: Optional[float] = None, + max_extent: Optional[float] = None, projection: Optional[str] = None, tile_size: Optional[float] = None, tile_grid: Optional[Union[_TileGridInterface, gpd.GeoDataFrame]] = None, ) -> List[gpd.GeoDataFrame]: """ - Split point geometries (e.g. sample locations for point-based extraction) - into groups of at most *max_points* each. + Group geometries (e.g. sample locations for point-based extraction) + into spatially clustered groups of at most *max_geometries* each. - Each row is treated as a single "feature" to group, regardless of whether - its geometry is a single ``Point`` or a multi-part geometry (e.g. - ``MultiPoint``) representing a cluster of locations that must stay - together: *max_points* limits the number of rows (features) per group, + Each row is treated as a single "feature" to group, regardless of its + geometry type (e.g. ``Point``, ``MultiPoint``, ``Polygon``): + *max_geometries* limits the number of rows (features) per group, not the number of individual coordinates. To limit individual coordinates instead, explode multi-part geometries into separate rows - first, e.g. with ``points.explode(index_parts=False)``. - - By default, grouping is done with a recursive quadtree split on the - geometries' centroids: the bounding box of a (sub)set is quartered and - each quarter is split again until it holds at most *max_points* - geometries. This keeps each resulting group spatially clustered, which is - useful for batch jobs doing point extractions, as opposed to a naive - split on row order. - - If *tile_grid* is given (e.g. a Sentinel-2 MGRS/UTM tile grid), geometries - are first grouped by the tile their centroid falls in, so that a group - never spans multiple tiles. This avoids batch jobs having to load - multiple (possibly non-overlapping) source products for a single query. + first, e.g. with ``geometries.explode(index_parts=False)``. + + Features are located by their ``representative_point()`` (computed in the input CRS), + they are not split themselves. For a single large area of interest that has to be split, + use :func:`split_area` instead. + + Grouping is done by recursive bisection (k-d tree style): a group is cut at the + weighted median of the feature locations along the longer axis of their spread, + until it satisfies all given limits (*max_geometries*, *max_weight*, *max_extent*) + or consists of a single feature. This keeps groups spatially clustered and balanced. + + If *tile_grid* is given (e.g. a Sentinel-2 MGRS/UTM tile grid), each feature is first + assigned to a single tile (the one with the largest overlap), and every tile is bisected separately. + Note that polygons crossing tile edges may still touch neighbouring tiles. + Features not intersecting any tile are grouped (and bisected) separately. Alternatively, *tile_size* and *projection* can be given to group - geometries in a regular grid of square tiles (anchored at the south-west - corner of the points' extent), analogous to :func:`split_area`. - - Tiles that end up with more than *max_points* geometries are further - split with the quadtree approach described above. Geometries that don't - fall inside any tile are grouped (and split) separately. - - :param points: a GeoDataFrame with a ``geometry`` column of ``Point`` - and/or ``MultiPoint`` geometries, a list of such shapely geometries, - or a single :class:`~shapely.geometry.MultiPoint` - (which is exploded into one row per point). - :param max_points: maximum number of geometries per group. - ``None`` (default) means no limit: geometries are then only grouped - per tile (or all in a single group when no tile grid is given). + features in a regular grid of square tiles anchored at the origin of the projection + (so grouping does not change when other features are added). + + :param geometries: GeoDataFrame with a ``geometry`` column. + Null or empty geometries are not allowed. + :param max_geometries: maximum number of geometries per group. + ``None`` (default) means no limit. + :param weight: weight of each feature, to be limited with *max_weight*: + ``None`` (every feature has weight 1), the name of a numeric column, + a pandas Series aligned with the index of *geometries*, + or ``"area"`` (if there is no column with that name): + geometry area in m² (computed in EPSG:6933, requires a CRS), + with a minimum of 100 m² (about one 10 m Sentinel-2 pixel) per feature. + :param max_weight: maximum total weight per group (requires *weight*). + A single feature heavier than this ends up alone in its own group (with a warning). + :param max_extent: maximum edge length (largest of width and height) + of the bounding box of the geometries in a group, in the units of the + working CRS (the tile grid's CRS if there is one, else the CRS of *geometries*). :param projection: EPSG string (e.g. ``"EPSG:3857"``) of the regular grid to group by. Must be combined with *tile_size*. :param tile_size: tile edge length in the unit of *projection*. Must be combined with *projection*. :param tile_grid: optional :class:`_TileGridInterface` instance or GeoDataFrame of tile polygons (with CRS set) to co-join geometries by - before splitting, e.g. a satellite tiling grid. When given, geometries - are only grouped together if their centroids fall in the same tile. + before splitting, e.g. a satellite tiling grid. Mutually exclusive with *projection* / *tile_size*. - :return: list of GeoDataFrames, each with at most *max_points* rows. - :raises JobSplittingFailure: on invalid input (e.g. non-point geometries, - non-positive *max_points*, contradictory grid arguments). + :return: list of GeoDataFrames, with the original index of *geometries* preserved. + :raises JobSplittingFailure: on invalid input (e.g. non-positive + *max_geometries*, null/empty geometries, contradictory grid arguments). - Like with :func:`split_area`, points without CRS information - (e.g. a list of shapely geometries) are assumed to be in the tile grid's CRS. + Like with :func:`split_area`, geometries without CRS information + are assumed to be in the tile grid's CRS. Without tile grid, no CRS is needed and the returned groups have no CRS either. """ - if isinstance(points, MultiPoint): - points = list(points.geoms) - if isinstance(points, list): - if not all(isinstance(p, (Point, MultiPoint)) for p in points): - raise JobSplittingFailure("All geometries in 'points' must be Point or MultiPoint instances.") - points = gpd.GeoDataFrame(geometry=points) - elif not isinstance(points, gpd.GeoDataFrame): - raise JobSplittingFailure( - f"Expected a GeoDataFrame, list of points, or MultiPoint, got {type(points).__name__}." - ) - - if "geometry" not in points.columns: + if not isinstance(geometries, gpd.GeoDataFrame): + raise JobSplittingFailure(f"Expected a GeoDataFrame, got {type(geometries).__name__}.") + if "geometry" not in geometries.columns: raise JobSplittingFailure("The GeoDataFrame must contain a 'geometry' column.") - if not points.geom_type.isin(["Point", "MultiPoint"]).all(): - raise JobSplittingFailure("All geometries must be of type Point or MultiPoint.") - if max_points is not None and max_points <= 0: - raise JobSplittingFailure(f"'max_points' must be positive, got {max_points!r}.") + if max_geometries is not None and max_geometries <= 0: + raise JobSplittingFailure(f"'max_geometries' must be positive, got {max_geometries!r}.") + if max_weight is not None: + if weight is None: + raise JobSplittingFailure("'max_weight' requires 'weight'.") + if max_weight <= 0: + raise JobSplittingFailure(f"'max_weight' must be positive, got {max_weight!r}.") + if max_extent is not None and max_extent <= 0: + raise JobSplittingFailure(f"'max_extent' must be positive, got {max_extent!r}.") if tile_grid is not None: if projection is not None or tile_size is not None: @@ -506,43 +566,57 @@ def split_points( raise JobSplittingFailure("'projection' and 'tile_size' must be provided together.") tile_grid = _SizeBasedTileGrid.from_size_projection(size=tile_size, projection=projection) - if points.crs is None and tile_grid is not None: + n_invalid = int((geometries.geometry.isna() | geometries.geometry.is_empty).sum()) + if n_invalid: + raise JobSplittingFailure(f"Found {n_invalid} null or empty geometries, which cannot be grouped.") + + if geometries.crs is None and tile_grid is not None: _log.warning( - "Points have no CRS information; assuming they are already in the tile grid's CRS (EPSG:%d).", + "Geometries have no CRS information; assuming they are already in the tile grid's CRS (EPSG:%d).", tile_grid.crs, ) - points = points.set_crs(f"EPSG:{tile_grid.crs}") - if points.crs is not None and points.crs.to_epsg() is not None: - _TileGridInterface._check_antimeridian_crossing(points.total_bounds, points.crs.to_epsg()) + geometries = geometries.set_crs(f"EPSG:{tile_grid.crs}") + if geometries.crs is not None and geometries.crs.to_epsg() is not None: + _TileGridInterface._check_antimeridian_crossing(geometries.total_bounds, geometries.crs.to_epsg()) - if points.empty: + if geometries.empty: return [] - centroids = points.geometry.centroid - index_groups: List = [] + weights = _resolve_weights(geometries, weight, max_weight) + + # Work with positions (not index labels), so duplicated index labels are no problem. + locations = geometries.geometry.representative_point() + working = geometries.geometry + if tile_grid is not None: + locations = locations.to_crs(epsg=tile_grid.crs) + working = working.to_crs(epsg=tile_grid.crs) + xs, ys = locations.x.to_numpy(), locations.y.to_numpy() + bounds = working.bounds.to_numpy() + all_positions = np.arange(len(geometries)) if tile_grid is None: - _quadtree_split_indices(centroids, points.index, max_points, index_groups) + position_groups = [all_positions] else: - grid_centroids = centroids.to_crs(epsg=tile_grid.crs) if isinstance(tile_grid, _SizeBasedTileGrid): - # Regular grid anchored at the south-west corner of the points' extent. - minx, miny, _, _ = grid_centroids.total_bounds - keys = [(grid_centroids.x - minx) // tile_grid.size, (grid_centroids.y - miny) // tile_grid.size] + # Regular grid anchored at the origin of the CRS. + keys = pd.DataFrame({"x": np.floor(xs / tile_grid.size), "y": np.floor(ys / tile_grid.size)}) elif isinstance(tile_grid, _PredefinedTileGrid): - joined = gpd.sjoin( - gpd.GeoDataFrame(geometry=grid_centroids), - tile_grid._gdf[["geometry"]].reset_index(drop=True), - how="left", - predicate="intersects", - ) - # A centroid on a shared tile edge can match multiple tiles: keep just the first match. - joined = joined.loc[~joined.index.duplicated(keep="first")] - # Unmatched centroids (index_right = NaN) get their own group. - keys = joined["index_right"].fillna(-1) + keys = pd.DataFrame({"tile": _predefined_tile_keys(tile_grid, working.to_numpy())}) else: raise JobSplittingFailure(f"Unsupported tile grid type: {type(tile_grid).__name__}.") - for idx in points.index.to_series().groupby(keys).groups.values(): - _quadtree_split_indices(centroids, idx, max_points, index_groups) - - return [points.loc[idx].reset_index(drop=True) for idx in index_groups] + position_groups = list(keys.groupby(list(keys.columns), sort=True).indices.values()) + + result = [] + for positions in position_groups: + for group in _bisect_group( + positions, + xs=xs, + ys=ys, + weights=weights, + bounds=bounds, + max_geometries=max_geometries, + max_weight=max_weight, + max_extent=max_extent, + ): + result.append(geometries.iloc[np.sort(group)]) + return result diff --git a/tests/extra/job_management/test_job_splitting.py b/tests/extra/job_management/test_job_splitting.py index 3ecebec8e..5c7a1b636 100644 --- a/tests/extra/job_management/test_job_splitting.py +++ b/tests/extra/job_management/test_job_splitting.py @@ -1,4 +1,7 @@ +import warnings + import geopandas as gpd +import pandas as pd import pytest import shapely @@ -7,8 +10,8 @@ _PredefinedTileGrid, _SizeBasedTileGrid, _TileGridInterface, + group_geometries, split_area, - split_points, ) from openeo.util import BBoxDict @@ -389,20 +392,20 @@ def test_antimeridian_not_checked_for_projected_crs(self): assert len(result) >= 1 -class TestSplitPoints: +class TestGroupGeometries: def _grid_points(self, n_side: int) -> gpd.GeoDataFrame: points = [shapely.geometry.Point(x, y) for x in range(n_side) for y in range(n_side)] return gpd.GeoDataFrame(geometry=points, crs="EPSG:4326") def test_single_group_when_under_limit(self): gdf = self._grid_points(5) # 25 points - result = split_points(gdf, max_points=100) + result = group_geometries(gdf, max_geometries=100) assert len(result) == 1 assert len(result[0]) == 25 - def test_splits_into_groups_of_max_points(self): + def test_splits_into_groups_of_max_geometries(self): gdf = self._grid_points(10) # 100 points - result = split_points(gdf, max_points=30) + result = group_geometries(gdf, max_geometries=30) assert sum(len(g) for g in result) == 100 assert all(len(g) <= 30 for g in result) assert len(result) > 1 @@ -412,61 +415,61 @@ def test_groups_are_spatially_clustered(self): left = [shapely.geometry.Point(x, 0) for x in range(10)] right = [shapely.geometry.Point(x + 100, 0) for x in range(10)] gdf = gpd.GeoDataFrame(geometry=left + right, crs="EPSG:4326") - result = split_points(gdf, max_points=10) + result = group_geometries(gdf, max_geometries=10) assert len(result) == 2 for group in result: xs = [geom.x for geom in group.geometry] assert max(xs) - min(xs) < 50 - def test_default_max_points_is_unlimited(self): + def test_default_max_geometries_is_unlimited(self): gdf = self._grid_points(30) # 900 points - result = split_points(gdf) + result = group_geometries(gdf) assert len(result) == 1 assert len(result[0]) == 900 - def test_default_max_points_groups_per_tile(self): + def test_default_max_geometries_groups_per_tile(self): gdf = self._grid_points(30) - result = split_points(gdf, tile_size=15, projection="EPSG:4326") + result = group_geometries(gdf, tile_size=15, projection="EPSG:4326") assert sorted(len(g) for g in result) == [225, 225, 225, 225] def test_empty_geodataframe(self): gdf = gpd.GeoDataFrame(geometry=[], crs="EPSG:4326") - assert split_points(gdf) == [] + assert group_geometries(gdf) == [] - def test_coincident_points_fall_back_to_chunking(self): - """Points that all share the same location can't be split by quadtree; fall back to plain chunking.""" + def test_coincident_points_are_split_by_row_order(self): + """Points that all share the same location are still split in groups of at most max_geometries.""" gdf = gpd.GeoDataFrame(geometry=[shapely.geometry.Point(0, 0)] * 25, crs="EPSG:4326") - result = split_points(gdf, max_points=10) + result = group_geometries(gdf, max_geometries=10) assert sum(len(g) for g in result) == 25 assert all(len(g) <= 10 for g in result) def test_missing_geometry_column_raises(self): gdf = gpd.GeoDataFrame({"foo": [1, 2]}) with pytest.raises(JobSplittingFailure, match="geometry"): - split_points(gdf) + group_geometries(gdf) def test_missing_crs_without_grid(self): gdf = gpd.GeoDataFrame(geometry=[shapely.geometry.Point(0, 0)]) - result = split_points(gdf) + result = group_geometries(gdf) assert len(result) == 1 assert result[0].crs is None - def test_non_positive_max_points_raises(self): + def test_non_positive_max_geometries_raises(self): gdf = self._grid_points(2) - with pytest.raises(JobSplittingFailure, match="max_points"): - split_points(gdf, max_points=0) + with pytest.raises(JobSplittingFailure, match="max_geometries"): + group_geometries(gdf, max_geometries=0) def test_preserves_extra_columns(self): gdf = gpd.GeoDataFrame( {"id": [1, 2, 3], "geometry": [shapely.geometry.Point(x, 0) for x in range(3)]}, crs="EPSG:4326", ) - result = split_points(gdf, max_points=10) + result = group_geometries(gdf, max_geometries=10) assert len(result) == 1 assert list(result[0]["id"]) == [1, 2, 3] def test_multipoint_rows_treated_as_single_feature(self): - """A row with a MultiPoint geometry counts as one feature towards max_points, not one per coordinate.""" + """A row with a MultiPoint geometry counts as one feature towards max_geometries, not one per coordinate.""" gdf = gpd.GeoDataFrame( geometry=[ shapely.geometry.MultiPoint([(0, 0), (0.1, 0.1), (0.2, 0.2)]), # 3 coordinates, 1 feature @@ -474,11 +477,11 @@ def test_multipoint_rows_treated_as_single_feature(self): ], crs="EPSG:4326", ) - result = split_points(gdf, max_points=10) + result = group_geometries(gdf, max_geometries=10) assert len(result) == 1 assert len(result[0]) == 2 # 2 rows/features, regardless of coordinate count - def test_tile_grid_groups_never_span_multiple_tiles(self): + def test_tile_grid_points_never_span_multiple_tiles(self): tile_grid = gpd.GeoDataFrame( {"tile": ["A", "B"]}, geometry=[shapely.geometry.box(0, 0, 1, 1), shapely.geometry.box(1, 0, 2, 1)], @@ -489,7 +492,7 @@ def test_tile_grid_groups_never_span_multiple_tiles(self): points += [shapely.geometry.Point(1.1 + 0.1 * i, 0.5) for i in range(5)] # tile B gdf = gpd.GeoDataFrame(geometry=points, crs="EPSG:4326") - result = split_points(gdf, max_points=100, tile_grid=tile_grid) + result = group_geometries(gdf, max_geometries=100, tile_grid=tile_grid) assert len(result) == 2 for group in result: xs = [geom.x for geom in group.geometry] @@ -500,7 +503,7 @@ def test_tile_grid_oversized_tile_is_further_split(self): points = [shapely.geometry.Point(x, y) for x in range(10) for y in range(10)] # 100 points, 1 tile gdf = gpd.GeoDataFrame(geometry=points, crs="EPSG:4326") - result = split_points(gdf, max_points=30, tile_grid=tile_grid) + result = group_geometries(gdf, max_geometries=30, tile_grid=tile_grid) assert sum(len(g) for g in result) == 100 assert all(len(g) <= 30 for g in result) assert len(result) > 1 @@ -511,7 +514,7 @@ def test_tile_grid_unmatched_points_grouped_separately(self): geometry=[shapely.geometry.Point(0.5, 0.5), shapely.geometry.Point(50, 50)], crs="EPSG:4326", ) - result = split_points(gdf, max_points=10, tile_grid=tile_grid) + result = group_geometries(gdf, max_geometries=10, tile_grid=tile_grid) assert len(result) == 2 assert sum(len(g) for g in result) == 2 @@ -520,7 +523,7 @@ def test_tile_grid_reprojects_when_crs_differs(self): geometry=[shapely.geometry.box(-111_320, -111_325, 111_320, 111_325)], crs="EPSG:3857" ) gdf = gpd.GeoDataFrame(geometry=[shapely.geometry.Point(0.1, 0.1)], crs="EPSG:4326") - result = split_points(gdf, max_points=10, tile_grid=tile_grid) + result = group_geometries(gdf, max_geometries=10, tile_grid=tile_grid) assert len(result) == 1 assert len(result[0]) == 1 @@ -528,23 +531,22 @@ def test_tile_grid_missing_crs_raises(self): tile_grid = gpd.GeoDataFrame(geometry=[shapely.geometry.box(0, 0, 1, 1)]) gdf = self._grid_points(2) with pytest.raises(JobSplittingFailure, match="CRS"): - split_points(gdf, tile_grid=tile_grid) - - def test_list_of_points(self): - points = [shapely.geometry.Point(x, 0) for x in range(5)] - result = split_points(points, max_points=10) - assert len(result) == 1 - assert len(result[0]) == 5 + group_geometries(gdf, tile_grid=tile_grid) - def test_multipoint_input_is_exploded(self): - multipoint = shapely.geometry.MultiPoint([(x, 0) for x in range(5)]) - result = split_points(multipoint, max_points=2) - assert sum(len(g) for g in result) == 5 - assert all(len(g) <= 2 for g in result) + @pytest.mark.parametrize( + "geometries", + [ + [shapely.geometry.Point(x, 0) for x in range(5)], + shapely.geometry.MultiPoint([(x, 0) for x in range(5)]), + ], + ) + def test_non_geodataframe_raises(self, geometries): + with pytest.raises(JobSplittingFailure, match="Expected a GeoDataFrame"): + group_geometries(geometries, max_geometries=10) def test_points_without_crs_assume_grid_crs(self, caplog): - points = [shapely.geometry.Point(100_000 * x, 0) for x in range(3)] - result = split_points(points, max_points=10, tile_size=150_000, projection="EPSG:3857") + points = gpd.GeoDataFrame(geometry=[shapely.geometry.Point(100_000 * x, 0) for x in range(3)]) + result = group_geometries(points, max_geometries=10, tile_size=150_000, projection="EPSG:3857") assert [len(g) for g in result] == [2, 1] assert all(g.crs.to_epsg() == 3857 for g in result) assert "assuming they are already in the tile grid's CRS (EPSG:3857)" in caplog.text @@ -556,24 +558,192 @@ def test_tile_grid_interface_instance(self): tile_grid = _PredefinedTileGrid( tiles=[shapely.geometry.box(0, 0, 1, 1), shapely.geometry.box(1, 0, 2, 1)], crs=4326 ) - assert len(split_points(gdf, max_points=10, tile_grid=tile_grid)) == 2 + assert len(group_geometries(gdf, max_geometries=10, tile_grid=tile_grid)) == 2 tile_grid = _SizeBasedTileGrid(epsg=4326, size=1.0) - assert len(split_points(gdf, max_points=10, tile_grid=tile_grid)) == 2 + assert len(group_geometries(gdf, max_geometries=10, tile_grid=tile_grid)) == 2 + + def test_null_geometry_raises(self): + gdf = gpd.GeoDataFrame(geometry=[shapely.geometry.Point(0, 0), None], crs="EPSG:4326") + with pytest.raises(JobSplittingFailure, match="1 null or empty"): + group_geometries(gdf) + + def test_empty_geometry_raises(self): + gdf = gpd.GeoDataFrame(geometry=[shapely.geometry.Point(0, 0), shapely.geometry.Point()], crs="EPSG:4326") + with pytest.raises(JobSplittingFailure, match="1 null or empty"): + group_geometries(gdf) + + def test_duplicated_index_keeps_all_rows_once(self): + gdf = self._grid_points(4) + gdf.index = [0, 0, 1, 1, 2, 2, 3, 3] * 2 + result = group_geometries(gdf, max_geometries=3) + assert sum(len(g) for g in result) == 16 + coords = sorted((g.x, g.y) for group in result for g in group.geometry) + assert coords == sorted((g.x, g.y) for g in gdf.geometry) + assert sorted(i for group in result for i in group.index) == sorted(gdf.index) + + def test_original_index_preserved(self): + gdf = self._grid_points(3) + gdf.index = list("abcdefghi") + result = group_geometries(gdf, max_geometries=4) + assert sorted(i for group in result for i in group.index) == list("abcdefghi") + for group in result: + assert (gdf.loc[group.index].geometry.values == group.geometry.values).all() + + def test_polygons_in_geographic_crs_emit_no_warning(self): + gdf = gpd.GeoDataFrame( + geometry=[shapely.geometry.box(x, 0, x + 1, 1) for x in range(10)], + crs="EPSG:4326", + ) + with warnings.catch_warnings(): + warnings.simplefilter("error", UserWarning) + result = group_geometries(gdf, max_geometries=4) + assert sum(len(g) for g in result) == 10 + + def test_concave_polygon_located_by_representative_point(self): + # U shape: its centroid (4.4, 3.7) lies in the notch, outside the polygon. + u_shape = shapely.geometry.box(0, 0, 10, 10).difference(shapely.geometry.box(2, 2, 9, 10)) + assert not u_shape.contains(u_shape.centroid) + gdf = gpd.GeoDataFrame( + geometry=[u_shape, shapely.geometry.Point(1, 6), shapely.geometry.Point(7, 0.5)], crs="EPSG:3857" + ) + result = group_geometries(gdf, tile_size=5, projection="EPSG:3857") + assert sorted(sorted(g.index) for g in result) == [[0, 1], [2]] + + def test_collinear_points_are_split_balanced(self): + gdf = gpd.GeoDataFrame(geometry=[shapely.geometry.Point(3, y) for y in range(100)], crs="EPSG:4326") + result = group_geometries(gdf, max_geometries=30) + assert [len(g) for g in result] == [25, 25, 25, 25] + + def test_balanced_bisection(self): + gdf = self._grid_points(10) + result = group_geometries(gdf, max_geometries=30) + assert sorted(len(g) for g in result) == [25, 25, 25, 25] + + def test_weight_area_respects_max_weight(self): + big = [shapely.geometry.box(2000 * i, 0, 2000 * i + 1000, 1000) for i in range(3)] # 1e6 m² each + small = [shapely.geometry.box(10_000 + 100 * i, 0, 10_010 + 100 * i, 10) for i in range(3)] # 100 m² each + gdf = gpd.GeoDataFrame(geometry=big + small, crs="EPSG:6933") + result = group_geometries(gdf, weight="area", max_weight=2.5e6) + assert len(result) > 1 + for group in result: + assert (group.geometry.area.clip(lower=100)).sum() <= 2.5e6 + assert sum(len(g) for g in result) == 6 + + def test_weight_area_floor_applies_to_points(self): + gdf = gpd.GeoDataFrame(geometry=[shapely.geometry.Point(i, 0) for i in range(10)], crs="EPSG:6933") + result = group_geometries(gdf, weight="area", max_weight=250) + assert all(len(g) <= 2 for g in result) + + def test_weight_area_without_crs_raises(self): + gdf = gpd.GeoDataFrame(geometry=[shapely.geometry.box(0, 0, 1, 1)]) + with pytest.raises(JobSplittingFailure, match="CRS"): + group_geometries(gdf, weight="area", max_weight=10) + + def test_weight_column(self): + gdf = self._grid_points(3) + gdf["w"] = [1, 2, 3, 1, 2, 3, 1, 2, 3] + result = group_geometries(gdf, weight="w", max_weight=6) + assert sum(len(g) for g in result) == 9 + assert all(g["w"].sum() <= 6 for g in result) + assert len(result) > 1 - def test_list_with_non_points_raises(self): - with pytest.raises(JobSplittingFailure, match="Point or MultiPoint"): - split_points([shapely.geometry.box(0, 0, 1, 1)]) + def test_weight_series(self): + gdf = self._grid_points(3) + weights = pd.Series([1, 2, 3, 1, 2, 3, 1, 2, 3], index=gdf.index) + result = group_geometries(gdf, weight=weights, max_weight=6) + assert sum(len(g) for g in result) == 9 + assert all(weights.loc[g.index].sum() <= 6 for g in result) - def test_non_point_geometries_raise(self): - gdf = gpd.GeoDataFrame(geometry=[shapely.geometry.box(0, 0, 1, 1)], crs="EPSG:4326") - with pytest.raises(JobSplittingFailure, match="Point or MultiPoint"): - split_points(gdf) + def test_unknown_weight_column_raises(self): + with pytest.raises(JobSplittingFailure, match="not found"): + group_geometries(self._grid_points(2), weight="nope", max_weight=1) + + def test_oversize_feature_gets_own_group_with_warning(self, caplog): + gdf = self._grid_points(2) + gdf["w"] = [10, 1, 1, 1] + result = group_geometries(gdf, weight="w", max_weight=5) + heavy = [g for g in result if 0 in g.index] + assert len(heavy) == 1 and len(heavy[0]) == 1 + assert "exceeding 'max_weight'" in caplog.text + + def test_max_weight_without_weight_raises(self): + with pytest.raises(JobSplittingFailure, match="requires 'weight'"): + group_geometries(self._grid_points(2), max_weight=5) + + @pytest.mark.parametrize("max_weight", [0, -1]) + def test_non_positive_max_weight_raises(self, max_weight): + gdf = self._grid_points(2) + gdf["w"] = 1 + with pytest.raises(JobSplittingFailure, match="max_weight"): + group_geometries(gdf, weight="w", max_weight=max_weight) + + @pytest.mark.parametrize("bad", [-1.0, float("nan")]) + def test_invalid_weight_values_raise(self, bad): + gdf = self._grid_points(2) + gdf["w"] = [1.0, 1.0, 1.0, bad] + with pytest.raises(JobSplittingFailure, match="finite and non-negative"): + group_geometries(gdf, weight="w", max_weight=5) + + def test_max_extent(self): + gdf = gpd.GeoDataFrame(geometry=[shapely.geometry.Point(x, 0) for x in range(100)], crs="EPSG:3857") + result = group_geometries(gdf, max_extent=30) + assert len(result) > 1 + for group in result: + minx, _, maxx, _ = group.total_bounds + assert maxx - minx <= 30 + + def test_max_extent_uses_geometry_bounds(self): + gdf = gpd.GeoDataFrame( + geometry=[shapely.geometry.box(0, 0, 10, 1), shapely.geometry.box(0, 2, 10, 3)], crs="EPSG:3857" + ) + assert len(group_geometries(gdf, max_extent=10)) == 1 + assert len(group_geometries(gdf, max_extent=5)) == 2 # single features exceeding the limit stay alone + + @pytest.mark.parametrize("max_extent", [0, -5]) + def test_non_positive_max_extent_raises(self, max_extent): + with pytest.raises(JobSplittingFailure, match="max_extent"): + group_geometries(self._grid_points(2), max_extent=max_extent) + + def test_size_based_grid_is_globally_anchored(self): + def grouping(gdf): + result = group_geometries(gdf, tile_size=5, projection="EPSG:3857") + return {frozenset(g.index) for g in result} + + base = gpd.GeoDataFrame(geometry=[shapely.geometry.Point(x, 1) for x in (1, 3, 6, 8, 11)], crs="EPSG:3857") + extended = gpd.GeoDataFrame( + geometry=list(base.geometry) + [shapely.geometry.Point(-1000, 1)], crs="EPSG:3857" + ) + assert grouping(extended) - {frozenset({5})} == grouping(base) + + def test_predefined_grid_polygon_assigned_to_largest_overlap_tile(self): + tile_grid = gpd.GeoDataFrame( + geometry=[shapely.geometry.box(0, 0, 10, 10), shapely.geometry.box(10, 0, 20, 10)], crs="EPSG:3857" + ) + gdf = gpd.GeoDataFrame( + geometry=[ + shapely.geometry.Point(5, 5), + shapely.geometry.Point(15, 5), + shapely.geometry.box(8, 2, 13, 4), # 2 wide in tile A, 3 wide in tile B + ], + crs="EPSG:3857", + ) + result = group_geometries(gdf, tile_grid=tile_grid) + assert sorted(sorted(g.index) for g in result) == [[0], [1, 2]] + + def test_polygons_grouped_by_centroid(self): + polygons = [shapely.geometry.box(x, 0, x + 1, 1) for x in (0, 1, 100, 101)] + gdf = gpd.GeoDataFrame(geometry=polygons, crs="EPSG:4326") + result = group_geometries(gdf, max_geometries=2) + assert len(result) == 2 + for group in result: + xs = [geom.centroid.x for geom in group.geometry] + assert max(xs) - min(xs) < 50 def test_tile_size_projection_groups_by_regular_grid(self): points = [shapely.geometry.Point(0.1 * i, 0.5) for i in range(5)] points += [shapely.geometry.Point(1.1 + 0.1 * i, 0.5) for i in range(5)] gdf = gpd.GeoDataFrame(geometry=points, crs="EPSG:4326") - result = split_points(gdf, max_points=100, tile_size=1.0, projection="EPSG:4326") + result = group_geometries(gdf, max_geometries=100, tile_size=1.0, projection="EPSG:4326") assert len(result) == 2 for group in result: xs = [geom.x for geom in group.geometry] @@ -584,9 +754,9 @@ def test_tile_size_projection_reprojects(self): gdf = gpd.GeoDataFrame( geometry=[shapely.geometry.Point(0.0, 0.0), shapely.geometry.Point(0.1, 0.0)], crs="EPSG:4326" ) - result = split_points(gdf, max_points=10, tile_size=50_000, projection="EPSG:3857") + result = group_geometries(gdf, max_geometries=10, tile_size=50_000, projection="EPSG:3857") assert len(result) == 1 - result = split_points(gdf, max_points=10, tile_size=5_000, projection="EPSG:3857") + result = group_geometries(gdf, max_geometries=10, tile_size=5_000, projection="EPSG:3857") assert len(result) == 2 @pytest.mark.parametrize( @@ -601,10 +771,10 @@ def test_invalid_grid_argument_combinations_raise(self, kwargs): if kwargs.get("tile_grid") == "grid": kwargs["tile_grid"] = gpd.GeoDataFrame(geometry=[shapely.geometry.box(0, 0, 1, 1)], crs="EPSG:4326") with pytest.raises(JobSplittingFailure): - split_points(self._grid_points(2), **kwargs) + group_geometries(self._grid_points(2), **kwargs) def test_antimeridian_crossing_points_raises(self): """Geographic points with longitude outside [-180, 180] are rejected to avoid antimeridian issues.""" gdf = gpd.GeoDataFrame(geometry=[shapely.geometry.Point(190.0, 0.0)], crs="EPSG:4326") with pytest.raises(JobSplittingFailure, match="antimeridian"): - split_points(gdf, max_points=10) + group_geometries(gdf, max_geometries=10) From 30f9394e5579f639b1160935086609292b53d619 Mon Sep 17 00:00:00 2001 From: Hans Vanrompay Date: Tue, 10 Nov 2026 10:50:21 +0100 Subject: [PATCH 06/12] update readme --- CHANGELOG.md | 6 +- docs/cookbook/job_manager.rst | 24 ++- openeo/extra/job_management/_job_splitting.py | 155 ++++-------------- .../job_management/test_job_splitting.py | 90 +--------- 4 files changed, 43 insertions(+), 232 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 40fbd9ac8..7d8f40f36 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,7 +8,6 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Added -<<<<<<< HEAD - Richer (HTML based) representation of cube dimension metadata in Jupyter context ([#827](https://github.com/Open-EO/openeo-python-client/issues/827)) ### Changed @@ -22,10 +21,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [0.54.0] - 2026-10-28 -- Add public `split_points` helper in `openeo.extra.job_management` for point-based job splitting, including optional tile-grid co-grouping. -======= -- Add public `split_points` helper in `openeo.extra.job_management` for point-based job splitting, including optional co-grouping by tile grid or by `tile_size` + `projection`. ->>>>>>> 3796ed2b (review) +- Add public `group_geometries` helper in `openeo.extra.job_management` for geometry-grouping based job splitting, including optional tile-grid co-grouping. ### Changed diff --git a/docs/cookbook/job_manager.rst b/docs/cookbook/job_manager.rst index 433b892d6..77a33acb8 100644 --- a/docs/cookbook/job_manager.rst +++ b/docs/cookbook/job_manager.rst @@ -331,7 +331,7 @@ Null or empty geometries are not allowed. Geometries are not split themselves: use ``split_area`` for a large area of interest. Like with ``split_area``, geometries without CRS information are assumed to be in the CRS of the tile grid (if any). -Groups are formed by recursive bisection at the (weighted) median along the longer axis, +Groups are formed by recursive bisection at the median along the longer axis, so that batches stay geographically clustered and balanced. The returned GeoDataFrames keep the original index, to link back to the input rows: @@ -344,19 +344,9 @@ The returned GeoDataFrames keep the original index, to link back to the input ro batches = group_geometries(points, max_geometries=500) # batches is a list of GeoDataFrames, each with at most 500 rows -Batches can also be limited by a ``weight`` (a column name, a pandas Series, -or ``"area"`` for the geometry area in m², at least 100 m² per feature) -with ``max_weight``, and by the bounding box edge length of a batch with ``max_extent`` -(in units of the CRS of the geometries, or of the tile grid if any): - -.. code-block:: python - - batches = group_geometries(polygons, weight="area", max_weight=1e9, max_extent=50_000) - Optionally, a ``tile_grid`` (e.g. a satellite tiling grid) can be passed so -that each feature is first assigned to a single tile (the one with the largest overlap) -and batches are formed per tile. Polygons crossing tile edges may still touch -neighbouring tiles, but points are guaranteed to stay within one tile: +that each feature is first assigned to the tile containing its representative point +and batches are formed per tile: .. code-block:: python @@ -380,6 +370,14 @@ geometries are simply grouped per tile: # one batch per tile batches = group_geometries(points, tile_grid=tile_grid) +Current limitations: + +- Geometries are never split: a polygon crossing tile edges, or larger than a tile, + ends up in a single batch and extends beyond its tile. + Use ``split_area`` for large areas of interest. +- Batches are limited by number of geometries only, not by area or processing cost. + Use a tile grid to limit the spatial extent of batches. + Customizing Job Handling ======================== diff --git a/openeo/extra/job_management/_job_splitting.py b/openeo/extra/job_management/_job_splitting.py index 9a389431b..2c8c22b20 100644 --- a/openeo/extra/job_management/_job_splitting.py +++ b/openeo/extra/job_management/_job_splitting.py @@ -14,10 +14,6 @@ _log = logging.getLogger(__name__) -# Minimum area weight (m²) of a feature when weighting by "area": roughly one 10 m Sentinel-2 pixel, -# so that points and lines are not "free". -_MIN_AREA_WEIGHT = 100.0 - class JobSplittingFailure(Exception): pass @@ -364,54 +360,16 @@ def split_area( return grid.get_tiles(spatial_extent) -def _resolve_weights( - geometries: gpd.GeoDataFrame, - weight: Union[None, str, pd.Series], - max_weight: Optional[float], -) -> np.ndarray: - """Resolve the *weight* argument of :func:`group_geometries` to a validated float array (one value per row).""" - if weight is None: - return np.ones(len(geometries)) - - if isinstance(weight, str): - if weight in geometries.columns: - values = geometries[weight] - elif weight == "area": - if geometries.crs is None: - raise JobSplittingFailure("weight='area' requires geometries with a CRS.") - values = np.maximum(geometries.geometry.to_crs("EPSG:6933").area.to_numpy(), _MIN_AREA_WEIGHT) - else: - raise JobSplittingFailure(f"Weight column {weight!r} not found in the GeoDataFrame.") - elif isinstance(weight, pd.Series): - if not weight.index.equals(geometries.index): - if not (weight.index.is_unique and geometries.index.is_unique): - raise JobSplittingFailure("'weight' Series must be aligned with the index of 'geometries'.") - weight = weight.reindex(geometries.index) - values = weight - else: - raise JobSplittingFailure(f"'weight' must be None, a column name or a pandas Series, got {weight!r}.") - - try: - values = np.asarray(values, dtype=float) - except (TypeError, ValueError) as e: - raise JobSplittingFailure("Weights must be numeric.") from e - if not np.isfinite(values).all() or (values < 0).any(): - raise JobSplittingFailure("Weights must be finite and non-negative.") - return values - - -def _predefined_tile_keys(tile_grid: "_PredefinedTileGrid", geoms: np.ndarray) -> np.ndarray: - """For each geometry (in the grid CRS), the position of the intersecting tile with largest overlap (-1: none).""" - tiles = tile_grid._gdf.geometry.to_numpy() - tile_idx, geom_idx = shapely.STRtree(tiles).query(geoms, predicate="intersects")[::-1] - areas = shapely.area(shapely.intersection(geoms[geom_idx], tiles[tile_idx])) - # Sort per geometry by decreasing overlap area, then tile position; take the first of each geometry. - order = np.lexsort((tile_idx, -areas, geom_idx)) - geom_idx, tile_idx = geom_idx[order], tile_idx[order] - first = np.ones(len(geom_idx), dtype=bool) - first[1:] = geom_idx[1:] != geom_idx[:-1] - keys = np.full(len(geoms), -1) - keys[geom_idx[first]] = tile_idx[first] +def _predefined_tile_keys(tile_grid: "_PredefinedTileGrid", points: np.ndarray) -> np.ndarray: + """For each point (in the grid CRS), the position of the first tile containing it (-1: none).""" + # TODO: polygons are assigned by their representative point only, so they may extend beyond their tile. + # Assigning them to e.g. the tile with the largest overlap could be a future addition. + n_tiles = len(tile_grid._gdf) + point_idx, tile_idx = shapely.STRtree(tile_grid._gdf.geometry.to_numpy()).query(points, predicate="intersects") + # Lowest tile position wins for points on shared tile edges. + keys = np.full(len(points), n_tiles) + np.minimum.at(keys, point_idx, tile_idx) + keys[keys == n_tiles] = -1 return keys @@ -420,50 +378,23 @@ def _bisect_group( *, xs: np.ndarray, ys: np.ndarray, - weights: np.ndarray, - bounds: np.ndarray, max_geometries: Optional[int], - max_weight: Optional[float], - max_extent: Optional[float], ) -> List[np.ndarray]: """ - Recursively bisect a group of rows (given as positions) at the weighted median of their locations, - along the longer axis of the locations' spread, until limits are satisfied. + Recursively bisect a group of rows (given as positions) at the median of their locations, + along the longer axis of the locations' spread, until groups have at most *max_geometries* rows. """ result = [] stack = [positions] while stack: pos = stack.pop() - if len(pos) == 1: - if max_weight is not None and weights[pos[0]] > max_weight: - _log.warning( - "Feature at position %d has weight %s exceeding 'max_weight' %s: putting it in its own group.", - pos[0], - weights[pos[0]], - max_weight, - ) - result.append(pos) - continue - fits = ( - (max_geometries is None or len(pos) <= max_geometries) - and (max_weight is None or weights[pos].sum() <= max_weight) - and ( - max_extent is None - or max( - bounds[pos, 2].max() - bounds[pos, 0].min(), - bounds[pos, 3].max() - bounds[pos, 1].min(), - ) - <= max_extent - ) - ) - if fits: + if max_geometries is None or len(pos) <= max_geometries: result.append(pos) continue px, py = xs[pos], ys[pos] axis = px if np.ptp(px) >= np.ptp(py) else py pos = pos[np.argsort(axis, kind="stable")] - cumulative = np.cumsum(weights[pos]) - cut = int(np.clip(np.searchsorted(cumulative, cumulative[-1] / 2, side="right"), 1, len(pos) - 1)) + cut = len(pos) // 2 stack.append(pos[cut:]) stack.append(pos[:cut]) return result @@ -473,9 +404,6 @@ def group_geometries( geometries: gpd.GeoDataFrame, *, max_geometries: Optional[int] = None, - weight: Union[None, str, pd.Series] = None, - max_weight: Optional[float] = None, - max_extent: Optional[float] = None, projection: Optional[str] = None, tile_size: Optional[float] = None, tile_grid: Optional[Union[_TileGridInterface, gpd.GeoDataFrame]] = None, @@ -496,33 +424,28 @@ def group_geometries( use :func:`split_area` instead. Grouping is done by recursive bisection (k-d tree style): a group is cut at the - weighted median of the feature locations along the longer axis of their spread, - until it satisfies all given limits (*max_geometries*, *max_weight*, *max_extent*) - or consists of a single feature. This keeps groups spatially clustered and balanced. + median of the feature locations along the longer axis of their spread, + until it has at most *max_geometries* features. + This keeps groups spatially clustered and balanced. If *tile_grid* is given (e.g. a Sentinel-2 MGRS/UTM tile grid), each feature is first - assigned to a single tile (the one with the largest overlap), and every tile is bisected separately. - Note that polygons crossing tile edges may still touch neighbouring tiles. - Features not intersecting any tile are grouped (and bisected) separately. + assigned to the tile containing its representative point, and every tile is bisected separately. + Features whose representative point is not in any tile are grouped (and bisected) separately. Alternatively, *tile_size* and *projection* can be given to group features in a regular grid of square tiles anchored at the origin of the projection (so grouping does not change when other features are added). + Current limitations: + + - Features are never split: a polygon crossing tile edges, or larger than a tile, + is assigned to a single tile and extends beyond it. Use :func:`split_area` for large areas. + - Groups are limited by feature count only, not by area or processing cost. + Use a tile grid to limit the spatial extent of groups. + :param geometries: GeoDataFrame with a ``geometry`` column. Null or empty geometries are not allowed. :param max_geometries: maximum number of geometries per group. ``None`` (default) means no limit. - :param weight: weight of each feature, to be limited with *max_weight*: - ``None`` (every feature has weight 1), the name of a numeric column, - a pandas Series aligned with the index of *geometries*, - or ``"area"`` (if there is no column with that name): - geometry area in m² (computed in EPSG:6933, requires a CRS), - with a minimum of 100 m² (about one 10 m Sentinel-2 pixel) per feature. - :param max_weight: maximum total weight per group (requires *weight*). - A single feature heavier than this ends up alone in its own group (with a warning). - :param max_extent: maximum edge length (largest of width and height) - of the bounding box of the geometries in a group, in the units of the - working CRS (the tile grid's CRS if there is one, else the CRS of *geometries*). :param projection: EPSG string (e.g. ``"EPSG:3857"``) of the regular grid to group by. Must be combined with *tile_size*. :param tile_size: tile edge length in the unit of *projection*. @@ -545,13 +468,6 @@ def group_geometries( raise JobSplittingFailure("The GeoDataFrame must contain a 'geometry' column.") if max_geometries is not None and max_geometries <= 0: raise JobSplittingFailure(f"'max_geometries' must be positive, got {max_geometries!r}.") - if max_weight is not None: - if weight is None: - raise JobSplittingFailure("'max_weight' requires 'weight'.") - if max_weight <= 0: - raise JobSplittingFailure(f"'max_weight' must be positive, got {max_weight!r}.") - if max_extent is not None and max_extent <= 0: - raise JobSplittingFailure(f"'max_extent' must be positive, got {max_extent!r}.") if tile_grid is not None: if projection is not None or tile_size is not None: @@ -582,16 +498,12 @@ def group_geometries( if geometries.empty: return [] - weights = _resolve_weights(geometries, weight, max_weight) - + # TODO: limiting groups by area or processing cost (e.g. a weight per feature) is not supported yet. # Work with positions (not index labels), so duplicated index labels are no problem. locations = geometries.geometry.representative_point() - working = geometries.geometry if tile_grid is not None: locations = locations.to_crs(epsg=tile_grid.crs) - working = working.to_crs(epsg=tile_grid.crs) xs, ys = locations.x.to_numpy(), locations.y.to_numpy() - bounds = working.bounds.to_numpy() all_positions = np.arange(len(geometries)) if tile_grid is None: @@ -601,22 +513,13 @@ def group_geometries( # Regular grid anchored at the origin of the CRS. keys = pd.DataFrame({"x": np.floor(xs / tile_grid.size), "y": np.floor(ys / tile_grid.size)}) elif isinstance(tile_grid, _PredefinedTileGrid): - keys = pd.DataFrame({"tile": _predefined_tile_keys(tile_grid, working.to_numpy())}) + keys = pd.DataFrame({"tile": _predefined_tile_keys(tile_grid, locations.to_numpy())}) else: raise JobSplittingFailure(f"Unsupported tile grid type: {type(tile_grid).__name__}.") position_groups = list(keys.groupby(list(keys.columns), sort=True).indices.values()) result = [] for positions in position_groups: - for group in _bisect_group( - positions, - xs=xs, - ys=ys, - weights=weights, - bounds=bounds, - max_geometries=max_geometries, - max_weight=max_weight, - max_extent=max_extent, - ): + for group in _bisect_group(positions, xs=xs, ys=ys, max_geometries=max_geometries): result.append(geometries.iloc[np.sort(group)]) return result diff --git a/tests/extra/job_management/test_job_splitting.py b/tests/extra/job_management/test_job_splitting.py index 5c7a1b636..7d3f394ab 100644 --- a/tests/extra/job_management/test_job_splitting.py +++ b/tests/extra/job_management/test_job_splitting.py @@ -1,7 +1,6 @@ import warnings import geopandas as gpd -import pandas as pd import pytest import shapely @@ -619,91 +618,6 @@ def test_balanced_bisection(self): result = group_geometries(gdf, max_geometries=30) assert sorted(len(g) for g in result) == [25, 25, 25, 25] - def test_weight_area_respects_max_weight(self): - big = [shapely.geometry.box(2000 * i, 0, 2000 * i + 1000, 1000) for i in range(3)] # 1e6 m² each - small = [shapely.geometry.box(10_000 + 100 * i, 0, 10_010 + 100 * i, 10) for i in range(3)] # 100 m² each - gdf = gpd.GeoDataFrame(geometry=big + small, crs="EPSG:6933") - result = group_geometries(gdf, weight="area", max_weight=2.5e6) - assert len(result) > 1 - for group in result: - assert (group.geometry.area.clip(lower=100)).sum() <= 2.5e6 - assert sum(len(g) for g in result) == 6 - - def test_weight_area_floor_applies_to_points(self): - gdf = gpd.GeoDataFrame(geometry=[shapely.geometry.Point(i, 0) for i in range(10)], crs="EPSG:6933") - result = group_geometries(gdf, weight="area", max_weight=250) - assert all(len(g) <= 2 for g in result) - - def test_weight_area_without_crs_raises(self): - gdf = gpd.GeoDataFrame(geometry=[shapely.geometry.box(0, 0, 1, 1)]) - with pytest.raises(JobSplittingFailure, match="CRS"): - group_geometries(gdf, weight="area", max_weight=10) - - def test_weight_column(self): - gdf = self._grid_points(3) - gdf["w"] = [1, 2, 3, 1, 2, 3, 1, 2, 3] - result = group_geometries(gdf, weight="w", max_weight=6) - assert sum(len(g) for g in result) == 9 - assert all(g["w"].sum() <= 6 for g in result) - assert len(result) > 1 - - def test_weight_series(self): - gdf = self._grid_points(3) - weights = pd.Series([1, 2, 3, 1, 2, 3, 1, 2, 3], index=gdf.index) - result = group_geometries(gdf, weight=weights, max_weight=6) - assert sum(len(g) for g in result) == 9 - assert all(weights.loc[g.index].sum() <= 6 for g in result) - - def test_unknown_weight_column_raises(self): - with pytest.raises(JobSplittingFailure, match="not found"): - group_geometries(self._grid_points(2), weight="nope", max_weight=1) - - def test_oversize_feature_gets_own_group_with_warning(self, caplog): - gdf = self._grid_points(2) - gdf["w"] = [10, 1, 1, 1] - result = group_geometries(gdf, weight="w", max_weight=5) - heavy = [g for g in result if 0 in g.index] - assert len(heavy) == 1 and len(heavy[0]) == 1 - assert "exceeding 'max_weight'" in caplog.text - - def test_max_weight_without_weight_raises(self): - with pytest.raises(JobSplittingFailure, match="requires 'weight'"): - group_geometries(self._grid_points(2), max_weight=5) - - @pytest.mark.parametrize("max_weight", [0, -1]) - def test_non_positive_max_weight_raises(self, max_weight): - gdf = self._grid_points(2) - gdf["w"] = 1 - with pytest.raises(JobSplittingFailure, match="max_weight"): - group_geometries(gdf, weight="w", max_weight=max_weight) - - @pytest.mark.parametrize("bad", [-1.0, float("nan")]) - def test_invalid_weight_values_raise(self, bad): - gdf = self._grid_points(2) - gdf["w"] = [1.0, 1.0, 1.0, bad] - with pytest.raises(JobSplittingFailure, match="finite and non-negative"): - group_geometries(gdf, weight="w", max_weight=5) - - def test_max_extent(self): - gdf = gpd.GeoDataFrame(geometry=[shapely.geometry.Point(x, 0) for x in range(100)], crs="EPSG:3857") - result = group_geometries(gdf, max_extent=30) - assert len(result) > 1 - for group in result: - minx, _, maxx, _ = group.total_bounds - assert maxx - minx <= 30 - - def test_max_extent_uses_geometry_bounds(self): - gdf = gpd.GeoDataFrame( - geometry=[shapely.geometry.box(0, 0, 10, 1), shapely.geometry.box(0, 2, 10, 3)], crs="EPSG:3857" - ) - assert len(group_geometries(gdf, max_extent=10)) == 1 - assert len(group_geometries(gdf, max_extent=5)) == 2 # single features exceeding the limit stay alone - - @pytest.mark.parametrize("max_extent", [0, -5]) - def test_non_positive_max_extent_raises(self, max_extent): - with pytest.raises(JobSplittingFailure, match="max_extent"): - group_geometries(self._grid_points(2), max_extent=max_extent) - def test_size_based_grid_is_globally_anchored(self): def grouping(gdf): result = group_geometries(gdf, tile_size=5, projection="EPSG:3857") @@ -715,7 +629,7 @@ def grouping(gdf): ) assert grouping(extended) - {frozenset({5})} == grouping(base) - def test_predefined_grid_polygon_assigned_to_largest_overlap_tile(self): + def test_predefined_grid_polygon_assigned_by_representative_point(self): tile_grid = gpd.GeoDataFrame( geometry=[shapely.geometry.box(0, 0, 10, 10), shapely.geometry.box(10, 0, 20, 10)], crs="EPSG:3857" ) @@ -723,7 +637,7 @@ def test_predefined_grid_polygon_assigned_to_largest_overlap_tile(self): geometry=[ shapely.geometry.Point(5, 5), shapely.geometry.Point(15, 5), - shapely.geometry.box(8, 2, 13, 4), # 2 wide in tile A, 3 wide in tile B + shapely.geometry.box(8, 2, 13, 4), # representative point (10.5, 3) lies in tile B ], crs="EPSG:3857", ) From 8cec4875114b444e0e1927fc404b56d29c2d98d6 Mon Sep 17 00:00:00 2001 From: Hans Vanrompay Date: Tue, 10 Nov 2026 10:52:50 +0100 Subject: [PATCH 07/12] clean-up --- docs/cookbook/job_manager.rst | 16 ---------------- 1 file changed, 16 deletions(-) diff --git a/docs/cookbook/job_manager.rst b/docs/cookbook/job_manager.rst index 77a33acb8..01b192064 100644 --- a/docs/cookbook/job_manager.rst +++ b/docs/cookbook/job_manager.rst @@ -202,21 +202,6 @@ to resume where you left off. Parquet support requires the ``pyarrow`` package (see :ref:`optional dependencies `). -SQLite (experimental) ------- - -A SQLite database file (e.g. ``jobs.sqlite`` or ``jobs.db``) only needs the Python standard library. -It updates individual rows in a transaction instead of rewriting the whole file, -which is more robust against interruptions and faster for large job databases: - -.. code-block:: python - - job_db = create_job_db("jobs.sqlite", df=df) - -.. note:: - - Avoid placing a SQLite job database on a network file system (NFS, SMB, ...), - as file locking is unreliable there. STAC API (experimental) ----------------------- @@ -619,7 +604,6 @@ Job Database .. autoclass:: openeo.extra.job_management.ParquetJobDatabase -.. autoclass:: openeo.extra.job_management.sqlite_job_db.SqliteJobDatabase .. autofunction:: openeo.extra.job_management.create_job_db From 540422df1deed72501ee57b73425ae303443e1f2 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 10 Nov 2026 14:43:22 +0000 Subject: [PATCH 08/12] Fix group_geometries tile_grid type contract Co-authored-by: HansVRP <57984106+HansVRP@users.noreply.github.com> --- openeo/extra/job_management/_job_splitting.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/openeo/extra/job_management/_job_splitting.py b/openeo/extra/job_management/_job_splitting.py index 2c8c22b20..50b79e43c 100644 --- a/openeo/extra/job_management/_job_splitting.py +++ b/openeo/extra/job_management/_job_splitting.py @@ -406,7 +406,7 @@ def group_geometries( max_geometries: Optional[int] = None, projection: Optional[str] = None, tile_size: Optional[float] = None, - tile_grid: Optional[Union[_TileGridInterface, gpd.GeoDataFrame]] = None, + tile_grid: Optional[Union[_PredefinedTileGrid, _SizeBasedTileGrid, gpd.GeoDataFrame]] = None, ) -> List[gpd.GeoDataFrame]: """ Group geometries (e.g. sample locations for point-based extraction) @@ -450,9 +450,10 @@ def group_geometries( to group by. Must be combined with *tile_size*. :param tile_size: tile edge length in the unit of *projection*. Must be combined with *projection*. - :param tile_grid: optional :class:`_TileGridInterface` instance or - GeoDataFrame of tile polygons (with CRS set) to co-join geometries by - before splitting, e.g. a satellite tiling grid. + :param tile_grid: optional :class:`_PredefinedTileGrid` or + :class:`_SizeBasedTileGrid` instance, or GeoDataFrame of tile polygons + (with CRS set) to co-join geometries by before splitting, e.g. a + satellite tiling grid. Mutually exclusive with *projection* / *tile_size*. :return: list of GeoDataFrames, with the original index of *geometries* preserved. :raises JobSplittingFailure: on invalid input (e.g. non-positive From 1ea2a60240b5e192525df1ad6d802800b580521f Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 10 Nov 2026 14:44:40 +0000 Subject: [PATCH 09/12] Restrict group_geometries tile_grid to supported grid types Co-authored-by: HansVRP <57984106+HansVRP@users.noreply.github.com> --- openeo/extra/job_management/_job_splitting.py | 5 +++++ tests/extra/job_management/test_job_splitting.py | 13 +++++++++++++ 2 files changed, 18 insertions(+) diff --git a/openeo/extra/job_management/_job_splitting.py b/openeo/extra/job_management/_job_splitting.py index 50b79e43c..d317a55fa 100644 --- a/openeo/extra/job_management/_job_splitting.py +++ b/openeo/extra/job_management/_job_splitting.py @@ -478,6 +478,11 @@ def group_geometries( ) if isinstance(tile_grid, gpd.GeoDataFrame): tile_grid = _PredefinedTileGrid(tiles=tile_grid) + elif not isinstance(tile_grid, (_PredefinedTileGrid, _SizeBasedTileGrid)): + raise JobSplittingFailure( + "Unsupported tile_grid type: " + f"{type(tile_grid).__name__}. Expected _PredefinedTileGrid, _SizeBasedTileGrid, or GeoDataFrame." + ) elif projection is not None or tile_size is not None: if projection is None or tile_size is None: raise JobSplittingFailure("'projection' and 'tile_size' must be provided together.") diff --git a/tests/extra/job_management/test_job_splitting.py b/tests/extra/job_management/test_job_splitting.py index 7d3f394ab..e33a85a04 100644 --- a/tests/extra/job_management/test_job_splitting.py +++ b/tests/extra/job_management/test_job_splitting.py @@ -561,6 +561,19 @@ def test_tile_grid_interface_instance(self): tile_grid = _SizeBasedTileGrid(epsg=4326, size=1.0) assert len(group_geometries(gdf, max_geometries=10, tile_grid=tile_grid)) == 2 + def test_unsupported_tile_grid_interface_raises(self): + class CustomTileGrid(_TileGridInterface): + @property + def crs(self) -> int: + return 4326 + + def get_tiles(self, geometry): + return gpd.GeoDataFrame(geometry=[shapely.geometry.box(0, 0, 1, 1)], crs="EPSG:4326") + + gdf = gpd.GeoDataFrame(geometry=[shapely.geometry.Point(0.5, 0.5)], crs="EPSG:4326") + with pytest.raises(JobSplittingFailure, match="Unsupported tile_grid type: CustomTileGrid"): + group_geometries(gdf, max_geometries=10, tile_grid=CustomTileGrid()) + def test_null_geometry_raises(self): gdf = gpd.GeoDataFrame(geometry=[shapely.geometry.Point(0, 0), None], crs="EPSG:4326") with pytest.raises(JobSplittingFailure, match="1 null or empty"): From 05aeea4c28d2bee540b0cc4aca22809457803596 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 11 Nov 2026 08:25:05 +0000 Subject: [PATCH 10/12] Restore split_area aoi keyword compatibility Co-authored-by: HansVRP <57984106+HansVRP@users.noreply.github.com> --- openeo/extra/job_management/_job_splitting.py | 8 ++++---- tests/extra/job_management/test_job_splitting.py | 6 ++++++ 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/openeo/extra/job_management/_job_splitting.py b/openeo/extra/job_management/_job_splitting.py index d317a55fa..125954900 100644 --- a/openeo/extra/job_management/_job_splitting.py +++ b/openeo/extra/job_management/_job_splitting.py @@ -307,7 +307,7 @@ def get_tiles(self, geometry: Union[Dict, Polygon, MultiPolygon]) -> gpd.GeoData def split_area( - spatial_extent: Union[Dict, MultiPolygon, Polygon], + aoi: Union[Dict, MultiPolygon, Polygon], *, projection: Optional[str] = None, tile_size: Optional[float] = None, @@ -325,7 +325,7 @@ def split_area( (e.g. :class:`_PredefinedTileGrid`) or a :class:`~geopandas.GeoDataFrame` as *tile_grid*. - :param spatial_extent: area of interest as a bounding-box dict + :param aoi: area of interest as a bounding-box dict (keys ``west``, ``south``, ``east``, ``north``, optionally ``crs``), a :class:`~shapely.geometry.Polygon`, or a :class:`~shapely.geometry.MultiPolygon`. @@ -347,7 +347,7 @@ def split_area( ) if isinstance(tile_grid, gpd.GeoDataFrame): tile_grid = _PredefinedTileGrid(tiles=tile_grid) - return tile_grid.get_tiles(spatial_extent) + return tile_grid.get_tiles(aoi) # --- Size-based splitting path --- if tile_size is None: @@ -357,7 +357,7 @@ def split_area( raise JobSplittingFailure("'projection' is required when using size-based tiling.") grid = _SizeBasedTileGrid(epsg=normalize_crs(projection), size=tile_size) - return grid.get_tiles(spatial_extent) + return grid.get_tiles(aoi) def _predefined_tile_keys(tile_grid: "_PredefinedTileGrid", points: np.ndarray) -> np.ndarray: diff --git a/tests/extra/job_management/test_job_splitting.py b/tests/extra/job_management/test_job_splitting.py index e33a85a04..ca334d733 100644 --- a/tests/extra/job_management/test_job_splitting.py +++ b/tests/extra/job_management/test_job_splitting.py @@ -278,6 +278,12 @@ def test_returns_geodataframe(self): assert isinstance(result, gpd.GeoDataFrame) assert result.crs.to_epsg() == 4326 + def test_aoi_keyword_argument(self): + aoi = {"west": 0.0, "south": 0.0, "east": 10_000.0, "north": 10_000.0, "crs": "EPSG:3857"} + result = split_area(aoi=aoi, tile_size=10_000.0, projection="EPSG:3857") + assert len(result) == 1 + assert result.crs.to_epsg() == 3857 + def test_with_tile_size_and_projection(self): aoi = {"west": 0.0, "south": 0.0, "east": 1.0, "north": 1.0, "crs": "EPSG:4326"} result = split_area(aoi, projection="EPSG:4326", tile_size=1.0) From cbe7c8fd13ad82bc935890e7f4953dfeec44447c Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 11 Nov 2026 08:27:03 +0000 Subject: [PATCH 11/12] Use GeoPandas spatial index for tile lookup Co-authored-by: HansVRP <57984106+HansVRP@users.noreply.github.com> --- openeo/extra/job_management/_job_splitting.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openeo/extra/job_management/_job_splitting.py b/openeo/extra/job_management/_job_splitting.py index 125954900..61704d205 100644 --- a/openeo/extra/job_management/_job_splitting.py +++ b/openeo/extra/job_management/_job_splitting.py @@ -365,7 +365,7 @@ def _predefined_tile_keys(tile_grid: "_PredefinedTileGrid", points: np.ndarray) # TODO: polygons are assigned by their representative point only, so they may extend beyond their tile. # Assigning them to e.g. the tile with the largest overlap could be a future addition. n_tiles = len(tile_grid._gdf) - point_idx, tile_idx = shapely.STRtree(tile_grid._gdf.geometry.to_numpy()).query(points, predicate="intersects") + point_idx, tile_idx = tile_grid._gdf.sindex.query(points, predicate="intersects") # Lowest tile position wins for points on shared tile edges. keys = np.full(len(points), n_tiles) np.minimum.at(keys, point_idx, tile_idx) From 101def118f30f5117061bd82c609f67b1ebc1376 Mon Sep 17 00:00:00 2001 From: Hans Vanrompay Date: Wed, 11 Nov 2026 09:28:04 +0100 Subject: [PATCH 12/12] additional notes on caveats --- docs/cookbook/job_manager.rst | 15 ++++++++++++++- openeo/extra/job_management/_job_splitting.py | 4 ++++ 2 files changed, 18 insertions(+), 1 deletion(-) diff --git a/docs/cookbook/job_manager.rst b/docs/cookbook/job_manager.rst index 01b192064..b7de4f602 100644 --- a/docs/cookbook/job_manager.rst +++ b/docs/cookbook/job_manager.rst @@ -331,7 +331,8 @@ The returned GeoDataFrames keep the original index, to link back to the input ro Optionally, a ``tile_grid`` (e.g. a satellite tiling grid) can be passed so that each feature is first assigned to the tile containing its representative point -and batches are formed per tile: +and batches are formed per tile. +Features outside every tile are batched separately: .. code-block:: python @@ -360,8 +361,20 @@ Current limitations: - Geometries are never split: a polygon crossing tile edges, or larger than a tile, ends up in a single batch and extends beyond its tile. Use ``split_area`` for large areas of interest. +- Multi-part geometries (e.g. ``MultiPolygon``) are kept together as one feature, + placed by a single representative point, even if their parts are far apart. + To group parts individually, explode them into separate rows first + (the original index is kept, to link back to the input rows; + note that a single feature may then be spread over multiple batches): + + .. code-block:: python + + batches = group_geometries(points.explode(index_parts=False), max_geometries=500) + - Batches are limited by number of geometries only, not by area or processing cost. Use a tile grid to limit the spatial extent of batches. +- With overlapping tiles (e.g. Sentinel-2 MGRS), a feature is assigned to the first tile + (in grid order) containing its representative point, not to the tile with the largest overlap. Customizing Job Handling ======================== diff --git a/openeo/extra/job_management/_job_splitting.py b/openeo/extra/job_management/_job_splitting.py index 61704d205..56e31f94a 100644 --- a/openeo/extra/job_management/_job_splitting.py +++ b/openeo/extra/job_management/_job_splitting.py @@ -441,6 +441,10 @@ def group_geometries( is assigned to a single tile and extends beyond it. Use :func:`split_area` for large areas. - Groups are limited by feature count only, not by area or processing cost. Use a tile grid to limit the spatial extent of groups. + - Multi-part geometries are placed by a single representative point, + so their parts can be far away from the rest of their group. + - With overlapping tiles, a feature is assigned to the first tile (in grid order) + containing its representative point, not to the tile with the largest overlap. :param geometries: GeoDataFrame with a ``geometry`` column. Null or empty geometries are not allowed.