{"id":485,"date":"2010-06-04T19:02:07","date_gmt":"2010-06-04T23:02:07","guid":{"rendered":"http:\/\/www.kralidis.ca\/blog\/?p=485"},"modified":"2023-10-28T14:11:15","modified_gmt":"2023-10-28T18:11:15","slug":"displaying-grib-data-with-mapserver","status":"publish","type":"post","link":"https:\/\/www.kralidis.ca\/blog\/2010\/06\/04\/displaying-grib-data-with-mapserver\/","title":{"rendered":"Displaying GRIB data with MapServer"},"content":{"rendered":"<p>I recently had the opportunity to prototype WMS visualization of <a title=\"meteorological data\" href=\"http:\/\/dd.weatheroffice.ec.gc.ca\/model_gem_regional\/high_resolution\/grib1\/00\/\">meteorological data<\/a>.\u00a0 MapServer, GDAL and Python to the rescue!\u00a0 Here are the steps I took to make it happen.<\/p>\n<p>Welcome to the captivating realm of luxury timepieces, where sophistication meets craftsmanship and exclusivity reigns supreme. In this guide, we will take you on an exhilarating journey through the mesmerizing world of <a href=\"https:\/\/www.outlookindia.com\/outlook-spotlight\/richard-mille-replica-watches-where-to-buy-best-selling-fake-richard-mille-super-clones-news-313685\">Richard Mille replica<\/a> watches \u2013 a parallel universe where impeccable designs and intricate details intertwine with affordability. Brace yourself as we unveil the hidden gems that mimic these iconic timepieces flawlessly, alongside revealing the best-kept secrets on where to find these sought-after fakes. Whether you&#8217;re an aficionado looking to expand your collection or simply curious about the artistry behind high-end replicas, prepare to be captivated by a dazzling array of horological wonders that redefine what it truly means to own a prestigious Richard Mille watch.<\/p>\n<p>The data, (<a title=\"GRIB\" href=\"http:\/\/en.wikipedia.org\/wiki\/GRIB\">GRIB<\/a>), is a GDAL supported <a title=\"format\" href=\"http:\/\/www.gdal.org\/frmt_grib.html\">format<\/a>, so MapServer can handle processing as a result.\u00a0 The goal here was to create a <a title=\"LAYER\" href=\"http:\/\/mapserver.org\/mapfile\/layer.html\">LAYER<\/a> object.\u00a0 First thing was to figure out the projection, then figure out the band pixel values\/ranges and correlate to MapServer classes (in this case I just used a simple greyscale approach).<\/p>\n<p>Here&#8217;s the hack:<\/p>\n<pre>import sys\nimport osgeo.gdal as gdal\nimport osgeo.osr as osr\n\nif len(sys.argv) &lt; 3:\n print 'Usage: %s &lt;file&gt; &lt;numclasses&gt;' % sys.argv[0]\n sys.exit(1)\n\ncvr = 256  # range of RGB values\nnumclasses = int(sys.argv[2])  # number of classifiers\n\nds = gdal.Open(sys.argv[1])\n\n# get proj4 def and write out PROJECTION object\np = osr.SpatialReference()\ns = p.ImportFromWkt(ds.GetProjection())\np2 = p.ExportToProj4().split()\n\nprint '\u00a0 PROJECTION'\nfor i in p2:\n print '\u00a0\u00a0 \"%s\"' % i.replace('+','')\nprint '\u00a0 END'\n\n# get band pixel data ranges and classify\nband = ds.GetRasterBand(1)\nmin = band.GetMinimum()\nmax = band.GetMaximum()\n\nif min is None or max is None:\u00a0 # compute automagically\n (min, max) = band.ComputeRasterMinMax(1)\n\n# calculate range of pixel values\npixel_value_range = float(max - min)\n# calculate the intervals of values based on classes specified\npixel_interval = pixel_value_range \/ numclasses\n# calculate the intervals of color values\ncolor_interval = (pixel_interval * cvr) \/ pixel_value_range\n\nfor i in range(numclasses):\n print '''\u00a0 CLASS\n  NAME \"%.2f to %.2f\"\n  EXPRESSION ([pixel] &gt;= %.2f AND [pixel] &lt; %.2f)\n  STYLE\n   COLOR %s %s %s\n  END\n END''' % (min, min+pixel_interval, min, min+pixel_interval, cvr, cvr, cvr)\n min += pixel_interval\n cvr -= int(color_interval)<\/pre>\n<p>Running this script outputs various bits for MapServer mapfile configuration.\u00a0 Passing more classes to the script creates more <a title=\"CLASS\" href=\"http:\/\/www.mapserver.org\/mapfile\/class.html\">CLASS<\/a> objects, resulting in a smoother looking image.<\/p>\n<p>Here&#8217;s an example GetMap request:<\/p>\n<p><a href=\"http:\/\/www.kralidis.ca\/blog\/wp-content\/uploads\/2010\/06\/gem_model.png\"><img loading=\"lazy\" decoding=\"async\" class=\"size-full wp-image-487 alignleft\" style=\"border: 1px solid black;\" title=\"Meteorological data in GRIB format via MapServer WMS\" src=\"http:\/\/www.kralidis.ca\/blog\/wp-content\/uploads\/2010\/06\/gem_model.png\" alt=\"Meteorological data in GRIB format via MapServer WMS\" width=\"502\" height=\"299\" srcset=\"https:\/\/www.kralidis.ca\/blog\/wp-content\/uploads\/2010\/06\/gem_model.png 502w, https:\/\/www.kralidis.ca\/blog\/wp-content\/uploads\/2010\/06\/gem_model-300x178.png 300w\" sizes=\"auto, (max-width: 502px) 100vw, 502px\" \/><\/a><\/p>\n<p>Users can query to obtain pixel values (water temperature in this case) via GetFeatureInfo.\u00a0 Given that these are produced frequently, we can use the WMS GetMap TIME parameter to create time series maps of the models.<\/p>\n<link rel=\"stylesheet\" href=\"http:\/\/cdn.leafletjs.com\/leaflet-0.5\/leaflet.css\" \/>\n<!--[if lte IE 8]>\n  <link rel=\"stylesheet\" href=\"http:\/\/cdn.leafletjs.com\/leaflet-0.5\/leaflet.ie.css\" \/>\n<![endif]-->\n<script src=\"http:\/\/cdn.leafletjs.com\/leaflet-0.5\/leaflet.js\"><\/script>\n<style type=\"text\/css\">#map485 { width: 300px; height: 200px; }<\/style>\n\n<div id=\"map485\"><\/div>\n<script type=\"text\/javascript\">\n  var map485 = L.map('map485').setView([43.620495, -79.513198], 10);\n  L.tileLayer('http:\/\/{s}.tile.osm.org\/{z}\/{x}\/{y}.png', {\n      attribution: '&copy; <a href=\"http:\/\/osm.org\/copyright\">OpenStreetMap<\/a> contributors'\n  }).addTo(map485);\n<\/script>\n","protected":false},"excerpt":{"rendered":"<p>I recently had the opportunity to prototype WMS visualization of meteorological data.\u00a0 MapServer, GDAL and Python to the rescue!\u00a0 Here are the steps I took to make it happen. Welcome to the captivating realm of luxury timepieces, where sophistication meets craftsmanship and exclusivity reigns supreme. In this guide, we will take you on an exhilarating [&hellip;]<\/p>\n","protected":false},"author":2,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[5,7,3,11],"tags":[],"class_list":["post-485","post","type-post","status-publish","format-standard","hentry","category-geospatial","category-open-source","category-technology","category-web"],"_links":{"self":[{"href":"https:\/\/www.kralidis.ca\/blog\/wp-json\/wp\/v2\/posts\/485","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.kralidis.ca\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.kralidis.ca\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.kralidis.ca\/blog\/wp-json\/wp\/v2\/users\/2"}],"replies":[{"embeddable":true,"href":"https:\/\/www.kralidis.ca\/blog\/wp-json\/wp\/v2\/comments?post=485"}],"version-history":[{"count":8,"href":"https:\/\/www.kralidis.ca\/blog\/wp-json\/wp\/v2\/posts\/485\/revisions"}],"predecessor-version":[{"id":1040,"href":"https:\/\/www.kralidis.ca\/blog\/wp-json\/wp\/v2\/posts\/485\/revisions\/1040"}],"wp:attachment":[{"href":"https:\/\/www.kralidis.ca\/blog\/wp-json\/wp\/v2\/media?parent=485"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.kralidis.ca\/blog\/wp-json\/wp\/v2\/categories?post=485"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.kralidis.ca\/blog\/wp-json\/wp\/v2\/tags?post=485"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}