public ImageCollector(Tile[] t, int x, int y, Trace tr, Images i) { super(); this.t = t; this.tr = tr; Node n = new Node(2f, 0f); Projection p1 = ProjFactory.getInstance(n, 0, 1500, xSize, ySize); if (p1.isOrthogonal()) { // without overscan // FIXME add overscan support for windows if (Configuration.getCfgBitState(Configuration.CFGBIT_TMS_SPLITSCREEN) || Configuration.getCfgBitState(Configuration.CFGBIT_TMS_BACKGROUND)) { yScreenOverscan = 0; xScreenOverscan = 0; } else { // with overscan yScreenOverscan = y * 12 / 100; xScreenOverscan = x * 12 / 100; } if (tr.isShowingSplitIconMenu()) { yScreenOverscan = 0; } xSize = x + 2 * xScreenOverscan; ySize = y + 2 * yScreenOverscan; } else { // without overscan xSize = x; ySize = y; xScreenOverscan = 0; yScreenOverscan = 0; } img[0] = Image.createImage(xSize, ySize); img[1] = Image.createImage(xSize, ySize); try { pc[0] = new PaintContext(tr, i); pc[0].setP(p1); pc[0].state = PaintContext.STATE_READY; pc[1] = new PaintContext(tr, i); pc[1].setP(ProjFactory.getInstance(n, 0, 1500, xSize, ySize)); pc[1].state = PaintContext.STATE_READY; } catch (Exception e) { // TODO Auto-generated catch block e.printStackTrace(); } nextSc.setP(ProjFactory.getInstance(mapCenter, nextSc.course, nextSc.scale, xSize, ySize)); processorThread = new Thread(this, "ImageCollector"); // #if polish.android processorThread.setPriority(Thread.MAX_PRIORITY); // #else processorThread.setPriority(Thread.MIN_PRIORITY); // #endif // #if not polish.api.paintdirect processorThread.start(); // #endif }
/** * copy the last created image to the real screen * * <p>public void restart() { processorThread = new Thread(this, "ImageCollector"); //#if * polish.android processorThread.setPriority(Thread.MAX_PRIORITY); //#else * processorThread.setPriority(Thread.MIN_PRIORITY); //#endif //#if not polish.api.paintdirect * processorThread.start(); //#endif } * * <p>/** copy the last created image to the real screen but with the last collected position and * direction in the center */ public Node paint(PaintContext screenPc) { PaintContext paintPC; // System.out.println("paint this: " + screenPc); // System.out.println("paint image: " + pc[nextPaint]); if (suspended || !collectorReady) { return new Node(0, 0); } // Define the parameters for the next image that will be created nextSc.center = screenPc.center.copy(); nextSc.course = screenPc.course; nextSc.scale = screenPc.scale; nextSc.dest = screenPc.dest; nextSc.xSize = screenPc.xSize; nextSc.ySize = screenPc.ySize; Projection p = ProjFactory.getInstance(nextSc.center, nextSc.course, nextSc.scale, xSize, ySize); // System.out.println("p =" + p); Projection p1 = ProjFactory.getInstance( nextSc.center, pc[nextPaint].course, pc[nextPaint].scale, xSize, ySize); // System.out.println("p =" + p1); nextSc.setP(p); screenPc.setP(p); synchronized (this) { if (pc[nextPaint].state != PaintContext.STATE_READY) { logger.error( Locale.get( "imagecollector.ImageCollectorNonReadyPaintContext") /*ImageCollector was trying to draw a non ready PaintContext */ + pc[nextPaint].state); return new Node(0, 0); } paintPC = pc[nextPaint]; paintPC.state = PaintContext.STATE_IN_PAINT; } int screenXCenter = xSize / 2 - xScreenOverscan; int screenYCenter = ySize / 2 - yScreenOverscan; int newXCenter = screenXCenter; int newYCenter = screenYCenter; // return center of the map image drawn to the caller Node getDrawnCenter = paintPC.center.copy(); if (p.isOrthogonal()) { // maps can painted so that the hotspot is at the predefined point on the screen // therfore the offset is useful in that case its not necessary to create a new image // if the position has changed less then half of the offset if (lastCreatedSc != null) { p1.forward(lastCreatedSc.center, oldCenter); newXCenter = oldCenter.x - p.getImageCenter().x + screenXCenter; newYCenter = oldCenter.y - p.getImageCenter().y + screenYCenter; // System.out.println("Paint pos = " + newXCenter + "/" + // newYCenter); // System.out.println("Paint ysize=" + ySize + " nextSc.xSize=" // + nextSc.ySize + " hotspot=" + p.getImageCenter()); } screenPc.g.drawImage( img[nextPaint], newXCenter, newYCenter, Graphics.VCENTER | Graphics.HCENTER); // Test if the new center is around the middle of the screen, in which // case we don't need to redraw (recreate a new image), as nothing has changed. if (Math.abs(newXCenter - screenXCenter) > 4 || Math.abs(newYCenter - screenYCenter) > 4 || paintPC.course != nextSc.course) { // The center of the screen has moved or rotated, so need // to redraw the map image needRedraw = true; // System.out.println("wakeup thread because course or position changed"); // System.out.println("Changed " + newXCenter + "->" + // screenXCenter + " and " + newYCenter + "->" + screenYCenter); } } else { screenPc.g.drawImage( img[nextPaint], screenXCenter, screenYCenter, Graphics.VCENTER | Graphics.HCENTER); p.forward(lastCreatedSc.center, oldCenter); newXCenter = oldCenter.x - p.getImageCenter().x + screenXCenter; newYCenter = oldCenter.y - p.getImageCenter().y + screenYCenter; if (Math.abs(newXCenter - screenXCenter) > 1 || Math.abs(newYCenter - screenYCenter) > 1 || paintPC.course != nextSc.course) { needRedraw = true; } } // screenPc.g.drawArc(newXCenter-14, newYCenter-14, 28, 28, 0, 360); // if (p instanceof Proj3D){ // screenPc.g.setColor(255,50,50); // IntPoint pt0 = new IntPoint(); // IntPoint pt1 = new IntPoint(); // Proj3D p3=(Proj3D)p; // p.forward(p3.borderLD,pt0); // p.forward(p3.borderLU,pt1); // screenPc.g.drawLine(pt0.x, pt0.y, pt1.x, pt1.y); // p.forward(p3.borderRU,pt0); // screenPc.g.drawLine(pt0.x, pt0.y, pt1.x, pt1.y); // p.forward(p3.borderRD,pt1); // screenPc.g.drawLine(pt0.x, pt0.y, pt1.x, pt1.y); // p.forward(p3.borderLD,pt0); // screenPc.g.drawLine(pt0.x, pt0.y, pt1.x, pt1.y); // // } String name = null; Way wayForName = null; /** * used to check for pixel distances because checking for meters from converted pixels requires * to be exactly on the pixel when zoomed out far */ final int SQUARE_MAXPIXELS = 5 * 5; // Tolerance of 15 pixels converted to meters float pixDest = 15 / paintPC.ppm; if (pixDest < 15) { pixDest = 15; } if (paintPC.trace.gpsRecenter) { // Show closest routable way name if map is gpscentered and we are // closer // than SQUARE_MAXPIXELS or 30 m (including penalty) to it. // If the routable way is too far away, we try the closest way. if (paintPC.bUsedGpsCenter == false) { if (paintPC.squareDstWithPenToActualRoutableWay < SQUARE_MAXPIXELS || paintPC.getDstFromSquareDst(paintPC.squareDstWithPenToActualRoutableWay) < 30) { wayForName = paintPC.actualRoutableWay; } else if (paintPC.squareDstWithPenToWay < SQUARE_MAXPIXELS || paintPC.getDstFromSquareDst(paintPC.squareDstWithPenToWay) < 30) { wayForName = paintPC.actualWay; } } else { if (paintPC.getDstFromRouteableWay() < 100) { wayForName = paintPC.actualRoutableWay; } else if (paintPC.getDstFromWay() < 100) { wayForName = paintPC.actualWay; } } } else if (paintPC.getDstFromSquareDst(paintPC.squareDstWithPenToWay) <= pixDest) { // If not gpscentered show closest way name if it's no more than 15 // pixels away. wayForName = paintPC.actualWay; } /* * As we are double buffering pc, nothing should be writing to paintPC * therefore it should be safe to access the volatile variable actualWay */ if (paintPC.actualWay != null) { screenPc.actualWay = paintPC.actualWay; screenPc.actualSingleTile = paintPC.actualSingleTile; tr.actualWay = paintPC.actualWay; tr.actualSingleTile = paintPC.actualSingleTile; } if (wayForName != null) { int nummaxspeed; String maxspeed = ""; String winter = ""; // store for OSM editing if (wayForName.getMaxSpeed() != 0) { nummaxspeed = wayForName.getMaxSpeed(); if (Configuration.getCfgBitState(Configuration.CFGBIT_MAXSPEED_WINTER) && (wayForName.getMaxSpeedWinter() > 0)) { nummaxspeed = wayForName.getMaxSpeedWinter(); winter = Locale.get("imagecollector.Winter") /*W */; } if (nummaxspeed == Legend.MAXSPEED_MARKER_NONE) { maxspeed = Locale.get("imagecollector.SL") /* SL:*/ + winter + Locale.get("imagecollector.MaxSpeedNone") /*none*/; } else if (nummaxspeed == Legend.MAXSPEED_MARKER_VARIABLE) { maxspeed = Locale.get("imagecollector.SL") /* SL:*/ + winter + Locale.get("imagecollector.MaxSpeedVariable") /*var*/; } else if (Configuration.getCfgBitState(Configuration.CFGBIT_METRIC)) { maxspeed = Locale.get("imagecollector.SL") /* SL:*/ + winter + nummaxspeed; } else { // Round up at this point, as the the previouse two // conversions // were rounded down already. (Seems to work better for // speed limits of // 20mph and 30mph) maxspeed = Locale.get("imagecollector.SL") /* SL:*/ + winter + ((int) (nummaxspeed / 1.609344f + 0.5f)); } } if (wayForName.nameIdx != -1) { name = screenPc.trace.getName(wayForName.nameIdx); } else { // #if polish.api.bigstyles WayDescription wayDesc = Legend.getWayDescription(wayForName.type); // #else WayDescription wayDesc = Legend.getWayDescription((short) (wayForName.type & 0xff)); // #endif name = Locale.get("imagecollector.unnamed") /*(unnamed */ + wayDesc.description + ")"; } if (name == null) { name = maxspeed; } else { name = name + maxspeed; } // If there's an URL associated with way, show a letter next to name if (wayForName.urlIdx != -1) { name = name + Locale.get("imagecollector.W") /* W*/; } // Show 'P' for phone number if (wayForName.phoneIdx != -1) { name = name + Locale.get("imagecollector.P") /* P*/; } } // use the nearest routable way for the the speed limit detection if // it's // closer than 30 m or SQUARE_MAXPIXELS including penalty if (paintPC.squareDstWithPenToActualRoutableWay < SQUARE_MAXPIXELS || paintPC.getDstFromSquareDst(paintPC.squareDstWithPenToActualRoutableWay) < 30) { tr.actualSpeedLimitWay = paintPC.actualRoutableWay; } else { tr.actualSpeedLimitWay = null; } boolean showLatLon = Configuration.getCfgBitState(Configuration.CFGBIT_SHOWLATLON); LayoutElement e = Trace.tl.ele[TraceLayout.WAYNAME]; if (showLatLon) { // #if polish.api.finland // show Finnish ETRS-TM35FIN coordinates // FIXME: add a config option for selection of coordinates if (false) { PositionMark pmETRS = ETRSTM35FINconvert.latlonToEtrs(paintPC.center.radlat, paintPC.center.radlon); e.setText( Locale.get("imagecollector.lat") /* lat: */ + Float.toString(pmETRS.lat) + " " + Locale.get("imagecollector.lon") /* lon: */ + Float.toString(pmETRS.lon)); } else { // #endif e.setText( Locale.get("imagecollector.lat") /* lat: */ + Float.toString(paintPC.center.radlat * MoreMath.FAC_RADTODEC) + " " + Locale.get("imagecollector.lon") /* lon: */ + Float.toString(paintPC.center.radlon * MoreMath.FAC_RADTODEC)); // #if polish.api.finland } // #endif } else { if (name != null && name.length() > 0) { e.setText(name); } else { e.setText(" "); } } if (paintPC.scale != screenPc.scale) { // System.out.println("wakeup thread because scale changed"); needRedraw = true; } // when the projection has changed we must redraw if (!paintPC.getP().getProjectionID().equals(screenPc.getP().getProjectionID())) { // System.out.println("wakeup thread because projection changed"); needRedraw = true; } synchronized (this) { paintPC.state = PaintContext.STATE_READY; if (needRedraw) { notify(); } else { // System.out.println("No need to redraw after painting"); } } // currentVisibleSc=lastCreatedSc.cloneToScreenContext(); return getDrawnCenter; }
public void run() { PaintContext createPC = null; final byte MAXCRASHES = 5; byte crash = 0; do { try { while (!shutdown) { if (!needRedraw || suspended) { synchronized (this) { try { /* FIXME: We still have some situations where redraw is not done automatically immediately, * e.g. on Nokia 5800 after returning from another Displayable * Therefore reduce the timeout for redrawing anyway from 30 seconds to 1 seconds * if the last user interaction happened less than 1.5 secs before if (Trace.getDurationSinceLastUserActionTime() > 1500 ) { wait(30000); } else { wait(1000); } */ wait(30000); } catch (InterruptedException e) { continue; // Recheck condition of the loop } } } needRedraw = false; // moved here and deleted from the bottom of this routine // #debug debug logger.debug("Redrawing Map"); iDrawState = 1; synchronized (this) { while (pc[nextCreate].state != PaintContext.STATE_READY && !shutdown) { try { // System.out.println("img not ready"); wait(1000); } catch (InterruptedException e) { } } if (suspended || shutdown) { continue; } pc[nextCreate].state = PaintContext.STATE_IN_CREATE; } iDrawState = 2; createPC = pc[nextCreate]; long startTime = System.currentTimeMillis(); // create PaintContext createPC.xSize = nextSc.xSize; createPC.ySize = nextSc.ySize; createPC.center = nextSc.center.copy(); mapCenter = nextSc.center.copy(); createPC.scale = nextSc.scale; createPC.course = nextSc.course; // Projection p = ProjFactory.getInstance(createPC.center, // nextSc.course, nextSc.scale, xSize, ySize); createPC.setP(nextSc.getP()); // p.inverse(xSize, 0, createPC.screenRU); // p.inverse(0, ySize, createPC.screenLD); // pcCollect.trace = nextSc.trace; // pcCollect.dataReader = nextSc.dataReader; // cleans the screen createPC.g = img[nextCreate].getGraphics(); createPC.g.setColor(Legend.COLORS[Legend.COLOR_MAP_BACKGROUND]); createPC.g.fillRect(0, 0, xSize, ySize); // createPC.g.setColor(0x00FF0000); // createPC.g.drawRect(0, 0, xSize - 1, ySize - 1); // createPC.g.drawRect(20, 20, xSize - 41, ySize - 41); createPC.squareDstWithPenToWay = Float.MAX_VALUE; createPC.squareDstWithPenToActualRoutableWay = Float.MAX_VALUE; createPC.squareDstWithPenToRoutePath = Float.MAX_VALUE; createPC.squareDstToRoutePath = Float.MAX_VALUE; createPC.dest = nextSc.dest; createPC.waysPainted = 0; // System.out.println("create " + pcCollect); Way.setupDirectionalPenalty(createPC, tr.speed, tr.gpsRecenter && !tr.gpsRecenterInvalid); float boost = Configuration.getMaxDetailBoostMultiplier(); /* * layers containing highlighted path segments */ createPC.hlLayers = 0; /* * highlighted path is on top if gps recentered, but if not it might still come to top * when we determine during painting that the cursor is closer than 25 meters at the route line. */ createPC.highlightedPathOnTop = tr.gpsRecenter; /** * At the moment we don't really have proper layer support in the data yet, so only split * it into Area, Way and Node layers */ byte layersToRender[] = { Tile.LAYER_AREA, 1 | Tile.LAYER_AREA, 2 | Tile.LAYER_AREA, 3 | Tile.LAYER_AREA, 4 | Tile.LAYER_AREA, 0, 1, 2, 3, 4, Tile.LAYER_NODE, 0 | Tile.LAYER_HIGHLIGHT, 1 | Tile.LAYER_HIGHLIGHT, 2 | Tile.LAYER_HIGHLIGHT, 3 | Tile.LAYER_HIGHLIGHT }; /** * Draw each layer separately to enforce paint ordering: * * <p>Go through the entire tile tree multiple times to get the drawing order correct. * * <p>The first 5 layers correspond to drawing areas with the osm layer tag of (< -1, -1, * 0, 1, >1), then next 5 layers are drawing streets with osm layer tag (< -1, -1, 0, 1, * >1). * * <p>Then we draw the highlighted streets and finally we draw the POI layer. * * <p>So e. g. layer 7 corresponds to all streets that have no osm layer tag or layer = 0. */ boolean simplifyMap = Configuration.getCfgBitState(Configuration.CFGBIT_SIMPLIFY_MAP_WHEN_BUSY); boolean skippableLayer = false; if (Configuration.getCfgBitState(Configuration.CFGBIT_TMS_BACKGROUND)) { RasterTile.drawRasterMap(createPC, createPC.trace.rootWindow); } for (byte layer = 0; layer < layersToRender.length; layer++) { if (simplifyMap) { skippableLayer = ((layer < 5 && layer > 1) // #if polish.api.finland // don't skip node layer where speed camera is if camera alert is on || (layer == 14 && !Configuration.getCfgBitState(Configuration.CFGBIT_SPEEDCAMERA_ALERT)) // #else || (layer == 14) // #endif // #if polish.android || (Trace.getInstance().mapBrowsing && ((layer < 5) || layer == 14)) // #else || (Trace.getInstance().mapBrowsing && ((layer < 5 && layer > 1) || layer == 14)) // #endif ); // skip update if a new one is queued if (needRedraw && skippableLayer) { continue; } } if (layersToRender[layer] == Tile.LAYER_NODE) { tr.resetClickableMarkers(); } // render only highlight layers which actually have highlighted path segments if ((layersToRender[layer] & Tile.LAYER_HIGHLIGHT) > 0 && layersToRender[layer] != Tile.LAYER_NODE) { /** * as we do two passes for each way layer when gps recentered - one for the ways and * one for the route line on top, we can use in the second pass the determined route * path connection / idx to highlight the route line in the correct / prior route line * color. when not gps recentered, this info will be by one image obsolete however */ if (layersToRender[layer] == Tile .LAYER_HIGHLIGHT /*(0 | Tile.LAYER_HIGHLIGHT) pointless bitwise operation*/) { /* * only take ImageCollector loops into account for dstToRoutePath if ways were painted * otherwise this would trigger wrong route recalculations */ if (createPC.waysPainted != 0) { // RouteInstructions.dstToRoutePath = // createPC.getDstFromSquareDst(createPC.squareDstToRoutePath); RouteInstructions.dstToRoutePath = createPC.getDstFromRouteSegment(); if (RouteInstructions.dstToRoutePath != RouteInstructions.DISTANCE_UNKNOWN) { RouteInstructions.routePathConnection = createPC.routePathConnection; RouteInstructions.pathIdxInRoutePathConnection = createPC.pathIdxInRoutePathConnection; RouteInstructions.actualRoutePathWay = createPC.actualRoutePathWay; // when we determine during painting that the cursor is closer than 25 meters at // the route line, bring it to the top if (RouteInstructions.dstToRoutePath < 25) { createPC.highlightedPathOnTop = true; } } // System.out.println("waysPainted: " + createPC.waysPainted); } else { // FIXME: Sometimes there are ImageCollector loop with no way pained even when // ways would be there and tile data is fully loaded // Update 2011-06-11: Might be fixed with the patch from gojkos at [ // sharenav-Bugs-3310178 ] Delayed map draw on LG cookie phone // Update 2012-03-17 (patch from walter9): The image collector has been started // twice. This seems to be fixed now in Trace.java.startImageCollector() System.out.println("No ways painted in this ImageCollector loop"); } } byte relLayer = (byte) (((int) layersToRender[layer]) & 0x0000000F); if ((createPC.hlLayers & (1 << relLayer)) == 0) { continue; } } minTile = Legend.scaleToTile((int) (createPC.scale / (boost * overviewTileScaleBoost))); if (t[0] != null) { if (needRedraw && skippableLayer) { continue; } t[0].paint(createPC, layersToRender[layer]); if (needRedraw && skippableLayer) { continue; } } if ((minTile >= 1) && (t[1] != null)) { if (needRedraw && skippableLayer) { continue; } t[1].paint(createPC, layersToRender[layer]); if (needRedraw && skippableLayer) { continue; } Thread.yield(); } if ((minTile >= 2) && (t[2] != null)) { if (needRedraw && skippableLayer) { continue; } t[2].paint(createPC, layersToRender[layer]); if (needRedraw && skippableLayer) { continue; } Thread.yield(); } if ((minTile >= 3) && (t[3] != null)) { if (needRedraw && skippableLayer) { continue; } t[3].paint(createPC, layersToRender[layer]); if (needRedraw && skippableLayer) { continue; } Thread.yield(); } /** Drawing waypoints */ if (t[DictReader.GPXZOOMLEVEL] != null) { t[DictReader.GPXZOOMLEVEL].paint(createPC, layersToRender[layer]); } if (suspended) { // Don't continue rendering if suspended createPC.state = PaintContext.STATE_READY; break; } } /** Drawing debuginfo for routing */ if (!suspended && t[DictReader.ROUTEZOOMLEVEL] != null && (Configuration.getCfgBitState(Configuration.CFGBIT_ROUTE_CONNECTIONS) || Configuration.getCfgBitState(Configuration.CFGBIT_SHOW_TURN_RESTRICTIONS))) { t[DictReader.ROUTEZOOMLEVEL].paint(createPC, (byte) 0); } iDrawState = 0; icDuration = System.currentTimeMillis() - startTime; // #mdebug logger.info("Painting map took " + icDuration + " ms"); // #enddebug System.out.println("Painting map took " + icDuration + " ms " + xSize + "/" + ySize); createPC.state = PaintContext.STATE_READY; lastCreatedSc = createPC.cloneToScreenContext(); if (!shutdown) { newCollected(); } createImageCount++; // needRedraw = false; tr.cleanup(); // System.out.println("create ready"); // System.gc(); } } catch (OutOfMemoryError oome) { if (createPC != null) { createPC.state = PaintContext.STATE_READY; } String recoverZoomedIn = ""; crash++; if (tr.scale > 10000 && crash < MAXCRASHES) { tr.scale /= 1.5f; recoverZoomedIn = Locale.get("imagecollector.ZoomingInToRecover") /* Zooming in to recover.*/; } logger.fatal( Locale.get( "imagecollector.ImageCollectorRanOutOfMemory") /*ImageCollector ran out of memory: */ + oome.getMessage() + recoverZoomedIn); } catch (Exception e) { crash++; logger.exception( Locale.get( "imagecollector.ImageCollectorCrashed") /*ImageCollector thread crashed unexpectedly with error */, e); } if (crash >= MAXCRASHES) { logger.fatal( Locale.get( "imagecollector.ImageCollectorCrashedAborting") /*ImageCollector crashed too often. Aborting.*/); } } while (!shutdown && crash < MAXCRASHES); processorThread = null; synchronized (this) { notifyAll(); } }