[GRASS-SVN] r57533 - in grass-addons/grass7/raster: . r.sun.hourly
svn_grass at osgeo.org
svn_grass at osgeo.org
Wed Aug 28 12:39:21 PDT 2013
Author: annakrat
Date: 2013-08-28 12:39:20 -0700 (Wed, 28 Aug 2013)
New Revision: 57533
Added:
grass-addons/grass7/raster/r.sun.hourly/
grass-addons/grass7/raster/r.sun.hourly/Makefile
grass-addons/grass7/raster/r.sun.hourly/r.sun.hourly.html
grass-addons/grass7/raster/r.sun.hourly/r.sun.hourly.py
grass-addons/grass7/raster/r.sun.hourly/test.r.sun.hourly.sh
Log:
r.sun.hourly: initial version, wrapper script for r.sun, mode 1 (co-author: wenzeslaus)
Added: grass-addons/grass7/raster/r.sun.hourly/Makefile
===================================================================
--- grass-addons/grass7/raster/r.sun.hourly/Makefile (rev 0)
+++ grass-addons/grass7/raster/r.sun.hourly/Makefile 2013-08-28 19:39:20 UTC (rev 57533)
@@ -0,0 +1,7 @@
+MODULE_TOPDIR = ../..
+
+PGM=r.sun.hourly
+
+include $(MODULE_TOPDIR)/include/Make/Script.make
+
+default: script
Property changes on: grass-addons/grass7/raster/r.sun.hourly/Makefile
___________________________________________________________________
Added: svn:mime-type
+ text/x-makefile
Added: svn:eol-style
+ native
Added: grass-addons/grass7/raster/r.sun.hourly/r.sun.hourly.html
===================================================================
--- grass-addons/grass7/raster/r.sun.hourly/r.sun.hourly.html (rev 0)
+++ grass-addons/grass7/raster/r.sun.hourly/r.sun.hourly.html 2013-08-28 19:39:20 UTC (rev 57533)
@@ -0,0 +1,43 @@
+<h2>DESCRIPTION</h2>
+
+<em>r.sun.mode1</em> is a convenient script for running
+r.sun for multiple times in a loop. It corresponds to mode 1
+(see r.sun <a href="r.sun.html">manual page</a>).
+
+<h3>Output parameters explanation</h3>
+Output is one or more series of raster maps.
+
+Series of maps are (if flag <i>t</i> is checked) registered
+to space time raster dataset with absolute time and point time (not interval time).
+Option <i>year</i> has to be specified so that the raster maps can be registered
+to space time dataset or assigned a timestamp. The reason is that it is not possible
+to assign time without date.
+
+For GRASS 6, only timestamp is assigned.
+
+<h2>EXAMPLE</h2>
+
+<div class="code"><pre>
+r.sun.mode1 elev_in=elevation start_time=8 end_time=15 beam_rad_basename=beam nprocs=4 -t
+# show information about newly created space time dataset
+t.info beam
+
+# show raster maps registered in beam dataset
+t.rast.list beam
+</pre></div>
+
+
+<h2>SEE ALSO</h2>
+
+<em>
+<a href="r.sun.html">r.sun</a>
+<a href="r.sun.daily.html">r.sun.daily</a> in Addons
+</em>
+
+
+<h2>AUTHOR</h2>
+
+Vaclav Petras, Anna Petrasova<br>
+
+<p>
+<i>Last changed: $Date$</i>
Property changes on: grass-addons/grass7/raster/r.sun.hourly/r.sun.hourly.html
___________________________________________________________________
Added: svn:mime-type
+ text/html
Added: svn:keywords
+ Author Date Id
Added: svn:eol-style
+ native
Added: grass-addons/grass7/raster/r.sun.hourly/r.sun.hourly.py
===================================================================
--- grass-addons/grass7/raster/r.sun.hourly/r.sun.hourly.py (rev 0)
+++ grass-addons/grass7/raster/r.sun.hourly/r.sun.hourly.py 2013-08-28 19:39:20 UTC (rev 57533)
@@ -0,0 +1,422 @@
+#!/usr/bin/env python
+
+############################################################################
+#
+# MODULE: r.sun.hourly for GRASS 6 and 7
+# AUTHOR(S): Vaclav Petras, Anna Petrasova
+# PURPOSE:
+# COPYRIGHT: (C) 2013 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: Runs r.sun in loop for given time range (mode 1)
+#% keywords: raster
+#% keywords: sun
+#%end
+#%option
+#% type: string
+#% gisprompt: old,cell,raster
+#% key: elev_in
+#% description: Name of the input elevation raster map [meters]
+#% required : yes
+#%end
+#%option
+#% type: string
+#% gisprompt: old,cell,raster
+#% key: asp_in
+#% description: Name of the input aspect map (terrain aspect or azimuth of the solar panel) [decimal degrees]
+#%end
+#%option
+#% type: string
+#% gisprompt: old,cell,raster
+#% key: slope_in
+#% description: Name of the input slope raster map (terrain slope or solar panel inclination) [decimal degrees]
+#%end
+#%option
+#% key: start_time
+#% type: double
+#% label: Start time of interval
+#% description: Use up to 2 decimal places
+#% options: 0-24
+#% required : yes
+#%end
+#%option
+#% key: end_time
+#% type: double
+#% label: End time of interval
+#% description: Use up to 2 decimal places
+#% options: 0-24
+#% required : yes
+#%end
+#%option
+#% key: time_step
+#% type: double
+#% label: Time step for running r.sun [decimal hours]
+#% description: Use up to 2 decimal places
+#% options: 0-24
+#% answer: 1
+#%end
+#%option
+#% key: day
+#% type: integer
+#% description: No. of day of the year
+#% options: 1-365
+#% required : yes
+#%end
+#%option
+#% key: year
+#% type: integer
+#% label: Year used for map registration into temporal dataset or r.timestamp
+#% description: This value is not used in r.sun calcluations
+#% options: 1900-9999
+#% required: yes
+#% answer: 1900
+#%end
+#%option
+#% key: civil_time
+#% type: double
+#% description: Civil time zone value, if none, the time will be local solar time
+#%end
+#%option
+#% key: beam_rad_basename
+#% type: string
+#% label: Base name for output beam irradiance raster maps [Wh.m-2]
+#% description: Underscore and day number are added to the base name for each map
+#%end
+#%option
+#% key: diff_rad_basename
+#% type: string
+#% label: Base name for output diffuse irradiance raster maps [Wh.m-2]
+#% description: Underscore and day number are added to the base name for each map
+#%end
+#%option
+#% key: refl_rad_basename
+#% type: string
+#% label: Base name for output ground reflected irradiance raster maps [Wh.m-2]
+#% description: Underscore and day number are added to the base name for each map
+#%end
+#%option
+#% key: glob_rad_basename
+#% type: string
+#% label: Base name for output global (total) irradiance raster maps [Wh.m-2]
+#% description: Underscore and day number are added to the base name for each map
+#%end
+#%option
+#% key: incidout_basename
+#% type: string
+#% label: Base name for output incidence angle raster maps
+#% description: Underscore and day number are added to the base name for each map
+#%end
+#%option
+#% key: nprocs
+#% type: integer
+#% description: Number of r.sun processes to run in parallel
+#% options: 1-
+#% answer: 1
+#%end
+#%flag
+#% key: t
+#% description: Dataset name is the same as the base name for the output series of maps
+#% label: Register created series of output maps into temporal dataset
+#%end
+#%flag
+#% key: overwrite
+#% description: Allow output files to overwrite existing files
+#%end
+
+
+import os
+import datetime
+import atexit
+from multiprocessing import Process
+
+import grass.script as grass
+import grass.script.core as core
+
+TMP = []
+
+
+def cleanup():
+ if len(TMP):
+ core.info(_("Cleaning %d temporary maps...") % len(TMP))
+ for rast in TMP:
+ grass.run_command('g.remove', rast=rast, quiet=True)
+
+
+def is_grass_7():
+ if core.version()['version'].split('.')[0] == '7':
+ return True
+ return False
+
+
+def create_tmp_map_name(name):
+ return '{mod}{pid}_{map_}_tmp'.format(mod='r_sun_crop',
+ pid=os.getpid(),
+ map_=name)
+
+
+# add latitude map
+def run_r_sun(elevation, aspect, slope, day, time, beam_rad, diff_rad,
+ refl_rad, glob_rad, incidout, suffix):
+ params = {}
+ if beam_rad:
+ params.update({'beam_rad': beam_rad + suffix})
+ if diff_rad:
+ params.update({'diff_rad': diff_rad + suffix})
+ if refl_rad:
+ params.update({'refl_rad': refl_rad + suffix})
+ if glob_rad:
+ params.update({'glob_rad': glob_rad + suffix})
+ if incidout:
+ params.update({'incidout': incidout + suffix})
+
+ if is_grass_7():
+ grass.run_command('r.sun', elev_in=elevation, asp_in=aspect,
+ slope_in=slope,
+ day=day, time=time,
+ overwrite=core.overwrite(), quiet=True,
+ **params)
+ else:
+ grass.run_command('r.sun', elevin=elevation, aspin=aspect,
+ slopein=slope,
+ day=day, time=time,
+ overwrite=core.overwrite(), quiet=True,
+ **params)
+
+
+def set_color_table(rasters):
+ if is_grass_7():
+ grass.run_command('r.colors', map=rasters, col='gyr', quiet=True)
+ else:
+ for rast in rasters:
+ grass.run_command('r.colors', map=rast, col='gyr', quiet=True)
+
+
+def set_time_stamp(raster, time):
+ grass.run_command('r.timestamp', map=raster, date=time, quiet=True)
+
+
+def format_time(time):
+ return '%05.2f' % time
+
+
+def check_time_map_names(basename, mapset, start_time, end_time, time_step):
+ if not basename:
+ return
+ for time in frange(start_time, end_time, time_step):
+ map_ = '%s%s%s' % (basename, '_', format_time(time))
+ if grass.find_file(map_, element='cell', mapset=mapset)['file']:
+ grass.fatal(_("Raster map <%s> already exists. Change the base name or allow overwrite.") % map_)
+
+
+def frange(x, y, step):
+ while x <= y:
+ yield x
+ x += step
+
+
+def format_grass_time(dt):
+ """!Format datetime object to grass timestamps.
+ Copied from temporal framework to use thsi script also in GRASS 6.
+ """
+ # GRASS datetime month names
+ month_names = ["", "jan", "feb", "mar", "apr", "may", "jun",
+ "jul", "aug", "sep", "oct", "nov", "dec"]
+ return "%.2i %s %.4i %.2i:%.2i:%.2i" % (dt.day, month_names[dt.month],
+ dt.year, dt.hour, dt.minute, dt.second)
+
+
+def main():
+ options, flags = grass.parser()
+
+ elevation_input = options['elev_in']
+ aspect_input = options['asp_in']
+ slope_input = options['slope_in']
+
+ beam_rad_basename = options['beam_rad_basename']
+ diff_rad_basename = options['diff_rad_basename']
+ refl_rad_basename = options['refl_rad_basename']
+ glob_rad_basename = options['glob_rad_basename']
+ incidout_basename = options['incidout_basename']
+
+ if not any([beam_rad_basename, diff_rad_basename,
+ refl_rad_basename, glob_rad_basename,
+ incidout_basename]):
+ grass.fatal(_("No output specified."))
+
+ start_time = float(options['start_time'])
+ end_time = float(options['end_time'])
+ time_step = float(options['time_step'])
+ nprocs = int(options['nprocs'])
+ day = int(options['day'])
+ temporal = flags['t']
+ year = int(options['year'])
+
+ if not is_grass_7() and temporal:
+ grass.warning(_("Flag t has effect only in GRASS 7"))
+
+ # check: start < end
+ if start_time > end_time:
+ grass.fatal(_("Start time is after end time."))
+ if time_step >= end_time - start_time:
+ grass.fatal(_("Time step is too big."))
+
+ # here we check all the days
+ if not grass.overwrite():
+ check_time_map_names(beam_rad_basename, grass.gisenv()['MAPSET'],
+ start_time, end_time, time_step)
+ check_time_map_names(diff_rad_basename, grass.gisenv()['MAPSET'],
+ start_time, end_time, time_step)
+ check_time_map_names(refl_rad_basename, grass.gisenv()['MAPSET'],
+ start_time, end_time, time_step)
+ check_time_map_names(glob_rad_basename, grass.gisenv()['MAPSET'],
+ start_time, end_time, time_step)
+
+ # check for slope/aspect
+ if not aspect_input or not slope_input:
+ params = {}
+ if not aspect_input:
+ aspect_input = create_tmp_map_name('aspect')
+ params.update({'aspect': aspect_input})
+ TMP.append(aspect_input)
+ if not slope_input:
+ slope_input = create_tmp_map_name('slope')
+ params.update({'slope': slope_input})
+ TMP.append(slope_input)
+
+ grass.info(_("Running r.slope.aspect..."))
+ grass.run_command('r.slope.aspect', elevation=elevation_input, quiet=True, **params)
+
+ grass.info(_("Running r.sun in a loop..."))
+ count = 0
+ # Parallel processing
+ proc_list = []
+ proc_count = 0
+ suffixes = []
+ suffixes_all = []
+ times = list(frange(start_time, end_time, time_step))
+ num_times = len(times)
+ core.percent(0, num_times, 1)
+ for time in times:
+ count += 1
+ core.percent(count, num_times, 10)
+
+ suffix = '_' + format_time(time)
+ proc_list.append(Process(target=run_r_sun,
+ args=(elevation_input, aspect_input,
+ slope_input, day, time,
+ beam_rad_basename,
+ diff_rad_basename,
+ refl_rad_basename,
+ glob_rad_basename,
+ incidout_basename,
+ suffix)))
+
+ proc_list[proc_count].start()
+ proc_count += 1
+ suffixes.append(suffix)
+ suffixes_all.append(suffix)
+
+ if proc_count == nprocs or proc_count == num_times or count == num_times:
+ proc_count = 0
+ exitcodes = 0
+ for proc in proc_list:
+ proc.join()
+ exitcodes += proc.exitcode
+
+ if exitcodes != 0:
+ core.fatal(_("Error while r.sun computation"))
+
+ # Empty process list
+ proc_list = []
+ suffixes = []
+ # FIXME: how percent really works?
+ # core.percent(1, 1, 1)
+
+ # add timestamps either via temporal framework in 7 or r.timestamp in 6.x
+ if is_grass_7() and temporal:
+ core.info(_("Registering created maps into temporal dataset..."))
+ import grass.temporal as tgis
+
+ def registerToTemporal(basename, suffixes, mapset, start_time, time_step, title, desc):
+ maps = ','.join([basename + suf + '@' + mapset for suf in suffixes])
+ tgis.open_new_space_time_dataset(basename, type='strds', temporaltype='absolute',
+ title=title, descr=desc,
+ semantic='mean', dbif=None, overwrite=grass.overwrite())
+ tgis.register_maps_in_space_time_dataset(
+ type='rast', name=basename, maps=maps, start=start_time, end=None,
+ increment=time_step, dbif=None, interval=False)
+ # Make sure the temporal database exists
+ tgis.init()
+
+ mapset = grass.gisenv()['MAPSET']
+ absolute_time = datetime.datetime(year, 1, 1) + \
+ datetime.timedelta(days=day - 1) + \
+ datetime.timedelta(hours=start_time)
+ start = absolute_time.strftime("%Y-%m-%d %H:%M:%S")
+ step = datetime.timedelta(hours=time_step)
+ step = "%d seconds" % step.seconds
+
+ if beam_rad_basename:
+ registerToTemporal(beam_rad_basename, suffixes_all, mapset, start, step,
+ title="Beam irradiance",
+ desc="Output beam irradiance raster maps [Wh.m-2]")
+ if diff_rad_basename:
+ registerToTemporal(diff_rad_basename, suffixes_all, mapset, start, step,
+ title="Diffuse irradiance",
+ desc="Output diffuse irradiance raster maps [Wh.m-2]")
+ if refl_rad_basename:
+ registerToTemporal(refl_rad_basename, suffixes_all, mapset, start, step,
+ title="Reflected irradiance",
+ desc="Output reflected irradiance raster maps [Wh.m-2]")
+ if glob_rad_basename:
+ registerToTemporal(glob_rad_basename, suffixes_all, mapset, start, step,
+ title="Total irradiance",
+ desc="Output total irradiance raster maps [Wh.m-2]")
+ if incidout_basename:
+ registerToTemporal(incidout_basename, suffixes_all, mapset, start, step,
+ title="Incidence angle",
+ desc="Output incidence angle raster maps")
+
+ else:
+ absolute_time = datetime.datetime(year, 1, 1) + \
+ datetime.timedelta(days=day - 1)
+ for i, time in enumerate(times):
+ grass_time = format_grass_time(absolute_time + datetime.timedelta(hours=time))
+ if beam_rad_basename:
+ set_time_stamp(beam_rad_basename + suffixes_all[i], time=grass_time)
+ if diff_rad_basename:
+ set_time_stamp(diff_rad_basename + suffixes_all[i], time=grass_time)
+ if refl_rad_basename:
+ set_time_stamp(refl_rad_basename + suffixes_all[i], time=grass_time)
+ if glob_rad_basename:
+ set_time_stamp(glob_rad_basename + suffixes_all[i], time=grass_time)
+ if incidout_basename:
+ set_time_stamp(incidout_basename + suffixes_all[i], time=grass_time)
+
+ if beam_rad_basename:
+ maps = [beam_rad_basename + suf for suf in suffixes_all]
+ set_color_table(maps)
+ if diff_rad_basename:
+ maps = [diff_rad_basename + suf for suf in suffixes_all]
+ set_color_table(maps)
+ if refl_rad_basename:
+ maps = [refl_rad_basename + suf for suf in suffixes_all]
+ set_color_table(maps)
+ if glob_rad_basename:
+ maps = [glob_rad_basename + suf for suf in suffixes_all]
+ set_color_table(maps)
+ if incidout_basename:
+ maps = [incidout_basename + suf for suf in suffixes_all]
+ set_color_table(maps)
+
+
+
+if __name__ == "__main__":
+ atexit.register(cleanup)
+ main()
Property changes on: grass-addons/grass7/raster/r.sun.hourly/r.sun.hourly.py
___________________________________________________________________
Added: svn:executable
+ *
Added: svn:mime-type
+ text/x-python
Added: svn:eol-style
+ native
Added: grass-addons/grass7/raster/r.sun.hourly/test.r.sun.hourly.sh
===================================================================
--- grass-addons/grass7/raster/r.sun.hourly/test.r.sun.hourly.sh (rev 0)
+++ grass-addons/grass7/raster/r.sun.hourly/test.r.sun.hourly.sh 2013-08-28 19:39:20 UTC (rev 57533)
@@ -0,0 +1,125 @@
+#!/bin/sh
+
+############################################################################
+#
+# TEST: test.r.sun.hourly
+# AUTHOR(S): Vaclav Petras, Anna Petrasova
+# PURPOSE: This is test for r.sun.hourly module
+# COPYRIGHT: (C) 2013 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.
+#
+#############################################################################
+
+# @preprocess step
+# The region setting should work for UTM and LL test locations
+# region could be set to some real values to avoid unable to read fp range warning
+g.region s=30 n=40 w=40 e=60 res=10
+r.mapcalc --o expr="building = 50"
+
+g.region s=0 n=80 w=0 e=120 res=10
+r.mapcalc --o expr="terrain = 10"
+
+r.mapcalc --o expr="terrain = terrain + building"
+
+map_number_separator="_"
+map_number_pattern="[0-9]{2}.[0-9]{2}"
+
+map_basename=reflmap
+decimal_map_basename=reflmap_decimal
+temporal_map_basename=reflmap_temporal
+
+map_names_file=`g.tempfile pid=$$`
+created_map_names_file=`g.tempfile pid=$$`
+
+decimal_map_names_file=`g.tempfile pid=$$`
+decimal_created_map_names_file=`g.tempfile pid=$$`
+
+temporal_dataset_file=`g.tempfile pid=$$`
+temporal_map_names_file=`g.tempfile pid=$$`
+temporal_created_dataset_file=`g.tempfile pid=$$`
+temporal_created_map_names_file=`g.tempfile pid=$$`
+year=2001
+
+cat > "${map_names_file}" << EOF
+${map_basename}_11.50
+${map_basename}_14.50
+${map_basename}_17.50
+EOF
+
+cat > "${decimal_map_names_file}" << EOF
+${decimal_map_basename}_07.00
+${decimal_map_basename}_08.33
+${decimal_map_basename}_09.67
+${decimal_map_basename}_11.00
+EOF
+
+cat > "${temporal_dataset_file}" << EOF
+${temporal_map_basename}@`g.mapset -p`
+EOF
+
+cat > "${temporal_map_names_file}" << EOF
+${temporal_map_basename}_11.50 landsat ${year}-04-10 11:30:00 None
+${temporal_map_basename}_14.50 landsat ${year}-04-10 14:30:00 None
+${temporal_map_basename}_17.50 landsat ${year}-04-10 17:30:00 None
+EOF
+
+# The @test
+
+NAME="Missing ouput parameter test (module should fail)"
+r.sun.hourly elev_in=terrain start_time=11.50 end_time=18.20 time_step=3 day=80
+echo "$NAME: r.sun.hourly returned: $? (expecting 1)"
+
+NAME="Wrong start and end time parameter values test (module should fail)"
+r.sun.hourly elev_in=terrain start_time=11.50 end_time=9.00 time_step=3 day=80 reflrad_basename=${map_basename}
+echo "$NAME: r.sun.hourly returned: $? (expecting 1)"
+
+NAME="Wrong time step parameter value test (module should fail)"
+r.sun.hourly elev_in=terrain start_time=10.60 end_time=11.20 time_step=0.60 day=80 reflrad_basename=${map_basename}
+echo "$NAME: r.sun.hourly returned: $? (expecting 1)"
+
+NAME="Map creation test"
+r.sun.hourly elev_in=terrain start_time=11.50 end_time=20.00 time_step=3 day=80 reflrad_basename=${map_basename}
+
+g.mlist -e type=rast pattern=${map_basename}${map_number_separator}${map_number_pattern} sep=newline > ${created_map_names_file}
+
+diff ${map_names_file} ${created_map_names_file}
+echo "$NAME: Diff returned $? (expecting 0)"
+
+NAME="Map creation test with too much decimal places"
+r.sun.hourly elev_in=terrain start_time=7.0000 end_time=11.0000 time_step=1.3333 day=80 reflrad_basename=${decimal_map_basename}
+
+g.mlist -e type=rast pattern=${decimal_map_basename}${map_number_separator}${map_number_pattern} sep=newline > ${decimal_created_map_names_file}
+
+diff ${decimal_map_names_file} ${decimal_created_map_names_file}
+echo "$NAME: Diff returned $? (expecting 0)"
+
+NAME="Temporal dataset creation test"
+r.sun.hourly -t elev_in=terrain start_time=11.50 end_time=20.00 time_step=3 day=100 year=${year} reflrad_basename=${temporal_map_basename}
+
+t.list type=strds > ${temporal_created_dataset_file}
+
+t.rast.list input=${temporal_map_basename} method=col > ${temporal_created_map_names_file}
+
+diff ${temporal_dataset_file} ${temporal_created_dataset_file}
+echo "$NAME (maps temporal dataset subtest): Diff returned $? (expecting 0)"
+
+diff --ignore-all-space ${temporal_map_names_file} ${temporal_created_map_names_file}
+echo "$NAME (maps sub-test): Diff returned $? (expecting 0)"
+
+NAME="Map already exists test (module should fail)"
+r.sun.hourly elev_in=terrain start_time=11.50 end_time=20.00 time_step=3 day=80 reflrad_basename=${map_basename}
+echo "$NAME: r.sun.hourly returned: $? (expecting 1)"
+
+# clean
+rm ${map_names_file} ${created_map_names_file}
+g.remove rast=`g.mlist -e type=rast pattern=${map_basename}${map_number_separator}${map_number_pattern} sep=,`
+
+rm ${decimal_map_names_file} ${decimal_created_map_names_file}
+g.remove rast=`g.mlist -e type=rast pattern=${decimal_map_basename}${map_number_separator}${map_number_pattern} sep=,`
+
+rm ${temporal_map_names_file} ${temporal_dataset_file} ${temporal_created_dataset_file} ${temporal_created_map_names_file}
+t.remove -rf inputs=${temporal_map_basename}
+
Property changes on: grass-addons/grass7/raster/r.sun.hourly/test.r.sun.hourly.sh
___________________________________________________________________
Added: svn:executable
+ *
Added: svn:mime-type
+ text/x-sh
Added: svn:eol-style
+ native
More information about the grass-commit
mailing list