basemap scalebar

Does the capability exist in basemap to create a scale bar on the map? If not, is this planned for the future?

For reference, see the -L option in GMT’s psbasemap, pscoast, etc.

Thanks,

···

Michael Hearne

mhearne@…924…

(303) 273-8620

USGS National Earthquake Information Center

1711 Illinois St. Golden CO 80401

Senior Software Engineer

Synergetics, Inc.


Michael Hearne wrote:

Does the capability exist in basemap to create a scale bar on the map? If not, is this planned for the future?

For reference, see the -L option in GMT's psbasemap, pscoast, etc.

Thanks,

Michael: Is this the sort of thing you're looking for?

#from mpl_toolkits.basemap import Basemap, pyproj # for svn
from matplotlib.toolkits.basemap import Basemap, pyproj
from pylab import *
# add drawscale method to Basemap class.
class Basemap2(Basemap):
    def drawscale(self,x1,x2,y,yoffset=None):
        """draw a simple map scale from x1,y to x2,y in map projection
        coordinates, label it with actual distance in km"""
        length = sqrt((x2-x1)**2)
        if yoffset is None: yoffset = 0.1*length
        lon1,lat1 = self(x1,y,inverse=True)
        lon2,lat2 = self(x2,y,inverse=True)
        gc = pyproj.Geod(a=self.rmajor,b=self.rminor)
        az12,az21,dist = gc.inv(lon1,lat1,lon2,lat2)
        m.plot([x1,x2],[y,y],color='k')
        m.plot([x1,x1],[y-yoffset,y+yoffset],color='k')
        m.plot([x2,x2],[y-yoffset,y+yoffset],color='k')
        text(x1+0.5*length,y-yoffset,'%d km' % (dist/1000.,),\ verticalalignment='top',\
        horizontalalignment='center',fontsize=9)
# setup of basemap ('lcc' = lambert conformal conic).
# use major and minor sphere radii from WGS84 ellipsoid.
m =\ Basemap2(llcrnrlon=-145.5,llcrnrlat=1.,urcrnrlon=-2.566,urcrnrlat=46.352,\
         rsphere=(6378137.00,6356752.3142),\
         resolution='l',area_thresh=1000.,projection='lcc',\
         lat_1=50.,lon_0=-107.)
# draw coastlines and political boundaries.
m.drawcoastlines()
m.fillcontinents()
# draw parallels and meridians.
# label on left, right and bottom of map.
m.drawparallels(arange(0.,80,20.),labels=[1,1,0,1])
m.drawmeridians(arange(10.,360.,30.),labels=[1,1,0,1])
# draw a line from x1,y to x2,y and label it with distance in km.
length = 1.e6
x1,x2,y = 0.5*m.xmax, 0.5*m.xmax+length, 0.35*m.ymax
m.drawscale(x1,x2,y)
title('a simple map scale')
show()

If so, I could add something like this in the next release.

-Jeff

···

--
Jeffrey S. Whitaker Phone : (303)497-6313
Meteorologist FAX : (303)497-6449
NOAA/OAR/PSD R/PSD1 Email : Jeffrey.S.Whitaker@...259...
325 Broadway Office : Skaggs Research Cntr 1D-124
Boulder, CO, USA 80303-3328 Web : Jeffrey S. Whitaker: NOAA Physical Sciences Laboratory

Jeff - That would replicate the “simple” scale-bar from GMT. Below is my not-complete attempt at replicating the “fancy” scale bar. It would need some options for specifying different units (miles, nautical miles, etc.) and perhaps some more attention to spacing of the text from the scale bar and tick marks…

–Mike

from numpy import *

from matplotlib.toolkits.basemap import Basemap, pyproj

from pylab import *

add drawscale method to Basemap class.

class Basemap2(Basemap):

def drawscale(self,lon,lat,length,yoffset=None):

“”"draw a fancy map scale from lon-length/2,lat-yoffset to

