Quantum GIS API Documentation  1.7.5-Wroclaw
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
qgsmarkersymbollayerv2.cpp
Go to the documentation of this file.
1 
4 
5 #include "qgsrendercontext.h"
6 #include "qgsapplication.h"
7 #include "qgslogger.h"
8 #include "qgsproject.h"
9 
10 #include <QPainter>
11 #include <QSvgRenderer>
12 #include <QFileInfo>
13 #include <QDir>
14 
15 #include <cmath>
16 
17 // MSVC compiler doesn't have defined M_PI in math.h
18 #ifndef M_PI
19 #define M_PI 3.14159265358979323846
20 #endif
21 
22 #define DEG2RAD(x) ((x)*M_PI/180)
23 
24 
25 static QPointF _rotatedOffset( const QPointF& offset, double angle )
26 {
27  angle = DEG2RAD( angle );
28  double c = cos( angle ), s = sin( angle );
29  return QPointF( offset.x() * c - offset.y() * s, offset.x() * s + offset.y() * c );
30 }
31 
33 
34 QgsSimpleMarkerSymbolLayerV2::QgsSimpleMarkerSymbolLayerV2( QString name, QColor color, QColor borderColor, double size, double angle )
35 {
36  mName = name;
37  mColor = color;
39  mSize = size;
40  mAngle = angle;
41  mOffset = QPointF( 0, 0 );
42 }
43 
45 {
51 
52  if ( props.contains( "name" ) )
53  name = props["name"];
54  if ( props.contains( "color" ) )
55  color = QgsSymbolLayerV2Utils::decodeColor( props["color"] );
56  if ( props.contains( "color_border" ) )
57  borderColor = QgsSymbolLayerV2Utils::decodeColor( props["color_border"] );
58  if ( props.contains( "size" ) )
59  size = props["size"].toDouble();
60  if ( props.contains( "angle" ) )
61  angle = props["angle"].toDouble();
62 
63  QgsSimpleMarkerSymbolLayerV2* m = new QgsSimpleMarkerSymbolLayerV2( name, color, borderColor, size, angle );
64  if ( props.contains( "offset" ) )
65  m->setOffset( QgsSymbolLayerV2Utils::decodePoint( props["offset"] ) );
66  return m;
67 }
68 
69 
71 {
72  return "SimpleMarker";
73 }
74 
76 {
77  QColor brushColor = mColor;
78  QColor penColor = mBorderColor;
79  if ( context.alpha() < 1 )
80  {
81  penColor.setAlphaF( context.alpha() );
82  brushColor.setAlphaF( context.alpha() );
83  }
84  mBrush = QBrush( brushColor );
85  mPen = QPen( penColor );
86  mPen.setWidthF( context.outputLineWidth( mPen.widthF() ) );
87 
88  QColor selBrushColor = context.selectionColor();
89  QColor selPenColor = selBrushColor == mColor ? selBrushColor : mBorderColor;
90  if ( context.alpha() < 1 )
91  {
92  selBrushColor.setAlphaF( context.alpha() );
93  selPenColor.setAlphaF( context.alpha() );
94  }
95  mSelBrush = QBrush( selBrushColor );
96  mSelPen = QPen( selPenColor );
97  mSelPen.setWidthF( context.outputLineWidth( mPen.widthF() ) );
98 
99  bool hasDataDefinedRotation = context.renderHints() & QgsSymbolV2::DataDefinedRotation;
100  bool hasDataDefinedSize = context.renderHints() & QgsSymbolV2::DataDefinedSizeScale;
101 
102  // use caching only when:
103  // - the size and rotation is not data-defined
104  // - drawing to screen (not printer)
105  mUsingCache = !hasDataDefinedRotation && !hasDataDefinedSize && !context.renderContext().forceVectorOutput();
106 
107  // use either QPolygonF or QPainterPath for drawing
108  // TODO: find out whether drawing directly doesn't bring overhead - if not, use it for all shapes
109  if ( !prepareShape() ) // drawing as a polygon
110  {
111  if ( preparePath() ) // drawing as a painter path
112  {
113  // some markers can't be drawn as a polygon (circle, cross)
114  // For these set the selected border color to the selected color
115 
116  if ( mName != "circle" )
117  mSelPen.setColor( selBrushColor );
118  }
119  else
120  {
121  QgsDebugMsg( "unknown symbol" );
122  return;
123  }
124  }
125 
126  QMatrix transform;
127 
128  // scale the shape (if the size is not going to be modified)
129  if ( !hasDataDefinedSize )
130  {
131  double scaledSize = context.outputLineWidth( mSize );
132  if ( mUsingCache )
133  scaledSize *= context.renderContext().rasterScaleFactor();
134  double half = scaledSize / 2.0;
135  transform.scale( half, half );
136  }
137 
138  // rotate if the rotation is not going to be changed during the rendering
139  if ( !hasDataDefinedRotation && mAngle != 0 )
140  {
141  transform.rotate( mAngle );
142  }
143 
144  if ( !mPolygon.isEmpty() )
145  mPolygon = transform.map( mPolygon );
146  else
147  mPath = transform.map( mPath );
148 
149  if ( mUsingCache )
150  {
151  prepareCache( context );
152  }
153  else
154  {
155  mCache = QImage();
156  mSelCache = QImage();
157  }
158 }
159 
160 
162 {
163  double scaledSize = context.outputPixelSize( mSize );
164 
165  // calculate necessary image size for the cache
166  double pw = (( mPen.widthF() == 0 ? 1 : mPen.widthF() ) + 1 ) / 2 * 2; // make even (round up); handle cosmetic pen
167  int imageSize = (( int ) scaledSize + pw ) / 2 * 2 + 1; // make image width, height odd; account for pen width
168 
169  double center = (( double ) imageSize / 2 ) + 0.5; // add 1/2 pixel for proper rounding when the figure's coordinates are added
170 
171  mCache = QImage( QSize( imageSize, imageSize ), QImage::Format_ARGB32_Premultiplied );
172  mCache.fill( 0 );
173 
174  QPainter p;
175  p.begin( &mCache );
176  p.setRenderHint( QPainter::Antialiasing );
177  p.setBrush( mBrush );
178  p.setPen( mPen );
179  p.translate( QPointF( center, center ) );
180  drawMarker( &p, context );
181  p.end();
182 
183  // Construct the selected version of the Cache
184 
185  QColor selColor = context.selectionColor();
186 
187  mSelCache = QImage( QSize( imageSize, imageSize ), QImage::Format_ARGB32_Premultiplied );
188  mSelCache.fill( 0 );
189 
190  p.begin( &mSelCache );
191  p.setRenderHint( QPainter::Antialiasing );
192  p.setBrush( mSelBrush );
193  p.setPen( mSelPen );
194  p.translate( QPointF( center, center ) );
195  drawMarker( &p, context );
196  p.end();
197 
198  // Check that the selected version is different. If not, then re-render,
199  // filling the background with the selection color and using the normal
200  // colors for the symbol .. could be ugly!
201 
202  if ( mSelCache == mCache )
203  {
204  p.begin( &mSelCache );
205  p.setRenderHint( QPainter::Antialiasing );
206  p.fillRect( 0, 0, imageSize, imageSize, selColor );
207  p.setBrush( mBrush );
208  p.setPen( mPen );
209  p.translate( QPointF( center, center ) );
210  drawMarker( &p, context );
211  p.end();
212  }
213 }
214 
216 {
217 }
218 
220 {
221  mPolygon.clear();
222 
223  if ( mName == "rectangle" )
224  {
225  mPolygon = QPolygonF( QRectF( QPointF( -1, -1 ), QPointF( 1, 1 ) ) );
226  return true;
227  }
228  else if ( mName == "diamond" )
229  {
230  mPolygon << QPointF( -1, 0 ) << QPointF( 0, 1 )
231  << QPointF( 1, 0 ) << QPointF( 0, -1 );
232  return true;
233  }
234  else if ( mName == "pentagon" )
235  {
236  mPolygon << QPointF( sin( DEG2RAD( 288.0 ) ), - cos( DEG2RAD( 288.0 ) ) )
237  << QPointF( sin( DEG2RAD( 216.0 ) ), - cos( DEG2RAD( 216.0 ) ) )
238  << QPointF( sin( DEG2RAD( 144.0 ) ), - cos( DEG2RAD( 144.0 ) ) )
239  << QPointF( sin( DEG2RAD( 72.0 ) ), - cos( DEG2RAD( 72.0 ) ) )
240  << QPointF( 0, -1 );
241  return true;
242  }
243  else if ( mName == "triangle" )
244  {
245  mPolygon << QPointF( -1, 1 ) << QPointF( 1, 1 ) << QPointF( 0, -1 );
246  return true;
247  }
248  else if ( mName == "equilateral_triangle" )
249  {
250  mPolygon << QPointF( sin( DEG2RAD( 240.0 ) ), - cos( DEG2RAD( 240.0 ) ) )
251  << QPointF( sin( DEG2RAD( 120.0 ) ), - cos( DEG2RAD( 120.0 ) ) )
252  << QPointF( 0, -1 );
253  return true;
254  }
255  else if ( mName == "star" )
256  {
257  double sixth = 1.0 / 3;
258 
259  mPolygon << QPointF( 0, -1 )
260  << QPointF( -sixth, -sixth )
261  << QPointF( -1, -sixth )
262  << QPointF( -sixth, 0 )
263  << QPointF( -1, 1 )
264  << QPointF( 0, + sixth )
265  << QPointF( 1, 1 )
266  << QPointF( + sixth, 0 )
267  << QPointF( 1, -sixth )
268  << QPointF( + sixth, -sixth );
269  return true;
270  }
271  else if ( mName == "regular_star" )
272  {
273  double inner_r = cos( DEG2RAD( 72.0 ) ) / cos( DEG2RAD( 36.0 ) );
274 
275  mPolygon << QPointF( inner_r * sin( DEG2RAD( 324.0 ) ), - inner_r * cos( DEG2RAD( 324.0 ) ) ) // 324
276  << QPointF( sin( DEG2RAD( 288.0 ) ) , - cos( DEG2RAD( 288 ) ) ) // 288
277  << QPointF( inner_r * sin( DEG2RAD( 252.0 ) ), - inner_r * cos( DEG2RAD( 252.0 ) ) ) // 252
278  << QPointF( sin( DEG2RAD( 216.0 ) ) , - cos( DEG2RAD( 216.0 ) ) ) // 216
279  << QPointF( 0, inner_r ) // 180
280  << QPointF( sin( DEG2RAD( 144.0 ) ) , - cos( DEG2RAD( 144.0 ) ) ) // 144
281  << QPointF( inner_r * sin( DEG2RAD( 108.0 ) ), - inner_r * cos( DEG2RAD( 108.0 ) ) ) // 108
282  << QPointF( sin( DEG2RAD( 72.0 ) ) , - cos( DEG2RAD( 72.0 ) ) ) // 72
283  << QPointF( inner_r * sin( DEG2RAD( 36.0 ) ), - inner_r * cos( DEG2RAD( 36.0 ) ) ) // 36
284  << QPointF( 0, -1 ); // 0
285  return true;
286  }
287  else if ( mName == "arrow" )
288  {
289  mPolygon
290  << QPointF( 0, -1 )
291  << QPointF( 0.5, -0.5 )
292  << QPointF( 0.25, -0.25 )
293  << QPointF( 0.25, 1 )
294  << QPointF( -0.25, 1 )
295  << QPointF( -0.25, -0.5 )
296  << QPointF( -0.5, -0.5 );
297  return true;
298  }
299  else if ( mName == "filled_arrowhead" )
300  {
301  mPolygon << QPointF( 0, 0 ) << QPointF( -1, 1 ) << QPointF( -1, -1 );
302  return true;
303  }
304 
305  return false;
306 }
307 
309 {
310  mPath = QPainterPath();
311 
312  if ( mName == "circle" )
313  {
314  mPath.addEllipse( QRectF( -1, -1, 2, 2 ) ); // x,y,w,h
315  return true;
316  }
317  else if ( mName == "cross" )
318  {
319  mPath.moveTo( -1, 0 );
320  mPath.lineTo( 1, 0 ); // horizontal
321  mPath.moveTo( 0, -1 );
322  mPath.lineTo( 0, 1 ); // vertical
323  return true;
324  }
325  else if ( mName == "cross2" )
326  {
327  mPath.moveTo( -1, -1 );
328  mPath.lineTo( 1, 1 );
329  mPath.moveTo( 1, -1 );
330  mPath.lineTo( -1, 1 );
331  return true;
332  }
333  else if ( mName == "line" )
334  {
335  mPath.moveTo( 0, -1 );
336  mPath.lineTo( 0, 1 ); // vertical line
337  return true;
338  }
339  else if ( mName == "arrowhead" )
340  {
341  mPath.moveTo( 0, 0 );
342  mPath.lineTo( -1, -1 );
343  mPath.moveTo( 0, 0 );
344  mPath.lineTo( -1, 1 );
345  return true;
346  }
347 
348  return false;
349 }
350 
352 {
353  QgsRenderContext& rc = context.renderContext();
354  QPainter* p = rc.painter();
355  if ( !p )
356  {
357  return;
358  }
359 
360  QPointF off( context.outputLineWidth( mOffset.x() ), context.outputLineWidth( mOffset.y() ) );
361  if ( mAngle )
362  off = _rotatedOffset( off, mAngle );
363 
364  if ( mUsingCache )
365  {
366  // we will use cached image
367  QImage &img = context.selected() ? mSelCache : mCache;
368  double s = img.width() / context.renderContext().rasterScaleFactor();
369  p->drawImage( QRectF( point.x() - s / 2.0 + off.x(),
370  point.y() - s / 2.0 + off.y(),
371  s, s ), img );
372  }
373  else
374  {
375  QMatrix transform;
376 
377  // bool hasDataDefinedRotation = context.renderHints() & QgsSymbolV2::DataDefinedRotation;
378  bool hasDataDefinedSize = context.renderHints() & QgsSymbolV2::DataDefinedSizeScale;
379 
380  // move to the desired position
381  transform.translate( point.x() + off.x(), point.y() + off.y() );
382 
383  // resize if necessary
384  if ( hasDataDefinedSize )
385  {
386  double scaledSize = context.outputLineWidth( mSize );
387  double half = scaledSize / 2.0;
388  transform.scale( half, half );
389  }
390 
391  // rotate if necessary
392  if ( mAngle != 0 )
393  {
394  transform.rotate( mAngle );
395  }
396 
397  p->setBrush( context.selected() ? mSelBrush : mBrush );
398  p->setPen( context.selected() ? mSelPen : mPen );
399 
400  if ( !mPolygon.isEmpty() )
401  p->drawPolygon( transform.map( mPolygon ) );
402  else
403  p->drawPath( transform.map( mPath ) );
404  }
405 }
406 
407 
409 {
410  QgsStringMap map;
411  map["name"] = mName;
412  map["color"] = QgsSymbolLayerV2Utils::encodeColor( mColor );
413  map["color_border"] = QgsSymbolLayerV2Utils::encodeColor( mBorderColor );
414  map["size"] = QString::number( mSize );
415  map["angle"] = QString::number( mAngle );
416  map["offset"] = QgsSymbolLayerV2Utils::encodePoint( mOffset );
417  return map;
418 }
419 
421 {
423  m->setOffset( mOffset );
424  return m;
425 }
426 
428 {
429  if ( mPolygon.count() != 0 )
430  {
431  p->drawPolygon( mPolygon );
432  }
433  else
434  {
435  p->drawPath( mPath );
436  }
437 }
438 
439 
441 
442 
444 {
445  mPath = symbolNameToPath( name );
446  mSize = size;
447  mAngle = angle;
448  mOffset = QPointF( 0, 0 );
449 }
450 
451 
453 {
454  QString name = DEFAULT_SVGMARKER_NAME;
455  double size = DEFAULT_SVGMARKER_SIZE;
457 
458  if ( props.contains( "name" ) )
459  name = props["name"];
460  if ( props.contains( "size" ) )
461  size = props["size"].toDouble();
462  if ( props.contains( "angle" ) )
463  angle = props["angle"].toDouble();
464 
465  QgsSvgMarkerSymbolLayerV2* m = new QgsSvgMarkerSymbolLayerV2( name, size, angle );
466  if ( props.contains( "offset" ) )
467  m->setOffset( QgsSymbolLayerV2Utils::decodePoint( props["offset"] ) );
468  return m;
469 }
470 
471 
473 {
474  return "SvgMarker";
475 }
476 
478 {
479  double pictureSize = 0;
480  QgsRenderContext& rc = context.renderContext();
481 
482  if ( rc.painter() && rc.painter()->device() )
483  {
484  //correct QPictures DPI correction
485  pictureSize = context.outputLineWidth( mSize ) / rc.painter()->device()->logicalDpiX() * mPicture.logicalDpiX();
486  }
487  else
488  {
489  pictureSize = context.outputLineWidth( mSize );
490  }
491  QRectF rect( QPointF( -pictureSize / 2.0, -pictureSize / 2.0 ), QSizeF( pictureSize, pictureSize ) );
492  QSvgRenderer renderer( mPath );
493  QPainter painter( &mPicture );
494  renderer.render( &painter, rect );
495  QPainter selPainter( &mSelPicture );
496  selPainter.setRenderHint( QPainter::Antialiasing );
497  selPainter.setBrush( QBrush( context.selectionColor() ) );
498  selPainter.setPen( Qt::NoPen );
499  selPainter.drawEllipse( QPointF( 0, 0 ), pictureSize*0.6, pictureSize*0.6 );
500  renderer.render( &selPainter, rect );
501 
502  mOrigSize = mSize; // save in case the size would be data defined
503 }
504 
506 {
507 }
508 
509 
511 {
512  QPainter* p = context.renderContext().painter();
513  if ( !p )
514  {
515  return;
516  }
517 
518  p->save();
519  QPointF outputOffset = QPointF( context.outputLineWidth( mOffset.x() ), context.outputLineWidth( mOffset.y() ) );
520  if ( mAngle )
521  outputOffset = _rotatedOffset( outputOffset, mAngle );
522  p->translate( point + outputOffset );
523 
525  {
526  double s = mSize / mOrigSize;
527  p->scale( s, s );
528  }
529 
530  if ( mAngle != 0 )
531  p->rotate( mAngle );
532 
533  QPicture &pct = context.selected() ? mSelPicture : mPicture;
534  p->drawPicture( 0, 0, pct );
535 
536  p->restore();
537 }
538 
539 
541 {
542  QgsStringMap map;
543  map["name"] = symbolPathToName( mPath );
544  map["size"] = QString::number( mSize );
545  map["angle"] = QString::number( mAngle );
546  map["offset"] = QgsSymbolLayerV2Utils::encodePoint( mOffset );
547  return map;
548 }
549 
551 {
553  m->setOffset( mOffset );
554  return m;
555 }
556 
557 
559 {
560  // copied from QgsMarkerCatalogue - TODO: unify
561  QStringList list;
562  QStringList svgPaths = QgsApplication::svgPaths();
563 
564  for ( int i = 0; i < svgPaths.size(); i++ )
565  {
566  QDir dir( svgPaths[i] );
567  foreach( QString item, dir.entryList( QDir::Dirs | QDir::NoDotAndDotDot ) )
568  {
569  svgPaths.insert( i + 1, dir.path() + "/" + item );
570  }
571 
572  foreach( QString item, dir.entryList( QStringList( "*.svg" ), QDir::Files ) )
573  {
574  // TODO test if it is correct SVG
575  list.append( dir.path() + "/" + item );
576  }
577  }
578  return list;
579 }
580 
582 {
583  // copied from QgsSymbol::setNamedPointSymbol - TODO: unify
584 
585  // we might have a full path...
586  if ( QFile( name ).exists() )
587  return QFileInfo( name ).canonicalFilePath();
588 
589  // SVG symbol not found - probably a relative path was used
590 
591  QStringList svgPaths = QgsApplication::svgPaths();
592  for ( int i = 0; i < svgPaths.size(); i++ )
593  {
594  QgsDebugMsg( "SvgPath: " + svgPaths[i] );
595  QFileInfo myInfo( name );
596  QString myFileName = myInfo.fileName(); // foo.svg
597  QString myLowestDir = myInfo.dir().dirName();
598  QString myLocalPath = svgPaths[i] + "/" + myLowestDir + "/" + myFileName;
599 
600  QgsDebugMsg( "Alternative svg path: " + myLocalPath );
601  if ( QFile( myLocalPath ).exists() )
602  {
603  QgsDebugMsg( "Svg found in alternative path" );
604  return QFileInfo( myLocalPath ).canonicalFilePath();
605  }
606  else if ( myInfo.isRelative() )
607  {
608  QFileInfo pfi( QgsProject::instance()->fileName() );
609  QString alternatePath = pfi.canonicalPath() + QDir::separator() + name;
610  if ( pfi.exists() && QFile( alternatePath ).exists() )
611  {
612  QgsDebugMsg( "Svg found in alternative path" );
613  return QFileInfo( alternatePath ).canonicalFilePath();
614  }
615  else
616  {
617  QgsDebugMsg( "Svg not found in project path" );
618  }
619  }
620  else
621  {
622  //couldnt find the file, no happy ending :-(
623  QgsDebugMsg( "Computed alternate path but no svg there either" );
624  }
625  }
626  return QString();
627 }
628 
630 {
631  // copied from QgsSymbol::writeXML
632 
633  QFileInfo fi( path );
634  if ( !fi.exists() )
635  return path;
636 
637  path = fi.canonicalFilePath();
638 
639  QStringList svgPaths = QgsApplication::svgPaths();
640 
641  for ( int i = 0; i < svgPaths.size(); i++ )
642  {
643  QString dir = QFileInfo( svgPaths[i] ).canonicalFilePath();
644 
645  if ( !dir.isEmpty() && path.startsWith( dir ) )
646  {
647  path = path.mid( dir.size() );
648  break;
649  }
650  }
651 
652  return path;
653 }
654 
655 
657 
658 QgsFontMarkerSymbolLayerV2::QgsFontMarkerSymbolLayerV2( QString fontFamily, QChar chr, double pointSize, QColor color, double angle )
659 {
661  mChr = chr;
662  mColor = color;
663  mAngle = angle;
664  mSize = pointSize;
665  mOffset = QPointF( 0, 0 );
666 }
667 
669 {
671  QChar chr = DEFAULT_FONTMARKER_CHR;
672  double pointSize = DEFAULT_FONTMARKER_SIZE;
675 
676  if ( props.contains( "font" ) )
677  fontFamily = props["font"];
678  if ( props.contains( "chr" ) && props["chr"].length() > 0 )
679  chr = props["chr"].at( 0 );
680  if ( props.contains( "size" ) )
681  pointSize = props["size"].toDouble();
682  if ( props.contains( "color" ) )
683  color = QgsSymbolLayerV2Utils::decodeColor( props["color"] );
684  if ( props.contains( "angle" ) )
685  angle = props["angle"].toDouble();
686 
687  QgsFontMarkerSymbolLayerV2* m = new QgsFontMarkerSymbolLayerV2( fontFamily, chr, pointSize, color, angle );
688  if ( props.contains( "offset" ) )
689  m->setOffset( QgsSymbolLayerV2Utils::decodePoint( props["offset"] ) );
690  return m;
691 }
692 
694 {
695  return "FontMarker";
696 }
697 
699 {
700  mFont = QFont( mFontFamily );
701  mFont.setPixelSize( context.outputLineWidth( mSize ) );
702  QFontMetrics fm( mFont );
703  mChrOffset = QPointF( fm.width( mChr ) / 2, -fm.ascent() / 2 );
704 
705  mOrigSize = mSize; // save in case the size would be data defined
706 }
707 
709 {
710 }
711 
713 {
714  QPainter* p = context.renderContext().painter();
715  QColor penColor = context.selected() ? context.selectionColor() : mColor;
716  penColor.setAlphaF( context.alpha() );
717  p->setPen( penColor );
718  p->setFont( mFont );
719 
720 
721  p->save();
722  QPointF outputOffset = QPointF( context.outputLineWidth( mOffset.x() ), context.outputLineWidth( mOffset.y() ) );
723  if ( mAngle )
724  outputOffset = _rotatedOffset( outputOffset, mAngle );
725  p->translate( point + outputOffset );
726 
728  {
729  double s = mSize / mOrigSize;
730  p->scale( s, s );
731  }
732 
733  if ( mAngle != 0 )
734  p->rotate( mAngle );
735 
736  p->drawText( -mChrOffset, mChr );
737  p->restore();
738 }
739 
741 {
742  QgsStringMap props;
743  props["font"] = mFontFamily;
744  props["chr"] = mChr;
745  props["size"] = QString::number( mSize );
746  props["color"] = QgsSymbolLayerV2Utils::encodeColor( mColor );
747  props["angle"] = QString::number( mAngle );
748  props["offset"] = QgsSymbolLayerV2Utils::encodePoint( mOffset );
749  return props;
750 }
751 
753 {
755  m->setOffset( mOffset );
756  return m;
757 }