[GRASS-SVN] r65935 - in grass-addons/grass7/imagery: . i.histo.match i.nightlights.intercalibration i.segment.hierarchical

svn_grass at osgeo.org svn_grass at osgeo.org
Fri Aug 14 13:40:21 PDT 2015


Author: nikosa
Date: 2015-08-14 13:40:21 -0700 (Fri, 14 Aug 2015)
New Revision: 65935

Added:
   grass-addons/grass7/imagery/i.nightlights.intercalibration/
   grass-addons/grass7/imagery/i.nightlights.intercalibration/Makefile
   grass-addons/grass7/imagery/i.nightlights.intercalibration/i.nightlights.intercalibration.html
   grass-addons/grass7/imagery/i.nightlights.intercalibration/i.nightlights.intercalibration.py
   grass-addons/grass7/imagery/i.nightlights.intercalibration/intercalibration_coefficients.py
   grass-addons/grass7/imagery/i.nightlights.intercalibration/intercalibration_equations.py
   grass-addons/grass7/imagery/i.nightlights.intercalibration/intercalibration_models.py
   grass-addons/grass7/imagery/i.nightlights.intercalibration/test_intercalibration_models.py
Modified:
   grass-addons/grass7/imagery/i.histo.match/i.histo.match.py
   grass-addons/grass7/imagery/i.segment.hierarchical/Makefile
Log:
?\206?\153nter-satellite calibration on DMSP-OLS Nighttime Lights Time Series

Modified: grass-addons/grass7/imagery/i.histo.match/i.histo.match.py
===================================================================
--- grass-addons/grass7/imagery/i.histo.match/i.histo.match.py	2015-08-14 16:14:42 UTC (rev 65934)
+++ grass-addons/grass7/imagery/i.histo.match/i.histo.match.py	2015-08-14 20:40:21 UTC (rev 65935)
@@ -1,32 +1,35 @@
 #!/usr/bin/env python
 # -*- coding: utf-8 -*-
-############################################################################
-#
-# MODULE:       i.histo.match
-# AUTHOR(S):    Luca Delucchi, Fondazione E. Mach (Italy)
-#               original PERL code was developed by:
-#               Laura Zampa (2004) student of Dipartimento di Informatica e 
-#               Telecomunicazioni, Facoltà di Ingegneria,
-#                University of Trento  and ITC-irst, Trento (Italy)
-#
-# PURPOSE:      Calculate histogram matching of several images
-# COPYRIGHT:    (C) 2011 by the GRASS Development Team
-#
-#               This program is free software under the GNU General
-#               Public License (>=v2). Read the file COPYING that
-#               comes with GRASS for details.
-#
-# TODO: use "BEGIN TRANSACTION" etc?
-#############################################################################
+
+"""
+ MODULE:       i.histo.match
+ AUTHOR(S):    Luca Delucchi, Fondazione E. Mach (Italy)
+               original PERL code was developed by:
+               Laura Zampa (2004) student of Dipartimento di Informatica e 
+               Telecomunicazioni, Facoltà di Ingegneria,
+                University of Trento  and ITC-irst, Trento (Italy)
+
+ PURPOSE:      Calculate histogram matching of several images
+ COPYRIGHT:    (C) 2011 by the GRASS Development Team
+
+               This program is free software under the GNU General
+               Public License (>=v2). Read the file COPYING that
+               comes with GRASS for details.
+
+ TODO: use "BEGIN TRANSACTION" etc?
+"""
+
 #%module
 #% description: Calculate histogram matching of several images.
 #% keyword: imagery
 #% keyword: histogram matching
 #%end
+
 #%option G_OPT_R_INPUTS
 #% description: Name of raster maps to analize
 #% required: yes
 #%end
+
 #%option
 #% key: suffix
 #% type: string
@@ -35,14 +38,17 @@
 #% required: no
 #% answer: match
 #%end
+
 #%option G_OPT_R_OUTPUT
 #% description: Name for mosaic output map
 #% required: no
 #%end
+
 #%option G_OPT_DB_DATABASE
 #% required : no
 #% answer: $GISDBASE/$LOCATION_NAME/$MAPSET/histo.db
 #%end
+
 #%option
 #% key: max
 #% type: integer

Added: grass-addons/grass7/imagery/i.nightlights.intercalibration/Makefile
===================================================================
--- grass-addons/grass7/imagery/i.nightlights.intercalibration/Makefile	                        (rev 0)
+++ grass-addons/grass7/imagery/i.nightlights.intercalibration/Makefile	2015-08-14 20:40:21 UTC (rev 65935)
@@ -0,0 +1,10 @@
+MODULE_TOPDIR = ../..
+
+PGM = i.nightlights.intercalibration
+
+ETCFILES = intercalibration_equations intercalibration_coefficients intercalibration_models 
+
+include $(MODULE_TOPDIR)/include/Make/Script.make
+include $(MODULE_TOPDIR)/include/Make/Python.make
+
+default: script

