Calculate pixel values from latitude/longitude coordinates (using matplotlib Basemap)?

Two things are happening to cause your pixel positions to be off.

Up vote 5 down vote favorite 3 share g+ share fb share tw.

I need to convert map coordinates into pixels (in order to make a clickable map in html). Here is a sample map (made using the Basemap package from matplotlib). I have put some labels on it and attempted to calculate the midpoints of the labels in pixels: #!

/usr/bin/env python # -*- coding: utf-8 -*- ## Step 0: some points to plot names = u"Reykjavík", u"Höfn", u"Húsavík" lats = 64.133333, 64.25, 66.05 lons = -21.933333, -15.216667, -17.316667 ## Step 1: draw a map using matplotlib/Basemap from mpl_toolkits. Basemap import Basemap import matplotlib. Pyplot as plt M = Basemap(projection='merc',resolution='c', llcrnrlat=63,urcrnrlat=67, llcrnrlon=-24,urcrnrlon=-13) x, y = M(lons, lats) # transform coordinates according to projection boxes = for xa, ya, name in zip(x, y, names): box = plt.

Text(xa, ya, name, bbox=dict(facecolor='white', alpha=0.5)) boxes. Append(box) M.bluemarble() # a bit fuzzy at this resolution... plt. Savefig('test.

Png', bbox_inches="tight", pad_inches=0.01) # Step 2: get the coordinates of the textboxes in pixels and calculate the # midpoints F = plt.gcf() # get current figure R = F.canvas. Get_renderer() midpoints = for box in boxes: bb = box. Get_window_extent(renderer=R) midpoints.

Append((int((bb. P00 + bb. P10) / 2), int((bb.

P01 + bb. P11) / 2))) These calculated points are in the approximately correct relative relation to each other, but do not coincide with the true points. The following code snippet should put a red dot on the midpoint of each label: # Step 3: use PIL to draw dots on top of the labels from PIL import Image, ImageDraw im = Image.

Open("test. Png") draw = ImageDraw. Draw(im) for x, y in midpoints: y = im.

Size1 - y # PIL counts rows from top not bottom draw. Ellipse((x-5, y-5, x+5, y+5), fill="#ff0000") im. Save("test.

Png", "PNG") Red dots should be in the middle of the labels. I guess that the error comes in where I extract the coordinates of the text boxes (in Step #2). Any help much appreciated.

Notes Perhaps the solution is something along the lines of this answer? Python matplotlib geospatial link|improve this question edited Jan 26 at 15:12 asked Jan 24 at 16:22Michael Dunn1,542415 100% accept rate.

Two things are happening to cause your pixel positions to be off. The dpi used to calculated the text position is different from that used to save the figure. When you use the bbox_inches option in the savefig call, it eliminates a lot of white space.

You don't take this into account when you are drawing your circles with PIL (or checking where someone clicked. Also you add a padding in this savefig call that you may need to account for if it's very large (as I show in my example below). Probably it will not matter if you still use 0.01.

To fix this first issue, just force the figure and the savefig call to use the same DPI. To fix the second issue, document the (0,0) position (Axes units) of the axes in pixels, and shift your text positions accordingly. Here's a slightly modified version of your code: #!

/usr/bin/env python # -*- coding: utf-8 -*- ## Step 0: some points to plot names = u"Reykjavík", u"Höfn", u"Húsavík" lats = 64.133333, 64.25, 66.05 lons = -21.933333, -15.216667, -17.316667 ## Step 1: draw a map using matplotlib/Basemap from mpl_toolkits. Basemap import Basemap import matplotlib. Pyplot as plt # predefined dpi FIGDPI=80 # set dpi of figure, so that all calculations use this value plt.gcf().

Set_dpi(FIGDPI) M = Basemap(projection='merc',resolution='c', llcrnrlat=63,urcrnrlat=67, llcrnrlon=-24,urcrnrlon=-13) x, y = M(lons, lats) # transform coordinates according to projection boxes = for xa, ya, name in zip(x, y, names): box = plt. Text(xa, ya, name, bbox=dict(facecolor='white', alpha=0.5)) boxes. Append(box) M.bluemarble() # a bit fuzzy at this resolution... # predefine padding in inches PADDING = 2 # force dpi to same value you used in your calculations plt.

Savefig('test. Png', bbox_inches="tight", pad_inches=PADDING,dpi=FIGDPI) # document shift due to loss of white space and added padding origin = plt.gca().transAxes. Transform((0,0)) padding = FIGDPI*PADDING,FIGDPI*PADDING Step #2 is unchanged Step #3 takes account of the origin # Step 3: use PIL to draw dots on top of the labels from PIL import Image, ImageDraw im = Image.

Open("test. Png") draw = ImageDraw. Draw(im) for x, y in midpoints: # deal with shift x = x-origin0+padding0 y = y-origin1+padding1 y = im.

Size1 - y # PIL counts rows from top not bottom draw. Ellipse((x-5, y-5, x+5, y+5), fill="#ff0000") im. Save("test.

Png", "PNG") This results in: Notice that I used an exaggerated PADDING value to test that everything still works, and a value of 0.01 would produce your original figure.

That's amazing! Thanks so much for a full, clear, and tested answer. – Michael Dunn Jan 28 at 17:43.

I cant really gove you an answer,but what I can give you is a way to a solution, that is you have to find the anglde that you relate to or peaks your interest. A good paper is one that people get drawn into because it reaches them ln some way.As for me WW11 to me, I think of the holocaust and the effect it had on the survivors, their families and those who stood by and did nothing until it was too late.

Related Questions