/////////////////////////////////////////////////////////////////////////////// // Copyright (C) 2002-2025, Open Design Alliance (the "Alliance"). // All rights reserved. // // This software and its documentation and related materials are owned by // the Alliance. The software may only be incorporated into application // programs owned by members of the Alliance, subject to a signed // Membership Agreement and Supplemental Software License Agreement with the // Alliance. The structure and organization of this software are the valuable // trade secrets of the Alliance and its suppliers. The software is also // protected by copyright law and international treaty provisions. Application // programs incorporating this software must include the following statement // with their copyright notices: // // This application incorporates Open Design Alliance software pursuant to a license // agreement with Open Design Alliance. // Open Design Alliance Copyright (C) 2002-2025 by Open Design Alliance. // All rights reserved. // // By use of this software, its documentation or related materials, you // acknowledge and accept the above terms. /////////////////////////////////////////////////////////////////////////////// #include "OdDbGeoMapHelper.h" #include "OdDbGeoMapImageCreator.h" #include "OdGeoMapTilesCache.h" #include "OdGeneralMaps.h" #include "DbHostAppServices.h" #include "DbDictionaryVar.h" #include "GiContextForDbDatabase.h" #include "DbGsManager.h" #include "ColorMapping.h" #include "Gi/GiClipBoundary.h" #include "Ge/GeLineSeg2d.h" bool OdDbGeoMapHelper::loadFontForGeoMap(const OdString& sFont, double dTextHeight, OdDbDatabase* pDb, OdGiTextStyle& textStyle) { textStyle.setFont(sFont, false, false, 1, 0); textStyle.setBigFontFileName(OD_T("")); textStyle.setTextSize(dTextHeight); textStyle.setObliquingAngle(0.); textStyle.setXScale(1.); textStyle.setTrackingPercent(1.); textStyle.setVertical(false); textStyle.setOverlined(false); textStyle.setUnderlined(false); textStyle.setStriked(false); textStyle.setBackward(false); textStyle.setUpsideDown(false); textStyle.loadStyleRec(pDb); // arx check return value of loadStyleRec, we can't... // therefore compare result with default simplex.shx (default == our font not loaded): OdFontPtr pFont = textStyle.getFont(); return pFont->getFileName().iCompare("simplex.shx") != 0; } void OdDbGeoMapHelper::drawProvidersStrings(OdGiViewportDraw* pVd, OdDbDatabase* pDb, const OdStringArray& arrCopyrightStrings, const OdGePoint3dArray& arrVertices, const OdGePoint3d& ptTextPosition, double dTextHeight, const OdCmColor& textColor) { //attributionStringInSingleLine: OdString strText; for (OdUInt32 i = 0; i < arrCopyrightStrings.size(); ++i) { strText += arrCopyrightStrings[i]; strText += L' '; // Last (terminating) space is required because of right alignment } if (!strText.isEmpty() && arrVertices.size() == 4) { // PLEASE NOTE: arx has extents() function in thier GiTextStyle. // Here we are using pVd->context()->textExtentsBox() hoping for simular result. OdGeVector3d vU = (arrVertices[1] - arrVertices[0]).normalize(); OdGeVector3d vV = (arrVertices[2] - arrVertices[1]).normalize(); OdGeLine3d line1(arrVertices[0], vV); OdGeLine3d line2(ptTextPosition, -vU); OdGePoint3d ptInt; OdGeTol gTolerance = 1.e-6; //in arx it's gTol, but we have changed it #CORE-15623 if (line2.intersectWith(line1, ptInt, gTolerance)) { OdGeVector3d vPlacing = ptTextPosition - ptInt; double dPlacingLength = vPlacing.length(); if (!OdZero(dPlacingLength, gTolerance.equalPoint()) && vPlacing.isCodirectionalTo(vU)) { //check dTextHeight: double dTextHeightCorrected = dTextHeight; if (OdZero(dTextHeightCorrected, gTolerance.equalPoint())) { //ODA_ASSERT(!"GeoMap TextHeight is near ZERO!"); dTextHeightCorrected = (arrVertices[2] - arrVertices[1]).length() * 0.01; } //apply textStyle: OdGiTextStyle textStyle; OdString sSegoeUI(OD_T("Segoe UI")); if (!loadFontForGeoMap(sSegoeUI, dTextHeightCorrected, pDb, textStyle)) { OdString sTahoma(OD_T("Tahoma")); if (!loadFontForGeoMap(sTahoma, dTextHeightCorrected, pDb, textStyle)) { OdDbHostAppServices* pSvc = pDb->appServices(); if (!loadFontForGeoMap(pSvc->getAlternateFontName(), dTextHeightCorrected, pDb, textStyle)) { return; } } } OdGePoint3d min, max; pVd->context()->textExtentsBox(textStyle, strText, -1, 0, min, max); double dTextWidth = max.x - min.x; if (!OdZero(dTextWidth, gTolerance.equalPoint())) { OdString sRes = strText; if (dTextWidth > dPlacingLength) //modify result string? { int nFullTextLength = strText.getLength(); sRes = strText.right(static_cast(dPlacingLength / dTextWidth * nFullTextLength)); //check if we need to remove more symbols: while (sRes.getLength()) { pVd->context()->textExtentsBox(textStyle, sRes, -1, 0, min, max); if (max.x - min.x <= dPlacingLength) { break; } sRes = sRes.right(sRes.getLength() - 1); } //or check if we have removed more than needed: while (sRes.getLength() < nFullTextLength) { OdString sBuf = strText.right(sRes.getLength() + 1); OdGePoint3d minBuf, maxBuf; pVd->context()->textExtentsBox(textStyle, sBuf, -1, 0, minBuf, maxBuf); if (maxBuf.x - minBuf.x > dPlacingLength) { break; } min = minBuf; max = maxBuf; sRes = sBuf; } if (sRes.isEmpty()) { return; } } OdGePoint3d ptResultTextPos = ptTextPosition; ptResultTextPos.z += 0.001; ptResultTextPos -= (max.x - min.x) * vU; ptResultTextPos -= (max.y + min.y) * vV; //yeah, "+", not sure... OdCmEntityColor oldColor = pVd->subEntityTraits().trueColor(); pVd->subEntityTraits().setTrueColor(textColor.entityColor()); pVd->geometry().text(ptResultTextPos, vU.crossProduct(vV).normal(), vU, sRes, -1, false, &textStyle); pVd->subEntityTraits().setTrueColor(oldColor); } } } } } ////////////////////////////// // OdDbGeoMapHelper ////////////////////////////// OdResult OdDbGeoMapHelper::getMap(const OdDbGeoMap * pGeoMap, OdBinaryData & imgPixelData) { OdDbDatabasePtr pHostDb = pGeoMap->database(); if (pHostDb.isNull()) { return eWrongDatabase; } OdDbDatabasePtr pDb = pHostDb->appServices()->createDatabase(); if (pDb.isNull()) { return eCreateFailed; } OdGsDevicePtr pDevice; OdGsModulePtr pGs = ::odrxDynamicLinker()->loadModule(OdWinBitmapModuleName); if (!pGs.isNull()) { pDevice = pGs->createBitmapDevice(); } if (pDevice.isNull()) { return eDeviceNotFound; } OdGiContextForDbDatabasePtr pDwgContext = OdGiContextForDbDatabase::createObject(); pDwgContext->setDatabase(pDb); pDwgContext->enableGsModel(false); pDevice = OdDbGsManager::setupActiveLayoutViews(pDevice, pDwgContext); pDevice->setLogicalPalette(::odcmAcadDarkPalette(), 256); pDevice->setBackgroundColor(ODRGB(255, 255, 255)); pDwgContext->setPaletteBackground(ODRGB(255, 255, 255)); OdGsDCRect screenRect(OdGsDCPoint(0, long(pGeoMap->imageSize().y)), OdGsDCPoint(long(pGeoMap->imageSize().x), 0)); pDevice->onSize(screenRect); OdGsView* pView = pDevice->viewAt(0); double dWidth = pGeoMap->imageWidth(); double dHeight = pGeoMap->imageHeight(); OdGePoint3dArray arrVertices; pGeoMap->getVertices(arrVertices); OdGePoint3d ptCenter = OdGeExtents3d().addPoints(arrVertices).center(); pView->setView( ptCenter + OdGeVector3d::kZAxis, ptCenter, OdGeVector3d::kYAxis, dWidth, dHeight); pView->setMode(OdGsView::k2DOptimized); OdDbGeoMapImageCreatorPtr pGeoMapImageCreator = OdDbGeoMapImageCreator::createObject(); pGeoMapImageCreator->setGeoMapId(pGeoMap->id()); pView->add(pGeoMapImageCreator, NULL); pDevice->update(); OdGiRasterImagePtr pImage = OdGiRasterImage::cast(pDevice->properties()->getAt(OD_T("RasterImage"))); OdSmartPtr pDesc = OdGiRasterImageDesc::createObject(pImage); pDesc->setColorDepth(32); pDesc->pixelFormat().setRGBA(); pDesc->setScanLinesAlignment(4); OdGiRasterImagePtr pRaster = pImage->convert(true, 50., 50., 0.0, 0, false, true, false, pDesc, true); const OdUInt8* pData = pRaster->scanLines(); imgPixelData.assign(pData, pData + 4 * pRaster->pixelWidth() * pRaster->pixelHeight()); return eOk; } bool OdDbGeoMapHelper::normalizeSize(double dWidth, double dHeight, OdUInt32& uWidth, OdUInt32& uHeight) { if (OdZero(dWidth, 1.e-6) || OdZero(dHeight, 1.e-6)) { uWidth = 0; uHeight = 0; return false; } if (dWidth < 1. || dHeight < 1.) { double dScale = dWidth <= dHeight ? 200. / dWidth : 200. / dHeight; dWidth *= dScale; dHeight *= dScale; } uWidth = static_cast(dWidth); uHeight = static_cast(dHeight); return uWidth && uHeight; } OdResult OdDbGeoMapHelper::getImageScale(const OdDbGeoData * pGeoData, OdUInt8 uLOD, OdUInt32 uTileSize, double& dScale) { dScale = 0.1; OdGePoint2d ptLatLonMin, ptLatLonMax; OdGeneralMaps::convertPixelXYToLatLong(0, 0, uLOD, uTileSize, ptLatLonMin.x, ptLatLonMin.y); OdGeneralMaps::convertPixelXYToLatLong(uTileSize, uTileSize, uLOD, uTileSize, ptLatLonMax.x, ptLatLonMax.y); OdGeExtents3d extents; OdGePoint3d ptRes; pGeoData->transformFromLonLatAlt(OdGePoint3d(ptLatLonMin.y, ptLatLonMin.x, 0.), ptRes); extents.addPoint(ptRes); pGeoData->transformFromLonLatAlt(OdGePoint3d(ptLatLonMin.y, ptLatLonMax.x, 0.), ptRes); extents.addPoint(ptRes); pGeoData->transformFromLonLatAlt(OdGePoint3d(ptLatLonMax.y, ptLatLonMin.x, 0.), ptRes); extents.addPoint(ptRes); pGeoData->transformFromLonLatAlt(OdGePoint3d(ptLatLonMax.y, ptLatLonMax.x, 0.), ptRes); extents.addPoint(ptRes); dScale = uTileSize / (extents.maxPoint().x - extents.minPoint().x); return eOk; } OdUInt8 OdDbGeoMapHelper::getOptimalLOD(const OdDbGeoData* pGeoData, OdUInt8 uMinLOD, OdUInt8 uMaxLOD, OdUInt32 uTileSize, double dViewportDiagLengthInPixels, double dMapDiagLength) { OdGePoint3d ptDesignLLA; pGeoData->transformToLonLatAlt(pGeoData->designPoint(), ptDesignLLA); double dHorizontalUnitScale = pGeoData->horizontalUnitScale(); double dPrevValue = 0.; double dValue = 1.e9; OdUInt8 uLOD = uMinLOD; for (OdUInt8 i = uLOD; i <= uMaxLOD; ++i) { dPrevValue = dValue; double dCurrent = fabs((dHorizontalUnitScale * dMapDiagLength / dViewportDiagLengthInPixels) - OdGeneralMaps::groundResolution(ptDesignLLA.y, i, uTileSize)); if (dValue > dCurrent) { dValue = dCurrent; } if (dPrevValue > dCurrent) { uLOD = i; } } return uLOD; } OdResult OdDbGeoMapHelper::getImageSize(double dScale, const OdGePoint3dArray & arrVertices, OdGePoint3d & ptImageBottomLeft, OdUInt32& uWidth, OdUInt32& uHeight, double & dImageWidth, double & dImageHeight) { OdGePoint3d ptImgBottomLeft; OdGePoint3d ptImgUpperRight; { OdGeExtents3d ext; ext.addPoints(arrVertices); ptImgBottomLeft.set(ext.minPoint().x, ext.minPoint().y, 0.); ptImgUpperRight.set(ext.maxPoint().x, ext.maxPoint().y, 0.); } //due to scale 1.02, we must also expand request area in future dImageWidth = (ptImgUpperRight.x - ptImgBottomLeft.x) * 1.02; dImageHeight = (ptImgUpperRight.y - ptImgBottomLeft.y) * 1.02; ptImageBottomLeft = (ptImgBottomLeft + ptImgUpperRight.asVector()) * 0.5 - OdGeVector3d(dImageWidth, dImageHeight, 0.) * 0.5; double dAspectRatio = dImageHeight / dImageWidth; double dWidth = (ptImgUpperRight.x - ptImgBottomLeft.x) * dScale; double dHeight = (ptImgUpperRight.y - ptImgBottomLeft.y) * dScale; uWidth = 0; uHeight = 0; if (!OdDbGeoMapHelper::normalizeSize(dWidth, dHeight, uWidth, uHeight)) { uWidth = 1; uHeight = 1; } // max ImageSize 2000x2000 if (uWidth > 2000 || uHeight > 2000) { if (uWidth > uHeight) { uWidth = 2000; uHeight = static_cast(dAspectRatio * 2000.); } else { uHeight = 2000; uWidth = static_cast(2000. / dAspectRatio); } } ////but min ImageSize 50õ50, and it's more preferable if (uWidth < 50 || uHeight < 50) { if (uWidth >= uHeight) { uHeight = 50; uWidth = static_cast(50. / dAspectRatio); } else { uWidth = 50; uHeight = static_cast(dAspectRatio * 50.); } } if (uWidth & 1) { ++uWidth; uHeight = static_cast(uWidth * dAspectRatio); } return eOk; } void OdDbGeoMapHelper::createLLAExtentsBy4Points(const OdGePoint2dArray& arrInput, OdGePoint2dArray& arrRes) { const OdUInt32 uSize = arrInput.size(); const OdUInt32 uStepsCount = 9; arrRes.resize(uSize * uStepsCount); OdUInt32 uIndex = 0; for (OdUInt32 i = 0; i < uSize; ++i) { OdGeVector2d vShift = (arrInput[(i + 1) % uSize] - arrInput[i]) / uStepsCount; for (OdUInt32 j = 0; j < uStepsCount; ++j) { arrRes[uIndex++] = arrInput[i] + vShift * j; } } } bool OdDbGeoMapHelper::checkPointInsidePolygon(const OdGePoint2d & ptInput, const OdGePoint2dArray & arrPt) { const OdUInt32 uSize = arrPt.size(); const OdGePoint2d * pPtLast = &arrPt[uSize - 1]; bool b4 = pPtLast->y >= ptInput.y; bool bInside = false; OdUInt32 uPointsDone = 0; const OdGePoint2d * pData = arrPt.asArrayPtr(); if (uSize >= 4) { OdUInt32 uSteps = uSize / 4; uPointsDone = uSteps * 4; do { bool b0 = pData[0].y >= ptInput.y; bool b1 = pData[1].y >= ptInput.y; bool b2 = pData[2].y >= ptInput.y; bool b3 = pData[3].y >= ptInput.y; if (b4 != b0 && b4 == (*pPtLast - pData[0]).crossProduct(ptInput - *pPtLast) >= 0.0) { bInside = !bInside; } if (b0 != b1 && b0 == (pData[0] - pData[1]).crossProduct(ptInput - pData[0]) >= 0.0) { bInside = !bInside; } if (b1 != b2 && b1 == (pData[1] - pData[2]).crossProduct(ptInput - pData[1]) >= 0.0) { bInside = !bInside; } if (b2 != b3 && b2 == (pData[2] - pData[3]).crossProduct(ptInput - pData[2]) >= 0.0) { bInside = !bInside; } b4 = b3; pPtLast = pData + 3; pData += 4; //shift 4 points --uSteps; } while (uSteps); } if (uPointsDone < uSize) { OdUInt32 uPointsLeft = uSize - uPointsDone; do { if (b4 != pData[0].y >= ptInput.y && b4 == (*pPtLast - pData[0]).crossProduct(ptInput - *pPtLast) >= 0.0) { bInside = !bInside; } pPtLast = pData; b4 = pData[0].y >= ptInput.y; ++pData; --uPointsLeft; } while (uPointsLeft); } return bInside; } bool OdDbGeoMapHelper::getPolygonsPoint(const OdGePoint2dArray& arr1, const OdGePoint2dArray& arr2, OdGePoint2d & pt) { const OdUInt32 uArr1Size = arr1.size(); const OdUInt32 uArr2Size = arr2.size(); for (OdUInt32 i = 0; i < uArr1Size; ++i) { if (checkPointInsidePolygon(arr1[i], arr2)) { pt = arr1[i]; return true; } } for (OdUInt32 i = 0; i < uArr2Size; ++i) { if (checkPointInsidePolygon(arr2[i], arr1)) { pt = arr2[i]; return true; } } double dTolerance = 1.e-10; OdGeLineSeg2d seg2; for (OdUInt32 i = 0; i < uArr2Size; ++i) { seg2.set(arr2[i], arr2[(i + 1) % uArr2Size]); if (dTolerance <= seg2.length()) { OdGeLineSeg2d seg1; for (OdUInt32 j = 0; j < uArr1Size; ++j) { seg1.set(arr1[j], arr1[(j + 1) % uArr1Size]); if (dTolerance <= seg1.length() && seg1.intersectWith(seg2, pt)) { return true; } } return false; } } return false; } void OdDbGeoMapHelper::getTextColor(OdGeoMapType eType, OdCmColor& cmColor) { //arx: kBingRoad, kEsriOpenStreetMap, kEsriDarkGray - black // kBingAerial, kBingHybrid, kEsriImagery, kEsriStreets, kEsriLightGray - white // our variant is better: switch (eType) { case kBingRoad: case kEsriOpenStreetMap: case kEsriStreets: case kEsriLightGray: cmColor.setRGB(0, 0, 0); //black break; case kBingAerial: case kBingHybrid: case kEsriImagery: case kEsriDarkGray: default: cmColor.setRGB(255, 255, 255); //white break; } } void OdDbGeoMapHelper::getChunkVertices(OdUInt8 uLOD, OdUInt32 uTileSize, OdUInt32 uTileX, OdUInt32 uTileY, const OdDbGeoData * pGeoData, OdGePoint3dArray & arrChunkVertices) { double dLatitudeMin = 0., dLongitudeMin = 0.; double dLatitudeMax = 0., dLongitudeMax = 0.; OdGeneralMaps::convertTileXYToLatLong(uTileX, uTileY, uLOD, uTileSize, dLatitudeMin, dLongitudeMin); OdGeneralMaps::convertTileXYToLatLong(uTileX + 1, uTileY + 1, uLOD, uTileSize, dLatitudeMax, dLongitudeMax); const OdUInt32 uRectSize = 4; arrChunkVertices.resize(uRectSize); arrChunkVertices[0].set(dLongitudeMin, dLatitudeMin, 0.); arrChunkVertices[1].set(dLongitudeMax, dLatitudeMin, 0.); arrChunkVertices[2].set(dLongitudeMax, dLatitudeMax, 0.); arrChunkVertices[3].set(dLongitudeMin, dLatitudeMax, 0.); for (OdUInt32 i = 0; i < uRectSize; ++i) { pGeoData->transformFromLonLatAlt(arrChunkVertices[i], arrChunkVertices[i]); } } void OdDbGeoMapHelper::getChunkVertices2d(OdUInt8 uLOD, OdUInt32 uTileSize, OdUInt32 uTileX, OdUInt32 uTileY, const OdDbGeoData* pGeoData, OdGePoint2dArray& arrChunkVertices2d) { OdGePoint3dArray arrChunkVertices; getChunkVertices(uLOD, uTileSize, uTileX, uTileY, pGeoData, arrChunkVertices); arrChunkVertices2d.resize(arrChunkVertices.size()); for (OdUInt32 i = 0; i < arrChunkVertices.size(); ++i) { arrChunkVertices2d[i] = arrChunkVertices[i].convert2d(); } } void OdDbGeoMapHelper::addTileToStack(OdStack& stackTiles, OdGeoMapType eGeoMapType, OdUInt8 uLOD, OdUInt32 uTileSize, OdUInt32 uTileX, OdUInt32 uTileY, const OdDbGeoData * pGeoData, const OdGePoint2dArray & arrMapExtents, const OdGePoint2dArray& arrGlobalMapExtents, const std::set & arrTiles) { if (!OdGeneralMaps::isTileValid(uLOD, uTileX, uTileY)) { return; } OdDbGeoMapTile tile(eGeoMapType, uLOD, uTileX, uTileY); if (arrTiles.find(tile) != arrTiles.end()) { return; } OdGePoint2dArray arrChunkVertices; getChunkVertices2d(uLOD, uTileSize, uTileX, uTileY, pGeoData, arrChunkVertices); OdGePoint2d ptRes; if (OdDbGeoMapHelper::getPolygonsPoint(arrChunkVertices, arrMapExtents, ptRes) && OdDbGeoMapHelper::getPolygonsPoint(arrChunkVertices, arrGlobalMapExtents, ptRes)) { stackTiles.push(tile); } } OdResult OdDbGeoMapHelper::getGlobalMapExtents(const OdDbGeoData * pGeoData, OdGePoint2dArray & arrMapExtents) { OdDbGeoCoordinateSystemPtr pCs; OdResult res = OdDbGeoCoordinateSystem::create(pGeoData->coordinateSystem(), pCs); if (eOk != res) { return res; } OdGeExtents2d extGeo; pCs->getGeodeticExtents(extGeo); OdGePoint2d ptMin; OdGePoint2d ptMax; ptMin.x = OdGeneralMaps::clip(extGeo.minPoint().x, OdGeneralMaps::MinLongitude, OdGeneralMaps::MaxLongitude); ptMin.y = OdGeneralMaps::clip(extGeo.minPoint().y, OdGeneralMaps::MinLatitude, OdGeneralMaps::MaxLatitude); ptMax.x = OdGeneralMaps::clip(extGeo.maxPoint().x, OdGeneralMaps::MinLongitude, OdGeneralMaps::MaxLongitude); ptMax.y = OdGeneralMaps::clip(extGeo.maxPoint().y, OdGeneralMaps::MinLatitude, OdGeneralMaps::MaxLatitude); OdGeVector2d vShift(0.01, 0.01); ptMin += vShift; ptMax -= vShift; OdGePoint2dArray arrInput; arrInput.resize(4); arrInput[0].set(ptMin.x, ptMin.y); arrInput[1].set(ptMax.x, ptMin.y); arrInput[2].set(ptMax.x, ptMax.y); arrInput[3].set(ptMin.x, ptMax.y); OdGePoint2dArray arrResLLA; OdDbGeoMapHelper::createLLAExtentsBy4Points(arrInput, arrResLLA); arrMapExtents.resize(arrResLLA.size()); OdGePoint3d ptBuf; for (OdUInt32 i = 0; i < arrResLLA.size(); ++i) { pGeoData->transformFromLonLatAlt(OdGePoint3d(arrResLLA[i].x, arrResLLA[i].y, 0.), ptBuf); arrMapExtents[i] = ptBuf.convert2d(); } return eOk; } OdResult OdDbGeoMapHelper::getGeoMapTiles(std::set& arrTiles, const OdDbGeoData* pGeoData, OdGeoMapType eGeoMapType, OdUInt8 uLOD, OdUInt32 uTileSize, const OdGePoint2dArray& arrMapExtents, const OdGePoint2dArray& arrGlobalMapExtents) { // get first tile OdGePoint2d pt2d; OdDbGeoMapHelper::getPolygonsPoint(arrMapExtents, arrGlobalMapExtents, pt2d); OdGePoint3d pt3d(pt2d.x, pt2d.y, pGeoData->designPoint().z); OdGePoint3d pt3dLLA; pGeoData->transformToLonLatAlt(pt3d, pt3dLLA); OdUInt32 uTileX, uTileY; OdGeneralMaps::convertLatLongToTileXY(pt3dLLA.y, pt3dLLA.x, uLOD, uTileSize, uTileX, uTileY); OdDbGeoMapTile firstTile(eGeoMapType, uLOD, uTileX, uTileY); OdStack stackTiles; stackTiles.push(firstTile); while (!stackTiles.empty()) { OdDbGeoMapTile* pTile = stackTiles.top(); uTileX = pTile->getX(); uTileY = pTile->getY(); arrTiles.insert(*pTile); stackTiles.pop(); addTileToStack(stackTiles, eGeoMapType, uLOD, uTileSize, uTileX - 1, uTileY, pGeoData, arrMapExtents, arrGlobalMapExtents, arrTiles); addTileToStack(stackTiles, eGeoMapType, uLOD, uTileSize, uTileX + 1, uTileY, pGeoData, arrMapExtents, arrGlobalMapExtents, arrTiles); addTileToStack(stackTiles, eGeoMapType, uLOD, uTileSize, uTileX, uTileY - 1, pGeoData, arrMapExtents, arrGlobalMapExtents, arrTiles); addTileToStack(stackTiles, eGeoMapType, uLOD, uTileSize, uTileX, uTileY + 1, pGeoData, arrMapExtents, arrGlobalMapExtents, arrTiles); if (arrTiles.size() > 2500) { //extents are to big return eInvalidExtents; } } return eOk; } OdGeoMapType OdDbGeoMapHelper::getGeoMapType(OdDbObjectId viewportId) { OdDbObjectPtr pVp = viewportId.safeOpenObject(); OdDbDictionaryPtr pExtDictionary = pVp->extensionDictionary().openObject(); OdDbDictionaryVarPtr pDictionaryVar; if (pExtDictionary.get()) { OdDbDictionaryPtr pExtVariableDictionary = pExtDictionary->getAt(L"AcDbVariableDictionary").openObject(); if (pExtVariableDictionary.get()) { pDictionaryVar = pExtVariableDictionary->getAt(L"GEOMAPMODE").openObject(); } } if (pDictionaryVar.isNull()) { return OdGeoMapType::kNoMap; } OdString sGeoMapType = pDictionaryVar->value(); return (OdGeoMapType)Od_strtoint(sGeoMapType); } OdResult OdDbGeoMapHelper::getGeoData(OdDbDatabase * pDb, OdDbGeoDataPtr & pGeoData) { OdDbObjectId idGeoData; if (eOk != oddbGetGeoDataObjId(pDb, idGeoData) || idGeoData.isNull() || (pGeoData = idGeoData.openObject()).isNull() ) { return eInvalidInput; } return eOk; } void OdDbGeoMapHelper::drawTiles(OdGiViewportDraw* pVd, const OdDbGeoData * pGeoData, const OdGePoint2dArray & arrGlobalMapExtents, const std::set & arrTiles, const OdArray & arrTilesImages, OdUInt32 uTileSize) { // clipping boundary OdGiClipBoundary boundary; boundary.m_vNormal = pGeoData->upDirection(); boundary.m_Points = arrGlobalMapExtents; boundary.m_dFrontClipZ = 0.; boundary.m_dBackClipZ = 0.; boundary.m_bClippingFront = false; boundary.m_bClippingBack = false; boundary.m_bDrawBoundary = false; pVd->geometry().pushClipBoundary(&boundary); double dZCorrection = 0.1; double dHorizontalUnitScale = pGeoData->horizontalUnitScale(); if (dHorizontalUnitScale > OdGeContext::gTol.equalPoint()) { dZCorrection = 0.1 / dHorizontalUnitScale; } if (pVd->viewport().viewDir().z > 0) { dZCorrection = -dZCorrection; } OdUInt32 uIndex = 0; std::set::const_iterator pIt = arrTiles.begin(); std::set::const_iterator pEnd = arrTiles.end(); for (; pIt != pEnd; ++pIt, ++uIndex) { // get chunk clip boundary const OdDbGeoMapTile& tile = *pIt; OdGePoint3dArray arrChunkVertices; getChunkVertices(tile.getLOD(), uTileSize, tile.getX(), tile.getY(), pGeoData, arrChunkVertices); for (OdUInt32 i = 0; i < 4; ++i) { arrChunkVertices[i].z = dZCorrection; } arrChunkVertices.swap(1, 3); //correct order for draw func // draw tile odgiImageViewport(pVd->geometry(), arrTilesImages[uIndex], arrChunkVertices.asArrayPtr()); } pVd->geometry().popClipBoundary(); }