Ticket #13124: josm_ticket_13124_gpx_heat_map_by_kidelo.patch

File josm_ticket_13124_gpx_heat_map_by_kidelo.patch, 62.8 KB (added by kidelo, 7 years ago)

GPX Heat Map Extension by kidelo

  • src/org/openstreetmap/josm/gui/layer/gpx/GpxDrawHelper.java

     
    44import static org.openstreetmap.josm.tools.I18n.marktr;
    55import static org.openstreetmap.josm.tools.I18n.tr;
    66
     7import java.awt.AlphaComposite;
    78import java.awt.BasicStroke;
    89import java.awt.Color;
     10import java.awt.Composite;
    911import java.awt.Graphics2D;
     12import java.awt.LinearGradientPaint;
     13import java.awt.MultipleGradientPaint;
     14import java.awt.Paint;
    1015import java.awt.Point;
     16import java.awt.Rectangle;
    1117import java.awt.RenderingHints;
    1218import java.awt.Stroke;
     19import java.awt.image.BufferedImage;
     20import java.awt.image.DataBufferInt;
     21import java.awt.image.Raster;
    1322import java.util.ArrayList;
    1423import java.util.Arrays;
    1524import java.util.Collection;
    1625import java.util.Collections;
    1726import java.util.Date;
     27import java.util.LinkedList;
    1828import java.util.List;
    1929
    2030import org.openstreetmap.josm.Main;
     
    2838import org.openstreetmap.josm.data.preferences.ColorProperty;
    2939import org.openstreetmap.josm.gui.MapView;
    3040import org.openstreetmap.josm.tools.ColorScale;
     41import org.openstreetmap.josm.tools.Utils;
    3142
    3243/**
    3344 * Class that helps to draw large set of GPS tracks with different colors and options
     
    4556
    4657    // draw lines between points belonging to different segments
    4758    private boolean forceLines;
     59    // use alpha blending for line draw
     60    private boolean alphaLines;
    4861    // draw direction arrows on the lines
    4962    private boolean direction;
    50     /** don't draw lines if longer than x meters **/
     63    /** width of line for paint **/
    5164    private int lineWidth;
     65    /** don't draw lines if longer than x meters **/
    5266    private int maxLineLength;
     67    // draw lines
    5368    private boolean lines;
    5469    /** paint large dots for points **/
    5570    private boolean large;
     
    7388    private boolean computeCacheColorDynamic;
    7489    private ColorMode computeCacheColored;
    7590    private int computeCacheColorTracksTune;
     91    private int computeCacheHeatMapDrawColorTableIdx;
    7692
    7793    //// Color-related fields
    7894    /** Mode of the line coloring **/
     
    104120        {+ll0, -sl4, +sl4, -ll0}, {+ll0, +sl9, +ll0, -sl9}
    105121    };
    106122
     123    /** heat map parameters **/
     124
     125    // enabled or not (override by settings)
     126    private boolean heatMapEnabled = false;
     127    // draw small extra line
     128    private boolean heatMapDrawExtraLine = false;
     129    // used index for color table (parameter)
     130    private int heatMapDrawColorTableIdx = 0;
     131
     132    // normal buffered image and draw object (cached)
     133    private BufferedImage heatMapImgGray = null;
     134    private Graphics2D heatMapGraph2d = null;
     135
     136    // some cached values
     137    Rectangle heatMapCacheScreenBounds = new Rectangle();
     138    int heatMapCacheVisibleSegments = 0;
     139    double heatMapCacheZoomScale = 0;
     140    int heatMapCacheLineWith = 0;
     141
     142    // copied value for line drawing
     143    private List<Integer> heatMapPolyX = new ArrayList<Integer>();
     144    private List<Integer> heatMapPolyY = new ArrayList<Integer>();
     145
     146    // based on color map aka inferno with adjustment to more brighter values
     147    // Used to reconstruct the colormap in viscm (http://bids.github.io/colormap/)
     148    // parameters = {'xp': [-2.059155383623448, 55.527989656842408, 34.409268195655187, 4.76708575112832, -9.505963894261754},
     149    //               'yp': [-25.66489361702122, -20.376712328767127, 26.997716894977174, 20.56737588652487, 32.047872340425585},
     150    //               'min_Jp': 33.3692722372,
     151    //               'max_Jp': 95}
     152    private static final double[][] colorMapJosmInfernoBright = {
     153            {0.21520753, 0.27038249, 0.57012105},
     154            {0.22312459, 0.27103303, 0.57166455},
     155            {0.23080347, 0.27168503, 0.57324007},
     156            {0.23833063, 0.27231877, 0.57483869},
     157            {0.24568074, 0.27294692, 0.57646522},
     158            {0.25289476, 0.2735611, 0.57811579},
     159            {0.25997831, 0.27416334, 0.57979067},
     160            {0.2669379, 0.27475519, 0.58148978},
     161            {0.27380298, 0.27532947, 0.58321},
     162            {0.28055428, 0.27589622, 0.58495399},
     163            {0.287239, 0.2764405, 0.58671621},
     164            {0.29381911, 0.27697939, 0.58850116},
     165            {0.30035424, 0.27749166, 0.59030167},
     166            {0.3067935, 0.27799996, 0.59212335},
     167            {0.31320428, 0.2784783, 0.59395808},
     168            {0.31952836, 0.27895311, 0.59581191},
     169            {0.32583529, 0.27939586, 0.59767646},
     170            {0.3320659, 0.27983431, 0.59955752},
     171            {0.33828602, 0.28023999, 0.60144712},
     172            {0.34443919, 0.2806404, 0.60335045},
     173            {0.35058925, 0.28100652, 0.60525968},
     174            {0.35668082, 0.28136625, 0.60717967},
     175            {0.36277287, 0.28169157, 0.60910303},
     176            {0.3688165, 0.28200806, 0.61103375},
     177            {0.37486069, 0.28229156, 0.61296536},
     178            {0.38086771, 0.28256263, 0.61490057},
     179            {0.38687304, 0.28280326, 0.61683417},
     180            {0.39285321, 0.28302691, 0.61876728},
     181            {0.39882725, 0.28322388, 0.6206963},
     182            {0.40478897, 0.28339829, 0.62262037},
     183            {0.41073803, 0.28355108, 0.62453795},
     184            {0.41668846, 0.28367469, 0.62644568},
     185            {0.42261777, 0.28378305, 0.62834468},
     186            {0.42856299, 0.28385455, 0.63022847},
     187            {0.43448626, 0.28391267, 0.63210021},
     188            {0.44042184, 0.28393692, 0.63395343},
     189            {0.44634815, 0.28394123, 0.63578925},
     190            {0.45227252, 0.28392157, 0.63760476},
     191            {0.45820558, 0.28387152, 0.63939628},
     192            {0.46412436, 0.28380662, 0.6411656},
     193            {0.47006356, 0.28370439, 0.64290476},
     194            {0.47599406, 0.28358471, 0.64461676},
     195            {0.48192565, 0.28344156, 0.64629781},
     196            {0.48786962, 0.28326756, 0.64794343},
     197            {0.49380435, 0.28307827, 0.64955593},
     198            {0.49975251, 0.28285825, 0.65112821},
     199            {0.50570289, 0.28261587, 0.65266041},
     200            {0.51164619, 0.2823585, 0.65415271},
     201            {0.51760901, 0.28206718, 0.65559589},
     202            {0.52356861, 0.28175913, 0.65699349},
     203            {0.52952249, 0.28143693, 0.65834417},
     204            {0.5354961, 0.28108192, 0.65963775},
     205            {0.54146547, 0.28071283, 0.66087903},
     206            {0.54742976, 0.28033111, 0.66206616},
     207            {0.55340844, 0.27992224, 0.66318972},
     208            {0.55938599, 0.27949898, 0.66425255},
     209            {0.56535835, 0.27906539, 0.66525388},
     210            {0.57133473, 0.2786149, 0.66618743},
     211            {0.57731677, 0.27814689, 0.66704956},
     212            {0.58329279, 0.27767173, 0.66784277},
     213            {0.58926275, 0.27719044, 0.66856469},
     214            {0.59523878, 0.27669389, 0.66920646},
     215            {0.601212, 0.27619042, 0.66976956},
     216            {0.60717734, 0.27568537, 0.67025404},
     217            {0.61313444, 0.27518023, 0.67065755},
     218            {0.6190896, 0.27467077, 0.67097344},
     219            {0.62504118, 0.27415958, 0.67119949},
     220            {0.63098171, 0.27365438, 0.67133763},
     221            {0.63691049, 0.27315721, 0.67138563},
     222            {0.64282668, 0.27267021, 0.67134132},
     223            {0.64873334, 0.2721922, 0.67119941},
     224            {0.65463011, 0.27172501, 0.67095685},
     225            {0.66051031, 0.27127617, 0.67061602},
     226            {0.66637279, 0.27084839, 0.67017505},
     227            {0.67221629, 0.27044454, 0.66963216},
     228            {0.67803951, 0.27006762, 0.66898569},
     229            {0.68384101, 0.26972075, 0.66823411},
     230            {0.68962043, 0.2694062, 0.66737478},
     231            {0.69537788, 0.26912584, 0.66640432},
     232            {0.70110815, 0.26888634, 0.66532544},
     233            {0.70680955, 0.26869125, 0.66413723},
     234            {0.71248034, 0.26854423, 0.66283897},
     235            {0.71811869, 0.26844898, 0.66143017},
     236            {0.72372274, 0.26840929, 0.6599105},
     237            {0.72929057, 0.26842895, 0.65827991},
     238            {0.7348202, 0.26851179, 0.65653854},
     239            {0.74030966, 0.26866163, 0.65468679},
     240            {0.74575692, 0.26888225, 0.65272532},
     241            {0.75115994, 0.26917738, 0.65065505},
     242            {0.75651668, 0.26955067, 0.64847715},
     243            {0.76182512, 0.27000568, 0.64619309},
     244            {0.76708321, 0.27054582, 0.64380458},
     245            {0.77228898, 0.27117434, 0.64131363},
     246            {0.77744045, 0.27189431, 0.63872252},
     247            {0.78253571, 0.27270861, 0.63603377},
     248            {0.7875729, 0.27361986, 0.63325018},
     249            {0.79255022, 0.27463045, 0.6303748},
     250            {0.79746596, 0.27574247, 0.62741092},
     251            {0.80231908, 0.27695738, 0.62436032},
     252            {0.80710794, 0.27827681, 0.62122676},
     253            {0.81183046, 0.27970239, 0.6180159},
     254            {0.8164853, 0.28123501, 0.61473188},
     255            {0.82107123, 0.28287522, 0.611379},
     256            {0.82558714, 0.28462328, 0.60796168},
     257            {0.83003205, 0.28647909, 0.60448445},
     258            {0.8344051, 0.28844227, 0.60095193},
     259            {0.83870608, 0.29051198, 0.59736649},
     260            {0.84293394, 0.29268742, 0.59373445},
     261            {0.84708799, 0.29496738, 0.59006162},
     262            {0.8511679, 0.29735032, 0.58635274},
     263            {0.85517346, 0.29983446, 0.58261253},
     264            {0.85910459, 0.30241779, 0.57884566},
     265            {0.86296154, 0.30509821, 0.57505503},
     266            {0.8667442, 0.30787334, 0.57124668},
     267            {0.87045278, 0.31074062, 0.56742555},
     268            {0.87408763, 0.3136974, 0.5635959},
     269            {0.87764922, 0.31674094, 0.55976186},
     270            {0.88113814, 0.31986844, 0.55592693},
     271            {0.88455499, 0.32307701, 0.55209513},
     272            {0.88790044, 0.32636363, 0.54827073},
     273            {0.89117526, 0.32972536, 0.54445721},
     274            {0.89438031, 0.33315921, 0.54065783},
     275            {0.89751647, 0.33666226, 0.53687571},
     276            {0.90058468, 0.34023153, 0.53311396},
     277            {0.90358593, 0.34386414, 0.52937539},
     278            {0.90652122, 0.34755725, 0.52566255},
     279            {0.90939159, 0.35130809, 0.5219778},
     280            {0.91219812, 0.35511391, 0.51832354},
     281            {0.91494189, 0.35897199, 0.51470218},
     282            {0.91762398, 0.36287988, 0.51111515},
     283            {0.92024546, 0.36683514, 0.50756411},
     284            {0.92280743, 0.3708354, 0.50405059},
     285            {0.92531095, 0.37487835, 0.5005761},
     286            {0.92775723, 0.37896156, 0.49714278},
     287            {0.93014721, 0.38308319, 0.4937508},
     288            {0.93248195, 0.38724127, 0.49040111},
     289            {0.93476246, 0.3914339, 0.48709457},
     290            {0.93698975, 0.39565929, 0.48383192},
     291            {0.93916494, 0.39991543, 0.48061464},
     292            {0.94128894, 0.40420083, 0.47744286},
     293            {0.94336261, 0.40851411, 0.47431661},
     294            {0.94538686, 0.4128538, 0.47123623},
     295            {0.94736256, 0.41721856, 0.468202},
     296            {0.94929056, 0.42160708, 0.46521414},
     297            {0.95117192, 0.42601776, 0.46227381},
     298            {0.95300724, 0.4304498, 0.45938022},
     299            {0.95479724, 0.43490219, 0.4565332},
     300            {0.95654264, 0.43937392, 0.45373274},
     301            {0.95824414, 0.44386407, 0.45097877},
     302            {0.95990241, 0.44837175, 0.44827118},
     303            {0.96151825, 0.45289592, 0.44561039},
     304            {0.96309223, 0.45743586, 0.44299605},
     305            {0.9646248, 0.46199106, 0.44042751},
     306            {0.96611649, 0.46656086, 0.43790454},
     307            {0.96756782, 0.47114466, 0.43542688},
     308            {0.96897927, 0.47574187, 0.43299428},
     309            {0.9703513, 0.48035197, 0.43060645},
     310            {0.97168455, 0.48497424, 0.42826362},
     311            {0.9729793, 0.48960837, 0.42596514},
     312            {0.97423583, 0.49425407, 0.42371048},
     313            {0.97545448, 0.49891094, 0.42149932},
     314            {0.97663559, 0.50357865, 0.41933134},
     315            {0.97777945, 0.50825686, 0.41720624},
     316            {0.97888633, 0.51294528, 0.41512369},
     317            {0.97995654, 0.5176436, 0.41308347},
     318            {0.98099046, 0.52235141, 0.41108558},
     319            {0.98198808, 0.5270687, 0.40912927},
     320            {0.98294961, 0.53179527, 0.40721424},
     321            {0.98387521, 0.53653092, 0.40534022},
     322            {0.98476504, 0.54127549, 0.40350692},
     323            {0.98561924, 0.54602881, 0.40171405},
     324            {0.98643793, 0.55079072, 0.39996136},
     325            {0.98722122, 0.55556111, 0.3982486},
     326            {0.98796936, 0.5603397, 0.39657571},
     327            {0.98868226, 0.56512655, 0.39494219},
     328            {0.98936, 0.56992153, 0.3933478},
     329            {0.99000264, 0.57472455, 0.39179229},
     330            {0.99061026, 0.57953551, 0.39027544},
     331            {0.99118291, 0.5843543, 0.38879701},
     332            {0.99172063, 0.58918085, 0.38735678},
     333            {0.99222348, 0.59401505, 0.38595451},
     334            {0.99269152, 0.59885681, 0.38458997},
     335            {0.99312476, 0.60370607, 0.38326287},
     336            {0.99352325, 0.60856274, 0.38197295},
     337            {0.99388704, 0.61342672, 0.38071994},
     338            {0.9942162, 0.61829793, 0.37950355},
     339            {0.9945108, 0.62317625, 0.37832349},
     340            {0.99477091, 0.62806159, 0.37717942},
     341            {0.99499662, 0.63295383, 0.376071},
     342            {0.99518804, 0.63785286, 0.37499782},
     343            {0.99534511, 0.64275869, 0.37395927},
     344            {0.99546806, 0.64767111, 0.372955},
     345            {0.99555705, 0.65258996, 0.37198453},
     346            {0.99561226, 0.65751512, 0.37104728},
     347            {0.99563387, 0.66244643, 0.37014263},
     348            {0.99562207, 0.66738375, 0.36926988},
     349            {0.99557709, 0.6723269, 0.36842829},
     350            {0.99549919, 0.67727574, 0.367617},
     351            {0.99538832, 0.68223028, 0.3668348},
     352            {0.99524486, 0.6871903, 0.36608079},
     353            {0.99506928, 0.69215551, 0.36535402},
     354            {0.99486193, 0.69712571, 0.36465327},
     355            {0.99462316, 0.70210071, 0.36397724},
     356            {0.99435337, 0.70708033, 0.36332451},
     357            {0.99405297, 0.71206436, 0.36269354},
     358            {0.99372226, 0.7170527, 0.36208252},
     359            {0.99336115, 0.72204548, 0.36148919},
     360            {0.99297075, 0.72704212, 0.36091215},
     361            {0.99255157, 0.73204239, 0.36034933},
     362            {0.99210417, 0.7370461, 0.35979848},
     363            {0.9916291, 0.74205305, 0.35925722},
     364            {0.99112697, 0.74706302, 0.35872299},
     365            {0.99059766, 0.75207625, 0.35819249},
     366            {0.99004191, 0.75709248, 0.3576629},
     367            {0.98946091, 0.76211118, 0.35713157},
     368            {0.98885534, 0.76713218, 0.35659523},
     369            {0.98822592, 0.77215526, 0.3560504},
     370            {0.98757341, 0.77718026, 0.35549344},
     371            {0.98689724, 0.78220771, 0.35491939},
     372            {0.98619894, 0.78723702, 0.35432474},
     373            {0.98547981, 0.79226775, 0.35370552},
     374            {0.98474069, 0.79729971, 0.35305716},
     375            {0.98398247, 0.80233272, 0.35237485},
     376            {0.98320475, 0.80736733, 0.3516524},
     377            {0.98240874, 0.81240322, 0.35088461},
     378            {0.9815964, 0.81743967, 0.35006665},
     379            {0.9807688, 0.8224765, 0.34919255},
     380            {0.97992705, 0.82751352, 0.34825605},
     381            {0.97906945, 0.83255201, 0.34724796},
     382            {0.97819995, 0.83759034, 0.34616365},
     383            {0.97731989, 0.84262826, 0.34499577},
     384            {0.97643067, 0.84766553, 0.34373656},
     385            {0.97553084, 0.85270337, 0.34237486},
     386            {0.9746244, 0.85774026, 0.34090421},
     387            {0.97371347, 0.86277567, 0.3393158},
     388            {0.97279937, 0.8678095, 0.33759927},
     389            {0.97188135, 0.87284272, 0.33574113},
     390            {0.97096472, 0.87787333, 0.33373367},
     391            {0.97005177, 0.88290083, 0.33156512},
     392            {0.96914211, 0.88792612, 0.3292193},
     393            {0.9682402, 0.89294775, 0.32668465},
     394            {0.96735004, 0.89796453, 0.32394813},
     395            {0.96647255, 0.90297683, 0.32099108},
     396            {0.96561166, 0.9079836, 0.31779718},
     397            {0.96477281, 0.91298312, 0.31435094},
     398            {0.96395774, 0.91797553, 0.31062911},
     399            {0.96317157, 0.92295935, 0.30661145},
     400            {0.96242046, 0.92793272, 0.30227735},
     401            {0.96170672, 0.93289563, 0.29759626},
     402            {0.96103783, 0.93784572, 0.29254395},
     403            {0.96041973, 0.94278138, 0.2870899},
     404            {0.95985661, 0.94770189, 0.28119495},
     405            {0.95935719, 0.95260448, 0.27482525},
     406            {0.95892721, 0.9574878, 0.26793369},
     407            {0.95857402, 0.9623498, 0.2604697},
     408            {0.9583055, 0.96718821, 0.25237504}
     409
     410    }; // end of colorMapJosmInfernoBright
     411
     412    // Used to reconstruct the colormap in viscm (http://bids.github.io/colormap/)
     413    // {'xp': [-8.9697272381347659, -14.962877923066287, -32.371553722153038, -30.373836827175865, -6.9720103431575922, -8.684339110280888},
     414    // 'yp': [-25.513698630136986, 15.296803652968038, -30.650684931506845, 20.719178082191775, 25.570776255707756, 32.990867579908667},
     415    // 'min_Jp': 33.3692722372,
     416    // 'max_Jp': 95}
     417    private static final double[][] colorMapJosmViridisBright = {
     418            {0.0331179, 0.29975688, 0.57579112},
     419            {0.04256739, 0.3028265, 0.57286799},
     420            {0.0512467, 0.30584793, 0.57009011},
     421            {0.05914659, 0.30882508, 0.5674503},
     422            {0.06654831, 0.3117815, 0.5647918},
     423            {0.07345228, 0.31469781, 0.56227052},
     424            {0.07995739, 0.31757925, 0.55986398},
     425            {0.08619278, 0.32044241, 0.55745447},
     426            {0.09212195, 0.32327213, 0.55517502},
     427            {0.09780928, 0.32607626, 0.55297584},
     428            {0.10331033, 0.32886259, 0.55080222},
     429            {0.10859491, 0.33162106, 0.54875221},
     430            {0.11372563, 0.33436292, 0.54673886},
     431            {0.11870231, 0.33708616, 0.54479015},
     432            {0.12351267, 0.33978626, 0.54295921},
     433            {0.12822783, 0.3424784, 0.541114},
     434            {0.13279867, 0.34515022, 0.53938101},
     435            {0.13725748, 0.34780851, 0.53770555},
     436            {0.14161799, 0.35045603, 0.53606914},
     437            {0.14585622, 0.3530865, 0.53454415},
     438            {0.15002582, 0.35571293, 0.5330082},
     439            {0.15408488, 0.35832466, 0.5315781},
     440            {0.15806357, 0.36092928, 0.53018482},
     441            {0.16195691, 0.36352554, 0.52884943},
     442            {0.16576237, 0.36611275, 0.52758751},
     443            {0.16949979, 0.36869662, 0.52634711},
     444            {0.1731454, 0.37127046, 0.52520656},
     445            {0.17673382, 0.37384485, 0.52406183},
     446            {0.18023309, 0.37641026, 0.52302135},
     447            {0.18367353, 0.37897678, 0.5219859},
     448            {0.18703181, 0.38153698, 0.52104134},
     449            {0.19032855, 0.38409867, 0.52011314},
     450            {0.19354787, 0.38665632, 0.5192658},
     451            {0.19670438, 0.38921652, 0.51843878},
     452            {0.19978526, 0.39177424, 0.51769017},
     453            {0.20280277, 0.39433623, 0.51695934},
     454            {0.20574448, 0.39689659, 0.51631112},
     455            {0.20862219, 0.39946359, 0.51567252},
     456            {0.21142395, 0.40203013, 0.51511745},
     457            {0.21415805, 0.40460435, 0.51457709},
     458            {0.21681705, 0.40718098, 0.51410601},
     459            {0.21940294, 0.40976427, 0.5136727},
     460            {0.22191346, 0.4123542, 0.51328427},
     461            {0.22434688, 0.41494912, 0.51295975},
     462            {0.22670152, 0.4175556, 0.51265342},
     463            {0.22897578, 0.42016834, 0.51241261},
     464            {0.23116721, 0.42279102, 0.51221511},
     465            {0.23327224, 0.42542553, 0.5120539},
     466            {0.23529113, 0.42806929, 0.51195199},
     467            {0.23721876, 0.4307259, 0.51189091},
     468            {0.23905088, 0.43339672, 0.51186833},
     469            {0.24078829, 0.43607984, 0.51190068},
     470            {0.24242777, 0.43877648, 0.5119848},
     471            {0.24395722, 0.44149191, 0.51209793},
     472            {0.24538089, 0.44422266, 0.51226326},
     473            {0.24669486, 0.44696981, 0.512479},
     474            {0.24789484, 0.44973443, 0.51274365},
     475            {0.24897073, 0.45251957, 0.51304809},
     476            {0.24992008, 0.45532509, 0.51339708},
     477            {0.25074106, 0.45815079, 0.51379462},
     478            {0.25142853, 0.46099761, 0.51423996},
     479            {0.2519772, 0.46386643, 0.51473252},
     480            {0.25238166, 0.46675805, 0.51527187},
     481            {0.25263645, 0.46967322, 0.51585774},
     482            {0.25273608, 0.47261257, 0.51648993},
     483            {0.25267511, 0.47557666, 0.51716828},
     484            {0.25244821, 0.47856593, 0.51789267},
     485            {0.25205017, 0.4815807, 0.51866293},
     486            {0.25147595, 0.48462119, 0.51947882},
     487            {0.25072074, 0.48768749, 0.52034},
     488            {0.24977992, 0.49077954, 0.52124597},
     489            {0.24864914, 0.49389721, 0.52219605},
     490            {0.24732429, 0.4970402, 0.52318934},
     491            {0.2458015, 0.50020813, 0.5242247},
     492            {0.24407712, 0.50340048, 0.52530069},
     493            {0.24213937, 0.50661827, 0.52641341},
     494            {0.23998809, 0.50986008, 0.52756205},
     495            {0.2376243, 0.51312431, 0.5287454},
     496            {0.23504515, 0.51641008, 0.52996075},
     497            {0.23223599, 0.51971846, 0.53120267},
     498            {0.22919943, 0.52304738, 0.53246911},
     499            {0.22593842, 0.52639479, 0.53375736},
     500            {0.22243635, 0.52976175, 0.53506084},
     501            {0.21869708, 0.53314592, 0.53637622},
     502            {0.21472177, 0.53654544, 0.53769908},
     503            {0.21049097, 0.53996144, 0.53902151},
     504            {0.20602332, 0.5433894, 0.54034076},
     505            {0.20129189, 0.54683133, 0.54164703},
     506            {0.19632014, 0.5502822, 0.54293704},
     507            {0.1910817, 0.5537437, 0.54420016},
     508            {0.1856016, 0.55721085, 0.54543206},
     509            {0.17986279, 0.56068409, 0.5466222},
     510            {0.17388472, 0.56415953, 0.54776421},
     511            {0.16767592, 0.56763485, 0.54884959},
     512            {0.16124118, 0.57110836, 0.54986858},
     513            {0.15461572, 0.57457555, 0.55081476},
     514            {0.14782653, 0.57803342, 0.55167986},
     515            {0.14090518, 0.58147916, 0.55245531},
     516            {0.13391472, 0.5849079, 0.5531362},
     517            {0.12692017, 0.58831582, 0.55371706},
     518            {0.12000191, 0.59169907, 0.55419371},
     519            {0.11326013, 0.59505365, 0.55456398},
     520            {0.10681409, 0.59837568, 0.55482785},
     521            {0.10079543, 0.60166197, 0.55498638},
     522            {0.09534887, 0.60490977, 0.55504241},
     523            {0.09063347, 0.60811639, 0.55500215},
     524            {0.08679049, 0.61128079, 0.55487004},
     525            {0.08394854, 0.6144023, 0.55465197},
     526            {0.08221537, 0.61748019, 0.55435753},
     527            {0.08164325, 0.6205151, 0.55399342},
     528            {0.08223013, 0.62350855, 0.55356369},
     529            {0.08394439, 0.62646065, 0.55308231},
     530            {0.08668611, 0.6293742, 0.55254872},
     531            {0.09034668, 0.63225032, 0.55197376},
     532            {0.09479378, 0.63509139, 0.55135952},
     533            {0.09989897, 0.63789906, 0.55071423},
     534            {0.10553908, 0.64067585, 0.55003665},
     535            {0.11160448, 0.64342303, 0.54933941},
     536            {0.11800283, 0.64614329, 0.54861541},
     537            {0.12465323, 0.64883798, 0.54787462},
     538            {0.13149053, 0.65150897, 0.54711829},
     539            {0.13846531, 0.65415809, 0.5463437},
     540            {0.1455335, 0.65678673, 0.54555555},
     541            {0.15265709, 0.65939627, 0.54476003},
     542            {0.15981518, 0.66198821, 0.54395146},
     543            {0.16698676, 0.66456378, 0.54313117},
     544            {0.17415531, 0.66712409, 0.54230031},
     545            {0.18130458, 0.66967023, 0.54146307},
     546            {0.18842545, 0.67220323, 0.54061925},
     547            {0.19551341, 0.67472397, 0.53976647},
     548            {0.2025629, 0.67723331, 0.53890516},
     549            {0.20956984, 0.67973202, 0.53803564},
     550            {0.2165314, 0.68222082, 0.53715809},
     551            {0.22344573, 0.68470038, 0.53627262},
     552            {0.23031175, 0.6871713, 0.53537923},
     553            {0.23712903, 0.68963415, 0.53447784},
     554            {0.24389765, 0.69208946, 0.53356833},
     555            {0.25061808, 0.6945377, 0.53265048},
     556            {0.25729111, 0.69697932, 0.53172407},
     557            {0.26391778, 0.6994147, 0.53078879},
     558            {0.27049933, 0.70184423, 0.52984433},
     559            {0.27703712, 0.70426823, 0.52889033},
     560            {0.28353264, 0.70668701, 0.52792642},
     561            {0.28998746, 0.70910086, 0.52695218},
     562            {0.29640321, 0.71151001, 0.52596722},
     563            {0.30278152, 0.71391471, 0.5249711},
     564            {0.30912407, 0.71631516, 0.52396339},
     565            {0.31543254, 0.71871153, 0.52294366},
     566            {0.3217086, 0.72110401, 0.52191145},
     567            {0.3279539, 0.72349274, 0.52086633},
     568            {0.33417008, 0.72587784, 0.51980786},
     569            {0.34035875, 0.72825944, 0.5187356},
     570            {0.34652147, 0.73063763, 0.51764911},
     571            {0.35265864, 0.73301261, 0.51654918},
     572            {0.35877137, 0.73538448, 0.51543583},
     573            {0.3648628, 0.73775319, 0.51430692},
     574            {0.37093438, 0.74011877, 0.51316203},
     575            {0.37698751, 0.74248126, 0.51200075},
     576            {0.38302357, 0.7448407, 0.51082267},
     577            {0.38904388, 0.7471971, 0.5096274},
     578            {0.39504973, 0.74955049, 0.50841457},
     579            {0.40104082, 0.75190103, 0.5071855},
     580            {0.40701899, 0.75424866, 0.50593917},
     581            {0.41298655, 0.75659325, 0.50467398},
     582            {0.41894464, 0.75893479, 0.50338958},
     583            {0.42489434, 0.76127325, 0.50208564},
     584            {0.43083671, 0.76360862, 0.50076184},
     585            {0.43677259, 0.76594088, 0.4994181},
     586            {0.44270136, 0.7682702, 0.49805594},
     587            {0.44862605, 0.77059628, 0.49667271},
     588            {0.45454756, 0.77291909, 0.49526814},
     589            {0.46046679, 0.77523857, 0.49384196},
     590            {0.46638456, 0.77755469, 0.4923939},
     591            {0.47230148, 0.7798674, 0.49092398},
     592            {0.47821759, 0.78217677, 0.48943283},
     593            {0.48413494, 0.78448256, 0.48791871},
     594            {0.49005423, 0.78678471, 0.48638138},
     595            {0.49597616, 0.78908317, 0.48482063},
     596            {0.50190137, 0.79137786, 0.48323626},
     597            {0.5078303, 0.79366877, 0.48162833},
     598            {0.5137638, 0.79595578, 0.47999631},
     599            {0.51970278, 0.79823879, 0.47833963},
     600            {0.52564777, 0.80051774, 0.47665809},
     601            {0.53159931, 0.80279254, 0.47495153},
     602            {0.53755788, 0.80506315, 0.47321975},
     603            {0.54352447, 0.80732942, 0.47146196},
     604            {0.54949945, 0.80959129, 0.46967805},
     605            {0.55548322, 0.81184871, 0.4678679},
     606            {0.56147617, 0.8141016, 0.46603131},
     607            {0.5674787, 0.81634992, 0.46416812},
     608            {0.57349149, 0.81859354, 0.46227771},
     609            {0.57951582, 0.82083223, 0.46035867},
     610            {0.58555114, 0.82306609, 0.45841193},
     611            {0.59159777, 0.82529507, 0.45643726},
     612            {0.59765599, 0.82751909, 0.45443444},
     613            {0.60372607, 0.82973812, 0.45240324},
     614            {0.60980907, 0.83195194, 0.45034236},
     615            {0.61590608, 0.83416034, 0.44825035},
     616            {0.62201604, 0.83636352, 0.44612867},
     617            {0.62813915, 0.83856142, 0.443977},
     618            {0.6342756, 0.84075401, 0.44179501},
     619            {0.64042559, 0.84294124, 0.43958235},
     620            {0.64658927, 0.84512308, 0.43733862},
     621            {0.65276961, 0.84729891, 0.43505957},
     622            {0.65896438, 0.84946917, 0.43274795},
     623            {0.66517356, 0.85163387, 0.43040351},
     624            {0.67139725, 0.85379299, 0.4280257},
     625            {0.67763555, 0.85594651, 0.42561396},
     626            {0.68388856, 0.8580944, 0.42316766},
     627            {0.69015635, 0.86023667, 0.42068611},
     628            {0.69643989, 0.86237309, 0.41816731},
     629            {0.70274062, 0.86450334, 0.41560843},
     630            {0.70905639, 0.86662792, 0.41301167},
     631            {0.71538721, 0.86874683, 0.41037609},
     632            {0.72173308, 0.87086011, 0.40770069},
     633            {0.72809396, 0.87296778, 0.40498434},
     634            {0.7344698, 0.87506988, 0.40222583},
     635            {0.74086053, 0.87716647, 0.39942384},
     636            {0.74726606, 0.8792576, 0.39657691},
     637            {0.75368625, 0.88134336, 0.39368347},
     638            {0.76012096, 0.88342383, 0.39074178},
     639            {0.76656999, 0.88549913, 0.38774996},
     640            {0.77303308, 0.8875694, 0.38470595},
     641            {0.77950995, 0.88963477, 0.38160749},
     642            {0.78600023, 0.89169545, 0.37845211},
     643            {0.79250349, 0.89375165, 0.37523713},
     644            {0.79901921, 0.89580363, 0.37195957},
     645            {0.80554676, 0.8978517, 0.36861621},
     646            {0.8120854, 0.89989622, 0.3652035},
     647            {0.81863422, 0.90193761, 0.36171754},
     648            {0.82519216, 0.90397637, 0.35815405},
     649            {0.83175795, 0.9060131, 0.35450833},
     650            {0.83833008, 0.90804849, 0.3507752},
     651            {0.84490673, 0.91008337, 0.34694895},
     652            {0.85148576, 0.91211872, 0.34302325},
     653            {0.85806684, 0.91415505, 0.33898699},
     654            {0.86465276, 0.91619201, 0.3348209},
     655            {0.87123317, 0.91823316, 0.33052851},
     656            {0.87780375, 0.9202804, 0.32609988},
     657            {0.88435971, 0.92233593, 0.32152327},
     658            {0.89091043, 0.92439784, 0.31675269},
     659            {0.89743393, 0.9264737, 0.31180336},
     660            {0.9039243, 0.92856651, 0.3066523},
     661            {0.91038551, 0.93067656, 0.30124469},
     662            {0.91678774, 0.93281453, 0.29559791},
     663            {0.92313653, 0.9349809, 0.28963085},
     664            {0.92939556, 0.93718902, 0.28335908},
     665            {0.93555967, 0.93944353, 0.2766966},
     666            {0.94159054, 0.94175935, 0.26963707},
     667            {0.94745672, 0.94414992, 0.26213946},
     668            {0.95312442, 0.94662987, 0.25415069},
     669            {0.9585465, 0.94921781, 0.24566287},
     670            {0.96367764, 0.95193147, 0.23667632},
     671            {0.96847776, 0.95478608, 0.22720755},
     672            {0.97291733, 0.95779215, 0.21729217},
     673            {0.97698301, 0.96095356, 0.20697158}
     674
     675    }; // end of colorMapJosmViridisBright
     676
     677    // diverging red to blue, designed by http://colorbrewer2.org/
     678    private static Color[] colorMapJosmRed2Blue = {
     679            new Color(0, 0, 0),
     680            new Color(49, 54, 149),
     681            new Color(69, 117, 180),
     682            new Color(116, 173, 209),
     683            new Color(171, 217, 233),
     684            new Color(224, 243, 248),
     685            new Color(254, 224, 144),
     686            new Color(253, 174, 97),
     687            new Color(244, 109, 67),
     688            new Color(215, 48, 39),
     689            new Color(165, 0, 38),
     690    };
     691
     692    // diverging brown to green, designed by http://colorbrewer2.org/
     693    private static Color[] colorMapJosmBrown2SeaGreen = {
     694            new Color(0, 0, 0),
     695            new Color(1, 102, 9),
     696            new Color(53, 151, 143),
     697            new Color(128, 205, 193),
     698            new Color(199, 234, 229),
     699            new Color(246, 232, 195),
     700            new Color(223, 194, 125),
     701            new Color(191, 129, 45),
     702            new Color(140, 81, 10)
     703    };
     704
     705    // setup color maps used by heat map
     706    private static Color[] heatMapLutColorJosmInferno = createColorFromRawArray(colorMapJosmInfernoBright);
     707    private static Color[] heatMapLutColorJosmViridis = createColorFromRawArray(colorMapJosmViridisBright);
     708    private static Color[] heatMapLutColorJosmBrown2Sea = createColorLut(colorMapJosmBrown2SeaGreen);
     709    private static Color[] heatMapLutColorJosmRed2Blue = createColorLut(colorMapJosmRed2Blue);
     710
     711    // user defined heatmap color
     712    private Color[] heatMapLutUserColor = createColorLut(Color.BLACK, Color.WHITE);
     713
     714    // heat map color in use
     715    private Color[] heatMapLutColor = null;
     716
    107717    private void setupColors() {
    108718        hdopAlpha = Main.pref.getInteger("hdop.color.alpha", -1);
    109719        velocityScale = ColorScale.createHSBScale(256);
     
    111721        hdopScale = ColorScale.createHSBScale(256).makeReversed().addTitle(tr("HDOP"));
    112722        dateScale = ColorScale.createHSBScale(256).addTitle(tr("Time"));
    113723        directionScale = ColorScale.createCyclicScale(256).setIntervalCount(4).addTitle(tr("Direction"));
     724        heatMapLutColor = heatMapLutUserColor;
     725
    114726        systemOfMeasurementChanged(null, null);
    115727    }
    116728
     
    127739     * Different color modes
    128740     */
    129741    public enum ColorMode {
    130         NONE, VELOCITY, HDOP, DIRECTION, TIME;
     742        NONE, VELOCITY, HDOP, DIRECTION, TIME, HEATMAP;
    131743
    132744        static ColorMode fromIndex(final int index) {
    133745            return values()[index];
     
    198810        forceLines = Main.pref.getBoolean("draw.rawgps.lines.force", spec, false);
    199811        direction = Main.pref.getBoolean("draw.rawgps.direction", spec, false);
    200812        lineWidth = Main.pref.getInteger("draw.rawgps.linewidth", spec, 0);
     813        alphaLines = Main.pref.getBoolean("draw.rawgps.lines.alpha-blend", spec, false);
    201814
    202815        if (!data.fromServer) {
    203816            maxLineLength = Main.pref.getInteger("draw.rawgps.max-line-length.local", spec, -1);
     
    219832        minTrackDurationForTimeColoring = Main.pref.getInteger("draw.rawgps.date-coloring-min-dt", 60);
    220833        largePointAlpha = Main.pref.getInteger("draw.rawgps.large.alpha", -1) & 0xFF;
    221834
     835        // get heatmap parameters
     836        heatMapEnabled = Main.pref.getBoolean("draw.rawgps.heatmap.enabled", spec, false);
     837        heatMapDrawExtraLine = Main.pref.getBoolean("draw.rawgps.heatmap.line-extra", spec, false);
     838        heatMapDrawColorTableIdx = Main.pref.getInteger("draw.rawgps.heatmap.colormap", specName(layerName), 0);
     839
    222840        neutralColor = getColor(layerName, true);
    223841        velocityScale.setNoDataColor(neutralColor);
    224842        dateScale.setNoDataColor(neutralColor);
     
    230848
    231849    public void drawAll(Graphics2D g, MapView mv, List<WayPoint> visibleSegments) {
    232850
     851        final long timeStart = System.currentTimeMillis();
     852
    233853        checkCache();
    234854
    235855        // STEP 2b - RE-COMPUTE CACHE DATA *********************
     
    237857            calculateColors();
    238858        }
    239859
    240         Stroke storedStroke = g.getStroke();
     860        fixColors(visibleSegments);
     861
     862        // backup the environment
     863        Composite oldComposite = g.getComposite();
     864        Stroke oldStroke = g.getStroke();
     865        Paint oldPaint = g.getPaint();
    241866
     867        // set hints for the render
    242868        g.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
    243869            Main.pref.getBoolean("mappaint.gpx.use-antialiasing", false) ?
    244870                    RenderingHints.VALUE_ANTIALIAS_ON : RenderingHints.VALUE_ANTIALIAS_OFF);
     
    246872        if (lineWidth != 0) {
    247873            g.setStroke(new BasicStroke(lineWidth, BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND));
    248874        }
    249         fixColors(visibleSegments);
    250         drawLines(g, mv, visibleSegments);
     875
     876        // global enabled or select via color
     877        boolean useHeatMap = heatMapEnabled || ColorMode.HEATMAP == colored;
     878
     879        // default global alpha level
     880        float layerAlpha = 1.00f;
     881
     882        // extract current alpha blending value
     883        if (oldComposite instanceof AlphaComposite) {
     884            layerAlpha = ((AlphaComposite) oldComposite).getAlpha();
     885        }
     886
     887        // use heatmap background layer
     888        if (useHeatMap) {
     889            drawHeatMap(g, mv, visibleSegments);
     890        } else {
     891            // use normal line style or alpha-blending lines
     892            if (!alphaLines) {
     893                drawLines(g, mv, visibleSegments);
     894            } else {
     895                drawLinesAlpha(g, mv, visibleSegments, layerAlpha);
     896            }
     897        }
     898
     899        // override global alpha settings (smooth overlay)
     900        if (alphaLines || useHeatMap) {
     901            g.setComposite(AlphaComposite.SrcOver.derive(0.25f * layerAlpha));
     902        }
     903
     904        // normal overlays
    251905        drawArrows(g, mv, visibleSegments);
    252906        drawPoints(g, mv, visibleSegments);
    253         if (lineWidth != 0) {
    254             g.setStroke(storedStroke);
     907
     908        // restore environment
     909        g.setPaint(oldPaint);
     910        g.setStroke(oldStroke);
     911        g.setComposite(oldComposite);
     912
     913        // show some debug info
     914        if (Main.isDebugEnabled() && !visibleSegments.isEmpty()) {
     915            final long timeDiff = System.currentTimeMillis() - timeStart;
     916
     917            Main.debug("gpxdraw::draw takes " +
     918                         Utils.getDurationString(timeDiff) +
     919                         "(" +
     920                         "segments= " + visibleSegments.size() +
     921                         ", per 10000 = " + Utils.getDurationString(10000 * timeDiff / visibleSegments.size()) +
     922                         ")"
     923              );
    255924        }
    256925    }
    257926
     
    3911060            }
    3921061        }
    3931062
     1063        // heat mode
     1064        if (ColorMode.HEATMAP == colored) {
     1065
     1066            // generate new user color map
     1067            heatMapLutUserColor = createColorLut(Color.BLACK, neutralColor.darker(),
     1068                                                 neutralColor, neutralColor.brighter(), Color.WHITE);
     1069
     1070            // decide what, keep order is sync with setting on GUI
     1071            Color[][] lut = {
     1072                    heatMapLutUserColor,
     1073                    heatMapLutColorJosmInferno,
     1074                    heatMapLutColorJosmViridis,
     1075                    heatMapLutColorJosmBrown2Sea,
     1076                    heatMapLutColorJosmRed2Blue,
     1077            };
     1078
     1079            // select by index
     1080            if (heatMapDrawColorTableIdx < lut.length) {
     1081                heatMapLutColor = lut[ heatMapDrawColorTableIdx ];
     1082            } else {
     1083                // fallback
     1084                heatMapLutColor = heatMapLutUserColor;
     1085            }
     1086
     1087            // force redraw of image
     1088            heatMapCacheVisibleSegments = 0;
     1089
     1090        } // end of if (ColorMode.HEATMAP == colored)
     1091
    3941092        computeCacheInSync = true;
    3951093    }
    3961094
     
    5501248        } // end if large
    5511249    }
    5521250
     1251    // draw GPS a lines by using alpha blending
     1252    private void drawLinesAlpha(Graphics2D g, MapView mv, List<WayPoint> visibleSegments, float layerAlpha) {
     1253
     1254        // early abort
     1255        if (lineWidth < 1) return;
     1256
     1257        // 1st. backup the paint environment ----------------------------------
     1258        Composite oldComposite = g.getComposite();
     1259        Stroke oldStroke = g.getStroke();
     1260        Paint oldPaint = g.getPaint();
     1261
     1262        // 2nd. determine current scale factors -------------------------------
     1263
     1264        // cache scale of view
     1265        final double zoomScale = mv.getScale();
     1266
     1267        // 3rd. determine current paint parameters -----------------------------
     1268
     1269        // alpha value is based on zoom and line with combined with global layer alpha
     1270        float theLineAlpha = Math.min(Math.max((0.50f/(float) zoomScale)/(lineWidth + 1), 0.001f), 0.50f) * layerAlpha;
     1271        final int theLineWith = (int) (lineWidth / zoomScale) + 1;
     1272
     1273        // 4th setup virtual paint area ----------------------------------------
     1274
     1275        // set line format and alpha channel for all overlays (more lines -> few overlap -> more transparency)
     1276        g.setStroke(new BasicStroke(theLineWith, BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND));
     1277        g.setComposite(AlphaComposite.SrcOver.derive(theLineAlpha));
     1278
     1279        // last used / calculated entries
     1280        Point lastPaintPnt = null;
     1281
     1282        // 5th draw the layer ---------------------------------------------------
     1283
     1284        // for all points
     1285        for (WayPoint trkPnt : visibleSegments) {
     1286
     1287            // transform coordinates
     1288            final Point paintPnt = mv.getPoint(trkPnt.getEastNorth());
     1289
     1290            // skip single points
     1291            if (lastPaintPnt != null && trkPnt.drawLine && !lastPaintPnt.equals(paintPnt)) {
     1292
     1293                // set different color
     1294                g.setColor(trkPnt.customColoring);
     1295
     1296                // draw it
     1297                g.drawLine(lastPaintPnt.x, lastPaintPnt.y, paintPnt.x, paintPnt.y);
     1298
     1299            } // end of if()
     1300
     1301            lastPaintPnt = paintPnt;
     1302
     1303        } // end of for()
     1304
     1305        // @last restore modified paint environment -----------------------------
     1306        g.setPaint(oldPaint);
     1307        g.setStroke(oldStroke);
     1308        g.setComposite(oldComposite);
     1309    }
     1310
     1311    // creates a linear distributed colormap by linear blending between colors
     1312    protected static Color[] createColorLut(Color... colors) {
     1313
     1314        // number of lookup entries
     1315        int tableSize = 256;
     1316
     1317        // create image an paint object
     1318        BufferedImage img = new BufferedImage(tableSize, 1, BufferedImage.TYPE_INT_RGB);
     1319        Graphics2D g = img.createGraphics();
     1320
     1321        float[] fract = new float[ colors.length ];
     1322
     1323        // distribute fractions (define position of color in map)
     1324        for (int i = 0; i < colors.length; ++i) {
     1325            fract[i] = i * (1.0f / colors.length);
     1326        }
     1327
     1328        // draw the gradient map
     1329        LinearGradientPaint gradient = new LinearGradientPaint(0, 0, tableSize, 1, fract, colors,
     1330                                                               MultipleGradientPaint.CycleMethod.NO_CYCLE);
     1331        g.setPaint(gradient);
     1332        g.fillRect(0, 0, tableSize, 1);
     1333        g.dispose();
     1334
     1335        // access it via raw interface
     1336        final Raster imgRaster = img.getData();
     1337
     1338        // the pixel storage
     1339        int[] pixel = new int[1];
     1340
     1341        Color[] colorTable = new Color[tableSize];
     1342
     1343        // map the range 0..255 to 0..pi/2
     1344        final double mapTo90Deg = Math.PI / 2.0 / 255.0;
     1345
     1346        // create the lookup table
     1347        for (int i = 0; i < tableSize; i++) {
     1348
     1349            // get next single pixel
     1350            imgRaster.getDataElements((int) (i * (double) img.getWidth() / tableSize), 0, pixel);
     1351
     1352            // get color and map
     1353            Color c = new Color(pixel[0]);
     1354
     1355            // smooth alpha like sin curve
     1356            int alpha = (int) (Math.sin(i * mapTo90Deg) * 255);
     1357
     1358            // alpha with pre-offset, first color -> full transparent
     1359            alpha = i > 0 ? (75 + alpha) : 0;
     1360
     1361            // shrink to maximum bound
     1362            if (alpha > 255) {
     1363                alpha = 255;
     1364            }
     1365
     1366            // increase transparency for higher values ( avoid big saturation )
     1367            if (i > 240 && 255 == alpha) {
     1368                alpha -= (i - 240);
     1369            }
     1370
     1371            // fill entry in table, assign a alpha value
     1372            colorTable[i] = new Color(c.getRed(), c.getGreen(), c.getBlue(), alpha);
     1373        }
     1374
     1375        // transform into lookup table
     1376        return colorTable;
     1377
     1378    } // end of createColorLut()
     1379
     1380    // creates a colormap by using a static color map with 1..n colors (RGB 0.0 ..1.0)
     1381    protected static Color[] createColorFromRawArray(double[][] data) {
     1382
     1383        // create the array
     1384        Color[] color = new Color[ data.length ];
     1385
     1386        for (int k = 0; k < data.length; k++) {
     1387           // cast an map to linear array
     1388           color[k] = new Color((float) data[k][0], (float) data[k][1], (float) data[k][2]);
     1389        }
     1390
     1391        // forward
     1392        return createColorLut(color);
     1393
     1394    } // end of createColorFromRawArray()
     1395
     1396    // collect and draw GPS segments and displays a heat-map
     1397    private void drawHeatMap(Graphics2D g, MapView mv, List<WayPoint> visibleSegments) {
     1398
     1399        // early abort
     1400        if (lineWidth < 1) return;
     1401
     1402        long timeStart;
     1403
     1404        // 1st setup virtual paint area ----------------------------------------
     1405
     1406        // get bounds of screen image and projection
     1407        final Rectangle screenBounds = g.getDeviceConfiguration().getBounds();
     1408
     1409        // cache scale of view
     1410        final double zoomScale = mv.getScale();
     1411
     1412        if (Main.isDebugEnabled()) {
     1413            Main.debug("gpxdraw::heat : bound = " + screenBounds.width + "x" + screenBounds.height);
     1414        }
     1415
     1416        // HACK: sometime screen bounds does not return valid values when picture is shifted
     1417        // therefore we use a bigger area to avoid missing parts of image
     1418        screenBounds.width = screenBounds.width * 3 / 2;
     1419        screenBounds.height = screenBounds.height * 3 / 2;
     1420
     1421        // new image buffer needed
     1422        boolean imageSetup = !heatMapCacheScreenBounds.equals(screenBounds) || null == heatMapImgGray;
     1423
     1424        // screen bounds changed, need new image buffer ?
     1425        if (imageSetup) {
     1426            timeStart = System.currentTimeMillis();
     1427
     1428            // we would use a "pure" grayscale image, but there is not efficient way to map gray scale values to RGB)
     1429            heatMapImgGray = new BufferedImage(screenBounds.width, screenBounds.height, BufferedImage.TYPE_INT_ARGB);
     1430
     1431            heatMapGraph2d = heatMapImgGray.createGraphics();
     1432            heatMapGraph2d.setBackground(new Color(0, 0, 0, 255));
     1433            heatMapGraph2d.setColor(Color.WHITE);
     1434
     1435            // cache it
     1436            heatMapCacheScreenBounds = screenBounds;
     1437
     1438            // show some debug info
     1439            if (Main.isDebugEnabled()) {
     1440                Main.debug("gpxdraw::heat : new buffer takes " + Utils.getDurationString(System.currentTimeMillis() - timeStart));
     1441            }
     1442        }
     1443
     1444        // shortcuts
     1445        final BufferedImage imgG = heatMapImgGray;
     1446        final Graphics2D gB = heatMapGraph2d;
     1447
     1448        // 2nd. determine current scale factors -------------------------------
     1449
     1450        // alpha combines both values, therefore the foreground shall be lighter
     1451        final float lineAlphaB = Math.min(Math.max((0.40f/(float) zoomScale)/(lineWidth + 1), 0.001f), 0.50f);
     1452        final float lineAlphaF = lineAlphaB / 1.5f;
     1453
     1454        // derive draw parameters
     1455        final Composite fC = AlphaComposite.SrcOver.derive(lineAlphaF);
     1456        final Composite bC = AlphaComposite.SrcOver.derive(lineAlphaB);
     1457
     1458        // the line width (foreground: draw extra small footprint line of track)
     1459        final int lineWidthB = Math.max((int) (lineWidth / zoomScale) + 1, 2);
     1460        final int lineWidthF = lineWidthB > 2 ? lineWidth - 1 : 0;
     1461
     1462        final Stroke bS = new BasicStroke(lineWidthB, BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND);
     1463        final Stroke fS = new BasicStroke(lineWidthF, BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND);
     1464
     1465        // set initial values
     1466        gB.setStroke(bS); gB.setComposite(bC);
     1467
     1468        if (Main.isDebugEnabled()) {
     1469            Main.debug("gpxdraw::heat : GPX segments = " + visibleSegments.size());
     1470            Main.debug("gpxdraw::heat : zoomscale = " + zoomScale);
     1471            Main.debug("gpxdraw::heat : f-line-alpha = " + lineAlphaF);
     1472            Main.debug("gpxdraw::heat : b-line-alpha = " + lineAlphaB);
     1473            Main.debug("gpxdraw::heat : f-line-width = " + lineWidthF);
     1474            Main.debug("gpxdraw::heat : b-line-with = " + lineWidthB);
     1475        }
     1476
     1477        // 3rd Calculate the heat map data by draw GPX traces with alpha value ----------
     1478
     1479        timeStart = System.currentTimeMillis();
     1480
     1481        // clear data storage
     1482        heatMapPolyX.clear(); heatMapPolyY.clear();
     1483
     1484        // dummy list
     1485        List<WayPoint> listSegm = new LinkedList<>();
     1486
     1487        // recalculation of image needed
     1488        final boolean imageRecalc = heatMapCacheVisibleSegments != visibleSegments.size() ||
     1489                                    heatMapCacheZoomScale != zoomScale ||
     1490                                    heatMapCacheLineWith != lineWidth;
     1491
     1492        // need re-generation of gray image ?
     1493        if (imageSetup || imageRecalc) {
     1494
     1495            // clear background
     1496            gB.clearRect(0, 0, heatMapImgGray.getWidth(), heatMapImgGray.getHeight());
     1497
     1498            // use real data
     1499            listSegm = visibleSegments;
     1500
     1501            // remember
     1502            heatMapCacheVisibleSegments = visibleSegments.size();
     1503            heatMapCacheZoomScale = zoomScale;
     1504            heatMapCacheLineWith = lineWidth;
     1505        }
     1506
     1507        // for all points, draw single lines by using optimize drawing
     1508        for (WayPoint trkPnt : listSegm) {
     1509
     1510            // something to paint or color changed (new segment needed, decrease performance ;-()
     1511            if (!trkPnt.drawLine && !heatMapPolyX.isEmpty()) {
     1512
     1513                // convert to primitive type
     1514                final int[] polyXArr = heatMapPolyX.stream().mapToInt(Integer::intValue).toArray();
     1515                final int[] polyYArr = heatMapPolyY.stream().mapToInt(Integer::intValue).toArray();
     1516
     1517                // a.) draw background
     1518                gB.drawPolyline(polyXArr, polyYArr, polyXArr.length);
     1519
     1520                // b.) draw extra foreground
     1521                if (heatMapDrawExtraLine && lineWidthF > 0) {
     1522
     1523                    gB.setStroke(fS); gB.setComposite(fC);
     1524                    gB.drawPolyline(polyXArr, polyYArr, polyXArr.length);
     1525                    gB.setStroke(bS); gB.setComposite(bC);
     1526                }
     1527
     1528                // drop used pints
     1529                heatMapPolyX.clear(); heatMapPolyY.clear();
     1530
     1531            } else {
     1532
     1533                // get transformed coordinates
     1534                final Point paintPnt = mv.getPoint(trkPnt.getEastNorth());
     1535
     1536                // store only the integer part (make sense because pixel is 1:1 here)
     1537                heatMapPolyX.add((int) paintPnt.getX());
     1538                heatMapPolyY.add((int) paintPnt.getY());
     1539            }
     1540
     1541        } // end of for()
     1542
     1543        // show some debug info
     1544        if (Main.isDebugEnabled()) {
     1545            Main.debug("gpxdraw::heat : gray-image takes " + Utils.getDurationString(System.currentTimeMillis() - timeStart));
     1546        }
     1547
     1548        // 4th. Draw data on target layer, map data via color lookup table --------------
     1549
     1550        timeStart = System.currentTimeMillis();
     1551
     1552        final int[] imgPixels = ((DataBufferInt) imgG.getRaster().getDataBuffer()).getData();
     1553
     1554        // samples offset and bounds are scaled with line width derived from zoom level
     1555        final int offX = Math.max(1, lineWidthB / 2);
     1556        final int offY = Math.max(1, lineWidthB / 2);
     1557
     1558        final int maxPixelX = imgG.getWidth();
     1559        final int maxPixelY = imgG.getHeight();
     1560
     1561        int lastPixelY = 0; int lastPixelColor = 0;
     1562
     1563        // resample gray scale image with line linear weight of next sample in line
     1564        // process each line and draw pixels / rectangles with same color with one operations
     1565        for (int x = 0; x < maxPixelX; x += offX) {
     1566            for (int y = 0; y < maxPixelY; y += offY) {
     1567
     1568                int thePixelColor = 0;
     1569
     1570                // sample the image (it is gray scale)
     1571                int offset = (x * maxPixelX) + y;
     1572
     1573                // merge next pixels of window of line
     1574                for (int k = 0; k < offX && offset + k < imgPixels.length; k++) {
     1575                    thePixelColor += imgPixels[offset+k] & 0xFF;
     1576                }
     1577
     1578                // mean value
     1579                thePixelColor /= offX;
     1580
     1581                // restart -> use initial sample
     1582                if (0 == y) {
     1583                    lastPixelY = 0; lastPixelColor = thePixelColor;
     1584                }
     1585
     1586                // different color to last one ?
     1587                if (Math.abs(lastPixelColor - thePixelColor) > 1) {
     1588
     1589                    // draw only foreground pixels, skip small variations
     1590                    if (lastPixelColor > 1+1) {
     1591
     1592                        // gray to RGB mapping
     1593                        g.setColor(heatMapLutColor[ lastPixelColor ]);
     1594
     1595                        // start point for draw (
     1596                        int yN = lastPixelY > 0 ? lastPixelY - 1 : y;
     1597
     1598                        // box from from last Y pixel to current pixel
     1599                        if (offX < lineWidth + 2) {
     1600                            g.fillRect(yN, x, offY + y - yN, offX);
     1601                        } else {
     1602                            g.drawRect(yN, x, offY + y - yN, offX);
     1603                        }
     1604                    }
     1605                    // restart detection
     1606                    lastPixelY = y; lastPixelColor = thePixelColor;
     1607                }
     1608            }
     1609
     1610        } // end of for()
     1611
     1612        // show some debug info
     1613        if (Main.isDebugEnabled()) {
     1614            Main.debug("gpxdraw::heat : color-image takes " + Utils.getDurationString(System.currentTimeMillis() - timeStart));
     1615        }
     1616
     1617    } // end of drawHeatMap()
     1618
    5531619    private void fixColors(List<WayPoint> visibleSegments) {
    5541620        for (WayPoint trkPnt : visibleSegments) {
    5551621            if (trkPnt.customColoring == null) {
     
    5641630    private void checkCache() {
    5651631        if ((computeCacheMaxLineLengthUsed != maxLineLength) || (!neutralColor.equals(computeCacheColorUsed))
    5661632                || (computeCacheColored != colored) || (computeCacheColorTracksTune != colorTracksTune)
    567                 || (computeCacheColorDynamic != colorModeDynamic)) {
     1633                || (computeCacheColorDynamic != colorModeDynamic)
     1634                || (computeCacheHeatMapDrawColorTableIdx != heatMapDrawColorTableIdx)
     1635      ) {
    5681636            computeCacheMaxLineLengthUsed = maxLineLength;
    5691637            computeCacheInSync = false;
    5701638            computeCacheColorUsed = neutralColor;
    5711639            computeCacheColored = colored;
    5721640            computeCacheColorTracksTune = colorTracksTune;
    5731641            computeCacheColorDynamic = colorModeDynamic;
     1642            computeCacheHeatMapDrawColorTableIdx = heatMapDrawColorTableIdx;
    5741643        }
    5751644    }
    5761645
     
    5801649
    5811650    public void drawColorBar(Graphics2D g, MapView mv) {
    5821651        int w = mv.getWidth();
     1652
     1653        // set do default
     1654        g.setComposite(AlphaComposite.SrcOver.derive(1.00f));
     1655
    5831656        if (colored == ColorMode.HDOP) {
    5841657            hdopScale.drawColorBar(g, w-30, 50, 20, 100, 1.0);
    5851658        } else if (colored == ColorMode.VELOCITY) {
  • src/org/openstreetmap/josm/gui/preferences/display/GPXSettingsPanel.java

     
    5555    private final JRadioButton colorTypeDirection = new JRadioButton(tr("Direction (red = west, yellow = north, green = east, blue = south)"));
    5656    private final JRadioButton colorTypeDilution = new JRadioButton(tr("Dilution of Position (red = high, green = low, if available)"));
    5757    private final JRadioButton colorTypeTime = new JRadioButton(tr("Track date"));
     58    private final JRadioButton colorTypeHeatMap = new JRadioButton(tr("Heat Map (dark = few tracks, bright = many tracks)"));
    5859    private final JRadioButton colorTypeNone = new JRadioButton(tr("Single Color (can be customized for named layers)"));
    5960    private final JRadioButton colorTypeGlobal = new JRadioButton(tr("Use global settings"));
    6061    private final JosmComboBox<String> colorTypeVelocityTune = new JosmComboBox<>(new String[] {tr("Car"), tr("Bicycle"), tr("Foot")});
     62    private final JosmComboBox<String> colorTypeHeatMapTune = new JosmComboBox<>(new String[] {tr("User"), tr("Inferno"), tr("Viridis"),
     63                                                                                 tr("Wood"), tr("Heat")});
    6164    private final JCheckBox makeAutoMarkers = new JCheckBox(tr("Create markers when reading GPX"));
    6265    private final JCheckBox drawGpsArrows = new JCheckBox(tr("Draw Direction Arrows"));
    6366    private final JCheckBox drawGpsArrowsFast = new JCheckBox(tr("Fast drawing (looks uglier)"));
     
    6871    private final JosmComboBox<String> audioWaypointLabel = new JosmComboBox<>(LABEL_PATTERN_DESC);
    6972    private final JosmTextField audioWaypointLabelPattern = new JosmTextField();
    7073    private final JCheckBox useGpsAntialiasing = new JCheckBox(tr("Smooth GPX graphics (antialiasing)"));
     74    private final JCheckBox drawLineWithAlpha = new JCheckBox(tr("Draw with Opacity (alpha blending) "));
    7175
    7276    private String layerName;
    7377    private final boolean local; // flag to display LocalOnly checkbox
     
    179183            drawGpsArrowsMinDist.setEnabled(drawGpsArrows.isSelected() && drawGpsArrows.isEnabled());
    180184        });
    181185        drawGpsArrows.setToolTipText(tr("Draw direction arrows for lines, connecting GPS points."));
    182         add(drawGpsArrows, GBC.eop().insets(40, 0, 0, 0));
     186        add(drawGpsArrows, GBC.eop().insets(20, 0, 0, 0));
    183187
    184188        // drawGpsArrowsFast
    185189        drawGpsArrowsFast.setToolTipText(tr("Draw the direction arrows using table lookups instead of complex math."));
    186         add(drawGpsArrowsFast, GBC.eop().insets(60, 0, 0, 0));
     190        add(drawGpsArrowsFast, GBC.eop().insets(40, 0, 0, 0));
    187191        ExpertToggleAction.addVisibilitySwitcher(drawGpsArrowsFast);
    188192
    189193        // drawGpsArrowsMinDist
    190194        drawGpsArrowsMinDist.setToolTipText(tr("Do not draw arrows if they are not at least this distance away from the last one."));
    191         add(new JLabel(tr("Minimum distance (pixels)")), GBC.std().insets(60, 0, 0, 0));
     195        add(new JLabel(tr("Minimum distance (pixels)")), GBC.std().insets(40, 0, 0, 0));
    192196        add(drawGpsArrowsMinDist, GBC.eol().fill(GBC.HORIZONTAL).insets(5, 0, 0, 5));
    193197
    194198        // hdopCircleGpsPoints
     
    210214        add(useGpsAntialiasing, GBC.eop().insets(20, 0, 0, 0));
    211215        ExpertToggleAction.addVisibilitySwitcher(useGpsAntialiasing);
    212216
     217        // alpha blending
     218        drawLineWithAlpha.setToolTipText(tr("Apply dynamic alpha-blending and adjust width based on zoom level for all GPX lines."));
     219        add(drawLineWithAlpha, GBC.eop().insets(20, 0, 0, 0));
     220        ExpertToggleAction.addVisibilitySwitcher(drawLineWithAlpha);
     221
    213222        // colorTracks
    214223        ButtonGroup colorGroup = new ButtonGroup();
    215224        if (layerName != null) {
     
    220229        colorGroup.add(colorTypeDirection);
    221230        colorGroup.add(colorTypeDilution);
    222231        colorGroup.add(colorTypeTime);
     232        colorGroup.add(colorTypeHeatMap);
    223233
    224234        colorTypeVelocity.addChangeListener(e -> {
    225235            colorTypeVelocityTune.setEnabled(colorTypeVelocity.isSelected());
    226236            colorDynamic.setEnabled(colorTypeVelocity.isSelected() || colorTypeDilution.isSelected());
    227237        });
     238
     239        colorTypeHeatMap.addChangeListener(e -> {
     240            colorTypeHeatMapTune.setEnabled(colorTypeHeatMap.isSelected());
     241            colorDynamic.setEnabled(false);
     242        });
     243
    228244        colorTypeDilution.addChangeListener(e -> colorDynamic.setEnabled(colorTypeVelocity.isSelected() || colorTypeDilution.isSelected()));
    229245
    230246        colorTypeNone.setToolTipText(tr("All points and track segments will have the same color. Can be customized in Layer Manager."));
     
    233249        colorTypeDilution.setToolTipText(
    234250                tr("Colors points and track segments by dilution of position (HDOP). Your capture device needs to log that information."));
    235251        colorTypeTime.setToolTipText(tr("Colors points and track segments by its timestamp."));
     252        colorTypeHeatMap.setToolTipText(tr("Collected points and track segments for a position and displayed as heat map."));
    236253
    237254        // color Tracks by Velocity Tune
    238255        colorTypeVelocityTune.setToolTipText(tr("Allows to tune the track coloring for different average speeds."));
    239256
     257        colorTypeHeatMapTune.setToolTipText(tr("Selects the color schema for heat map."));
     258
    240259        add(Box.createVerticalGlue(), GBC.eol().insets(0, 20, 0, 0));
    241260
    242261        add(new JLabel(tr("Track and Point Coloring")), GBC.eol().insets(20, 0, 0, 0));
     
    249268        add(colorTypeDirection, GBC.eol().insets(40, 0, 0, 0));
    250269        add(colorTypeDilution, GBC.eol().insets(40, 0, 0, 0));
    251270        add(colorTypeTime, GBC.eol().insets(40, 0, 0, 0));
     271        add(colorTypeHeatMap, GBC.std().insets(40, 0, 0, 0));
     272        add(colorTypeHeatMapTune, GBC.eop().insets(5, 0, 0, 5));
     273
    252274        ExpertToggleAction.addVisibilitySwitcher(colorTypeDirection);
    253275        ExpertToggleAction.addVisibilitySwitcher(colorTypeDilution);
    254276
     
    315337        drawRawGpsMaxLineLengthLocal.setText(Integer.toString(Main.pref.getInteger("draw.rawgps.max-line-length.local", layerName, -1)));
    316338        drawRawGpsMaxLineLength.setText(Integer.toString(Main.pref.getInteger("draw.rawgps.max-line-length", layerName, 200)));
    317339        drawLineWidth.setText(Integer.toString(Main.pref.getInteger("draw.rawgps.linewidth", layerName, 0)));
     340        drawLineWithAlpha.setSelected(Main.pref.getBoolean("draw.rawgps.lines.alpha-blend", layerName, false));
    318341        forceRawGpsLines.setSelected(Main.pref.getBoolean("draw.rawgps.lines.force", layerName, false));
    319342        drawGpsArrows.setSelected(Main.pref.getBoolean("draw.rawgps.direction", layerName, false));
    320343        drawGpsArrowsFast.setSelected(Main.pref.getBoolean("draw.rawgps.alternatedirection", layerName, false));
     
    322345        hdopCircleGpsPoints.setSelected(Main.pref.getBoolean("draw.rawgps.hdopcircle", layerName, false));
    323346        largeGpsPoints.setSelected(Main.pref.getBoolean("draw.rawgps.large", layerName, false));
    324347        useGpsAntialiasing.setSelected(Main.pref.getBoolean("mappaint.gpx.use-antialiasing", false));
     348
    325349        drawRawGpsLinesActionListener.actionPerformed(null);
    326350
    327351        if (layerName != null && Main.pref.get("draw.rawgps.colors."+layerName).isEmpty()) {
     
    336360            case 2: colorTypeDilution.setSelected(true); break;
    337361            case 3: colorTypeDirection.setSelected(true); break;
    338362            case 4: colorTypeTime.setSelected(true); break;
     363            case 5: colorTypeHeatMap.setSelected(true); break;
    339364            default: Main.warn("Unknown color type: " + colorType);
    340365            }
    341366            int ccts = Main.pref.getInteger("draw.rawgps.colorTracksTune", layerName, 45);
    342367            colorTypeVelocityTune.setSelectedIndex(ccts == 10 ? 2 : (ccts == 20 ? 1 : 0));
    343368            colorTypeVelocityTune.setEnabled(colorTypeVelocity.isSelected() && colorTypeVelocity.isEnabled());
     369
     370            colorTypeHeatMapTune.setSelectedIndex(Main.pref.getInteger("draw.rawgps.heatmap.colormap", layerName, 0));
     371            colorTypeHeatMapTune.setEnabled(colorTypeHeatMap.isSelected() && colorTypeHeatMap.isEnabled());
     372
    344373            colorDynamic.setSelected(Main.pref.getBoolean("draw.rawgps.colors.dynamic", layerName, false));
    345374            colorDynamic.setEnabled(colorTypeVelocity.isSelected() || colorTypeDilution.isSelected());
    346375        }
     
    385414        Main.pref.put("draw.rawgps.hdopcircle"+layerNameDot, hdopCircleGpsPoints.isSelected());
    386415        Main.pref.put("draw.rawgps.large"+layerNameDot, largeGpsPoints.isSelected());
    387416        Main.pref.put("draw.rawgps.linewidth"+layerNameDot, drawLineWidth.getText());
     417        Main.pref.put("draw.rawgps.lines.alpha-blend"+layerNameDot, drawLineWithAlpha.isSelected());
     418
    388419        Main.pref.put("mappaint.gpx.use-antialiasing", useGpsAntialiasing.isSelected());
    389420
    390421        TemplateEntryProperty.forMarker(layerName).put(waypointLabelPattern.getText());
     
    403434            Main.pref.putInteger("draw.rawgps.colors"+layerNameDot, 3);
    404435        } else if (colorTypeTime.isSelected()) {
    405436            Main.pref.putInteger("draw.rawgps.colors"+layerNameDot, 4);
     437        } else if (colorTypeHeatMap.isSelected()) {
     438            Main.pref.putInteger("draw.rawgps.colors"+layerNameDot, 5);
    406439        } else {
    407440            Main.pref.putInteger("draw.rawgps.colors"+layerNameDot, 0);
    408441        }
    409442        Main.pref.put("draw.rawgps.colors.dynamic"+layerNameDot, colorDynamic.isSelected());
    410443        int ccti = colorTypeVelocityTune.getSelectedIndex();
    411444        Main.pref.putInteger("draw.rawgps.colorTracksTune"+layerNameDot, ccti == 2 ? 10 : (ccti == 1 ? 20 : 45));
     445
     446        Main.pref.putInteger("draw.rawgps.heatmap.colormap"+layerNameDot, colorTypeHeatMapTune.getSelectedIndex());
     447
    412448        return false;
    413449    }
    414450