Files
mohamad d4ec85add4 feat: add configurable people maps
Add independent map profiles, offline city lookup, bundled world artwork
and collision-free portrait placement with optional person details.

Deploy with updated requirements, migrations 0029-0031 and collectstatic.
2026-11-03 04:41:47 +08:00

4.8 KiB
Raw Permalink Blame History

People maps

People maps are independent of login accounts. One person can appear on several maps, with a different location on each map. Existing HTML sections are untouched.

Deployment

Install requirements.txt, run python manage.py migrate, and collect static files using your normal deployment settings. Migrations 0029–0031 are generated by Django. They create the map tables, add the built-in background selector and add the optional people directory setting. They do not change existing pages or import live content. No production database changes were made during implementation.

Admin workflow

  1. Under Pages → Map people, create people with a name, photo, qualifications, role, institution, biography and optional profile URL.
  2. Under Pages → People maps, create a map. Choose a built-in background using Background style: Classic silhouette, Atlas with country borders, Midnight dotted continents, or Ocean gradient. Thumbnail previews appear below the selector. All four SVG maps are bundled in static files and ship to the server through collectstatic; no upload or external map service is required. Choose Aurora (dark), Atlas (light), or Classic (the original black / blue / turquoise palette). Classic reproduces the palette and portrait treatment, rather than the exact hand-drawn silhouette in the reference screenshot. Optionally upload your own world artwork; its projection must be equirectangular (longitude -180…180, latitude 90…-90).
  3. Add memberships on the map edit screen. Select an existing person using the searchable selector, or create one with the adjacent add button.
  4. Choose Country and city or Coordinates for each membership. City mode accepts an English country name or ISO code and a recognized city name or alias. Coordinates are resolved and stored on save. The local GeoNames dataset covers cities of at least 15,000 inhabitants. Canonical city names take priority over another town's aliases. Unknown or ambiguous cities require the coordinates method; there is no silent guess. Coordinates mode accepts decimal latitude (-90…90) and longitude (-180…180), including zero and negative values.
  5. In the map's Map placements inline, enter the exact local page path, such as /about/, /, /projects/example/, /projects/example/module/, or a custom page's path. Select before or after content, display order, and active status. Add several placements to reuse the same map. Separate maps can have different members. Placements can also be managed under Pages → Map placements.

Paths are exact and case-sensitive, including trailing slashes. Update a placement when changing a page's URL. Page publication and project access rules still apply: this feature adds no new public page routes. Disabling a map hides all its placements; disabling one placement hides only that occurrence. To replace an existing HTML map, first configure the new map and placement, then separately disable the old HTML section when ready. No existing section is automatically replaced.

Admins can turn Show people details below the map off on each map to hide the directory cards. It is on by default. Portraits remain visible on the map; when details are hidden, portraits do not link to hidden cards.

Display behavior

The map uses local SVG artwork, CSS and JavaScript, without remote map tiles, tracking, API keys or geocoding requests. Portraits and labels are placed in the nearest available rectangle on deterministic expanding rings. Leaders connect each displaced portrait to the actual geographic anchor. If the map becomes very dense, its canvas grows vertically to preserve non-overlap. Minimum canvas width is 900px; phones scroll the map horizontally. Every person also has a readable directory card below the map, including without JavaScript. Selecting a portrait jumps to its card. Different placements generate unique anchor IDs.

Natural Earth land data is public domain: https://www.naturalearthdata.com/about/terms-of-use/ City resolution uses the geonamescache package and its GeoNames data: https://www.geonames.org/ (CC BY).

Verification

venv/bin/python manage.py test apps.pages.tests apps.products.tests --settings=config.settings.preview --noinput
venv/bin/python manage.py makemigrations --check --dry-run --settings=config.settings.preview
venv/bin/python scripts/verify_people_map.py

The optional browser verification needs Playwright and installed Google Chrome. It uses synthetic preview data, renders the actual template and assets without writing database records, tests 14 people and 60 people at the same location at desktop and phone widths, checks bounding boxes for collisions, tests portrait navigation and page overflow, and saves screenshots to artifacts/people-map/.