Map drawing

matplotlib


In [42]:
from mpl_toolkits.basemap import Basemap
import matplotlib.pyplot as plt
import numpy as np
% matplotlib inline

In [2]:
def make_basemap(ax, resolution='i'):

    bmap = Basemap(projection='lcc',
                   lat_0=46, lon_0=-62,
                   resolution=resolution, area_thresh=0.1,
                   llcrnrlon=-64.0, llcrnrlat=44.8,
                   urcrnrlon=-59.0, urcrnrlat=46.6,
                   ax=ax)

    return bmap

In [60]:
def draw_basemap(bmap):
    bmap.drawcoastlines(color='#9caf9c')
    bmap.drawcountries()
    bmap.fillcontinents(color='#d8e3d8')
    bmap.drawmapboundary(color = 'gray')
    bmap.drawmeridians(np.arange(0, 360, 1), color='gray')
    bmap.drawparallels(np.arange(-90, 90, 1), color='gray')
    return bmap

In [61]:
fig = plt.figure(figsize = (10,8))
ax = fig.add_subplot(111)
m = make_basemap(ax, 'i')
draw_basemap(m)
plt.show()



In [62]:
import fiona
from shapely.geometry import shape, Point, LineString
from shapely.ops import transform
from functools import partial
import pyproj as pp

import os
env = %env

In [63]:
fname = os.path.join(env['HOME'], 'Dropbox/geotransect_test/data/transects/Transects.shp')

In [64]:
ll_nad83 = pp.Proj("+proj=longlat +ellps=GRS80 +datum=NAD83 +no_defs")
utm_nad83 = pp.Proj("+init=EPSG:26920")

project = partial(pp.transform, utm_nad83, ll_nad83)

In [65]:
with fiona.open(fname) as f:
    print "bounds:", f.bounds
    print f.crs['init']
    l3 = shape(f[3]['geometry'])
    print l3


bounds: (374927.2425956799, 4977236.903160162, 741586.3600145737, 5157057.377605482)
epsg:26920
LINESTRING (495524.8389834152 5070711.245478929, 495524.8389834152 5063723.697214352, 495649.6166309969 5060978.588967554, 496273.5048689055 5059730.812491737, 495275.2836882517 5057983.925425593, 496398.2825164873 5056736.148949776, 497022.1707543959 5054115.81835056, 496523.060164069 5052618.486579579, 497271.7260495593 5050996.377161017, 497271.7260495593 5048251.268914219, 498769.05782054 5046005.271257748, 499392.9460584486 5043135.385363368, 499392.9460584486 5041138.94300206, 498644.2801729583 5038892.94534559, 497770.8366398863 5036646.947689119, 497022.1707543959 5033652.284147157, 497271.7260495593 5028910.733539051, 497271.7260495593 5026789.513530162, 497271.7260495593 5026789.513530162, 497271.7260495593 5026789.513530162)

In [66]:
def plot_element(m, element, colour='b'):
    # Plots a line or point given an
    # element with lon,lat coordinates.
    # Requires a Basemap object.
    lo, la = element.xy
    x, y = m(lo, la)
    return m.plot(x, y, color=colour, linewidth=3, solid_capstyle='round')

Let's reorganize things a bit.


In [67]:
fig = plt.figure(figsize=(10,8))
ax = fig.add_subplot(111)

pad = 20000  # Interior padding in m

with fiona.open(fname) as c:
    
    # Get & convert bounds of shapefile.
    llx, lly, urx, ury = c.bounds
    ll = transform(project, Point(llx-pad, lly-pad))
    ur = transform(project, Point(urx+pad, ury+pad))
    
    mid = Point(ll.x + 0.5*(ur.x-ll.x), ll.y + 0.5*(ur.y - ll.y))

    # Generate Basemap object.
    m = Basemap(projection='tmerc',
                lon_0=mid.x, lat_0=mid.y,
                resolution='i',  # c, l, i, h, f
                llcrnrlon=ll.x, llcrnrlat=ll.y,
                urcrnrlon=ur.x, urcrnrlat=ur.y)
    
    # Step over shapes in collection.
    for s in c:
        line = shape(s['geometry'])
        line_t = transform(project, line)
        plot_element(m, line_t)

# Finish drawing the basemap.
draw_basemap(m)
        
