mockgeo 0.1.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- mockgeo-0.1.0/CHANGELOG.md +35 -0
- mockgeo-0.1.0/CITATION.cff +20 -0
- mockgeo-0.1.0/LICENSE +21 -0
- mockgeo-0.1.0/MANIFEST.in +8 -0
- mockgeo-0.1.0/PKG-INFO +367 -0
- mockgeo-0.1.0/README.md +324 -0
- mockgeo-0.1.0/pyproject.toml +133 -0
- mockgeo-0.1.0/setup.cfg +4 -0
- mockgeo-0.1.0/src/mockgeo/__init__.py +100 -0
- mockgeo-0.1.0/src/mockgeo/__main__.py +7 -0
- mockgeo-0.1.0/src/mockgeo/_rng.py +66 -0
- mockgeo-0.1.0/src/mockgeo/census.py +75 -0
- mockgeo-0.1.0/src/mockgeo/city.py +954 -0
- mockgeo-0.1.0/src/mockgeo/cli.py +312 -0
- mockgeo-0.1.0/src/mockgeo/crs.py +84 -0
- mockgeo-0.1.0/src/mockgeo/faker.py +202 -0
- mockgeo-0.1.0/src/mockgeo/formats.py +459 -0
- mockgeo-0.1.0/src/mockgeo/geometry.py +438 -0
- mockgeo-0.1.0/src/mockgeo/interop.py +50 -0
- mockgeo-0.1.0/src/mockgeo/mobility.py +513 -0
- mockgeo-0.1.0/src/mockgeo/names.py +426 -0
- mockgeo-0.1.0/src/mockgeo/network.py +227 -0
- mockgeo-0.1.0/src/mockgeo/points.py +260 -0
- mockgeo-0.1.0/src/mockgeo/py.typed +0 -0
- mockgeo-0.1.0/src/mockgeo/pytest_plugin.py +94 -0
- mockgeo-0.1.0/src/mockgeo/raster.py +378 -0
- mockgeo-0.1.0/src/mockgeo/transit.py +406 -0
- mockgeo-0.1.0/src/mockgeo.egg-info/PKG-INFO +367 -0
- mockgeo-0.1.0/src/mockgeo.egg-info/SOURCES.txt +40 -0
- mockgeo-0.1.0/src/mockgeo.egg-info/dependency_links.txt +1 -0
- mockgeo-0.1.0/src/mockgeo.egg-info/entry_points.txt +5 -0
- mockgeo-0.1.0/src/mockgeo.egg-info/requires.txt +16 -0
- mockgeo-0.1.0/src/mockgeo.egg-info/top_level.txt +1 -0
- mockgeo-0.1.0/tests/conftest.py +45 -0
- mockgeo-0.1.0/tests/test_city.py +217 -0
- mockgeo-0.1.0/tests/test_cli.py +121 -0
- mockgeo-0.1.0/tests/test_formats.py +169 -0
- mockgeo-0.1.0/tests/test_geometry.py +96 -0
- mockgeo-0.1.0/tests/test_mobility.py +118 -0
- mockgeo-0.1.0/tests/test_plugin_faker_interop.py +161 -0
- mockgeo-0.1.0/tests/test_points.py +93 -0
- mockgeo-0.1.0/tests/test_transit_raster_census.py +146 -0
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to this project will be documented in this file.
|
|
4
|
+
The format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/) and the
|
|
5
|
+
project uses [Semantic Versioning](https://semver.org/).
|
|
6
|
+
|
|
7
|
+
## [Unreleased]
|
|
8
|
+
|
|
9
|
+
## [0.1.0] - 2026-11-08
|
|
10
|
+
### Added
|
|
11
|
+
- **Points**: uniform (bbox or polygon), Thomas and Matern cluster processes,
|
|
12
|
+
points along lines and density-weighted sampling from a function or weight grid.
|
|
13
|
+
- **Synthetic cities** with `grid`, `organic` (perturbed Voronoi) and `radial` (ring
|
|
14
|
+
roads and spokes) layouts: noded street network with classes, names and speed
|
|
15
|
+
limits; blocks; recursively subdivided parcels with house numbers and addresses;
|
|
16
|
+
building footprints with use, floors, height, roof, year and population; POIs with
|
|
17
|
+
categories and names; parks and ponds. `City.validate()` re-checks ring closure,
|
|
18
|
+
orientation, simplicity, containment and network connectivity.
|
|
19
|
+
- **Mobility**: simulated GPS tracks (car, bike, walk) with speed limits, turn and
|
|
20
|
+
signal stops, correlated noise, signal gaps and outliers; trip tables; OD matrices on a
|
|
21
|
+
zone grid; a GTFS-like transit feed with a validator and reader.
|
|
22
|
+
- **Rasters**: diamond-square DEMs with hills and valleys, fractal value noise,
|
|
23
|
+
land-cover grids, Esri ASCII grid and XYZ export.
|
|
24
|
+
- **Attributes**: name, address and postcode generators for seven locales, census-like
|
|
25
|
+
block tables.
|
|
26
|
+
- **Formats**: GeoJSON, GeoJSONSeq, CSV, WKT, GPX 1.1, GTFS zip; GeoJSON and GPX
|
|
27
|
+
validators; optional NumPy, Shapely and GeoPandas bridges.
|
|
28
|
+
- `mockgeo.faker.GeoProvider` Faker provider.
|
|
29
|
+
- pytest plugin with `mockgeo_seed`, `mock_city`, `mock_city_factory`, `mock_points`
|
|
30
|
+
and `mock_tracks` fixtures.
|
|
31
|
+
- `mockgeo` command line with `city`, `points`, `tracks`, `dem`, `landcover`, `gtfs`
|
|
32
|
+
and `trips` sub-commands.
|
|
33
|
+
|
|
34
|
+
[Unreleased]: https://github.com/YusufEminoglu/mockgeo_sdk/compare/v0.1.0...HEAD
|
|
35
|
+
[0.1.0]: https://github.com/YusufEminoglu/mockgeo_sdk/releases/tag/v0.1.0
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
cff-version: 1.2.0
|
|
2
|
+
message: "If you use this software, please cite it as below."
|
|
3
|
+
authors:
|
|
4
|
+
- family-names: "Eminoğlu"
|
|
5
|
+
given-names: "Yusuf"
|
|
6
|
+
email: "yusufeminoglu@gmail.com"
|
|
7
|
+
orcid: "https://orcid.org/0009-0005-6000-2934"
|
|
8
|
+
title: "mockgeo"
|
|
9
|
+
version: "0.1.0"
|
|
10
|
+
date-released: "2026-11-08"
|
|
11
|
+
license: "MIT"
|
|
12
|
+
repository-code: "https://github.com/YusufEminoglu/mockgeo_sdk"
|
|
13
|
+
url: "https://pypi.org/project/mockgeo/"
|
|
14
|
+
abstract: "Deterministic synthetic geospatial test data: cities, street networks, buildings, GPS tracks, GTFS feeds, DEMs and fake addresses for tests, demos and benchmarks."
|
|
15
|
+
keywords:
|
|
16
|
+
- gis
|
|
17
|
+
- synthetic-data
|
|
18
|
+
- test-data
|
|
19
|
+
- geojson
|
|
20
|
+
- pytest
|
mockgeo-0.1.0/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Yusuf Eminoğlu
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
mockgeo-0.1.0/PKG-INFO
ADDED
|
@@ -0,0 +1,367 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: mockgeo
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Deterministic synthetic geospatial test data: cities, street networks, buildings, GPS tracks, GTFS feeds, DEMs and fake addresses for tests, demos and benchmarks.
|
|
5
|
+
Author-email: Yusuf Eminoğlu <yusufeminoglu@gmail.com>
|
|
6
|
+
License-Expression: MIT
|
|
7
|
+
Project-URL: Homepage, https://github.com/YusufEminoglu/mockgeo_sdk
|
|
8
|
+
Project-URL: Repository, https://github.com/YusufEminoglu/mockgeo_sdk
|
|
9
|
+
Project-URL: Issues, https://github.com/YusufEminoglu/mockgeo_sdk/issues
|
|
10
|
+
Project-URL: Changelog, https://github.com/YusufEminoglu/mockgeo_sdk/blob/main/CHANGELOG.md
|
|
11
|
+
Keywords: gis,geospatial,synthetic-data,test-data,mock,fixtures,pytest,faker,geojson,gpx,gtfs,dem,point-process,street-network,procedural-generation
|
|
12
|
+
Classifier: Development Status :: 4 - Beta
|
|
13
|
+
Classifier: Framework :: Pytest
|
|
14
|
+
Classifier: Programming Language :: Python :: 3
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
20
|
+
Classifier: Operating System :: OS Independent
|
|
21
|
+
Classifier: Topic :: Scientific/Engineering :: GIS
|
|
22
|
+
Classifier: Topic :: Software Development :: Testing
|
|
23
|
+
Classifier: Topic :: Software Development :: Testing :: Mocking
|
|
24
|
+
Classifier: Intended Audience :: Developers
|
|
25
|
+
Classifier: Intended Audience :: Science/Research
|
|
26
|
+
Classifier: Typing :: Typed
|
|
27
|
+
Requires-Python: >=3.9
|
|
28
|
+
Description-Content-Type: text/markdown
|
|
29
|
+
License-File: LICENSE
|
|
30
|
+
Provides-Extra: numpy
|
|
31
|
+
Requires-Dist: numpy>=1.20.0; extra == "numpy"
|
|
32
|
+
Provides-Extra: geo
|
|
33
|
+
Requires-Dist: shapely>=2.0.0; extra == "geo"
|
|
34
|
+
Requires-Dist: geopandas>=0.12.0; extra == "geo"
|
|
35
|
+
Provides-Extra: faker
|
|
36
|
+
Requires-Dist: Faker>=18.0.0; extra == "faker"
|
|
37
|
+
Provides-Extra: dev
|
|
38
|
+
Requires-Dist: pytest>=7.0.0; extra == "dev"
|
|
39
|
+
Requires-Dist: pytest-cov>=4.0.0; extra == "dev"
|
|
40
|
+
Requires-Dist: ruff>=0.1.0; extra == "dev"
|
|
41
|
+
Requires-Dist: mypy>=1.0.0; extra == "dev"
|
|
42
|
+
Dynamic: license-file
|
|
43
|
+
|
|
44
|
+
# mockgeo
|
|
45
|
+
|
|
46
|
+
[](https://pypi.org/project/mockgeo/)
|
|
47
|
+
[](https://pypi.org/project/mockgeo/)
|
|
48
|
+
[](https://github.com/YusufEminoglu/mockgeo_sdk/blob/main/LICENSE)
|
|
49
|
+
[](https://docs.astral.sh/ruff/)
|
|
50
|
+
|
|
51
|
+
**Deterministic synthetic geospatial test data for unit tests, demos, tutorials and benchmarks.**
|
|
52
|
+
|
|
53
|
+
`mockgeo` builds a believable small city from a seed: a noded street network, blocks,
|
|
54
|
+
parcels, building footprints with heights and uses, named POIs, parks and ponds. On top of
|
|
55
|
+
it you get simulated GPS tracks, trip and OD tables, a GTFS-like transit feed, a census
|
|
56
|
+
table, fractal DEMs and land-cover grids. Everything is topologically valid, reproducible
|
|
57
|
+
byte-for-byte, and written in pure Python with **no required dependencies**.
|
|
58
|
+
|
|
59
|
+
```bash
|
|
60
|
+
pip install mockgeo
|
|
61
|
+
mockgeo city --seed 1 --size 2km -o city.geojson
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
- **Zero dependencies.** The core uses only the standard library, so it installs fast in
|
|
65
|
+
any CI job. NumPy, Shapely/GeoPandas and Faker are optional extras.
|
|
66
|
+
- **Deterministic.** The same seed and arguments give identical bytes, independent of
|
|
67
|
+
`PYTHONHASHSEED` and of the order in which you call generators.
|
|
68
|
+
- **Valid by construction, and checked.** Rings are closed and counter-clockwise, no ring
|
|
69
|
+
self-intersects, parcels lie inside blocks and buildings inside parcels, POIs inside
|
|
70
|
+
buildings, GPS ground truth on the streets. `City.validate()` checks all of it again.
|
|
71
|
+
- **Batteries for testing.** A pytest plugin with ready fixtures (`mock_city`,
|
|
72
|
+
`mock_points`, `mock_tracks`), a Faker provider, and validators for GeoJSON, GPX and
|
|
73
|
+
GTFS that you can reuse in your own tests.
|
|
74
|
+
- **Obviously fake.** Cities are centred in the open North Atlantic (`-35.0, 42.0`) by
|
|
75
|
+
default and names are recombined from small built-in word lists, so mock data cannot be
|
|
76
|
+
mistaken for a real place, business or person. Pass `origin=(lon, lat)` to move it.
|
|
77
|
+
|
|
78
|
+
## Installation
|
|
79
|
+
|
|
80
|
+
```bash
|
|
81
|
+
pip install mockgeo # standard library only
|
|
82
|
+
pip install "mockgeo[numpy]" # Raster.to_numpy(), interop.to_numpy()
|
|
83
|
+
pip install "mockgeo[geo]" # Shapely / GeoPandas bridges
|
|
84
|
+
pip install "mockgeo[faker]" # mockgeo.faker.GeoProvider
|
|
85
|
+
pip install "mockgeo[dev]" # pytest, pytest-cov, ruff, mypy
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
Python 3.9 to 3.13 on Windows, macOS and Linux.
|
|
89
|
+
|
|
90
|
+
## Quickstart
|
|
91
|
+
|
|
92
|
+
```python
|
|
93
|
+
import mockgeo
|
|
94
|
+
|
|
95
|
+
city = mockgeo.generate_city(seed=1, size="2km", layout="organic", locale="en_GB")
|
|
96
|
+
print(city.name, city.summary())
|
|
97
|
+
assert city.validate() == [] # topology re-checked
|
|
98
|
+
|
|
99
|
+
city.to_geojson("city.geojson") # all layers, WGS84 lon/lat (RFC 7946)
|
|
100
|
+
buildings = city.features(["buildings"]) # list of GeoJSON-like dicts
|
|
101
|
+
local = city.features(["parcels"], crs="local") # metres from the city centre
|
|
102
|
+
|
|
103
|
+
tracks = mockgeo.gps_tracks(city, n=5, seed=1, mode="bike", noise=4.0)
|
|
104
|
+
open("tracks.gpx", "w", encoding="utf-8").write(mockgeo.dumps_gpx(tracks))
|
|
105
|
+
|
|
106
|
+
feed = mockgeo.transit_feed(city, n_routes=4, seed=1)
|
|
107
|
+
assert mockgeo.validate_gtfs(feed) == []
|
|
108
|
+
feed.write("feed.zip")
|
|
109
|
+
|
|
110
|
+
dem = mockgeo.dem(257, cell_size=10, seed=1, hills=3, valley=True)
|
|
111
|
+
dem.to_ascii_grid("dem.asc") # opens in QGIS / GDAL / rasterio
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
## What you can generate
|
|
115
|
+
|
|
116
|
+
### Points
|
|
117
|
+
|
|
118
|
+
| Function | Pattern |
|
|
119
|
+
| --- | --- |
|
|
120
|
+
| `uniform_points(n, bbox=..., polygon=...)` | Complete spatial randomness in a box or any polygon |
|
|
121
|
+
| `thomas_points(n_clusters, mean_size, sigma, ...)` | Thomas cluster process (Gaussian offspring) |
|
|
122
|
+
| `matern_points(n_clusters, mean_size, radius, ...)` | Matern cluster process (uniform disc offspring) |
|
|
123
|
+
| `points_along_line(line, n= / spacing=, jitter=)` | Even, random or jittered positions along a polyline |
|
|
124
|
+
| `density_weighted_points(n, density, bbox)` | Intensity from a function `f(x, y)` or a 2D weight grid |
|
|
125
|
+
|
|
126
|
+
Clustered processes simulate parents in an enlarged window to avoid edge effects, and
|
|
127
|
+
accept `n=` to return exactly that many points. `points_to_features()` wraps any of them
|
|
128
|
+
as GeoJSON features.
|
|
129
|
+
|
|
130
|
+
### Synthetic city
|
|
131
|
+
|
|
132
|
+
`generate_city(seed, size, layout, locale, origin, block_size, irregularity, name)`
|
|
133
|
+
|
|
134
|
+
| Layout | Street pattern |
|
|
135
|
+
| --- | --- |
|
|
136
|
+
| `grid` | Manhattan grid with arterials every few blocks; `irregularity` jitters the nodes |
|
|
137
|
+
| `organic` | Voronoi diagram of a perturbed lattice, irregular junctions, perimeter road |
|
|
138
|
+
| `radial` | Concentric ring roads and spokes; spokes double outwards to keep blocks small |
|
|
139
|
+
|
|
140
|
+
Layers (`city.layers[...]`, or `city.features(layers)` for export copies):
|
|
141
|
+
|
|
142
|
+
| Layer | Geometry | Main attributes |
|
|
143
|
+
| --- | --- | --- |
|
|
144
|
+
| `streets` | LineString per segment | `name`, `highway` (primary / secondary / tertiary / residential), `maxspeed`, `lanes`, `width`, `way_id` |
|
|
145
|
+
| `intersections` | Point | `degree`, `traffic_signals` |
|
|
146
|
+
| `blocks` | Polygon | `land_use`, `zone` (downtown / inner / outer), `postcode`, `area_m2` |
|
|
147
|
+
| `parcels` | Polygon | `block_id`, `street`, `housenumber`, `address` |
|
|
148
|
+
| `buildings` | Polygon | `use`, `floors`, `height`, `roof`, `year_built`, `population`, `address` |
|
|
149
|
+
| `pois` | Point | `category`, `name`, `address`, `opening_hours`, `within` |
|
|
150
|
+
| `parks` | Polygon | `name`, `area_m2` |
|
|
151
|
+
| `water` | Polygon | `park_id`, `water` = pond |
|
|
152
|
+
|
|
153
|
+
How it stays valid: every street face is convex; blocks are faces shrunk by half the
|
|
154
|
+
right-of-way of each bounding street; parcels come from recursive splits of the block
|
|
155
|
+
across its longest edge (convex pieces of a convex polygon); buildings are set back and
|
|
156
|
+
scaled inside their parcel, with some L shapes that are validated before use. House
|
|
157
|
+
numbers run odd on the left and even on the right, increasing along each named street.
|
|
158
|
+
|
|
159
|
+
A building as exported (`city.features(["buildings"])[0]`, coordinates shortened):
|
|
160
|
+
|
|
161
|
+
```json
|
|
162
|
+
{
|
|
163
|
+
"type": "Feature",
|
|
164
|
+
"geometry": {"type": "Polygon", "coordinates": [[[-35.0118464, 41.9912307],
|
|
165
|
+
[-35.0117906, 41.9912307], [-35.0117906, 41.991193], [-35.0116767, 41.991193],
|
|
166
|
+
[-35.0116767, 41.9913104], [-35.0118464, 41.9913104], [-35.0118464, 41.9912307]]]},
|
|
167
|
+
"properties": {
|
|
168
|
+
"layer": "buildings", "id": "bldg-0", "parcel_id": "parcel-0", "block_id": "block-0",
|
|
169
|
+
"use": "residential", "floors": 2, "height": 8.7, "roof": "gabled", "year_built": 1958,
|
|
170
|
+
"area_m2": 163.7, "population": 8, "street": "Pine Avenue", "housenumber": 1,
|
|
171
|
+
"postcode": "37996", "address": "1 Pine Avenue, Fairdale 37996"
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
A POI and a street segment:
|
|
177
|
+
|
|
178
|
+
```json
|
|
179
|
+
{"type": "Feature", "geometry": {"type": "Point", "coordinates": [-35.0073403, 41.9915604]},
|
|
180
|
+
"properties": {"layer": "pois", "id": "poi-0", "category": "restaurant",
|
|
181
|
+
"name": "The Highland Kitchen", "within": "bldg-20",
|
|
182
|
+
"address": "4 Lake Terrace, Fairdale 37996", "opening_hours": "Mo-Fr 09:00-17:00; Sa 10:00-14:00"}}
|
|
183
|
+
|
|
184
|
+
{"type": "Feature", "geometry": {"type": "LineString",
|
|
185
|
+
"coordinates": [[-35.0121015, 41.9910068], [-35.0105889, 41.9910068]]},
|
|
186
|
+
"properties": {"layer": "streets", "id": "street-0", "name": "Pine Avenue", "highway": "primary",
|
|
187
|
+
"maxspeed": 50, "lanes": 4, "width": 24.0, "length_m": 125.0, "way_id": 0,
|
|
188
|
+
"from_node": 0, "to_node": 1}}
|
|
189
|
+
```
|
|
190
|
+
|
|
191
|
+
`city.network` is a `StreetNetwork` with `shortest_path(u, v, weight="length"|"time")`,
|
|
192
|
+
`nearest_node`, `nearest_street`, `distance_to_network` and `is_connected`.
|
|
193
|
+
|
|
194
|
+
### Mobility
|
|
195
|
+
|
|
196
|
+
- `gps_tracks(city, n, seed, mode="car"|"bike"|"walk", interval, noise, stops, gaps, outlier_rate)`
|
|
197
|
+
simulates driving rather than interpolating: vehicles accelerate to the street's speed
|
|
198
|
+
limit, slow for sharp turns, stop at red lights and in short congestion, and arrive at
|
|
199
|
+
rest. The receiver adds AR(1)-correlated noise, can lose the signal for 15 to 90 s (the
|
|
200
|
+
track is split into segments) and can emit multipath outliers. Each `TrackPoint` keeps
|
|
201
|
+
`true_x`/`true_y`, which lie exactly on the network, so you can score map matching.
|
|
202
|
+
- `generate_trips(city, n)` gives person trips from homes (weighted by population) to
|
|
203
|
+
activity places (weighted by floor area) with purpose, mode, times and network distance.
|
|
204
|
+
- `od_matrix(city, trips, k)` aggregates trips to a `k x k` zone grid (`zones(city, k)`).
|
|
205
|
+
- `transit_feed(city, n_routes, headway_min)` writes `agency`, `stops`, `routes`, `trips`,
|
|
206
|
+
`stop_times`, `calendar`, `shapes` and `feed_info`; `validate_gtfs()` checks required
|
|
207
|
+
columns, unique ids, references, increasing sequences and times. Zips are
|
|
208
|
+
byte-reproducible.
|
|
209
|
+
|
|
210
|
+
Track rows (`track.rows()`, as written by `mockgeo tracks -o tracks.csv`):
|
|
211
|
+
|
|
212
|
+
```text
|
|
213
|
+
track_id,seq,time,lon,lat,speed_ms,segment,outlier
|
|
214
|
+
track-0,0,2026-01-05T08:46:08Z,-35.00757,42.0082671,0.0,0,0
|
|
215
|
+
track-0,1,2026-01-05T08:46:09Z,-35.0075445,42.0082774,1.8,0,0
|
|
216
|
+
track-0,2,2026-01-05T08:46:10Z,-35.0075311,42.0082745,3.6,0,0
|
|
217
|
+
```
|
|
218
|
+
|
|
219
|
+
### Rasters
|
|
220
|
+
|
|
221
|
+
- `dem(size, cell_size, seed, roughness, relief, base, hills, valley)`: diamond-square
|
|
222
|
+
fractal terrain, optional Gaussian hills/basins and a meandering river valley.
|
|
223
|
+
- `fractal_noise(size, seed, octaves, persistence, scale)`: value-noise fBm in `[0, 1]`.
|
|
224
|
+
- `landcover(size, seed, elevation=None)`: water, wetland, grassland, cropland, forest,
|
|
225
|
+
urban and bare rock (`LANDCOVER_CLASSES`) driven by elevation and moisture noise.
|
|
226
|
+
- `Raster` offers `sample(x, y)` (bilinear), `stats()`, `to_ascii_grid()` (Esri ASCII),
|
|
227
|
+
`to_xyz()` and `to_numpy()`.
|
|
228
|
+
|
|
229
|
+
### Attributes and names
|
|
230
|
+
|
|
231
|
+
`NameGenerator(locale)` builds street, city, park, person and POI names, postcodes and
|
|
232
|
+
addresses in the local order for `en_US`, `en_GB`, `de_DE`, `fr_FR`, `es_ES`, `it_IT` and
|
|
233
|
+
`tr_TR`. For example, `tr_TR` gives `Bahar Eczanesi | Defne Sokak No:4, 37095 Yeşilköy`.
|
|
234
|
+
`census_blocks(city)` returns block polygons with population (consistent with the
|
|
235
|
+
buildings), households, three age bands, jobs and density.
|
|
236
|
+
|
|
237
|
+
## Output formats
|
|
238
|
+
|
|
239
|
+
| Format | Functions | CLI extension |
|
|
240
|
+
| --- | --- | --- |
|
|
241
|
+
| GeoJSON (RFC 7946) | `dumps_geojson`, `write_geojson`, `City.to_geojson` | `.geojson`, `.json` |
|
|
242
|
+
| GeoJSONSeq | `dumps_geojsonseq(features, rs=False)` (`rs=True` for RFC 8142) | `.geojsonl`, `.ndjson` |
|
|
243
|
+
| CSV | `dumps_csv` (x/y for points, WKT otherwise; plain rows too) | `.csv` |
|
|
244
|
+
| WKT | `to_wkt`, `dumps_wkt` | `.wkt` |
|
|
245
|
+
| GPX 1.1 | `dumps_gpx(tracks, waypoints=pois)` | `.gpx` |
|
|
246
|
+
| GTFS | `TransitFeed.write("feed.zip")` or a directory | `mockgeo gtfs` |
|
|
247
|
+
| Esri ASCII grid / XYZ | `Raster.to_ascii_grid`, `Raster.to_xyz` | `.asc`, `.xyz` |
|
|
248
|
+
| GeoPandas / Shapely | `interop.to_geodataframe`, `interop.city_to_geodataframes`, `interop.to_shapely` | |
|
|
249
|
+
|
|
250
|
+
`validate_geojson()` and `validate_gpx()` return a list of problems, so they double as
|
|
251
|
+
assertions in your own tests.
|
|
252
|
+
|
|
253
|
+
## pytest plugin
|
|
254
|
+
|
|
255
|
+
Installing mockgeo registers a pytest plugin (`pytest11` entry point):
|
|
256
|
+
|
|
257
|
+
```python
|
|
258
|
+
def test_my_spatial_join(mock_city, mock_points):
|
|
259
|
+
buildings = mock_city.features(["buildings"])
|
|
260
|
+
assert my_join(buildings, mock_points)
|
|
261
|
+
|
|
262
|
+
def test_my_map_matcher(mock_tracks, mock_city):
|
|
263
|
+
for track in mock_tracks:
|
|
264
|
+
matched = my_match(track, mock_city.network)
|
|
265
|
+
...
|
|
266
|
+
|
|
267
|
+
def test_bigger(mock_city_factory):
|
|
268
|
+
city = mock_city_factory(seed=3, size="1500m", layout="radial")
|
|
269
|
+
```
|
|
270
|
+
|
|
271
|
+
| Fixture | Value |
|
|
272
|
+
| --- | --- |
|
|
273
|
+
| `mockgeo_seed` | 42, or `--mockgeo-seed`, or `mockgeo_seed = ...` in the ini file |
|
|
274
|
+
| `mock_city` | 600 m grid `City` |
|
|
275
|
+
| `mock_city_factory` | callable taking `generate_city` arguments (cached per arguments) |
|
|
276
|
+
| `mock_points` | 200 uniform lon/lat Point features over the city |
|
|
277
|
+
| `mock_tracks` | 3 simulated car tracks on `mock_city` |
|
|
278
|
+
|
|
279
|
+
Fixtures hand out deep copies of cached objects, so tests can mutate them safely. Disable
|
|
280
|
+
the plugin with `-p no:mockgeo`.
|
|
281
|
+
|
|
282
|
+
## Faker provider
|
|
283
|
+
|
|
284
|
+
```python
|
|
285
|
+
from faker import Faker
|
|
286
|
+
from mockgeo.faker import GeoProvider
|
|
287
|
+
|
|
288
|
+
fake = Faker()
|
|
289
|
+
fake.add_provider(GeoProvider)
|
|
290
|
+
Faker.seed(42)
|
|
291
|
+
|
|
292
|
+
fake.geo_point() # {"type": "Point", "coordinates": [lon, lat]}
|
|
293
|
+
fake.geo_polygon(n_vertices=7, radius_m=80) # simple, closed, CCW
|
|
294
|
+
fake.building_footprint() # Feature with floors, height, use
|
|
295
|
+
fake.geojson_feature_collection(10, "LineString")
|
|
296
|
+
fake.wkt_polygon() # "POLYGON ((...))"
|
|
297
|
+
fake.mock_address("de_DE") # e.g. "Lindenweg 12, 48151 Neuburg"
|
|
298
|
+
fake.poi("fr_FR") # Feature with category, name, address
|
|
299
|
+
```
|
|
300
|
+
|
|
301
|
+
## Command line
|
|
302
|
+
|
|
303
|
+
```text
|
|
304
|
+
$ mockgeo city --seed 1 --size 2km -o city.geojson --summary
|
|
305
|
+
Fairdale: 8286 features -> city.geojson
|
|
306
|
+
{
|
|
307
|
+
"streets": 808,
|
|
308
|
+
"intersections": 425,
|
|
309
|
+
"blocks": 384,
|
|
310
|
+
"parcels": 3175,
|
|
311
|
+
"buildings": 2990,
|
|
312
|
+
"pois": 462,
|
|
313
|
+
"parks": 29,
|
|
314
|
+
"water": 13,
|
|
315
|
+
"street_length_km": 84.0,
|
|
316
|
+
"population": 49097
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
$ mockgeo points --n 1000 --bbox 29.0,41.0,29.1,41.1 --process thomas -o pts.csv
|
|
320
|
+
1000 points -> pts.csv
|
|
321
|
+
|
|
322
|
+
$ mockgeo tracks --seed 1 --n 5 --mode bike -o bike.gpx
|
|
323
|
+
5 tracks, 694 fixes -> bike.gpx
|
|
324
|
+
|
|
325
|
+
$ mockgeo dem --size 513 --hills 3 --valley -o dem.asc
|
|
326
|
+
DEM 513x513, 0.5-304.5 m -> dem.asc
|
|
327
|
+
|
|
328
|
+
$ mockgeo gtfs --seed 1 --size 2km -o feed.zip
|
|
329
|
+
4 routes, 25 stops, 776 trips -> feed.zip
|
|
330
|
+
|
|
331
|
+
$ mockgeo trips --size 2km --n 500 --od --zones 3
|
|
332
|
+
origin,destination,trips,mean_distance_m
|
|
333
|
+
Z00,Z01,1,750.0
|
|
334
|
+
Z00,Z02,3,1597.2
|
|
335
|
+
...
|
|
336
|
+
```
|
|
337
|
+
|
|
338
|
+
Sub-commands: `city`, `points`, `tracks`, `dem`, `landcover`, `gtfs`, `trips`. Without
|
|
339
|
+
`-o` the result goes to stdout (UTF-8), the summary line to stderr. Run
|
|
340
|
+
`mockgeo <command> --help` for every option; `python -m mockgeo` works too.
|
|
341
|
+
|
|
342
|
+
## Determinism
|
|
343
|
+
|
|
344
|
+
Every generator draws from `random.Random` streams seeded with a SHA-512 hash of
|
|
345
|
+
`(seed, generator name)`. Seeds may be integers or strings. Output is identical across
|
|
346
|
+
runs and machines with the same Python version; coordinates are rounded on export
|
|
347
|
+
(7 decimals for lon/lat, 3 for metres) so last-bit floating point differences between
|
|
348
|
+
platforms do not show up. Pass `seed=None` for fresh randomness.
|
|
349
|
+
|
|
350
|
+
## Development
|
|
351
|
+
|
|
352
|
+
```bash
|
|
353
|
+
git clone https://github.com/YusufEminoglu/mockgeo_sdk.git
|
|
354
|
+
cd mockgeo_sdk
|
|
355
|
+
pip install -e ".[dev,numpy,geo,faker]"
|
|
356
|
+
python -m pytest --cov=mockgeo
|
|
357
|
+
python -m ruff check . && python -m ruff format --check src tests
|
|
358
|
+
python -m mypy src
|
|
359
|
+
```
|
|
360
|
+
|
|
361
|
+
## Citation
|
|
362
|
+
|
|
363
|
+
See [`CITATION.cff`](CITATION.cff).
|
|
364
|
+
|
|
365
|
+
## License
|
|
366
|
+
|
|
367
|
+
MIT, see [LICENSE](LICENSE).
|