Class MapView2D

All Implemented Interfaces:
IFeedbackProvider, IFileDropHandler, ComponentListener, FocusListener, MouseListener, ImageObserver, MenuContainer, Serializable, EventListener, Accessible, RootPaneContainer, WindowConstants
Direct Known Subclasses:
DemoMapView

public class MapView2D extends BaseView
A two-dimensional map view that renders world maps using configurable projections and themes.

Responsibilities

Shared vs. per-view data

Country and city data were previously stored in static fields, which created a hidden singleton: all MapView2D instances shared — and could inadvertently overwrite — the same dataset. The fields are now instance fields so that multiple views can independently hold different datasets. The static setter methods (setCountries(java.util.List<edu.cnu.mdi.mapping.loader.GeoJsonCountryLoader.CountryFeature>), setCities(java.util.List<edu.cnu.mdi.mapping.loader.GeoJsonCityLoader.CityFeature>)) are retained for backwards compatibility; they set the data on the most-recently-constructed instance through a static reference that is updated in the constructor. Callers that only ever create one view are unaffected.

Side panel layout

The control panel (MapControlPanel) and feedback pane (FeedbackPane) are placed together in a combined east-side strip whose preferred width in pixels is controlled by getSidePanelWidth().

See Also:
  • Field Details

    • LAT_PREFIX

      protected static final String LAT_PREFIX
      Feedback-pane prefix used for latitude values.
      See Also:
    • LON_PREFIX

      protected static final String LON_PREFIX
      Feedback-pane prefix used for longitude values.
      See Also:
    • DEG

      protected static final String DEG
      Unicode degree symbol used by geographic feedback text.
      See Also:
    • controlPanel

      protected MapControlPanel controlPanel
      Default side control panel. May be null if a subclass replaces the control panel with a custom component.
    • latLon

      protected final Point2D.Double latLon
      Reusable lat/lon workspace for the feedback method.
  • Constructor Details

  • Method Details

    • getCountryRenderer

      protected CountryRenderer getCountryRenderer()
      Returns the current country renderer.
      Returns:
      current renderer, or null before country data are loaded
    • installEtopo5Layer

      protected Layer installEtopo5Layer(Etopo5Loader loader, boolean initiallyShown)
      Installs ETOPO5 terrain and bathymetry into the standard ETOPO5 layer.
      Parameters:
      loader - loaded ETOPO5 data
      initiallyShown - initial layer visibility
      Returns:
      the ETOPO5 layer
    • useStandardGraticules

      protected boolean useStandardGraticules()
      Returns whether the standard graticule layer should be included in this view.

      Subclasses that don't want the standard graticule can override this to return false and then provide their own graticule implementation as an extra layer.

      Returns:
      true to include the standard graticule, false to omit it
    • getSidePanelWidth

      protected int getSidePanelWidth()
      Gets the preferred width of the east-side strip containing the map control panel, custom application panels, and feedback pane.

      Subclasses may override this to request a wider or narrower side panel. The override should not depend on subclass instance fields, because this method is called during MapView2D construction before the subclass constructor body has run. Returning a literal or subclass static constant is safe.

      Returns:
      preferred side-panel width in pixels
    • getElevation

      public double getElevation(double lat, double lon)
      Returns interpolated elevation or bathymetry at a geographic location.

      Elevation remains available even when the ETOPO5 layer is hidden. Layer visibility is a presentation choice and should not affect calculations such as radar line-of-sight or land-height feedback.

      Parameters:
      lat - latitude in degrees
      lon - longitude in degrees
      Returns:
      elevation in metres, or NaN if ETOPO5 is not installed or either coordinate is non-finite
    • setCountries

      public void setCountries(List<GeoJsonCountryLoader.CountryFeature> countries)
      Sets the country features rendered by the standard country-fill and country-boundary layers.
      Parameters:
      countries - country features; must not be null
    • setCities

      public void setCities(List<GeoJsonCityLoader.CityFeature> cities)
      Sets the city features used by this view.

      The features are rendered by the standard Cities MDI layer. If the active projection already exists, the city renderer is rebuilt immediately.

      Parameters:
      cities - list of city features; must not be null
    • getProjection

      public IMapProjection getProjection()
      Returns the currently active map projection.
      Returns:
      the active IMapProjection; never null after construction
    • setProjection

      public void setProjection(EProjection projectionType)
      Switches the active projection to one of MDI's built-in projection types, rebuilding all dependent renderers and resetting the container's world coordinate system.

      The new projection is created by ProjectionFactory using the theme currently selected in the control panel, so the visual style is preserved across projection changes.

      Country and city data must have been set (via setCountries(java.util.List<edu.cnu.mdi.mapping.loader.GeoJsonCountryLoader.CountryFeature>) and setCities(java.util.List<edu.cnu.mdi.mapping.loader.GeoJsonCityLoader.CityFeature>)) before calling this method; if either list is null the corresponding renderer is skipped.

      This method preserves the existing MDI enum-based projection API. For application-supplied projections, use setProjection(IMapProjection).

      Parameters:
      projectionType - the new built-in projection type; must not be null
    • getCityRenderer

      protected CityPointRenderer getCityRenderer()
      Returns the active CityPointRenderer.

      May be null if setCities(List) has not been called yet. MapControlPanel null-checks before using this.

      Returns:
      the city renderer, or null
    • getGraticuleRenderer

      protected GraticuleRenderer getGraticuleRenderer()
      Returns the active graticule renderer.

      Used by MapControlPanel to toggle adaptive spacing and edge labels. The instance is replaced whenever the projection changes (see setProjection(edu.cnu.mdi.mapping.projection.EProjection)), so callers should re-read it rather than caching the reference; the view re-applies the persisted toggle state to each newly built renderer, so the user's choices survive a projection switch.

      Returns:
      the graticule renderer; never null after construction
    • setGraticuleAdaptive

      protected void setGraticuleAdaptive(boolean adaptive)
      Enables or disables zoom-adaptive graticule spacing, persisting the choice across projection switches and refreshing the view.
      Parameters:
      adaptive - true for adaptive spacing; false for the fixed step
    • setGraticuleLabels

      protected void setGraticuleLabels(boolean labels)
      Enables or disables graticule edge labels, persisting the choice across projection switches and refreshing the view.
      Parameters:
      labels - true to draw coordinate labels along the viewport edges
    • getMapProjection

      protected IMapProjection getMapProjection()
      Returns the active map projection.

      Equivalent to getProjection() but package-accessible without an explicit cast; used by MapControlPanel.

      Returns:
      the active projection; never null after construction
    • getCountryCount

      public int getCountryCount()
      Returns the number of countries currently loaded in this view.

      Used by MapViewInfo.getTechnicalNotes() to produce a dynamic count rather than a hardcoded string.

      Returns:
      country count, or 0 if data has not been set
    • getCityCount

      public int getCityCount()
      Returns the number of cities currently loaded in this view.

      Used by MapViewInfo.getTechnicalNotes() to produce a dynamic count rather than a hardcoded string.

      Returns:
      city count, or 0 if data has not been set
    • addShapefile

      public void addShapefile(ShapeFeatureRenderer renderer)
      Appends a ShapeFeatureRenderer to the end of the extra-layer list. Extra layers are drawn after country polygons and before city markers, in the order they were added.

      Typical use:

      
       List<ShapeFeature> rivers = new ShapefileFeatureLoader().load(Path.of("ne_10m_rivers_lake_centerlines.shp"));
       ShapeFeatureStyle style = new ShapeFeatureStyle().strokeColor(new Color(0x6B9FD4)).strokeWidth(0.8f);
       mapView.addLayer(new ShapeFeatureRenderer(rivers, mapView.getProjection(), style));
       
      Parameters:
      renderer - the layer to add; must not be null
    • addShapefile

      public void addShapefile(ShapeFeatureRenderer renderer, String name)
      Adds a named shapefile renderer as an editable MDI layer.
      Parameters:
      renderer - shapefile renderer
      name - layer display name
    • loadShapefileLayer

      public static List<ShapeFeature> loadShapefileLayer(MapView2D mapView, Path path, String name, ShapeFeatureStyle style)
      Convenience method that loads a shapefile from the given path, creates a ShapeFeatureRenderer with the specified style, and adds it as a new layer with the given name.

      Exceptions are caught and logged to the console; the method does not throw.

      Parameters:
      mapView - the view to add the layer to; must not be null
      path - filesystem path to the shapefile; must not be null
      name - display name shown in the Shapefiles menu; must not be null
      style - rendering style for the shapefile features; must not be null
    • removeLayer

      public void removeLayer(ShapeFeatureRenderer renderer)
      Removes the shapefile layer hosting the supplied renderer.
      Parameters:
      renderer - renderer whose layer should be removed
    • clearLayers

      public void clearLayers()
      Removes all extra layers added via addLayer. The base layers (countries, cities, graticule) are unaffected.
    • drawSymbol

      public void drawSymbol(Graphics2D g, double lat, double lon, SymbolType type, int size, Color lineColor, Color fillColor)
      Draws a map symbol at the given latitude and longitude using the active projection to convert to screen coordinates.

      Used by MapControlPanel to draw the projection center marker. Can also be used by application code to draw symbols on top of the map without managing coordinate transforms.

      Parameters:
      g - graphics context to draw on; must not be null
      lat - latitude in degrees; must be between -90 and 90
      lon - longitude in degrees; must be between -180 and 180
      type - symbol type; must not be null
      size - symbol size in pixels; must be positive
      lineColor - color for symbol outlines and lines; must not be null
      fillColor - color for symbol fills; must not be null
    • getLayers

      public List<ShapefileLayer> getLayers()
      Returns the imported shapefile layers.
      Returns:
      unmodifiable layer list
    • getViewInfo

      public AbstractViewInfo getViewInfo()
      Override to supply structured information for the info dialog. The default returns null (no info available).

      Returns a MapViewInfo bound to this view so that the info dialog can display live dataset counts rather than hardcoded strings.

      Overrides:
      getViewInfo in class BaseView
      Returns:
      view info, or null
    • getCountryAtPoint

      public String getCountryAtPoint(Point pp, IContainer container)
      Returns the name of the country under the given screen-space point, or null if no country is found.

      Delegates to CountryRenderer.pickCountry(java.awt.Point, edu.cnu.mdi.container.IContainer) and formats the result as "Admin Name (ISO3)" when a hit is found.

      Parameters:
      pp - screen-space point to test; must not be null
      container - container providing the coordinate transform
      Returns:
      formatted country name string, or null
    • onLand

      public boolean onLand(Point pp, IContainer container)
      Returns whether the given screen-space point is on land (inside a country polygon).

      Delegates to CountryRenderer.pickCountry(java.awt.Point, edu.cnu.mdi.container.IContainer) and returns true if a hit is found.

      Parameters:
      pp - screen-space point to test; must not be null
      container - container providing the coordinate transform
      Returns:
      true if the point is on land, false otherwise
    • localToLatLonDeg

      public void localToLatLonDeg(Point pp, Point2D.Double latLon)
      Converts a screen-space point to latitude and longitude in degrees.

      Delegates to MapContainer.localToLatLon(java.awt.Point, java.awt.geom.Point2D.Double) for the actual conversion.

      Parameters:
      pp - screen-space point to convert; must not be null
      latLon - output parameter to receive the latitude and longitude in degrees; must not be null
    • getFeedbackStrings

      public void getFeedbackStrings(IContainer container, Point pp, Point2D.Double wp, List<String> feedbackStrings)
      Default feedback implementation: no-op. Subclasses override to provide coordinate or status strings.

      Appends the following strings (in order) to feedbackStrings:

      1. Number of countries loaded.
      2. Number of cities loaded.
      3. Active projection name.
      4. Screen coordinates of the cursor.
      5. World (projection-space) coordinates.
      6. Latitude and longitude in degrees (only when cursor is on map).
      7. Picked country name and ISO code (only when cursor is on a country polygon).
      8. Styled feedback text from any extra layers (ShapeFeatureRenderer) whose IPickable.pick(java.awt.Point, edu.cnu.mdi.container.IContainer) returns a non-null result. Each layer contributes at most one string; all hit layers are reported.
      9. Picked city name and population (only when cursor is near a city dot).
      Specified by:
      getFeedbackStrings in interface IFeedbackProvider
      Overrides:
      getFeedbackStrings in class BaseView
      Parameters:
      container - the container.
      pp - the mouse location.
      wp - the corresponding world point.
      feedbackStrings - the List of feedback strings to add to.
    • prepareForExit

      public void prepareForExit()
      Releases resources held by the MapContainer when the view is closing.
      Overrides:
      prepareForExit in class BaseView
    • filesDropped

      public void filesDropped(List<File> files)
      Loads each dropped .shp file as a new layer, exactly as though it had been chosen via the Shapefiles menu's "Open Shapefile..." action: the display name is the filename without its extension (ShapefileMenu.baseName(Path)), the style is chosen from the first feature's geometry type (ShapefileMenu.defaultStyle(int)), and a successful load is recorded in the same "Recent Shapefiles" list the menu uses (ShapefileMenu.recordRecentlyOpened(File)).

      Only invoked when BaseView.enableFileDrop(Predicate) was called in the constructor, which it is unless the subclass opted out of shapefile functionality via includeShapeFileMenu(). A failure to load one dropped file is logged and does not prevent the rest of the batch from loading.

      Specified by:
      filesDropped in interface IFileDropHandler
      Overrides:
      filesDropped in class BaseView
      Parameters:
      files - the accepted dropped files; never null, never empty
    • includeShapeFileMenu

      protected boolean includeShapeFileMenu()
      Returns whether the Shapefile menu should be included in this view's menu bar. Subclasses that don't support shapefile layers can override this to return false to exclude the menu and its associated functionality.
      Returns:
      true to include the Shapefile menu, false to omit it
    • drawCustomMapContent

      protected void drawCustomMapContent(Graphics2D g2, IContainer container)
      Placeholder method for subclasses to override and draw custom map content after the standard layers. Not called by default since most views won't need it, but available for extensibility when needed.

      This is not registered as an after-draw IDrawable since that would require a separate registration and coordinate transform for each subclass; instead, subclasses can call this method directly from their own after-draw implementation after calling super.drawCustomMapContent(g2).

      Parameters:
      g2 - the Graphics2D context to draw on
    • getCurrentMapTheme

      public MapTheme getCurrentMapTheme()
      Returns the map theme that should be used when creating a new projection.

      If the standard MapControlPanel is installed, this delegates to that panel. If a subclass has replaced the control panel, this falls back to the active projection's theme, and finally to the light theme.

      Returns:
      the current map theme
    • setProjection

      public void setProjection(IMapProjection newProjection)
      Switches the active projection to an application-supplied projection.

      This is the extensibility path for projections that are not represented by EProjection. The supplied projection must be fully initialized, including its MapTheme. The view rebuilds its graticule, country, city, and extra-layer renderers and resets the world coordinate system using the projection's own IMapProjection.getXYBounds().

      Parameters:
      newProjection - the new projection; must not be null
    • setMapControlPanel

      protected void setMapControlPanel(JPanel newControlPanel)
      Replaces the standard map control panel with a subclass/application supplied component.

      This method is intended for subclasses such as Mosaic views that want their own projection selector and display controls. It is safe to call from the subclass constructor after super(...) returns.

      Parameters:
      newControlPanel - the replacement control panel; must not be null
    • getMapControlPanel

      public MapControlPanel getMapControlPanel()
      Returns the standard map control panel, or null if a subclass has replaced it with a custom component.
      Returns:
      the map control panel, or null
    • addCustomSidePanelComponent

      protected void addCustomSidePanelComponent(JPanel component)
      Adds an application-supplied component to the side panel, below the map control panel and above the feedback pane.

      This is intended for domain-specific controls such as Mosaic buttons for Monte Carlo points, prepatches, theta patches, final patches, and patch labels.

      Parameters:
      component - the component to add; ignored if null
    • getEtopo5Layer

      public Layer getEtopo5Layer()
      Returns the ETOPO5 layer.
      Returns:
      ETOPO5 layer, or null if not installed
    • getEtopo5Renderer

      protected Etopo5Renderer getEtopo5Renderer()
      Returns the ETOPO5 renderer.
      Returns:
      renderer, or null if not installed
    • getCountryFillLayer

      public Layer getCountryFillLayer()
      Returns the country-fill layer.
      Returns:
      country-fill layer
    • getCountryBoundaryLayer

      public Layer getCountryBoundaryLayer()
      Returns the country-boundary layer.
      Returns:
      country-boundary layer
    • getCityLayer

      public Layer getCityLayer()
      Returns the standard city layer.
      Returns:
      city layer, or null if city data have not been set
    • getGraticuleLayer

      public Layer getGraticuleLayer()
      Returns the standard graticule layer.
      Returns:
      the graticule layer; never null after construction
    • hoverUpdate

      public void hoverUpdate(HoverEvent he)
      Handle a hover event.

      Subclasses can override this method to respond to hover events, which are triggered by mouse movement with no buttons down. The default implementation is a no-op.

      When the user hovers over the map, this method displays a small window with the country name and any configured shapefile feedback fields found under the cursor. The information is shown in the HoverInfoWindow provided by the MapContainer.

      Overrides:
      hoverUpdate in class BaseView
      Parameters:
      he - the hover event
    • getMapHoverText

      protected String getMapHoverText(Point point, IContainer sourceContainer)
      Builds the unified country and shapefile text shown by the hover popup. Registered shapefile feedback fields are included in layer order.
      Parameters:
      point - cursor location in container coordinates
      sourceContainer - container used for hit testing
      Returns:
      plain, possibly multi-line hover text, or null for no hit
    • isHoveringEnabled

      public boolean isHoveringEnabled()
      Returns whether delayed mouse-over feedback windows are enabled. Hover feedback is disabled by default.
      Returns:
      true when hover feedback is enabled
    • setHoveringEnabled

      public void setHoveringEnabled(boolean enabled)
      Enables or disables delayed mouse-over feedback windows. The feature is disabled by default. Disabling it immediately hides any popup that is currently visible.
      Parameters:
      enabled - true to enable map hovering
    • clearCustomSidePanelComponents

      protected void clearCustomSidePanelComponents()
      Removes all custom application components from the side panel.