37#include <wx/geometry.h>
57#define __CALL_CONVENTION
59#define __CALL_CONVENTION
78static std::list<float_2Dpt> g_pvshp;
79static std::list<GLvertexshp *> g_vertexesshp;
80static int g_typeshp, g_posshp;
81static float_2Dpt g_p1shp, g_p2shp;
83void __CALL_CONVENTION shpscombineCallback(GLdouble coords[3],
84 GLdouble *vertex_data[4],
89 vertex =
new GLvertexshp();
90 g_vertexesshp.push_back(vertex);
92 vertex->info.x = coords[0];
93 vertex->info.y = coords[1];
95 *dataOut = vertex->data;
98void __CALL_CONVENTION shpserrorCallback(GLenum errorCode) {
99 const GLubyte *estring;
100 estring = gluErrorString(errorCode);
104void __CALL_CONVENTION shpsbeginCallback(GLenum type) {
107 case GL_TRIANGLE_STRIP:
108 case GL_TRIANGLE_FAN:
112 printf(
"tess unhandled begin type: %d\n", type);
118void __CALL_CONVENTION shpsendCallback() {}
120void __CALL_CONVENTION shpsvertexCallback(GLvoid *arg) {
122 vertex = (GLvertexshp *)arg;
124 p.y = vertex->info.x;
125 p.x = vertex->info.y;
128 if (g_typeshp != GL_TRIANGLES) {
130 g_pvshp.push_back(g_p1shp);
131 g_pvshp.push_back(g_p2shp);
134 if (g_typeshp == GL_TRIANGLE_STRIP)
136 else if (g_posshp == 0)
141 g_pvshp.push_back(p);
146ShapeBaseChartSet::ShapeBaseChartSet() : _loaded(false) {
147 land_color = wxColor(170, 175, 80);
149void ShapeBaseChartSet::SetBasemapLandColor(wxColor color) {
152wxColor ShapeBaseChartSet::GetBasemapLandColor() {
return land_color; }
154wxPoint2DDouble ShapeBaseChartSet::GetDoublePixFromLL(
ViewPort &vp,
double lat,
157 p.m_x -= vp.rv_rect.x, p.m_y -= vp.rv_rect.y;
165 if (_basemap_map.find(
Quality::low) != _basemap_map.end()) {
171 if (_basemap_map.find(
Quality::high) != _basemap_map.end()) {
178 if (_basemap_map.find(
Quality::full) != _basemap_map.end()) {
181 if (_basemap_map.find(
Quality::high) != _basemap_map.end()) {
187 if (_basemap_map.find(
Quality::low) != _basemap_map.end()) {
194 if (_basemap_map.find(
Quality::full) != _basemap_map.end() &&
198 }
else if (_basemap_map.find(
Quality::high) != _basemap_map.end() &&
206 }
else if (_basemap_map.find(
Quality::low) != _basemap_map.end() &&
211 return LowestQualityBaseMap();
214void ShapeBaseChartSet::Reset() {
216 wxString basemap_dir;
217 if (gWorldShapefileLocation.empty()) {
218 basemap_dir = g_Platform->GetSharedDataDir();
219 basemap_dir.Append(
"basemap_shp");
221 basemap_dir = gWorldShapefileLocation;
224 LoadBasemaps(basemap_dir.ToStdString());
226void ShapeBaseChartSet::LoadBasemaps(
const std::string &dir) {
228 _basemap_map.clear();
230 if (fs::exists(ShapeBaseChart::ConstructPath(dir,
"crude_10x10"))) {
231 auto c =
ShapeBaseChart(ShapeBaseChart::ConstructPath(dir,
"crude_10x10"),
232 300000000, land_color);
237 if (fs::exists(ShapeBaseChart::ConstructPath(dir,
"low"))) {
238 _basemap_map.insert(std::make_pair(
240 15000000, land_color)));
242 if (fs::exists(ShapeBaseChart::ConstructPath(dir,
"medium"))) {
243 _basemap_map.insert(std::make_pair(
245 ShapeBaseChart(ShapeBaseChart::ConstructPath(dir,
"medium"), 1000000,
248 if (fs::exists(ShapeBaseChart::ConstructPath(dir,
"high"))) {
249 _basemap_map.insert(std::make_pair(
251 ShapeBaseChart(ShapeBaseChart::ConstructPath(dir,
"high"), 300000,
254 if (fs::exists(ShapeBaseChart::ConstructPath(dir,
"full"))) {
255 _basemap_map.insert(std::make_pair(
266 if (!fs::exists(_filename)) {
270 std::unique_ptr<shp::ShapefileReader> temp_reader(
271 new shp::ShapefileReader(_filename));
272 if (!temp_reader->isOpen()) {
273 MESSAGE_LOG <<
"Shapefile " << _filename <<
" is not opened";
281 auto bounds = temp_reader->getBounds();
282 _is_usable = temp_reader->getCount() > 1 && bounds.getMaxX() <= 180 &&
283 bounds.getMinX() >= -180 && bounds.getMinY() >= -90 &&
291 _is_usable &= temp_reader->getGeometryType() == shp::GeometryType::Polygon;
294 for (
auto field : temp_reader->getFields()) {
295 if (field.getName() ==
"x") {
297 }
else if (field.getName() ==
"y") {
301 _is_tiled = (has_x && has_y);
302 if (_is_usable && _is_tiled) {
304 for (
auto const &feature : *temp_reader) {
310 auto f1 = feature.getAttributes();
313 _tiles[
LatLonKey(std::any_cast<int>(feature.getAttributes()[
"y"]),
314 std::any_cast<int>(feature.getAttributes()[
"x"]))]
320 _reader = temp_reader.release();
325void ShapeBaseChart::DoDrawPolygonFilled(
ocpnDC &pnt,
ViewPort &vp,
326 const shp::Feature &feature) {
327 double old_x = -9999999.0, old_y = -9999999.0;
328 auto polygon =
static_cast<shp::Polygon *
>(feature.getGeometry());
329 pnt.SetBrush(_color);
330 for (
auto &ring : polygon->getRings()) {
331 wxPoint *poly_pt =
new wxPoint[ring.getPoints().size()];
333 auto bbox = vp.GetBBox();
334 for (
auto &point : ring.getPoints()) {
337 ShapeBaseChartSet::GetDoublePixFromLL(vp, point.getY(), point.getX());
338 if (round(q.m_x) != round(old_x) || round(q.m_y) != round(old_y)) {
339 poly_pt[cnt].x = round(q.m_x);
340 poly_pt[cnt].y = round(q.m_y);
348 pnt.DrawPolygonTessellated(cnt, poly_pt, 0, 0);
355void ShapeBaseChart::AddPointToTessList(shp::Point &point,
ViewPort &vp,
356 GLUtesselator *tobj,
bool idl) {
358 if (glChartCanvas::HasNormalizedViewPort(vp)) {
359 q = ShapeBaseChartSet::GetDoublePixFromLL(vp, point.getY(), point.getX());
361 q.m_x = point.getY(), q.m_y = point.getX();
363 GLvertexshp *vertex =
new GLvertexshp();
364 g_vertexesshp.push_back(vertex);
365 if (vp.m_projection_type != PROJECTION_POLAR) {
369 if (idl && (point.getX() == 180)) {
370 if (vp.m_projection_type == PROJECTION_MERCATOR ||
371 vp.m_projection_type == PROJECTION_EQUIRECTANGULAR) {
379 vertex->info.x = q.m_x;
380 vertex->info.y = q.m_y;
382 gluTessVertex(tobj, (GLdouble *)vertex, (GLdouble *)vertex);
387void ShapeBaseChart::DoDrawPolygonFilledGL(
ocpnDC &pnt,
ViewPort &vp,
388 const shp::Feature &feature) {
392 vp.GetBBox().GetMinLon() <= -180 || vp.GetBBox().GetMaxLon() >= 180;
393 auto polygon =
static_cast<shp::Polygon *
>(feature.getGeometry());
394 for (
auto &ring : polygon->getRings()) {
396 GLUtesselator *tobj = gluNewTess();
398 gluTessCallback(tobj, GLU_TESS_VERTEX, (_GLUfuncptr)&shpsvertexCallback);
399 gluTessCallback(tobj, GLU_TESS_BEGIN, (_GLUfuncptr)&shpsbeginCallback);
400 gluTessCallback(tobj, GLU_TESS_END, (_GLUfuncptr)&shpsendCallback);
401 gluTessCallback(tobj, GLU_TESS_COMBINE, (_GLUfuncptr)&shpscombineCallback);
402 gluTessCallback(tobj, GLU_TESS_ERROR, (_GLUfuncptr)&shpserrorCallback);
404 gluTessNormal(tobj, 0, 0, 1);
405 gluTessProperty(tobj, GLU_TESS_WINDING_RULE, GLU_TESS_WINDING_NONZERO);
407 gluTessBeginPolygon(tobj, NULL);
408 gluTessBeginContour(tobj);
409 for (
auto &point : ring.getPoints()) {
410 AddPointToTessList(point, vp, tobj, idl);
413 gluTessEndContour(tobj);
414 gluTessEndPolygon(tobj);
417 for (
auto ver : g_vertexesshp) delete ver;
418 g_vertexesshp.clear();
420 float_2Dpt *polyv =
new float_2Dpt[g_pvshp.size()];
422 for (
auto pt : g_pvshp) {
425 size_t polycnt = g_pvshp.size();
433 mat4x4_scale_aniso(mvp, m, 2.0 / (
float)vp.
pix_width,
437 float *pvt =
new float[2 * (polycnt)];
438 for (
size_t i = 0; i < polycnt; i++) {
439 float_2Dpt *pc = polyv + i;
444 pvt[(i * 2) + 1] = q.m_y;
447 GLShaderProgram *shader = pcolor_tri_shader_program[pnt.m_canvasIndex];
451 colorv[0] = _color.Red() / float(256);
452 colorv[1] = _color.Green() / float(256);
453 colorv[2] = _color.Blue() / float(256);
455 shader->SetUniform4fv(
"color", colorv);
457 shader->SetAttributePointerf(
"position", pvt);
459 glDrawArrays(GL_TRIANGLES, 0, polycnt);
464 glDeleteBuffers(1, &vbo);
469void ShapeBaseChart::DrawPolygonFilled(
ocpnDC &pnt,
ViewPort &vp) {
473 if (!_reader && !_loading) {
475 _loaded = std::async(std::launch::async, [&]() {
482 if (_loaded.wait_for(std::chrono::milliseconds(0)) ==
483 std::future_status::ready) {
484 _is_usable = _loaded.get();
491 LLBBox bbox = vp.GetBBox();
495 int lat_start = floor(bbox.GetMinLat());
497 lat_start = lat_start - (pmod + (lat_start % pmod));
499 lat_start = lat_start - (lat_start % pmod);
503 int lon_start = floor(bbox.GetMinLon());
505 lon_start = lon_start - (pmod + (lon_start % pmod));
507 lon_start = lon_start - (lon_start % pmod);
510 for (
int i = lat_start; i < ceil(bbox.GetMaxLat()) + pmod; i += pmod) {
511 for (
int j = lon_start; j < ceil(bbox.GetMaxLon()) + pmod; j += pmod) {
515 }
else if (j >= 180) {
518 for (
auto fid : _tiles[
LatLonKey(i, lon)]) {
519 auto const &feature = _reader->getFeature(fid);
521 DoDrawPolygonFilled(pnt, vp,
524 DoDrawPolygonFilledGL(pnt, vp,
531 for (
auto const &feature : *_reader) {
533 DoDrawPolygonFilled(pnt, vp,
536 DoDrawPolygonFilledGL(pnt, vp,
545 if (!_reader && !_loading) {
547 _loaded = std::async(std::launch::async, [&]() {
554 if (_loaded.wait_for(std::chrono::milliseconds(0)) ==
555 std::future_status::ready) {
556 _is_usable = _loaded.get();
563 double norm_lon1 = lon1;
564 double norm_lon2 = lon2;
566 while (norm_lon1 < -180) norm_lon1 += 360;
567 while (norm_lon1 > 180) norm_lon1 -= 360;
568 while (norm_lon2 < -180) norm_lon2 += 360;
569 while (norm_lon2 > 180) norm_lon2 -= 360;
572 auto A = std::make_pair(lat1, norm_lon1);
573 auto B = std::make_pair(lat2, norm_lon2);
577 double minLat = std::min(lat1, lat2);
578 double maxLat = std::max(lat1, lat2);
579 double minLon = std::min(norm_lon1, norm_lon2);
580 double maxLon = std::max(norm_lon1, norm_lon2);
592 for (
int i = latmin; i <= latmax; i +=
_dmod) {
593 for (
int j = lonmin; j < lonmax; j +=
_dmod) {
596 while (lon < -180) lon += 360;
597 while (lon >= 180) lon -= 360;
600 auto tileIter = _tiles.find(
LatLonKey(i, lon));
601 if (tileIter != _tiles.end()) {
602 for (
auto fid : tileIter->second) {
603 auto const &feature = _reader->getFeature(fid);
604 if (PolygonLineIntersect(feature, A, B)) {
613 for (
auto const &feature : *_reader) {
614 if (PolygonLineIntersect(feature, A, B)) {
626 if (_loaded.valid()) {
632bool ShapeBaseChart::LineLineIntersect(
const std::pair<double, double> &A,
633 const std::pair<double, double> &B,
634 const std::pair<double, double> &C,
635 const std::pair<double, double> &D) {
636 constexpr double EPSILON = 1e-9;
639 double minABx = std::min(A.first, B.first);
640 double maxABx = std::max(A.first, B.first);
641 double minCDx = std::min(C.first, D.first);
642 double maxCDx = std::max(C.first, D.first);
644 if (maxABx < minCDx || maxCDx < minABx)
return false;
646 double minABy = std::min(A.second, B.second);
647 double maxABy = std::max(A.second, B.second);
648 double minCDy = std::min(C.second, D.second);
649 double maxCDy = std::max(C.second, D.second);
651 if (maxABy < minCDy || maxCDy < minABy)
return false;
654 double a1 = B.second - A.second;
655 double b1 = A.first - B.first;
656 double c1 = a1 * A.first + b1 * A.second;
659 double a2 = D.second - C.second;
660 double b2 = C.first - D.first;
661 double c2 = a2 * C.first + b2 * C.second;
663 double determinant = a1 * b2 - a2 * b1;
665 if (std::abs(determinant) < EPSILON) {
669 if (std::abs(a1 * C.first + b1 * C.second - c1) < EPSILON &&
670 std::abs(a1 * D.first + b1 * D.second - c1) < EPSILON) {
672 return !(maxABx < minCDx || maxCDx < minABx || maxABy < minCDy ||
679 double x = (b2 * c1 - b1 * c2) / determinant;
680 double y = (a1 * c2 - a2 * c1) / determinant;
683 return (minABx <= x && x <= maxABx && minABy <= y && y <= maxABy &&
684 minCDx <= x && x <= maxCDx && minCDy <= y && y <= maxCDy);
687bool ShapeBaseChart::PolygonLineIntersect(
const shp::Feature &feature,
688 const std::pair<double, double> &A,
689 const std::pair<double, double> &B) {
690 auto geometry = feature.getGeometry();
696 auto polygon =
dynamic_cast<shp::Polygon *
>(geometry);
702 double minLat = std::min(A.first, B.first);
703 double maxLat = std::max(A.first, B.first);
704 double minLon = std::min(A.second, B.second);
705 double maxLon = std::max(A.second, B.second);
707 for (
auto &ring : polygon->getRings()) {
708 const auto &points = ring.getPoints();
709 if (points.size() < 3)
continue;
712 double minRingLat = std::numeric_limits<double>::max();
713 double maxRingLat = std::numeric_limits<double>::lowest();
714 double minRingLon = std::numeric_limits<double>::max();
715 double maxRingLon = std::numeric_limits<double>::lowest();
718 std::vector<std::pair<double, double>> ringPoints;
719 ringPoints.reserve(points.size());
721 for (
const auto &point : points) {
722 double lat = point.getY();
723 double lon = point.getX();
725 minRingLat = std::min(minRingLat, lat);
726 maxRingLat = std::max(maxRingLat, lat);
727 minRingLon = std::min(minRingLon, lon);
728 maxRingLon = std::max(maxRingLon, lon);
730 ringPoints.emplace_back(lat, lon);
734 if (maxLat < minRingLat || minLat > maxRingLat || maxLon < minRingLon ||
735 minLon > maxRingLon) {
740 size_t numPoints = ringPoints.size();
741 std::pair<double, double> previous = ringPoints.back();
743 for (
size_t i = 0; i < numPoints; i++) {
744 const auto ¤t = ringPoints[i];
746 if (LineLineIntersect(A, B, previous, current)) {
760 chart.SetColor(land_color);
761 chart.RenderViewOnDC(dc, vp);
General chart base definitions.
Wrapper class for OpenGL shader programs.
A latitude/longitude key for 1x1 or 10x10 degree grid tiles.
Manages a set of ShapeBaseChart objects at different resolutions.
bool IsUsable()
Checks if the chart set contains at least one usable chart.
Represents a basemap chart based on shapefile data.
bool CrossesLand(double &lat1, double &lon1, double &lat2, double &lon2)
Determines if a line segment between two geographical points intersects any land mass represented in ...
void CancelLoading()
Cancel the chart loading operation.
int _dmod
Tile size in degrees.
bool LoadSHP()
Loads the shapefile data into memory.
ViewPort - Core geographic projection and coordinate transformation engine.
int pix_height
Height of the viewport in physical pixels.
int pix_width
Width of the viewport in physical pixels.
wxPoint2DDouble GetDoublePixFromLL(double lat, double lon)
Convert latitude and longitude on the ViewPort to physical pixel coordinates with double precision.
double chart_scale
Chart scale denominator (e.g., 50000 for a 1:50000 scale).
Device context class that can use either wxDC or OpenGL for drawing.
Global variables stored in configuration file.
OpenGL chart rendering canvas.
Enhanced logging interface on top of wx/log.h.
ShapeBaseChartSet gShapeBasemap
global instance
@ full
Full resolution of the OSM dataset.
@ crude
Planetary scale dataset.
@ medium
Medium resolution.