Skip to content

Commit

Permalink
feat: auto-populate markets
Browse files Browse the repository at this point in the history
  • Loading branch information
qthequartermasterman committed Dec 24, 2023
1 parent fe3a4df commit c815861
Show file tree
Hide file tree
Showing 7 changed files with 260 additions and 59 deletions.
105 changes: 105 additions & 0 deletions docs/Background/geography/loot_locations.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
# Scavenging Locations

<div id="map"></div>

The following locations are useful for providing opportunities for loot or dungeons. Generally, these locations tend
not to be the location of significant story. For more details on the Scavenging system (including loot tables), consult
the *Fallout Core Rulebook* (p. 195).

Unless other information is provided, each of the locations in the list have no prescribed special features, and are
included in the list solely to contain a geotag to be used on the auto-rendered map.

## Super Markets

Super Markets are a great place to find food, medicine, and other useful items.

<geotag name="Supermarket" zoom=TOWN latitude=29.8779870 longitude=-95.6488309 icon=SUPER_DUPER_MART />
<geotag name="Supermarket" zoom=TOWN latitude=29.6000278 longitude=-95.6233281 icon=SUPER_DUPER_MART />
<geotag name="Supermarket" zoom=TOWN latitude=29.7670296 longitude=-95.5515031 icon=SUPER_DUPER_MART />
<geotag name="Supermarket" zoom=TOWN latitude=29.6216449 longitude=-95.5578334 icon=SUPER_DUPER_MART />
<geotag name="Supermarket" zoom=TOWN latitude=29.6902257 longitude=-95.4151183 icon=SUPER_DUPER_MART />
<geotag name="Supermarket" zoom=TOWN latitude=29.7392004 longitude=-95.4111693 icon=SUPER_DUPER_MART />
<geotag name="Supermarket" zoom=TOWN latitude=29.6875540 longitude=-95.4913350 icon=SUPER_DUPER_MART />
<geotag name="Supermarket" zoom=TOWN latitude=29.8225916 longitude=-95.5232432 icon=SUPER_DUPER_MART />
<geotag name="Supermarket" zoom=TOWN latitude=29.8210878 longitude=-95.5249442 icon=SUPER_DUPER_MART />
<geotag name="Supermarket" zoom=TOWN latitude=29.7896319 longitude=-95.4112065 icon=SUPER_DUPER_MART />
<geotag name="Super-Duper Mart" zoom=WASTELAND latitude=29.8606530 longitude=-95.7164480 icon=SUPER_DUPER_MART />
<geotag name="Supermarket" zoom=TOWN latitude=29.8711790 longitude=-95.4061220 icon=SUPER_DUPER_MART />
<geotag name="Supermarket" zoom=TOWN latitude=29.7543255 longitude=-95.3616311 icon=SUPER_DUPER_MART />
<geotag name="Supermarket" zoom=TOWN latitude=29.7711432 longitude=-95.5991940 icon=SUPER_DUPER_MART />
<geotag name="Supermarket" zoom=TOWN latitude=29.6723340 longitude=-95.5300380 icon=SUPER_DUPER_MART />
<geotag name="Supermarket" zoom=TOWN latitude=29.7504960 longitude=-95.7983050 icon=SUPER_DUPER_MART />
<geotag name="Supermarket" zoom=TOWN latitude=29.7117132 longitude=-95.3128462 icon=SUPER_DUPER_MART />
<geotag name="Supermarket" zoom=TOWN latitude=29.7524669 longitude=-95.4999989 icon=SUPER_DUPER_MART />
<geotag name="Supermarket" zoom=TOWN latitude=29.7794337 longitude=-95.7170091 icon=SUPER_DUPER_MART />
<geotag name="Supermarket" zoom=TOWN latitude=29.7892877 longitude=-95.7167418 icon=SUPER_DUPER_MART />
<geotag name="Supermarket" zoom=TOWN latitude=29.7911709 longitude=-95.7213141 icon=SUPER_DUPER_MART />
<geotag name="Supermarket" zoom=TOWN latitude=29.7123729 longitude=-95.7718631 icon=SUPER_DUPER_MART />
<geotag name="Supermarket" zoom=TOWN latitude=29.7809732 longitude=-95.7490959 icon=SUPER_DUPER_MART />
<geotag name="Supermarket" zoom=TOWN latitude=29.5973965 longitude=-95.5253515 icon=SUPER_DUPER_MART />
<geotag name="Supermarket" zoom=TOWN latitude=29.5636313 longitude=-95.5655009 icon=SUPER_DUPER_MART />
<geotag name="Supermarket" zoom=TOWN latitude=29.5826319 longitude=-95.5834046 icon=SUPER_DUPER_MART />
<geotag name="Supermarket" zoom=TOWN latitude=29.5886311 longitude=-95.5995381 icon=SUPER_DUPER_MART />
<geotag name="Supermarket" zoom=TOWN latitude=29.5431629 longitude=-95.5481738 icon=SUPER_DUPER_MART />
<geotag name="Supermarket" zoom=TOWN latitude=29.6078877 longitude=-95.4775703 icon=SUPER_DUPER_MART />
<geotag name="Supermarket" zoom=TOWN latitude=29.8368658 longitude=-95.6446754 icon=SUPER_DUPER_MART />
<geotag name="Supermarket" zoom=TOWN latitude=29.8356222 longitude=-95.6483971 icon=SUPER_DUPER_MART />
<geotag name="Supermarket" zoom=TOWN latitude=29.8322662 longitude=-95.7046406 icon=SUPER_DUPER_MART />
<geotag name="Supermarket" zoom=TOWN latitude=29.8316729 longitude=-95.7085700 icon=SUPER_DUPER_MART />
<geotag name="Supermarket" zoom=TOWN latitude=29.7495946 longitude=-95.4618721 icon=SUPER_DUPER_MART />
<geotag name="Supermarket" zoom=TOWN latitude=29.8105410 longitude=-95.5256538 icon=SUPER_DUPER_MART />
<geotag name="Supermarket" zoom=TOWN latitude=29.8020916 longitude=-95.4159097 icon=SUPER_DUPER_MART />
<geotag name="Supermarket" zoom=TOWN latitude=29.8229583 longitude=-95.4097761 icon=SUPER_DUPER_MART />
<geotag name="Supermarket" zoom=TOWN latitude=29.5945796 longitude=-95.4663316 icon=SUPER_DUPER_MART />
<geotag name="Supermarket" zoom=TOWN latitude=29.8809901 longitude=-95.7002784 icon=SUPER_DUPER_MART />
<geotag name="Supermarket" zoom=TOWN latitude=29.8126507 longitude=-95.7701896 icon=SUPER_DUPER_MART />
<geotag name="Supermarket" zoom=TOWN latitude=29.8657949 longitude=-95.6702815 icon=SUPER_DUPER_MART />
<geotag name="Supermarket" zoom=TOWN latitude=29.8667256 longitude=-95.6839984 icon=SUPER_DUPER_MART />
<geotag name="Supermarket" zoom=TOWN latitude=29.7058516 longitude=-95.5468867 icon=SUPER_DUPER_MART />
<geotag name="Supermarket" zoom=TOWN latitude=29.7381154 longitude=-95.6247919 icon=SUPER_DUPER_MART />
<geotag name="Supermarket" zoom=TOWN latitude=29.8784732 longitude=-95.6671319 icon=SUPER_DUPER_MART />
<geotag name="Supermarket" zoom=TOWN latitude=29.7013659 longitude=-95.5773981 icon=SUPER_DUPER_MART />
<geotag name="Super-Duper Mart" zoom=WASTELAND latitude=29.7439900 longitude=-95.7930340 icon=SUPER_DUPER_MART />
<geotag name="Super-Duper Mart" zoom=WASTELAND latitude=29.6521000 longitude=-95.6174000 icon=SUPER_DUPER_MART />
<geotag name="Super-Duper Mart" zoom=WASTELAND latitude=29.6792670 longitude=-95.5613180 icon=SUPER_DUPER_MART />
<geotag name="Super-Duper Mart" zoom=WASTELAND latitude=29.7354850 longitude=-95.5116920 icon=SUPER_DUPER_MART />
<geotag name="Supermarket" zoom=TOWN latitude=29.8111878 longitude=-95.6878767 icon=SUPER_DUPER_MART />
<geotag name="Supermarket" zoom=TOWN latitude=29.8535816 longitude=-95.6824645 icon=SUPER_DUPER_MART />
<geotag name="Supermarket" zoom=TOWN latitude=29.6464818 longitude=-95.5827063 icon=SUPER_DUPER_MART />
<geotag name="Supermarket" zoom=TOWN latitude=29.7911983 longitude=-95.5209816 icon=SUPER_DUPER_MART />
<geotag name="Supermarket" zoom=TOWN latitude=29.7527466 longitude=-95.5016279 icon=SUPER_DUPER_MART />
<geotag name="Supermarket" zoom=TOWN latitude=29.7404182 longitude=-95.7753557 icon=SUPER_DUPER_MART />
<geotag name="Supermarket" zoom=TOWN latitude=29.7930173 longitude=-95.7746464 icon=SUPER_DUPER_MART />
<geotag name="Supermarket" zoom=TOWN latitude=29.8073613 longitude=-95.4088622 icon=SUPER_DUPER_MART />
<geotag name="Supermarket" zoom=TOWN latitude=29.8045727 longitude=-95.4004585 icon=SUPER_DUPER_MART />
<geotag name="Supermarket" zoom=TOWN latitude=29.7968241 longitude=-95.4864439 icon=SUPER_DUPER_MART />
<geotag name="Supermarket" zoom=TOWN latitude=29.7466025 longitude=-95.4863817 icon=SUPER_DUPER_MART />
<geotag name="Supermarket" zoom=TOWN latitude=29.5516946 longitude=-95.5843055 icon=SUPER_DUPER_MART />
<geotag name="Supermarket" zoom=TOWN latitude=29.6894869 longitude=-95.5015487 icon=SUPER_DUPER_MART />
<geotag name="Supermarket" zoom=TOWN latitude=29.7688039 longitude=-95.3966726 icon=SUPER_DUPER_MART />
<geotag name="Supermarket" zoom=TOWN latitude=29.5889911 longitude=-95.6003083 icon=SUPER_DUPER_MART />
<geotag name="Supermarket" zoom=TOWN latitude=29.8137026 longitude=-95.3981434 icon=SUPER_DUPER_MART />
<geotag name="Supermarket" zoom=TOWN latitude=29.7350969 longitude=-95.5705068 icon=SUPER_DUPER_MART />
<geotag name="Supermarket" zoom=TOWN latitude=29.8316216 longitude=-95.4848820 icon=SUPER_DUPER_MART />
<geotag name="Supermarket" zoom=TOWN latitude=29.7745983 longitude=-95.4002443 icon=SUPER_DUPER_MART />
<geotag name="Supermarket" zoom=TOWN latitude=29.5298455 longitude=-95.5108212 icon=SUPER_DUPER_MART />
<geotag name="Supermarket" zoom=TOWN latitude=29.7757349 longitude=-95.7502060 icon=SUPER_DUPER_MART />
<geotag name="Supermarket" zoom=TOWN latitude=29.6638346 longitude=-95.6441845 icon=SUPER_DUPER_MART />
<geotag name="Supermarket" zoom=TOWN latitude=29.7494691 longitude=-95.7503867 icon=SUPER_DUPER_MART />
<geotag name="Supermarket" zoom=TOWN latitude=29.7362661 longitude=-95.5335481 icon=SUPER_DUPER_MART />
<geotag name="Supermarket" zoom=TOWN latitude=29.8180432 longitude=-95.5447357 icon=SUPER_DUPER_MART />
<geotag name="Supermarket" zoom=TOWN latitude=29.9078721 longitude=-95.5857979 icon=SUPER_DUPER_MART />
<geotag name="Supermarket" zoom=TOWN latitude=29.7219216 longitude=-95.5015286 icon=SUPER_DUPER_MART />
<geotag name="Supermarket" zoom=TOWN latitude=29.7220201 longitude=-95.5006682 icon=SUPER_DUPER_MART />
<geotag name="Supermarket" zoom=TOWN latitude=29.7823174 longitude=-95.5221864 icon=SUPER_DUPER_MART />
<geotag name="Supermarket" zoom=TOWN latitude=29.7059387 longitude=-95.5467615 icon=SUPER_DUPER_MART />
<geotag name="Supermarket" zoom=TOWN latitude=29.7384958 longitude=-95.5370970 icon=SUPER_DUPER_MART />
<geotag name="Supermarket" zoom=TOWN latitude=29.8136332 longitude=-95.4605775 icon=SUPER_DUPER_MART />
<geotag name="Supermarket" zoom=TOWN latitude=29.8480550 longitude=-95.3915426 icon=SUPER_DUPER_MART />
<geotag name="Supermarket" zoom=TOWN latitude=29.7843221 longitude=-95.3617855 icon=SUPER_DUPER_MART />
<geotag name="Supermarket" zoom=TOWN latitude=29.8669271 longitude=-95.4858994 icon=SUPER_DUPER_MART />
<geotag name="Supermarket" zoom=TOWN latitude=29.6501737 longitude=-95.5653613 icon=SUPER_DUPER_MART />
<geotag name="Supermarket" zoom=TOWN latitude=29.7063997 longitude=-95.5539970 icon=SUPER_DUPER_MART />
<geotag name="Supermarket" zoom=TOWN latitude=29.7426202 longitude=-95.5604977 icon=SUPER_DUPER_MART />
<geotag name="Supermarket" zoom=TOWN latitude=29.7029754 longitude=-95.5541551 icon=SUPER_DUPER_MART />
<geotag name="Supermarket" zoom=TOWN latitude=29.8682858 longitude=-95.4721302 icon=SUPER_DUPER_MART />
3 changes: 2 additions & 1 deletion mkdocs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ nav:
- 'Background/geography/roads.md'
- 'Background/geography/nuclear_targets.md'
- 'Background/geography/misc.md'
- 'Background/geography/loot_locations.md'
- "Pre-war Companies": Background/companies.md
- "Sim Settlements 2 Elements": Background/sim-settlements-elements.md
- 'Background/theming_music.md'
Expand Down Expand Up @@ -113,7 +114,7 @@ plugins:
- search

