Coverage for brodata / bhr.py: 81%

359 statements  

« prev     ^ index     » next       coverage.py v7.14.0, created at 2026-05-13 12:57 +0000

1# ruff: noqa: F401 

2import logging 

3import tempfile 

4 

5import pandas as pd 

6 

7from . import bro 

8 

9logger = logging.getLogger(__name__) 

10 

11 

12class _BoreholeResearch(bro.FileOrUrl): 

13 """Class to represent a Borehole Research (BHR) from the BRO. 

14 

15 BHR-GT: https://www.bro-productomgeving.nl/__attachments/1607501782/DO_ResponseBHR_GT_O_DP.xml?inst-v=b22a9e44-fb13-4fb2-8480-df44d20adeb7 

16 link found at https://www.bro-productomgeving.nl/bpo/latest/bhr-gt-voorbeeldberichten-uitgifte) 

17 """ 

18 

19 def _read_contents(self, tree): 

20 ns = { 

21 "brocom": "http://www.broservices.nl/xsd/brocommon/3.0", 

22 "gml": "http://www.opengis.net/gml/3.2", 

23 "bhrgtcom": "http://www.broservices.nl/xsd/bhrgtcommon/2.1", 

24 "xmlns": self._xmlns, 

25 } 

26 bhr = self._get_main_object(tree, self._object_name, ns) 

27 for key in bhr.attrib: 

28 setattr(self, key.split("}", 1)[1], bhr.attrib[key]) 

29 for child in bhr: 

30 key = self._get_tag(child) 

31 if len(child) == 0: 

32 setattr(self, key, child.text) 

33 elif key == "standardizedLocation": 

34 self._read_standardized_location(child) 

35 elif key == "deliveredLocation": 

36 self._read_delivered_location(child) 

37 elif key in ["researchReportDate"]: 

38 setattr(self, key, self._read_date(child)) 

39 elif key in ["siteCharacteristic"]: 

40 for grandchild in child: 

41 key = self._get_tag(grandchild) 

42 if key in [ 

43 "landUse", 

44 "drained", 

45 "soilUse", 

46 "positionOnGroundBody", 

47 "temporaryChange", 

48 ]: 

49 setattr(self, key, grandchild.text) 

50 elif key in [ 

51 "meanHighestGroundwaterTable", 

52 "meanLowestGroundwaterTable", 

53 ]: 

54 setattr(self, key, float(grandchild.text)) 

55 else: 

56 self._warn_unknown_tag(key) 

57 elif key in ["registrationHistory", "reportHistory"]: 

58 self._read_children_of_children(child) 

59 elif key == "deliveredVerticalPosition": 

60 self._read_delivered_vertical_position(child) 

61 elif key == "boring": 

62 if self._check_single_child_with_tag(child, "Boring"): 

63 child = child[0] 

64 self._read_boring(child) 

65 elif key == "boreholeSampleDescription": 

66 self._read_borehole_sample_description(child) 

67 elif key == "boreholeSampleAnalysis": 

68 # Check if there's an BoreholeSampleAnalysis wrapper element 

69 if self._check_single_child_with_tag(child, "BoreholeSampleAnalysis"): 

70 child = child[0] 

71 self._read_borehole_sample_analysis(child) 

72 else: 

73 self._warn_unknown_tag(key) 

74 for key in [ 

75 "completedInterval", 

76 "boredInterval", 

77 "sampledInterval", 

78 "investigatedInterval", 

79 "boringVelocity", 

80 "excavatedLayer", 

81 ]: 

82 if hasattr(self, key): 

83 setattr(self, key, pd.DataFrame(getattr(self, key))) 

84 

85 def _read_boring(self, node): 

86 for child in node: 

87 key = self._get_tag(child) 

88 if key in ["boringStartDate", "boringEndDate"]: 

89 setattr(self, key, self._read_date(child)) 

90 elif key in [ 

91 "preparation", 

92 "trajectoryExcavated", 

93 "rockReached", 

94 "boringProcedure", 

95 "casingUsed", 

96 "flushingMedium", 

97 "stopCriterion", 

98 "trajectoryRemoved", 

99 "samplingProcedure", 

100 "subsurfaceContaminated", 

101 "boreholeCompleted", 

102 ]: 

103 setattr(self, key, child.text) 

104 elif key in [ 

105 "finalDepthBoring", 

106 "groundwaterLevel", 

107 "finalDepthSampling", 

108 "finalDepthExcavation", 

109 "finalDepthTemporaryCasing", 

110 ]: 

111 setattr(self, key, self._parse_float(child)) 

112 elif key in [ 

113 "temporaryCasingUsed", 

114 "flushingMediumUsed", 

115 "flushingAdditive", 

116 ]: 

117 setattr(self, key, child.text) 

118 elif key == "boredTrajectory": 

119 for grandchild in child: 

120 key = self._get_tag(grandchild) 

121 if key in ["beginDepth", "endDepth"]: 

122 setattr(self, key, self._parse_float(grandchild)) 

123 elif key in ["boredInterval", "sampledInterval", "completedInterval"]: 

124 self._read_interval(child, key) 

125 elif key == "boringTool": 

126 self._read_boring_tool(child) 

127 elif key == "boringVelocity": 

128 if not hasattr(self, key): 

129 setattr(self, key, []) 

130 bv = {} 

131 for grandchild in child: 

132 key2 = self._get_tag(grandchild) 

133 if key2 in ["elapsedTime", "depth"]: 

134 bv[key2] = self._parse_float(grandchild) 

135 else: 

136 self._warn_unknown_tag(key2) 

137 self.boringVelocity.append(bv) 

138 elif key == "excavatedLayer": 

139 if not hasattr(self, key): 

140 setattr(self, key, []) 

141 el = {} 

142 for grandchild in child: 

143 key2 = self._get_tag(grandchild) 

144 if key2 in ["upperBoundary", "lowerBoundary"]: 

145 el[key2] = self._parse_float(grandchild) 

146 elif key2 in ["excavatedMaterial"]: 

147 el[key2] = grandchild.text 

148 else: 

149 self._warn_unknown_tag(key2) 

150 self.excavatedLayer.append(el) 

151 else: 

152 self._warn_unknown_tag(key) 

153 

154 def _read_borehole_sample_analysis(self, node): 

155 for child in node: 

156 key = self._get_tag(child) 

157 if key == "analysisReportDate": 

158 setattr(self, key, self._read_date(child)) 

159 elif key in ["analysisType", "locationSpecific", "analysisProcedure"]: 

160 setattr(self, key, child.text) 

161 elif key == "investigatedInterval": 

162 if not hasattr(self, key): 

163 setattr(self, key, []) 

164 # Check if there's an InvestigatedInterval wrapper element 

165 if self._check_single_child_with_tag(child, "InvestigatedInterval"): 

166 child = child[0] 

167 ii = self._read_investigated_interval(child) 

168 self.investigatedInterval.append(ii) 

169 else: 

170 self._warn_unknown_tag(key) 

171 

172 def _read_investigated_interval(self, node): 

173 d = {} 

174 for child in node: 

175 key = self._get_tag(child) 

176 if key in ["beginDepth", "endDepth"]: 

177 d[key] = self._parse_float(child) 

178 elif key in [ 

179 "locationSpecific", 

180 "sampleQuality", 

181 "analysisType", 

182 "waterContentDetermined", 

183 "organicMatterContentDetermined", 

184 "carbonateContentDetermined", 

185 "volumetricMassDensityDetermined", 

186 "volumetricMassDensitySolidsDetermined", 

187 "described", 

188 ]: 

189 d[key] = child.text 

190 elif key == "particleSizeDistributionDetermination": 

191 if key not in d: 

192 d[key] = [] 

193 for grandchild in child: 

194 key2 = self._get_tag(grandchild) 

195 if key2 == "ParticleSizeDistributionDetermination": 

196 psdd = self._read_particle_size_distribution_determination( 

197 grandchild 

198 ) 

199 d[key].append(psdd) 

200 else: 

201 self._warn_unknown_tag(key2) 

202 elif key == "consistencyLimitsDetermination": 

203 if key not in d: 

204 d[key] = [] 

205 for grandchild in child: 

206 key2 = self._get_tag(grandchild) 

207 if key2 == "ConsistencyLimitsDetermination": 

208 cld = self._read_consistency_limits_determination(grandchild) 

209 d[key].append(cld) 

210 else: 

211 self._warn_unknown_tag(key2) 

212 elif key == "waterContentDetermination": 

213 d[key] = self._read_water_content_determination(child) 

214 elif key == "volumetricMassDensityDetermination": 

215 d[key] = self._read_volumetric_mass_density_determination(child) 

216 elif key == "volumetricMassDensitySolidsDetermination": 

217 d[key] = self._read_volumetric_mass_density_solids_determination(child) 

218 else: 

219 self._warn_unknown_tag(key) 

220 if "particleSizeDistributionDetermination" in d: 

221 d["particleSizeDistributionDetermination"] = pd.DataFrame( 

222 d["particleSizeDistributionDetermination"] 

223 ) 

224 if "consistencyLimitsDetermination" in d: 

225 d["consistencyLimitsDetermination"] = pd.DataFrame( 

226 d["consistencyLimitsDetermination"] 

227 ) 

228 return d 

229 

230 def _read_water_content_determination(self, node): 

231 d = {} 

232 for child in node: 

233 key = self._get_tag(child) 

234 if key in [ 

235 "determinationProcedure", 

236 "determinationMethod", 

237 "sampleMoistness", 

238 "removedMaterial", 

239 ]: 

240 d[key] = child.text 

241 elif key == "determinationResult": 

242 for grandchild in child: 

243 key = self._get_tag(grandchild) 

244 if key in ["waterContent"]: 

245 d[key] = self._parse_float(grandchild) 

246 elif key in [ 

247 "dryingTemperature", 

248 "dryingPeriod", 

249 "saltCorrectionMethod", 

250 ]: 

251 d[key] = grandchild.text 

252 else: 

253 self._warn_unknown_tag(key) 

254 d[key] = self._parse_float(child) 

255 else: 

256 self._warn_unknown_tag(key) 

257 return d 

258 

259 def _read_volumetric_mass_density_determination(self, node): 

260 d = {} 

261 for child in node: 

262 key = self._get_tag(child) 

263 if key in [ 

264 "determinationProcedure", 

265 "determinationMethod", 

266 "sampleMoistness", 

267 ]: 

268 d[key] = child.text 

269 elif key in ["volumetricMassDensity"]: 

270 d[key] = self._parse_float(child) 

271 

272 def _read_volumetric_mass_density_solids_determination(self, node): 

273 d = {} 

274 for child in node: 

275 key = self._get_tag(child) 

276 if key in [ 

277 "determinationProcedure", 

278 "determinationMethod", 

279 "usedMedium", 

280 "sampleContainerVolume", 

281 "removedMaterial", 

282 ]: 

283 d[key] = child.text 

284 elif key in ["volumetricMassDensitySolids"]: 

285 d[key] = self._parse_float(child) 

286 

287 def _read_particle_size_distribution_determination(self, node): 

288 d = {} 

289 for child in node: 

290 key = self._get_tag(child) 

291 if key in [ 

292 "determinationProcedure", 

293 "determinationMethod", 

294 "particleSizeDistributionStandardised", 

295 "dispersionMethod", 

296 ]: 

297 d[key] = child.text 

298 elif key == "nonStandardisedFraction": 

299 if key not in d: 

300 d[key] = [] 

301 d2 = {} 

302 for grandchild in child: 

303 key2 = self._get_tag(grandchild) 

304 if key2 in ["lowerBoundary", "upperBoundary"]: 

305 d2[key2] = int(grandchild.text) 

306 elif key2 in ["proportion"]: 

307 d2[key2] = self._parse_float(grandchild) 

308 else: 

309 self._warn_unknown_tag(key2) 

310 d[key].append(d2) 

311 else: 

312 self._warn_unknown_tag(key) 

313 if "nonStandardisedFraction" in d: 

314 d["nonStandardisedFraction"] = pd.DataFrame(d["nonStandardisedFraction"]) 

315 return d 

316 

317 def _read_consistency_limits_determination(self, node): 

318 d = {} 

319 for child in node: 

320 key = self._get_tag(child) 

321 if key in [ 

322 "determinationProcedure", 

323 "determinationMethod", 

324 "usedMedium", 

325 "conusType", 

326 ]: 

327 d[key] = child.text 

328 elif key in [ 

329 "fractionLarger500um", 

330 "liquidLimit", 

331 "plasticLimit", 

332 "plasticityIndex", 

333 ]: 

334 d[key] = self._parse_float(child) 

335 elif key == "performanceIrregularity": 

336 if key not in d: 

337 d[key] = [] 

338 d[key].append(child.text) 

339 elif key == "plasticityAtSpecificWaterContent": 

340 if key not in d: 

341 d[key] = [] 

342 d2 = {} 

343 for grandchild in child: 

344 key2 = self._get_tag(grandchild) 

345 if key2 in ["waterContent", "penetrationDepth"]: 

346 d2[key2] = self._parse_float(grandchild) 

347 

348 else: 

349 self._warn_unknown_tag(key) 

350 if "plasticityAtSpecificWaterContent" in d: 

351 d["plasticityAtSpecificWaterContent"] = pd.DataFrame( 

352 d["plasticityAtSpecificWaterContent"] 

353 ) 

354 return d 

355 

356 def _read_interval(self, node, key): 

357 if not hasattr(self, key): 

358 setattr(self, key, []) 

359 d = {} 

360 to_float = ["beginDepth", "endDepth"] 

361 self._read_children_of_children(node, d, to_float=to_float) 

362 getattr(self, key).append(d) 

363 

364 def _read_boring_tool(self, node): 

365 d = {} 

366 to_float = ["boringToolDiameter", "beginDepth", "endDepth"] 

367 self._read_children_of_children(node, d, to_float=to_float) 

368 self.boringTool = d 

369 

370 def _read_borehole_sample_description(self, node): 

371 for child in node: 

372 key = self._get_tag(child) 

373 if key == "BoreholeSampleDescription": 

374 self._read_borehole_sample_description(child) 

375 elif key == "descriptiveBoreholeLog": 

376 if not hasattr(self, "descriptiveBoreholeLog"): 

377 self.descriptiveBoreholeLog = [] 

378 if self._check_single_child_with_tag(child, "DescriptiveBoreholeLog"): 

379 child = child[0] 

380 dbl = self._read_descriptive_borehole_log(child) 

381 self.descriptiveBoreholeLog.append(dbl) 

382 elif key == "descriptionReportDate": 

383 setattr(self, key, self._read_date(child)) 

384 elif key == "result": 

385 self._read_borehole_sample_description_result(child) 

386 elif key in ["phenomenonTime", "resultTime"]: 

387 setattr(self, key, self._read_time_instant(child)) 

388 elif key in [ 

389 "descriptionProcedure", 

390 "procedure", 

391 "observedProperty", 

392 "featureOfInterest", 

393 "descriptionMethod", 

394 "descriptionLocation", 

395 "fractionDistributionDetermined", 

396 ]: 

397 setattr(self, key, child.text) 

398 elif key in ["lowerBoundarySandFraction"]: 

399 setattr(self, key, int(child.text)) 

400 elif key == "soilClassification": 

401 if hasattr(self, key): 

402 self._raise_assumed_single(key) 

403 setattr(self, key, self._read_soil_classification(child)) 

404 else: 

405 self._warn_unknown_tag(key) 

406 

407 def _read_soil_classification(self, node): 

408 d = {} 

409 for child in node: 

410 key = self._get_tag(child) 

411 if key in [ 

412 "codeGroup", 

413 "classificationCode", 

414 "featureTop", 

415 "soilClass", 

416 "subsoilPeat", 

417 "lowerBoundaryPeat", 

418 "textureClass", 

419 "textureProfile", 

420 "subsoilDuinVagueSoil", 

421 "carbonateProfile", 

422 "peatClass", 

423 "reworkingClass", 

424 "groundwaterTableClass", 

425 "featureSite", 

426 ]: 

427 d[key] = child.text 

428 elif key == "featureBottom": 

429 for grandchild in child: 

430 key = self._get_tag(grandchild) 

431 if key == "feature": 

432 d[key] = grandchild.text 

433 elif key == "beginDepth": 

434 d[key] = float(grandchild.text) 

435 else: 

436 self._warn_unknown_tag(key) 

437 else: 

438 self._warn_unknown_tag(key) 

439 return d 

440 

441 def _read_borehole_sample_description_result(self, node): 

442 boreholeSampleDescription = [] 

443 for child in node: 

444 key = self._get_tag(child) 

445 if len(child) == 0: 

446 setattr(self, key, child.text) 

447 elif key == "soilLayer": 

448 d = {} 

449 to_float = [ 

450 "upperBoundary", 

451 "lowerBoundary", 

452 "organicMatterContent", 

453 "clayContent", 

454 ] 

455 to_int = ["numberOfLayerComponents"] 

456 self._read_children_of_children( 

457 child, d=d, to_float=to_float, to_int=to_int 

458 ) 

459 boreholeSampleDescription.append(d) 

460 elif key == "litterLayer": 

461 if hasattr(self, key): 

462 self._raise_assumed_single(key) 

463 setattr(self, key, {}) 

464 for grandchild in child: 

465 key = self._get_tag(grandchild) 

466 if key in [ 

467 "upperBoundary", 

468 "lowerBoundary", 

469 "organicMatterContent", 

470 ]: 

471 self.litterLayer[key] = float(grandchild.text) 

472 elif key in ["horizonCode", "litterType"]: 

473 self.litterLayer[key] = grandchild.text 

474 else: 

475 self._warn_unknown_tag(key) 

476 

477 else: 

478 self._warn_unknown_tag(key) 

479 df = pd.DataFrame(boreholeSampleDescription) 

480 if hasattr(self, "boreholeSampleDescription"): 

481 self._raise_assumed_single("boreholeSampleDescription") 

482 setattr(self, "boreholeSampleDescription", df) 

483 

484 

485class GeotechnicalBoreholeResearch(_BoreholeResearch): 

486 """Class to represent a Geotechnical Borehole Research (BHR_GT) from the BRO.""" 

487 

488 _object_name = "BHR_GT_O" 

489 _xmlns = "http://www.broservices.nl/xsd/dsbhr-gt/2.1" 

490 _rest_url = "https://publiek.broservices.nl/sr/bhrgt/v2" 

491 _char = "BHR_GT_C" 

492 

493 

494def bhrgt_graph( 

495 xml_file, 

496 to_file=None, 

497 timeout=5, 

498 language="nl", 

499 asNAP=False, 

500 topMv=None, 

501 bottomMv=None, 

502 topNap=None, 

503 bottomNap=None, 

504 return_fname=False, 

505): 

506 """ 

507 Generate a svg-graph of a bhrgt-file (GeotechnicalBoreholeResearch). 

508 

509 Parameters 

510 ---------- 

511 xml_file : str 

512 The filename of the xml-file to generate a graphical representation of. 

513 to_file : str, optional 

514 The filename to save the svg-file to. The default is None. 

515 timeout : int or float, optional 

516 A number indicating how many seconds to wait for the client to make a connection 

517 and/or send a response. The default is 5. 

518 language : str, optional 

519 DESCRIPTION. The default is "nl". 

520 asNAP : bool, optional 

521 If True, display the height of the drilling in m NAP. If False, display the 

522 height in meter below surface level. The default is False. 

523 topMv : float, optional 

524 Highest point in the graph, in m below surface level (mv). Needs to be specified 

525 together with bottomMv. The default is None. 

526 bottomMv : float, optional 

527 Lowest point in the graph, in m below surface level (mv). Needs to be specified 

528 together with topMv. The default is None. 

529 topNap : float, optional 

530 Highest point in the graph, in m NAP. Needs to be specified together with 

531 bottomNap. The default is None. 

532 bottomNap : float, optional 

533 Lowest point in the graph, in m NAP. Needs to be specified together with 

534 topNAP. The default is None. 

535 return_fname : bool, optional 

536 If True, Return the filename of the svg-file. The default is False. 

537 

538 Returns 

539 ------- 

540 IPython.display.SVG or str 

541 A graphical representation of the svg-file or the filename of the svg-file. 

542 

543 """ 

544 url = "https://publiek.broservices.nl/sr/bhrgt/v2/profile/graph/dispatch" 

545 params = {"language": language, "asNAP": asNAP} 

546 

547 if ((topMv is None) + (bottomMv is None)) == 1: 

548 raise (ValueError("Both topMv and bottomMv need to be specified, or none")) 

549 if topMv is not None: 

550 params["topMv"] = topMv 

551 params["bottomMv"] = bottomMv 

552 

553 if ((topNap is None) + (bottomNap is None)) == 1: 

554 raise (ValueError("Both topNap and bottomNap need to be specified, or none")) 

555 if topNap is not None: 

556 params["topNap"] = topNap 

557 params["bottomNap"] = bottomNap 

558 

559 with open(xml_file, "rb") as data: 

560 r = bro.util.post_with_rate_limit( 

561 url, data=data, timeout=timeout, params=params 

562 ) 

563 r.raise_for_status() 

564 if to_file is None: 

565 to_file = tempfile.NamedTemporaryFile(suffix=".svg").name 

566 with open(to_file, "w", encoding="utf-8") as f: 

567 f.write(r.text) 

568 if return_fname: 

569 return to_file 

570 else: 

571 from IPython.display import SVG 

572 

573 return SVG(to_file) 

574 

575 

576class PedologicalBoreholeResearch(_BoreholeResearch): 

577 """Class to represent a Pedological Borehole Research (BHR_P) from the BRO.""" 

578 

579 _object_name = "BHR_O" 

580 _xmlns = "http://www.broservices.nl/xsd/dsbhr/2.0" 

581 _rest_url = "https://publiek.broservices.nl/sr/bhrp/v2" 

582 _char = "BHR_C" 

583 

584 

585class GeologicalBoreholeResearch(_BoreholeResearch): 

586 """Class to represent a Geological Borehole Research (BHR_G) from the BRO.""" 

587 

588 _object_name = "BHR_G_O" 

589 _xmlns = "http://www.broservices.nl/xsd/dsbhrg/3.1" 

590 _rest_url = "https://publiek.broservices.nl/sr/bhrg/v3" 

591 _char = "BHR_G_C" 

592 

593 

594def get_bro_ids_of_bronhouder(bronhouder, bhr_class=GeotechnicalBoreholeResearch): 

595 """ 

596 Retrieve list of BRO (Basisregistratie Ondergrond) IDs for a given bronhouder. 

597 

598 This function sends a GET request to the REST API to fetch the BRO IDs associated 

599 with the specified bronhouder. If the request is unsuccessful, it logs an error 

600 message. 

601 

602 Parameters 

603 ---------- 

604 bronhouder : str 

605 The identifier for the bronhouder to retrieve the associated BRO IDs. 

606 bhr_class : class 

607 The class of borehole objects. The default is GeotechnicalBoreholeResearch. 

608 Other options are PedologicalBoreholeResearch and GeologicalBoreholeResearch. 

609 

610 Returns 

611 ------- 

612 list or None 

613 A list of BRO IDs if the request is successful. Returns `None` if the request 

614 fails. 

615 """ 

616 return bro._get_bro_ids_of_bronhouder(bhr_class, bronhouder) 

617 

618 

619def get_data_for_bro_ids(bhr_class=GeotechnicalBoreholeResearch, **kwargs): 

620 _check_first_argument(bhr_class) 

621 return bro._get_data_for_bro_ids(bhr_class, **kwargs) 

622 

623 

624def get_characteristics(bhr_class=GeotechnicalBoreholeResearch, **kwargs): 

625 """ 

626 Get characteristics of a set of registered objects for a given object class. 

627 

628 The maximum number of objects that can be retrieved is 2000 for a single request. 

629 

630 Parameters 

631 ---------- 

632 bhr_class : class 

633 The class of borehole objects. The default is GeotechnicalBoreholeResearch. 

634 Other options are PedologicalBoreholeResearch and GeologicalBoreholeResearch. 

635 tmin : str or pd.Timestamp, optional 

636 The minimum registrationPeriod of the requested characteristics. The default is 

637 None. 

638 tmax : str or pd.Timestamp, optional 

639 The maximum registrationPeriod of the requested characteristics. The default is 

640 None. 

641 extent : list or tuple of 4 floats, optional 

642 Download the characteristics within extent ([xmin, xmax, ymin, ymax]). The 

643 default is None. 

644 x : float, optional 

645 The x-coordinate of the center of the circle in which the characteristics are 

646 requested. The default is None. 

647 y : float, optional 

648 The y-coordinate of the center of the circle in which the characteristics are 

649 requested. The default is None. 

650 radius : float, optional 

651 The radius in meters of the center of the circle in which the characteristics 

652 are requested. The default is 1000.0. 

653 epsg : str, optional 

654 The coordinate reference system of the specified extent, x or y, and of the 

655 resulting GeoDataFrame. The default is 28992, which is the Dutch RD-system. 

656 to_file : str, optional 

657 When not None, save the characteristics to a file with a name as specified in 

658 to_file. The defaults None. 

659 use_all_corners_of_extent : bool, optional 

660 Because the extent by default is given in epsg 28992, some locations along the 

661 border of a requested extent will not be returned in the result. To solve this 

662 issue, when use_all_corners_of_extent is True, all four corners of the extent 

663 are used to calculate the minimum and maximum lan and lon values. The default is 

664 True. 

665 timeout : int or float, optional 

666 A number indicating how many seconds to wait for the client to make a connection 

667 and/or send a response. The default is 5. 

668 

669 Returns 

670 ------- 

671 gpd.GeoDataFrame 

672 A GeoDataFrame contraining the characteristics. 

673 

674 Notes 

675 ----- 

676 Haalt de karakteristieken op van een set van registratie objecten, gegeven een 

677 kenmerkenverzameling (kenset). 

678 

679 De karakteristieken geven een samenvatting van een object zodat een verdere selectie 

680 gemaakt kan worden. Het past in een tweetrapsbenadering, waarbij de eerste stap 

681 bestaat uit het ophalen van de karakteristieken en de 2e stap uit het ophalen van de 

682 gewenste registratie objecten. Het resultaat van deze operatie is gemaximaliseerd op 

683 2000. 

684 """ 

685 if not isinstance(bhr_class, type): 

686 raise TypeError( 

687 "First argument must be a class (a subclass of _BoreholeResearch), " 

688 "for example GeotechnicalBoreholeResearch. " 

689 "If you passed other arguments positionally, use keyword arguments " 

690 "instead, e.g. extent=[xmin, xmax, ymin, ymax]." 

691 ) 

692 return bro._get_characteristics(bhr_class, **kwargs) 

693 

694 

695def get_data_in_extent(bhr_class=GeotechnicalBoreholeResearch, **kwargs): 

696 _check_first_argument(bhr_class) 

697 return bro._get_data_in_extent(bhr_class, **kwargs) 

698 

699 

700def _check_first_argument(bhr_class): 

701 if not isinstance(bhr_class, type): 

702 raise TypeError( 

703 "First argument must be a class (a subclass of _BoreholeResearch), " 

704 "for example GeotechnicalBoreholeResearch. " 

705 "If you passed other arguments positionally, use keyword arguments " 

706 "instead, e.g. extent=[xmin, xmax, ymin, ymax]." 

707 )