lon-length/2,lat-yoffset, label it with actual distance in km"""

length = length*1000 #input length is km

#we need 5 sets of x coordinates (in map units)

#center of scale

xc,yc = self(lon,lat)

#left edge of scale

lon1,lat1 = self(xc-length/2,yc,inverse=True)

x1,y1 = self(lon1,lat1)

#quarter scale

lon2,lat2 = self(xc-length/4,yc,inverse=True)

x2,y2 = self(lon2,lat2)

#three quarter scale

lon3,lat3 = self(xc+length/4,yc,inverse=True)

x3,y3 = self(lon3,lat3)

#right edge of scale

lon4,lat4 = self(xc+length/2,yc,inverse=True)

x4,y4 = self(lon4,lat4)

if yoffset is None: yoffset = 0.1*length

#plot top line

ytop = yc+yoffset/2

ybottom = yc-yoffset/2

ytick = ybottom - yoffset/2

ytext = ytick - yoffset/2

m.plot([x1,x4],[ytop,ytop],color=‘k’)

#plot bottom line

m.plot([x1,x4],[ybottom,ybottom],color=‘k’)

#plot left edge

m.plot([x1,x1],[ybottom,ytop],color=‘k’)

#plot right edge

m.plot([x4,x4],[ybottom,ytop],color=‘k’)

#make a filled black box from left edge to 1/4 way across

fill([x1,x2,x2,x1,x1],[ytop,ytop,ybottom,ybottom,ytop],‘k’)

#make a filled white box from 1/4 way across to 1/2 way across

fill([x2,xc,xc,x2,x2],[ytop,ytop,ybottom,ybottom,ytop],‘w’)

#make a filled white box from 1/2 way across to 3/4 way across

fill([xc,x3,x3,xc,xc],[ytop,ytop,ybottom,ybottom,ytop],‘k’)

#make a filled white box from 3/4 way across to end

fill([x3,x4,x4,x3,x3],[ytop,ytop,ybottom,ybottom,ytop],‘w’)

#plot 3 tick marks at left edge, center, and right edge

m.plot([x1,x1],[ytick,ybottom],color=‘k’)

m.plot([xc,xc],[ytick,ybottom],color=‘k’)

m.plot([x4,x4],[ytick,ybottom],color=‘k’)

#label 3 tick marks

text(x1,ytext,’%d’ % (0),\

	    horizontalalignment='center',\

	    verticalalignment='top',\

	    fontsize=9)

text(xc,ytext,’%d’ % (round((length/2)/1000)),\

	    horizontalalignment='center',\

	    verticalalignment='top',\

	    fontsize=9)

text(x4,ytext,’%d’ % (round((length)/1000)),\

	    horizontalalignment='center',\

	    verticalalignment='top',\

	    fontsize=9)

#put units on top

text(xc,ytop+yoffset/2,‘km’,\

	    horizontalalignment='center',\

	    verticalalignment='bottom',\

	    fontsize=9)

setup of basemap (‘lcc’ = lambert conformal conic).

use major and minor sphere radii from WGS84 ellipsoid.

m = Basemap2(llcrnrlon=-145.5,llcrnrlat=1.,urcrnrlon=-2.566,urcrnrlat=46.352,\

	      rsphere=(6378137.00,6356752.3142),\

	      resolution='l',area_thresh=1000.,projection='lcc',\

	      lat_1=50.,lon_0=-107.)

draw coastlines and political boundaries.

m.drawcoastlines()

m.fillcontinents()

draw parallels and meridians.

label on left, right and bottom of map.

m.drawparallels(arange(0.,80,20.),labels=[1,1,0,1])

m.drawmeridians(arange(10.,360.,30.),labels=[1,1,0,1])

draw a line from x1,y to x2,y and label it with distance in km.

length = 3000 #kilometers

x1,y1 = 0.25m.xmax, 0.25m.ymax

lon1,lat1 = m(x1,y1,inverse=True)

m.drawscale(lon1,lat1,length)

title(‘a fancy map scale’)

show()

···

Michael Hearne

mhearne@…924…

(303) 273-8620

USGS National Earthquake Information Center

1711 Illinois St. Golden CO 80401

Senior Software Engineer

Synergetics, Inc.


Michael Hearne wrote:

Jeff - That would replicate the "simple" scale-bar from GMT. Below is my not-complete attempt at replicating the "fancy" scale bar. It would need some options for specifying different units (miles, nautical miles, etc.) and perhaps some more attention to spacing of the text from the scale bar and tick marks...

--Mike

Mike: Very nice! Do you want the scale to show the true distance on the earth (in which case the labels will vary depending on where the label is placed), or the distance in map projection coordinates (in which case the labels are constant)? Or perhaps a lat/lon value could be given to specify where the scale is true?

-Jeff

···

from numpy import *
from matplotlib.toolkits.basemap import Basemap, pyproj
from pylab import *
# add drawscale method to Basemap class.
class Basemap2(Basemap):
   def drawscale(self,lon,lat,length,yoffset=None):
       """draw a fancy map scale from lon-length/2,lat-yoffset to
       lon-length/2,lat-yoffset, label it with actual distance in km"""
       length = length*1000 #input length is km

       #we need 5 sets of x coordinates (in map units)
       #center of scale
       xc,yc = self(lon,lat)
       #left edge of scale
       lon1,lat1 = self(xc-length/2,yc,inverse=True)
       x1,y1 = self(lon1,lat1)
       #quarter scale
       lon2,lat2 = self(xc-length/4,yc,inverse=True)
       x2,y2 = self(lon2,lat2)
       #three quarter scale
       lon3,lat3 = self(xc+length/4,yc,inverse=True)
       x3,y3 = self(lon3,lat3)
       #right edge of scale
       lon4,lat4 = self(xc+length/2,yc,inverse=True)
       x4,y4 = self(lon4,lat4)
              if yoffset is None: yoffset = 0.1*length

       #plot top line
       ytop = yc+yoffset/2
       ybottom = yc-yoffset/2
       ytick = ybottom - yoffset/2
       ytext = ytick - yoffset/2
       m.plot([x1,x4],[ytop,ytop],color='k')
       #plot bottom line
       m.plot([x1,x4],[ybottom,ybottom],color='k')
       #plot left edge
       m.plot([x1,x1],[ybottom,ytop],color='k')
       #plot right edge
       m.plot([x4,x4],[ybottom,ytop],color='k')

       #make a filled black box from left edge to 1/4 way across
       fill([x1,x2,x2,x1,x1],[ytop,ytop,ybottom,ybottom,ytop],'k')
       #make a filled white box from 1/4 way across to 1/2 way across
       fill([x2,xc,xc,x2,x2],[ytop,ytop,ybottom,ybottom,ytop],'w')
       #make a filled white box from 1/2 way across to 3/4 way across
       fill([xc,x3,x3,xc,xc],[ytop,ytop,ybottom,ybottom,ytop],'k')
       #make a filled white box from 3/4 way across to end
       fill([x3,x4,x4,x3,x3],[ytop,ytop,ybottom,ybottom,ytop],'w')
              #plot 3 tick marks at left edge, center, and right edge
       m.plot([x1,x1],[ytick,ybottom],color='k')
       m.plot([xc,xc],[ytick,ybottom],color='k')
       m.plot([x4,x4],[ytick,ybottom],color='k')

       #label 3 tick marks
       text(x1,ytext,'%d' % (0),\
   horizontalalignment='center',\
   verticalalignment='top',\
   fontsize=9)
       text(xc,ytext,'%d' % (round((length/2)/1000)),\
   horizontalalignment='center',\
   verticalalignment='top',\
   fontsize=9)
       text(x4,ytext,'%d' % (round((length)/1000)),\
   horizontalalignment='center',\
   verticalalignment='top',\
   fontsize=9)

       #put units on top
       text(xc,ytop+yoffset/2,'km',\
   horizontalalignment='center',\
   verticalalignment='bottom',\
   fontsize=9)

# setup of basemap ('lcc' = lambert conformal conic).
# use major and minor sphere radii from WGS84 ellipsoid.
m = Basemap2(llcrnrlon=-145.5,llcrnrlat=1.,urcrnrlon=-2.566,urcrnrlat=46.352,\
     rsphere=(6378137.00,6356752.3142),\
     resolution='l',area_thresh=1000.,projection='lcc',\
     lat_1=50.,lon_0=-107.)
# draw coastlines and political boundaries.
m.drawcoastlines()
m.fillcontinents()
# draw parallels and meridians.
# label on left, right and bottom of map.
m.drawparallels(arange(0.,80,20.),labels=[1,1,0,1])
m.drawmeridians(arange(10.,360.,30.),labels=[1,1,0,1])
# draw a line from x1,y to x2,y and label it with distance in km.
length = 3000 #kilometers
x1,y1 = 0.25*m.xmax, 0.25*m.ymax
lon1,lat1 = m(x1,y1,inverse=True)
m.drawscale(lon1,lat1,length)
title('a fancy map scale')
show()

------------------------------------------------------
Michael Hearne
mhearne@...924... <mailto:mhearne@…924…>
(303) 273-8620
USGS National Earthquake Information Center
1711 Illinois St. Golden CO 80401
Senior Software Engineer
Synergetics, Inc.
------------------------------------------------------

--
Jeffrey S. Whitaker Phone : (303)497-6313
NOAA/OAR/CDC R/PSD1 FAX : (303)497-6449
325 Broadway Boulder, CO, USA 80305-3328

Jeff - I think the way GMT does it would be okay - they have a latitude of true scale, which I usually choose as the center latitude of the map.

I was thinking we should allow people to choose the “simple” or “fancy” options. Do you think it will be okay to have the height of the bar and the text offset be relative to the length of it? I suppose if the height becomes a problem, people could use the yoffset keyword…

–Mike

···

On Mar 4, 2008, at 6:05 AM, Jeff Whitaker wrote:

Michael Hearne wrote:

Jeff - That would replicate the “simple” scale-bar from GMT. Below is my not-complete attempt at replicating the “fancy” scale bar. It would need some options for specifying different units (miles, nautical miles, etc.) and perhaps some more attention to spacing of the text from the scale bar and tick marks…

–Mike

Mike: Very nice! Do you want the scale to show the true distance on the earth (in which case the labels will vary depending on where the label is placed), or the distance in map projection coordinates (in which case the labels are constant)? Or perhaps a lat/lon value could be given to specify where the scale is true?

-Jeff

from numpy import *

from matplotlib.toolkits.basemap import Basemap, pyproj

from pylab import *

add drawscale method to Basemap class.

class Basemap2(Basemap):

def drawscale(self,lon,lat,length,yoffset=None):

   """draw a fancy map scale from lon-length/2,lat-yoffset to
   lon-length/2,lat-yoffset, label it with actual distance in km"""
   length = length*1000 #input length is km
   #we need 5 sets of x coordinates (in map units)
   #center of scale
   xc,yc = self(lon,lat)
   #left edge of scale
   lon1,lat1 = self(xc-length/2,yc,inverse=True)
   x1,y1 = self(lon1,lat1)
   #quarter scale
   lon2,lat2 = self(xc-length/4,yc,inverse=True)
   x2,y2 = self(lon2,lat2)
   #three quarter scale
   lon3,lat3 = self(xc+length/4,yc,inverse=True)
   x3,y3 = self(lon3,lat3)
   #right edge of scale
   lon4,lat4 = self(xc+length/2,yc,inverse=True)
   x4,y4 = self(lon4,lat4)
          if yoffset is None: yoffset = 0.1*length
   #plot top line
   ytop = yc+yoffset/2
   ybottom = yc-yoffset/2
   ytick = ybottom - yoffset/2
   ytext = ytick - yoffset/2
   m.plot([x1,x4],[ytop,ytop],color='k')
   #plot bottom line
   m.plot([x1,x4],[ybottom,ybottom],color='k')
   #plot left edge
   m.plot([x1,x1],[ybottom,ytop],color='k')
   #plot right edge
   m.plot([x4,x4],[ybottom,ytop],color='k')
   #make a filled black box from left edge to 1/4 way across
   fill([x1,x2,x2,x1,x1],[ytop,ytop,ybottom,ybottom,ytop],'k')
   #make a filled white box from 1/4 way across to 1/2 way across
   fill([x2,xc,xc,x2,x2],[ytop,ytop,ybottom,ybottom,ytop],'w')
   #make a filled white box from 1/2 way across to 3/4 way across
   fill([xc,x3,x3,xc,xc],[ytop,ytop,ybottom,ybottom,ytop],'k')
   #make a filled white box from 3/4 way across to end
   fill([x3,x4,x4,x3,x3],[ytop,ytop,ybottom,ybottom,ytop],'w')
          #plot 3 tick marks at left edge, center, and right edge
   m.plot([x1,x1],[ytick,ybottom],color='k')
   m.plot([xc,xc],[ytick,ybottom],color='k')
   m.plot([x4,x4],[ytick,ybottom],color='k')
   #label 3 tick marks
   text(x1,ytext,'%d' % (0),\

horizontalalignment=‘center’,\

verticalalignment=‘top’,\

fontsize=9)

   text(xc,ytext,'%d' % (round((length/2)/1000)),\

horizontalalignment=‘center’,\

verticalalignment=‘top’,\

fontsize=9)

   text(x4,ytext,'%d' % (round((length)/1000)),\

horizontalalignment=‘center’,\

verticalalignment=‘top’,\

fontsize=9)

   #put units on top
   text(xc,ytop+yoffset/2,'km',\

horizontalalignment=‘center’,\

verticalalignment=‘bottom’,\

fontsize=9)

setup of basemap (‘lcc’ = lambert conformal conic).

use major and minor sphere radii from WGS84 ellipsoid.

m = Basemap2(llcrnrlon=-145.5,llcrnrlat=1.,urcrnrlon=-2.566,urcrnrlat=46.352,\

 rsphere=(6378137.00,6356752.3142),\
 resolution='l',area_thresh=1000.,projection='lcc',\
 lat_1=50.,lon_0=-107.)

draw coastlines and political boundaries.

m.drawcoastlines()

m.fillcontinents()

draw parallels and meridians.

label on left, right and bottom of map.

m.drawparallels(arange(0.,80,20.),labels=[1,1,0,1])

m.drawmeridians(arange(10.,360.,30.),labels=[1,1,0,1])

draw a line from x1,y to x2,y and label it with distance in km.

length = 3000 #kilometers

x1,y1 = 0.25m.xmax, 0.25m.ymax

lon1,lat1 = m(x1,y1,inverse=True)

m.drawscale(lon1,lat1,length)

title(‘a fancy map scale’)

show()


Michael Hearne

mhearne@…924… mailto:mhearne@...924...

(303) 273-8620

USGS National Earthquake Information Center

1711 Illinois St. Golden CO 80401

Senior Software Engineer

Synergetics, Inc.


Jeffrey S. Whitaker Phone : (303)497-6313

NOAA/OAR/CDC R/PSD1 FAX : (303)497-6449

325 Broadway Boulder, CO, USA 80305-3328


Michael Hearne

mhearne@…924…

(303) 273-8620

USGS National Earthquake Information Center

1711 Illinois St. Golden CO 80401

Senior Software Engineer

Synergetics, Inc.


Hi,

this scalebar is a really good idea!

However, I suggest that all parameters must be optional:
- The position could be by default somewhere in the lower left corner
(for example). It may be interesting to find a "best" position using
the algo of legend.
- Then length could be estimated from automatically from the map coordinates.

···

On Tue, Mar 4, 2008 at 3:47 PM, Michael Hearne <mhearne@...924...> wrote:

Jeff - I think the way GMT does it would be okay - they have a latitude of
true scale, which I usually choose as the center latitude of the map.

I was thinking we should allow people to choose the "simple" or "fancy"
options. Do you think it will be okay to have the height of the bar and the
text offset be relative to the length of it? I suppose if the height
becomes a problem, people could use the yoffset keyword...

--Mike

On Mar 4, 2008, at 6:05 AM, Jeff Whitaker wrote:

Michael Hearne wrote:
Jeff - That would replicate the "simple" scale-bar from GMT. Below is my
not-complete attempt at replicating the "fancy" scale bar. It would need
some options for specifying different units (miles, nautical miles, etc.)
and perhaps some more attention to spacing of the text from the scale bar
and tick marks...

--Mike

Mike: Very nice! Do you want the scale to show the true distance on the
earth (in which case the labels will vary depending on where the label is
placed), or the distance in map projection coordinates (in which case the
labels are constant)? Or perhaps a lat/lon value could be given to specify
where the scale is true?

-Jeff

from numpy import *
from matplotlib.toolkits.basemap import Basemap, pyproj
from pylab import *
# add drawscale method to Basemap class.
class Basemap2(Basemap):
   def drawscale(self,lon,lat,length,yoffset=None):
       """draw a fancy map scale from lon-length/2,lat-yoffset to
       lon-length/2,lat-yoffset, label it with actual distance in km"""
       length = length*1000 #input length is km

       #we need 5 sets of x coordinates (in map units)
       #center of scale
       xc,yc = self(lon,lat)
       #left edge of scale
       lon1,lat1 = self(xc-length/2,yc,inverse=True)
       x1,y1 = self(lon1,lat1)
       #quarter scale
       lon2,lat2 = self(xc-length/4,yc,inverse=True)
       x2,y2 = self(lon2,lat2)
       #three quarter scale
       lon3,lat3 = self(xc+length/4,yc,inverse=True)
       x3,y3 = self(lon3,lat3)
       #right edge of scale
       lon4,lat4 = self(xc+length/2,yc,inverse=True)
       x4,y4 = self(lon4,lat4)
              if yoffset is None: yoffset = 0.1*length

       #plot top line
       ytop = yc+yoffset/2
       ybottom = yc-yoffset/2
       ytick = ybottom - yoffset/2
       ytext = ytick - yoffset/2
       m.plot([x1,x4],[ytop,ytop],color='k')
       #plot bottom line
       m.plot([x1,x4],[ybottom,ybottom],color='k')
       #plot left edge
       m.plot([x1,x1],[ybottom,ytop],color='k')
       #plot right edge
       m.plot([x4,x4],[ybottom,ytop],color='k')

       #make a filled black box from left edge to 1/4 way across
       fill([x1,x2,x2,x1,x1],[ytop,ytop,ybottom,ybottom,ytop],'k')
       #make a filled white box from 1/4 way across to 1/2 way across
       fill([x2,xc,xc,x2,x2],[ytop,ytop,ybottom,ybottom,ytop],'w')
       #make a filled white box from 1/2 way across to 3/4 way across
       fill([xc,x3,x3,xc,xc],[ytop,ytop,ybottom,ybottom,ytop],'k')
       #make a filled white box from 3/4 way across to end
       fill([x3,x4,x4,x3,x3],[ytop,ytop,ybottom,ybottom,ytop],'w')
              #plot 3 tick marks at left edge, center, and right edge
       m.plot([x1,x1],[ytick,ybottom],color='k')
       m.plot([xc,xc],[ytick,ybottom],color='k')
       m.plot([x4,x4],[ytick,ybottom],color='k')

       #label 3 tick marks
       text(x1,ytext,'%d' % (0),\
   horizontalalignment='center',\
   verticalalignment='top',\
   fontsize=9)
       text(xc,ytext,'%d' % (round((length/2)/1000)),\
   horizontalalignment='center',\
   verticalalignment='top',\
   fontsize=9)
       text(x4,ytext,'%d' % (round((length)/1000)),\
   horizontalalignment='center',\
   verticalalignment='top',\
   fontsize=9)

       #put units on top
       text(xc,ytop+yoffset/2,'km',\
   horizontalalignment='center',\
   verticalalignment='bottom',\
   fontsize=9)

# setup of basemap ('lcc' = lambert conformal conic).
# use major and minor sphere radii from WGS84 ellipsoid.
m =
Basemap2(llcrnrlon=-145.5,llcrnrlat=1.,urcrnrlon=-2.566,urcrnrlat=46.352,\
     rsphere=(6378137.00,6356752.3142),\
     resolution='l',area_thresh=1000.,projection='lcc',\
     lat_1=50.,lon_0=-107.)
# draw coastlines and political boundaries.
m.drawcoastlines()
m.fillcontinents()
# draw parallels and meridians.
# label on left, right and bottom of map.
m.drawparallels(arange(0.,80,20.),labels=[1,1,0,1])
m.drawmeridians(arange(10.,360.,30.),labels=[1,1,0,1])
# draw a line from x1,y to x2,y and label it with distance in km.
length = 3000 #kilometers
x1,y1 = 0.25*m.xmax, 0.25*m.ymax
lon1,lat1 = m(x1,y1,inverse=True)
m.drawscale(lon1,lat1,length)
title('a fancy map scale')
show()

------------------------------------------------------
Michael Hearne
mhearne@...924... <mailto:mhearne@…924…>
(303) 273-8620
USGS National Earthquake Information Center
1711 Illinois St. Golden CO 80401
Senior Software Engineer
Synergetics, Inc.
------------------------------------------------------

--
Jeffrey S. Whitaker Phone : (303)497-6313
NOAA/OAR/CDC R/PSD1 FAX : (303)497-6449
325 Broadway Boulder, CO, USA 80305-3328

------------------------------------------------------
Michael Hearne
mhearne@...924...
(303) 273-8620
USGS National Earthquake Information Center
1711 Illinois St. Golden CO 80401
Senior Software Engineer
Synergetics, Inc.
------------------------------------------------------

-------------------------------------------------------------------------
This SF.net email is sponsored by: Microsoft
Defy all challenges. Microsoft(R) Visual Studio 2008.
http://clk.atdmt.com/MRT/go/vse0120000070mrt/direct/01/
_______________________________________________
Matplotlib-users mailing list
Matplotlib-users@lists.sourceforge.net
matplotlib-users List Signup and Options

--
Stephane Raynaud

Stephane Raynaud wrote:

Hi,

this scalebar is a really good idea!

However, I suggest that all parameters must be optional:
- The position could be by default somewhere in the lower left corner
(for example). It may be interesting to find a "best" position using
the algo of legend.
- Then length could be estimated from automatically from the map coordinates.

Stephane: While I agree it would be nice to be able to just say 'give me a scalebar', I don't think having Basemap choose a default location and size would be very useful. You want the scalebar to be where there is nothing else drawn on the map, and this will be different in every case. Plus, you probably want the length to be a nice round number, not an arbitrary fraction of the map domain.

-Jeff

···

On Tue, Mar 4, 2008 at 3:47 PM, Michael Hearne <mhearne@...924...> wrote:
  

Jeff - I think the way GMT does it would be okay - they have a latitude of
true scale, which I usually choose as the center latitude of the map.

I was thinking we should allow people to choose the "simple" or "fancy"
options. Do you think it will be okay to have the height of the bar and the
text offset be relative to the length of it? I suppose if the height
becomes a problem, people could use the yoffset keyword...

--Mike

On Mar 4, 2008, at 6:05 AM, Jeff Whitaker wrote:

Michael Hearne wrote:
Jeff - That would replicate the "simple" scale-bar from GMT. Below is my
not-complete attempt at replicating the "fancy" scale bar. It would need
some options for specifying different units (miles, nautical miles, etc.)
and perhaps some more attention to spacing of the text from the scale bar
and tick marks...

--Mike

Mike: Very nice! Do you want the scale to show the true distance on the
earth (in which case the labels will vary depending on where the label is
placed), or the distance in map projection coordinates (in which case the
labels are constant)? Or perhaps a lat/lon value could be given to specify
where the scale is true?

-Jeff

from numpy import *
from matplotlib.toolkits.basemap import Basemap, pyproj
from pylab import *
# add drawscale method to Basemap class.
class Basemap2(Basemap):
   def drawscale(self,lon,lat,length,yoffset=None):
       """draw a fancy map scale from lon-length/2,lat-yoffset to
       lon-length/2,lat-yoffset, label it with actual distance in km"""
       length = length*1000 #input length is km

       #we need 5 sets of x coordinates (in map units)
       #center of scale
       xc,yc = self(lon,lat)
       #left edge of scale
       lon1,lat1 = self(xc-length/2,yc,inverse=True)
       x1,y1 = self(lon1,lat1)
       #quarter scale
       lon2,lat2 = self(xc-length/4,yc,inverse=True)
       x2,y2 = self(lon2,lat2)
       #three quarter scale
       lon3,lat3 = self(xc+length/4,yc,inverse=True)
       x3,y3 = self(lon3,lat3)
       #right edge of scale
       lon4,lat4 = self(xc+length/2,yc,inverse=True)
       x4,y4 = self(lon4,lat4)
              if yoffset is None: yoffset = 0.1*length

       #plot top line
       ytop = yc+yoffset/2
       ybottom = yc-yoffset/2
       ytick = ybottom - yoffset/2
       ytext = ytick - yoffset/2
       m.plot([x1,x4],[ytop,ytop],color='k')
       #plot bottom line
       m.plot([x1,x4],[ybottom,ybottom],color='k')
       #plot left edge
       m.plot([x1,x1],[ybottom,ytop],color='k')
       #plot right edge
       m.plot([x4,x4],[ybottom,ytop],color='k')

       #make a filled black box from left edge to 1/4 way across
       fill([x1,x2,x2,x1,x1],[ytop,ytop,ybottom,ybottom,ytop],'k')
       #make a filled white box from 1/4 way across to 1/2 way across
       fill([x2,xc,xc,x2,x2],[ytop,ytop,ybottom,ybottom,ytop],'w')
       #make a filled white box from 1/2 way across to 3/4 way across
       fill([xc,x3,x3,xc,xc],[ytop,ytop,ybottom,ybottom,ytop],'k')
       #make a filled white box from 3/4 way across to end
       fill([x3,x4,x4,x3,x3],[ytop,ytop,ybottom,ybottom,ytop],'w')
              #plot 3 tick marks at left edge, center, and right edge
       m.plot([x1,x1],[ytick,ybottom],color='k')
       m.plot([xc,xc],[ytick,ybottom],color='k')
       m.plot([x4,x4],[ytick,ybottom],color='k')

       #label 3 tick marks
       text(x1,ytext,'%d' % (0),\
   horizontalalignment='center',\
   verticalalignment='top',\
   fontsize=9)
       text(xc,ytext,'%d' % (round((length/2)/1000)),\
   horizontalalignment='center',\
   verticalalignment='top',\
   fontsize=9)
       text(x4,ytext,'%d' % (round((length)/1000)),\
   horizontalalignment='center',\
   verticalalignment='top',\
   fontsize=9)

       #put units on top
       text(xc,ytop+yoffset/2,'km',\
   horizontalalignment='center',\
   verticalalignment='bottom',\
   fontsize=9)

# setup of basemap ('lcc' = lambert conformal conic).
# use major and minor sphere radii from WGS84 ellipsoid.
m =
Basemap2(llcrnrlon=-145.5,llcrnrlat=1.,urcrnrlon=-2.566,urcrnrlat=46.352,\
     rsphere=(6378137.00,6356752.3142),\
     resolution='l',area_thresh=1000.,projection='lcc',\
     lat_1=50.,lon_0=-107.)
# draw coastlines and political boundaries.
m.drawcoastlines()
m.fillcontinents()
# draw parallels and meridians.
# label on left, right and bottom of map.
m.drawparallels(arange(0.,80,20.),labels=[1,1,0,1])
m.drawmeridians(arange(10.,360.,30.),labels=[1,1,0,1])
# draw a line from x1,y to x2,y and label it with distance in km.
length = 3000 #kilometers
x1,y1 = 0.25*m.xmax, 0.25*m.ymax
lon1,lat1 = m(x1,y1,inverse=True)
m.drawscale(lon1,lat1,length)
title('a fancy map scale')
show()

------------------------------------------------------
Michael Hearne
mhearne@...924... <mailto:mhearne@…924…>
(303) 273-8620
USGS National Earthquake Information Center
1711 Illinois St. Golden CO 80401
Senior Software Engineer
Synergetics, Inc.
------------------------------------------------------

--
Jeffrey S. Whitaker Phone : (303)497-6313
NOAA/OAR/CDC R/PSD1 FAX : (303)497-6449
325 Broadway Boulder, CO, USA 80305-3328

------------------------------------------------------
Michael Hearne
mhearne@...924...
(303) 273-8620
USGS National Earthquake Information Center
1711 Illinois St. Golden CO 80401
Senior Software Engineer
Synergetics, Inc.
------------------------------------------------------

-------------------------------------------------------------------------
This SF.net email is sponsored by: Microsoft
Defy all challenges. Microsoft(R) Visual Studio 2008.
http://clk.atdmt.com/MRT/go/vse0120000070mrt/direct/01/
_______________________________________________
Matplotlib-users mailing list
Matplotlib-users@lists.sourceforge.net
matplotlib-users List Signup and Options

--
Jeffrey S. Whitaker Phone : (303)497-6313
NOAA/OAR/CDC R/PSD1 FAX : (303)497-6449
325 Broadway Boulder, CO, USA 80305-3328

Stephane Raynaud wrote:
> Hi,
>
> this scalebar is a really good idea!
>
> However, I suggest that all parameters must be optional:
> - The position could be by default somewhere in the lower left corner
> (for example). It may be interesting to find a "best" position using
> the algo of legend.
> - Then length could be estimated from automatically from the map coordinates.
>
>
>
Stephane: While I agree it would be nice to be able to just say 'give
me a scalebar', I don't think having Basemap choose a default location
and size would be very useful. You want the scalebar to be where there
is nothing else drawn on the map, and this will be different in every
case.

Sure, but the algorithm used by legend tries to put the legend where
there is nothing drawn.

Plus, you probably want the length to be a nice round number, not
an arbitrary fraction of the map domain.

I was not thinking about a simple fraction, but a nice length taken
within values derived from a Locator scaled by a fraction on the map
domain.

···

On Thu, Mar 6, 2008 at 6:15 PM, Jeff Whitaker <jswhit@...146...> wrote:

-Jeff

> On Tue, Mar 4, 2008 at 3:47 PM, Michael Hearne <mhearne@...924...> wrote:
>
>> Jeff - I think the way GMT does it would be okay - they have a latitude of
>> true scale, which I usually choose as the center latitude of the map.
>>
>> I was thinking we should allow people to choose the "simple" or "fancy"
>> options. Do you think it will be okay to have the height of the bar and the
>> text offset be relative to the length of it? I suppose if the height
>> becomes a problem, people could use the yoffset keyword...
>>
>> --Mike
>>
>>
>> On Mar 4, 2008, at 6:05 AM, Jeff Whitaker wrote:
>>
>> Michael Hearne wrote:
>> Jeff - That would replicate the "simple" scale-bar from GMT. Below is my
>> not-complete attempt at replicating the "fancy" scale bar. It would need
>> some options for specifying different units (miles, nautical miles, etc.)
>> and perhaps some more attention to spacing of the text from the scale bar
>> and tick marks...
>>
>> --Mike
>>
>> Mike: Very nice! Do you want the scale to show the true distance on the
>> earth (in which case the labels will vary depending on where the label is
>> placed), or the distance in map projection coordinates (in which case the
>> labels are constant)? Or perhaps a lat/lon value could be given to specify
>> where the scale is true?
>>
>> -Jeff
>>
>> from numpy import *
>> from matplotlib.toolkits.basemap import Basemap, pyproj
>> from pylab import *
>> # add drawscale method to Basemap class.
>> class Basemap2(Basemap):
>> def drawscale(self,lon,lat,length,yoffset=None):
>> """draw a fancy map scale from lon-length/2,lat-yoffset to
>> lon-length/2,lat-yoffset, label it with actual distance in km"""
>> length = length*1000 #input length is km
>>
>> #we need 5 sets of x coordinates (in map units)
>> #center of scale
>> xc,yc = self(lon,lat)
>> #left edge of scale
>> lon1,lat1 = self(xc-length/2,yc,inverse=True)
>> x1,y1 = self(lon1,lat1)
>> #quarter scale
>> lon2,lat2 = self(xc-length/4,yc,inverse=True)
>> x2,y2 = self(lon2,lat2)
>> #three quarter scale
>> lon3,lat3 = self(xc+length/4,yc,inverse=True)
>> x3,y3 = self(lon3,lat3)
>> #right edge of scale
>> lon4,lat4 = self(xc+length/2,yc,inverse=True)
>> x4,y4 = self(lon4,lat4)
>> if yoffset is None: yoffset = 0.1*length
>>
>> #plot top line
>> ytop = yc+yoffset/2
>> ybottom = yc-yoffset/2
>> ytick = ybottom - yoffset/2
>> ytext = ytick - yoffset/2
>> m.plot([x1,x4],[ytop,ytop],color='k')
>> #plot bottom line
>> m.plot([x1,x4],[ybottom,ybottom],color='k')
>> #plot left edge
>> m.plot([x1,x1],[ybottom,ytop],color='k')
>> #plot right edge
>> m.plot([x4,x4],[ybottom,ytop],color='k')
>>
>> #make a filled black box from left edge to 1/4 way across
>> fill([x1,x2,x2,x1,x1],[ytop,ytop,ybottom,ybottom,ytop],'k')
>> #make a filled white box from 1/4 way across to 1/2 way across
>> fill([x2,xc,xc,x2,x2],[ytop,ytop,ybottom,ybottom,ytop],'w')
>> #make a filled white box from 1/2 way across to 3/4 way across
>> fill([xc,x3,x3,xc,xc],[ytop,ytop,ybottom,ybottom,ytop],'k')
>> #make a filled white box from 3/4 way across to end
>> fill([x3,x4,x4,x3,x3],[ytop,ytop,ybottom,ybottom,ytop],'w')
>> #plot 3 tick marks at left edge, center, and right edge
>> m.plot([x1,x1],[ytick,ybottom],color='k')
>> m.plot([xc,xc],[ytick,ybottom],color='k')
>> m.plot([x4,x4],[ytick,ybottom],color='k')
>>
>> #label 3 tick marks
>> text(x1,ytext,'%d' % (0),\
>> horizontalalignment='center',\
>> verticalalignment='top',\
>> fontsize=9)
>> text(xc,ytext,'%d' % (round((length/2)/1000)),\
>> horizontalalignment='center',\
>> verticalalignment='top',\
>> fontsize=9)
>> text(x4,ytext,'%d' % (round((length)/1000)),\
>> horizontalalignment='center',\
>> verticalalignment='top',\
>> fontsize=9)
>>
>> #put units on top
>> text(xc,ytop+yoffset/2,'km',\
>> horizontalalignment='center',\
>> verticalalignment='bottom',\
>> fontsize=9)
>>
>> # setup of basemap ('lcc' = lambert conformal conic).
>> # use major and minor sphere radii from WGS84 ellipsoid.
>> m =
>> Basemap2(llcrnrlon=-145.5,llcrnrlat=1.,urcrnrlon=-2.566,urcrnrlat=46.352,\
>> rsphere=(6378137.00,6356752.3142),\
>> resolution='l',area_thresh=1000.,projection='lcc',\
>> lat_1=50.,lon_0=-107.)
>> # draw coastlines and political boundaries.
>> m.drawcoastlines()
>> m.fillcontinents()
>> # draw parallels and meridians.
>> # label on left, right and bottom of map.
>> m.drawparallels(arange(0.,80,20.),labels=[1,1,0,1])
>> m.drawmeridians(arange(10.,360.,30.),labels=[1,1,0,1])
>> # draw a line from x1,y to x2,y and label it with distance in km.
>> length = 3000 #kilometers
>> x1,y1 = 0.25*m.xmax, 0.25*m.ymax
>> lon1,lat1 = m(x1,y1,inverse=True)
>> m.drawscale(lon1,lat1,length)
>> title('a fancy map scale')
>> show()
>>
>>
>>
>>
>> ------------------------------------------------------
>> Michael Hearne
>> mhearne@...924... <mailto:mhearne@…924…>
>> (303) 273-8620
>> USGS National Earthquake Information Center
>> 1711 Illinois St. Golden CO 80401
>> Senior Software Engineer
>> Synergetics, Inc.
>> ------------------------------------------------------
>>
>>
>>
>>
>>
>> --
>> Jeffrey S. Whitaker Phone : (303)497-6313
>> NOAA/OAR/CDC R/PSD1 FAX : (303)497-6449
>> 325 Broadway Boulder, CO, USA 80305-3328
>>
>>
>>
>>
>>
>> ------------------------------------------------------
>> Michael Hearne
>> mhearne@...924...
>> (303) 273-8620
>> USGS National Earthquake Information Center
>> 1711 Illinois St. Golden CO 80401
>> Senior Software Engineer
>> Synergetics, Inc.
>> ------------------------------------------------------
>>
>>
>> -------------------------------------------------------------------------
>> This SF.net email is sponsored by: Microsoft
>> Defy all challenges. Microsoft(R) Visual Studio 2008.
>> http://clk.atdmt.com/MRT/go/vse0120000070mrt/direct/01/
>> _______________________________________________
>> Matplotlib-users mailing list
>> Matplotlib-users@lists.sourceforge.net
>> matplotlib-users List Signup and Options
>>
>>
>>
>
>
>
>

--
Jeffrey S. Whitaker Phone : (303)497-6313
NOAA/OAR/CDC R/PSD1 FAX : (303)497-6449
325 Broadway Boulder, CO, USA 80305-3328

--
Stephane Raynaud

Stephane Raynaud wrote:

···

On Thu, Mar 6, 2008 at 6:15 PM, Jeff Whitaker <jswhit@...146...> wrote:
  

Stephane Raynaud wrote:
> Hi,
>
> this scalebar is a really good idea!
>
> However, I suggest that all parameters must be optional:
> - The position could be by default somewhere in the lower left corner
> (for example). It may be interesting to find a "best" position using
> the algo of legend.
> - Then length could be estimated from automatically from the map coordinates.
>
Stephane: While I agree it would be nice to be able to just say 'give
me a scalebar', I don't think having Basemap choose a default location
and size would be very useful. You want the scalebar to be where there
is nothing else drawn on the map, and this will be different in every
case.
    
Sure, but the algorithm used by legend tries to put the legend where
there is nothing drawn.

Plus, you probably want the length to be a nice round number, not
an arbitrary fraction of the map domain.
    
I was not thinking about a simple fraction, but a nice length taken
within values derived from a Locator scaled by a fraction on the map
domain.

Stephane: Sounds reasonable, but I don't have time to work on this now. Patches are always welcome! (including your caching module)

-Jeff

--
Jeffrey S. Whitaker Phone : (303)497-6313
NOAA/OAR/CDC R/PSD1 FAX : (303)497-6449
325 Broadway Boulder, CO, USA 80305-3328