plt.show()


Add rasters, eg topo or hillshade.


In [81]:
def draw_basemap(bmap):
    bmap.drawcoastlines(color='#9caf9c')
    bmap.drawcountries()
#    bmap.fillcontinents(color='#d8e3d8')
    bmap.drawmapboundary(color = 'gray')
    bmap.drawmeridians(np.arange(0, 360, 1), color='gray')
    bmap.drawparallels(np.arange(-90, 90, 1), color='gray')
    return bmap

In [50]:
import rasterio
from affine import Affine

In [68]:
shade = os.path.join(env['HOME'], 'Dropbox/geotransect_test/data/elevation/DEM_slope.tif')
with rasterio.drivers(CPL_DEBUG=True):
    with rasterio.open(shade) as src:
        v = src.read()
        meta = src.meta
        aff = meta['affine']
        h = meta['height']
        w = meta['width']
meta


Out[68]:
{'affine': Affine(20.0, 0.0, 210068.0,
       0.0, -20.0, 5239478.0),
 'count': 1,
 'crs': {},
 'driver': u'GTiff',
 'dtype': 'float32',
 'height': 22088,
 'nodata': None,
 'transform': (210068.0, 20.0, 0.0, 5239478.0, 0.0, -20.0),
 'width': 27880}

In [52]:
aff


Out[52]:
Affine(0.0008333333333333334, 0.0, -68.00041666666667,
       0.0, -0.0008333333333333334, 47.000416666666666)

In [53]:
print aff * (0, 0)   # Upper left corner
print aff * (0, h)   # Lower left
print aff * (w, 0)   # Upper right

# Raster is UTM
# ll = transform(project, Point(aff * (0, h)))
# ur = transform(project, Point(aff * (w, 0)))

ll = Point(aff * (0, 0))
ur = Point(aff * (w, 0))


(-68.00041666666667, 47.000416666666666)
(-68.00041666666667, 42.999583333333334)
(-57.999583333333334, 47.000416666666666)

In [54]:
extent = [ll.coords.xy[0][0], ur.coords.xy[0][0], ll.coords.xy[1][0], ur.coords.xy[1][0]]  # L, R, B, T

In [69]:
image = np.repeat(np.swapaxes(np.swapaxes(v, 1, 2), 0, 2), 3, axis=-1)
image.shape


Out[69]:
(22088, 27880, 3)

This is big. It takes ages to plot just an imshow, and is just black...

Let's try another


In [76]:
dem = os.path.join(env['HOME'], 'Dropbox/geotransect_test/data/elevation/NS_DEM_ascii.asc')

It's a monochrome raster so we'll need rasterio.


In [77]:
with rasterio.drivers(CPL_DEBUG=True):
    with rasterio.open(shade) as src:
        v = src.read()
        meta = src.meta
        aff = meta['affine']
        h = meta['height']
        w = meta['width']

In [78]:
image = np.repeat(np.swapaxes(np.swapaxes(v, 1, 2), 0, 2), 3, axis=-1)
image.shape


Out[78]:
(4801, 12001, 3)

In [79]:
image = np.flipud(image)

In [82]:
fig = plt.figure(figsize=(10,8))
ax = fig.add_subplot(111)

pad = 20000  # Interior padding in m

with fiona.open(fname) as c:
    
    # Get & convert bounds of shapefile.
    llx, lly, urx, ury = c.bounds
    ll = transform(project, Point(llx-pad, lly-pad))
    ur = transform(project, Point(urx+pad, ury+pad))
    
    mid = Point(ll.x + 0.5*(ur.x-ll.x), ll.y + 0.5*(ur.y - ll.y))

    # Generate Basemap object.
    m = Basemap(projection='tmerc',
                lon_0=mid.x, lat_0=mid.y,
                resolution='i',  # c, l, i, h, f
                llcrnrlon=ll.x, llcrnrlat=ll.y,
                urcrnrlon=ur.x, urcrnrlat=ur.y)
    
#     # Step over shapes in collection.
#     for s in c:
#         line = shape(s['geometry'])
#         line_t = transform(project, line)
#         plot_element(m, line_t)

m.imshow(image)

# Finish drawing the basemap.
draw_basemap(m)
               
plt.show()


Try using GDAL directly