Added: grass-addons/grass7/imagery/i.nightlights.intercalibration/i.nightlights.intercalibration.html
===================================================================
--- grass-addons/grass7/imagery/i.nightlights.intercalibration/i.nightlights.intercalibration.html	                        (rev 0)
+++ grass-addons/grass7/imagery/i.nightlights.intercalibration/i.nightlights.intercalibration.html	2015-08-14 20:40:21 UTC (rev 65935)
@@ -0,0 +1,105 @@
+<h2 id="description">DESCRIPTION</h2>
+<p><em>i.nightlights.intercalibration</em> is a GRASS-GIS module performing inter-satellite calibration on DMSP-OLS nighttime lights time series. Based on "well known" emprirical regression models, it calibrates average visible band Digital Number values.</p>
+<p><strong>Note</strong>, the module is still under testing. Eventually minor, but important, changes might be applied to the intercalibration process.</p>
+<h3 id="overview">Overview</h3>
+<pre><code>   +----------------------------------------------------------------------+
+   |                                                                      |
+   |          +-----------------+                                         |
+   | DN  +--> |Calibration Model| +-->  Calibrated DN                     |
+   |          +---^-------------+            ^                            |
+   |              |                          |                            |
+   |              |             +--Evaluation+Methods-------------------+ |
+   |              |             |                                       | |
+   |              |             | ?                                     | |
+   |              |             |                                       | |
+   |              |             +---------------------------------------+ |
+   |              |                                                       |
+   | +--Regression+Models-----------------------------------------------+ |
+   | |                                                                  | |
+   | |  Elvidge, 2009/2014:  DNc = C0 + C1×DN + C2×DNv2                 | |
+   | |                                                                  | |
+   | |  Liu, 2012:  based on Elvidge's model + optimal threshold method | |
+   | |                                                                  | |
+   | |  Wu, 2014:            DNc + 1 = a×(DN + 1)^b                     | |
+   | |                                                                  | |
+   | |  Others?                                                         | |
+   | |                                                                  | |
+   | +------------------------------------------------------------------+ |
+   |                                                                      |
+   +----------------------------------------------------------------------+</code></pre>
+<h3 id="details">Details</h3>
+<p>From a review paper:</p>
+<blockquote>
+<p>"Several methods were proposed to overcome the lack of inter-satellite calibration. These include the invariant region and the quadratic regression method proposed by Elvidge et al. [23], the second-order regression and optimal threshold method proposed by Liu et al. [24], and a power-law regression method proposed by Wu et al. [25]. Although studies based on these calibration methods showed performance improvement after the rectification [24,25], the assumption that the nighttime light remains stableover time in a particular area requires a careful choice of the invariant region manually." [Huang 2014]</p>
+</blockquote>
+<p>References above are: [23]: [Elvidge 2009] [24]: [Liu 2012], [25]: [Wu 2013]</p>
+<h2 id="notes">NOTES</h2>
+<p>?</p>
+<h2 id="examples">EXAMPLES</h2>
+<p>Given all maps are imported in GRASS' data base, which are:</p>
+<div class="code">
+<p>g.list rast pattern=F* sep=,</p>
+<p>F101992,F101993,F101994,F121994,F121995,F121996,F121997,F121998,F121999,F141997, F141998,F141999,F142000,F142001,F142002,F142003,F152000,F152001,F152002,F152003, F152004,F152005,F152006,F152007,F162004,F162005,F162006,F162007,F162008, F162009,F182010,F182011,F182012</p>
+</div>
+<p>the "default" inter-calibration, based on [Elvidge 2014] can be performed as:</p>
+<div class="code">
+<p>i.nightlights.intercalibration.py image=<code>g.list rast pattern=F* sep=,</code> model=wu2013 output=l --o</p>
+</div>
+<h2 id="remarks">Remarks</h2>
+<p>The calibration models do <em>not</em> include regression coefficients for all of the yearly products. In such a case, the module will fail and inform with an error message like:</p>
+<div class="code">
+<p>i.nightlights.intercalibration image=<code>g.list rast pattern=F?????? sep=,</code> model=liu2012 --v</p>
+<p>... ValueError: The selected model does not know about this combination of Satellite + Year!</p>
+</div>
+<h3 id="example-figures">Example figures</h3>
+<div class="figure">
+<p>To add...</p>
+</div>
+<h2 id="todo">TODO</h2>
+<p>in general:</p>
+<ul>
+<li><p>Fix html manual!</p></li>
+<li><p>improve missing key handling and error reporting</p></li>
+<li><p>code deduplication</p></li>
+<li><p>test -- will it compile in other systems?</p></li>
+</ul>
+<p>in <code>i.nightlights.intercalibration.py</code>:</p>
+<ul>
+<li>use <code>*args</code> or <code>**kwargs</code> where appropriate</li>
+</ul>
+<p>in <code>calibration_models.py</code>:</p>
+<ul>
+<li><p>improve checks for missing combinations of Satellite + Year in models</p></li>
+<li><p>separate test_function from this "module"</p></li>
+</ul>
+<p>another module?</p>
+<ul>
+<li>Accuracy assessment of inter-calibrated nighttime lights time series [Wu 2013]: TLI= Σi DNi × Ci where DNi is the grey value of i-level pixels and Ci is the number of i-level pixels</li>
+</ul>
+<h2 id="references">REFERENCES</h2>
+<p>[Review paper(s)]</p>
+<ul>
+<li>Application of DMSP⁄OLS Nighttime Light Images A Meta-Analysis and a Systematic Literature Review [Huang 2014]</li>
+</ul>
+<p>[Empirical second order regression model by Elvidge, 2009 | Y = C0 + C1*X + C2*X^2 ]</p>
+<ul>
+<li><p>Estimating Land Development Time Lags in China Using DMSP⁄OLS Nighttime Light Image [Zhang 2015]</p></li>
+<li><p>National Trends in Satellite-Observed Lighting 1992–2012 [Elvidge 2014]</p></li>
+<li><p>Comparative Estimation of Urban Development in China’s Cities Using socioeconomic and DMSP⁄OLS Night Light Data [Fan 2014]</p></li>
+<li><p>The Integrated Use of DMSP-OLS Nighttime Light and MODIS Data for Monitoring Large-Scale Impervious Surface Dynamics A Case Study in the Yangtze River Delta [Shao & Liu 2014]</p></li>
+<li><p>Characterizing Spatio-Temporal Dynamics of Urbanization in China Using Time Series of DMSP⁄OLS Night Light Data [Xu 2014]</p></li>
+<li><p>Night on Earth Mapping decadal changes of anthropogenic night light in Asia [Small & Elvidge 2013]</p></li>
+</ul>
+<p>[Second order regression model & optimal threshold method by Liu, 2012]</p>
+<ul>
+<li><p>Modeling In-Use Steel Stock in China's Buildings and Civil Engineering Infrastructure Using Time-Series of DMSP⁄OLS Nighttime Lights [Liang 2014]</p></li>
+<li><p>Dynamics of Urbanization Levels in China from 1992 to 2012 Perspective from DMSP⁄OLS Nighttime Light Data] [?]</p></li>
+</ul>
+<p>[Non-linear, power regression model]</p>
+<ul>
+<li>Intercalibration of DMSP-OLS night-time light data by the invariant region method [Wu 2013]</li>
+</ul>
+<h2 id="see-also">SEE ALSO</h2>
+<p>?</p>
+<h2 id="authors">AUTHORS</h2>
+<p>Nikos Alexandris</p>

Added: grass-addons/grass7/imagery/i.nightlights.intercalibration/i.nightlights.intercalibration.py
===================================================================
--- grass-addons/grass7/imagery/i.nightlights.intercalibration/i.nightlights.intercalibration.py	                        (rev 0)
+++ grass-addons/grass7/imagery/i.nightlights.intercalibration/i.nightlights.intercalibration.py	2015-08-14 20:40:21 UTC (rev 65935)
@@ -0,0 +1,525 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+
+"""
+MODULE:         i.nightlights.intercalibration
+
+AUTHOR:         Nikos Alexandris <nik at nikosalexandris.net> Trikala, March 2015
+
+PURPOSE:        Performing inter-satellite calibration on DMSP-OLS Nighttime 
+                Lights Time Series (cleaned up average visible band) based on
+                regression models proposed by Elvidge (2009/2014), Liu (2012)
+                and Wu (2013).
+                
+                - Elvidge (2009)
+                
+                  Empirical second order polynomial regression model:
+                  DN adj. = C0 + C1 × DN + C2 × DN^2
+                  
+                  where:
+                  
+                  - DN adj.:    Adjusted Digital Numbers
+                  - DN          Raw Digital Number
+                  - C0:         First polynomial constant
+                  - C1:         Second polynomial constant
+                  - C2:         Third polynomial constant
+
+                
+                - Elvidge (2014)
+                
+                  Same model as in 2009, improved coefficients
+
+
+                - Liu (2012)
+                
+                  Empirical second order polynomial regression model & optimal 
+                  threshold method:  DNc = a * DN^2 + b * DN + c
+                  
+                  where:
+
+                  - DNc:        Calibrated Digital Number
+                  - DN:         Raw Digital Number
+                  - a:          First polynomial constant
+                  - b:          Second polynomial constant
+                  - c:          Third polynomial constant
+
+
+                - Wu (2013)
+                
+                  Power calibration model:  DNc + 1 = a * (DN + 1)^b 
+
+                  where:
+
+                  - DNc:        Calibrated Digital Number
+                  - DN:         Raw Digital Number
+                  - a:          Constant
+                  - b:          Power
+
+               Overview
+               
+   +----------------------------------------------------------------------+
+   |                                                                      |
+   |          +-----------------+                                         |
+   | DN  +--> |Calibration Model| +-->  Calibrated DN                     |
+   |          +---^-------------+            ^                            |
+   |              |                          |                            |
+   |              |             +--Evaluation+Methods-------------------+ |
+   |              |             |                                       | |
+   |              |             | ? Not Implemented                     | |
+   |              |             |                                       | |
+   |              |             +---------------------------------------+ |
+   |              |                                                       |
+   | +--Regression+Models-----------------------------------------------+ |
+   | |                                                                  | |
+   | |  Elvidge, 2009/2014:  DNc = C0 + C1×DN + C2×DNv2                 | |
+   | |                                                                  | |
+   | |  Liu, 2012:  based on Elvidge's model + optimal threshold method | |
+   | |                                                                  | |
+   | |  Wu, 2014:            DNc + 1 = a×(DN + 1)^b                     | |
+   | |                                                                  | |
+   | |  Others?                                                         | |
+   | |                                                                  | |
+   | +------------------------------------------------------------------+ |
+   |                                               http://asciiflow.com   |
+   +----------------------------------------------------------------------+
+
+   
+               Sources
+               
+               - <http://ngdc.noaa.gov/eog/dmsp.html>
+               
+               - <http://ngdc.noaa.gov/eog/dmsp/downloadV4composites.html>
+               
+               - Metadata on DMSP-OLS:
+               <https://catalog.data.gov/harvest/object/e84ef28f-7935-4ca2-b9c7-7a77cb156c4c/html>
+
+               - From <http://ngdc.noaa.gov/eog/gcv4_readme.txt> on the data
+               this module is meant to process:
+               
+                 F1?YYYY_v4b_stable_lights.avg_vis.tif: The cleaned up avg_vis 
+                 contains the lights from cities, towns, and other sites with 
+                 persistent lighting, including gas flares. Ephemeral events, 
+                 such as fires have been discarded. Then the background noise 
+                 was identified and replaced with values of zero. Data values 
+                 range from 1-63. Areas with zero cloud-free observations are 
+                 represented by the value 255.
+
+
+ COPYRIGHT:    (C) 2014 by the GRASS Development Team
+
+               This program is free software under the GNU General Public
+               License (>=v2). Read the file COPYING that comes with GRASS
+               for details.
+"""
+
+#%Module
+#%  description: Performs inter-satellite calibration on DMSP-OLS Nighttime Lights Time Series
+#%  keywords: imagery
+#%  keywords: inter-satellite
+#%  keywords: calibration
+#%  keywords: nighttime lights
+#%  keywords: time series
+#%  keywords: DMSP-OLS
+#%End
+
+#%flag
+#%  key: i
+#%  description: Print out calibration equations
+#%end
+
+#% flag
+#%  key: e
+#%  description: Evaluation based on the Normalised Difference Index
+#% end
+
+#% flag
+#%  key: g
+#%  description: Print in shell script style (currently only NDI via -e)
+#% end
+
+#%flag
+#%  key: k
+#%  description: Keep current computational region settings
+#%end
+
+#%flag
+#% key: z
+#%  description: Exclude zero values from the analysis (retain zero cells in output)
+#%end
+
+#%flag
+#% key: n
+#%  description: Exclude zero values from the analysis (set zero cells to NULL in output)
+#%end
+
+#%flag
+#% key: t
+#%  description: Do not try to transfer timestamps (for input without timestamp)
+#%end
+
+#% rules
+#%  exclusive: -z,-n
+#% end
+
+#%option G_OPT_R_INPUTS
+#% key: image
+#% key_desc: name
+#% description: Clean average DMSP-OLS visible band digital number image(s)
+#% required : yes
+#% multiple : yes
+#%end
+
+#%option G_OPT_R_BASENAME_OUTPUT
+#% key: suffix
+#% key_desc: suffix
+#% type: string
+#% label: output file(s) suffix
+#% description: Suffix for calibrated average digital number output image(s)
+#% required: yes
+#% answer: c
+#%end
+
+#%option
+#% key: model
+#% key_desc: author
+#% type: string
+#% label: Calibration model
+#% description: Inter-satellite calibration model for average DMSP-OLS nighttime lights time series
+#% descriptions: Elvidge (2009 or 2014), Liu 2012, Wu 2013
+#% options: elvidge2009,elvidge2014,liu2012,wu2013
+#% required: yes
+#% answer: elvidge2014
+#% guisection: Calibration Model
+#% multiple : no
+#%end
+
+
+# required librairies -------------------------------------------------------
+import os
+import sys
+sys.path.insert(1, os.path.join(os.path.dirname(sys.path[0]),
+                                'etc', 'i.nightlights.intercalibration'))
+
+import atexit
+import grass.script as grass
+from grass.exceptions import CalledModuleError
+from grass.pygrass.modules.shortcuts import general as g
+#from grass.pygrass.modules.shortcuts import raster as r
+#from grass.pygrass.raster.abstract import Info
+
+from intercalibration_models import Elvidge, Liu2012, Wu2013
+
+
+# any constants? -------------------------------------------------------------
+MODELS = {'elvidge': Elvidge, 'liu2012': Liu2012, 'wu2013' : Wu2013}
+
+# helper functions ----------------------------------------------------------
+def cleanup():
+    """
+    Clean up temporary maps
+    """
+    grass.run_command('g.remove', flags='f', type="rast",
+                      pattern='tmp.{pid}*'.format(pid=os.getpid()), quiet=True)
+                      
+def run(cmd, **kwargs):
+    """
+    Pass required arguments to grass commands (?)
+    """
+    grass.run_command(cmd, quiet=True, **kwargs)
+
+#def model_description(model_class):
+#    """
+#    Model's generic function
+#    """
+#    return model.equation
+
+def retrieve_model_parameters(model_class, *args, **kwargs):
+    """
+    Run the user-requested calibration model and return model class objects
+    from which the calibration coefficients, the associated RMSE, and the
+    mapcalc formula of interest will be retrieved.
+    """
+    model = model_class(*args, **kwargs)
+    citation = model.citation
+    coefficients = model.coefficients
+    coefficients_r2 = model.report_r2()
+    mapcalc_formula = model.mapcalc
+    return citation, coefficients, coefficients_r2, mapcalc_formula
+
+def total_light_index(ntl_image):
+    """
+    Evaluation index (TLI) which represents the sum of grey values in an area.
+    """
+    univar = grass.parse_command("r.univar",
+                                 map=ntl_image,
+                                 flags='g',
+                                 parse=(grass.parse_key_val,
+                                {'sep': '='}))
+    return float(univar['sum'])
+
+def normalised_difference_index(tli_one, tli_two):
+    """
+    Normalised Difference Index based on the total light indices of two
+    satellite images in a certain year.
+    """
+    return abs(tli_one - tli_two) / (tli_one + tli_two)
+
+def main():
+    """
+    Main program: get nameds for input, output suffix, options and flags
+    """
+    input_list = options['image'].split(',')
+    outputsuffix = options['suffix']
+
+    # Select model based on author    
+    author_year = options['model']
+    if 'elvidge' in author_year:
+        version = author_year[7:]
+        author_year = 'elvidge'    
+    else:
+        version = None
+    Model = MODELS[author_year]
+    # ----------------------------
+
+    # flags    
+    info = flags['i']
+    keep_region = flags['k']
+    timestamps = not(flags['t'])
+    zero = flags['z']
+    null = flags['n']  ### either zero or null, not both --- FixMe! ###    
+    evaluation = flags['e']    
+    shell = flags['g']     
+
+    # ----------------------------------------------------------------------
+    # Get working...
+    # ----------------------------------------------------------------------
+    msg = ("|i Inter-satellite calibration of DMSP-OLS Nighttime Stable "
+        "Lights")
+    g.message(msg)
+
+    # -----------------------------------------------------------------------
+    # Temporary Region and Files
+    # -----------------------------------------------------------------------
+
+    if not keep_region:
+        grass.use_temp_region()  # to safely modify the region
+    tmpfile = grass.tempfile()  # Temporary file - replace with os.getpid?
+    tmp = "tmp." + grass.basename(tmpfile)  # use its basename
+
+    # -----------------------------------------------------------------------
+    # Loop over list of input images
+    # -----------------------------------------------------------------------            
+
+    for image in input_list:
+        
+        satellite = image[0:3]
+        year = image[3:8]
+
+        # -------------------------------------------------------------------
+        # Match region to input image if... ?
+        # -------------------------------------------------------------------
+    
+        if not keep_region:
+            run('g.region', rast=image)   # ## FixMe?
+            msg = "\n|! Matching region extent to map {name}"
+            msg = msg.format(name=image)
+            g.message(msg)
+    
+        elif keep_region:
+            grass.warning(_('Operating on current region'))
+                
+        # -------------------------------------------------------------------
+        # Retrieve coefficients
+        # -------------------------------------------------------------------
+    
+        msg = "\n|> Calibrating average visible Digital Number values "
+        g.message(msg)
+
+        # if "version" == True use Elvidge, else use Liu2012 or Wu2013
+        args = (satellite, year, version) if version else (satellite, year)
+        model_parameters = retrieve_model_parameters(Model, *args)
+
+#        # print model's generic equation?
+#        if info:
+#            print this
+#            print that
+
+
+        # split parameters in usable variables
+        citation, coefficients, r2, mapcalc_formula = model_parameters    
+            
+        msg = "   Regression coefficients: " + str(coefficients) + ' | '
+        msg += r2
+        g.message(msg)
+    
+        # Temporary Map
+        tmp_cdn = "{prefix}.Calibrated".format(prefix=tmp)
+        
+        # -------------------------------------------------------------------
+        # Formula for mapcalc
+        # -------------------------------------------------------------------
+        
+        equation = "{out} = {inputs}"        
+        calibration_formula = equation.format(out=tmp_cdn, inputs=mapcalc_formula)
+
+        # alternatives
+        if zero:
+            zcf = "{out} = if(Input == 0, 0, {formula})"
+            calibration_formula = zcf.format(out=tmp_cdn, formula=mapcalc_formula)
+            msg = "\n|i Excluding zero cells from the analysis"
+            g.message(msg)
+
+        elif null:
+            ncf = "{out} = if(Input == 0, null(), {formula})"
+            calibration_formula = ncf.format(out=tmp_cdn, formula=mapcalc_formula)
+            msg = "\n|i Setting zero cells to NULL"
+            g.message(msg)
+
+        # Compress even more? -----------------------------------------------
+#        if zero or null:
+#            zero = 0 if zero else ('null()')
+#            equation = "{out} = if(Input == 0, {zn}, {formula})"
+#            calibration_formula = equation.format(out=tmp_cdn, zero, formula=mapcalc_formula)
+        # ----------------------------------------------- Compress even more? 
+
+        # replace the "dummy" string...
+        calibration_formula = calibration_formula.replace("Input", image)
+
+        # -------------------------------------------------------------------
+        # Calibrate
+        # -------------------------------------------------------------------
+        if info:
+            print "\n|i Mapcalc formula:  ", mapcalc_formula
+
+        grass.mapcalc(calibration_formula, overwrite=True)
+    
+        # -------------------------------------------------------------------        
+        # Transfer timestamps, if any
+        # -------------------------------------------------------------------        
+
+        if timestamps:
+            
+            try:
+                datetime = grass.read_command("r.timestamp", map=image)
+                run("r.timestamp", map=tmp_cdn, date=datetime)
+
+                msg = "\n|i Timestamping: {stamp}".format(stamp=datetime)
+                g.message(msg)
+
+            except CalledModuleError:
+                    grass.fatal(_('\n|* Timestamp is missing! '
+                    'Please add one to the input map if further times series '
+                    'analysis is important. '
+                    'If you don\'t need it, you may use the -t flag.'))
+
+        else:
+            grass.warning(_('As requested, timestamp transferring not attempted.'))
+
+        # -------------------------------------------------------------------------
+        # add timestamps and register to spatio-temporal raster data set
+        # -------------------------------------------------------------------------
+
+# ToDo -- borrowed from r.sun.daily
+# - change flag for "don't timestamp", see above
+# - use '-t' for temporal, makes more sense
+# - adapt following
+
+        # temporal = flags['t']
+        # if temporal:
+        #     core.info(_("Registering created maps into temporal dataset..."))
+        #     import grass.temporal as tgis
+
+        #     def registerToTemporal(basename, suffixes, mapset, start_day, day_step,
+        #                            title, desc):
+        #         """
+        #         Register daily output maps in spatio-temporal raster data set
+        #         """
+        #         maps = ','.join([basename + suf + '@' + mapset for suf in suffixes])
+        #         tgis.open_new_stds(basename, type='strds', temporaltype='relative',
+        #                            title=title, descr=desc, semantic='sum',
+        #                            dbif=None, overwrite=grass.overwrite())
+
+        #         tgis.register_maps_in_space_time_dataset(type='rast',
+        #                                                  name=basename, maps=maps,
+        #                                                  start=start_day, end=None,
+        #                                                  unit='days',
+        #                                                  increment=day_step,
+        #                                                  dbif=None, interval=False)
+
+        # -------------------------------------------------------------------        
+        # Normalised Difference Index (NDI), if requested
+        # -------------------------------------------------------------------        
+        
+        ndi = float()
+        if evaluation:
+
+            # total light indices for input, tmp_cdn images
+            tli_image = total_light_index(image)
+            tli_tmp_cdn = total_light_index(tmp_cdn)
+
+            # build
+            ndi = normalised_difference_index(tli_image, tli_tmp_cdn)
+            
+            # verbosity
+            msg = '\n|i NDI for {dn}: {index}'.format(dn=image, index=round(ndi, 3))
+            g.message(msg)
+                
+            # report if -g
+            if shell:
+                print 'ndi={index}'.format(index=round(ndi,3))
+
+            # else, report
+            else:
+                print '\n|i Normalised Difference Index: {index}'.format(index=round(ndi,3))
+
+        # -------------------------------------------------------------------
+        # Strings for metadata
+        # -------------------------------------------------------------------
+
+        history_calibration = 'Regression model: '
+        history_calibration += mapcalc_formula
+        if ndi:
+            history_calibration += '(NDI: {ndi})'.format(ndi=ndi)
+        title_calibration = 'Calibrated DMSP-OLS Stable Lights'
+        description_calibration = ('Inter-satellite calibrated average '
+                                   'Digital Number values')
+        units_calibration = 'Digital Numbers (Calibrated)'
+
+        source1_calibration = citation
+        source2_calibration = ''
+
+        # history entry
+        run("r.support", map=tmp_cdn, title=title_calibration,
+            units=units_calibration, description=description_calibration,
+            source1=source1_calibration, source2=source2_calibration,
+            history=history_calibration)
+
+        # -------------------------------------------------------------------
+        # Add suffix to basename & rename end product
+        # -------------------------------------------------------------------
+        name = "{prefix}.{suffix}"
+        name = name.format(prefix=image.split('@')[0], suffix=outputsuffix)
+        calibrated_name = name
+        run("g.rename", rast=(tmp_cdn, calibrated_name))
+
+        # -------------------------------------------------------------------
+        # Restore region
+        # -------------------------------------------------------------------
+
+        if not keep_region:
+            grass.del_temp_region()  # restoring previous region settings
+            g.message("|! Original Region restored")
+
+
+        # -------------------------------------------------------------------
+        # Things left to do... ?
+        # -------------------------------------------------------------------
+
+        # model equations (and citation?)
+        #if info:
+            #print "\n|citation:\n  ", citation
+
+if __name__ == "__main__":
+    options, flags = grass.parser()
+    atexit.register(cleanup)
+    sys.exit(main())


