- Timestamp:
- 2024-08-08T23:04:30+02:00 (3 months ago)
- Location:
- trunk/src/org/openstreetmap/josm
- Files:
-
- 4 added
- 4 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/src/org/openstreetmap/josm/data/osm/visitor/paint/MapRendererFactory.java
r17333 r19176 192 192 tr("Renders the map using style rules in a set of style sheets.") 193 193 ); 194 register( 195 StyledTiledMapRenderer.class, 196 tr("Styled Map Renderer (tiled)"), 197 tr("Renders the map using style rules in a set of style sheets by tile.") 198 ); 194 199 } 195 200 … … 319 324 */ 320 325 public boolean isWireframeMapRendererActive() { 321 return WireframeMapRenderer.class.equals(activeRenderer); 326 return isMapRendererActive(WireframeMapRenderer.class); 327 } 328 329 /** 330 * <p>Replies true, if currently the specified map renderer is active. Otherwise, false.</p> 331 * 332 * @param clazz The class that we are checking to see if it is the current renderer 333 * @return true, if currently the wireframe map renderer is active. Otherwise, false 334 * @since 19176 335 */ 336 public boolean isMapRendererActive(Class<? extends AbstractMapRenderer> clazz) { 337 return clazz.equals(activeRenderer); 322 338 } 323 339 } -
trunk/src/org/openstreetmap/josm/gui/MainMenu.java
r18814 r19176 104 104 import org.openstreetmap.josm.actions.SplitWayAction; 105 105 import org.openstreetmap.josm.actions.TaggingPresetSearchAction; 106 import org.openstreetmap.josm.actions.TiledRenderToggleAction; 106 107 import org.openstreetmap.josm.actions.UnGlueAction; 107 108 import org.openstreetmap.josm.actions.UnJoinNodeWayAction; … … 249 250 /** View / Wireframe View */ 250 251 public final WireframeToggleAction wireFrameToggleAction = new WireframeToggleAction(); 252 /** View / Tiled Rendering */ 253 public final TiledRenderToggleAction tiledRenderToggleAction = new TiledRenderToggleAction(); 251 254 /** View / Hatch area outside download */ 252 255 public final DrawBoundariesOfDownloadedDataAction drawBoundariesOfDownloadedDataAction = new DrawBoundariesOfDownloadedDataAction(); … … 800 803 wireframe.setAccelerator(wireFrameToggleAction.getShortcut().getKeyStroke()); 801 804 wireFrameToggleAction.addButtonModel(wireframe.getModel()); 805 // -- tiled render toggle action -- not intended to be permanently an "Expert" mode option 806 final JCheckBoxMenuItem tiledRender = new JCheckBoxMenuItem(tiledRenderToggleAction); 807 viewMenu.add(tiledRender); 808 tiledRenderToggleAction.addButtonModel(tiledRender.getModel()); 809 ExpertToggleAction.addVisibilitySwitcher(tiledRender); 810 // -- hatch toggle action 802 811 final JCheckBoxMenuItem hatchAreaOutsideDownloadMenuItem = drawBoundariesOfDownloadedDataAction.getCheckbox(); 803 812 viewMenu.add(hatchAreaOutsideDownloadMenuItem); -
trunk/src/org/openstreetmap/josm/gui/MapView.java
r19108 r19176 7 7 import java.awt.BasicStroke; 8 8 import java.awt.Color; 9 import java.awt.Component; 9 10 import java.awt.Dimension; 10 11 import java.awt.Graphics; 11 12 import java.awt.Graphics2D; 13 import java.awt.GraphicsEnvironment; 12 14 import java.awt.Point; 13 15 import java.awt.Rectangle; 14 16 import java.awt.Shape; 15 17 import java.awt.Stroke; 18 import java.awt.Transparency; 16 19 import java.awt.event.ComponentAdapter; 17 20 import java.awt.event.ComponentEvent; … … 331 334 } 332 335 336 private static BufferedImage getAcceleratedImage(Component mv, int width, int height) { 337 if (GraphicsEnvironment.isHeadless()) { 338 return new BufferedImage(width, height, BufferedImage.TYPE_3BYTE_BGR); 339 } 340 return mv.getGraphicsConfiguration().createCompatibleImage(width, height, Transparency.OPAQUE); 341 } 342 333 343 // remebered geometry of the component 334 344 private Dimension oldSize; … … 549 559 550 560 if (null == offscreenBuffer || offscreenBuffer.getWidth() != width || offscreenBuffer.getHeight() != height) { 551 offscreenBuffer = new BufferedImage(width, height, BufferedImage.TYPE_3BYTE_BGR);561 offscreenBuffer = getAcceleratedImage(this, width, height); 552 562 } 553 563 … … 555 565 if (null == nonChangedLayersBuffer 556 566 || nonChangedLayersBuffer.getWidth() != width || nonChangedLayersBuffer.getHeight() != height) { 557 nonChangedLayersBuffer = new BufferedImage(width, height, BufferedImage.TYPE_3BYTE_BGR);567 nonChangedLayersBuffer = getAcceleratedImage(this, width, height); 558 568 } 559 569 Graphics2D g2 = nonChangedLayersBuffer.createGraphics(); -
trunk/src/org/openstreetmap/josm/gui/layer/OsmDataLayer.java
r19116 r19176 50 50 import javax.swing.JScrollPane; 51 51 52 import org.apache.commons.jcs3.access.CacheAccess; 53 import org.openstreetmap.gui.jmapviewer.OsmMercator; 52 54 import org.openstreetmap.josm.actions.AutoScaleAction; 53 55 import org.openstreetmap.josm.actions.ExpertToggleAction; … … 57 59 import org.openstreetmap.josm.data.Bounds; 58 60 import org.openstreetmap.josm.data.Data; 61 import org.openstreetmap.josm.data.IBounds; 59 62 import org.openstreetmap.josm.data.ProjectionBounds; 60 63 import org.openstreetmap.josm.data.UndoRedoHandler; 64 import org.openstreetmap.josm.data.cache.JCSCacheManager; 61 65 import org.openstreetmap.josm.data.conflict.Conflict; 62 66 import org.openstreetmap.josm.data.conflict.ConflictCollection; … … 71 75 import org.openstreetmap.josm.data.gpx.IGpxTrackSegment; 72 76 import org.openstreetmap.josm.data.gpx.WayPoint; 77 import org.openstreetmap.josm.data.osm.BBox; 73 78 import org.openstreetmap.josm.data.osm.DataIntegrityProblemException; 74 79 import org.openstreetmap.josm.data.osm.DataSelectionListener; … … 78 83 import org.openstreetmap.josm.data.osm.DownloadPolicy; 79 84 import org.openstreetmap.josm.data.osm.HighlightUpdateListener; 85 import org.openstreetmap.josm.data.osm.INode; 80 86 import org.openstreetmap.josm.data.osm.IPrimitive; 87 import org.openstreetmap.josm.data.osm.IRelation; 88 import org.openstreetmap.josm.data.osm.IWay; 81 89 import org.openstreetmap.josm.data.osm.Node; 82 90 import org.openstreetmap.josm.data.osm.OsmPrimitive; … … 92 100 import org.openstreetmap.josm.data.osm.visitor.OsmPrimitiveVisitor; 93 101 import org.openstreetmap.josm.data.osm.visitor.paint.AbstractMapRenderer; 102 import org.openstreetmap.josm.data.osm.visitor.paint.ImageCache; 94 103 import org.openstreetmap.josm.data.osm.visitor.paint.MapRendererFactory; 104 import org.openstreetmap.josm.data.osm.visitor.paint.StyledTiledMapRenderer; 105 import org.openstreetmap.josm.data.osm.visitor.paint.TileZXY; 95 106 import org.openstreetmap.josm.data.osm.visitor.paint.relations.MultipolygonCache; 96 107 import org.openstreetmap.josm.data.preferences.BooleanProperty; … … 105 116 import org.openstreetmap.josm.gui.MapView; 106 117 import org.openstreetmap.josm.gui.MapViewState.MapViewPoint; 118 import org.openstreetmap.josm.gui.NavigatableComponent; 119 import org.openstreetmap.josm.gui.PrimitiveHoverListener; 107 120 import org.openstreetmap.josm.gui.datatransfer.ClipboardUtils; 108 121 import org.openstreetmap.josm.gui.datatransfer.data.OsmLayerTransferData; … … 145 158 * @since 17 146 159 */ 147 public class OsmDataLayer extends AbstractOsmDataLayer implements Listener, DataSelectionListener, HighlightUpdateListener { 160 public class OsmDataLayer extends AbstractOsmDataLayer 161 implements Listener, DataSelectionListener, HighlightUpdateListener, PrimitiveHoverListener { 162 private static final int MAX_ZOOM = 30; 163 private static final int OVER_ZOOM = 2; 148 164 private static final int HATCHED_SIZE = 15; 149 165 // U+2205 EMPTY SET … … 156 172 /** Flag used to know if the layer is being uploaded */ 157 173 private final AtomicBoolean isUploadInProgress = new AtomicBoolean(false); 174 /** 175 * A cache used for painting 176 */ 177 private final CacheAccess<TileZXY, ImageCache> cache = JCSCacheManager.getCache("osmDataLayer:" + System.identityHashCode(this)); 178 /** The map paint index that was painted (used to invalidate {@link #cache}) */ 179 private int lastDataIdx; 180 /** The last zoom level (we invalidate all tiles when switching layers) */ 181 private int lastZoom; 182 private boolean hoverListenerAdded; 158 183 159 184 /** … … 498 523 */ 499 524 @Override public void paint(final Graphics2D g, final MapView mv, Bounds box) { 525 if (!hoverListenerAdded) { 526 MainApplication.getMap().mapView.addPrimitiveHoverListener(this); 527 hoverListenerAdded = true; 528 } 500 529 boolean active = mv.getLayerManager().getActiveLayer() == this; 501 530 boolean inactive = !active && Config.getPref().getBoolean("draw.data.inactive_color", true); 502 531 boolean virtual = !inactive && mv.isVirtualNodesEnabled(); 503 532 paintHatch(g, mv, active); 533 paintData(g, mv, box, inactive, virtual); 534 } 535 536 private void paintHatch(final Graphics2D g, final MapView mv, boolean active) { 504 537 // draw the hatched area for non-downloaded region. only draw if we're the active 505 538 // and bounds are defined; don't draw for inactive layers or loaded GPX files etc 506 if (active && DrawingPreference.SOURCE_BOUNDS_PROP.get() && !data.getDataSources().isEmpty()) {539 if (active && Boolean.TRUE.equals(DrawingPreference.SOURCE_BOUNDS_PROP.get()) && !data.getDataSources().isEmpty()) { 507 540 // initialize area with current viewport 508 541 Rectangle b = mv.getBounds(); … … 537 570 } 538 571 } 539 572 } 573 574 private void paintData(final Graphics2D g, final MapView mv, Bounds box, boolean inactive, boolean virtual) { 575 // Used to invalidate cache 576 int zoom = getZoom(mv); 577 if (zoom != lastZoom) { 578 // We just mark the previous zoom as dirty before moving in. 579 // It means we don't have to traverse up/down z-levels marking tiles as dirty (this can get *very* expensive). 580 this.cache.getMatching("TileZXY\\{" + lastZoom + "/.*") 581 .forEach((tile, imageCache) -> this.cache.put(tile, imageCache.becomeDirty())); 582 } 583 lastZoom = zoom; 540 584 AbstractMapRenderer painter = MapRendererFactory.getInstance().createActiveRenderer(g, mv, inactive); 541 painter.enableSlowOperations(mv.getMapMover() == null || !mv.getMapMover().movementInProgress() 542 || !PROPERTY_HIDE_LABELS_WHILE_DRAGGING.get()); 543 painter.render(data, virtual, box); 585 if (!(painter instanceof StyledTiledMapRenderer) || zoom - OVER_ZOOM > Config.getPref().getInt("mappaint.fast_render.zlevel", 16)) { 586 painter.enableSlowOperations(mv.getMapMover() == null || !mv.getMapMover().movementInProgress() 587 || !PROPERTY_HIDE_LABELS_WHILE_DRAGGING.get()); 588 } else { 589 StyledTiledMapRenderer renderer = (StyledTiledMapRenderer) painter; 590 renderer.setCache(box, this.cache, zoom, (tile) -> { 591 /* This causes "bouncing". I'm not certain why. 592 if (oldState.equalsInWindow(mv.getState())) { (oldstate = mv.getState()) 593 final Point upperLeft = mv.getPoint(tile); 594 final Point lowerRight = mv.getPoint(new TileZXY(tile.zoom(), tile.x() + 1, tile.y() + 1)); 595 GuiHelper.runInEDT(() -> mv.repaint(0, upperLeft.x, upperLeft.y, lowerRight.x - upperLeft.x, lowerRight.y - upperLeft.y)); 596 } 597 */ 598 // Invalidate doesn't trigger an instant repaint, but putting this off lets us batch the repaints needed for multiple tiles 599 MainApplication.worker.submit(this::invalidate); 600 }); 601 602 if (this.data.getMappaintCacheIndex() != this.lastDataIdx) { 603 this.cache.clear(); 604 this.lastDataIdx = this.data.getMappaintCacheIndex(); 605 Logging.trace("OsmDataLayer {0} paint cache cleared", this.getName()); 606 } 607 } 608 painter.render(this.data, virtual, box); 544 609 MainApplication.getMap().conflictDialog.paintConflicts(g, mv); 545 610 } … … 1148 1213 removeClipboardDataFor(this); 1149 1214 recentRelations.clear(); 1215 if (hoverListenerAdded) { 1216 hoverListenerAdded = false; 1217 MainApplication.getMap().mapView.removePrimitiveHoverListener(this); 1218 } 1150 1219 } 1151 1220 … … 1166 1235 @Override 1167 1236 public void processDatasetEvent(AbstractDatasetChangedEvent event) { 1237 resetTiles(event.getPrimitives()); 1168 1238 invalidate(); 1169 1239 setRequiresSaveToFile(true); … … 1173 1243 @Override 1174 1244 public void selectionChanged(SelectionChangeEvent event) { 1245 Set<IPrimitive> primitives = new HashSet<>(event.getAdded()); 1246 primitives.addAll(event.getRemoved()); 1247 resetTiles(primitives); 1175 1248 invalidate(); 1249 } 1250 1251 private void resetTiles(Collection<? extends IPrimitive> primitives) { 1252 if (primitives.size() >= this.data.allNonDeletedCompletePrimitives().size() || primitives.size() > 100) { 1253 dirtyAll(); 1254 return; 1255 } 1256 if (primitives.size() < 5) { 1257 for (IPrimitive p : primitives) { 1258 resetTiles(p); 1259 } 1260 return; 1261 } 1262 // Most of the time, a selection is going to be a big box. 1263 // So we want to optimize for that case. 1264 BBox box = null; 1265 for (IPrimitive primitive : primitives) { 1266 if (primitive == null || primitive.getDataSet() != this.getDataSet()) continue; 1267 final Collection<? extends IPrimitive> referrers = primitive.getReferrers(); 1268 if (box == null) { 1269 box = new BBox(primitive.getBBox()); 1270 } else { 1271 box.addPrimitive(primitive, 0); 1272 } 1273 for (IPrimitive referrer : referrers) { 1274 box.addPrimitive(referrer, 0); 1275 } 1276 } 1277 if (box != null) { 1278 resetBounds(box.getMinLat(), box.getMinLon(), box.getMaxLat(), box.getMaxLon()); 1279 } 1280 } 1281 1282 private void resetTiles(IPrimitive p) { 1283 if (p instanceof INode) { 1284 resetBounds(getInvalidatedBBox((INode) p, null)); 1285 } else if (p instanceof IWay) { 1286 IWay<?> way = (IWay<?>) p; 1287 for (int i = 0; i < way.getNodesCount() - 1; i++) { 1288 resetBounds(getInvalidatedBBox(way.getNode(i), way.getNode(i + 1))); 1289 } 1290 } else if (p instanceof IRelation<?>) { 1291 for (IPrimitive member : ((IRelation<?>) p).getMemberPrimitivesList()) { 1292 resetTiles(member); 1293 } 1294 } else { 1295 throw new IllegalArgumentException("Unsupported primitive type: " + p.getClass().getName()); 1296 } 1297 } 1298 1299 private BBox getInvalidatedBBox(INode first, INode second) { 1300 final BBox bbox = new BBox(first); 1301 if (second != null) { 1302 bbox.add(second); 1303 } 1304 return bbox; 1305 } 1306 1307 private void resetBounds(IBounds bbox) { 1308 resetBounds(bbox.getMinLat(), bbox.getMinLon(), bbox.getMaxLat(), bbox.getMaxLon()); 1309 } 1310 1311 private void resetBounds(double minLat, double minLon, double maxLat, double maxLon) { 1312 // Get the current zoom. Hopefully we aren't painting with a different navigatable component 1313 final int currentZoom = lastZoom; 1314 final AtomicInteger counter = new AtomicInteger(); 1315 TileZXY.boundsToTiles(minLat, minLon, maxLat, maxLon, currentZoom, 1).limit(100).forEach(tile -> { 1316 final ImageCache imageCache = this.cache.get(tile); 1317 if (imageCache != null && !imageCache.isDirty()) { 1318 this.cache.put(tile, imageCache.becomeDirty()); 1319 } 1320 counter.incrementAndGet(); 1321 }); 1322 if (counter.get() > 100) { 1323 dirtyAll(); 1324 } 1325 } 1326 1327 private void dirtyAll() { 1328 this.cache.getMatching(".*").forEach((key, value) -> { 1329 this.cache.remove(key); 1330 this.cache.put(key, value.becomeDirty()); 1331 }); 1332 } 1333 1334 /** 1335 * Get the zoom for a {@link NavigatableComponent} 1336 * @param navigatableComponent The component to get the zoom from 1337 * @return The zoom for the navigatable component 1338 */ 1339 private static int getZoom(NavigatableComponent navigatableComponent) { 1340 final double scale = navigatableComponent.getScale(); 1341 // We might have to fall back to the old method if user is reprojecting 1342 // 256 is the "target" size, (TODO check HiDPI!) 1343 final int targetSize = Config.getPref().getInt("mappaint.fast_render.tile_size", 256); 1344 final double topResolution = 2 * Math.PI * OsmMercator.EARTH_RADIUS / targetSize; 1345 int zoom; 1346 for (zoom = 0; zoom < MAX_ZOOM; zoom++) { // Use something like imagery.{generic|tms}.max_zoom_lvl (20 is a bit too low for our needs) 1347 if (scale > topResolution / Math.pow(2, zoom)) { 1348 zoom = zoom > 0 ? zoom - 1 : zoom; 1349 break; 1350 } 1351 } 1352 // We paint at a few levels higher, note that the tiles are appropriately sized (if 256 is the "target" size, the tiles should be 1353 // 64px square). 1354 zoom += OVER_ZOOM; 1355 return zoom; 1176 1356 } 1177 1357 … … 1310 1490 1311 1491 @Override 1492 public void primitiveHovered(PrimitiveHoverEvent e) { 1493 List<IPrimitive> primitives = new ArrayList<>(2); 1494 primitives.add(e.getHoveredPrimitive()); 1495 primitives.add(e.getPreviousPrimitive()); 1496 primitives.removeIf(Objects::isNull); 1497 resetTiles(primitives); 1498 this.invalidate(); 1499 } 1500 1501 @Override 1312 1502 public void setName(String name) { 1313 1503 if (data != null) {
Note:
See TracChangeset
for help on using the changeset viewer.