In [83]:
from osgeo import gdal

In [88]:
fig = plt.figure(figsize=(10,8))
ax = fig.add_subplot(111)

pad = 20000  # Interior padding in m

with fiona.open(fname) as c:
    
    # Get & convert bounds of shapefile.
    llx, lly, urx, ury = c.bounds
    ll = transform(project, Point(llx-pad, lly-pad))
    ur = transform(project, Point(urx+pad, ury+pad))
    
    mid = Point(ll.x + 0.5*(ur.x-ll.x), ll.y + 0.5*(ur.y - ll.y))

    # Generate Basemap object.
    m = Basemap(projection='tmerc',
                lon_0=mid.x, lat_0=mid.y,
                resolution='i',  # c, l, i, h, f
                llcrnrlon=ll.x, llcrnrlat=ll.y,
                urcrnrlon=ur.x, urcrnrlat=ur.y)
   
ds = gdal.Open(dem)
data = ds.ReadAsArray()
np.flipud(data)
x = np.linspace(0, m.urcrnrx, data.shape[1])
y = np.linspace(0, m.urcrnry, data.shape[0])
xx, yy = np.meshgrid(x, y)
m.pcolormesh(xx, yy, data)

# Finish drawing the basemap.
draw_basemap(m)
               
plt.show()


And we should make a 'highlight' map.


In [15]:
with fiona.open(fname) as f:
    print "bounds:", f.bounds
    print f.crs['init']
    print "properties:"
    for i in f:
        print 'Transect '+str(i['properties']['id'])+': ', i['properties']['basin']
    l = list(f)
l[0]


bounds: (374927.2425956799, 4977236.903160162, 741586.3600145737, 5157057.377605482)
epsg:26920
properties:
Transect 1:  Windsor
Transect 10:  Cape Breton
Transect 9:  Cape Breton
Transect 8:  Cumberland
Transect 7:  Cumberland
Transect 6:  Cumberland
Transect 5:  Cumberland
Transect 4:  Windsor
Transect 3:  Windsor
Transect 2:  Windsor
Transect 11:  Windsor
Out[15]:
{'geometry': {'coordinates': [(460813.5136088787, 5015683.790168589),
   (453092.0251554878, 5013612.942185644),
   (448375.00827867264, 5011588.003221451),
   (440742.20387044473, 5006591.4431860605),
   (427316.81442305824, 4995840.115532931),
   (420004.4467151909, 4994173.51402661),
   (411484.7976334155, 4987389.277370967),
   (406342.51740607404, 4980118.4846734395),
   (406342.51740607404, 4980118.4846734395)],
  'type': 'LineString'},
 'id': '0',
 'properties': OrderedDict([(u'id', 1), (u'name', u'Transect 1'), (u'basin', u'Windsor')]),
 'type': 'Feature'}

In [16]:
transect = 3

In [17]:
fig = plt.figure(figsize=(10,8))
ax = fig.add_subplot(111)

pad = 20000  # Interior padding in m
res = 'i'

with fiona.open(fname) as c:
    bounds = c.bounds
    lst = list(c)
 
if transect:
    pad = 5000
    res = 'h'
    
    for s in lst:
        if s['properties']['id'] == transect:
            bounds = shape(s['geometry']).bounds
    
print bounds
llx, lly, urx, ury = bounds
w, h = urx - llx, ury - lly

x_adj, y_adj = 0, 0
if h > (w*3/8):
    x_adj = ((8*h/3.) - w) / 2.  # Aspect is hard-coded in uberplot
else:                            # TODO Fix that!
    y_adj = ((3*w/8.) - h) / 2.

print x_adj, y_adj
ll = transform(project, Point(llx-pad-x_adj, lly-pad-y_adj))
ur = transform(project, Point(urx+pad+x_adj, ury+pad+y_adj))
mid = Point(ll.x + 0.5*(ur.x-ll.x), ll.y + 0.5*(ur.y - ll.y))

# Generate Basemap object.
m = Basemap(projection='tmerc',
            lon_0=mid.x, lat_0=mid.y,
            resolution=res,  # c, l, i, h, f
            llcrnrlon=ll.x, llcrnrlat=ll.y,
            urcrnrlon=ur.x, urcrnrlat=ur.y)

