Skip to content

Add polygon, polyline, and rectangle support#67

Open
jimsafley wants to merge 12 commits into
feature/multi-locationfrom
feature/shape-support
Open

Add polygon, polyline, and rectangle support#67
jimsafley wants to merge 12 commits into
feature/multi-locationfrom
feature/shape-support

Conversation

@jimsafley

@jimsafley jimsafley commented May 7, 2026

Copy link
Copy Markdown
Member

Extends location support beyond point markers to include polygons, polylines, and rectangles. Shapes can be drawn, edited, labeled, and deleted on the item edit map, and render natively on browse, item show, and exhibit maps. Existing point locations and the geographic radius search are unaffected.

This release also redesigns map popups to match Omeka S Mapping style, and replaces the term "balloon" with "popup" throughout.

jimsafley added 6 commits May 6, 2026 23:51
Stores all location types as GeoJSON in a new geometry_json column,
with latitude and longitude computed from it for backward-compatible
address search. Enables the Leaflet.draw shape tools on the edit form,
adds Leaflet.deflate to collapse small shapes at low zoom, and updates
the browse map, item show page, exhibit layout, and static site export
to render shapes alongside point markers.
'Balloon' is legacy terminology; 'popup' is the standard term used
throughout the mapping library. Also switches CSS class names from
underscores to hyphens to match standard convention.
Add a gray full-bleed header (label or item title) to browse, item
show, and exhibit popups. Browse popup order: header → thumbnail →
title link → snippet. Exhibit popup: header → attachment body →
title link. Scope CSS with :has() to avoid affecting edit-form popups.
Remove theme-inherited dotted border-bottom from popup links.
Store the bounding box center as latitude/longitude on shape
_locationData so that _mapForm has coordinates to seed the initial
map view when re-rendering after a failed save. Previously, shapes
had no lat/lng in _locationData, causing the map to center at 0°N 0°E.
@jimsafley jimsafley requested a review from zerocrates May 7, 2026 21:10
@zerocrates zerocrates changed the base branch from master to feature/multi-location May 8, 2026 15:13
@zerocrates

Copy link
Copy Markdown
Member

I changed the base so this will be easier to look at in the interface.

jimsafley added 3 commits May 8, 2026 12:30
Simplify the listener that calls popup.update() after images load,
removing the dependency on Leaflet's private _panAnim API. Move it
from addMarker into addLayerFromGeometry so shapes get the same
treatment as markers.
Moves SR announcement logic from addMarker into
addLayerFromGeometry so it covers both markers and shapes.
Shapes now announce title, bounding box center coordinates,
and open/close status, consistent with markers.
The previous check accepted values like {"type":"Point"} (no coordinates)
or {"type":"LineString","coordinates":[]} which would cause PHP warnings
in beforeSave() and could crash the edit form when loaded.
@ebellempire ebellempire mentioned this pull request May 14, 2026

@zerocrates zerocrates left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this looks pretty good.

Only thing not really in the individual comments is, I have a vague sense that there's maybe more special-casing for Point then there needs to be... I know it needs some stuff like zoom/fit handling but there are some places, like loading in the current values when creating the form, when i would have thought you could just roll in the locations all with the same codepath using the json, rather than needing to call two different methods depending on whether it's a point or shape.


OmekaMap.prototype = {

map: null,

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why drop these initializations? I feel like it's easier to be able to just see what's available on the map object at a glance.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Restored them. Done in the "restore map prototype fields" commit.

Comment thread views/shared/javascripts/map.js Outdated

link.appendTo(listElement);
listElement.appendTo(list);
jQuery.each(this.deflateGroup.getLayers(), function (index, layer) {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This looks like it will make the list have all the shapes come after the points, is that what happens?

My assumption is that we don't really need markers as a markers-only array anymore and having it have all the locations makes more sense vs. iterating over two separate structures.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, the old code listed all points then all shapes. Fixed: this.markers is now this.locations and holds every layer (points and shapes) in load order, so buildListLinks iterates one structure and interleaves them.

// they cluster alongside point markers.
this.deflateGroup = L.deflate({
minSize: 10,
markerLayer: this.clusterGroup,

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This looks like it might break if clustering is disabled... or does null just work as a default here?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

null just works.

Comment thread GeolocationPlugin.php
queue_css_file('geolocation-marker', null, null, 'css', $version);
queue_js_file(['leaflet/leaflet', 'leaflet/leaflet-providers', 'leaflet-draw/leaflet.draw', 'map'], 'javascripts', [], $version);

if (get_option('geolocation_cluster')) {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why the switch to always load the clustering support?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Removed that override so the item-show map follows the geolocation_cluster option like the browse map, and reverted _head() to load markercluster only when clustering is enabled.

jimsafley added 3 commits July 3, 2026 11:00
Addresses review comments 1 and 2 on PR #67.

- Restore the at-a-glance property declarations on OmekaMap.prototype
  (updated to current names: locationBounds, deflateGroup, locations).
- Rename this.markers -> this.locations everywhere and push both point
  markers and shape layers into it, so the sidebar list is built from a
  single load-ordered structure (interleaving points and shapes) instead
  of iterating markers then deflate layers (shapes always listed last).
  buildListLinks branches on L.Marker for click behavior; _geolocationTitle
  is now set on every browse-map layer for uniform labeling.
Addresses review comments 3 and 4 on PR #67.

GeolocationMapSingle hard-coded cluster=true, overriding the admin's
geolocation_cluster setting and forcing markercluster to load on every
page. Drop the override so the item-show map defaults from the option
like the browse map, and load markercluster's CSS/JS only when clustering
is enabled (leaflet-deflate still loads unconditionally). Also expand the
L.deflate comment to note that markerLayer is safely null when clustering
is off (Leaflet.Deflate falls back to its own FeatureGroup).
Addresses the top-level review note on PR #67 about Point special-casing.

Collapse OmekaMapForm's separate addLocation(lat,lng,...) and
addShape(json,...) into a single addLocation(locationData) that builds
either a marker or a shape from geometry_json via L.GeoJSON.geometryToLayer.
Loading existing values, the draw CREATED handler, and the geocoder now all
call it with a location object instead of branching on point vs shape.
@jimsafley

Copy link
Copy Markdown
Member Author

I have a vague sense that there's maybe more special-casing for Point then there needs to be

Good call. I unified the edit form's two methods (addLocation/addShape) into one addLocation(locationData).

I did look at the read-only side (addMarker/addShapeLayer) too but deliberately left it split: there the two paths route to different layer groups, so the distinction is legitimate, I think.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants