Line data Source code
1 : /******************************************************************************
2 : *
3 : * Project: OpenGIS Simple Features Reference Implementation
4 : * Purpose: Implements OGRDGNLayer class.
5 : * Author: Frank Warmerdam, warmerdam@pobox.com
6 : *
7 : ******************************************************************************
8 : * Copyright (c) 2000, Frank Warmerdam (warmerdam@pobox.com)
9 : *
10 : * SPDX-License-Identifier: MIT
11 : ****************************************************************************/
12 :
13 : #include "ogr_dgn.h"
14 : #include "cpl_conv.h"
15 : #include "ogr_featurestyle.h"
16 : #include "ogr_api.h"
17 :
18 : #include <algorithm>
19 : #include <cmath>
20 : #include <list>
21 :
22 : /************************************************************************/
23 : /* OGRDGNLayer() */
24 : /************************************************************************/
25 :
26 76 : OGRDGNLayer::OGRDGNLayer(OGRDGNDataSource *poDS, const char *pszName,
27 76 : DGNHandle hDGNIn, int bUpdateIn)
28 76 : : m_poDS(poDS), poFeatureDefn(new OGRFeatureDefn(pszName)), iNextShapeId(0),
29 76 : hDGN(hDGNIn), bUpdate(bUpdateIn)
30 : {
31 :
32 : /* -------------------------------------------------------------------- */
33 : /* Work out what link format we are using. */
34 : /* -------------------------------------------------------------------- */
35 : OGRFieldType eLinkFieldType;
36 :
37 76 : pszLinkFormat =
38 76 : const_cast<char *>(CPLGetConfigOption("DGN_LINK_FORMAT", "FIRST"));
39 :
40 76 : if (EQUAL(pszLinkFormat, "FIRST"))
41 76 : eLinkFieldType = OFTInteger;
42 0 : else if (EQUAL(pszLinkFormat, "LIST"))
43 0 : eLinkFieldType = OFTIntegerList;
44 0 : else if (EQUAL(pszLinkFormat, "STRING"))
45 0 : eLinkFieldType = OFTString;
46 : else
47 : {
48 0 : CPLError(CE_Warning, CPLE_AppDefined,
49 : "DGN_LINK_FORMAT=%s, but only FIRST, LIST or STRING "
50 : "supported.",
51 : pszLinkFormat);
52 0 : pszLinkFormat = const_cast<char *>("FIRST");
53 0 : eLinkFieldType = OFTInteger;
54 : }
55 76 : pszLinkFormat = CPLStrdup(pszLinkFormat);
56 :
57 : /* -------------------------------------------------------------------- */
58 : /* Create the feature definition. */
59 : /* -------------------------------------------------------------------- */
60 76 : SetDescription(poFeatureDefn->GetName());
61 76 : poFeatureDefn->Reference();
62 :
63 76 : OGRFieldDefn oField("", OFTInteger);
64 :
65 : /* -------------------------------------------------------------------- */
66 : /* Element type */
67 : /* -------------------------------------------------------------------- */
68 76 : oField.SetName("Type");
69 76 : oField.SetType(OFTInteger);
70 76 : oField.SetWidth(2);
71 76 : oField.SetPrecision(0);
72 76 : poFeatureDefn->AddFieldDefn(&oField);
73 :
74 : /* -------------------------------------------------------------------- */
75 : /* Level number. */
76 : /* -------------------------------------------------------------------- */
77 76 : oField.SetName("Level");
78 76 : oField.SetType(OFTInteger);
79 76 : oField.SetWidth(2);
80 76 : oField.SetPrecision(0);
81 76 : poFeatureDefn->AddFieldDefn(&oField);
82 :
83 : /* -------------------------------------------------------------------- */
84 : /* graphic group */
85 : /* -------------------------------------------------------------------- */
86 76 : oField.SetName("GraphicGroup");
87 76 : oField.SetType(OFTInteger);
88 76 : oField.SetWidth(4);
89 76 : oField.SetPrecision(0);
90 76 : poFeatureDefn->AddFieldDefn(&oField);
91 :
92 : /* -------------------------------------------------------------------- */
93 : /* ColorIndex */
94 : /* -------------------------------------------------------------------- */
95 76 : oField.SetName("ColorIndex");
96 76 : oField.SetType(OFTInteger);
97 76 : oField.SetWidth(3);
98 76 : oField.SetPrecision(0);
99 76 : poFeatureDefn->AddFieldDefn(&oField);
100 :
101 : /* -------------------------------------------------------------------- */
102 : /* Weight */
103 : /* -------------------------------------------------------------------- */
104 76 : oField.SetName("Weight");
105 76 : oField.SetType(OFTInteger);
106 76 : oField.SetWidth(2);
107 76 : oField.SetPrecision(0);
108 76 : poFeatureDefn->AddFieldDefn(&oField);
109 :
110 : /* -------------------------------------------------------------------- */
111 : /* Style */
112 : /* -------------------------------------------------------------------- */
113 76 : oField.SetName("Style");
114 76 : oField.SetType(OFTInteger);
115 76 : oField.SetWidth(1);
116 76 : oField.SetPrecision(0);
117 76 : poFeatureDefn->AddFieldDefn(&oField);
118 :
119 : /* -------------------------------------------------------------------- */
120 : /* EntityNum */
121 : /* -------------------------------------------------------------------- */
122 76 : oField.SetName("EntityNum");
123 76 : oField.SetType(eLinkFieldType);
124 76 : oField.SetWidth(0);
125 76 : oField.SetPrecision(0);
126 76 : poFeatureDefn->AddFieldDefn(&oField);
127 :
128 : /* -------------------------------------------------------------------- */
129 : /* MSLink */
130 : /* -------------------------------------------------------------------- */
131 76 : oField.SetName("MSLink");
132 76 : oField.SetType(eLinkFieldType);
133 76 : oField.SetWidth(0);
134 76 : oField.SetPrecision(0);
135 76 : poFeatureDefn->AddFieldDefn(&oField);
136 :
137 : /* -------------------------------------------------------------------- */
138 : /* Text */
139 : /* -------------------------------------------------------------------- */
140 76 : oField.SetName("Text");
141 76 : oField.SetType(OFTString);
142 76 : oField.SetWidth(0);
143 76 : oField.SetPrecision(0);
144 76 : poFeatureDefn->AddFieldDefn(&oField);
145 :
146 : /* -------------------------------------------------------------------- */
147 : /* ULink */
148 : /* -------------------------------------------------------------------- */
149 76 : oField.SetName("ULink");
150 76 : oField.SetType(OFTString);
151 76 : oField.SetSubType(OFSTJSON);
152 76 : oField.SetWidth(0);
153 76 : oField.SetPrecision(0);
154 76 : poFeatureDefn->AddFieldDefn(&oField);
155 :
156 : /* -------------------------------------------------------------------- */
157 : /* Create template feature for evaluating simple expressions. */
158 : /* -------------------------------------------------------------------- */
159 76 : poEvalFeature = new OGRFeature(poFeatureDefn);
160 :
161 : /* TODO: I am intending to keep track of simple attribute queries (ones
162 : using only FID, Type and Level and short circuiting their operation
163 : based on the index. However, there are some complexities with
164 : complex elements, and spatial queries that have caused me to put it
165 : off for now.
166 : */
167 76 : }
168 :
169 : /************************************************************************/
170 : /* ~OGRDGNLayer() */
171 : /************************************************************************/
172 :
173 152 : OGRDGNLayer::~OGRDGNLayer()
174 :
175 : {
176 76 : if (m_nFeaturesRead > 0)
177 : {
178 52 : CPLDebug("Mem", "%d features read on layer '%s'.",
179 26 : static_cast<int>(m_nFeaturesRead), poFeatureDefn->GetName());
180 : }
181 :
182 76 : delete poEvalFeature;
183 :
184 76 : poFeatureDefn->Release();
185 :
186 76 : CPLFree(pszLinkFormat);
187 152 : }
188 :
189 : /************************************************************************/
190 : /* ISetSpatialFilter() */
191 : /************************************************************************/
192 :
193 4 : OGRErr OGRDGNLayer::ISetSpatialFilter(int, const OGRGeometry *poGeomIn)
194 :
195 : {
196 4 : if (!InstallFilter(poGeomIn))
197 2 : return OGRERR_NONE;
198 :
199 2 : if (m_poFilterGeom != nullptr)
200 : {
201 1 : DGNSetSpatialFilter(hDGN, m_sFilterEnvelope.MinX,
202 : m_sFilterEnvelope.MinY, m_sFilterEnvelope.MaxX,
203 : m_sFilterEnvelope.MaxY);
204 : }
205 : else
206 : {
207 1 : DGNSetSpatialFilter(hDGN, 0.0, 0.0, 0.0, 0.0);
208 : }
209 :
210 2 : ResetReading();
211 2 : return OGRERR_NONE;
212 : }
213 :
214 : /************************************************************************/
215 : /* ResetReading() */
216 : /************************************************************************/
217 :
218 24 : void OGRDGNLayer::ResetReading()
219 :
220 : {
221 24 : iNextShapeId = 0;
222 24 : DGNRewind(hDGN);
223 24 : }
224 :
225 : /************************************************************************/
226 : /* GetFeature() */
227 : /************************************************************************/
228 :
229 0 : OGRFeature *OGRDGNLayer::GetFeature(GIntBig nFeatureId)
230 :
231 : {
232 0 : if (nFeatureId > INT_MAX ||
233 0 : !DGNGotoElement(hDGN, static_cast<int>(nFeatureId)))
234 0 : return nullptr;
235 :
236 : // We should likely clear the spatial search region as it affects
237 : // DGNReadElement(), but I will defer that for now.
238 :
239 0 : DGNElemCore *psElement = DGNReadElement(hDGN);
240 0 : OGRFeature *poFeature = ElementToFeature(psElement, 0);
241 0 : DGNFreeElement(hDGN, psElement);
242 :
243 0 : if (poFeature == nullptr)
244 0 : return nullptr;
245 :
246 0 : if (poFeature->GetFID() != nFeatureId)
247 : {
248 0 : delete poFeature;
249 0 : return nullptr;
250 : }
251 :
252 0 : return poFeature;
253 : }
254 :
255 : /************************************************************************/
256 : /* ConsiderBrush() */
257 : /* */
258 : /* Method to set the style for a polygon, including a brush if */
259 : /* appropriate. */
260 : /************************************************************************/
261 :
262 10 : void OGRDGNLayer::ConsiderBrush(DGNElemCore *psElement, const char *pszPen,
263 : OGRFeature *poFeature)
264 :
265 : {
266 10 : int nFillColor = 0;
267 10 : int gv_red = 0;
268 10 : int gv_green = 0;
269 10 : int gv_blue = 0;
270 :
271 14 : if (DGNGetShapeFillInfo(hDGN, psElement, &nFillColor) &&
272 4 : DGNLookupColor(hDGN, nFillColor, &gv_red, &gv_green, &gv_blue))
273 : {
274 8 : CPLString osFullStyle;
275 4 : osFullStyle.Printf("BRUSH(fc:#%02x%02x%02x,id:\"ogr-brush-0\")", gv_red,
276 4 : gv_green, gv_blue);
277 :
278 4 : if (nFillColor != psElement->color)
279 : {
280 0 : osFullStyle += ';';
281 0 : osFullStyle += pszPen;
282 : }
283 4 : poFeature->SetStyleString(osFullStyle.c_str());
284 : }
285 : else
286 6 : poFeature->SetStyleString(pszPen);
287 10 : }
288 :
289 : /************************************************************************/
290 : /* ElementToFeature() */
291 : /************************************************************************/
292 :
293 284 : OGRFeature *OGRDGNLayer::ElementToFeature(DGNElemCore *psElement, int nRecLevel)
294 :
295 : {
296 284 : OGRFeature *poFeature = new OGRFeature(poFeatureDefn);
297 :
298 284 : poFeature->SetFID(psElement->element_id);
299 284 : poFeature->SetField("Type", psElement->type);
300 284 : poFeature->SetField("Level", psElement->level);
301 284 : poFeature->SetField("GraphicGroup", psElement->graphic_group);
302 284 : poFeature->SetField("ColorIndex", psElement->color);
303 284 : poFeature->SetField("Weight", psElement->weight);
304 284 : poFeature->SetField("Style", psElement->style);
305 :
306 284 : m_nFeaturesRead++;
307 :
308 : /* -------------------------------------------------------------------- */
309 : /* Collect linkage information */
310 : /* -------------------------------------------------------------------- */
311 284 : const int MAX_LINK = 100;
312 :
313 : int anEntityNum[MAX_LINK];
314 284 : anEntityNum[0] = 0;
315 :
316 : int anMSLink[MAX_LINK];
317 284 : anMSLink[0] = 0;
318 :
319 284 : CPLJSONObject uLinkData;
320 :
321 284 : int iLink = 0;
322 284 : int nLinkCount = 0;
323 284 : int uLinkCount = 0;
324 :
325 284 : int nLinkType = 0;
326 284 : int nLinkSize = 0;
327 :
328 : // coverity[tained_data]
329 : unsigned char *pabyData =
330 284 : DGNGetLinkage(hDGN, psElement, iLink, &nLinkType, anEntityNum + iLink,
331 284 : anMSLink + iLink, &nLinkSize);
332 :
333 310 : while (pabyData)
334 : {
335 : CPLJSONArray previousValues =
336 26 : uLinkData.GetArray(std::to_string(nLinkType));
337 26 : if (!previousValues.IsValid())
338 : {
339 26 : uLinkData.Add(std::to_string(nLinkType), CPLJSONArray());
340 26 : previousValues = uLinkData.GetArray(std::to_string(nLinkType));
341 : }
342 26 : CPLJSONArray rawWords;
343 230 : for (int i = 0; i < nLinkSize - 1; i += 2)
344 : {
345 204 : rawWords.Add(
346 204 : CPLSPrintf("0x%02x%02x", pabyData[i + 1], pabyData[i]));
347 : }
348 26 : CPLJSONObject theNewObject = CPLJSONObject();
349 26 : theNewObject.Add("size", nLinkSize);
350 26 : previousValues.Add(theNewObject);
351 26 : switch (nLinkType)
352 : {
353 0 : case 24721: // OdDgDBLinkage::kOracle
354 : {
355 0 : theNewObject.Add("raw", rawWords);
356 0 : theNewObject.Add("type", "Oracle");
357 : }
358 0 : break;
359 0 : case 32047: // OdDgDBLinkage::kODBC
360 : {
361 0 : theNewObject.Add("raw", rawWords);
362 0 : theNewObject.Add("type", "ODBC");
363 : }
364 0 : break;
365 0 : case 6549: // 0x1995 Application ID by IPCC/Portugal
366 : {
367 0 : theNewObject.Add("domain", CPLSPrintf("0x%02x", pabyData[5]));
368 0 : theNewObject.Add("subdomain",
369 0 : CPLSPrintf("0x%02x", pabyData[4]));
370 0 : theNewObject.Add("family", CPLSPrintf("0x%02x", pabyData[7]));
371 0 : theNewObject.Add("object", CPLSPrintf("0x%02x", pabyData[6]));
372 0 : theNewObject.Add("key", CPLSPrintf("%02x%02x%02x%02x",
373 0 : pabyData[5], pabyData[4],
374 0 : pabyData[7], pabyData[6]));
375 0 : theNewObject.Add("type", "IPCC/Portugal");
376 : }
377 0 : break;
378 26 : default:
379 : {
380 26 : theNewObject.Add("raw", rawWords);
381 26 : theNewObject.Add("type", "unknown");
382 : }
383 26 : break;
384 : }
385 :
386 26 : uLinkCount++;
387 26 : iLink++;
388 :
389 26 : if (anEntityNum[nLinkCount] != 0 || anMSLink[nLinkCount] != 0)
390 : {
391 0 : nLinkCount++;
392 0 : if (nLinkCount == MAX_LINK)
393 : {
394 0 : break;
395 : }
396 : }
397 :
398 26 : anEntityNum[nLinkCount] = 0;
399 26 : anMSLink[nLinkCount] = 0;
400 :
401 : // coverity[tained_data]
402 26 : pabyData = DGNGetLinkage(hDGN, psElement, iLink, &nLinkType,
403 26 : anEntityNum + nLinkCount,
404 26 : anMSLink + nLinkCount, &nLinkSize);
405 : }
406 :
407 : /* -------------------------------------------------------------------- */
408 : /* Apply attribute linkage to feature. */
409 : /* -------------------------------------------------------------------- */
410 284 : if (uLinkCount > 0)
411 : {
412 25 : poFeature->SetField("ULink", uLinkData.ToString().c_str());
413 : }
414 284 : if (nLinkCount > 0)
415 : {
416 0 : if (EQUAL(pszLinkFormat, "FIRST"))
417 : {
418 0 : poFeature->SetField("EntityNum", anEntityNum[0]);
419 0 : poFeature->SetField("MSLink", anMSLink[0]);
420 : }
421 0 : else if (EQUAL(pszLinkFormat, "LIST"))
422 : {
423 0 : poFeature->SetField("EntityNum", nLinkCount, anEntityNum);
424 0 : poFeature->SetField("MSLink", nLinkCount, anMSLink);
425 : }
426 0 : else if (EQUAL(pszLinkFormat, "STRING"))
427 : {
428 : char szEntityList[MAX_LINK * 9];
429 : char szMSLinkList[MAX_LINK * 9];
430 0 : int nEntityLen = 0;
431 0 : int nMSLinkLen = 0;
432 :
433 0 : for (iLink = 0; iLink < nLinkCount; iLink++)
434 : {
435 0 : if (iLink != 0)
436 : {
437 0 : szEntityList[nEntityLen++] = ',';
438 0 : szMSLinkList[nMSLinkLen++] = ',';
439 : }
440 :
441 0 : snprintf(szEntityList + nEntityLen,
442 0 : sizeof(szEntityList) - nEntityLen, "%d",
443 : anEntityNum[iLink]);
444 0 : snprintf(szMSLinkList + nMSLinkLen,
445 0 : sizeof(szMSLinkList) - nMSLinkLen, "%d",
446 : anMSLink[iLink]);
447 :
448 0 : nEntityLen +=
449 0 : static_cast<int>(strlen(szEntityList + nEntityLen));
450 0 : nMSLinkLen +=
451 0 : static_cast<int>(strlen(szMSLinkList + nMSLinkLen));
452 : }
453 :
454 0 : poFeature->SetField("EntityNum", szEntityList);
455 0 : poFeature->SetField("MSLink", szMSLinkList);
456 : }
457 : }
458 :
459 : /* -------------------------------------------------------------------- */
460 : /* Lookup color. */
461 : /* -------------------------------------------------------------------- */
462 284 : int gv_red = 0;
463 284 : int gv_green = 0;
464 284 : int gv_blue = 0;
465 :
466 284 : char szFSColor[128] = {};
467 284 : szFSColor[0] = '\0';
468 284 : if (DGNLookupColor(hDGN, psElement->color, &gv_red, &gv_green, &gv_blue))
469 : {
470 : char gv_color[128];
471 284 : CPLsnprintf(gv_color, sizeof(gv_color), "%f %f %f 1.0", gv_red / 255.0,
472 : gv_green / 255.0, gv_blue / 255.0);
473 :
474 284 : snprintf(szFSColor, sizeof(szFSColor), "c:#%02x%02x%02x", gv_red,
475 : gv_green, gv_blue);
476 : }
477 :
478 : /* -------------------------------------------------------------------- */
479 : /* Generate corresponding PEN style. */
480 : /* -------------------------------------------------------------------- */
481 : char szPen[256];
482 284 : szPen[0] = '\0';
483 :
484 284 : if (psElement->style == DGNS_SOLID)
485 258 : snprintf(szPen, sizeof(szPen), "PEN(id:\"ogr-pen-0\"");
486 26 : else if (psElement->style == DGNS_DOTTED)
487 26 : snprintf(szPen, sizeof(szPen), "PEN(id:\"ogr-pen-5\"");
488 0 : else if (psElement->style == DGNS_MEDIUM_DASH)
489 0 : snprintf(szPen, sizeof(szPen), "PEN(id:\"ogr-pen-2\"");
490 0 : else if (psElement->style == DGNS_LONG_DASH)
491 0 : snprintf(szPen, sizeof(szPen), "PEN(id:\"ogr-pen-4\"");
492 0 : else if (psElement->style == DGNS_DOT_DASH)
493 0 : snprintf(szPen, sizeof(szPen), "PEN(id:\"ogr-pen-6\"");
494 0 : else if (psElement->style == DGNS_SHORT_DASH)
495 0 : snprintf(szPen, sizeof(szPen), "PEN(id:\"ogr-pen-3\"");
496 0 : else if (psElement->style == DGNS_DASH_DOUBLE_DOT)
497 0 : snprintf(szPen, sizeof(szPen), "PEN(id:\"ogr-pen-7\"");
498 0 : else if (psElement->style == DGNS_LONG_DASH_SHORT_DASH)
499 0 : snprintf(szPen, sizeof(szPen), "PEN(p:\"10px 5px 4px 5px\"");
500 : else
501 0 : snprintf(szPen, sizeof(szPen), "PEN(id:\"ogr-pen-0\"");
502 :
503 284 : if (strlen(szFSColor) > 0)
504 284 : snprintf(szPen + strlen(szPen), sizeof(szPen) - strlen(szPen), ",%s",
505 : szFSColor);
506 :
507 284 : if (psElement->weight > 1)
508 0 : snprintf(szPen + strlen(szPen), sizeof(szPen) - strlen(szPen),
509 : ",w:%dpx", psElement->weight);
510 :
511 284 : strcat(szPen, ")");
512 :
513 284 : switch (psElement->stype)
514 : {
515 26 : case DGNST_MULTIPOINT:
516 26 : if (psElement->type == DGNT_SHAPE)
517 : {
518 9 : OGRLinearRing *poLine = new OGRLinearRing();
519 9 : DGNElemMultiPoint *psEMP =
520 : reinterpret_cast<DGNElemMultiPoint *>(psElement);
521 :
522 9 : poLine->setNumPoints(psEMP->num_vertices);
523 54 : for (int i = 0; i < psEMP->num_vertices; i++)
524 : {
525 45 : poLine->setPoint(i, psEMP->vertices[i].x,
526 : psEMP->vertices[i].y,
527 : psEMP->vertices[i].z);
528 : }
529 :
530 9 : OGRPolygon *poPolygon = new OGRPolygon();
531 9 : poPolygon->addRingDirectly(poLine);
532 :
533 9 : poFeature->SetGeometryDirectly(poPolygon);
534 :
535 9 : ConsiderBrush(psElement, szPen, poFeature);
536 : }
537 17 : else if (psElement->type == DGNT_CURVE)
538 : {
539 0 : OGRLineString *poLine = new OGRLineString();
540 0 : DGNElemMultiPoint *psEMP =
541 : reinterpret_cast<DGNElemMultiPoint *>(psElement);
542 0 : const int nPoints = 5 * psEMP->num_vertices;
543 : DGNPoint *pasPoints = static_cast<DGNPoint *>(
544 0 : CPLMalloc(sizeof(DGNPoint) * nPoints));
545 :
546 0 : DGNStrokeCurve(hDGN, psEMP, nPoints, pasPoints);
547 :
548 0 : poLine->setNumPoints(nPoints);
549 0 : for (int i = 0; i < nPoints; i++)
550 : {
551 0 : poLine->setPoint(i, pasPoints[i].x, pasPoints[i].y,
552 0 : pasPoints[i].z);
553 : }
554 :
555 0 : poFeature->SetGeometryDirectly(poLine);
556 0 : CPLFree(pasPoints);
557 :
558 0 : poFeature->SetStyleString(szPen);
559 : }
560 : else
561 : {
562 17 : DGNElemMultiPoint *psEMP =
563 : reinterpret_cast<DGNElemMultiPoint *>(psElement);
564 :
565 17 : if (psEMP->num_vertices > 0)
566 : {
567 17 : OGRLineString *poLine = new OGRLineString();
568 17 : poLine->setNumPoints(psEMP->num_vertices);
569 121 : for (int i = 0; i < psEMP->num_vertices; i++)
570 : {
571 104 : poLine->setPoint(i, psEMP->vertices[i].x,
572 : psEMP->vertices[i].y,
573 : psEMP->vertices[i].z);
574 : }
575 :
576 17 : poFeature->SetGeometryDirectly(poLine);
577 : }
578 :
579 17 : poFeature->SetStyleString(szPen);
580 : }
581 26 : break;
582 :
583 6 : case DGNST_ARC:
584 : {
585 6 : DGNElemArc *psArc = reinterpret_cast<DGNElemArc *>(psElement);
586 : int nPoints = static_cast<int>(
587 6 : std::max(1.0, std::abs(psArc->sweepang) / 5.0) + 1.0);
588 6 : if (nPoints > 90)
589 0 : nPoints = 90;
590 :
591 6 : DGNPoint asPoints[90] = {};
592 6 : DGNStrokeArc(hDGN, psArc, nPoints, asPoints);
593 :
594 6 : OGRLineString *poLine = new OGRLineString();
595 6 : poLine->setNumPoints(nPoints);
596 444 : for (int i = 0; i < nPoints; i++)
597 : {
598 438 : poLine->setPoint(i, asPoints[i].x, asPoints[i].y,
599 : asPoints[i].z);
600 : }
601 :
602 6 : poFeature->SetGeometryDirectly(poLine);
603 6 : poFeature->SetStyleString(szPen);
604 : }
605 6 : break;
606 :
607 9 : case DGNST_TEXT:
608 : {
609 9 : OGRPoint *poPoint = new OGRPoint();
610 9 : DGNElemText *psText = reinterpret_cast<DGNElemText *>(psElement);
611 :
612 9 : poPoint->setX(psText->origin.x);
613 9 : poPoint->setY(psText->origin.y);
614 9 : poPoint->setZ(psText->origin.z);
615 :
616 9 : poFeature->SetGeometryDirectly(poPoint);
617 :
618 9 : const auto &osEncoding = m_poDS->GetEncoding();
619 18 : std::string osText;
620 9 : if (!osEncoding.empty() && osEncoding != CPL_ENC_UTF8)
621 : {
622 2 : osText = CPLString(psText->string)
623 1 : .Recode(osEncoding.c_str(), CPL_ENC_UTF8);
624 : }
625 : else
626 : {
627 8 : osText = psText->string;
628 : }
629 :
630 9 : const size_t nOgrFSLen = osText.size() + 150;
631 9 : char *pszOgrFS = static_cast<char *>(CPLMalloc(nOgrFSLen));
632 :
633 : // setup the basic label.
634 9 : snprintf(pszOgrFS, nOgrFSLen, "LABEL(t:\"%s\"", osText.c_str());
635 :
636 : // set the color if we have it.
637 9 : if (strlen(szFSColor) > 0)
638 9 : snprintf(pszOgrFS + strlen(pszOgrFS),
639 9 : nOgrFSLen - strlen(pszOgrFS), ",%s", szFSColor);
640 :
641 : // Add the size info in ground units.
642 : // TODO: std::abs
643 9 : if (std::abs(psText->height_mult) >= 6.0)
644 2 : CPLsnprintf(pszOgrFS + strlen(pszOgrFS),
645 2 : nOgrFSLen - strlen(pszOgrFS), ",s:%dg",
646 2 : static_cast<int>(psText->height_mult));
647 7 : else if (std::abs(psText->height_mult) > 0.1)
648 7 : CPLsnprintf(pszOgrFS + strlen(pszOgrFS),
649 7 : nOgrFSLen - strlen(pszOgrFS), ",s:%.3fg",
650 : psText->height_mult);
651 : else
652 0 : CPLsnprintf(pszOgrFS + strlen(pszOgrFS),
653 0 : nOgrFSLen - strlen(pszOgrFS), ",s:%.12fg",
654 : psText->height_mult);
655 :
656 : // Add the font name. Name it MstnFont<FONTNUMBER> if not available
657 : // in the font list. #3392
658 : static const char *const papszFontList[] = {
659 : "STANDARD",
660 : "WORKING",
661 : "FANCY",
662 : "ENGINEERING",
663 : "NEWZERO",
664 : "STENCEL", // 0-5
665 : "USTN_FANCY",
666 : "COMPRESSED",
667 : "STENCEQ",
668 : nullptr,
669 : "hand",
670 : "ARCH", // 6-11
671 : "ARCHB",
672 : nullptr,
673 : nullptr,
674 : "IGES1001",
675 : "IGES1002",
676 : "IGES1003", // 12-17
677 : "CENTB",
678 : "MICROS",
679 : nullptr,
680 : nullptr,
681 : "ISOFRACTIONS",
682 : "ITALICS", // 18-23
683 : "ISO30",
684 : nullptr,
685 : "GREEK",
686 : "ISOREC",
687 : "Isoeq",
688 : nullptr, // 24-29
689 : "ISO_FONTLEFT",
690 : "ISO_FONTRIGHT",
691 : "INTL_ENGINEERING",
692 : "INTL_WORKING",
693 : "ISOITEQ",
694 : nullptr, // 30-35
695 : "USTN FONT 26",
696 : nullptr,
697 : nullptr,
698 : nullptr,
699 : nullptr,
700 : "ARCHITECTURAL", // 36-41
701 : "BLOCK_OUTLINE",
702 : "LOW_RES_FILLED",
703 : nullptr,
704 : nullptr,
705 : nullptr,
706 : nullptr, // 42-47
707 : nullptr,
708 : nullptr,
709 : "UPPERCASE",
710 : nullptr,
711 : nullptr,
712 : nullptr, // 48-53
713 : nullptr,
714 : nullptr,
715 : nullptr,
716 : nullptr,
717 : nullptr,
718 : nullptr, // 54-49
719 : "FONT060",
720 : "din",
721 : "dinit",
722 : "helvl",
723 : "HELVLIT",
724 : "helv", // 60-65
725 : "HELVIT",
726 : "cent",
727 : "CENTIT",
728 : "SCRIPT",
729 : nullptr,
730 : nullptr, // 66-71
731 : nullptr,
732 : nullptr,
733 : nullptr,
734 : nullptr,
735 : "MICROQ",
736 : "dotfont", // 72-77
737 : "DOTIT",
738 : nullptr,
739 : nullptr,
740 : nullptr,
741 : nullptr,
742 : nullptr, // 78-83
743 : nullptr,
744 : nullptr,
745 : nullptr,
746 : nullptr,
747 : nullptr,
748 : nullptr, // 84-89
749 : nullptr,
750 : nullptr,
751 : "FONT092",
752 : nullptr,
753 : "FONT094",
754 : nullptr, // 90-95
755 : nullptr,
756 : nullptr,
757 : nullptr,
758 : nullptr,
759 : "ANSI_SYMBOLS",
760 : "FEATURE_CONTROL_SYSMBOLS", // 96-101
761 : "SYMB_FAST",
762 : nullptr,
763 : nullptr,
764 : "INTL_ISO",
765 : "INTL_ISO_EQUAL",
766 : "INTL_ISO_ITALIC", // 102-107
767 : "INTL_ISO_ITALIC_EQUAL"}; // 108
768 :
769 9 : if (psText->font_id <= 108 &&
770 9 : papszFontList[psText->font_id] != nullptr)
771 : {
772 9 : snprintf(pszOgrFS + strlen(pszOgrFS),
773 9 : nOgrFSLen - strlen(pszOgrFS), ",f:%s",
774 9 : papszFontList[psText->font_id]);
775 : }
776 : else
777 : {
778 0 : snprintf(pszOgrFS + strlen(pszOgrFS),
779 0 : nOgrFSLen - strlen(pszOgrFS), ",f:MstnFont%d",
780 : psText->font_id);
781 : }
782 :
783 : // Add the angle, if not horizontal
784 9 : if (psText->rotation != 0.0)
785 0 : snprintf(pszOgrFS + strlen(pszOgrFS),
786 0 : nOgrFSLen - strlen(pszOgrFS), ",a:%d",
787 0 : static_cast<int>(psText->rotation + 0.5));
788 :
789 9 : snprintf(pszOgrFS + strlen(pszOgrFS), nOgrFSLen - strlen(pszOgrFS),
790 : ")");
791 :
792 9 : poFeature->SetStyleString(pszOgrFS);
793 9 : CPLFree(pszOgrFS);
794 :
795 9 : poFeature->SetField("Text", osText.c_str());
796 : }
797 9 : break;
798 :
799 1 : case DGNST_COMPLEX_HEADER:
800 : {
801 1 : DGNElemComplexHeader *psHdr =
802 : reinterpret_cast<DGNElemComplexHeader *>(psElement);
803 2 : OGRMultiLineString oChildren;
804 :
805 : /* collect subsequent child geometries. */
806 : // we should disable the spatial filter ... add later.
807 3 : for (int iChild = 0; iChild < psHdr->numelems && nRecLevel < 20;
808 : iChild++)
809 : {
810 2 : OGRFeature *poChildFeature = nullptr;
811 2 : DGNElemCore *psChildElement = DGNReadElement(hDGN);
812 : // should verify complex bit set, not another header.
813 :
814 2 : if (psChildElement != nullptr)
815 : {
816 : poChildFeature =
817 2 : ElementToFeature(psChildElement, nRecLevel + 1);
818 2 : DGNFreeElement(hDGN, psChildElement);
819 : }
820 :
821 4 : if (poChildFeature != nullptr &&
822 2 : poChildFeature->GetGeometryRef() != nullptr)
823 : {
824 2 : OGRGeometry *poGeom = poChildFeature->GetGeometryRef();
825 2 : if (wkbFlatten(poGeom->getGeometryType()) == wkbLineString)
826 2 : oChildren.addGeometry(poGeom);
827 : }
828 :
829 2 : if (poChildFeature != nullptr)
830 2 : delete poChildFeature;
831 : }
832 :
833 : // Try to assemble into polygon geometry.
834 1 : OGRGeometry *poGeom = nullptr;
835 :
836 1 : if (psElement->type == DGNT_COMPLEX_SHAPE_HEADER)
837 0 : poGeom = OGRGeometry::FromHandle(
838 : OGRBuildPolygonFromEdges(OGRGeometry::ToHandle(&oChildren),
839 : TRUE, TRUE, 100000, nullptr));
840 : else
841 1 : poGeom = oChildren.clone();
842 :
843 1 : if (poGeom != nullptr)
844 1 : poFeature->SetGeometryDirectly(poGeom);
845 :
846 1 : ConsiderBrush(psElement, szPen, poFeature);
847 : }
848 1 : break;
849 :
850 242 : default:
851 242 : break;
852 : }
853 :
854 : /* -------------------------------------------------------------------- */
855 : /* Fixup geometry dimension. */
856 : /* -------------------------------------------------------------------- */
857 284 : if (poFeature->GetGeometryRef() != nullptr)
858 84 : poFeature->GetGeometryRef()->setCoordinateDimension(
859 42 : DGNGetDimension(hDGN));
860 :
861 568 : return poFeature;
862 : }
863 :
864 : /************************************************************************/
865 : /* GetNextFeature() */
866 : /************************************************************************/
867 :
868 37 : OGRFeature *OGRDGNLayer::GetNextFeature()
869 :
870 : {
871 37 : DGNGetElementIndex(hDGN, nullptr);
872 :
873 37 : DGNElemCore *psElement = nullptr;
874 285 : while ((psElement = DGNReadElement(hDGN)) != nullptr)
875 : {
876 282 : if (psElement->deleted)
877 : {
878 0 : DGNFreeElement(hDGN, psElement);
879 0 : continue;
880 : }
881 :
882 282 : OGRFeature *poFeature = ElementToFeature(psElement, 0);
883 282 : DGNFreeElement(hDGN, psElement);
884 :
885 282 : if (poFeature == nullptr)
886 0 : continue;
887 :
888 282 : if (poFeature->GetGeometryRef() == nullptr)
889 : {
890 242 : delete poFeature;
891 242 : continue;
892 : }
893 :
894 74 : if ((m_poAttrQuery == nullptr || m_poAttrQuery->Evaluate(poFeature)) &&
895 34 : FilterGeometry(poFeature->GetGeometryRef()))
896 34 : return poFeature;
897 :
898 6 : delete poFeature;
899 : }
900 :
901 3 : return nullptr;
902 : }
903 :
904 : /************************************************************************/
905 : /* TestCapability() */
906 : /************************************************************************/
907 :
908 72 : int OGRDGNLayer::TestCapability(const char *pszCap)
909 :
910 : {
911 72 : if (EQUAL(pszCap, OLCRandomRead))
912 0 : return TRUE;
913 :
914 72 : else if (EQUAL(pszCap, OLCSequentialWrite))
915 16 : return bUpdate;
916 56 : else if (EQUAL(pszCap, OLCRandomWrite))
917 0 : return FALSE; /* maybe later? */
918 :
919 56 : else if (EQUAL(pszCap, OLCFastFeatureCount))
920 0 : return m_poFilterGeom == nullptr || m_poAttrQuery == nullptr;
921 :
922 56 : else if (EQUAL(pszCap, OLCFastSpatialFilter))
923 0 : return FALSE;
924 :
925 56 : else if (EQUAL(pszCap, OLCFastGetExtent))
926 0 : return TRUE;
927 :
928 56 : else if (EQUAL(pszCap, OLCZGeometries))
929 0 : return TRUE;
930 :
931 56 : else if (EQUAL(pszCap, OLCStringsAsUTF8))
932 4 : return !m_poDS->GetEncoding().empty();
933 :
934 52 : return FALSE;
935 : }
936 :
937 : /************************************************************************/
938 : /* GetFeatureCount() */
939 : /************************************************************************/
940 :
941 0 : GIntBig OGRDGNLayer::GetFeatureCount(int bForce)
942 :
943 : {
944 : /* -------------------------------------------------------------------- */
945 : /* If any odd conditions are in effect collect the information */
946 : /* normally. */
947 : /* -------------------------------------------------------------------- */
948 0 : if (m_poFilterGeom != nullptr || m_poAttrQuery != nullptr)
949 0 : return OGRLayer::GetFeatureCount(bForce);
950 :
951 : /* -------------------------------------------------------------------- */
952 : /* Otherwise scan the index. */
953 : /* -------------------------------------------------------------------- */
954 0 : int nElementCount = 0;
955 0 : const DGNElementInfo *pasIndex = DGNGetElementIndex(hDGN, &nElementCount);
956 :
957 0 : int nFeatureCount = 0;
958 0 : bool bInComplexShape = false;
959 :
960 0 : for (int i = 0; i < nElementCount; i++)
961 : {
962 0 : if (pasIndex[i].flags & DGNEIF_DELETED)
963 0 : continue;
964 :
965 0 : switch (pasIndex[i].stype)
966 : {
967 0 : case DGNST_MULTIPOINT:
968 : case DGNST_ARC:
969 : case DGNST_TEXT:
970 0 : if (!(pasIndex[i].flags & DGNEIF_COMPLEX) || !bInComplexShape)
971 : {
972 0 : nFeatureCount++;
973 0 : bInComplexShape = false;
974 : }
975 0 : break;
976 :
977 0 : case DGNST_COMPLEX_HEADER:
978 0 : nFeatureCount++;
979 0 : bInComplexShape = true;
980 0 : break;
981 :
982 0 : default:
983 0 : break;
984 : }
985 : }
986 :
987 0 : return nFeatureCount;
988 : }
989 :
990 : /************************************************************************/
991 : /* IGetExtent() */
992 : /************************************************************************/
993 :
994 0 : OGRErr OGRDGNLayer::IGetExtent(int /* iGeomField */, OGREnvelope *psExtent,
995 : bool /* bForce */)
996 : {
997 : double adfExtents[6];
998 :
999 0 : if (!DGNGetExtents(hDGN, adfExtents))
1000 0 : return OGRERR_FAILURE;
1001 :
1002 0 : psExtent->MinX = adfExtents[0];
1003 0 : psExtent->MinY = adfExtents[1];
1004 0 : psExtent->MaxX = adfExtents[3];
1005 0 : psExtent->MaxY = adfExtents[4];
1006 :
1007 0 : return OGRERR_NONE;
1008 : }
1009 :
1010 : /************************************************************************/
1011 : /* LineStringToElementGroup() */
1012 : /* */
1013 : /* Convert an OGR line string to one or more DGN elements. If */
1014 : /* the input is too long for a single element (more than 38 */
1015 : /* points) we split it into multiple LINE_STRING elements, and */
1016 : /* prefix with a complex group header element. */
1017 : /* */
1018 : /* This method can create handle creating shapes, or line */
1019 : /* strings for the aggregate object, but the components of a */
1020 : /* complex shape group are always line strings. */
1021 : /************************************************************************/
1022 :
1023 : constexpr int MAX_ELEM_POINTS = 38;
1024 :
1025 29 : DGNElemCore **OGRDGNLayer::LineStringToElementGroup(const OGRLineString *poLS,
1026 : int nGroupType)
1027 :
1028 : {
1029 29 : const int nTotalPoints = poLS->getNumPoints();
1030 29 : int iGeom = 0;
1031 : DGNElemCore **papsGroup = static_cast<DGNElemCore **>(
1032 29 : CPLCalloc(sizeof(void *), (nTotalPoints / (MAX_ELEM_POINTS - 1)) + 3));
1033 :
1034 59 : for (int iNextPoint = 0; iNextPoint < nTotalPoints;)
1035 : {
1036 30 : DGNPoint asPoints[MAX_ELEM_POINTS] = {};
1037 30 : int nThisCount = 0;
1038 :
1039 : // we need to repeat end points of elements.
1040 : // cppcheck-suppress duplicateExpression
1041 30 : if (iNextPoint != 0)
1042 1 : iNextPoint--;
1043 :
1044 199 : for (; iNextPoint < nTotalPoints && nThisCount < MAX_ELEM_POINTS;
1045 : iNextPoint++, nThisCount++)
1046 : {
1047 169 : asPoints[nThisCount].x = poLS->getX(iNextPoint);
1048 169 : asPoints[nThisCount].y = poLS->getY(iNextPoint);
1049 169 : asPoints[nThisCount].z = poLS->getZ(iNextPoint);
1050 : }
1051 :
1052 30 : if (nTotalPoints <= MAX_ELEM_POINTS)
1053 28 : papsGroup[0] =
1054 28 : DGNCreateMultiPointElem(hDGN, nGroupType, nThisCount, asPoints);
1055 : else
1056 2 : papsGroup[++iGeom] = DGNCreateMultiPointElem(hDGN, DGNT_LINE_STRING,
1057 : nThisCount, asPoints);
1058 : }
1059 :
1060 : /* -------------------------------------------------------------------- */
1061 : /* We needed to make into a group. Create the complex header */
1062 : /* from the rest of the group. */
1063 : /* -------------------------------------------------------------------- */
1064 29 : if (papsGroup[0] == nullptr)
1065 : {
1066 1 : if (nGroupType == DGNT_SHAPE)
1067 0 : nGroupType = DGNT_COMPLEX_SHAPE_HEADER;
1068 : else
1069 1 : nGroupType = DGNT_COMPLEX_CHAIN_HEADER;
1070 :
1071 1 : papsGroup[0] = DGNCreateComplexHeaderFromGroup(hDGN, nGroupType, iGeom,
1072 : papsGroup + 1);
1073 : }
1074 :
1075 29 : return papsGroup;
1076 : }
1077 :
1078 : /************************************************************************/
1079 : /* TranslateLabel() */
1080 : /* */
1081 : /* Translate LABEL feature. */
1082 : /************************************************************************/
1083 :
1084 2 : DGNElemCore **OGRDGNLayer::TranslateLabel(OGRFeature *poFeature)
1085 :
1086 : {
1087 2 : OGRPoint *poPoint = poFeature->GetGeometryRef()->toPoint();
1088 2 : const char *pszText = poFeature->GetFieldAsString("Text");
1089 :
1090 4 : OGRStyleMgr oMgr;
1091 2 : oMgr.InitFromFeature(poFeature);
1092 2 : OGRStyleLabel *poLabel = reinterpret_cast<OGRStyleLabel *>(oMgr.GetPart(0));
1093 2 : if (poLabel != nullptr && poLabel->GetType() != OGRSTCLabel)
1094 : {
1095 0 : delete poLabel;
1096 0 : poLabel = nullptr;
1097 : }
1098 :
1099 2 : double dfRotation = 0.0;
1100 2 : double dfCharHeight = 100.0;
1101 2 : int nFontID = 1; // 1 is the default font for DGN. Not 0.
1102 :
1103 2 : if (poLabel != nullptr)
1104 : {
1105 : GBool bDefault;
1106 :
1107 1 : if (poLabel->TextString(bDefault) != nullptr && !bDefault)
1108 1 : pszText = poLabel->TextString(bDefault);
1109 1 : dfRotation = poLabel->Angle(bDefault);
1110 :
1111 1 : poLabel->Size(bDefault);
1112 1 : if (!bDefault && poLabel->GetUnit() == OGRSTUGround)
1113 0 : dfCharHeight = poLabel->Size(bDefault);
1114 : // this part is really kind of bogus.
1115 1 : if (!bDefault && poLabel->GetUnit() == OGRSTUMM)
1116 1 : dfCharHeight = poLabel->Size(bDefault) / 1000.0;
1117 :
1118 : /* get font id */
1119 : static const char *const papszFontNumbers[] = {
1120 : "STANDARD=0",
1121 : "WORKING=1",
1122 : "FANCY=2",
1123 : "ENGINEERING=3",
1124 : "NEWZERO=4",
1125 : "STENCEL=5",
1126 : "USTN_FANCY=7",
1127 : "COMPRESSED=8",
1128 : "STENCEQ=9",
1129 : "hand=10",
1130 : "ARCH=11",
1131 : "ARCHB=12",
1132 : "IGES1001=15",
1133 : "IGES1002=16",
1134 : "IGES1003=17",
1135 : "CENTB=18",
1136 : "MICROS=19",
1137 : "ISOFRACTIONS=22",
1138 : "ITALICS=23",
1139 : "ISO30=24",
1140 : "GREEK=25",
1141 : "ISOREC=26",
1142 : "Isoeq=27",
1143 : "ISO_FONTLEFT=30",
1144 : "ISO_FONTRIGHT=31",
1145 : "INTL_ENGINEERING=32",
1146 : "INTL_WORKING=33",
1147 : "ISOITEQ=34",
1148 : "USTN FONT 26=36",
1149 : "ARCHITECTURAL=41",
1150 : "BLOCK_OUTLINE=42",
1151 : "LOW_RES_FILLED=43",
1152 : "UPPERCASE50",
1153 : "FONT060=60",
1154 : "din=61",
1155 : "dinit=62",
1156 : "helvl=63",
1157 : "HELVLIT=64",
1158 : "helv=65",
1159 : "HELVIT=66",
1160 : "cent=67",
1161 : "CENTIT=68",
1162 : "SCRIPT=69",
1163 : "MICROQ=76",
1164 : "dotfont=77",
1165 : "DOTIT=78",
1166 : "FONT092=92",
1167 : "FONT094=94",
1168 : "ANSI_SYMBOLS=100",
1169 : "FEATURE_CONTROL_SYSMBOLS=101",
1170 : "SYMB_FAST=102",
1171 : "INTL_ISO=105",
1172 : "INTL_ISO_EQUAL=106",
1173 : "INTL_ISO_ITALIC=107",
1174 : "INTL_ISO_ITALIC_EQUAL=108",
1175 : nullptr};
1176 :
1177 1 : const char *pszFontName = poLabel->FontName(bDefault);
1178 1 : if (!bDefault && pszFontName != nullptr)
1179 : {
1180 1 : const char *pszFontNumber = CSLFetchNameValue(
1181 : const_cast<char **>(papszFontNumbers), pszFontName);
1182 :
1183 1 : if (pszFontNumber != nullptr)
1184 : {
1185 1 : nFontID = atoi(pszFontNumber);
1186 : }
1187 : }
1188 : }
1189 :
1190 2 : std::string osText;
1191 2 : const auto &osEncoding = m_poDS->GetEncoding();
1192 2 : if (!osEncoding.empty() && osEncoding != CPL_ENC_UTF8)
1193 : {
1194 1 : osText = CPLString(pszText).Recode(CPL_ENC_UTF8, osEncoding.c_str());
1195 : }
1196 : else
1197 : {
1198 1 : osText = pszText;
1199 : }
1200 :
1201 : DGNElemCore **papsGroup =
1202 2 : static_cast<DGNElemCore **>(CPLCalloc(sizeof(void *), 2));
1203 2 : papsGroup[0] =
1204 2 : DGNCreateTextElem(hDGN, osText.c_str(), nFontID, DGNJ_LEFT_BOTTOM,
1205 : dfCharHeight, dfCharHeight, dfRotation, nullptr,
1206 : poPoint->getX(), poPoint->getY(), poPoint->getZ());
1207 :
1208 2 : if (poLabel)
1209 1 : delete poLabel;
1210 :
1211 4 : return papsGroup;
1212 : }
1213 :
1214 : /************************************************************************/
1215 : /* ICreateFeature() */
1216 : /* */
1217 : /* Create a new feature and write to file. */
1218 : /************************************************************************/
1219 :
1220 72 : OGRErr OGRDGNLayer::ICreateFeature(OGRFeature *poFeature)
1221 :
1222 : {
1223 72 : if (!bUpdate)
1224 : {
1225 0 : CPLError(CE_Failure, CPLE_AppDefined,
1226 : "Attempt to create feature on read-only DGN file.");
1227 0 : return OGRERR_FAILURE;
1228 : }
1229 :
1230 72 : return CreateFeatureWithGeom(poFeature, poFeature->GetGeometryRef());
1231 : }
1232 :
1233 : /************************************************************************/
1234 : /* CreateFeatureWithGeom() */
1235 : /* */
1236 : /* Create an element or element group from a given geometry and */
1237 : /* the given feature. This method recurses to handle */
1238 : /* collections as essentially independent features. */
1239 : /************************************************************************/
1240 :
1241 98 : OGRErr OGRDGNLayer::CreateFeatureWithGeom(OGRFeature *poFeature,
1242 : const OGRGeometry *poGeom)
1243 :
1244 : {
1245 :
1246 98 : if (poGeom == nullptr || poGeom->IsEmpty())
1247 : {
1248 36 : CPLError(CE_Failure, CPLE_AppDefined,
1249 : "Features with empty, geometry collection geometries not\n"
1250 : "supported in DGN format.");
1251 36 : return OGRERR_FAILURE;
1252 : }
1253 :
1254 : /* -------------------------------------------------------------------- */
1255 : /* Translate the geometry. */
1256 : /* -------------------------------------------------------------------- */
1257 62 : DGNElemCore **papsGroup = nullptr;
1258 62 : const char *pszStyle = poFeature->GetStyleString();
1259 :
1260 62 : if (wkbFlatten(poGeom->getGeometryType()) == wkbPoint)
1261 : {
1262 16 : const OGRPoint *poPoint = poGeom->toPoint();
1263 16 : const char *pszText = poFeature->GetFieldAsString("Text");
1264 :
1265 16 : if ((pszText == nullptr || strlen(pszText) == 0) &&
1266 0 : (pszStyle == nullptr || strstr(pszStyle, "LABEL") == nullptr))
1267 : {
1268 : // Treat a non text point as a degenerate line.
1269 14 : DGNPoint asPoints[2] = {};
1270 14 : asPoints[0].x = poPoint->getX();
1271 14 : asPoints[0].y = poPoint->getY();
1272 14 : asPoints[0].z = poPoint->getZ();
1273 14 : asPoints[1] = asPoints[0];
1274 :
1275 : papsGroup =
1276 14 : static_cast<DGNElemCore **>(CPLCalloc(sizeof(void *), 2));
1277 14 : papsGroup[0] =
1278 14 : DGNCreateMultiPointElem(hDGN, DGNT_LINE, 2, asPoints);
1279 : }
1280 : else
1281 : {
1282 2 : papsGroup = TranslateLabel(poFeature);
1283 : }
1284 : }
1285 46 : else if (wkbFlatten(poGeom->getGeometryType()) == wkbLineString)
1286 : {
1287 : papsGroup =
1288 16 : LineStringToElementGroup(poGeom->toLineString(), DGNT_LINE_STRING);
1289 : }
1290 30 : else if (wkbFlatten(poGeom->getGeometryType()) == wkbPolygon)
1291 : {
1292 13 : const OGRPolygon *poPoly = poGeom->toPolygon();
1293 :
1294 : DGNElemCore **papsGroupExt =
1295 13 : LineStringToElementGroup(poPoly->getExteriorRing(), DGNT_SHAPE);
1296 :
1297 13 : const int innerRingsCnt = poPoly->getNumInteriorRings();
1298 :
1299 13 : if (innerRingsCnt > 0)
1300 : {
1301 0 : CPLDebug("InnerRings", "there are %d inner rings", innerRingsCnt);
1302 0 : std::list<DGNElemCore *> dgnElements;
1303 :
1304 0 : for (int i = 0; papsGroupExt[i] != nullptr; i++)
1305 : {
1306 0 : dgnElements.push_back(papsGroupExt[i]);
1307 : }
1308 0 : CPLFree(papsGroupExt);
1309 :
1310 : // get all interior rings and create complex group shape
1311 0 : for (int iRing = 0; iRing < innerRingsCnt; iRing++)
1312 : {
1313 0 : DGNElemCore **papsGroupInner = LineStringToElementGroup(
1314 0 : poPoly->getInteriorRing(iRing), DGNT_SHAPE);
1315 0 : papsGroupInner[0]->properties |= DGNPF_HOLE;
1316 0 : DGNUpdateElemCoreExtended(hDGN, papsGroupInner[0]);
1317 0 : for (int i = 0; papsGroupInner[i] != nullptr; i++)
1318 : {
1319 0 : dgnElements.push_back(papsGroupInner[i]);
1320 : }
1321 0 : CPLFree(papsGroupInner);
1322 : }
1323 0 : int index = 1;
1324 : papsGroup = static_cast<DGNElemCore **>(
1325 0 : CPLCalloc(sizeof(void *), dgnElements.size() + 2));
1326 0 : for (std::list<DGNElemCore *>::iterator list_iter =
1327 0 : dgnElements.begin();
1328 0 : list_iter != dgnElements.end(); ++list_iter)
1329 : {
1330 0 : papsGroup[index++] = *list_iter;
1331 : }
1332 :
1333 : // papsGroup[0] = DGNCreateComplexHeaderFromGroup(
1334 : // hDGN, DGNT_COMPLEX_SHAPE_HEADER, dgnElements.size(),
1335 : // papsGroup+1 );
1336 0 : DGNPoint asPoints[1] = {};
1337 0 : papsGroup[0] = DGNCreateCellHeaderFromGroup(
1338 0 : hDGN, "", 1, nullptr, static_cast<int>(dgnElements.size()),
1339 : papsGroup + 1, asPoints + 0, 1.0, 1.0, 0.0);
1340 0 : DGNAddShapeFillInfo(hDGN, papsGroup[0], 6);
1341 : }
1342 : else
1343 : {
1344 13 : papsGroup = papsGroupExt;
1345 : }
1346 : }
1347 17 : else if (wkbFlatten(poGeom->getGeometryType()) == wkbMultiPolygon ||
1348 13 : wkbFlatten(poGeom->getGeometryType()) == wkbMultiPoint ||
1349 35 : wkbFlatten(poGeom->getGeometryType()) == wkbMultiLineString ||
1350 5 : wkbFlatten(poGeom->getGeometryType()) == wkbGeometryCollection)
1351 : {
1352 42 : for (auto &&poMember : poGeom->toGeometryCollection())
1353 : {
1354 26 : OGRErr eErr = CreateFeatureWithGeom(poFeature, poMember);
1355 26 : if (eErr != OGRERR_NONE)
1356 1 : return eErr;
1357 : }
1358 :
1359 16 : return OGRERR_NONE;
1360 : }
1361 : else
1362 : {
1363 0 : CPLError(CE_Failure, CPLE_AppDefined,
1364 : "Unsupported geometry type (%s) for DGN.",
1365 0 : OGRGeometryTypeToName(poGeom->getGeometryType()));
1366 0 : return OGRERR_FAILURE;
1367 : }
1368 :
1369 : /* -------------------------------------------------------------------- */
1370 : /* Add other attributes. */
1371 : /* -------------------------------------------------------------------- */
1372 45 : int nLevel = poFeature->GetFieldAsInteger("Level");
1373 45 : int nGraphicGroup = poFeature->GetFieldAsInteger("GraphicGroup");
1374 45 : int nColor = poFeature->GetFieldAsInteger("ColorIndex");
1375 45 : int nWeight = poFeature->GetFieldAsInteger("Weight");
1376 45 : int nStyle = poFeature->GetFieldAsInteger("Style");
1377 45 : int nMSLink = poFeature->GetFieldAsInteger("MSLink");
1378 :
1379 : // TODO: Use std::max and std::min.
1380 45 : nLevel = std::max(0, std::min(63, nLevel));
1381 45 : nColor = std::max(0, std::min(255, nColor));
1382 45 : nWeight = std::max(0, std::min(31, nWeight));
1383 45 : nStyle = std::max(0, std::min(7, nStyle));
1384 45 : nMSLink = std::max(0, nMSLink);
1385 :
1386 45 : DGNUpdateElemCore(hDGN, papsGroup[0], nLevel, nGraphicGroup, nColor,
1387 : nWeight, nStyle);
1388 45 : DGNAddMSLink(hDGN, papsGroup[0], DGNLT_ODBC, 0, nMSLink);
1389 : /* -------------------------------------------------------------------- */
1390 : /* Write to file. */
1391 : /* -------------------------------------------------------------------- */
1392 92 : for (int i = 0; papsGroup[i] != nullptr; i++)
1393 : {
1394 47 : DGNWriteElement(hDGN, papsGroup[i]);
1395 :
1396 47 : if (i == 0)
1397 45 : poFeature->SetFID(papsGroup[i]->element_id);
1398 :
1399 47 : DGNFreeElement(hDGN, papsGroup[i]);
1400 : }
1401 :
1402 45 : CPLFree(papsGroup);
1403 :
1404 45 : return OGRERR_NONE;
1405 : }
1406 :
1407 : /************************************************************************/
1408 : /* GetDataset() */
1409 : /************************************************************************/
1410 :
1411 18 : GDALDataset *OGRDGNLayer::GetDataset()
1412 : {
1413 18 : return m_poDS;
1414 : }
|