hooks:
- render_map/render_map.py
- render_map/plugin_hooks.py

extra:
# GOOGLE_MAPS_API_KEY: !ENV GOOGLE_MAPS_API_KEY # ReadTheDocs doesn't support loading from env variables!
Expand Down
1 change: 0 additions & 1 deletion render_map/__init__.py
Original file line number Diff line number Diff line change
@@ -1 +0,0 @@
from render_map.render_map import *
70 changes: 70 additions & 0 deletions render_map/auto_populate_map.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
"""Automatically populate the map with supermarkets and other landmarks using the Overpass (Open Street Map) API."""
from __future__ import annotations

import overpy
from render_map import mapping

RADIUS=25000
CENTRAL_LATITUDE=29.7063997
CENTRAL_LONGITUDE=-95.553997


SUPER_MARKET_QUERY="""[out:json];
(node["building"="supermarket"](around:{radius},{lat},{lon});
node["shop"="supermarket"](around:{radius},{lat},{lon});
/*way["shop"="supermarket"](around:{radius},{lat},{lon});*/
);
(._;>;);
out meta;
"""


API = overpy.Overpass()

def choose_supermarket_name_zoom(node:overpy.Node) -> tuple[str|None, mapping.ZoomLevel]:
"""Choose the game name and map zoom level for a supermarket, based on the properties of the supermarket in the
real world.
Args:
node: The node in OpenStreetMap representing the supermarket.
Returns:
Game name and map zoom level for the supermarket.
"""
name_from_node = node.tags.get("name", None)
# If the supermarket is not named in OpenStreetMap, we'll (unfairly) assume it's not a very important supermarket.
if name_from_node is None:
return None, mapping.ZoomLevel.WASTELAND
# Super-Duper Mart is implied to be a chain of very large supermarkets, likely wholesale. In the video games, there
# is only one Super-Duper Mart in its corresponding city metro-area.
if "walmart" in name_from_node.lower() or "sam's" in name_from_node.lower() or "costco" in name_from_node.lower():
return "Super-Duper Mart", mapping.ZoomLevel.WASTELAND
# TODO: Provide more plausible and generic names for super markets.
return "Supermarket", mapping.ZoomLevel.TOWN


