D3 renderer & GUI: file:// CORS fix, branch jitter, default GeoJSON, Summary-tree labels, flat output#48
Open
plemey wants to merge 15 commits into
Open
D3 renderer & GUI: file:// CORS fix, branch jitter, default GeoJSON, Summary-tree labels, flat output#48plemey wants to merge 15 commits into
plemey wants to merge 15 commits into
Conversation
The D3 renderer wrote the input JSON to a sibling data.json that main.js fetched via d3.json(). Opened from disk (file://), that XHR is blocked by CORS, so the visualization never loaded. main.js now carries a `var SPREAD3_DATA = null;` hook and a loadData() helper that uses the inlined data when present and otherwise falls back to fetching data.json (still works over HTTP). D3Renderer replaces the marker with the input JSON instead of copying a separate data.json. Also unblock the build on modern JDKs (incidental, required to compile): - build.xml: javac source/target 1.6 -> 1.8 (1.6 unsupported on JDK 19; StandardCharsets needs 1.7+) - remove dead, non-portable internal-JDK imports that fail or warn on newer JDKs: com.sun.corba...Util (ContinuousTreeParser) and com.sun.org.apache.xerces.internal...SynchronizedSymbolTable (MergePanel); neither was used. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…impose In discrete tree visualizations, several branches connecting the same two locations were drawn as identical SVG arcs (same endpoints, same radius, same sweep) and superimposed, so independent transitions between the same pair of locations could not be visually distinguished. generateLinesLayer now groups branches by their unordered location pair and gives each member of a group a distinct arc side (sweep flag) and a small radius multiplier, so they fan out: pairs split above/below the chord and same-side members nest at different curvatures. Singleton pairs and continuous branches (no locationId) are unchanged. The same per-branch side/multiplier is applied in the curvature-slider redraw, so dragging the slider keeps the fan. Controlled by a new `lineCurvatureJitter` (0 disables; larger spreads more). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Previously the "Load GeoJSON file:" control left settings.geojsonFilename null until the user manually picked a file, so no base map was shown unless one was loaded every time. data/geoJSON_maps/world.geojson is now bundled into the jar (build.xml copies it to geojson/world.geojson) and used as the default. Utils.getDefaultGeojsonPath() resolves it: from a packaged jar the resource is extracted to a temp file named world.geojson (so it can be read with a FileReader and the derived layer id stays "world.geojson"); from the IDE the data/ folder is used directly. The result is cached. All four input panels (Discrete, Continuous, TimeSlicer, BayesFactors) default settings.geojsonFilename to this path only when it is still null, so a manual Load always overrides it; the button shows the active file as a tooltip, updated on load. Jar grows ~3.5 -> 4.4 MB (the GeoJSON compresses well). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Relabel the user-facing input-type and analysis-type strings, since summary trees need not be MCC trees. Internal enum codes (MCC_TREE, DISCRETE_TREE, ...) are unchanged so behaviour and any persisted values are unaffected. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
In jar mode the renderer preserved the renderers/d3/d3renderer/ prefix from the jar entry names, so output landed in <output>/renderers/d3/d3renderer/. Strip that prefix so index.html and main.js are written directly into the user-selected folder, matching what the IDE code path already did. index.html references main.js by a relative path, so they only need to sit together. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Previously nothing indicated that world.geojson is loaded by default, so users were unaware a base map was already selected. The "Load GeoJSON file:" button now displays the active file's name; when it is the bundled default it is tagged "(default)", and the tag drops once the user loads another file. The full path remains in the tooltip. Applied to all four input panels. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The curvature slider recomputed each branch's stroke-dasharray (reveal mask) with getTotalLength() evaluated at the start of the d3 transition, i.e. against the old, shorter arc before it morphed to the new curvature. The mask was then too short for the new, longer arc, leaving the segment near the target unpainted so the branch appeared to stop short of its location (only when increasing curvature). Recompute the dash mask on the transition "end" event, from the final morphed path length, so it matches the new geometry. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
getRunningJarName() split the class URL on "/" and returned only the segment containing "!" minus its last char, i.e. just the jar's basename (e.g. "spreaD3.jar"). new JarFile(name) then resolved it against the current working directory, so rendering only worked when the app was launched from the jar's own directory and threw NoSuchFileException otherwise. Parse the jar's absolute path out of the "jar:file:/path/app.jar!/..." URL (via URI so encoded characters like spaces are handled), with a plain-string fallback. Verified: a headless render() now succeeds from an unrelated working directory, where it previously failed. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Both were ~2014 releases. These are drop-in upgrades to current stable versions (no API changes for the calls used here). Updated the bundled jars, build.xml zipgroupfileset includes, and the Eclipse .classpath entries. Verified end-to-end against the new jars: commons-io still drives the renderer file inlining (flat index.html/main.js written) and gson still parses the bundled world.geojson. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Joda-Time is superseded by the JDK's built-in java.time (available since Java 8, which the build now targets). Replaced all usages across TimeParser, DateEditor, JsonMerger and KmlRenderer: - DateTimeFormat.forPattern -> DateTimeFormatter.ofPattern - formatter.print(d) / d.toString -> d.format(formatter) - formatter.parseLocalDate / parseDateTime -> LocalDate.parse(s, formatter) - new LocalDate(y, m, d) -> LocalDate.of(...); getMonthOfYear -> getMonthValue - new LocalDate(java.util.Date) -> date.toInstant().atZone(...).toLocalDate() - KmlRenderer interval/Duration math reworked onto LocalDate.atStartOfDay() + java.time.Duration (between / ofMillis) Patterns use 'uuuu' (proleptic year), not 'yyyy' (year-of-era), because the code supports negative/zero years for ancient dates; 'yyyy' cannot represent them in java.time. Removed joda-time-2.7.jar and its build.xml / .classpath references. Verified: TimeParser round-trips slash, decimal and negative-year MRSD inputs (e.g. -100.5 -> -0100/06/01) and the project builds without the jar. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The desktop app rendered in a dated Metal/Nimbus look: on macOS it tried to load Quaqua (abandoned, and not even bundled), so it fell back to the default L&F. Set FlatLaf (FlatLightLaf) as the primary look & feel in Spread3UIApp, applied before any Swing components are created. The previous Quaqua (mac) / Nimbus (non-mac) / system-default attempts are kept, gated behind !lafLoaded, so they only run if FlatLaf is unavailable. The FlatLaf call is guarded with Throwable so a missing jar degrades gracefully. Add a View menu (ViewMenuFactory) with a "Dark theme" checkbox that swaps FlatLightLaf <-> FlatDarkLaf and calls FlatLaf.updateUI() to restyle all open windows live. Bundle flatlaf-3.7.1.jar (build.xml + .classpath). Verified: builds; FlatLaf loads as "FlatLaf Light"; toggle round-trips Light -> Dark -> Light headlessly. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The "Select location attribute" combo previously defaulted to the first annotation name. It now selects "location" by default, or "loc" as a second choice, when either is present in the tree's annotation list; otherwise the existing first-item default is kept. The default is applied after the listener is registered, so it also populates settings.locationAttributeName and advances to the coordinates step, as a manual pick would. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…attribute The previous change selected the default location attribute inside the background populate block, before the selector was added to the panel. That fired the selection listener too early (it runs resetFlags/removeChildComponents and adds "Setup location coordinates"), so the setup component appeared next to "Load tree file" and a manual re-selection was needed to place it correctly. Move the default selection into the SwingWorker's done() (EDT, after the selector is built and added). It now behaves exactly like a manual pick: the listener adds "Setup location coordinates" underneath the selector and sets settings.locationAttributeName, with no second selection required. Only fires when the preferred value isn't already selected. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The location coordinates editor built its top button bar from a JMenuBar used as a generic container (BorderLayout, holding a button panel, added to the dialog's NORTH -- never set as the window's menu bar). Under FlatLaf the menu-bar UI assumes real menus, so the arbitrary panel inside it was not painted until a hover/click forced a repaint; the previous Quaqua/Metal L&F tolerated the misuse. Use a plain JPanel instead -- the correct component for a row of buttons, which paints reliably under any look & feel. Same layout, no behaviour change; removed the now-unused JMenuBar import. This was the only such misuse in the codebase. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The main window opened at 1400x700, leaving a lot of empty horizontal space. Set the default width to 800 (height unchanged); the window stays resizable and the 300x200 minimum is unchanged. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
This PR bundles several independent renderer/UI improvements (plus the incidental
build fixes needed to compile/verify on a modern JDK). They are independent and
could be split if a reviewer prefers.
1. Fix
file://CORS error when loading a visualizationOpening a visualization from disk (
file://) failed: the browser blocked thedata.jsonXHR as a CORS violation. The data is now inlined intomain.js(
var SPREAD3_DATA = ...) via aloadData()helper that falls back to fetchingdata.jsonwhen not inlined;D3Renderersubstitutes the input JSON into thegenerated
main.jsinstead of copying a separatedata.json.2. Jitter the curvature of overlapping discrete branches
Branches connecting the same two locations were drawn as identical arcs and
superimposed.
generateLinesLayergroups branches by their unordered locationpair and gives each a distinct arc side (sweep flag) and radius multiplier
so they fan out. Singletons/continuous branches unchanged. New
lineCurvatureJitter.3. Load a bundled world.geojson by default
data/geoJSON_maps/world.geojsonis bundled into the jar and used as the defaultfor "Load GeoJSON file:".
Utils.getDefaultGeojsonPath()resolves it (jar →extract to temp; IDE →
data/; cached). Panels default to it only when null, so amanual Load overrides it. Note: the field now defaults non-null (a world layer
is always included unless another file is loaded; no in-GUI "none" option yet).
4. Indicate the active GeoJSON on the Load button
So users know a default is already loaded, the "Load GeoJSON file:" button now
shows the active file's name, tagged "(default)" when it is the bundled world map;
the tag drops once another file is loaded. Full path stays in the tooltip.
5. Rename "MCC tree" → "Summary tree" in the GUI
Relabel the user-facing input-/analysis-type strings (summary trees need not be
MCC). Internal enum codes (
MCC_TREE, ...) unchanged.6. Write output directly into the selected folder
Jar mode preserved the
renderers/d3/d3renderer/prefix from jar entry names,nesting output deeply. That prefix is stripped so
index.html/main.jslanddirectly in the user-selected folder, matching the IDE path.
7. Fix branches painted short of the location after increasing curvature
The curvature slider recomputed each branch's
stroke-dasharray(reveal mask)with
getTotalLength()measured at the start of the transition — against theold, shorter arc. The mask was then too short for the new, longer arc, leaving the
segment near the target unpainted (branch appears to stop short). The mask is now
recomputed on the transition
endevent from the final morphed length.9. Bump gson 2.3.1 -> 2.11.0 and commons-io 2.4 -> 2.16.1
Both were ~2014 releases; upgraded to current stable versions (drop-in, no API
changes for the calls used). Updated the bundled jars, the build.xml includes and
the Eclipse
.classpath. Verified end-to-end: commons-io drives the rendererfile inlining and gson parses the bundled world.geojson against the new jars.
10. Migrate Joda-Time to java.time (drop the dependency)
Joda-Time (2014) is superseded by the JDK's built-in
java.time. Migrated allusages across
TimeParser,DateEditor,JsonMergerandKmlRenderer(formatter/parse/print,
LocalDate.of,getMonthValue,Date→LocalDate, andKmlRenderer's interval/Duration math) and removed
joda-time-2.7.jar. Patternsuse
uuuu(proleptic year) notyyyy(year-of-era), so the existingnegative/zero-year (ancient date) support is preserved. Verified
TimeParserround-trips slash, decimal and negative-year inputs (e.g.
-100.5→-0100/06/01).11. Modernize the Swing look & feel (FlatLaf + light/dark toggle)
The desktop app rendered in a dated Metal/Nimbus look (on macOS it tried the
abandoned, unbundled Quaqua L&F and fell back to the default). FlatLaf
(
FlatLightLaf) is now the primary look & feel, set before any Swing componentsare created; the previous Quaqua/Nimbus/system-default attempts are kept as
fallbacks (gated behind
!lafLoaded), and the FlatLaf call is guarded withThrowableso a missing jar degrades gracefully. A new View menu adds aDark theme checkbox that swaps
FlatLightLaf<->FlatDarkLafand callsFlatLaf.updateUI()to restyle all open windows live. Bundlesflatlaf-3.7.1.jar.Independent of the deferred d3 renderer work.
12. Default the discrete location attribute to "location" / "loc"
The "Select location attribute" combo (discrete tree panel) previously defaulted
to the first annotation name. It now selects
locationby default, orlocas asecond choice, when present in the tree's annotation list (otherwise the first
item is kept). The selection is applied on the EDT in the tree-load worker's
done()(after theselector is built and added), so it behaves exactly like a manual pick: the
"Setup location coordinates" component appears underneath and
settings.locationAttributeNameis set, with no second selection required.13. Fix the Setup-locations window not rendering under FlatLaf
The location coordinates editor built its top button bar from a
JMenuBarusedas a generic container (added to the dialog's NORTH, never set as the window's
menu bar). Under FlatLaf the menu-bar UI assumes real menus, so the panel inside
it was not painted until a hover/click forced a repaint (the previous L&F
tolerated the misuse). Replaced it with a plain
JPanel-- same layout, nobehaviour change. This was the only such misuse in the codebase.
14. Narrow the default main window (1400 -> 800)
The main window opened at 1400x700, leaving a lot of empty horizontal space. The
default width is now 800 (height unchanged); the window stays resizable and the
300x200 minimum is unchanged.
Incidental: unblock the build on modern JDKs
build.xml:javac source/target1.6→1.8.com.sun.corba...UtilinContinuousTreeParser,com.sun.org.apache.xerces.internal...SynchronizedSymbolTablein
MergePanel) that were unused and break/warn on newer JDKs.Testing
ant build→ BUILD SUCCESSFUL; jar contains patchedmain.jsandgeojson/world.geojson.node --checkpasses on the template and a simulated inlined bundle.parseGeoJSON()).render()writes<output>/index.html+main.jswith data inlined).8. Resolve the running jar by absolute path
getRunningJarName()returned only the jar's basename, sonew JarFile(name)resolved it against the working directory — rendering only worked when the app
was launched from the jar's own directory and threw
NoSuchFileExceptionotherwise. The absolute path is now parsed from the
jar:file:/...!/...URL(via
URI, so encoded chars are handled), with a string fallback. Verified: aheadless
render()now succeeds from an unrelated working directory.Follow-up (deferred): d3 v3.5 -> v7 renderer upgrade
The renderer bundle (
src/renderers/d3/d3renderer/main.js) embeds d3 v3.5.17and jQuery v1.4.2. Upgrading is deliberately out of scope for this PR because
it is a standalone project, not an in-repo edit:
phylogeography/d3-renderer(a webpack project, last touched Feb 2017). This repo contains only the built
artifact. The app code to migrate is ~120 KB across ~11 modules
(lines / topo / points / areas / counts / main / locations / time / ...), plus
three vendored components (
d3-legend,jquery.simple-color,d3.slider).full v3 API:
d3.scale.*,d3.geo.*,d3.svg.*,d3.layout.hierarchy,d3.behavior.drag, callback-styled3.json) and webpack 1.9 -> 5 (configgit+ssh).d3.sliderhas no v4+ release (replace or rewrite);d3-svg-legendchanged APIs.file://CORS failure, so the remaining motivation is maintainability.Recommended approach: a separate PR against
phylogeography/d3-renderer(port themodules onto a fresh webpack 5 + d3 v7 skeleton, replace the dead vendored
components), with visual regression testing across all four output types
(discrete, continuous, time-slicer, bayes), then drop the rebuilt bundle into
SpreaD3.