Skip to content

Commit

Permalink
Fix #11487: Have josm render data to tiles
Browse files Browse the repository at this point in the history
This adds a new rendering method that renders async. This avoids blocking the UI.

Where this is useful:
* Large datasets (think county or country level)

Where this is ''not'' useful:
* Micromapping -- the tiles aren't being rendered ''exactly'' where they should
  be and there are some minor rendering artifacts.

Known issues:
* Some tiles aren't exactly where they should be (off by a pixel or two -- by
  default, we use the old render method at z16+)
* Rendering of tiles is slow -- there is ''some'' prework done to render tiles
  in batches. The primary reason rendering is slow is we are effectively
  rendering 25 total tiles (to avoid movement of text, we render 2 tiles in each
  directory and only keep the middle one)
* Due to the above speed issue, hovering over an object will cause the highlight
  to render in slowly.

New advanced preferences:
* `mappaint.fast_render.tile_size` -- controls the number of pixels in a tile
* `mappaint.fast_render.zlevel` -- controls the maximum z level at which tiles
  are generated

git-svn-id: https://josm.openstreetmap.de/svn/trunk@19176 0c6e7542-c601-0410-84e7-c038aed88b3b
  • Loading branch information
taylor.smock committed Aug 8, 2024
1 parent faad75b commit d8cb740
Show file tree
Hide file tree
Showing 8 changed files with 889 additions and 8 deletions.
69 changes: 69 additions & 0 deletions src/org/openstreetmap/josm/actions/TiledRenderToggleAction.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
// License: GPL. For details, see LICENSE file.
package org.openstreetmap.josm.actions;

import static org.openstreetmap.josm.tools.I18n.tr;

import java.awt.event.ActionEvent;
import java.awt.event.KeyEvent;

import org.openstreetmap.josm.data.osm.visitor.paint.MapRendererFactory;
import org.openstreetmap.josm.data.osm.visitor.paint.StyledMapRenderer;
import org.openstreetmap.josm.data.osm.visitor.paint.StyledTiledMapRenderer;
import org.openstreetmap.josm.gui.MainApplication;
import org.openstreetmap.josm.gui.layer.OsmDataLayer;
import org.openstreetmap.josm.tools.Shortcut;

/**
* This class enables and disables tiled rendering mode.
* This is intended to be short-term until the tiled rendering
* has no significant issues at high zoom levels.
* @since 19176
*/
public class TiledRenderToggleAction extends ToggleAction implements ExpertToggleAction.ExpertModeChangeListener {
/**
* Create a new action for toggling render methods
*/
public TiledRenderToggleAction() {
super(tr("Tiled Rendering"),
null,
tr("Enable/disable rendering the map in tiles"),
Shortcut.registerShortcut("menu:view:tiled", tr("View: {0}", tr("Tiled View")), KeyEvent.CHAR_UNDEFINED, Shortcut.NONE),
false /* register toolbar */
);
setToolbarId("tiledRendering");
MainApplication.getToolbar().register(this);
setSelected(false); // Always start disabled (until we are confident in the renderer)
if (MapRendererFactory.getInstance().isMapRendererActive(StyledTiledMapRenderer.class)) {
MapRendererFactory.getInstance().activate(StyledMapRenderer.class);
}
ExpertToggleAction.addExpertModeChangeListener(this, true);
}

@Override
protected boolean listenToSelectionChange() {
return false;
}

@Override
public void expertChanged(boolean isExpert) {
this.updateEnabledState();
}

@Override
protected void updateEnabledState() {
setEnabled(getLayerManager().getActiveData() != null && ExpertToggleAction.isExpert());
}

@Override
public void actionPerformed(ActionEvent e) {
toggleSelectedState(e);
if (isSelected()) {
MapRendererFactory.getInstance().activate(StyledTiledMapRenderer.class);
} else {
MapRendererFactory.getInstance().activate(StyledMapRenderer.class);
}

notifySelectedState();
getLayerManager().getLayersOfType(OsmDataLayer.class).forEach(OsmDataLayer::invalidate);
}
}
68 changes: 68 additions & 0 deletions src/org/openstreetmap/josm/data/osm/visitor/paint/ImageCache.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
// License: GPL. For details, see LICENSE file.
package org.openstreetmap.josm.data.osm.visitor.paint;

import java.awt.Image;

import jakarta.annotation.Nullable;

/**
* A record for keeping the image information for a tile. Used in conjunction with {@link TileZXY} for
* {@link org.openstreetmap.josm.data.cache.JCSCacheManager}.
* @since 19176
*/
public final class ImageCache {
private final boolean isDirty;
private final StyledTiledMapRenderer.TileLoader imageFuture;
private final Image image;
/**
* Create a new {@link ImageCache} object
* @param image The image to paint (optional; either this or {@link #imageFuture} must be specified)
* @param imageFuture The future for the image (optional; either this or {@link #image} must be specified)
* @param isDirty {@code true} if the tile needs to be repainted
*/
ImageCache(Image image, StyledTiledMapRenderer.TileLoader imageFuture, boolean isDirty) {
this.image = image;
this.imageFuture = imageFuture;
this.isDirty = isDirty;
if (image == null && imageFuture == null) {
throw new IllegalArgumentException("Either image or imageFuture must be non-null");
}
}

/**
* Check if this tile is dirty
* @return {@code true} if this is a dirty tile
*/
public boolean isDirty() {
return this.isDirty;
}

/**
* Mark this tile as dirty
* @return The tile to put in the cache
*/
public ImageCache becomeDirty() {
if (this.isDirty) {
return this;
}
return new ImageCache(this.image, this.imageFuture, true);
}

/**
* Get the image to paint
* @return The image (may be {@code null})
*/
@Nullable
public Image image() {
return this.image;
}

/**
* Get the image future
* @return The image future (may be {@code null})
*/
@Nullable
StyledTiledMapRenderer.TileLoader imageFuture() {
return this.imageFuture;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -191,6 +191,11 @@ private void registerDefaultRenderers() {
tr("Styled Map Renderer"),
tr("Renders the map using style rules in a set of style sheets.")
);
register(
StyledTiledMapRenderer.class,
tr("Styled Map Renderer (tiled)"),
tr("Renders the map using style rules in a set of style sheets by tile.")
);
}

/**
Expand Down Expand Up @@ -318,6 +323,17 @@ public List<Descriptor> getMapRendererDescriptors() {
* @return true, if currently the wireframe map renderer is active. Otherwise, false
*/
public boolean isWireframeMapRendererActive() {
return WireframeMapRenderer.class.equals(activeRenderer);
return isMapRendererActive(WireframeMapRenderer.class);
}

/**
* <p>Replies true, if currently the specified map renderer is active. Otherwise, false.</p>
*
* @param clazz The class that we are checking to see if it is the current renderer
* @return true, if currently the wireframe map renderer is active. Otherwise, false
* @since 19176
*/
public boolean isMapRendererActive(Class<? extends AbstractMapRenderer> clazz) {
return clazz.equals(activeRenderer);
}
}
Loading

0 comments on commit d8cb740

Please sign in to comment.