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.
Files changed (42) hide show
  1. mockgeo-0.1.0/CHANGELOG.md +35 -0
  2. mockgeo-0.1.0/CITATION.cff +20 -0
  3. mockgeo-0.1.0/LICENSE +21 -0
  4. mockgeo-0.1.0/MANIFEST.in +8 -0
  5. mockgeo-0.1.0/PKG-INFO +367 -0
  6. mockgeo-0.1.0/README.md +324 -0
  7. mockgeo-0.1.0/pyproject.toml +133 -0
  8. mockgeo-0.1.0/setup.cfg +4 -0
  9. mockgeo-0.1.0/src/mockgeo/__init__.py +100 -0
  10. mockgeo-0.1.0/src/mockgeo/__main__.py +7 -0
  11. mockgeo-0.1.0/src/mockgeo/_rng.py +66 -0
  12. mockgeo-0.1.0/src/mockgeo/census.py +75 -0
  13. mockgeo-0.1.0/src/mockgeo/city.py +954 -0
  14. mockgeo-0.1.0/src/mockgeo/cli.py +312 -0
  15. mockgeo-0.1.0/src/mockgeo/crs.py +84 -0
  16. mockgeo-0.1.0/src/mockgeo/faker.py +202 -0
  17. mockgeo-0.1.0/src/mockgeo/formats.py +459 -0
  18. mockgeo-0.1.0/src/mockgeo/geometry.py +438 -0
  19. mockgeo-0.1.0/src/mockgeo/interop.py +50 -0
  20. mockgeo-0.1.0/src/mockgeo/mobility.py +513 -0
  21. mockgeo-0.1.0/src/mockgeo/names.py +426 -0
  22. mockgeo-0.1.0/src/mockgeo/network.py +227 -0
  23. mockgeo-0.1.0/src/mockgeo/points.py +260 -0
  24. mockgeo-0.1.0/src/mockgeo/py.typed +0 -0
  25. mockgeo-0.1.0/src/mockgeo/pytest_plugin.py +94 -0
  26. mockgeo-0.1.0/src/mockgeo/raster.py +378 -0
  27. mockgeo-0.1.0/src/mockgeo/transit.py +406 -0
  28. mockgeo-0.1.0/src/mockgeo.egg-info/PKG-INFO +367 -0
  29. mockgeo-0.1.0/src/mockgeo.egg-info/SOURCES.txt +40 -0
  30. mockgeo-0.1.0/src/mockgeo.egg-info/dependency_links.txt +1 -0
  31. mockgeo-0.1.0/src/mockgeo.egg-info/entry_points.txt +5 -0
  32. mockgeo-0.1.0/src/mockgeo.egg-info/requires.txt +16 -0
  33. mockgeo-0.1.0/src/mockgeo.egg-info/top_level.txt +1 -0
  34. mockgeo-0.1.0/tests/conftest.py +45 -0
  35. mockgeo-0.1.0/tests/test_city.py +217 -0
  36. mockgeo-0.1.0/tests/test_cli.py +121 -0
  37. mockgeo-0.1.0/tests/test_formats.py +169 -0
  38. mockgeo-0.1.0/tests/test_geometry.py +96 -0
  39. mockgeo-0.1.0/tests/test_mobility.py +118 -0
  40. mockgeo-0.1.0/tests/test_plugin_faker_interop.py +161 -0
  41. mockgeo-0.1.0/tests/test_points.py +93 -0
  42. 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.
@@ -0,0 +1,8 @@
1
+ include LICENSE
2
+ include README.md
3
+ include CHANGELOG.md
4
+ include CITATION.cff
5
+ include pyproject.toml
6
+ include src/mockgeo/py.typed
7
+ recursive-include src/mockgeo *.py
8
+ recursive-include tests *.py
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
+ [![PyPI version](https://img.shields.io/pypi/v/mockgeo.svg?color=10b981)](https://pypi.org/project/mockgeo/)
47
+ [![Python version support](https://img.shields.io/pypi/pyversions/mockgeo.svg?color=3b82f6)](https://pypi.org/project/mockgeo/)
48
+ [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](https://github.com/YusufEminoglu/mockgeo_sdk/blob/main/LICENSE)
49
+ [![Code style: Ruff](https://img.shields.io/badge/code%20style-ruff-D7FF64.svg)](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).