# Step over shapes in collection.
for s in lst:
    line = shape(s['geometry'])
    line_t = transform(project, line)
    if s['properties']['id'] == transect:
        col = 'r'
    else:
        col = 'b'
    plot_element(m, line_t, colour=col)
    
# Finish drawing the basemap.
draw_basemap(m)
        
plt.show()


(439874.3860545542, 4992945.077960907, 444288.25335393683, 5020142.28970269)
34056.0153394 0

In [18]:
transect = 5

In [19]:
fig = plt.figure(figsize=(10,8))
ax = fig.add_subplot(111)

pad = 20000  # Interior padding in m
res = 'i'

with fiona.open(fname) as c:
    bounds = c.bounds
    lst = list(c)
 
if transect:
    pad = 5000
    res = 'h'
    
    for s in lst:
        if s['properties']['id'] == transect:
            bounds = shape(s['geometry']).bounds
    
print bounds
llx, lly, urx, ury = bounds
w, h = urx - llx, ury - lly

print w, h

x_adj, y_adj = 0, 0
if h > (w*3/8):
    print "adjusting x"
    x_adj = ((8*h/3.) - w) / 2.  # Aspect is hard-coded in uberplot
else:                            # TODO Fix that!
    print "adjusting y"
    y_adj = ((3*w/8.) - h) / 2.

print x_adj, y_adj
ll = transform(project, Point(llx-pad-x_adj, lly-pad-y_adj))
ur = transform(project, Point(urx+pad+x_adj, ury+pad+y_adj))
mid = Point(ll.x + 0.5*(ur.x-ll.x), ll.y + 0.5*(ur.y - ll.y))

# Generate Basemap object.
m = Basemap(projection='tmerc',
            lon_0=mid.x, lat_0=mid.y,
            resolution=res,  # c, l, i, h, f
            llcrnrlon=ll.x, llcrnrlat=ll.y,
            urcrnrlon=ur.x, urcrnrlat=ur.y)

# Step over shapes in collection.
for s in lst:
    line = shape(s['geometry'])
    line_t = transform(project, line)
    if s['properties']['id'] == transect:
        col = 'r'
    else:
        col = 'b'
    plot_element(m, line_t, colour=col)
    
# Finish drawing the basemap.
draw_basemap(m)
        
plt.show()


(374927.2425956799, 5058639.008075397, 491594.343084589, 5069245.1081198435)
116667.100489 10606.1000444
adjusting y
0 16572.0313194

In [17]:
with fiona.open(fname) as c:
    bounds = c.bounds
    lst = [shape(l['geometry']) for l in c]
lst[2]


Out[17]:

In [19]:
basins = os.path.join(env['HOME'], 'Dropbox/geotransect_test/data/basins/basins.shp')
with fiona.open(basins) as f:
    print "bounds:", f.bounds
    print f.crs['init']
    print "properties:"
    for i in f.items():
        print i[1]['properties']


bounds: (-7056561.932813847, 4974553.613141321, 535772.2601273971, 5648353.284436868)
epsg:26920
properties:
OrderedDict([(u'name', u'Cumberland')])
OrderedDict([(u'name', u'Minas')])
OrderedDict([(u'name', u'Windsor-Kennetcook')])

QGIS

We will try using the QGIS Composer's interface.


In [6]:
from qgis.core import *
import os

In [7]:
QgsApplication.setPrefixPath("/Applications/QGIS.app/Contents/MacOS")
QgsApplication.initQgis()

In [8]:
env = %env

In [9]:
print "Loading",
data_source = os.path.join(env['HOME'], 'Dropbox/geotransect_test/data/seismic/matt/seismic_lines.shp')
print data_source, os.path.isfile(data_source)

layer_name = "seismic"
provider_name = "ogr"

layer = QgsVectorLayer(data_source, layer_name, provider_name)
if layer.isValid():
    print "Success!"
else: 
    print "Layer failed to load!"


Loading /Users/matt/Dropbox/geotransect_test/data/seismic/matt/seismic_lines.shp True
Layer failed to load!

If you see Layer failed to load! then there's not much point in carrying on.



In [10]:
layer.id()


Out[10]:
u'seismic20150302161207128'

In [13]:
from PyQt4.QtGui import QImage, QPainter
from PyQt4.QtCore import QSize

