Skip to content

Commit

Permalink
Merge GH-1793 (KMZ ground overlay support)
Browse files Browse the repository at this point in the history
  • Loading branch information
dg0yt authored Dec 20, 2020
2 parents 2bfa408 + da74804 commit 0f872e8
Show file tree
Hide file tree
Showing 28 changed files with 1,233 additions and 77 deletions.
56 changes: 34 additions & 22 deletions src/core/map_printer.cpp
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
/*
* Copyright 2012, 2013 Thomas Schöps
* Copyright 2012-2018 Kai Pastor
* Copyright 2012-2020 Kai Pastor
*
* This file is part of OpenOrienteering.
*
Expand Down Expand Up @@ -391,6 +391,12 @@ const QPrinterInfo* MapPrinter::imageTarget()
return &image_target;
}

const QPrinterInfo* MapPrinter::kmzTarget()
{
static QPrinterInfo kmz_target; // TODO: set name and features?
return &kmz_target;
}


// QPageSize (::key(), ::name()) made this list mostly obsolete.
// But we keep it in v0.9 for loading maps where we used names
Expand Down Expand Up @@ -454,14 +460,17 @@ void MapPrinter::setTarget(const QPrinterInfo* new_target)
target = new_target;
else if (new_target == imageTarget())
target = new_target;
else if (new_target == kmzTarget())
target = new_target;
else
{
// We don't own this target, so we need to make a copy.
target_copy = *new_target;
target = &target_copy;
}

if (old_target == imageTarget() || new_target == imageTarget())
if (old_target == imageTarget() || new_target == imageTarget()
|| old_target == kmzTarget() || new_target == kmzTarget())
{
// No page margins. Will emit pageFormatChanged( ).
setCustomPageSize(page_format.page_rect.size());
Expand Down Expand Up @@ -536,7 +545,8 @@ bool MapPrinter::isPrinter() const
{
bool is_printer = target
&& target != imageTarget()
&& target != pdfTarget();
&& target != pdfTarget()
&& target != kmzTarget();
return is_printer;
}

Expand All @@ -547,7 +557,7 @@ void MapPrinter::setPrintArea(const QRectF& area)
{
print_area = area;

if (target == imageTarget() && print_area.size() != page_format.paper_dimensions)
if ((target == imageTarget() || target == kmzTarget()) && print_area.size() != page_format.paper_dimensions)
setCustomPageSize(print_area.size() * scale_adjustment);

updatePageBreaks();
Expand Down Expand Up @@ -627,7 +637,7 @@ void MapPrinter::setOverlap(qreal h_overlap, qreal v_overlap)

void MapPrinter::updatePaperDimensions()
{
if (target == imageTarget() && page_format.page_size == QPageSize::Custom)
if ((target == imageTarget() || target == kmzTarget()) && page_format.page_size == QPageSize::Custom)
{
// No margins, no need to query QPrinter.
page_format.page_rect = QRectF(QPointF(0.0, 0.0), page_format.paper_dimensions);
Expand All @@ -639,7 +649,7 @@ void MapPrinter::updatePaperDimensions()

QPrinter* printer = target ? new QPrinter(*target, QPrinter::HighResolution)
: new QPrinter(QPrinter::HighResolution);
if (!printer->isValid() || target == imageTarget() || target == pdfTarget())
if (!printer->isValid() || target == imageTarget() || target == kmzTarget() || target == pdfTarget())
printer->setOutputFormat(QPrinter::PdfFormat);

if (page_format.page_size == QPageSize::Custom)
Expand All @@ -657,7 +667,7 @@ void MapPrinter::updatePaperDimensions()
page_format.page_rect = printer->paperRect(QPrinter::Millimeter);
page_format.paper_dimensions = page_format.page_rect.size();

if ( target != imageTarget() && target != pdfTarget() &&
if ( target != imageTarget() && target != kmzTarget() && target != pdfTarget() &&
page_format.page_size != QPageSize::Custom )
{
page_format.page_rect = printer->pageRect(QPrinter::Millimeter);
Expand Down Expand Up @@ -896,7 +906,7 @@ void MapPrinter::takePrinterSettings(const QPrinter* printer)
if (!printer) return;

MapPrinterPageFormat f(*printer);
if (target == pdfTarget() || target == imageTarget())
if (target == pdfTarget() || target == imageTarget() || target == kmzTarget())
{
f.page_rect = QRectF(QPointF(0.0, 0.0), f.paper_dimensions);
}
Expand All @@ -921,6 +931,20 @@ void MapPrinter::takePrinterSettings(const QPrinter* printer)


void MapPrinter::drawPage(QPainter* device_painter, const QRectF& page_extent, QImage* page_buffer) const
{
// Determine transformation and clipping for page extent and region
const qreal units_per_mm = options.resolution / 25.4;
// Translate for top left page margin
auto transform = QTransform::fromScale(units_per_mm, units_per_mm);
transform.translate(page_format.page_rect.left(), page_format.page_rect.top());
// Convert native map scale to print scale
transform.scale(scale_adjustment, scale_adjustment);
// Translate and clip for margins and print area
transform.translate(-page_extent.left(), -page_extent.top());
drawPage(device_painter, page_extent, transform, page_buffer);
}

void MapPrinter::drawPage(QPainter* device_painter, const QRectF& page_extent, const QTransform& page_extent_transform, QImage* page_buffer) const
{
// Logical units per mm
const qreal units_per_mm = options.resolution / 25.4;
Expand All @@ -930,18 +954,6 @@ void MapPrinter::drawPage(QPainter* device_painter, const QRectF& page_extent, Q
| QPainter::Antialiasing
| QPainter::SmoothPixmapTransform;

// Determine transformation and clipping for page extent and region
const auto page_extent_transform = [this, units_per_mm, page_extent]() {
// Translate for top left page margin
auto transform = QTransform::fromScale(units_per_mm, units_per_mm);
transform.translate(page_format.page_rect.left(), page_format.page_rect.top());
// Convert native map scale to print scale
transform.scale(scale_adjustment, scale_adjustment);
// Translate and clip for margins and print area
transform.translate(-page_extent.left(), -page_extent.top());
return transform;
}();

const auto page_region_used = page_extent.intersected(print_area);


Expand All @@ -964,7 +976,7 @@ void MapPrinter::drawPage(QPainter* device_painter, const QRectF& page_extent, Q
* When the target is an image, use the temporary image to enforce the given
* resolution.
*/
const bool use_buffer_for_map = rasterModeSelected() || target == imageTarget() || engineWillRasterize();
const bool use_buffer_for_map = rasterModeSelected() || target == imageTarget() || target == kmzTarget() || engineWillRasterize();
bool use_page_buffer = use_buffer_for_map;

