/////////////////////////////////////////////////////////////////////////////// // 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 "OdGeoMapTilesCache.h" #include "OdGeoMapCurlRequstHelper.h" #include "OdGeneralMaps.h" #include "Gi/GiRasterWrappers.h" #include "UInt32Array.h" #include "FlatMemStream.h" OdGeoMapTilesCache::OdGeoMapTilesCache() : m_uCachedTilesCount(5000) , m_uMultiRequestsCount(16) { m_arrProvider.resize(8); UseBingOrAzureMaps(true); { //Esri OdString sEsriMapKey; odrxSystemServices()->getEnvVar(L"OD_ESRI_MAP_KEY", sEsriMapKey); if (sEsriMapKey.isEmpty()) { ODA_ASSERT_ONCE(!"OD_ESRI_MAP_KEY is not specified!"); } OdAnsiString sAnsiEsriMapKey = OdAnsiString(sEsriMapKey); //Esri Imagery m_arrProvider[3] = (OdGeoMapProviderInfoPtr(new OdGeoMapEsriImageryProviderInfo(sAnsiEsriMapKey, kEsriImagery))); //Esri OSM m_arrProvider[4] = OdGeoMapProviderInfoPtr(new OdGeoMapEsriOSMProviderInfo(sAnsiEsriMapKey, kEsriOpenStreetMap)); m_arrProvider[5] = OdGeoMapProviderInfoPtr(new OdGeoMapEsriOSMProviderInfo(sAnsiEsriMapKey, kEsriStreets)); m_arrProvider[6] = OdGeoMapProviderInfoPtr(new OdGeoMapEsriOSMProviderInfo(sAnsiEsriMapKey, kEsriLightGray)); m_arrProvider[7] = OdGeoMapProviderInfoPtr(new OdGeoMapEsriOSMProviderInfo(sAnsiEsriMapKey, kEsriDarkGray)); } } OdGeoMapTilesCache::~OdGeoMapTilesCache() { } void OdGeoMapTilesCache::UseBingOrAzureMaps(bool bBingOrAzureMaps) { clearCache(); TD_AUTOLOCK(m_mutex); // NOTE: // Bing Maps for Enterprise is deprecatedand will be retired. // Free (Basic) account customers can continue to use Bing Maps for Enterprise services until June 30th, 2025. // Enterprise account customers can continue to use Bing Maps for Enterprise services until June 30th, 2028. if (bBingOrAzureMaps) { //Bing OdString sBingMapKey; odrxSystemServices()->getEnvVar(L"OD_BING_MAP_KEY", sBingMapKey); if (sBingMapKey.isEmpty()) { odrxSystemServices()->getEnvVar(L"CS_MAP_KEY", sBingMapKey); if (sBingMapKey.isEmpty()) { ODA_ASSERT_ONCE(!"OD_BING_MAP_KEY (or CS_MAP_KEY) is not specified!"); } } OdAnsiString sAnsiBingMapKey = OdAnsiString(sBingMapKey); m_arrProvider[0] = OdGeoMapProviderInfoPtr(new OdGeoMapBingProviderInfo(sAnsiBingMapKey, kAerial)); m_arrProvider[1] = OdGeoMapProviderInfoPtr(new OdGeoMapBingProviderInfo(sAnsiBingMapKey, kRoad)); m_arrProvider[2] = OdGeoMapProviderInfoPtr(new OdGeoMapBingProviderInfo(sAnsiBingMapKey, kHybrid)); } else { //Azure OdString sAzureMapsKey; odrxSystemServices()->getEnvVar(L"OD_AZURE_MAPS_KEY", sAzureMapsKey); if (sAzureMapsKey.isEmpty()) { ODA_ASSERT_ONCE(!"OD_AZURE_MAPS_KEY is not specified!"); } OdAnsiString sAnsiAzureMapsKey = OdAnsiString(sAzureMapsKey); m_arrProvider[0] = OdGeoMapProviderInfoPtr(new OdGeoMapAzureProviderInfo(sAnsiAzureMapsKey, 256, 1, 19, kAerial)); m_arrProvider[1] = OdGeoMapProviderInfoPtr(new OdGeoMapAzureProviderInfo(sAnsiAzureMapsKey, 256, 0, 22, kRoad)); m_arrProvider[2] = OdGeoMapProviderInfoPtr(new OdGeoMapAzureProviderInfo(sAnsiAzureMapsKey, 256, 0, 22, kHybrid)); } } void OdGeoMapTilesCache::setCachedTilesCount(OdUInt32 uCachedTilesCount) { TD_AUTOLOCK(m_mutex); m_uCachedTilesCount = uCachedTilesCount; checkTilesLimit(); } void OdGeoMapTilesCache::setMultiRequestsCount(OdUInt32 uMultiRequestsCount) { TD_AUTOLOCK(m_mutex); m_uMultiRequestsCount = uMultiRequestsCount; } OdResult OdGeoMapTilesCache::getTiles(const std::set & arrTiles, OdArray& arrRes) { TD_AUTOLOCK(m_mutex); const OdUInt32 uSize = OdUInt32(arrTiles.size()); arrRes.resize(uSize); //get cached images, generate absent adresses array: OdAnsiStringArray arrAbsentAdresses; arrAbsentAdresses.reserve(uSize); OdArray arrAbsentTiles; arrAbsentTiles.reserve(uSize); OdUInt32Array arrAbsenceIndexes; arrAbsenceIndexes.reserve(uSize); OdAnsiString sAdress; std::set::const_iterator pItTiles = arrTiles.begin(); for (OdUInt32 i = 0; i < uSize; ++i, ++pItTiles) { getTileUrl(*pItTiles, sAdress); Map::iterator pIt = m_map.find(*pItTiles); if (m_map.end() == pIt) { arrAbsentTiles.push_back(*pItTiles); arrAbsentAdresses.push_back(sAdress); arrAbsenceIndexes.push_back(i); } else { //assigne result: arrRes[i] = pIt->second; } } //request absent images: OdArray arrAbsentImages; OdResult res = OdGeoMapCurlRequstHelper::multiRequst(arrAbsentAdresses, arrAbsentImages, m_uMultiRequestsCount); if (eOk != res) { return res; } //fill result array with new images and add images to cache: for (OdUInt32 i = 0; i < arrAbsentImages.size(); ++i) { OdGiRasterImagePtr pImage = arrAbsentImages[i]; if (pImage.get()) { OdSmartPtr pDesc = OdGiRasterImageDesc::createObject(pImage); pImage = pImage->convert(true, 50., 50., 0.0, 0, false, true, false, pDesc); } //assigne result: arrRes[arrAbsenceIndexes[i]] = pImage; //put result in cache: const OdDbGeoMapTile& tile = arrAbsentTiles[i]; m_map.insert(Map::value_type(tile, pImage)); m_deque.push_back(tile); //check cache limit: checkTilesLimit(); } //create sub images for non existent images, put them into cache: pItTiles = arrTiles.begin(); for (OdUInt32 i = 0; i < arrRes.size(); ++i, ++pItTiles) { if (arrRes[i].isNull()) { // take image subpart of parent tile OdArray arrBadTilesImages; OdDbGeoMapTile tile = *pItTiles; OdUInt32 uParentTileX = tile.getX(); OdUInt32 uParentTileY = tile.getY(); OdUInt32 uParentSteps = 0; do { if (!tile.makeParentTile()) { return eInternetFileNotFound; } std::set arrBadTiles; arrBadTiles.insert(tile); res = getTiles(arrBadTiles, arrBadTilesImages); if (eOk != res) { return res; } ++uParentSteps; } while (arrBadTilesImages[0].isNull()); OdGiRasterImagePtr pRaster = arrBadTilesImages[0]; OdUInt32 uTileSize = pRaster->pixelWidth(); //crop subImage from parent tile OdUInt32 uChunkDifference = 1 << uParentSteps; // pow(2, nParentSteps) OdUInt32 uPixelX, uPixelY; OdGeneralMaps::convertTileXYToPixelXY(uParentTileX, uParentTileY, uTileSize, uPixelX, uPixelY); OdUInt32 uChunkSize = uTileSize * uChunkDifference; uPixelX %= uChunkSize; uPixelY %= uChunkSize; uPixelX /= uChunkDifference; uPixelY /= uChunkDifference; OdUInt32 uSubTileSize = uTileSize / uChunkDifference; if (0 == uSubTileSize) { uSubTileSize = 1; } pRaster = pRaster->crop(uPixelX, uPixelY, uSubTileSize, uSubTileSize); OdSmartPtr pDesc = OdGiRasterImageDesc::createObject(pRaster); pDesc->setPixelWidth(uTileSize); pDesc->setPixelHeight(uTileSize); //assigne result: pRaster = pRaster->convert(true, 50., 50., 0.0, 0, false, false, false, pDesc); arrRes[i] = pRaster; } } return eOk; } OdResult OdGeoMapTilesCache::getTileInfo(OdGeoMapType eType, OdUInt32& uTileSize, OdUInt8& uMinLOD, OdUInt8& uMaxLOD) { TD_AUTOLOCK(m_mutex); OdUInt32 uIndex = OdUInt32(eType) - 1; if (uIndex >= m_arrProvider.size()) { return eInvalidInput; } return m_arrProvider[uIndex]->getTileInfo(uTileSize, uMinLOD, uMaxLOD); } OdResult OdGeoMapTilesCache::getTileUrl(const OdDbGeoMapTile& tile, OdAnsiString& sUrl) { TD_AUTOLOCK(m_mutex); OdUInt32 uIndex = OdUInt32(tile.getType()) - 1; if (uIndex >= m_arrProvider.size()) { return eInvalidInput; } return m_arrProvider[uIndex]->getTileUrl(tile.getLOD(), tile.getX(), tile.getY(), sUrl); } OdResult OdGeoMapTilesCache::getCopyrightStrings(OdGeoMapType eType, const OdDbGeoData* pGeoData, OdUInt8 uLOD, const OdGePoint2dArray& arrImageArea, OdStringArray& arrCopyrightStrings) { TD_AUTOLOCK(m_mutex); OdUInt32 uIndex = OdUInt32(eType) - 1; if (uIndex >= m_arrProvider.size()) { return eInvalidInput; } return m_arrProvider[uIndex]->getCopyrightStrings(pGeoData, uLOD, arrImageArea, arrCopyrightStrings); } OdResult OdGeoMapTilesCache::getBrandLogo(OdGeoMapType eType, OdGiRasterImagePtr& pRasterLogo, bool bUpdate) { TD_AUTOLOCK(m_mutex); OdUInt32 uIndex = OdUInt32(eType) - 1; if (uIndex >= m_arrProvider.size()) { return eInvalidInput; } return m_arrProvider[uIndex]->getBrandLogo(pRasterLogo, bUpdate); } void OdGeoMapTilesCache::clearCache() { TD_AUTOLOCK(m_mutex); m_deque.clear(); m_map.clear(); } void OdGeoMapTilesCache::checkTilesLimit() { //check cache limit: while (m_deque.size() > m_uCachedTilesCount) { const OdDbGeoMapTile& tile = m_deque.front(); m_map.erase(tile); m_deque.pop_front(); } } //g_OdGeoMapTilesCache OdGeoMapTilesCache* g_OdGeoMapTilesCache = NULL;