Property changes on: grass-addons/grass7/imagery/i.nightlights.intercalibration/i.nightlights.intercalibration.py
___________________________________________________________________
Added: svn:executable
   + *

Added: grass-addons/grass7/imagery/i.nightlights.intercalibration/intercalibration_coefficients.py
===================================================================
--- grass-addons/grass7/imagery/i.nightlights.intercalibration/intercalibration_coefficients.py	                        (rev 0)
+++ grass-addons/grass7/imagery/i.nightlights.intercalibration/intercalibration_coefficients.py	2015-08-14 20:40:21 UTC (rev 65935)
@@ -0,0 +1,182 @@
+# -*- coding: utf-8 -*-
+"""
+ at author: nik | Created on Wed Feb 25 23:20:05 2015
+
+Regression coefficients derived from models for
+Inter-Satellite Calibration  of  DMSP-OLS Night-Time Light Time Series 
+
+Elvidge 2009, 2014:  dn_adjusted = C0 + C1 × dn + C2 × dn^2
+Liu 2012:  dn_calibrated = a * dn^2 + b * dn + c
+Wu 2013:  dn_calibrated + 1 = a * (dn + 1)^b
+"""
+
+CITATIONS = {
+    'ELVIDGE2009':
+    ('Elvidge, Christopher D., Daniel Ziskin, '
+     'Kimberly E. Baugh, Benjamin T. Tuttle, Tilottama Ghosh, Dee W. Pack, '
+     'Edward H. Erwin, and Mikhail Zhizhin. “A Fifteen Year Record of '
+     'Global Natural Gas Flaring Derived from Satellite Data.” Energies 2, '
+     'no. 2 (August 7, 2009): 595–622.'),
+    'ELVIDGE2014':
+    ('Elvidge, Christopher D., Feng-Chi Hsu, '
+     'Kimberly E. Baugh, and Tilottama Ghosh. “National Trends in '
+     'Satellite-Observed Lighting.” Global Urban Monitoring and '
+     'Assessment Through Earth Observation (2014): 97.'),
+    'LIU2012':
+    ('Liu, Zhifeng, Chunyang He, Qiaofeng Zhang, '
+     'Qingxu Huang, and Yang Yang. '
+     '"Extracting the Dynamics of Urban Expansion in China Using DMSP-OLS '
+     'Nighttime Light Data from 1992 to 2008." '
+     'Landscape and Urban Planning 106, no. 1 (May 2012): 62-72.'),
+    'WU2013':
+    ('Jiansheng Wu, Shengbin He, Jian Peng, Weifeng Li '
+     '& Xiaohong Zhong (2013). '
+     'Intercalibration of DMSP-OLS night-time light data by the invariant '
+     'region method, International Journal of Remote Sensing, 34:20, '
+     '7356-7368. DOI:10.1080/01431161.2013.820365')}
+
+COEFFICIENTS = {
+ 'ELVIDGE2009': {'F12': {'1994': (0.1651, 1.1244, -0.0018, 0.915),
+   '1995': (0.4103, 1.2116, -0.0035, 0.937),
+   '1996': (0.2228, 1.27, -0.004, 0.944),
+   '1997': (-0.0008, 1.1651, -0.0023, 0.945),
+   '1998': (0.1535, 1.0451, -0.0009, 0.956),
+   '1999': (0.0, 1.0, 0.0, 1.0)},
+  'F14': {'1997': (0.0291, 1.6568, -0.0103, 0.941),
+   '1998': (0.1831, 1.598, -0.0096, 0.972),
+   '1999': (-0.1674, 1.5116, -0.0078, 0.971),
+   '2000': (0.1061, 1.3877, -0.0059, 0.972),
+   '2001': (-0.2595, 1.3467, -0.0053, 0.963),
+   '2002': (0.4486, 1.1983, -0.0035, 0.927),
+   '2003': (-0.2768, 1.2838, -0.0044, 0.938)},
+  'F15': {'2000': (0.1029, 1.0845, -0.001, 0.97),
+   '2001': (-0.4365, 1.085, -0.0009, -0.959),
+   '2002': (-0.2173, 0.9715, 0.0008, 0.966),
+   '2003': (-0.2244, 1.5238, -0.0079, 0.936),
+   '2004': (-0.3657, 1.3772, -0.0056, 0.948),
+   '2005': (-0.6201, 1.3504, -0.0049, 0.934),
+   '2006': (-0.6005, 1.3551, -0.0049, 0.939),
+   '2007': (-0.1615, 1.396, -0.0054, 0.947),
+   '2008': (0.5031, 0.937, 0.0004, 0.92)},
+  'F16': {'2004': (-0.4436, 1.2081, -0.003, 0.95),
+   '2005': (-0.2375, 1.4249, -0.0063, 0.937),
+   '2006': (0.0287, 1.1338, -0.0013, 0.938),
+   '2007': (0.321, 0.9216, 0.0013, 0.949),
+   '2008': (-0.1203, 1.0155, -0.0001, 0.946)}},
+ 'ELVIDGE2014': {'F10': {'1992': (-2.057, 1.5903, -0.009, 0.9075),
+   '1993': (-1.0582, 1.5983, -0.0093, 0.936),
+   '1994': (-0.3458, 1.4864, -0.0079, 0.9243)},
+  'F12': {'1994': (-0.689, 1.177, -0.0025, 0.9071),
+   '1995': (-0.0515, 1.2293, -0.0038, 0.9178),
+   '1996': (-0.0959, 1.2727, -0.004, 0.9319),
+   '1997': (-0.3321, 1.1782, -0.0026, 0.9245),
+   '1998': (-0.0608, 1.0648, -0.0013, 0.9536),
+   '1999': (0.0, 1.0, 0.0, 1.0)},
+  'F14': {'1997': (-1.1323, 1.7696, -0.0122, 0.9101),
+   '1998': (-0.1917, 1.6321, -0.0101, 0.9723),
+   '1999': (-0.1557, 1.5055, -0.0078, 0.9717),
+   '2000': (1.0988, 1.3155, -0.0053, 0.9278),
+   '2001': (0.1943, 1.3219, -0.0051, 0.9448),
+   '2002': (1.0517, 1.1905, -0.0036, 0.9203),
+   '2003': (0.739, 1.2416, -0.004, 0.9432)},
+  'F15': {'2000': (0.1254, 1.0452, -0.001, 0.932),
+   '2001': (-0.7024, 1.1081, -0.0012, 0.9593),
+   '2002': (0.0491, 0.9568, 0.001, 0.9658),
+   '2003': (0.2217, 1.5122, -0.008, 0.9314),
+   '2004': (0.5751, 1.3335, -0.0051, 0.9479),
+   '2005': (0.6367, 1.2838, -0.0041, 0.9335),
+   '2006': (0.8261, 1.279, -0.0041, 0.9387),
+   '2007': (1.3606, 1.2974, -0.0045, 0.9013)},
+  'F16': {'2004': (0.2853, 1.1955, -0.0034, 0.9039),
+   '2005': (-0.0001, 1.4159, -0.0063, 0.939),
+   '2006': (0.1065, 1.1371, -0.0016, 0.9199),
+   '2007': (0.6394, 0.9114, 0.0014, 0.9511),
+   '2008': (0.5564, 0.9931, 0.0, 0.945),
+   '2009': (0.9492, 1.0683, -0.0016, 0.8918),
+   '2010': (2.343, 0.5102, 0.0065, 0.8462),
+   '2011': (1.8956, 0.7345, 0.003, 0.9095),
+   '2012': (1.875, 0.6203, 0.0052, 0.9392)},
+  'F18': {'2004': (0.2853, 1.1955, -0.0034, 0.9039),
+   '2005': (-0.0001, 1.4159, -0.0063, 0.939),
+   '2006': (0.1065, 1.1371, -0.0016, 0.9199),
+   '2007': (0.6394, 0.9114, 0.0014, 0.9511),
+   '2008': (0.5564, 0.9931, 0.0, 0.945),
+   '2009': (0.9492, 1.0683, -0.0016, 0.8918),
+   '2010': (2.343, 0.5102, 0.0065, 0.8462),
+   '2011': (1.8956, 0.7345, 0.003, 0.9095),
+   '2012': (1.875, 0.6203, 0.0052, 0.9392)}},
+ 'LIU2012': {'F10': {'1992': (0.0029, 0.9699, -0.4454, 0.899),
+   '1993': (0.003, 1.0904, -0.5829, 0.9027),
+   '1994': (0.0056, 0.9038, -0.0699, 0.885)},
+  'F12': {'1994': (0.0028, 1.0569, -0.4794, 0.8984),
+   '1995': (0.0088, 0.5959, 1.6317, 0.8623),
+   '1996': (0.0097, 0.5674, 1.5939, 0.8319),
+   '1997': (0.0092, 0.4851, 1.9491, 0.8386),
+   '1998': (0.0105, 0.3659, 2.3604, 0.8429),
+   '1999': (0.009, 0.5033, 2.1102, 0.9119)},
+  'F14': {'1997': (0.0015, 1.0296, 0.7414, 0.8318),
+   '1998': (0.0056, 0.8389, 0.7931, 0.8584),
+   '1999': (0.001, 1.0659, 0.7002, 0.9186),
+   '2000': (0.0057, 0.7197, 1.3015, 0.9325),
+   '2001': (0.0012, 0.9877, 0.2367, 0.9576),
+   '2002': (-0.003, 1.1597, 0.4874, 0.899),
+   '2003': (-0.0083, 1.5049, -0.5827, 0.9629)},
+  'F15': {'2000': (0.0085, 0.503, 2.1202, 0.8845),
+   '2001': (0.0019, 0.9849, -0.4446, 0.9166),
+   '2002': (0.0009, 0.9596, -0.5467, 0.9632),
+   '2003': (-0.0125, 1.7694, -0.9178, 0.9221),
+   '2004': (-0.0074, 1.4864, 0.1417, 0.9643),
+   '2005': (-0.0041, 1.3075, 0.3526, 0.9212),
+   '2006': (-0.0049, 1.315, 0.8122, 0.9674),
+   '2007': (-0.004, 1.2713, 0.4571, 0.977),
+   '2008': (0.0016, 0.8727, 0.2472, 0.9487)},
+  'F16': {'2004': (-0.0005, 1.071, 0.2026, 0.9263),
+   '2005': (-0.0032, 1.2913, -0.5429, 0.9649),
+   '2006': (-0.0048, 1.2948, 0.0273, 0.9717),
+   '2007': (0.0, 1.0, 0.0, 1.0),
+   '2008': (0.0014, 0.9151, 0.7329, 0.9864)}},
+ 'WU2013': {'F10': {'1992': (0.8959, 1.031, 0.9492),
+   '1993': (0.6821, 1.1181, 0.8731),
+   '1994': (0.9127, 1.064, 0.9112)},
+  'F12': {'1994': (0.4225, 1.3025, 0.8559),
+   '1995': (0.3413, 1.3604, 0.9275),
+   '1996': (0.9247, 1.0576, 0.9541),
+   '1997': (0.3912, 1.3182, 0.9042),
+   '1998': (0.9734, 1.0312, 0.9125),
+   '1999': (1.2743, 0.9539, 0.8846)},
+  'F14': {'1997': (1.3041, 0.9986, 0.8945),
+   '1998': (0.9824, 1.107, 0.9589),
+   '1999': (1.0347, 1.0904, 0.9479),
+   '2000': (0.9885, 1.0702, 0.9047),
+   '2001': (0.9282, 1.0928, 0.9706),
+   '2002': (0.9748, 1.0857, 0.9752),
+   '2003': (0.9144, 1.1062, 0.9156)},
+  'F15': {'2000': (0.8028, 1.0855, 0.9242),
+   '2001': (0.8678, 1.0646, 0.87),
+   '2002': (0.7706, 1.092, 0.8854),
+   '2003': (0.9852, 1.1141, 0.9544),
+   '2004': (0.864, 1.1671, 0.9352),
+   '2005': (0.5918, 1.2894, 0.9322),
+   '2006': (0.9926, 1.1226, 0.9145),
+   '2007': (1.1823, 1.085, 0.9041)},
+  'F16': {'2004': (0.7638, 1.1507, 0.9123),
+   '2005': (0.6984, 1.2292, 0.862),
+   '2006': (0.9028, 1.1306, 0.9412),
+   '2007': (0.8864, 1.1112, 0.9576),
+   '2008': (0.9971, 1.0977, 0.9653),
+   '2009': (1.4637, 0.9858, 0.8735),
+   '2010': (0.8114, 1.0849, 0.9542)},
+  'F18': {'2004': (0.7638, 1.1507, 0.9123),
+   '2005': (0.6984, 1.2292, 0.862),
+   '2006': (0.9028, 1.1306, 0.9412),
+   '2007': (0.8864, 1.1112, 0.9576),
+   '2008': (0.9971, 1.0977, 0.9653),
+   '2009': (1.4637, 0.9858, 0.8735),
+   '2010': (0.8114, 1.0849, 0.9542)}}
+}
+
+# reusable & stand-alone
+if __name__ == "__main__":
+    print ('Citations and coefficients for inter-satellite DMSP-OLS NightTime '
+           'Lights Time Series calibration models')
+    print CITATIONS

