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)), _tileRatio(1.0),
125 _style(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()
166 void ENCAtlas::load(const Projection
&in
, const Projection
&out
,
167 qreal deviceRatio
, bool hidpi
)
172 _tileRatio
= deviceRatio
;
176 _style
= new Style(deviceRatio
);
178 QPixmapCache::clear();
181 void ENCAtlas::unload()
191 int ENCAtlas::zoomFit(const QSize
&size
, const RectC
&rect
)
193 if (rect
.isValid()) {
194 RectD
pr(rect
, _projection
, 10);
196 for (auto it
= _data
.cbegin(); it
!= _data
.cend(); ++it
) {
197 Range
z(zooms(it
.key()));
202 for (int i
= z
.min() + 1; i
<= z
.max(); i
++) {
203 Transform
t(transform(i
));
204 QRectF
r(t
.proj2img(pr
.topLeft()), t
.proj2img(pr
.bottomRight()));
206 if (size
.width() < r
.width() || size
.height() < r
.height()) {
215 IntendedUsage
usage(_data
.lastKey());
217 _zoom
= zooms(usage
).max();
225 int ENCAtlas::zoomIn()
229 if (_zoom
+ 1 <= zooms(_usage
).max())
232 auto it
= _data
.find(_usage
);
233 if (++it
!= _data
.end()) {
235 _zoom
= zooms(it
.key()).min();
244 int ENCAtlas::zoomOut()
248 if (_zoom
- 1 >= zooms(_usage
).min())
251 auto it
= _data
.find(_usage
);
252 if (it
!= _data
.begin()) {
255 _zoom
= zooms(it
.key()).max();
264 void ENCAtlas::setZoom(int zoom
)
270 Transform
ENCAtlas::transform(int zoom
) const
272 int z
= zoom
+ Util::log2i(TILE_SIZE
);
274 double scale
= _projection
.isGeographic()
275 ? 360.0 / (1<<z
) : (2.0 * M_PI
* WGS84_RADIUS
) / (1<<z
);
276 PointD
topLeft(_projection
.ll2xy(_llBounds
.topLeft()));
277 return Transform(ReferencePoint(PointD(0, 0), topLeft
),
278 PointD(scale
, scale
));
281 void ENCAtlas::updateTransform()
283 _transform
= transform(_zoom
);
285 RectD
prect(_llBounds
, _projection
);
286 _bounds
= QRectF(_transform
.proj2img(prect
.topLeft()),
287 _transform
.proj2img(prect
.bottomRight()));
290 bool ENCAtlas::isRunning(int zoom
, const QPoint
&xy
) const
292 for (int i
= 0; i
< _jobs
.size(); i
++) {
293 const QList
<ENC::RasterTile
> &tiles
= _jobs
.at(i
)->tiles();
294 for (int j
= 0; j
< tiles
.size(); j
++) {
295 const ENC::RasterTile
&mt
= tiles
.at(j
);
296 if (mt
.zoom() == zoom
&& mt
.xy() == xy
)
304 void ENCAtlas::runJob(ENCJob
*job
)
308 connect(job
, &ENCJob::finished
, this, &ENCAtlas::jobFinished
);
312 void ENCAtlas::removeJob(ENCJob
*job
)
314 _jobs
.removeOne(job
);
318 void ENCAtlas::jobFinished(ENCJob
*job
)
320 const QList
<ENC::RasterTile
> &tiles
= job
->tiles();
322 for (int i
= 0; i
< tiles
.size(); i
++) {
323 const ENC::RasterTile
&mt
= tiles
.at(i
);
325 QPixmapCache::insert(key(mt
.zoom(), mt
.xy()), mt
.pixmap());
333 void ENCAtlas::cancelJobs(bool wait
)
335 for (int i
= 0; i
< _jobs
.size(); i
++)
336 _jobs
.at(i
)->cancel(wait
);
339 QString
ENCAtlas::key(int zoom
, const QPoint
&xy
) const
341 return path() + "-" + QString::number(zoom
) + "_"
342 + QString::number(xy
.x()) + "_" + QString::number(xy
.y());
345 void ENCAtlas::draw(QPainter
*painter
, const QRectF
&rect
, Flags flags
)
348 QPointF
tl(floor(rect
.left() / TILE_SIZE
) * TILE_SIZE
,
349 floor(rect
.top() / TILE_SIZE
) * TILE_SIZE
);
350 QSizeF
s(rect
.right() - tl
.x(), rect
.bottom() - tl
.y());
351 int width
= ceil(s
.width() / TILE_SIZE
);
352 int height
= ceil(s
.height() / TILE_SIZE
);
354 QList
<RasterTile
> tiles
;
356 for (int i
= 0; i
< width
; i
++) {
357 for (int j
= 0; j
< height
; j
++) {
358 QPoint
ttl(tl
.x() + i
* TILE_SIZE
, tl
.y() + j
* TILE_SIZE
);
359 if (isRunning(_zoom
, ttl
))
363 if (QPixmapCache::find(key(_zoom
, ttl
), &pm
))
364 painter
->drawPixmap(ttl
, pm
);
366 tiles
.append(RasterTile(_projection
, _transform
, _style
,
367 _data
.value(_usage
), _zoom
, zooms(_usage
),
368 QRect(ttl
, QSize(TILE_SIZE
, TILE_SIZE
)), _tileRatio
));
372 if (!tiles
.isEmpty()) {
373 if (flags
& Map::Block
) {
374 QFuture
<void> future
= QtConcurrent::map(tiles
, &RasterTile::render
);
375 future
.waitForFinished();
377 for (int i
= 0; i
< tiles
.size(); i
++) {
378 const RasterTile
&mt
= tiles
.at(i
);
379 const QPixmap
&pm
= mt
.pixmap();
380 painter
->drawPixmap(mt
.xy(), pm
);
381 QPixmapCache::insert(key(mt
.zoom(), mt
.xy()), pm
);
384 runJob(new ENCJob(tiles
));
388 Map
*ENCAtlas::create(const QString
&path
, const Projection
&proj
, bool *isDir
)
395 return new ENCAtlas(path
);