diff --git a/docs/python-api.rst b/docs/python-api.rst index 8ede326..7e91051 100644 --- a/docs/python-api.rst +++ b/docs/python-api.rst @@ -291,9 +291,9 @@ This method also accepts ``offset=`` and ``limit=`` arguments, for specifying an Counting rows ------------- -To count the number of rows that would be returned by a where filter, use ``.count_where(where, where_args)``: +To count the number of rows that would be returned by a where filter, use ``.count_where(where, where_args)`` : - >>> db["dogs"].count_where("age > ?", [1]): + >>> db["dogs"].count_where("age > ?", [1]) 2 .. _python_api_pks_and_rows_where: @@ -1246,7 +1246,7 @@ To change the primary key for a table, use ``pk=``. This can be passed a single Changing not null status ------------------------ -You can change the ``NOT NULL`` status of columns by using ``not_null=``. You can pass this a set of columns to make those columns ``NOT NULL``: +You can change the ``NOT NULL`` status of columns by using ``not_null=``. You can pass this a set of columns to make those columns ``NOT NULL`` : .. code-block:: python @@ -2340,26 +2340,6 @@ For example: # [thumbnail] BLOB # ) -.. _find_spatialite: - -Finding SpatiaLite -================== - -The ``find_spatialite()`` function searches for the `SpatiaLite `__ SQLite extension in some common places. It returns a string path to the location, or ``None`` if SpatiaLite was not found. - -You can use it in code like this: - -.. code-block:: python - - from sqlite_utils import Database - from sqlite_utils.gis import find_spatialite - - db = Database("mydb.db") - spatialite = find_spatialite() - if spatialite: - db.conn.enable_load_extension(True) - db.conn.load_extension(spatialite) - .. _python_api_register_function: Registering custom SQL functions @@ -2440,3 +2420,40 @@ If that option isn't relevant to your use-case you can to quote a string for use "'hello'" >>> db.quote("hello'this'has'quotes") "'hello''this''has''quotes'" + +Spatialite helpers +================== + +`SpatiaLite `__ is a geographic extension to SQLite (similar to PostgreSQL + PostGIS). Using requires finding, loading and initializing the extension, adding geometry columns to existing tables and optionally creating spatial indexes. The utilities here help streamline that setup. + + + +.. _init_spatialite: + +Initialize Spatialite +----------------------- + +.. autofunction:: sqlite_utils.gis.init_spatialite + + +.. _find_spatialite: + +Finding Spatialite +----------------- + +.. autofunction:: sqlite_utils.gis.find_spatialite + + +.. _add_geometry_column: + +Adding geometry columns +----------------------- + +.. autofunction:: sqlite_utils.gis.add_geometry_column + +.. _create_spatial_index: + +Creating a spatial index +------------------------ + +.. autofunction:: sqlite_utils.gis.create_spatial_index diff --git a/sqlite_utils/gis.py b/sqlite_utils/gis.py index 3373af2..4881913 100644 --- a/sqlite_utils/gis.py +++ b/sqlite_utils/gis.py @@ -8,6 +8,22 @@ SPATIALITE_PATHS = ( def find_spatialite() -> str: + """ + The ``find_spatialite()`` function searches for the `SpatiaLite `__ SQLite extension in some common places. It returns a string path to the location, or ``None`` if SpatiaLite was not found. + + You can use it in code like this: + + .. code-block:: python + + from sqlite_utils import Database + from sqlite_utils.gis import find_spatialite + + db = Database("mydb.db") + spatialite = find_spatialite() + if spatialite: + db.conn.enable_load_extension(True) + db.conn.load_extension(spatialite) + """ for path in SPATIALITE_PATHS: if os.path.exists(path): return path @@ -15,7 +31,31 @@ def find_spatialite() -> str: def init_spatialite(db: Database, path: str) -> None: - "Load spatialite extension for a database" + """ + The ``init_spatialite`` function will load and initalize the Spatialite extension. + The ``path`` argument should be an absolute path to the compiled extension, which + can be found using ``find_spatialite``. + + .. code-block:: python + + from sqlite_utils.gis import find_spatialite, init_spatialite + + db = Database("mydb.db") + init_spatialite(db, find_spatialite()) + + If you've installed Spatialite somewhere unexpected (for testing an alternate version, for example) + you can pass in an absolute path: + + .. code-block:: python + + .. code-block:: python + + from sqlite_utils.gis import init_spatialite + + db = Database("mydb.db") + init_spatialite(db, "./local/mod_spatialite.dylib") + + """ db.conn.enable_load_extension(True) db.conn.load_extension(path) # Initialize SpatiaLite if not yet initialized @@ -32,7 +72,27 @@ def add_geometry_column( coord_dimension: str = "XY", not_null: bool = False, ) -> None: - "Add a geometry column to a table" + """ + In Spatialite, a geometry column can only be added to an existing table. + To do so, use ``add_geometry_column``, passing in a :ref:`table ` + and geometry type. + + By default, this will add a nullable column called ``geometry`` using + `SRID 4326 `__. These can be customized using + the ``column_name`` and ``srid`` arguments. + + .. code-block:: python + + from sqlite_utils.gis import find_spatialite, init_spatialite, add_geometry_column + + db = Database("mydb.db") + init_spatialite(db, find_spatialite()) + + # the table must exist before adding a geometry column + db["locations"].create({"name": str}) + add_geometry_column(db["locations"], "POINT") + + """ table.db.execute( "SELECT AddGeometryColumn(?, ?, ?, ?, ?, ?);", [table.name, column_name, srid, geometry_type, coord_dimension, int(not_null)], @@ -40,5 +100,26 @@ def add_geometry_column( def create_spatial_index(table: Table, column_name: str = "geometry") -> None: - "Create a spatial index for a table and column" + """ + A spatial index allows for significantly faster bounding box queries. + To create on, use ``create_spatial_index`` with a :ref:`table ` + and the name of an existing geometry column. + + .. code-block:: python + + from sqlite_utils.gis import add_geometry_column, create_spatial_index + + # assuming Spatialite is loaded, create the table, add the column + db["locations"].create({"name": str}) + add_geometry_column(db["locations"], "POINT", "geometry") + + # now we can index it + create_spatial_index(db["locations"], "geometry") + + # the spatial index is a virtual table, which we can inspect + print(db["idx_locations_geometry"].schema) + # outputs: + # CREATE VIRTUAL TABLE "idx_locations_geometry" USING rtree(pkid, xmin, xmax, ymin, ymax) + + """ table.db.execute("select CreateSpatialIndex(?, ?)", [table.name, column_name])