def populate_supermarkets(radius:float, latitude:float, longitude:float) -> str:
"""Generate geotags for supermarkets in the game world, using the locations of supermarkets in the real world (using
the Overpass API).
Args:
radius: The radius in meters to search for supermarkets.
latitude: The latitude to search for supermarkets.
longitude: The longitude to search for supermarkets.
Returns:
A string of geotags for the supermarkets.
"""
shops = API.query(SUPER_MARKET_QUERY.format(radius=radius, lat=latitude, lon=longitude))
geotags: list[mapping.GeoLink] = []
for node in shops.nodes:
name, zoom = choose_supermarket_name_zoom(node)
if name is None:
continue
geotags.append(
mapping.GeoLink(
name=name, latitude=node.lat, longitude=node.lon, zoom=zoom, icon=mapping.map_icons.MapIcon.SUPER_DUPER_MART
)

)
return "\n".join(geotag.get_tag() for geotag in geotags)
77 changes: 20 additions & 57 deletions render_map/render_map.py → render_map/mapping.py
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,26 @@ class GeoLink(pydantic.BaseModel):
zoom: ZoomLevel = pydantic.Field(default=ZoomLevel.WASTELAND, validate_default=True)
uuid: str = pydantic.Field(default_factory=lambda: str(uuid.uuid4()))

