Ignore:
Timestamp:
2017-02-14T23:37:10+01:00 (7 years ago)
Author:
Don-vip
Message:

fix #14343 - GPS heat map should also support point clouds (modified patch by kidelo)

File:
1 edited

Legend:

Unmodified
Added
Removed
  • trunk/src/org/openstreetmap/josm/gui/layer/gpx/GpxDrawHelper.java

    r11483 r11566  
    2828import java.util.Date;
    2929import java.util.List;
     30import java.util.Random;
    3031
    3132import javax.swing.ImageIcon;
     
    9697    private int computeCacheColorTracksTune;
    9798    private int computeCacheHeatMapDrawColorTableIdx;
     99    private boolean computeCacheHeatMapDrawPointMode;
     100    private int computeCacheHeatMapDrawGain;
     101    private int computeCacheHeatMapDrawLowerLimit;
    98102
    99103    //// Color-related fields
     
    135139    // used index for color table (parameter)
    136140    private int heatMapDrawColorTableIdx;
     141    // use point or line draw mode
     142    private boolean heatMapDrawPointMode;
     143    // extra gain > 0 or < 0 attenuation, 0 = default
     144    private int heatMapDrawGain;
     145    // do not draw elements with value lower than this limit
     146    private int heatMapDrawLowerLimit;
    137147
    138148    // normal buffered image and draw object (cached)
     
    156166
    157167    // user defined heatmap color
    158     private Color[] heatMapLutColor = createColorLut(Color.BLACK, Color.WHITE);
     168    private Color[] heatMapLutColor = createColorLut(0, Color.BLACK, Color.WHITE);
    159169
    160170    private void setupColors() {
     
    278288        heatMapEnabled = Main.pref.getBoolean("draw.rawgps.heatmap.enabled", spec, false);
    279289        heatMapDrawExtraLine = Main.pref.getBoolean("draw.rawgps.heatmap.line-extra", spec, false);
    280         heatMapDrawColorTableIdx = Main.pref.getInteger("draw.rawgps.heatmap.colormap", specName(layerName), 0);
     290        heatMapDrawColorTableIdx = Main.pref.getInteger("draw.rawgps.heatmap.colormap", spec, 0);
     291        heatMapDrawPointMode = Main.pref.getBoolean("draw.rawgps.heatmap.use-points", spec, false);
     292        heatMapDrawGain = Main.pref.getInteger("draw.rawgps.heatmap.gain", spec, 0);
     293        heatMapDrawLowerLimit = Main.pref.getInteger("draw.rawgps.heatmap.lower-limit", spec, 0);
     294
     295        // shrink to range
     296        heatMapDrawGain = Math.min(Math.max(-10, heatMapDrawGain), 10);
    281297
    282298        neutralColor = getColor(layerName, true);
     
    514530        if (ColorMode.HEATMAP == colored) {
    515531
    516             // generate and get new user color map
    517             heatMapLutColor = selectColorMap(neutralColor != null ? neutralColor : Color.WHITE, heatMapDrawColorTableIdx);
     532            // get new user color map and refresh visibility level
     533            heatMapLutColor = createColorLut(heatMapDrawLowerLimit,
     534                                             selectColorMap(neutralColor != null ? neutralColor : Color.WHITE, heatMapDrawColorTableIdx));
    518535
    519536            // force redraw of image
     
    795812
    796813    /**
    797      * Creates a linear distributed colormap by linear blending between colors
     814     * Creates a distributed colormap by linear blending between colors
     815     * @param lowerLimit lower limit for first visible color
    798816     * @param colors 1..n colors
    799817     * @return array of Color objects
    800818     */
    801     protected static Color[] createColorLut(Color... colors) {
     819    protected static Color[] createColorLut(int lowerLimit, Color... colors) {
    802820
    803821        // number of lookup entries
     
    825843
    826844            // smooth alpha like sin curve
    827             int alpha = (int) (Math.sin(i * mapTo90Deg) * 255);
     845            int alpha = (i > lowerLimit) ? (int) (Math.sin((i-lowerLimit) * mapTo90Deg) * 255) : 0;
    828846
    829847            // alpha with pre-offset, first color -> full transparent
    830             alpha = i > 0 ? (10 + alpha) : 0;
     848            alpha = alpha > 0 ? (20 + alpha) : 0;
    831849
    832850            // shrink to maximum bound
     
    916934        }
    917935
    918         return createColorLut(colorList.toArray(new Color[ colorList.size() ]));
     936        return createColorLut(0, colorList.toArray(new Color[ colorList.size() ]));
    919937    }
    920938
     
    928946    protected static Color[] selectColorMap(Color userColor, int tableIdx) {
    929947
     948        // generate new user color map ( dark, user color, white )
     949        Color[] userColor1 = createColorLut(0, userColor.darker(), userColor, userColor.brighter(), Color.WHITE);
     950
     951        // generate new user color map ( white -> color )
     952        Color[] userColor2 = createColorLut(0, Color.WHITE, Color.WHITE, userColor);
     953
    930954        // generate new user color map
    931         Color[] nextUserColor = createColorLut(Color.BLACK, userColor.darker(),
    932                                                userColor, userColor.brighter(), Color.WHITE);
     955        Color[] colorTrafficLights = createColorLut(0, Color.WHITE, Color.GREEN.darker(), Color.YELLOW, Color.RED);
    933956
    934957        // decide what, keep order is sync with setting on GUI
    935958        Color[][] lut = {
    936                 nextUserColor,
     959                userColor1,
     960                userColor2,
     961                colorTrafficLights,
    937962                heatMapLutColorJosmInferno,
    938963                heatMapLutColorJosmViridis,
     
    941966        };
    942967
     968        // default case
     969        Color[] nextUserColor = userColor1;
     970
    943971        // select by index
    944972        if (tableIdx < lut.length) {
     
    946974        }
    947975
     976        // adjust color map
    948977        return nextUserColor;
    949978    }
     
    9711000     * @param backStroke      stroke use to draw background objects
    9721001     */
    973     private void drawHeatGrayMap(Graphics2D gB, MapView mv, List<WayPoint> listSegm,
    974                                  Composite foreComp, Stroke foreStroke,
    975                                  Composite backComp, Stroke backStroke) {
     1002    private void drawHeatGrayLineMap(Graphics2D gB, MapView mv, List<WayPoint> listSegm,
     1003                                     Composite foreComp, Stroke foreStroke,
     1004                                     Composite backComp, Stroke backStroke) {
    9761005
    9771006        // draw foreground
     
    10371066
    10381067        // always full or outlines at big samples rasters
    1039         final boolean drawOutlines = (outlineWidth > 0) && ((0 == sampleRaster) || (sampleRaster > 8));
     1068        final boolean drawOutlines = (outlineWidth > 0) && ((0 == sampleRaster) || (sampleRaster > 10));
    10401069
    10411070        // backup stroke
     
    11431172            heatMapGraph2d.setColor(Color.WHITE);
    11441173
     1174            // fast draw ( maybe help or not )
     1175            heatMapGraph2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_OFF);
     1176            heatMapGraph2d.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_SPEED);
     1177            heatMapGraph2d.setRenderingHint(RenderingHints.KEY_ALPHA_INTERPOLATION, RenderingHints.VALUE_ALPHA_INTERPOLATION_SPEED);
     1178            heatMapGraph2d.setRenderingHint(RenderingHints.KEY_DITHERING, RenderingHints.VALUE_DITHER_DISABLE);
     1179            heatMapGraph2d.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_NEAREST_NEIGHBOR);
     1180            heatMapGraph2d.setRenderingHint(RenderingHints.KEY_STROKE_CONTROL, RenderingHints.VALUE_STROKE_PURE);
     1181            heatMapGraph2d.setRenderingHint(RenderingHints.KEY_COLOR_RENDERING, RenderingHints.VALUE_COLOR_RENDER_SPEED);
     1182
    11451183            // cache it
    11461184            heatMapCacheScreenBounds = screenBounds;
     
    11501188
    11511189        // the line width (foreground: draw extra small footprint line of track)
    1152         final int lineWidthB = (int) Math.max(1.5f * (globalLineWidth / zoomScale) + 1, 2);
    1153         final int lineWidthF = lineWidthB > 2 ? (globalLineWidth - 1) : 0;
     1190        int lineWidthB = (int) Math.max(1.5f * (globalLineWidth / zoomScale) + 1, 2);
     1191        int lineWidthF = lineWidthB > 2 ? (globalLineWidth - 1) : 0;
     1192
     1193        // global alpha adjustment
     1194        float lineAlpha = Math.min(Math.max((0.40f/(float) zoomScale)/(globalLineWidth+1), 0.01f), 0.40f);
     1195
     1196        // adjust 0.15 .. 1.85
     1197        float scaleAlpha = 1.0f + ((heatMapDrawGain/10.0f) * 0.85f);
     1198
     1199        // add to calculated values
     1200        float lineAlphaBPoint = Math.max(Math.min(((lineAlpha * 0.65f) * scaleAlpha), 0.90f), 0.001f);
     1201        float lineAlphaBLine = Math.max(Math.min(((lineAlpha * 1.00f) * scaleAlpha), 0.90f), 0.001f);
     1202        float lineAlphaFLine = Math.max(Math.min(((lineAlpha / 1.50f) * scaleAlpha), 0.90f), 0.001f);
     1203
     1204        // 3rd Calculate the heat map data by draw GPX traces with alpha value ----------
    11541205
    11551206        // recalculation of image needed
     
    11571208                                    heatMapCacheLineWith != globalLineWidth;
    11581209
    1159         // 3rd Calculate the heat map data by draw GPX traces with alpha value ----------
    1160 
    11611210        // need re-generation of gray image ?
    11621211        if (imageSetup || imageRecalc) {
     
    11651214            heatMapGraph2d.clearRect(0, 0, heatMapImgGray.getWidth(), heatMapImgGray.getHeight());
    11661215
    1167             // alpha combines both values, therefore the foreground shall be lighter
    1168             final float lineAlphaB = Math.min(Math.max((0.40f/(float) zoomScale)/(globalLineWidth + 1), 0.01f), 0.40f);
    1169             final float lineAlphaF = lineAlphaB / 1.5f;
    1170 
    1171             // derive draw parameters and draw
    1172             drawHeatGrayMap(heatMapGraph2d, mv, visibleSegments,
    1173                             lineWidthF > 1 ? AlphaComposite.SrcOver.derive(lineAlphaF) : null,
    1174                             new BasicStroke(lineWidthF, BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND),
    1175                             AlphaComposite.SrcOver.derive(lineAlphaB),
    1176                             new BasicStroke(lineWidthB, BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND));
    1177 
    1178             // remember draw parameters
     1216            // point or line blending
     1217            if (heatMapDrawPointMode) {
     1218                heatMapGraph2d.setComposite(AlphaComposite.SrcOver.derive(lineAlphaBPoint));
     1219                drawHeatGrayDotMap(heatMapGraph2d, mv, visibleSegments, lineWidthB);
     1220
     1221            } else {
     1222                drawHeatGrayLineMap(heatMapGraph2d, mv, visibleSegments,
     1223                                    lineWidthF > 1 ? AlphaComposite.SrcOver.derive(lineAlphaFLine) : null,
     1224                                    new BasicStroke(lineWidthF, BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND),
     1225                                    AlphaComposite.SrcOver.derive(lineAlphaBLine),
     1226                                    new BasicStroke(lineWidthB, BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND));
     1227            }
     1228
     1229            // remember draw parameter
    11791230            heatMapMapViewState = mapViewState;
    11801231            heatMapCacheLineWith = globalLineWidth;
     
    11821233
    11831234        // 4th. Draw data on target layer, map data via color lookup table --------------
    1184         drawHeatMapGrayMap(g, heatMapImgGray, lineWidthB > 2 ? (lineWidthB / 2) : 1, lineWidth > 2 ? (lineWidth - 2) : 1);
     1235        drawHeatMapGrayMap(g, heatMapImgGray, lineWidthB > 2 ? (int) (lineWidthB*1.25f) : 1, lineWidth > 2 ? (lineWidth - 2) : 1);
     1236    }
     1237
     1238
     1239    /**
     1240     * Draw a dotted heat map
     1241     *
     1242     * @param gB              the common draw object to use
     1243     * @param mv              the meta data to current displayed area
     1244     * @param listSegm        segments visible in the current scope of mv
     1245     * @param drawSize        draw size of draw element
     1246     */
     1247    private void drawHeatGrayDotMap(Graphics2D gB, MapView mv, List<WayPoint> listSegm, int drawSize) {
     1248
     1249        // typical rendering rate -> use realtime preview instead of accurate display
     1250        final double maxSegm = 25000, nrSegms = listSegm.size();
     1251
     1252        // determine random drop rate
     1253        final double randomDrop = Math.min(nrSegms > maxSegm ? (nrSegms - maxSegm) / nrSegms : 0, 0.70f);
     1254
     1255        // http://www.nstb.tc.faa.gov/reports/PAN94_0716.pdf#page=22
     1256        // Global Average Position Domain Accuracy, typical -> not worst case !
     1257        // < 4.218 m Vertical
     1258        // < 2.168 m Horizontal
     1259        final double pixelRmsX = (100 / mv.getDist100Pixel()) * 2.168;
     1260        final double pixelRmsY = (100 / mv.getDist100Pixel()) * 4.218;
     1261
     1262        Point lastPnt = null;
     1263
     1264        // for all points, draw single lines
     1265        for (WayPoint trkPnt : listSegm) {
     1266
     1267            // get transformed coordinates
     1268            final Point paintPnt = mv.getPoint(trkPnt.getEastNorth());
     1269
     1270            // end of line segment or end of list reached
     1271            if (trkPnt.drawLine && null != lastPnt) {
     1272                drawHeatSurfaceLine(gB, paintPnt, lastPnt, drawSize, pixelRmsX, pixelRmsY, randomDrop);
     1273            }
     1274
     1275            // remember
     1276            lastPnt = paintPnt;
     1277        }
     1278    }
     1279
     1280    /**
     1281     * Draw a dotted surface line
     1282     *
     1283     * @param g                 the common draw object to use
     1284     * @param fromPnt           start point
     1285     * @param toPnt             end point
     1286     * @param drawSize          size of draw elements
     1287     * @param rmsSizeX          RMS size of circle for X (width)
     1288     * @param rmsSizeY          RMS size of circle for Y (height)
     1289     * @param dropRate          Pixel render drop rate
     1290     */
     1291    private void drawHeatSurfaceLine(Graphics2D g, Point fromPnt, Point toPnt, int drawSize, double rmsSizeX, double rmsSizeY, double dropRate) {
     1292
     1293        // collect frequently used items
     1294        final int fromX = (int) fromPnt.getX(); final int deltaX = (int) (toPnt.getX() - fromX);
     1295        final int fromY = (int) fromPnt.getY(); final int deltaY = (int) (toPnt.getY() - fromY);
     1296
     1297        // use same random values for each point
     1298        final Random heatMapRandom = new Random(fromX+fromY+deltaX+deltaY);
     1299
     1300        // cache distance between start and end point
     1301        final int dist = (int) Math.abs(fromPnt.distance(toPnt));
     1302
     1303        // number of increment ( fill wide distance tracks )
     1304        double scaleStep = Math.max(1.0f / dist, dist > 100 ? 0.10f : 0.20f);
     1305
     1306        // number of additional random points
     1307        int rounds = Math.min(drawSize/2, 1)+1;
     1308
     1309        // decrease random noise at high drop rate ( more accurate draw of fewer points )
     1310        rmsSizeX *= (1.0d - dropRate);
     1311        rmsSizeY *= (1.0d - dropRate);
     1312
     1313        double scaleVal = 0;
     1314
     1315        // interpolate line draw ( needs separate point instead of line )
     1316        while (scaleVal < (1.0d-0.0001d)) {
     1317
     1318            // get position
     1319            final double pntX = fromX + scaleVal * deltaX;
     1320            final double pntY = fromY + scaleVal * deltaY;
     1321
     1322            // add random distribution around sampled point
     1323            for (int k = 0; k < rounds; k++) {
     1324
     1325                // add error distribution, first point with less error
     1326                int x = (int) (pntX + heatMapRandom.nextGaussian() * (k > 0 ? rmsSizeX : rmsSizeX/4));
     1327                int y = (int) (pntY + heatMapRandom.nextGaussian() * (k > 0 ? rmsSizeY : rmsSizeY/4));
     1328
     1329                // draw it, even drop is requested
     1330                if (heatMapRandom.nextDouble() >= dropRate) {
     1331                    g.fillRect(x-drawSize, y-drawSize, drawSize, drawSize);
     1332                }
     1333            }
     1334            scaleVal += scaleStep;
     1335        }
    11851336    }
    11861337
     
    12061357                || (computeCacheColorDynamic != colorModeDynamic)
    12071358                || (computeCacheHeatMapDrawColorTableIdx != heatMapDrawColorTableIdx)
    1208                 || (!neutralColor.equals(computeCacheColorUsed))
     1359                || (!neutralColor.equals(computeCacheColorUsed)
     1360                || (computeCacheHeatMapDrawPointMode != heatMapDrawPointMode)
     1361                || (computeCacheHeatMapDrawGain != heatMapDrawGain))
     1362                || (computeCacheHeatMapDrawLowerLimit != heatMapDrawLowerLimit)
    12091363      ) {
    12101364            computeCacheMaxLineLengthUsed = maxLineLength;
     
    12151369            computeCacheColorDynamic = colorModeDynamic;
    12161370            computeCacheHeatMapDrawColorTableIdx = heatMapDrawColorTableIdx;
     1371            computeCacheHeatMapDrawPointMode = heatMapDrawPointMode;
     1372            computeCacheHeatMapDrawGain = heatMapDrawGain;
     1373            computeCacheHeatMapDrawLowerLimit = heatMapDrawLowerLimit;
    12171374        }
    12181375    }
Note: See TracChangeset for help on using the changeset viewer.