Added: grass-addons/grass7/imagery/i.nightlights.intercalibration/intercalibration_equations.py
===================================================================
--- grass-addons/grass7/imagery/i.nightlights.intercalibration/intercalibration_equations.py	                        (rev 0)
+++ grass-addons/grass7/imagery/i.nightlights.intercalibration/intercalibration_equations.py	2015-08-14 20:40:21 UTC (rev 65935)
@@ -0,0 +1,102 @@
+# -*- coding: utf-8 -*-
+"""
+Convert (and export -- needs uncommenting!) intercalibration equations
+(strings), sourced in form of csv, to human readable string representations
+(__str__ method of a model's class) and r.mapcalc compatible expressions.
+
+ at author: nik | Created on Wed Mar 11 18:34:03 2015
+"""
+
+import os
+import StringIO
+import csv
+import collections
+
+csvstring = """csvauthor|model|formula
+ELVIDGE2009|DNadj. = ({c0}) + ({c1}) * DN + ({c2}) * DN^2|({c0}) +({c1})*{dummy} + ({c2})*{dummy}^2
+ELVIDGE2014|DNadj. = ({c0}) + ({c1}) * DN + ({c2}) * DN^2|({c0}) +({c1})*{dummy} + ({c2})*{dummy}^2
+LIU2012|DNadj. = {c0} + {c1} * DN + {c2} * DN^2|({c0}) + ({c1})*{dummy} +({c2})*{dummy}^2
+WU2013|DNc + 1 = {a} * (DNm + 1)^{b}|({a}) * ({dummy} + 1)^({b})"""
+
+# fake it...
+csvfile = StringIO.StringIO(csvstring)
+
+
+def csv_to_dictionary(csvfile):
+    """
+    """
+    equations = {}  # empty dictionary
+    #csvFile = open(csvfile, 'rb')
+    csvReader = csv.reader(csvfile, delimiter='|')
+
+    rows = []
+    fields = []
+    for row in csvReader:
+        rows.append(row)
+    fields = rows.pop(0)[1:]  # header
+
+    def transform(row):
+        """
+        """
+        author = row[0].replace(" ", "_")  # key: class name, replace ''w/ _
+
+        # namedtuple
+        strings = collections.namedtuple(author, [fields[0], fields[1]])
+
+        # feed namedtuples
+        strings.model, strings.formula = (str(row[1]), str(row[2]))
+
+        # feed EQUATION
+        equations[author] = equations.get(author, strings)
+
+    # apply helper function to all rows
+    map(transform, rows)
+
+    # return requestred dictionary
+    return equations
+
+
+def export_to_ascii(dictionary, filename, separator):
+    """
+    Exporting ... to an ASCII file
+    """
+
+    # convert dictionary to string
+    dictionary = str(dictionary)
+
+    # define filename
+    filename += '.py'
+
+    # don't overwrite!
+    if not os.path.exists(filename):
+
+        # structure informative message
+        msg = '> Exporting python dictionary as is...'
+        print msg
+
+        # open, write and close file
+        asciif = open(filename, 'w')
+        asciif.write(dictionary)
+        asciif.close()
+
+    else:
+        print '{f} already exists!'.format(f=filename)
+
+
+def main():
+    """
+    Execute main program. Note, filename is hardcoded!
+    """
+    # csvfile = 'equations.csv'
+    dictionary = csv_to_dictionary(csvfile)
+    # print dictionary
+
+    # uncomment to export, hardcoded filename
+    # doesn't make sense with named tuples though!
+    # asciifile = 'intercalibration_equations.ascii'
+    # export_to_ascii(dictionary, asciifile, # separator='|')
+    return dictionary
+
+
+if __name__ == "__main__":
+    main()

Added: grass-addons/grass7/imagery/i.nightlights.intercalibration/intercalibration_models.py
===================================================================
--- grass-addons/grass7/imagery/i.nightlights.intercalibration/intercalibration_models.py	                        (rev 0)
+++ grass-addons/grass7/imagery/i.nightlights.intercalibration/intercalibration_models.py	2015-08-14 20:40:21 UTC (rev 65935)
@@ -0,0 +1,357 @@
+# -*- coding: utf-8 -*-
+"""
+Unified class for DMSP-OLS Inter-Satellite Calibration Model
+ at author: nik | Created on Wed Mar 11 18:07:20 2015
+"""
+
+from intercalibration_coefficients import CITATIONS, COEFFICIENTS
+import intercalibration_equations
+
+# globals
+DUMMY_MAPCALC_STRING = 'Input'
+EQUATIONS = intercalibration_equations.main()
+
+
+class CalibrationModel:
+    """
+    Common attributes for all models:
+
+    # author: Note, Elvidge's two models require a "version"
+        to select which coefficients to use?
+
+    # citation: based on author, hardcoded for each Sub-Class,
+        use a _citation method
+
+    # satellite year coefficients: a tuple (pair or triplet, so far) mapcalc
+    """
+    def __init__(self, author, satellite, year):
+        """
+        Create object for the calibration model
+        """
+        # set key for MODEL, MAPCALC, COEFFICIENTS, CITATIONS
+        self.author = str(author)
+
+        # get/set input
+        self.satellite = satellite
+        self.year = str(year)
+
+        # set citation
+        self.citation = CITATIONS[self.author]
+
+        # check...
+        self.verify_year(author=self.author, satellite=self.satellite,
+                         year=self.year)
+
+        # load coefficients and R^2 in tuple, float
+        self.set_coefficients()
+        self.set_r2()
+
+        # build euqations for model (string) and r.mapcalc
+        self.build_model()
+        self._mapcalc()
+
+    def __str__(self):
+        msg = 'Calibration model by ... : '
+        msg += '...mode...\n'
+        return msg + '  ' + self._model + '\n'
+
+    def verify_year(self, author, satellite, year):
+        """
+        Check if coefficients exist for requested year, satellite, author
+        """
+        # retrieve years from COEFFICIENTS dictionary
+        available_years = COEFFICIENTS[author][satellite].keys()
+
+        # does the requested year exist?
+        if year not in available_years:
+            raise ValueError('The selected model does not know about '
+                             'this combination of Satellite + Year!')
+        else:
+            return True
+
+    def set_coefficients(self):
+        """
+        Set the model's coefficients for the requested satellite and year
+        """
+        self.a = COEFFICIENTS[self.author][self.satellite][self.year][0]
+        self.b = COEFFICIENTS[self.author][self.satellite][self.year][1]
+        self.coefficients = (self.a, self.b)
+
+    def get_coefficients(self):
+        """
+        Return the model's coefficients for the requested satellite and year
+        """
+        return (self.a, self.b)
+
+    def set_r2(self):
+        """
+        Set the R^2 statistic for the requested coefficients
+        """
+        self.r2 = COEFFICIENTS[self.author][self.satellite][self.year][2]
+
+    def report_r2(self):
+        """
+        Report the associated R^2 value for the coefficients in question
+        """
+        msg = "Associated R^2: "
+        return msg + str(self.r2)
+
+    def is_dn_valid(self, dn):
+        """
+        Control whether the given DN is valid
+        """
+        if type(dn) != int:
+            raise ValueError('The provided Digital Number value is NOT an '
+                             'integer!')
+
+        if 0 > dn or dn > 63:
+            raise ValueError('The provided Digital Number value is out of the '
+                             'expected range [0,63]')
+        else:
+            return True
+
+    def build_model(self):
+        pass
+
+    # def calibrate(self, dn):
+    #     """
+    #     Calibrate a clean average visible band Digital Number value
+    #     """
+    #     model = EQUATIONS[self.author].model  # read equations.py
+
+    # def _mapcalc(self):
+    #     """
+    #     Retrieve the model's formula for r.mapcalc
+    #     """
+    #     formula = EQUATIONS[self.author].formula  # read euqations.py
+
+    def get_mapcalc(self):
+        return self.mapcalc
+
+class Elvidge(CalibrationModel):
+    """
+    Empirical second order, DMSP-OLS inter-satellite, calibration model
+    proposed by Elvidge, 2009  or  Elvidge, 2014.
+    DN adj. = C0 + C1×DN + C2×DN^2
+    """
+    def __init__(self, satellite, year, version):
+        """
+        Create object for the polynomial calibration model
+        proposed by Elvidge 2009/2014
+        """
+        # set key for MODEL, MAPCALC, COEFFICIENTS, CITATIONS
+        author = str('ELVIDGE')
+
+        # which version of Elvidge's model?
+        if not version:
+            self.version = '2014'  # alternative coefficients: Elvidge 2009
+        else:
+            self.version = version
+
+        # set key for COEFFICIENTS
+        author += str(self.version)
+
+        # initialise object attributes from the Super-Class
+        CalibrationModel.__init__(self, author, satellite, year)
+
+    def _citation(self):
+        if self.version == '2014':
+            self.citation = self._citation_2014
+        elif self.version == '2009':
+            self.citation = self._citation_2009
+
+    def __str__(self):
+        """
+        Return a string representation of the calibration model
+        """
+        msg = 'Calibration model proposed by Elvidge, '
+        msg += str(self.version) + '\n  '
+        msg += '[DN adj. = C0 + C1*DN + C2*DN^2]\n'
+        return msg + '  ' + self._model + '\n'
+
+    def set_coefficients(self):
+            """
+            Set coefficients
+            """
+            self.c0 = COEFFICIENTS[self.author][self.satellite][self.year][0]
+            self.c1 = COEFFICIENTS[self.author][self.satellite][self.year][1]
+            self.c2 = COEFFICIENTS[self.author][self.satellite][self.year][2]
+            self.coefficients = (self.c0, self.c1, self.c2)
+
+    def get_coefficients(self):
+            """
+            Triplet tuple
+            """
+            return (self.c0, self.c1, self.c2)
+
+    def set_r2(self):
+            """
+            set R^2
+            """
+            self.r2 = COEFFICIENTS[self.author][self.satellite][self.year][3]
+
+    def build_model(self):
+        """
+        Build model equation, first to serve __str__
+        """
+        # model = 'DNadj. = ({c0}) + ({c1}) * DN + ({c2}) * DN^2'
+        model = EQUATIONS[self.author].model
+        self._model = model.format(c0=self.c0, c1=self.c1, c2=self.c2)
+
+    def calibrate(self, dn):
+        """
+        Calibrate DMSP-OLS NightTime Lights average visible band Digital
+        Number values based on Elvidge's calibration polynomial model and
+        build a calibration equation for the requested satellite and year.
+        """
+        if self.is_dn_valid(dn):
+            cdn = self.c0 + (self.c1 * dn) + (self.c2 * (dn**2))
+        model = EQUATIONS[self.author].model  # look in equations.py
+        self._model = model.format(dn=dn, cdn=cdn, c0=self.c0,
+                                   c1=self.c1, c2=self.c2)
+        return cdn
+
+    def _mapcalc(self):
+        """
+        Return equation for GRASS GIS' mapcalc
+        """
+        # formula = '{c0} + {c1}*{dummy} + {c2}*{dummy}^2'
+        formula = EQUATIONS[self.author].formula  # look in equations.py
+        self.mapcalc = formula.format(c0=self.c0, c1=self.c1,
+                                      dummy=DUMMY_MAPCALC_STRING, c2=self.c2)
+
+
+class Liu2012(CalibrationModel):
+    """
+    Empirical second order calibration model (& optimal threshold method)
+    proposed by Liu, 2012.  DNc = a * DN^2 + b * DN + c, where:
+    - DNc:
+    - DN:
+    - a:
+    - b:
+    - c:
+    """
+    def __init__(self, satellite, year):
+        """
+        Create object for the polynomial calibration model
+        proposed by Elvidge 2009/2014
+        """
+        # set key for MODEL, MAPCALC, COEFFICIENTS, CITATIONS
+        author = str('LIU2012')
+
+        # initialise object attributes from the Super-Class
+        CalibrationModel.__init__(self, author, satellite, year)
+
+    def __str__(self):
+        """
+        Return a string representation of the calibration model
+        """
+        msg = 'Calibration model by Liu, 2012: '
+        msg += 'DNc = a * DN^2 + b * DN + c\n'
+        return msg + '  ' + self._model + '\n'
+
+    def set_coefficients(self):
+            """
+            set coefficients
+            """
+            self.c0 = COEFFICIENTS[self.author][self.satellite][self.year][0]
+            self.c1 = COEFFICIENTS[self.author][self.satellite][self.year][1]
+            self.c2 = COEFFICIENTS[self.author][self.satellite][self.year][2]
+            self.coefficients = (self.c0, self.c1, self.c2)
+
+    def get_coefficients(self):
+            """
+            # triplet tuple
+            """
+            return (self.c0, self.c1, self.c2)
+
+    def set_r2(self):
+            """
+            set R^2
+            """
+            self.r2 = COEFFICIENTS[self.author][self.satellite][self.year][3]
+
+    def build_model(self):
+        # model = 'DNadj. = {c0} + {c1} * DN + {c2} * DN^2'
+        model = EQUATIONS[self.author].model
+        self._model = model.format(c0=self.c0, c1=self.c1, c2=self.c2)
+
+    def calibrate(self, dn):
+        """
+        Calibrate DMSP-OLS NightTime Lights average visible band Digital
+        Number values based on Elvidge's calibration polynomial model and
+        build a calibration equation for the requested satellite and year.
+        """
+        if self.is_dn_valid(dn):
+            cdn = self.c0 + (self.c1 * dn) + (self.c2 * (dn**2))
+
+        # Update _model as well!
+        model = '{cdn} = ({c0}) + ({c1}) * {dn} + ({c2}) * {dn}^2'
+        self._model = model.format(dn=dn, cdn=cdn, c0=self.c0,
+                                   c1=self.c1, c2=self.c2)
+        return cdn
+
+    def _mapcalc(self):
+        """
+        Return equation for GRASS GIS' mapcalc
+        """
+        formula = EQUATIONS[self.author].formula
+        print "FORMULA: ", formula
+        self.mapcalc = formula.format(c0=self.c0, c1=self.c1,
+                                      dummy=DUMMY_MAPCALC_STRING, c2=self.c2)
+
+
+class Wu2013(CalibrationModel):
+    """
+    Power calibration model proposed by Wu 2013.
+    DNc + 1 = a * (DN + 1)^b
+    Subclass, inheriting from CalibrationModel
+    """
+
+    def __init__(self, satellite, year):
+        """
+        Create object for the power calibration model
+        proposed by Wu, 2013
+        """
+        author = str('WU2013')
+
+        # initialise object attributes from the Super-Class
+        CalibrationModel.__init__(self, author, satellite, year)
+
+    def __str__(self):
+        """
+        """
+        msg = 'Calibration model by Wu, 2013: '
+        msg += 'DNc + 1 = a * (DN + 1)^b\n'
+        return msg + '  ' + self._model + '\n'
+
+    def build_model(self):
+        """
+        """
+        model = EQUATIONS[self.author].model
+        self._model = model.format(a=self.a, b=self.b)
+
+    def calibrate(self, dn):
+        """
+        Calibrate a clean average visible band Digital Number value
+        """
+        cdn = self.a * (dn + 1)**self.b
+
+        # Update _model as well!
+        model = '{cdn} = {a} * ({dn} + 1)^{b})'
+        self._model = model.format(dn=dn, cdn=cdn, a=self.a,
+                                   b=self.b)
+        return cdn
+
+    def _mapcalc(self):
+        """
+        """
+        formula = EQUATIONS[self.author].formula
+        self.mapcalc = formula.format(a=self.a, dummy=DUMMY_MAPCALC_STRING,
+                                      b=self.b)
+
+
+# reusable & stand-alone
+if __name__ == "__main__":
+    print ('Calibration models for DMSP-OLS NightTime Lights Time Series'
+           ' (Running as stand-alone tool?)\n')