def get_tag(self, include_uuid:bool=False) -> str:
"""Get the geotag as a string.
Args:
include_uuid: Whether to include the uuid in the geotag.
Returns:
The geotag as a string.
"""
soup = bs4.BeautifulSoup("", "html.parser")
tag = soup.new_tag("geotag")
tag["name"] = self.name
tag["latitude"] = str(self.latitude)
tag["longitude"] = str(self.longitude)
tag["icon"] = self.icon.name
tag["zoom"] = str(self.zoom.name)
if include_uuid:
tag["uuid"] = self.uuid

return str(tag)

GEO_LINKS: list[GeoLink] = []

Expand Down Expand Up @@ -128,60 +148,3 @@ def create_map_template(config: mkdocs.plugins.MkDocsConfig) -> str:
return map_source


@mkdocs.plugins.event_priority(0)
def on_page_markdown(
markdown: str,
page: mkdocs.plugins.Page,
config: mkdocs.plugins.MkDocsConfig,
**kwargs,
):
"""Find all geotags in the markdown and process them into a list of GeoLink objects.
Args:
markdown: The markdown to search for geotags.
page: The page object.
config: The mkdocs config.
Returns:
A list of GeoLink objects.
"""
# Skip, if page is excluded
if page.file.inclusion.is_excluded():
return

