Ignore:
Timestamp:
2009-06-06T22:25:31+02:00 (15 years ago)
Author:
stoecker
Message:

lots of updates due to josm changes

Location:
applications/editors/josm/plugins/slippymap
Files:
2 edited

Legend:

Unmodified
Added
Removed
  • applications/editors/josm/plugins/slippymap/build.xml

    r14015 r15707  
    2626                <attribute name="Plugin-Description" value="Displays a slippy map grid in JOSM. Can load tiles from slippy map as background and request updates."/>
    2727                <attribute name="Plugin-Link" value="http://wiki.openstreetmap.org/index.php/JOSM/Plugins/SlippyMap"/>
    28                 <attribute name="Plugin-Mainversion" value="1465"/>
     28                <attribute name="Plugin-Mainversion" value="1646"/>
    2929                <attribute name="Plugin-Version" value="${version.entry.commit.revision}"/>
    3030            </manifest>
  • applications/editors/josm/plugins/slippymap/src/org/openstreetmap/josm/plugins/slippymap/SlippyMapLayer.java

    r14732 r15707  
    3636/**
    3737 * Class that displays a slippy map layer.
    38  * 
     38 *
    3939 * @author Frederik Ramm <frederik@remote.org>
    4040 * @author LuVar <lubomir.varga@freemap.sk>
    4141 * @author Dave Hansen <dave@sr71.net>
    42  * 
     42 *
    4343 */
    4444public class SlippyMapLayer extends Layer implements ImageObserver,
    45                 PreferenceChangedListener {
    46         /**
    47          * Actual zoom lvl. Initial zoom lvl is set to
    48          * {@link SlippyMapPreferences#getMinZoomLvl()}.
    49          */
    50         public int currentZoomLevel = SlippyMapPreferences.getMinZoomLvl();
    51         private HashMap<SlippyMapKey, SlippyMapTile> tileStorage = null;
    52 
    53         Point[][] pixelpos = new Point[21][21];
    54         LatLon lastTopLeft;
    55         LatLon lastBotRight;
    56         int z12x0, z12x1, z12y0, z12y1;
    57         private Image bufferImage;
    58         private SlippyMapTile clickedTile;
    59         private boolean needRedraw;
    60         private JPopupMenu tileOptionMenu;
    61 
    62         @SuppressWarnings("serial")
    63         public SlippyMapLayer() {
    64                 super(tr("Slippy Map"));
    65                 background = true;
    66 
    67                 clearTileStorage();
    68 
    69                 tileOptionMenu = new JPopupMenu();
    70                 tileOptionMenu.add(new JMenuItem(new AbstractAction(tr("Load Tile")) {
    71                         public void actionPerformed(ActionEvent ae) {
    72                                 if (clickedTile != null) {
    73                                         loadSingleTile(clickedTile);
    74                                         needRedraw = true;
    75                                         Main.map.repaint();
    76                                 }
    77                         }
    78                 }));
    79 
    80                 tileOptionMenu.add(new JMenuItem(new AbstractAction(
    81                                 tr("Show Tile Status")) {
    82                         public void actionPerformed(ActionEvent ae) {
    83                                 if (clickedTile != null) {
    84                                         clickedTile.loadMetadata();
    85                                         needRedraw = true;
    86                                         Main.map.repaint();
    87                                 }
    88                         }
    89                 }));
    90 
    91                 tileOptionMenu.add(new JMenuItem(new AbstractAction(
    92                                 tr("Request Update")) {
    93                         public void actionPerformed(ActionEvent ae) {
    94                                 if (clickedTile != null) {
    95                                         clickedTile.requestUpdate();
    96                                         needRedraw = true;
    97                                         Main.map.repaint();
    98                                 }
    99                         }
    100                 }));
    101 
    102                 tileOptionMenu.add(new JMenuItem(new AbstractAction(
    103                                 tr("Load All Tiles")) {
    104                         public void actionPerformed(ActionEvent ae) {
    105                                 loadAllTiles();
    106                                 needRedraw = true;
    107                                 Main.map.repaint();
    108                         }
    109                 }));
    110 
    111                 // increase and decrease commands
    112                 tileOptionMenu.add(new JMenuItem(
    113                                 new AbstractAction(tr("Increase zoom")) {
    114                                         public void actionPerformed(ActionEvent ae) {
    115                                                 increaseZoomLevel();
    116                                                 needRedraw = true;
    117                                                 Main.map.repaint();
    118                                         }
    119                                 }));
    120 
    121                 tileOptionMenu.add(new JMenuItem(
    122                                 new AbstractAction(tr("Decrease zoom")) {
    123                                         public void actionPerformed(ActionEvent ae) {
    124                                                 decreaseZoomLevel();
    125                                                 Main.map.repaint();
    126                                         }
    127                                 }));
    128                 // end of adding menu commands
    129 
    130                 SwingUtilities.invokeLater(new Runnable() {
    131                         public void run() {
    132                                 Main.map.mapView.addMouseListener(new MouseAdapter() {
    133                                         @Override
    134                                         public void mouseClicked(MouseEvent e) {
    135                                                 if (e.getButton() != MouseEvent.BUTTON3)
    136                                                         return;
    137                                                 clickedTile = getTileForPixelpos(e.getX(), e.getY());
    138                                                 tileOptionMenu.show(e.getComponent(), e.getX(), e
    139                                                                 .getY());
    140                                         }
    141                                 });
    142 
    143                                 listeners.add(new LayerChangeListener() {
    144                                         public void activeLayerChange(Layer oldLayer, Layer newLayer) {
    145                                         }
    146 
    147                                         public void layerAdded(Layer newLayer) {
    148                                         }
    149 
    150                                         public void layerRemoved(Layer oldLayer) {
    151                                                 Main.pref.listener.remove(SlippyMapLayer.this);
    152                                         }
    153                                 });
    154                         }
    155                 });
    156 
    157                 Main.pref.listener.add(this);
    158         }
    159 
    160         /**
    161          * Zoom in, go closer to map.
    162          */
    163         public void increaseZoomLevel() {
    164                 if (currentZoomLevel < SlippyMapPreferences.getMaxZoomLvl()) {
    165                         currentZoomLevel++;
    166                         Main.debug("increasing zoom level to: " + currentZoomLevel);
    167                         needRedraw = true;
    168                 } else {
    169                         System.err.println("current zoom lvl ("+currentZoomLevel+") couldnt be increased. "+
    170                                                          "MaxZoomLvl ("+SlippyMapPreferences.getMaxZoomLvl()+") reached.");
    171                 }
    172         }
    173 
    174         /**
    175          * Zoom out from map.
    176          */
    177         public void decreaseZoomLevel() {
    178                 if (currentZoomLevel > SlippyMapPreferences.getMinZoomLvl()) {
    179                         Main.debug("decreasing zoom level to: " + currentZoomLevel);
    180                         currentZoomLevel--;
    181                         needRedraw = true;
    182                 } else {
    183                         System.err.println("current zoom lvl couldnt be decreased. MinZoomLvl reached.");
    184                 }
    185         }
    186 
    187         public void clearTileStorage() {
    188                 // when max zoom lvl is begin saved, this method is called and probably
    189                 // the setting isnt saved yet.
    190                 int maxZoom = 30; // SlippyMapPreferences.getMaxZoomLvl();
    191                 tileStorage = new HashMap<SlippyMapKey, SlippyMapTile>();
    192 
    193                 checkTileStorage();
    194         }
    195 
    196         class TileTimeComp implements Comparator<SlippyMapTile> {
    197                         public int compare(SlippyMapTile s1, SlippyMapTile s2) {
    198                                         long t1 = s1.access_time();
    199                                         long t2 = s2.access_time();
    200                                         if (s1 == s2)
    201                                                         return 0;
    202                                         if (t1 == t2) {
    203                                                         t1 = s1.hashCode();
    204                                                         t2 = s2.hashCode();
    205                                         }
    206                                         if (t1 < t2)
    207                                                         return -1;
    208                                         return 1;
    209                         }
    210         }
    211 
    212         long lastCheck = 0;
    213         /**
    214          * <p>
    215          * Check if tiles.size() is not more than max_nr_tiles. If yes, oldest tiles by timestamp
    216          * are fired out from cache.
    217          * </p>
    218          */
    219         public void checkTileStorage() {
    220                 int maxZoom = 30; // SlippyMapPreferences.getMaxZoomLvl();
    221                 long now = System.currentTimeMillis();
    222                 if (now - lastCheck < 1000)
    223                                 return;
    224                 lastCheck = now;
    225                 TreeSet<SlippyMapTile> tiles = new TreeSet<SlippyMapTile>(new TileTimeComp());
    226                 tiles.addAll(tileStorage.values());
    227                 int max_nr_tiles = 100;
    228                 if (tiles.size() < max_nr_tiles) {
    229                         Main.debug("total of " + tiles.size() + " loaded tiles, size OK " + now);
    230                         return;
    231                 }
    232                 int nr_to_drop = tiles.size() - max_nr_tiles;;
    233                 Main.debug("total of " + tiles.size() + " tiles, need to flush " + nr_to_drop + " tiles");
    234                 for (SlippyMapTile t : tiles) {
    235                         if (nr_to_drop <= 0)
    236                                         break;
    237                         t.dropImage();
    238                         nr_to_drop--;
    239                 }
    240         }
    241 
    242         void loadSingleTile(SlippyMapTile tile)
    243         {
    244                 tile.loadImage();
    245                 this.checkTileStorage();
    246         }
    247 
    248         void loadAllTiles() {
    249                 MapView mv = Main.map.mapView;
    250                 LatLon topLeft = mv.getLatLon(0, 0);
    251                 LatLon botRight = mv.getLatLon(mv.getWidth(), mv.getHeight());
    252                 z12x0 = lonToTileX(topLeft.lon());
    253                 z12x1 = lonToTileX(botRight.lon());
    254                 z12y0 = latToTileY(topLeft.lat());
    255                 z12y1 = latToTileY(botRight.lat());
    256                 if (z12x0 > z12x1) {
    257                         int tmp = z12x0;
    258                         z12x0 = z12x1;
    259                         z12x1 = tmp;
    260                 }
    261                 if (z12y0 > z12y1) {
    262                         int tmp = z12y0;
    263                         z12y0 = z12y1;
    264                         z12y1 = tmp;
    265                 }
    266                 // if there is more than 18 tiles on screen in any direction, do not
    267                 // load all tiles!
    268                 if (z12x1 - z12x0 > 18) {
    269                         System.out
    270                                         .println("Not downloading all tiles because there is more than 18 tiles on X axis!");
    271                         return;
    272                 }
    273                 if (z12y1 - z12y0 > 18) {
    274                         System.out
    275                                         .println("Not downloading all tiles because there is more than 18 tiles on Y axis!");
    276                         return;
    277                 }
    278 
    279                 for (int x = z12x0 - 1; x <= z12x1; x++) {
    280                         for (int y = z12y0 - 1; y <= z12y1; y++) {
    281                                 SlippyMapKey key = new SlippyMapKey(currentZoomLevel, x, y);
    282                                 SlippyMapTile tile = tileStorage.get(key);
    283                                 if (!key.valid) {
    284                                                 System.out.println("paint-1() made invalid key");
    285                                                 continue;
    286                                 }
    287                                 if (tile == null)
    288                                         tileStorage.put(key,
    289                                                         tile = new SlippyMapTile(x, y, currentZoomLevel));
    290                                 if (tile.getImage() == null) {
    291                                         this.loadSingleTile(tile);
    292                                 }
    293                         }
    294                 }
    295         }
    296 
    297         /*
    298          * Attempt to approximate how much the image is
    299          * being scaled.  For instance, a 100x100 image
    300          * being scaled to 50x50 would return 0.25.
    301          */
    302         double getImageScaling(Image img, Point p0, Point p1)
    303         {
    304                 int realWidth = img.getWidth(this);
    305                 int realHeight = img.getHeight(this);
    306                 if (realWidth == -1 || realHeight == -1)
    307                                 return 1.0;
    308                 int drawWidth = p1.x - p0.x;
    309                 int drawHeight = p1.x - p0.x;
    310 
    311                 double drawArea = drawWidth * drawHeight;
    312                 double realArea = realWidth * realHeight;
    313 
    314                 return drawArea / realArea;
    315         }
    316 
    317         /**
     45    PreferenceChangedListener {
     46    /**
     47     * Actual zoom lvl. Initial zoom lvl is set to
     48     * {@link SlippyMapPreferences#getMinZoomLvl()}.
    31849     */
    319         @Override
    320         public void paint(Graphics g, MapView mv) {
    321                 long start = System.currentTimeMillis();
    322                 LatLon topLeft = mv.getLatLon(0, 0);
    323                 LatLon botRight = mv.getLatLon(mv.getWidth(), mv.getHeight());
    324                 Graphics oldg = g;
    325 
    326                 if (botRight.lon() == 0.0 || botRight.lat() == 0) {
    327                                 // probably still initializing
    328                                 return;
    329                 }
    330                 if (lastTopLeft != null && lastBotRight != null
    331                                 && topLeft.equalsEpsilon(lastTopLeft)
    332                                 && botRight.equalsEpsilon(lastBotRight) && bufferImage != null
    333                                 && mv.getWidth() == bufferImage.getWidth(null)
    334                                 && mv.getHeight() == bufferImage.getHeight(null) && !needRedraw) {
    335 
    336                         g.drawImage(bufferImage, 0, 0, null);
    337                         return;
    338                 }
    339 
    340                 needRedraw = false;
    341                 lastTopLeft = topLeft;
    342                 lastBotRight = botRight;
    343                 bufferImage = mv.createImage(mv.getWidth(), mv.getHeight());
    344                 g = bufferImage.getGraphics();
    345 
    346                 z12x0 = lonToTileX(topLeft.lon());
    347                 z12x1 = lonToTileX(botRight.lon());
    348                 z12y0 = latToTileY(topLeft.lat());
    349                 z12y1 = latToTileY(botRight.lat());
    350 
    351                 if (z12x0 > z12x1) {
    352                         int tmp = z12x0;
    353                         z12x0 = z12x1;
    354                         z12x1 = tmp;
    355                 }
    356                 if (z12y0 > z12y1) {
    357                         int tmp = z12y0;
    358                         z12y0 = z12y1;
    359                         z12y1 = tmp;
    360                 }
    361 
    362                 if (z12x1 - z12x0 > 18)
    363                         return;
    364                 if (z12y1 - z12y0 > 18)
    365                         return;
    366 
    367                 for (int x = z12x0 - 1; x <= z12x1 + 1; x++) {
    368                         double lon = tileXToLon(x);
    369                         for (int y = z12y0 - 1; y <= z12y1 + 1; y++) {
    370                                 LatLon tmpLL = new LatLon(tileYToLat(y), lon);
    371                                 pixelpos[x - z12x0 + 1][y - z12y0 + 1] = mv.getPoint(Main.proj
    372                                                 .latlon2eastNorth(tmpLL));
    373                         }
    374                 }
    375 
    376                 int fontHeight = g.getFontMetrics().getHeight();
    377 
    378                 g.setColor(Color.DARK_GRAY);
    379 
    380                 float fadeBackground = SlippyMapPreferences.getFadeBackground();
    381 
    382                 Double imageScale = null;
    383                 int count = 0;
    384                 for (int x = z12x0 - 1; x <= z12x1; x++) {
    385                         for (int y = z12y0 - 1; y <= z12y1; y++) {
    386                                 SlippyMapKey key = new SlippyMapKey(currentZoomLevel, x, y);
    387                                 SlippyMapTile tile;
    388                                 tile = tileStorage.get(key);
    389                                 if (!key.valid) {
    390                                                 System.out.println("loadAllTiles() made invalid key");
    391                                                 continue;
    392                                 }
    393                                 if (tile == null) {
    394                                         tile = new SlippyMapTile(x, y, currentZoomLevel);
    395                                         tileStorage.put(key, tile);
    396                                         if (SlippyMapPreferences.getAutoloadTiles()) {
    397                                                 // TODO probably do on background
    398                                                 loadSingleTile(tile);
    399                                         }
    400                                         checkTileStorage();
    401                                 }
    402                                 Image img = tile.getImage();
    403 
    404                                 if (img != null) {
    405                                         Point p = pixelpos[x - z12x0 + 1][y - z12y0 + 1];
    406                                         Point p2 = pixelpos[x - z12x0 + 2][y - z12y0 + 2];
    407                                         g.drawImage(img, p.x, p.y, p2.x - p.x, p2.y - p.y, this);
    408                                         if (imageScale == null)
    409                                                 imageScale = new Double(getImageScaling(img, p, p2));
    410                                         count++;
    411                                         if (fadeBackground != 0f) {
    412                                                 // dimm by painting opaque rect...
    413                                                 g.setColor(new Color(1f, 1f, 1f, fadeBackground));
    414                                                 g.fillRect(p.x, p.y, p2.x - p.x, p2.y - p.y);
    415                                         }// end of if dim != 0
    416                                 }// end of if img != null
    417                         }// end of for
    418                 }// end of for
    419                 g.setColor(Color.red);
    420                 for (int x = z12x0 - 1; x <= z12x1; x++) {
    421                         Point p = pixelpos[x - z12x0 + 1][0];
    422 
    423                         if(SlippyMapPreferences.getDrawDebug()) {
    424                                 if (x % 32 == 0) {
    425                                         // level 7 tile boundary
    426                                         g.fillRect(p.x - 1, 0, 3, mv.getHeight());
    427                                 } else {
    428                                         g.drawLine(p.x, 0, p.x, mv.getHeight());
    429                                 }
    430                         }//end of if draw debug
    431 
    432                         for (int y = z12y0 - 1; y <= z12y1; y++) {
    433                                 SlippyMapKey key = new SlippyMapKey(currentZoomLevel, x, y);
    434                                 int texty = p.y + 2 + fontHeight;
    435                                 SlippyMapTile tile = tileStorage.get(key);
    436                                 if (tile == null) {
    437                                         continue;
    438                                 }
    439                                 if (!key.valid) {
    440                                                 System.out.println("paint-0() made invalid key");
    441                                                 continue;
    442                                 }
    443                                 if (tile.getImage() == null) {
    444                                                 loadSingleTile(tile);
    445                                 }
    446                                 p = pixelpos[x - z12x0 + 1][y - z12y0 + 2];
    447                                
    448                                 if(SlippyMapPreferences.getDrawDebug()) {
    449                                         g.drawString("x=" + x + " y=" + y + " z=" + currentZoomLevel
    450                                                         + "", p.x + 2, texty);
    451                                         texty += 1 + fontHeight;
    452                                         if ((x % 32 == 0) && (y % 32 == 0)) {
    453                                                 g.drawString("x=" + x / 32 + " y=" + y / 32 + " z=7",
    454                                                                 p.x + 2, texty);
    455                                                 texty += 1 + fontHeight;
    456                                         }
    457                                 }//end of if draw debug
    458 
    459                                 String md = tile.getMetadata();
    460                                 if (md != null) {
    461                                         g.drawString(md, p.x + 2, texty);
    462                                         texty += 1 + fontHeight;
    463                                 }
    464 
    465                                 if (tile.getImage() == null) {
    466                                         g.drawString(tr("image not loaded"), p.x + 2, texty);
    467                                         texty += 1 + fontHeight;
    468                                 }
    469 
    470                                 if(SlippyMapPreferences.getDrawDebug()) {
    471                                         if (x == z12x0 - 1) {
    472                                                 if (y % 32 == 31) {
    473                                                         g.fillRect(0, p.y - 1, mv.getWidth(), 3);
    474                                                 } else {
    475                                                         g.drawLine(0, p.y, mv.getWidth(), p.y);
    476                                                 }
    477                                         }
    478                                 }//end of if draw debug
    479                         } // end of for
    480                 }
    481 
    482                 oldg.drawImage(bufferImage, 0, 0, null);
    483 
    484                 if (imageScale != null) {
    485                         // If each source image pixel is being stretched into > 3
    486                         // drawn pixels, zoom in... getting too pixelated
    487                         if (imageScale > 3) {
    488                                 if (SlippyMapPreferences.getAutozoom()) {
    489                                     Main.debug("autozoom increase: "+z12x1+" " + z12x0 + " " + z12y1 + " " + z12y0
    490                                                                         + topLeft + " " + botRight + " scale: " + imageScale);
    491                                         increaseZoomLevel();
    492                                 }
    493                                 this.paint(oldg, mv);
    494                         }
    495 
    496                         // If each source image pixel is being squished into > 0.32
    497                         // of a drawn pixels, zoom out.
    498                         if (imageScale < 0.32) {
    499                                 if (SlippyMapPreferences.getAutozoom()) {
    500                                     Main.debug("autozoom decrease: "+z12x1+" " + z12x0 + " " + z12y1 + " " + z12y0
    501                                                                         + topLeft + " " + botRight + " scale: " + imageScale);
    502                                         decreaseZoomLevel();
    503                                 }
    504                                 this.paint(oldg, mv);
    505                         }
    506                 }       
    507                 g.setColor(Color.black);
    508                 g.drawString("currentZoomLevel=" + currentZoomLevel, 120, 120);
    509         }// end of paint metod
    510 
    511         SlippyMapTile getTileForPixelpos(int px, int py) {
    512                 int tilex = z12x1;
    513                 int tiley = z12y1;
    514                 for (int x = z12x0; x <= z12x1; x++) {
    515 
    516                         if (pixelpos[x - z12x0 + 1][0].x > px) {
    517                                 tilex = x - 1;
    518                                 break;
    519                         }
    520                 }
    521                 if (tilex == -1)
    522                         return null;
    523                 for (int y = z12y0; y <= z12y1; y++) {
    524 
    525                         if (pixelpos[0][y - z12y0 + 1].y > py) {
    526                                 tiley = y - 1;
    527                                 break;
    528                         }
    529                 }
    530                 if (tiley == -1) {
    531                         return null;
    532                 }
    533 
    534                 SlippyMapKey key = new SlippyMapKey(currentZoomLevel, tilex, tiley);
    535                 if (!key.valid) {
    536                         System.err.println("getTileForPixelpos("+px+","+py+") made invalid key");
    537                         return null;
    538                 }
    539                 SlippyMapTile tile = tileStorage.get(key);
    540                 if (tile == null)
    541                         tileStorage.put(key, tile = new SlippyMapTile(tilex, tiley, currentZoomLevel));
    542                 checkTileStorage();
    543                 return tile;
    544         }
    545 
    546         @Override
    547         public Icon getIcon() {
    548                 return ImageProvider.get("slippymap");
    549         }
    550 
    551         @Override
    552         public Object getInfoComponent() {
    553                 return null;
    554         }
    555 
    556         @Override
    557         public Component[] getMenuEntries() {
    558                 return new Component[] {
    559                                 new JMenuItem(new LayerListDialog.ShowHideLayerAction(this)),
    560                                 new JMenuItem(new LayerListDialog.DeleteLayerAction(this)),
    561                                 new JSeparator(),
    562                                 // color,
    563                                 new JMenuItem(new RenameLayerAction(associatedFile, this)),
    564                                 new JSeparator(),
    565                                 new JMenuItem(new LayerListPopup.InfoAction(this)) };
    566         }
    567 
    568         @Override
    569         public String getToolTipText() {
    570                 return null;
    571         }
    572 
    573         @Override
    574         public boolean isMergable(Layer other) {
    575                 return false;
    576         }
    577 
    578         @Override
    579         public void mergeFrom(Layer from) {
    580         }
    581 
    582         @Override
    583         public void visitBoundingBox(BoundingXYVisitor v) {
    584         }
    585 
    586         private int latToTileY(double lat) {
    587                 double l = lat / 180 * Math.PI;
    588                 double pf = Math.log(Math.tan(l) + (1 / Math.cos(l)));
    589                 return (int) (Math.pow(2.0, currentZoomLevel - 1) * (Math.PI - pf) / Math.PI);
    590         }
    591 
    592         private int lonToTileX(double lon) {
    593                 return (int) (Math.pow(2.0, currentZoomLevel - 3) * (lon + 180.0) / 45.0);
    594         }
    595 
    596         private double tileYToLat(int y) {
    597                 return Math.atan(Math.sinh(Math.PI
    598                                 - (Math.PI * y / Math.pow(2.0, currentZoomLevel - 1))))
    599                                 * 180 / Math.PI;
    600         }
    601 
    602         private double tileXToLon(int x) {
    603                 return x * 45.0 / Math.pow(2.0, currentZoomLevel - 3) - 180.0;
    604         }
    605 
    606         public boolean imageUpdate(Image img, int infoflags, int x, int y,
    607                         int width, int height) {
    608                 boolean done = ((infoflags & (ERROR | FRAMEBITS | ALLBITS)) != 0);
    609                 if ((infoflags & ERROR) != 0) {
    610                                 String url = "unknown";
    611                                 for (SlippyMapTile tile : tileStorage.values()) {
    612                                                 if (tile.getImage() != img)
    613                                                                 continue;
    614                                                 url = tile.getImageURL().toString();
    615                                 }
    616                                 System.err.println("imageUpdate(" + img + ") error " + url +")");
    617                 }
    618                 if ((infoflags & SOMEBITS) != 0) {
    619                                 //if (y%100 == 0)
    620                                 //      System.out.println("imageUpdate("+img+") SOMEBITS ("+x+","+y+")");
    621                 }
    622                 // Repaint immediately if we are done, otherwise batch up
    623                 // repaint requests every 100 milliseconds
    624                 needRedraw = true;
    625                 Main.map.repaint(done ? 0 : 100);
    626                 return !done;
    627         }
    628 
    629         /*
    630          * (non-Javadoc)
    631          *
    632          * @seeorg.openstreetmap.josm.data.Preferences.PreferenceChangedListener#
    633          * preferenceChanged(java.lang.String, java.lang.String)
    634          */
    635         public void preferenceChanged(String key, String newValue) {
    636                 if (key.startsWith(SlippyMapPreferences.PREFERENCE_PREFIX)) {
    637                         // System.err.println(this + ".preferenceChanged('" + key + "', '"
    638                         // + newValue + "') called");
    639                         // when fade background changed, no need to clear tile storage
    640                         // TODO move this code to SlippyMapPreferences class.
    641                         if (!key.equals(SlippyMapPreferences.PREFERENCE_FADE_BACKGROUND)) {
    642                                 clearTileStorage();
    643                         }
    644                 }
    645         }
    646 
    647         @Override
    648         public void destroy() {
    649                 Main.pref.listener.remove(SlippyMapLayer.this);
    650         }
     50    public int currentZoomLevel = SlippyMapPreferences.getMinZoomLvl();
     51    private HashMap<SlippyMapKey, SlippyMapTile> tileStorage = null;
     52
     53    Point[][] pixelpos = new Point[21][21];
     54    LatLon lastTopLeft;
     55    LatLon lastBotRight;
     56    int z12x0, z12x1, z12y0, z12y1;
     57    private Image bufferImage;
     58    private SlippyMapTile clickedTile;
     59    private boolean needRedraw;
     60    private JPopupMenu tileOptionMenu;
     61
     62    @SuppressWarnings("serial")
     63    public SlippyMapLayer() {
     64        super(tr("Slippy Map"));
     65        background = true;
     66
     67        clearTileStorage();
     68
     69        tileOptionMenu = new JPopupMenu();
     70        tileOptionMenu.add(new JMenuItem(new AbstractAction(tr("Load Tile")) {
     71            public void actionPerformed(ActionEvent ae) {
     72                if (clickedTile != null) {
     73                    loadSingleTile(clickedTile);
     74                    needRedraw = true;
     75                    Main.map.repaint();
     76                }
     77            }
     78        }));
     79
     80        tileOptionMenu.add(new JMenuItem(new AbstractAction(
     81                tr("Show Tile Status")) {
     82            public void actionPerformed(ActionEvent ae) {
     83                if (clickedTile != null) {
     84                    clickedTile.loadMetadata();
     85                    needRedraw = true;
     86                    Main.map.repaint();
     87                }
     88            }
     89        }));
     90
     91        tileOptionMenu.add(new JMenuItem(new AbstractAction(
     92                tr("Request Update")) {
     93            public void actionPerformed(ActionEvent ae) {
     94                if (clickedTile != null) {
     95                    clickedTile.requestUpdate();
     96                    needRedraw = true;
     97                    Main.map.repaint();
     98                }
     99            }
     100        }));
     101
     102        tileOptionMenu.add(new JMenuItem(new AbstractAction(
     103                tr("Load All Tiles")) {
     104            public void actionPerformed(ActionEvent ae) {
     105                loadAllTiles();
     106                needRedraw = true;
     107                Main.map.repaint();
     108            }
     109        }));
     110
     111        // increase and decrease commands
     112        tileOptionMenu.add(new JMenuItem(
     113                new AbstractAction(tr("Increase zoom")) {
     114                    public void actionPerformed(ActionEvent ae) {
     115                        increaseZoomLevel();
     116                        needRedraw = true;
     117                        Main.map.repaint();
     118                    }
     119                }));
     120
     121        tileOptionMenu.add(new JMenuItem(
     122                new AbstractAction(tr("Decrease zoom")) {
     123                    public void actionPerformed(ActionEvent ae) {
     124                        decreaseZoomLevel();
     125                        Main.map.repaint();
     126                    }
     127                }));
     128        // end of adding menu commands
     129
     130        SwingUtilities.invokeLater(new Runnable() {
     131            public void run() {
     132                Main.map.mapView.addMouseListener(new MouseAdapter() {
     133                    @Override
     134                    public void mouseClicked(MouseEvent e) {
     135                        if (e.getButton() != MouseEvent.BUTTON3)
     136                            return;
     137                        clickedTile = getTileForPixelpos(e.getX(), e.getY());
     138                        tileOptionMenu.show(e.getComponent(), e.getX(), e
     139                                .getY());
     140                    }
     141                });
     142
     143                listeners.add(new LayerChangeListener() {
     144                    public void activeLayerChange(Layer oldLayer, Layer newLayer) {
     145                    }
     146
     147                    public void layerAdded(Layer newLayer) {
     148                    }
     149
     150                    public void layerRemoved(Layer oldLayer) {
     151                        Main.pref.listener.remove(SlippyMapLayer.this);
     152                    }
     153                });
     154            }
     155        });
     156
     157        Main.pref.listener.add(this);
     158    }
     159
     160    /**
     161     * Zoom in, go closer to map.
     162     */
     163    public void increaseZoomLevel() {
     164        if (currentZoomLevel < SlippyMapPreferences.getMaxZoomLvl()) {
     165            currentZoomLevel++;
     166            Main.debug("increasing zoom level to: " + currentZoomLevel);
     167            needRedraw = true;
     168        } else {
     169            System.err.println("current zoom lvl ("+currentZoomLevel+") couldnt be increased. "+
     170                             "MaxZoomLvl ("+SlippyMapPreferences.getMaxZoomLvl()+") reached.");
     171        }
     172    }
     173
     174    /**
     175     * Zoom out from map.
     176     */
     177    public void decreaseZoomLevel() {
     178        if (currentZoomLevel > SlippyMapPreferences.getMinZoomLvl()) {
     179            Main.debug("decreasing zoom level to: " + currentZoomLevel);
     180            currentZoomLevel--;
     181            needRedraw = true;
     182        } else {
     183            System.err.println("current zoom lvl couldnt be decreased. MinZoomLvl reached.");
     184        }
     185    }
     186
     187    public void clearTileStorage() {
     188        // when max zoom lvl is begin saved, this method is called and probably
     189        // the setting isnt saved yet.
     190        int maxZoom = 30; // SlippyMapPreferences.getMaxZoomLvl();
     191        tileStorage = new HashMap<SlippyMapKey, SlippyMapTile>();
     192
     193        checkTileStorage();
     194    }
     195
     196    class TileTimeComp implements Comparator<SlippyMapTile> {
     197            public int compare(SlippyMapTile s1, SlippyMapTile s2) {
     198                    long t1 = s1.access_time();
     199                    long t2 = s2.access_time();
     200                    if (s1 == s2)
     201                            return 0;
     202                    if (t1 == t2) {
     203                            t1 = s1.hashCode();
     204                            t2 = s2.hashCode();
     205                    }
     206                    if (t1 < t2)
     207                            return -1;
     208                    return 1;
     209            }
     210    }
     211
     212    long lastCheck = 0;
     213    /**
     214     * <p>
     215     * Check if tiles.size() is not more than max_nr_tiles. If yes, oldest tiles by timestamp
     216     * are fired out from cache.
     217     * </p>
     218     */
     219    public void checkTileStorage() {
     220        int maxZoom = 30; // SlippyMapPreferences.getMaxZoomLvl();
     221        long now = System.currentTimeMillis();
     222        if (now - lastCheck < 1000)
     223                return;
     224        lastCheck = now;
     225        TreeSet<SlippyMapTile> tiles = new TreeSet<SlippyMapTile>(new TileTimeComp());
     226        tiles.addAll(tileStorage.values());
     227        int max_nr_tiles = 100;
     228        if (tiles.size() < max_nr_tiles) {
     229            Main.debug("total of " + tiles.size() + " loaded tiles, size OK " + now);
     230            return;
     231        }
     232        int nr_to_drop = tiles.size() - max_nr_tiles;;
     233        Main.debug("total of " + tiles.size() + " tiles, need to flush " + nr_to_drop + " tiles");
     234        for (SlippyMapTile t : tiles) {
     235            if (nr_to_drop <= 0)
     236                    break;
     237            t.dropImage();
     238            nr_to_drop--;
     239        }
     240    }
     241
     242    void loadSingleTile(SlippyMapTile tile)
     243    {
     244        tile.loadImage();
     245        this.checkTileStorage();
     246    }
     247
     248    void loadAllTiles() {
     249        MapView mv = Main.map.mapView;
     250        LatLon topLeft = mv.getLatLon(0, 0);
     251        LatLon botRight = mv.getLatLon(mv.getWidth(), mv.getHeight());
     252        z12x0 = lonToTileX(topLeft.lon());
     253        z12x1 = lonToTileX(botRight.lon());
     254        z12y0 = latToTileY(topLeft.lat());
     255        z12y1 = latToTileY(botRight.lat());
     256        if (z12x0 > z12x1) {
     257            int tmp = z12x0;
     258            z12x0 = z12x1;
     259            z12x1 = tmp;
     260        }
     261        if (z12y0 > z12y1) {
     262            int tmp = z12y0;
     263            z12y0 = z12y1;
     264            z12y1 = tmp;
     265        }
     266        // if there is more than 18 tiles on screen in any direction, do not
     267        // load all tiles!
     268        if (z12x1 - z12x0 > 18) {
     269            System.out
     270                    .println("Not downloading all tiles because there is more than 18 tiles on X axis!");
     271            return;
     272        }
     273        if (z12y1 - z12y0 > 18) {
     274            System.out
     275                    .println("Not downloading all tiles because there is more than 18 tiles on Y axis!");
     276            return;
     277        }
     278
     279        for (int x = z12x0 - 1; x <= z12x1; x++) {
     280            for (int y = z12y0 - 1; y <= z12y1; y++) {
     281                SlippyMapKey key = new SlippyMapKey(currentZoomLevel, x, y);
     282                SlippyMapTile tile = tileStorage.get(key);
     283                if (!key.valid) {
     284                        System.out.println("paint-1() made invalid key");
     285                        continue;
     286                }
     287                if (tile == null)
     288                    tileStorage.put(key,
     289                            tile = new SlippyMapTile(x, y, currentZoomLevel));
     290                if (tile.getImage() == null) {
     291                    this.loadSingleTile(tile);
     292                }
     293            }
     294        }
     295    }
     296
     297    /*
     298     * Attempt to approximate how much the image is
     299     * being scaled.  For instance, a 100x100 image
     300     * being scaled to 50x50 would return 0.25.
     301     */
     302    double getImageScaling(Image img, Point p0, Point p1)
     303    {
     304        int realWidth = img.getWidth(this);
     305        int realHeight = img.getHeight(this);
     306        if (realWidth == -1 || realHeight == -1)
     307                return 1.0;
     308        int drawWidth = p1.x - p0.x;
     309        int drawHeight = p1.x - p0.x;
     310
     311        double drawArea = drawWidth * drawHeight;
     312        double realArea = realWidth * realHeight;
     313
     314        return drawArea / realArea;
     315    }
     316
     317    /**
     318     */
     319    @Override
     320    public void paint(Graphics g, MapView mv) {
     321        long start = System.currentTimeMillis();
     322        LatLon topLeft = mv.getLatLon(0, 0);
     323        LatLon botRight = mv.getLatLon(mv.getWidth(), mv.getHeight());
     324        Graphics oldg = g;
     325
     326        if (botRight.lon() == 0.0 || botRight.lat() == 0) {
     327                // probably still initializing
     328                return;
     329        }
     330        if (lastTopLeft != null && lastBotRight != null
     331                && topLeft.equalsEpsilon(lastTopLeft)
     332                && botRight.equalsEpsilon(lastBotRight) && bufferImage != null
     333                && mv.getWidth() == bufferImage.getWidth(null)
     334                && mv.getHeight() == bufferImage.getHeight(null) && !needRedraw) {
     335
     336            g.drawImage(bufferImage, 0, 0, null);
     337            return;
     338        }
     339
     340        needRedraw = false;
     341        lastTopLeft = topLeft;
     342        lastBotRight = botRight;
     343        bufferImage = mv.createImage(mv.getWidth(), mv.getHeight());
     344        g = bufferImage.getGraphics();
     345
     346        z12x0 = lonToTileX(topLeft.lon());
     347        z12x1 = lonToTileX(botRight.lon());
     348        z12y0 = latToTileY(topLeft.lat());
     349        z12y1 = latToTileY(botRight.lat());
     350
     351        if (z12x0 > z12x1) {
     352            int tmp = z12x0;
     353            z12x0 = z12x1;
     354            z12x1 = tmp;
     355        }
     356        if (z12y0 > z12y1) {
     357            int tmp = z12y0;
     358            z12y0 = z12y1;
     359            z12y1 = tmp;
     360        }
     361
     362        if (z12x1 - z12x0 > 18)
     363            return;
     364        if (z12y1 - z12y0 > 18)
     365            return;
     366
     367        for (int x = z12x0 - 1; x <= z12x1 + 1; x++) {
     368            double lon = tileXToLon(x);
     369            for (int y = z12y0 - 1; y <= z12y1 + 1; y++) {
     370                LatLon tmpLL = new LatLon(tileYToLat(y), lon);
     371                pixelpos[x - z12x0 + 1][y - z12y0 + 1] = mv.getPoint(Main.proj
     372                        .latlon2eastNorth(tmpLL));
     373            }
     374        }
     375
     376        int fontHeight = g.getFontMetrics().getHeight();
     377
     378        g.setColor(Color.DARK_GRAY);
     379
     380        float fadeBackground = SlippyMapPreferences.getFadeBackground();
     381
     382        Double imageScale = null;
     383        int count = 0;
     384        for (int x = z12x0 - 1; x <= z12x1; x++) {
     385            for (int y = z12y0 - 1; y <= z12y1; y++) {
     386                SlippyMapKey key = new SlippyMapKey(currentZoomLevel, x, y);
     387                SlippyMapTile tile;
     388                tile = tileStorage.get(key);
     389                if (!key.valid) {
     390                        System.out.println("loadAllTiles() made invalid key");
     391                        continue;
     392                }
     393                if (tile == null) {
     394                    tile = new SlippyMapTile(x, y, currentZoomLevel);
     395                    tileStorage.put(key, tile);
     396                    if (SlippyMapPreferences.getAutoloadTiles()) {
     397                        // TODO probably do on background
     398                        loadSingleTile(tile);
     399                    }
     400                    checkTileStorage();
     401                }
     402                Image img = tile.getImage();
     403
     404                if (img != null) {
     405                    Point p = pixelpos[x - z12x0 + 1][y - z12y0 + 1];
     406                    Point p2 = pixelpos[x - z12x0 + 2][y - z12y0 + 2];
     407                    g.drawImage(img, p.x, p.y, p2.x - p.x, p2.y - p.y, this);
     408                    if (imageScale == null)
     409                        imageScale = new Double(getImageScaling(img, p, p2));
     410                    count++;
     411                    if (fadeBackground != 0f) {
     412                        // dimm by painting opaque rect...
     413                        g.setColor(new Color(1f, 1f, 1f, fadeBackground));
     414                        g.fillRect(p.x, p.y, p2.x - p.x, p2.y - p.y);
     415                    }// end of if dim != 0
     416                }// end of if img != null
     417            }// end of for
     418        }// end of for
     419        g.setColor(Color.red);
     420        for (int x = z12x0 - 1; x <= z12x1; x++) {
     421            Point p = pixelpos[x - z12x0 + 1][0];
     422
     423            if(SlippyMapPreferences.getDrawDebug()) {
     424                if (x % 32 == 0) {
     425                    // level 7 tile boundary
     426                    g.fillRect(p.x - 1, 0, 3, mv.getHeight());
     427                } else {
     428                    g.drawLine(p.x, 0, p.x, mv.getHeight());
     429                }
     430            }//end of if draw debug
     431
     432            for (int y = z12y0 - 1; y <= z12y1; y++) {
     433                SlippyMapKey key = new SlippyMapKey(currentZoomLevel, x, y);
     434                int texty = p.y + 2 + fontHeight;
     435                SlippyMapTile tile = tileStorage.get(key);
     436                if (tile == null) {
     437                    continue;
     438                }
     439                if (!key.valid) {
     440                        System.out.println("paint-0() made invalid key");
     441                        continue;
     442                }
     443                if (tile.getImage() == null) {
     444                        loadSingleTile(tile);
     445                }
     446                p = pixelpos[x - z12x0 + 1][y - z12y0 + 2];
     447
     448                if(SlippyMapPreferences.getDrawDebug()) {
     449                    g.drawString("x=" + x + " y=" + y + " z=" + currentZoomLevel
     450                            + "", p.x + 2, texty);
     451                    texty += 1 + fontHeight;
     452                    if ((x % 32 == 0) && (y % 32 == 0)) {
     453                        g.drawString("x=" + x / 32 + " y=" + y / 32 + " z=7",
     454                                p.x + 2, texty);
     455                        texty += 1 + fontHeight;
     456                    }
     457                }//end of if draw debug
     458
     459                String md = tile.getMetadata();
     460                if (md != null) {
     461                    g.drawString(md, p.x + 2, texty);
     462                    texty += 1 + fontHeight;
     463                }
     464
     465                if (tile.getImage() == null) {
     466                    g.drawString(tr("image not loaded"), p.x + 2, texty);
     467                    texty += 1 + fontHeight;
     468                }
     469
     470                if(SlippyMapPreferences.getDrawDebug()) {
     471                    if (x == z12x0 - 1) {
     472                        if (y % 32 == 31) {
     473                            g.fillRect(0, p.y - 1, mv.getWidth(), 3);
     474                        } else {
     475                            g.drawLine(0, p.y, mv.getWidth(), p.y);
     476                        }
     477                    }
     478                }//end of if draw debug
     479            } // end of for
     480        }
     481
     482        oldg.drawImage(bufferImage, 0, 0, null);
     483
     484        if (imageScale != null) {
     485            // If each source image pixel is being stretched into > 3
     486            // drawn pixels, zoom in... getting too pixelated
     487            if (imageScale > 3) {
     488                if (SlippyMapPreferences.getAutozoom()) {
     489                    Main.debug("autozoom increase: "+z12x1+" " + z12x0 + " " + z12y1 + " " + z12y0
     490                                    + topLeft + " " + botRight + " scale: " + imageScale);
     491                    increaseZoomLevel();
     492                }
     493                this.paint(oldg, mv);
     494            }
     495
     496            // If each source image pixel is being squished into > 0.32
     497            // of a drawn pixels, zoom out.
     498            if (imageScale < 0.32) {
     499                if (SlippyMapPreferences.getAutozoom()) {
     500                    Main.debug("autozoom decrease: "+z12x1+" " + z12x0 + " " + z12y1 + " " + z12y0
     501                                    + topLeft + " " + botRight + " scale: " + imageScale);
     502                    decreaseZoomLevel();
     503                }
     504                this.paint(oldg, mv);
     505            }
     506        }
     507        g.setColor(Color.black);
     508        g.drawString("currentZoomLevel=" + currentZoomLevel, 120, 120);
     509    }// end of paint metod
     510
     511    SlippyMapTile getTileForPixelpos(int px, int py) {
     512        int tilex = z12x1;
     513        int tiley = z12y1;
     514        for (int x = z12x0; x <= z12x1; x++) {
     515
     516            if (pixelpos[x - z12x0 + 1][0].x > px) {
     517                tilex = x - 1;
     518                break;
     519            }
     520        }
     521        if (tilex == -1)
     522            return null;
     523        for (int y = z12y0; y <= z12y1; y++) {
     524
     525            if (pixelpos[0][y - z12y0 + 1].y > py) {
     526                tiley = y - 1;
     527                break;
     528            }
     529        }
     530        if (tiley == -1) {
     531            return null;
     532        }
     533
     534        SlippyMapKey key = new SlippyMapKey(currentZoomLevel, tilex, tiley);
     535        if (!key.valid) {
     536            System.err.println("getTileForPixelpos("+px+","+py+") made invalid key");
     537            return null;
     538        }
     539        SlippyMapTile tile = tileStorage.get(key);
     540        if (tile == null)
     541            tileStorage.put(key, tile = new SlippyMapTile(tilex, tiley, currentZoomLevel));
     542        checkTileStorage();
     543        return tile;
     544    }
     545
     546    @Override
     547    public Icon getIcon() {
     548        return ImageProvider.get("slippymap");
     549    }
     550
     551    @Override
     552    public Object getInfoComponent() {
     553        return null;
     554    }
     555
     556    @Override
     557    public Component[] getMenuEntries() {
     558        return new Component[] {
     559                new JMenuItem(new LayerListDialog.ShowHideLayerAction(this)),
     560                new JMenuItem(new LayerListDialog.DeleteLayerAction(this)),
     561                new JSeparator(),
     562                // color,
     563                new JMenuItem(new RenameLayerAction(getAssociatedFile(), this)),
     564                new JSeparator(),
     565                new JMenuItem(new LayerListPopup.InfoAction(this)) };
     566    }
     567
     568    @Override
     569    public String getToolTipText() {
     570        return null;
     571    }
     572
     573    @Override
     574    public boolean isMergable(Layer other) {
     575        return false;
     576    }
     577
     578    @Override
     579    public void mergeFrom(Layer from) {
     580    }
     581
     582    @Override
     583    public void visitBoundingBox(BoundingXYVisitor v) {
     584    }
     585
     586    private int latToTileY(double lat) {
     587        double l = lat / 180 * Math.PI;
     588        double pf = Math.log(Math.tan(l) + (1 / Math.cos(l)));
     589        return (int) (Math.pow(2.0, currentZoomLevel - 1) * (Math.PI - pf) / Math.PI);
     590    }
     591
     592    private int lonToTileX(double lon) {
     593        return (int) (Math.pow(2.0, currentZoomLevel - 3) * (lon + 180.0) / 45.0);
     594    }
     595
     596    private double tileYToLat(int y) {
     597        return Math.atan(Math.sinh(Math.PI
     598                - (Math.PI * y / Math.pow(2.0, currentZoomLevel - 1))))
     599                * 180 / Math.PI;
     600    }
     601
     602    private double tileXToLon(int x) {
     603        return x * 45.0 / Math.pow(2.0, currentZoomLevel - 3) - 180.0;
     604    }
     605
     606    public boolean imageUpdate(Image img, int infoflags, int x, int y,
     607            int width, int height) {
     608        boolean done = ((infoflags & (ERROR | FRAMEBITS | ALLBITS)) != 0);
     609        if ((infoflags & ERROR) != 0) {
     610                String url = "unknown";
     611                for (SlippyMapTile tile : tileStorage.values()) {
     612                        if (tile.getImage() != img)
     613                                continue;
     614                        url = tile.getImageURL().toString();
     615                }
     616                System.err.println("imageUpdate(" + img + ") error " + url +")");
     617        }
     618        if ((infoflags & SOMEBITS) != 0) {
     619                //if (y%100 == 0)
     620                //    System.out.println("imageUpdate("+img+") SOMEBITS ("+x+","+y+")");
     621        }
     622        // Repaint immediately if we are done, otherwise batch up
     623        // repaint requests every 100 milliseconds
     624        needRedraw = true;
     625        Main.map.repaint(done ? 0 : 100);
     626        return !done;
     627    }
     628
     629    /*
     630     * (non-Javadoc)
     631     *
     632     * @seeorg.openstreetmap.josm.data.Preferences.PreferenceChangedListener#
     633     * preferenceChanged(java.lang.String, java.lang.String)
     634     */
     635    public void preferenceChanged(String key, String newValue) {
     636        if (key.startsWith(SlippyMapPreferences.PREFERENCE_PREFIX)) {
     637            // System.err.println(this + ".preferenceChanged('" + key + "', '"
     638            // + newValue + "') called");
     639            // when fade background changed, no need to clear tile storage
     640            // TODO move this code to SlippyMapPreferences class.
     641            if (!key.equals(SlippyMapPreferences.PREFERENCE_FADE_BACKGROUND)) {
     642                clearTileStorage();
     643            }
     644        }
     645    }
     646
     647    @Override
     648    public void destroy() {
     649        Main.pref.listener.remove(SlippyMapLayer.this);
     650    }
    651651}
Note: See TracChangeset for help on using the changeset viewer.