Ejemplo n.º 1
0
///////////////////////////////////////////////////////////////////////////////
// Called when applying a point style on a feature geometry.  Point styles can
// be applied to all feature geometry types.
void SE_Renderer::ProcessPoint(SE_ApplyContext* ctx, SE_RenderPointStyle* style, RS_Bounds* bounds)
{
    // the feature geometry we're applying the style on...
    LineBuffer* featGeom = ctx->geometry;

    double angleRad = 0.0;
    if (style->angleControl == SE_AngleControl_FromGeometry)
    {
        switch (featGeom->geom_type())
        {
            case GeometryType_LineString:
            case GeometryType_MultiLineString:
            case GeometryType_Polygon:
            case GeometryType_MultiPolygon:
            {
                double x0, y0;
                featGeom->Centroid(LineBuffer::ctLine, &x0, &y0, &angleRad);
                break;
            }
        }
    }

    angleRad += style->angleRad;

    // also account for any viewport rotation
    angleRad += GetWorldToScreenRotation();

    SE_Matrix xform;
    bool yUp = YPointsUp();

    // see StylizationEngine::Stylize for a detailed explanation of these transforms
    SE_Matrix xformbase;
    xformbase.translate(style->offset[0], style->offset[1]);
    xformbase.rotate(yUp? angleRad : -angleRad);
    xformbase.premultiply(*ctx->xform);

    // render the points
    for (int i=0; i<featGeom->point_count(); ++i)
    {
        double x, y;
        featGeom->get_point(i, x, y);

        // transform to screen space - feature geometry is in [the original] mapping space
        WorldToScreenPoint(x, y, x, y);

        xform = xformbase;
        xform.translate(x, y);

        if (style->drawLast)
            AddLabel(featGeom, style, xform, angleRad);
        else
            DrawSymbol(style->symbol, xform, angleRad, style->addToExclusionRegion);
    }

    if (bounds)
    {
        // get the symbol bounds after applying the transforms
        bounds->minx = bounds->miny = +DBL_MAX;
        bounds->maxx = bounds->maxy = -DBL_MAX;
        for (int i=0; i<4; ++i)
        {
            RS_F_Point xfpt;
            xformbase.transform(style->bounds[i].x, style->bounds[i].y, xfpt.x, xfpt.y);
            bounds->add_point(xfpt);
        }
    }
}
Ejemplo n.º 2
0
void PolygonAdapter::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 an area style, so quit
    if (FeatureTypeStyleVisitor::DetermineFeatureTypeStyle(style) != FeatureTypeStyleVisitor::ftsArea)
        return;

    //-------------------------------------------------------
    // determine the rule for the feature
    //-------------------------------------------------------

    MdfModel::RuleCollection* arc = style->GetRules();
    MdfModel::AreaRule* rule = NULL;

    for (int i=0; i<arc->GetCount(); ++i)
    {
        rule = static_cast<MdfModel::AreaRule*>(arc->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::AreaSymbolization2D* asym = rule->GetSymbolization();
    if (asym == NULL)
        return;

    //-------------------------------------------------------
    // evaluate the style to use
    //-------------------------------------------------------

    // quick check if style is already cached
    RS_FillStyle* fillStyle = m_hAreaSymCache[asym];
    if (!fillStyle)
    {
        // if not, then we need to either cache or evaluate it
        fillStyle = &m_fillStyle;
        ObtainStyle(asym, *fillStyle);
    }

    //-------------------------------------------------------
    // compute the clip offset from the styles
    //-------------------------------------------------------

    double clipOffsetWU = 0.0;  // in mapping units

    bool bClip      = renderer->RequiresClipping();
    bool bLabelClip = renderer->RequiresLabelClipping();

    if (bClip || bLabelClip)
    {
        double mapScale = renderer->GetMapScale();

        // in meters in device units
        double clipOffsetMeters = GetClipOffset(fillStyle->outline(), mapScale);

        // add one pixel's worth to handle any roundoff
        clipOffsetMeters += METERS_PER_INCH / renderer->GetDpi();

        // limit the offset to something reasonable
        if (clipOffsetMeters > MAX_CLIPOFFSET_IN_METERS)
            clipOffsetMeters = MAX_CLIPOFFSET_IN_METERS;

        // convert to mapping units
        clipOffsetWU = clipOffsetMeters * mapScale / renderer->GetMetersPerUnit();
    }

    //-------------------------------------------------------
    // prepare the geometry on which to apply the style
    //-------------------------------------------------------

    LineBuffer* lb = geometry;
    std::auto_ptr<LineBuffer> spClipLB;

    if (bClip)
    {
        // the clip region is the map request extents expanded by the offset
        RS_Bounds clip = renderer->GetBounds();
        clip.minx -= clipOffsetWU;
        clip.miny -= clipOffsetWU;
        clip.maxx += clipOffsetWU;
        clip.maxy += clipOffsetWU;

        // clip geometry to given extents
        LineBuffer* lbc = lb->Clip(clip, 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
    //-------------------------------------------------------

    renderer->ProcessPolygon(lb, *fillStyle);

    //-------------------------------------------------------
    // do labeling if needed
    //-------------------------------------------------------

    MdfModel::Label* label = rule->GetLabel();
    if (label && label->GetSymbol())
    {
        // Make sure the geometry is clipped if label clipping is specified.
        // If bClip is true then the geometry is already clipped.
        if (!bClip && bLabelClip)
        {
            // the clip region is the map request extents expanded by the offset
            RS_Bounds clip = renderer->GetBounds();
            clip.minx -= clipOffsetWU;
            clip.miny -= clipOffsetWU;
            clip.maxx += clipOffsetWU;
            clip.maxy += clipOffsetWU;

            LineBuffer* lbc = lb->Clip(clip, 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);
            }
        }

        double cx = std::numeric_limits<double>::quiet_NaN();
        double cy = std::numeric_limits<double>::quiet_NaN();
        double dummy;

        // multi should work for simple polygons too
        lb->Centroid(LineBuffer::ctArea, &cx, &cy, &dummy);

        if (!_isnan(cx) && !_isnan(cy))
            AddLabel(cx, cy, 0.0, false, label, RS_OverpostType_AllFit, true, renderer, lb);
    }

    // free clipped line buffer if the geometry was clipped
    if (spClipLB.get())
        LineBufferPool::FreeLineBuffer(m_lbPool, spClipLB.release());
}
Ejemplo n.º 3
0
void PolylineAdapter::Stylize(Renderer*                   renderer,
                              RS_FeatureReader*           features,
                              bool                        initialPass,
                              FdoExpressionEngine*        exec,
                              LineBuffer*                 geometry,
                              MdfModel::FeatureTypeStyle* style,
                              const MdfModel::MdfString*  tooltip,
                              const MdfModel::MdfString*  url,
                              RS_ElevationSettings*       elevSettings,
                              CSysTransformer*            /*layer2mapxformer*/)
{
    m_exec = exec;

    // no need to do anything if the style is not a line style, so quit
    if (FeatureTypeStyleVisitor::DetermineFeatureTypeStyle(style) != FeatureTypeStyleVisitor::ftsLine)
        return;

    //-------------------------------------------------------
    // determine the rule for the feature
    //-------------------------------------------------------

    MdfModel::RuleCollection* lrc = style->GetRules();
    MdfModel::LineRule* rule = NULL;

    for (int i=0; i<lrc->GetCount(); ++i)
    {
        rule = static_cast<MdfModel::LineRule*>(lrc->GetAt(i));

        // apply any filter on the rule - if it fails move to the next rule
        if (!ExecFdoFilter(&rule->GetFilter()))
        {
            // don't stylize with failed rule
            rule = NULL;
            continue;
        }

        break;
    }

    if (!rule)
        return;

    MdfModel::LineSymbolizationCollection* lsymc = rule->GetSymbolizations();
    if (lsymc == NULL)
        return;

    int nSyms = lsymc->GetCount();

    //-------------------------------------------------------
    // evaluate all the styles once
    //-------------------------------------------------------

    // temporary array used to store pointers to each evaluated style
    RS_LineStroke** ppStrokes = (RS_LineStroke**)alloca(nSyms * sizeof(RS_LineStroke*));
    if (!ppStrokes)
        return;

    size_t tempIndex = 0;
    for (int i=0; i<nSyms; ++i)
    {
        MdfModel::LineSymbolization2D* lsym = lsymc->GetAt(i);

        // don't render if there's no symbolization
        if (lsym == NULL)
        {
            ppStrokes[i] = NULL;
            continue;
        }

        // quick check if style is already cached
        RS_LineStroke* cachedStyle = m_hLineSymCache[lsym];
        if (cachedStyle)
        {
            ppStrokes[i] = cachedStyle;
        }
        else
        {
            // if not, then we need to either cache or evaluate it

            // make sure the vector has a style in the slot we need
            if (tempIndex >= m_lineSyms.size())
            {
                _ASSERT(tempIndex == m_lineSyms.size());

                // allocate a new style and add it to the vector
                m_lineSyms.push_back(new RS_LineStroke());
            }

            // use the existing style in the vector
            ppStrokes[i] = m_lineSyms[tempIndex];
            ObtainStyle(lsym, *ppStrokes[i]);

            ++tempIndex;
        }
    }

    //-------------------------------------------------------
    // compute the clip offset from the styles
    //-------------------------------------------------------

    double clipOffsetWU = 0.0;  // in mapping units

    bool bClip      = renderer->RequiresClipping();
    bool bLabelClip = renderer->RequiresLabelClipping();

    if (bClip || bLabelClip)
    {
        double mapScale = renderer->GetMapScale();

        double clipOffsetMeters = 0.0;  // in device units
        for (int i=0; i<nSyms; ++i)
        {
            if (ppStrokes[i])
            {
                double styleClipOffset = GetClipOffset(*ppStrokes[i], mapScale);
                clipOffsetMeters = rs_max(styleClipOffset, clipOffsetMeters);
            }
        }

        // add one pixel's worth to handle any roundoff
        clipOffsetMeters += METERS_PER_INCH / renderer->GetDpi();

        // limit the offset to something reasonable
        if (clipOffsetMeters > MAX_CLIPOFFSET_IN_METERS)
            clipOffsetMeters = MAX_CLIPOFFSET_IN_METERS;

        // convert to mapping units
        clipOffsetWU = clipOffsetMeters * mapScale / renderer->GetMetersPerUnit();
    }

    //-------------------------------------------------------
    // prepare the geometry on which to apply the style
    //-------------------------------------------------------

    LineBuffer* lb = geometry;
    std::auto_ptr<LineBuffer> spClipLB;

    if (bClip)
    {
        // the clip region is the map request extents expanded by the offset
        RS_Bounds clip = renderer->GetBounds();
        clip.minx -= clipOffsetWU;
        clip.miny -= clipOffsetWU;
        clip.maxx += clipOffsetWU;
        clip.maxy += clipOffsetWU;

        // clip geometry to given extents
        LineBuffer* lbc = lb->Clip(clip, 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
    //-------------------------------------------------------

    for (int i=0; i<nSyms; ++i)
    {
        if (ppStrokes[i])
            renderer->ProcessPolyline(lb, *ppStrokes[i]);
    }

    //-------------------------------------------------------
    // do labeling if needed
    //-------------------------------------------------------

    MdfModel::Label* label = rule->GetLabel();
    if (label && label->GetSymbol())
    {
        // Make sure the geometry is clipped if label clipping is specified.
        // If bClip is true then the geometry is already clipped.
        if (!bClip && bLabelClip)
        {
            // the clip region is the map request extents expanded by the offset
            RS_Bounds clip = renderer->GetBounds();
            clip.minx -= clipOffsetWU;
            clip.miny -= clipOffsetWU;
            clip.maxx += clipOffsetWU;
            clip.maxy += clipOffsetWU;

            LineBuffer* lbc = lb->Clip(clip, 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);
            }
        }

        double cx = std::numeric_limits<double>::quiet_NaN();
        double cy = std::numeric_limits<double>::quiet_NaN();
        double slope_rad = 0.0;

        // multi should work for simple polylines too
        lb->Centroid(LineBuffer::ctLine, &cx, &cy, &slope_rad);

        if (!_isnan(cx) && !_isnan(cy))
            AddLabel(cx, cy, slope_rad, true, label, RS_OverpostType_AllFit, true, renderer, label->GetSymbol()->IsAdvancedPlacement()? lb : NULL);
    }

    // free clipped line buffer if the geometry was clipped
    if (spClipLB.get())
        LineBufferPool::FreeLineBuffer(m_lbPool, spClipLB.release());
}
void SE_PositioningAlgorithms::Default(SE_ApplyContext* applyCtx,
                                       SE_RenderStyle*  rstyle)
{
    // the style needs to contain at least one primitive
    SE_RenderPrimitiveList& prims = rstyle->symbol;
    if (prims.size() == 0)
        return;

    SE_Renderer* se_renderer = applyCtx->renderer;
    LineBuffer* geometry = applyCtx->geometry;
    SE_Matrix& xform = *applyCtx->xform;

    double cx = 0.0;
    double cy = 0.0;
    double offsetX = 0.0;
    double offsetY = 0.0;
    double angleRad = 0.0;

    switch (rstyle->type)
    {
        case SE_RenderStyle_Point:
        {
            SE_RenderPointStyle* rpStyle = (SE_RenderPointStyle*)rstyle;

            // get the feature centroid (no angle for point centroids)
            geometry->Centroid(LineBuffer::ctPoint, &cx, &cy, NULL);

            // account for the point usage angle control and offset
            angleRad = rpStyle->angleRad;
            offsetX  = rpStyle->offset[0];
            offsetY  = rpStyle->offset[1];

            break;
        }

        case SE_RenderStyle_Line:
        {
            SE_RenderLineStyle* rlStyle = (SE_RenderLineStyle*)rstyle;

            // get the feature centroid and angle
            double fAngleRad;
            geometry->Centroid(LineBuffer::ctLine, &cx, &cy, &fAngleRad);

            // account for the angle control
            angleRad = rlStyle->angleRad;
            if (rlStyle->angleControl == SE_AngleControl_FromGeometry)
                angleRad += fAngleRad;

            break;
        }

        case SE_RenderStyle_Area:
        {
            SE_RenderAreaStyle* raStyle = (SE_RenderAreaStyle*)rstyle;

            // get the feature centroid (no angle for area centroids)
            geometry->Centroid(LineBuffer::ctArea, &cx, &cy, NULL);

            // account for the angle control
            angleRad = raStyle->angleRad;

            break;
        }
    }

    // don't add a label if we can't compute the centroid
    if (_isnan(cx) || _isnan(cy))
        return;

    // need to convert centroid to screen units
    se_renderer->WorldToScreenPoint(cx, cy, cx, cy);

    // also account for any viewport rotation
    angleRad += se_renderer->GetWorldToScreenRotation();

    // see StylizationEngine::Stylize for a detailed explanation of these transforms
    bool yUp = se_renderer->YPointsUp();
    SE_Matrix xformLabel;
    xformLabel.translate(offsetX, offsetY);
    xformLabel.rotate(yUp? angleRad : -angleRad);
    xformLabel.premultiply(xform);
    xformLabel.translate(cx, cy);

    se_renderer->AddLabel(geometry, rstyle, xformLabel, angleRad);
}
void SE_PositioningAlgorithms::EightSurrounding(SE_ApplyContext* applyCtx,
                                                SE_RenderStyle*  rstyle,
                                                double           mm2su)
{
    SE_Renderer* se_renderer = applyCtx->renderer;
    LineBuffer* geometry = applyCtx->geometry;

    // eight surrounding labeling only applies to point feature geometry
    switch (geometry->geom_type())
    {
        case GeometryType_Point:
        case GeometryType_MultiPoint:
            break;

        default:
            return;
    }

    // eight surrounding labeling only works with point styles
    if (rstyle->type != SE_RenderStyle_Point)
        return;

    // the style needs to contain at least one primitive
    SE_RenderPrimitiveList& prims = rstyle->symbol;
    if (prims.size() == 0)
        return;

    SE_RenderPointStyle* rpstyle = (SE_RenderPointStyle*)rstyle;

    // get actual feature point and transform to screen space
    // TODO: in the case of a multi-point feature we get the average of all the points;
    //       generating candidate labels around this point doesn't make a whole lot of
    //       sense
    double cx = 0.0;
    double cy = 0.0;
    geometry->Centroid(LineBuffer::ctPoint, &cx, &cy, NULL);

    // don't add a label if we can't compute the centroid
    if (_isnan(cx) || _isnan(cy))
        return;

    se_renderer->WorldToScreenPoint(cx, cy, cx, cy);

    // Get the extent of the last drawn point symbol so that we know how much to offset
    // the label.  This call assumes the symbol draws right before the label.
    // TODO: remove this assumption
    const RS_F_Point* cfpts = se_renderer->GetLastSymbolExtent();
    RS_F_Point fpts[4];
    if(cfpts[0].x == 0 && cfpts[0].y == 0 &&
        cfpts[1].x == 0 && cfpts[1].y == 0 &&
        cfpts[2].x == 0 && cfpts[2].y == 0 &&
        cfpts[3].x == 0 && cfpts[3].y == 0)
    {
        for (int i=0; i<4; ++i)
        {
            fpts[i].x = cx;
            fpts[i].y = cy;
        }
    }
    else
        memcpy(fpts, cfpts, 4*sizeof(RS_F_Point));

    double dx = fpts[1].x - fpts[0].x;
    double dy = fpts[1].y - fpts[0].y;
    double symbol_rot_rad = atan2(dy, dx);

    // factor out position and rotation
    SE_Matrix ixform;
    ixform.translate(-cx, -cy);     // factor out point position
    ixform.rotate(-symbol_rot_rad); // factor out rotation
    for (int i=0; i<4; ++i)
        ixform.transform(fpts[i].x, fpts[i].y);

    bool yUp = se_renderer->YPointsUp();
    if (!yUp)
        symbol_rot_rad = -symbol_rot_rad;

    // unrotated bounds
    RS_Bounds symbol_bounds(fpts[0].x, fpts[0].y, fpts[2].x, fpts[2].y);
    double symbol_width  = symbol_bounds.width();   // symbol width in screen units
    double symbol_height = symbol_bounds.height();  // symbol height in screen units

    // offset the label from the symbol's edge
    double offset = POINT_LABEL_OFFSET_MM * mm2su;  // offset in screen units

    // make sure we have at least one pixel's worth of offset
    double screenUnitsPerPixel = MILLIMETERS_PER_INCH * se_renderer->GetScreenUnitsPerMillimeterDevice() / se_renderer->GetDpi();
    if (offset < screenUnitsPerPixel)
        offset = screenUnitsPerPixel;

    // compute how far label needs to be offset from center point of symbol
    double w2 = 0.5 * symbol_width;
    double h2 = 0.5 * symbol_height;
    double ch = 0.0;    // vertical center point
    double cw = 0.0;    // horizontal center point

    w2 += offset;
    h2 += offset;

    bool useBounds = symbol_bounds.IsValid();
    if (useBounds)
    {
        symbol_bounds.maxx += offset;    symbol_bounds.maxy += offset;
        symbol_bounds.minx -= offset;    symbol_bounds.miny -= offset;
        ch = 0.5*(symbol_bounds.maxy + symbol_bounds.miny);
        cw = 0.5*(symbol_bounds.maxx + symbol_bounds.minx);
    }

    // get the viewport rotation
    double w2sAngleRad = se_renderer->GetWorldToScreenRotation();

    // take into account rotation of the symbol - find increased extents
    // of the symbol bounds due to the rotation
    double op_pts[16];
    if (symbol_rot_rad != 0.0)
    {
        double cs = cos(symbol_rot_rad);
        double sn = sin(symbol_rot_rad);

        // check to see if the bounds have been set
        double wcs, nwcs, wsn, nwsn, hsn, nhsn, hcs, nhcs, cwsn, cwcs, chsn, chcs;
        if (useBounds)
        {
            wcs = symbol_bounds.maxx * cs;   nwcs = symbol_bounds.minx * cs;
            wsn = symbol_bounds.maxx * sn;   nwsn = symbol_bounds.minx * sn;
            hsn = symbol_bounds.maxy * sn;   nhsn = symbol_bounds.miny * sn;
            hcs = symbol_bounds.maxy * cs;   nhcs = symbol_bounds.miny * cs;
        }
        else
        {
            wcs = w2 * cs;   nwcs = -wcs;
            wsn = w2 * sn;   nwsn = -wsn;
            hsn = h2 * sn;   nhsn = -hsn;
            hcs = h2 * cs;   nhcs = -hcs;
        }

        cwsn = cw * sn;     chsn = ch * sn;
        cwcs = cw * cs;     chcs = ch * cs;

        // Find the octant that the symbol 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).
        // NOTE: The symbol rotation includes the viewport rotation.  We want to use the
        //       relative angle between these to compute the quadrant (it's the angle of
        //       the symbol relative to the viewport which matters).
        double relativeAngle = symbol_rot_rad - w2sAngleRad;
        double nangle = fmod(relativeAngle * M_180PI, 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)
        {
            symbol_bounds.maxx = w2; symbol_bounds.minx = -w2;
            symbol_bounds.maxy = h2; symbol_bounds.miny = -h2;
        }
        op_pts[0 ] = symbol_bounds.maxx; op_pts[1 ] = ch;
        op_pts[2 ] = symbol_bounds.maxx; op_pts[3 ] = symbol_bounds.maxy;
        op_pts[4 ] = cw;                 op_pts[5 ] = symbol_bounds.maxy;
        op_pts[6 ] = symbol_bounds.minx; op_pts[7 ] = symbol_bounds.maxy;
        op_pts[8 ] = symbol_bounds.minx; op_pts[9 ] = ch;
        op_pts[10] = symbol_bounds.minx; op_pts[11] = symbol_bounds.miny;
        op_pts[12] = cw;                 op_pts[13] = symbol_bounds.miny;
        op_pts[14] = symbol_bounds.maxx; op_pts[15] = symbol_bounds.miny;
    }

    // check if the incoming point style contains just a single text element
    bool foundSingleText = false;
    if (prims.size() == 1)
    {
        if (prims[0]->type == SE_RenderPrimitive_Text)
            foundSingleText = true;
    }

    // OK, who says I can't write bad code? Behold:
    SE_LabelInfo candidates[8];
    double yScale = yUp? 1.0 : -1.0; // which way does y go in the renderer?

    double angleRad = rpstyle->angleRad;

    // also account for the viewport rotation
    angleRad += w2sAngleRad;

    if (foundSingleText)
    {
        // In this case we set the appropriate alignments for the single text element
        // in each candidate label.  This allows us to draw the symbol directly at the
        // candidate points surrounding the feature point.

        SE_RenderStyle* st0 = se_renderer->CloneRenderStyle(rpstyle);
        ((SE_RenderText*)st0->symbol[0])->tdef.halign() = RS_HAlignment_Left;
        ((SE_RenderText*)st0->symbol[0])->tdef.valign() = RS_VAlignment_Half;
        UpdateStyleBounds(st0, se_renderer);
        candidates[0].Set(cx + op_pts[ 0], cy + op_pts[ 1]*yScale, RS_Units_Device, angleRad, st0);

        SE_RenderStyle* st1 = se_renderer->CloneRenderStyle(st0);
        ((SE_RenderText*)st1->symbol[0])->tdef.valign() = RS_VAlignment_Descent;
        UpdateStyleBounds(st1, se_renderer);
        candidates[1].Set(cx + op_pts[ 2], cy + op_pts[ 3]*yScale, RS_Units_Device, angleRad, st1);

        SE_RenderStyle* st2 = se_renderer->CloneRenderStyle(st1);
        ((SE_RenderText*)st2->symbol[0])->tdef.halign() = RS_HAlignment_Center;
        UpdateStyleBounds(st2, se_renderer);
        candidates[2].Set(cx + op_pts[ 4], cy + op_pts[ 5]*yScale, RS_Units_Device, angleRad, st2);

        SE_RenderStyle* st3 = se_renderer->CloneRenderStyle(st2);
        ((SE_RenderText*)st3->symbol[0])->tdef.halign() = RS_HAlignment_Right;
        UpdateStyleBounds(st3, se_renderer);
        candidates[3].Set(cx + op_pts[ 6], cy + op_pts[ 7]*yScale, RS_Units_Device, angleRad, st3);

        SE_RenderStyle* st4 = se_renderer->CloneRenderStyle(st3);
        ((SE_RenderText*)st4->symbol[0])->tdef.valign() = RS_VAlignment_Half;
        UpdateStyleBounds(st4, se_renderer);
        candidates[4].Set(cx + op_pts[ 8], cy + op_pts[ 9]*yScale, RS_Units_Device, angleRad, st4);

        SE_RenderStyle* st5 = se_renderer->CloneRenderStyle(st4);
        ((SE_RenderText*)st5->symbol[0])->tdef.valign() = RS_VAlignment_Ascent;
        UpdateStyleBounds(st5, se_renderer);
        candidates[5].Set(cx + op_pts[10], cy + op_pts[11]*yScale, RS_Units_Device, angleRad, st5);

        SE_RenderStyle* st6 = se_renderer->CloneRenderStyle(st5);
        ((SE_RenderText*)st6->symbol[0])->tdef.halign() = RS_HAlignment_Center;
        UpdateStyleBounds(st6, se_renderer);
        candidates[6].Set(cx + op_pts[12], cy + op_pts[13]*yScale, RS_Units_Device, angleRad, st6);

        SE_RenderStyle* st7 = se_renderer->CloneRenderStyle(st6);
        ((SE_RenderText*)st7->symbol[0])->tdef.halign() = RS_HAlignment_Left;
        UpdateStyleBounds(st7, se_renderer);
        candidates[7].Set(cx + op_pts[14], cy + op_pts[15]*yScale, RS_Units_Device, angleRad, st7);
    }
    else
    {
        // In the general case we have to account for the label symbol's extents when we
        // position each candidate.  For example, for candidate 1 (top right) we adjust the
        // position so that the bottom left corner of the label symbol's extent ends up at
        // the top right candidate point.

        double labelMinX = rpstyle->bounds[0].x;
        double labelMinY = rpstyle->bounds[0].y;
        double labelMaxX = rpstyle->bounds[2].x;
        double labelMaxY = rpstyle->bounds[2].y;
        double labelCtrX = 0.5*(labelMinX + labelMaxX);
        double labelCtrY = 0.5*(labelMinY + labelMaxY);

        SE_RenderStyle* st0 = se_renderer->CloneRenderStyle(rpstyle);
        candidates[0].Set(cx + op_pts[ 0] - labelMinX, cy + (op_pts[ 1] - labelCtrY)*yScale, RS_Units_Device, angleRad, st0);

        SE_RenderStyle* st1 = se_renderer->CloneRenderStyle(st0);
        candidates[1].Set(cx + op_pts[ 2] - labelMinX, cy + (op_pts[ 3] - labelMinY)*yScale, RS_Units_Device, angleRad, st1);

        SE_RenderStyle* st2 = se_renderer->CloneRenderStyle(st1);
        candidates[2].Set(cx + op_pts[ 4] - labelCtrX, cy + (op_pts[ 5] - labelMinY)*yScale, RS_Units_Device, angleRad, st2);

        SE_RenderStyle* st3 = se_renderer->CloneRenderStyle(st2);
        candidates[3].Set(cx + op_pts[ 6] - labelMaxX, cy + (op_pts[ 7] - labelMinY)*yScale, RS_Units_Device, angleRad, st3);

        SE_RenderStyle* st4 = se_renderer->CloneRenderStyle(st3);
        candidates[4].Set(cx + op_pts[ 8] - labelMaxX, cy + (op_pts[ 9] - labelCtrY)*yScale, RS_Units_Device, angleRad, st4);

        SE_RenderStyle* st5 = se_renderer->CloneRenderStyle(st4);
        candidates[5].Set(cx + op_pts[10] - labelMaxX, cy + (op_pts[11] - labelMaxY)*yScale, RS_Units_Device, angleRad, st5);

        SE_RenderStyle* st6 = se_renderer->CloneRenderStyle(st5);
        candidates[6].Set(cx + op_pts[12] - labelCtrX, cy + (op_pts[13] - labelMaxY)*yScale, RS_Units_Device, angleRad, st6);

        SE_RenderStyle* st7 = se_renderer->CloneRenderStyle(st6);
        candidates[7].Set(cx + op_pts[14] - labelMinX, cy + (op_pts[15] - labelMaxY)*yScale, RS_Units_Device, angleRad, st7);
    }

    se_renderer->ProcessSELabelGroup(candidates, 8, RS_OverpostType_FirstFit, true, NULL);
}
Ejemplo n.º 6
0
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());
}