dotted-map 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 (34) hide show
  1. dotted_map-0.1.0/.github/workflows/release.yml +24 -0
  2. dotted_map-0.1.0/.github/workflows/tests.yml +38 -0
  3. dotted_map-0.1.0/.gitignore +13 -0
  4. dotted_map-0.1.0/.readthedocs.yaml +19 -0
  5. dotted_map-0.1.0/CHANGELOG.md +9 -0
  6. dotted_map-0.1.0/CITATION.cff +41 -0
  7. dotted_map-0.1.0/LICENSE +28 -0
  8. dotted_map-0.1.0/PKG-INFO +85 -0
  9. dotted_map-0.1.0/README.md +60 -0
  10. dotted_map-0.1.0/docs/_static/custom.css +6 -0
  11. dotted_map-0.1.0/docs/_static/world.png +0 -0
  12. dotted_map-0.1.0/docs/api.rst +38 -0
  13. dotted_map-0.1.0/docs/conf.py +147 -0
  14. dotted_map-0.1.0/docs/guide.rst +342 -0
  15. dotted_map-0.1.0/docs/implementation.rst +99 -0
  16. dotted_map-0.1.0/docs/index.rst +62 -0
  17. dotted_map-0.1.0/docs/regions.rst +33 -0
  18. dotted_map-0.1.0/pyproject.toml +63 -0
  19. dotted_map-0.1.0/src/dotted_map/__init__.py +26 -0
  20. dotted_map-0.1.0/src/dotted_map/__main__.py +5 -0
  21. dotted_map-0.1.0/src/dotted_map/cli.py +274 -0
  22. dotted_map-0.1.0/src/dotted_map/core.py +589 -0
  23. dotted_map-0.1.0/src/dotted_map/data/countries.geojson.gz +0 -0
  24. dotted_map-0.1.0/src/dotted_map/data/land.geojson.gz +0 -0
  25. dotted_map-0.1.0/src/dotted_map/export.py +50 -0
  26. dotted_map-0.1.0/src/dotted_map/py.typed +0 -0
  27. dotted_map-0.1.0/src/dotted_map/regions.py +159 -0
  28. dotted_map-0.1.0/tests/test_cli.py +124 -0
  29. dotted_map-0.1.0/tests/test_export.py +68 -0
  30. dotted_map-0.1.0/tests/test_map.py +255 -0
  31. dotted_map-0.1.0/tests/test_regions.py +62 -0
  32. dotted_map-0.1.0/tools/make_data.py +80 -0
  33. dotted_map-0.1.0/tools/showcase.py +38 -0
  34. dotted_map-0.1.0/uv.lock +1730 -0
