Line data Source code
1 : /****************************************************************************** 2 : * 3 : * Project: Microstation DGN Access Library 4 : * Purpose: DGN Access Library file open code. 5 : * Author: Frank Warmerdam, warmerdam@pobox.com 6 : * 7 : ****************************************************************************** 8 : * Copyright (c) 2000, Avenza Systems Inc, http://www.avenza.com/ 9 : * 10 : * SPDX-License-Identifier: MIT 11 : ****************************************************************************/ 12 : 13 : #include "dgnlibp.h" 14 : 15 : /************************************************************************/ 16 : /* DGNTestOpen() */ 17 : /************************************************************************/ 18 : 19 : /** 20 : * Test if header is DGN. 21 : * 22 : * @param pabyHeader block of header data from beginning of file. 23 : * @param nByteCount number of bytes in pabyHeader. 24 : * 25 : * @return TRUE if the header appears to be from a DGN file, otherwise FALSE. 26 : */ 27 : 28 3928 : int DGNTestOpen(GByte *pabyHeader, int nByteCount) 29 : 30 : { 31 3928 : if (nByteCount < 4) 32 0 : return FALSE; 33 : 34 : // Is it a cell library? 35 3928 : if (pabyHeader[0] == 0x08 && pabyHeader[1] == 0x05 && 36 0 : pabyHeader[2] == 0x17 && pabyHeader[3] == 0x00) 37 0 : return TRUE; 38 : 39 : // Is it not a regular 2D or 3D file? 40 3928 : if ((pabyHeader[0] != 0x08 && pabyHeader[0] != 0xC8) || 41 194 : pabyHeader[1] != 0x09 || pabyHeader[2] != 0xFE || pabyHeader[3] != 0x02) 42 3734 : return FALSE; 43 : 44 194 : return TRUE; 45 : } 46 : 47 : /************************************************************************/ 48 : /* DGNOpen() */ 49 : /************************************************************************/ 50 : 51 : /** 52 : * Open a DGN file. 53 : * 54 : * The file is opened, and minimally verified to ensure it is a DGN (ISFF) 55 : * file. If the file cannot be opened for read access an error with code 56 : * CPLE_OpenFailed with be reported via CPLError() and NULL returned. 57 : * If the file header does 58 : * not appear to be a DGN file, an error with code CPLE_AppDefined will be 59 : * reported via CPLError(), and NULL returned. 60 : * 61 : * If successful a handle for further access is returned. This should be 62 : * closed with DGNClose() when no longer needed. 63 : * 64 : * DGNOpen() does not scan the file on open, and should be very fast even for 65 : * large files. 66 : * 67 : * @param pszFilename name of file to try opening. 68 : * @param bUpdate should the file be opened with read+update (r+) mode? 69 : * 70 : * @return handle to use for further access to file using DGN API, or NULL 71 : * if open fails. 72 : */ 73 : 74 110 : DGNHandle DGNOpen(const char *pszFilename, int bUpdate) 75 : 76 : { 77 : /* -------------------------------------------------------------------- */ 78 : /* Open the file. */ 79 : /* -------------------------------------------------------------------- */ 80 110 : VSILFILE *fp = VSIFOpenL(pszFilename, bUpdate ? "rb+" : "rb"); 81 110 : if (fp == nullptr) 82 : { 83 0 : CPLError(CE_Failure, CPLE_OpenFailed, 84 : "Unable to open `%s' for read access.\n", pszFilename); 85 0 : return nullptr; 86 : } 87 : 88 : /* -------------------------------------------------------------------- */ 89 : /* Verify the format ... add later. */ 90 : /* -------------------------------------------------------------------- */ 91 : GByte abyHeader[512]; 92 : const int nHeaderBytes = 93 110 : static_cast<int>(VSIFReadL(abyHeader, 1, sizeof(abyHeader), fp)); 94 110 : if (!DGNTestOpen(abyHeader, nHeaderBytes)) 95 : { 96 0 : CPLError(CE_Failure, CPLE_AppDefined, 97 : "File `%s' does not have expected DGN header.\n", pszFilename); 98 0 : VSIFCloseL(fp); 99 0 : return nullptr; 100 : } 101 : 102 110 : VSIRewindL(fp); 103 : 104 : /* -------------------------------------------------------------------- */ 105 : /* Create the info structure. */ 106 : /* -------------------------------------------------------------------- */ 107 110 : DGNInfo *psDGN = static_cast<DGNInfo *>(CPLCalloc(sizeof(DGNInfo), 1)); 108 110 : psDGN->fp = fp; 109 110 : psDGN->next_element_id = 0; 110 : 111 110 : psDGN->got_tcb = false; 112 110 : psDGN->scale = 1.0; 113 110 : psDGN->origin_x = 0.0; 114 110 : psDGN->origin_y = 0.0; 115 110 : psDGN->origin_z = 0.0; 116 : 117 110 : psDGN->index_built = false; 118 110 : psDGN->element_count = 0; 119 110 : psDGN->element_index = nullptr; 120 : 121 110 : psDGN->got_bounds = false; 122 : 123 110 : if (abyHeader[0] == 0xC8) 124 42 : psDGN->dimension = 3; 125 : else 126 68 : psDGN->dimension = 2; 127 : 128 110 : psDGN->has_spatial_filter = false; 129 110 : psDGN->sf_converted_to_uor = false; 130 110 : psDGN->select_complex_group = false; 131 110 : psDGN->in_complex_group = false; 132 : 133 110 : return (DGNHandle)psDGN; 134 : } 135 : 136 : /************************************************************************/ 137 : /* DGNSetOptions() */ 138 : /************************************************************************/ 139 : 140 : /** 141 : * Set file access options. 142 : * 143 : * Sets a flag affecting how the file is accessed. Currently 144 : * there is only one support flag: 145 : * 146 : * DGNO_CAPTURE_RAW_DATA: If this is enabled (it is off by default), 147 : * then the raw binary data associated with elements will be kept in 148 : * the raw_data field within the DGNElemCore when they are read. This 149 : * is required if the application needs to interpret the raw data itself. 150 : * It is also necessary if the element is to be written back to this file, 151 : * or another file using DGNWriteElement(). Off by default (to conserve 152 : * memory). 153 : * 154 : * @param hDGN handle to file returned by DGNOpen(). 155 : * @param nOptions ORed option flags. 156 : */ 157 : 158 34 : void DGNSetOptions(DGNHandle hDGN, int nOptions) 159 : 160 : { 161 34 : DGNInfo *psDGN = (DGNInfo *)hDGN; 162 : 163 34 : psDGN->options = nOptions; 164 34 : } 165 : 166 : /************************************************************************/ 167 : /* DGNSetSpatialFilter() */ 168 : /************************************************************************/ 169 : 170 : /** 171 : * Set rectangle for which features are desired. 172 : * 173 : * If a spatial filter is set with this function, DGNReadElement() will 174 : * only return spatial elements (elements with a known bounding box) and 175 : * only those elements for which this bounding box overlaps the requested 176 : * region. 177 : * 178 : * If all four values (dfXMin, dfXMax, dfYMin and dfYMax) are zero, the 179 : * spatial filter is disabled. Note that installing a spatial filter 180 : * won't reduce the amount of data read from disk. All elements are still 181 : * scanned, but the amount of processing work for elements outside the 182 : * spatial filter is minimized. 183 : * 184 : * @param hDGN Handle from DGNOpen() for file to update. 185 : * @param dfXMin minimum x coordinate for extents (georeferenced coordinates). 186 : * @param dfYMin minimum y coordinate for extents (georeferenced coordinates). 187 : * @param dfXMax maximum x coordinate for extents (georeferenced coordinates). 188 : * @param dfYMax maximum y coordinate for extents (georeferenced coordinates). 189 : */ 190 : 191 2 : void DGNSetSpatialFilter(DGNHandle hDGN, double dfXMin, double dfYMin, 192 : double dfXMax, double dfYMax) 193 : 194 : { 195 2 : DGNInfo *psDGN = (DGNInfo *)hDGN; 196 : 197 2 : if (dfXMin == 0.0 && dfXMax == 0.0 && dfYMin == 0.0 && dfYMax == 0.0) 198 : { 199 1 : psDGN->has_spatial_filter = false; 200 1 : return; 201 : } 202 : 203 1 : psDGN->has_spatial_filter = true; 204 1 : psDGN->sf_converted_to_uor = false; 205 : 206 1 : psDGN->sf_min_x_geo = dfXMin; 207 1 : psDGN->sf_min_y_geo = dfYMin; 208 1 : psDGN->sf_max_x_geo = dfXMax; 209 1 : psDGN->sf_max_y_geo = dfYMax; 210 : 211 1 : DGNSpatialFilterToUOR(psDGN); 212 : } 213 : 214 : /************************************************************************/ 215 : /* DGNSpatialFilterToUOR() */ 216 : /************************************************************************/ 217 : 218 2 : void DGNSpatialFilterToUOR(DGNInfo *psDGN) 219 : 220 : { 221 2 : if (psDGN->sf_converted_to_uor || !psDGN->has_spatial_filter || 222 2 : !psDGN->got_tcb) 223 1 : return; 224 : 225 1 : DGNPoint sMin = {psDGN->sf_min_x_geo, psDGN->sf_min_y_geo, 0}; 226 : 227 1 : DGNPoint sMax = {psDGN->sf_max_x_geo, psDGN->sf_max_y_geo, 0}; 228 : 229 1 : DGNInverseTransformPoint(psDGN, &sMin); 230 1 : DGNInverseTransformPoint(psDGN, &sMax); 231 : 232 1 : psDGN->sf_min_x = (GUInt32)(sMin.x + 2147483648.0); 233 1 : psDGN->sf_min_y = (GUInt32)(sMin.y + 2147483648.0); 234 1 : psDGN->sf_max_x = (GUInt32)(sMax.x + 2147483648.0); 235 1 : psDGN->sf_max_y = (GUInt32)(sMax.y + 2147483648.0); 236 : 237 1 : psDGN->sf_converted_to_uor = true; 238 : } 239 : 240 : /************************************************************************/ 241 : /* DGNClose() */ 242 : /************************************************************************/ 243 : 244 : /** 245 : * Close DGN file. 246 : * 247 : * @param hDGN Handle from DGNOpen() for file to close. 248 : */ 249 : 250 110 : void DGNClose(DGNHandle hDGN) 251 : 252 : { 253 110 : DGNInfo *psDGN = (DGNInfo *)hDGN; 254 : 255 110 : VSIFCloseL(psDGN->fp); 256 110 : CPLFree(psDGN->element_index); 257 110 : CPLFree(psDGN); 258 110 : } 259 : 260 : /************************************************************************/ 261 : /* DGNGetDimension() */ 262 : /************************************************************************/ 263 : 264 : /** 265 : * Return 2D/3D dimension of file. 266 : * 267 : * Return 2 or 3 depending on the dimension value of the provided file. 268 : */ 269 : 270 42 : int DGNGetDimension(DGNHandle hDGN) 271 : 272 : { 273 42 : DGNInfo *psDGN = (DGNInfo *)hDGN; 274 : 275 42 : return psDGN->dimension; 276 : }