6 #include "BuildingGraphicsItem.h"
8 #include "MarbleDebug.h"
10 #include "GeoDataPlacemark.h"
11 #include "GeoDataLinearRing.h"
12 #include "GeoDataPolygon.h"
13 #include "GeoDataBuilding.h"
14 #include "GeoDataMultiGeometry.h"
15 #include "GeoDataPolyStyle.h"
16 #include "OsmPlacemarkData.h"
17 #include "GeoPainter.h"
20 #include <QApplication>
25 BuildingGraphicsItem::BuildingGraphicsItem(
const GeoDataPlacemark *placemark,
const GeoDataBuilding *building)
26 : AbstractGeoPolygonGraphicsItem(placemark, building)
28 if (
const auto ring = geodata_cast<GeoDataLinearRing>(&building->multiGeometry()->at(0))) {
30 }
else if (
const auto poly = geodata_cast<GeoDataPolygon>(&building->multiGeometry()->at(0))) {
34 setZValue(building->height());
35 Q_ASSERT(building->height() > 0.0);
38 paintLayers << QStringLiteral(
"Polygon/Building/frame")
39 << QStringLiteral(
"Polygon/Building/roof");
40 setPaintLayers(paintLayers);
43 BuildingGraphicsItem::~BuildingGraphicsItem()
45 qDeleteAll(m_cachedOuterPolygons);
46 qDeleteAll(m_cachedInnerPolygons);
47 qDeleteAll(m_cachedOuterRoofPolygons);
48 qDeleteAll(m_cachedInnerRoofPolygons);
51 void BuildingGraphicsItem::initializeBuildingPainting(
const GeoPainter* painter,
const ViewportParams *viewport,
52 bool &drawAccurate3D,
bool &isCameraAboveBuilding )
const
54 drawAccurate3D =
false;
55 isCameraAboveBuilding =
false;
58 double const physicalSize = 1.0;
59 int const pixelSize = qRound(physicalSize * screen->physicalDotsPerInch() / (IN2M * M2MM));
61 QPointF offsetAtCorner = buildingOffset(
QPointF(0, 0), viewport, &isCameraAboveBuilding);
62 qreal maxOffset = qMax( qAbs( offsetAtCorner.
x() ), qAbs( offsetAtCorner.
y() ) );
63 drawAccurate3D = painter->mapQuality() ==
HighQuality ? maxOffset > pixelSize : maxOffset > 1.5 * pixelSize;
66 void BuildingGraphicsItem::updatePolygons(
const ViewportParams &viewport,
69 bool &hasInnerBoundaries)
const
74 hasInnerBoundaries = polygon() ? !polygon()->innerBoundaries().isEmpty() :
false;
76 if (hasInnerBoundaries) {
77 screenPolygons(viewport, polygon(), innerPolygons, outerPolygons);
80 viewport.screenCoordinates(polygon()->outerBoundary(), outerPolygons);
83 viewport.screenCoordinates(*ring(), outerPolygons);
87 QPointF BuildingGraphicsItem::centroid(
const QPolygonF &polygon,
double &area)
89 auto centroid =
QPointF(0.0, 0.0);
91 for (
auto i=0, n=polygon.
size(); i<n; ++i) {
92 auto const x0 = polygon[i].x();
93 auto const y0 = polygon[i].y();
94 auto const j = i == n-1 ? 0 : i+1;
95 auto const x1 = polygon[j].x();
96 auto const y1 = polygon[j].y();
97 auto const a = x0*y1 - x1*y0;
99 centroid.rx() += (x0 + x1)*a;
100 centroid.ry() += (y0 + y1)*a;
107 QPointF BuildingGraphicsItem::buildingOffset(
const QPointF &point,
const ViewportParams *viewport,
bool* isCameraAboveBuilding)
const
109 qreal
const cameraFactor = 0.5 * tan(0.5 * 110 * DEG2RAD);
110 Q_ASSERT(building()->height() > 0.0);
111 qreal
const buildingFactor = building()->height() / EARTH_RADIUS;
113 qreal
const cameraHeightPixel = viewport->width() * cameraFactor;
114 qreal buildingHeightPixel = viewport->radius() * buildingFactor;
115 qreal
const cameraDistance = cameraHeightPixel-buildingHeightPixel;
117 if (isCameraAboveBuilding) {
118 *isCameraAboveBuilding = cameraDistance > 0;
121 qreal
const cc = cameraDistance * cameraHeightPixel;
122 qreal
const cb = cameraDistance * buildingHeightPixel;
130 qreal
const offsetX = point.
x() - viewport->width() / 2.0;
131 qreal
const offsetY = point.
y() - viewport->height() / 2.0;
133 qreal
const shiftX = offsetX * cb / (cc + offsetX);
134 qreal
const shiftY = offsetY * cb / (cc + offsetY);
136 return QPointF(shiftX, shiftY);
139 void BuildingGraphicsItem::paint(GeoPainter* painter,
const ViewportParams* viewport,
const QString &layer,
int tileZoomLevel)
142 if (tileZoomLevel == 17) {
145 AbstractGeoPolygonGraphicsItem::paint(painter, viewport, layer, tileZoomLevel );
149 setZValue(building()->height());
153 qDeleteAll(m_cachedOuterPolygons);
154 qDeleteAll(m_cachedInnerPolygons);
155 qDeleteAll(m_cachedOuterRoofPolygons);
156 qDeleteAll(m_cachedInnerRoofPolygons);
157 m_cachedOuterPolygons.clear();
158 m_cachedInnerPolygons.clear();
159 m_cachedOuterRoofPolygons.clear();
160 m_cachedInnerRoofPolygons.clear();
161 updatePolygons(*viewport, m_cachedOuterPolygons,
162 m_cachedInnerPolygons,
163 m_hasInnerBoundaries);
164 if (m_cachedOuterPolygons.isEmpty()) {
167 paintFrame(painter, viewport);
169 if (m_cachedOuterPolygons.isEmpty()) {
172 paintRoof(painter, viewport);
174 mDebug() <<
"Didn't expect to have to paint layer " << layer <<
", ignoring it.";
178 void BuildingGraphicsItem::paintRoof(GeoPainter* painter,
const ViewportParams* viewport)
181 bool isCameraAboveBuilding;
182 initializeBuildingPainting(painter, viewport, drawAccurate3D, isCameraAboveBuilding);
183 if (!isCameraAboveBuilding) {
188 if (s_previousStyle != style().data()) {
189 isValid = configurePainter(painter, *viewport);
191 QFont font = painter->font();
194 painter->setFont(font);
197 s_previousStyle = style().data();
199 if (!isValid)
return;
203 if ( drawAccurate3D) {
204 if (m_hasInnerBoundaries) {
206 QPen const currentPen = painter->pen();
210 painter->createFillPolygons( m_cachedOuterRoofPolygons,
211 m_cachedInnerRoofPolygons );
213 for(
const QPolygonF* fillPolygon: fillPolygons ) {
214 painter->drawPolygon(*fillPolygon);
217 painter->setPen(currentPen);
219 for(
const QPolygonF* outerRoof: m_cachedOuterRoofPolygons ) {
220 painter->drawPolyline( *outerRoof );
222 for(
const QPolygonF* innerRoof: m_cachedInnerRoofPolygons ) {
223 painter->drawPolyline( *innerRoof );
225 qDeleteAll(fillPolygons);
228 for(
const QPolygonF* outerRoof: m_cachedOuterRoofPolygons ) {
229 painter->drawPolygon( *outerRoof );
234 QPointF const offset = buildingOffset(m_cachedOuterPolygons[0]->boundingRect().
center(), viewport);
235 painter->translate(offset);
237 if (m_hasInnerBoundaries) {
239 QPen const currentPen = painter->pen();
243 m_cachedInnerPolygons );
245 for(
const QPolygonF* fillPolygon: fillPolygons ) {
246 painter->drawPolygon(*fillPolygon);
249 painter->setPen(currentPen);
251 for(
const QPolygonF* outerPolygon: m_cachedOuterPolygons ) {
252 painter->drawPolyline( *outerPolygon );
254 for(
const QPolygonF* innerPolygon: m_cachedInnerPolygons ) {
255 painter->drawPolyline( *innerPolygon );
257 qDeleteAll(fillPolygons);
260 for(
const QPolygonF* outerPolygon: m_cachedOuterPolygons ) {
261 painter->drawPolygon( *outerPolygon );
264 painter->translate(-offset);
270 double maxArea = 0.0;
272 for (
int i = 0; i < m_cachedOuterRoofPolygons.size(); ++i) {
273 const QPolygonF *outerRoof = m_cachedOuterRoofPolygons[i];
278 if (!building()->
name().isEmpty() || !building()->entries().isEmpty()) {
280 qreal size = polygonSize.
width() * polygonSize.
height();
281 if (size > maxSize) {
284 roofCenter = centroid(*outerRoof, area);
285 maxArea = qMax(area, maxArea);
290 if (drawAccurate3D && !building()->
name().isEmpty() && !roofCenter.
isNull()) {
291 double const w2 = 0.5 * painter->fontMetrics().horizontalAdvance(building()->
name());
292 double const ascent = painter->fontMetrics().ascent();
293 double const descent = painter->fontMetrics().descent();
294 double const a2 = 0.5 * painter->fontMetrics().ascent();
301 painter->drawTextFragment(roofCenter.
toPoint(), building()->
name(),
302 painter->font().pointSize(), painter->brush().color());
308 if (!building()->entries().isEmpty() && maxArea > 1600 * building()->entries().size()) {
309 for(
const auto &entry: building()->entries()) {
311 viewport->screenCoordinates(entry.point, x, y);
313 point += buildingOffset(point, viewport);
314 painter->drawTextFragment(point.
toPoint(),
315 building()->
name(), painter->font().pointSize(), painter->brush().color(),
316 GeoPainter::RoundFrame);
321 void BuildingGraphicsItem::paintFrame(GeoPainter *painter,
const ViewportParams *viewport)
324 if (building()->height() == 0.0) {
328 if ((polygon() && !viewport->resolves(polygon()->outerBoundary().latLonAltBox(), 4))
329 || (ring() && !viewport->resolves(ring()->latLonAltBox(), 4))) {
334 bool isCameraAboveBuilding;
335 initializeBuildingPainting(painter, viewport, drawAccurate3D, isCameraAboveBuilding);
338 if (s_previousStyle != style().data()) {
339 isValid = configurePainterForFrame(painter);
341 s_previousStyle = style().data();
343 if (!isValid)
return;
345 if ( drawAccurate3D && isCameraAboveBuilding ) {
346 for (
const QPolygonF *outline: m_cachedOuterPolygons) {
347 if (outline->isEmpty()) {
351 int const size = outline->size();
353 outerRoof->
reserve(outline->size());
355 QPointF shiftA = a + buildingOffset(a, viewport);
356 outerRoof->
append(shiftA);
357 for (
int i=1; i<size; ++i) {
358 QPointF const & b = (*outline)[i];
359 QPointF const shiftB = b + buildingOffset(b, viewport);
361 bool backface = (b.
x() - a.
x()) * (shiftA.
y() - a.
y())
362 - (b.
y() - a.
y()) * (shiftA.
x() - a.
x()) >= 0;
366 buildingSide << a << shiftA << shiftB << b;
367 painter->drawPolygon(buildingSide);
371 outerRoof->
append(shiftA);
373 m_cachedOuterRoofPolygons.append(outerRoof);
375 for (
const QPolygonF *outline: m_cachedInnerPolygons) {
376 if (outline->isEmpty()) {
380 int const size = outline->size();
382 innerRoof->
reserve(outline->size());
384 QPointF shiftA = a + buildingOffset(a, viewport);
385 innerRoof->
append(shiftA);
386 for (
int i=1; i<size; ++i) {
387 QPointF const & b = (*outline)[i];
388 QPointF const shiftB = b + buildingOffset(b, viewport);
390 bool backface = (b.
x() - a.
x()) * (shiftA.
y() - a.
y())
391 - (b.
y() - a.
y()) * (shiftA.
x() - a.
x()) >= 0;
395 buildingSide << a << shiftA << shiftB << b;
396 painter->drawPolygon(buildingSide);
400 innerRoof->
append(shiftA);
402 m_cachedInnerRoofPolygons.append(innerRoof);
407 m_cachedInnerPolygons );
409 for(
QPolygonF* fillPolygon: fillPolygons ) {
410 painter->drawPolygon(*fillPolygon);
412 qDeleteAll(fillPolygons);
416 void BuildingGraphicsItem::screenPolygons(
const ViewportParams &viewport,
const GeoDataPolygon *polygon,
423 viewport.screenCoordinates(polygon->outerBoundary(), outerPolygons);
426 for (
const GeoDataLinearRing &innerBoundary: innerBoundaries) {
428 viewport.screenCoordinates(innerBoundary, innerPolygonsPerBoundary);
430 innerPolygons.
reserve(innerPolygons.
size() + innerPolygonsPerBoundary.
size());
431 for(
QPolygonF* innerPolygonPerBoundary: innerPolygonsPerBoundary ) {
432 innerPolygons << innerPolygonPerBoundary;
437 bool BuildingGraphicsItem::contains(
const QPoint &screenPosition,
const ViewportParams *viewport)
const
439 if (m_cachedOuterPolygons.isEmpty()) {
441 return AbstractGeoPolygonGraphicsItem::contains(screenPosition, viewport);
444 QPointF const point = screenPosition;
445 for (
auto polygon: m_cachedOuterRoofPolygons) {
447 for (
auto polygon: m_cachedInnerRoofPolygons) {
455 for (
auto polygon: m_cachedOuterPolygons) {
457 for (
auto polygon: m_cachedInnerPolygons) {
468 bool BuildingGraphicsItem::configurePainterForFrame(GeoPainter *painter)
const
470 QPen currentPen = painter->pen();
472 GeoDataStyle::ConstPtr style = this->style();
474 painter->setPen(
QPen() );
477 const GeoDataPolyStyle& polyStyle = style->polyStyle();
483 if (!polyStyle.fill()) {
487 const QColor paintedColor = polyStyle.paintedColor().
darker(150);
488 if (painter->brush().color() != paintedColor) {
489 painter->setBrush(paintedColor);