@@ -0,0 +1,24 @@
1
+ # Publishes a release to PyPI when a tag such as v0.2.0 is pushed, by trusted
2
+ # publishing (no token is stored): on PyPI, the project's publisher is this
3
+ # repository, this workflow (release.yml) and the environment "pypi".
4
+ name: Release
5
+
6
+ on:
7
+ push:
8
+ tags: ["v*"]
9
+
10
+ jobs:
11
+ publish:
12
+ runs-on: ubuntu-latest
13
+ environment: pypi
14
+ permissions:
15
+ contents: read
16
+ id-token: write
17
+ steps:
18
+ - uses: actions/checkout@v7
19
+ - uses: astral-sh/setup-uv@68559e99cc807c1eb7e9238fd43a15bba460eec2 # v10.3.3
20
+ - name: The tag matches the version in pyproject.toml
21
+ run: test "v$(uv version --short)" = "$GITHUB_REF_NAME"
22
+ - run: uv run --locked pytest
23
+ - run: uv build
24
+ - run: uv publish --trusted-publishing always
@@ -0,0 +1,38 @@
1
+ name: Tests
2
+
3
+ on:
4
+ push:
5
+ pull_request:
6
+
7
+ jobs:
8
+ test:
9
+ runs-on: ubuntu-latest
10
+ strategy:
11
+ matrix:
12
+ python: ["3.10", "3.11", "3.12", "3.13", "3.14", "3.15"]
13
+ steps:
14
+ - uses: actions/checkout@v7
15
+ # rsvg-convert, and the Cairo library that CairoSVG needs, so that
16
+ # both converters are tested.
17
+ - run: sudo apt-get update && sudo apt-get install -y librsvg2-bin
18
+ - uses: astral-sh/setup-uv@68559e99cc807c1eb7e9238fd43a15bba460eec2 # v10.3.3
19
+ - run: uv run --locked --python ${{ matrix.python }} pytest
20
+
21
+ oldest:
22
+ # The oldest versions of the dependencies that pyproject.toml allows.
23
+ runs-on: ubuntu-latest
24
+ steps:
25
+ - uses: actions/checkout@v7
26
+ - uses: astral-sh/setup-uv@68559e99cc807c1eb7e9238fd43a15bba460eec2 # v10.3.3
27
+ - run: uv venv --python 3.10
28
+ # A block scalar, because ":all: " would end a plain YAML value.
29
+ - run: |
30
+ uv pip install --resolution lowest-direct --only-binary :all: -e . "pytest>=8"
31
+ - run: .venv/bin/python -m pytest tests
32
+
33
+ docs:
34
+ runs-on: ubuntu-latest
35
+ steps:
36
+ - uses: actions/checkout@v7
37
+ - uses: astral-sh/setup-uv@68559e99cc807c1eb7e9238fd43a15bba460eec2 # v10.3.3
38
+ - run: uv run --locked --python 3.14 --group docs sphinx-build -W --keep-going -b html docs docs/_build/html
@@ -0,0 +1,13 @@
1
+ __pycache__/
2
+ .pytest_cache/
3
+ .ruff_cache/
4
+ .venv/
5
+ dist/
6
+ .coverage
7
+ docs/_build/
8
+ docs/_generated/
9
+ # Maps drawn while trying things out; the README picture is kept.
10
+ /*.svg
11
+ /*.png
12
+ /*.pdf
13
+ /*.json
@@ -0,0 +1,19 @@
1
+ # How Read the Docs builds the documentation at
2
+ # https://dotted-map.readthedocs.io/ (see
3
+ # https://docs.readthedocs.com/platform/stable/config-file/v2.html).
4
+ version: 2
5
+
6
+ build:
7
+ os: ubuntu-24.04
8
+ tools:
9
+ python: "3.14"
10
+ jobs:
11
+ # dotted-map with the docs dependency group of pyproject.toml, which
12
+ # needs pip 25.1 or later.
13
+ install:
14
+ - python -m pip install --upgrade pip
15
+ - python -m pip install . --group docs
16
+
17
+ sphinx:
18
+ configuration: docs/conf.py
19
+ fail_on_warning: true
@@ -0,0 +1,9 @@
1
+ # Changelog
2
+
3
+ ## 0.1.0 (2026-11-15)
4
+
5
+ The first release: dotted maps of any area, in any projection, framed on,
6
+ highlighted by or restricted to countries, continents and subregions, with
7
+ pins and curved routes, drawn as SVG, PNG, PDF or JSON from Python or the
8
+ command line. It replaces the code of the scipython.com post
9
+ [Making a map out of dots](https://scipython.com/blog/making-a-map-out-of-dots/).
@@ -0,0 +1,41 @@
1
+ cff-version: 1.2.0
2
+ message: If you use this software, please cite it as below.
3
+ type: software
4
+ title: dotted-map
5
+ abstract: >-
6
+ Draw maps on which land is a regular lattice of dots, as SVG, PNG and PDF
7
+ images for graphic design and infographics.
8
+ authors:
9
+ - given-names: Christian
10
+ family-names: Hill
11
+ license: BSD-3-Clause
12
+ repository-code: "https://github.com/xnx/dotted-map"
13
+ url: "https://dotted-map.readthedocs.io"
14
+ keywords:
15
+ - cartography
16
+ - dotted map
17
+ - graphic design
18
+ - halftone
19
+ - map projection
20
+ references:
21
+ - type: data
22
+ title: Natural Earth
23
+ url: "https://www.naturalearthdata.com"
24
+ version: 5.1.2
25
+ authors:
26
+ - name: Natural Earth
27
+ - type: article
28
+ title: The Equal Earth map projection
29
+ authors:
30
+ - given-names: Bojan
31
+ family-names: Šavrič
32
+ - given-names: Tom
33
+ family-names: Patterson
34
+ - given-names: Bernhard
35
+ family-names: Jenny
36
+ journal: International Journal of Geographical Information Science
37
+ volume: 33
38
+ issue: 3
39
+ start: 454
40
+ year: 2019
41
+ doi: 10.1080/13658816.2018.1504949
@@ -0,0 +1,28 @@
1
+ BSD 3-Clause License
2
+
3
+ Copyright (c) 2026, Christian Hill
4
+
5
+ Redistribution and use in source and binary forms, with or without
6
+ modification, are permitted provided that the following conditions are met:
7
+
8
+ 1. Redistributions of source code must retain the above copyright notice, this
9
+ list of conditions and the following disclaimer.
10
+
11
+ 2. Redistributions in binary form must reproduce the above copyright notice,
12
+ this list of conditions and the following disclaimer in the documentation
13
+ and/or other materials provided with the distribution.
14
+
15
+ 3. Neither the name of the copyright holder nor the names of its
16
+ contributors may be used to endorse or promote products derived from
17
+ this software without specific prior written permission.
18
+
19
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
20
+ AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21
+ IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
22
+ DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
23
+ FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24
+ DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
25
+ SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
26
+ CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
27
+ OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28
+ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
@@ -0,0 +1,85 @@
1
+ Metadata-Version: 2.5
2
+ Name: dotted-map
3
+ Version: 0.1.0
4
+ Summary: Draw maps on which land is a regular lattice of dots, as SVG, PNG or PDF images
5
+ Project-URL: Documentation, https://dotted-map.readthedocs.io
6
+ Project-URL: Repository, https://github.com/xnx/dotted-map
7
+ Project-URL: Issues, https://github.com/xnx/dotted-map/issues
8
+ Project-URL: Changelog, https://github.com/xnx/dotted-map/blob/main/CHANGELOG.md
9
+ Author: Christian Hill
10
+ License-Expression: BSD-3-Clause
11
+ License-File: LICENSE
12
+ Keywords: cartography,dots,graphic design,halftone,map,svg
13
+ Classifier: Intended Audience :: Science/Research
14
+ Classifier: Programming Language :: Python :: 3
15
+ Classifier: Topic :: Multimedia :: Graphics
16
+ Classifier: Topic :: Scientific/Engineering :: GIS
17
+ Classifier: Topic :: Scientific/Engineering :: Visualization
18
+ Requires-Python: >=3.10
19
+ Requires-Dist: numpy>=1.24
20
+ Requires-Dist: pyproj>=3.6
21
+ Requires-Dist: shapely>=2.0
22
+ Provides-Extra: cairo
23
+ Requires-Dist: cairosvg>=2.7; extra == 'cairo'
24
+ Description-Content-Type: text/markdown
25
+
26
+ # dotted-map
27
+
28
+ [![PyPI](https://img.shields.io/pypi/v/dotted-map)](https://pypi.org/project/dotted-map/)
29
+ [![Python versions](https://img.shields.io/pypi/pyversions/dotted-map)](https://pypi.org/project/dotted-map/)
30
+ [![Tests](https://github.com/xnx/dotted-map/actions/workflows/tests.yml/badge.svg)](https://github.com/xnx/dotted-map/actions/workflows/tests.yml)
31
+ [![Documentation](https://app.readthedocs.org/projects/dotted-map/badge/?version=stable)](https://dotted-map.readthedocs.io/en/stable/)
32
+
33
+ Draw maps on which land is a regular lattice of dots, as SVG, PNG and PDF
34
+ images for graphic design and infographics.
35
+
36
+ ![A dotted map of the world, with eight cities marked in orange and joined by curved routes](https://raw.githubusercontent.com/xnx/dotted-map/main/docs/_static/world.png)
37
+
38
+ The maps are drawn in an equal-area projection, or any other that
39
+ [PROJ](https://proj.org) supports, with coastlines found from the fraction of
40
+ each dot's cell that is land. The land and country outlines of
41
+ [Natural Earth](https://www.naturalearthdata.com) are built in, so a country,
42
+ continent or subregion can be named to frame the map, to highlight it or to
43
+ draw it alone. The documentation is at https://dotted-map.readthedocs.io.
44
+
45
+ ## Command line
46
+
47
+ ```console
48
+ $ dotted-map -o world.svg
49
+ $ dotted-map --region France --highlight 'France=#0055a4' --colour '#c5cad3' -o france.png
50
+ $ dotted-map --region Italy --only Italy --shape hexagon --halftone -o italy.pdf
51
+ $ dotted-map --region GBR IRL --pin London=-0.13,51.51 --pin Dublin=-6.26,53.35 --arc London Dublin -o uk.svg
52
+ $ dotted-map --projection '+proj=ortho +lon_0=-30 +lat_0=30' --sea '#dde1e8' -o globe.svg
53
+ $ dotted-map --list-regions
54
+ ```
55
+
56
+ ## Python
57
+
58
+ ```python
59
+ from dotted_map import DottedMap
60
+
61
+ m = DottedMap(region="Japan", width=600, spacing=8)
62
+ m.add_pin(139.69, 35.69) # Tokyo.
63
+ m.save("japan.svg", colour="#bc002d", sea="#dde1e8", halftone=True)
64
+
65
+ data = m.to_dict() # Every dot, for use elsewhere.
66
+ ```
67
+
68
+ Colours and sizes can be functions of longitude and latitude, for maps of
69
+ data.
70
+
71
+ ## Installation
72
+
73
+ ```console
74
+ $ pip install dotted-map
75
+ ```
76
+
77
+ The dotted-map package needs Python 3.10 or later, NumPy, pyproj and Shapely.
78
+ PNG and PDF output also needs [CairoSVG](https://cairosvg.org)
79
+ (`pip install "dotted-map[cairo]"`) or the `rsvg-convert` program of
80
+ [librsvg](https://gitlab.gnome.org/GNOME/librsvg).
81
+
82
+ ## Licence
83
+
84
+ BSD-3-Clause. The land and country outlines are made from Natural Earth's
85
+ 1:50m data, which is in the public domain.
@@ -0,0 +1,60 @@
1
+ # dotted-map
2
+
3
+ [![PyPI](https://img.shields.io/pypi/v/dotted-map)](https://pypi.org/project/dotted-map/)
4
+ [![Python versions](https://img.shields.io/pypi/pyversions/dotted-map)](https://pypi.org/project/dotted-map/)
5
+ [![Tests](https://github.com/xnx/dotted-map/actions/workflows/tests.yml/badge.svg)](https://github.com/xnx/dotted-map/actions/workflows/tests.yml)
6
+ [![Documentation](https://app.readthedocs.org/projects/dotted-map/badge/?version=stable)](https://dotted-map.readthedocs.io/en/stable/)
7
+
8
+ Draw maps on which land is a regular lattice of dots, as SVG, PNG and PDF
9
+ images for graphic design and infographics.
10
+
11
+ ![A dotted map of the world, with eight cities marked in orange and joined by curved routes](https://raw.githubusercontent.com/xnx/dotted-map/main/docs/_static/world.png)
12
+
13
+ The maps are drawn in an equal-area projection, or any other that
14
+ [PROJ](https://proj.org) supports, with coastlines found from the fraction of
15
+ each dot's cell that is land. The land and country outlines of
16
+ [Natural Earth](https://www.naturalearthdata.com) are built in, so a country,
17
+ continent or subregion can be named to frame the map, to highlight it or to
18
+ draw it alone. The documentation is at https://dotted-map.readthedocs.io.
19
+
20
+ ## Command line
21
+
22
+ ```console
23
+ $ dotted-map -o world.svg
24
+ $ dotted-map --region France --highlight 'France=#0055a4' --colour '#c5cad3' -o france.png
25
+ $ dotted-map --region Italy --only Italy --shape hexagon --halftone -o italy.pdf
26
+ $ dotted-map --region GBR IRL --pin London=-0.13,51.51 --pin Dublin=-6.26,53.35 --arc London Dublin -o uk.svg
27
+ $ dotted-map --projection '+proj=ortho +lon_0=-30 +lat_0=30' --sea '#dde1e8' -o globe.svg
28
+ $ dotted-map --list-regions
29
+ ```
30
+
31
+ ## Python
32
+
33
+ ```python
34
+ from dotted_map import DottedMap
35
+
36
+ m = DottedMap(region="Japan", width=600, spacing=8)
37
+ m.add_pin(139.69, 35.69) # Tokyo.
38
+ m.save("japan.svg", colour="#bc002d", sea="#dde1e8", halftone=True)
39
+
40
+ data = m.to_dict() # Every dot, for use elsewhere.
41
+ ```
42
+
43
+ Colours and sizes can be functions of longitude and latitude, for maps of
44
+ data.
45
+
46
+ ## Installation
47
+
48
+ ```console
49
+ $ pip install dotted-map
50
+ ```
51
+
52
+ The dotted-map package needs Python 3.10 or later, NumPy, pyproj and Shapely.
53
+ PNG and PDF output also needs [CairoSVG](https://cairosvg.org)
54
+ (`pip install "dotted-map[cairo]"`) or the `rsvg-convert` program of
55
+ [librsvg](https://gitlab.gnome.org/GNOME/librsvg).
56
+
57
+ ## Licence
58
+
59
+ BSD-3-Clause. The land and country outlines are made from Natural Earth's
60
+ 1:50m data, which is in the public domain.
@@ -0,0 +1,6 @@
1
+ /* The example maps have dark dots on a transparent background: give them
2
+ a light background, so that they show in the dark theme too. */
3
+ img.dotted-map-example {
4
+ background: #fbfbfc;
5
+ border-radius: 0.25rem;
6
+ }
Binary file
@@ -0,0 +1,38 @@
1
+ *************
2
+ API reference
3
+ *************
4
+
5
+ These names can be imported from ``dotted_map``.
6
+
7
+ .. currentmodule:: dotted_map
8
+
9
+ Maps
10
+ ====
11
+
12
+ .. autoclass:: DottedMap
13
+ :members: svg, save, to_dict, add_pin, add_arc, nearest, lonlat_to_px,
14
+ px_to_lonlat
15
+
16
+ .. autofunction:: auto_projection
17
+
18
+ .. data:: WORLD
19
+
20
+ The extent of the whole world, ``(-180, 180, -90, 90)``.
21
+
22
+ Regions and land
23
+ ================
24
+
25
+ .. autofunction:: region
26
+
27
+ .. autofunction:: countries
28
+
29
+ .. autoclass:: Country
30
+
31
+ .. autofunction:: land
32
+
33
+ .. autofunction:: read_geojson
34
+
35
+ Errors
36
+ ======
37
+
38
+ .. autoexception:: ExportError
@@ -0,0 +1,147 @@
1
+ # conf.py
2
+ # Configuration for the Sphinx documentation of dotted-map.
3
+ #
4
+ # SPDX-License-Identifier: BSD-3-Clause
5
+
6
+ import contextlib
7
+ import io
8
+ import os
9
+ import shlex
10
+ import sys
11
+ from pathlib import Path
12
+
13
+ from docutils import nodes
14
+ from docutils.parsers.rst import directives
15
+ from docutils.statemachine import StringList
16
+ from sphinx.util.docutils import SphinxDirective
17
+
18
+ import dotted_map
19
+ from dotted_map.cli import main
20
+
21
+ project = "dotted-map"
22
+ author = "Christian Hill"
23
+ copyright = "2026, Christian Hill"
24
+ release = dotted_map.__version__
25
+ version = ".".join(release.split(".")[:2])
26
+
27
+ extensions = ["sphinx.ext.autodoc", "sphinx.ext.napoleon"]
28
+ autodoc_member_order = "bysource"
29
+ napoleon_numpy_docstring = False
30
+ default_role = "py:obj"
31
+ exclude_patterns = ["_build"]
32
+
33
+ html_theme = "furo"
34
+ html_title = f"dotted-map {release}"
35
+ html_static_path = ["_static"]
36
+ html_css_files = ["custom.css"]
37
+
38
+ # The pictures, the table of regions and the command-line help are made by
39
+ # the package itself whenever the documentation is built, so that they
40
+ # always match the code.
41
+ GENERATED = Path(__file__).parent / "_generated"
42
+ GENERATED.mkdir(exist_ok=True)
43
+
44
+
45
+ def _write(name, text):
46
+ (GENERATED / name).write_text(text, encoding="utf-8")
47
+
48
+
49
+ class _Example(SphinxDirective):
50
+ """Show an example and the picture it draws, as name.svg.
51
+
52
+ The content is run, so the picture always matches it."""
53
+
54
+ required_arguments = 1
55
+ has_content = True
56
+ option_spec = {"alt": directives.unchanged_required}
57
+
58
+ def run(self):
59
+ name = self.arguments[0]
60
+ lines = [line.rstrip() for line in self.content]
61
+ self.draw(name, lines)
62
+ if not (GENERATED / f"{name}.svg").exists():
63
+ raise self.error(f"the example did not write {name}.svg")
64
+ rst = [f".. code-block:: {self.language}", ""]
65
+ rst += [" " + line for line in self.display(name, lines)]
66
+ rst += ["", f".. image:: /_generated/{name}.svg",
67
+ " :class: dotted-map-example",
68
+ f" :alt: {self.options['alt']}", ""]
69
+ node = nodes.container()
70
+ self.state.nested_parse(StringList(rst), self.content_offset, node)
71
+ return node.children
72
+
73
+
74
+ class CommandExample(_Example):
75
+ """A dotted-map command; the content is its arguments, apart from -o.
76
+
77
+ .. dotted-map:: italy
78
+ :alt: Italy, drawn in green hexagons.
79
+
80
+ --region Italy --only Italy --shape hexagon --colour '#009246'
81
+ """
82
+
83
+ language = "console"
84
+
85
+ def draw(self, name, lines):
86
+ args = shlex.split(" ".join(line.rstrip("\\") for line in lines))
87
+ with contextlib.redirect_stderr(io.StringIO()) as messages:
88
+ status = main([*args, "-o", str(GENERATED / f"{name}.svg")])
89
+ if status:
90
+ raise self.error(f"dotted-map exited with status {status}: "
91
+ f"{messages.getvalue()}")
92
+
93
+ def display(self, name, lines):
94
+ lines = [f"$ dotted-map {lines[0]}"] + [f" {line.lstrip()}"
95
+ for line in lines[1:]]
96
+ lines[-1] += f" -o {name}.svg"
97
+ return lines
98
+
99
+
100
+ class PythonExample(_Example):
101
+ """Python code that saves a map as name.svg."""
102
+
103
+ language = "python"
104
+
105
+ def draw(self, name, lines):
106
+ with contextlib.chdir(GENERATED):
107
+ exec("\n".join(lines), {"__name__": "example"})
108
+
109
+ def display(self, name, lines):
110
+ return lines
111
+
112
+
113
+ def setup(app):
114
+ app.add_directive("dotted-map", CommandExample)
115
+ app.add_directive("dotted-map-python", PythonExample)
116
+
117
+
118
+ # The picture on the front page, which is also in the README.
119
+ sys.path.insert(0, str(Path(__file__).parent.parent / "tools"))
120
+ from showcase import STYLE, world # noqa: E402
121
+
122
+ _write("world.svg", world().svg(**STYLE))
123
+
124
+ # The table of the countries that can be named.
125
+ _rows = [".. list-table::", " :header-rows: 1", "",
126
+ " * - Code", " - ISO alpha-2", " - Name",
127
+ " - Continent", " - Subregion"]
128
+ for _c in dotted_map.countries():
129
+ _rows += [f" * - ``{_c.code}``",
130
+ f" - ``{_c.iso_a2}``" if _c.iso_a2 else " -",
131
+ f" - {_c.name}", f" - {_c.continent}",
132
+ f" - {_c.subregion}"]
133
+ _write("countries.inc", "\n".join(_rows) + "\n")
134
+ _write("groups.inc", "\n".join([
135
+ "**Continents:** " + ", ".join(sorted(
136
+ {c.continent for c in dotted_map.countries()})) + ".",
137
+ "",
138
+ "**Subregions:** " + ", ".join(sorted(
139
+ {c.subregion for c in dotted_map.countries()})) + ".", ""]))
140
+
141
+ # The command-line help.
142
+ os.environ["COLUMNS"] = "79"
143
+ os.environ["NO_COLOR"] = "1"
144
+ _help = io.StringIO()
145
+ with contextlib.redirect_stdout(_help), contextlib.suppress(SystemExit):
146
+ main(["--help"])
147
+ _write("help.txt", _help.getvalue())