auto first_front_template = map.getFirstFrontTemplate();
Expand Down Expand Up @@ -1232,7 +1244,7 @@ void MapPrinter::drawSeparationPages(QPrinter* printer, QPainter* device_painter

// Translate and clip for margins and print area
device_painter->translate(-page_extent.left(), -page_extent.top());
device_painter->setClipRect(page_extent.intersected(print_area), Qt::ReplaceClip);
device_painter->setClipRect(page_extent.intersected(print_area).adjusted(-10, 10, 10, 10), Qt::ReplaceClip);

bool need_new_page = false;
for (int i = map.getNumColors() - 1; i >= 0; --i)
Expand Down
7 changes: 6 additions & 1 deletion src/core/map_printer.h
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
/*
* Copyright 2012, 2013 Thomas Schöps
* Copyright 2012-2019 Kai Pastor
* Copyright 2012-2020 Kai Pastor
*
* This file is part of OpenOrienteering.
*
Expand Down Expand Up @@ -229,6 +229,9 @@ Q_OBJECT
/** Returns a QPrinterInfo pointer which signals printing to a raster file. */
static const QPrinterInfo* imageTarget();

/** Returns a QPrinterInfo pointer which signals printing to a KML ground overlay. */
static const QPrinterInfo* kmzTarget();

/** Returns a reference to a hash which maps paper sizes to names as C strings.
* These strings are not translated.
*
Expand Down Expand Up @@ -361,6 +364,8 @@ Q_OBJECT
* buffer but refers to the logical coordinates of device_painter. */
void drawPage(QPainter* device_painter, const QRectF& page_extent, QImage* page_buffer = nullptr) const;

void drawPage(QPainter* device_painter, const QRectF& page_extent, const QTransform& page_extent_transform, QImage* page_buffer = nullptr) const;

/** Draws the separations as distinct pages to the printer. */
void drawSeparationPages(QPrinter* printer, QPainter* device_painter, const QRectF& page_extent) const;

Expand Down
4 changes: 3 additions & 1 deletion src/gdal/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
#
# Copyright 2016-2017 Kai Pastor
# Copyright 2016-2020 Kai Pastor
#
# This file is part of OpenOrienteering.
#
Expand Down Expand Up @@ -28,10 +28,12 @@ set(MAPPER_GDAL_HEADERS
)

set(MAPPER_GDAL_SOURCES
gdal_file.cpp
gdal_image_reader.cpp
gdal_manager.cpp
gdal_settings_page.cpp
gdal_template.cpp
kmz_groundoverlay_export.cpp
ogr_file_format.cpp
ogr_template.cpp
mapper-osmconf.ini
Expand Down
73 changes: 73 additions & 0 deletions src/gdal/gdal_file.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
/*
* Copyright 2020 Kai Pastor
*
* This file is part of OpenOrienteering.
*
* OpenOrienteering is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* OpenOrienteering is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with OpenOrienteering. If not, see <http://www.gnu.org/licenses/>.
*/

#include "gdal_file.h"

#include <cpl_conv.h>
#include <cpl_vsi.h>
#include <gdal.h>

#include <QByteArray>
#include <QString>


namespace OpenOrienteering {

namespace GdalFile {

bool exists(const QByteArray& filepath)
{
VSIStatBufL stat_buf;
return VSIStatExL(filepath, &stat_buf, VSI_STAT_EXISTS_FLAG) == 0;
}

bool isRelative(const QByteArray& filepath)
{
return CPLIsFilenameRelative(filepath) == TRUE;
}


bool isDir(const QByteArray& filepath)
{
VSIStatBufL stat_buf;
return VSIStatExL(filepath, &stat_buf, VSI_STAT_EXISTS_FLAG | VSI_STAT_NATURE_FLAG) == 0
&& VSI_ISDIR(stat_buf.st_mode);
}

bool mkdir(const QByteArray& filepath)
{
// 0777 would be right for POSIX mkdir with umask, but we
// cannot rely on umask for all paths supported by VSIMkdir.
return VSIMkdir(filepath, 0755) == 0;
}


QByteArray tryToFindRelativeTemplateFile(const QByteArray& template_path, const QByteArray& map_path)
{
QByteArray filepath = map_path + '/' + template_path;
if (!exists(filepath))
filepath = QByteArray(CPLGetDirname(map_path)) + '/' + template_path;
if (!exists(filepath))
filepath.clear();
return filepath;
}

} // namespace GdalUtil

} // namespace OpenOrienteering
71 changes: 71 additions & 0 deletions src/gdal/gdal_file.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
/*
* Copyright 2020 Kai Pastor
*
* This file is part of OpenOrienteering.
*
* OpenOrienteering is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* OpenOrienteering is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with OpenOrienteering. If not, see <http://www.gnu.org/licenses/>.
*/

#ifndef OPENORIENTEERING_GDAL_FILE_H
#define OPENORIENTEERING_GDAL_FILE_H

class QByteArray;

namespace OpenOrienteering {


/**
* Utility functions which use GDAL's VSI-aware file API.
*
* Paths must be passed as, and are returned as, UTF-8.
*
* \see https://gdal.org/user/virtual_file_systems.html
*/
namespace GdalFile {

/**
* Checks if a file exists.
*/
bool exists(const QByteArray& filepath);

/**
* Checks if a path is regarded as relative.
*/
bool isRelative(const QByteArray& filepath);

/**
* Checks if a path is an existing directory.
*/
bool isDir(const QByteArray& filepath);

/**
* Creates a directory.
*/
bool mkdir(const QByteArray& filepath);


/**
* GDAL-based implementation of Template::tryToFindRelativeTemplateFile().
*
* Returns an absolute path when the given template path identifies an existing
* file relative to the map path, or an empty value otherwise.
*/
QByteArray tryToFindRelativeTemplateFile(const QByteArray& template_path, const QByteArray& map_path);


} // namespace GdalUtil

} // namespace OpenOrienteering

#endif // OPENORIENTEERING_GDAL_FILE_H
Loading

0 comments on commit 0f872e8

Please sign in to comment.