void MgdFeatureInfoRenderer::DrawScreenText(const RS_TextMetrics& tm, RS_TextDef& tdef, double insx, double insy, RS_F_Point* path, int npts, double param_position) { if (m_pointTest && m_featurePending) { if (path) { // TODO: can we reach here? } else { // block text // Check that we have a valid text metrics if ( tm.font != NULL ) { RS_Bounds b(DBL_MAX, DBL_MAX, -DBL_MAX, -DBL_MAX); for (size_t i = 0; i < tm.line_pos.size(); i++) { b.add_point(tm.line_pos[i].ext[0]); b.add_point(tm.line_pos[i].ext[2]); } double angleRad = tdef.rotation() * M_PI180; if (!YPointsUp()) angleRad = -angleRad; double angleCos = cos(angleRad); double angleSin = sin(angleRad); double sx = m_point[2] - insx, sy = m_point[3] - insy; double rx = sx * angleCos + sy * angleSin; double ry = sy * angleCos - sx * angleSin; if (rx > b.minx && rx < b.maxx && ry > b.miny && ry < b.maxy) SetSelected(); } } } }
void GeometryAdapter::AddLabel(double x, double y, double slope_rad, bool useSlope, MdfModel::Label* label, RS_OverpostType type, bool exclude, Renderer* renderer, LineBuffer* lb) { MdfModel::TextSymbol* text = label->GetSymbol(); RS_String txt; EvalString(text->GetText(), txt); if (!txt.empty()) { RS_TextDef def; ConvertTextDef(text, def); if (useSlope) def.rotation() = slope_rad * M_180PI; RS_LabelInfo info(x, y, 0.0, 0.0, RS_Units_Model, def); renderer->ProcessLabelGroup(&info, 1, txt, type, exclude, lb, text->GetScaleLimit()); } }
void SE_Renderer::DrawSymbol(SE_RenderPrimitiveList& symbol, const SE_Matrix& xform, double angleRad, bool excludeRegion) { RS_Bounds extents(DBL_MAX, DBL_MAX, DBL_MAX, -DBL_MAX, -DBL_MAX, -DBL_MAX); unsigned int nprims = symbol.size(); for (unsigned int i=0; i<nprims; ++i) { SE_RenderPrimitive* primitive = symbol[i]; if (primitive->type == SE_RenderPrimitive_Polygon || primitive->type == SE_RenderPrimitive_Polyline) { SE_RenderPolyline* rp = (SE_RenderPolyline*)primitive; LineBuffer* lb = rp->geometry->xf_buffer(); // update the extents with this primitive RS_Bounds lbnds; lb->ComputeBounds(lbnds); extents.add_bounds(lbnds); if (m_bSelectionMode) { if (primitive->type == SE_RenderPrimitive_Polygon) DrawScreenPolygon(lb, &xform, m_selFillColor); m_selLineStroke.cap = rp->lineStroke.cap; m_selLineStroke.join = rp->lineStroke.join; m_selLineStroke.miterLimit = rp->lineStroke.miterLimit; DrawScreenPolyline(lb, &xform, m_selLineStroke); } else { if (primitive->type == SE_RenderPrimitive_Polygon) DrawScreenPolygon(lb, &xform, ((SE_RenderPolygon*)primitive)->fill); DrawScreenPolyline(lb, &xform, rp->lineStroke); } } else if (primitive->type == SE_RenderPrimitive_Text) { SE_RenderText* tp = (SE_RenderText*)primitive; // update the extents with this primitive's bounds for (int j=0; j<4; ++j) extents.add_point(primitive->bounds[j]); // get position and angle to use double x, y; xform.transform(tp->position[0], tp->position[1], x, y); RS_TextDef tdef = tp->tdef; tdef.rotation() += angleRad * M_180PI; if (m_bSelectionMode) { tdef.textcolor() = m_textForeColor; tdef.ghostcolor() = m_textBackColor; // tdef.framecolor() = m_textBackColor; // tdef.opaquecolor() = m_textBackColor; } // Here we cannot use the cached RS_TextMetrics in the SE_RenderText object. // We must recalculate the text metrics with the new tdef before we can call DrawScreenText. RS_TextMetrics tm; if (this->GetRSFontEngine()->GetTextMetrics(tp->content, tdef, tm, false)) DrawScreenText(tm, tdef, x, y, NULL, 0, 0.0); } else if (primitive->type == SE_RenderPrimitive_Raster) { SE_RenderRaster* rp = (SE_RenderRaster*)primitive; if (m_bSelectionMode) { // if the raster symbol is selected, then draw the mask selection polygon only LineBuffer *lb = LineBufferPool::NewLineBuffer(m_pPool, 5); std::auto_ptr<LineBuffer> spLB(lb); lb->MoveTo(rp->bounds[3].x, rp->bounds[3].y); for (int i = 0; i < 4; ++i) { lb->LineTo(rp->bounds[i].x, rp->bounds[i].y); } DrawScreenPolygon(lb, &xform, m_selFillColor); DrawScreenPolyline(lb, &xform, m_selLineStroke); LineBufferPool::FreeLineBuffer(m_pPool, spLB.release()); } else { ImageData& imgData = rp->imageData; if (imgData.data != NULL) { // update the extents with this primitive's bounds for (int j=0; j<4; ++j) extents.add_point(primitive->bounds[j]); // get position and angle to use double x, y; xform.transform(rp->position[0], rp->position[1], x, y); double angleDeg = (rp->angleRad + angleRad) * M_180PI; DrawScreenRaster(imgData.data, imgData.size, imgData.format, imgData.width, imgData.height, x, y, rp->extent[0], rp->extent[1], angleDeg, rp->opacity); } } } } if (nprims > 0) { // always compute the last symbol extent xform.transform(extents.minx, extents.miny, m_lastSymbolExtent[0].x, m_lastSymbolExtent[0].y); xform.transform(extents.maxx, extents.miny, m_lastSymbolExtent[1].x, m_lastSymbolExtent[1].y); xform.transform(extents.maxx, extents.maxy, m_lastSymbolExtent[2].x, m_lastSymbolExtent[2].y); xform.transform(extents.minx, extents.maxy, m_lastSymbolExtent[3].x, m_lastSymbolExtent[3].y); if (excludeRegion) AddExclusionRegion(m_lastSymbolExtent, 4); } else { // symbol contains no primitives - update last symbol extent assuming // zero symbol extent, but don't add any exclusion region xform.transform(0.0, 0.0, m_lastSymbolExtent[0].x, m_lastSymbolExtent[0].y); m_lastSymbolExtent[1].x = m_lastSymbolExtent[2].x = m_lastSymbolExtent[3].x = m_lastSymbolExtent[0].x; m_lastSymbolExtent[1].y = m_lastSymbolExtent[2].y = m_lastSymbolExtent[3].y = m_lastSymbolExtent[0].y; } }
void PointAdapter::Stylize(Renderer* renderer, RS_FeatureReader* features, bool initialPass, SE_Evaluator* eval, LineBuffer* geometry, MdfModel::FeatureTypeStyle* style, const MdfModel::MdfString* tooltip, const MdfModel::MdfString* url, RS_ElevationSettings* elevSettings, CSysTransformer* /*layer2mapxformer*/) { m_eval = eval; // no need to do anything if the style is not a point style, so quit if (FeatureTypeStyleVisitor::DetermineFeatureTypeStyle(style) != FeatureTypeStyleVisitor::ftsPoint) return; //------------------------------------------------------- // determine the rule for the feature //------------------------------------------------------- MdfModel::RuleCollection* prc = style->GetRules(); MdfModel::PointRule* rule = NULL; for (int i=0; i<prc->GetCount(); ++i) { rule = static_cast<MdfModel::PointRule*>(prc->GetAt(i)); // apply any filter on the rule - if it fails move to the next rule if (!ExecFilter(&rule->GetFilter())) { // don't stylize with failed rule rule = NULL; continue; } break; } if (!rule) return; MdfModel::PointSymbolization2D* psym = rule->GetSymbolization(); MdfModel::PointTypeStyle* pfs = (MdfModel::PointTypeStyle*)style; //------------------------------------------------------- // prepare the geometry on which to apply the style //------------------------------------------------------- // NOTE: clipping of geometry for rendering (the RequiresClipping // option) does not need to be done for points. Points // outside the map extents already get clipped away as part // of the FDO query. LineBuffer* lb = geometry; std::auto_ptr<LineBuffer> spClipLB; if (renderer->RequiresClipping()) { // clip geometry to given extents // NOTE: point styles do not require a clip offset LineBuffer* lbc = lb->Clip(renderer->GetBounds(), LineBuffer::ctAGF, m_lbPool); if (lbc != lb) { // if the clipped buffer is NULL (completely clipped) just move on to // the next feature if (!lbc) return; // otherwise continue processing with the clipped buffer lb = lbc; if (lb != geometry) spClipLB.reset(lb); } } //------------------------------------------------------- // do the StartFeature notification //------------------------------------------------------- RS_String tip; //TODO: this should be quick since we are not assigning RS_String eurl; const RS_String &theme = rule->GetLegendLabel(); if (tooltip && !tooltip->empty()) EvalString(*tooltip, tip); if (url && !url->empty()) EvalString(*url, eurl); // elevation settings RS_ElevationType elevType = RS_ElevationType_RelativeToGround; double zOffset = 0.0; double zExtrusion = 0.0; GetElevationParams(elevSettings, zOffset, zExtrusion, elevType); renderer->StartFeature(features, initialPass, tip.empty()? NULL : &tip, eurl.empty()? NULL : &eurl, theme.empty()? NULL : &theme, zOffset, zExtrusion, elevType); //------------------------------------------------------- // apply the style to the geometry using the renderer //------------------------------------------------------- // Process point symbol, if any. If there is no point symbol, there may // be a label which we will use as a symbol instead. This one does not // obey overposting, it is always there. The marker specified in the rule // is the one that does overposting. double mdefW = 0.01; double mdefH = 0.01; RS_Units mdefU = RS_Units_Device; double mdefRot = 0.0; // the actual position used for the marker by the renderer // may be returned in this structure to help place labels better RS_Bounds bounds = RS_Bounds(1.0, 1.0, 0.0, 0.0); // init invalid if (psym && psym->GetSymbol()) { // quick check if style is already cached RS_MarkerDef* cachedStyle = m_hPointSymCache[psym]; if (cachedStyle) { mdefW = cachedStyle->width(); mdefH = cachedStyle->height(); mdefU = cachedStyle->units(); mdefRot = cachedStyle->rotation(); renderer->ProcessMarker(lb, *cachedStyle, pfs->IsAllowOverpost(), &bounds); } else { RS_MarkerDef mdef; ObtainStyle(psym, mdef); mdefW = mdef.width(); mdefH = mdef.height(); mdefU = mdef.units(); mdefRot = mdef.rotation(); renderer->ProcessMarker(lb, mdef, pfs->IsAllowOverpost(), &bounds); } } //------------------------------------------------------- // do labeling if needed //------------------------------------------------------- MdfModel::Label* label = rule->GetLabel(); if (label && label->GetSymbol()) { // NOTE: clipping of geometry for labeling (the RequiresLabelClipping // option) does not need to be done for points. // TODO: compute label position double cx = std::numeric_limits<double>::quiet_NaN(); double cy = std::numeric_limits<double>::quiet_NaN(); double dummy; // multi should work for simple polygons also lb->Centroid(LineBuffer::ctPoint, &cx, &cy, &dummy); if (!_isnan(cx) && !_isnan(cy)) { // if there was no point symbol, the label is the symbol, // so we send without overposting and at the center point if (!psym || !psym->GetSymbol() || pfs->IsDisplayAsText()) { AddLabel(cx, cy, 0.0, false, label, RS_OverpostType_All, !pfs->IsAllowOverpost(), renderer, lb); } else { MdfModel::TextSymbol* text = label->GetSymbol(); RS_String txt; EvalString(text->GetText(), txt); if (!txt.empty()) { RS_TextDef def; ConvertTextDef(text, def); // if there's a symbol there are 8 possible positions to place the label // around the symbol // NOTE: at this point we know that mdef has been initialized with // whatever was in psym->GetSymbol() and that expressions have // been evaluated double op_pts[16]; // offset the label from the symbol's edge double offset = 0.001 * POINT_LABEL_OFFSET_MM; // in meters if (def.rotation() != 0.0) { // if the text label has rotation put the text at least half the font height // away, so that it doesn't intersect with the marker at the worst-case (45 // degree) rotation. offset += 0.5*def.font().height(); } // in case of mapping space we need to scale by map scale if (mdefU != RS_Units_Device) offset *= renderer->GetMapScale(); // compute how far label needs to be offset from center point of symbol double w = 0.5 * mdefW; double h = 0.5 * mdefH; double ch = 0; // vertical center point double cw = 0; // horizontal center point w += offset; h += offset; bool useBounds = bounds.IsValid(); if (useBounds) { bounds.maxx += offset; bounds.maxy += offset; bounds.minx -= offset; bounds.miny -= offset; ch = 0.5*(bounds.maxy + bounds.miny); cw = 0.5*(bounds.maxx + bounds.minx); } // take into account rotation of the symbol // find increased extents of the symbol bounds due to the rotation if (mdefRot != 0.0) { double rotRad = mdefRot * M_PI180; double cs = cos(rotRad); double sn = sin(rotRad); double wcs, nwcs, wsn, nwsn, hsn, nhsn, hcs, nhcs, cwsn, cwcs, chsn, chcs; // check to see if the bounds have been set if (useBounds) { wcs = bounds.maxx * cs; nwcs = bounds.minx * cs; wsn = bounds.maxx * sn; nwsn = bounds.minx * sn; hsn = bounds.maxy * sn; nhsn = bounds.miny * sn; hcs = bounds.maxy * cs; nhcs = bounds.miny * cs; } else { wcs = w * cs; nwcs = -wcs; wsn = w * sn; nwsn = -wsn; hsn = h * sn; nhsn = -hsn; hcs = h * cs; nhcs = -hcs; } cwsn = cw * sn; chsn = ch * sn; cwcs = cw * cs; chcs = ch * cs; // find the octant that the marker is rotated into, and shift the points accordingly. // this way, the overpost points are still within 22.5 degrees of an axis-aligned box. // (position 0 will always be the closest to Center-Right) double nangle = fmod(mdefRot, 360.0); if (nangle < 0.0) nangle += 360.0; int i = (((int)((nangle/45.0) + 0.5)) << 1) & 0x0000000f; // i is 2 * the octant op_pts[i++] = wcs - chsn; op_pts[i++] = wsn + chcs; i &= 0x0000000f; // & 15 does (mod 16) op_pts[i++] = wcs - hsn; op_pts[i++] = wsn + hcs; i &= 0x0000000f; op_pts[i++] = cwcs - hsn; op_pts[i++] = cwsn + hcs; i &= 0x0000000f; op_pts[i++] = nwcs - hsn; op_pts[i++] = nwsn + hcs; i &= 0x0000000f; op_pts[i++] = nwcs - chsn; op_pts[i++] = nwsn + chcs; i &= 0x0000000f; op_pts[i++] = nwcs - nhsn; op_pts[i++] = nwsn + nhcs; i &= 0x0000000f; op_pts[i++] = cwcs - nhsn; op_pts[i++] = cwsn + nhcs; i &= 0x0000000f; op_pts[i++] = wcs - nhsn; op_pts[i] = wsn + nhcs; } else { if (!useBounds) { bounds.maxx = w; bounds.minx = -w; bounds.maxy = h; bounds.miny = -h; } op_pts[ 0] = bounds.maxx; op_pts[ 1] = ch; op_pts[ 2] = bounds.maxx; op_pts[ 3] = bounds.maxy; op_pts[ 4] = cw; op_pts[ 5] = bounds.maxy; op_pts[ 6] = bounds.minx; op_pts[ 7] = bounds.maxy; op_pts[ 8] = bounds.minx; op_pts[ 9] = ch; op_pts[10] = bounds.minx; op_pts[11] = bounds.miny; op_pts[12] = cw; op_pts[13] = bounds.miny; op_pts[14] = bounds.maxx; op_pts[15] = bounds.miny; } RS_LabelInfo candidates[8]; def.halign() = RS_HAlignment_Left; def.valign() = RS_VAlignment_Half; candidates[0] = RS_LabelInfo(cx, cy, op_pts[0], op_pts[1], mdefU, def); def.valign() = RS_VAlignment_Descent; candidates[1] = RS_LabelInfo(cx, cy, op_pts[2], op_pts[3], mdefU, def); def.halign() = RS_HAlignment_Center; candidates[2] = RS_LabelInfo(cx, cy, op_pts[4], op_pts[5], mdefU, def); def.halign() = RS_HAlignment_Right; candidates[3] = RS_LabelInfo(cx, cy, op_pts[6], op_pts[7], mdefU, def); def.valign() = RS_VAlignment_Half; candidates[4] = RS_LabelInfo(cx, cy, op_pts[8], op_pts[9], mdefU, def); def.valign() = RS_VAlignment_Ascent; candidates[5] = RS_LabelInfo(cx, cy, op_pts[10], op_pts[11], mdefU, def); def.halign() = RS_HAlignment_Center; candidates[6] = RS_LabelInfo(cx, cy, op_pts[12], op_pts[13], mdefU, def); def.halign() = RS_HAlignment_Left; candidates[7] = RS_LabelInfo(cx, cy, op_pts[14], op_pts[15], mdefU, def); renderer->ProcessLabelGroup(candidates, 8, txt, RS_OverpostType_FirstFit, true, lb, text->GetScaleLimit()); } } } } // free clipped line buffer if the geometry was clipped if (spClipLB.get()) LineBufferPool::FreeLineBuffer(m_lbPool, spClipLB.release()); }
bool GeometryAdapter::ConvertTextDef(MdfModel::TextSymbol* text, RS_TextDef& tdef) { // foreground color bool cacheable = EvalColor(text->GetForegroundColor(), tdef.textcolor()); // background style and color switch (text->GetBackgroundStyle()) { case MdfModel::TextSymbol::Transparent : tdef.textbg() = RS_TextBackground_None; break; case MdfModel::TextSymbol::Ghosted : tdef.textbg() = RS_TextBackground_Ghosted; cacheable = EvalColor(text->GetBackgroundColor(), tdef.ghostcolor()) && cacheable; break; case MdfModel::TextSymbol::Opaque : tdef.textbg() = RS_TextBackground_Opaque; cacheable = EvalColor(text->GetBackgroundColor(), tdef.opaquecolor()) && cacheable; break; } // font style RS_FontStyle_Mask style = RS_FontStyle_Regular; bool setting = false; cacheable = EvalBoolean(text->GetBold(), setting) && cacheable; if (setting) style = (RS_FontStyle_Mask)(style | RS_FontStyle_Bold); cacheable = EvalBoolean(text->GetItalic(), setting) && cacheable; if (setting) style = (RS_FontStyle_Mask)(style | RS_FontStyle_Italic); cacheable = EvalBoolean(text->GetUnderlined(), setting) && cacheable; if (setting) style = (RS_FontStyle_Mask)(style | RS_FontStyle_Underline); tdef.font().style() = style; // font name tdef.font().name() = text->GetFontName(); // font height double val; cacheable = EvalDouble(text->GetSizeY(), val) && cacheable; tdef.font().height() = MdfModel::LengthConverter::UnitToMeters(text->GetUnit(), val); // rotation cacheable = EvalDouble(text->GetRotation(), tdef.rotation()) && cacheable; tdef.rotation() = fmod(tdef.rotation(), 360.0); // units tdef.font().units() = (text->GetSizeContext() == MdfModel::MappingUnits)? RS_Units_Model : RS_Units_Device; // alignment cacheable = ConvertTextHAlign(text->GetHorizontalAlignment(), tdef.halign()) && cacheable; cacheable = ConvertTextVAlign(text->GetVerticalAlignment(), tdef.valign()) && cacheable; return cacheable; }