2 #include <QPixmapCache>
3 #include "common/wgs84.h"
4 #include "GUI/format.h"
14 Range
ENCAtlas::zooms(IntendedUsage usage
)
41 ENCAtlas::IntendedUsage
ENCAtlas::usage(const QString
&path
)
44 QString
basename(fi
.baseName());
46 if (basename
.size() != 8)
48 int iu
= basename
.at(2).digitValue();
52 return (IntendedUsage
)iu
;
55 bool ENCAtlas::processRecord(const ISO8211::Record
&record
, QByteArray
&file
,
58 if (record
.size() < 2)
61 const ENC::ISO8211::Field
&f
= record
.at(1);
62 const QByteArray
&ba
= f
.tag();
65 QByteArray
FILE, IMPL
;
67 if (!f
.subfield("IMPL", &IMPL
))
69 if (!f
.subfield("FILE", &FILE))
72 if (IMPL
== "BIN" && FILE.endsWith("000")) {
73 QByteArray SLAT
, WLON
, NLAT
, ELON
;
75 if (!f
.subfield("SLAT", &SLAT
))
77 if (!f
.subfield("WLON", &WLON
))
79 if (!f
.subfield("NLAT", &NLAT
))
81 if (!f
.subfield("ELON", &ELON
))
84 bool ok1
, ok2
, ok3
, ok4
;
85 bounds
= RectC(Coordinates(WLON
.toDouble(&ok1
), NLAT
.toDouble(&ok2
)),
86 Coordinates(ELON
.toDouble(&ok3
), SLAT
.toDouble(&ok4
)));
87 if (!(ok1
&& ok2
&& ok3
&& ok4
))
90 file
= FILE.replace('\\', '/');
99 void ENCAtlas::addMap(const QDir
&dir
, const QByteArray
&file
,
102 QString
path(dir
.absoluteFilePath(file
));
103 if (!QFileInfo::exists(path
)) {
104 qWarning("%s: No such map file", qPrintable(path
));
107 if (!bounds
.isValid()) {
108 qWarning("%s: Invalid map bounds", qPrintable(path
));
112 IntendedUsage iu
= usage(path
);
113 auto it
= _data
.find(iu
);
114 if (it
== _data
.end())
115 it
= _data
.insert(iu
, new AtlasData(_cache
, _lock
));
117 it
.value()->addMap(bounds
, path
);
119 _name
= "ENC (" + Format::coordinates(bounds
.center(), DecimalDegrees
) + ")";
123 ENCAtlas::ENCAtlas(const QString
&fileName
, QObject
*parent
)
124 : Map(fileName
, parent
), _projection(PCS::pcs(3857)),
125 _tileRatio(1.0), _zoom(0), _valid(false)
127 QDir
dir(QFileInfo(fileName
).absoluteDir());
128 ISO8211
ddf(fileName
);
129 ISO8211::Record record
;
133 if (!ddf
.readDDR()) {
134 _errorString
= ddf
.errorString();
137 while (ddf
.readRecord(record
)) {
138 if (processRecord(record
, file
, bounds
))
139 addMap(dir
, file
, bounds
);
141 if (!ddf
.errorString().isNull()) {
142 _errorString
= ddf
.errorString();
146 if (_data
.isEmpty()) {
147 _errorString
= "No usable ENC map found";
151 _usage
= _data
.firstKey();
152 _zoom
= zooms(_usage
).min();
155 _cache
.setMaxCost(10);
160 ENCAtlas::~ENCAtlas()
165 void ENCAtlas::load(const Projection
&in
, const Projection
&out
,
166 qreal deviceRatio
, bool hidpi
)
171 _tileRatio
= deviceRatio
;
174 QPixmapCache::clear();
177 void ENCAtlas::unload()
184 int ENCAtlas::zoomFit(const QSize
&size
, const RectC
&rect
)
186 if (rect
.isValid()) {
187 RectD
pr(rect
, _projection
, 10);
189 for (auto it
= _data
.cbegin(); it
!= _data
.cend(); ++it
) {
190 Range
z(zooms(it
.key()));
195 for (int i
= z
.min() + 1; i
<= z
.max(); i
++) {
196 Transform
t(transform(i
));
197 QRectF
r(t
.proj2img(pr
.topLeft()), t
.proj2img(pr
.bottomRight()));
199 if (size
.width() < r
.width() || size
.height() < r
.height()) {
208 IntendedUsage
usage(_data
.lastKey());
210 _zoom
= zooms(usage
).max();
218 int ENCAtlas::zoomIn()
222 if (_zoom
+ 1 <= zooms(_usage
).max())
225 auto it
= _data
.find(_usage
);
226 if (++it
!= _data
.end()) {
228 _zoom
= zooms(it
.key()).min();
237 int ENCAtlas::zoomOut()
241 if (_zoom
- 1 >= zooms(_usage
).min())
244 auto it
= _data
.find(_usage
);
245 if (it
!= _data
.begin()) {
248 _zoom
= zooms(it
.key()).max();
257 void ENCAtlas::setZoom(int zoom
)
263 Transform
ENCAtlas::transform(int zoom
) const
265 int z
= zoom
+ Util::log2i(TILE_SIZE
);
267 double scale
= _projection
.isGeographic()
268 ? 360.0 / (1<<z
) : (2.0 * M_PI
* WGS84_RADIUS
) / (1<<z
);
269 PointD
topLeft(_projection
.ll2xy(_llBounds
.topLeft()));
270 return Transform(ReferencePoint(PointD(0, 0), topLeft
),
271 PointD(scale
, scale
));
274 void ENCAtlas::updateTransform()
276 _transform
= transform(_zoom
);
278 RectD
prect(_llBounds
, _projection
);
279 _bounds
= QRectF(_transform
.proj2img(prect
.topLeft()),
280 _transform
.proj2img(prect
.bottomRight()));
283 bool ENCAtlas::isRunning(int zoom
, const QPoint
&xy
) const
285 for (int i
= 0; i
< _jobs
.size(); i
++) {
286 const QList
<ENC::RasterTile
> &tiles
= _jobs
.at(i
)->tiles();
287 for (int j
= 0; j
< tiles
.size(); j
++) {
288 const ENC::RasterTile
&mt
= tiles
.at(j
);
289 if (mt
.zoom() == zoom
&& mt
.xy() == xy
)
297 void ENCAtlas::runJob(ENCJob
*job
)
301 connect(job
, &ENCJob::finished
, this, &ENCAtlas::jobFinished
);
305 void ENCAtlas::removeJob(ENCJob
*job
)
307 _jobs
.removeOne(job
);
311 void ENCAtlas::jobFinished(ENCJob
*job
)
313 const QList
<ENC::RasterTile
> &tiles
= job
->tiles();
315 for (int i
= 0; i
< tiles
.size(); i
++) {
316 const ENC::RasterTile
&mt
= tiles
.at(i
);
318 QPixmapCache::insert(key(mt
.zoom(), mt
.xy()), mt
.pixmap());
326 void ENCAtlas::cancelJobs(bool wait
)
328 for (int i
= 0; i
< _jobs
.size(); i
++)
329 _jobs
.at(i
)->cancel(wait
);
332 QString
ENCAtlas::key(int zoom
, const QPoint
&xy
) const
334 return path() + "-" + QString::number(zoom
) + "_"
335 + QString::number(xy
.x()) + "_" + QString::number(xy
.y());
338 void ENCAtlas::draw(QPainter
*painter
, const QRectF
&rect
, Flags flags
)
341 QPointF
tl(floor(rect
.left() / TILE_SIZE
) * TILE_SIZE
,
342 floor(rect
.top() / TILE_SIZE
) * TILE_SIZE
);
343 QSizeF
s(rect
.right() - tl
.x(), rect
.bottom() - tl
.y());
344 int width
= ceil(s
.width() / TILE_SIZE
);
345 int height
= ceil(s
.height() / TILE_SIZE
);
347 QList
<RasterTile
> tiles
;
349 for (int i
= 0; i
< width
; i
++) {
350 for (int j
= 0; j
< height
; j
++) {
351 QPoint
ttl(tl
.x() + i
* TILE_SIZE
, tl
.y() + j
* TILE_SIZE
);
352 if (isRunning(_zoom
, ttl
))
356 if (QPixmapCache::find(key(_zoom
, ttl
), &pm
))
357 painter
->drawPixmap(ttl
, pm
);
359 tiles
.append(RasterTile(_projection
, _transform
,
360 _data
.value(_usage
), _zoom
, zooms(_usage
),
361 QRect(ttl
, QSize(TILE_SIZE
, TILE_SIZE
)), _tileRatio
));
365 if (!tiles
.isEmpty()) {
366 if (flags
& Map::Block
) {
367 QFuture
<void> future
= QtConcurrent::map(tiles
, &RasterTile::render
);
368 future
.waitForFinished();
370 for (int i
= 0; i
< tiles
.size(); i
++) {
371 const RasterTile
&mt
= tiles
.at(i
);
372 const QPixmap
&pm
= mt
.pixmap();
373 painter
->drawPixmap(mt
.xy(), pm
);
374 QPixmapCache::insert(key(mt
.zoom(), mt
.xy()), pm
);
377 runJob(new ENCJob(tiles
));
381 Map
*ENCAtlas::create(const QString
&path
, bool *isDir
)
386 return new ENCAtlas(path
);