In [16]:
# Create image
img = QImage(QSize(800, 600), QImage.Format_ARGB32_Premultiplied)

In [17]:
# Create painter
p = QPainter(img)
#p.begin(img)
p.setRenderHint(QPainter.Antialiasing)

render = QgsMapRenderer()

In [18]:
lst = []  # Layer set

In [20]:
QgsMapLayerRegistry.instance().addMapLayer(layer)
lst.append(layer.id())
render.setLayerSet(lst)

In [ ]:
comp = QgsComposition(render)
comp.setPlotStyle(QgsComposition.Print)
composerMap = QgsComposerMap(comp, 5,5,200,200)
comp.addItem(composerMap)

printer = QPrinter()
printer.setOutputFormat(QPrinter.PdfFormat)
printer.setOutputFileName("out.pdf")
printer.setPaperSize(QSizeF(comp.paperWidth(), comp.paperHeight()),    QPrinter.Millimeter)
printer.setFullPage(True)
printer.setColorMode(QPrinter.Color)
printer.setResolution(comp.printResolution())

pdfPainter = QPainter(printer)
paperRectMM = printer.pageRect(QPrinter.Millimeter)
paperRectPixel = printer.pageRect(QPrinter.DevicePixel)
comp.render(pdfPainter, paperRectPixel, paperRectMM)
pdfPainter.end()
app.exitQgis()

In [21]:
# set extent
rect = QgsRectangle(render.fullExtent())
rect.scale(1.1)
render.setExtent(rect)

# set output size
render.setOutputSize(img.size(), img.logicalDpiX())

# do the rendering
render.render(p)

p.end()

# save image
img.save("render.png","png")


Out[21]:
True

In [66]:
ls -l render.png


-rw-r--r--@ 1 matt  staff  1978  2 Mar 14:41 render.png

The following example is from GIS StackExchange. It doesn't work either. In fact, it often crashes Python.


In [1]:
# Uncomment these to run
# from qgis.core import *
# from qgis.gui import *
# from PyQt4.QtCore import *
# from PyQt4.QtGui import *
# from PyQt4.QtXml import *

QgsApplication.setPrefixPath("/usr", True)
QgsApplication.initQgis()
app = QgsApplication([], True)

fh = open("eg.csv","w")
fh.write("""
x,y,name
153.0278, -27.4679, Brisbane
144.2500, -23.4500, Longreach
145.7753, -16.9256, Cairns
""")
fh.close()

uri = "eg.csv?delimiter=%s&xField=%s&yField=%s" % (",", "x", "y")
layer = QgsVectorLayer(uri, "eglayer", "delimitedtext")
print "layer isValid", layer.isValid()
layerset = []
QgsMapLayerRegistry.instance().addMapLayer(layer)
layerset.append(layer.id())

myMapRenderer = QgsMapRenderer()
myMapRenderer.setLayerSet(layerset)
mapRectangle = QgsRectangle(140,-28,155,-15)
myMapRenderer.setExtent(mapRectangle)

comp = QgsComposition(myMapRenderer)
comp.setPlotStyle(QgsComposition.Print)
composerMap = QgsComposerMap(comp, 5,5,200,200)
comp.addItem(composerMap)
printer = QPrinter()
printer.setOutputFormat(QPrinter.PdfFormat)
printer.setOutputFileName("out.pdf")
printer.setPaperSize(QSizeF(comp.paperWidth(), comp.paperHeight()), QPrinter.Millimeter)
printer.setFullPage(True)
printer.setColorMode(QPrinter.Color)
printer.setResolution(comp.printResolution())

pdfPainter = QPainter(printer)
paperRectMM = printer.pageRect(QPrinter.Millimeter)
paperRectPixel = printer.pageRect(QPrinter.DevicePixel)
comp.render(pdfPainter, paperRectPixel, paperRectMM)
pdfPainter.end()
app.exitQgis()


---------------------------------------------------------------------------
NameError                                 Traceback (most recent call last)
<ipython-input-1-beda4beafa87> in <module>()
      6 # from PyQt4.QtXml import *
      7 
----> 8 QgsApplication.setPrefixPath("/usr", True)
      9 QgsApplication.initQgis()
     10 app = QgsApplication([], True)

NameError: name 'QgsApplication' is not defined

In [1]:


In [ ]: