source: grass-addons/grass7/misc/m.printws/m.printws.py@ 69427

Last change on this file since 69427 was 69427, checked in by kuszinger, 8 years ago

xml "&" workaround, GRASS_REGION provision, documentation enhancements

  • Property svn:executable set to *
File size: 35.5 KB
Line 
1#!/usr/bin/env python
2
3############################################################################
4#
5# MODULE: m.printws
6#
7# AUTHOR(S): Robert Kuszinger
8#
9# PURPOSE: Creates carographic-like map sheet page from
10# a workspace composition
11#
12# COPYRIGHT: (C) 2016 by GRASS development team
13#
14# This program is free software under the GNU General
15# Public License (>=v2). Read the file COPYING that
16# comes with GRASS for details.
17#
18#############################################################################
19
20#%module
21#% description: Opens a workspace file and creates a map sheet according to its visible contents.
22#% keyword: map
23#% keyword: print
24#% keyword: layout
25#% keyword: workspace
26#%end
27#%option G_OPT_F_BIN_INPUT
28#% key: input
29#% description: Name of workspace file to process
30#% required: YES
31#% gisprompt: old,bin,file
32#% guisection: Input
33#%end
34#%option
35#% key: dpi
36#% type: integer
37#% answer: 150
38#% multiple: no
39#% description: DPI for the generated page
40#% guisection: Output
41#%end
42#%option G_OPT_F_OUTPUT
43#% description: Name of output file without extension
44#% key: output
45#% gisprompt: new,file,file
46#% guisection: Output
47#%end
48#%option
49#% key: page
50#% type: string
51#% options: A4landscape,A4portrait,LETTERlandscape,LETTERportrait,A3landscape,A3portrait,Flexi
52#% answer: A4landscape
53#% description: Output map page size
54#% guisection: Output
55#%end
56#%option
57#% key: format
58#% type: string
59#% options: pdf,png,tiff,bmp,ppm,jpg
60#% answer: pdf
61#% description: Output file format
62#% guisection: Output
63#%end
64#%option
65#% key: maintitle
66#% type: string
67#% description: Main title of map sheet
68#% guisection: Titles
69#%end
70#%option
71#% key: font
72#% type: string
73#% description: Font for title above and postscript under the map
74#% guisection: Titles
75#%end
76#%option G_OPT_C
77#% key: titlecolor
78#% type: string
79#% description: Title text color
80#% guisection: Titles
81#%end
82#%option
83#% key: maintitlesize
84#% type: integer
85#% description: Main title font size in layout units
86#% guisection: Titles
87#%end
88#%option
89#% key: subtitle
90#% type: string
91#% description: Subtitle text above the map sheet in the middle
92#% guisection: Titles
93#%end
94#%option
95#% key: subtitlesize
96#% type: integer
97#% description: Subtitle font size in layout units
98#% guisection: Titles
99#%end
100#%option
101#% key: psunderleft
102#% type: string
103#% description: Postscript text under the map sheet on the left
104#% guisection: Titles
105#%end
106#%option
107#% key: psunderright
108#% type: string
109#% description: Postscript text under the map sheet on the right
110#% guisection: Titles
111#%end
112#%option
113#% key: psundercentral
114#% type: string
115#% description: Postscript text under the map sheet, centered
116#% guisection: Titles
117#%end
118#%option
119#% key: pssize
120#% type: integer
121#% description: Postscript text font size in layout units
122#% guisection: Titles
123#%end
124#%option G_OPT_M_REGION
125#% key:region
126#% description: Name of region to use - uses workspace displayed area if empty
127#% required: NO
128#% gisprompt: old,windows,region
129#% guisection: Input
130#%end
131#%flag
132#% key: d
133#% description: Debug - Leave temp files as is for alternative usage or checkup
134#% guisection: Optional
135#% suppress_required: yes
136#%end
137#%option
138#% key: layunits
139#% type: string
140#% options: cm,mm,inch
141#% answer: mm
142#% description: Unit used for layout specification
143#% guisection: Layout
144#%end
145#%option
146#% key: pagemargin
147#% type: string
148#% description: Margins in layout units left,right,top,bottom
149#% guisection: Layout
150#%end
151#%option
152#% key: mapupperleft
153#% type: string
154#% answer: -1,-1
155#% description: Map frame upper left coordinates - negative means centering
156#% guisection: Layout
157#%end
158#%option
159#% key: mapsize
160#% type: string
161#% answer: 1000
162#% description: Map frame size in layout units as width,height
163#% guisection: Layout
164#%end
165#%option
166#% key: screendpi
167#% type: integer
168#% answer: 100
169#% description: The DPI of your monitor
170#% guisection: Layout
171#%end
172
173
174import sys
175import os
176
177# Windows pwd module workaround
178hasPwd = True
179try:
180 import pwd
181except ImportError:
182 hasPwd = False
183
184
185import atexit
186import re
187import tempfile
188import grass.script as grass
189from grass.exceptions import CalledModuleError
190from grass.script.utils import try_rmdir
191import copy
192import time
193import unicodedata
194
195# workspace file is XML so we use an XML parser
196import xml.dom.minidom
197
198# initialize global vars
199TMPFORMAT = 'BMP'
200TMPLOC = None
201LAYERCOUNT = 10
202# Following declarations MAY will used in future for sure.
203SRCGISRC = None
204GISDBASE = None
205# temp dir
206REMOVE_TMPDIR = True
207PROXIES = {}
208
209# UPSIZE is better global as it is universal at a moment
210# an we save a lot of parameter passing when parsing xml
211global UPSIZE
212UPSIZE = 1.0
213
214# set upsize "constants"
215
216UPSD = {}
217ALLTASKDIC = {}
218ALLTASKDIC['width'] = 1.0 # 1 by 1 correction if any
219UPSD['*'] = ALLTASKDIC
220
221DVECTDIC = {}
222DVECTDIC['size'] = 1.0 # symbol size
223DVECTDIC['label_size'] = 1.5 # label size
224UPSD['d.vect'] = DVECTDIC
225
226DGRIDDIC = {}
227DGRIDDIC['size'] = 0.0 # force not touching grid line distance
228DGRIDDIC['fontsize'] = 1.0 # 1 by 1 correction if any
229UPSD['d.grid'] = DGRIDDIC
230
231
232
233# PAGE dictionary
234
235PAGEDIC = {}
236PAGEDIC['A4portrait'] = (210.0, 297.0, '', 'A4')
237PAGEDIC['A4landscape'] = (297.0, 210.0, '', 'A4')
238PAGEDIC['A3portrait'] = (297.0, 420.0, '', 'A3')
239PAGEDIC['A3landscape'] = (420.0, 297.0, '', 'A3')
240PAGEDIC['LETTERportrait'] = (215.9, 297.4, '', 'Letter')
241PAGEDIC['LETTERlandscape'] = (297.4, 215.9, '', 'Letter')
242PAGEDIC['Flexi'] = (300, 300, '', 'Flexi')
243
244
245# HTML DECODE
246HTMLDIC = {}
247HTMLDIC['>'] = '>'
248HTMLDIC['&lt;'] = '<'
249HTMLDIC['&amp;'] = '&'
250HTMLDIC['&quot;'] = '"'
251
252
253
254def cleanthisandthat(intext):
255 # As of 10. September 2016 some modules (d.wms) creates
256 # lines in XML workspace files which are not well-formed
257 # before parsing them, we need to correct it.
258 # Handled errors:
259 # single & which is not &amp; in urls
260 # Once workspace files are always good this function could be NOOP
261 outtext = ''
262 for line in intext.splitlines():
263 m = re.search('http\://',line)
264 if m:
265 line2 = re.sub('\&amp\;','SAVED___amp\;',line)
266 line3 = re.sub('\&','&amp;',line2)
267 line4 = re.sub('SAVED___amp\;','&amp;',line3)
268 outtext = outtext + line4 + "\n"
269 else:
270 outtext = outtext + line + "\n"
271 return outtext
272
273
274def cleanup():
275
276 # No cleanup is done here
277 # see end of main()
278 # kept for later
279 grass.verbose(_("Module cleanup"))
280
281# test
282# m.printws.py --overwrite input=/home/kuszi/grassdata/workspaces_7/EURASEAA.gxw dpi=100 output=/home/kuszi/grassdata/mapdefs/euraseeaa.bmp page=A4portrait maintitle=$DISPLAY pagemargin=0
283
284def upsizeifnecessary(task, lastparam, value, upsize):
285 val = UPSD.get('*').get(lastparam, 0.0)
286 #print task + " " + lastparam + " " + str(value) + " " + str(upsize)
287 if val > 0:
288 #print "## " + task + " " + lastparam + " " + str(value) + " " + str(upsize) + " > " + str(float(value) * val * upsize)
289 return str(float(value) * val * UPSIZE)
290 val = UPSD.get(task, {}).get(lastparam, 0.0)
291 if val > 0:
292 #print "## " + task + " " + lastparam + " " + str(value) + " " + str(upsize) + " > " + str(float(value) * val * upsize)
293 return str(float(value) * val * UPSIZE)
294 return value
295
296
297def htmldecode(str):
298 dic = HTMLDIC
299 answer = str
300 for key in dic:
301 answer = answer.replace(key, dic[key])
302 return answer
303
304
305
306def processlayer(dom,flagdic,paramdic):
307 task = dom.getElementsByTagName("task")[0]
308 command = task.getAttribute('name')
309 params = task.getElementsByTagName("parameter")
310 paramdic['task'] = command
311 for p in params:
312 elements = p.getElementsByTagName("value") #sometimes there are empty <value> tags in workspace files
313 if len(elements) > 0:
314 nodes = elements[0].childNodes
315 if len(nodes) > 0:
316 paramdic[p.getAttribute('name')] = upsizeifnecessary(paramdic['task'],p.getAttribute('name'),nodes[0].data,UPSIZE)
317
318 flags = task.getElementsByTagName("flag")
319 for f in flags:
320 if (f.getAttribute('name') <> 'verbose') and (f.getAttribute('name') <> 'overwrite') and (f.getAttribute('name') <> 'quiet'):
321 flagdic [f.getAttribute('name')] = f.getAttribute('name')
322
323
324def processoverlay(dom,flagdic,paramdic):
325 params = dom.getElementsByTagName("parameter")
326 for p in params:
327 elements=p.getElementsByTagName("value") #sometimes there are empty <value> tags in workspace files
328 if len(elements) > 0:
329 paramdic[p.getAttribute('name')] = upsizeifnecessary(paramdic['task'],p.getAttribute('name'),elements[0].childNodes[0].data,UPSIZE)
330
331 flags = dom.getElementsByTagName("flag")
332 for f in flags:
333 if (f.getAttribute('name') <> 'verbose') and (f.getAttribute('name') <> 'overwrite') and (f.getAttribute('name') <> 'quiet'):
334 flagdic [f.getAttribute('name')] = f.getAttribute('name')
335
336
337def processlayers(dom,l):
338 # processing layers of a display. Layers are returned in the l array
339 for lay in dom:
340 if lay.getAttribute('checked') == '1':
341 paramdic = {}
342 flagdic = {}
343 opacity = lay.getAttribute('opacity')
344 if opacity.startswith('1'):
345 opacity = '1'
346 processlayer(lay,flagdic,paramdic)
347 l.insert(
348 0, (opacity, paramdic['task'] , paramdic, flagdic))
349
350def processoverlays(dom,l):
351 # processing layers of a display. Layers are returned in the l array
352 for lay in dom:
353 paramdic = {}
354 flagdic = {}
355 task = lay.getAttribute('name')
356 paramdic['task'] = task
357 opacity = '1'
358 processoverlay(lay,flagdic,paramdic)
359 l.append((opacity, paramdic['task'] , paramdic, flagdic))
360
361
362def readworkspace(wspname):
363 # READS WORKSPACE FILE
364 displaydic = {} # adding support for more displays
365 grass.verbose(_("Layers: "))
366 f = open(wspname, 'r')
367 textraw = f.read()
368 f.close()
369 text = cleanthisandthat(textraw)
370 model = xml.dom.minidom.parseString(text)
371 displays = model.getElementsByTagName("display")
372 for display in displays:
373 extents = []
374 layers = []
375 displayname = display.getAttribute('name')
376 extentall = display.getAttribute('extent')
377 extents = extentall.split(",")
378 dimall = display.getAttribute('dim')
379 dims = dimall.split(",")
380 extents.extend(dims)
381 layersmodel = display.getElementsByTagName('layer')
382 processlayers(layersmodel,layers)
383 overlaysmodel = display.getElementsByTagName('overlay')
384 processoverlays(overlaysmodel,layers)
385 layers.insert(0, extents)
386 displaydic[displayname]=layers
387 return displaydic
388
389def converttommfrom(value, fromunit):
390 #converting some basic units to mm
391 d = {'mm': 1, 'cm': 10, 'inch': 25.4}
392 return (value * d[fromunit])
393
394
395def getpagemargins(option, unit):
396 # we live on mm so convert user input as specified by unit
397 d = {}
398 if len(option) < 1:
399 d['l'] = 25.0
400 d['r'] = 25.0
401 d['t'] = 25.0
402 d['b'] = 25.0
403 return d
404 temp = option.split(",")
405 d['l'] = converttommfrom(float(temp[0]), unit)
406 if len(temp) < 4:
407 d['r'] = d['l']
408 d['t'] = d['l']
409 d['b'] = d['l']
410 return d
411 d['r'] = converttommfrom(float(temp[1]), unit)
412 d['t'] = converttommfrom(float(temp[2]), unit)
413 d['b'] = converttommfrom(float(temp[3]), unit)
414 return d
415
416
417def getpagedata(page):
418 # returns page description data dictionary for the selected page
419 d = PAGEDIC
420 w = d[page][0]
421 h = d[page][1]
422 return {'width': w, 'w': w, 'height': h, 'h': h, 'page': d[page][3], 'parameters': d[page][2]}
423
424
425def getpagesizes(page):
426 # return page sizes only in dictionary
427 d = PAGEDIC
428 w = d[page][0]
429 h = d[page][1]
430 return {'width': w, 'w': w, 'height': h, 'h': h}
431
432
433def dictodots(dic, dpi):
434 # takes all values from a dictionary and returns another dic with
435 # incoming mm values converted to dots
436 d2 = {}
437 for key in dic:
438 d2[key] = int(round(dic[key] / 25.4 * dpi))
439 return d2
440
441
442def dictomm(dic, dpi):
443 # takes all values from a dictionary and returns another dic with
444 # incoming dot values converted to mm
445 d2 = {}
446 for key in dic:
447 d2[key] = dic[key] * 25.4 / dpi
448 return d2
449
450
451def getmaxframeindots(marginsindots, pagesizesindots):
452 #returns available area on page in print dots (=pixels)
453 l = marginsindots['l']
454 r = pagesizesindots['w'] - marginsindots['r']
455 t = marginsindots['t']
456 b = pagesizesindots['h'] - marginsindots['b']
457 return {'t': t, 'b': b, 'l': l, 'r': r}
458
459
460def getmapUL(option, unit):
461 # processes user entered option for map area upper left corner
462 # r - right d - down from top left of page
463 d = {}
464 if len(option) < 1:
465 d['r'] = 0.0
466 d['d'] = 0.0
467 return d
468 temp = option.split(",")
469 d['r'] = converttommfrom(float(temp[0]), unit)
470 if len(temp) < 2:
471 d['d'] = d['r']
472 return d
473 d['d'] = converttommfrom(float(temp[1]), unit)
474 return d
475
476
477def getmapsizes(option, unit):
478 # processes user entered option for map size
479 # width and height
480 d = {}
481 if len(option) < 1:
482 d['w'] = 1000.0
483 d['h'] = 1000.0
484 return d
485 temp = option.split(",")
486 d['w'] = converttommfrom(float(temp[0]), unit)
487 if len(temp) < 2:
488 d['h'] = d['w']
489 return d
490 d['h'] = converttommfrom(float(temp[1]), unit)
491 return d
492
493
494def getmapframeindots(mapulindots, mapsizesindots, mxfd):
495 d = {}
496 # if map frame is bigger then area between margins it is
497 # shrinked to fit
498 mirrorwidth = abs(mxfd['r'] - mxfd['l']) + 1
499 mirrorheight = abs(mxfd['b'] - mxfd['t']) + 1
500 if mirrorwidth < mapsizesindots['w']:
501 wr = float(mirrorwidth) / mapsizesindots['w']
502 mapsizesindots['w'] = int(round(mapsizesindots['w'] * wr))
503 mapsizesindots['h'] = int(round(mapsizesindots['h'] * wr))
504 if mirrorheight < mapsizesindots['h']:
505 hr = float(mirrorheight) / mapsizesindots['h']
506 mapsizesindots['w'] = int(round(mapsizesindots['w'] * hr))
507 mapsizesindots['h'] = int(round(mapsizesindots['h'] * hr))
508 if mapulindots['r'] < 0:
509 realw = min(mirrorwidth, mapsizesindots['w'])
510 unusedhalf = int(round((mirrorwidth - realw) / 2))
511 d['l'] = mxfd['l'] + unusedhalf
512 d['r'] = mxfd['r'] - unusedhalf
513 else:
514 d['l'] = max(mxfd['l'], mapulindots['r'])
515 d['r'] = min(mxfd['r'], mapulindots['r'] + mapsizesindots['w'])
516 if mapulindots['d'] < 0:
517 realh = min(mirrorheight, mapsizesindots['h'])
518 unusedhalf = int(round((mirrorheight - realh) / 2))
519 d['t'] = mxfd['t'] + unusedhalf
520 d['b'] = mxfd['b'] - unusedhalf
521 else:
522 d['t'] = max(mxfd['t'], mapulindots['d'])
523 d['b'] = min(mxfd['b'], mapulindots['d'] + mapsizesindots['h'])
524 d['h'] = d['b'] - d['t'] + 1
525 d['w'] = d['r'] - d['l'] + 1
526 return d
527
528
529def render(astring, pdic, fdic):
530 grass.verbose(_("printws: Rendering into - BASE: " + LASTFILE))
531 grass.verbose(_("printws: Rendering command: " + astring))
532
533 pdic = copy.deepcopy(pdic) # parameters
534 fdic = copy.deepcopy(fdic) # flags
535
536 flags = ''
537 for key in fdic:
538 if key:
539 # grass.message(" KEY:"+str(key)) #was debug message
540 flags = flags + key
541 pdic['flags'] = flags
542
543 task = pdic['task']
544 del pdic['task']
545 # it should be replaced by grass.* API calls
546 # os.system(astring)
547 grass.run_command(task, **pdic) # migration is going on
548
549
550def getfontbypattern(kindpattern):
551 # truetype and others which are likely utf8 start with capital font names
552 # also some fonts with _ in names seemed to be utf8 for sure
553 # fonts with space and : and without capital letter are excluded from
554 # randomization
555 s = grass.read_command("d.fontlist")
556 safe = 'romans'
557 split = s.splitlines()
558 for l in split:
559 # check if it has : or space.
560 m = re.search('[\:\ ]+', l, re.IGNORECASE)
561 if not m:
562 m = re.search('(.*' + kindpattern + '.*)', l, re.IGNORECASE)
563 if m:
564 if (safe == 'romans') or (len(safe) > len(l)):
565 # change only to simpler variant
566 # simpler variant names are usually shorter
567 safe = l
568 if safe == 'romans':
569 for l in split:
570 # check if it has : or space.
571 m = re.search('[\:\ ]+', l, re.IGNORECASE)
572 if not m:
573 m = re.search('[A-Z].+[_].+', l, re.IGNORECASE)
574 if m:
575 safe = l
576 return safe # returns first suitable font, won't run through all of them
577 return safe
578 # print "printws: Selected font: " + safe
579
580
581def decodetextmacros(text, dic):
582 # Yes, indeed, macros ARE case sensitive !!!
583 result = text
584 for key in dic:
585 result = re.sub(key, dic[key], result)
586 return result
587
588#-----------------------------------------------------
589#-----------------------------------------------------
590#-----------------------------------------------------
591#------------------- MAIN ---------------------------
592#-----------------------------------------------------
593#-----------------------------------------------------
594#-----------------------------------------------------
595#-----------------------------------------------------
596
597
598def main():
599
600 # Following declarations MAY will used in future for sure.
601 global GISDBASE, LAYERCOUNT, LASTFILE
602
603 # Check if ImageMagick is available since it is essential
604 if os.name == 'nt':
605 if grass.find_program('magick', '-version'):
606 grass.verbose(_('printws: ImageMagick is available: OK!'))
607 else:
608 grass.fatal('ImageMagick is not accessible. See documentation of m.printws module for details.')
609 else:
610 if grass.find_program('convert', '-version'):
611 grass.verbose(_('printws: ImageMagick is available: OK!'))
612 else:
613 grass.fatal('ImageMagick is not accessible. See documentation of m.printws module for details.')
614
615 textmacros = {}
616 # %nam% macros are kept for backward compatibility
617 textmacros['%TIME24%'] = time.strftime("%H:%M:%S")
618 textmacros['%DATEYMD%'] = time.strftime("%Y.%m.%d")
619 textmacros['%DATEMDY%'] = time.strftime("%m/%d/%Y")
620 if not hasPwd:
621 textmacros['%USERNAME%'] = '(user unknown)'
622 else:
623 textmacros['%USERNAME%'] = pwd.getpwuid(os.getuid())[0]
624 # using $ for macros in the future. New items should be created
625 # exclusively as $macros later on
626 textmacros['\$TIME24'] = textmacros['%TIME24%']
627 textmacros['\$DATEYMD'] = textmacros['%DATEYMD%']
628 textmacros['\$DATEMDY'] = textmacros['%DATEMDY%']
629 textmacros['\$USERNAME'] = textmacros['%USERNAME%']
630
631 textmacros['\$SPC'] = u'\u00A0' #?? d.text won't display this at string end hmmm
632
633
634 # saves region for restoring at end
635 # doing with official method:
636 grass.use_temp_region()
637
638 # getting/setting screen/print dpi ratio
639
640 if len(options['dpi']) > 0:
641 dpioption = float(options['dpi'])
642 else:
643 dpioption = 150.0
644
645 if len(options['screendpi']) > 0:
646 screendpioption = float(options['screendpi'])
647 else:
648 screendpioption = 100.0
649
650 global UPSIZE
651 UPSIZE = float(dpioption) / float(screendpioption)
652
653 if len(options['input']) > 0:
654 displays = readworkspace(options['input'])
655 else:
656 quit()
657
658 textmacros['%GXW%'] = options['input']
659 textmacros['\$GXW'] = textmacros['%GXW%']
660
661 displaycounter = 0
662
663 # there could be multiple displays in a workspace so we loop them
664 # each display is a whole and independent file assembly
665 for key in displays:
666 textmacros['%DISPLAY%'] = key
667 textmacros['\$DISPLAY'] = key
668 grass.verbose(_('printws: rendering display: ' + key))
669 displaycounter = displaycounter + 1
670 layers = copy.deepcopy(displays[key])
671
672 # extracting extent information from layers dic and erase the item
673 # extents[0-5] w s e n minz maxz ; extents [6-9] window x y w h
674 extents = layers[0]
675 grass.verbose("m.printws: EXTENTS from workspace:" +
676 str(extents)) # was debug message
677 del layers[0]
678
679 regionmode = ''
680 if len(options['region']) > 0:
681 grass.run_command("g.region", region=options['region'])
682 regionmode = 'region'
683 else:
684 grass.run_command("g.region", "", w=extents[0], s=extents[
685 1], e=extents[2], n=extents[3])
686 regionmode = 'window'
687
688 # setting GRASS rendering environment
689
690 # dummy file name is defined since the following lines
691 # when switching on the cairo driver would create
692 # an empty map.png in the current directory
693 os.environ['GRASS_RENDER_FILE'] = os.path.join(TMPDIR, str(os.getpid(
694 )) + '_DIS_' + str(00) + '_GEN_' + str(00) + '.png')
695 os.environ['GRASS_RENDER_IMMEDIATE'] = 'cairo'
696 os.environ['GRASS_RENDER_FILE_READ'] = 'TRUE'
697 os.environ['GRASS_RENDER_TRANSPARENT'] = 'TRUE'
698 os.environ['GRASS_RENDER_FILE_COMPRESSION'] = '0'
699 os.environ['GRASS_RENDER_FILE_MAPPED'] = 'TRUE'
700
701 # reading further options and setting defaults
702
703 if len(options['page']) > 0:
704 pageoption = options['page']
705 else:
706 pageoption = 'A4landscape'
707
708 # parsing titles, etc.
709 if len(options['font']) > 0:
710 isAsterisk = options['font'].find('*')
711 if isAsterisk > 0:
712 titlefont = getfontbypattern(
713 options['font'].replace('*', ''))
714 else:
715 titlefont = options['font']
716 else:
717 titlefont = getfontbypattern('Open') # try to find something UTF-8
718 grass.verbose(_("printws: titlefont: " + titlefont))
719
720 if len(options['titlecolor']) > 0:
721 titlecolor = options['titlecolor']
722 else:
723 titlecolor = black
724
725 if len(options['maintitlesize']) > 0:
726 maintitlesize = converttommfrom(
727 float(options['maintitlesize']), options['layunits'])
728 else:
729 maintitlesize = 10.0
730
731 if len(options['subtitlesize']) > 0:
732 subtitlesize = converttommfrom(
733 float(options['subtitlesize']), options['layunits'])
734 else:
735 subtitlesize = 7.0
736
737
738 if len(options['pssize']) > 0:
739 pssize = converttommfrom(
740 float(options['pssize']), options['layunits'])
741 else:
742 pssize = 5.0
743
744 # Please fasten your seatbelts :) Calculations start here.
745 # -------------------------------------------------------------------
746
747 pagesizes = getpagesizes(pageoption)
748 pagesizesindots = dictodots(pagesizes, dpioption)
749
750 # Leave space for titles up and ps down - still in mm !!
751 upperspace = 0
752 subtitletop = 0
753 titletop = 0
754 if len(options['maintitle']) > 0:
755 titletop = 0.4 * maintitlesize
756 upperspace = upperspace + titletop + maintitlesize
757 if len(options['subtitle']) > 0:
758 subtitletop = upperspace + 0.4 * subtitlesize
759 upperspace = subtitletop + subtitlesize + 1
760 lowerspace = 0
761 if (len(options['psundercentral']) > 0) or (len(options['psunderright']) > 0) or (len(options['psunderleft']) > 0):
762 lowerspace = lowerspace + pssize + 2
763
764 os.environ['GRASS_RENDER_WIDTH'] = str(pagesizesindots['w'])
765 os.environ['GRASS_RENDER_HEIGHT'] = str(pagesizesindots['h'])
766
767 pagemargins = getpagemargins(
768 options['pagemargin'], options['layunits'])
769 pagemarginsindots = dictodots(pagemargins, dpioption)
770
771 # Getting max drawing area in dots
772 mxfd = getmaxframeindots(pagemarginsindots, pagesizesindots)
773 maxframe = str(mxfd['t']) + ',' + str(mxfd['b']) + \
774 ',' + str(mxfd['l']) + ',' + str(mxfd['r'])
775
776 # convert font size in mm to percentage for d.text
777 mxfmm = dictomm(mxfd, dpioption)
778 maintitlesize = float(maintitlesize) / (mxfmm['b'] - mxfmm['t']) * 100.0
779 subtitlesize = float(subtitlesize) / (mxfmm['b'] - mxfmm['t']) * 100.0
780
781 pssize = float(pssize) / (mxfmm['r'] - mxfmm['l']) * 100.0
782 # subtitle location is another issue
783 subtitletoppercent = 100.0 - subtitletop / \
784 (mxfmm['b'] - mxfmm['t']) * 100.0
785 titletoppercent = 100.0 - titletop / \
786 (mxfmm['b'] - mxfmm['t']) * 100.0
787
788 mapul = getmapUL(options['mapupperleft'], options['layunits'])
789 mapulindots = dictodots(mapul, dpioption)
790
791 mapsizes = getmapsizes(options['mapsize'], options['layunits'])
792 mapsizesindots = dictodots(mapsizes, dpioption)
793
794 # Correcting map area ratio to ratio of region edges
795 # OR screen window edges depeding on "regionmode"
796 # for later: grass.use_temp_region()
797 s = grass.read_command("g.region", flags='p')
798 kv = grass.parse_key_val(s, sep=':')
799 regioncols = float(kv['cols'].strip())
800 regionrows = float(kv['rows'].strip())
801 ewres = float(kv['ewres'].strip())
802 nsres = float(kv['nsres'].strip())
803 sizex = regioncols * ewres
804 sizey = regionrows * nsres
805
806 if regionmode == 'region':
807 hregionratio = sizex / sizey
808 grass.verbose(_("printws: REGION MODE - region "))
809 else: # surprisingly doing the SAME
810 # using screen window ratio for map area
811 # next line was a test for this but didn't help on gadgets positioning
812 #hregionratio = float(extents[8]) / float(extents[9])
813 hregionratio = sizex / sizey
814 grass.verbose(_("printws: REGION MODE - window"))
815 hmapratio = mapsizes['w'] / mapsizes['h']
816
817 grass.verbose(_("printws: raw mapsizes: " + str(mapsizesindots)))
818 grass.verbose(_("printws: hr: " + str(hregionratio)))
819 grass.verbose(_("printws: hm: " + str(hmapratio)))
820 if hregionratio > hmapratio:
821 grass.verbose(
822 _("printws: Map area height correction / " + str(hregionratio)))
823 mapsizes['h'] = mapsizes['w'] / hregionratio
824 elif hregionratio < hmapratio:
825 grass.verbose(
826 _("printws: Map area width correction * " + str(hregionratio)))
827 mapsizes['w'] = mapsizes['h'] * hregionratio
828 mapsizesindots = dictodots(mapsizes, dpioption)
829
830 # changing region resolution to match print resolution
831 # to eliminate unnecessary CPU heating/data transfer
832 # so as to make it faster
833 # with only invisible detail loss.
834 colsregiontomap = mapsizesindots['w'] / regioncols
835 rowsregiontomap = mapsizesindots['h'] / regionrows
836
837 newewres = ewres
838 newnsres = nsres
839
840 # if colsregiontomap < 1:
841 # CHANGE: also enables raising of resolution to prevent
842 # pixelation because of low resolution setting...
843 newewres = ewres / colsregiontomap
844 # if rowsregiontomap < 1:
845 newnsres = nsres / rowsregiontomap
846
847 grass.run_command("g.region", ewres=str(newewres), nsres=str(newnsres))
848
849 # !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
850 # it seems that d.wms uses the GRASS_REGION from region info
851 # others may also do so we set it
852 # !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
853 kv2 = {}
854 kv2['e'] = kv['east']
855 kv2['n'] = kv['north']
856 kv2['s'] = kv['south']
857 kv2['w'] = kv['west']
858 kv2['ewres'] = str(newewres)
859 kv2['nsres'] = str(newnsres)
860 #kv2['rows'] #- autocalculated to resolution - no need to set explicitly
861 #kv2['cols'] #- autocalculated to resolution - no need to set explicitly
862 #grass.message(str(kv2))
863 #grass.message(grass.region_env(**kv2))
864 #grass.message(s)
865 os.environ['GRASS_REGION'] = grass.region_env(**kv2)
866
867
868 # Getting mapping area in dots
869 # Correcting mxfd to leave space for title and subscript
870 pagemarginstitles = copy.deepcopy(pagemargins)
871 pagemarginstitles['t'] = pagemarginstitles['t'] + upperspace
872 pagemarginstitles['b'] = pagemarginstitles['b'] + lowerspace
873 pagemarginsindotstitles = dictodots(pagemarginstitles, dpioption)
874 mxfdtitles = getmaxframeindots(
875 pagemarginsindotstitles, pagesizesindots)
876
877 mpfd = getmapframeindots(mapulindots, mapsizesindots, mxfdtitles)
878 if pageoption == 'Flexi':
879 # For 'Flexi' page we modify the setup to create
880 # a page containing only the map without margins
881 grass.verbose(_("printws: pre Flexi mapframe: " + str(mpfd)))
882 mpfd['b'] = mpfd['b'] - mpfd['t']
883 mpfd['t'] = 0
884 mpfd['r'] = mpfd['r'] - mpfd['l']
885 mpfd['l'] = 0
886 os.environ['GRASS_RENDER_WIDTH'] = str(mpfd['r'])
887 os.environ['GRASS_RENDER_HEIGHT'] = str(mpfd['b'])
888 grass.verbose(_("printws: post Flexi mapframe: " + str(mpfd)))
889 mapframe = str(mpfd['t']) + ',' + str(mpfd['b']) + \
890 ',' + str(mpfd['l']) + ',' + str(mpfd['r'])
891
892 grass.verbose(_("printws: DOT VALUES ARE:"))
893 grass.verbose(_("printws: maxframe: " + str(mxfd)))
894 grass.verbose(_("printws: maxframe: " + maxframe))
895 grass.verbose(_("printws: mapframe: " + str(mpfd)))
896 grass.verbose(_("printws: mapframe: " + mapframe))
897 grass.verbose(_("printws: page: " + str(pagesizesindots)))
898 grass.verbose(_("printws: margins: " + str(pagemarginsindots)))
899 grass.verbose(_("printws: mapUL: " + str(mapulindots)))
900 grass.verbose(_("printws: mapsizes (corrected): " + str(mapsizesindots)))
901 grass.verbose(_("printws: ewres (corrected): " + str(newewres)))
902 grass.verbose(_("printws: nsres (corrected): " + str(newnsres)))
903
904 # quit()
905
906 # ------------------- INMAP -------------------
907
908
909
910 # Do not limit -map. It was: -limit map 720000000 before...
911 # So we can grow on disk as long as it lasts
912 imcommand = 'convert -limit memory 720000000 -units PixelsPerInch -density ' + \
913 str(int(dpioption)) + ' '
914
915 if os.name == 'nt':
916 imcommand = 'magick ' + imcommand
917
918 os.environ['GRASS_RENDER_FRAME'] = mapframe
919
920 grass.verbose(_("printws: Rendering: the following layers: "))
921 lastopacity = '-1'
922
923 for lay in layers:
924 grass.verbose(_(lay[1] + ' at: ' + lay[0] + ' opacity'))
925 if lay[0] == '1':
926 if lastopacity <> '1':
927 LASTFILE = os.path.join(TMPDIR, str(os.getpid()) + \
928 '_DIS_' + str(displaycounter) + '_GEN_' + \
929 str(LAYERCOUNT) + '.' + TMPFORMAT)
930 os.environ['GRASS_RENDER_FILE'] = LASTFILE
931 LAYERCOUNT = LAYERCOUNT + 2
932 imcommand = imcommand + ' ' + LASTFILE
933 lastopacity = '1'
934 render(lay[1], lay[2], lay[3])
935 else:
936 lastopacity = lay[0]
937 LASTFILE = os.path.join(TMPDIR, str(os.getpid(
938 )) + '_DIS_' + str(displaycounter) + '_GEN_' + str(LAYERCOUNT) + '.' + TMPFORMAT)
939 LAYERCOUNT = LAYERCOUNT + 2
940 os.environ['GRASS_RENDER_FILE'] = LASTFILE
941 grass.verbose("LAY: " + str(lay))
942 render(lay[1], lay[2], lay[3])
943 imcommand = imcommand + \
944 ' \( ' + LASTFILE + ' -channel a -evaluate multiply ' + \
945 lay[0] + ' +channel \)'
946
947 # setting resolution back to pre-script state since map rendering is
948 # finished
949 # CHANGE: not necessary anymore since we use temp_region now
950 # However, since we did set GRASS_REGION, let's redo it here
951
952 os.environ.pop('GRASS_REGION')
953
954
955 # ------------------- OUTSIDE MAP texts, etc -------------------
956 if pageoption =='Flexi':
957 grass.verbose(_('m.printws: WARNING! Felxi mode, will not create titles, etc...'))
958 else:
959 os.environ['GRASS_RENDER_FRAME'] = maxframe
960
961 dict = {}
962 dict['task'] = "d.text"
963 dict['color'] = titlecolor
964 dict['font'] = titlefont
965 dict['charset'] = "UTF-8"
966
967 if len(options['maintitle']) > 1:
968 dict['text'] = decodetextmacros(options['maintitle'], textmacros)
969 dict['at'] = "50," + str(titletoppercent)
970 dict['align'] = "uc"
971 dict['size'] = str(maintitlesize)
972 render(str(dict), dict, {})
973
974 if len(options['subtitle']) > 1:
975 dict['text'] = decodetextmacros(options['subtitle'], textmacros)
976 dict['at'] = "50," + str(subtitletoppercent)
977 dict['align'] = "uc"
978 dict['size'] = str(subtitlesize)
979 render(str(dict), dict, {})
980
981 dict['size'] = str(pssize)
982
983 if len(options['psundercentral']) > 1:
984 dict['text'] = decodetextmacros(
985 options['psundercentral'], textmacros)
986 dict['at'] = "50,1"
987 dict['align'] = "lc"
988 render(str(dict), dict, {})
989 if len(options['psunderleft']) > 1:
990 dict['text'] = decodetextmacros(options['psunderleft'], textmacros)
991 dict['at'] = "0,1"
992 dict['align'] = "ll"
993 render(str(dict), dict, {})
994 if len(options['psunderright']) > 1:
995 dict['text'] = decodetextmacros(
996 options['psunderright'], textmacros)
997 dict['at'] = "100,1"
998 dict['align'] = "lr"
999 render(str(dict), dict, {})
1000
1001 # ------------------- GENERATING OUTPUT FILE -------------------
1002
1003 if len(options['output']) > 1:
1004 output = options['output']
1005 else:
1006 output = 'map_' + str(os.getpid())
1007
1008 # remove extension AND display number and naming if any
1009 output = os.path.splitext(output)[0]
1010 output = re.sub('_DISPLAY_[0-9]+_.*', '', output)
1011
1012 if len(options['format']) > 1:
1013 extension = options['format']
1014 else:
1015 extension = 'pdf'
1016
1017 displaypart = ''
1018 if len(displays) > 1:
1019 displaypart = '_DISPLAY_' + str(displaycounter) + '_' + key
1020
1021 pagedata = getpagedata(pageoption)
1022 #params= ' -extent '+str(pagesizesindots['w'])+'x'+str(pagesizesindots['h'])+' -gravity center -compress jpeg -page '+pagedata['page']+' '+pagedata['parameters']+' -units PixelsPerInch -density '+str(dpioption)+'x'+str(dpioption)+' '
1023 params = ' -compress jpeg -quality 92 ' + \
1024 pagedata['parameters'] + ' -units PixelsPerInch -density ' + \
1025 str(int(dpioption)) + ' '
1026
1027 imcommand = imcommand + ' -layers flatten ' + params + \
1028 '"' + output + displaypart + '.' + extension + '"'
1029
1030 grass.verbose(
1031 _('printws: And the imagemagick command is... ' + imcommand))
1032 os.system(imcommand)
1033
1034 if not flags['d']:
1035 grass.verbose(_('printws: Doing graceful cleanup...'))
1036 os.system('rm ' + os.path.join(TMPDIR, str(os.getpid()) + '*_GEN_*'))
1037 if REMOVE_TMPDIR:
1038 try_rmdir(TMPDIR)
1039 else:
1040 grass.message("\n%s\n" % _(
1041 "printws: Temp dir remove failed. Do it yourself, please:"))
1042 sys.stderr.write('%s\n' % TMPDIR % ' <---- this')
1043
1044 # restoring pre-script region
1045 # - not necessary as we are using grass.use_temp_region() in the future
1046
1047 return 0
1048
1049if __name__ == "__main__":
1050 options, flags = grass.parser()
1051 global TMPDIR
1052 TMPDIR = tempfile.mkdtemp()
1053 atexit.register(cleanup)
1054 sys.exit(main())
Note: See TracBrowser for help on using the repository browser.