Discard empty segments
[GPXSee.git] / src / map / IMG / rgnfile.cpp
blobae7a7f8135dc5cfb8c2ba7c64d3ba42e7c4a00f8
1 #include "common/rectc.h"
2 #include "common/garmin.h"
3 #include "common/hash.h"
4 #include "deltastream.h"
5 #include "huffmanstream.h"
6 #include "style.h"
7 #include "lblfile.h"
8 #include "netfile.h"
9 #include "nodfile.h"
10 #include "rgnfile.h"
12 using namespace Garmin;
13 using namespace IMG;
15 #define MASK(bits) ((1U << (bits)) - 1U)
17 static quint64 pointId(const QPoint &pos, quint32 type, const QString &label)
19 quint64 hash = qHash(pos) ^ qHash(label);
20 quint64 id = ((quint64)type)<<40 | (hash & 0xFFFFFFFFFF);
22 // Increase rendering priorities for some special items
23 if (!Style::isCountry(type) && !Style::isMarina(type))
24 id |= 1ULL<<63;
26 return id;
29 static double d2m(quint32 val, quint32 flags)
31 return (flags & 1) ? val / 10.0 : val;
34 RGNFile::~RGNFile()
36 delete _huffmanTable;
39 bool RGNFile::readRasterInfo(Handle &hdl, const LBLFile *lbl, quint32 size,
40 MapData::Poly *poly) const
42 quint32 id;
43 quint32 top, right, bottom, left;
45 if (!(lbl && lbl->imageIdSize()))
46 return false;
47 if (size < lbl->imageIdSize() + 16U)
48 return false;
50 if (!(readVUInt32(hdl, lbl->imageIdSize(), id)
51 && readUInt32(hdl, top) && readUInt32(hdl, right)
52 && readUInt32(hdl, bottom) && readUInt32(hdl, left)))
53 return false;
55 poly->raster = Raster(lbl, id, QRect(QPoint(left, top), QPoint(right,
56 bottom)));
58 return true;
61 bool RGNFile::readDepthInfo(Handle &hdl, quint8 flags, quint32 size,
62 MapData::Point *point) const
64 quint32 depth = 0;
65 quint32 units = (flags >> 3) & 3;
66 quint32 val;
68 if (!size) {
69 depth = flags & 0x3f;
70 units = (flags >> 5) & 2;
71 } else if (size == 1) {
72 if (!readUInt8(hdl, val))
73 return false;
74 depth = val | ((quint32)flags & 7) << 8;
75 } else if (size < 4) {
76 Q_ASSERT(!(flags & 4));
77 if (!readVUInt32(hdl, size, val))
78 return false;
79 depth = val | ((quint32)flags & 3) << (size * 8);
80 } else
81 return false;
83 point->label = QString::number(d2m(depth, units));
85 return true;
88 bool RGNFile::readObstructionInfo(Handle &hdl, quint8 flags, quint32 size,
89 MapData::Point *point) const
91 quint32 val, rb = size;
92 quint32 units = (flags >> 3) & 3;
94 if (!size)
95 return false;
97 if ((flags & 7) == 7) {
98 if (!readUInt8(hdl, val))
99 return false;
100 rb--;
102 if (!readVUInt32(hdl, rb, val))
103 return false;
105 point->label = QString::number(d2m(val, units));
107 return true;
110 bool RGNFile::readLabel(Handle &hdl, const LBLFile *lbl, Handle &lblHdl,
111 quint8 flags, quint32 size, MapData::Point *point) const
113 if (!(flags & 1))
114 return true;
115 if (!lbl)
116 return false;
118 point->label = lbl->label(lblHdl, this, hdl, size);
119 point->classLabel = true;
121 return true;
124 bool RGNFile::readClassFields(Handle &hdl, SegmentType segmentType,
125 void *object, const LBLFile *lbl, Handle &lblHdl) const
127 quint8 flags;
128 quint32 rs = 0;
129 MapData::Poly *poly = (segmentType == Polygon)
130 ? (MapData::Poly *) object : 0;
131 MapData::Point *point = (segmentType == Point)
132 ? (MapData::Point *) object : 0;
134 if (!readByte(hdl, &flags))
135 return false;
137 switch (flags >> 5) {
138 case 4:
139 rs = 1;
140 break;
141 case 5:
142 rs = 2;
143 break;
144 case 6:
145 rs = 3;
146 break;
147 case 7:
148 if (!readVUInt32(hdl, rs))
149 return false;
150 break;
153 quint32 off = pos(hdl);
155 if (poly && Style::isRaster(poly->type))
156 readRasterInfo(hdl, lbl, rs, poly);
157 if (point && Style::isDepthPoint(point->type))
158 readDepthInfo(hdl, flags, rs, point);
159 if (point && Style::isObstructionPoint(point->type))
160 readObstructionInfo(hdl, flags, rs, point);
161 if (point && !Style::isMarinePoint(point->type))
162 readLabel(hdl, lbl, lblHdl, flags, rs, point);
164 return seek(hdl, off + rs);
167 bool RGNFile::skipLclFields(Handle &hdl, const quint32 flags[3]) const
169 quint32 bitfield = 0xFFFFFFFF;
171 if (flags[0] & 0x20000000)
172 if (!readVBitfield32(hdl, bitfield))
173 return false;
175 for (int i = 0, j = 0; i < 29; i++) {
176 if ((flags[0] >> i) & 1) {
177 if (bitfield & 1) {
178 quint32 m = flags[(j >> 4) + 1] >> ((j * 2) & 0x1e) & 3;
180 quint32 skip = 0;
181 if (m == 3) {
182 if (!readVUInt32(hdl, skip))
183 return false;
184 } else
185 skip = m + 1;
186 if (!seek(hdl, pos(hdl) + skip))
187 return false;
189 bitfield >>= 1;
190 j++;
194 return true;
197 bool RGNFile::skipGblFields(Handle &hdl, quint32 flags) const
199 int cnt = 0;
201 do {
202 cnt = cnt + (flags & 3);
203 flags = flags >> 2;
204 } while (flags != 0);
206 return seek(hdl, pos(hdl) + cnt);
209 bool RGNFile::load(Handle &hdl)
211 quint16 hdrLen;
213 if (!(seek(hdl, _gmpOffset) && readUInt16(hdl, hdrLen)
214 && seek(hdl, _gmpOffset + 0x15) && readUInt32(hdl, _base.offset)
215 && readUInt32(hdl, _base.size)))
216 return false;
218 if (hdrLen >= 0x71) {
219 if (!(readUInt32(hdl, _polygons.offset) && readUInt32(hdl, _polygons.size)
220 && seek(hdl, _gmpOffset + 0x29) && readUInt32(hdl, _polygonsGblFlags)
221 && readUInt32(hdl, _polygonsLclFlags[0])
222 && readUInt32(hdl, _polygonsLclFlags[1])
223 && readUInt32(hdl, _polygonsLclFlags[2])
224 && readUInt32(hdl, _lines.offset) && readUInt32(hdl, _lines.size)
225 && seek(hdl, _gmpOffset + 0x45) && readUInt32(hdl, _linesGblFlags)
226 && readUInt32(hdl, _linesLclFlags[0])
227 && readUInt32(hdl, _linesLclFlags[1])
228 && readUInt32(hdl, _linesLclFlags[2])
229 && readUInt32(hdl, _points.offset) && readUInt32(hdl, _points.size)
230 && seek(hdl, _gmpOffset + 0x61) && readUInt32(hdl, _pointsGblFlags)
231 && readUInt32(hdl, _pointsLclFlags[0])
232 && readUInt32(hdl, _pointsLclFlags[1])
233 && readUInt32(hdl, _pointsLclFlags[2])))
234 return false;
237 if (hdrLen >= 0x7D) {
238 quint32 info;
239 if (!(seek(hdl, _gmpOffset + 0x71) && readUInt32(hdl, _dict.offset)
240 && readUInt32(hdl, _dict.size) && readUInt32(hdl, info)))
241 return false;
243 if (_dict.size && _dict.offset && (info & 0x1E)) {
244 _huffmanTable = new HuffmanTable(((info >> 1) & 0xF) - 1);
245 if (!_huffmanTable->load(this, hdl))
246 return false;
250 return true;
253 void RGNFile::clear()
255 delete _huffmanTable;
256 _huffmanTable = 0;
259 bool RGNFile::polyObjects(Handle &hdl, const SubDiv *subdiv,
260 SegmentType segmentType, const LBLFile *lbl, Handle &lblHdl, NETFile *net,
261 Handle &netHdl, QList<MapData::Poly> *polys) const
263 const SubDiv::Segment &segment = (segmentType == Line)
264 ? subdiv->lines() : subdiv->polygons();
266 if (!segment.isValid())
267 return true;
268 if (!seek(hdl, segment.offset()))
269 return false;
271 quint32 labelPtr;
272 quint8 type, len8, bitstreamInfo;
273 qint16 lon, lat;
274 quint16 len;
276 while (pos(hdl) < segment.end()) {
277 MapData::Poly poly;
279 if (!(readByte(hdl, &type) && readUInt24(hdl, labelPtr)
280 && readInt16(hdl, lon) && readInt16(hdl, lat)))
281 return false;
282 if (type & 0x80) {
283 if (!readUInt16(hdl, len))
284 return false;
285 } else {
286 if (!readByte(hdl, &len8))
287 return false;
288 len = len8;
290 if (!readByte(hdl, &bitstreamInfo))
291 return false;
293 poly.type = (segmentType == Polygon)
294 ? ((quint32)(type & 0x7F)) << 8 : ((quint32)(type & 0x3F)) << 8;
295 if (segmentType == Line && type & 0x40)
296 poly.oneway = true;
299 QPoint pos(subdiv->lon() + LS(lon, 24-subdiv->bits()),
300 subdiv->lat() + LS(lat, 24-subdiv->bits()));
301 Coordinates c(toWGS24(pos.x()), toWGS24(pos.y()));
302 poly.boundingRect = RectC(c, c);
303 poly.points.append(QPointF(c.lon(), c.lat()));
305 qint32 lonDelta, latDelta;
306 DeltaStream stream(*this, hdl, len);
307 if (!stream.init(bitstreamInfo, labelPtr & 0x400000, false))
308 return false;
309 while (stream.readNext(lonDelta, latDelta)) {
310 if (!(lonDelta || latDelta))
311 continue;
312 pos.rx() += LS(lonDelta, (24-subdiv->bits()));
313 if (pos.rx() >= 0x800000 && subdiv->lon() >= 0)
314 pos.rx() = 0x7fffff;
315 pos.ry() += LS(latDelta, (24-subdiv->bits()));
317 Coordinates c(toWGS24(pos.x()), toWGS24(pos.y()));
318 poly.points.append(QPointF(c.lon(), c.lat()));
319 poly.boundingRect = poly.boundingRect.united(c);
321 if (!(stream.atEnd() && stream.flush()))
322 return false;
324 if (lbl && (labelPtr & 0x3FFFFF)) {
325 if (labelPtr & 0x800000) {
326 quint32 lblOff;
327 if (net && net->lblOffset(netHdl, labelPtr & 0x3FFFFF, lblOff)
328 && lblOff)
329 poly.label = lbl->label(lblHdl, lblOff, false, true,
330 Style::isContourLine(poly.type));
331 } else
332 poly.label = lbl->label(lblHdl, labelPtr & 0x3FFFFF, false,
333 true, Style::isContourLine(poly.type));
336 polys->append(poly);
339 return true;
342 bool RGNFile::extPolyObjects(Handle &hdl, const SubDiv *subdiv, quint32 shift,
343 SegmentType segmentType, const LBLFile *lbl, Handle &lblHdl,
344 QList<MapData::Poly> *polys) const
346 quint32 labelPtr, len;
347 quint8 type, subtype;
348 qint16 lon, lat;
349 const SubDiv::Segment &segment = (segmentType == Line)
350 ? subdiv->extLines() : subdiv->extPolygons();
353 if (!segment.isValid())
354 return true;
355 if (!seek(hdl, segment.offset()))
356 return false;
358 while (pos(hdl) < segment.end()) {
359 MapData::Poly poly;
360 QPoint pos;
362 if (!(readByte(hdl, &type) && readByte(hdl, &subtype)
363 && readInt16(hdl, lon) && readInt16(hdl, lat)
364 && readVUInt32(hdl, len)))
365 return false;
366 Q_ASSERT(SubFile::pos(hdl) + len <= segment.end());
368 poly.type = 0x10000 | (quint16(type)<<8) | (subtype & 0x1F);
369 labelPtr = 0;
371 if (_huffmanTable) {
372 pos = QPoint(LS(subdiv->lon(), 8) + LS(lon, 32-subdiv->bits()),
373 LS(subdiv->lat(), 8) + LS(lat, (32-subdiv->bits())));
375 qint32 lonDelta, latDelta;
376 BitStream4F bs(*this, hdl, len);
377 HuffmanDeltaStreamF stream(bs, *_huffmanTable);
378 if (!stream.init(segmentType == Line))
379 return false;
381 if (shift) {
382 if (!stream.readOffset(lonDelta, latDelta))
383 return false;
384 pos = QPoint(pos.x() | LS(lonDelta, 32-subdiv->bits()-shift),
385 pos.y() | LS(latDelta, 32-subdiv->bits()-shift));
387 Coordinates c(toWGS32(pos.x()), toWGS32(pos.y()));
388 poly.boundingRect = RectC(c, c);
389 poly.points.append(QPointF(c.lon(), c.lat()));
391 while (stream.readNext(lonDelta, latDelta)) {
392 if (!(lonDelta | latDelta))
393 break;
395 pos.rx() += LS(lonDelta, 32-subdiv->bits()-shift);
396 pos.ry() += LS(latDelta, 32-subdiv->bits()-shift);
398 Coordinates c(toWGS32(pos.x()), toWGS32(pos.y()));
399 poly.points.append(QPointF(c.lon(), c.lat()));
400 poly.boundingRect = poly.boundingRect.united(c);
403 if (!(stream.atEnd() && bs.flush()))
404 return false;
405 } else {
406 pos = QPoint(subdiv->lon() + LS(lon, 24-subdiv->bits()),
407 subdiv->lat() + LS(lat, 24-subdiv->bits()));
408 Coordinates c(toWGS24(pos.x()), toWGS24(pos.y()));
409 poly.boundingRect = RectC(c, c);
410 poly.points.append(QPointF(c.lon(), c.lat()));
412 quint8 bitstreamInfo;
413 if (!readByte(hdl, &bitstreamInfo))
414 return false;
416 qint32 lonDelta, latDelta;
417 DeltaStream stream(*this, hdl, len - 1);
418 if (!stream.init(bitstreamInfo, false, true))
419 return false;
420 while (stream.readNext(lonDelta, latDelta)) {
421 if (!(lonDelta || latDelta))
422 continue;
423 pos.rx() += LS(lonDelta, 24-subdiv->bits());
424 if (pos.rx() >= 0x800000 && subdiv->lon() >= 0)
425 pos.rx() = 0x7fffff;
426 pos.ry() += LS(latDelta, 24-subdiv->bits());
428 Coordinates c(toWGS24(pos.x()), toWGS24(pos.y()));
429 poly.points.append(QPointF(c.lon(), c.lat()));
430 poly.boundingRect = poly.boundingRect.united(c);
432 if (!(stream.atEnd() && stream.flush()))
433 return false;
436 if (subtype & 0x20 && !readUInt24(hdl, labelPtr))
437 return false;
438 if (subtype & 0x80 && !readClassFields(hdl, segmentType, &poly, lbl,
439 lblHdl))
440 return false;
441 if (subtype & 0x40 && !skipLclFields(hdl, segmentType == Line
442 ? _linesLclFlags : _polygonsLclFlags))
443 return false;
444 quint32 gblFlags = (segmentType == Line)
445 ? _linesGblFlags : _polygonsGblFlags;
446 if (gblFlags && !skipGblFields(hdl, gblFlags))
447 return false;
449 if (lbl && (labelPtr & 0x3FFFFF))
450 poly.label = lbl->label(lblHdl, labelPtr & 0x3FFFFF, false, true,
451 Style::isContourLine(poly.type));
453 polys->append(poly);
456 return true;
459 bool RGNFile::pointObjects(Handle &hdl, const SubDiv *subdiv,
460 SegmentType segmentType, const LBLFile *lbl, Handle &lblHdl,
461 QList<MapData::Point> *points) const
463 const SubDiv::Segment &segment = (segmentType == IndexedPoint)
464 ? subdiv->idxPoints() : subdiv->points();
467 if (!segment.isValid())
468 return true;
469 if (!seek(hdl, segment.offset()))
470 return false;
472 while (pos(hdl) < segment.end()) {
473 MapData::Point point;
474 quint8 type, subtype;
475 qint16 lon, lat;
476 quint32 labelPtr;
478 if (!(readByte(hdl, &type) && readUInt24(hdl, labelPtr)
479 && readInt16(hdl, lon) && readInt16(hdl, lat)))
480 return false;
481 if (labelPtr & 0x800000) {
482 if (!readByte(hdl, &subtype))
483 return false;
484 } else
485 subtype = 0;
487 QPoint pos(subdiv->lon() + LS(lon, 24-subdiv->bits()),
488 subdiv->lat() + LS(lat, 24-subdiv->bits()));
490 point.type = (quint16)type<<8 | subtype;
491 point.coordinates = Coordinates(toWGS24(pos.x()), toWGS24(pos.y()));
492 if (lbl && (labelPtr & 0x3FFFFF))
493 point.label = lbl->label(lblHdl, labelPtr & 0x3FFFFF,
494 labelPtr & 0x400000, !(Style::isCountry(point.type)
495 || Style::isState(point.type)), Style::isSpot(point.type));
496 point.id = pointId(pos, point.type, point.label.text());
498 points->append(point);
501 return true;
504 bool RGNFile::extPointObjects(Handle &hdl, const SubDiv *subdiv,
505 const LBLFile *lbl, Handle &lblHdl, QList<MapData::Point> *points) const
507 const SubDiv::Segment &segment = subdiv->extPoints();
510 if (!segment.isValid())
511 return true;
512 if (!seek(hdl, segment.offset()))
513 return false;
515 while (pos(hdl) < segment.end()) {
516 MapData::Point point;
517 qint16 lon, lat;
518 quint8 type, subtype;
519 quint32 labelPtr = 0;
521 if (!(readByte(hdl, &type) && readByte(hdl, &subtype)
522 && readInt16(hdl, lon) && readInt16(hdl, lat)))
523 return false;
525 point.type = 0x10000 | (((quint32)type)<<8) | (subtype & 0x1F);
527 if (subtype & 0x20 && !readUInt24(hdl, labelPtr))
528 return false;
529 if (subtype & 0x80 && !readClassFields(hdl, Point, &point, lbl, lblHdl))
530 return false;
531 if (subtype & 0x40 && !skipLclFields(hdl, _pointsLclFlags))
532 return false;
533 if (_pointsGblFlags && !skipGblFields(hdl, _pointsGblFlags))
534 return false;
536 QPoint p(subdiv->lon() + LS(lon, 24-subdiv->bits()),
537 subdiv->lat() + LS(lat, 24-subdiv->bits()));
539 // Discard NT points breaking style draw order logic (and causing huge
540 // performance drawback)
541 if (point.type == 0x11400)
542 continue;
544 point.coordinates = Coordinates(toWGS24(p.x()), toWGS24(p.y()));
545 if (lbl && (labelPtr & 0x3FFFFF))
546 point.label = lbl->label(lblHdl, labelPtr & 0x3FFFFF);
547 point.id = pointId(p, point.type, point.label.text());
549 points->append(point);
552 return true;
555 bool RGNFile::links(Handle &hdl, const SubDiv *subdiv, quint32 shift,
556 const NETFile *net, Handle &netHdl, const NODFile *nod, Handle &nodHdl,
557 Handle &nodHdl2, const LBLFile *lbl, Handle &lblHdl,
558 QList<MapData::Poly> *lines) const
560 quint32 size, blockIndexId;
561 quint8 flags;
562 const SubDiv::Segment &segment = subdiv->roadReferences();
564 if (!net || !nod)
565 return false;
566 if (!segment.isValid())
567 return true;
568 if (!seek(hdl, segment.offset()))
569 return false;
571 while (pos(hdl) < segment.end()) {
572 if (!readVUInt32(hdl, size))
573 return false;
575 quint32 entryStart = pos(hdl);
577 if (!(readByte(hdl, &flags) && readVUInt32(hdl, nod->indexIdSize(),
578 blockIndexId)))
579 return false;
581 quint8 bits[3];
582 for (int i = 0; i < 3; i++)
583 bits[i] = 0x4000a08 >> (((flags >> (2*i) & 3) << 3) ^ 0x10);
584 quint8 byteSize = bs(bits[0] + bits[1] + bits[2]);
586 quint32 counts;
587 if (!readVUInt32(hdl, byteSize, counts))
588 return false;
590 quint16 b8 = bits[0] ? (MASK(bits[0]) & counts) + 1 : 0;
591 quint16 b10 = bits[1] ? (MASK(bits[1]) & (counts >> bits[0])) + 1 : 0;
592 quint16 b16 = bits[2] ? (MASK(bits[2]) & (counts >> (bits[0] + bits[1])))
593 + 1 : 0;
595 NODFile::BlockInfo blockInfo;
596 if (!nod->blockInfo(nodHdl, blockIndexId, blockInfo))
597 return false;
599 quint8 linkId, lineId;
600 for (int i = 0; i < b8 + b10 + b16; i++) {
601 if (!b8 || b8 <= i) {
602 quint16 v16;
603 if (!readUInt16(hdl, v16))
604 return false;
606 if (!b16 || b8 + b16 <= i) {
607 int shift = ((i - (b8 + b16)) * 10) & 7;
608 linkId = (quint8)(v16 >> shift);
609 lineId = (((v16 >> shift) >> 8) & 3) + 1;
611 if (shift < 6 && i < b8 + b10 + b16 - 1)
612 seek(hdl, pos(hdl) - 1);
613 } else {
614 linkId = (quint8)v16;
615 lineId = v16 >> 8;
616 Q_ASSERT(lineId > 4);
618 } else {
619 if (!readByte(hdl, &linkId))
620 return false;
621 lineId = 0;
624 net->link(subdiv, shift, netHdl, nod, nodHdl, nodHdl2, lbl, lblHdl,
625 blockInfo, linkId, lineId, lines);
628 if (entryStart + size != pos(hdl))
629 return false;
632 return true;
635 bool RGNFile::segments(Handle &hdl, SubDiv *subdiv, SubDiv::Segment seg[5]) const
637 if (subdiv->offset() == subdiv->end() || !(subdiv->objects() & 0x1F))
638 return true;
640 quint32 offset = _base.offset + subdiv->offset();
641 if (!seek(hdl, offset))
642 return false;
644 int no = 0;
645 for (int i = 0; i < 5; i++)
646 if (subdiv->objects() & (1<<i))
647 no++;
649 quint32 start = offset + 2 * (no - 1);
650 quint32 ls = 0;
651 int lt = 0;
653 for (int i = 0; i < 5; i++) {
654 if (subdiv->objects() & (1<<i)) {
655 if (ls) {
656 quint16 po;
657 if (!readUInt16(hdl, po) || !po)
658 return false;
659 start = offset + po;
660 seg[lt] = SubDiv::Segment(ls, start);
663 lt = i;
664 ls = start;
668 seg[lt] = SubDiv::Segment(ls,
669 subdiv->end() ? _base.offset + subdiv->end() : _base.offset + _base.size);
671 return true;
674 bool RGNFile::subdivInit(Handle &hdl, SubDiv *subdiv) const
676 SubDiv::Segment std[5], extPoints, extLines, extPolygons;
678 if (!segments(hdl, subdiv, std))
679 return false;
681 if (subdiv->extPointsOffset() != subdiv->extPointsEnd()) {
682 quint32 start = _points.offset + subdiv->extPointsOffset();
683 quint32 end = subdiv->extPointsEnd()
684 ? _points.offset + subdiv->extPointsEnd()
685 : _points.offset + _points.size;
686 extPoints = SubDiv::Segment(start, end);
688 if (subdiv->extPolygonsOffset() != subdiv->extPolygonsEnd()) {
689 quint32 start = _polygons.offset + subdiv->extPolygonsOffset();
690 quint32 end = subdiv->extPolygonsEnd()
691 ? _polygons.offset + subdiv->extPolygonsEnd()
692 : _polygons.offset + _polygons.size;
693 extPolygons = SubDiv::Segment(start, end);
695 if (subdiv->extLinesOffset() != subdiv->extLinesEnd()) {
696 quint32 start = _lines.offset + subdiv->extLinesOffset();
697 quint32 end = subdiv->extLinesEnd()
698 ? _lines.offset + subdiv->extLinesEnd()
699 : _lines.offset + _lines.size;
700 extLines = SubDiv::Segment(start, end);
703 subdiv->init(std[Point], std[IndexedPoint], std[Line], std[Polygon],
704 std[RoadReference], extPoints, extLines, extPolygons);
706 return true;