geolinks, markdown = find_geo_links(markdown)
GEO_LINKS.extend(geolinks)

return markdown


@mkdocs.plugins.event_priority(0)
def on_page_context(
context: dict,
page: mkdocs.plugins.Page,
config: mkdocs.plugins.MkDocsConfig,
**kwargs,
):
"""Add the map template to the page context.
This will place a map inside all pages that have a div with id="map".
Args:
context: The page context.
page: The page object.
config: The mkdocs config.
Returns:
The page context.
"""
# Skip, if page is excluded
if page.file.inclusion.is_excluded():
return

if """<div id="map"></div>""" in page.content:
map_source = create_map_template(config)

page.content += "\n\n\n\n"
page.content += map_source

return context
62 changes: 62 additions & 0 deletions render_map/plugin_hooks.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
from __future__ import annotations

import mkdocs.plugins

from render_map import mapping

@mkdocs.plugins.event_priority(0)
def on_page_markdown(
markdown: str,
page: mkdocs.plugins.Page,
config: mkdocs.plugins.MkDocsConfig,
**kwargs,
):
"""Find all geotags in the markdown and process them into a list of GeoLink objects.
Args:
markdown: The markdown to search for geotags.
page: The page object.
config: The mkdocs config.
Returns:
A list of GeoLink objects.
"""
# Skip, if page is excluded
if page.file.inclusion.is_excluded():
return

geolinks, markdown = mapping.find_geo_links(markdown)
mapping.GEO_LINKS.extend(geolinks)

return markdown


@mkdocs.plugins.event_priority(0)
def on_page_context(
context: dict,
page: mkdocs.plugins.Page,
config: mkdocs.plugins.MkDocsConfig,
**kwargs,
):
"""Add the map template to the page context.
This will place a map inside all pages that have a div with id="map".
Args:
context: The page context.
page: The page object.
config: The mkdocs config.
Returns:
The page context.
"""
# Skip, if page is excluded
if page.file.inclusion.is_excluded():
return

if """<div id="map"></div>""" in page.content:
map_source = mapping.create_map_template(config)
page.content += "\n\n\n\n"
page.content += map_source

return context
1 change: 1 addition & 0 deletions requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,5 @@ beautifulsoup4==4.12.2
mkdocs-include-markdown-plugin==6.0.3
mkdocs-material==9.4.6
mkdocs-macros-plugin==1.0.4
overpy==0.7
pydantic==2.4.2

0 comments on commit c815861

Please sign in to comment.