Added: grass-addons/grass7/imagery/i.nightlights.intercalibration/test_intercalibration_models.py
===================================================================
--- grass-addons/grass7/imagery/i.nightlights.intercalibration/test_intercalibration_models.py	                        (rev 0)
+++ grass-addons/grass7/imagery/i.nightlights.intercalibration/test_intercalibration_models.py	2015-08-14 20:40:21 UTC (rev 65935)
@@ -0,0 +1,160 @@
+# -*- coding: utf-8 -*-
+"""
+Functions to test Python classes for inter-satellite calibration of nighttime
+lights time series.
+
+ at author = nik
+"""
+
+# required librairies
+import random
+from intercalibration_coefficients import COEFFICIENTS
+from intercalibration_models import Elvidge, Liu2012, Wu2013
+
+# helper functions
+def random_digital_numbers(count=3):
+    """
+    Return a user-requested amount of random Digital Number values for testing
+    purposes
+    """
+    digital_numbers = []
+
+    for dn in range(0, count):
+        digital_numbers.append(random.randint(0, 63))
+
+    return digital_numbers
+
+
+def random_digital_number():
+    """
+    Return one random of Digital Number values for testing purposes
+    """
+    return random.randint(0, 63)
+
+
+def calibrate_digital_number(dn, c0, c1, c2):
+    """
+    Calibrate a "raw" digital number based on Elvidge's calibration
+    polynomial model
+    """
+    if type(dn) != int:
+        raise ValueError('The provided Digital Number value is NOT an '
+                         'integer!')
+
+    if 0 > dn or dn > 63:
+        raise ValueError('The provided Digital Number value is out of the '
+                         'expected range [0, 63]')
+
+    return c0 + (c1 * dn) + (c2 * (dn**2))
+
+
+def test_model(author):
+    """
+    Testing the "?" model
+    """
+    print ">>> Testing -----------------------------------------------------\n"
+
+    # -----------------------------------------------------------------------
+    # set required values
+    print " >> Pre-Setting (randomely) required values for testing purposes:"
+    print " * Assigning author and model version...",
+    version = ''
+    if not author:
+        version = random.choice(['2009', '2014'])
+        author = 'ELVIDGE' + str(version)
+    else:
+        version = author[7:]
+
+    print author
+
+    print " * Assigning a random satellite...",
+    satellite = random.choice(COEFFICIENTS[author].keys())
+    print satellite
+
+    print " * Assiging a random year...",
+    year = random.choice(COEFFICIENTS[author][satellite].keys())
+    print year
+
+    print " * Assiging a random c0 coefficient...",
+    c0 = COEFFICIENTS[author][satellite][year][0]
+    print " Random coefficient c0: ", c0
+
+    print " * Assiging a random c1 coefficient...",
+    c1 = COEFFICIENTS[author][satellite][year][1]
+    print " Random coefficient c1: ", c1
+
+    c2 = float()
+    if 'WU' not in author:
+        print " * Assiging a random c2 coefficient..."
+        c2 = COEFFICIENTS[author][satellite][year][2]
+        print " Random coefficient c2: ", c2
+
+#    R2 = coefficients[author][satellite][year][3]
+#    print " Associated R^2 value: ", R2
+    print
+
+    # -----------------------------------------------------------------------
+    print " >> Testing ? class:"
+    print
+    print (' [ Usage:  ?(satellite, year, model version)\n\n'
+           '   where:  DN: input Digital Number value (integer)\n'
+           '           Coefficients: a pair or triplet of floating point '
+
+           'values (tuple)\n\n'
+           '   eg:     ? = Liu2012(F10, 1992, 2009) ]')
+    print
+    if 'ELVIDGE' in author:
+        test_model = Elvidge(satellite, year, version)
+    elif 'LIU' in author:
+        test_model = Liu2012(satellite, year)
+    elif 'WU' in author:
+        test_model = Wu2013(satellite, year)
+
+    print " * Testing 'citation'  method:\n\n", test_model.citation
+    print
+    print " * Testing '__str__' of class:\n\n ", test_model
+    print " * Testing 'satellite': ", test_model.satellite
+    print " * Testing 'year':      ", test_model.year
+    print " * Testing 'veify_year': ", test_model.verify_year(author,
+                                                              satellite, year)
+    print " * Testing 'coefficients': ", test_model.coefficients
+    print " * Testing 'r2': ", test_model.r2
+    print " * Testing 'report_r2' method: ", test_model.report_r2()
+    dn = random_digital_number()
+    print " > A random digital number: ", dn
+    print " * Testing 'is_dn_valid': ", test_model.is_dn_valid(dn)
+    print " * Testing 'calibrate' method:  ", test_model.calibrate(dn)
+    print " * Testing '_model' (hidden): ", test_model._model
+    print " * Testing 'mapcalc': ", test_model.mapcalc
+    print " * Testing 'get_mapcalc': ", test_model.get_mapcalc()
+    print
+
+    # -----------------------------------------------------------------------
+    print " >> Testing helper functions: "
+    dn = random_digital_number()
+    print (" * Testing 'random_digital_number()' method (and type()): ",
+           dn, "|", type(dn))
+    print (" * Testing 'calibrate_digital_number' method: ",
+           calibrate_digital_number(dn, c0, c1, c2))
+    print
+
+    # -----------------------------------------------------------------------
+    print " * Testing three random Digital number values:\n"
+    for dn in random_digital_numbers(3):
+        print "   (Random) DN: ", dn
+        print "   Coefficients: ", test_model.coefficients
+        print "   Model: ", test_model.calibrate(dn), "\n"
+
+
+# reusable & stand-alone
+if __name__ == "__main__":
+    print ('Testing classes for calibration models for DMSP-OLS NightTime '
+           'Lights Time Series')
+    print
+    
+    # uncomment to test
+    test_model('ELVIDGE2009')
+    test_model('ELVIDGE2014')
+    test_model('LIU2012')
+    test_model('WU2013')
+

Modified: grass-addons/grass7/imagery/i.segment.hierarchical/Makefile
===================================================================
--- grass-addons/grass7/imagery/i.segment.hierarchical/Makefile	2015-08-14 16:14:42 UTC (rev 65934)
+++ grass-addons/grass7/imagery/i.segment.hierarchical/Makefile	2015-08-14 20:40:21 UTC (rev 65935)
@@ -2,19 +2,9 @@
 
 PGM = i.segment.hierarchical
 
+ETCFILES = isegpatch
+
 include $(MODULE_TOPDIR)/include/Make/Script.make
 include $(MODULE_TOPDIR)/include/Make/Python.make
 
-MODULES = isegpatch
-
-ETCDIR = $(ETC)/i.segment.hierarchical
-
-PYFILES := $(patsubst %,$(ETCDIR)/%.py,$(MODULES))
-
-default: script $(PYFILES)
-
-$(ETCDIR):
-	$(MKDIR) $@
-
-$(ETCDIR)/%: % | $(ETCDIR)
-	$(INSTALL_DATA) $< $@
+default: script



More information about the grass-commit mailing list