Changeset 599

Show
Ignore:
Timestamp:
07/16/08 12:57:21 (2 months ago)
Author:
tom
Message:

BIG CHANGES in tom-tweenlite branch, to allow zooming around arbitrary points... MapEvent?.panDelta may be broken while zooming, but it's worth it I promise

Files:

Legend:

Unmodified
Added
Removed
Modified
Copied
Moved
  • branches/tom-tweenlite/lib/com/modestmaps/core/Coordinate.as

    r292 r599  
    1111            public var zoom:Number; 
    1212             
    13             public static const MAX_ZOOM:Number = 20; 
    14          
    1513            public function Coordinate(row:Number, column:Number, zoom:Number) 
    1614            { 
  • branches/tom-tweenlite/lib/com/modestmaps/core/MarkerClip.as

    r595 r599  
    262262            protected function onMapPanned(event:MapEvent):void 
    263263            { 
    264                 if (starting) { 
     264                if (map.grid.panning && map.grid.zooming) { 
     265                        // only reprojecting can save you now 
     266                        dirty = true; 
     267                        return; 
     268                } 
     269                else if (starting) { 
    265270                    x = starting.x + event.panDelta.x; 
    266271                    y = starting.y + event.panDelta.y; 
    267272                } 
    268273                else { 
     274                        // TODO: this shouldn't really ever happen, and the following might not work: 
    269275                    x = event.panDelta.x; 
    270276                    y = event.panDelta.y;                    
     
    295301                     */  
    296302                    dirty = true; 
    297                 // updateClips(); 
     303                updateClips(); 
    298304            } 
    299305             
  • branches/tom-tweenlite/lib/com/modestmaps/core/TileGrid.as

    r598 r599  
    2727                // we need a reference to our map for createTile() only,  
    2828                // slightly messy but better than a whole TileFactory hierarchy! 
     29                // TODO: can we just use a function? how would TweenMap override it? 
    2930                private var map:Map; 
    3031 
     
    3940                protected var minTx:Number, maxTx:Number, minTy:Number, maxTy:Number; 
    4041 
    41                 // pan and zoom 
    42                 protected var _panX:Number; 
    43                 protected var _panY:Number; 
    44                 protected var _scale:Number; 
    45                  
     42                // pan and zoom etc are stored in here 
     43                // NB: this matrix is never applied to a DisplayObject's transform 
     44                //     because it would require scaling tile positions to compensate 
     45                //     instead, we adapt its values such that the current zoom level 
     46                //     is approximately scale 1, and positions make sense in screen pixels 
    4647                protected var worldMatrix:Matrix; 
     48                 
     49                // this turns screen points into coordinates 
    4750                protected var _invertedMatrix:Matrix; // use lazy getter for this 
    4851                 
    49                 // these also have lazy getters 
     52                // the corners of the screen, in map coordinates 
     53                // (these also have lazy getters) 
    5054                protected var _topLeftCoordinate:Coordinate; 
    5155                protected var _bottomRightCoordinate:Coordinate; 
     
    114118                protected static const DEFAULT_MAX_TILES_TO_KEEP:int = 256; 
    115119                protected static const DEFAULT_TILE_BUFFER:int = 0; 
    116                 protected static const DEFAULT_ENFORCE_BOUNDS:Boolean = false; 
     120                protected static const DEFAULT_ENFORCE_BOUNDS:Boolean = true; 
    117121 
    118122                /** if we don't have a tile at currentZoom, onRender will look for tiles up to 5 levels out. 
     
    155159                        this.provider = provider; 
    156160 
    157                         _panX = -provider.tileWidth/2; 
    158                         _panY = -provider.tileHeight/2; 
    159                         _scale = 1; 
     161                        doubleClickEnabled = true; 
    160162 
    161163                        // from provider: 
     
    180182 
    181183                        well = new Sprite(); 
     184                        well.doubleClickEnabled = true; 
     185                        well.mouseEnabled = true; 
     186                        well.mouseChildren = false; 
    182187                        addChild(well); 
    183188 
    184                       worldMatrix = new Matrix(); 
     189/*                    worldMatrix = new Matrix(); 
    185190                        worldMatrix.translate(_panX,_panX); 
    186191                        worldMatrix.scale(_scale,_scale); 
    187                         worldMatrix.translate(mapWidth/2, mapHeight/2); 
     192                        worldMatrix.translate(mapWidth/2, mapHeight/2); */ 
     193 
     194                        worldMatrix = new Matrix(); 
     195/*                      worldMatrix.translate(-provider.tileWidth/2, -provider.tileHeight/2); 
     196                        worldMatrix.scale(1, 1); 
     197                        worldMatrix.translate(mapWidth/2, mapHeight/2); */ 
    188198                         
    189199                        addEventListener(Event.ADDED_TO_STAGE, onAddedToStage);                  
     
    229239                                } 
    230240                                 
    231                                 debugField.text = "tx: " + _panX.toFixed(3) + " ty: " + _panY.toFixed(3) + " sc: " + _scale.toFixed(4) 
     241                                debugField.text = "tx: " + tx.toFixed(3) + " ty: " + tx.toFixed(3) + " sc: " + scale.toFixed(4) 
    232242                                                + "\nfps: " + fps.toFixed(0) 
    233243                                                + "\ncurrent child count: " + well.numChildren 
     
    253263                        if (!dirty || !stage) { 
    254264                                return; 
     265                        } 
     266 
     267                        enforceBounds(); 
     268 
     269                        if (zooming && panning) { 
     270                                map.onExtentChanged(map.getExtent()); 
     271                        }  
     272                        else if (panning) { 
     273                                if (startPan) { 
     274                                        dispatchEvent(new MapEvent(MapEvent.PANNED, new Point(worldMatrix.tx-startPan.x, worldMatrix.ty-startPan.y))); 
     275                                }                        
     276                                else { 
     277                                        trace("panning but no startPan"); 
     278                                }                
     279                        }        
     280                        else if (zooming) { 
     281                        var zoomEvent:MapEvent = new MapEvent(MapEvent.ZOOMED_BY); 
     282                zoomEvent.zoomDelta = zoomLevel-startZoom; 
     283                zoomEvent.zoomLevel = zoomLevel; 
     284                dispatchEvent(zoomEvent); 
    255285                        } 
    256286                         
     
    476506                                tile.x = positionCol*provider.tileWidth*positionScaleCompensation; 
    477507                                tile.y = positionRow*provider.tileHeight*positionScaleCompensation; 
    478                                 tile.scaleX = tile.scaleY = Math.pow(2, zoomLevel-tile.zoom);                            
     508                                 
     509                                var tileScale:Number = Math.pow(2, zoomLevel-tile.zoom); 
     510                                tileScale = Math.ceil(tileScale * provider.tileWidth) / provider.tileWidth; // round up to the nearest pixel 
     511                                tile.scaleX = tile.scaleY = tileScale;                           
    479512                        } 
    480513                         
     
    482515                        // let's make sure we keep them around: 
    483516                        var maxRecentlySeen:int = Math.max(visibleTiles.length,maxTilesToKeep); 
    484 /*                      trace(); 
    485                         trace('visibleTiles', visibleTiles.length); 
    486                         trace('maxRecentlySeen', maxRecentlySeen); 
    487                         trace('recentlySeen', recentlySeen.length); */ 
    488517                         
    489518                        // prune cache of already seen tiles if it's getting too big: 
     
    691720                }        
    692721                 
    693                 public function mousePressed(event:MouseEvent=null):void 
     722                public function mousePressed(event:MouseEvent):void 
    694723                { 
    695724                        prepareForPanning(true); 
    696                         pmouse = globalToLocal(new Point(event.stageX, event.stageY)); 
     725                        pmouse = new Point(event.stageX, event.stageY); 
    697726                        stage.addEventListener(MouseEvent.MOUSE_MOVE, mouseDragged); 
    698727                        stage.addEventListener(MouseEvent.MOUSE_UP, mouseReleased); 
     
    700729                } 
    701730 
    702                 public function mouseReleased(event:Event=null):void 
     731                public function mouseReleased(event:Event):void 
    703732                { 
    704733                        stage.removeEventListener(MouseEvent.MOUSE_MOVE, mouseDragged); 
     
    713742                public function mouseDragged(event:MouseEvent):void 
    714743                { 
    715                         var mousePoint:Point = globalToLocal(new Point(event.stageX, event.stageY)); 
    716                         _panX += (mousePoint.x - pmouse.x) / _scale
    717                         _panY += (mousePoint.y - pmouse.y) / _scale
     744                        var mousePoint:Point = new Point(event.stageX, event.stageY); 
     745                        tx += mousePoint.x - pmouse.x
     746                        ty += mousePoint.y - pmouse.y
    718747                        pmouse = mousePoint; 
    719                         calculateMatrix(); 
    720                         var origin:Point = startPan || new Point(_panX,_panY); 
    721                         dispatchEvent(new MapEvent(MapEvent.PANNED, new Point(-(origin.x-_panX)*_scale, -(origin.y-_panY)*_scale))); 
    722748                        event.updateAfterEvent(); 
    723749                }        
    724750 
    725                 private function calculateMatrix():void 
    726                 { 
    727                         // first, simply do what was asked: 
    728                         worldMatrix.identity(); 
    729                         worldMatrix.translate(_panX,_panY); 
    730                         worldMatrix.scale(_scale,_scale); 
    731                         worldMatrix.translate(mapWidth/2, mapHeight/2); 
    732                          
    733                         // nullify things that will be recalculated as needed 
    734                         _invertedMatrix = null; 
    735                         _topLeftCoordinate = null; 
    736                         _bottomRightCoordinate = null; 
    737  
    738                         // enforce map bounds? 
    739                         if (enforceBounds()) { 
    740                                 // try again! 
    741                                 worldMatrix.identity(); 
    742                                 worldMatrix.translate(_panX,_panY); 
    743                                 worldMatrix.scale(_scale,_scale); 
    744                                 worldMatrix.translate(mapWidth/2, mapHeight/2);                          
    745  
    746                                 // nullify things that will be recalculated as needed 
    747                                 _invertedMatrix = null; 
    748                                 _topLeftCoordinate = null; 
    749                                 _bottomRightCoordinate = null; 
    750                         } 
    751                          
    752                         // and request a redraw: 
    753                         dirty = true; 
    754                 } 
    755                                                  
    756751                // today is all about lazy evaluation 
    757                 // this gets set to null by calculateMatrix 
     752                // this gets set to null by 'dirty = true' 
    758753                // and only calculated again if you need it 
    759754                protected function get invertedMatrix():Matrix 
     
    762757                                _invertedMatrix = worldMatrix.clone(); 
    763758                                _invertedMatrix.invert(); 
     759                                _invertedMatrix.scale(scale/provider.tileWidth, scale/provider.tileHeight); 
    764760                        } 
    765761                        return _invertedMatrix; 
     
    774770                        if (!_topLeftCoordinate) { 
    775771                                var tl:Point = invertedMatrix.transformPoint(new Point()); 
    776                                 tl.x *= _scale/provider.tileWidth; 
    777                                 tl.y *= _scale/provider.tileHeight; 
    778772                                _topLeftCoordinate = new Coordinate(tl.y, tl.x, zoomLevel);                      
    779773                        } 
     
    789783                        if (!_bottomRightCoordinate) { 
    790784                                var br:Point = invertedMatrix.transformPoint(new Point(mapWidth, mapHeight)); 
    791                                 br.x *= _scale/provider.tileWidth; 
    792                                 br.y *= _scale/provider.tileHeight; 
    793785                                _bottomRightCoordinate = new Coordinate(br.y, br.x, zoomLevel);                  
    794786                        } 
     
    805797                { 
    806798                        var c:Point = invertedMatrix.transformPoint(new Point(mapWidth/2, mapHeight/2)); 
    807                         c.x *= _scale/provider.tileWidth; 
    808                         c.y *= _scale/provider.tileHeight; 
    809799                        return new Coordinate(c.y, c.x, zoomLevel);                      
    810800                } 
     
    842832                         
    843833                        var p:Point = invertedMatrix.transformPoint(point); 
    844                         return new Coordinate(_scale*p.y/provider.tileHeight, _scale*p.x/provider.tileWidth, zoomLevel); 
     834                        return new Coordinate(p.y, p.x, zoomLevel); 
    845835                } 
    846836                 
     
    855845                                } 
    856846                        } 
    857                         startPan = new Point(_panX,_panY); 
     847                        startPan = new Point(tx, ty); 
    858848                        panning = true; 
    859849                        dispatchEvent(new MapEvent(MapEvent.START_PANNING)); 
     
    874864                public function prepareForZooming():void 
    875865                { 
     866                        //trace(getTimer(), 'TileGrid.prepareForZooming'); 
    876867                        if (startZoom >= 0) { 
    877868                                doneZooming(); 
     
    886877                public function doneZooming():void 
    887878                { 
     879                        //trace(getTimer(), 'TileGrid.doneZooming'); 
    888880                        startZoom = -1; 
    889881                        zooming = false; 
     
    895887                public function resetTiles(coord:Coordinate, point:Point):void 
    896888                { 
    897                         // set new scale according to zoom 
    898                         _scale = Math.pow(2, coord.zoom); 
    899  
    900                         // figure out where in the world we are                  
    901                         _panX = -provider.tileWidth*coord.column/_scale; 
    902                         _panY = -provider.tileHeight*coord.row/_scale; 
    903  
    904                         // plus the offset                       
    905                         _panX += point.x/_scale; 
    906                         _panY += point.y/_scale; 
    907  
     889                        //trace("resetTiles", coord, point); 
     890                         
     891                        var sc:Number = Math.pow(2, coord.zoom); 
     892 
     893                        worldMatrix.identity(); 
     894                        //worldMatrix.translate( -mapWidth/2, -mapHeight/2 ); 
     895                        worldMatrix.scale(sc, sc); 
     896                        worldMatrix.translate(mapWidth/2, mapHeight/2 ); 
     897                        worldMatrix.translate(point.x, point.y); 
     898                        worldMatrix.translate(-provider.tileWidth*coord.column, -provider.tileHeight*coord.row); 
     899 
     900                        dirty = true; 
    908901                        // reset the worldMatrix 
    909                         calculateMatrix(); 
    910                 } 
    911                  
    912                 public function get panX():Number 
    913                 { 
    914                         return _panX; 
    915                 } 
    916  
    917                 public function set panX(n:Number):void 
    918                 { 
    919                     if (n != panX) 
    920                     { 
    921                         var origin:Point = startPan || new Point(_panX, _panY); 
    922                         _panX = n; 
    923                         calculateMatrix(); 
    924                         dispatchEvent(new MapEvent(MapEvent.PANNED, new Point(-(origin.x - _panX) * _scale, -(origin.y - _panY) * _scale))); 
    925                 } 
    926                 } 
    927  
    928                 public function get panY():Number 
    929                 { 
    930                         return _panY; 
    931                 } 
    932                 public function set panY(n:Number):void 
    933                 { 
    934                     if (n != panY) 
    935                     { 
    936                         var origin:Point = startPan || new Point(_panX, _panY); 
    937                         _panY = n; 
    938                         calculateMatrix(); 
    939                         dispatchEvent(new MapEvent(MapEvent.PANNED, new Point(-(origin.x - _panX) * _scale, -(origin.y - _panY) * _scale))); 
    940                     } 
     902                        //calculateMatrix(); 
    941903                } 
    942904 
    943905                public function get zoomLevel():Number 
    944906                { 
    945                         return Math.log(_scale) / Math.log(2); 
     907                        return Math.log(scale) / Math.log(2); 
    946908                } 
    947909 
     
    950912                    if (zoomLevel != n) 
    951913                    { 
    952                         _scale = Math.pow(2, n);                                                 
    953                         calculateMatrix(); 
    954                         var zoomEvent:MapEvent = new MapEvent(MapEvent.ZOOMED_BY); 
    955                 zoomEvent.zoomDelta = zoomLevel-startZoom; 
    956                 zoomEvent.zoomLevel = zoomLevel; 
    957                 dispatchEvent(zoomEvent); 
     914                        scale = Math.pow(2, n);                                          
    958915            } 
    959916                } 
     
    961918                public function get scale():Number 
    962919                { 
    963                         return _scale
     920                        return Math.sqrt(worldMatrix.a * worldMatrix.a + worldMatrix.b * worldMatrix.b)
    964921                } 
    965922 
     
    968925                    if (scale != n) 
    969926                    { 
    970                         var old:Number = zoomLevel; 
    971                         _scale = n; 
    972                         calculateMatrix();       
    973                         prepareForZooming(); 
    974                         var zoomEvent:MapEvent = new MapEvent(MapEvent.ZOOMED_BY); 
    975                 zoomEvent.zoomDelta = zoomLevel-old; 
    976                 zoomEvent.zoomLevel = zoomLevel; 
    977                 dispatchEvent(zoomEvent); 
    978                         doneZooming(); 
     927                        var needsStop:Boolean = false; 
     928                        if (!zooming) { 
     929                                prepareForZooming(); 
     930                                needsStop = true; 
     931                        } 
     932                         
     933                        var sc:Number = n / scale; 
     934                        worldMatrix.translate(-mapWidth/2, -mapHeight/2); 
     935                        worldMatrix.scale(sc, sc); 
     936                        worldMatrix.translate(mapWidth/2, mapHeight/2); 
     937                         
     938                        dirty = true;    
     939                         
     940                        if (needsStop) { 
     941                                doneZooming(); 
     942                        } 
    979943                    } 
    980944                } 
     
    984948                    if (mapWidth != p.x || mapHeight != p.y) 
    985949                    { 
     950                        var dx:Number = p.x - mapWidth; 
     951                        var dy:Number = p.y - mapHeight; 
     952                         
     953                        // maintain the center point: 
     954                        tx += dx/2; 
     955                        ty += dy/2; 
     956                         
    986957                        mapWidth = p.x; 
    987958                        mapHeight = p.y; 
    988959                scrollRect = new Rectangle(0, 0, mapWidth, mapHeight); 
    989                         calculateMatrix(); 
     960                         
     961                        dirty = true; 
     962 
    990963                        // force this but only for onResize 
    991964                        onRender(); 
     
    10601033                } 
    10611034                 
    1062                 /** called inside of calculateMatrix before events are fired 
     1035                /** called inside of onRender before events are fired 
    10631036                 *  don't use setters inside of here to correct values otherwise we'll get stuck in a loop! */ 
    1064               protected function enforceBounds():Boolean 
     1037              protected function enforceBounds():Boolean 
    10651038                { 
    10661039                        if (!enforceBoundsEnabled) { 
     
    10681041                        } 
    10691042                         
    1070 /*                      if (zoomLevel < minZoom) { 
    1071                                 _scale = Math.pow(2, minZoom); 
    1072                                 return true; 
    1073                         } 
    1074  
    1075                         if (zoomLevel > maxZoom) { 
    1076                                 _scale = Math.pow(2, maxZoom); 
    1077                                 return true; 
    1078                         } */ 
     1043                        if (zoomLevel < minZoom) { 
     1044                                scale = Math.pow(2, minZoom); 
     1045                        } 
     1046                        else if (zoomLevel > maxZoom) { 
     1047                                scale = Math.pow(2, maxZoom); 
     1048                        }  
    10791049                         
    10801050                        var touched:Boolean = false; 
     
    10881058                        if (rightX-leftX > maxTx-minTx) { 
    10891059                                //trace("CENTERING X"); 
    1090                                 _panX = -(minTx+maxTx)/2; 
     1060                                //_panX = -(minTx+maxTx)/2; 
     1061                                worldMatrix.tx = (mapWidth-(minTx+maxTx)*scale)/2; 
    10911062                                touched = true; 
    10921063                        } 
    10931064                        else if (leftX < minTx) { 
    10941065                                //trace("TOO LEFT"); 
    1095                                 _panX += leftX-minTx; 
     1066                                //_panX += leftX-minTx; 
     1067                                worldMatrix.tx += (leftX-minTx)*scale;                           
    10961068                                touched = true; 
    10971069                        } 
    10981070                        else if (rightX > maxTx) { 
    10991071                                //trace("TOO RIGHT"); 
    1100                                 _panX += rightX-maxTx; 
     1072                                //_panX += rightX-maxTx; 
     1073                                worldMatrix.tx += (rightX-maxTx)*scale;                          
    11011074                                touched = true; 
    1102                         }   
     1075                        } 
    11031076 
    11041077                        var upY:Number = tl.row * provider.tileHeight; 
     
    11071080                        if (downY-upY > maxTy-minTy) { 
    11081081                                //trace("CENTERING Y"); 
    1109                                 _panY = -(minTy+maxTy)/2; 
     1082                                //_panY = -(minTy+maxTy)/2; 
     1083                                worldMatrix.ty = (mapHeight-(minTy+maxTy)*scale)/2; 
    11101084                                touched = true; 
    11111085                        } 
    11121086                        else if (upY < minTy) { 
    11131087                                //trace("TOO HIGH"); 
    1114                                 _panY += upY-minTy; 
     1088                                //_panY += upY-minTy; 
     1089                                worldMatrix.ty += (upY-minTy)*scale; 
    11151090                                touched = true; 
    11161091                        } 
    11171092                        else if (downY > maxTy) { 
    11181093                                //trace("TOO LOW"); 
    1119                                 _panY += downY-maxTy; 
     1094                                //_panY += downY-maxTy; 
     1095                                worldMatrix.ty += (downY-maxTy)*scale; 
    11201096                                touched = true; 
    11211097                        }  
    11221098 
     1099                        if (touched) { 
     1100                                _invertedMatrix = null; 
     1101                                _topLeftCoordinate = null; 
     1102                                _bottomRightCoordinate = null;                           
     1103                        } 
     1104 
    11231105                        //trace("bounds of visible map area: ", leftX, rightX, upY, downY); 
    11241106 
    11251107                        return touched;                  
    1126                 } 
    1127  
    1128                 override public function set doubleClickEnabled(enabled:Boolean):void 
    1129                 { 
    1130                         if (enabled) { 
    1131                                 well.doubleClickEnabled = true; 
    1132                                 well.mouseEnabled = true; 
    1133                                 well.mouseChildren = false; 
    1134                         } 
    1135                         else { 
    1136                                 well.doubleClickEnabled = false; 
    1137                         } 
    1138                         super.doubleClickEnabled = false; 
    11391108                } 
    11401109                 
     
    11441113                        if (d) { 
    11451114                                if (stage) stage.invalidate(); 
     1115                                 
     1116                                _invertedMatrix = null; 
     1117                                _topLeftCoordinate = null; 
     1118                                _bottomRightCoordinate = null;                   
    11461119                        } 
    11471120                } 
     
    11511124                        return _dirty; 
    11521125                } 
    1153                                                  
     1126 
     1127                public function getMatrix():Matrix 
     1128                { 
     1129                        return worldMatrix.clone(); 
     1130                } 
     1131 
     1132                public function setMatrix(m:Matrix):void 
     1133                { 
     1134                        worldMatrix = m; 
     1135                        dirty = true; 
     1136                } 
     1137                 
     1138                public function get a():Number 
     1139                { 
     1140                        return worldMatrix.a; 
     1141                } 
     1142                public function get b():Number 
     1143                { 
     1144                        return worldMatrix.b; 
     1145                } 
     1146                public function get c():Number 
     1147                { 
     1148                        return worldMatrix.c; 
     1149                } 
     1150                public function get d():Number 
     1151                { 
     1152                        return worldMatrix.d; 
     1153                } 
     1154                public function get tx():Number 
     1155                { 
     1156                        return worldMatrix.tx; 
     1157                } 
     1158                public function get ty():Number 
     1159                { 
     1160                        return worldMatrix.ty; 
     1161                } 
     1162 
     1163                public function set a(n:Number):void 
     1164                { 
     1165                        worldMatrix.a = n; 
     1166                        dirty = true; 
     1167                } 
     1168                public function set b(n:Number):void 
     1169                { 
     1170                        worldMatrix.b = n; 
     1171                        dirty = true; 
     1172                } 
     1173                public function set c(n:Number):void 
     1174                { 
     1175                        worldMatrix.c = n; 
     1176                        dirty = true; 
     1177                } 
     1178                public function set d(n:Number):void 
     1179                { 
     1180                        worldMatrix.d = n; 
     1181                        dirty = true; 
     1182                } 
     1183                public function set tx(n:Number):void 
     1184                { 
     1185                        worldMatrix.tx = n; 
     1186                        dirty = true; 
     1187                } 
     1188                public function set ty(n:Number):void 
     1189                { 
     1190                        worldMatrix.ty = n; 
     1191                        dirty = true; 
     1192                } 
     1193                                                                 
    11541194        } 
    11551195         
  • branches/tom-tweenlite/lib/com/modestmaps/Map.as

    r595 r599  
    3939        import flash.events.MouseEvent; 
    4040        import flash.external.ExternalInterface; 
     41        import flash.geom.Matrix; 
    4142        import flash.geom.Point; 
    4243        import flash.geom.Rectangle; 
     
    9192            * @see com.modestmaps.core.TileGrid 
    9293            */ 
    93             public function Map(width:Number=320, height:Number=240, draggable:Boolean=true, provider:IMapProvider=null, ... rest) 
     94            public function Map(width:Number=320, height:Number=240, draggable:Boolean=true, mapProvider:IMapProvider=null, ... rest) 
    9495            { 
    9596                if (!Reactor.running()) 
     
    100101                } 
    101102 
    102                         // don't call set map provider here 
    103                         // the extents are all squirrely 
    104                 mapProvider = provider || new MicrosoftProvider(MicrosoftProvider.ROAD); 
     103                        if (!mapProvider) mapProvider = new MicrosoftProvider(MicrosoftProvider.ROAD); 
     104 
     105                        // don't call setMapProvider here 
     106                        // the extent calculations are all squirrely 
     107                this.mapProvider = mapProvider; 
    105108 
    106109                        // initialize the grid (so point/location/coordinate functions should be valid after this) 
     
    140143                else { 
    141144                        var extent:MapExtent = new MapExtent(85, -85, 180, -180); 
    142                         //setExtent(extent); 
    143145                         
    144146                        var l1:Location = mapProvider.coordinateLocation(mapProvider.outerLimits()[0]); 
     
    157159                                        extent.east = l2.lon; 
    158160                                } 
    159                                  
    160                                 //trace(extent);                         
    161161 
    162162                        setExtent(extent);                       
     
    307307                var centerCoord:Coordinate = (new Coordinate(centerRow, centerColumn, centerZoom)).zoomTo(initZoom); 
    308308                 
     309                trace("centering on", centerCoord); 
     310                 
    309311                return coordinatePosition(centerCoord); 
    310312                } 
     
    502504                if (!grid.panning && !grid.zooming) { 
    503505                        grid.prepareForPanning(); 
    504                         grid.panY = grid.panY+(mapHeight*panFraction/grid.scale); 
     506                        grid.ty += (mapHeight*panFraction); 
    505507                    grid.donePanning(); 
    506508                } 
     
    512514                if (!grid.panning && !grid.zooming) { 
    513515                        grid.prepareForPanning(); 
    514                         grid.panY = grid.panY-(mapHeight*panFraction/grid.scale); 
     516                        grid.ty -= (mapHeight*panFraction); 
    515517                    grid.donePanning(); 
    516518                } 
     
    522524                if (!grid.panning && !grid.zooming) { 
    523525                        grid.prepareForPanning(); 
    524                         grid.panX = grid.panX+(mapWidth*panFraction/grid.scale); 
     526                        grid.tx += (mapWidth*panFraction); 
    525527                    grid.donePanning(); 
    526528                } 
     
    532534                if (!grid.panning && !grid.zooming) { 
    533535                        grid.prepareForPanning(); 
    534                         grid.panX = grid.panX-(mapWidth*panFraction/grid.scale); 
     536                        grid.tx -= (mapWidth*panFraction); 
    535537                    grid.donePanning(); 
    536538                } 
    537539            } 
    538              
     540 
     541                /** zoom in, keeping the requested point in the same place */ 
     542        public function zoomInAbout(targetPoint:Point=null, duration:Number=-1):void 
     543        { 
     544            zoomByAbout(1, targetPoint, duration); 
     545        } 
     546 
     547                /** zoom out, keeping the requested point in the same place */ 
     548        public function zoomOutAbout(targetPoint:Point=null, duration:Number=-1):void 
     549        { 
     550            zoomByAbout(-1, targetPoint, duration); 
     551        } 
     552         
     553                /** zoom in or out by zoomDelta, keeping the requested point in the same place */ 
     554        public function zoomByAbout(zoomDelta:int, targetPoint:Point=null, duration:Number=-1):void 
     555        { 
     556            if (!targetPoint) targetPoint = new Point(mapWidth/2, mapHeight/2);          
     557                 
     558                var sc:Number = Math.pow(2, zoomDelta); 
     559                         
     560                        grid.prepareForZooming(); 
     561                        grid.prepareForPanning(); 
     562                         
     563                        var m:Matrix = grid.getMatrix(); 
     564                         
     565                        m.translate(-targetPoint.x, -targetPoint.y); 
     566                        m.scale(sc, sc); 
     567                        m.translate(targetPoint.x, targetPoint.y);               
     568                 
     569            &