diff --git a/docs/外部参考.md b/docs/外部参考.md
index 1fa63ae..7a94e6a 100644
--- a/docs/外部参考.md
+++ b/docs/外部参考.md
@@ -1,6 +1,8 @@
# 外部参考
-本项目独立实现 CM93 importer,不复制 OpenCPN GPL 源码。
+本项目独立实现 CM93 importer;为保证 S-52 / CM93 对照审计可在不同机器复现,仓库内另行保存一份只读 OpenCPN GPL 上游源码快照,作为外部参考资料,不与 CM95 自有实现混合。
+
+固定参考快照:`docs/外部参考/OpenCPN源码基线_f7211b4e/`,上游 commit 为 `f7211b4e1874c453e37a7f190aa191c55a25c32e`,目录内保留原始版权声明、GPL 许可证和 SHA-256 校验清单。
用于核对行为和 MySQL 兼容性的公开参考:
diff --git a/docs/外部参考/OpenCPN源码基线_f7211b4e/上游提交.txt b/docs/外部参考/OpenCPN源码基线_f7211b4e/上游提交.txt
new file mode 100644
index 0000000..8edef98
--- /dev/null
+++ b/docs/外部参考/OpenCPN源码基线_f7211b4e/上游提交.txt
@@ -0,0 +1 @@
+f7211b4e1874c453e37a7f190aa191c55a25c32e
diff --git a/docs/外部参考/OpenCPN源码基线_f7211b4e/上游源码/gui/src/cm93.cpp b/docs/外部参考/OpenCPN源码基线_f7211b4e/上游源码/gui/src/cm93.cpp
new file mode 100644
index 0000000..4735ad7
--- /dev/null
+++ b/docs/外部参考/OpenCPN源码基线_f7211b4e/上游源码/gui/src/cm93.cpp
@@ -0,0 +1,6527 @@
+/**************************************************************************
+ * Copyright (C) 2010 by David S. Register *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, see . *
+ **************************************************************************/
+
+/**
+ * \file
+ *
+ * Implement cm93.h -- CM93 chart state
+ */
+
+#include
+#include
+#include
+
+// For compilers that support precompilation, includes "wx.h".
+#include "wx/wxprec.h"
+
+#ifndef WX_PRECOMP
+#include "wx/wx.h"
+#endif // precompiled headers
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#include "model/plugin_comm.h"
+#include "model/chartdata_input_stream.h"
+
+#include "chcanv.h"
+#include "cm93.h"
+#include "detail_slider.h"
+#include "gui_lib.h"
+#include "line_clip.h"
+#include "model/georef.h"
+#include "mygeom.h"
+#include "navutil.h" // for LogMessageOnce
+#include "ocpndc.h"
+#include "ocpn_pixel.h" // for ocpnUSE_DIBSECTION
+#include "ocpn_platform.h"
+#include "pluginmanager.h" // for PlugInManager
+#include "s52plib.h"
+#include "s52s57.h"
+#include "s57chart.h"
+#include "top_frame.h"
+
+#ifdef ocpnUSE_GL
+#include "gl_chart_canvas.h"
+#endif
+
+#ifdef __VISUALC__
+#include
+#endif
+
+extern s52plib *ps52plib;
+
+CM93OffsetDialog *g_pCM93OffsetDialog;
+
+#include
+WX_DEFINE_OBJARRAY(Array_Of_M_COVR_Desc);
+
+#include
+WX_DEFINE_LIST(List_Of_M_COVR_Desc);
+
+static int Get_CM93_CellIndex(double lat, double lon, int scale);
+
+void Get_CM93_Cell_Origin(int cellindex, int scale, double *lat, double *lon);
+
+void appendOSDirSep(wxString *pString) {
+ wxChar sep = wxFileName::GetPathSeparator();
+ if (pString->Last() != sep) pString->Append(sep);
+}
+
+//----------------------------------------------------------------------------
+// M_COVR_Desc Implementation
+//----------------------------------------------------------------------------
+
+M_COVR_Desc::M_COVR_Desc() {
+ pvertices = NULL;
+ gl_screen_vertices = NULL;
+ gl_screen_projection_type = PROJECTION_UNKNOWN;
+
+ user_xoff = 0.;
+ user_yoff = 0.;
+ m_centerlat_cos = 1.0;
+ m_buser_offsets = false;
+
+ m_ngl_vertices = 0;
+ gl_screen_vertices = NULL;
+}
+
+M_COVR_Desc::~M_COVR_Desc() {
+ delete[] pvertices;
+ delete[] gl_screen_vertices;
+}
+
+int M_COVR_Desc::GetWKBSize() {
+ int size = 0;
+
+ size = sizeof(int); // size itself
+ size += sizeof(int); // m_cell_index;
+ size += sizeof(int); // m_object_id;
+ size += sizeof(int); // m_subcell
+ size += sizeof(int); // m_nvertices;
+ size += m_nvertices * sizeof(float_2Dpt); // pvertices;
+
+ size += sizeof(int); // m_npub_year;
+ size += 8 * sizeof(double); // all the rest
+
+ return size;
+}
+
+bool M_COVR_Desc::WriteWKB(void *p) {
+ if (p) {
+ int *pr = (int *)p;
+ *pr++ = GetWKBSize();
+
+ *pr++ = m_cell_index;
+ *pr++ = m_object_id;
+ *pr++ = m_subcell;
+
+ *pr++ = m_nvertices;
+
+ float_2Dpt *pfo = (float_2Dpt *)pr;
+ float_2Dpt *pfi = pvertices;
+ for (int i = 0; i < m_nvertices; i++) *pfo++ = *pfi++;
+
+ int *pi = (int *)pfo;
+ *pi++ = m_npub_year;
+
+ double *pd = (double *)pi;
+ *pd++ = transform_WGS84_offset_x;
+ *pd++ = transform_WGS84_offset_y;
+ *pd++ = m_covr_lat_min;
+ *pd++ = m_covr_lat_max;
+ *pd++ = m_covr_lon_min;
+ *pd++ = m_covr_lon_max;
+
+ double centerlat_cos =
+ cos(((m_covr_lat_min + m_covr_lat_max) / 2.) * PI / 180.);
+
+ *pd++ = user_xoff * centerlat_cos;
+ *pd++ = user_yoff * centerlat_cos;
+ }
+
+ return true;
+}
+
+int M_COVR_Desc::ReadWKB(wxFFileInputStream &ifs) {
+ // Read the length of the WKB
+ int length = 0;
+ if (!ifs.Read(&length, sizeof(int)).Eof()) {
+ ifs.Read(&m_cell_index, sizeof(int));
+ ifs.Read(&m_object_id, sizeof(int));
+ ifs.Read(&m_subcell, sizeof(int));
+
+ ifs.Read(&m_nvertices, sizeof(int));
+
+ pvertices = new float_2Dpt[m_nvertices];
+
+ ifs.Read(pvertices, m_nvertices * sizeof(float_2Dpt));
+
+ ifs.Read(&m_npub_year, sizeof(int));
+
+ ifs.Read(&transform_WGS84_offset_x, sizeof(double));
+ ifs.Read(&transform_WGS84_offset_y, sizeof(double));
+ ifs.Read(&m_covr_lat_min, sizeof(double));
+ ifs.Read(&m_covr_lat_max, sizeof(double));
+ ifs.Read(&m_covr_lon_min, sizeof(double));
+ ifs.Read(&m_covr_lon_max, sizeof(double));
+
+ m_centerlat_cos = cos(((m_covr_lat_min + m_covr_lat_max) / 2.) * PI / 180.);
+
+ ifs.Read(&user_xoff, sizeof(double));
+ ifs.Read(&user_yoff, sizeof(double));
+
+ user_xoff /= m_centerlat_cos;
+ user_yoff /= m_centerlat_cos;
+
+ if ((fabs(user_xoff) > 1.) || (fabs(user_yoff) > 1.))
+ m_buser_offsets = true;
+ else
+ m_buser_offsets = false;
+
+ m_covr_bbox.Set(m_covr_lat_min, m_covr_lon_min, m_covr_lat_max,
+ m_covr_lon_max);
+ }
+ return length;
+}
+
+OCPNRegion M_COVR_Desc::GetRegion(const ViewPort &vp, wxPoint *pwp) {
+ float_2Dpt *p = pvertices;
+
+ for (int ip = 0; ip < m_nvertices; ip++) {
+ double plon = p->x;
+ if (fabs(plon - vp.clon) > 180.) {
+ if (plon > vp.clon)
+ plon -= 360.;
+ else
+ plon += 360.;
+ }
+
+ double easting, northing, epix, npix;
+ toSM(p->y, plon + 360., vp.clat, vp.clon + 360, &easting, &northing);
+
+ // easting -= transform_WGS84_offset_x;
+ easting -= user_xoff;
+ // northing -= transform_WGS84_offset_y;
+ northing -= user_yoff;
+
+ epix = easting * vp.view_scale_ppm;
+ npix = northing * vp.view_scale_ppm;
+
+ pwp[ip].x = (int)round((vp.pix_width / 2) + epix);
+ pwp[ip].y = (int)round((vp.pix_height / 2) - npix);
+
+ p++;
+ }
+
+ return OCPNRegion(m_nvertices, pwp);
+}
+
+//----------------------------------------------------------------------------
+// cm93 covr_set object class
+// This is a helper class which holds all the known information
+// relating to cm93 cell MCOVR objects of a particular scale
+//----------------------------------------------------------------------------
+
+char sig_version[] = "COVR1002";
+
+class covr_set {
+public:
+ covr_set(cm93chart *parent);
+ ~covr_set();
+
+ bool Init(wxChar scale_char, wxString &prefix);
+ unsigned int GetCoverCount() { return m_covr_array_outlines.GetCount(); }
+ M_COVR_Desc *GetCover(unsigned int im) { return &m_covr_array_outlines[im]; }
+ void Add_MCD(M_COVR_Desc *pmcd);
+ bool Add_Update_MCD(M_COVR_Desc *pmcd);
+ bool IsCovrLoaded(int cell_index);
+ int Find_MCD(M_COVR_Desc *pmcd);
+ M_COVR_Desc *Find_MCD(int cell_index, int object_id, int sbcell);
+
+ cm93chart *m_pParent;
+ wxChar m_scale_char;
+ int m_scale;
+
+ wxString m_cachefile;
+
+ // array, for chart outline rendering
+ Array_Of_M_COVR_Desc m_covr_array_outlines;
+
+ // This is a hash, indexed by cell index, elements
+ // contain the number of M_COVRs found on this particular cell
+ std::unordered_map m_cell_hash;
+};
+
+covr_set::covr_set(cm93chart *parent) { m_pParent = parent; }
+
+covr_set::~covr_set() {
+ // Create/Update the cache
+ if (m_cachefile.IsEmpty())
+ return; // presumably for Z scale charts
+ // for which we create no cache
+
+ if (m_covr_array_outlines.GetCount()) {
+ wxFFileOutputStream ofs(m_cachefile);
+ if (ofs.IsOk()) {
+ ofs.Write(sig_version, 8); // write signature
+
+ for (unsigned int i = 0; i < m_covr_array_outlines.GetCount(); i++) {
+ int wkbsize = m_covr_array_outlines[i].GetWKBSize();
+ if (wkbsize) {
+ char *p = (char *)malloc(wkbsize * sizeof(char));
+ m_covr_array_outlines[i].WriteWKB(p);
+ ofs.Write(p, wkbsize);
+ free(p);
+ }
+ }
+ ofs.Close();
+ }
+ }
+}
+
+bool covr_set::Init(wxChar scale_char, wxString &prefix) {
+ m_scale_char = scale_char;
+
+ switch (m_scale_char) {
+ case 'Z':
+ m_scale = 20000000;
+ break;
+ case 'A':
+ m_scale = 3000000;
+ break;
+ case 'B':
+ m_scale = 1000000;
+ break;
+ case 'C':
+ m_scale = 200000;
+ break;
+ case 'D':
+ m_scale = 100000;
+ break;
+ case 'E':
+ m_scale = 50000;
+ break;
+ case 'F':
+ m_scale = 20000;
+ break;
+ case 'G':
+ m_scale = 7500;
+ break;
+ default:
+ m_scale = 20000000;
+ break;
+ }
+
+ // Create the cache file name
+ wxString prefix_string = prefix;
+ wxString sep(wxFileName::GetPathSeparator());
+ prefix_string.Replace(sep, "_");
+ prefix_string.Replace(":", "_"); // for Windows
+
+ m_cachefile = g_Platform->GetPrivateDataDir();
+ appendOSDirSep(&m_cachefile);
+
+ m_cachefile += "cm93";
+ appendOSDirSep(&m_cachefile);
+
+ m_cachefile +=
+ prefix_string; // include the cm93 prefix string in the cache file name
+ m_cachefile += "_"; // to support multiple cm93 data sets
+
+ wxString cache_old_old_name = m_cachefile;
+ cache_old_old_name += "coverset.";
+ cache_old_old_name += m_scale_char;
+
+ wxString cache_old_name = m_cachefile;
+ cache_old_name += "coverset_sig.";
+ cache_old_name += m_scale_char;
+
+ m_cachefile += "coverset_sigp.";
+ m_cachefile += m_scale_char;
+
+ wxFileName fn(m_cachefile);
+ if (!fn.DirExists()) wxFileName::Mkdir(fn.GetPath(), 0777, wxPATH_MKDIR_FULL);
+
+ // Preload the cache
+ if (!wxFileName::FileExists(m_cachefile)) {
+ // The signed file does not exist
+ // Check for an old style file, and delete if found.
+ if (wxFileName::FileExists(cache_old_name)) ::wxRemoveFile(cache_old_name);
+ if (wxFileName::FileExists(cache_old_old_name))
+ ::wxRemoveFile(cache_old_old_name);
+ return false;
+ }
+
+ wxFFileInputStream ifs(m_cachefile);
+ if (ifs.IsOk()) {
+ char sig_bytes[9];
+ // Validate the file signature
+ if (!ifs.Read(&sig_bytes, 8).Eof()) {
+ if (strncmp(sig_bytes, sig_version, 8)) {
+ return false; // bad signature match
+ }
+ } else
+ return false; // short file
+
+ bool b_cont = true;
+ while (b_cont) {
+ M_COVR_Desc *pmcd = new M_COVR_Desc;
+ int length = pmcd->ReadWKB(ifs);
+
+ if (length) {
+ m_covr_array_outlines.Add(pmcd);
+
+ if (m_cell_hash.find(pmcd->m_cell_index) == m_cell_hash.end())
+ m_cell_hash[pmcd->m_cell_index] = 0; // initialize the element
+
+ m_cell_hash[pmcd->m_cell_index]++; // add this M_COVR to the hash map
+
+ } else {
+ delete pmcd;
+ b_cont = false;
+ }
+ }
+ }
+
+ return true;
+}
+
+void covr_set::Add_MCD(M_COVR_Desc *pmcd) {
+ m_covr_array_outlines.Add(pmcd);
+
+ if (m_cell_hash.find(pmcd->m_cell_index) ==
+ m_cell_hash.end()) // not present yet?
+ m_cell_hash[pmcd->m_cell_index] = 0; // initialize
+
+ m_cell_hash[pmcd->m_cell_index]++; // add this M_COVR to the hash map
+}
+
+bool covr_set::IsCovrLoaded(int cell_index) {
+ return (m_cell_hash.find(cell_index) != m_cell_hash.end());
+}
+
+bool covr_set::Add_Update_MCD(M_COVR_Desc *pmcd) {
+ if (m_cell_hash.find(pmcd->m_cell_index) ==
+ m_cell_hash.end()) // not present yet?
+ {
+ m_covr_array_outlines.Add(pmcd);
+ m_cell_hash[pmcd->m_cell_index] = 1; // initialize
+ return true;
+ }
+ // There is at least one MCD already in place for this cell index
+ // We need to search the entire table to see if any of those MCD's
+ // correspond to this MCD's object identifier and subcell, as well as cell
+ // index
+ else {
+ bool b_found = false;
+ for (unsigned int i = 0; i < m_covr_array_outlines.GetCount(); i++) {
+ M_COVR_Desc *pmcd_candidate = &m_covr_array_outlines[i];
+ if ((pmcd_candidate->m_cell_index == pmcd->m_cell_index) &&
+ (pmcd_candidate->m_object_id == pmcd->m_object_id) &&
+ (pmcd_candidate->m_subcell == pmcd->m_subcell))
+
+ {
+ b_found = true;
+ break;
+ }
+ }
+
+ if (!b_found) {
+ m_covr_array_outlines.Add(pmcd);
+ m_cell_hash[pmcd->m_cell_index]++; // add this M_COVR to the hash map
+ return true;
+ } else
+ return false;
+ }
+}
+
+int covr_set::Find_MCD(M_COVR_Desc *pmcd) {
+ if (m_cell_hash.find(pmcd->m_cell_index) ==
+ m_cell_hash.end()) // not present?
+ return -1;
+ else {
+ // There is at least one MCD already in place for this cell index
+ // We need to search the entire table to see if any of those MCD's
+ // correspond to this MCD's object identifier as well as cell index
+
+ for (unsigned int i = 0; i < m_covr_array_outlines.GetCount(); i++) {
+ M_COVR_Desc *pmcd_candidate = &m_covr_array_outlines[i];
+ if ((pmcd_candidate->m_cell_index == pmcd->m_cell_index) &&
+ (pmcd_candidate->m_object_id == pmcd->m_object_id) &&
+ (pmcd_candidate->m_subcell == pmcd->m_subcell)) {
+ return (int)i;
+ }
+ }
+ }
+ return -1;
+}
+
+M_COVR_Desc *covr_set::Find_MCD(int cell_index, int object_id, int subcell) {
+ if (m_cell_hash.find(cell_index) == m_cell_hash.end()) // not present?
+ return NULL;
+
+ for (unsigned int i = 0; i < m_covr_array_outlines.GetCount(); i++) {
+ M_COVR_Desc *pmcd_candidate = &m_covr_array_outlines[i];
+ if ((pmcd_candidate->m_cell_index == cell_index) &&
+ (pmcd_candidate->m_object_id == object_id) &&
+ (pmcd_candidate->m_subcell == subcell))
+
+ return pmcd_candidate;
+ }
+
+ return NULL;
+}
+
+// CM93 Encode/Decode support tables
+
+static unsigned char Table_0[] = {
+ 0x0CD, 0x0EA, 0x0DC, 0x048, 0x03E, 0x06D, 0x0CA, 0x07B, 0x052, 0x0E1, 0x0A4,
+ 0x08E, 0x0AB, 0x005, 0x0A7, 0x097, 0x0B9, 0x060, 0x039, 0x085, 0x07C, 0x056,
+ 0x07A, 0x0BA, 0x068, 0x06E, 0x0F5, 0x05D, 0x002, 0x04E, 0x00F, 0x0A1, 0x027,
+ 0x024, 0x041, 0x034, 0x000, 0x05A, 0x0FE, 0x0CB, 0x0D0, 0x0FA, 0x0F8, 0x06C,
+ 0x074, 0x096, 0x09E, 0x00E, 0x0C2, 0x049, 0x0E3, 0x0E5, 0x0C0, 0x03B, 0x059,
+ 0x018, 0x0A9, 0x086, 0x08F, 0x030, 0x0C3, 0x0A8, 0x022, 0x00A, 0x014, 0x01A,
+ 0x0B2, 0x0C9, 0x0C7, 0x0ED, 0x0AA, 0x029, 0x094, 0x075, 0x00D, 0x0AC, 0x00C,
+ 0x0F4, 0x0BB, 0x0C5, 0x03F, 0x0FD, 0x0D9, 0x09C, 0x04F, 0x0D5, 0x084, 0x01E,
+ 0x0B1, 0x081, 0x069, 0x0B4, 0x009, 0x0B8, 0x03C, 0x0AF, 0x0A3, 0x008, 0x0BF,
+ 0x0E0, 0x09A, 0x0D7, 0x0F7, 0x08C, 0x067, 0x066, 0x0AE, 0x0D4, 0x04C, 0x0A5,
+ 0x0EC, 0x0F9, 0x0B6, 0x064, 0x078, 0x006, 0x05B, 0x09B, 0x0F2, 0x099, 0x0CE,
+ 0x0DB, 0x053, 0x055, 0x065, 0x08D, 0x007, 0x033, 0x004, 0x037, 0x092, 0x026,
+ 0x023, 0x0B5, 0x058, 0x0DA, 0x02F, 0x0B3, 0x040, 0x05E, 0x07F, 0x04B, 0x062,
+ 0x080, 0x0E4, 0x06F, 0x073, 0x01D, 0x0DF, 0x017, 0x0CC, 0x028, 0x025, 0x02D,
+ 0x0EE, 0x03A, 0x098, 0x0E2, 0x001, 0x0EB, 0x0DD, 0x0BC, 0x090, 0x0B0, 0x0FC,
+ 0x095, 0x076, 0x093, 0x046, 0x057, 0x02C, 0x02B, 0x050, 0x011, 0x00B, 0x0C1,
+ 0x0F0, 0x0E7, 0x0D6, 0x021, 0x031, 0x0DE, 0x0FF, 0x0D8, 0x012, 0x0A6, 0x04D,
+ 0x08A, 0x013, 0x043, 0x045, 0x038, 0x0D2, 0x087, 0x0A0, 0x0EF, 0x082, 0x0F1,
+ 0x047, 0x089, 0x06A, 0x0C8, 0x054, 0x01B, 0x016, 0x07E, 0x079, 0x0BD, 0x06B,
+ 0x091, 0x0A2, 0x071, 0x036, 0x0B7, 0x003, 0x03D, 0x072, 0x0C6, 0x044, 0x08B,
+ 0x0CF, 0x015, 0x09F, 0x032, 0x0C4, 0x077, 0x083, 0x063, 0x020, 0x088, 0x0F6,
+ 0x0AD, 0x0F3, 0x0E8, 0x04A, 0x0E9, 0x035, 0x01C, 0x05F, 0x019, 0x01F, 0x07D,
+ 0x070, 0x0FB, 0x0D1, 0x051, 0x010, 0x0D3, 0x02E, 0x061, 0x09D, 0x05C, 0x02A,
+ 0x042, 0x0BE, 0x0E6};
+
+static unsigned char Encode_table[256];
+static unsigned char Decode_table[256];
+
+static bool cm93_decode_table_created;
+
+// Case-insensitive cm93 directory tree depth-first traversal to find the
+// dictionary... This could be made simpler, but matches the old code better as
+// is
+class FindCM93Dictionary : public wxDirTraverser {
+public:
+ FindCM93Dictionary(wxString &path) : m_path(path) {}
+
+ virtual wxDirTraverseResult OnFile(const wxString &filename) {
+ wxString name = filename.AfterLast(wxFileName::GetPathSeparator()).Lower();
+ if (name == "cm93obj.dic") {
+ m_path = filename;
+ return wxDIR_STOP;
+ }
+
+ return wxDIR_CONTINUE;
+ }
+
+ virtual wxDirTraverseResult OnDir(const wxString &WXUNUSED(dirname)) {
+ return wxDIR_CONTINUE;
+ }
+
+private:
+ wxString &m_path;
+};
+
+cm93_dictionary::cm93_dictionary() {
+ m_S57ClassArray = NULL;
+ m_AttrArray = NULL;
+ m_GeomTypeArray = NULL;
+ ;
+ m_ValTypeArray = NULL;
+ m_max_class = 0;
+ m_ok = false;
+}
+
+bool cm93_dictionary::LoadDictionary(const wxString &dictionary_dir) {
+ int i, nline;
+ wxString line;
+ wxString dir(dictionary_dir); // a copy
+ bool ret_val = false;
+
+ wxChar sep = wxFileName::GetPathSeparator();
+ if (dir.Last() != sep) dir.Append(sep);
+
+ m_dict_dir = dir;
+
+ // Build some array strings for Feature decoding
+
+ wxString sf(dir);
+ sf.Append("CM93OBJ.DIC");
+
+ if (!wxFileName::FileExists(sf)) {
+ sf = dir;
+ sf.Append("cm93obj.dic");
+ if (!wxFileName::FileExists(sf)) return false;
+ }
+
+ wxTextFile file;
+ if (!file.Open(sf)) return false;
+
+ nline = file.GetLineCount();
+
+ if (!nline) return false;
+
+ // Read the file once to get the max class number
+ int iclass_max = 0;
+
+ for (i = 0; i < nline; i++) {
+ line = file.GetLine(i);
+
+ wxStringTokenizer tkz(line, "|");
+ // while ( tkz.HasMoreTokens() )
+ {
+ // 6 char class name
+ wxString class_name = tkz.GetNextToken();
+
+ // class number, ascii
+ wxString token = tkz.GetNextToken();
+ long liclass;
+ token.ToLong(&liclass);
+ int iclass = liclass;
+ if (iclass > iclass_max) iclass_max = iclass;
+
+ // geom type, ascii
+ wxString geo_type = tkz.GetNextToken();
+ }
+ }
+
+ m_max_class = iclass_max;
+
+ // Create the class name array
+ m_S57ClassArray = new wxArrayString;
+ m_S57ClassArray->Add("NULLNM", iclass_max + 1);
+
+ // And an array of ints describing the geometry type per class
+ m_GeomTypeArray = (int *)malloc((iclass_max + 1) * sizeof(int));
+
+ // Iterate over the file, filling in the values
+ for (i = 0; i < nline; i++) {
+ line = file.GetLine(i);
+
+ wxStringTokenizer tkz(line, "|");
+ // while ( tkz.HasMoreTokens() )
+ {
+ // 6 char class name
+ wxString class_name = tkz.GetNextToken();
+
+ // class number, ascii
+ wxString token = tkz.GetNextToken();
+ long liclass;
+ token.ToLong(&liclass);
+ int iclass = liclass;
+
+ // geom type, ascii
+ wxString geo_type = tkz.GetNextToken();
+
+ m_S57ClassArray->Insert(class_name, iclass);
+ m_S57ClassArray->RemoveAt(iclass + 1);
+
+ int igeom_type = -1; // default unknown
+ wxChar geo_type_primary = geo_type[0];
+
+ if (geo_type_primary == 'A')
+ igeom_type = 3;
+ else if (geo_type_primary == 'L')
+ igeom_type = 2;
+ else if (geo_type_primary == 'P')
+ igeom_type = 1;
+
+ // Note: there are other types in the file, e.g. 'C'. Dunno what this
+ // is Also, some object classes want multiple geometries, like PA, PLA,
+ // etc. Take only primary, ignore the rest
+
+ m_GeomTypeArray[iclass] = igeom_type;
+ }
+ }
+ file.Close();
+
+ // Build some array strings for Attribute decoding
+
+ wxString sfa(dir);
+ sfa.Append("ATTRLUT.DIC");
+
+ if (!wxFileName::FileExists(sfa)) {
+ sfa = dir;
+ sfa.Append("attrlut.dic");
+ }
+
+ if (wxFileName::FileExists(sfa)) {
+ wxFFileInputStream filea(sfa);
+
+ if (filea.IsOk()) {
+ // Read the file once to get the max attr number
+ int iattr_max = 0;
+
+ while (!filea.Eof()) {
+ // read a line
+ line.Empty();
+ while (1) {
+ char a = filea.GetC();
+ if (filea.Eof()) break;
+ line.Append(a);
+ if (a == 0x0a) break;
+ }
+
+ if (!line.StartsWith((const wxChar *)";")) {
+ wxStringTokenizer tkz(line, "|");
+ {
+ // 6 attribute label
+ wxString class_name = tkz.GetNextToken();
+
+ // attribute number, ascii
+ wxString token = tkz.GetNextToken();
+ long liattr;
+ token.ToLong(&liattr);
+ int iattr = liattr;
+ if (iattr > iattr_max) iattr_max = iattr;
+ }
+ }
+ }
+
+ m_max_attr = iattr_max;
+
+ filea.SeekI(0);
+
+ // Create the attribute label array
+
+ m_AttrArray = new wxArrayString;
+ m_AttrArray->Add("NULLNM", iattr_max + 1);
+
+ // And an array of chars describing the attribute value type
+ m_ValTypeArray = (char *)malloc((iattr_max + 1) * sizeof(char));
+
+ // Iterate over the file, filling in the values
+ while (!filea.Eof()) {
+ // read a line
+ line.Empty();
+ while (1) {
+ char a = filea.GetC();
+ if (filea.Eof()) break;
+ line.Append(a);
+ if (a == 0x0a) break;
+ }
+
+ if (!line.StartsWith((const wxChar *)";")) {
+ wxStringTokenizer tkz(line, "|");
+ {
+ // 6 char class name
+ wxString attr_name = tkz.GetNextToken();
+
+ // class number, ascii
+ wxString token = tkz.GetNextToken();
+ long liattr;
+ token.ToLong(&liattr);
+ int iattr = liattr;
+
+ m_AttrArray->Insert(attr_name, iattr);
+ m_AttrArray->RemoveAt(iattr + 1);
+
+ // Skip some
+ token = tkz.GetNextToken();
+ token = tkz.GetNextToken();
+ token = tkz.GetNextToken();
+ token = tkz.GetNextToken().Trim();
+
+ char atype = '?';
+ if (token.IsSameAs("aFLOAT"))
+ atype = 'R';
+ else if (token.IsSameAs("aBYTE"))
+ atype = 'B';
+ else if (token.IsSameAs("aSTRING"))
+ atype = 'S';
+ else if (token.IsSameAs("aCMPLX"))
+ atype = 'C';
+ else if (token.IsSameAs("aLIST"))
+ atype = 'L';
+ else if (token.IsSameAs("aWORD10"))
+ atype = 'W';
+ else if (token.IsSameAs("aLONG"))
+ atype = 'G';
+
+ m_ValTypeArray[iattr] = atype;
+ }
+ }
+ }
+ ret_val = true;
+ } else // stream IsOK
+ {
+ ret_val = false;
+ }
+ }
+
+ else // Look for alternate file
+ {
+ sfa = dir;
+ sfa.Append("CM93ATTR.DIC");
+
+ if (!wxFileName::FileExists(sfa)) {
+ sfa = dir;
+ sfa.Append("cm93attr.dic");
+ }
+
+ if (wxFileName::FileExists(sfa)) {
+ wxFFileInputStream filea(sfa);
+
+ if (filea.IsOk()) {
+ // Read the file once to get the max attr number
+ int iattr_max = 0;
+
+ while (!filea.Eof()) {
+ // read a line
+ line.Empty();
+ while (1) {
+ char a = filea.GetC();
+ if (filea.Eof()) break;
+ line.Append(a);
+ if (a == 0x0a) break;
+ }
+
+ if (line[0] != ';') {
+ wxStringTokenizer tkz(line, "|");
+ if (tkz.CountTokens()) {
+ // 6 attribute label
+ wxString class_name = tkz.GetNextToken();
+
+ // attribute number, ascii
+ wxString token = tkz.GetNextToken();
+ long liattr;
+ token.ToLong(&liattr);
+ int iattr = liattr;
+ if (iattr > iattr_max) iattr_max = iattr;
+ }
+ }
+ }
+
+ m_max_attr = iattr_max;
+
+ filea.SeekI(0);
+
+ // Create the attribute label array
+
+ m_AttrArray = new wxArrayString;
+ m_AttrArray->Add("NULLNM", iattr_max + 1);
+
+ // And an array of chars describing the attribute value type
+ m_ValTypeArray = (char *)malloc((iattr_max + 1) * sizeof(char));
+ for (int iat = 0; iat < iattr_max + 1; iat++) m_ValTypeArray[iat] = '?';
+
+ // Iterate over the file, filling in the values
+ while (!filea.Eof()) {
+ // read a line
+ line.Empty();
+ while (1) {
+ char a = filea.GetC();
+ if (filea.Eof()) break;
+ line.Append(a);
+ if (a == 0x0a) break;
+ }
+
+ if (line[0] != ';') {
+ wxStringTokenizer tkz(line, "|\r\n");
+ if (tkz.CountTokens() >= 3) {
+ // 6 char class name
+ wxString attr_name = tkz.GetNextToken();
+
+ // class number, ascii
+ wxString token = tkz.GetNextToken();
+ long liattr;
+ token.ToLong(&liattr);
+ int iattr = liattr;
+
+ m_AttrArray->Insert(attr_name, iattr);
+ m_AttrArray->RemoveAt(iattr + 1);
+
+ token = tkz.GetNextToken().Trim();
+
+ char atype = '?';
+ if (token.IsSameAs("aFLOAT"))
+ atype = 'R';
+ else if (token.IsSameAs("aBYTE"))
+ atype = 'B';
+ else if (token.IsSameAs("aSTRING"))
+ atype = 'S';
+ else if (token.IsSameAs("aCMPLX"))
+ atype = 'C';
+ else if (token.IsSameAs("aLIST"))
+ atype = 'L';
+ else if (token.IsSameAs("aWORD10"))
+ atype = 'W';
+ else if (token.IsSameAs("aLONG"))
+ atype = 'G';
+
+ m_ValTypeArray[iattr] = atype;
+ }
+ }
+ }
+ ret_val = true;
+ } else // stream IsOK
+ ret_val = false;
+ }
+ }
+
+ if (ret_val) {
+ m_ok = true;
+
+ wxString msg("Loaded CM93 Dictionary from ");
+ msg.Append(dir);
+ wxLogMessage(msg);
+ }
+
+ return ret_val;
+}
+
+wxString cm93_dictionary::GetClassName(int iclass) {
+ if ((iclass > m_max_class) || (iclass < 0))
+ return ("Unknown");
+ else
+ return (m_S57ClassArray->Item(iclass));
+}
+
+wxString cm93_dictionary::GetAttrName(int iattr) {
+ if ((iattr > m_max_attr) || (iattr < 0))
+ return ("UnknownAttr");
+ else
+ return (m_AttrArray->Item(iattr));
+}
+
+// char vtype = m_pDict->m_ValTypeArray[iattr];
+char cm93_dictionary::GetAttrType(int iattr) {
+ if ((iattr > m_max_attr) || (iattr < 0))
+ return ('?');
+ else
+ return (m_ValTypeArray[iattr]);
+}
+
+cm93_dictionary::~cm93_dictionary() {
+ delete m_S57ClassArray;
+ free(m_GeomTypeArray);
+ delete m_AttrArray;
+ free(m_ValTypeArray);
+}
+
+// CM93 Decode support routines
+
+void CreateDecodeTable() {
+ int i;
+ for (i = 0; i < 256; i++) {
+ Encode_table[i] = Table_0[i] ^ 8;
+ }
+
+ for (i = 0; i < 256; i++) {
+ unsigned char a = Encode_table[i];
+ Decode_table[(int)a] = (unsigned char)i;
+ }
+}
+
+static int read_and_decode_bytes(FILE *stream, void *p, int nbytes) {
+ if (0 == nbytes) // declare victory if no bytes requested
+ return 1;
+
+ // read into callers buffer
+ if (fread(p, nbytes, 1, stream) != 1) return 0;
+
+ // decode inplace
+ unsigned char *q = (unsigned char *)p;
+
+ for (int i = 0; i < nbytes; i++) {
+ unsigned char a = *q;
+ int b = a;
+ unsigned char c = Decode_table[b];
+ *q = c;
+
+ q++;
+ }
+ return 1;
+}
+
+static int read_and_decode_double(FILE *stream, double *p) {
+ double t;
+ // read into temp buffer
+ if (fread(&t, sizeof(double), 1, stream) != 1) return 0;
+
+ // decode inplace
+ unsigned char *q = (unsigned char *)&t;
+
+ for (unsigned int i = 0; i < sizeof(double); i++) {
+ unsigned char a = *q;
+ int b = a;
+ unsigned char c = Decode_table[b];
+ *q = c;
+
+ q++;
+ }
+
+ // copy to target
+ *p = t;
+
+ return 1;
+}
+
+static int read_and_decode_int(FILE *stream, int *p) {
+ int t;
+ // read into temp buffer
+ if (fread(&t, sizeof(int), 1, stream) != 1) return 0;
+
+ // decode inplace
+ unsigned char *q = (unsigned char *)&t;
+
+ for (unsigned int i = 0; i < sizeof(int); i++) {
+ unsigned char a = *q;
+ int b = a;
+ unsigned char c = Decode_table[b];
+ *q = c;
+
+ q++;
+ }
+
+ // copy to target
+ *p = t;
+
+ return 1;
+}
+
+static int read_and_decode_ushort(FILE *stream, unsigned short *p) {
+ unsigned short t;
+ // read into temp buffer
+ if (fread(&t, sizeof(unsigned short), 1, stream) != 1) return 0;
+
+ // decode inplace
+ unsigned char *q = (unsigned char *)&t;
+
+ for (unsigned int i = 0; i < sizeof(unsigned short); i++) {
+ unsigned char a = *q;
+ int b = a;
+ unsigned char c = Decode_table[b];
+ *q = c;
+
+ q++;
+ }
+
+ // copy to target
+ *p = t;
+
+ return 1;
+}
+
+// Calculate the CM93 CellIndex integer for a given Lat/Lon, at a given scale
+
+int Get_CM93_CellIndex(double lat, double lon, int scale) {
+ int retval = 0;
+
+ int dval;
+ switch (scale) {
+ case 20000000:
+ dval = 120;
+ break; // Z
+ case 3000000:
+ dval = 60;
+ break; // A
+ case 1000000:
+ dval = 30;
+ break; // B
+ case 200000:
+ dval = 12;
+ break; // C
+ case 100000:
+ dval = 3;
+ break; // D
+ case 50000:
+ dval = 1;
+ break; // E
+ case 20000:
+ dval = 1;
+ break; // F
+ case 7500:
+ dval = 1;
+ break; // G
+ default:
+ dval = 1;
+ break;
+ }
+
+ // Longitude
+ double lon1 = (lon + 360.) * 3.; // basic cell size is 20 minutes
+ while (lon1 >= 1080.0) lon1 -= 1080.0;
+ unsigned short lon2 = (unsigned short)floor(lon1 / dval); // normalize
+ unsigned short lon3 = lon2 * dval;
+
+ retval = lon3;
+
+ // Latitude
+ double lat1 = (lat * 3.) + 270. - 30;
+ unsigned short lat2 = (unsigned short)floor(lat1 / dval); // normalize
+ unsigned short lat3 = lat2 * dval;
+
+ retval += (lat3 + 30) * 10000;
+
+ return retval;
+}
+
+// Calculate the Lat/Lon of the lower left corner of a CM93 cell,
+// given a CM93 CellIndex and scale
+// Returned longitude value is always > 0
+void Get_CM93_Cell_Origin(int cellindex, int scale, double *lat, double *lon) {
+ // Longitude
+ double idx1 = cellindex % 10000;
+ double lont = (idx1 / 3.);
+
+ *lon = lont;
+
+ // Latitude
+ int idx2 = cellindex / 10000;
+ double lat1 = idx2 - 270.;
+ *lat = lat1 / 3.;
+}
+
+// Answer the query: "Is there a cm93 cell at the specified scale which
+// contains a given lat/lon?"
+bool Is_CM93Cell_Present(wxString &fileprefix, double lat, double lon,
+ int scale_index) {
+ int scale;
+ int dval;
+ wxChar scale_char;
+
+ switch (scale_index) {
+ case 0:
+ scale = 20000000;
+ dval = 120;
+ scale_char = 'Z';
+ break; // Z
+ case 1:
+ scale = 3000000;
+ dval = 60;
+ scale_char = 'A';
+ break; // A
+ case 2:
+ scale = 1000000;
+ dval = 30;
+ scale_char = 'B';
+ break; // B
+ case 3:
+ scale = 200000;
+ dval = 12;
+ scale_char = 'C';
+ break; // C
+ case 4:
+ scale = 100000;
+ dval = 3;
+ scale_char = 'D';
+ break; // D
+ case 5:
+ scale = 50000;
+ dval = 1;
+ scale_char = 'E';
+ break; // E
+ case 6:
+ scale = 20000;
+ dval = 1;
+ scale_char = 'F';
+ break; // F
+ case 7:
+ scale = 7500;
+ dval = 1;
+ scale_char = 'G';
+ break; // G
+ default:
+ scale = 20000000;
+ dval = 120;
+ scale_char = ' ';
+ break;
+ }
+
+ int cellindex = Get_CM93_CellIndex(lat, lon, scale);
+
+ // Create the file name
+ wxString file;
+
+ int ilat = cellindex / 10000;
+ int ilon = cellindex % 10000;
+
+ int jlat = (((ilat - 30) / dval) * dval) + 30; // normalize
+ int jlon = (ilon / dval) * dval;
+
+ int ilatroot = (((ilat - 30) / 60) * 60) + 30;
+ int ilonroot = (ilon / 60) * 60;
+
+ wxString fileroot;
+ fileroot.Printf("%04d%04d", ilatroot, ilonroot);
+ appendOSDirSep(&fileroot);
+
+ wxString sdir(fileprefix);
+ sdir += fileroot;
+ sdir += scale_char;
+
+ wxString tfile;
+ tfile.Printf("?%03d%04d.", jlat, jlon);
+ tfile += scale_char;
+
+ // Validate that the directory exists, adjusting case if necessary
+ if (!::wxDirExists(sdir)) {
+ wxString old_scalechar(scale_char);
+ wxString new_scalechar = old_scalechar.Lower();
+
+ sdir = fileprefix;
+ sdir += fileroot;
+ sdir += new_scalechar;
+ }
+
+ if (::wxDirExists(sdir)) {
+ wxDir dir(sdir);
+
+ wxArrayString file_array;
+ int n_files = dir.GetAllFiles(sdir, &file_array, tfile, wxDIR_FILES);
+
+ if (n_files) return true;
+
+ // Try with alternate case of m_scalechar
+ wxString old_scalechar(scale_char);
+ wxString new_scalechar = old_scalechar.Lower();
+
+ wxString tfile1;
+ tfile1.Printf("?%03d%04d.", jlat, jlon);
+ tfile1 += new_scalechar;
+
+ int n_files1 = dir.GetAllFiles(sdir, &file_array, tfile1, wxDIR_FILES);
+
+ if (n_files1) return true;
+
+ // try compressed
+ n_files = dir.GetAllFiles(sdir, &file_array, tfile + ".xz", wxDIR_FILES);
+
+ if (n_files) return true;
+ }
+
+ return false;
+}
+
+static bool read_header_and_populate_cib(FILE *stream, Cell_Info_Block *pCIB) {
+ // Read header, populate Cell_Info_Block
+
+ // This 128 byte block is read element-by-element, to allow for
+ // endian-ness correction by element.
+ // Unused elements are read and, well, unused.
+
+ header_struct header;
+
+ memset((void *)&header, 0, sizeof(header));
+
+ read_and_decode_double(stream, &header.lon_min);
+ read_and_decode_double(stream, &header.lat_min);
+ read_and_decode_double(stream, &header.lon_max);
+ read_and_decode_double(stream, &header.lat_max);
+
+ read_and_decode_double(stream, &header.easting_min);
+ read_and_decode_double(stream, &header.northing_min);
+ read_and_decode_double(stream, &header.easting_max);
+ read_and_decode_double(stream, &header.northing_max);
+
+ read_and_decode_ushort(stream, &header.usn_vector_records);
+ read_and_decode_int(stream, &header.n_vector_record_points);
+ read_and_decode_int(stream, &header.m_46);
+ read_and_decode_int(stream, &header.m_4a);
+ read_and_decode_ushort(stream, &header.usn_point3d_records);
+ read_and_decode_int(stream, &header.m_50);
+ read_and_decode_int(stream, &header.m_54);
+ read_and_decode_ushort(stream, &header.usn_point2d_records);
+ read_and_decode_ushort(stream, &header.m_5a);
+ read_and_decode_ushort(stream, &header.m_5c);
+ read_and_decode_ushort(stream, &header.usn_feature_records);
+
+ read_and_decode_int(stream, &header.m_60);
+ read_and_decode_int(stream, &header.m_64);
+ read_and_decode_ushort(stream, &header.m_68);
+ read_and_decode_ushort(stream, &header.m_6a);
+ read_and_decode_ushort(stream, &header.m_6c);
+ read_and_decode_int(stream, &header.m_nrelated_object_pointers);
+
+ read_and_decode_int(stream, &header.m_72);
+ read_and_decode_ushort(stream, &header.m_76);
+
+ read_and_decode_int(stream, &header.m_78);
+ read_and_decode_int(stream, &header.m_7c);
+
+ // Calculate and record the cell coordinate transform coefficients
+
+ double delta_x = header.easting_max - header.easting_min;
+ if (delta_x < 0)
+ delta_x += CM93_semimajor_axis_meters * 2.0 * PI; // add one trip around
+
+ pCIB->transform_x_rate = delta_x / 65535;
+ pCIB->transform_y_rate = (header.northing_max - header.northing_min) / 65535;
+
+ pCIB->transform_x_origin = header.easting_min;
+ pCIB->transform_y_origin = header.northing_min;
+
+ pCIB->min_lat = header.lat_min;
+ pCIB->min_lon = header.lon_min;
+
+ // pCIB->m_cell_mcovr_array.Empty();
+
+ // Extract some table sizes from the header, and pre-allocate the tables
+ // We do it this way to avoid incremental realloc() calls, which are
+ // expensive
+
+ pCIB->m_nfeature_records = header.usn_feature_records;
+ pCIB->pobject_block =
+ (Object *)calloc(pCIB->m_nfeature_records * sizeof(Object), 1);
+
+ pCIB->m_n_point2d_records = header.usn_point2d_records;
+ pCIB->p2dpoint_array =
+ (cm93_point *)malloc(pCIB->m_n_point2d_records * sizeof(cm93_point));
+
+ pCIB->pprelated_object_block =
+ (Object **)malloc(header.m_nrelated_object_pointers * sizeof(Object *));
+
+ pCIB->object_vector_record_descriptor_block =
+ (vector_record_descriptor *)malloc((header.m_4a + header.m_46) *
+ sizeof(vector_record_descriptor));
+
+ pCIB->attribute_block_top = (unsigned char *)calloc(header.m_78, 1);
+
+ pCIB->m_nvector_records = header.usn_vector_records;
+ pCIB->edge_vector_descriptor_block = (geometry_descriptor *)malloc(
+ header.usn_vector_records * sizeof(geometry_descriptor));
+
+ pCIB->pvector_record_block_top =
+ (cm93_point *)malloc(header.n_vector_record_points * sizeof(cm93_point));
+
+ pCIB->m_n_point3d_records = header.usn_point3d_records;
+ pCIB->point3d_descriptor_block = (geometry_descriptor *)malloc(
+ pCIB->m_n_point3d_records * sizeof(geometry_descriptor));
+
+ pCIB->p3dpoint_array =
+ (cm93_point_3d *)malloc(header.m_50 * sizeof(cm93_point_3d));
+
+ return true;
+}
+
+static bool read_vector_record_table(FILE *stream, int count,
+ Cell_Info_Block *pCIB) {
+ bool brv;
+
+ geometry_descriptor *p = pCIB->edge_vector_descriptor_block;
+ cm93_point *q = pCIB->pvector_record_block_top;
+
+ for (int iedge = 0; iedge < count; iedge++) {
+ p->index = iedge;
+
+ unsigned short npoints;
+ brv = !(read_and_decode_ushort(stream, &npoints) == 0);
+ if (!brv) return false;
+
+ p->n_points = npoints;
+ p->p_points = q;
+
+ // brv = read_and_decode_bytes(stream, q, p->n_points *
+ // sizeof(cm93_point));
+ // if(!brv)
+ // return false;
+
+ unsigned short x, y;
+ for (int index = 0; index < p->n_points; index++) {
+ if (!read_and_decode_ushort(stream, &x)) return false;
+ if (!read_and_decode_ushort(stream, &y)) return false;
+
+ q[index].x = x;
+ q[index].y = y;
+ }
+
+ // Compute and store the min/max of this block of n_points
+ cm93_point *t = p->p_points;
+
+ p->x_max = t->x;
+ p->x_min = t->x;
+ p->y_max = t->y;
+ p->y_min = t->y;
+
+ t++;
+
+ for (int j = 0; j < p->n_points - 1; j++) {
+ if (t->x >= p->x_max) p->x_max = t->x;
+
+ if (t->x <= p->x_min) p->x_min = t->x;
+
+ if (t->y >= p->y_max) p->y_max = t->y;
+
+ if (t->y <= p->y_max) p->y_min = t->y;
+
+ t++;
+ }
+
+ // Advance the block pointer
+ q += p->n_points;
+
+ // Advance the geometry descriptor pointer
+ p++;
+ }
+
+ return true;
+}
+
+static bool read_3dpoint_table(FILE *stream, int count, Cell_Info_Block *pCIB) {
+ geometry_descriptor *p = pCIB->point3d_descriptor_block;
+ cm93_point_3d *q = pCIB->p3dpoint_array;
+
+ for (int i = 0; i < count; i++) {
+ unsigned short npoints;
+ if (!read_and_decode_ushort(stream, &npoints)) return false;
+
+ p->n_points = npoints;
+ p->p_points = (cm93_point *)q; // might not be the right cast
+
+ // unsigned short t = p->n_points;
+
+ // if(!read_and_decode_bytes(stream, q, t*6))
+ // return false;
+
+ unsigned short x, y, z;
+ for (int index = 0; index < p->n_points; index++) {
+ if (!read_and_decode_ushort(stream, &x)) return false;
+ if (!read_and_decode_ushort(stream, &y)) return false;
+ if (!read_and_decode_ushort(stream, &z)) return false;
+
+ q[index].x = x;
+ q[index].y = y;
+ q[index].z = z;
+ }
+
+ p++;
+ q++;
+ }
+
+ return true;
+}
+
+static bool read_2dpoint_table(FILE *stream, int count, Cell_Info_Block *pCIB) {
+ // int rv = read_and_decode_bytes(stream, pCIB->p2dpoint_array, count *
+ // 4);
+
+ unsigned short x, y;
+ for (int index = 0; index < count; index++) {
+ if (!read_and_decode_ushort(stream, &x)) return false;
+ if (!read_and_decode_ushort(stream, &y)) return false;
+
+ pCIB->p2dpoint_array[index].x = x;
+ pCIB->p2dpoint_array[index].y = y;
+ }
+
+ return true;
+}
+
+static bool read_feature_record_table(FILE *stream, int n_features,
+ Cell_Info_Block *pCIB) {
+ try {
+ Object *pobj = pCIB->pobject_block; // head of object array
+
+ vector_record_descriptor *pobject_vector_collection =
+ pCIB->object_vector_record_descriptor_block;
+
+ Object **p_relob =
+ pCIB->pprelated_object_block; // head of previously allocated related
+ // object pointer block
+
+ unsigned char *puc_var10 = pCIB->attribute_block_top; // m_3a;
+ int puc10count = 0; // should be same as header.m_78
+
+ unsigned char object_type;
+ unsigned char geom_prim;
+ unsigned short obj_desc_bytes = 0;
+
+ unsigned int t;
+ unsigned short index;
+ unsigned short n_elements;
+
+ for (int iobject = 0; iobject < n_features; iobject++) {
+ // read the object definition
+ read_and_decode_bytes(stream, &object_type, 1); // read the object type
+ read_and_decode_bytes(stream, &geom_prim,
+ 1); // read the object geometry primitive type
+ read_and_decode_ushort(stream,
+ &obj_desc_bytes); // read the object byte count
+
+ pobj->otype = object_type;
+ pobj->geotype = geom_prim;
+
+ switch (pobj->geotype & 0x0f) {
+ case 4: // AREA
+ {
+ if (!read_and_decode_ushort(stream, &n_elements)) return false;
+
+ pobj->n_geom_elements = n_elements;
+ t = (pobj->n_geom_elements * 2) + 2;
+ obj_desc_bytes -= t;
+
+ pobj->pGeometry =
+ pobject_vector_collection; // save pointer to created
+ // vector_record_descriptor in the
+ // object
+
+ for (unsigned short i = 0; i < pobj->n_geom_elements; i++) {
+ if (!read_and_decode_ushort(stream, &index)) return false;
+
+ if ((index & 0x1fff) > pCIB->m_nvector_records)
+ return false; // error in this cell, ignore all of it
+
+ geometry_descriptor *u = &pCIB->edge_vector_descriptor_block[(
+ index & 0x1fff)]; // point to the vector descriptor
+
+ pobject_vector_collection->pGeom_Description = u;
+ pobject_vector_collection->segment_usage =
+ (unsigned char)(index >> 13);
+
+ pobject_vector_collection++;
+ }
+
+ break;
+ } // AREA geom
+
+ case 2: // LINE geometry
+ {
+ if (!read_and_decode_ushort(
+ stream, &n_elements)) // read geometry element count
+ return false;
+
+ pobj->n_geom_elements = n_elements;
+ t = (pobj->n_geom_elements * 2) + 2;
+ obj_desc_bytes -= t;
+
+ pobj->pGeometry =
+ pobject_vector_collection; // save pointer to created
+ // vector_record_descriptor in the
+ // object
+
+ for (unsigned short i = 0; i < pobj->n_geom_elements; i++) {
+ unsigned short geometry_index;
+
+ if (!read_and_decode_ushort(stream, &geometry_index)) return false;
+
+ if ((geometry_index & 0x1fff) > pCIB->m_nvector_records)
+ // *(int *)(0) = 0; // error
+ return 0; // error, bad pointer
+
+ geometry_descriptor *u = &pCIB->edge_vector_descriptor_block[(
+ geometry_index & 0x1fff)]; // point to the vector descriptor
+
+ pobject_vector_collection->pGeom_Description = u;
+ pobject_vector_collection->segment_usage =
+ (unsigned char)(geometry_index >> 13);
+
+ pobject_vector_collection++;
+ }
+
+ break;
+ }
+
+ case 1: {
+ if (!read_and_decode_ushort(stream, &index)) return false;
+
+ obj_desc_bytes -= 2;
+
+ pobj->n_geom_elements = 1; // one point
+
+ pobj->pGeometry = &pCIB->p2dpoint_array[index]; // cm93_point *
+
+ break;
+ }
+
+ case 8: {
+ if (!read_and_decode_ushort(stream, &index)) return false;
+ obj_desc_bytes -= 2;
+
+ pobj->n_geom_elements = 1; // one point
+
+ pobj->pGeometry =
+ &pCIB->point3d_descriptor_block[index]; // geometry_descriptor *
+
+ break;
+ }
+
+ } // switch
+
+ if ((pobj->geotype & 0x10) == 0x10) // children/related
+ {
+ unsigned char nrelated;
+ if (!read_and_decode_bytes(stream, &nrelated, 1)) return false;
+
+ pobj->n_related_objects = nrelated;
+ t = (pobj->n_related_objects * 2) + 1;
+ obj_desc_bytes -= t;
+
+ pobj->p_related_object_pointer_array = p_relob;
+ p_relob += pobj->n_related_objects;
+
+ Object **w = (Object **)pobj->p_related_object_pointer_array;
+ for (unsigned char j = 0; j < pobj->n_related_objects; j++) {
+ if (!read_and_decode_ushort(stream, &index)) return false;
+
+ if (index > pCIB->m_nfeature_records)
+ // *(int *)(0) = 0; // error
+ return false;
+
+ Object *prelated_object = &pCIB->pobject_block[index];
+ *w = prelated_object; // fwd link
+
+ prelated_object->p_related_object_pointer_array =
+ pobj; // back link, array of 1 element
+ w++;
+ }
+ }
+
+ if ((pobj->geotype & 0x20) == 0x20) {
+ unsigned short nrelated;
+ if (!read_and_decode_ushort(stream, &nrelated)) return false;
+
+ pobj->n_related_objects = (unsigned char)(nrelated & 0xFF);
+ obj_desc_bytes -= 2;
+ }
+
+ if ((pobj->geotype & 0x40) == 0x40) {
+ }
+
+ if ((pobj->geotype & 0x80) == 0x80) // attributes
+ {
+ unsigned char nattr;
+ if (!read_and_decode_bytes(stream, &nattr, 1)) return false; // m_od
+
+ pobj->n_attributes = nattr;
+ obj_desc_bytes -= 5;
+
+ pobj->attributes_block = puc_var10;
+ puc_var10 += obj_desc_bytes;
+
+ puc10count += obj_desc_bytes;
+
+ if (!read_and_decode_bytes(stream, pobj->attributes_block,
+ obj_desc_bytes))
+ return false; // the attributes....
+
+ if ((pobj->geotype & 0x0f) == 1) {
+ }
+ }
+
+ pobj++; // next object
+ }
+
+ // wxASSERT(puc10count == pCIB->m_22->m_78);
+ }
+
+ catch (...) {
+ printf("catch on read_feature_record_table\n");
+ }
+
+ return true;
+}
+
+bool Ingest_CM93_Cell(const char *cell_file_name, Cell_Info_Block *pCIB) {
+ try {
+ int file_length;
+
+ // Get the file length
+ FILE *flstream = fopen(cell_file_name, "rb");
+ if (!flstream) return false;
+
+ fseek(flstream, 0, SEEK_END);
+ file_length = ftell(flstream);
+ fclose(flstream);
+
+ // Open the file
+ FILE *stream = fopen(cell_file_name, "rb");
+ if (!stream) return false;
+
+ // Validate the integrity of the cell file
+
+ unsigned short word0 = 0;
+ ;
+ int int0 = 0;
+ int int1 = 0;
+ ;
+
+ read_and_decode_ushort(stream,
+ &word0); // length of prolog + header (10 + 128)
+ read_and_decode_int(stream, &int0); // length of table 1
+ read_and_decode_int(stream, &int1); // length of table 2
+
+ int test = word0 + int0 + int1;
+ if (test != file_length) {
+ fclose(stream);
+ return false; // file is corrupt
+ }
+
+ // Cell is OK, proceed to ingest
+
+ if (!read_header_and_populate_cib(stream, pCIB)) {
+ fclose(stream);
+ return false;
+ }
+
+ if (!read_vector_record_table(stream, pCIB->m_nvector_records, pCIB)) {
+ fclose(stream);
+ return false;
+ }
+
+ if (!read_3dpoint_table(stream, pCIB->m_n_point3d_records, pCIB)) {
+ fclose(stream);
+ return false;
+ }
+
+ if (!read_2dpoint_table(stream, pCIB->m_n_point2d_records, pCIB)) {
+ fclose(stream);
+ return false;
+ }
+
+ if (!read_feature_record_table(stream, pCIB->m_nfeature_records, pCIB)) {
+ fclose(stream);
+ return false;
+ }
+
+ // int file_end = ftell(stream);
+
+ // wxASSERT(file_end == file_length);
+
+ fclose(stream);
+
+ return true;
+ }
+
+ catch (...) {
+ return false;
+ }
+}
+
+//----------------------------------------------------------------------------------
+// cm93chart Implementation
+//----------------------------------------------------------------------------------
+
+cm93chart::cm93chart() {
+ m_ChartType = CHART_TYPE_CM93;
+
+ // Create the decode table once, if needed
+ if (!cm93_decode_table_created) {
+ CreateDecodeTable();
+ cm93_decode_table_created = true;
+ }
+
+ m_pDict = NULL;
+ m_pManager = NULL;
+
+ m_current_cell_vearray_offset = 0;
+
+ m_ncontour_alloc = 100; // allocate inital vertex count container array
+ m_pcontour_array = (int *)malloc(m_ncontour_alloc * sizeof(int));
+
+ // Establish a common reference point for the cell
+ ref_lat = 0.;
+ ref_lon = 0.;
+
+ // Need a covr_set
+ m_pcovr_set = new covr_set(this);
+
+ // Make initial allocation of shared outline drawing buffer
+ m_pDrawBuffer = (wxPoint *)malloc(4 * sizeof(wxPoint));
+ m_nDrawBufferSize = 1;
+
+ // Set up the chart context
+ m_this_chart_context = (chart_context *)calloc(sizeof(chart_context), 1);
+ m_this_chart_context->chart = this;
+ m_RAZBuilt = true;
+}
+
+cm93chart::~cm93chart() {
+ free(m_pcontour_array);
+
+ delete m_pcovr_set;
+
+ free(m_pDrawBuffer);
+}
+
+void cm93chart::Unload_CM93_Cell() {
+ free(m_CIB.pobject_block);
+ // free(m_CIB.m_2a);
+ free(m_CIB.p2dpoint_array);
+ free(m_CIB.pprelated_object_block);
+ free(m_CIB.object_vector_record_descriptor_block);
+ free(m_CIB.attribute_block_top);
+ free(m_CIB.edge_vector_descriptor_block);
+ free(m_CIB.pvector_record_block_top);
+ free(m_CIB.point3d_descriptor_block);
+ free(m_CIB.p3dpoint_array);
+}
+
+// The idea here is to suggest to upper layers the appropriate scale values
+// to be used with this chart If max is too large, performance suffers, and
+// the charts are very cluttered onscreen. If the min is too small, then the
+// chart rendereding will be over-scaled, and accuracy suffers. In some ways,
+// this is subjective.....
+
+double cm93chart::GetNormalScaleMin(double canvas_scale_factor,
+ bool b_allow_overzoom) {
+ switch (GetNativeScale()) {
+ case 20000000:
+ return 3000000.; // Z
+ case 3000000:
+ return 1000000.; // A
+ case 1000000:
+ return 200000.; // B
+ case 200000:
+ return 40000.; // C
+ case 100000:
+ return 20000.; // D
+ case 50000:
+ return 10000.; // E
+ case 20000:
+ return 5000.; // F
+ case 7500:
+ return 3500.; // G
+ }
+
+ return 1.0;
+}
+
+double cm93chart::GetNormalScaleMax(double canvas_scale_factor,
+ int canvas_width) {
+ /*
+ XXX previous declaration hides overloaded virtual function
+ and it was calling:
+ s57chart::GetNormalScaleMax( canvas_scale_factor, canvas_width )
+ should we restore this behavior?
+ */
+ switch (GetNativeScale()) {
+ case 20000000:
+ return 50000000.; // Z
+ case 3000000:
+ return 6000000.; // A
+ case 1000000:
+ return 2000000.; // B
+ case 200000:
+ return 400000.; // C
+ case 100000:
+ return 200000.; // D
+ case 50000:
+ return 100000.; // E
+ case 20000:
+ return 40000.; // F
+ case 7500:
+ return 15000.; // G
+ }
+
+ return 1.0e7;
+}
+
+void cm93chart::GetPointPix(ObjRazRules *rzRules, float north, float east,
+ wxPoint *r) {
+ wxPoint2DDouble en(east, north);
+ GetPointPix(rzRules, &en, r, 1);
+}
+
+void cm93chart::GetPointPix(ObjRazRules *rzRules, wxPoint2DDouble *en,
+ wxPoint *r, int nPoints) {
+ S57Obj *obj = rzRules->obj;
+
+ double xr = obj->x_rate;
+ double xo = obj->x_origin;
+ double yr = obj->y_rate;
+ double yo = obj->y_origin;
+
+ if (m_vp_current.m_projection_type == PROJECTION_MERCATOR) {
+ if (m_vp_current.GetBBox().GetMaxLon() >= 180. &&
+ rzRules->obj->BBObj.GetMaxLon() < m_vp_current.GetBBox().GetMinLon())
+ xo += mercator_k0 * WGS84_semimajor_axis_meters * 2.0 * PI;
+ else if ((m_vp_current.GetBBox().GetMinLon() <= -180. &&
+ rzRules->obj->BBObj.GetMinLon() >
+ m_vp_current.GetBBox().GetMaxLon()) ||
+ (rzRules->obj->BBObj.GetMaxLon() >= 180 &&
+ m_vp_current.GetBBox().GetMinLon() <= 0.))
+ xo -= mercator_k0 * WGS84_semimajor_axis_meters * 2.0 * PI;
+
+ for (int i = 0; i < nPoints; i++) {
+ double valx = (en[i].m_x * xr) + xo;
+ double valy = (en[i].m_y * yr) + yo;
+
+ r[i].x = ((valx - m_easting_vp_center) * m_view_scale_ppm) +
+ m_pixx_vp_center + 0.5;
+ r[i].y = m_pixy_vp_center -
+ ((valy - m_northing_vp_center) * m_view_scale_ppm) + 0.5;
+ }
+ } else {
+ for (int i = 0; i < nPoints; i++) {
+ double valx = (en[i].m_x * xr) + xo;
+ double valy = (en[i].m_y * yr) + yo;
+
+ double lat, lon;
+ fromSM(valx - m_easting_vp_center, valy - m_northing_vp_center,
+ m_vp_current.clat, m_vp_current.clon, &lat, &lon);
+
+ double rotation = m_vp_current.rotation;
+ m_vp_current.SetRotationAngle(0);
+ r[i] = m_vp_current.GetPixFromLL(lat, lon);
+ m_vp_current.SetRotationAngle(rotation);
+ }
+ }
+}
+
+void cm93chart::GetPixPoint(int pixx, int pixy, double *plat, double *plon,
+ ViewPort *vpt) {
+#if 1
+ vpt->GetLLFromPix(wxPoint(pixx, pixy), plat, plon);
+
+ // if ( *plon < 0. )
+ // *plon += 360.;
+
+#else
+ // Use Mercator estimator
+ int dx = pixx - (vpt->pix_width / 2);
+ int dy = (vpt->pix_height / 2) - pixy;
+
+ double xp = (dx * cos(vpt->skew)) - (dy * sin(vpt->skew));
+ double yp = (dy * cos(vpt->skew)) + (dx * sin(vpt->skew));
+
+ double d_east = xp / vpt->view_scale_ppm;
+ double d_north = yp / vpt->view_scale_ppm;
+
+ double slat, slon;
+ fromSM(d_east, d_north, vpt->clat, vpt->clon, &slat, &slon);
+
+ if (slon > 360.) slon -= 360.;
+
+ *plat = slat;
+ *plon = slon;
+#endif
+}
+
+bool cm93chart::AdjustVP(ViewPort &vp_last, ViewPort &vp_proposed) {
+ if (IsCacheValid()) {
+ // If this viewpoint is same scale as last...
+ if (vp_last.view_scale_ppm == vp_proposed.view_scale_ppm) {
+ // then require this viewport to be exact integral pixel difference from
+ // last adjusting clat/clat and SM accordingly
+#if 1
+ wxPoint2DDouble p = vp_proposed.GetDoublePixFromLL(ref_lat, ref_lon) -
+ vp_last.GetDoublePixFromLL(ref_lat, ref_lon);
+
+ double xlat, xlon;
+ vp_last.GetLLFromPix(wxPoint(round(p.m_x), round(p.m_y)), &xlat, &xlon);
+#else
+ double prev_easting_c, prev_northing_c;
+ toSM(vp_last.clat, vp_last.clon, ref_lat, ref_lon, &prev_easting_c,
+ &prev_northing_c);
+
+ double easting_c, northing_c;
+ toSM(vp_proposed.clat, vp_proposed.clon, ref_lat, ref_lon, &easting_c,
+ &northing_c);
+
+ double delta_pix_x =
+ (easting_c - prev_easting_c) * vp_proposed.view_scale_ppm;
+ int dpix_x = (int)round(delta_pix_x);
+ double dpx = dpix_x;
+
+ double delta_pix_y =
+ (northing_c - prev_northing_c) * vp_proposed.view_scale_ppm;
+ int dpix_y = (int)round(delta_pix_y);
+ double dpy = dpix_y;
+
+ double c_east_d = (dpx / vp_proposed.view_scale_ppm) + prev_easting_c;
+ double c_north_d = (dpy / vp_proposed.view_scale_ppm) + prev_northing_c;
+
+ double xlat, xlon;
+ fromSM(c_east_d, c_north_d, ref_lat, ref_lon, &xlat, &xlon);
+#endif
+ vp_proposed.clon = xlon;
+ vp_proposed.clat = xlat;
+
+ return true;
+ }
+ }
+
+ return false;
+}
+
+//-----------------------------------------------------------------------
+// Calculate and Set ViewPoint Constants
+//-----------------------------------------------------------------------
+
+void cm93chart::SetVPParms(const ViewPort &vpt) {
+ if (m_vp_current == vpt) {
+ return;
+ }
+ // Save a copy for later reference
+ m_vp_current = vpt;
+
+ // Set up local SM rendering constants
+ m_pixx_vp_center = vpt.pix_width / 2;
+ m_pixy_vp_center = vpt.pix_height / 2;
+ m_view_scale_ppm = vpt.view_scale_ppm;
+
+ toSM(vpt.clat, vpt.clon, ref_lat, ref_lon, &m_easting_vp_center,
+ &m_northing_vp_center);
+
+ vp_transform.easting_vp_center = m_easting_vp_center;
+ vp_transform.northing_vp_center = m_northing_vp_center;
+
+ if (g_bDebugCM93) {
+ // Fetch the lat/lon of the screen corner points
+ ViewPort vptl = vpt;
+ LLBBox box = vptl.GetBBox();
+ double ll_lon = box.GetMinLon();
+ double ll_lat = box.GetMinLat();
+
+ double ur_lon = box.GetMaxLon();
+ double ur_lat = box.GetMaxLat();
+
+ printf(
+ "cm93chart::SetVPParms ll_lon: %g ll_lat: %g ur_lon: %g ur_lat: "
+ " %g m_dval: %g\n",
+ ll_lon, ll_lat, ur_lon, ur_lat, m_dval);
+ }
+
+ // Create an array of CellIndexes covering the current viewport
+ std::vector vpcells = GetVPCellArray(vpt);
+
+ // Check the member array to see if all these viewport cells have been
+ // loaded
+ bool bcell_is_in;
+
+ for (unsigned int i = 0; i < vpcells.size(); i++) {
+ bcell_is_in = false;
+ for (unsigned int j = 0; j < m_cells_loaded_array.size(); j++) {
+ if (vpcells[i] == m_cells_loaded_array[j]) {
+ bcell_is_in = true;
+ break;
+ }
+ }
+
+ // The cell is not in place, so go load it
+ if (!bcell_is_in) {
+#ifndef __OCPN__ANDROID__
+ AbstractPlatform::ShowBusySpinner();
+#endif
+ int cell_index = vpcells[i];
+
+ if (loadcell_in_sequence(cell_index, '0')) // Base cell
+ {
+ ProcessVectorEdges();
+ CreateObjChain(cell_index, (int)'0', vpt.view_scale_ppm);
+
+ ForceEdgePriorityEvaluate(); // need to re-evaluate priorities
+
+ m_cells_loaded_array.push_back(cell_index);
+
+ Unload_CM93_Cell();
+ }
+
+ char loadcell_key = 'A'; // starting
+
+ // Load any subcells in sequence
+ // On successful load, add it to the member list and process the cell
+ while (loadcell_in_sequence(cell_index, loadcell_key)) {
+ ProcessVectorEdges();
+ CreateObjChain(cell_index, (int)loadcell_key, vpt.view_scale_ppm);
+
+ ForceEdgePriorityEvaluate(); // need to re-evaluate priorities
+
+ if (std::find(m_cells_loaded_array.begin(), m_cells_loaded_array.end(),
+ cell_index) == m_cells_loaded_array.end())
+ m_cells_loaded_array.push_back(cell_index);
+
+ Unload_CM93_Cell();
+
+ loadcell_key++;
+ }
+
+ AssembleLineGeometry();
+
+ ClearDepthContourArray();
+ BuildDepthContourArray();
+
+ // Set up the chart context
+ m_this_chart_context->m_pvc_hash = &Get_vc_hash();
+ m_this_chart_context->m_pve_hash = &Get_ve_hash();
+
+ m_this_chart_context->pFloatingATONArray = pFloatingATONArray;
+ m_this_chart_context->pRigidATONArray = pRigidATONArray;
+ m_this_chart_context->chart = this;
+ m_this_chart_context->chart_type = GetChartType();
+
+ m_this_chart_context->safety_contour = m_next_safe_cnt;
+ m_this_chart_context->vertex_buffer = GetLineVertexBuffer();
+ m_this_chart_context->pt2GetAssociatedObjects =
+ &s57chart::GetAssociatedObjects;
+
+ // Loop and populate all the objects
+ for (int i = 0; i < PI_PRIO_NUM; ++i) {
+ for (int j = 0; j < PI_LUPNAME_NUM; j++) {
+ ObjRazRules *top = razRules[i][j];
+ while (top) {
+ if (top->obj) top->obj->m_chart_context = m_this_chart_context;
+ top = top->next;
+ }
+ }
+ }
+
+ AbstractPlatform::HideBusySpinner();
+ }
+ }
+}
+
+std::vector cm93chart::GetVPCellArray(const ViewPort &vpt) {
+ // Fetch the lat/lon of the screen corner points
+ ViewPort vptl = vpt;
+ LLBBox box = vptl.GetBBox();
+ double ll_lon = box.GetMinLon();
+ double ll_lat = box.GetMinLat();
+
+ double ur_lon = box.GetMaxLon();
+ double ur_lat = box.GetMaxLat();
+
+ // CLip upper latitude to avoid trying to fetch non-existent cells.
+ ur_lat = wxMin(ur_lat, 79.99999);
+
+ // CLip upper latitude to avoid trying to fetch non-existent cells above N80.
+ ur_lat = wxMin(ur_lat, 79.99999);
+
+ // Adjust to always positive for easier cell calculations
+ if (ll_lon < 0) {
+ ll_lon += 360;
+ ur_lon += 360;
+ }
+
+ // Create an array of CellIndexes covering the current viewport
+ std::vector vpcells;
+
+ int lower_left_cell = Get_CM93_CellIndex(ll_lat, ll_lon, GetNativeScale());
+ vpcells.push_back(lower_left_cell); // always add the lower left cell
+
+ if (g_bDebugCM93)
+ printf("cm93chart::GetVPCellArray Adding %d\n", lower_left_cell);
+
+ double rlat, rlon;
+ Get_CM93_Cell_Origin(lower_left_cell, GetNativeScale(), &rlat, &rlon);
+
+ // Use exact integer math here
+ // It is more obtuse, but it removes dependency on FP rounding policy
+
+ int loni_0 = (int)wxRound(rlon * 3);
+ int loni_20 = loni_0 + (int)m_dval; // already added the lower left cell
+ int lati_20 = (int)wxRound(rlat * 3);
+
+ while (lati_20 < (ur_lat * 3.)) {
+ while (loni_20 < (ur_lon * 3.)) {
+ unsigned int next_lon = loni_20 + 1080;
+ while (next_lon >= 1080) next_lon -= 1080;
+
+ unsigned int next_cell = next_lon;
+
+ next_cell += (lati_20 + 270) * 10000;
+
+ vpcells.push_back((int)next_cell);
+ if (g_bDebugCM93)
+ printf("cm93chart::GetVPCellArray Adding %d\n", next_cell);
+
+ loni_20 += (int)m_dval;
+ }
+ lati_20 += (int)m_dval;
+ loni_20 = loni_0;
+ }
+
+ return vpcells;
+}
+
+void cm93chart::ProcessVectorEdges() {
+ // Create the vector(edge) map for this cell, appending to the existing
+ // member hash map
+ auto &vehash = Get_ve_hash();
+
+ m_current_cell_vearray_offset =
+ vehash.size(); // keys start at the current size
+ geometry_descriptor *pgd = m_CIB.edge_vector_descriptor_block;
+
+ for (int iedge = 0; iedge < m_CIB.m_nvector_records; iedge++) {
+ VE_Element *vep = new VE_Element;
+ vep->index = iedge + m_current_cell_vearray_offset;
+ vep->nCount = pgd->n_points;
+ vep->pPoints = NULL;
+ vep->max_priority = -99; // Default
+
+ if (pgd->n_points) {
+ float *pPoints = (float *)malloc(pgd->n_points * 2 * sizeof(float));
+ vep->pPoints = pPoints;
+
+ cm93_point *ppt = pgd->p_points;
+
+ // Get a bounding box for the edge
+ double east_max = -1e7;
+ double east_min = 1e7;
+ double north_max = -1e7;
+ double north_min = 1e7;
+
+ for (int ip = 0; ip < pgd->n_points; ip++) {
+ *pPoints++ = ppt->x;
+ *pPoints++ = ppt->y;
+
+ east_max = wxMax(east_max, ppt->x);
+ east_min = wxMin(east_min, ppt->x);
+ north_max = wxMax(north_max, ppt->y);
+ north_min = wxMin(north_min, ppt->y);
+
+ ppt++;
+ }
+
+ cm93_point p;
+ double lat1, lon1, lat2, lon2;
+
+ // TODO Not precisely correct, transform should account for
+ // "trans_WGS84_offset_x"
+ p.x = east_min;
+ p.y = north_min;
+ Transform(&p, 0, 0, &lat1, &lon1);
+
+ p.x = east_max;
+ p.y = north_max;
+ Transform(&p, 0, 0, &lat2, &lon2);
+
+ // if(lon1 > lon2)
+ // lon2 += 360;
+
+ vep->edgeBBox.Set(lat1, lon1, lat2, lon2);
+ }
+
+ vehash[vep->index] = vep;
+
+ pgd++; // next geometry descriptor
+ }
+}
+
+int cm93chart::CreateObjChain(int cell_index, int subcell,
+ double view_scale_ppm) {
+ LUPrec *LUP;
+ LUPname LUP_Name = PAPER_CHART;
+
+ m_CIB.m_cell_mcovr_list.Clear();
+
+ // CALLGRIND_START_INSTRUMENTATION
+
+ Object *pobjectDef = m_CIB.pobject_block; // head of object array
+ m_CIB.b_have_offsets = false; // will be set if any M_COVRs in this cell have
+ // defined, non-zero WGS84 offsets
+ m_CIB.b_have_user_offsets = false; // will be set if any M_COVRs in this cell
+ // have user defined offsets
+
+ int iObj = 0;
+ S57Obj *obj;
+
+ double scale = top_frame::Get()->GetBestVPScale(this);
+ int nativescale = GetNativeScale();
+
+ while (iObj < m_CIB.m_nfeature_records) {
+ if ((pobjectDef != NULL)) {
+ Extended_Geometry *xgeom = BuildGeom(pobjectDef, NULL, iObj);
+
+ obj = NULL;
+ if (NULL != xgeom)
+ obj =
+ CreateS57Obj(cell_index, iObj, subcell, pobjectDef, m_pDict, xgeom,
+ ref_lat, ref_lon, GetNativeScale(), view_scale_ppm);
+
+ if (obj) {
+ wxString objnam = obj->GetAttrValueAsString("OBJNAM");
+ wxString fe_name = wxString(obj->FeatureName, wxConvUTF8);
+ if (fe_name == "_texto") objnam = obj->GetAttrValueAsString("_texta");
+ if (objnam.Len() > 0) {
+ wxString cellname = wxString::Format("%i_%i", cell_index, subcell);
+ SendVectorChartObjectInfo(cellname, fe_name, objnam, obj->m_lat,
+ obj->m_lon, scale, nativescale);
+ }
+ // Build/Maintain the ATON floating/rigid arrays
+ if (GEO_POINT == obj->Primitive_type) {
+ // set floating platform
+ if ((!strncmp(obj->FeatureName, "LITFLT", 6)) ||
+ (!strncmp(obj->FeatureName, "LITVES", 6)) ||
+ (!strncmp(obj->FeatureName, "BOY", 3))) {
+ pFloatingATONArray->Add(obj);
+ }
+
+ // set rigid platform
+ if (!strncmp(obj->FeatureName, "BCN", 3)) pRigidATONArray->Add(obj);
+
+ // Mark the object as an ATON
+ if ((!strncmp(obj->FeatureName, "LIT", 3)) ||
+ (!strncmp(obj->FeatureName, "LIGHTS", 6)) ||
+ (!strncmp(obj->FeatureName, "BCN", 3)) ||
+ (!strncmp(obj->FeatureName, "_slgto", 6)) ||
+ (!strncmp(obj->FeatureName, "_boygn", 6)) ||
+ (!strncmp(obj->FeatureName, "_bcngn", 6)) ||
+ (!strncmp(obj->FeatureName, "_extgn", 6)) ||
+ (!strncmp(obj->FeatureName, "TOWERS", 6)) ||
+ (!strncmp(obj->FeatureName, "BOY", 3))) {
+ obj->bIsAton = true;
+ }
+ }
+
+ // Mark th object as an "associable depth area"
+ // Flag is used by conditional symbology
+ if (GEO_AREA == obj->Primitive_type) {
+ if (!strncmp(obj->FeatureName, "DEPARE", 6) ||
+ !strncmp(obj->FeatureName, "DRGARE", 6))
+ obj->bIsAssociable = true;
+ }
+
+ // This is where Simplified or Paper-Type point features are
+ // selected In the case where the chart needs alternate LUPS
+ // loaded, do so. This case is triggered when the UpdateLUP()
+ // method has been called on a partially loaded chart.
+
+ switch (obj->Primitive_type) {
+ case GEO_POINT:
+ case GEO_META:
+ case GEO_PRIM:
+ if (PAPER_CHART == ps52plib->m_nSymbolStyle)
+ LUP_Name = PAPER_CHART;
+ else
+ LUP_Name = SIMPLIFIED;
+
+ if (m_b2pointLUPS) {
+ LUPname LUPO_Name;
+ if (PAPER_CHART == ps52plib->m_nSymbolStyle)
+ LUPO_Name = SIMPLIFIED;
+ else
+ LUPO_Name = PAPER_CHART;
+
+ // Load the alternate LUP
+ LUPrec *LUPO =
+ ps52plib->S52_LUPLookup(LUPO_Name, obj->FeatureName, obj);
+ if (LUPO) {
+ ps52plib->_LUP2rules(LUPO, obj);
+ _insertRules(obj, LUPO, this);
+ }
+ }
+ break;
+
+ case GEO_LINE:
+ LUP_Name = LINES;
+ break;
+
+ case GEO_AREA:
+ if (PLAIN_BOUNDARIES == ps52plib->m_nBoundaryStyle)
+ LUP_Name = PLAIN_BOUNDARIES;
+ else
+ LUP_Name = SYMBOLIZED_BOUNDARIES;
+
+ if (m_b2lineLUPS) {
+ LUPname LUPO_Name;
+ if (PLAIN_BOUNDARIES == ps52plib->m_nBoundaryStyle)
+ LUPO_Name = SYMBOLIZED_BOUNDARIES;
+ else
+ LUPO_Name = PLAIN_BOUNDARIES;
+
+ // Load the alternate LUP
+ LUPrec *LUPO =
+ ps52plib->S52_LUPLookup(LUPO_Name, obj->FeatureName, obj);
+ if (LUPO) {
+ ps52plib->_LUP2rules(LUPO, obj);
+ _insertRules(obj, LUPO, this);
+ }
+ }
+ break;
+ }
+
+ LUP = ps52plib->S52_LUPLookup(LUP_Name, obj->FeatureName, obj);
+
+ if (NULL == LUP) {
+ if (g_bDebugCM93) {
+ wxString msg(obj->FeatureName, wxConvUTF8);
+ msg.Prepend(" CM93 could not find LUP for ");
+ LogMessageOnce(msg);
+ }
+ if (0 == obj->nRef) delete obj;
+ } else {
+ // Convert LUP to rules set
+ ps52plib->_LUP2rules(LUP, obj);
+
+ // Add linked object/LUP to the working set
+ _insertRules(obj, LUP, this);
+
+ // Establish Object's Display Category
+ obj->m_DisplayCat = LUP->DISC;
+
+ // Establish objects base display priority
+ obj->m_DPRI = LUP->DPRI - '0';
+
+ // Populate the chart context
+ obj->m_chart_context = m_this_chart_context;
+ }
+ }
+
+ }
+
+ else // objectdef == NULL
+ break;
+
+ pobjectDef++;
+
+ iObj++;
+ }
+
+ // CALLGRIND_STOP_INSTRUMENTATION
+
+ return 1;
+}
+
+InitReturn cm93chart::Init(const wxString &name, ChartInitFlag flags) {
+ m_FullPath = name;
+ m_Description = m_FullPath;
+
+ wxFileName fn(name);
+
+ if (!m_prefix.Len())
+ m_prefix = fn.GetPath(wxPATH_GET_VOLUME | wxPATH_GET_SEPARATOR);
+
+ m_scalechar = fn.GetExt();
+
+ // Figure out the scale from the file name
+
+ int scale;
+ switch ((m_scalechar.mb_str())[(size_t)0]) {
+ case 'Z':
+ scale = 20000000;
+ break;
+ case 'A':
+ scale = 3000000;
+ break;
+ case 'B':
+ scale = 1000000;
+ break;
+ case 'C':
+ scale = 200000;
+ break;
+ case 'D':
+ scale = 100000;
+ break;
+ case 'E':
+ scale = 50000;
+ break;
+ case 'F':
+ scale = 20000;
+ break;
+ case 'G':
+ scale = 7500;
+ break;
+ default:
+ scale = 20000000;
+ break;
+ }
+
+ m_Chart_Scale = scale;
+
+ switch (GetNativeScale()) {
+ case 20000000:
+ m_dval = 120;
+ break; // Z
+ case 3000000:
+ m_dval = 60;
+ break; // A
+ case 1000000:
+ m_dval = 30;
+ break; // B
+ case 200000:
+ m_dval = 12;
+ break; // C
+ case 100000:
+ m_dval = 3;
+ break; // D
+ case 50000:
+ m_dval = 1;
+ break; // E
+ case 20000:
+ m_dval = 1;
+ break; // F
+ case 7500:
+ m_dval = 1;
+ break; // G
+ default:
+ m_dval = 1;
+ break;
+ }
+
+ // Set the nice name
+ wxString data = "CM93Chart ";
+ data.Append(m_scalechar);
+ wxString s;
+ s.Printf(" 1/%d", m_Chart_Scale);
+ data.Append(s);
+ m_Name = data;
+
+ // Initialize the covr_set
+ if (scale != 20000000)
+ m_pcovr_set->Init(m_scalechar.mb_str()[(size_t)0], m_prefix);
+
+ if (flags == THUMB_ONLY) {
+ // SetColorScheme(cs, false);
+
+ return INIT_OK;
+ }
+
+ if (!m_pManager) m_pManager = new cm93manager;
+
+ if (flags == HEADER_ONLY) return CreateHeaderDataFromCM93Cell();
+
+ // Load the cm93 dictionary if necessary
+ if (!m_pDict) {
+ if (m_pManager) {
+ if (m_pManager->Loadcm93Dictionary(name))
+ m_pDict = m_pManager->m_pcm93Dict;
+ else {
+ wxLogMessage(" CM93Chart Init cannot locate CM93 dictionary.");
+ return INIT_FAIL_REMOVE;
+ }
+ }
+ }
+
+ bReadyToRender = true;
+
+ return INIT_OK;
+}
+
+Extended_Geometry *cm93chart::BuildGeom(Object *pobject,
+ wxFileOutputStream *postream,
+ int iobject)
+
+{
+ wxString s;
+ int geomtype;
+
+ int geom_type_maybe = pobject->geotype;
+
+ switch (geom_type_maybe) {
+ case 1:
+ geomtype = 1;
+ break;
+ case 2:
+ geomtype = 2;
+ break;
+ case 4:
+ geomtype = 3;
+ break;
+ case 129:
+ geomtype = 1;
+ break;
+ case 130:
+ geomtype = 2;
+ break;
+ case 132:
+ geomtype = 3;
+ break;
+ case 8:
+ geomtype = 8;
+ break;
+ case 16:
+ geomtype = 16;
+ break;
+ case 161:
+ geomtype = 1;
+ break; // lighthouse first child
+ case 33:
+ geomtype = 1;
+ break;
+ default:
+ geomtype = -1;
+ break;
+ }
+
+ int iseg;
+
+ Extended_Geometry *ret_ptr = new Extended_Geometry;
+
+ int lon_max, lat_max, lon_min, lat_min;
+ lon_max = 0;
+ lon_min = 65536;
+ lat_max = 0;
+ lat_min = 65536;
+
+ switch (geomtype) {
+ case 3: // Areas
+ {
+ vector_record_descriptor *psegs =
+ (vector_record_descriptor *)pobject->pGeometry;
+
+ int nsegs = pobject->n_geom_elements;
+
+ ret_ptr->n_vector_indices = nsegs;
+ ret_ptr->pvector_index = (int *)malloc(nsegs * 3 * sizeof(int));
+
+ // Traverse the object once to get a maximum polygon vertex count
+ int n_maxvertex = 0;
+ for (int i = 0; i < nsegs; i++) {
+ geometry_descriptor *pgd =
+ (geometry_descriptor *)(psegs[i].pGeom_Description);
+ n_maxvertex += pgd->n_points;
+ }
+
+ // TODO May not need this fluff adder....
+ n_maxvertex += 1; // fluff
+
+ wxPoint2DDouble *pPoints =
+ (wxPoint2DDouble *)calloc((n_maxvertex) * sizeof(wxPoint2DDouble), 1);
+
+ int ip = 1;
+ int n_prev_vertex_index = 1;
+ bool bnew_ring = true;
+ int ncontours = 0;
+ iseg = 0;
+
+ cm93_point start_point;
+ start_point.x = 0;
+ start_point.y = 0;
+
+ cm93_point cur_end_point;
+ cur_end_point.x = 1;
+ cur_end_point.y = 1;
+
+ int n_max_points = -1;
+ while (iseg < nsegs) {
+ int type_seg = psegs[iseg].segment_usage;
+
+ geometry_descriptor *pgd =
+ (geometry_descriptor *)(psegs[iseg].pGeom_Description);
+
+ int npoints = pgd->n_points;
+ cm93_point *rseg = pgd->p_points;
+
+ n_max_points = wxMax(n_max_points, npoints);
+
+ // Establish ring starting conditions
+ if (bnew_ring) {
+ bnew_ring = false;
+
+ if ((type_seg & 4) == 0)
+ start_point = rseg[0];
+ else
+ start_point = rseg[npoints - 1];
+ }
+
+ if (((type_seg & 4) == 0)) {
+ cur_end_point = rseg[npoints - 1];
+ for (int j = 0; j < npoints; j++) {
+ // if(ncontours == 0) // outer
+ // ring describes envelope
+ {
+ lon_max = wxMax(lon_max, rseg[j].x);
+ lon_min = wxMin(lon_min, rseg[j].x);
+ lat_max = wxMax(lat_max, rseg[j].y);
+ lat_min = wxMin(lat_min, rseg[j].y);
+ }
+
+ pPoints[ip].m_x = rseg[j].x;
+ pPoints[ip].m_y = rseg[j].y;
+ ip++;
+ }
+ } else if ((type_seg & 4) == 4) // backwards
+ {
+ cur_end_point = rseg[0];
+ for (int j = npoints - 1; j >= 0; j--) {
+ // if(ncontours == 0) // outer
+ // ring describes envelope
+ {
+ lon_max = wxMax(lon_max, rseg[j].x);
+ lon_min = wxMin(lon_min, rseg[j].x);
+ lat_max = wxMax(lat_max, rseg[j].y);
+ lat_min = wxMin(lat_min, rseg[j].y);
+ }
+
+ pPoints[ip].m_x = rseg[j].x;
+ pPoints[ip].m_y = rseg[j].y;
+ ip++;
+ }
+ }
+
+ ip--; // skip the last point in each segment
+
+ ret_ptr->pvector_index[iseg * 3 + 0] =
+ 0; //-1; // first connected node
+ ret_ptr->pvector_index[iseg * 3 + 1] =
+ pgd->index + m_current_cell_vearray_offset; // edge index
+ ret_ptr->pvector_index[iseg * 3 + 2] =
+ 0; //-2; // last connected node
+
+ if ((cur_end_point.x == start_point.x) &&
+ (cur_end_point.y == start_point.y)) {
+ // done with a ring
+
+ ip++; // leave in ring closure point
+
+ int nRingVertex = ip - n_prev_vertex_index;
+
+ // possibly increase contour array size
+ if (ncontours > m_ncontour_alloc - 1) {
+ m_ncontour_alloc *= 2;
+ int *tmp = m_pcontour_array;
+ m_pcontour_array = (int *)realloc(m_pcontour_array,
+ m_ncontour_alloc * sizeof(int));
+ if (NULL == tmp) {
+ free(tmp);
+ tmp = NULL;
+ }
+ }
+ m_pcontour_array[ncontours] = nRingVertex; // store the vertex count
+
+ bnew_ring = true; // set for next ring
+ n_prev_vertex_index = ip;
+ ncontours++;
+ }
+ iseg++;
+ } // while iseg
+
+ ret_ptr->n_max_edge_points = n_max_points;
+
+ ret_ptr->n_contours =
+ ncontours; // parameters passed to trapezoid tesselator
+
+ if (0 == ncontours) ncontours = 1; // avoid 0 alloc
+ ret_ptr->contour_array = (int *)malloc(ncontours * sizeof(int));
+ memcpy(ret_ptr->contour_array, m_pcontour_array, ncontours * sizeof(int));
+
+ ret_ptr->vertex_array = pPoints;
+ ret_ptr->n_max_vertex = n_maxvertex;
+
+ ret_ptr->pogrGeom = NULL;
+
+ ret_ptr->xmin = lon_min;
+ ret_ptr->xmax = lon_max;
+ ret_ptr->ymin = lat_min;
+ ret_ptr->ymax = lat_max;
+
+ break;
+ } // case 3
+
+ case 1: // single points
+ {
+ cm93_point *pt = (cm93_point *)pobject->pGeometry;
+ ret_ptr->pogrGeom = NULL; // t;
+
+ ret_ptr->pointx = pt->x;
+ ret_ptr->pointy = pt->y;
+ break;
+ }
+
+ case 2: // LINE geometry
+ {
+ vector_record_descriptor *psegs =
+ (vector_record_descriptor *)pobject->pGeometry;
+
+ int nsegs = pobject->n_geom_elements;
+
+ ret_ptr->n_vector_indices = nsegs;
+ ret_ptr->pvector_index = (int *)malloc(nsegs * 3 * sizeof(int));
+
+ // Calculate the number of points
+ int n_maxvertex = 0;
+ for (int imseg = 0; imseg < nsegs; imseg++) {
+ geometry_descriptor *pgd =
+ (geometry_descriptor *)psegs->pGeom_Description;
+
+ n_maxvertex += pgd->n_points;
+ psegs++;
+ }
+
+ wxPoint2DDouble *pPoints =
+ (wxPoint2DDouble *)malloc(n_maxvertex * sizeof(wxPoint2DDouble));
+
+ psegs = (vector_record_descriptor *)pobject->pGeometry;
+
+ int ip = 0;
+ int lon_max, lat_max, lon_min, lat_min;
+ lon_max = 0;
+ lon_min = 65536;
+ lat_max = 0;
+ lat_min = 65536;
+ int n_max_points = -1;
+
+ for (int iseg = 0; iseg < nsegs; iseg++) {
+ int type_seg = psegs->segment_usage;
+
+ geometry_descriptor *pgd =
+ (geometry_descriptor *)psegs->pGeom_Description;
+
+ psegs++; // next segment
+
+ int npoints = pgd->n_points;
+ cm93_point *rseg = pgd->p_points;
+
+ n_max_points = wxMax(n_max_points, npoints);
+
+ if (((type_seg & 4) != 4)) {
+ for (int j = 0; j < npoints; j++) {
+ lon_max = wxMax(lon_max, rseg[j].x);
+ lon_min = wxMin(lon_min, rseg[j].x);
+ lat_max = wxMax(lat_max, rseg[j].y);
+ lat_min = wxMin(lat_min, rseg[j].y);
+
+ pPoints[ip].m_x = rseg[j].x;
+ pPoints[ip].m_y = rseg[j].y;
+ ip++;
+ }
+ }
+
+ else if ((type_seg & 4) == 4) // backwards
+ {
+ for (int j = npoints - 1; j >= 0; j--) {
+ lon_max = wxMax(lon_max, rseg[j].x);
+ lon_min = wxMin(lon_min, rseg[j].x);
+ lat_max = wxMax(lat_max, rseg[j].y);
+ lat_min = wxMin(lat_min, rseg[j].y);
+
+ pPoints[ip].m_x = rseg[j].x;
+ pPoints[ip].m_y = rseg[j].y;
+ ip++;
+ }
+ }
+
+ ret_ptr->pvector_index[iseg * 3 + 0] =
+ 0; //-1; // first connected node
+ ret_ptr->pvector_index[iseg * 3 + 1] =
+ pgd->index + m_current_cell_vearray_offset; // edge index
+ ret_ptr->pvector_index[iseg * 3 + 2] =
+ 0; //-2; // last connected node
+
+ } // for
+
+ ret_ptr->n_max_edge_points = n_max_points;
+
+ ret_ptr->vertex_array = pPoints;
+ ret_ptr->n_max_vertex = n_maxvertex;
+
+ ret_ptr->pogrGeom = NULL;
+
+ ret_ptr->xmin = lon_min;
+ ret_ptr->xmax = lon_max;
+ ret_ptr->ymin = lat_min;
+ ret_ptr->ymax = lat_max;
+
+ break;
+ } // case 2 (lines)
+
+ case 8: {
+ geometry_descriptor *pgd = (geometry_descriptor *)pobject->pGeometry;
+
+ int npoints = pgd->n_points;
+ cm93_point_3d *rseg = (cm93_point_3d *)pgd->p_points;
+
+ OGRMultiPoint *pSMP = new OGRMultiPoint;
+
+ int z;
+ double zp;
+ for (int ip = 0; ip < npoints; ip++) {
+ z = rseg[ip].z;
+
+ // This is a magic number if there ever was one.....
+ if (z >= 12000)
+ zp = double(z - 12000);
+ else
+ zp = z / 10.;
+
+ OGRPoint *ppoint = new OGRPoint(rseg[ip].x, rseg[ip].y, zp);
+ pSMP->addGeometryDirectly(ppoint);
+
+ lon_max = wxMax(lon_max, rseg[ip].x);
+ lon_min = wxMin(lon_min, rseg[ip].x);
+ lat_max = wxMax(lat_max, rseg[ip].y);
+ lat_min = wxMin(lat_min, rseg[ip].y);
+ }
+
+ ret_ptr->pogrGeom = pSMP;
+
+ ret_ptr->xmin = lon_min;
+ ret_ptr->xmax = lon_max;
+ ret_ptr->ymin = lat_min;
+ ret_ptr->ymax = lat_max;
+
+ break;
+ }
+
+ case 16:
+ break; // this is the case of objects with children
+ // the parent has no geometry.....
+
+ default: {
+ wxPrintf("Unexpected geomtype %d for Feature %d\n", geomtype, iobject);
+ break;
+ }
+
+ } // switch
+
+ return ret_ptr;
+}
+
+void cm93chart::Transform(cm93_point *s, double trans_x, double trans_y,
+ double *lat, double *lon) {
+ // Simple linear transform
+ double valx = (s->x * m_CIB.transform_x_rate) + m_CIB.transform_x_origin;
+ double valy = (s->y * m_CIB.transform_y_rate) + m_CIB.transform_y_origin;
+
+ // Add in the WGS84 offset corrections
+ valx -= trans_x;
+ valy -= trans_y;
+
+ // Convert to lat/lon
+ *lat =
+ (2.0 * atan(exp(valy / CM93_semimajor_axis_meters)) - PI / 2.) / DEGREE;
+ *lon = (valx / (DEGREE * CM93_semimajor_axis_meters));
+}
+
+cm93_attr_block::cm93_attr_block(void *block, cm93_dictionary *pdict) {
+ m_cptr = 0;
+ m_block = (unsigned char *)block;
+ m_pDict = pdict;
+}
+
+unsigned char *cm93_attr_block::GetNextAttr() {
+ // return current pointer
+ unsigned char *ret_val = m_block + m_cptr;
+
+ // Advance the pointer
+
+ unsigned char iattr = *(m_block + m_cptr);
+ m_cptr++;
+
+ // char vtype = m_pDict->m_ValTypeArray[iattr];
+ char vtype = m_pDict->GetAttrType(iattr);
+
+ switch (vtype) {
+ case 'I': // never seen?
+ m_cptr += 2;
+ break;
+ case 'B':
+ m_cptr += 1;
+ // pb = (unsigned char *)aval;
+ // sprintf(val, "%d", *pb);
+ // pvtype = 'I'; // override
+ break;
+ case 'S':
+ while (*(m_block + m_cptr)) m_cptr++;
+ m_cptr++; // skip terminator
+ // sprintf(val, "%s", aval);
+ break;
+ case 'R':
+ m_cptr += 4;
+ // pf = (float *)aval;
+ // sprintf(val, "%g", *pf);
+ break;
+ case 'W':
+ m_cptr += 2;
+ break;
+ case 'G':
+ m_cptr += 4;
+ break;
+ case 'C':
+ m_cptr += 3;
+ while (*(m_block + m_cptr)) m_cptr++;
+ m_cptr++; // skip terminator
+ // sprintf(val, "%s", &aval[3]);
+ // pvtype = 'S'; // override
+ break;
+ case 'L': {
+ unsigned char nl = *(m_block + m_cptr);
+ m_cptr++;
+ m_cptr += nl;
+
+ // pb = (unsigned char *)aval;
+ // unsigned char nl = *pb++;
+ // char vi[20];
+ // val[0] = 0;
+ // for(int i=0 ; ivalue;
+ int cur_attr = *pcur_attr;
+
+ wxString lstring;
+
+ switch (cur_attr) {
+ case 1:
+ lstring = "4";
+ break; // green
+ case 2:
+ lstring = "2";
+ break; // black
+ case 3:
+ lstring = "3";
+ break; // red
+ case 4:
+ lstring = "6";
+ break; // yellow
+ case 5:
+ lstring = "1";
+ break; // white
+ case 6:
+ lstring = "11";
+ break; // orange
+ case 7:
+ lstring = "2,6";
+ break; // black/yellow
+ case 8:
+ lstring = "2,6,2";
+ break; // black/yellow/black
+ case 9:
+ lstring = "6,2";
+ break; // yellow/black
+ case 10:
+ lstring = "6,2,6";
+ break; // yellow/black/yellow
+ case 11:
+ lstring = "3,1";
+ break; // red/white
+ case 12:
+ lstring = "4,3,4";
+ break; // green/red/green
+ case 13:
+ lstring = "3,4,3";
+ break; // red/green/red
+ case 14:
+ lstring = "2,3,2";
+ break; // black/red/black
+ case 15:
+ lstring = "6,3,6";
+ break; // yellow/red/yellow
+ case 16:
+ lstring = "4,3";
+ break; // green/red
+ case 17:
+ lstring = "3,4";
+ break; // red/green
+ case 18:
+ lstring = "4,1";
+ break; // green/white
+ default:
+ break;
+ }
+
+ if (lstring.Len()) {
+ free(pattValTmp->value); // free the old int pointer
+
+ pattValTmp->valType = OGR_STR;
+ pattValTmp->value = strdup(lstring.mb_str());
+ }
+}
+
+S57Obj *cm93chart::CreateS57Obj(int cell_index, int iobject, int subcell,
+ Object *pobject, cm93_dictionary *pDict,
+ Extended_Geometry *xgeom, double ref_lat,
+ double ref_lon, double scale,
+ double view_scale_ppm) {
+#define MAX_HDR_LINE 4000
+
+ // printf("%d\n", iobject);
+
+ int npub_year = 1993; // silly default
+
+ int iclass = pobject->otype;
+ int geomtype = pobject->geotype & 0x0f;
+
+ double tmp_transform_x = 0.;
+ double tmp_transform_y = 0.;
+
+ // Per object transfor offsets,
+ double trans_WGS84_offset_x = 0.;
+ double trans_WGS84_offset_y = 0.;
+
+ wxString sclass = pDict->GetClassName(iclass);
+ if (sclass == "Unknown") {
+ wxString msg;
+ msg.Printf(" CM93 Error...object type %d not found in CM93OBJ.DIC",
+ iclass);
+ wxLogMessage(msg);
+ delete xgeom;
+ return NULL;
+ }
+
+ wxString sclass_sub = sclass;
+
+ // Going to make some substitutions here
+ if (sclass.IsSameAs("ITDARE")) sclass_sub = "DEPARE";
+
+ if (sclass.IsSameAs("_m_sor")) sclass_sub = "M_COVR";
+
+ if (sclass.IsSameAs("SPOGRD")) sclass_sub = "DMPGRD";
+
+ if (sclass.IsSameAs("FSHHAV")) sclass_sub = "FSHFAC";
+
+ if (sclass.IsSameAs("OFSPRD")) sclass_sub = "CTNARE";
+
+ // Create the S57 Object
+ S57Obj *pobj = new S57Obj();
+
+ pobj->Index = iobject;
+
+ char u[201];
+ strncpy(u, sclass_sub.mb_str(), 199);
+ u[200] = '\0';
+ memcpy(pobj->FeatureName, u, 7);
+
+ pobj->attVal = new wxArrayOfS57attVal();
+
+ cm93_attr_block pab(pobject->attributes_block, pDict);
+
+ for (int jattr = 0; jattr < pobject->n_attributes; jattr++) {
+ unsigned char *curr_attr = pab.GetNextAttr();
+
+ unsigned char iattr = *curr_attr;
+
+ wxString sattr = pDict->GetAttrName(iattr);
+
+ char vtype = pDict->GetAttrType(iattr);
+
+ unsigned char *aval = curr_attr + 1;
+
+ char val[4000];
+ int *pi;
+ float *pf;
+ unsigned short *pw;
+ unsigned char *pb;
+ int *pAVI;
+ char *pAVS;
+ double *pAVR;
+ double dival;
+ int ival;
+
+ S57attVal *pattValTmp = new S57attVal;
+
+ switch (vtype) {
+ case 'I': // never seen?
+ pi = (int *)aval;
+ pAVI = (int *)malloc(sizeof(int)); // new int;
+ *pAVI = *pi;
+ pattValTmp->valType = OGR_INT;
+ pattValTmp->value = pAVI;
+ break;
+ case 'B':
+ pb = (unsigned char *)aval;
+ pAVI = (int *)malloc(sizeof(int)); // new int;
+ *pAVI = (int)(*pb);
+ pattValTmp->valType = OGR_INT;
+ pattValTmp->value = pAVI;
+ break;
+ case 'W': // aWORD10
+ pw = (unsigned short *)aval;
+ ival = (int)(*pw);
+ dival = ival;
+
+ pAVR = (double *)malloc(sizeof(double)); // new double;
+ *pAVR = dival / 10.;
+ pattValTmp->valType = OGR_REAL;
+ pattValTmp->value = pAVR;
+ break;
+ case 'G':
+ pi = (int *)aval;
+ pAVI = (int *)malloc(sizeof(int)); // new int;
+ *pAVI = (int)(*pi);
+ pattValTmp->valType = OGR_INT;
+ pattValTmp->value = pAVI;
+ break;
+
+ case 'S':
+ pAVS = strdup((char *)aval);
+ pattValTmp->valType = OGR_STR;
+ pattValTmp->value = pAVS;
+ break;
+
+ case 'C':
+ pAVS = strdup((const char *)&aval[3]);
+ pattValTmp->valType = OGR_STR;
+ pattValTmp->value = pAVS;
+ break;
+ case 'L': {
+ pb = (unsigned char *)aval;
+ unsigned char nl = *pb++;
+ char vi[20];
+ val[0] = 0;
+ for (int i = 0; i < nl; i++) {
+ sprintf(vi, "%d,", *pb++);
+ strcat(val, vi);
+ }
+ if (strlen(val)) val[strlen(val) - 1] = 0; // strip last ","
+
+ pAVS = strdup(val);
+ pattValTmp->valType = OGR_STR;
+ pattValTmp->value = pAVS;
+ break;
+ }
+ case 'R': {
+ pAVR = (double *)malloc(sizeof(double)); // new double;
+ pf = (float *)aval;
+#ifdef __ARM_ARCH
+ {
+ float __attribute__((aligned(16))) tf1;
+ unsigned char *pucf = (unsigned char *)pf;
+
+ memcpy(&tf1, pucf, sizeof(float));
+ *pAVR = tf1;
+ }
+#else
+ *pAVR = *pf;
+#endif
+ pattValTmp->valType = OGR_REAL;
+ pattValTmp->value = pAVR;
+ break;
+ }
+ default:
+ sattr.Clear(); // Unknown, TODO track occasional case '?'
+ break;
+ } // switch
+
+ if (sattr.IsSameAs("COLMAR")) {
+ translate_colmar(sclass, pattValTmp);
+ sattr = "COLOUR";
+ }
+ // XXX should be done from s57 list ans cm93 list for any mismatch
+ // ie cm93 QUASOU is an enum s57 is a list
+ if (pattValTmp->valType == OGR_INT &&
+ (sattr.IsSameAs("QUASOU") || sattr.IsSameAs("CATLIT"))) {
+ int v = *(int *)pattValTmp->value;
+ free(pattValTmp->value);
+ sprintf(val, "%d", v);
+ pAVS = strdup(val);
+ pattValTmp->valType = OGR_STR;
+ pattValTmp->value = pAVS;
+ }
+
+ // Do CM93 $SCODE attribute substitutions
+ if (sclass.IsSameAs("$AREAS") && (vtype == 'S') &&
+ sattr.IsSameAs("$SCODE")) {
+ if (!strcmp((char *)pattValTmp->value, "II25")) {
+ free(pattValTmp->value);
+ pattValTmp->value = strdup("BACKGROUND");
+ }
+ }
+
+ // Capture some attributes on the fly as needed
+ if (sattr.IsSameAs("RECDAT") || sattr.IsSameAs("_dgdat")) {
+ if (sclass_sub.IsSameAs("M_COVR") && (vtype == 'S')) {
+ wxString pub_date((char *)pattValTmp->value, wxConvUTF8);
+
+ wxDateTime upd;
+ upd.ParseFormat(pub_date, "%Y%m%d");
+ if (!upd.IsValid()) upd.ParseFormat("20000101", "%Y%m%d");
+ m_EdDate = upd;
+
+ pub_date.Truncate(4);
+
+ long nyear = 0;
+ pub_date.ToLong(&nyear);
+ npub_year = nyear;
+ }
+ }
+
+ // Capture the potential WGS84 transform offset for later use
+ if (sclass_sub.IsSameAs("M_COVR") && (vtype == 'R')) {
+ if (sattr.IsSameAs("_wgsox")) {
+ tmp_transform_x = *(double *)pattValTmp->value;
+ if (fabs(tmp_transform_x) > 1.0) // metres
+ m_CIB.b_have_offsets = true;
+ } else if (sattr.IsSameAs("_wgsoy")) {
+ tmp_transform_y = *(double *)pattValTmp->value;
+ if (fabs(tmp_transform_y) > 1.0) m_CIB.b_have_offsets = true;
+ }
+ }
+
+ if (sattr.Len()) {
+ wxASSERT(sattr.Len() == 6);
+ wxCharBuffer dbuffer = sattr.ToUTF8();
+ if (dbuffer.data()) {
+ pobj->att_array =
+ (char *)realloc(pobj->att_array, 6 * (pobj->n_attr + 1));
+
+ strncpy(pobj->att_array + (6 * sizeof(char) * pobj->n_attr),
+ dbuffer.data(), 6);
+ pobj->n_attr++;
+
+ pobj->attVal->Add(pattValTmp);
+ } else
+ delete pattValTmp;
+ } else
+ delete pattValTmp;
+
+ } // for
+
+ // ATON label optimization:
+ // Some CM93 ATON objects do not contain OBJNAM attribute, which means that
+ // no label is shown for these objects when ATON labals are requested Look
+ // for these cases, and change the INFORM attribute label to OBJNAM, if
+ // present.
+
+ if (1 == geomtype) {
+ if ((!strncmp(pobj->FeatureName, "LIT", 3)) ||
+ (!strncmp(pobj->FeatureName, "LIGHTS", 6)) ||
+ (!strncmp(pobj->FeatureName, "BCN", 3)) ||
+ (!strncmp(pobj->FeatureName, "_slgto", 6)) ||
+ (!strncmp(pobj->FeatureName, "_boygn", 6)) ||
+ (!strncmp(pobj->FeatureName, "_bcngn", 6)) ||
+ (!strncmp(pobj->FeatureName, "_extgn", 6)) ||
+ (!strncmp(pobj->FeatureName, "TOWERS", 6)) ||
+ (!strncmp(pobj->FeatureName, "BOY", 3))) {
+ bool bfound_OBJNAM = (pobj->GetAttributeIndex("OBJNAM") != -1);
+ bool bfound_INFORM = (pobj->GetAttributeIndex("INFORM") != -1);
+
+ if ((!bfound_OBJNAM) && (bfound_INFORM)) // can make substitution
+ {
+ char *patl = pobj->att_array;
+ for (int i = 0; i < pobj->n_attr; i++) { // find "INFORM"
+ if (!strncmp(patl, "INFORM", 6)) {
+ memcpy(patl, "OBJNAM", 6); // change to "OBJNAM"
+ break;
+ }
+
+ patl += 6;
+ }
+ }
+ }
+ }
+
+ switch (geomtype) {
+ case 4: {
+ pobj->Primitive_type = GEO_AREA;
+
+ // Check for and maintain the class array of M_COVR objects
+ if (sclass_sub.IsSameAs("M_COVR")) {
+ M_COVR_Desc *pmcd;
+
+ M_COVR_Desc *pmcd_look =
+ GetCoverSet()->Find_MCD(cell_index, iobject, subcell);
+ if (NULL == pmcd_look) // not found
+ {
+ double lat, lon;
+
+ pmcd = new M_COVR_Desc;
+
+ // Record unique identifiers for this M_COVR object
+ pmcd->m_cell_index = cell_index;
+ pmcd->m_object_id = iobject;
+ pmcd->m_subcell = subcell;
+
+ // User offsets start empty
+ pmcd->user_xoff = 0;
+ pmcd->user_yoff = 0;
+ pmcd->m_buser_offsets = false;
+
+ // Record the Publication Year of this cell
+ pmcd->m_npub_year = npub_year;
+
+ // Get number of exterior ring points(vertices)
+ int npta = xgeom->contour_array[0];
+ float_2Dpt *geoPt = new float_2Dpt[npta + 2]; // vertex array
+ float_2Dpt *ppt = geoPt;
+
+ pmcd->m_covr_lon_max = -1000.;
+ pmcd->m_covr_lon_min = 1000.;
+ pmcd->m_covr_lat_max = -1000.;
+ pmcd->m_covr_lat_min = 1000.;
+
+ // Transcribe exterior ring points to vertex array, in Lat/Lon
+ // coordinates
+ for (int ip = 0; ip < npta; ip++) {
+ cm93_point p;
+ p.x = (int)xgeom->vertex_array[ip + 1].m_x;
+ p.y = (int)xgeom->vertex_array[ip + 1].m_y;
+
+ Transform(&p, 0, 0, /*tmp_transform_x, tmp_transform_y,*/ &lat,
+ &lon);
+ ppt->x = lon;
+ ppt->y = lat;
+
+ pmcd->m_covr_lon_max = wxMax(pmcd->m_covr_lon_max, lon);
+ pmcd->m_covr_lon_min = wxMin(pmcd->m_covr_lon_min, lon);
+ pmcd->m_covr_lat_max = wxMax(pmcd->m_covr_lat_max, lat);
+ pmcd->m_covr_lat_min = wxMin(pmcd->m_covr_lat_min, lat);
+
+ ppt++;
+ }
+ pmcd->m_nvertices = npta;
+ pmcd->pvertices = geoPt;
+
+ pmcd->m_covr_bbox.Set(pmcd->m_covr_lat_min, pmcd->m_covr_lon_min,
+ pmcd->m_covr_lat_max, pmcd->m_covr_lon_max);
+
+ // Capture and store the potential WGS transform offsets grabbed
+ // during attribute decode
+ pmcd->transform_WGS84_offset_x = tmp_transform_x;
+ pmcd->transform_WGS84_offset_y = tmp_transform_y;
+
+ pmcd->m_centerlat_cos = cos(
+ ((pmcd->m_covr_lat_min + pmcd->m_covr_lat_max) / 2.) * PI / 180.);
+
+ // Add this MCD to the persistent class covr_set
+ GetCoverSet()->Add_Update_MCD(pmcd);
+
+ } else {
+ // If already in the coverset, are there user offsets applied to this
+ // MCD?
+ if (pmcd_look->m_buser_offsets) {
+ m_CIB.b_have_user_offsets = true;
+
+ m_CIB.user_xoff = pmcd_look->user_xoff;
+ m_CIB.user_yoff = pmcd_look->user_yoff;
+ }
+
+ pmcd = pmcd_look;
+ }
+
+ // Add this geometry to the currently loaded class M_COVR array
+ m_pcovr_array_loaded.Add(pmcd);
+
+ // Update the covr region
+ unsigned int n = pmcd->m_nvertices;
+ double *pts = new double[2 * n];
+
+ // copy into array of doubles
+ for (size_t i = 0; i < 2 * n; i++)
+ pts[i] = ((float *)pmcd->pvertices)[i];
+
+ // normalize to 0-360 coords for areas that cross 180 (will be adjusted
+ // in LLRegion)
+ if (LLRegion::PointsCCW(n, pts))
+ for (size_t i = 0; i < n; i++)
+ if (pts[2 * i + 1] < 0) pts[2 * i + 1] += 360;
+
+ // perform region union logic
+ LLRegion rgn_covr(n, pts);
+ m_region.Union(rgn_covr);
+ delete[] pts;
+
+ // Add the MCD it to the current (temporary) per cell list
+ // This array is used only to quickly find the M_COVR object
+ // parameters which apply to other objects loaded from this cell. We
+ // do this so we don't have to search the entire (worldwide) coverset
+ // for this chart scale
+ m_CIB.m_cell_mcovr_list.Append(pmcd);
+ }
+
+ // Declare x/y of the object to be average of all cm93points
+ pobj->x = (xgeom->xmin + xgeom->xmax) / 2.;
+ pobj->y = (xgeom->ymin + xgeom->ymax) / 2.;
+
+ // associate the vector(edge) index table
+ pobj->m_n_lsindex = xgeom->n_vector_indices;
+ pobj->m_lsindex_array =
+ xgeom->pvector_index; // object now owns the array
+ pobj->m_n_edge_max_points = 0; // xgeom->n_max_edge_points;
+
+ // Find the proper WGS offset for this object
+ if (m_CIB.b_have_offsets || m_CIB.b_have_user_offsets) {
+ double latc, lonc;
+ cm93_point pc;
+ pc.x = (short unsigned int)pobj->x;
+ pc.y = (short unsigned int)pobj->y;
+ Transform(&pc, 0., 0., &latc, &lonc);
+
+ M_COVR_Desc *pmcd = FindM_COVR_InWorkingSet(latc, lonc);
+ if (pmcd) {
+ trans_WGS84_offset_x = pmcd->user_xoff;
+ trans_WGS84_offset_y = pmcd->user_yoff;
+ }
+ }
+
+ // Set the s57obj bounding box as lat/lon
+ double lat1, lon1, lat2, lon2;
+ cm93_point p;
+
+ p.x = (int)xgeom->xmin;
+ p.y = (int)xgeom->ymin;
+ Transform(&p, trans_WGS84_offset_x, trans_WGS84_offset_y, &lat1, &lon1);
+ xgeom->ref_lat = lat1;
+ xgeom->ref_lon = lon1;
+
+ p.x = (int)xgeom->xmax;
+ p.y = (int)xgeom->ymax;
+ Transform(&p, trans_WGS84_offset_x, trans_WGS84_offset_y, &lat2, &lon2);
+ pobj->BBObj.Set(lat1, lon1, lat2, lon2);
+
+ // Set the object base point
+ p.x = (int)pobj->x;
+ p.y = (int)pobj->y;
+ Transform(&p, trans_WGS84_offset_x, trans_WGS84_offset_y, &lat1, &lon1);
+ pobj->m_lon = lon1;
+ pobj->m_lat = lat1;
+
+ if (1) {
+ // This will be a deferred tesselation.....
+
+ // Set up the conversion factors for use in the tesselator
+ xgeom->x_rate = m_CIB.transform_x_rate;
+ xgeom->x_offset = m_CIB.transform_x_origin - trans_WGS84_offset_x;
+ xgeom->y_rate = m_CIB.transform_y_rate;
+ xgeom->y_offset = m_CIB.transform_y_origin - trans_WGS84_offset_y;
+
+ pobj->pPolyTessGeo = new PolyTessGeo(xgeom);
+ }
+
+ break;
+ }
+
+ case 1: {
+ pobj->Primitive_type = GEO_POINT;
+ pobj->npt = 1;
+
+ pobj->x = xgeom->pointx;
+ pobj->y = xgeom->pointy;
+
+ double lat, lon;
+ cm93_point p;
+ p.x = xgeom->pointx;
+ p.y = xgeom->pointy;
+ Transform(&p, 0., 0., &lat, &lon);
+
+ // Find the proper WGS offset for this object
+ if (m_CIB.b_have_offsets || m_CIB.b_have_user_offsets) {
+ M_COVR_Desc *pmcd = FindM_COVR_InWorkingSet(lat, lon);
+ if (pmcd) {
+ trans_WGS84_offset_x = pmcd->user_xoff;
+ trans_WGS84_offset_y = pmcd->user_yoff;
+ }
+ }
+
+ // Transform again to pick up offsets
+ Transform(&p, trans_WGS84_offset_x, trans_WGS84_offset_y, &lat, &lon);
+
+ pobj->m_lat = lat;
+ pobj->m_lon = lon;
+
+ // make initial bounding box large enough for worst possible case
+ // it's not possible to know unless we knew the font, but this works
+ // except for huge font sizes
+ // this is not very good or accurate or efficient and hopefully we can
+ // replace the current bounding box logic with calculating logic
+ double llsize = 1e-3 / view_scale_ppm;
+
+ pobj->BBObj.Set(lat, lon, lat, lon);
+ pobj->BBObj.EnLarge(llsize);
+
+ break;
+ }
+
+ case 8: // wkbMultiPoint25D:
+ {
+ pobj->Primitive_type = GEO_POINT;
+
+ // Set the s57obj bounding box as lat/lon
+ double lat1, lon1, lat2, lon2;
+ cm93_point p;
+
+ p.x = (int)xgeom->xmin;
+ p.y = (int)xgeom->ymin;
+ Transform(&p, 0., 0., &lat1, &lon1);
+
+ p.x = (int)xgeom->xmax;
+ p.y = (int)xgeom->ymax;
+ Transform(&p, 0., 0., &lat2, &lon2);
+ pobj->BBObj.Set(lat1, lon1, lat2, lon2);
+
+ // and declare x/y of the object to be average of all cm93points
+ pobj->x = (xgeom->xmin + xgeom->xmax) / 2.;
+ pobj->y = (xgeom->ymin + xgeom->ymax) / 2.;
+
+ OGRMultiPoint *pGeo = (OGRMultiPoint *)xgeom->pogrGeom;
+ pobj->npt = pGeo->getNumGeometries();
+
+ pobj->geoPtz = (double *)malloc(pobj->npt * 3 * sizeof(double));
+ pobj->geoPtMulti = (double *)malloc(pobj->npt * 2 * sizeof(double));
+
+ double *pdd = pobj->geoPtz;
+ double *pdl = pobj->geoPtMulti;
+
+ for (int ip = 0; ip < pobj->npt; ip++) {
+ OGRPoint *ppt = (OGRPoint *)(pGeo->getGeometryRef(ip));
+
+ cm93_point p;
+ p.x = (int)ppt->getX();
+ p.y = (int)ppt->getY();
+ double depth = ppt->getZ();
+
+ double east = p.x;
+ double north = p.y;
+
+ double snd_trans_x = 0.;
+ double snd_trans_y = 0.;
+
+ // Find the proper offset for this individual sounding
+ if (m_CIB.b_have_user_offsets) {
+ double lats, lons;
+ Transform(&p, 0., 0., &lats, &lons);
+
+ M_COVR_Desc *pmcd = FindM_COVR_InWorkingSet(lats, lons);
+ if (pmcd) {
+ // For lat/lon calculation below
+ snd_trans_x = pmcd->user_xoff;
+ snd_trans_y = pmcd->user_yoff;
+
+ // Actual cm93 point of this sounding, back-converted from metres
+ // e/n
+ east -= pmcd->user_xoff / m_CIB.transform_x_rate;
+ north -= pmcd->user_yoff / m_CIB.transform_y_rate;
+ }
+ }
+
+ *pdd++ = east;
+ *pdd++ = north;
+ *pdd++ = depth;
+
+ // Save offset lat/lon of point in obj->geoPtMulti for later use in
+ // decomposed bboxes
+ Transform(&p, snd_trans_x, snd_trans_y, &lat1, &lon1);
+ *pdl++ = lon1;
+ *pdl++ = lat1;
+ }
+
+ // Set the object base point
+ p.x = (int)pobj->x;
+ p.y = (int)pobj->y;
+ Transform(&p, trans_WGS84_offset_x, trans_WGS84_offset_y, &lat1, &lon1);
+ pobj->m_lon = lon1;
+ pobj->m_lat = lat1;
+
+ delete pGeo;
+
+ break;
+ } // case 8
+
+ case 2: {
+ pobj->Primitive_type = GEO_LINE;
+
+ pobj->npt = xgeom->n_max_vertex;
+ pobj->geoPt = (pt *)xgeom->vertex_array;
+ xgeom->vertex_array = NULL; // object now owns the array
+
+ // Declare x/y of the object to be average of all cm93points
+ pobj->x = (xgeom->xmin + xgeom->xmax) / 2.;
+ pobj->y = (xgeom->ymin + xgeom->ymax) / 2.;
+
+ // associate the vector(edge) index table
+ pobj->m_n_lsindex = xgeom->n_vector_indices;
+ pobj->m_lsindex_array =
+ xgeom->pvector_index; // object now owns the array
+ pobj->m_n_edge_max_points = 0; // xgeom->n_max_edge_points;
+
+ // Find the proper WGS offset for this object
+ if (m_CIB.b_have_offsets || m_CIB.b_have_user_offsets) {
+ double latc, lonc;
+ cm93_point pc;
+ pc.x = (short unsigned int)pobj->x;
+ pc.y = (short unsigned int)pobj->y;
+ Transform(&pc, 0., 0., &latc, &lonc);
+
+ M_COVR_Desc *pmcd = FindM_COVR_InWorkingSet(latc, lonc);
+ if (pmcd) {
+ trans_WGS84_offset_x = pmcd->user_xoff;
+ trans_WGS84_offset_y = pmcd->user_yoff;
+ }
+ }
+
+ // Set the s57obj bounding box as lat/lon
+ double lat1, lon1, lat2, lon2;
+ cm93_point p;
+
+ p.x = (int)xgeom->xmin;
+ p.y = (int)xgeom->ymin;
+ Transform(&p, trans_WGS84_offset_x, trans_WGS84_offset_y, &lat1, &lon1);
+
+ p.x = (int)xgeom->xmax;
+ p.y = (int)xgeom->ymax;
+ Transform(&p, trans_WGS84_offset_x, trans_WGS84_offset_y, &lat2, &lon2);
+ pobj->BBObj.Set(lat1, lon1, lat2, lon2);
+
+ // Set the object base point
+ p.x = (int)pobj->x;
+ p.y = (int)pobj->y;
+ Transform(&p, trans_WGS84_offset_x, trans_WGS84_offset_y, &lat1, &lon1);
+ pobj->m_lon = lon1;
+ pobj->m_lat = lat1;
+
+ break;
+
+ } // case 2
+ default: {
+ // TODO GEO_PRIM here is a placeholder. Trace this code....
+ pobj->Primitive_type = GEO_PRIM;
+ break;
+ }
+
+ } // geomtype switch
+
+ // Is this a catagory-movable object?
+ if (!strncmp(pobj->FeatureName, "OBSTRN", 6) ||
+ !strncmp(pobj->FeatureName, "WRECKS", 6) ||
+ !strncmp(pobj->FeatureName, "DEPCNT", 6) ||
+ !strncmp(pobj->FeatureName, "UWTROC", 6)) {
+ pobj->m_bcategory_mutable = true;
+ } else {
+ pobj->m_bcategory_mutable = false;
+ }
+
+ // Build/Maintain a list of found OBJL types for later use
+ // And back-reference the appropriate list index in S57Obj for Display
+ // Filtering
+
+ pobj->iOBJL = -1; // deferred, done by OBJL filtering in the PLIB as needed
+
+ // Everything in Xgeom that is needed later has been given to the object
+ // So, the xgeom object can be deleted
+ // Except for area features, which will get deferred tesselation, and so need
+ // the Extended geometry point Those features will own the xgeom...
+ if (geomtype != 4) delete xgeom;
+
+ // Set the per-object transform coefficients
+ pobj->x_rate =
+ m_CIB.transform_x_rate *
+ (mercator_k0 * WGS84_semimajor_axis_meters / CM93_semimajor_axis_meters);
+ pobj->y_rate =
+ m_CIB.transform_y_rate *
+ (mercator_k0 * WGS84_semimajor_axis_meters / CM93_semimajor_axis_meters);
+ pobj->x_origin =
+ m_CIB.transform_x_origin *
+ (mercator_k0 * WGS84_semimajor_axis_meters / CM93_semimajor_axis_meters);
+ pobj->y_origin =
+ m_CIB.transform_y_origin *
+ (mercator_k0 * WGS84_semimajor_axis_meters / CM93_semimajor_axis_meters);
+
+ // Add in the possible offsets to WGS84 which come from the proper M_COVR
+ // containing this feature
+ pobj->x_origin -= trans_WGS84_offset_x;
+ pobj->y_origin -= trans_WGS84_offset_y;
+
+ // Mark the object chart type, for the convenience of S52PLIB
+ pobj->auxParm3 = CHART_TYPE_CM93;
+
+ return pobj;
+}
+
+// Find the proper M_COVR record within this current cell for this lat/lon
+M_COVR_Desc *cm93chart::FindM_COVR_InWorkingSet(double lat, double lon) {
+ M_COVR_Desc *ret = NULL;
+ // Default is to use the first M_COVR, the usual case
+ if (m_CIB.m_cell_mcovr_list.GetCount() == 1) {
+ wxList_Of_M_COVR_DescNode *node0 = m_CIB.m_cell_mcovr_list.GetFirst();
+ if (node0) ret = node0->GetData();
+ } else {
+ wxList_Of_M_COVR_DescNode *node = m_CIB.m_cell_mcovr_list.GetFirst();
+ while (node) {
+ M_COVR_Desc *pmcd = node->GetData();
+
+ if (G_PtInPolygon_FL(pmcd->pvertices, pmcd->m_nvertices, lon, lat)) {
+ ret = pmcd;
+ break;
+ }
+
+ node = node->GetNext();
+ }
+ }
+ return ret;
+}
+
+// Find the proper M_COVR record within this current cell for this lat/lon
+// And return the WGS84 offsets contained within
+wxPoint2DDouble cm93chart::FindM_COVROffset(double lat, double lon) {
+ wxPoint2DDouble ret(0., 0.);
+
+ // Default is to use the first M_COVR, the usual case
+ wxList_Of_M_COVR_DescNode *node0 = m_CIB.m_cell_mcovr_list.GetFirst();
+ if (node0) {
+ M_COVR_Desc *pmcd0 = node0->GetData();
+ ret.m_x = pmcd0->transform_WGS84_offset_x;
+ ret.m_y = pmcd0->transform_WGS84_offset_y;
+ }
+
+ // If there are more than one M_COVR in this cell, need to search
+ if (m_CIB.m_cell_mcovr_list.GetCount() > 1) {
+ wxList_Of_M_COVR_DescNode *node = m_CIB.m_cell_mcovr_list.GetFirst();
+ while (node) {
+ M_COVR_Desc *pmcd = node->GetData();
+
+ if (G_PtInPolygon_FL(pmcd->pvertices, pmcd->m_nvertices, lon, lat)) {
+ ret.m_x = pmcd->transform_WGS84_offset_x;
+ ret.m_y = pmcd->transform_WGS84_offset_y;
+ break;
+ }
+
+ node = node->GetNext();
+ }
+ }
+ return ret;
+}
+
+// Read the cm93 cell file header and create required Chartbase data
+// structures
+InitReturn cm93chart::CreateHeaderDataFromCM93Cell() {
+ // Figure out the scale from the file name
+ wxFileName fn(m_FullPath);
+ wxString ext = fn.GetExt();
+
+ int scale;
+ switch ((ext.mb_str())[(size_t)0]) {
+ case 'Z':
+ scale = 20000000;
+ break;
+ case 'A':
+ scale = 3000000;
+ break;
+ case 'B':
+ scale = 1000000;
+ break;
+ case 'C':
+ scale = 200000;
+ break;
+ case 'D':
+ scale = 100000;
+ break;
+ case 'E':
+ scale = 50000;
+ break;
+ case 'F':
+ scale = 20000;
+ break;
+ case 'G':
+ scale = 7500;
+ break;
+ default:
+ scale = 20000000;
+ break;
+ }
+
+ m_Chart_Scale = scale;
+
+ // Check with the manager to see if a chart of this scale has been
+ // processed If there is no manager, punt and open the chart
+ if (m_pManager) {
+ bool bproc = false;
+ switch (m_Chart_Scale) {
+ case 20000000:
+ bproc = m_pManager->m_bfoundZ;
+ break;
+ case 3000000:
+ bproc = m_pManager->m_bfoundA;
+ break;
+ case 1000000:
+ bproc = m_pManager->m_bfoundB;
+ break;
+ case 200000:
+ bproc = m_pManager->m_bfoundC;
+ break;
+ case 100000:
+ bproc = m_pManager->m_bfoundD;
+ break;
+ case 50000:
+ bproc = m_pManager->m_bfoundE;
+ break;
+ case 20000:
+ bproc = m_pManager->m_bfoundF;
+ break;
+ case 7500:
+ bproc = m_pManager->m_bfoundG;
+ break;
+ }
+
+ if (bproc) return INIT_FAIL_NOERROR;
+
+ // Inform the manager that a chart of this scale has been processed
+ switch (m_Chart_Scale) {
+ case 20000000:
+ m_pManager->m_bfoundZ = true;
+ break;
+ case 3000000:
+ m_pManager->m_bfoundA = true;
+ break;
+ case 1000000:
+ m_pManager->m_bfoundB = true;
+ break;
+ case 200000:
+ m_pManager->m_bfoundC = true;
+ break;
+ case 100000:
+ m_pManager->m_bfoundD = true;
+ break;
+ case 50000:
+ m_pManager->m_bfoundE = true;
+ break;
+ case 20000:
+ m_pManager->m_bfoundF = true;
+ break;
+ case 7500:
+ m_pManager->m_bfoundG = true;
+ break;
+ }
+ }
+
+ // Specify the whole world as chart coverage
+ m_FullExtent.ELON = 179.0;
+ m_FullExtent.WLON = -179.0;
+ m_FullExtent.NLAT = 80.0;
+ m_FullExtent.SLAT = -80.0;
+ m_bExtentSet = true;
+
+ // Populate one (huge) M_COVR Entry
+ m_nCOVREntries = 1;
+ m_pCOVRTablePoints = (int *)malloc(sizeof(int));
+ *m_pCOVRTablePoints = 4;
+ m_pCOVRTable = (float **)malloc(sizeof(float *));
+ float *pf = (float *)malloc(2 * 4 * sizeof(float));
+ *m_pCOVRTable = pf;
+ float *pfe = pf;
+
+ *pfe++ = m_FullExtent.NLAT; // LatMax;
+ *pfe++ = m_FullExtent.WLON; // LonMin;
+
+ *pfe++ = m_FullExtent.NLAT; // LatMax;
+ *pfe++ = m_FullExtent.ELON; // LonMax;
+
+ *pfe++ = m_FullExtent.SLAT; // LatMin;
+ *pfe++ = m_FullExtent.ELON; // LonMax;
+
+ *pfe++ = m_FullExtent.SLAT; // LatMin;
+ *pfe++ = m_FullExtent.WLON; // LonMin;
+
+ return INIT_OK;
+}
+
+void cm93chart::ProcessMCOVRObjects(int cell_index, char subcell) {
+ // Extract the m_covr structures inline
+
+ Object *pobject = m_CIB.pobject_block; // head of object array
+
+ int iObj = 0;
+ while (iObj < m_CIB.m_nfeature_records) {
+ if ((pobject != NULL)) {
+ // Look for and process m_covr object(s)
+ int iclass = pobject->otype;
+
+ wxString sclass = m_pDict->GetClassName(iclass);
+
+ if (sclass.IsSameAs("_m_sor")) {
+ M_COVR_Desc *pmcd =
+ m_pcovr_set->Find_MCD(cell_index, iObj, (int)subcell);
+ if (NULL == pmcd) {
+ Extended_Geometry *xgeom = BuildGeom(pobject, NULL, iObj);
+
+ // Decode the attributes, specifically looking for _wgsox, _wgsoy
+
+ double tmp_transform_x = 0.;
+ double tmp_transform_y = 0.;
+
+ cm93_attr_block pab(pobject->attributes_block, m_pDict);
+ for (int jattr = 0; jattr < pobject->n_attributes; jattr++) {
+ unsigned char *curr_attr = pab.GetNextAttr();
+ unsigned char iattr = *curr_attr;
+ wxString sattr = m_pDict->GetAttrName(iattr);
+ char vtype = m_pDict->GetAttrType(iattr);
+ unsigned char *aval = curr_attr + 1;
+
+ if (vtype == 'R') {
+ float *pf = (float *)aval;
+#ifdef __ARM_ARCH
+ float __attribute__((aligned(16))) tf1;
+ unsigned char *pucf = (unsigned char *)pf;
+ memcpy(&tf1, pucf, sizeof(float));
+ if (sattr.IsSameAs("_wgsox"))
+ tmp_transform_x = tf1;
+ else if (sattr.IsSameAs("_wgsoy"))
+ tmp_transform_y = tf1;
+#else
+ if (sattr.IsSameAs("_wgsox"))
+ tmp_transform_x = *pf;
+ else if (sattr.IsSameAs("_wgsoy"))
+ tmp_transform_y = *pf;
+#endif
+ }
+
+ } // for all attributes
+
+ if (NULL != xgeom) {
+ double lat, lon;
+
+ pmcd = new M_COVR_Desc;
+
+ // Record unique identifiers for this M_COVR object
+ pmcd->m_cell_index = cell_index;
+ pmcd->m_object_id = iObj;
+ pmcd->m_subcell = (int)subcell;
+
+ // Get number of exterior ring points(vertices)
+ int npta = xgeom->contour_array[0];
+ float_2Dpt *geoPt = new float_2Dpt[npta + 2]; // vertex array
+ float_2Dpt *ppt = geoPt;
+
+ // Transcribe exterior ring points to vertex array, in Lat/Lon
+ // coordinates
+ pmcd->m_covr_lon_max = -1000.;
+ pmcd->m_covr_lon_min = 1000.;
+ pmcd->m_covr_lat_max = -1000.;
+ pmcd->m_covr_lat_min = 1000.;
+
+ for (int ip = 0; ip < npta; ip++) {
+ cm93_point p;
+ p.x = (int)xgeom->vertex_array[ip + 1].m_x;
+ p.y = (int)xgeom->vertex_array[ip + 1].m_y;
+
+ Transform(&p, 0., 0., &lat, &lon);
+ ppt->x = lon;
+ ppt->y = lat;
+
+ pmcd->m_covr_lon_max = wxMax(pmcd->m_covr_lon_max, lon);
+ pmcd->m_covr_lon_min = wxMin(pmcd->m_covr_lon_min, lon);
+ pmcd->m_covr_lat_max = wxMax(pmcd->m_covr_lat_max, lat);
+ pmcd->m_covr_lat_min = wxMin(pmcd->m_covr_lat_min, lat);
+
+ ppt++;
+ }
+ pmcd->m_nvertices = npta;
+ pmcd->pvertices = geoPt;
+
+ pmcd->m_covr_bbox.Set(pmcd->m_covr_lat_min, pmcd->m_covr_lon_min,
+ pmcd->m_covr_lat_max, pmcd->m_covr_lon_max);
+
+ // Capture and store the potential WGS transform offsets grabbed
+ // during attribute decode
+ pmcd->transform_WGS84_offset_x = tmp_transform_x;
+ pmcd->transform_WGS84_offset_y = tmp_transform_y;
+
+ pmcd->m_centerlat_cos =
+ cos(((pmcd->m_covr_lat_min + pmcd->m_covr_lat_max) / 2.) * PI /
+ 180.);
+
+ // Add this object to the covr_set
+ m_pcovr_set->Add_Update_MCD(pmcd);
+
+ // Clean up the xgeom
+ free(xgeom->pvector_index);
+
+ delete xgeom;
+ }
+ }
+ }
+ }
+
+ else // objectdef == NULL
+ break;
+
+ pobject++;
+ iObj++;
+ }
+}
+
+bool cm93chart::UpdateCovrSet(ViewPort *vpt) {
+ // Create an array of CellIndexes covering the current viewport
+ std::vector vpcells = GetVPCellArray(*vpt);
+
+ // Check the member covr_set to see if all these viewport cells have had
+ // their m_covr loaded
+
+ for (unsigned int i = 0; i < vpcells.size(); i++) {
+ // If the cell is not already in the master coverset, go load enough of
+ // it to get the offsets and outlines.....
+ if (!m_pcovr_set->IsCovrLoaded(vpcells[i])) {
+ if (loadcell_in_sequence(vpcells[i], '0')) {
+ ProcessMCOVRObjects(vpcells[i], '0');
+ Unload_CM93_Cell(); // all done with this (sub)cell
+ }
+ m_pcovr_set->m_cell_hash[vpcells[i]] = 1;
+
+ char loadcell_key = 'A'; // starting subcells
+
+ // Load the subcells in sequence
+ // On successful load, add it to the covr set and process the cell
+ while (loadcell_in_sequence(vpcells[i], loadcell_key)) {
+ // Extract the m_covr structures inline
+
+ ProcessMCOVRObjects(vpcells[i], loadcell_key);
+
+ Unload_CM93_Cell(); // all done with this (sub)cell
+
+ loadcell_key++;
+
+ } // while
+ } // cell is not in
+ } // for cellindex array
+
+ return true;
+}
+
+bool cm93chart::IsPointInLoadedM_COVR(double xc, double yc) {
+ // Provisionally revert to older method pending investigation.
+#if 1
+ return m_region.Contains(yc, xc);
+#else
+ for (unsigned int im = 0; im < m_pcovr_array_loaded.GetCount(); im++) {
+ if (G_PtInPolygon_FL(m_pcovr_array_loaded[im]->pvertices,
+ m_pcovr_array_loaded[im]->m_nvertices, xc, yc))
+ return true;
+ }
+ return false;
+#endif
+}
+
+LLRegion cm93chart::GetValidRegion() { return m_region; }
+
+int cm93chart::loadcell_in_sequence(int cellindex, char subcell) {
+ int rv = loadsubcell(cellindex, subcell);
+
+ return rv;
+}
+
+int cm93chart::loadsubcell(int cellindex, wxChar sub_char) {
+ // Create the file name
+
+ int ilat = cellindex / 10000;
+ int ilon = cellindex % 10000;
+
+ if (g_bDebugCM93) {
+ double dlat = m_dval / 3.;
+ double dlon = m_dval / 3.;
+ double lat, lon;
+ Get_CM93_Cell_Origin(cellindex, GetNativeScale(), &lat, &lon);
+ printf(
+ "\n Attempting loadcell %d scale %lc, sub_char %lc at lat: %g/%g "
+ "lon:%g/%g\n",
+ cellindex, wxChar(m_scalechar[0]), sub_char, lat, lat + dlat, lon,
+ lon + dlon);
+ }
+
+ int jlat = (int)(((ilat - 30) / m_dval) * m_dval) + 30; // normalize
+ int jlon = (int)((ilon / m_dval) * m_dval);
+
+ int ilatroot = (((ilat - 30) / 60) * 60) + 30;
+ int ilonroot = (ilon / 60) * 60;
+
+ wxString file;
+ file.Printf("%04d%04d.", jlat, jlon);
+ file += m_scalechar;
+ file[0] = sub_char;
+
+ // We prefer to make use of the NoFind array to avoid file system access to
+ // cells known not to exist. However, when the arra becomes "large", then
+ // searching the array becomes slower than actually accessing the file system.
+ // So, detect this case, and skip the NoFind array if the array size is larger
+ // than nnn items. "nnn" determined by experimentation/intuition. Could also
+ // be platform dependent.
+ bool b_useNoFind = true;
+ if (m_noFindArray.GetCount() > 500) b_useNoFind = false;
+
+ wxString fileroot;
+ fileroot.Printf("%04d%04d", ilatroot, ilonroot);
+ appendOSDirSep(&fileroot);
+ fileroot.append(m_scalechar);
+ appendOSDirSep(&fileroot);
+ wxString key = fileroot;
+ key.append(file);
+ fileroot.Prepend(m_prefix);
+
+ file.Prepend(fileroot);
+
+ if (g_bDebugCM93) {
+ char sfile[200];
+ strncpy(sfile, file.mb_str(), 199);
+ sfile[199] = 0;
+ printf(" filename: %s\n", sfile);
+ }
+
+ bool bfound = false;
+ wxString compfile;
+ if (b_useNoFind) {
+ if (m_noFindArray.Index(key) == wxNOT_FOUND) {
+ if (::wxFileExists(file))
+ bfound = true;
+ else
+ m_noFindArray.Add(key);
+ }
+ } else {
+ if (::wxFileExists(file)) bfound = true;
+ ;
+ }
+
+ if (!bfound) { // try compressed version
+ if (b_useNoFind) {
+ if (m_noFindArray.Index(key + ".xz") == wxNOT_FOUND) {
+ if (::wxFileExists(file + ".xz")) {
+ compfile = file + ".xz";
+ }
+ } else {
+ m_noFindArray.Add(key + ".xz");
+ }
+ } else {
+ if (::wxFileExists(file + ".xz")) compfile = file + ".xz";
+ }
+ }
+
+ // Try again with alternate scale character
+ if (!bfound && !compfile.Length()) {
+ // Try with alternate case of m_scalechar
+ wxString new_scalechar = m_scalechar.Lower();
+
+ wxString file1;
+ file1.Printf("%04d%04d.", jlat, jlon);
+ file1 += new_scalechar;
+ file1[0] = sub_char;
+
+ fileroot.Printf("%04d%04d", ilatroot, ilonroot);
+ appendOSDirSep(&fileroot);
+ fileroot.append(new_scalechar);
+ appendOSDirSep(&fileroot);
+ key = fileroot;
+ key.append(file1);
+
+ fileroot.Prepend(m_prefix);
+
+ file1.Prepend(fileroot);
+
+ if (b_useNoFind) {
+ if (m_noFindArray.Index(key) == wxNOT_FOUND) {
+ if (::wxFileExists(file1)) {
+ bfound = true;
+ file = file1; // found the file as lowercase, substitute the name
+ } else {
+ m_noFindArray.Add(key);
+ }
+ }
+ } else {
+ if (::wxFileExists(file1)) {
+ bfound = true;
+ file = file1; // found the file as lowercase, substitute the name
+ }
+ }
+
+ if (!bfound) { // try compressed version
+ if (b_useNoFind) {
+ if (m_noFindArray.Index(key + ".xz") == wxNOT_FOUND) {
+ if (::wxFileExists(file1 + ".xz"))
+ compfile = file1 + ".xz";
+ else
+ m_noFindArray.Add(key + ".xz");
+ }
+ } else {
+ if (::wxFileExists(file1 + ".xz")) compfile = file1 + ".xz";
+ }
+ }
+ }
+
+ if (g_bDebugCM93) {
+ printf("noFind count: %d\n", (int)m_noFindArray.GetCount());
+ }
+
+ if (!bfound && !compfile.Length()) return 0;
+
+ // File is known to exist
+
+ wxString msg("Loading CM93 cell ");
+ msg += file;
+ wxLogMessage(msg);
+
+ // Set the member variable to be the actual file name for use in single
+ // chart mode info display
+ m_LastFileName = file;
+
+ // Decompress if needed
+ if (compfile.Length()) {
+ file = wxFileName::CreateTempFileName(wxFileName(compfile).GetFullName());
+ if (!DecompressXZFile(compfile, file)) {
+ wxRemoveFile(file);
+ return 0;
+ }
+ }
+
+ if (g_bDebugCM93) {
+ char str[256];
+ strncpy(str, msg.mb_str(), 255);
+ str[255] = 0;
+ printf(" %s\n", str);
+ }
+
+ // Ingest it
+ if (!Ingest_CM93_Cell((const char *)file.mb_str(), &m_CIB)) {
+ wxString msg(" cm93chart Error ingesting ");
+ msg.Append(file);
+ wxLogMessage(msg);
+
+ if (compfile.Length()) wxRemoveFile(file);
+ return 0;
+ }
+
+ if (compfile.Length()) wxRemoveFile(file);
+
+ return 1;
+}
+
+void cm93chart::SetUserOffsets(int cell_index, int object_id, int subcell,
+ int xoff, int yoff) {
+ M_COVR_Desc *pmcd = GetCoverSet()->Find_MCD(cell_index, object_id, subcell);
+ if (pmcd) {
+ pmcd->user_xoff = xoff;
+ pmcd->user_yoff = yoff;
+ pmcd->m_buser_offsets = true;
+ }
+}
+
+wxPoint *cm93chart::GetDrawBuffer(int nSize) {
+ // Reallocate the cm93chart DrawBuffer if it is currently too small
+ if (nSize > m_nDrawBufferSize) {
+ wxPoint *tmp = m_pDrawBuffer;
+ m_pDrawBuffer =
+ (wxPoint *)realloc(m_pDrawBuffer, sizeof(wxPoint) * (nSize + 1));
+ if (NULL == m_pDrawBuffer) {
+ free(tmp);
+ tmp = NULL;
+ } else
+ m_nDrawBufferSize = nSize + 1;
+ }
+ return m_pDrawBuffer;
+}
+
+//-----------------------------------------------------------------------------------------------
+// cm93manager Implementation
+//-----------------------------------------------------------------------------------------------
+
+cm93manager::cm93manager() {
+ m_pcm93Dict = NULL;
+
+ m_bfoundA = false;
+ m_bfoundB = false;
+ m_bfoundC = false;
+ m_bfoundD = false;
+ m_bfoundE = false;
+ m_bfoundF = false;
+ m_bfoundG = false;
+ m_bfoundZ = false;
+}
+
+cm93manager::~cm93manager() { delete m_pcm93Dict; }
+
+bool cm93manager::Loadcm93Dictionary(const wxString &name) {
+ // Find and load cm93_dictionary
+ if (!m_pcm93Dict) {
+ m_pcm93Dict = FindAndLoadDict(name);
+
+ if (!m_pcm93Dict) {
+ wxLogMessage(" Cannot load CM93 Dictionary.");
+ return false;
+ }
+
+ if (!m_pcm93Dict->IsOk()) {
+ wxLogMessage(" Error in loading CM93 Dictionary.");
+ delete m_pcm93Dict;
+ m_pcm93Dict = NULL;
+ return false;
+ ;
+ }
+ } else if (!m_pcm93Dict->IsOk()) {
+ wxLogMessage(" CM93 Dictionary is not OK.");
+ return false;
+ }
+
+ return true;
+}
+
+cm93_dictionary *cm93manager::FindAndLoadDict(const wxString &file) {
+ cm93_dictionary *retval = NULL;
+ cm93_dictionary *pdict = new cm93_dictionary();
+
+ // Search for the dictionary files all along the path of the passed
+ // parameter filename
+
+ wxFileName fn(file);
+ wxString path = fn.GetPath((int)(wxPATH_GET_SEPARATOR | wxPATH_GET_VOLUME));
+ wxString target;
+ unsigned int i = 0;
+
+ while (i < path.Len()) {
+ target.Append(path[i]);
+ if (path[i] == fn.GetPathSeparator()) {
+ if (pdict->LoadDictionary(target)) {
+ retval = pdict;
+ break;
+ }
+ if (pdict->LoadDictionary(target + "CM93ATTR")) {
+ retval = pdict;
+ break;
+ }
+ }
+ i++;
+ }
+
+ char t[100];
+ strncpy(t, target.mb_str(), 99);
+
+ if (retval == NULL) delete pdict;
+
+ return retval;
+}
+
+//----------------------------------------------------------------------------
+// cm93 Composite Chart object class Implementation
+//----------------------------------------------------------------------------
+cm93compchart::cm93compchart() {
+ m_ChartType = CHART_TYPE_CM93COMP;
+ m_pDictComposite = NULL;
+
+ // Supply a default name for status bar field
+ m_FullPath = "CM93";
+
+ // Set the "Description", so that it paints nice on the screen
+ m_Description = "CM93Composite";
+
+ m_SE = "";
+ m_datum_str = "WGS84";
+ m_SoundingsDatum = "Unknown";
+
+ for (int i = 0; i < 8; i++) m_pcm93chart_array[i] = NULL;
+
+ m_pcm93chart_current = NULL;
+
+ m_cmscale = -1;
+ m_Chart_Skew = 0.0;
+
+ m_pDummyBM = NULL;
+
+ SetSpecialOutlineCellIndex(0, 0, 0);
+ m_last_cell_adjustvp = NULL;
+
+ m_pcm93mgr = new cm93manager();
+}
+
+cm93compchart::~cm93compchart() {
+ if (g_pCM93OffsetDialog) {
+ g_pCM93OffsetDialog->Hide();
+ }
+
+ for (int i = 0; i < 8; i++) delete m_pcm93chart_array[i];
+
+ delete m_pDictComposite;
+ delete m_pDummyBM;
+ delete m_pcm93mgr;
+}
+
+InitReturn cm93compchart::Init(const wxString &name, ChartInitFlag flags) {
+ m_FullPath = name;
+
+ wxFileName fn(name);
+
+ wxString target;
+ wxString path;
+
+ // Verify that the passed file name exists
+ if (!fn.FileExists()) {
+ // It may be a directory
+ if (wxDir::Exists(name)) {
+ target = name;
+ appendOSDirSep(&target);
+ path = name;
+ appendOSDirSep(&path);
+ } else {
+ wxString msg(" CM93Composite Chart Init cannot find ");
+ msg.Append(name);
+ wxLogMessage(msg);
+ return INIT_FAIL_REMOVE;
+ }
+ } else // its a file that exists
+ {
+ // Get the cm93 cell database prefix
+ path = fn.GetPath((int)(wxPATH_GET_SEPARATOR | wxPATH_GET_VOLUME));
+
+ // Remove two subdirectories from the passed file name
+ // This will give a normal CM93 root
+ wxFileName file_path(path);
+ file_path.RemoveLastDir();
+ file_path.RemoveLastDir();
+
+ target = file_path.GetPath(wxPATH_GET_VOLUME | wxPATH_GET_SEPARATOR);
+ }
+
+ m_prefixComposite = target;
+
+ wxString msg("CM93Composite Chart Root is ");
+ msg.Append(m_prefixComposite);
+ wxLogMessage(msg);
+
+ if (flags == THUMB_ONLY) {
+ // SetColorScheme(cs, false);
+
+ return INIT_OK;
+ }
+
+ if (flags == HEADER_ONLY) return CreateHeaderData();
+
+ // Load the cm93 dictionary if necessary
+ if (!m_pDictComposite) {
+ if (!m_pDictComposite) // second try from the file
+ m_pDictComposite = FindAndLoadDictFromDir(path);
+
+ if (!m_pDictComposite) {
+ wxLogMessage(
+ " CM93Composite Chart Init cannot locate CM93 dictionary.");
+ return INIT_FAIL_REMOVE;
+ }
+ }
+
+ // Set the color scheme
+ SetColorScheme(m_global_color_scheme, false);
+
+ bReadyToRender = true;
+
+ return INIT_OK;
+}
+
+void cm93compchart::Activate() {
+ // if ( g_bShowCM93DetailSlider )
+ // {
+ // if ( !pPopupDetailSlider )
+ // {
+ // pPopupDetailSlider = new PopUpDSlide ( gFrame, -1 , 0,
+ // -CM93_ZOOM_FACTOR_MAX_RANGE, CM93_ZOOM_FACTOR_MAX_RANGE,
+ // wxPoint (
+ // g_cm93detail_dialog_x,
+ // g_cm93detail_dialog_y
+ // ), wxDefaultSize,
+ // wxSIMPLE_BORDER
+ // , _T (
+ // "cm93
+ // Detail"
+ // ) );
+ // }
+ //
+ // // Here is an ugly piece of code which prevents the slider
+ // from taking the keyboard focus
+ // // Only seems to work for Windows.....
+ // pPopupDetailSlider->Disable();
+ // pPopupDetailSlider->Show();
+ // pPopupDetailSlider->Enable();
+ // }
+}
+
+void cm93compchart::Deactivate() {
+ if (pPopupDetailSlider) {
+ pPopupDetailSlider->Destroy();
+ pPopupDetailSlider = NULL;
+ }
+}
+
+double scale_breaks[] = {
+ 5000., // G
+ 15000., // F
+ 40000., // E
+ 150000., // D
+ 300000., // C
+ 1000000., // B
+ 5000000., // A
+ 20000000. // Z
+};
+
+//-----------------------------------------------------------------------
+// Calculate and Set ViewPoint Constants
+//-----------------------------------------------------------------------
+
+int cm93compchart::GetCMScaleFromVP(const ViewPort &vpt) {
+ double scale_mpp = 3000 / vpt.view_scale_ppm;
+
+ double scale_mpp_adj = scale_mpp;
+
+ double scale_breaks_adj[7];
+
+ for (int i = 0; i < 7; i++) scale_breaks_adj[i] = scale_breaks[i];
+
+ if (g_cm93_zoom_factor) {
+#if 0
+ // Completely intuitive exponential curve adjustment
+ double efactor = ( double ) ( g_cm93_zoom_factor ) * ( .176 / 7. );
+ for ( int i=0 ; i < 7 ; i++ )
+ {
+ double efr = efactor * ( 7 - i );
+ scale_breaks_adj[i] = scale_breaks[i] * pow ( 10., efr );
+ if ( g_bDebugCM93 )
+ printf ( "g_cm93_zoom_factor: %2d efactor: %6g efr:%6g, scale_breaks[i]:%6g scale_breaks_adj[i]: %6g\n",
+ g_cm93_zoom_factor, efactor, efr, scale_breaks[i], scale_breaks_adj[i] );
+ }
+#else
+ // improved adjustment for small scales
+ double efr = (double)g_cm93_zoom_factor * pow(scale_mpp, -.05);
+ scale_mpp_adj *= pow(.6, efr);
+#endif
+ }
+
+ int cmscale_calc = 7;
+ int brk_index = 0;
+ while (cmscale_calc > 0) {
+ if (scale_mpp_adj < scale_breaks_adj[brk_index]) break;
+ cmscale_calc--;
+ brk_index++;
+ }
+
+ // Check for overzoom at the theoretically calcuolated chart scale
+ // If overzoomed possible, switch to larger scale chart if available
+ double zoom_factor = scale_breaks[7 - cmscale_calc] / vpt.chart_scale;
+ if (zoom_factor > 4.0) {
+ if (cmscale_calc < 7) cmscale_calc++;
+ }
+
+ return cmscale_calc;
+}
+
+void cm93compchart::SetVPParms(const ViewPort &vpt) {
+ m_vpt = vpt; // save a copy
+
+ int cmscale = GetCMScaleFromVP(vpt); // First order calculation of cmscale
+ m_cmscale = PrepareChartScale(vpt, cmscale, false);
+
+ // Continuoesly update the composite chart edition date to the latest cell
+ // decoded
+ if (m_pcm93chart_array[cmscale]) {
+ if (!m_EdDate.IsValid() ||
+ !m_pcm93chart_array[cmscale]->GetEditionDate().IsValid() ||
+ m_pcm93chart_array[cmscale]->GetEditionDate().IsLaterThan(m_EdDate))
+ m_EdDate = m_pcm93chart_array[cmscale]->GetEditionDate();
+ }
+}
+
+int cm93compchart::PrepareChartScale(const ViewPort &vpt, int cmscale,
+ bool bOZ_protect) {
+ if (g_bDebugCM93)
+ printf("\non SetVPParms, cmscale:%d, %c\n", cmscale,
+ (char)('A' + cmscale - 1));
+
+ wxChar ext;
+ bool cellscale_is_useable = false;
+ bool b_nochart = false;
+
+ while (!cellscale_is_useable) {
+ // Open the proper scale chart, if not already open
+ while (NULL == m_pcm93chart_array[cmscale]) {
+ if (Is_CM93Cell_Present(m_prefixComposite, vpt.clat, vpt.clon, cmscale)) {
+ if (g_bDebugCM93)
+ printf(" chart %c at VP clat/clon is present\n",
+ (char)('A' + cmscale - 1));
+
+ m_pcm93chart_array[cmscale] = new cm93chart();
+
+ ext = (wxChar)('A' + cmscale - 1);
+ if (cmscale == 0) ext = 'Z';
+
+ wxString file_dummy = "CM93.";
+ file_dummy << ext;
+
+ m_pcm93chart_array[cmscale]->SetCM93Dict(m_pDictComposite);
+ m_pcm93chart_array[cmscale]->SetCM93Prefix(m_prefixComposite);
+ m_pcm93chart_array[cmscale]->SetCM93Manager(m_pcm93mgr);
+
+ m_pcm93chart_array[cmscale]->SetColorScheme(m_global_color_scheme);
+ m_pcm93chart_array[cmscale]->Init(file_dummy, FULL_INIT);
+ } else if (cmscale == 0) {
+ // wxString msg;
+ // msg.Printf ( _T ( " CM93 finds no chart of
+ // any scale present at Lat/Lon %g %g" ),
+ // vpt.clat, vpt.clon ); wxLogMessage ( msg );
+ if (g_bDebugCM93)
+ printf(
+ " CM93 finds no chart of any scale present at Lat/Lon %g %g\n",
+ vpt.clat, vpt.clon);
+
+ b_nochart = true;
+ break;
+ }
+
+ else {
+ cmscale--; // revert to smaller scale if selected is not present
+ if (g_bDebugCM93)
+ printf(" no %c scale chart present, adjusting cmscale to %c\n",
+ (char)('A' + cmscale), (char)('A' + cmscale - 1));
+ }
+ }
+
+ m_pcm93chart_current = m_pcm93chart_array[cmscale];
+
+ if (b_nochart) {
+ if (g_bDebugCM93) printf(" b_nochart return\n");
+
+ m_pcm93chart_current = NULL;
+ for (int i = 0; i < 8; i++) {
+ delete m_pcm93chart_array[i];
+ m_pcm93chart_array[i] = NULL;
+ }
+
+ return cmscale;
+ }
+
+ if (m_pcm93chart_current) {
+ // Pass the parameters to the proper scale chart
+ // Which will also load the needed cell(s)
+ m_pcm93chart_current->SetVPParms(vpt);
+
+ // Check to see if the viewpoint center is actually on the selected
+ // chart
+ float yc = vpt.clat;
+ float xc = vpt.clon;
+
+ if (!m_pcm93chart_current->GetCoverSet()->GetCoverCount()) {
+ if (g_bDebugCM93)
+ printf(" chart %c has no M_COVR\n", (char)('A' + cmscale - 1));
+ }
+
+ if (m_pcm93chart_current->IsPointInLoadedM_COVR(xc, yc)) {
+ if (g_bDebugCM93)
+ printf(" chart %c contains clat/clon\n", (char)('A' + cmscale - 1));
+
+ cellscale_is_useable = true;
+ break;
+ }
+
+ // This commented block assumed that scale 0 coverage is available
+ // worlwide..... Might not be so with partial CM93 sets
+ /*
+ else if(cmscale == 0)
+ {
+ cellscale_is_useable = true;
+ break;
+ }
+ */
+
+ else if (vpt.b_quilt && vpt.b_FullScreenQuilt) {
+ ViewPort vp = vpt;
+
+ covr_set *pcover = m_pcm93chart_current->GetCoverSet();
+ if (pcover) {
+ bool boverlap = false;
+ for (unsigned int im = 0; im < pcover->GetCoverCount(); im++) {
+ M_COVR_Desc *mcd = pcover->GetCover(im);
+
+ if (!(vp.GetBBox().IntersectOut(mcd->m_covr_bbox))) {
+ boverlap = true;
+ break;
+ }
+ }
+ if (boverlap) cellscale_is_useable = true;
+ }
+ }
+
+ if (!cellscale_is_useable) {
+ if (cmscale > 0)
+ cmscale--; // revert to larger scale if the current scale cells do
+ // not contain VP
+ else
+ b_nochart = true; // we have retired to scale 0, and still no chart
+ // coverage, so stop already...
+ if (g_bDebugCM93)
+ printf(" VP is not in M_COVR, adjusting cmscale to %c\n",
+ (char)('A' + cmscale - 1));
+ }
+ }
+ }
+
+ // Final check the zoom factor
+ if (bOZ_protect) {
+ double zoom_factor = scale_breaks[7 - cmscale] / vpt.chart_scale;
+
+ if (zoom_factor > 4.0) {
+ // See if there is a larger scale chart present that will avoid overzoom
+
+ float yc = vpt.clat;
+ float xc = vpt.clon;
+
+ // Find out what the smallest available scale is that is not overzoomed
+ FillScaleArray(vpt.clat, vpt.clon);
+ int new_scale = cmscale;
+ bool b_found = false;
+ while (new_scale <= 7) {
+ if (m_bScale_Array[new_scale]) {
+ double new_zoom_factor =
+ scale_breaks[7 - new_scale] / vpt.chart_scale;
+
+ // Do not allow excessive "under-zoom", for performance reasons
+ if (new_zoom_factor < 1.0) {
+ b_found = true;
+ new_scale = cmscale;
+ break;
+ }
+
+ if (new_zoom_factor < 4.0) {
+ if (NULL == m_pcm93chart_array[new_scale]) {
+ m_pcm93chart_array[new_scale] = new cm93chart();
+
+ ext = (wxChar)('A' + new_scale - 1);
+ if (new_scale == 0) ext = 'Z';
+
+ wxString file_dummy = "CM93.";
+ file_dummy << ext;
+
+ m_pcm93chart_array[new_scale]->SetCM93Dict(m_pDictComposite);
+ m_pcm93chart_array[new_scale]->SetCM93Prefix(m_prefixComposite);
+ m_pcm93chart_array[new_scale]->SetCM93Manager(m_pcm93mgr);
+
+ m_pcm93chart_array[new_scale]->SetColorScheme(
+ m_global_color_scheme);
+ m_pcm93chart_array[new_scale]->Init(file_dummy, FULL_INIT);
+ }
+
+ m_pcm93chart_array[new_scale]->SetVPParms(vpt);
+ if (m_pcm93chart_array[new_scale]->IsPointInLoadedM_COVR(xc, yc)) {
+ b_found = true;
+ break;
+ }
+ }
+ }
+ new_scale++;
+ }
+ if (b_found) {
+ cmscale = new_scale;
+ m_pcm93chart_current = m_pcm93chart_array[cmscale];
+ }
+ }
+ }
+
+ return cmscale;
+}
+
+// Populate the member bool array describing which chart scales are available
+// at any location
+void cm93compchart::FillScaleArray(double lat, double lon) {
+ for (int cmscale = 0; cmscale < 8; cmscale++)
+ m_bScale_Array[cmscale] =
+ Is_CM93Cell_Present(m_prefixComposite, lat, lon, cmscale);
+}
+
+// These methods simply pass the called parameters to the currently active
+// cm93chart
+
+wxString cm93compchart::GetPubDate() {
+ wxString data;
+
+ if (NULL != m_pcm93chart_current)
+
+ data.Printf("%4d", m_current_cell_pub_date);
+ else
+ data = "????";
+ return data;
+}
+
+int cm93compchart::GetNativeScale() {
+ if (m_pcm93chart_current)
+ return m_pcm93chart_current->GetNativeScale();
+ else
+ return (int)1e8;
+}
+
+double cm93compchart::GetNormalScaleMin(double canvas_scale_factor,
+ bool b_allow_overzoom) {
+ double oz_factor;
+ oz_factor = 40.;
+
+ if (m_pcm93chart_current) {
+ int cmscale = 0;
+ if (m_pcm93chart_current->m_last_vp.IsValid()) {
+ FillScaleArray(m_pcm93chart_current->m_last_vp.clat,
+ m_pcm93chart_current->m_last_vp.clon);
+
+ // Find out what the smallest available scale is
+ cmscale = 7;
+ while (cmscale > 0) {
+ if (m_bScale_Array[cmscale]) break;
+ cmscale--;
+ }
+ }
+
+ // And return a sensible minimum scale, allowing selected overzoom.
+ switch (cmscale) {
+ case 0:
+ return 20000000. / oz_factor; // Z
+ case 1:
+ return 3000000. / oz_factor; // A
+ case 2:
+ return 1000000. / oz_factor; // B
+ case 3:
+ return 200000. / oz_factor; // C
+ case 4:
+ return 100000. / oz_factor; // D
+ case 5:
+ return 50000. / oz_factor; // E
+ case 6:
+ return 20000. / oz_factor; // F
+ case 7:
+ return 500.; // G
+ default:
+ return 500. / oz_factor;
+ }
+ } else
+ return 500.;
+}
+
+double cm93compchart::GetNormalScaleMax(double canvas_scale_factor,
+ int canvas_width) {
+ return (180. / 360.) * PI * 2 *
+ (WGS84_semimajor_axis_meters / (canvas_width / canvas_scale_factor));
+ // return 1.0e8;
+}
+
+wxPoint GetPixFromLLVP(double lat, double lon, const ViewPort &VPoint) {
+ // Inline the Simple Mercator Transform for performance reasons
+ double easting, northing;
+
+ double s, y3, s0, y30;
+ double z = WGS84_semimajor_axis_meters * mercator_k0;
+
+ double xlon = lon;
+
+ /* Make sure lon and lon0 are same phase */
+ if (lon * VPoint.clon < 0.) {
+ if (lon < 0.)
+ xlon += 360.;
+ else
+ xlon -= 360.;
+ }
+
+ // And choose the closest direction
+ if (fabs(xlon - VPoint.clon) > 180.) {
+ if (xlon > VPoint.clon)
+ xlon -= 360.;
+ else
+ xlon += 360.;
+ }
+ easting = (xlon - VPoint.clon) * DEGREE * z;
+
+ s = sin(lat * DEGREE);
+ y3 = (.5 * log((1 + s) / (1 - s))) * z;
+
+ s0 = sin(VPoint.clat * DEGREE);
+ y30 = (.5 * log((1 + s0) / (1 - s0))) * z;
+ northing = y3 - y30;
+
+ wxPoint r;
+
+ double epix = easting * VPoint.view_scale_ppm;
+ double npix = northing * VPoint.view_scale_ppm;
+ r.x = (int)round((VPoint.pix_width / 2) + epix);
+ r.y = (int)round((VPoint.pix_height / 2) - npix);
+
+ return r;
+}
+
+// extern void catch_signals(int signo);
+
+void cm93compchart::GetValidCanvasRegion(const ViewPort &VPoint,
+ OCPNRegion *pValidRegion) {
+ OCPNRegion screen_region(0, 0, VPoint.pix_width, VPoint.pix_height);
+ OCPNRegion ret = GetValidScreenCanvasRegion(
+ VPoint, g_bopengl ? VPoint.rv_rect : screen_region);
+ *pValidRegion = ret;
+}
+
+OCPNRegion cm93compchart::GetValidScreenCanvasRegion(
+ const ViewPort &VPoint, const OCPNRegion &ScreenRegion) {
+ OCPNRegion ret_region;
+
+ ViewPort vp = VPoint;
+
+ vp.rotation = 0.;
+
+ if (m_pcm93chart_current) {
+ int chart_native_scale = m_pcm93chart_current->GetNativeScale();
+
+ for (unsigned int im = 0;
+ im < m_pcm93chart_current->m_pcovr_array_loaded.GetCount(); im++) {
+ M_COVR_Desc *pmcd = (m_pcm93chart_current->m_pcovr_array_loaded[im]);
+
+ // We can make a quick test based on the bbox of the M_COVR and the
+ // bbox of the ViewPort
+
+ if (vp.GetBBox().IntersectOut(pmcd->m_covr_bbox)) continue;
+
+ wxPoint *DrawBuf = m_pcm93chart_current->GetDrawBuffer(pmcd->m_nvertices);
+
+ OCPNRegion rgn_covr = vp.GetVPRegionIntersect(
+ ScreenRegion, pmcd->m_nvertices, (float *)pmcd->pvertices,
+ chart_native_scale, DrawBuf);
+
+ if (rgn_covr.IsOk()) // not empty
+ ret_region.Union(rgn_covr);
+ }
+
+ } else
+ ret_region.Union(OCPNRegion(0, 0, 1, 1));
+
+ return ret_region;
+}
+
+LLRegion cm93compchart::GetValidRegion() {
+ if (m_pcm93chart_current) return m_pcm93chart_current->GetValidRegion();
+
+ return LLRegion(); // empty region
+}
+
+bool cm93compchart::RenderRegionViewOnGL(const wxGLContext &glc,
+ const ViewPort &VPoint,
+ const OCPNRegion &RectRegion,
+ const LLRegion &Region) {
+ SetVPParms(VPoint);
+
+ if (g_pCM93OffsetDialog && g_pCM93OffsetDialog->IsShown())
+ g_pCM93OffsetDialog->UpdateMCOVRList(VPoint);
+
+ return DoRenderRegionViewOnGL(glc, VPoint, RectRegion, Region);
+}
+
+bool cm93compchart::DoRenderRegionViewOnGL(const wxGLContext &glc,
+ const ViewPort &VPoint,
+ const OCPNRegion &RectRegion,
+ const LLRegion &Region) {
+ // g_bDebugCM93 = true;
+
+ // CALLGRIND_START_INSTRUMENTATION
+
+ ViewPort vp = VPoint;
+
+ bool render_return = false;
+ if (m_pcm93chart_current == 0) return render_return;
+
+ {
+ // This will be done later, in s57chart base class render method
+ /// m_pcm93chart_current->SetVPParms ( vp );
+
+ // Check the current chart scale to see if it covers the requested region
+ // totally
+ if (VPoint.b_quilt) {
+ LLRegion vpr_empty = Region;
+ LLRegion chart_region = GetValidRegion();
+
+ // old method which draws the regions from large to small scale, then
+ // finishes with the largest scale. This is broken on systems with broken
+ // clipping regions
+
+ // So we modify the algorithm as follows:
+ // a. Calculate the region patches from large scale to small scale,
+ // starting with the Reference scale, and
+ // ending when the total region requested is full.
+ // b. Save the calculated patches in an array as they are generated.
+ // c. Render the regions/scales saved in the array in reverse order, from
+ // small scale to large scale. d. Finally, render the Reference
+ // region/scale.
+ //
+ // This logic has the advantage that only the minimum necessary Object
+ // rendering is actually performed, and only within the minimum necessary
+ // region.
+
+ if (!chart_region.Empty()) vpr_empty.Subtract(chart_region);
+
+ if (!vpr_empty.Empty() &&
+ m_cmscale) // This chart scale does not fully cover the region
+ {
+ // Save the current cm93 chart scale for restoration later
+ int cmscale_save = m_cmscale;
+
+ LLRegion region_vect[8];
+
+ // Render smaller scale cells the entire requested region is full
+
+ while (!vpr_empty.Empty() && m_cmscale) {
+ // get the next smaller scale chart
+ m_cmscale = PrepareChartScale(vp, m_cmscale - 1, false);
+
+ if (m_pcm93chart_current) {
+ LLRegion sscale_region = GetValidRegion();
+
+ // Save the calculated per-scale region in the array
+ region_vect[m_cmscale] = sscale_region;
+ region_vect[m_cmscale].Intersect(vpr_empty);
+ // Only need to render that part of the vp that is not yet full
+ // Update the remaining empty region
+ vpr_empty.Subtract(sscale_region);
+ }
+
+ } // while
+
+ // Render all non-empty regions saved in the array, from small to large
+ // scale.
+ for (int i = 0; i < 8; i++) {
+ if (!region_vect[i].Empty()) {
+ m_cmscale = PrepareChartScale(vp, i, false);
+ if (m_pcm93chart_current)
+ render_return |= m_pcm93chart_current->RenderRegionViewOnGL(
+ glc, vp, RectRegion, region_vect[i]);
+ }
+ }
+
+ // restore the base chart pointer
+ m_cmscale = cmscale_save;
+ m_pcm93chart_current = m_pcm93chart_array[m_cmscale];
+ }
+
+ // Render the on-top Reference region/scale
+ if (m_pcm93chart_current) {
+ render_return |= m_pcm93chart_current->RenderRegionViewOnGL(
+ glc, vp, RectRegion, Region);
+ m_Name = m_pcm93chart_current->GetName();
+ }
+
+ } else // Single chart mode
+ {
+ if (m_pcm93chart_current) {
+ render_return = m_pcm93chart_current->RenderRegionViewOnGL(
+ glc, vp, RectRegion, Region);
+ m_Name = m_pcm93chart_current->GetLastFileName();
+ }
+ }
+ }
+
+ if (VPoint.m_projection_type != PROJECTION_MERCATOR)
+ return render_return; // TODO: fix below for non-mercator
+
+ if (!m_pcm93chart_current) return render_return;
+
+ // Render the cm93 cell's M_COVR outlines if called for
+ if (m_cell_index_special_outline) {
+#ifdef ocpnUSE_GL
+ wxGLCanvas *glc = top_frame::Get()->GetWxGlCanvas();
+ ocpnDC dc(*glc);
+#else
+ ocpnDC dc;
+#endif
+ dc.SetVP(VPoint);
+
+ covr_set *pcover = m_pcm93chart_current->GetCoverSet();
+
+ for (unsigned int im = 0; im < pcover->GetCoverCount(); im++) {
+ M_COVR_Desc *pmcd = pcover->GetCover(im);
+ if ((pmcd->m_cell_index == m_cell_index_special_outline) &&
+ (pmcd->m_object_id == m_object_id_special_outline) &&
+ (pmcd->m_subcell == m_subcell_special_outline))
+
+ {
+ // Draw this MCD's represented outline
+
+ // Case: vpBBox is completely inside the mcd box
+ // if(!(
+ // vp.vpBBox.IntersectOut(pmcd->m_covr_bbox)) ||
+ // !( vp.vpBBox.IntersectOut(pmcd->m_covr_bbox)))
+ {
+ float_2Dpt *p = pmcd->pvertices;
+ wxPoint *pwp = m_pcm93chart_current->GetDrawBuffer(pmcd->m_nvertices);
+
+ for (int ip = 0; ip < pmcd->m_nvertices; ip++) {
+ double plon = p->x;
+ if (fabs(plon - VPoint.clon) > 180.) {
+ if (plon > VPoint.clon)
+ plon -= 360.;
+ else
+ plon += 360.;
+ }
+
+ double easting, northing, epix, npix;
+ toSM(p->y, plon + 360., VPoint.clat, VPoint.clon + 360, &easting,
+ &northing);
+
+ // Outlines stored in MCDs are not adjusted for offsets
+ // easting -=
+ // pmcd->transform_WGS84_offset_x;
+ easting -= pmcd->user_xoff;
+ // northing -=
+ // pmcd->transform_WGS84_offset_y;
+ northing -= pmcd->user_yoff;
+
+ epix = easting * VPoint.view_scale_ppm;
+ npix = northing * VPoint.view_scale_ppm;
+
+ pwp[ip].x = (int)round((VPoint.pix_width / 2) + epix);
+ pwp[ip].y = (int)round((VPoint.pix_height / 2) - npix);
+
+ p++;
+ }
+
+ bool btest = true;
+ if (btest) {
+ wxPen pen(wxTheColourDatabase->Find("YELLOW"), 3);
+ wxDash dash1[2];
+ dash1[0] = 4; // Long dash
+ dash1[1] = 4; // Short gap
+ pen.SetStyle(wxPENSTYLE_USER_DASH);
+ pen.SetDashes(2, dash1);
+
+ dc.SetPen(pen);
+
+ for (int iseg = 0; iseg < pmcd->m_nvertices - 1; iseg++) {
+ int x0 = pwp[iseg].x;
+ int y0 = pwp[iseg].y;
+ int x1 = pwp[iseg + 1].x;
+ int y1 = pwp[iseg + 1].y;
+
+ ClipResult res = cohen_sutherland_line_clip_i(
+ &x0, &y0, &x1, &y1, 0, VPoint.pix_width, 0,
+ VPoint.pix_height);
+
+ if (res ==
+ Invisible) // Do not bother with segments that are invisible
+ continue;
+
+ dc.DrawLine(x0, y0, x1, y1);
+ }
+ }
+ }
+ }
+ }
+ }
+
+ return render_return;
+}
+
+bool cm93compchart::RenderRegionViewOnDC(wxMemoryDC &dc, const ViewPort &VPoint,
+ const OCPNRegion &Region) {
+ SetVPParms(VPoint);
+
+ if (g_pCM93OffsetDialog && g_pCM93OffsetDialog->IsShown())
+ g_pCM93OffsetDialog->UpdateMCOVRList(VPoint);
+
+ return DoRenderRegionViewOnDC(dc, VPoint, Region);
+}
+
+bool cm93compchart::RenderViewOnDC(wxMemoryDC &dc, const ViewPort &VPoint) {
+ const OCPNRegion vpr(0, 0, VPoint.pix_width, VPoint.pix_height);
+
+ SetVPParms(VPoint);
+
+ return DoRenderRegionViewOnDC(dc, VPoint, vpr);
+}
+
+bool cm93compchart::DoRenderRegionViewOnDC(wxMemoryDC &dc,
+ const ViewPort &VPoint,
+ const OCPNRegion &Region) {
+ // g_bDebugCM93 = true;
+
+ // CALLGRIND_START_INSTRUMENTATION
+ if (g_bDebugCM93) {
+ printf("\nOn DoRenderRegionViewOnDC Ref scale is %d, %c\n", m_cmscale,
+ (char)('A' + m_cmscale - 1));
+ OCPNRegionIterator upd(Region);
+ while (upd.HaveRects()) {
+ wxRect rect = upd.GetRect();
+ printf(" Region Rect: %d %d %d %d\n", rect.x, rect.y, rect.width,
+ rect.height);
+ upd.NextRect();
+ ;
+ }
+ }
+
+ ViewPort vp = VPoint;
+
+ bool render_return = false;
+ if (m_pcm93chart_current) {
+ m_pcm93chart_current->SetVPParms(vp);
+
+ // Check the current chart scale to see if it covers the requested region
+ // totally
+ if (VPoint.b_quilt) {
+ OCPNRegion vpr_empty = Region;
+
+ OCPNRegion chart_region = GetValidScreenCanvasRegion(vp, Region);
+
+ if (g_bDebugCM93) {
+ printf(
+ "On DoRenderRegionViewOnDC : Intersecting Ref region rectangles\n");
+ OCPNRegionIterator upd(chart_region);
+ while (upd.HaveRects()) {
+ wxRect rect = upd.GetRect();
+ printf(" Region Rect: %d %d %d %d\n", rect.x, rect.y, rect.width,
+ rect.height);
+ upd.NextRect();
+ }
+ }
+
+ if (!chart_region.IsEmpty()) vpr_empty.Subtract(chart_region);
+
+ if (!vpr_empty.Empty() &&
+ m_cmscale) // This chart scale does not fully cover the region
+ {
+ // Render the target scale chart on a temp dc for safekeeping
+#ifdef ocpnUSE_DIBSECTION
+ ocpnMemDC temp_dc;
+#else
+ wxMemoryDC temp_dc;
+#endif
+ if (!chart_region.IsEmpty())
+ render_return = m_pcm93chart_current->RenderRegionViewOnDC(
+ temp_dc, vp, chart_region);
+ else
+ render_return = false;
+
+ // Save the current cm93 chart pointer for restoration later
+ cm93chart *m_pcm93chart_save = m_pcm93chart_current;
+
+ // Prepare a blank quilt bitmap to build up the quilt upon
+ // We need to do this in order to avoid polluting any of the
+ // sub-chart cached bitmaps
+ if (m_pDummyBM) {
+ if ((m_pDummyBM->GetWidth() != VPoint.rv_rect.width) ||
+ (m_pDummyBM->GetHeight() != VPoint.rv_rect.height)) {
+ delete m_pDummyBM;
+ m_pDummyBM = NULL;
+ }
+ }
+ if (NULL == m_pDummyBM)
+ m_pDummyBM =
+ new wxBitmap(VPoint.rv_rect.width, VPoint.rv_rect.height, -1);
+
+#ifdef ocpnUSE_DIBSECTION // Clear the quilt
+ ocpnMemDC dumm_dc;
+#else
+ wxMemoryDC dumm_dc;
+#endif
+ dumm_dc.SelectObject(*m_pDummyBM);
+ dumm_dc.SetBackground(*wxBLACK_BRUSH);
+ dumm_dc.Clear();
+
+ int cmscale_next = m_cmscale;
+
+ // Render smaller scale cells onto a temporary DC, blitting the valid
+ // region onto the quilt dc until the region is full
+ while (!vpr_empty.Empty() && cmscale_next) {
+ // get the next smaller scale chart
+ cmscale_next--;
+ m_cmscale = PrepareChartScale(vp, cmscale_next, false);
+#ifdef ocpnUSE_DIBSECTION
+ ocpnMemDC build_dc;
+#else
+ wxMemoryDC build_dc;
+#endif
+
+ if (m_pcm93chart_current) {
+ if (g_bDebugCM93)
+ printf(" In DRRVOD, add quilt patch at %d, %c\n", m_cmscale,
+ (char)('A' + m_cmscale - 1));
+
+ m_pcm93chart_current->RenderRegionViewOnDC(build_dc, vp, Region);
+
+ OCPNRegion sscale_region = GetValidScreenCanvasRegion(vp, Region);
+
+ // Only need to render that part of the vp that is not yet full
+ sscale_region.Intersect(vpr_empty);
+
+ // Blit the smaller scale chart patch onto the target DC
+ OCPNRegionIterator upd(sscale_region);
+ while (upd.HaveRects()) {
+ wxRect rect = upd.GetRect();
+ dumm_dc.Blit(rect.x, rect.y, rect.width, rect.height, &build_dc,
+ rect.x, rect.y);
+ upd.NextRect();
+ }
+ build_dc.SelectObject(wxNullBitmap); // safely unmap the bmp
+
+ // Update the remaining empty region
+ if (!sscale_region.IsEmpty()) vpr_empty.Subtract(sscale_region);
+ }
+
+ } // while
+
+ // Finally, Blit the target scale chart as saved on temp_dc to quilt
+ // dc
+ OCPNRegionIterator updt(chart_region);
+ while (updt.HaveRects()) {
+ wxRect rect = updt.GetRect();
+ dumm_dc.Blit(rect.x, rect.y, rect.width, rect.height, &temp_dc,
+ rect.x, rect.y);
+ updt.NextRect();
+ }
+ temp_dc.SelectObject(wxNullBitmap); // safely unmap the base chart bmp
+
+ // restore the base chart pointer
+ m_pcm93chart_current = m_pcm93chart_save;
+
+ // We can unselect the target from the dummy DC, to avoid having to
+ // copy it.
+ dumm_dc.SelectObject(wxNullBitmap);
+
+ // And the return dc is the quilt
+ dc.SelectObject(*m_pDummyBM);
+
+ render_return = true;
+ } else {
+ m_pcm93chart_current->RenderRegionViewOnDC(dc, vp, Region);
+ render_return = true;
+ }
+ m_Name = m_pcm93chart_current->GetName();
+
+ } else // Single chart mode
+ {
+ render_return =
+ m_pcm93chart_current->RenderRegionViewOnDC(dc, vp, Region);
+ m_Name = m_pcm93chart_current->GetLastFileName();
+ }
+
+ } else {
+ // one must always return a valid bitmap selected into the specified DC
+ // Since the CM93 cell is not available at this location, select a dummy
+ // placeholder
+ if (m_pDummyBM) {
+ if ((m_pDummyBM->GetWidth() != VPoint.pix_width) ||
+ (m_pDummyBM->GetHeight() != VPoint.pix_height)) {
+ delete m_pDummyBM;
+ m_pDummyBM = NULL;
+ }
+ }
+
+ if (NULL == m_pDummyBM)
+ m_pDummyBM = new wxBitmap(VPoint.pix_width, VPoint.pix_height, -1);
+
+ // Clear the bitmap
+ wxMemoryDC mdc;
+ mdc.SelectObject(*m_pDummyBM);
+ mdc.SetBackground(*wxBLACK_BRUSH);
+ mdc.Clear();
+ mdc.SelectObject(wxNullBitmap);
+
+ dc.SelectObject(*m_pDummyBM);
+ }
+
+ // CALLGRIND_STOP_INSTRUMENTATION
+
+ // Render the cm93 cell's M_COVR outlines if called for
+ if (m_cell_index_special_outline && m_pcm93chart_current) {
+ covr_set *pcover = m_pcm93chart_current->GetCoverSet();
+
+ for (unsigned int im = 0; im < pcover->GetCoverCount(); im++) {
+ M_COVR_Desc *pmcd = pcover->GetCover(im);
+ if ((pmcd->m_cell_index == m_cell_index_special_outline) &&
+ (pmcd->m_object_id == m_object_id_special_outline) &&
+ (pmcd->m_subcell == m_subcell_special_outline))
+
+ {
+ // Draw this MCD's represented outline
+
+ // Case: vpBBox is completely inside the mcd box
+ // if(!(
+ // vp.vpBBox.IntersectOut(pmcd->m_covr_bbox)) ||
+ // !( vp.vpBBox.IntersectOut(pmcd->m_covr_bbox)))
+ {
+ float_2Dpt *p = pmcd->pvertices;
+ wxPoint *pwp = m_pcm93chart_current->GetDrawBuffer(pmcd->m_nvertices);
+
+ for (int ip = 0; ip < pmcd->m_nvertices; ip++) {
+ double plon = p->x;
+ if (fabs(plon - VPoint.clon) > 180.) {
+ if (plon > VPoint.clon)
+ plon -= 360.;
+ else
+ plon += 360.;
+ }
+
+ double easting, northing, epix, npix;
+ toSM(p->y, plon + 360., VPoint.clat, VPoint.clon + 360, &easting,
+ &northing);
+
+ // Outlines stored in MCDs are not adjusted for offsets
+ // easting -=
+ // pmcd->transform_WGS84_offset_x;
+ easting -= pmcd->user_xoff;
+ // northing -=
+ // pmcd->transform_WGS84_offset_y;
+ northing -= pmcd->user_yoff;
+
+ epix = easting * VPoint.view_scale_ppm;
+ npix = northing * VPoint.view_scale_ppm;
+
+ pwp[ip].x = (int)round((VPoint.pix_width / 2) + epix);
+ pwp[ip].y = (int)round((VPoint.pix_height / 2) - npix);
+
+ p++;
+ }
+
+ // Scrub the points
+ // looking for segments for which the wrong longitude decision was
+ // made
+ // TODO all this mole needs to be rethought, again
+ bool btest = true;
+ /*
+ wxPoint p0 = pwp[0];
+ for(int ip = 1 ; ip < pmcd->m_nvertices
+ ; ip++)
+ {
+ // if(((p0.x > VPoint.pix_width) &&
+ (pwp[ip].x < 0)) || ((p0.x < 0) && (pwp[ip].x > VPoint.pix_width)))
+ // btest = false;
+
+ p0 = pwp[ip];
+ }
+ */
+ if (btest) {
+ dc.SetPen(wxPen(wxTheColourDatabase->Find("YELLOW"), 4,
+ wxPENSTYLE_LONG_DASH));
+
+ for (int iseg = 0; iseg < pmcd->m_nvertices - 1; iseg++) {
+ int x0 = pwp[iseg].x;
+ int y0 = pwp[iseg].y;
+ int x1 = pwp[iseg + 1].x;
+ int y1 = pwp[iseg + 1].y;
+
+ ClipResult res = cohen_sutherland_line_clip_i(
+ &x0, &y0, &x1, &y1, 0, VPoint.pix_width, 0,
+ VPoint.pix_height);
+
+ if (res ==
+ Invisible) // Do not bother with segments that are invisible
+ continue;
+
+ dc.DrawLine(x0, y0, x1, y1);
+ }
+ }
+ }
+ }
+ }
+ }
+
+ return render_return;
+}
+
+void cm93compchart::UpdateRenderRegions(const ViewPort &VPoint) {
+ OCPNRegion full_screen_region(0, 0, VPoint.rv_rect.width,
+ VPoint.rv_rect.height);
+
+ ViewPort vp = VPoint;
+
+ SetVPParms(VPoint);
+
+ if (m_pcm93chart_current) {
+ m_pcm93chart_current->SetVPParms(vp);
+
+ // Check the current chart scale to see if it covers the requested region
+ // totally
+ if (VPoint.b_quilt) {
+ // Clear all the subchart regions
+ for (int i = 0; i < 8; i++) {
+ if (m_pcm93chart_array[i])
+ m_pcm93chart_array[i]->m_render_region.Clear();
+ }
+
+ OCPNRegion vpr_empty = full_screen_region;
+
+ OCPNRegion chart_region =
+ GetValidScreenCanvasRegion(vp, full_screen_region);
+ m_pcm93chart_current->m_render_region = chart_region; // update
+
+ if (!chart_region.IsEmpty()) vpr_empty.Subtract(chart_region);
+
+ if (!vpr_empty.Empty() &&
+ m_cmscale) // This chart scale does not fully cover the region
+ {
+ // Save the current cm93 chart pointer for restoration later
+ cm93chart *m_pcm93chart_save = m_pcm93chart_current;
+
+ int cmscale_next = m_cmscale;
+
+ while (!vpr_empty.Empty() && cmscale_next) {
+ // get the next smaller scale chart
+ cmscale_next--;
+ m_cmscale = PrepareChartScale(vp, cmscale_next, false);
+
+ if (m_pcm93chart_current) {
+ OCPNRegion sscale_region =
+ GetValidScreenCanvasRegion(vp, full_screen_region);
+ sscale_region.Intersect(vpr_empty);
+ m_pcm93chart_current->m_render_region = sscale_region;
+
+ // Update the remaining empty region
+ if (!sscale_region.IsEmpty()) vpr_empty.Subtract(sscale_region);
+ }
+
+ } // while
+
+ // restore the base chart pointer
+ m_pcm93chart_current = m_pcm93chart_save;
+ }
+ }
+ }
+}
+
+void cm93compchart::SetSpecialCellIndexOffset(int cell_index, int object_id,
+ int subcell, int xoff, int yoff) {
+ m_special_offset_x = xoff;
+ m_special_offset_y = yoff;
+
+ if (m_pcm93chart_current)
+ m_pcm93chart_current->SetUserOffsets(cell_index, object_id, subcell, xoff,
+ yoff);
+}
+
+bool cm93compchart::RenderNextSmallerCellOutlines(ocpnDC &dc, ViewPort &vp,
+ ChartCanvas *cc) {
+ if (m_cmscale >= 7) return false;
+#ifdef ocpnUSE_GL
+ glChartCanvas *glcc = cc->GetglCanvas();
+ if (!glcc) return false;
+#else
+ return false;
+#endif
+
+ int nss_max;
+
+ int nss = m_cmscale + 1;
+
+ // A little magic here.
+ // Drawing all larger scale cell outlines is way too expensive.
+ // So, stop the loop after we have rendered "something"
+ // But don't stop at all if the viewport scale is less than 3 million.
+ // This will have the effect of bringing in outlines of isolated large
+ // scale cells embedded within small scale cells, like isolated islands in
+ // the Pacific.
+ bool bdrawn = false;
+
+ nss_max = 7;
+
+#if 0 /* only if chart outlines are rendered grounded to the charts */
+ if(g_bopengl) { /* for opengl: lets keep this simple yet also functioning
+ unlike the unbounded version (which is interesting)
+ the small update rectangles normally encountered when panning
+ can cause too many charts to load */
+ if(nss_max > m_cmscale+3)
+ nss_max = m_cmscale+3;
+ }
+#endif
+ while (nss <= nss_max && (!bdrawn || (vp.chart_scale < 3e6))) {
+ cm93chart *psc = m_pcm93chart_array[nss];
+
+ if (!psc) {
+ m_pcm93chart_array[nss] = new cm93chart();
+ psc = m_pcm93chart_array[nss];
+
+ wxChar ext = (wxChar)('A' + nss - 1);
+ if (nss == 0) ext = 'Z';
+
+ wxString file_dummy = "CM93.";
+ file_dummy << ext;
+
+ psc->SetCM93Dict(m_pDictComposite);
+ psc->SetCM93Prefix(m_prefixComposite);
+ psc->SetCM93Manager(m_pcm93mgr);
+
+ psc->SetColorScheme(m_global_color_scheme);
+ psc->Init(file_dummy, FULL_INIT);
+ }
+
+ if (nss != 1) { // skip rendering the A scale outlines
+
+ // Make sure the covr bounding box is complete
+ psc->UpdateCovrSet(&vp);
+
+ // Render the chart outlines
+ covr_set *pcover = psc->GetCoverSet();
+
+ for (unsigned int im = 0; im < pcover->GetCoverCount(); im++) {
+ M_COVR_Desc *mcd = pcover->GetCover(im);
+
+ if (vp.GetBBox().IntersectOut(mcd->m_covr_bbox)) continue;
+
+ wxPoint *pwp = psc->GetDrawBuffer(mcd->m_nvertices);
+ bdrawn = RenderCellOutlines(dc, vp, pwp, mcd);
+ }
+ }
+ nss++;
+ }
+
+#ifdef ocpnUSE_GL
+ if (g_bopengl) {
+ glDisable(GL_LINE_STIPPLE);
+ glDisable(GL_LINE_SMOOTH);
+ glDisable(GL_BLEND);
+ }
+#endif
+ return true;
+}
+
+bool cm93compchart::RenderCellOutlines(ocpnDC &dc, ViewPort &vp, wxPoint *pwp,
+ M_COVR_Desc *mcd) {
+ float_2Dpt *p = mcd->pvertices;
+ int np = mcd->m_nvertices;
+
+ for (int ip = 0; ip < np; ip++, p++) {
+ pwp[ip] = vp.GetPixFromLL(p->y, p->x);
+
+ // Outlines stored in MCDs are not adjusted for offsets
+ pwp[ip].x -= mcd->user_xoff * vp.view_scale_ppm;
+ pwp[ip].y -= mcd->user_yoff * vp.view_scale_ppm;
+ }
+ // Scrub the points
+ // looking for segments for which the wrong longitude decision was made
+ // TODO all this mole needs to be rethought, again
+ wxPoint p0 = pwp[0];
+ for (int ip = 1; ip < np; ip++) {
+ if (((p0.x > vp.pix_width) && (pwp[ip].x < 0)) ||
+ ((p0.x < 0) && (pwp[ip].x > vp.pix_width)))
+ return false;
+
+ p0 = pwp[ip];
+ }
+
+ dc.DrawLines(mcd->m_nvertices, pwp, 0, 0, false);
+ return true;
+}
+
+void cm93compchart::GetPointPix(ObjRazRules *rzRules, float rlat, float rlon,
+ wxPoint *r) {
+ m_pcm93chart_current->GetPointPix(rzRules, rlat, rlon, r);
+}
+
+void cm93compchart::GetPointPix(ObjRazRules *rzRules, wxPoint2DDouble *en,
+ wxPoint *r, int nPoints) {
+ m_pcm93chart_current->GetPointPix(rzRules, en, r, nPoints);
+}
+
+void cm93compchart::GetPixPoint(int pixx, int pixy, double *plat, double *plon,
+ ViewPort *vpt) {
+ m_pcm93chart_current->GetPixPoint(pixx, pixy, plat, plon, vpt);
+}
+
+void cm93compchart::UpdateLUPs(s57chart *pOwner) {
+ for (int i = 0; i < 8; i++) {
+ if (m_pcm93chart_array[i]) m_pcm93chart_array[i]->UpdateLUPs(pOwner);
+ }
+}
+
+std::list *cm93compchart::GetAssociatedObjects(S57Obj *obj) {
+ if (m_pcm93chart_current)
+ return m_pcm93chart_current->GetAssociatedObjects(obj);
+ else
+ return NULL;
+}
+
+void cm93compchart::InvalidateCache() {
+ for (int i = 0; i < 8; i++) {
+ if (m_pcm93chart_array[i]) m_pcm93chart_array[i]->InvalidateCache();
+ }
+}
+
+void cm93compchart::ForceEdgePriorityEvaluate() {
+ for (int i = 0; i < 8; i++) {
+ if (m_pcm93chart_array[i])
+ m_pcm93chart_array[i]->ForceEdgePriorityEvaluate();
+ }
+}
+
+void cm93compchart::SetColorScheme(ColorScheme cs, bool bApplyImmediate) {
+ m_global_color_scheme = cs;
+
+ for (int i = 0; i < 8; i++) {
+ if (m_pcm93chart_array[i])
+ m_pcm93chart_array[i]->SetColorScheme(cs, bApplyImmediate);
+ }
+}
+
+ListOfObjRazRules *cm93compchart::GetObjRuleListAtLatLon(float lat, float lon,
+ float select_radius,
+ ViewPort *VPoint,
+ int selection_mask) {
+ float alon = lon;
+
+ ViewPort vp; // needs a new ViewPort also for ObjectRenderCheck()
+ vp = *VPoint;
+
+ if (!VPoint->b_quilt)
+ if (m_pcm93chart_current)
+ return m_pcm93chart_current->GetObjRuleListAtLatLon(lat, alon,
+ select_radius, &vp);
+ else {
+ // As default, return an empty list
+ ListOfObjRazRules *ret_ptr = new ListOfObjRazRules;
+ return ret_ptr;
+ }
+ else {
+ UpdateRenderRegions(*VPoint);
+
+ // Search all of the subcharts, looking for the one whose render region
+ // contains the requested point
+ wxPoint p = VPoint->GetPixFromLL(lat, lon);
+
+ for (int i = 0; i < 8; i++) {
+ if (m_pcm93chart_array[i]) {
+ if (!m_pcm93chart_array[i]->m_render_region.IsEmpty()) {
+ if (wxInRegion == m_pcm93chart_array[i]->m_render_region.Contains(p))
+ return m_pcm93chart_array[i]->GetObjRuleListAtLatLon(
+ lat, alon, select_radius, &vp, selection_mask);
+ }
+ }
+ }
+
+ // As default, return an empty list
+ ListOfObjRazRules *ret_ptr = new ListOfObjRazRules;
+
+ return ret_ptr;
+ }
+}
+
+std::unordered_map &cm93compchart::Get_ve_hash() {
+ return m_pcm93chart_current->Get_ve_hash();
+}
+
+std::unordered_map &cm93compchart::Get_vc_hash() {
+ return m_pcm93chart_current->Get_vc_hash();
+}
+
+bool cm93compchart::AdjustVP(ViewPort &vp_last, ViewPort &vp_proposed) {
+#ifdef ocpnUSE_GL
+ if (g_bopengl) {
+ /* need a full refresh if not in quilted mode, and the cell changed */
+ // TODO re-add this for multicanvas
+ // if ( !vp_last.b_quilt && m_last_cell_adjustvp != m_pcm93chart_current )
+ // glChartCanvas::Invalidate();
+
+ m_last_cell_adjustvp = m_pcm93chart_current;
+ }
+#endif
+
+ // All the below logic is slow, and really redundant.
+ // so, declare that cm93 charts do not require adjustment for optimum
+ // performance.
+
+ if (m_pcm93chart_current) return false;
+
+ // This may be a partial screen render
+ // If it is, the cmscale value on this render must match the same parameter
+ // on the last render.
+ // If it does not, the partial render will not quilt correctly with the
+ // previous data Detect this case, and indicate that the entire screen must
+ // be rendered.
+
+ int cmscale = GetCMScaleFromVP(
+ vp_proposed); // This is the scale that should be used, based on the vp
+
+ int cmscale_actual = PrepareChartScale(
+ vp_proposed, cmscale,
+ false); // this is the scale that will be used, based on cell coverage
+
+ if (g_bDebugCM93)
+ printf(" In AdjustVP, adjustment subchart scale is %c\n",
+ (char)('A' + cmscale_actual - 1));
+
+ // We always need to do a VP adjustment, independent of this method's
+ // return value. so, do an AdjustVP() based on the chart scale that WILL BE
+ // USED And be sure to return false if that adjust method suggests so.
+
+ bool single_adjust = false;
+ if (m_pcm93chart_array[cmscale_actual])
+ single_adjust =
+ m_pcm93chart_array[cmscale_actual]->AdjustVP(vp_last, vp_proposed);
+
+ if (m_cmscale != cmscale_actual) return false;
+
+ // In quilt mode, always indicate that the adjusted vp requires a full
+ // repaint
+ if (vp_last.b_quilt) return false;
+
+ return single_adjust;
+}
+
+ThumbData *cm93compchart::GetThumbData(int tnx, int tny, float lat, float lon) {
+ return (ThumbData *)NULL;
+}
+
+InitReturn cm93compchart::CreateHeaderData() {
+ m_Chart_Scale = 20000000;
+
+ // Read the root directory, getting subdirectories to build a small
+ // scale coverage region
+ wxRect extent_rect;
+
+ wxDir dirt(m_prefixComposite);
+ wxString candidate;
+ wxRegEx test("[0-9]+");
+
+ bool b_cont = dirt.GetFirst(&candidate);
+
+ while (b_cont) {
+ if (test.Matches(candidate) && (candidate.Len() == 8)) {
+ wxString dir = m_prefixComposite;
+ dir += candidate;
+ if (wxDir::Exists(dir)) {
+ wxFileName name(dir);
+ wxString num_name = name.GetName();
+ long number;
+ if (num_name.ToLong(&number)) {
+ int ilat = number / 10000;
+ int ilon = number % 10000;
+
+ int lat_base = (ilat - 270) / 3.;
+ int lon_base = ilon / 3.;
+ extent_rect.Union(wxRect(lon_base, lat_base, 20, 20));
+ }
+ }
+ }
+ b_cont = dirt.GetNext(&candidate);
+ }
+
+ // Specify the chart coverage
+ m_FullExtent.ELON = ((double)extent_rect.x + (double)extent_rect.width);
+ m_FullExtent.WLON = ((double)extent_rect.x);
+ m_FullExtent.NLAT = ((double)extent_rect.y + (double)extent_rect.height);
+ m_FullExtent.SLAT = ((double)extent_rect.y);
+ m_bExtentSet = true;
+
+ // Populate one M_COVR Entry
+ m_nCOVREntries = 1;
+ m_pCOVRTablePoints = (int *)malloc(sizeof(int));
+ *m_pCOVRTablePoints = 4;
+ m_pCOVRTable = (float **)malloc(sizeof(float *));
+ float *pf = (float *)malloc(2 * 4 * sizeof(float));
+ *m_pCOVRTable = pf;
+ float *pfe = pf;
+
+ *pfe++ = m_FullExtent.NLAT; // LatMax;
+ *pfe++ = m_FullExtent.WLON; // LonMin;
+
+ *pfe++ = m_FullExtent.NLAT; // LatMax;
+ *pfe++ = m_FullExtent.ELON; // LonMax;
+
+ *pfe++ = m_FullExtent.SLAT; // LatMin;
+ *pfe++ = m_FullExtent.ELON; // LonMax;
+
+ *pfe++ = m_FullExtent.SLAT; // LatMin;
+ *pfe++ = m_FullExtent.WLON; // LonMin;
+
+ return INIT_OK;
+}
+
+cm93_dictionary *cm93compchart::FindAndLoadDictFromDir(const wxString &dir) {
+ cm93_dictionary *retval = NULL;
+ cm93_dictionary *pdict = new cm93_dictionary();
+
+ // Quick look at the supplied directory...
+ if (pdict->LoadDictionary(dir)) return pdict;
+
+ // Otherwise, search for the dictionary files all along the path of the
+ // passed parameter
+
+ wxString path = dir;
+ wxString target;
+ unsigned int i = 0;
+
+ while (i < path.Len()) {
+ target.Append(path[i]);
+ if (path[i] == wxFileName::GetPathSeparator()) {
+ // wxString msg = _T ( " Looking for CM93 dictionary in "
+ // ); msg.Append ( target ); wxLogMessage ( msg );
+
+ if (pdict->LoadDictionary(target)) {
+ retval = pdict;
+ break;
+ }
+ }
+ i++;
+ }
+
+ if (NULL != retval) // Found it....
+ return retval;
+
+ // Dictionary was not found in linear path of supplied dir.
+ // Could be on branch, so, look at entire tree the hard way.
+
+ wxFileName fnc(dir);
+ wxString found_dict_file_name;
+
+ bool bdone = false;
+ while (!bdone) {
+ path = fnc.GetPath(wxPATH_GET_VOLUME); // get path without sep
+
+ wxString msg = " Looking harder for CM93 dictionary in ";
+ msg.Append(path);
+ wxLogMessage(msg);
+
+ if ((path.Len() == 0) || path.IsSameAs(fnc.GetPathSeparator())) {
+ bdone = true;
+ wxLogMessage("Early break1");
+ break;
+ }
+
+ // Abort the search loop if the directory tree does not contain some
+ // indication of CM93
+ if ((wxNOT_FOUND == path.Lower().Find("cm93"))) {
+ bdone = true;
+ wxLogMessage("Early break2");
+ break;
+ }
+
+ // Search here
+ // This takes a while to search a fully populated cm93 tree....
+ wxDir dir(path);
+
+ if (dir.IsOpened()) {
+ // Find the dictionary name, case insensitively
+ FindCM93Dictionary cm93Dictionary(found_dict_file_name);
+ dir.Traverse(cm93Dictionary);
+ bdone = found_dict_file_name.Len() != 0;
+ }
+
+ fnc.Assign(path); // convert the path to a filename for next loop
+ }
+
+ if (found_dict_file_name.Len()) {
+ wxFileName fnd(found_dict_file_name);
+ wxString dpath =
+ fnd.GetPath((int)(wxPATH_GET_SEPARATOR | wxPATH_GET_VOLUME));
+
+ if (pdict->LoadDictionary(dpath)) retval = pdict;
+ }
+
+ if (NULL == retval) delete pdict;
+
+ return retval;
+}
+
+void cm93compchart::CloseandReopenCurrentSubchart() {
+ delete m_pcm93chart_current;
+ m_pcm93chart_current = NULL;
+ m_pcm93chart_array[m_cmscale] = NULL;
+
+ SetVPParms(m_vpt);
+ InvalidateCache();
+}
+
+class CM93OffsetDialog;
+
+enum {
+ tlCELL = 0,
+ tlMCOVR,
+ tlSCALE,
+ tlXOFF,
+ tlYOFF,
+ tlUXOFF,
+ tlUYOFF,
+}; // OCPNOffsetListCtrl Columns;
+
+//---------------------------------------------------------------------------------------
+// OCPNOffsetListCtrl Definition
+//---------------------------------------------------------------------------------------
+class OCPNOffsetListCtrl : public wxListCtrl {
+public:
+ OCPNOffsetListCtrl(CM93OffsetDialog *parent, wxWindowID id,
+ const wxPoint &pos, const wxSize &size, long style);
+ ~OCPNOffsetListCtrl();
+
+ wxString OnGetItemText(long item, long column) const;
+ int OnGetItemColumnImage(long item, long column) const;
+
+ CM93OffsetDialog *m_parent;
+};
+
+OCPNOffsetListCtrl::OCPNOffsetListCtrl(CM93OffsetDialog *parent, wxWindowID id,
+ const wxPoint &pos, const wxSize &size,
+ long style)
+ : wxListCtrl(parent, id, pos, size, style) {
+ m_parent = parent;
+}
+
+OCPNOffsetListCtrl::~OCPNOffsetListCtrl() {}
+
+wxString OCPNOffsetListCtrl::OnGetItemText(long item, long column) const {
+ wxString ret;
+ M_COVR_Desc *pmcd = m_parent->m_pcovr_array[item];
+
+ switch (column) {
+ case tlCELL: {
+ ret.Printf("%d", pmcd->m_cell_index);
+ if (((int)'0') == pmcd->m_subcell)
+ ret.Prepend("0");
+ else {
+ char t = (char)pmcd->m_subcell;
+ wxString p;
+ p.Printf("%c", t);
+ ret.Prepend(p);
+ }
+
+ break;
+ }
+ case tlMCOVR:
+ ret.Printf("%d", pmcd->m_object_id);
+ break;
+
+ case tlSCALE:
+ ret = m_parent->m_selected_chart_scale_char;
+ break;
+
+ case tlXOFF:
+ ret.Printf("%g", pmcd->transform_WGS84_offset_x);
+ break;
+
+ case tlYOFF:
+ ret.Printf("%g", pmcd->transform_WGS84_offset_y);
+ break;
+
+ case tlUXOFF:
+ ret.Printf("%6.0f", pmcd->user_xoff * pmcd->m_centerlat_cos);
+ break;
+
+ case tlUYOFF:
+ ret.Printf("%6.0f", pmcd->user_yoff * pmcd->m_centerlat_cos);
+ break;
+
+ default:
+ break;
+ }
+ return ret;
+}
+
+int OCPNOffsetListCtrl::OnGetItemColumnImage(long item, long column) const {
+ return -1;
+}
+
+//---------------------------------------------------------------------------------------
+// CM93OffsetDialog Implementation
+//---------------------------------------------------------------------------------------
+
+IMPLEMENT_CLASS(CM93OffsetDialog, wxDialog)
+
+BEGIN_EVENT_TABLE(CM93OffsetDialog, wxDialog)
+EVT_CLOSE(CM93OffsetDialog::OnClose)
+END_EVENT_TABLE()
+
+CM93OffsetDialog::CM93OffsetDialog(wxWindow *parent) {
+ m_pparent = parent;
+ m_pcompchart = NULL;
+
+ m_xoff = 0;
+ m_yoff = 0;
+
+ m_selected_list_index = -1;
+ m_selected_cell_index = 0;
+
+ long wstyle = wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER;
+ wxDialog::Create(parent, -1, _("OpenCPN CM93 Cell Offset Adjustments"),
+ wxPoint(0, 0), wxSize(800, 200), wstyle);
+
+ wxFont *qFont = GetOCPNScaledFont(_("Dialog"));
+ SetFont(*qFont);
+
+ // A top-level sizer
+ wxBoxSizer *topSizer = new wxBoxSizer(wxHORIZONTAL);
+ SetSizer(topSizer);
+
+ int width;
+
+ long flags = wxLC_REPORT | wxLC_SINGLE_SEL | wxLC_HRULES | wxLC_VRULES |
+ wxBORDER_SUNKEN;
+#ifndef __WXQT__
+ flags |= wxLC_VIRTUAL;
+#endif
+
+ m_pListCtrlMCOVRs =
+ new OCPNOffsetListCtrl(this, -1, wxDefaultPosition, wxDefaultSize, flags);
+
+ m_pListCtrlMCOVRs->Connect(
+ wxEVT_COMMAND_LIST_ITEM_SELECTED,
+ wxListEventHandler(CM93OffsetDialog::OnCellSelected), NULL, this);
+
+ int dx = GetCharWidth();
+ int dy = GetCharHeight();
+
+ width = dx * 10;
+ m_pListCtrlMCOVRs->InsertColumn(tlCELL, _("Cell"), wxLIST_FORMAT_LEFT, width);
+
+ // width = 80;
+ m_pListCtrlMCOVRs->InsertColumn(tlMCOVR, _("M_COVR ID"), wxLIST_FORMAT_CENTER,
+ width);
+
+ // width = 80;
+ m_pListCtrlMCOVRs->InsertColumn(tlSCALE, _("Cell Scale"),
+ wxLIST_FORMAT_CENTER, width);
+
+ // width = 90;
+ m_pListCtrlMCOVRs->InsertColumn(tlXOFF, _("wgsox"), wxLIST_FORMAT_CENTER,
+ width);
+
+ // width = 90;
+ m_pListCtrlMCOVRs->InsertColumn(tlYOFF, _("wgsoy"), wxLIST_FORMAT_CENTER,
+ width);
+
+ // width = 90;
+ m_pListCtrlMCOVRs->InsertColumn(tlUXOFF, _("User X Offset"),
+ wxLIST_FORMAT_CENTER, width);
+
+ // width = 90;
+ m_pListCtrlMCOVRs->InsertColumn(tlUYOFF, _("User Y Offset"),
+ wxLIST_FORMAT_CENTER, width);
+
+ topSizer->Add(m_pListCtrlMCOVRs, 1, wxEXPAND | wxALL, 0);
+
+ wxBoxSizer *boxSizer02 = new wxBoxSizer(wxVERTICAL);
+ boxSizer02->AddSpacer(22);
+
+ wxStaticText *pStaticTextXoff = new wxStaticText(
+ this, wxID_ANY,
+ wxString::Format("%s (%s)", _("User X Offset"), _("meters")),
+ wxDefaultPosition, wxDefaultSize, 0);
+ boxSizer02->Add(pStaticTextXoff, 0, wxALL, 0);
+
+ m_pSpinCtrlXoff =
+ new wxSpinCtrl(this, wxID_ANY, wxEmptyString, wxDefaultPosition,
+ wxSize(50, -1), wxSP_ARROW_KEYS, -10000, 10000, 0);
+ m_pSpinCtrlXoff->Connect(wxEVT_COMMAND_SPINCTRL_UPDATED,
+ wxCommandEventHandler(CM93OffsetDialog::OnOffSetSet),
+ NULL, this);
+ boxSizer02->Add(m_pSpinCtrlXoff, 0, wxEXPAND | wxALL, 0);
+
+ wxStaticText *pStaticTextYoff = new wxStaticText(
+ this, wxID_ANY,
+ wxString::Format("%s (%s)", _("User Y Offset"), _("meters")),
+ wxDefaultPosition, wxDefaultSize, 0);
+ boxSizer02->Add(pStaticTextYoff, 0, wxALL, 0);
+
+ m_pSpinCtrlYoff =
+ new wxSpinCtrl(this, wxID_ANY, wxEmptyString, wxDefaultPosition,
+ wxSize(50, -1), wxSP_ARROW_KEYS, -10000, 10000, 0);
+ m_pSpinCtrlYoff->Connect(wxEVT_COMMAND_SPINCTRL_UPDATED,
+ wxCommandEventHandler(CM93OffsetDialog::OnOffSetSet),
+ NULL, this);
+ boxSizer02->Add(m_pSpinCtrlYoff, 0, wxEXPAND | wxALL, 0);
+
+ m_OKButton = new wxButton(this, wxID_ANY, _("OK"), wxDefaultPosition,
+ wxDefaultSize, 0);
+ m_OKButton->Connect(wxEVT_COMMAND_BUTTON_CLICKED,
+ wxCommandEventHandler(CM93OffsetDialog::OnOK), NULL,
+ this);
+ boxSizer02->Add(m_OKButton, 0, wxALL, 5);
+ m_OKButton->SetDefault();
+
+ topSizer->Add(boxSizer02, 0, wxEXPAND | wxALL, 2);
+
+ wxSize sz(800, dy * 8);
+#ifdef __WXQT__
+ sz = wxGetDisplaySize();
+ sz.y = dy * 8;
+#endif
+ SetSize(sz);
+
+ topSizer->Layout();
+
+ // This is silly, but seems to be required for __WXMSW__ build
+ // If not done, the SECOND invocation of dialog fails to expand the list to
+ // the full wxSizer size....
+ SetSize(GetSize().x, GetSize().y - 1);
+
+ SetColorScheme();
+
+ // GetSizer()->SetSizeHints(this);
+ Centre();
+
+#ifdef __WXQT__
+ Move(-1, 100);
+#endif
+}
+
+CM93OffsetDialog::~CM93OffsetDialog() { g_pCM93OffsetDialog = NULL; }
+
+void CM93OffsetDialog::OnClose(wxCloseEvent &event) {
+ if (m_pcompchart) {
+ m_pcompchart->SetSpecialOutlineCellIndex(0, 0, 0);
+
+ m_pcompchart->InvalidateCache();
+
+ if (m_pparent) {
+ m_pparent->Refresh(true);
+ top_frame::Get()->InvalidateAllGL();
+ }
+ }
+
+ if (m_pListCtrlMCOVRs->GetItemCount() > m_selected_list_index)
+ m_pListCtrlMCOVRs->SetItemState(m_selected_list_index, 0,
+ wxLIST_STATE_SELECTED);
+
+ Hide();
+}
+
+void CM93OffsetDialog::OnOK(wxCommandEvent &event) {
+#ifdef __WXQT__
+ UpdateOffsets();
+#endif
+ Close();
+}
+
+void CM93OffsetDialog::SetCM93Chart(cm93compchart *pchart) {
+ m_pcompchart = pchart;
+}
+
+void CM93OffsetDialog::OnOffSetSet(wxCommandEvent &event) {
+ m_xoff = m_pSpinCtrlXoff->GetValue() / m_centerlat_cos;
+ m_yoff = m_pSpinCtrlYoff->GetValue() / m_centerlat_cos;
+
+#ifndef __WXQT__
+ UpdateOffsets();
+#endif
+}
+
+void CM93OffsetDialog::UpdateOffsets() {
+ if (m_pcompchart && m_selected_cell_index) {
+ // Set the offsets of the selected cell/object
+ m_pcompchart->SetSpecialCellIndexOffset(m_selected_cell_index,
+ m_selected_object_id,
+ m_selected_subcell, m_xoff, m_yoff);
+
+ // Closing the current cell will record the offsets in the M_COVR cache
+ // file Re-opening will then refresh the M_COVRs in the cover set
+ AbstractPlatform::ShowBusySpinner();
+ m_pcompchart->CloseandReopenCurrentSubchart();
+ AbstractPlatform::HideBusySpinner();
+
+ if (m_pparent) {
+ m_pparent->Refresh(true);
+ top_frame::Get()->InvalidateAllGL();
+ }
+ }
+}
+
+void CM93OffsetDialog::SetColorScheme() { DimeControl(this); }
+
+void CM93OffsetDialog::OnCellSelected(wxListEvent &event) {
+ if (m_pcompchart) {
+ m_selected_list_index = event.GetIndex();
+
+ M_COVR_Desc *mcd = m_pcovr_array.Item(event.GetIndex());
+
+ if (m_selected_list_index > m_pListCtrlMCOVRs->GetItemCount())
+ return; // error
+
+ cm93chart *pchart = m_pcompchart->GetCurrentSingleScaleChart();
+ if (pchart) {
+ M_COVR_Desc *cached_mcd = pchart->GetCoverSet()->Find_MCD(
+ mcd->m_cell_index, mcd->m_object_id, mcd->m_subcell);
+ if (cached_mcd) {
+ m_pSpinCtrlXoff->SetValue(
+ wxRound(cached_mcd->user_xoff * cached_mcd->m_centerlat_cos));
+ m_pSpinCtrlYoff->SetValue(
+ wxRound(cached_mcd->user_yoff * cached_mcd->m_centerlat_cos));
+ }
+ }
+
+ m_pcompchart->SetSpecialOutlineCellIndex(mcd->m_cell_index,
+ mcd->m_object_id, mcd->m_subcell);
+
+ m_selected_cell_index = mcd->m_cell_index;
+ m_selected_object_id = mcd->m_object_id;
+ m_selected_subcell = mcd->m_subcell;
+ m_centerlat_cos = mcd->m_centerlat_cos;
+
+ m_pcompchart->InvalidateCache();
+
+ if (m_pparent) {
+ m_pparent->Refresh(true);
+ top_frame::Get()->InvalidateAllGL();
+ }
+ }
+}
+
+void CM93OffsetDialog::UpdateMCOVRList(const ViewPort &vpt) {
+ if (m_pcompchart) {
+ // In single chart mode, there is but one cm93chart (i.e. one "scale
+ // value") shown at any one time
+ cm93chart *pchart = m_pcompchart->GetCurrentSingleScaleChart();
+
+ if (pchart) {
+ m_selected_chart_scale_char = pchart->GetScaleChar();
+
+ m_pcovr_array.Clear();
+
+ // Get an array of cell indicies at the current viewport
+ std::vector cell_array = pchart->GetVPCellArray(vpt);
+
+ ViewPort vp;
+ vp = vpt;
+
+ // Get the cover set for the cm93chart
+ // and walk the set looking for matches to the viewport referenced cell
+ // array This will give us the covr descriptors of interest
+ covr_set *pcover = pchart->GetCoverSet();
+
+ for (unsigned int im = 0; im < pcover->GetCoverCount(); im++) {
+ M_COVR_Desc *mcd = pcover->GetCover(im);
+
+ for (unsigned int icell = 0; icell < cell_array.size(); icell++) {
+ if (cell_array[icell] == mcd->m_cell_index) {
+ wxPoint *pwp = pchart->GetDrawBuffer(mcd->m_nvertices);
+ OCPNRegion rgn = mcd->GetRegion(vp, pwp);
+
+ // if(
+ // !vp.GetBBox().IntersectOut(mcd->m_covr_bbox))
+ if (rgn.Contains(0, 0, vpt.pix_width, vpt.pix_height) !=
+ wxOutRegion)
+ m_pcovr_array.Add(mcd);
+ }
+ }
+ }
+
+ // Try to find and maintain the correct list selection, even though the
+ // list contents may have changed
+ int sel_index = -1;
+ for (unsigned int im = 0; im < m_pcovr_array.size(); im++) {
+ M_COVR_Desc *mcd = m_pcovr_array[im];
+ if ((m_selected_cell_index == mcd->m_cell_index) &&
+ (m_selected_object_id == mcd->m_object_id) &&
+ (m_selected_subcell == mcd->m_subcell)) {
+ sel_index = im;
+ break;
+ }
+ }
+
+ if (!m_pListCtrlMCOVRs->IsVirtual()) {
+ if (m_pListCtrlMCOVRs->GetItemCount())
+ m_pListCtrlMCOVRs->DeleteAllItems();
+
+ for (unsigned int i = 0; i < m_pcovr_array.GetCount(); i++) {
+ wxListItem item;
+ item.SetId(i);
+ m_pListCtrlMCOVRs->InsertItem(item);
+ for (int j = 0; j < tlUYOFF + 1; j++) {
+ item.SetColumn(j);
+ item.SetText(m_pListCtrlMCOVRs->OnGetItemText(i, j));
+ m_pListCtrlMCOVRs->SetItem(item);
+ }
+ }
+ } else {
+ m_pListCtrlMCOVRs->SetItemCount(m_pcovr_array.GetCount());
+ }
+
+ if (-1 != sel_index)
+ m_pListCtrlMCOVRs->SetItemState(sel_index, wxLIST_STATE_SELECTED,
+ wxLIST_STATE_SELECTED);
+ else
+ m_pListCtrlMCOVRs->SetItemState(sel_index, 0,
+ wxLIST_STATE_SELECTED); // deselect all
+
+ m_pListCtrlMCOVRs->Refresh(true);
+ }
+#ifdef __WXMSW__
+ m_pListCtrlMCOVRs->Refresh(false);
+#endif
+ }
+}
diff --git a/docs/外部参考/OpenCPN源码基线_f7211b4e/上游源码/libs/s52plib/src/Cs52_shaders.cpp b/docs/外部参考/OpenCPN源码基线_f7211b4e/上游源码/libs/s52plib/src/Cs52_shaders.cpp
new file mode 100644
index 0000000..41574a6
--- /dev/null
+++ b/docs/外部参考/OpenCPN源码基线_f7211b4e/上游源码/libs/s52plib/src/Cs52_shaders.cpp
@@ -0,0 +1,305 @@
+/***************************************************************************
+ *
+ * Project: OpenCPN
+ *
+ ***************************************************************************
+ * Copyright (C) 2017 by David S. Register *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
+ **************************************************************************/
+
+#include "Cs52_shaders.h"
+
+#if defined(USE_ANDROID_GLES2) || defined(ocpnUSE_GLSL)
+
+#ifdef USE_ANDROID_GLES2
+#include
+#include "qdebug.h"
+#endif
+
+#ifdef USE_ANDROID_GLES2
+const GLchar* Cpreamble =
+"\n";
+#else
+const GLchar* Cpreamble =
+"#version 120\n"
+"#define precision\n"
+"#define lowp\n"
+"#define mediump\n"
+"#define highp\n";
+#endif
+
+
+// Simple colored triangle shader
+
+static const GLchar* xcolor_tri_vertex_shader_source =
+ "attribute vec2 position;\n"
+ "uniform mat4 MVMatrix;\n"
+ "uniform mat4 TransformMatrix;\n"
+ "uniform vec4 color;\n"
+ "varying vec4 fragColor;\n"
+ "void main() {\n"
+ " fragColor = color;\n"
+ " gl_Position = MVMatrix * TransformMatrix * vec4(position, 0.0, 1.0);\n"
+ "}\n";
+
+static const GLchar* xcolor_tri_fragment_shader_source =
+ "precision lowp float;\n"
+ "varying vec4 fragColor;\n"
+ "void main() {\n"
+ " gl_FragColor = fragColor;\n"
+ "}\n";
+
+// Simple 2D texture shader
+static const GLchar* xtexture_2D_vertex_shader_source =
+ "attribute vec2 position;\n"
+ "attribute vec2 aUV;\n"
+ "uniform mat4 MVMatrix;\n"
+ "uniform mat4 TransformMatrix;\n"
+ "varying vec2 varCoord;\n"
+ "void main() {\n"
+ " gl_Position = MVMatrix * TransformMatrix * vec4(position, 0.0, 1.0);\n"
+ " varCoord = aUV;\n"
+ "}\n";
+
+static const GLchar* xtexture_2D_fragment_shader_source =
+ "precision lowp float;\n"
+ "uniform sampler2D uTex;\n"
+ "varying vec2 varCoord;\n"
+ "void main() {\n"
+ " gl_FragColor = texture2D(uTex, varCoord);\n"
+ "}\n";
+
+// 2D texture shader with color modulation, used for colored text
+static const GLchar * xtexture_2D_ColorMod_vertex_shader_source =
+ "precision highp float;\n"
+ "attribute vec2 position;\n"
+ "attribute vec2 aUV;\n"
+ "uniform mat4 MVMatrix;\n"
+ "uniform mat4 TransformMatrix;\n"
+ "varying vec2 varCoord;\n"
+ "void main() {\n"
+ " gl_Position = MVMatrix * TransformMatrix * vec4(position, 0.0, 1.0);\n"
+ " varCoord = aUV;\n"
+ "}\n";
+
+static const GLchar * xtexture_2D_ColorMod_fragment_shader_source =
+ "precision highp float;\n"
+ "uniform sampler2D uTex;\n"
+ "uniform vec4 color;\n"
+ "varying vec2 varCoord;\n"
+ "void main() {\n"
+ " vec4 col=texture2D(uTex, varCoord);\n"
+ " gl_FragColor = color;\n"
+ " gl_FragColor.a = col.a;\n"
+ "}\n";
+
+// Circle shader
+
+static const GLchar* xcircle_filled_vertex_shader_source =
+ "precision highp float;\n"
+ "attribute vec2 aPos;\n"
+ "uniform mat4 MVMatrix;\n"
+ "uniform mat4 TransformMatrix;\n"
+ "void main() {\n"
+ " gl_Position = MVMatrix * TransformMatrix * vec4(aPos, 0.0, 1.0);\n"
+ "}\n";
+
+static const GLchar* xcircle_filled_fragment_shader_source =
+ "precision highp float;\n"
+ "uniform float border_width;\n"
+ "uniform float circle_radius;\n"
+ "uniform vec4 circle_color;\n"
+ "uniform vec4 border_color;\n"
+ "uniform vec2 circle_center;\n"
+ "void main(){\n"
+ "float d = distance(gl_FragCoord.xy, circle_center);\n"
+ "if (d < (circle_radius - border_width)) { gl_FragColor = circle_color; }\n"
+ "else if (d < circle_radius) { gl_FragColor = border_color; }\n"
+ "else { gl_FragColor = vec4(0.0, 0.0, 0.0, 0.0); }\n"
+ "}\n";
+
+// Alpha 2D texture shader
+static const GLchar* xtexture_2DA_vertex_shader_source =
+ "attribute vec2 position;\n"
+ "attribute vec2 aUV;\n"
+ "uniform mat4 MVMatrix;\n"
+ "uniform mat4 TransformMatrix;\n"
+ "varying vec2 varCoord;\n"
+ "void main() {\n"
+ " gl_Position = MVMatrix * TransformMatrix * vec4(position, 0.0, 1.0);\n"
+ " varCoord = aUV;\n"
+ "}\n";
+
+static const GLchar* xtexture_2DA_fragment_shader_source =
+ "precision lowp float;\n"
+ "uniform sampler2D uTex;\n"
+ "varying vec2 varCoord;\n"
+ "uniform vec4 color;\n"
+ "void main() {\n"
+ " gl_FragColor = texture2D(uTex, varCoord) + color;\n"
+ "}\n";
+
+
+//https://vitaliburkov.wordpress.com/2016/09/17/simple-and-fast-high-quality-antialiased-lines-with-opengl/
+static const GLchar* xAALine_vertex_shader_source =
+ "uniform vec2 uViewPort; //Width and Height of the viewport\n"
+ "varying vec2 vLineCenter;\n"
+ "attribute vec2 position;\n"
+ "uniform mat4 MVMatrix;\n"
+ "uniform mat4 TransformMatrix;\n"
+ "void main()\n"
+ "{\n"
+ " vec4 pp = MVMatrix * vec4(position, 0.0, 1.0);\n"
+ " gl_Position = pp;\n"
+ " vec2 vp = uViewPort;\n"
+ " vLineCenter = 0.5*(pp.xy + vec2(1, 1))*vp;\n"
+ "}\n";
+
+
+static const GLchar* xAALine_fragment_shader_source =
+ "precision mediump float;\n"
+ "uniform float uLineWidth;\n"
+ "uniform vec4 color;\n"
+ "uniform float uBlendFactor; //1.5..2.5\n"
+ "varying vec2 vLineCenter;\n"
+ "void main()\n"
+ "{\n"
+ " vec4 col = color;\n"
+ " float d = length(vLineCenter-gl_FragCoord.xy);\n"
+ " float w = uLineWidth;\n"
+ " if (d>w)\n"
+ " col.w = 0.0;\n"
+ " else{\n"
+ " if(float((w/2-d)/(w/2)) < .5){\n"
+ " //col.w *= pow(float((w-d)/w), uBlendFactor);\n"
+ " col.w *= pow(float((w/2-d)/(w/2)), uBlendFactor);\n"
+ " }\n"
+ " }\n"
+ " gl_FragColor = col;\n"
+ "}\n";
+
+
+CGLShaderProgram *pCAALine_shader_program[2];
+CGLShaderProgram *pCcolor_tri_shader_program[2];
+CGLShaderProgram *pCtexture_2D_shader_program[2];
+CGLShaderProgram *pCcircle_filled_shader_program[2];
+CGLShaderProgram *pCtexture_2DA_shader_program[2];
+CGLShaderProgram *pCtexture_2D_Color_shader_program[2];
+
+bool bCShadersLoaded[2];
+
+bool loadCShaders(int index) {
+ // Are the shaders ready?
+ if (bCShadersLoaded[index]) {
+ reConfigureCShaders(index);
+ return true;
+ }
+
+ bool ret_val = true;
+
+ // Simple colored triangle shader
+ if (!pCcolor_tri_shader_program[index]) {
+ CGLShaderProgram *shaderProgram = new CGLShaderProgram;
+ shaderProgram->addShaderFromSource(xcolor_tri_vertex_shader_source, GL_VERTEX_SHADER);
+ shaderProgram->addShaderFromSource(xcolor_tri_fragment_shader_source, GL_FRAGMENT_SHADER);
+ shaderProgram->linkProgram();
+
+ if (shaderProgram->isOK())
+ pCcolor_tri_shader_program[index] = shaderProgram;
+ }
+
+ if (!pCtexture_2D_shader_program[index]) {
+ CGLShaderProgram *shaderProgram = new CGLShaderProgram;
+ shaderProgram->addShaderFromSource(xtexture_2D_vertex_shader_source, GL_VERTEX_SHADER);
+ shaderProgram->addShaderFromSource(xtexture_2D_fragment_shader_source, GL_FRAGMENT_SHADER);
+ shaderProgram->linkProgram();
+
+ if (shaderProgram->isOK())
+ pCtexture_2D_shader_program[index] = shaderProgram;
+ }
+
+ if (!pCtexture_2D_Color_shader_program[index]) {
+ CGLShaderProgram *shaderProgram = new CGLShaderProgram;
+ shaderProgram->addShaderFromSource(xtexture_2D_ColorMod_vertex_shader_source, GL_VERTEX_SHADER);
+ shaderProgram->addShaderFromSource(xtexture_2D_ColorMod_fragment_shader_source, GL_FRAGMENT_SHADER);
+ shaderProgram->linkProgram();
+
+ if (shaderProgram->isOK())
+ pCtexture_2D_Color_shader_program[index] = shaderProgram;
+ }
+
+#if 0
+
+ if (!pcircle_filled_shader_program[index]) {
+ GLShaderProgram *shaderProgram = new GLShaderProgram;
+ shaderProgram->addShaderFromSource(circle_filled_vertex_shader_source, GL_VERTEX_SHADER);
+ shaderProgram->addShaderFromSource(circle_filled_fragment_shader_source, GL_FRAGMENT_SHADER);
+ shaderProgram->linkProgram();
+
+ if (shaderProgram->isOK())
+ pcircle_filled_shader_program[index] = shaderProgram;
+ }
+
+ if (!ptexture_2DA_shader_program[index]) {
+ GLShaderProgram *shaderProgram = new GLShaderProgram;
+ shaderProgram->addShaderFromSource(texture_2DA_vertex_shader_source, GL_VERTEX_SHADER);
+ shaderProgram->addShaderFromSource(texture_2DA_fragment_shader_source, GL_FRAGMENT_SHADER);
+ shaderProgram->linkProgram();
+
+ if (shaderProgram->isOK())
+ ptexture_2DA_shader_program[index] = shaderProgram;
+ }
+
+ if (!pAALine_shader_program[index]) {
+ GLShaderProgram *shaderProgram = new GLShaderProgram;
+ shaderProgram->addShaderFromSource(AALine_fragment_shader_source, GL_FRAGMENT_SHADER);
+ shaderProgram->addShaderFromSource(AALine_vertex_shader_source, GL_VERTEX_SHADER);
+ shaderProgram->linkProgram();
+
+ if (shaderProgram->isOK())
+ pAALine_shader_program[index] = shaderProgram;
+ }
+
+#endif
+
+ bCShadersLoaded[index] = true;
+ reConfigureCShaders(index);
+
+ return ret_val;
+}
+
+void reConfigureCShaders(int index) {
+}
+
+void unloadCShaders() {
+ bCShadersLoaded[0] = bCShadersLoaded[1] = false;
+}
+
+CGLShaderProgram *CGetStaticTriShader() {
+ CGLShaderProgram *shaderProgram = new CGLShaderProgram;
+ shaderProgram->addShaderFromSource(xcolor_tri_vertex_shader_source, GL_VERTEX_SHADER);
+ shaderProgram->addShaderFromSource(xcolor_tri_fragment_shader_source, GL_FRAGMENT_SHADER);
+ shaderProgram->linkProgram();
+
+ if (shaderProgram->isOK())
+ return shaderProgram;
+ else
+ return NULL;
+}
+
+
+#endif
diff --git a/docs/外部参考/OpenCPN源码基线_f7211b4e/上游源码/libs/s52plib/src/Cs52_shaders.h b/docs/外部参考/OpenCPN源码基线_f7211b4e/上游源码/libs/s52plib/src/Cs52_shaders.h
new file mode 100644
index 0000000..5e8bf5b
--- /dev/null
+++ b/docs/外部参考/OpenCPN源码基线_f7211b4e/上游源码/libs/s52plib/src/Cs52_shaders.h
@@ -0,0 +1,186 @@
+/***************************************************************************
+ *
+ * Project: OpenCPN
+ *
+ ***************************************************************************
+ * Copyright (C) 2017 by David S. Register *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
+ **************************************************************************/
+
+#ifndef __XSHADERS_H__
+#define __XSHADERS_H__
+
+#include
+#ifndef WX_PRECOMP
+#include
+#endif // precompiled headers
+
+#include "s52plibGL.h"
+
+#ifdef __OCPN__ANDROID__
+#include "qdebug.h"
+#endif
+
+#include
+#include
+#include
+#include
+
+class CGLShaderProgram;
+
+extern CGLShaderProgram *pAALine_shader_program[2];
+extern CGLShaderProgram *pCcolor_tri_shader_program[2];
+extern CGLShaderProgram *pCtexture_2D_shader_program[2];
+extern CGLShaderProgram *pcircle_filled_shader_program[2];
+extern CGLShaderProgram *ptexture_2DA_shader_program[2];
+extern CGLShaderProgram *pCtexture_2D_Color_shader_program[2];
+
+extern const GLchar* Cpreamble;
+
+class CGLShaderProgram
+{
+public:
+ CGLShaderProgram() : programId_(0), linked_(false) {
+ programId_ = glCreateProgram();
+ }
+ ~CGLShaderProgram() { }
+
+ bool addShaderFromSource(std::string const &shaderSource, GLenum shaderType) {
+ char const *shaderCStr = shaderSource.c_str();
+ GLuint shaderId = glCreateShader(shaderType);
+
+ GLchar const* files[] = { Cpreamble, shaderCStr };
+ GLint lengths[] = { (GLint)strlen(Cpreamble), (GLint)strlen(shaderCStr) };
+
+ glShaderSource(shaderId, 2, files, lengths);
+
+ glCompileShader(shaderId);
+ glGetShaderiv(shaderId, GL_COMPILE_STATUS, &success);
+ if (!success) {
+ GLint logLength = 0;
+ glGetShaderiv(shaderId, GL_INFO_LOG_LENGTH, &logLength);
+ if (logLength > 0) {
+ auto log = std::unique_ptr(new char[logLength]);
+ glGetShaderInfoLog(shaderId, logLength, &logLength, log.get());
+ printf("ERROR::SHADER::COMPILATION_FAILED\n%s\n", log.get());
+#ifdef USE_ANDROID_GLES2
+ qDebug() << "SHADER COMPILE ERROR " << log.get();
+#endif
+ }
+ return false;
+ }
+
+ glAttachShader(programId_, shaderId);
+ return true;
+ }
+
+ bool linkProgram() {
+ glLinkProgram(programId_);
+ glGetProgramiv( programId_, GL_LINK_STATUS, &linkSuccess); //requesting the status
+ if (linkSuccess == GL_FALSE) {
+ GLint logLength = 0;
+ glGetShaderiv(programId_, GL_INFO_LOG_LENGTH, &logLength);
+ if (logLength > 0) {
+ auto log = std::unique_ptr(new char[logLength]);
+ glGetShaderInfoLog(programId_, logLength, &logLength, log.get());
+ printf("ERROR::SHADER::LINK_FAILED\n%s\n", log.get());
+ }
+ return false;
+ }
+ linked_ = true;
+ return true;
+ }
+
+
+ void Bind() { glUseProgram(programId_); }
+ void UnBind() {
+ glDisableVertexAttribArray(0);
+ glUseProgram(0);
+ }
+
+ void SetUniform1f( const std::string &name, float value) {
+ GLint loc = getUniformLocation(name);
+ glUniform1f( loc, value);
+ }
+ void SetUniform2fv( const std::string &name, float *value) {
+ GLint loc = getUniformLocation(name);
+ glUniform2fv( loc, 1, value);
+ }
+ void SetUniform4fv( const std::string &name, float *value) {
+ GLint loc = getUniformLocation(name);
+ glUniform4fv( loc, 1, value);
+ }
+ void SetUniform1i( const std::string &name, GLint value) {
+ GLint loc = getUniformLocation(name);
+ glUniform1i( loc, value);
+ }
+ void SetUniformMatrix4fv( const std::string &name, float *value) {
+ GLint matloc = getUniformLocation(name);
+ glUniformMatrix4fv(matloc, 1, GL_FALSE, value);
+ }
+
+ void SetAttributePointerf( const std::string &name, float *value ){
+ GLint aloc = getAttributeLocation( name);
+ glVertexAttribPointer(aloc, 2, GL_FLOAT, GL_FALSE, 2 * sizeof(float), value);
+ glEnableVertexAttribArray(aloc);
+
+ // Disable VBO's (vertex buffer objects) for attributes.
+ glBindBuffer(GL_ARRAY_BUFFER, 0);
+ glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
+
+ }
+
+ GLint getAttributeLocation(const std::string &name) {
+ if(m_attribLocationCache.find(name) != m_attribLocationCache.end())
+ return m_attribLocationCache[name];
+
+ GLint loc = glGetAttribLocation(programId_, name.c_str());
+ m_attribLocationCache[name] = loc;
+ return loc;
+ }
+
+
+ GLuint programId() const { return programId_; }
+ bool isOK() const { return linked_; }
+
+ GLint getUniformLocation(const std::string &name) {
+ if(m_uniformLocationCache.find(name) != m_uniformLocationCache.end())
+ return m_uniformLocationCache[name];
+
+ GLint loc = glGetUniformLocation(programId_, name.c_str());
+ m_uniformLocationCache[name] = loc;
+ return loc;
+ }
+
+private:
+ std::unordered_map m_uniformLocationCache;
+ std::unordered_map m_attribLocationCache;
+ GLuint programId_;
+ bool linked_;
+ GLint success;
+ GLint linkSuccess;
+
+
+};
+
+
+bool loadCShaders(int index = 0);
+void reConfigureCShaders(int index = 0);
+void unloadCShaders();
+
+CGLShaderProgram *CGetStaticTriShader();
+#endif
diff --git a/docs/外部参考/OpenCPN源码基线_f7211b4e/上游源码/libs/s52plib/src/DepthFont.cpp b/docs/外部参考/OpenCPN源码基线_f7211b4e/上游源码/libs/s52plib/src/DepthFont.cpp
new file mode 100755
index 0000000..a8a2792
--- /dev/null
+++ b/docs/外部参考/OpenCPN源码基线_f7211b4e/上游源码/libs/s52plib/src/DepthFont.cpp
@@ -0,0 +1,179 @@
+/***************************************************************************
+ *
+ * Project: OpenCPN
+ * Purpose: texture OpenGL text rendering built from wxFont
+ * Author: Sean D'Epagnier
+ *
+ ***************************************************************************
+ * Copyright (C) 2014 Sean D'Epagnier *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
+ **************************************************************************/
+
+#include
+
+#include "s52plibGL.h"
+
+#include "DepthFont.h"
+#include "s52_plib_utils.h"
+
+DepthFont::DepthFont() {
+ texobj = 0;
+ m_built = false;
+ m_scaleFactor = 0;
+ m_ContentScaleFactor = 1.0;
+}
+
+DepthFont::~DepthFont() { Delete(); }
+
+void DepthFont::Build(wxFont *font, double scale, double dip_factor) {
+ /* avoid rebuilding if the parameters are the same */
+ if (m_built && (*font == m_font)) return;
+
+ m_font = *font;
+ m_scaleFactor = scale;
+ m_dip_factor = dip_factor;
+
+ m_maxglyphw = 0;
+ m_maxglyphh = 0;
+
+ double scaler = m_ContentScaleFactor * dip_factor;
+
+ wxFont *scaled_font = GetS52Utils()->GetFont(font, scaler);
+ wxScreenDC sdc;
+ sdc.SetFont(*scaled_font);
+
+ for (int i = 0; i < 10; i++) {
+ wxCoord gw, gh;
+ wxString text;
+ text = wxString::Format(_T("%d"), i);
+ wxCoord descent, exlead;
+ sdc.GetTextExtent(text, &gw, &gh, &descent, &exlead,
+ scaled_font); // measure the text
+
+ tgi[i].width = gw;
+ tgi[i].height = gh; // - descent;
+
+ tgi[i].advance = gw;
+ tgi[i].advance *= dip_factor;
+ if (i == 1)
+ m_width_one = gw;
+
+ m_maxglyphw = wxMax(tgi[i].width + tgi[i].advance, m_maxglyphw);
+ m_maxglyphh = wxMax(tgi[i].height, m_maxglyphh);
+ }
+
+ int w = 10 * m_maxglyphw;
+ int h = m_maxglyphh;
+
+ /* make power of 2 */
+
+ for (tex_w = 1; tex_w < w; tex_w *= 2)
+ ;
+ for (tex_h = 1; tex_h < h; tex_h *= 2)
+ ;
+
+ wxBitmap tbmp(tex_w, tex_h);
+ wxMemoryDC dc;
+ dc.SelectObject(tbmp);
+ dc.SetFont(*scaled_font);
+
+ /* fill bitmap with black */
+ dc.SetBackground(wxBrush(wxColour(0, 0, 0)));
+ dc.Clear();
+
+ /* draw the text white */
+ dc.SetTextForeground(wxColour(255, 255, 255));
+
+ /* wxPen pen(wxColour( 255, 255, 255 ));
+ wxBrush brush(wxColour( 255, 255, 255 ), wxTRANSPARENT);
+ dc.SetPen(pen);
+ dc.SetBrush(brush);
+ */
+ int row = 0, col = 0;
+ for (int i = 0; i < 10; i++) {
+ tgi[i].x = col * m_maxglyphw;
+ tgi[i].y = row * m_maxglyphh;
+
+ wxString text;
+ text = wxString::Format(_T("%d"), i);
+
+ dc.DrawText(text, tgi[i].x, tgi[i].y);
+
+ col++;
+ }
+
+ dc.SelectObject(wxNullBitmap);
+
+ wxImage image = tbmp.ConvertToImage();
+
+ GLuint format, internalformat;
+ int stride;
+
+ format = GL_ALPHA;
+ internalformat = format;
+ stride = 1;
+
+ unsigned char *imgdata = image.GetData();
+
+ if (imgdata) {
+ unsigned char *teximage = (unsigned char *)malloc(stride * tex_w * tex_h);
+
+ for (int j = 0; j < tex_w * tex_h; j++)
+ for (int k = 0; k < stride; k++)
+ teximage[j * stride + k] = imgdata[3 * j];
+
+ glGenTextures(1, &texobj);
+ glBindTexture(GL_TEXTURE_2D, texobj);
+
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+
+ glTexImage2D(GL_TEXTURE_2D, 0, internalformat, tex_w, tex_h, 0, format,
+ GL_UNSIGNED_BYTE, teximage);
+
+ free(teximage);
+ }
+
+ m_built = true;
+}
+
+void DepthFont::Delete() {
+ if (texobj) {
+ glDeleteTextures(1, &texobj);
+ texobj = 0;
+ }
+ m_built = false;
+ m_scaleFactor = 0;
+}
+
+bool DepthFont::GetGLTextureRect(wxRect &texrect, int symIndex) {
+ if (symIndex < 10) {
+ texrect.x = tgi[symIndex].x;
+ texrect.y = tgi[symIndex].y;
+ texrect.width = tgi[symIndex].width + (m_width_one / 10);
+ texrect.height = tgi[symIndex].height;
+ return true;
+ } else {
+ texrect.x = tgi[0].x;
+ texrect.y = tgi[0].y;
+ texrect.width = tgi[0].width;
+ texrect.height = tgi[0].height;
+ return false;
+ }
+}
diff --git a/docs/外部参考/OpenCPN源码基线_f7211b4e/上游源码/libs/s52plib/src/DepthFont.h b/docs/外部参考/OpenCPN源码基线_f7211b4e/上游源码/libs/s52plib/src/DepthFont.h
new file mode 100644
index 0000000..4d4473e
--- /dev/null
+++ b/docs/外部参考/OpenCPN源码基线_f7211b4e/上游源码/libs/s52plib/src/DepthFont.h
@@ -0,0 +1,72 @@
+/***************************************************************************
+ *
+ * Project: OpenCPN
+ * Purpose: OpenGL text rendering
+ * Author: David Register, Sean D'Epagnier
+ *
+ ***************************************************************************
+ * Copyright (C) 2020 David Register *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
+ **************************************************************************/
+
+#ifndef __DEPTHFONT_H__
+#define __DEPTHFONT_H__
+
+#include
+
+#define SOUND_MAX_GLYPH 50
+
+struct SoundTexGlyphInfo {
+ int x, y, width, height;
+ float advance;
+};
+
+class DepthFont {
+public:
+ DepthFont();
+ ~DepthFont();
+
+ void Build(wxFont *font, double scale, double dip_factor = 1.0);
+ void Delete();
+
+ bool IsBuilt() { return m_built; }
+ unsigned int GetTexture() { return texobj; }
+ bool GetGLTextureRect(wxRect &texrect, int symIndex);
+ wxSize GLTextureSize() { return wxSize(tex_w, tex_h); };
+ double GetScale() { return m_scaleFactor; }
+ void SetContentScaleFactor(double s){m_ContentScaleFactor = s;}
+
+private:
+ wxFont m_font;
+
+ SoundTexGlyphInfo tgi[SOUND_MAX_GLYPH];
+
+ unsigned int texobj;
+ int tex_w, tex_h;
+ int m_maxglyphw;
+ int m_maxglyphh;
+ bool m_built;
+
+ float m_dx;
+ float m_dy;
+ double m_scaleFactor;
+ double m_ContentScaleFactor;
+ int m_width_one;
+ double m_dip_factor;
+};
+
+#endif // guard
diff --git a/docs/外部参考/OpenCPN源码基线_f7211b4e/上游源码/libs/s52plib/src/TexFont.cpp b/docs/外部参考/OpenCPN源码基线_f7211b4e/上游源码/libs/s52plib/src/TexFont.cpp
new file mode 100755
index 0000000..40245b7
--- /dev/null
+++ b/docs/外部参考/OpenCPN源码基线_f7211b4e/上游源码/libs/s52plib/src/TexFont.cpp
@@ -0,0 +1,542 @@
+/***************************************************************************
+ *
+ * Project: OpenCPN
+ * Purpose: texture OpenGL text rendering built from wxFont
+ * Author: Sean D'Epagnier
+ *
+ ***************************************************************************
+ * Copyright (C) 2014 Sean D'Epagnier *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
+ **************************************************************************/
+
+#include
+
+#ifdef ocpnUSE_GL
+#ifdef __OCPN_USE_GLEW__
+ #ifndef __OCPN__ANDROID__
+ #if defined(_WIN32)
+ #include "glew.h"
+ #elif defined(__WXQT__) || defined(__WXGTK__)
+ #include
+ #endif
+ #endif
+#endif
+
+
+#if defined(__OCPN__ANDROID__)
+ #include
+ #include // this is a cut-down version of gl.h
+ #include
+#elif defined(_WIN32)
+ #define GL_GLEXT_PROTOTYPES
+ #include
+ #include
+ //typedef void (__stdcall * _GLUfuncptr)(void);
+#elif defined(__WXOSX__)
+ #include
+ #include
+ typedef void (* _GLUfuncptr)();
+ #define GL_COMPRESSED_RGB_FXT1_3DFX 0x86B0
+#elif defined(__WXQT__) || defined(__WXGTK__)
+ #define GL_GLEXT_PROTOTYPES
+ #include
+ #include
+ #include
+#endif
+#endif
+
+#include "TexFont.h"
+#include "linmath.h"
+#ifdef ocpnUSE_GL
+ #include "Cs52_shaders.h"
+
+CGLShaderProgram *m_TexFontShader;
+#endif
+
+#include "s52_plib_utils.h"
+
+TexFont::TexFont() {
+ texobj = 0;
+ m_blur = false;
+ m_built = false;
+ m_color = wxColor(0, 0, 0);
+ m_angle = 0;
+ m_ContentScaleFactor = 1.0;
+
+ m_shadersLoaded = false;
+
+}
+
+TexFont::~TexFont() { Delete(); }
+
+void TexFont::Build(wxFont &font, double scale_factor, double dpi_factor, bool blur) {
+ /* avoid rebuilding if the parameters are the same */
+ if (m_built && (font == m_font) && (blur == m_blur)) return;
+
+ m_font = font;
+ m_blur = blur;
+
+ m_maxglyphw = 0;
+ m_maxglyphh = 0;
+
+ double scaler = scale_factor / dpi_factor;
+ scaler /= m_ContentScaleFactor;
+
+ wxFont *scaled_font = GetS52Utils()->GetFont(&font, scaler);
+ wxScreenDC sdc;
+ sdc.SetFont(*scaled_font);
+
+ for (int i = MIN_GLYPH; i < MAX_GLYPH; i++) {
+ wxCoord gw, gh;
+ wxString text;
+ if (i == DEGREE_GLYPH)
+ text = wxString::Format(_T("%c"), 0x00B0); //_T("?");
+ else
+ text = wxString::Format(_T("%c"), i);
+ wxCoord descent, exlead;
+ sdc.GetTextExtent(text, &gw, &gh, &descent, &exlead,
+ scaled_font); // measure the text
+
+ tgi[i].width = gw;
+ tgi[i].height = gh;
+
+ tgi[i].advance = gw;
+ tgi[i].advance *= dpi_factor;
+
+
+ m_maxglyphw = wxMax(tgi[i].width, m_maxglyphw);
+ m_maxglyphh = wxMax(tgi[i].height, m_maxglyphh);
+ }
+
+ /* add extra pixel to give a border between rows of characters
+ without this, in some cases a faint line can be see on the edge
+ from the character above */
+ m_maxglyphh++;
+
+ int w = COLS_GLYPHS * m_maxglyphw;
+ int h = ROWS_GLYPHS * m_maxglyphh;
+
+ wxASSERT(w < 2048 && h < 2048);
+
+ /* make power of 2 */
+ for (tex_w = 1; tex_w < w; tex_w *= 2)
+ ;
+ for (tex_h = 1; tex_h < h; tex_h *= 2)
+ ;
+
+ wxBitmap tbmp(tex_w, tex_h);
+ wxMemoryDC dc;
+ dc.SelectObject(tbmp);
+ dc.SetFont(*scaled_font);
+
+ /* fill bitmap with black */
+ dc.SetBackground(wxBrush(wxColour(0, 0, 0)));
+ dc.Clear();
+
+ /* draw the text white */
+ dc.SetTextForeground(wxColour(255, 255, 255));
+
+ /* wxPen pen(wxColour( 255, 255, 255 ));
+ wxBrush brush(wxColour( 255, 255, 255 ), wxTRANSPARENT);
+ dc.SetPen(pen);
+ dc.SetBrush(brush);
+ */
+ int row = 0, col = 0;
+ for (int i = MIN_GLYPH; i < MAX_GLYPH; i++) {
+ if (col == COLS_GLYPHS) {
+ col = 0;
+ row++;
+ }
+
+ tgi[i].x = col * m_maxglyphw;
+ tgi[i].y = row * m_maxglyphh;
+
+ wxString text;
+ if (i == DEGREE_GLYPH)
+ text = wxString::Format(_T("%c"), 0x00B0); //_T("?");
+ else
+ text = wxString::Format(_T("%c"), i);
+
+ dc.DrawText(text, tgi[i].x, tgi[i].y);
+
+ // dc.DrawRectangle(tgi[i].x, tgi[i].y, tgi[i].advance,
+ // tgi[i].height);
+ col++;
+ }
+
+ dc.SelectObject(wxNullBitmap);
+
+ wxImage image = tbmp.ConvertToImage();
+
+ unsigned int format = 0, internalformat = 0;
+ int stride;
+
+#ifdef ocpnUSE_GL
+ format = GL_ALPHA;
+#endif
+
+ internalformat = format;
+ stride = 1;
+
+ if (m_blur) image = image.Blur(1);
+
+ unsigned char *imgdata = image.GetData();
+
+ if (imgdata) {
+ unsigned char *teximage = (unsigned char *)malloc(stride * tex_w * tex_h);
+
+ for (int j = 0; j < tex_w * tex_h; j++)
+ for (int k = 0; k < stride; k++)
+ teximage[j * stride + k] = imgdata[3 * j];
+
+ Delete();
+
+#ifdef ocpnUSE_GL
+ glGenTextures(1, &texobj);
+ glBindTexture(GL_TEXTURE_2D, texobj);
+
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER,
+ GL_NEAREST /*GL_LINEAR*/);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
+
+ glTexImage2D(GL_TEXTURE_2D, 0, internalformat, tex_w, tex_h, 0, format,
+ GL_UNSIGNED_BYTE, teximage);
+#endif
+ free(teximage);
+
+ }
+
+ m_built = true;
+}
+
+void TexFont::Delete() {
+ if (texobj) {
+#ifdef ocpnUSE_GL
+ glDeleteTextures(1, &texobj);
+#endif
+ texobj = 0;
+ }
+ m_built = false;
+}
+
+void TexFont::GetTextExtent(const char *string, int *width, int *height) {
+ int w = 0, h = 0;
+
+ for (int i = 0; string[i]; i++) {
+ unsigned char c = string[i];
+ if (c == '\n') {
+ h += tgi[(int)'A'].height;
+ continue;
+ }
+ if (c == 0xc2 && (unsigned char)string[i + 1] == 0xb0) {
+ c = DEGREE_GLYPH;
+ i++;
+ }
+ if (c < MIN_GLYPH || c >= MAX_GLYPH) continue;
+
+ TexGlyphInfo &tgisi = tgi[c];
+ w += tgisi.advance;
+ if (tgisi.height > h) h = tgisi.height;
+ }
+ if (width) *width = w;
+ if (height) *height = h;
+}
+
+void TexFont::GetTextExtent(const wxString &string, int *width, int *height) {
+ GetTextExtent((const char *)string.ToUTF8(), width, height);
+}
+
+void TexFont::RenderGlyph(int c) {
+#ifdef ocpnUSE_GL
+ if (c < MIN_GLYPH || c >= MAX_GLYPH) return;
+
+ TexGlyphInfo &tgic = tgi[c];
+
+ int x = tgic.x, y = tgic.y;
+ float w = m_maxglyphw, h = m_maxglyphh;
+ float tx1 = (float)x / (float)tex_w;
+ float tx2 = (float)(x + w) / (float)tex_w;
+ float ty1 = (float)y / (float)tex_h;
+ float ty2 = (float)(y + h) / (float)tex_h;
+
+ if(!m_TexFontShader)
+ return;
+
+ float uv[8];
+ float coords[8];
+
+ // normal uv
+ uv[0] = tx1;
+ uv[1] = ty1;
+ uv[2] = tx2;
+ uv[3] = ty1;
+ uv[4] = tx2;
+ uv[5] = ty2;
+ uv[6] = tx1;
+ uv[7] = ty2;
+
+ // pixels
+ coords[0] = 0;
+ coords[1] = 0;
+ coords[2] = w;
+ coords[3] = 0;
+ coords[4] = w;
+ coords[5] = h;
+ coords[6] = 0;
+ coords[7] = h;
+
+ m_TexFontShader->Bind();
+
+ // Set up the texture sampler to texture unit 0
+ m_TexFontShader->SetUniform1i( "uTex", 0);
+
+ float colorv[4];
+ colorv[0] = m_color.Red() / float(256);
+ colorv[1] = m_color.Green() / float(256);
+ colorv[2] = m_color.Blue() / float(256);
+ colorv[3] = 0;
+
+ m_TexFontShader->SetUniform4fv( "color", colorv);
+
+ // Rotate
+ //float angle = 0;
+ mat4x4 I, Q;
+ mat4x4_identity(I);
+ mat4x4_identity(Q);
+ //mat4x4_rotate_Z(Q, I, m_angle);
+
+ // Translate
+ Q[3][0] = m_dx; // - m_vpwidth/2; //m_dx;
+ Q[3][1] = m_dy;// - m_vpheight/2; //m_dy;
+
+ // mat4x4_translate_in_place(I, m_dx, m_dy, 0);
+ // mat4x4_rotate_Z(Q, I, m_angle);
+ //mat4x4_translate_in_place(Q, -m_dx, -m_dy, 0);
+
+ m_TexFontShader->SetUniformMatrix4fv( "TransformMatrix", (GLfloat *)Q);
+
+// For some reason, glDrawElements is busted on Android
+// So we do this a hard ugly way, drawing two triangles...
+#if 0
+ GLushort indices1[] = {0,1,3,2};
+ glDrawElements(GL_TRIANGLE_STRIP, 4, GL_UNSIGNED_SHORT, indices1);
+#else
+
+ float co1[8];
+ co1[0] = coords[0];
+ co1[1] = coords[1];
+ co1[2] = coords[2];
+ co1[3] = coords[3];
+ co1[4] = coords[6];
+ co1[5] = coords[7];
+ co1[6] = coords[4];
+ co1[7] = coords[5];
+
+ float tco1[8];
+ tco1[0] = uv[0];
+ tco1[1] = uv[1];
+ tco1[2] = uv[2];
+ tco1[3] = uv[3];
+ tco1[4] = uv[6];
+ tco1[5] = uv[7];
+ tco1[6] = uv[4];
+ tco1[7] = uv[5];
+
+ m_TexFontShader->SetAttributePointerf( "position", co1);
+ m_TexFontShader->SetAttributePointerf( "aUV", tco1);
+
+ glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
+
+ m_TexFontShader->UnBind();
+
+#endif
+
+ m_dx += tgic.advance; // * cos(m_angle); // + tgic.advance * sin(m_angle);
+ //m_dy += tgic.advance * sin(m_angle); // - tgic.advance * cos(m_angle);
+#endif
+}
+
+void TexFont::RenderString(const char *string, int x, int y, float angle) {
+#ifdef ocpnUSE_GL
+
+#if !defined(USE_ANDROID_GLES2) && !defined(ocpnUSE_GLSL)
+
+ glPushMatrix();
+ glTranslatef(x, y, 0);
+
+ glPushMatrix();
+ glBindTexture(GL_TEXTURE_2D, texobj);
+
+ for (int i = 0; string[i]; i++) {
+ if (string[i] == '\n') {
+ glPopMatrix();
+ glTranslatef(0, tgi[(int)'A'].height, 0);
+ glPushMatrix();
+ continue;
+ }
+ /* degree symbol */
+ if ((unsigned char)string[i] == 0xc2 &&
+ (unsigned char)string[i + 1] == 0xb0) {
+ RenderGlyph(DEGREE_GLYPH);
+ i++;
+ continue;
+ }
+ RenderGlyph(string[i]);
+ }
+
+ glPopMatrix();
+ glPopMatrix();
+#else
+ //FIXME (dave) this is awful code, drawing chars glyph at a time.
+ //FIXME (dave) Also, need to render string at an angle....
+ m_dx = (x - m_vpwidth/2) * cos(angle) + (y - m_vpheight/2) * sin(angle);
+ m_dx += m_vpwidth/2;
+ m_dy = -(x - m_vpwidth/2) * sin(angle) + (y - m_vpheight/2) * cos(angle);
+ m_dy += m_vpheight/2;
+
+ m_dx = x;
+ m_dy = y;
+ m_angle = angle;
+
+ glBindTexture(GL_TEXTURE_2D, texobj);
+
+ for (int i = 0; string[i]; i++) {
+ if (string[i] == '\n') {
+ m_dy += tgi[(int)'A'].height;
+ continue;
+ }
+ /* degree symbol */
+ if ((unsigned char)string[i] == 0xc2 &&
+ (unsigned char)string[i + 1] == 0xb0) {
+ RenderGlyph(DEGREE_GLYPH);
+ i++;
+ continue;
+ }
+ RenderGlyph(string[i]);
+ }
+
+#endif
+#endif
+}
+
+void TexFont::RenderString(const wxString &string, int x, int y, float angle) {
+#ifdef ocpnUSE_GL
+ LoadTexFontShaders();
+#endif
+ RenderString((const char *)string.ToUTF8(), x, y, angle);
+}
+
+void TexFont::PrepareShader(int width, int height, double rotation){
+#ifdef ocpnUSE_GL
+ if(!m_TexFontShader)
+ LoadTexFontShaders();
+
+ m_vpwidth = width;
+ m_vpheight = height;
+
+ mat4x4 m;
+ float vp_transform[16];
+ mat4x4_identity(m);
+ mat4x4_scale_aniso((float(*)[4])vp_transform, m, 2.0 / (float)width,
+ -2.0 / (float)height, 1.0);
+ // Rotate
+ mat4x4 Q;
+ mat4x4_rotate_Z(Q, (float(*)[4])vp_transform, rotation);
+ mat4x4_translate_in_place(Q, -width / 2.0, -height / 2.0, 0);
+
+ mat4x4 I;
+ mat4x4_identity(I);
+
+ m_TexFontShader->Bind();
+ m_TexFontShader->SetUniformMatrix4fv( "MVMatrix", (GLfloat *)Q);
+ m_TexFontShader->SetUniformMatrix4fv( "TransformMatrix", (GLfloat *)I);
+#endif
+}
+
+#ifdef ocpnUSE_GL
+#if defined(USE_ANDROID_GLES2) || defined(ocpnUSE_GLSL)
+
+#ifdef USE_ANDROID_GLES2
+const GLchar* TexFont_preamble =
+"\n";
+#else
+const GLchar* TexFont_preamble =
+"#version 120\n"
+"#define precision\n"
+"#define lowp\n"
+"#define mediump\n"
+"#define highp\n";
+#endif
+
+
+// 2D texture shader with color modulation, used for colored text
+static const GLchar *TexFont_vertex_shader_source =
+ "precision highp float;\n"
+ "attribute vec2 position;\n"
+ "attribute vec2 aUV;\n"
+ "uniform mat4 MVMatrix;\n"
+ "uniform mat4 TransformMatrix;\n"
+ "varying vec2 varCoord;\n"
+ "void main() {\n"
+ " gl_Position = MVMatrix * TransformMatrix * vec4(position, 0.0, 1.0);\n"
+ " //varCoord = aUV.st;\n"
+ " varCoord = aUV;\n"
+ "}\n";
+
+static const GLchar *TexFont_fragment_shader_source =
+ "precision highp float;\n"
+ "uniform sampler2D uTex;\n"
+ "uniform vec4 color;\n"
+ "varying vec2 varCoord;\n"
+ "void main() {\n"
+ " vec4 col=texture2D(uTex, varCoord);\n"
+ " gl_FragColor = color;\n"
+ " gl_FragColor.a = col.a;\n"
+ "}\n";
+
+
+#if 1
+
+bool TexFont::LoadTexFontShaders() {
+ bool ret_val = true;
+
+ // Are the shaders ready?
+ if(m_TexFontShader)
+ return true;
+
+ // Simple colored triangle shader
+ if (!m_TexFontShader) {
+ CGLShaderProgram *shaderProgram = new CGLShaderProgram;
+ shaderProgram->addShaderFromSource(TexFont_vertex_shader_source, GL_VERTEX_SHADER);
+ shaderProgram->addShaderFromSource(TexFont_fragment_shader_source, GL_FRAGMENT_SHADER);
+ shaderProgram->linkProgram();
+
+ if (shaderProgram->isOK())
+ m_TexFontShader = shaderProgram;
+ }
+
+ m_shadersLoaded = true;
+
+ return ret_val;
+}
+
+
+#endif
+#endif
+#endif
diff --git a/docs/外部参考/OpenCPN源码基线_f7211b4e/上游源码/libs/s52plib/src/TexFont.h b/docs/外部参考/OpenCPN源码基线_f7211b4e/上游源码/libs/s52plib/src/TexFont.h
new file mode 100644
index 0000000..4148805
--- /dev/null
+++ b/docs/外部参考/OpenCPN源码基线_f7211b4e/上游源码/libs/s52plib/src/TexFont.h
@@ -0,0 +1,93 @@
+/***************************************************************************
+ *
+ * Project: OpenCPN
+ * Purpose: OpenGL text rendering
+ * Author: Sean D'Epagnier
+ *
+ ***************************************************************************
+ * Copyright (C) 2014 Sean D'Epagnier *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
+ **************************************************************************/
+
+#ifndef __TEXFONT_H__
+#define __TEXFONT_H__
+
+#include
+#include
+
+/* support ascii plus degree symbol for now pack font in a single texture 16x8
+ */
+#define DEGREE_GLYPH 127
+#define MIN_GLYPH 32
+#define MAX_GLYPH 128
+
+#define NUM_GLYPHS (MAX_GLYPH - MIN_GLYPH)
+
+#define COLS_GLYPHS 16
+#define ROWS_GLYPHS ((NUM_GLYPHS / COLS_GLYPHS) + 1)
+
+struct TexGlyphInfo {
+ int x, y, width, height;
+ float advance;
+};
+
+class TexFont {
+public:
+ TexFont();
+ ~TexFont();
+
+ void Build(wxFont &font, double scale_factor, double dpi_factor, bool blur = false);
+ void Delete();
+
+ void GetTextExtent(const wxString &string, int *width, int *height);
+ void RenderString(const char *string, int x=0, int y=0, float angle = 0.0);
+ void RenderString(const wxString &string, int x=0, int y=0, float angle = 0.0);
+ bool IsBuilt() { return m_built; }
+ void SetColor(wxColor &color) { m_color = color; }
+ void PrepareShader(int width, int height, double rotation);
+ void SetContentScaleFactor(double s){m_ContentScaleFactor = s;}
+
+private:
+ void GetTextExtent(const char *string, int *width, int *height);
+ void RenderGlyph(int c);
+ bool LoadTexFontShaders();
+
+ wxFont m_font;
+ bool m_blur;
+
+ TexGlyphInfo tgi[MAX_GLYPH];
+
+ unsigned int texobj;
+ int tex_w, tex_h;
+ int m_maxglyphw;
+ int m_maxglyphh;
+ bool m_built;
+
+ float m_dx, m_dy;
+ float m_angle;
+ int m_vpwidth, m_vpheight;
+
+ wxColor m_color;
+
+ bool m_shadersLoaded;
+ double m_ContentScaleFactor;
+
+};
+
+TexFont *GetTexFont(wxFont *key);
+
+#endif // guard
diff --git a/docs/外部参考/OpenCPN源码基线_f7211b4e/上游源码/libs/s52plib/src/chartsymbols.cpp b/docs/外部参考/OpenCPN源码基线_f7211b4e/上游源码/libs/s52plib/src/chartsymbols.cpp
new file mode 100644
index 0000000..b13a8b0
--- /dev/null
+++ b/docs/外部参考/OpenCPN源码基线_f7211b4e/上游源码/libs/s52plib/src/chartsymbols.cpp
@@ -0,0 +1,885 @@
+/***************************************************************************
+ *
+ * Project: OpenCPN
+ * Purpose: Chart Symbols
+ * Author: Jesper Weissglas
+ *
+ ***************************************************************************
+ * Copyright (C) 2010 by David S. Register *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
+ **************************************************************************/
+//#include "config.h"
+
+#include "wx/wxprec.h"
+
+#ifndef WX_PRECOMP
+#include "wx/wx.h"
+#endif
+
+#include
+#include
+
+#if defined(__OCPN__ANDROID__)
+ #include
+ #include // this is a cut-down version of gl.h
+ #include
+#elif defined(__MSVC__)
+ #include "glew.h"
+#elif defined(__WXOSX__)
+ #include
+ #include
+ typedef void (* _GLUfuncptr)();
+ #define GL_COMPRESSED_RGB_FXT1_3DFX 0x86B0
+#elif defined(__WXQT__) || defined(__WXGTK__)
+ #include
+ #include
+#endif
+
+#include "chartsymbols.h"
+#ifdef ocpnUSE_GL
+#include
+#endif
+
+#include "s52plib.h"
+
+//--------------------------------------------------------------------------------------
+
+ChartSymbols::ChartSymbols(void) {}
+
+ChartSymbols::~ChartSymbols(void) {}
+
+void ChartSymbols::InitializeTables(void) {
+ rasterSymbolsLoadedColorMapNumber = -1;
+ ColorTableIndex = 0;
+ rasterSymbolsTexture = 0;
+}
+
+void ChartSymbols::DeleteGlobals(void) {
+ m_symbolGraphicLocations.clear();
+
+ for (unsigned int i = 0; i < m_colorTables.GetCount(); i++) {
+ colTable *ct = (colTable *)m_colorTables.Item(i);
+ delete ct->tableName;
+ ct->colors.clear();
+ ct->wxColors.clear();
+ delete ct;
+ }
+
+ m_colorTables.Clear();
+}
+
+void ChartSymbols::ProcessColorTables(pugi::xml_node &node) {
+ for (pugi::xml_node child = node.first_child(); child != 0;
+ child = child.next_sibling()) {
+ const char *pcn = child.name();
+
+ if (!strcmp(pcn, "color-table")) {
+ colTable *colortable = new colTable;
+ colortable->tableName =
+ new wxString(child.first_attribute().value(), wxConvUTF8);
+
+ pugi::xml_node colorNode = child.first_child();
+ while (colorNode) {
+ if (!strcmp(colorNode.name(), "graphics-file")) {
+ colortable->rasterFileName =
+ wxString(colorNode.first_attribute().value(), wxConvUTF8);
+ }
+
+ if (!strcmp(colorNode.name(), "color")) {
+ wxString key;
+ S52color color;
+
+ for (pugi::xml_attribute attr = colorNode.first_attribute(); attr;
+ attr = attr.next_attribute()) {
+ const char *pca = attr.name();
+ if (!strcmp(pca, "name")) {
+ strncpy(color.colName, attr.value(), 5);
+ color.colName[5] = 0;
+ key = wxString(attr.value(), wxConvUTF8);
+
+ } else if (!strcmp(pca, "r")) {
+ color.R = attr.as_int();
+ } else if (!strcmp(pca, "g")) {
+ color.G = attr.as_int();
+ } else if (!strcmp(pca, "b")) {
+ color.B = attr.as_int();
+ }
+ }
+
+ colortable->colors[key] = color;
+ wxColour wxcolor(color.R, color.G, color.B);
+ colortable->wxColors[key] = wxcolor;
+ }
+
+ colorNode = colorNode.next_sibling();
+ }
+
+ m_colorTables.Add((void *)colortable);
+ }
+ }
+}
+
+#define TGET_INT_PROPERTY_VALUE(node, name, target) \
+ propVal = wxString(node->Attribute(name), wxConvUTF8); \
+ propVal.ToLong(&numVal, 0); \
+ target = numVal;
+void ChartSymbols::ProcessLookups(pugi::xml_node &node) {
+ Lookup lookup;
+
+ for (pugi::xml_node child = node.first_child(); child != 0;
+ child = child.next_sibling()) {
+ const char *pcn = child.name();
+
+ if (!strcmp(pcn, "lookup")) {
+ for (pugi::xml_attribute attr = child.first_attribute(); attr;
+ attr = attr.next_attribute()) {
+ const char *pca = attr.name();
+ if (!strcmp(pca, "name")) {
+ lookup.name = wxString(attr.value(), wxConvUTF8);
+ } else if (!strcmp(pca, "RCID")) {
+ lookup.RCID = attr.as_int();
+ } else if (!strcmp(pca, "id")) {
+ lookup.id = attr.as_int();
+ }
+ }
+ }
+
+ pugi::xml_node lookupNode = child.first_child();
+ while (lookupNode) {
+ const char *nodeText = lookupNode.first_child().value();
+
+ if (!strcmp(lookupNode.name(), "type")) {
+ if (!strcmp(nodeText, "Area"))
+ lookup.type = AREAS_T;
+ else if (!strcmp(nodeText, "Line"))
+ lookup.type = LINES_T;
+ else
+ lookup.type = POINT_T;
+ } else if (!strcmp(lookupNode.name(), "disp-prio")) {
+ if (!strcmp(nodeText, "Group 1"))
+ lookup.displayPrio = PRIO_GROUP1;
+ else if (!strcmp(nodeText, "Area 1"))
+ lookup.displayPrio = PRIO_AREA_1;
+ else if (!strcmp(nodeText, "Area 2"))
+ lookup.displayPrio = PRIO_AREA_2;
+ else if (!strcmp(nodeText, "Point Symbol"))
+ lookup.displayPrio = PRIO_SYMB_POINT;
+ else if (!strcmp(nodeText, "Line Symbol"))
+ lookup.displayPrio = PRIO_SYMB_LINE;
+ else if (!strcmp(nodeText, "Area Symbol"))
+ lookup.displayPrio = PRIO_SYMB_AREA;
+ else if (!strcmp(nodeText, "Routing"))
+ lookup.displayPrio = PRIO_ROUTEING;
+ else if (!strcmp(nodeText, "Hazards"))
+ lookup.displayPrio = PRIO_HAZARDS;
+ else if (!strcmp(nodeText, "Mariners"))
+ lookup.displayPrio = PRIO_MARINERS;
+ else
+ lookup.displayPrio = PRIO_NODATA;
+
+ }
+
+ else if (!strcmp(lookupNode.name(), "radar-prio")) {
+ if (!strcmp(nodeText, "On Top"))
+ lookup.radarPrio = RAD_OVER;
+ else
+ lookup.radarPrio = RAD_SUPP;
+ }
+
+ else if (!strcmp(lookupNode.name(), "table-name")) {
+ if (!strcmp(nodeText, "Simplified"))
+ lookup.tableName = SIMPLIFIED;
+ else if (!strcmp(nodeText, "Lines"))
+ lookup.tableName = LINES;
+ else if (!strcmp(nodeText, "Plain"))
+ lookup.tableName = PLAIN_BOUNDARIES;
+ else if (!strcmp(nodeText, "Symbolized"))
+ lookup.tableName = SYMBOLIZED_BOUNDARIES;
+ else
+ lookup.tableName = PAPER_CHART;
+ }
+
+ else if (!strcmp(lookupNode.name(), "display-cat")) {
+ if (!strcmp(nodeText, "Displaybase"))
+ lookup.displayCat = DISPLAYBASE;
+ else if (!strcmp(nodeText, "Standard"))
+ lookup.displayCat = STANDARD;
+ else if (!strcmp(nodeText, "Other"))
+ lookup.displayCat = OTHER;
+ else if (!strcmp(nodeText, "Mariners"))
+ lookup.displayCat = MARINERS_STANDARD;
+ else
+ lookup.displayCat = OTHER;
+ }
+
+ else if (!strcmp(lookupNode.name(), "comment")) {
+ lookup.comment = lookupNode.first_child().text().as_int();
+ }
+
+ else if (!strcmp(lookupNode.name(), "instruction")) {
+ wxString inst(nodeText, wxConvUTF8);
+ lookup.instruction = inst;
+ lookup.instruction.Append('\037');
+
+ }
+
+ else if (!strcmp(lookupNode.name(), "attrib-code")) {
+ int nc = strlen(nodeText);
+ if (nc >= 6) { // ignore spurious short fields
+ std::string attVal = std::string(nodeText);
+ if(attVal.length() == 6)
+ attVal += ' ';
+ lookup.attributeCodeArray.push_back(attVal);
+ }
+ }
+
+ lookupNode = lookupNode.next_sibling();
+ }
+
+ BuildLookup(lookup);
+ lookup.attributeCodeArray.clear();
+ }
+}
+
+void ChartSymbols::ProcessVectorTag(pugi::xml_node &vectorNode,
+ SymbolSizeInfo_t &vectorSize) {
+ vectorSize.size.x = vectorNode.attribute("width").as_int();
+ vectorSize.size.y = vectorNode.attribute("height").as_int();
+
+ for (pugi::xml_node child = vectorNode.first_child(); child != 0;
+ child = child.next_sibling()) {
+ const char *nodeType = child.name();
+
+ if (!strcmp(nodeType, "distance")) {
+ vectorSize.minDistance = child.attribute("min").as_int();
+ vectorSize.maxDistance = child.attribute("max").as_int();
+ }
+
+ else if (!strcmp(nodeType, "origin")) {
+ vectorSize.origin.x = child.attribute("x").as_int();
+ vectorSize.origin.y = child.attribute("y").as_int();
+ }
+
+ else if (!strcmp(nodeType, "pivot")) {
+ vectorSize.pivot.x = child.attribute("x").as_int();
+ vectorSize.pivot.y = child.attribute("y").as_int();
+ }
+ }
+}
+
+void ChartSymbols::ProcessLinestyles(pugi::xml_node &node) {
+ LineStyle lineStyle;
+
+ for (pugi::xml_node child = node.first_child(); child != 0;
+ child = child.next_sibling()) {
+ lineStyle.RCID = child.attribute("RCID").as_int();
+
+ pugi::xml_node lineNode = child.first_child();
+ while (lineNode) {
+ const char *nodeText = lineNode.first_child().value();
+ const char *nodeType = lineNode.name();
+
+ if (!strcmp(nodeType, "description"))
+ lineStyle.description = nodeText;
+ else if (!strcmp(nodeType, "name"))
+ lineStyle.name = nodeText;
+ else if (!strcmp(nodeType, "color-ref"))
+ lineStyle.colorRef = nodeText;
+ else if (!strcmp(nodeType, "HPGL"))
+ lineStyle.HPGL = nodeText;
+ else if (!strcmp(nodeType, "vector"))
+ ProcessVectorTag(lineNode, lineStyle.vectorSize);
+
+ lineNode = lineNode.next_sibling();
+ }
+ BuildLineStyle(lineStyle);
+ }
+}
+
+void ChartSymbols::ProcessPatterns(pugi::xml_node &node) {
+ OCPNPattern pattern;
+
+ for (pugi::xml_node child = node.first_child(); child != 0;
+ child = child.next_sibling()) {
+ pattern.RCID = child.attribute("RCID").as_int();
+
+ pattern.hasVector = false;
+ pattern.hasBitmap = false;
+ pattern.preferBitmap = true;
+
+ pugi::xml_node pattNode = child.first_child();
+ while (pattNode) {
+ const char *nodeText = pattNode.first_child().value();
+ const char *nodeType = pattNode.name();
+
+ if (!strcmp(nodeType, "description"))
+ pattern.description = nodeText;
+ else if (!strcmp(nodeType, "name"))
+ pattern.name = nodeText;
+ else if (!strcmp(nodeType, "filltype"))
+ pattern.fillType = nodeText[0];
+ else if (!strcmp(nodeType, "spacing"))
+ pattern.spacing = nodeText[0];
+ else if (!strcmp(nodeType, "definition"))
+ pattern.hasVector = !strcmp(nodeText, "V");
+ else if (!strcmp(nodeType, "color-ref"))
+ pattern.colorRef = nodeText;
+ else if (!strcmp(nodeType, "HPGL")) {
+ pattern.HPGL = nodeText;
+ pattern.hasVector = true;
+ }
+
+ else if (!strcmp(nodeType, "prefer-bitmap")) {
+ if (!strcmp(nodeText, "no"))
+ pattern.preferBitmap = false;
+ else if (!strcmp(nodeText, "false"))
+ pattern.preferBitmap = false;
+ }
+
+ else if (!strcmp(nodeType, "bitmap")) {
+ pattern.bitmapSize.size.x = pattNode.attribute("width").as_int();
+ pattern.bitmapSize.size.y = pattNode.attribute("height").as_int();
+
+ for (pugi::xml_node child = pattNode.first_child(); child != 0;
+ child = child.next_sibling()) {
+ const char *nodeType = child.name();
+
+ if (!strcmp(nodeType, "distance")) {
+ pattern.bitmapSize.minDistance = child.attribute("min").as_int();
+ pattern.bitmapSize.maxDistance = child.attribute("max").as_int();
+ } else if (!strcmp(nodeType, "origin")) {
+ pattern.bitmapSize.origin.x = child.attribute("x").as_int();
+ pattern.bitmapSize.origin.y = child.attribute("y").as_int();
+ } else if (!strcmp(nodeType, "pivot")) {
+ pattern.bitmapSize.pivot.x = child.attribute("x").as_int();
+ pattern.bitmapSize.pivot.y = child.attribute("y").as_int();
+ } else if (!strcmp(nodeType, "graphics-location")) {
+ pattern.bitmapSize.graphics.x = child.attribute("x").as_int();
+ pattern.bitmapSize.graphics.y = child.attribute("y").as_int();
+ }
+ }
+ }
+
+ else if (!strcmp(nodeType, "vector"))
+ ProcessVectorTag(pattNode, pattern.vectorSize);
+
+ pattNode = pattNode.next_sibling();
+ }
+
+ BuildPattern(pattern);
+ }
+}
+
+void ChartSymbols::ProcessSymbols(pugi::xml_node &node) {
+ ChartSymbol symbol;
+
+ for (pugi::xml_node child = node.first_child(); child != 0;
+ child = child.next_sibling()) {
+ symbol.RCID = child.attribute("RCID").as_int();
+
+ symbol.hasVector = false;
+ symbol.hasBitmap = false;
+ symbol.preferBitmap = true;
+
+ pugi::xml_node symbolNode = child.first_child();
+ while (symbolNode) {
+ const char *nodeText = symbolNode.first_child().value();
+ const char *nodeType = symbolNode.name();
+
+ if (!strcmp(nodeType, "description"))
+ symbol.description = nodeText;
+ else if (!strcmp(nodeType, "name"))
+ symbol.name = nodeText;
+ else if (!strcmp(nodeType, "definition"))
+ symbol.hasVector = !strcmp(nodeText, "V");
+ else if (!strcmp(nodeType, "color-ref"))
+ symbol.colorRef = nodeText;
+
+ else if (!strcmp(nodeType, "prefer-bitmap")) {
+ if (!strcmp(nodeText, "no"))
+ symbol.preferBitmap = false;
+ else if (!strcmp(nodeText, "false"))
+ symbol.preferBitmap = false;
+ }
+
+ else if (!strcmp(nodeType, "bitmap")) {
+ symbol.bitmapSize.size.x = symbolNode.attribute("width").as_int();
+ symbol.bitmapSize.size.y = symbolNode.attribute("height").as_int();
+ symbol.hasBitmap = true;
+
+ for (pugi::xml_node child = symbolNode.first_child(); child != 0;
+ child = child.next_sibling()) {
+ const char *nodeType = child.name();
+
+ if (!strcmp(nodeType, "distance")) {
+ symbol.bitmapSize.minDistance = child.attribute("min").as_int();
+ symbol.bitmapSize.maxDistance = child.attribute("max").as_int();
+ } else if (!strcmp(nodeType, "origin")) {
+ symbol.bitmapSize.origin.x = child.attribute("x").as_int();
+ symbol.bitmapSize.origin.y = child.attribute("y").as_int();
+ } else if (!strcmp(nodeType, "pivot")) {
+ symbol.bitmapSize.pivot.x = child.attribute("x").as_int();
+ symbol.bitmapSize.pivot.y = child.attribute("y").as_int();
+ } else if (!strcmp(nodeType, "graphics-location")) {
+ symbol.bitmapSize.graphics.x = child.attribute("x").as_int();
+ symbol.bitmapSize.graphics.y = child.attribute("y").as_int();
+ }
+ }
+ }
+
+ else if (!strcmp(nodeType, "vector")) {
+ symbol.vectorSize.size.x = symbolNode.attribute("width").as_int();
+ symbol.vectorSize.size.y = symbolNode.attribute("height").as_int();
+ symbol.hasVector = true;
+
+ for (pugi::xml_node child = symbolNode.first_child(); child != 0;
+ child = child.next_sibling()) {
+ const char *nodeType = child.name();
+
+ if (!strcmp(nodeType, "distance")) {
+ symbol.vectorSize.minDistance = child.attribute("min").as_int();
+ symbol.vectorSize.maxDistance = child.attribute("max").as_int();
+ } else if (!strcmp(nodeType, "origin")) {
+ symbol.vectorSize.origin.x = child.attribute("x").as_int();
+ symbol.vectorSize.origin.y = child.attribute("y").as_int();
+ } else if (!strcmp(nodeType, "pivot")) {
+ symbol.vectorSize.pivot.x = child.attribute("x").as_int();
+ symbol.vectorSize.pivot.y = child.attribute("y").as_int();
+ } else if (!strcmp(nodeType, "HPGL")) {
+ symbol.HPGL = wxString(child.first_child().value(), wxConvUTF8);
+ }
+ }
+ }
+
+ symbolNode = symbolNode.next_sibling();
+ }
+
+ BuildSymbol(symbol);
+ }
+}
+
+
+void ChartSymbols::BuildLookup(Lookup &lookup) {
+ auto LUP = new LUPrec();
+
+ LUP->RCID = lookup.RCID;
+ LUP->nSequence = lookup.id;
+ LUP->DISC = lookup.displayCat;
+ LUP->FTYP = lookup.type;
+ LUP->DPRI = lookup.displayPrio;
+ LUP->RPRI = lookup.radarPrio;
+ LUP->TNAM = lookup.tableName;
+ LUP->OBCL[6] = 0;
+ memcpy(LUP->OBCL, lookup.name.mb_str(), 7);
+
+ LUP->ATTArray = lookup.attributeCodeArray;
+
+ LUP->INST = lookup.instruction;
+ LUP->LUCM = lookup.comment;
+
+ // Add LUP to array
+ // Search the LUPArray to see if there is already a LUP with this RCID
+ // If found, replace it with the new LUP
+ // This provides a facility for updating the LUP tables after loading a basic
+ // set
+
+ unsigned int index = 0;
+ wxArrayOfLUPrec *pLUPARRAYtyped = plib->SelectLUPARRAY(LUP->TNAM);
+
+ while (index < pLUPARRAYtyped->GetCount()) {
+ auto pLUPCandidate = pLUPARRAYtyped->Item(index);
+ if (LUP->RCID == pLUPCandidate->RCID) {
+ pLUPARRAYtyped->RemoveAt(index);
+ plib->DestroyLUP(pLUPCandidate); // empties the LUP
+ break;
+ }
+ index++;
+ }
+ pLUPARRAYtyped->Add(LUP);
+}
+
+void ChartSymbols::BuildLineStyle(LineStyle &lineStyle) {
+ Rule *lnstmp = NULL;
+ Rule *lnst = (Rule *)calloc(1, sizeof(Rule));
+ plib->pAlloc->Add(lnst);
+
+ lnst->RCID = lineStyle.RCID;
+ memcpy(lnst->name.PANM, lineStyle.name.mb_str(), 8);
+ lnst->bitmap.PBTM = NULL;
+
+ lnst->vector.LVCT = (char *)malloc(lineStyle.HPGL.Len() + 1);
+ strcpy(lnst->vector.LVCT, lineStyle.HPGL.mb_str());
+
+ lnst->colRef.LCRF = (char *)malloc(lineStyle.colorRef.Len() + 1);
+ strcpy(lnst->colRef.LCRF, lineStyle.colorRef.mb_str());
+
+ lnst->pos.line.minDist.PAMI = lineStyle.vectorSize.minDistance;
+ lnst->pos.line.maxDist.PAMA = lineStyle.vectorSize.maxDistance;
+
+ lnst->pos.line.pivot_x.PACL = lineStyle.vectorSize.pivot.x;
+ lnst->pos.line.pivot_y.PARW = lineStyle.vectorSize.pivot.y;
+
+ lnst->pos.line.bnbox_w.PAHL = lineStyle.vectorSize.size.x;
+ lnst->pos.line.bnbox_h.PAVL = lineStyle.vectorSize.size.y;
+
+ lnst->pos.line.bnbox_x.SBXC = lineStyle.vectorSize.origin.x;
+ lnst->pos.line.bnbox_y.SBXR = lineStyle.vectorSize.origin.y;
+
+ lnstmp = (*plib->_line_sym)[lineStyle.name];
+
+ if (NULL == lnstmp)
+ (*plib->_line_sym)[lineStyle.name] = lnst;
+ else if (strncmp(lnst->name.LINM, lnstmp->name.LINM, 8))
+ (*plib->_line_sym)[lineStyle.name] = lnst;
+}
+
+
+void ChartSymbols::BuildPattern(OCPNPattern &pattern) {
+ Rule *pattmp = NULL;
+
+ Rule *patt = (Rule *)calloc(1, sizeof(Rule));
+ plib->pAlloc->Add(patt);
+
+ patt->RCID = pattern.RCID;
+ patt->exposition.PXPO = new wxString(pattern.description);
+ memcpy(patt->name.PANM, pattern.name.mb_str(), 8);
+ patt->bitmap.PBTM = NULL;
+ patt->fillType.PATP = pattern.fillType;
+ patt->spacing.PASP = pattern.spacing;
+
+ patt->vector.PVCT = (char *)malloc(pattern.HPGL.Len() + 1);
+ strcpy(patt->vector.PVCT, pattern.HPGL.mb_str());
+
+ patt->colRef.PCRF = (char *)malloc(pattern.colorRef.Len() + 1);
+ strcpy(patt->colRef.PCRF, pattern.colorRef.mb_str());
+
+ SymbolSizeInfo_t patternSize;
+
+ if (pattern.hasVector && !(pattern.preferBitmap && pattern.hasBitmap)) {
+ patt->definition.PADF = 'V';
+ patternSize = pattern.vectorSize;
+ } else {
+ patt->definition.PADF = 'R';
+ patternSize = pattern.bitmapSize;
+ }
+
+ patt->pos.patt.minDist.PAMI = patternSize.minDistance;
+ patt->pos.patt.maxDist.PAMA = patternSize.maxDistance;
+
+ patt->pos.patt.pivot_x.PACL = patternSize.pivot.x;
+ patt->pos.patt.pivot_y.PARW = patternSize.pivot.y;
+
+ patt->pos.patt.bnbox_w.PAHL = patternSize.size.x;
+ patt->pos.patt.bnbox_h.PAVL = patternSize.size.y;
+
+ patt->pos.patt.bnbox_x.SBXC = patternSize.origin.x;
+ patt->pos.patt.bnbox_y.SBXR = patternSize.origin.y;
+
+ wxRect graphicsLocation(pattern.bitmapSize.graphics, pattern.bitmapSize.size);
+ m_symbolGraphicLocations[pattern.name] = graphicsLocation;
+
+ // check if key already there
+
+ pattmp = (*plib->_patt_sym)[pattern.name];
+
+ if (NULL == pattmp) {
+ (*plib->_patt_sym)[pattern.name] = patt; // insert in hash table
+ } else // already something here with same key...
+ { // if the pattern names are not identical
+ if (strncmp(patt->name.PANM, pattmp->name.PANM, 8)) {
+ (*plib->_patt_sym)[pattern.name] = patt; // replace the pattern
+ plib->DestroyPatternRuleNode(
+ pattmp); // remember to free to replaced node
+ // the node itself is destroyed as part of pAlloc
+ }
+ }
+}
+
+
+void ChartSymbols::BuildSymbol(ChartSymbol &symbol) {
+ Rule *symb = (Rule *)calloc(1, sizeof(Rule));
+ plib->pAlloc->Add(symb);
+
+ wxString SVCT;
+ wxString SCRF;
+
+ symb->RCID = symbol.RCID;
+ memcpy(symb->name.SYNM, symbol.name.char_str(), 8);
+
+ symb->exposition.SXPO = new wxString(symbol.description);
+
+ symb->vector.SVCT = (char *)malloc(symbol.HPGL.Len() + 1);
+ strcpy(symb->vector.SVCT, symbol.HPGL.mb_str());
+
+ symb->colRef.SCRF = (char *)malloc(symbol.colorRef.Len() + 1);
+ strcpy(symb->colRef.SCRF, symbol.colorRef.mb_str());
+
+ symb->bitmap.SBTM = NULL;
+
+ SymbolSizeInfo_t symbolSize;
+
+ if (symbol.hasVector && !(symbol.preferBitmap && symbol.hasBitmap)) {
+ symb->definition.SYDF = 'V';
+ symbolSize = symbol.vectorSize;
+ } else {
+ symb->definition.SYDF = 'R';
+ symbolSize = symbol.bitmapSize;
+ }
+
+ symb->pos.symb.minDist.PAMI = symbolSize.minDistance;
+ symb->pos.symb.maxDist.PAMA = symbolSize.maxDistance;
+
+ symb->pos.symb.pivot_x.SYCL = symbolSize.pivot.x;
+ symb->pos.symb.pivot_y.SYRW = symbolSize.pivot.y;
+
+ symb->pos.symb.bnbox_w.SYHL = symbolSize.size.x;
+ symb->pos.symb.bnbox_h.SYVL = symbolSize.size.y;
+
+ symb->pos.symb.bnbox_x.SBXC = symbolSize.origin.x;
+ symb->pos.symb.bnbox_y.SBXR = symbolSize.origin.y;
+
+ wxRect graphicsLocation(symbol.bitmapSize.graphics, symbol.bitmapSize.size);
+ m_symbolGraphicLocations[symbol.name] = graphicsLocation;
+
+ // Already something here with same key? Then free its strings, otherwise they
+ // leak.
+ Rule *symbtmp = (*plib->_symb_sym)[symbol.name];
+ if (symbtmp) {
+ free(symbtmp->colRef.SCRF);
+ free(symbtmp->vector.SVCT);
+ delete symbtmp->exposition.SXPO;
+ }
+
+ (*plib->_symb_sym)[symbol.name] = symb;
+}
+
+bool ChartSymbols::LoadConfigFile(s52plib *plibArg,
+ const wxString &s52ilePath) {
+ plib = plibArg;
+
+ // Expect to find library data XML file in same folder as other S52 data.
+ // Files in CWD takes precedence.
+
+ wxString name, extension;
+ wxString xmlFileName = _T("chartsymbols.xml");
+
+ wxFileName::SplitPath(s52ilePath, &configFileDirectory, &name, &extension);
+ wxString fullFilePath =
+ configFileDirectory + wxFileName::GetPathSeparator() + xmlFileName;
+
+ if (wxFileName::FileExists(xmlFileName)) {
+ fullFilePath = xmlFileName;
+ configFileDirectory = _T(".");
+ }
+
+ if (!wxFileName::FileExists(fullFilePath)) {
+ wxString msg(_T("ChartSymbols ConfigFile not found: "));
+ msg += fullFilePath;
+ wxLogMessage(msg);
+ return false;
+ }
+
+ if (m_symbolsDoc.load_file(fullFilePath.fn_str())) {
+ wxString msg(_T("ChartSymbols loaded from "));
+ msg += fullFilePath;
+ wxLogMessage(msg);
+
+ pugi::xml_node elements = m_symbolsDoc.child("chartsymbols");
+
+ for (pugi::xml_node element = elements.first_child(); element;
+ element = element.next_sibling()) {
+ if (!strcmp(element.name(), "color-tables"))
+ ProcessColorTables(element);
+ else if (!strcmp(element.name(), "lookups"))
+ ProcessLookups(element);
+ else if (!strcmp(element.name(), "line-styles"))
+ ProcessLinestyles(element);
+ else if (!strcmp(element.name(), "patterns"))
+ ProcessPatterns(element);
+ else if (!strcmp(element.name(), "symbols"))
+ ProcessSymbols(element);
+ }
+ m_symbolsDoc.reset(); // purge the document to recover memory;
+ }
+
+
+ return true;
+}
+void ChartSymbols::SetColorTableIndex(int index, bool flush,
+ const ChartCtx& ctx) {
+ ColorTableIndex = index;
+ LoadRasterFileForColorTable(ColorTableIndex, flush, ctx);
+}
+
+int ChartSymbols::LoadRasterFileForColorTable(int tableNo, bool flush,
+ const ChartCtx& ctx) {
+ if (tableNo == rasterSymbolsLoadedColorMapNumber && !flush) {
+ if (ctx.m_use_opengl) {
+ if (rasterSymbolsTexture) return true;
+#ifdef ocpnUSE_GL
+ if (!ctx.m_texture_rectangle_format && rasterSymbols.IsOk())
+ return true;
+#endif
+ }
+ if (rasterSymbols.IsOk()) return true;
+ }
+
+ colTable *coltab = (colTable *)m_colorTables.Item(tableNo);
+
+ wxString filename = configFileDirectory + wxFileName::GetPathSeparator() +
+ coltab->rasterFileName;
+
+ wxImage rasterFileImg;
+ if (rasterFileImg.LoadFile(filename, wxBITMAP_TYPE_PNG)) {
+#ifdef ocpnUSE_GL
+ /* for opengl mode, load the symbols into a texture */
+ if (ctx.m_use_opengl && ctx.m_texture_rectangle_format) {
+ int w = rasterFileImg.GetWidth();
+ int h = rasterFileImg.GetHeight();
+
+ // Get the glRGBA format data from the wxImage
+ unsigned char *d = rasterFileImg.GetData();
+ unsigned char *a = rasterFileImg.GetAlpha();
+
+ /* combine rgb with alpha */
+ unsigned char *e = (unsigned char *)malloc(w * h * 4);
+ if (d && a) {
+ for (int y = 0; y < h; y++)
+ for (int x = 0; x < w; x++) {
+ int off = (y * w + x);
+
+ e[off * 4 + 0] = d[off * 3 + 0];
+ e[off * 4 + 1] = d[off * 3 + 1];
+ e[off * 4 + 2] = d[off * 3 + 2];
+ e[off * 4 + 3] = a[off];
+ }
+ }
+ if (!rasterSymbolsTexture) glGenTextures(1, &rasterSymbolsTexture);
+
+ glBindTexture(ctx.m_texture_rectangle_format, rasterSymbolsTexture);
+
+ /* unfortunately this texture looks terrible with compression */
+ GLuint format = GL_RGBA;
+ glTexImage2D(ctx.m_texture_rectangle_format, 0, format, w, h, 0, GL_RGBA,
+ GL_UNSIGNED_BYTE, e);
+
+ // glTexParameteri( g_texture_rectangle_format,
+ // GL_TEXTURE_MAG_FILTER, GL_NEAREST ); glTexParameteri(
+ // g_texture_rectangle_format, GL_TEXTURE_MIN_FILTER,
+ // GL_NEAREST );
+
+ glTexParameteri(ctx.m_texture_rectangle_format, GL_TEXTURE_WRAP_S,
+ GL_CLAMP_TO_EDGE);
+ glTexParameteri(ctx.m_texture_rectangle_format, GL_TEXTURE_WRAP_T,
+ GL_CLAMP_TO_EDGE);
+
+ glTexParameteri(ctx.m_texture_rectangle_format, GL_TEXTURE_MAG_FILTER,
+ GL_LINEAR); // No mipmapping
+ glTexParameteri(ctx.m_texture_rectangle_format, GL_TEXTURE_MIN_FILTER,
+ GL_LINEAR);
+
+ rasterSymbolsTextureSize = wxSize(w, h);
+
+ glDisable(GL_TEXTURE_2D);
+
+ free(e);
+ }
+#endif
+ { rasterSymbols = wxBitmap(rasterFileImg, -1 /*32*/); }
+
+ rasterSymbolsLoadedColorMapNumber = tableNo;
+ return true;
+ }
+
+ wxString msg(_T("ChartSymbols...Failed to load raster symbols file "));
+ msg += filename;
+ wxLogMessage(msg);
+ return false;
+}
+
+
+S52color *ChartSymbols::GetColor(const char *colorName, int fromTable) {
+ colTable *colortable;
+ wxString key(colorName, wxConvUTF8, 5);
+ colortable = (colTable *)m_colorTables.Item(fromTable);
+ return &(colortable->colors[key]);
+}
+
+wxColor ChartSymbols::GetwxColor(const wxString &colorName, int fromTable) {
+ colTable *colortable;
+ colortable = (colTable *)m_colorTables.Item(fromTable);
+ wxColor c = colortable->wxColors[colorName];
+ return c;
+}
+
+wxColor ChartSymbols::GetwxColor(const char *colorName, int fromTable) {
+ wxString key(colorName, wxConvUTF8, 5);
+ return GetwxColor(key, fromTable);
+}
+
+int ChartSymbols::FindColorTable(const wxString &tableName) {
+ for (unsigned int i = 0; i < m_colorTables.GetCount(); i++) {
+ colTable *ct = (colTable *)m_colorTables.Item(i);
+ if (tableName.IsSameAs(*ct->tableName)) {
+ return i;
+ }
+ }
+ return 0;
+}
+
+bool ChartSymbols::UpdateTableColor( std::string table_name, std::string color_name, wxColor& new_color){
+ int table_index = FindColorTable( wxString(table_name.c_str()));
+
+ colTable *colortable = (colTable *)m_colorTables.Item(table_index);
+ wxString key(color_name.c_str(), wxConvUTF8, 5);
+
+ colortable->wxColors[key] = new_color;
+ if (colortable->colors.find(key) != colortable->colors.end()) {
+ S52color *color = &colortable->colors[key];
+ color->R = new_color.Red();
+ color->G = new_color.Green();
+ color->B = new_color.Blue();
+ return true;
+ }
+ else
+ return false;
+}
+
+
+wxString ChartSymbols::HashKey(const char *symbolName) {
+ char key[9];
+ key[8] = 0;
+ strncpy(key, symbolName, 8);
+ return wxString(key, wxConvUTF8);
+}
+
+wxImage ChartSymbols::GetImage(const char *symbolName) {
+ wxRect bmArea = m_symbolGraphicLocations[HashKey(symbolName)];
+ if (rasterSymbols.IsOk()) {
+ wxBitmap bitmap = rasterSymbols.GetSubBitmap(bmArea);
+ return bitmap.ConvertToImage();
+ } else
+ return wxImage(1, 1);
+}
+
+unsigned int ChartSymbols::GetGLTextureRect(wxRect &rect,
+ const char *symbolName) {
+ rect = m_symbolGraphicLocations[HashKey(symbolName)];
+ return rasterSymbolsTexture;
+}
+
+wxSize ChartSymbols::GLTextureSize() { return rasterSymbolsTextureSize; }
diff --git a/docs/外部参考/OpenCPN源码基线_f7211b4e/上游源码/libs/s52plib/src/chartsymbols.h b/docs/外部参考/OpenCPN源码基线_f7211b4e/上游源码/libs/s52plib/src/chartsymbols.h
new file mode 100755
index 0000000..f626106
--- /dev/null
+++ b/docs/外部参考/OpenCPN源码基线_f7211b4e/上游源码/libs/s52plib/src/chartsymbols.h
@@ -0,0 +1,204 @@
+/***************************************************************************
+ *
+ * Project: OpenCPN
+ * Purpose: Chart Symbols
+ * Author: Jesper Weissglas
+ *
+ ***************************************************************************
+ * Copyright (C) 2010 by David S. Register *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
+ **************************************************************************/
+
+
+#ifndef _CHARTSYMBOLS_H_
+#define _CHARTSYMBOLS_H_
+
+#ifdef ocpnUSE_GL
+#ifdef __OCPN_USE_GLEW__
+ #ifndef __OCPN__ANDROID__
+ #if defined(_WIN32)
+ #include "glew.h"
+ #elif defined(__WXQT__) || defined(__WXGTK__)
+ #include
+ #endif
+ #endif
+#endif
+#endif
+
+#if defined(__OCPN__ANDROID__)
+ //#include
+ #include
+ #include // this is a cut-down version of gl.h
+ #include
+#elif defined(_WIN32)
+ #define GL_GLEXT_PROTOTYPES
+ #include
+ #include
+ //typedef void (__stdcall * _GLUfuncptr)(void);
+#elif defined(__WXOSX__)
+ #include
+ #include
+ typedef void (* _GLUfuncptr)();
+ #define GL_COMPRESSED_RGB_FXT1_3DFX 0x86B0
+#elif defined(__WXQT__) || defined(__WXGTK__)
+ #define GL_GLEXT_PROTOTYPES
+ #include
+ #include
+ #include
+#endif
+
+
+
+#include "s52s57.h"
+#include "pugixml.hpp"
+class s52plib;
+
+WX_DECLARE_STRING_HASH_MAP(wxRect, symbolGraphicsHashMap);
+
+class Lookup {
+public:
+ int RCID;
+ int id;
+ wxString name;
+ Object_t type; // 'A' Area, 'L' Line, 'P' Point
+ DisPrio displayPrio; // Display Priority
+ RadPrio radarPrio; // 'O' or 'S', Radar Priority
+ LUPname tableName; // FTYP: areas, points, lines
+ std::vector attributeCodeArray; // ArrayString of LUP Attributes
+ wxString instruction; // Instruction Field (rules)
+ DisCat displayCat; // Display Categorie: D/S/O, DisplayBase, Standard, Other
+ int comment; // Look-Up Comment (PLib3.x put 'groupes' here,
+};
+
+typedef struct _SymbolSizeInfo {
+ wxSize size;
+ wxPoint origin;
+ wxPoint pivot;
+ wxPoint graphics;
+ int minDistance;
+ int maxDistance;
+} SymbolSizeInfo_t;
+
+class OCPNPattern {
+public:
+ int RCID;
+ wxString name;
+ wxString description;
+ wxString colorRef;
+ bool hasVector;
+ bool hasBitmap;
+ bool preferBitmap;
+ char fillType;
+ char spacing;
+ SymbolSizeInfo_t bitmapSize;
+ SymbolSizeInfo_t vectorSize;
+ wxBitmap bitmap;
+ wxString HPGL;
+};
+
+class LineStyle {
+public:
+ int RCID;
+ wxString name;
+ wxString description;
+ wxString colorRef;
+ SymbolSizeInfo_t vectorSize;
+ wxBitmap bitmap;
+ wxString HPGL;
+};
+
+/** Generic method argument wrapping differences using opengl or not */
+struct ChartCtx {
+#ifdef ocpnUSE_GL
+ const bool m_use_opengl;
+ const GLenum m_texture_rectangle_format;
+ ChartCtx(bool use_opengl, GLenum rect_format)
+ : m_use_opengl(use_opengl), m_texture_rectangle_format(rect_format) {}
+#else
+ const bool m_use_opengl;
+ ChartCtx(bool use_opengl) : m_use_opengl(use_opengl) {}
+#endif
+};
+
+
+
+class ChartSymbol {
+public:
+ wxString name;
+ int RCID;
+ bool hasVector;
+ bool hasBitmap;
+ bool preferBitmap;
+ wxString description;
+ wxString colorRef;
+ SymbolSizeInfo_t bitmapSize;
+ SymbolSizeInfo_t vectorSize;
+ wxString HPGL;
+};
+
+class ChartSymbols {
+public:
+ ChartSymbols(void);
+ ~ChartSymbols(void);
+ bool LoadConfigFile(s52plib *plibArg, const wxString &path);
+
+ void InitializeTables(void);
+ void DeleteGlobals(void);
+ int FindColorTable(const wxString &tableName);
+ S52color *GetColor(const char *colorName, int fromTable);
+ wxColor GetwxColor(const wxString &colorName, int fromTable);
+ wxColor GetwxColor(const char *colorName, int fromTable);
+ wxString HashKey(const char *symbolName);
+ wxImage GetImage(const char *symbolName);
+ unsigned int GetGLTextureRect(wxRect &rect, const char *symbolName);
+ wxSize GLTextureSize();
+ void SetTextureFormat( int format){ m_texture_rectangle_format = format; }
+ int LoadRasterFileForColorTable(int table_nr, bool flush,
+ const ChartCtx& ctx);
+ void SetColorTableIndex(int index, bool flush, const ChartCtx& ctx);
+
+ wxArrayPtrVoid m_colorTables;
+ unsigned int rasterSymbolsTexture;
+ wxSize rasterSymbolsTextureSize;
+ wxBitmap rasterSymbols;
+ int rasterSymbolsLoadedColorMapNumber;
+ wxString configFileDirectory;
+ int ColorTableIndex;
+ symbolGraphicsHashMap m_symbolGraphicLocations;
+ bool UpdateTableColor( std::string table_name, std::string color_name, wxColor& new_color);
+
+private:
+ void BuildLineStyle(LineStyle &lineStyle);
+ void BuildLookup(Lookup &lookup);
+ void BuildPattern(OCPNPattern &pattern);
+ void BuildSymbol(ChartSymbol &symol);
+
+ void ProcessColorTables(pugi::xml_node &node);
+ void ProcessLookups(pugi::xml_node &node);
+ void ProcessLinestyles(pugi::xml_node &node);
+ void ProcessPatterns(pugi::xml_node &node);
+ void ProcessSymbols(pugi::xml_node &node);
+ void ProcessVectorTag(pugi::xml_node &vectorNode,
+ SymbolSizeInfo_t &vectorSize);
+
+ pugi::xml_document m_symbolsDoc;
+ GLenum m_texture_rectangle_format;
+
+ s52plib *plib;
+};
+
+#endif
diff --git a/docs/外部参考/OpenCPN源码基线_f7211b4e/上游源码/libs/s52plib/src/color_types.h b/docs/外部参考/OpenCPN源码基线_f7211b4e/上游源码/libs/s52plib/src/color_types.h
new file mode 100644
index 0000000..2f307bd
--- /dev/null
+++ b/docs/外部参考/OpenCPN源码基线_f7211b4e/上游源码/libs/s52plib/src/color_types.h
@@ -0,0 +1,58 @@
+/******************************************************************************
+ *
+ * Project: OpenCP
+ * Purpose: Color Types
+ * Author: David Register
+ *
+ ***************************************************************************
+ * Copyright (C) 2022 by David S. Register *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
+ ***************************************************************************
+ *
+ */
+
+#ifndef _COLORTYPES_H_
+#define _COLORTYPES_H_
+
+typedef struct _S52color {
+ char colName[20];
+ unsigned char R;
+ unsigned char G;
+ unsigned char B;
+} S52color;
+
+WX_DECLARE_STRING_HASH_MAP(S52color, colorHashMap);
+WX_DECLARE_STRING_HASH_MAP(wxColour, wxColorHashMap);
+
+typedef enum ColorScheme {
+ GLOBAL_COLOR_SCHEME_RGB,
+ GLOBAL_COLOR_SCHEME_DAY,
+ GLOBAL_COLOR_SCHEME_DUSK,
+ GLOBAL_COLOR_SCHEME_NIGHT,
+ N_COLOR_SCHEMES
+} _ColorScheme;
+
+typedef struct _colTable {
+ wxString *tableName;
+ wxString rasterFileName;
+ wxArrayPtrVoid *color;
+ colorHashMap colors;
+ wxColorHashMap wxColors;
+} colTable;
+
+
+#endif
diff --git a/docs/外部参考/OpenCPN源码基线_f7211b4e/上游源码/libs/s52plib/src/mygeom.cpp b/docs/外部参考/OpenCPN源码基线_f7211b4e/上游源码/libs/s52plib/src/mygeom.cpp
new file mode 100644
index 0000000..47f7cc0
--- /dev/null
+++ b/docs/外部参考/OpenCPN源码基线_f7211b4e/上游源码/libs/s52plib/src/mygeom.cpp
@@ -0,0 +1,1514 @@
+/******************************************************************************
+ *
+ * Project: OpenCPN
+ * Purpose: Tesselated Polygon Object
+ * Author: David Register
+ *
+ ***************************************************************************
+ * Copyright (C) 2010 by David S. Register *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
+ ***************************************************************************
+ *
+ *
+ */
+// For compilers that support precompilation, includes "wx.h".
+#include "wx/wxprec.h"
+
+#ifndef WX_PRECOMP
+#include "wx/wx.h"
+#endif // precompiled headers
+
+#if defined(__OCPN__ANDROID__)
+ #include
+ #include // this is a cut-down version of gl.h
+ #include
+ #define GLU_TESS_MAX_COORD 1.0e150
+#elif defined(__MSVC__)
+ #include "glew.h"
+#elif defined(__WXOSX__)
+ #include
+ #include
+ typedef void (* _GLUfuncptr)();
+ #define GL_COMPRESSED_RGB_FXT1_3DFX 0x86B0
+#elif defined(__WXQT__) || defined(__WXGTK__)
+ #include
+ #include
+#endif
+
+#include "wx/tokenzr.h"
+#include
+
+#include "s52plib.h"
+#include "gdal/ogr_geometry.h"
+
+#include "vector2D.h"
+
+#include "s52s57.h"
+#include "s52plib.h"
+
+#include "mygeom.h"
+#include "LOD_reduce.h"
+
+#include
+#include
+#include
+#include
+
+#include "tesselator.h"
+#include "Striper.h"
+
+#ifdef __WXMSW__
+#include
+#endif
+
+static constexpr double kMercatorK0 = 0.9996;
+static constexpr double kRadiansPerDegree = M_PI / 180.0;
+
+static const double CM93_semimajor_axis_meters =
+ 6378388.0; // CM93 semimajor axis
+
+// Module Internal Prototypes
+
+#if defined(__UNIX__) && \
+ !defined(__WXOSX__) // high resolution stopwatch for profiling
+class OCPNStopWatchTess {
+public:
+ OCPNStopWatchTess() { Reset(); }
+ void Reset() { clock_gettime(CLOCK_REALTIME, &tp); }
+
+ double GetTime() {
+ timespec tp_end;
+ clock_gettime(CLOCK_REALTIME, &tp_end);
+ return (tp_end.tv_sec - tp.tv_sec) * 1.e3 +
+ (tp_end.tv_nsec - tp.tv_nsec) / 1.e6;
+ }
+
+private:
+ timespec tp;
+};
+#endif
+
+//-----------------------------------------------------------
+//
+// Static memory allocators for libtess2
+//
+//-----------------------------------------------------------
+//#define USE_POOL 1
+
+void *stdAlloc(void *userData, unsigned int size) {
+ int *allocated = (int *)userData;
+ TESS_NOTUSED(userData);
+ *allocated += (int)size;
+ return malloc(size);
+}
+
+void stdFree(void *userData, void *ptr) {
+ TESS_NOTUSED(userData);
+ free(ptr);
+}
+
+struct MemPool {
+ unsigned char *buf;
+ unsigned int cap;
+ unsigned int size;
+};
+
+void *poolAlloc(void *userData, unsigned int size) {
+ struct MemPool *pool = (struct MemPool *)userData;
+ size = (size + 0x7) & ~0x7;
+ if (pool->size + size < pool->cap) {
+ unsigned char *ptr = pool->buf + pool->size;
+ pool->size += size;
+ return ptr;
+ }
+ printf("out of mem: %d < %d!\n", pool->size + size, pool->cap);
+ return 0;
+}
+
+void poolFree(void *userData, void *ptr) {
+ // empty
+ TESS_NOTUSED(userData);
+ TESS_NOTUSED(ptr);
+}
+
+/**
+ * Returns TRUE if the ring has clockwise winding.
+ *
+ * @return TRUE if clockwise otherwise FALSE.
+ */
+
+bool isRingClockwise(wxPoint2DDouble *pp, int nPointCount)
+
+{
+ double dfSum = 0.0;
+
+ for (int iVert = 0; iVert < nPointCount - 1; iVert++) {
+ dfSum +=
+ pp[iVert].m_x * pp[iVert + 1].m_y - pp[iVert].m_y * pp[iVert + 1].m_x;
+ }
+
+ dfSum +=
+ pp[nPointCount - 1].m_x * pp[0].m_y - pp[nPointCount - 1].m_y * pp[0].m_x;
+
+ return dfSum < 0.0;
+}
+
+//------------------------------------------------------------------------------
+// Extended_Geometry Implementation
+//------------------------------------------------------------------------------
+Extended_Geometry::Extended_Geometry() {
+ vertex_array = NULL;
+ contour_array = NULL;
+}
+
+Extended_Geometry::~Extended_Geometry() {
+ free(vertex_array);
+ free(contour_array);
+}
+
+//------------------------------------------------------------------------------
+// PolyTessGeo Implementation
+//------------------------------------------------------------------------------
+PolyTessGeo::PolyTessGeo() {
+ m_pxgeom = NULL;
+ m_printStats = false;
+ m_bstripify = false;
+ m_LOD_meters = 0;
+}
+
+// Build PolyTessGeo Object from Extended_Geometry
+PolyTessGeo::PolyTessGeo(Extended_Geometry *pxGeom) {
+ m_ppg_head = NULL;
+ m_bOK = false;
+
+ m_pxgeom = pxGeom;
+ m_printStats = false;
+ m_bstripify = false;
+ m_LOD_meters = 0;
+}
+
+// Build PolyTessGeo Object from OGR Polygon
+PolyTessGeo::PolyTessGeo(OGRPolygon *poly, bool bSENC_SM, double ref_lat,
+ double ref_lon, double LOD_meters) {
+ m_printStats = false;
+
+ ErrorCode = 0;
+ m_ppg_head = NULL;
+ m_pxgeom = NULL;
+ m_tess_orient = TESS_HORZ;
+
+ m_ref_lat = ref_lat;
+ m_ref_lon = ref_lon;
+ m_LOD_meters = LOD_meters;
+ m_b_senc_sm = bSENC_SM;
+ m_bmerc_transform = false;
+
+ // PolyGeo BBox as lat/lon
+ OGREnvelope Envelope;
+ poly->getEnvelope(&Envelope);
+ xmin = Envelope.MinX;
+ ymin = Envelope.MinY;
+ xmax = Envelope.MaxX;
+ ymax = Envelope.MaxY;
+
+ m_feature_ref_lat = ymin + (ymax - ymin) / 2;
+ m_feature_ref_lon = xmin + (xmax - xmin) / 2;
+
+ toSM_plib(m_feature_ref_lat, m_feature_ref_lon, ref_lat, ref_lon,
+ &m_feature_easting, &m_feature_northing);
+
+ // Build the array of contour point counts
+ // Get total number of contours
+ m_ncnt = poly->getNumInteriorRings() + 1;
+
+ // build the contour point countarray
+ m_cntr = (int *)malloc(m_ncnt * sizeof(int));
+
+ m_cntr[0] = poly->getExteriorRing()->getNumPoints();
+
+ for (int i = 1; i < m_ncnt; i++) {
+ m_cntr[i] = poly->getInteriorRing(i - 1)->getNumPoints();
+ }
+
+ // Build the point array index table
+
+ OGRPoint p;
+ m_vertexPtrArray = (double **)malloc(m_ncnt * sizeof(double *));
+
+ m_vertexPtrArray[0] = (double *)malloc(m_cntr[0] * sizeof(double) * 2);
+ double *pp = m_vertexPtrArray[0];
+ for (int i = 0; i < m_cntr[0]; i++) {
+ poly->getExteriorRing()->getPoint(i, &p);
+
+ // Calculate SM from feature reference point
+ double easting, northing;
+ toSM_plib(p.getY(), p.getX(), m_feature_ref_lat, m_feature_ref_lon, &easting,
+ &northing);
+ *pp++ = easting; // x
+ *pp++ = northing; // y
+ }
+
+ for (int i = 1; i < m_ncnt; i++) {
+ m_vertexPtrArray[i] = (double *)malloc(m_cntr[i] * sizeof(double) * 2);
+ double *pp = m_vertexPtrArray[i];
+ for (int j = 0; j < m_cntr[i]; j++) {
+ poly->getInteriorRing(i - 1)->getPoint(j, &p);
+
+ double easting, northing;
+ toSM_plib(p.getY(), p.getX(), m_feature_ref_lat, m_feature_ref_lon, &easting,
+ &northing);
+ *pp++ = easting; // x
+ *pp++ = northing; // y
+ }
+ }
+
+ mx_rate = 1.0;
+ mx_offset = 0.0;
+ my_rate = 1.0;
+ my_offset = 0.0;
+
+ m_bstripify = true;
+
+ earthAxis = CM93_semimajor_axis_meters * kMercatorK0;
+
+ m_bcm93 = false;
+
+ BuildTessGLU();
+
+ // Free the working memory
+ for (int i = 0; i < m_ncnt; i++) free(m_vertexPtrArray[i]);
+
+ free(m_vertexPtrArray);
+ m_vertexPtrArray = nullptr;
+}
+
+void PolyTessGeo::SetExtents(double x_left, double y_bot, double x_right,
+ double y_top) {
+ xmin = x_left;
+ ymin = y_bot;
+ xmax = x_right;
+ ymax = y_top;
+}
+
+PolyTessGeo::~PolyTessGeo() {
+ delete m_ppg_head;
+ delete m_pxgeom;
+}
+
+int PolyTessGeo::BuildDeferredTess(void) {
+ if (m_pxgeom) {
+ // For cm93
+ m_b_senc_sm = false;
+ m_bmerc_transform = true;
+ m_tess_orient = TESS_HORZ; // prefer horizontal tristrips
+
+ // Get total number of contours
+ m_ncnt = m_pxgeom->n_contours;
+
+ // build the contour point countarray
+ m_cntr = (int *)malloc(m_ncnt * sizeof(int));
+
+ for (int i = 0; i < m_ncnt; i++) {
+ m_cntr[i] = m_pxgeom->contour_array[i];
+ }
+
+ // Build the point array index table
+
+ m_vertexPtrArray = (double **)malloc(m_ncnt * sizeof(double *));
+
+ double *vertex_pointer = (double *)m_pxgeom->vertex_array;
+ vertex_pointer += 2; // skip 0
+
+ for (int k = 0; k < m_ncnt; k++) {
+ m_vertexPtrArray[k] = vertex_pointer;
+ vertex_pointer += 2 * m_cntr[k];
+ }
+
+ mx_rate = m_pxgeom->x_rate;
+ mx_offset = m_pxgeom->x_offset;
+ my_rate = m_pxgeom->y_rate;
+ my_offset = m_pxgeom->y_offset;
+
+ m_feature_easting = 0;
+ m_feature_northing = 0;
+
+ m_ref_lat = m_pxgeom->ref_lat;
+ m_ref_lon = m_pxgeom->ref_lon;
+
+ // Note Bene... cm93 assumes no flattening constant
+ earthAxis = CM93_semimajor_axis_meters;
+
+ m_bcm93 = true;
+
+ // For cm93, we prefer TessGLU, since it produces more TriangleStrips, so
+ // renders faster with less storage.
+ int rv = BuildTessGLU();
+
+ // All done with this geometry
+ delete m_pxgeom;
+ m_pxgeom = NULL;
+
+ // Free the working memory
+ free(m_vertexPtrArray);
+ m_vertexPtrArray = nullptr;
+
+ return rv;
+
+ } else
+ return 0;
+}
+
+void beginCallback(GLenum which, void *polyData);
+void errorCallback(GLenum errorCode, void *polyData);
+void endCallback(void *polyData);
+void vertexCallback(GLvoid *vertex, void *polyData);
+void combineCallback(GLdouble coords[3], GLdouble *vertex_data[4],
+ GLfloat weight[4], GLdouble **dataOut, void *polyData);
+
+// Build PolyTessGeo Object from OGR Polygon
+// Using OpenGL/GLU tesselator
+int PolyTessGeo::BuildTessGLU() {
+ unsigned int iir, ip;
+ GLdouble *geoPt;
+
+#if 0
+ // Make a quick sanity check of the polygon coherence
+ bool b_ok = true;
+ OGRLineString *tls = poly->getExteriorRing();
+ if(!tls) {
+ b_ok = false;
+ }
+ else {
+ int tnpta = poly->getExteriorRing()->getNumPoints();
+ if(tnpta < 3 )
+ b_ok = false;
+ }
+
+
+ for( iir=0 ; iir < poly->getNumInteriorRings() ; iir++)
+ {
+ int tnptr = poly->getInteriorRing(iir)->getNumPoints();
+ if( tnptr < 3 )
+ b_ok = false;
+ }
+
+ if( !b_ok )
+ return ERROR_BAD_OGRPOLY;
+
+#endif
+
+ // Allocate a work buffer, which will be grown as needed
+#define NINIT_BUFFER_LEN 10000
+ m_pwork_buf = (GLdouble *)malloc(NINIT_BUFFER_LEN * 2 * sizeof(GLdouble));
+ m_buf_len = NINIT_BUFFER_LEN * 2;
+ m_buf_idx = 0;
+
+ // Create an array to hold pointers to allocated vertices created by
+ // "combine" callback, so that they may be deleted after tesselation.
+ m_pCombineVertexArray = new wxArrayPtrVoid;
+
+ // Create tesselator
+ GLUtessobj = gluNewTess();
+
+ // Register the callbacks
+ gluTessCallback(GLUtessobj, GLU_TESS_BEGIN_DATA,
+ (GLvoid(*)()) & beginCallback);
+ gluTessCallback(GLUtessobj, GLU_TESS_BEGIN_DATA,
+ (GLvoid(*)()) & beginCallback);
+ gluTessCallback(GLUtessobj, GLU_TESS_VERTEX_DATA,
+ (GLvoid(*)()) & vertexCallback);
+ gluTessCallback(GLUtessobj, GLU_TESS_END_DATA, (GLvoid(*)()) & endCallback);
+ gluTessCallback(GLUtessobj, GLU_TESS_COMBINE_DATA,
+ (GLvoid(*)()) & combineCallback);
+ gluTessCallback(GLUtessobj, GLU_TESS_ERROR_DATA,
+ (GLvoid(*)()) & errorCallback);
+
+ gluTessProperty(GLUtessobj, GLU_TESS_WINDING_RULE, GLU_TESS_WINDING_POSITIVE);
+ gluTessNormal(GLUtessobj, 0, 0, 1);
+
+ // Get total number of points(vertices) to build a buffer
+ int npta = 0;
+
+ for (int i = 0; i < m_ncnt; i++) npta += m_cntr[i];
+
+ geoPt =
+ (GLdouble *)malloc((npta + 6) * 3 * sizeof(GLdouble)); // vertex array
+
+ // Grow the work buffer if necessary
+
+ if ((npta * 4) > m_buf_len) {
+ m_pwork_buf = (GLdouble *)realloc(m_pwork_buf, npta * 4 * sizeof(GLdouble));
+ m_buf_len = npta * 4;
+ }
+
+ // Define the polygon
+ gluTessBeginPolygon(GLUtessobj, this);
+
+ // Check and account for winding direction of ring
+
+ double x0, y0, x, y;
+ // OGRPoint p;
+
+ wxPoint2DDouble *pp = (wxPoint2DDouble *)m_vertexPtrArray[0];
+ bool cw = !isRingClockwise(pp, m_cntr[0]);
+
+ // pp++; // skip 0?
+
+ if (cw) {
+ x0 = pp->m_x;
+ y0 = pp->m_y;
+ } else {
+ x0 = pp[m_cntr[0] - 1].m_x;
+ y0 = pp[m_cntr[0] - 1].m_y;
+ }
+
+ unsigned int ptValid = m_cntr[0];
+
+ // Transcribe points to vertex array, in proper order with no duplicates
+ // also, accounting for tess_orient
+ GLdouble *ppt = geoPt;
+
+ for (ip = 0; ip < (unsigned int)m_cntr[0]; ip++) {
+ int pidx;
+ if (cw)
+ pidx = m_cntr[0] - ip - 1;
+
+ else
+ pidx = ip;
+
+ x = pp[pidx].m_x;
+ y = pp[pidx].m_y;
+
+ if ((fabs(x - x0) > EQUAL_EPS) || (fabs(y - y0) > EQUAL_EPS)) {
+ if (m_tess_orient == TESS_VERT) {
+ *ppt++ = x;
+ *ppt++ = y;
+ *ppt++ = 0;
+ } else {
+ *ppt++ = y;
+ *ppt++ = x;
+ *ppt++ = 0;
+ }
+ } else
+ ptValid--;
+
+ x0 = x;
+ y0 = y;
+ }
+
+ // Apply LOD reduction
+
+ if (ptValid > 20 && (m_LOD_meters > .01)) {
+ std::vector bool_keep(ptValid, false);
+
+ // Keep a few key points
+ bool_keep[0] = true;
+ bool_keep[1] = true;
+ bool_keep[ptValid - 1] = true;
+ bool_keep[ptValid - 2] = true;
+
+ DouglasPeuckerDI(geoPt, 1, ptValid - 2, m_LOD_meters, bool_keep);
+
+ // Create a new buffer
+ double *LOD_result = (double *)malloc((m_cntr[0]) * 3 * sizeof(double));
+ double *plod = LOD_result;
+ int kept_LOD = 0;
+
+ for (unsigned int i = 0; i < ptValid; i++) {
+ if (bool_keep[i]) {
+ double x = geoPt[i * 3];
+ double y = geoPt[(i * 3) + 1];
+ *plod++ = x;
+ *plod++ = y;
+ *plod++ = 0;
+ kept_LOD++;
+ }
+ }
+
+ // Copy the lod points back into the vertex buffer
+ memcpy(geoPt, LOD_result, kept_LOD * 3 * sizeof(double));
+
+ free(LOD_result);
+ ptValid = kept_LOD;
+ }
+
+ // Declare the gluContour and copy the points
+ gluTessBeginContour(GLUtessobj);
+
+ double *DPrun = geoPt;
+ for (ip = 0; ip < ptValid; ip++) {
+ for( int i = 0; i < 3; ++i ) {
+ x = DPrun[i];
+ if (x < -GLU_TESS_MAX_COORD) {
+ m_bOK = false;
+ ErrorCode = 1;
+ return 1;
+ }
+ if (x > GLU_TESS_MAX_COORD) {
+ m_bOK = false;
+ ErrorCode = 2;
+ return 1;
+ }
+ }
+ gluTessVertex(GLUtessobj, DPrun, DPrun);
+ DPrun += 3;
+ }
+
+ gluTessEndContour(GLUtessobj);
+
+ // Now the interior contours
+#if 1
+ int gpIndex = m_cntr[0];
+
+ for (iir = 0; iir < (unsigned int)m_ncnt - 1; iir++) {
+ wxPoint2DDouble *pp = (wxPoint2DDouble *)m_vertexPtrArray[iir + 1];
+
+ int npti = m_cntr[iir + 1];
+ ptValid = npti;
+
+ double *ppt = &geoPt[gpIndex * 3]; // next available location in geoPT
+ double *DPStart = ppt;
+
+ // Check and account for winding direction of ring
+ bool cw = isRingClockwise(pp, m_cntr[iir + 1]);
+
+ if (cw) {
+ x0 = pp[0].m_x;
+ y0 = pp[0].m_y;
+ } else {
+ x0 = pp[npti - 1].m_x;
+ y0 = pp[npti - 1].m_y;
+ }
+
+ // Transcribe points to vertex array, in proper order with no duplicates
+ // also, accounting for tess_orient
+ for (int ip = 0; ip < npti; ip++) {
+ int pidx;
+ if (cw) // interior contours must be cw
+ pidx = npti - ip - 1;
+ else
+ pidx = ip;
+
+ x = pp[pidx].m_x;
+ y = pp[pidx].m_y;
+
+ if ((fabs(x - x0) > EQUAL_EPS) || (fabs(y - y0) > EQUAL_EPS)) {
+ if (m_tess_orient == TESS_VERT) {
+ *ppt++ = x;
+ *ppt++ = y;
+ *ppt++ = 0;
+ } else {
+ *ppt++ = y;
+ *ppt++ = x;
+ *ppt++ = 0;
+ }
+ } else
+ ptValid--;
+
+ x0 = x;
+ y0 = y;
+ }
+
+ // Declare the gluContour and reference the points
+ gluTessBeginContour(GLUtessobj);
+
+ double *DPruni = DPStart;
+ for (ip = 0; ip < ptValid; ip++) {
+ gluTessVertex(GLUtessobj, DPruni, DPruni);
+ DPruni += 3;
+ }
+
+ gluTessEndContour(GLUtessobj);
+
+ gpIndex += m_cntr[iir + 1];
+ }
+#endif
+
+ m_pTPG_Last = NULL;
+ m_pTPG_Head = NULL;
+ m_nvmax = 0;
+
+ // Ready to kick off the tesselator
+ gluTessEndPolygon(GLUtessobj); // here it goes
+
+ m_nvertex_max = m_nvmax; // record largest vertex count, updates in callback
+
+ // Tesselation all done, so...
+
+ // Create the data structures
+
+ m_ppg_head = new PolyTriGroup;
+ m_ppg_head->m_bSMSENC = m_b_senc_sm;
+
+ m_ppg_head->nContours = m_ncnt;
+
+ m_ppg_head->pn_vertex = m_cntr; // pointer to array of poly vertex counts
+ m_ppg_head->data_type = DATA_TYPE_DOUBLE;
+
+ // Transcribe the raw geometry buffer
+ // Converting to float as we go, and
+ // allowing for tess_orient
+ // Also, convert to SM if requested
+
+ // Recalculate the size of the geometry buffer
+ unsigned int nptfinal = m_cntr[0] + 2;
+ // for(int i=0 ; i < nint ; i++)
+ // nptfinal += cntr[i+1] + 2;
+
+ // No longer need the full geometry in the SENC,
+ nptfinal = 1;
+
+ m_nwkb = (nptfinal + 1) * 2 * sizeof(float);
+ m_ppg_head->pgroup_geom = (float *)calloc(sizeof(float), (nptfinal + 1) * 2);
+ float *vro = m_ppg_head->pgroup_geom;
+ ppt = geoPt;
+ float tx, ty;
+
+ for (ip = 0; ip < nptfinal; ip++) {
+ if (TESS_HORZ == m_tess_orient) {
+ ty = *ppt++;
+ tx = *ppt++;
+ } else {
+ tx = *ppt++;
+ ty = *ppt++;
+ }
+
+ if (m_b_senc_sm) {
+ // Calculate SM from chart common reference point
+ double easting, northing;
+ toSM_plib(ty, tx, m_ref_lat, m_ref_lon, &easting, &northing);
+ *vro++ = easting; // x
+ *vro++ = northing; // y
+ } else {
+ *vro++ = tx; // lon
+ *vro++ = ty; // lat
+ }
+
+ ppt++; // skip z
+ }
+
+ m_ppg_head->tri_prim_head = m_pTPG_Head; // head of linked list of TriPrims
+
+ // Convert the Triangle vertex arrays into a single memory allocation of
+ // floats to reduce SENC size and enable efficient access later
+
+ // First calculate the total byte size
+ int total_byte_size = 2 * sizeof(float);
+ TriPrim *p_tp = m_ppg_head->tri_prim_head;
+ while (p_tp) {
+ total_byte_size += p_tp->nVert * 2 * sizeof(float);
+ p_tp = p_tp->p_next; // pick up the next in chain
+ }
+
+ float *vbuf = (float *)malloc(total_byte_size);
+ p_tp = m_ppg_head->tri_prim_head;
+ float *p_run = vbuf;
+ while (p_tp) {
+ float *pfbuf = p_run;
+ GLdouble *pdouble_buf = (GLdouble *)p_tp->p_vertex;
+
+ for (int i = 0; i < p_tp->nVert * 2; ++i) {
+ float x = (float)(*((GLdouble *)pdouble_buf));
+ pdouble_buf++;
+ *p_run++ = x;
+ }
+
+ free(p_tp->p_vertex);
+ p_tp->p_vertex = (double *)pfbuf;
+ p_tp = p_tp->p_next; // pick up the next in chain
+ }
+ m_ppg_head->bsingle_alloc = true;
+ m_ppg_head->single_buffer = (unsigned char *)vbuf;
+ m_ppg_head->single_buffer_size = total_byte_size;
+ m_ppg_head->data_type = DATA_TYPE_FLOAT;
+
+ gluDeleteTess(GLUtessobj);
+
+ free(m_pwork_buf);
+ m_pwork_buf = NULL;
+
+ free(geoPt);
+
+ // Free up any "Combine" vertices created
+ for (unsigned int i = 0; i < m_pCombineVertexArray->GetCount(); i++)
+ free(m_pCombineVertexArray->Item(i));
+ delete m_pCombineVertexArray;
+
+ m_bOK = true;
+
+ return 0;
+}
+
+int PolyTessGeo::BuildTess(void) {
+ // Setup the tesselator
+
+ TESSalloc ma;
+ TESStesselator *tess = 0;
+ const int nvp = 3;
+// unsigned char* vflags = 0;
+#ifdef USE_POOL
+ struct MemPool pool;
+ unsigned char mem[4024 * 1024];
+// int nvflags = 0;
+#else
+ int allocated = 0;
+#endif
+
+#ifdef USE_POOL
+
+ pool.size = 0;
+ pool.cap = sizeof(mem);
+ pool.buf = mem;
+ memset(&ma, 0, sizeof(ma));
+ ma.memalloc = poolAlloc;
+ ma.memfree = poolFree;
+ ma.userData = (void *)&pool;
+ ma.extraVertices = 256; // realloc not provided, allow 256 extra vertices.
+
+#else
+
+ memset(&ma, 0, sizeof(ma));
+ ma.memalloc = stdAlloc;
+ ma.memfree = stdFree;
+ ma.userData = (void *)&allocated;
+ ma.extraVertices = 256; // realloc not provided, allow 256 extra vertices.
+#endif
+
+ tess = tessNewTess(&ma);
+ if (!tess) return -1;
+
+ // tessSetOption(tess, TESS_CONSTRAINED_DELAUNAY_TRIANGULATION, 1);
+
+ // Create the contour vertex arrays
+
+ int iir, ip;
+
+ // Get max number number of points(vertices) in any contour
+ int npta = m_cntr[0];
+ npta += 2; // fluff
+
+ for (iir = 0; iir < m_ncnt - 1; iir++) {
+ int nptr = m_cntr[iir + 1];
+ npta = wxMax(npta, nptr);
+ }
+
+ TESSreal *geoPt = (TESSreal *)malloc(
+ (npta)*2 * sizeof(TESSreal)); // tess input vertex array
+ TESSreal *ppt = geoPt;
+
+ // Create input structures
+
+ // Exterior Ring
+ int npte = m_cntr[0];
+ unsigned int ptValid = npte;
+
+ // Check and account for winding direction of ring
+
+ double x0, y0, x, y;
+ OGRPoint p;
+
+ wxPoint2DDouble *pp = (wxPoint2DDouble *)m_vertexPtrArray[0];
+ bool cw = isRingClockwise(pp, m_cntr[0]);
+
+ // pp++; // skip 0?
+
+ if (cw) {
+ x0 = pp->m_x;
+ y0 = pp->m_y;
+ } else {
+ x0 = pp[npte - 1].m_x;
+ y0 = pp[npte - 1].m_y;
+ }
+
+ // Transcribe points to vertex array, in proper order with no duplicates
+ // also, accounting for tess_orient
+ for (ip = 0; ip < npte; ip++) {
+ int pidx;
+ if (cw)
+ pidx = npte - ip - 1;
+
+ else
+ pidx = ip;
+
+ x = pp[pidx].m_x;
+ y = pp[pidx].m_y;
+
+ if ((fabs(x - x0) > EQUAL_EPS) || (fabs(y - y0) > EQUAL_EPS)) {
+ if (m_tess_orient == TESS_VERT) {
+ *ppt++ = x;
+ *ppt++ = y;
+ } else {
+ *ppt++ = y;
+ *ppt++ = x;
+ }
+ } else
+ ptValid--;
+
+ x0 = x;
+ y0 = y;
+ }
+
+ // Apply LOD reduction
+ int beforeLOD = ptValid;
+ int afterLOD = beforeLOD;
+
+ std::vector bool_keep;
+ if (ptValid > 5 && (m_LOD_meters > .01)) {
+ for (unsigned int i = 0; i < ptValid; i++) bool_keep.push_back(false);
+
+ // Keep a few key points
+ bool_keep[0] = true;
+ bool_keep[1] = true;
+ bool_keep[ptValid - 1] = true;
+ bool_keep[ptValid - 2] = true;
+
+ DouglasPeuckerFI(geoPt, 1, ptValid - 2, m_LOD_meters, bool_keep);
+
+ // Create a new buffer
+ float *LOD_result = (float *)malloc((npte)*2 * sizeof(float));
+ float *plod = LOD_result;
+ int kept_LOD = 0;
+
+ for (unsigned int i = 0; i < ptValid; i++) {
+ if (bool_keep[i]) {
+ float x = geoPt[i * 2];
+ float y = geoPt[(i * 2) + 1];
+ *plod++ = x;
+ *plod++ = y;
+ kept_LOD++;
+ }
+ }
+
+ beforeLOD = ptValid;
+ afterLOD = kept_LOD;
+
+ tessAddContour(tess, 2, LOD_result, sizeof(float) * 2, kept_LOD);
+
+ free(LOD_result);
+ } else {
+ tessAddContour(tess, 2, geoPt, sizeof(float) * 2, ptValid);
+ }
+
+#if 1
+ // Now the interior contours
+ for (iir = 0; iir < m_ncnt - 1; iir++) {
+ ppt = geoPt;
+ wxPoint2DDouble *pp = (wxPoint2DDouble *)m_vertexPtrArray[iir + 1];
+
+ int npti = m_cntr[iir + 1];
+ ptValid = npti;
+
+ // Check and account for winding direction of ring
+ bool cw = isRingClockwise(pp, m_cntr[iir + 1]);
+
+ if (!cw) {
+ x0 = pp[0].m_x;
+ y0 = pp[0].m_y;
+ } else {
+ x0 = pp[npti - 1].m_x;
+ y0 = pp[npti - 1].m_y;
+ }
+
+ // Transcribe points to vertex array, in proper order with no duplicates
+ // also, accounting for tess_orient
+ for (int ip = 0; ip < npti; ip++) {
+ OGRPoint p;
+ int pidx;
+ if (!cw) // interior contours must be cw
+ pidx = npti - ip - 1;
+ else
+ pidx = ip;
+
+ x = pp[pidx].m_x;
+ y = pp[pidx].m_y;
+
+ if ((fabs(x - x0) > EQUAL_EPS) || (fabs(y - y0) > EQUAL_EPS)) {
+ if (m_tess_orient == TESS_VERT) {
+ *ppt++ = x;
+ *ppt++ = y;
+ } else {
+ *ppt++ = y;
+ *ppt++ = x;
+ }
+ } else
+ ptValid--;
+
+ x0 = x;
+ y0 = y;
+ }
+
+ tessAddContour(tess, 2, geoPt, sizeof(float) * 2, ptValid);
+ }
+#endif
+
+ // Ready to kick off the tesselator
+
+ TriPrim *pTPG_Last = NULL;
+ TriPrim *pTPG_Head = NULL;
+
+ // s_nvmax = 0;
+
+ // OCPNStopWatchTess tt0;
+
+ if (!tessTesselate(tess, TESS_WINDING_POSITIVE, TESS_POLYGONS, nvp, 2, 0))
+ return -1;
+ double tessTime = 0; // tt0.GetTime();
+
+ // Tesselation all done, so...
+ // Create the data structures
+
+ // Make a linked list of TriPrims from tess output arrays
+
+ const TESSreal *verts = tessGetVertices(tess);
+ const int *vinds = tessGetVertexIndices(tess);
+ const int *elems = tessGetElements(tess);
+ const int nverts = tessGetVertexCount(tess);
+ int nelems = tessGetElementCount(tess);
+
+ bool skip = false;
+ // skip = true;
+
+ double stripTime = 0;
+ // tt0.Reset();
+
+ int bytes_needed_vbo = 0;
+ float *vbo = 0;
+
+ if (m_bstripify && nelems && !skip) {
+ STRIPERCREATE sc;
+ sc.DFaces = (udword *)elems;
+ sc.NbFaces = nelems;
+ sc.AskForWords = false;
+ sc.ConnectAllStrips = false;
+ sc.OneSided = false;
+ sc.SGIAlgorithm = false;
+
+ Striper Strip;
+ Strip.Init(sc);
+
+ STRIPERRESULT sr;
+ Strip.Compute(sr);
+
+ stripTime = 0; // tt0.GetTime();
+
+ /*
+ fprintf(stdout, "Number of strips: %d\n", sr.NbStrips);
+ fprintf(stdout, "Number of points: %d\n", nelems);
+ uword* Refs = (uword*)sr.StripRuns;
+ for(udword i=0;i= 3) { // this is a valid primitive
+
+ TriPrim *pTPG = new TriPrim;
+ if (NULL == pTPG_Last) {
+ pTPG_Head = pTPG;
+ pTPG_Last = pTPG;
+ } else {
+ pTPG_Last->p_next = pTPG;
+ pTPG_Last = pTPG;
+ }
+
+ pTPG->p_next = NULL;
+ pTPG->nVert = NbRefs;
+
+ // pTPG->p_vertex = (double *)malloc(NbRefs * 2 *
+ // sizeof(double)); GLdouble *pdd =
+ // (GLdouble*)pTPG->p_vertex;
+
+ pTPG->p_vertex = (double *)vbo_run;
+
+ // Preset LLBox bounding box limits
+ double sxmax = -1e8;
+ double sxmin = 1e8;
+ double symax = -1e8;
+ double symin = 1e8;
+
+ if (NbRefs > 3)
+ pTPG->type = GL_TRIANGLE_STRIP;
+ else
+ pTPG->type = GL_TRIANGLES;
+
+ // Add the first two points
+ int vindex[3];
+ vindex[0] = *Refs++;
+ vindex[1] = *Refs++;
+ unsigned int np = 2; // for the first triangle
+
+ for (unsigned int i = 2; i < NbRefs; i++) {
+ vindex[np] = *Refs++;
+ np++;
+
+ for (unsigned int j = 0; j < np; ++j) {
+ double yd = verts[vindex[j] * 2];
+ double xd = verts[vindex[j] * 2 + 1];
+
+ // Calculate LLBox bounding box for each Tri-prim
+ if (m_bmerc_transform) {
+ double valx = (xd * mx_rate) + mx_offset;
+ double valy = (yd * my_rate) + my_offset;
+
+ // quickly convert to lat/lon
+ double lat = (2.0 * atan(exp(valy / CM93_semimajor_axis_meters)) -
+ M_PI / 2.) /
+ kRadiansPerDegree;
+ double lon = (valx / (kRadiansPerDegree * CM93_semimajor_axis_meters));
+
+ sxmax = wxMax(lon, sxmax);
+ sxmin = wxMin(lon, sxmin);
+ symax = wxMax(lat, symax);
+ symin = wxMin(lat, symin);
+ } else {
+ sxmax = wxMax(xd, sxmax);
+ sxmin = wxMin(xd, sxmin);
+ symax = wxMax(yd, symax);
+ symin = wxMin(yd, symin);
+ }
+
+ // Append this point to TriPrim vbo
+
+ *vbo_run++ =
+ (xd) + m_feature_easting; // adjust to chart ref coordinates
+ *vbo_run++ = (yd) + m_feature_northing;
+ } // For
+
+ // Compute the final LLbbox for this TriPrim chain
+ double minlat, minlon, maxlat, maxlon;
+ fromSM_plib(sxmin, symin, m_feature_ref_lat, m_feature_ref_lon, &minlat,
+ &minlon);
+ fromSM_plib(sxmax, symax, m_feature_ref_lat, m_feature_ref_lon, &maxlat,
+ &maxlon);
+
+ pTPG->tri_box.Set(minlat, minlon, maxlat, maxlon);
+
+ // set for next single point
+ np = 0;
+ }
+ } else
+ Refs += sr.StripLengths[i];
+
+ } // for strips
+ } else { // not stripified
+
+ m_nvertex_max = nverts; // record largest vertex count
+
+ bytes_needed_vbo = nelems * nvp * 2 * sizeof(float);
+ vbo = (float *)malloc(bytes_needed_vbo);
+ float *vbo_run = vbo;
+
+ for (int i = 0; i < nelems; ++i) {
+ const int *p = &elems[i * nvp];
+
+ TriPrim *pTPG = new TriPrim;
+ if (NULL == pTPG_Last) {
+ pTPG_Head = pTPG;
+ pTPG_Last = pTPG;
+ } else {
+ pTPG_Last->p_next = pTPG;
+ pTPG_Last = pTPG;
+ }
+
+ pTPG->p_next = NULL;
+ pTPG->type = GL_TRIANGLES;
+ pTPG->nVert = nvp;
+
+ // pTPG->p_vertex = (double *)malloc(nvp * 2 * sizeof(double));
+ // GLdouble *pdd = (GLdouble*)pTPG->p_vertex;
+
+ pTPG->p_vertex = (double *)vbo_run;
+
+ // Preset LLBox bounding box limits
+ double sxmax = -1e8;
+ double sxmin = 1e8;
+ double symax = -1e8;
+ double symin = 1e8;
+
+ for (size_t j = 0; j < nvp && p[j] != TESS_UNDEF; ++j) {
+ double yd = verts[p[j] * 2];
+ double xd = verts[p[j] * 2 + 1];
+
+ // Calculate LLBox bounding box for each Tri-prim
+ if (m_bmerc_transform) {
+ double valx = (xd * mx_rate) + mx_offset;
+ double valy = (yd * my_rate) + my_offset;
+
+ // quickly convert to lat/lon
+ double lat =
+ (2.0 * atan(exp(valy / CM93_semimajor_axis_meters)) - M_PI / 2.) /
+ kRadiansPerDegree;
+ double lon = (valx / (kRadiansPerDegree * CM93_semimajor_axis_meters));
+
+ sxmax = wxMax(lon, sxmax);
+ sxmin = wxMin(lon, sxmin);
+ symax = wxMax(lat, symax);
+ symin = wxMin(lat, symin);
+ } else {
+ sxmax = wxMax(xd, sxmax);
+ sxmin = wxMin(xd, sxmin);
+ symax = wxMax(yd, symax);
+ symin = wxMin(yd, symin);
+ }
+
+ // Append this point to TriPrim, converting to SM if called for
+
+ if (m_b_senc_sm) {
+ double easting, northing;
+ toSM_plib(yd, xd, m_ref_lat, m_ref_lon, &easting, &northing);
+ *vbo_run++ = easting;
+ *vbo_run++ = northing;
+ } else {
+ *vbo_run++ = xd;
+ *vbo_run++ = yd;
+ }
+ }
+
+ pTPG->tri_box.Set(symin, sxmin, symax, sxmax);
+ }
+ } // stripify
+
+ if (m_printStats) {
+ int nTri = 0;
+ int nStrip = 0;
+
+ TriPrim *p_tp = pTPG_Head;
+ while (p_tp) {
+ if (p_tp->type == GL_TRIANGLES) nTri++;
+ if (p_tp->type == GL_TRIANGLE_STRIP) nStrip++;
+
+ p_tp = p_tp->p_next; // pick up the next in chain
+ }
+
+ if ((nTri + nStrip) > 10000) {
+ printf("LOD: %d/%d\n", afterLOD, beforeLOD);
+
+ printf("Tess time(ms): %f\n", tessTime);
+ printf("Strip time(ms): %f\n", stripTime);
+
+ printf("Primitives: Tri: %5d Strip: %5d Total: %5d\n", nTri, nStrip,
+ nTri + nStrip);
+ printf("\n");
+ }
+ }
+
+ m_ppg_head = new PolyTriGroup;
+ m_ppg_head->m_bSMSENC = m_b_senc_sm;
+
+ m_ppg_head->nContours = m_ncnt;
+ m_ppg_head->pn_vertex = m_cntr; // pointer to array of poly vertex counts
+ m_ppg_head->tri_prim_head = pTPG_Head; // head of linked list of TriPrims
+ // m_ppg_head->data_type = DATA_TYPE_DOUBLE;
+
+ // Transcribe the raw geometry buffer
+ // Converting to float as we go, and
+ // allowing for tess_orient
+ // Also, convert to SM if requested
+
+ int nptfinal = npta;
+
+ // No longer need the full geometry in the SENC,
+ nptfinal = 1;
+
+ m_nwkb = (nptfinal + 1) * 2 * sizeof(float);
+ m_ppg_head->pgroup_geom = (float *)malloc(m_nwkb);
+ float *vro = m_ppg_head->pgroup_geom;
+ ppt = geoPt;
+ float tx, ty;
+
+ for (ip = 0; ip < nptfinal; ip++) {
+ if (TESS_HORZ == m_tess_orient) {
+ ty = *ppt++;
+ tx = *ppt++;
+ } else {
+ tx = *ppt++;
+ ty = *ppt++;
+ }
+
+ if (m_b_senc_sm) {
+ // Calculate SM from chart common reference point
+ double easting, northing;
+ toSM_plib(ty, tx, 0 /*ref_lat*/, 0 /*ref_lon*/, &easting, &northing);
+ *vro++ = easting; // x
+ *vro++ = northing; // y
+ } else {
+ *vro++ = tx; // lon
+ *vro++ = ty; // lat
+ }
+
+ ppt++; // skip z
+ }
+
+ m_ppg_head->bsingle_alloc = true;
+ m_ppg_head->single_buffer = (unsigned char *)vbo;
+ m_ppg_head->single_buffer_size = bytes_needed_vbo;
+ m_ppg_head->data_type = DATA_TYPE_FLOAT;
+
+ free(geoPt);
+
+ // All allocated buffers are owned now by the m_ppg_head
+ // And will be freed on dtor of this object
+
+ if (tess) tessDeleteTess(tess);
+
+ m_bOK = true;
+
+ return 0;
+}
+
+// GLU tesselation support functions
+void beginCallback(GLenum which, void *polyData) {
+ PolyTessGeo *pThis = (PolyTessGeo *)polyData;
+
+ pThis->m_buf_idx = 0;
+ pThis->m_nvcall = 0;
+ pThis->m_gltri_type = which;
+}
+
+void errorCallback(GLenum errorCode, void *polyData) {
+ const GLubyte *estring;
+
+ estring = gluErrorString(errorCode);
+ printf("Tessellation Error: %s\n", estring);
+ exit(0);
+}
+
+void endCallback(void *polyData) {
+ PolyTessGeo *pThis = (PolyTessGeo *)polyData;
+
+ // Create a TriPrim
+ if (pThis->m_nvcall >
+ pThis->m_nvmax) // keep track of largest number of triangle vertices
+ pThis->m_nvmax = pThis->m_nvcall;
+
+ switch (pThis->m_gltri_type) {
+ case GL_TRIANGLE_FAN:
+ case GL_TRIANGLE_STRIP:
+ case GL_TRIANGLES: {
+ TriPrim *pTPG = new TriPrim;
+ if (NULL == pThis->m_pTPG_Last) {
+ pThis->m_pTPG_Head = pTPG;
+ pThis->m_pTPG_Last = pTPG;
+ } else {
+ pThis->m_pTPG_Last->p_next = pTPG;
+ pThis->m_pTPG_Last = pTPG;
+ }
+
+ pTPG->p_next = NULL;
+ pTPG->type = pThis->m_gltri_type;
+ pTPG->nVert = pThis->m_nvcall;
+
+ // Calculate bounding box
+ double sxmax = -1e8; // this poly BBox
+ double sxmin = 1e8;
+ double symax = -1e8;
+ double symin = 1e8;
+
+ GLdouble *pvr = pThis->m_pwork_buf;
+ for (int iv = 0; iv < pThis->m_nvcall; iv++) {
+ GLdouble xd, yd;
+ xd = *pvr++;
+ yd = *pvr++;
+
+ if (pThis->m_bcm93) {
+ // cm93 hits here
+ double valx = (xd * pThis->mx_rate) + pThis->mx_offset +
+ pThis->m_feature_easting;
+ double valy = (yd * pThis->my_rate) + pThis->my_offset +
+ pThis->m_feature_northing;
+
+ // Convert to lat/lon
+ double lat =
+ (2.0 * atan(exp(valy / CM93_semimajor_axis_meters)) - M_PI / 2.) /
+ kRadiansPerDegree;
+ double lon = (valx / (kRadiansPerDegree * CM93_semimajor_axis_meters));
+
+ sxmax = wxMax(lon, sxmax);
+ sxmin = wxMin(lon, sxmin);
+ symax = wxMax(lat, symax);
+ symin = wxMin(lat, symin);
+ } else {
+ // ENC hits here, values are SM measures from feature reference point
+ double valx = (xd * pThis->mx_rate) + pThis->mx_offset +
+ pThis->m_feature_easting;
+ double valy = (yd * pThis->my_rate) + pThis->my_offset +
+ pThis->m_feature_northing;
+
+ sxmax = wxMax(valx, sxmax);
+ sxmin = wxMin(valx, sxmin);
+ symax = wxMax(valy, symax);
+ symin = wxMin(valy, symin);
+ }
+ }
+
+ // Compute the final LLbbox for this TriPrim chain
+ if (pThis->m_bcm93)
+ pTPG->tri_box.Set(symin, sxmin, symax, sxmax);
+ else {
+ double minlat, minlon, maxlat, maxlon;
+ fromSM_plib(sxmin, symin, pThis->m_ref_lat, pThis->m_ref_lon, &minlat,
+ &minlon);
+ fromSM_plib(sxmax, symax, pThis->m_ref_lat, pThis->m_ref_lon, &maxlat,
+ &maxlon);
+ pTPG->tri_box.Set(minlat, minlon, maxlat, maxlon);
+ }
+
+ // Transcribe this geometry to TriPrim,
+ {
+ GLdouble *pds = pThis->m_pwork_buf;
+ pTPG->p_vertex = (double *)malloc(pThis->m_nvcall * 2 * sizeof(double));
+ GLdouble *pdd = (GLdouble *)pTPG->p_vertex;
+
+ for (int ip = 0; ip < pThis->m_nvcall; ip++) {
+ double dlon = *pds++;
+ double dlat = *pds++;
+ *pdd++ =
+ dlon +
+ pThis->m_feature_easting; // adjust to feature ref coordinates
+ *pdd++ = dlat + pThis->m_feature_northing;
+ }
+ }
+
+ break;
+ }
+ default: {
+ // sprintf(buf, "....begin Callback unknown\n");
+ break;
+ }
+ }
+}
+
+void vertexCallback(GLvoid *vertex, void *polyData) {
+ PolyTessGeo *pThis = (PolyTessGeo *)polyData;
+
+ GLdouble *pointer;
+
+ pointer = (GLdouble *)vertex;
+
+ if (pThis->m_buf_idx > pThis->m_buf_len - 4) {
+ int new_buf_len = pThis->m_buf_len + 100;
+ GLdouble *tmp = pThis->m_pwork_buf;
+
+ pThis->m_pwork_buf =
+ (GLdouble *)realloc(pThis->m_pwork_buf, new_buf_len * sizeof(GLdouble));
+ if (NULL == pThis->m_pwork_buf) {
+ free(tmp);
+ tmp = NULL;
+ } else
+ pThis->m_buf_len = new_buf_len;
+ }
+
+ if (pThis->m_tess_orient == TESS_VERT) {
+ pThis->m_pwork_buf[pThis->m_buf_idx++] = pointer[0];
+ pThis->m_pwork_buf[pThis->m_buf_idx++] = pointer[1];
+ } else {
+ pThis->m_pwork_buf[pThis->m_buf_idx++] = pointer[1];
+ pThis->m_pwork_buf[pThis->m_buf_idx++] = pointer[0];
+ }
+
+ pThis->m_nvcall++;
+}
+
+/* combineCallback is used to create a new vertex when edges
+ * intersect.
+ */
+void combineCallback(GLdouble coords[3], GLdouble *vertex_data[4],
+ GLfloat weight[4], GLdouble **dataOut, void *polyData) {
+ PolyTessGeo *pThis = (PolyTessGeo *)polyData;
+
+ GLdouble *vertex = (GLdouble *)malloc(6 * sizeof(GLdouble));
+
+ vertex[0] = coords[0];
+ vertex[1] = coords[1];
+ vertex[2] = coords[2];
+ vertex[3] = vertex[4] = vertex[5] = 0.; // 01/13/05 bugfix
+
+ *dataOut = vertex;
+
+ pThis->m_pCombineVertexArray->Add(vertex);
+}
+
+wxStopWatch *s_stwt;
+
+//------------------------------------------------------------------------------
+// PolyTriGroup Implementation
+//------------------------------------------------------------------------------
+PolyTriGroup::PolyTriGroup() {
+ pn_vertex = NULL; // pointer to array of poly vertex counts
+ pgroup_geom = NULL; // pointer to Raw geometry, used for contour line drawing
+ tri_prim_head = NULL; // head of linked list of TriPrims
+ m_bSMSENC = false;
+ bsingle_alloc = false;
+ single_buffer = NULL;
+ single_buffer_size = 0;
+ data_type = DATA_TYPE_DOUBLE;
+ sfactor = 1.0;
+}
+
+PolyTriGroup::~PolyTriGroup() {
+ free(pn_vertex);
+ free(pgroup_geom);
+ // Walk the list of TriPrims, deleting as we go
+ TriPrim *tp_next;
+ TriPrim *tp = tri_prim_head;
+
+ if (bsingle_alloc) {
+ free(single_buffer);
+ while (tp) {
+ tp_next = tp->p_next;
+ delete tp;
+ tp = tp_next;
+ }
+ } else {
+ while (tp) {
+ tp_next = tp->p_next;
+ tp->FreeMem();
+ delete tp;
+ tp = tp_next;
+ }
+ }
+}
+
+//------------------------------------------------------------------------------
+// TriPrim Implementation
+//------------------------------------------------------------------------------
+TriPrim::TriPrim() {}
+
+TriPrim::~TriPrim() {}
+
+void TriPrim::FreeMem() { free(p_vertex); }
diff --git a/docs/外部参考/OpenCPN源码基线_f7211b4e/上游源码/libs/s52plib/src/mygeom.h b/docs/外部参考/OpenCPN源码基线_f7211b4e/上游源码/libs/s52plib/src/mygeom.h
new file mode 100644
index 0000000..50ae402
--- /dev/null
+++ b/docs/外部参考/OpenCPN源码基线_f7211b4e/上游源码/libs/s52plib/src/mygeom.h
@@ -0,0 +1,304 @@
+/******************************************************************************
+ *
+ * Project: OpenCPN
+ * Purpose: Tesselation of Polygon Object
+ * Author: David Register
+ *
+ ***************************************************************************
+ * Copyright (C) 2010 by David S. Register *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
+ ***************************************************************************
+ *
+ *
+ */
+
+
+#ifndef __MYGEOM_H
+#define __MYGEOM_H
+
+// For compilers that support precompilation, includes "wx/wx.h".
+#include "wx/wxprec.h"
+//#include
+
+#if 0
+#if defined(__OCPN__ANDROID__)
+ #include
+ #include // this is a cut-down version of gl.h
+ #include
+#elif defined(__MSVC__)
+ #include "glew.h"
+#elif defined(__WXOSX__)
+ #include
+ #include
+ typedef void (* _GLUfuncptr)();
+ #define GL_COMPRESSED_RGB_FXT1_3DFX 0x86B0
+#elif defined(__WXQT__) || defined(__WXGTK__)
+ #include
+ #include
+#endif
+#endif
+
+#if defined(__OCPN__ANDROID__)
+#include
+#include // this is a cut-down version of gl.h
+#include
+#else
+#ifdef ocpnUSE_GL
+#if defined(__MSVC__)
+#include "glew.h"
+#elif defined(__WXOSX__)
+#include
+#include
+typedef void (* _GLUfuncptr)();
+#define GL_COMPRESSED_RGB_FXT1_3DFX 0x86B0
+#elif defined(__WXQT__) || defined(__WXGTK__)
+#include
+#include
+#endif
+#endif
+#endif
+
+
+class OGRGeometry;
+class OGRPolygon;
+
+#define TESS_VERT 0 // constants describing preferred tess orientation
+#define TESS_HORZ 1
+
+#define EQUAL_EPS 1.0e-7 // tolerance value
+
+// nota bene These definitions are identical to OpenGL prototypes
+#define PTG_TRIANGLES 0x0004
+#define PTG_TRIANGLE_STRIP 0x0005
+#define PTG_TRIANGLE_FAN 0x0006
+
+// Error Return Codes
+#define ERROR_NONE 0
+#define ERROR_NO_DLL 1
+#define ERROR_BAD_OGRPOLY 2
+
+// Some external prototypes
+
+#define DATA_TYPE_FLOAT 0
+#define DATA_TYPE_DOUBLE 1
+#define DATA_TYPE_SHORT 2
+
+//--------------------------------------------------------------------------------------------------
+//
+// Internal data structures used for describing/rendering tesselated
+// polygons
+//
+//--------------------------------------------------------------------------------------------------
+
+class Extended_Geometry {
+public:
+ Extended_Geometry();
+ ~Extended_Geometry();
+
+ OGRGeometry *pogrGeom;
+ int n_vector_indices;
+ int *pvector_index;
+ int n_contours; // parameters passed to trapezoid tesselator
+ int *contour_array;
+ int n_max_vertex;
+ int pointx;
+ int pointy;
+ wxPoint2DDouble *vertex_array;
+ int xmin, xmax, ymin, ymax;
+ int n_max_edge_points;
+
+ // Conversion parameters
+ // for (assummed linear) convertions from vertex_array points to
+ // easting/northing, metres from 0,0 To convert to lat/lon, use simple
+ // merctor equations
+ double x_rate;
+ double x_offset;
+ double y_rate;
+ double y_offset;
+ double ref_lat; // Lower left corner of the cell containing this feature
+ double ref_lon;
+};
+
+class TriPrim {
+public:
+ TriPrim();
+ ~TriPrim();
+ void FreeMem(void);
+
+ unsigned int type; // Type of triangle primitive
+ // May be PTG_TRIANGLES
+ // PTG_TRIANGLE_STRIP
+ // PTG_TRIANGLE_FAN
+
+ int nVert;
+ double *p_vertex; // Pointer to vertex array, x,y,x,y.....
+
+ LLBBox tri_box;
+
+ TriPrim *p_next; // chain link
+};
+
+class LegacyTriPrim {
+public:
+ LegacyTriPrim();
+ ~LegacyTriPrim();
+ void FreeMem(void);
+
+ unsigned int type; // Type of triangle primitive
+ // May be PTG_TRIANGLES
+ // PTG_TRIANGLE_STRIP
+ // PTG_TRIANGLE_FAN
+
+ int nVert;
+ double *p_vertex; // Pointer to vertex array, x,y,x,y.....
+
+ double minx, miny, maxx, maxy;
+
+ LegacyTriPrim *p_next; // chain link
+};
+
+class PolyTriGroup {
+public:
+ PolyTriGroup();
+ PolyTriGroup(Extended_Geometry *pxGeom);
+ ~PolyTriGroup();
+
+ int nContours;
+ int *pn_vertex; // pointer to array of poly vertex counts
+ float *pgroup_geom; // pointer to Raw geometry, used for contour line drawing
+
+ TriPrim *tri_prim_head; // head of linked list of TriPrims
+ bool m_bSMSENC;
+ bool bsingle_alloc;
+ unsigned char *single_buffer;
+ int single_buffer_size;
+ int data_type; // p_vertex in TriPrim chain is FLOAT or DOUBLE
+ float sfactor;
+ float soffset;
+
+private:
+ int my_bufgets(char *buf, int buf_len_max);
+};
+
+typedef struct {
+ int ilseg;
+ int irseg;
+ double loy;
+ double hiy;
+
+} trapz_t;
+
+//--------------------------------------------------------------------------------------------------
+//
+// Triangle Tesselator Class
+//
+//--------------------------------------------------------------------------------------------------
+class OGRPolygon;
+class GLUtesselator;
+
+class PolyTessGeo {
+public:
+ PolyTessGeo();
+ ~PolyTessGeo();
+
+ PolyTessGeo(OGRPolygon *poly, bool bSENC_SM, double ref_lat, double ref_lon,
+ double LOD_meters); // Build this from OGRPolygon
+
+ PolyTessGeo(Extended_Geometry *pxGeom);
+
+ PolyTessGeo(unsigned char *polybuf, int nrecl, int index,
+ int senc_file_version);
+
+ bool IsOk() { return m_bOK; }
+
+ int BuildDeferredTess(void);
+
+ double Get_xmin() { return xmin; }
+ double Get_xmax() { return xmax; }
+ double Get_ymin() { return ymin; }
+ double Get_ymax() { return ymax; }
+ void SetExtents(double x_left, double y_bot, double x_right, double y_top);
+
+ PolyTriGroup *Get_PolyTriGroup_head() { return m_ppg_head; }
+ int GetnVertexMax() { return m_nvertex_max; }
+ void SetnVertexMax(int max) { m_nvertex_max = max; }
+ int GetnContours() { return m_ncnt; }
+
+ int ErrorCode;
+ void GetRefPos( double *lat, double *lon);
+ void Set_PolyTriGroup_head(PolyTriGroup *head) { m_ppg_head = head; }
+ void Set_OK(bool bok) { m_bOK = bok; }
+
+ void SetPPGHead(PolyTriGroup *head) { m_ppg_head = head; }
+ int my_bufgets(char *buf, int buf_len_max);
+
+ GLUtesselator *GLUtessobj;
+ wxArrayPtrVoid *m_pCombineVertexArray;
+ double *m_pwork_buf;
+ int m_buf_len;
+ int m_buf_idx;
+ TriPrim *m_pTPG_Last;
+ TriPrim *m_pTPG_Head;
+ int m_gltri_type;
+ int m_nvcall;
+ int m_nvmax;
+ bool m_bmerc_transform;
+ double mx_rate, mx_offset, my_rate, my_offset;
+ bool m_b_senc_sm;
+ double m_ref_lat, m_ref_lon;
+ int m_tess_orient;
+ double m_feature_ref_lat, m_feature_ref_lon;
+ double m_feature_easting, m_feature_northing;
+
+ double earthAxis;
+ bool m_bcm93;
+
+private:
+ int BuildTess(void);
+ // int BuildTessGL2(void);
+ // int PolyTessGeoGL(OGRPolygon *poly, bool bSENC_SM, double ref_lat, double
+ // ref_lon);
+ int BuildTessGLU(void);
+ int BuildTessGLFromXG(void);
+
+ // Data
+
+ bool m_bOK;
+
+ Extended_Geometry *m_pxgeom;
+
+ double xmin, xmax, ymin, ymax;
+ PolyTriGroup *m_ppg_head; // head of a PolyTriGroup chain
+ int m_nvertex_max; // and computed max vertex count
+ // used by drawing primitives as
+ // optimization
+
+ int m_ncnt;
+ int m_nwkb;
+ int *m_cntr;
+ char *m_buf_head;
+ char *m_buf_ptr; // used to read passed SENC record
+ int m_nrecl;
+
+ double m_LOD_meters;
+
+ double **m_vertexPtrArray;
+ bool m_printStats;
+ bool m_bstripify;
+};
+
+#endif
diff --git a/docs/外部参考/OpenCPN源码基线_f7211b4e/上游源码/libs/s52plib/src/s52_plib_utils.cpp b/docs/外部参考/OpenCPN源码基线_f7211b4e/上游源码/libs/s52plib/src/s52_plib_utils.cpp
new file mode 100644
index 0000000..c292546
--- /dev/null
+++ b/docs/外部参考/OpenCPN源码基线_f7211b4e/上游源码/libs/s52plib/src/s52_plib_utils.cpp
@@ -0,0 +1,20 @@
+#include "s52_plib_utils.h"
+
+static class _DefaultUtils : public S52PlibUtils {
+ /** Default implementation: return unscaled font. */
+ wxFont* GetFont(wxFont* font, double scale) override { return font; }
+ wxFont* GetDefaultFont(wxString label, int Ptsize) override {
+ return nullptr;
+ }
+ wxFont* GetScaledFont(int pointSize, wxFontFamily family, wxFontStyle style,
+ wxFontWeight weight, const wxString faceName,
+ double scale) override {
+ return nullptr;
+ }
+
+} DefaultUtils;
+
+static S52PlibUtils* s_the_utils = &DefaultUtils;
+
+S52PlibUtils* GetS52Utils() { return s_the_utils; }
+void SetUtils(S52PlibUtils* utils) { s_the_utils = utils; }
diff --git a/docs/外部参考/OpenCPN源码基线_f7211b4e/上游源码/libs/s52plib/src/s52_plib_utils.h b/docs/外部参考/OpenCPN源码基线_f7211b4e/上游源码/libs/s52plib/src/s52_plib_utils.h
new file mode 100644
index 0000000..e067951
--- /dev/null
+++ b/docs/外部参考/OpenCPN源码基线_f7211b4e/上游源码/libs/s52plib/src/s52_plib_utils.h
@@ -0,0 +1,20 @@
+#ifndef S52_PLIB_UTILS_H
+#define S52_PLIB_UTILS_H
+
+#include
+
+/** Interface implemented by calling code */
+class S52PlibUtils {
+public:
+ /** Get a scaled font based on given; created if not already existing. */
+ virtual wxFont* GetFont(wxFont* font, double scale) = 0;
+ virtual wxFont* GetDefaultFont(wxString label, int Ptsize) = 0;
+ virtual wxFont* GetScaledFont(int pointSize, wxFontFamily family,
+ wxFontStyle style, wxFontWeight weight,
+ const wxString faceName, double scale) = 0;
+};
+
+S52PlibUtils* GetS52Utils();
+
+void SetUtils(S52PlibUtils* utils);
+#endif // S52_PLIB_UTILS_H
diff --git a/docs/外部参考/OpenCPN源码基线_f7211b4e/上游源码/libs/s52plib/src/s52cnsy.cpp b/docs/外部参考/OpenCPN源码基线_f7211b4e/上游源码/libs/s52plib/src/s52cnsy.cpp
new file mode 100644
index 0000000..a117c1b
--- /dev/null
+++ b/docs/外部参考/OpenCPN源码基线_f7211b4e/上游源码/libs/s52plib/src/s52cnsy.cpp
@@ -0,0 +1,6261 @@
+/******************************************************************************
+ *
+ * Project: OpenCPN
+ * Purpose: S52 Conditional Symbology Library
+ * Author: David Register, Sylvain Duclos
+ *
+ ***************************************************************************
+ * Copyright (C) 2010 by David S. Register *
+ *
+ * Copyright (C) 2000-2001 Sylvain Duclos
+ * sylvain_duclos@yahoo.com
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
+ ***************************************************************************
+ *
+ */
+
+#include "wx/wxprec.h"
+
+#ifndef WX_PRECOMP
+#include "wx/wx.h"
+#endif // precompiled headers
+
+#include "wx/tokenzr.h"
+
+#include "s52plib.h"
+#include "s52utils.h"
+
+bool GetDoubleAttr(S57Obj *obj, const char *AttrName, double &val);
+
+#define UNKNOWN 1e6 // HUGE_VAL // INFINITY/NAN
+
+#ifndef chk_snprintf
+#define chk_snprintf(buf, len, fmt, ...) \
+ { \
+ int r = snprintf(buf, len, fmt, ##__VA_ARGS__); \
+ if (r == -1 || r >= len) wxLogWarning("snprint overrun"); \
+ }
+#endif
+
+WX_DEFINE_ARRAY_DOUBLE(double, ArrayOfSortedDoubles);
+
+// size of attributes value list buffer
+#define LISTSIZE 32 // list size
+
+extern s52plib *ps52plib;
+
+wxString *CSQUAPNT01(S57Obj *obj);
+wxString *CSQUALIN01(S57Obj *obj);
+
+wxArrayPtrVoid *GetChartFloatingATONArray(ObjRazRules *rzRules) {
+ S57Obj *obj = rzRules->obj;
+ if (obj->m_chart_context)
+ return obj->m_chart_context->pFloatingATONArray;
+ else
+ return NULL;
+}
+
+wxArrayPtrVoid *GetChartRigidATONArray(ObjRazRules *rzRules) {
+ S57Obj *obj = rzRules->obj;
+ if (obj->m_chart_context->chart)
+ return obj->m_chart_context->pRigidATONArray;
+ else
+ return NULL;
+}
+
+static void *CLRLIN01(void *param) {
+ ObjRazRules *rzRules = (ObjRazRules *)param;
+ // S57Obj *obj = rzRules->obj;
+
+ printf("s52csny : CLRLIN01 ERROR no conditional symbology for: %s\n",
+ rzRules->LUP->OBCL);
+ return NULL;
+}
+
+static void *DATCVR01(void *param) {
+ // Remarks: This conditional symbology procedure describes procedures for:
+ // - symbolizing the limit of ENC coverage;
+ // - symbolizing navigational purpose boundaries ("scale boundarie"); and
+ // - indicating overscale display.
+ //
+ // Note that the mandatory meta object CATQUA is symbolized by the look-up
+ // table.
+ //
+ // Because the methods adopted by an ECDIS to meet the IMO and IHO
+ // requirements listed on the next page will depend on the manufacturer's
+ // software, and cannot be described in terms of a flow chart in the same way
+ // as other conditional procedures, this procedure is in the form of written
+ // notes.
+
+ // ObjRazRules *rzRules = (ObjRazRules *)param;
+ // S57Obj *obj = rzRules->obj;
+
+ wxString rule_str;
+ ///////////////////////
+ // 1- REQUIREMENT
+ // (IMO/IHO specs. explenation)
+
+ ///////////////////////
+ // 2- ENC COVERAGE
+ //
+ // 2.1- Limit of ENC coverage
+ // datcvr01 = g_string_new(";OP(3OD11060);LC(HODATA01)");
+ rule_str.Append(_T("LC(HODATA01)"));
+ // rule_str.Append("AC(DEPDW)");
+ // FIXME: get cell extend
+
+ // 2.2- No data areas
+ // This can be done outside of CS (ie when clearing the screen in Mesa)
+ // FIXME: ";OP(0---);AC(NODATA)"
+ // FIXME: set geo to cover earth (!)
+
+ //////////////////////
+ // 3- SCALE BOUNDARIES
+ //
+ // 3.1- Chart scale boundaties
+ // FIXME;
+ // g_string_append(datcvr01, ";LS(SOLD,1,CHGRD)");
+ // -OR- LC(SCLBDYnn) (?)
+ //
+ // ;OP(3OS21030)
+
+ // 3.2- Graphical index of navigational purpose
+ // FIXME: draw extent of available SENC in DB
+
+ //////////////////////
+ // 4- OVERSCALE
+ //
+ // FIXME: get meta date CSCL of DSPM field
+ // FIXME: get object M_CSCL or CSCALE
+ //
+ // 4.1- Overscale indication
+ // FIXME: compute, scale = [denominator of the compilation scale] /
+ // [denominator of the display scale]
+ // FIXME: draw overscale indication (ie TX("X%3.1f",scale))
+ //
+ // 4.2- Ovescale area at a chart scale boundary
+ // FIXME: test if next chart is over scale (ie going from large scale chart
+ // to a small scale chart)
+ // FIXME: draw AP(OVERSC01) on overscale part of display
+ // g_string(";OP(3OS21030)");
+
+ //
+ // 4.3- Larger scale data available
+ // FIXME: display indication of better scale available (?)
+
+ wxString datcvr01;
+ datcvr01.Append(rule_str);
+ datcvr01.Append('\037');
+
+ char *r = (char *)malloc(datcvr01.Len() + 1);
+ strcpy(r, datcvr01.mb_str());
+
+ return r;
+}
+
+bool GetIntAttr(S57Obj *obj, const char *AttrName, int &val) {
+ int idx = obj->GetAttributeIndex(AttrName);
+
+ if (idx >= 0) {
+ // using idx to get the attribute value
+ S57attVal *v = obj->attVal->Item(idx);
+
+ assert(v->valType == OGR_INT);
+ val = *(int *)(v->value);
+
+ return true;
+ } else
+ return false;
+}
+#if 0
+bool GetIntAttr(S57Obj *obj, const char *AttrName, int &val)
+{
+ char *attList = (char *)calloc(obj->attList->Len()+1, 1);
+ strncpy(attList, obj->attList->mb_str(), obj->attList->Len());
+
+ char *patl = attList;
+ char *patr;
+ int idx = 0;
+ while(*patl)
+ {
+ patr = patl;
+ while(*patr != '\037')
+ patr++;
+
+ if(!strncmp(patl, AttrName, 6))
+ break;
+
+ patl = patr + 1;
+ idx++;
+ }
+
+ if(!*patl) // Requested Attribute not found
+ {
+ free(attList);
+ return false; // so don't return a value
+ }
+
+ // using idx to get the attribute value
+ wxArrayOfS57attVal *pattrVal = obj->attVal;
+
+ S57attVal *v = pattrVal->Item(idx);
+ val = *(int*)(v->value);
+
+ free(attList);
+ return true;
+}
+#endif
+/*
+bool GetFloatAttr(S57Obj *obj, char *AttrName, float &val)
+{
+ char *attList = (char *)(obj->attList->mb_str()); //attList is
+wxString
+
+ char *patl = attList;
+ char *patr;
+ int idx = 0;
+ while(*patl)
+ {
+ patr = patl;
+ while(*patr != '\037')
+ patr++;
+
+ if(!strncmp(patl, AttrName, 6))
+ break;
+
+ patl = patr + 1;
+ idx++;
+ }
+
+ if(!*patl) // Requested Attribute
+not found
+ {
+ free(attList);
+ return false; // so don't return a
+value
+ }
+
+// using idx to get the attribute value
+ wxArrayOfS57attVal *pattrVal = obj->attVal;
+
+ S57attVal *v = pattrVal->Item(idx);
+ val = *(float*)(v->value);
+
+ free(attList);
+ return true;
+}
+
+*/
+bool GetDoubleAttr(S57Obj *obj, const char *AttrName, double &val) {
+ int idx = obj->GetAttributeIndex(AttrName);
+
+ if (idx >= 0) {
+ // using idx to get the attribute value
+
+ S57attVal *v = obj->attVal->Item(idx);
+ assert(v->valType == OGR_REAL);
+ val = *(double *)(v->value);
+
+ return true;
+ } else
+ return false;
+}
+
+bool GetStringAttr(S57Obj *obj, const char *AttrName, char *pval, int nc) {
+ if (!obj || !AttrName || !pval || nc <= 0) return false;
+
+ pval[0] = '\0';
+
+ int idx = obj->GetAttributeIndex(AttrName);
+ if (idx < 0) return false;
+
+ S57attVal *v = obj->attVal->Item(idx);
+ assert(v->valType == OGR_STR);
+
+ const char *val = static_cast(v->value);
+ if (!val) return true;
+
+ strncpy(pval, val, nc - 1);
+ pval[nc - 1] = '\0';
+ return true;
+}
+
+wxString *GetStringAttrWXS(S57Obj *obj, const char *AttrName) {
+ int idx = obj->GetAttributeIndex(AttrName);
+
+ if (idx >= 0) {
+ // using idx to get the attribute value
+ S57attVal *v = obj->attVal->Item(idx);
+
+ assert(v->valType == OGR_STR);
+ char *val = (char *)(v->value);
+
+ return new wxString(val, wxConvUTF8);
+ } else
+ return NULL;
+}
+
+static int _parseList(const char *str_in, char *buf, int buf_size)
+// Put a string of comma delimited number in an array (buf).
+// Return: the number of value in buf.
+// Assume: - number < 256,
+// - list size less than buf_size .
+// Note: buf is \0 terminated for strpbrk().
+{
+ char *str = (char *)str_in;
+ int i = 0;
+
+ if (NULL != str && *str != '\0') {
+ do {
+ if (i >= LISTSIZE - 1) {
+ printf("OVERFLOW --value in list lost!!\n");
+ break;
+ }
+
+ /*
+ if (255 < (unsigned char) atoi(str)) {
+ PRINTF("value overflow (>255)\n");
+ exit(0);
+ }
+ */
+
+ buf[i++] = (unsigned char)atoi(str);
+
+ while (isdigit(*str)) str++; // next
+
+ } while (*str++ != '\0'); // skip ',' or exit
+ }
+
+ buf[i] = '\0';
+
+ return i;
+}
+
+static int _atPtPos(S57Obj *objNew, wxArrayPtrVoid *curntList, int bSectorCheck)
+// return TRUE if there is a light at this position
+// or if its an extended arc radius else FALSE
+{
+ unsigned int i;
+
+ if (NULL == curntList) return false;
+
+ for (i = 0; i < curntList->GetCount(); i++) {
+ S57Obj *objOld = (S57Obj *)curntList->Item(i);
+
+ if ((objOld->x == objNew->x) && (objOld->y == objNew->y)) {
+ if (!bSectorCheck) return TRUE;
+ /*
+ else {
+ // check for extend arc radius
+ GString *Asectr1str = S57_getAttVal(geoOld, "SECTR1");
+ GString *Asectr2str = S57_getAttVal(geoOld, "SECTR2");
+ GString *Bsectr1str = S57_getAttVal(geoNew, "SECTR1");
+ GString *Bsectr2str = S57_getAttVal(geoNew, "SECTR2");
+
+ // check present
+ if (NULL == Asectr1str ||
+ NULL == Asectr1str ||
+ NULL == Asectr1str ||
+ NULL == Asectr1str)
+ return FALSE;
+
+ {
+ double Asectr1 = atof(Asectr1str->str);
+ double Asectr2 = atof(Asectr2str->str);
+ double Bsectr1 = atof(Bsectr1str->str);
+ double Bsectr2 = atof(Bsectr2str->str);
+ double Asweep = (Asectr1 > Asectr2) ?
+ Asectr2-Asectr1+360 : Asectr2-Asectr1;
+ double Bsweep = (Bsectr1 > Bsectr2) ?
+ Bsectr2-Bsectr1+360 : Bsectr2-Bsectr1;
+
+ // check sector overlap
+ if (Asectr2<=Bsectr1 || Asectr1>=Bsectr2) {
+ if (Asweep == Bsweep) {
+ g_string_truncate(Bsectr2str, 0);
+ g_string_sprintf(Bsectr2str, "%f",Bsectr2-1);
+ S57_setAtt(geoNew, "SECTR2", Bsectr2str->str);
+ }
+
+ return FALSE;
+ }
+
+ // check if other sector larger
+ if (Asweep >= Bsweep)
+ return TRUE;
+ }
+ }
+ */
+ }
+ }
+
+ return FALSE;
+}
+
+wxString _selSYcol(char *buf, bool bsectr, double valnmr) {
+ wxString sym;
+
+ if (!bsectr) {
+ sym = _T(";SY(LITDEF11"); // default
+
+ // max 1 color
+ if ('\0' == buf[1]) {
+ if (strpbrk(buf, "\003"))
+ sym = _T(";SY(LIGHTS11");
+ else if (strpbrk(buf, "\004"))
+ sym = _T(";SY(LIGHTS12");
+ else if (strpbrk(buf, "\001\006\011"))
+ sym = _T(";SY(LIGHTS13");
+ } else {
+ // max 2 color
+ if ('\0' == buf[2]) {
+ if (strpbrk(buf, "\001") && strpbrk(buf, "\003"))
+ sym = _T(";SY(LIGHTS11");
+ else if (strpbrk(buf, "\001") && strpbrk(buf, "\004"))
+ sym = _T(";SY(LIGHTS12");
+ }
+ }
+ }
+
+ else // all-round fixed light symbolized as a circle, radius depends on color
+ // This treatment is seen on SeeMyDenc by SevenCs
+ // This may not be S-52 compliant....
+ {
+ // Another non-standard extension....
+ // All round light circle diameter is scaled if the light has a
+ // reasonable VALNMR attribute
+ int radius = 3;
+ if (valnmr > 0) {
+ if (valnmr < 7.0)
+ radius = 3;
+ else if (valnmr < 15.0)
+ radius = 10;
+ else if (valnmr < 30.0)
+ radius = 15;
+ else
+ radius = 20;
+ }
+
+ // max 1 color
+ if ('\0' == buf[1]) {
+ if (strpbrk(buf, "\003"))
+ sym.Printf(_T(",LITRD, 2,0,360,%d,0"), radius + 1);
+ else if (strpbrk(buf, "\004"))
+ sym.Printf(_T(",LITGN, 2,0,360,%d,0"), radius);
+ else if (strpbrk(buf, "\001\006\011"))
+ sym.Printf(_T(",LITYW, 2,0,360,%d,0"), radius + 2);
+ else if (strpbrk(buf, "\014"))
+ sym.Printf(_T(",CHMGD, 2,0,360,%d,0"), radius + 3);
+ else
+ sym.Printf(_T(",CHMGD, 2,0,360,%d,0"), radius + 5); // default
+
+ } else if ('\0' == buf[2]) // or 2 color
+ {
+ if (strpbrk(buf, "\001") && strpbrk(buf, "\003"))
+ sym.Printf(_T(",LITRD, 2,0,360,%d,0"), radius + 1);
+ else if (strpbrk(buf, "\001") && strpbrk(buf, "\004"))
+ sym.Printf(_T(",LITGN, 2,0,360,%d,0"), radius);
+ else
+ sym.Printf(_T(",CHMGD, 2,0,360,%d,0"), radius + 5); // default
+
+ } else
+ sym.Printf(_T(",CHMGD, 2,0,360,%d,0"), radius + 5);
+
+ if (sym.Len()) sym.Prepend(_T(";CA(OUTLW, 4"));
+ }
+
+ return sym;
+}
+
+static double _DEPVAL01(S57Obj *obj, double least_depth)
+// Remarks: S-57 Appendix B1 Annex A requires in Section 6 that areas of rocks
+// be encoded as area obstruction, and that area OBSTRNs and area WRECKS be
+// covered by either group 1 object DEPARE or group 1 object UNSARE. If the
+// value of the attribute VALSOU for an area OBSTRN or WRECKS is missing, the
+// DRVAL1 of an underlying DEPARE is the preferred default for establishing a
+// depth value. This procedure either finds the shallowest DRVAL1 of the one or
+// more underlying DEPAREs, or returns an "unknown"" depth value to the main
+// procedure for the next default procedure.
+
+// NOTE: UNSARE test is useless since least_depth is already UNKNOWN
+{
+ least_depth = UNKNOWN;
+
+ /*
+ S57_geo *geoTmp = geo;
+
+ // NOTE: the geo list is unchange (_UDWHAZ03 will unlink geo)
+ while (NULL != (geoTmp = S57_nextObj(geoTmp))) {
+ GString *objlstr = S57_getAttVal(geoTmp, "OBJL");
+ int objl = (NULL == objlstr) ? 0 : atoi(objlstr->str);
+
+ // get area DEPARE that intersect this area
+ if (DEPARE==objl && LINES_T==S57_getObjtype(geo)) {
+ GString *drval1str = S57_getAttVal(geoTmp, "DRVAL1");
+ double drval1 = (NULL == drval1str) ? 9.0 :
+ atof(drval1str->str);
+
+ if (NULL != drval1str) {
+ if (UNKNOWN==least_depth || least_depth *pobj_list = NULL;
+ if (obj->m_chart_context->chart){
+ chart_context *pcc = obj->m_chart_context;
+ s57chart *chart = (s57chart*)obj->m_chart_context->chart;
+ pobj_list = (chart->*pcc->pt2GetAssociatedObjects)(obj);
+ }
+ else{
+ danger = false;
+ }
+
+ if (pobj_list) {
+ for (S57Obj* ptest_obj: *pobj_list) {
+ if (GEO_LINE == ptest_obj->Primitive_type) {
+ double drval2 = 0.0;
+ GetDoubleAttr(ptest_obj, "DRVAL2", drval2);
+
+ if (drval2 < safety_contour) {
+ danger = TRUE;
+ break;
+ }
+ } else {
+ double drval1 = 0.0;
+ GetDoubleAttr(ptest_obj, "DRVAL1", drval1);
+
+#if 0
+ double drval2 = 0.0;
+ GetDoubleAttr(ptest_obj, "DRVAL2", drval2);
+
+ if(expsou == 1 || depth_value < drval2 )
+ b_promote = true;
+#endif
+
+ if (drval1 >= safety_contour && expsou != 1) {
+ danger = TRUE;
+ break;
+ }
+ }
+ }
+
+ delete pobj_list;
+ }
+ }
+
+ if (TRUE == danger) {
+ int watlev = 0; // Enum 0 invalid
+ GetIntAttr(obj, "WATLEV", watlev);
+
+ if ((1 == watlev) || (2 == watlev)) {
+ // dry
+ } else {
+ udwhaz03str = _T(";SY(ISODGR51)"); //_T(";OP(8OD14010);SY(ISODGR51)");
+ // S57_setAtt(geo, "SCAMIN", "INFINITE");
+ }
+
+ // Move this object to DisplayBase category
+ rzRules->obj->m_DisplayCat = DISPLAYBASE;
+
+ }
+
+ if (promote_return) *promote_return = b_promote;
+
+ wxString *ret_str = new wxString(udwhaz03str);
+ return ret_str;
+}
+
+// Remarks: An object of the class "depth area" is coloured and covered with
+// fill patterns according to the mariners selections of shallow contour, safety
+// contour and deep contour. This requires a decision making process based on
+// DRVAL1 and DRVAL2. Objects of the class "dredged area" are handled by this
+// routine as well to ensure a consistent symbolization of areas that represent
+// the surface of the seabed.
+
+static void *DEPARE01(void *param) {
+ ObjRazRules *rzRules = (ObjRazRules *)param;
+ S57Obj *obj = rzRules->obj;
+
+ double drval1, drval2;
+ bool drval1_found;
+
+ // Determine the color based on mariner selections
+
+ drval1 = -1.0; // default values
+ drval1_found = GetDoubleAttr(obj, "DRVAL1", drval1);
+ // drval2 = drval1 + 0.01;
+ GetDoubleAttr(obj, "DRVAL2", drval2);
+ //make sure drval2 is higher then drval1, always even in bad charts
+ if (drval2 <= drval1) drval2 = drval1 + 0.01;
+
+ // Create a string of the proper color reference
+
+ wxString rule_str = _T("AC(DEPIT)");
+
+ if (drval1 >= 0.0 && drval2 > 0.0) rule_str = _T("AC(DEPVS)");
+
+ if (TRUE == S52_getMarinerParam(S52_MAR_TWO_SHADES)) {
+ if (drval1 >= S52_getMarinerParam(S52_MAR_SAFETY_CONTOUR) &&
+ drval2 > S52_getMarinerParam(S52_MAR_SAFETY_CONTOUR)) {
+ rule_str = _T("AC(DEPDW)");
+ }
+ } else {
+ if (drval1 >= S52_getMarinerParam(S52_MAR_SHALLOW_CONTOUR) &&
+ drval2 > S52_getMarinerParam(S52_MAR_SHALLOW_CONTOUR))
+ rule_str = _T("AC(DEPMS)");
+
+ if (drval1 >= S52_getMarinerParam(S52_MAR_SAFETY_CONTOUR) &&
+ drval2 > S52_getMarinerParam(S52_MAR_SAFETY_CONTOUR)) {
+ rule_str = _T("AC(DEPMD)");
+ }
+
+ if (drval1 >= S52_getMarinerParam(S52_MAR_DEEP_CONTOUR) &&
+ drval2 > S52_getMarinerParam(S52_MAR_DEEP_CONTOUR)) {
+ rule_str = _T("AC(DEPDW)");
+ }
+ }
+
+ // If object is DRGARE....
+
+ if (!strncmp(rzRules->LUP->OBCL, "DRGARE", 6)) {
+ if (!drval1_found) // If DRVAL1 was not defined...
+ {
+ rule_str = _T("AC(DEPMD)");
+ }
+ rule_str.Append(_T(";AP(DRGARE01)"));
+ rule_str.Append(_T(";LS(DASH,1,CHGRF)"));
+
+ // Todo Restrictions
+ /*
+ char pval[30];
+ if(true == GetStringAttr(obj, "RESTRN", pval, 20))
+ {
+ GString *rescsp01 = _RESCSP01(geo);
+ if (NULL != rescsp01)
+ {
+ g_string_append(depare01, rescsp01->str);
+ g_string_free(rescsp01, TRUE);
+ }
+ }
+ */
+ }
+
+ rule_str.Append('\037');
+
+ return strdup(rule_str.mb_str());
+}
+/*
+static void *DEPCNT02A(void *param)
+{
+ // ObjRazRules *rzRules = (ObjRazRules *)param;
+ // S57Obj *obj = rzRules->obj;
+
+ // Add another rule onto the ruleList
+
+ Rules *r = NULL;
+
+ r = (Rules*)calloc(1, sizeof(Rules));
+ r->ruleType = RUL_SIM_LN;
+ r->INSTstr = "SOLD,1,DEPCN"; // points into
+the plib data space
+
+ return NULL;
+
+}
+*/
+
+static void *DEPCNT02(void *param)
+// Remarks: An object of the class "depth contour" or "line depth area" is
+// highlighted and must be shown under all circumstances if it matches the
+// safety contour depth value entered by the mariner (see IMO PS 3.6). But,
+// while the mariner is free to enter any safety contour depth value that he
+// thinks is suitable for the safety of his ship, the SENC only contains a
+// limited choice of depth contours. This symbology procedure determines whether
+// a contour matches the selected safety contour. If the selected safety contour
+// does not exist in the data, the procedure will default to the next deeper
+// contour. The contour selected is highlighted as the safety contour and put in
+// DISPLAYBASE. The procedure also identifies any line segment of the spatial
+// component of the object that has a "QUAPOS" value indicating unreliable
+// positioning, and symbolizes it with a double dashed line.
+//
+// Note: Depth contours are not normally labeled. The ECDIS may provide labels,
+// on demand only as with other text, or provide the depth value on cursor
+// picking
+{
+ // GString *depcnt02 = NULL;
+ // int safe = FALSE; // initialy not a safety contour
+ // GString *objlstr = NULL;
+ // int objl = 0;
+ // GString *quaposstr = NULL;
+ // int quapos = 0;
+ double drval1, drval2;
+ bool safe = FALSE;
+ wxString rule_str;
+ double safety_contour = S52_getMarinerParam(S52_MAR_SAFETY_CONTOUR);
+
+ // objlstr = S57_getAttVal(geo, "OBJL");
+ // objl = (NULL == objlstr) ? 0 : atoi(objlstr->str);
+
+ ObjRazRules *rzRules = (ObjRazRules *)param;
+ S57Obj *obj = rzRules->obj;
+ // Debug
+ //if(obj->Index == 5014)
+ //int tty = 5;
+
+ if ((!strncmp(obj->FeatureName, "DEPARE", 6)) &&
+ GEO_LINE == obj->Primitive_type) {
+ drval1 = 0.0; // default values
+ GetDoubleAttr(obj, "DRVAL1", drval1);
+ drval2 = drval1;
+ GetDoubleAttr(obj, "DRVAL2", drval2);
+
+ if (drval1 <= safety_contour) {
+ if (drval2 >= safety_contour) safe = TRUE;
+ }
+
+ else {
+ double next_safe_contour = 1e6;
+ next_safe_contour = obj->m_chart_context->safety_contour;
+ if (fabs(drval1 - next_safe_contour) < 1e-4) safe = true;
+ }
+
+ } else {
+ // continuation A (DEPCNT)
+ double valdco = 0;
+ GetDoubleAttr(obj, "VALDCO", valdco);
+
+ if (valdco == safety_contour)
+ safe = TRUE; // this is useless !?!?
+
+ else {
+ double next_safe_contour = 1e6;
+ next_safe_contour = obj->m_chart_context->safety_contour;
+ if (fabs(valdco - next_safe_contour) < 1e-4) safe = true;
+ }
+
+ /*
+ if (valdco > safety_contour)
+ {
+ safe = FALSE;
+ if (1 == S52_state)
+ return NULL;
+ else {
+ S57_geo *geoTmp = geo;
+
+ // get area DEPARE & DRGARE that intersect this line
+ while (NULL != (geoTmp =
+ S57_nextObj(geoTmp))){ GString *drval1str = S57_getAttVal(geoTmp,
+ "DRVAL1"); double drval1 = (NULL == drval1str) ? 0.0 :
+ atof(drval1str->str);
+
+ if (NULL == drval1str) {
+ safe = TRUE;
+ break;
+ }
+
+ if (drval1 <
+ S52_getMarinerParam(S52_MAR_SAFETY_CONTOUR)) { safe = TRUE; break;
+ }
+ }
+ // debug trace
+ //if (safe) PRINTF("** DEPCN: SAFE FOUND**\n");
+ }
+
+ */
+ }
+
+ // Continuation B
+ int quapos = 0;
+ GetIntAttr(obj, "QUAPOS",
+ quapos); // QUAPOS is an E (Enumerated) type attribute
+
+ if (0 != quapos) {
+ if (2 <= quapos && quapos < 10) {
+ if (safe) {
+ wxString safeCntr = _T("LS(DASH,2,DEPSC)");
+ S57Obj tempObj;
+ LUPrec *safelup = ps52plib->S52_LUPLookup(PLAIN_BOUNDARIES, "SAFECD",
+ &tempObj, false);
+ if (safelup) safeCntr = safelup->INST;
+ rule_str = _T(";") + safeCntr;
+ } else
+ rule_str = _T(";LS(DASH,1,DEPCN)");
+ }
+ } else {
+ if (safe) {
+ wxString safeCntr = _T("LS(SOLD,2,DEPSC)");
+ S57Obj tempObj;
+ LUPrec *safelup =
+ ps52plib->S52_LUPLookup(PLAIN_BOUNDARIES, "SAFECN", &tempObj, false);
+ if (safelup) safeCntr = safelup->INST;
+ rule_str = _T(";") + safeCntr;
+ } else
+ rule_str = _T(";LS(SOLD,1,DEPCN)");
+ }
+
+ if (safe) {
+ // S57_setAtt(geo, "SCAMIN", "INFINITE");
+ // rule_str.Prepend(_T(";OP(8OD13010)")); //depcnt02 =
+ // g_string_prepend(depcnt02, ";OP(8OD13010)");
+ // Move this object to DisplayBase category
+ rzRules->obj->m_DisplayCat = DISPLAYBASE;
+ rzRules->obj->Scamin = 1e8+1; // effectively no SCAMIN
+ // rzRules->LUP->DPRI = PRIO_HAZARDS;
+
+ } else {
+ // rule_str.Prepend(_T(";OP(---33020)")); //depcnt02 =
+ // g_string_prepend(depcnt02, ";OP(---33020)");
+ }
+ // facultative in S-52
+ // if (TRUE == S52_getMarinerParam(S52_MAR_SHOW_TEXT)) {
+ // GString *sndfrm02 = _SNDFRM02(geo, depth_value);
+ // depcnt02 = g_string_append(depcnt02, sndfrm02->str);
+ // g_string_free(sndfrm02, TRUE);
+ //}
+
+ // debug
+ // PRINTF("depth= %f\n", depth_value);
+
+ // S57_unlinkObj(geo);
+
+ rule_str.Append('\037');
+
+ return strdup(rule_str.mb_str());
+
+ // return depcnt02;
+}
+
+static void *DEPVAL01(void *param) {
+ ObjRazRules *rzRules = (ObjRazRules *)param;
+ // S57Obj *obj = rzRules->obj;
+
+ printf("s52csny : DEPVAL01 ERROR no conditional symbology for: %s\n",
+ rzRules->LUP->OBCL);
+ return NULL;
+}
+
+static void *LEGLIN02(void *param) {
+ ObjRazRules *rzRules = (ObjRazRules *)param;
+ // S57Obj *obj = rzRules->obj;
+
+ printf("s52csny : LEGLIN02 ERROR no conditional symbology for: %s\n",
+ rzRules->LUP->OBCL);
+ return NULL;
+}
+
+/*
+static void *LIGHTS04A(void *param)
+{
+ ObjRazRules *rzRules = (ObjRazRules *)param;
+ S57Obj *obj = rzRules->obj;
+
+ wxString rule_str;
+
+ char col_str[2];
+ GetStringAttr(obj, "COLOUR", col_str, 1);
+
+ double height_val = 0;
+ GetDoubleAttr(obj, "HEIGHT", height_val);
+
+// if(obj->attList->Contains(wxString("HEIGHT")))
+// int uupr = 5;
+
+// Different symbology depending upon Paper or Simplified Mariner Selection
+
+
+ if(ps52plib->m_nSymbolStyle == PAPER_CHART)
+ {
+ if(col_str[0] == '3')
+ { // red
+ if(height_val)
+ rule_str = _T("SY(LIGHTS93)"); // all round
+ else
+ rule_str = _T("SY(LIGHTS01)"); // flare
+ }
+
+ else if(col_str[0] == '4') // green
+ {
+ if(height_val)
+ rule_str = _T("SY(LIGHTS92)");
+ else
+ rule_str = _T("SY(LIGHTS02)");
+ }
+
+ else // Generic,
+shows as yellow
+ {
+ if(height_val)
+ rule_str = _T("SY(LIGHTS91)");
+ else
+ rule_str = _T("SY(LIGHTS03)");
+ }
+ }
+
+ else // must be
+Simplified
+ {
+ if(col_str[0] == '3')
+ { // red
+ if(height_val)
+ rule_str = _T("SY(LIGHTS93)"); // all round
+ else
+ rule_str = _T("SY(LIGHTS01)"); // flare
+ }
+
+ else if(col_str[0] == '4') // green
+ {
+ if(height_val)
+ rule_str = _T("SY(LIGHTS92)");
+ else
+ rule_str = _T("SY(LIGHTS02)");
+ }
+
+ else // Generic,
+shows as yellow
+ {
+ if(height_val)
+ rule_str = _T("SY(LIGHTS91)");
+ else
+ rule_str = _T("SY(LIGHTS03)");
+ }
+ }
+
+
+
+ rule_str.Append('\037');
+
+ char *r = (char *)malloc(rule_str.Len() + 1);
+ strcpy(r, rule_str.mb_str());
+ return r;
+}
+
+// A simple placeholder for Conditional Symbology method LIGHTS05
+static void *LIGHTS05A(void *param)
+{
+ ObjRazRules *rzRules = (ObjRazRules *)param;
+ S57Obj *obj = rzRules->obj;
+
+ wxString rule_str;
+
+ char col_str[2];
+ GetStringAttr(obj, "COLOUR", col_str, 1);
+
+ double height_val = 0;
+ GetDoubleAttr(obj, "HEIGHT", height_val);
+
+ if(col_str[0] == '3')
+ { // red
+ rule_str = _T("SY(LIGHTS11)"); // flare
+ }
+
+ else if(col_str[0] == '4') // green
+ rule_str = _T("SY(LIGHTS12)");
+
+ else // Generic, shows as
+yellow rule_str = _T("SY(LIGHTS13)");
+
+
+
+ rule_str.Append('\037');
+
+ char *r = (char *)malloc(rule_str.Len() + 1);
+ strcpy(r, rule_str.mb_str());
+ return r;
+}
+*/
+
+static wxString _LITDSN01(S57Obj *obj);
+static void *LIGHTS06(void *param);
+
+static void *LIGHTS05(void *param)
+// Remarks: A light is one of the most complex S-57 objects. Its presentation
+// depends on whether it is a light on a floating or fixed platform, its range,
+// it's colour and so on. This conditional symbology procedure derives the
+// correct presentation from these parameters and also generates an area that
+// shows the coverage of the light.
+//
+// Notes on light sectors:
+// 1.) The radial leg-lines defining the light sectors are normally drawn to
+// only 25mm from the light to avoid clutter (see Part C). However, the mariner
+// should be able to select "full light-sector lines" and have the leg-lines
+// extended to the nominal range of the light (VALMAR).
+//
+// 2.) Part C of this procedure symbolizes the sectors at the light itself. In
+// addition, it should be possible, upon request, for the mariner to be capable
+// of identifying the colour and sector limit lines of the sectors affecting the
+// ship even if the light itself is off the display. [ed. last sentence in bold]
+
+{
+ // As transition, we use the PLIB 4.0 LIGHTS06 Procedure
+ return LIGHTS06(param);
+
+#define UNKNOWN_DOUBLE -9;
+ wxString lights05;
+
+ ObjRazRules *rzRules = (ObjRazRules *)param;
+ S57Obj *obj = rzRules->obj;
+
+ double valnmr = UNKNOWN_DOUBLE;
+ GetDoubleAttr(obj, "VALNMR", valnmr);
+
+ char catlitstr[20] = {'\0'};
+ GetStringAttr(obj, "CATLIT", catlitstr, 19);
+
+ char litvisstr[20] = {'\0'};
+ ;
+ GetStringAttr(obj, "LITVIS", litvisstr, 19);
+
+ char catlit[LISTSIZE] = {'\0'};
+ char litvis[LISTSIZE] = {'\0'};
+ char col_str[20] = {'\0'};
+
+ bool flare_at_45 = false;
+ double sectr1 = UNKNOWN_DOUBLE;
+ double sectr2 = UNKNOWN_DOUBLE;
+ double sweep = 0.;
+ char colist[LISTSIZE] = {'\0'}; // colour list
+ bool b_isflare = false;
+
+ wxString orientstr;
+
+ if (strlen(catlitstr)) {
+ _parseList(catlitstr, catlit, sizeof(colist));
+
+ // FIXME: OR vs AND/OR
+ if (strpbrk(catlit, "\010\013")) {
+ lights05.Append(_T(";SY(LIGHTS82)"));
+ goto l05_end;
+ }
+
+ if (strpbrk(catlit, "\011")) {
+ lights05.Append(_T(";SY(LIGHTS81)"));
+ goto l05_end;
+ }
+
+ /*
+ if (strpbrk(catlit, "\001\020")) {
+ orientstr = S57_getAttVal(geo, "ORIENT");
+ if (NULL != orientstr) {
+ // FIXME: create a geo object (!?) LINE of lenght VALNMR
+ // using ORIENT (from seaward) & POINT_T position
+ g_string_append(lights05, ";LS(DASH,1,CHBLK)");
+ }
+ }
+ */
+ }
+
+ // Continuation A
+
+ GetStringAttr(obj, "COLOUR", col_str, 19);
+
+ if (strlen(col_str))
+ _parseList(col_str, colist, sizeof(colist));
+ else {
+ colist[0] = '\014'; // magenta (12)
+ colist[1] = '\000';
+ }
+
+ GetDoubleAttr(obj, "SECTR1", sectr1);
+ GetDoubleAttr(obj, "SECTR2", sectr2);
+
+ if ((-9 == sectr1) || (-9 == sectr2)) {
+ // This is not a sector light
+
+ // What follows is one interpretation of the modern (3_3 +)
+ // Presentation Library CS flow chart, which I(dsr) have never seen
+ // verbatim. We will use flare light symbols for floating aids, and all
+ // round sector lights for fixed aids.
+
+ wxString ssym;
+
+ //FIXME plib
+// if (_atPtPos(obj, GetChartFloatingATONArray(rzRules),
+// false)) // Is this LIGHTS feature colocated with ...ANY...
+// // floating aid?
+// {
+// flare_at_45 = false;
+//
+// // TODO create LightArray in s57chart.
+// // Then, if another LIGHT object is colocated here, set flare_at_45
+// /* if(_atPtPos(obj, rzRules->chart->pLIGHTSArray, false)) // Is
+// this LIGHTS feature colocated with another LIGHTS?
+//
+//
+// // If the light is white, yellow, or orange, make it a
+// flare at 45 degrees if(strpbrk(colist, "\001\005\011")) flare_at_45 =
+// true;
+// */
+// ssym = _selSYcol(colist, 0, valnmr); // flare
+// b_isflare = true;
+// } else
+ {
+ ssym = _selSYcol(colist, 1, valnmr); // all round light
+ b_isflare = false;
+ }
+
+ // Is the light a directional or moire?
+ if (strpbrk(catlit, "\001\016")) {
+ if (orientstr.Len()) {
+ lights05.Append(ssym);
+ lights05.Append(orientstr);
+ lights05.Append(
+ _T(";TE('%03.0lf deg','ORIENT',3,3,3,'15110',3,1,CHBLK,23)" ));
+ } else
+ lights05.Append(_T(";SY(QUESMRK1)"));
+ } else {
+ lights05.Append(ssym);
+ if (b_isflare) {
+ if (flare_at_45)
+ lights05.Append(_T(",45)"));
+ else
+ lights05.Append(_T(",135)"));
+ }
+ }
+
+ goto l05_end;
+ }
+
+ // Continuation B --sector light
+ if (-9 == sectr1) {
+ sectr1 = 0.0;
+ sectr2 = 0.0;
+ } else
+ sweep = (sectr1 > sectr2) ? sectr2 - sectr1 + 360 : sectr2 - sectr1;
+
+ if (sweep < 1.0 || sweep == 360.0) {
+ // handle all round light
+ wxString ssym = _selSYcol(colist, 1, valnmr); // all round light
+ lights05.Append(ssym);
+
+ /*
+ if (TRUE == S52_getMarinerParam(S52_MAR_SHOW_TEXT)) {
+ GString *litdsn01 = _LITDSN01(geo);
+ if (NULL != litdsn01) {
+ g_string_append(lights05, ";TX('");
+ g_string_append(lights05, litdsn01->str);
+ g_string_append(lights05, "',3,2,3,'15110',2,0,CHBLK,23)" );
+ g_string_free(litdsn01, TRUE);
+ }
+
+ }
+ */
+ goto l05_end;
+ }
+
+ /*
+ // scan for other lights with sector overlap at this position
+ // compute light sector radius according to other sector
+ if (1 == S52_state)
+ {
+ _setPtPos(geo, SECTRLIST);
+ g_string_free(lights05, TRUE);
+ return NULL;
+ }
+ else
+ {
+ extend_arc_radius = _atPtPos(geo, SECTRLIST);
+
+ // passe value via attribs to _renderAC
+ if (extend_arc_radius)
+ // FIXME: draw radius 25 mm
+ S57_setAtt(geo, "extend_arc_radius", "Y");
+ else
+ // FIXME: draw radius 20 mm
+ S57_setAtt(geo, "extend_arc_radius", "N");
+ }
+ */
+ // setup sector
+ {
+ // Build the (opencpn private) command string like this:
+ // e.g. ";CA(OUTLW, 4,LITRD, 2, sectr1, sectr2, radius)"
+
+ double arc_radius = 20.; // mm
+ double sector_radius = 25.;
+
+ // Another non-standard extension....
+ // Sector light arc radius is scaled if the light has a reasonable
+ // VALNMR attribute
+ if (valnmr > 0) {
+ if (valnmr < 15.0)
+ arc_radius = 10.;
+ else if (valnmr < 30.0)
+ arc_radius = 15.;
+ else
+ arc_radius = 20.;
+ }
+
+ char sym[80];
+ strcpy(sym, ";CA(OUTLW, 4");
+
+ // max 1 color
+ if ('\0' == colist[1]) {
+ if (strpbrk(colist, "\003"))
+ strcat(sym, ",LITRD, 2");
+ else if (strpbrk(colist, "\004"))
+ strcat(sym, ",LITGN, 2");
+ else if (strpbrk(colist, "\001\006\013"))
+ strcat(sym, ",LITYW, 2");
+ else
+ strcat(sym, ",CHMGD, 2"); // default is magenta
+
+ } else if ('\0' == colist[2]) {
+ if (strpbrk(colist, "\001") && strpbrk(colist, "\003"))
+ strcat(sym, ",LITRD, 2");
+ else if (strpbrk(colist, "\001") && strpbrk(colist, "\004"))
+ strcat(sym, ",LITGN, 2");
+ else
+ strcat(sym, ",CHMGD, 2"); // default is magenta
+ } else
+ strcat(sym, ",CHMGD, 2"); // default is magenta
+
+ if (strlen(litvisstr)) // Obscured/faint sector?
+ {
+ _parseList(litvisstr, litvis, sizeof(litvis));
+
+ if (strpbrk(litvis, "\003\007\010")) strcpy(sym, ";CA(CHBLK, 4,CHBRN, 1");
+ }
+
+ if (sectr2 <= sectr1) sectr2 += 360;
+
+ // Sectors are defined from seaward
+ if (sectr1 > 180)
+ sectr1 -= 180;
+ else
+ sectr1 += 180;
+
+ if (sectr2 > 180)
+ sectr2 -= 180;
+ else
+ sectr2 += 180;
+
+ char arc_data[80];
+ sprintf(arc_data, ",%5.1f, %5.1f, %5.1f, %5.1f", sectr1, sectr2, arc_radius,
+ sector_radius);
+
+ strcat(sym, arc_data);
+
+ wxString ssym(sym, wxConvUTF8);
+ lights05 = ssym;
+
+ goto l05_end;
+ }
+
+l05_end:
+
+ // if( ps52plib->m_bShowLdisText )
+ {
+ // Only show Light in certain position once. Otherwise there will be
+ // clutter.
+ static double lastLat, lastLon;
+ static wxString lastDescription;
+ bool isFirstSector = true;
+
+ if (lastLat == obj->m_lat && lastLon == obj->m_lon) isFirstSector = false;
+ lastLat = obj->m_lat;
+ lastLon = obj->m_lon;
+
+ wxString litdsn01 = _LITDSN01(obj);
+
+ if (litdsn01.Len() && isFirstSector) {
+ lastDescription = litdsn01;
+ lights05.Append(_T(";TX('"));
+ lights05.Append(litdsn01);
+
+ if (flare_at_45)
+ lights05.Append(_T("',3,3,3,'15110',2,-1,CHBLK,23)" ));
+ else
+ lights05.Append(_T("',3,2,3,'15110',2,0,CHBLK,23)" ));
+ }
+
+ if (!isFirstSector && lastDescription != litdsn01) {
+ lastDescription = litdsn01;
+ lights05.Append(_T(";TX('"));
+ lights05.Append(litdsn01);
+ lights05.Append(_T("',3,2,3,'15110',2,1,CHBLK,23)" ));
+ }
+ }
+
+ lights05.Append('\037');
+
+ char *r = (char *)malloc(lights05.Len() + 1);
+ strcpy(r, lights05.mb_str());
+
+ return r;
+}
+
+static void *LIGHTS06(void *param)
+// Remarks: A light is one of the most complex S-57 objects. Its presentation
+// depends on whether it is a light on a floating or fixed platform, its range,
+// it's colour and so on. This conditional symbology procedure derives the
+// correct presentation from these parameters and also generates an area that
+// shows the coverage of the light.
+//
+// Notes on light sectors:
+// 1.) The radial leg-lines defining the light sectors are normally drawn to
+// only 25mm from the light to avoid clutter (see Part C). However, the mariner
+// should be able to select "full light-sector lines" and have the leg-lines
+// extended to the nominal range of the light (VALMAR).
+//
+// 2.) Part C of this procedure symbolizes the sectors at the light itself. In
+// addition, it should be possible, upon request, for the mariner to be capable
+// of identifying the colour and sector limit lines of the sectors affecting the
+// ship even if the light itself is off the display. [ed. last sentence in bold]
+
+{
+#define UNKNOWN_DOUBLE -9;
+ wxString lights06;
+
+ ObjRazRules *rzRules = (ObjRazRules *)param;
+ S57Obj *obj = rzRules->obj;
+
+ double valnmr = 9.0;
+ GetDoubleAttr(obj, "VALNMR", valnmr);
+
+ char catlitstr[20] = {'\0'};
+ GetStringAttr(obj, "CATLIT", catlitstr, 19);
+
+ char litvisstr[20] = {'\0'};
+ ;
+ GetStringAttr(obj, "LITVIS", litvisstr, 19);
+
+ char catlit[LISTSIZE] = {'\0'};
+ char litvis[LISTSIZE] = {'\0'};
+ char col_str[20] = {'\0'};
+
+ bool flare_at_45 = false;
+ double sectr1 = UNKNOWN_DOUBLE;
+ double sectr2 = UNKNOWN_DOUBLE;
+ double sweep = 0.;
+ char colist[LISTSIZE] = {'\0'}; // colour list
+ bool b_isflare = false;
+
+ wxString orientstr;
+
+ if (strlen(catlitstr)) {
+ _parseList(catlitstr, catlit, sizeof(colist));
+
+ if (strpbrk(catlit, "\010\013")) {
+ lights06.Append(_T(";SY(LIGHTS82)"));
+ goto l06_end;
+ }
+
+ if (strpbrk(catlit, "\011")) {
+ lights06.Append(_T(";SY(LIGHTS81)"));
+ goto l06_end;
+ }
+
+ /*
+ * if (strpbrk(catlit, "\001\020")) {
+ * orientstr = S57_getAttVal(geo, "ORIENT");
+ * if (NULL != orientstr) {
+ * // FIXME: create a geo object (!?) LINE of lenght VALNMR
+ * // using ORIENT (from seaward) & POINT_T position
+ * g_string_append(lights05, ";LS(DASH,1,CHBLK)");
+ }
+ }
+ */
+ }
+
+ // Continuation A
+
+ GetStringAttr(obj, "COLOUR", col_str, 19);
+
+ if (strlen(col_str))
+ _parseList(col_str, colist, sizeof(colist));
+ else {
+ colist[0] = '\014'; // magenta (12)
+ colist[1] = '\000';
+ }
+
+ GetDoubleAttr(obj, "SECTR1", sectr1);
+ GetDoubleAttr(obj, "SECTR2", sectr2);
+
+ if ((-9 == sectr1) || (-9 == sectr2)) {
+ // This is not a sector light
+
+ wxString ssym;
+
+ if (valnmr < 10.0) {
+ // TODO create LightArray in s57chart.
+ // Then, if another LIGHT object is colocated here, set flare_at_45
+ /* if(_atPtPos(obj, rzRules->chart->pLIGHTSArray, false)) // Is
+ * this LIGHTS feature colocated with another LIGHTS?
+ *
+ *
+ * // If the light is white, yellow, or orange, make it a
+ * flare at 45 degrees if(strpbrk(colist, "\001\005\011")) flare_at_45 =
+ * true;
+ */
+ ssym = _selSYcol(colist, 0, valnmr); // flare
+ b_isflare = true;
+ flare_at_45 = false;
+ } else {
+ ssym = _selSYcol(colist, 1, valnmr); // all round light
+ b_isflare = false;
+ }
+
+ // Is the light a directional or moire?
+ if (strpbrk(catlit, "\001\016")) {
+ if (orientstr.Len()) {
+ lights06.Append(ssym);
+ lights06.Append(orientstr);
+ lights06.Append(
+ _T(";TE('%03.0lf deg','ORIENT',3,3,3,'15110',3,1,CHBLK,23)" ));
+ } else
+ lights06.Append(_T(";SY(QUESMRK1)"));
+ } else {
+ lights06.Append(ssym);
+
+ if (b_isflare) {
+ if (flare_at_45)
+ lights06.Append(_T(",45)"));
+ else
+ lights06.Append(_T(",135)"));
+ }
+ }
+
+ goto l06_end;
+ }
+
+ // Continuation B --sector light
+ if (-9 == sectr1) {
+ sectr1 = 0.0;
+ sectr2 = 0.0;
+ } else
+ sweep = (sectr1 > sectr2) ? sectr2 - sectr1 + 360 : sectr2 - sectr1;
+
+ if (sweep < 1.0 || sweep == 360.0) {
+ // handle all round light
+ wxString ssym = _selSYcol(colist, 1, valnmr); // all round light
+ lights06.Append(ssym);
+
+ /*
+ * if (TRUE == S52_getMarinerParam(S52_MAR_SHOW_TEXT)) {
+ * GString *litdsn01 = _LITDSN01(geo);
+ * if (NULL != litdsn01) {
+ * g_string_append(lights05, ";TX('");
+ * g_string_append(lights05, litdsn01->str);
+ * g_string_append(lights05, "',3,2,3,'15110',2,0,CHBLK,23)"
+ );
+ * g_string_free(litdsn01, TRUE);
+ }
+
+ }
+ */
+ goto l06_end;
+ }
+
+ /*
+ * // scan for other lights with sector overlap at this position
+ * // compute light sector radius according to other sector
+ * if (1 == S52_state)
+ * {
+ * _setPtPos(geo, SECTRLIST);
+ * g_string_free(lights05, TRUE);
+ * return NULL;
+ }
+ else
+ {
+ extend_arc_radius = _atPtPos(geo, SECTRLIST);
+
+ // passe value via attribs to _renderAC
+ if (extend_arc_radius)
+ // FIXME: draw radius 25 mm
+ S57_setAtt(geo, "extend_arc_radius", "Y");
+ else
+ // FIXME: draw radius 20 mm
+ S57_setAtt(geo, "extend_arc_radius", "N");
+ }
+ */
+ // setup sector
+ {
+ // Build the (opencpn private) command string like this:
+ // e.g. ";CA(OUTLW, 4,LITRD, 2, sectr1, sectr2, radius)"
+
+ double arc_radius = 20.; // mm
+ double sector_radius = 25.;
+
+ // Another non-standard extension....
+ // Sector light arc radius is scaled if the light has a reasonable
+ // VALNMR attribute
+ if (valnmr > 0) {
+ if (valnmr < 15.0)
+ arc_radius = 10.;
+ else if (valnmr < 30.0)
+ arc_radius = 15.;
+ else
+ arc_radius = 20.;
+ }
+
+ char sym[80];
+ strcpy(sym, ";CA(OUTLW, 4");
+
+ // max 1 color
+ if ('\0' == colist[1]) {
+ if (strpbrk(colist, "\003"))
+ strcat(sym, ",LITRD, 2");
+ else if (strpbrk(colist, "\004"))
+ strcat(sym, ",LITGN, 2");
+ else if (strpbrk(colist, "\001\006\013"))
+ strcat(sym, ",LITYW, 2");
+ else
+ strcat(sym, ",CHMGD, 2"); // default is magenta
+
+ } else if ('\0' == colist[2]) {
+ if (strpbrk(colist, "\001") && strpbrk(colist, "\003"))
+ strcat(sym, ",LITRD, 2");
+ else if (strpbrk(colist, "\001") && strpbrk(colist, "\004"))
+ strcat(sym, ",LITGN, 2");
+ else
+ strcat(sym, ",CHMGD, 2"); // default is magenta
+ } else
+ strcat(sym, ",CHMGD, 2"); // default is magenta
+
+ if (strlen(litvisstr)) // Obscured/faint sector?
+ {
+ _parseList(litvisstr, litvis, sizeof(litvis));
+
+ if (strpbrk(litvis, "\003\007\010")) strcpy(sym, ";CA(CHBLK, 4,CHBRN, 1");
+ }
+
+ if (sectr2 <= sectr1) sectr2 += 360;
+
+ // Sectors are defined from seaward
+ if (sectr1 > 180)
+ sectr1 -= 180;
+ else
+ sectr1 += 180;
+
+ if (sectr2 > 180)
+ sectr2 -= 180;
+ else
+ sectr2 += 180;
+
+ char arc_data[80];
+ sprintf(arc_data, ",%5.1f, %5.1f, %5.1f, %5.1f", sectr1, sectr2, arc_radius,
+ sector_radius);
+
+ strcat(sym, arc_data);
+
+ wxString ssym(sym, wxConvUTF8);
+ lights06 = ssym;
+
+ goto l06_end;
+ }
+
+l06_end:
+
+ // if( ps52plib->m_bShowLdisText )
+ {
+ // Only show Light in certain position once. Otherwise there will be
+ // clutter.
+ static double lastLat, lastLon;
+ static wxString lastDescription;
+ bool isFirstSector = true;
+
+ if (lastLat == obj->m_lat && lastLon == obj->m_lon) isFirstSector = false;
+ lastLat = obj->m_lat;
+ lastLon = obj->m_lon;
+
+ wxString litdsn01 = _LITDSN01(obj);
+
+ if (litdsn01.Len() && isFirstSector) {
+ lastDescription = litdsn01;
+ lights06.Append(_T(";TX('"));
+ lights06.Append(litdsn01);
+
+ if (flare_at_45)
+ lights06.Append(_T("',3,3,3,'15110',2,-1,CHBLK,23)" ));
+ else
+ lights06.Append(_T("',3,2,3,'15110',2,0,CHBLK,23)" ));
+ }
+
+ if (!isFirstSector && lastDescription != litdsn01) {
+ lastDescription = litdsn01;
+ lights06.Append(_T(";TX('"));
+ lights06.Append(litdsn01);
+ lights06.Append(_T("',3,2,3,'15110',2,1,CHBLK,23)" ));
+ }
+ }
+
+ lights06.Append('\037');
+
+ char *r = (char *)malloc(lights06.Len() + 1);
+ strcpy(r, lights06.mb_str());
+
+ return r;
+}
+
+static void *LITDSN01(void *param) {
+ ObjRazRules *rzRules = (ObjRazRules *)param;
+ // S57Obj *obj = rzRules->obj;
+
+ printf("s52csny : LITDSN01 ERROR no conditional symbology for: %s\n",
+ rzRules->LUP->OBCL);
+ return NULL;
+}
+
+/*
+static void *OBSTRN04a(void *param)
+{
+ ObjRazRules *rzRules = (ObjRazRules *)param;
+// S57Obj *obj = rzRules->obj;
+
+ static int f03;
+ if(!f03)
+ printf("s52csny : OBSTRN04 ERROR no conditional symbology for:
+%s\n",rzRules->LUP->OBCL); f03++; return NULL;
+}
+*/
+
+wxString SNDFRM02(S57Obj *obj, double depth_value);
+
+static void *OBSTRN04(void *param)
+// Remarks: Obstructions or isolated underwater dangers of depths less than the
+// safety contour which lie within the safe waters defined by the safety contour
+// are to be presented by a specific isolated danger symbol and put in IMO
+// category DISPLAYBASE (see (3), App.2, 1.3). This task is performed
+// by the sub-procedure "UDWHAZ03" which is called by this symbology
+// procedure. Objects of the class "under water rock" are handled by this
+// routine as well to ensure a consistent symbolization of isolated dangers on
+// the seabed.
+{
+ wxString obstrn04str;
+ // GString *sndfrm02str = NULL;
+ wxString *udwhaz03str = NULL;
+ // GString *valsoustr = S57_getAttVal(geo, "VALSOU");
+ bool b_promote = false;
+
+ ObjRazRules *rzRules = (ObjRazRules *)param;
+ S57Obj *obj = rzRules->obj;
+
+ // TODO Debug Hook
+ // if(obj->Index == 534)
+ // int yyp = 5;
+
+ double valsou = UNKNOWN;
+ double depth_value = UNKNOWN;
+ double least_depth = UNKNOWN;
+
+ wxString sndfrm02str;
+ wxString *quapnt01str = NULL;
+
+ GetDoubleAttr(obj, "VALSOU", valsou);
+ wxString *objName = GetStringAttrWXS(obj, "OBJNAM");
+
+ if (valsou != UNKNOWN) {
+ depth_value = valsou;
+ sndfrm02str = SNDFRM02(obj, valsou);
+ } else {
+ if (GEO_AREA == obj->Primitive_type)
+ least_depth = _DEPVAL01(obj, least_depth);
+
+ if (UNKNOWN == least_depth) {
+ int catobs = 0;
+ GetIntAttr(obj, "CATOBS", catobs);
+ int watlev = 0;
+ GetIntAttr(obj, "WATLEV", watlev);
+ int expsou = 0;
+ GetIntAttr(obj, "EXPSOU", expsou);
+ if (expsou != 1) {
+ if (6 == catobs)
+ depth_value = 0.01;
+ else if (0 == watlev) // default
+ depth_value = -15.0;
+ else {
+ switch (watlev) {
+ case 5:
+ depth_value = 0.0;
+ break;
+ case 3:
+ depth_value = 0.01;
+ break;
+ case 4:
+ case 1:
+ case 2:
+ default:
+ depth_value = -15.0;
+ break;
+ }
+ }
+ }
+ } else
+ depth_value = least_depth;
+ }
+
+ udwhaz03str = _UDWHAZ03(obj, depth_value, rzRules, &b_promote);
+
+ if (GEO_POINT == obj->Primitive_type) {
+ // Continuation A
+ int sounding = FALSE;
+ quapnt01str = CSQUAPNT01(obj);
+
+ if (0 != udwhaz03str->Len()) {
+ obstrn04str.Append(*udwhaz03str);
+ obstrn04str.Append(*quapnt01str);
+
+ goto end;
+ }
+
+ if (UNKNOWN != valsou) {
+ if (valsou <= 20.0) {
+ int watlev = -9;
+ GetIntAttr(obj, "WATLEV", watlev);
+
+ if (!strncmp(obj->FeatureName, "UWTROC", 6)) {
+ if (-9 == watlev) { // default
+ obstrn04str.Append(_T(";SY(DANGER51)"));
+ sounding = TRUE;
+ } else {
+ switch (watlev) {
+ case 3:
+ obstrn04str.Append(_T(";SY(DANGER51)"));
+ sounding = TRUE;
+ break;
+ case 4:
+ case 5:
+ obstrn04str.Append(_T(";SY(UWTROC04)"));
+ sounding = FALSE;
+ break;
+ default:
+ obstrn04str.Append(_T(";SY(DANGER51)"));
+ sounding = TRUE;
+ break;
+ }
+ }
+ if (b_promote) {
+ // Move this UWTROC object to DisplayBase category
+ rzRules->obj->m_DisplayCat = DISPLAYBASE;
+ }
+ } else { // OBSTRN
+ if (-9 == watlev) { // default
+ obstrn04str.Append(_T(";SY(DANGER01)"));
+ sounding = TRUE;
+ } else {
+ switch (watlev) {
+ case 1:
+ case 2:
+ obstrn04str.Append(_T(";SY(LNDARE01)"));
+ sounding = FALSE;
+ break;
+ case 3:
+ obstrn04str.Append(_T(";SY(DANGER52)"));
+ sounding = TRUE;
+ break;
+ case 4:
+ case 5:
+ obstrn04str.Append(_T(";SY(DANGER53)"));
+ sounding = TRUE;
+ break;
+ default:
+ obstrn04str.Append(_T(";SY(DANGER51)"));
+ sounding = TRUE;
+ break;
+ }
+ }
+ }
+ } else { // valsou > 20.0
+ obstrn04str.Append(_T(";SY(DANGER52)"));
+ sounding = TRUE;
+ }
+ } else { // NO valsou
+ // GString *objlstr = S57_getAttVal(geo, "OBJL");
+ // int objl = (NULL == objlstr)? 0 :
+ // atoi(objlstr->str);
+ int watlev = -9;
+ GetIntAttr(obj, "WATLEV", watlev);
+ // GString *watlevstr = S57_getAttVal(geo, "WATLEV");
+
+ if (!strncmp(obj->FeatureName, "UWTROC", 6)) {
+ if (watlev == -9) // default
+ obstrn04str.Append(_T(";SY(UWTROC04)"));
+ else {
+ switch (watlev) {
+ case 2:
+ obstrn04str.Append(_T(";SY(LNDARE01)"));
+ break;
+ case 3:
+ obstrn04str.Append(_T(";SY(UWTROC03)"));
+ break;
+ default:
+ obstrn04str.Append(_T(";SY(UWTROC04)"));
+ break;
+ }
+ }
+
+ if (b_promote) {
+ // Move this UWTROC object to DisplayBase category
+ rzRules->obj->m_DisplayCat = DISPLAYBASE;
+ }
+ } else { // OBSTRN
+ if (-9 == watlev) // default
+ obstrn04str = _T(";SY(OBSTRN01)");
+ else {
+ switch (watlev) {
+ case 1:
+ obstrn04str.Append(_T(";SY(OBSTRN11)"));
+ break;
+ case 2:
+ obstrn04str.Append(_T(";SY(OBSTRN11)"));
+ break;
+ case 3:
+ obstrn04str.Append(_T(";SY(OBSTRN01)"));
+ break;
+ case 4:
+ obstrn04str.Append(_T(";SY(OBSTRN03)"));
+ break;
+ case 5:
+ obstrn04str.Append(_T(";SY(OBSTRN03)"));
+ break;
+ default:
+ obstrn04str.Append(_T(";SY(OBSTRN01)"));
+ break;
+ }
+ }
+ }
+ }
+
+ if (sounding) obstrn04str.Append(sndfrm02str);
+
+ obstrn04str.Append(*quapnt01str);
+
+ goto end;
+
+ } // if geopoint
+ else {
+ if (GEO_LINE == obj->Primitive_type) {
+ // Continuation B
+
+ quapnt01str = CSQUAPNT01(obj);
+
+ if (quapnt01str->Len() > 1) {
+ long quapos;
+ quapnt01str->ToLong(&quapos);
+ if (2 <= quapos && quapos < 10) {
+ if (udwhaz03str->Len())
+ obstrn04str.Append(_T(";LC(LOWACC41)"));
+ else
+ obstrn04str.Append(_T(";LC(LOWACC31)"));
+ }
+ goto end;
+ }
+
+ if (udwhaz03str->Len()) {
+ obstrn04str.Append(_T("LS(DOTT,2,CHBLK)"));
+ goto end;
+ }
+
+ if (UNKNOWN != valsou) {
+ if (valsou <= 20.0)
+ obstrn04str.Append(_T(";LS(DOTT,2,CHBLK)"));
+ else
+ obstrn04str.Append(_T(";LS(DASH,2,CHBLK)"));
+ } else
+ obstrn04str.Append(_T(";LS(DOTT,2,CHBLK)"));
+
+ if (udwhaz03str->Len()) {
+ // Show the isolated danger symbol at the midpoint of the line
+ } else {
+ if (UNKNOWN != valsou)
+ if (valsou <= 20.0) obstrn04str.Append(sndfrm02str);
+ }
+ }
+
+ else // Area feature
+ {
+ quapnt01str = CSQUAPNT01(obj);
+
+ if (0 != udwhaz03str->Len()) {
+ obstrn04str.Append(_T(";AC(DEPVS);AP(FOULAR01)"));
+ obstrn04str.Append(_T(";LS(DOTT,2,CHBLK)"));
+ obstrn04str.Append(*udwhaz03str);
+ obstrn04str.Append(*quapnt01str);
+
+ goto end;
+ }
+
+ if (UNKNOWN != valsou) {
+ // BUG in CA49995B.000 if we get here because there is no color
+ // beside NODATA (ie there is a hole in group 1 area!)
+ // g_string_append(obstrn04, ";AC(UINFR)");
+
+ if (valsou <= 20.0)
+ obstrn04str.Append(_T(";LS(DOTT,2,CHBLK)"));
+ else
+ obstrn04str.Append(_T(";LS(DASH,2,CHBLK)"));
+
+ obstrn04str.Append(sndfrm02str);
+
+ } else {
+ int watlev = -9;
+ GetIntAttr(obj, "WATLEV", watlev);
+ // GString *watlevstr = S57_getAttVal(geo,
+ // "WATLEV");
+
+ if (watlev == -9) // default
+ obstrn04str.Append(_T(";AC(DEPVS);LS(DOTT,2,CHBLK)"));
+ else {
+ switch (watlev) {
+ case 1:
+ case 2:
+ obstrn04str.Append(_T(";AC(CHBRN);LS(SOLD,2,CSTLN)"));
+ break;
+ case 4:
+ obstrn04str.Append(_T(";AC(DEPIT);LS(DASH,2,CSTLN)"));
+ break;
+ case 5:
+ case 3: {
+ int catobs = -9;
+ GetIntAttr(obj, "CATOBS", catobs);
+ if (6 == catobs)
+ obstrn04str.Append(
+ _T(";AC(DEPVS);AP(FOULAR01);LS(DOTT,2,CHBLK)"));
+ else
+ obstrn04str.Append(_T(";AC(DEPVS);LS(DOTT,2,CHBLK)"));
+ } break;
+ default:
+ obstrn04str.Append(_T(";AC(DEPVS);LS(DOTT,2,CHBLK)"));
+ break;
+ }
+ }
+ }
+ obstrn04str.Append(*quapnt01str);
+ goto end;
+
+ /*
+ // Continuation C (AREAS_T)
+ GString *quapnt01str = CSQUAPNT01(geo);
+ if (NULL != udwhaz03str) {
+ g_string_append(obstrn04str,
+ ";AC(DEPVS);AP(FOULAR01)"); g_string_append(obstrn04str,
+ ";LS(DOTT,2,CHBLK)"); g_string_append(obstrn04str, udwhaz03str->str);
+ if (NULL != quapnt01str)
+ g_string_append(obstrn04str,
+ quapnt01str->str);
+
+ return obstrn04str;
+ }
+
+ if (UNKNOWN != valsou) {
+ // BUG in CA49995B.000 if we get here because there is no
+ color
+ // beside NODATA (ie there is a hole in group 1 area!)
+ //g_string_append(obstrn04, ";AC(UINFR)");
+
+ if (valsou <= 20.0)
+ g_string_append(obstrn04str,
+ ";LS(DOTT,2,CHBLK)"); else g_string_append(obstrn04str,
+ ";LS(DASH,2,CHBLK)");
+
+ g_string_append(obstrn04str, sndfrm02str->str);
+
+ } else {
+ GString *watlevstr = S57_getAttVal(geo, "WATLEV");
+
+ if (NULL == watlevstr) // default
+ g_string_append(obstrn04str,
+ ";AC(DEPVS);LS(DOTT,2,CHBLK)"); else { if ('3' == *watlevstr->str) {
+ GString *catobsstr =
+ S57_getAttVal(geo, "CATOBS"); if (NULL != catobsstr && '6' ==
+ *catobsstr->str) g_string_append(obstrn04str,
+ ";AC(DEPVS);AP(FOULAR01);LS(DOTT,2,CHBLK)"); } else { switch
+ (*watlevstr->str) { case '1': case '2': g_string_append(obstrn04str,
+ ";AC(CHBRN);LS(SOLD,2,CSTLN)"); break; case '4':
+ g_string_append(obstrn04str, ";AC(DEPIT);LS(DASH,2,CSTLN)"); break;
+ case '5':
+ case '3':
+ default :
+ g_string_append(obstrn04str, ";AC(DEPVS);LS(DOTT,2,CHBLK)"); break;
+ }
+ }
+ }
+ }
+
+ g_string_append(obstrn04str, quapnt01str->str);
+
+ return obstrn04str;
+ */
+ } // area
+ }
+
+end:
+
+ // This is a specialization, to print OBJNAM for obstructions, if available
+ // Seen in NZ ENCs, e.g. "Horn Rock"
+ if (objName) {
+ obstrn04str.Append(_T(";TX(OBJNAM,1,2,3,'15118',-1,-1,CHBLK,26)"));
+ delete objName;
+ }
+
+ obstrn04str.Append('\037');
+
+ char *r = (char *)malloc(obstrn04str.Len() + 1);
+ strcpy(r, obstrn04str.mb_str());
+
+ delete udwhaz03str;
+ delete quapnt01str;
+
+ return r;
+}
+
+static void *OWNSHP02(void *param) {
+ ObjRazRules *rzRules = (ObjRazRules *)param;
+ // S57Obj *obj = rzRules->obj;
+
+ printf("s52csny : OWNSHP02 ERROR no conditional symbology for: %s\n",
+ rzRules->LUP->OBCL);
+ return NULL;
+}
+
+static void *PASTRK01(void *param) {
+ ObjRazRules *rzRules = (ObjRazRules *)param;
+ // S57Obj *obj = rzRules->obj;
+
+ printf("s52csny : PASTRK01 ERROR no conditional symbology for: %s\n",
+ rzRules->LUP->OBCL);
+ return NULL;
+}
+
+static void *QUALIN01(void *param);
+static void *QUAPNT01(void *param);
+
+static void *QUAPOS01(void *param)
+// Remarks: The attribute QUAPOS, which identifies low positional accuracy, is
+// attached to the spatial object, not the feature object. In OpenCPN
+// implementation, QUAPOS of Point Objects has been converted to QUALTY
+// attribute of object.
+//
+// This procedure passes the object to procedure QUALIN01 or QUAPNT01,
+// which traces back to the spatial object, retrieves any QUAPOS attributes,
+// and returns the appropriate symbolization to QUAPOS01.
+{
+ ObjRazRules *rzRules = (ObjRazRules *)param;
+ S57Obj *obj = rzRules->obj;
+
+ wxString *q = NULL;
+
+ if (GEO_LINE == obj->Primitive_type)
+ q = CSQUALIN01(obj);
+
+ else
+ q = CSQUAPNT01(obj);
+
+ char *r = (char *)malloc(q->Len() + 1);
+ strcpy(r, q->mb_str());
+
+ delete q;
+
+ return r;
+}
+
+static void *QUALIN01(void *param)
+// Remarks: The attribute QUAPOS, which identifies low positional accuracy, is
+// attached only to the spatial component(s) of an object.
+//
+// A line object may be composed of more than one spatial object.
+//
+// This procedure looks at each of the spatial
+// objects, and symbolizes the line according to the positional accuracy.
+{
+ ObjRazRules *rzRules = (ObjRazRules *)param;
+ S57Obj *obj = rzRules->obj;
+
+ wxString *q = CSQUALIN01(obj);
+ char *r = (char *)malloc(q->Len() + 1);
+ strcpy(r, q->mb_str());
+
+ delete q;
+ return r;
+}
+
+wxString *CSQUALIN01(S57Obj *obj)
+// Remarks: The attribute QUAPOS, which identifies low positional accuracy, is
+// attached only to the spatial component(s) of an object.
+//
+// A line object may be composed of more than one spatial object.
+//
+// This procedure looks at each of the spatial
+// objects, and symbolizes the line according to the positional accuracy.
+{
+ wxString qualino1;
+ int quapos = 0;
+ bool bquapos = GetIntAttr(obj, "QUAPOS", quapos);
+ const char *line = NULL;
+
+ if (bquapos) {
+ if (2 <= quapos && quapos < 10) line = "LC(LOWACC21)";
+ } else {
+ if (!strncmp("COALNE", obj->FeatureName, 6)) {
+ int conrad;
+ bool bconrad = GetIntAttr(obj, "CONRAD", conrad);
+
+ if (bconrad) {
+ if (1 == conrad)
+ line = "LS(SOLD,3,CHMGF);LS(SOLD,1,CSTLN)";
+ else
+ line = "LS(SOLD,1,CSTLN)";
+ } else
+ line = "LS(SOLD,1,CSTLN)";
+
+ } else // LNDARE
+ line = "LS(SOLD,1,CSTLN)";
+ }
+
+ if (NULL != line) qualino1.Append(wxString(line, wxConvUTF8));
+
+ qualino1.Append('\037');
+
+ wxString *r = new wxString(qualino1);
+
+ /* char *r = (char *)malloc(qualino1.Len() + 1);
+ strcpy(r, qualino1.mb_str());
+ */
+ return r;
+}
+
+static void *QUAPNT01(void *param)
+// Remarks: The attribute QUAPOS, which identifies low positional accuracy, is
+// attached only to the spatial component(s) of an object.
+//
+// This procedure retrieves any QUALTY (ne QUAPOS) attributes, and returns the
+// appropriate symbols to the calling procedure.
+
+{
+ ObjRazRules *rzRules = (ObjRazRules *)param;
+ S57Obj *obj = rzRules->obj;
+
+ wxString *q = CSQUAPNT01(obj);
+
+ char *r = (char *)malloc(q->Len() + 1);
+ strcpy(r, q->mb_str());
+
+ return r;
+}
+
+wxString *CSQUAPNT01(S57Obj *obj)
+// Remarks: The attribute QUAPOS, which identifies low positional accuracy, is
+// attached only to the spatial component(s) of an object.
+//
+// This procedure retrieves any QUALTY (ne QUAPOS) attributes, and returns the
+// appropriate symbols to the calling procedure.
+
+{
+ wxString quapnt01;
+ int accurate = TRUE;
+ int qualty = 10;
+ bool bquapos = GetIntAttr(obj, "QUAPOS", qualty);
+
+ if (bquapos) {
+ if (2 <= qualty && qualty < 10) accurate = FALSE;
+ }
+
+ if (!accurate) {
+ switch (qualty) {
+ case 4:
+ quapnt01.Append(_T(";SY(QUAPOS01)"));
+ break; // "PA"
+ case 5:
+ quapnt01.Append(_T(";SY(QUAPOS02)"));
+ break; // "PD"
+ case 7:
+ case 8:
+ quapnt01.Append(_T(";SY(QUAPOS03)"));
+ break; // "REP"
+ default:
+ quapnt01.Append(_T(";SY(LOWACC03)"));
+ break; // "?"
+ }
+ }
+
+ quapnt01.Append('\037');
+
+ wxString *r = new wxString;
+
+ *r = quapnt01;
+
+ /* char *r = (char *)malloc(quapnt01.Len() + 1);
+ strcpy(r, quapnt01.mb_str());
+ */
+ return r;
+}
+
+static void *SLCONS03(void *param)
+
+// Remarks: Shoreline construction objects which have a QUAPOS attribute on
+// their
+// spatial component indicating that their position is unreliable are symbolized
+// by a special linestyle in the place of the varied linestyles normally used.
+// Otherwise this procedure applies the normal symbolization.
+{
+ ObjRazRules *rzRules = (ObjRazRules *)param;
+ S57Obj *obj = rzRules->obj;
+
+ wxString slcons03;
+
+ bool bvalstr;
+ int ival;
+
+ const char *cmdw = NULL; // command word
+
+ int quapos;
+ bool bquapos = GetIntAttr(obj, "QUAPOS", quapos);
+
+ if (GEO_POINT == obj->Primitive_type) {
+ if (bquapos) {
+ if (2 <= quapos && quapos < 10) cmdw = "SY(LOWACC01)";
+ }
+ } else {
+ // This instruction not found in PLIB 3.4, but seems to appear in later PLIB
+ // implementations by commercial ECDIS providers, so.....
+ if (GEO_AREA == obj->Primitive_type) {
+ slcons03 = _T("AP(CROSSX01);");
+ }
+
+ // GEO_LINE and GEO_AREA are the same
+ if (bquapos) {
+ if (2 <= quapos && quapos < 10) cmdw = "LC(LOWACC01)";
+ } else {
+ bvalstr = GetIntAttr(obj, "CONDTN", ival);
+
+ if (bvalstr && (1 == ival || 2 == ival))
+ cmdw = "LS(DASH,1,CSTLN)";
+ else {
+ ival = 0;
+ bvalstr = GetIntAttr(obj, "CATSLC", ival);
+
+ if (bvalstr &&
+ (6 == ival || 15 == ival || 16 == ival)) // Some sort of wharf
+ cmdw = "LS(SOLD,4,CSTLN)";
+ else {
+ bvalstr = GetIntAttr(obj, "WATLEV", ival);
+
+ if (bvalstr && 2 == ival)
+ cmdw = "LS(SOLD,2,CSTLN)";
+ else if (bvalstr && (3 == ival || 4 == ival))
+ cmdw = "LS(DASH,2,CSTLN)";
+ else
+ cmdw = "LS(SOLD,2,CSTLN)"; // default
+ }
+ }
+ }
+ }
+
+ // WARNING: not explicitly specified in S-52 !!
+ // FIXME:this is to put AC(DEPIT) --intertidal area
+ // Could this be bug in OGR ?
+ /*
+ if (AREAS_T == S57_getObjtype(geo)) {
+ GString *seabed01 = NULL;
+ GString *drval1str = S57_getAttVal(geo, "DRVAL1");
+ double drval1 = (NULL == drval1str)? -UNKNOWN :
+ atof(drval1str->str); GString *drval2str = S57_getAttVal(geo, "DRVAL2");
+ double drval2 = (NULL == drval2str)? -UNKNOWN :
+ atof(drval2str->str);
+ // NOTE: change sign of infinity (minus) to get out of bound in seabed01
+
+
+ PRINTF("***********drval1=%f drval2=%f \n", drval1, drval2);
+ seabed01 = _SEABED01(drval1, drval2);
+ slcons03 = g_string_new(seabed01->str);
+ g_string_free(seabed01, TRUE);
+
+ }
+ */
+
+ if (NULL != cmdw) slcons03.Append(wxString(cmdw, wxConvUTF8));
+
+ // Match CM93 CMAPECS presentation?
+ /*
+ if (GEO_AREA == obj->Primitive_type)
+ slcons03.Append(_T(";AC(LANDA)"));
+ */
+
+ slcons03.Append('\037');
+
+ char *r = (char *)malloc(slcons03.Len() + 1);
+ strcpy(r, slcons03.mb_str());
+
+ return r;
+}
+
+static void *RESARE02(void *param)
+// Remarks: A list-type attribute is used because an area of the object class
+// RESARE may have more than one category (CATREA). For example an inshore
+// traffic zone might also have fishing and anchoring prohibition and a
+// prohibited area might also be a bird sanctuary or a mine field.
+//
+// This conditional procedure is set up to ensure that the categories of most
+// importance to safe navigation are prominently symbolized, and to pass on
+// all given information with minimum clutter. Only the most significant
+// restriction is symbolized, and an indication of further limitations is given
+// by a subscript "!" or "I". Further details are given under conditional
+// symbology procedure RESTRN01
+//
+// Other object classes affected by attribute RESTRN are handled by
+// conditional symbology procedure RESTRN01.
+{
+ ObjRazRules *rzRules = (ObjRazRules *)param;
+ S57Obj *obj = rzRules->obj;
+
+ wxString resare02;
+
+ wxString *restrnstr = GetStringAttrWXS(obj, "RESTRN");
+ // GString *restrnstr = S57_getAttVal(geo, "RESTRN");
+
+ char restrn[LISTSIZE] = {'\0'};
+ // GString *catreastr = S57_getAttVal(geo, "CATREA");
+ wxString *catreastr = GetStringAttrWXS(obj, "CATREA");
+
+ char catrea[LISTSIZE] = {'\0'};
+ wxString symb;
+ wxString line;
+ wxString prio;
+
+ if (NULL != catreastr)
+ _parseList(catreastr->mb_str(), catrea, sizeof(catrea));
+
+ if (NULL != restrnstr) {
+ _parseList(restrnstr->mb_str(), restrn, sizeof(restrn));
+
+ if (strpbrk(restrn, "\007\010\016")) { // entry restrictions
+ // Continuation A
+ if (strpbrk(restrn,
+ "\001\002\003\004\005\006")) // anchoring, fishing, trawling
+ symb = _T(";SY(ENTRES61)");
+ else {
+ if (NULL != catreastr &&
+ strpbrk(catrea, "\001\010\011\014\016\023\025\031"))
+ symb = _T(";SY(ENTRES61)");
+ else {
+ if (strpbrk(restrn, "\011\012\013\014\015"))
+ symb = _T(";SY(ENTRES71)");
+ else {
+ if (NULL != catreastr &&
+ strpbrk(catrea, "\004\005\006\007\012\022\024\026\027\030"))
+ symb = _T(";SY(ENTRES71)");
+ else
+ symb = _T(";SY(ENTRES51)");
+ }
+ }
+ }
+
+ if (TRUE == S52_getMarinerParam(S52_MAR_SYMBOLIZED_BND))
+ line = _T(";LC(RESARE51)");
+ else
+ line = _T( ";LS(DASH,2,CHMGD)");
+
+ prio = _T(";OP(6---)"); // display prio set to 6
+
+ } else {
+ if (strpbrk(restrn, "\001\002")) { // anchoring
+ // Continuation B
+ if (strpbrk(restrn, "\003\004\005\006"))
+ symb = _T(";SY(ACHRES61)");
+ else {
+ if (NULL != catreastr &&
+ strpbrk(catrea, "\001\010\011\014\016\023\025\031"))
+ symb = _T(";SY(ACHRES61)");
+ else {
+ if (strpbrk(restrn, "\011\012\013\014\015"))
+ symb = _T(";SY(ACHRES71)");
+ else {
+ if (NULL != catreastr &&
+ strpbrk(catrea, "\004\005\006\007\012\022\024\026\027\030"))
+ symb = _T( ";SY(ACHRES71)");
+ else
+ symb = _T(";SY(RESTRN51)");
+ }
+ }
+ }
+
+ if (TRUE == S52_getMarinerParam(S52_MAR_SYMBOLIZED_BND))
+ line = _T(";LC(RESARE51)"); // could be ACHRES51 when _drawLC is
+ // implemented fully
+ else
+ line = _T(";LS(DASH,2,CHMGD)");
+
+ prio = _T(";OP(6---)"); // display prio set to 6
+
+ } else {
+ if (strpbrk(restrn, "\003\004\005\006")) { // fishing/trawling
+ // Continuation C
+ if (NULL != catreastr &&
+ strpbrk(catrea, "\001\010\011\014\016\023\025\031"))
+ symb = _T(";SY(FSHRES51)");
+ else {
+ if (strpbrk(restrn, "\011\012\013\014\015"))
+ symb = _T(";SY(FSHRES71)");
+ else {
+ if (NULL != catreastr &&
+ strpbrk(catrea, "\004\005\006\007\012\022\024\026\027\030"))
+ symb = _T(";SY(FSHRES71)");
+ else
+ symb = _T(";SY(FSHRES51)");
+ }
+ }
+
+ if (TRUE == S52_getMarinerParam(S52_MAR_SYMBOLIZED_BND))
+ line = _T(";LC(FSHRES51)");
+ else
+ line = _T(";LS(DASH,2,CHMGD)");
+
+ prio = _T(";OP(6---)"); // display prio set to 6
+
+ } else {
+ if (strpbrk(restrn,
+ "\011\012\013\014\015")) // diving, dredging, waking...
+ symb = _T(";SY(INFARE51)");
+ else
+ symb = _T(";SY(RSRDEF51)");
+
+ if (TRUE == S52_getMarinerParam(S52_MAR_SYMBOLIZED_BND))
+ line = _T(";LC(CTYARE51)");
+ else
+ line = _T(";LS(DASH,2,CHMGD)");
+ }
+ // Todo more for s57 3.1 Look at caris catalog ATTR::RESARE
+ }
+ }
+ } else {
+ // Continuation D
+ if (NULL != catreastr) {
+ if (strpbrk(catrea, "\001\010\011\014\016\023\025\031")) {
+ if (strpbrk(catrea, "\004\005\006\007\012\022\024\026\027\030"))
+ symb = _T(";SY(CTYARE71)");
+ else
+ symb = _T(";SY(CTYARE51)");
+ } else {
+ if (strpbrk(catrea, "\004\005\006\007\012\022\024\026\027\030"))
+ symb = _T(";SY(INFARE51)");
+ else
+ symb = _T(";SY(RSRDEF51)");
+ }
+ } else
+ symb = _T(";SY(RSRDEF51)");
+
+ if (TRUE == S52_getMarinerParam(S52_MAR_SYMBOLIZED_BND))
+ line = _T(";LC(CTYARE51)");
+ else
+ line = _T(";LS(DASH,2,CHMGD)");
+ }
+
+ // create command word
+ if (prio.Len()) resare02.Append(prio);
+ resare02.Append(line);
+ resare02.Append(symb);
+
+ resare02.Append('\037');
+
+ char *r = (char *)malloc(resare02.Len() + 1);
+ strcpy(r, resare02.mb_str());
+
+ delete restrnstr;
+ delete catreastr;
+
+ return r;
+}
+
+static void *_RESCSP01(void *param);
+static void *RESTRN01(void *param)
+// Remarks: Objects subject to RESTRN01 are actually symbolised in sub-process
+// RESCSP01, since the latter can also be accessed from other conditional
+// symbology procedures. RESTRN01 merely acts as a "signpost" for
+// RESCSP01.
+//
+// Object class RESARE is symbolised for the effect of attribute RESTRN in a
+// separate conditional symbology procedure called RESARE02.
+//
+// Since many of the areas concerned cover shipping channels, the number of
+// symbols used is minimised to reduce clutter. To do this, values of RESTRN are
+// ranked for significance as follows: "Traffic Restriction" values of RESTRN:
+// (1) RESTRN 7,8: entry prohibited or restricted
+// RESTRN 14: IMO designated "area to be avoided" part of a TSS
+// (2) RESTRN 1,2: anchoring prohibited or restricted
+// (3) RESTRN 3,4,5,6: fishing or trawling prohibited or restricted
+// (4) "Other Restriction" values of RESTRN are:
+// RESTRN 9, 10: dredging prohibited or restricted,
+// RESTRN 11,12: diving prohibited or restricted,
+// RESTRN 13 : no wake area.
+{
+ ObjRazRules *rzRules = (ObjRazRules *)param;
+ S57Obj *obj = rzRules->obj;
+
+ wxString *restrnstr = GetStringAttrWXS(obj, "RESTRN");
+
+ // GString *restrn01str = S57_getAttVal(geo, "RESTRN");
+ char *restrn01 = NULL;
+
+ if (NULL != restrnstr)
+ restrn01 = (char *)_RESCSP01(param);
+ else
+ restrn01 = NULL;
+
+ delete restrnstr;
+ return restrn01;
+}
+
+static void *_RESCSP01(void *param)
+// Remarks: See procedure RESTRN01
+{
+ ObjRazRules *rzRules = (ObjRazRules *)param;
+ S57Obj *obj = rzRules->obj;
+
+ wxString rescsp01;
+ // char *rescsp01 = NULL;
+ wxString *restrnstr = GetStringAttrWXS(obj, "RESTRN");
+ // GString *restrnstr = S57_getAttVal(geo, "RESTRN");
+ char restrn[LISTSIZE] = {'\0'}; // restriction list
+ wxString symb;
+ char *r = NULL;
+
+ if (restrnstr->Len()) {
+ _parseList(restrnstr->mb_str(), restrn, sizeof(restrn));
+
+ if (strpbrk(restrn, "\007\010\016")) {
+ // continuation A
+ if (strpbrk(restrn, "\001\002\003\004\005\006"))
+ symb = _T(";SY(ENTRES61)");
+ else {
+ if (strpbrk(restrn, "\011\012\013\014\015"))
+ symb = _T(";SY(ENTRES71)");
+ else
+ symb = _T(";SY(ENTRES51)");
+ }
+ } else {
+ if (strpbrk(restrn, "\001\002")) {
+ // continuation B
+ if (strpbrk(restrn, "\003\004\005\006"))
+ symb = _T(";SY(ACHRES61)");
+ else {
+ if (strpbrk(restrn, "\011\012\013\014\015"))
+ symb = _T( ";SY(ACHRES71)");
+ else
+ symb = _T(";SY(ACHRES51)");
+ }
+
+ } else {
+ if (strpbrk(restrn, "\003\004\005\006")) {
+ // continuation C
+ if (strpbrk(restrn, "\011\012\013\014\015"))
+ symb = _T(";SY(FSHRES71)");
+ else
+ symb = _T(";SY(FSHRES51)");
+
+ } else {
+ if (strpbrk(restrn, "\011\012\013\014\015"))
+ symb = _T(";SY(INFARE51)");
+ else
+ symb = _T(";SY(RSRDEF51)");
+ }
+ }
+ }
+
+ rescsp01.Append(symb);
+ rescsp01.Append('\037');
+
+ r = (char *)malloc(rescsp01.Len() + 1);
+ strcpy(r, rescsp01.mb_str());
+
+ delete restrnstr;
+ }
+
+ return r;
+}
+
+static void *SEABED01(void *param) {
+ ObjRazRules *rzRules = (ObjRazRules *)param;
+ // S57Obj *obj = rzRules->obj;
+
+// CPLError((CPLErr)0, 0,
+// "s52csny : SEABED01 ERROR no conditional symbology for: %s\n",
+// rzRules->LUP->OBCL);
+ return NULL;
+}
+
+/*
+static void *SNDFRM02(void *param)
+{
+ ObjRazRules *rzRules = (ObjRazRules *)param;
+// S57Obj *obj = rzRules->obj;
+
+ CPLError((CPLErr)0, 0,"s52csny : SNDFRM02 ERROR no conditional symbology
+for: %s\n",rzRules->LUP->OBCL); return NULL;
+}
+*/
+
+wxString SNDFRM02(S57Obj *obj, double depth_value);
+
+static void *SOUNDG02(void *param)
+// Remarks: In S-57 soundings are elements of sounding arrays rather than
+// individual objects. Thus the conditional symbology methodology must examine
+// each sounding of a sounding array one by one. To symbolize the depth values
+// it calls the procedure SNDFRM02 which in turn translates the depth values
+// into a set of symbols to be shown at the soundings position.
+{
+ // Shortcut. This CS method causes a branch to an S52plib method
+ // which splits multi-point soundings into separate point objects,
+ // and then calls CS(SOUNDG03) on successive points below.
+ char *r = (char *)malloc(6);
+ strcpy(r, "MP();");
+
+ return r;
+}
+
+static void *SOUNDG03(void *param)
+// Remarks: SOUNDG03 is a private conditional symbology,
+// called to render individual points of a multi-point sounding set.
+{
+ ObjRazRules *rzRules = (ObjRazRules *)param;
+ S57Obj *obj = rzRules->obj;
+
+ wxString s = SNDFRM02(obj, obj->z);
+
+ char *r = (char *)malloc(s.Len() + 1);
+ strcpy(r, s.mb_str());
+
+ return r;
+}
+
+wxString SNDFRM02(S57Obj *obj, double depth_value_in)
+// Remarks: Soundings differ from plain text because they have to be readable
+// under all circumstances and their digits are placed according to special
+// rules. This conditional symbology procedure accesses a set of carefully
+// designed sounding symbols provided by the symbol library and composes them to
+// sounding labels. It symbolizes swept depth and it also symbolizes for low
+// reliability as indicated by attributes QUASOU and QUAPOS.
+{
+ wxString sndfrm02;
+ char temp_str[LISTSIZE] = {'\0'};
+ wxString symbol_prefix;
+
+ char symbol_prefix_a[200];
+
+ wxString *tecsoustr = GetStringAttrWXS(obj, "TECSOU");
+ char tecsou[LISTSIZE] = {'\0'};
+
+ wxString *quasoustr = GetStringAttrWXS(obj, "QUASOU");
+ char quasou[LISTSIZE] = {'\0'};
+
+ wxString *statusstr = GetStringAttrWXS(obj, "STATUS");
+ char status[LISTSIZE] = {'\0'};
+
+ double leading_digit = 0.0;
+
+ double safety_depth = S52_getMarinerParam(S52_MAR_SAFETY_DEPTH);
+
+ // Do the math to convert soundings to ft/metres/fathoms on request
+ double depth_value = depth_value_in;
+
+ // If the sounding value from the ENC (or SENC) is bogus, so state
+ if (depth_value_in > 40000.) depth_value = 99999.;
+ if (depth_value_in < -1000.) depth_value = 0.;
+
+ switch (ps52plib->m_nDepthUnitDisplay) {
+ case 0:
+ depth_value = depth_value * (1.0 / 0.3048); // feet
+ safety_depth = safety_depth * (1.0 / 0.3048);
+ break;
+ case 2:
+ depth_value = depth_value * (1.0 / 0.3048) / 6; // fathoms
+ safety_depth = safety_depth * (1.0 / 0.3048) / 6;
+ break;
+ default:
+ break;
+ }
+
+ // FIXME: test to fix the rounding error (!?)
+ depth_value += (depth_value > 0.0) ? 0.01 : -0.01;
+ leading_digit = (int)fabs(depth_value);
+
+ if (depth_value <= safety_depth) // S52_getMarinerParam(S52_MAR_SAFETY_DEPTH)
+ symbol_prefix = _T("SOUNDS");
+ else
+ symbol_prefix = _T("SOUNDG");
+
+ strcpy(symbol_prefix_a, symbol_prefix.mb_str());
+
+ if (NULL != tecsoustr) {
+ _parseList(tecsoustr->mb_str(), tecsou, sizeof(tecsou));
+ if (strpbrk(tecsou, "\006")) {
+ chk_snprintf(temp_str, LISTSIZE, ";SY(%sB1)", symbol_prefix_a);
+ sndfrm02.Append(wxString(temp_str, wxConvUTF8));
+ }
+ }
+
+ if (NULL != quasoustr)
+ _parseList(quasoustr->mb_str(), quasou, sizeof(quasou));
+ if (NULL != statusstr)
+ _parseList(statusstr->mb_str(), status, sizeof(status));
+
+ if (strpbrk(quasou, "\003\004\005\010\011") || strpbrk(status, "\022")) {
+ chk_snprintf(temp_str, LISTSIZE, ";SY(%sC2)", symbol_prefix_a);
+ sndfrm02.Append(wxString(temp_str, wxConvUTF8));
+ } else {
+ int quapos = 0;
+ GetIntAttr(obj, "QUAPOS", quapos);
+ if (0 != quapos) {
+ if (2 <= quapos && quapos < 10) {
+ chk_snprintf(temp_str, LISTSIZE, ";SY(%sC2)", symbol_prefix_a);
+ sndfrm02.Append(wxString(temp_str, wxConvUTF8));
+ }
+ }
+ }
+
+ // Continuation A
+ if (fabs(depth_value) < 10.0) {
+ // If showing as "feet", round off to one digit only
+ if ((ps52plib->m_nDepthUnitDisplay == 0) && (depth_value > 0)) {
+ double r1 = depth_value;
+ depth_value = wxRound(r1);
+ leading_digit = (int)depth_value;
+ }
+
+ if (depth_value < 10.0) {
+ // can be above water (negative)
+ int fraction = (int)abs((fabs(depth_value) - leading_digit) * 10);
+
+ chk_snprintf(temp_str, LISTSIZE, ";SY(%s1%1i)", symbol_prefix_a,
+ (int)abs(leading_digit));
+ sndfrm02.Append(wxString(temp_str, wxConvUTF8));
+ if (fraction > 0) {
+ chk_snprintf(temp_str, LISTSIZE, ";SY(%s5%1i)", symbol_prefix_a,
+ fraction);
+ sndfrm02.Append(wxString(temp_str, wxConvUTF8));
+ }
+
+ // above sea level (negative)
+ if (depth_value < 0.0) {
+ chk_snprintf(temp_str, LISTSIZE, ";SY(%sA1)", symbol_prefix_a);
+ sndfrm02.Append(wxString(temp_str, wxConvUTF8));
+ }
+ goto return_point;
+ }
+ }
+
+ if (fabs(depth_value) < 31.0) {
+ bool b_2digit = false;
+ double depth_value_pos = fabs(depth_value);
+
+ // If showing as "feet", round off to two digits only
+ if ((ps52plib->m_nDepthUnitDisplay == 0) && (depth_value_pos > 0)) {
+ double r1 = depth_value;
+ depth_value = wxRound(r1);
+ leading_digit = (int)depth_value_pos;
+ b_2digit = true;
+ }
+
+ double fraction = fabs(depth_value_pos - floor(leading_digit));
+
+ if (fraction != 0.0) {
+ fraction = fraction * 10;
+ if (leading_digit >= 10.0) {
+ chk_snprintf(temp_str, LISTSIZE, ";SY(%s2%1i)", symbol_prefix_a,
+ (int)leading_digit / 10);
+ sndfrm02.Append(wxString(temp_str, wxConvUTF8));
+ }
+
+ double first_digit = floor(leading_digit / 10);
+ int secnd_digit = (int)(floor(leading_digit - (first_digit * 10)));
+ chk_snprintf(temp_str, LISTSIZE, ";SY(%s1%1i)", symbol_prefix_a,
+ secnd_digit /*(int)leading_digit*/);
+ sndfrm02.Append(wxString(temp_str, wxConvUTF8));
+
+ if (!b_2digit) {
+ if ((int)fraction > 0) {
+ chk_snprintf(temp_str, LISTSIZE, ";SY(%s5%1i)", symbol_prefix_a,
+ (int)fraction);
+ sndfrm02.Append(wxString(temp_str, wxConvUTF8));
+ }
+ }
+
+ if (depth_value < 0.0) {
+ chk_snprintf(temp_str, LISTSIZE, ";SY(%sA1)", symbol_prefix_a);
+ sndfrm02.Append(wxString(temp_str, wxConvUTF8));
+ }
+
+ goto return_point;
+ }
+ }
+
+ // Continuation B
+ if (fabs(depth_value) < 100.0) {
+ leading_digit = fabs(leading_digit);
+
+ double first_digit = floor(leading_digit / 10);
+ double secnd_digit = floor(leading_digit - (first_digit * 10));
+
+ if (depth_value < 0.0) {
+ chk_snprintf(temp_str, LISTSIZE, ";SY(%s2%1i)", symbol_prefix_a,
+ (int)first_digit);
+ sndfrm02.Append(wxString(temp_str, wxConvUTF8));
+ chk_snprintf(temp_str, LISTSIZE, ";SY(%s1%1i)", symbol_prefix_a,
+ (int)secnd_digit);
+ sndfrm02.Append(wxString(temp_str, wxConvUTF8));
+ chk_snprintf(temp_str, LISTSIZE, ";SY(%sA1)", symbol_prefix_a);
+ sndfrm02.Append(wxString(temp_str, wxConvUTF8));
+ } else {
+ chk_snprintf(temp_str, LISTSIZE, ";SY(%s1%1i)", symbol_prefix_a,
+ (int)first_digit);
+ sndfrm02.Append(wxString(temp_str, wxConvUTF8));
+ chk_snprintf(temp_str, LISTSIZE, ";SY(%s0%1i)", symbol_prefix_a,
+ (int)secnd_digit);
+ sndfrm02.Append(wxString(temp_str, wxConvUTF8));
+ }
+ goto return_point;
+ }
+
+ if (depth_value < 1000.0) {
+ double first_digit = floor(leading_digit / 100);
+ double secnd_digit = floor((leading_digit - (first_digit * 100)) / 10);
+ double third_digit =
+ floor(leading_digit - (first_digit * 100) - (secnd_digit * 10));
+
+ chk_snprintf(temp_str, LISTSIZE, ";SY(%s2%1i)", symbol_prefix_a,
+ (int)first_digit);
+ sndfrm02.Append(wxString(temp_str, wxConvUTF8));
+ chk_snprintf(temp_str, LISTSIZE, ";SY(%s1%1i)", symbol_prefix_a,
+ (int)secnd_digit);
+ sndfrm02.Append(wxString(temp_str, wxConvUTF8));
+ chk_snprintf(temp_str, LISTSIZE, ";SY(%s0%1i)", symbol_prefix_a,
+ (int)third_digit);
+ sndfrm02.Append(wxString(temp_str, wxConvUTF8));
+
+ goto return_point;
+ }
+
+ if (depth_value < 10000.0) {
+ double first_digit = floor(leading_digit / 1000);
+ double secnd_digit = floor((leading_digit - (first_digit * 1000)) / 100);
+ double third_digit = floor(
+ (leading_digit - (first_digit * 1000) - (secnd_digit * 100)) / 10);
+ double last_digit = floor(leading_digit - (first_digit * 1000) -
+ (secnd_digit * 100) - (third_digit * 10));
+
+ chk_snprintf(temp_str, LISTSIZE, ";SY(%s2%1i)", symbol_prefix_a,
+ (int)first_digit);
+ sndfrm02.Append(wxString(temp_str, wxConvUTF8));
+ chk_snprintf(temp_str, LISTSIZE, ";SY(%s1%1i)", symbol_prefix_a,
+ (int)secnd_digit);
+ sndfrm02.Append(wxString(temp_str, wxConvUTF8));
+ chk_snprintf(temp_str, LISTSIZE, ";SY(%s0%1i)", symbol_prefix_a,
+ (int)third_digit);
+ sndfrm02.Append(wxString(temp_str, wxConvUTF8));
+ chk_snprintf(temp_str, LISTSIZE, ";SY(%s4%1i)", symbol_prefix_a,
+ (int)last_digit);
+ sndfrm02.Append(wxString(temp_str, wxConvUTF8));
+
+ goto return_point;
+ }
+
+ // Continuation C
+ {
+ double first_digit = floor(leading_digit / 10000);
+ double secnd_digit = floor((leading_digit - (first_digit * 10000)) / 1000);
+ double third_digit = floor(
+ (leading_digit - (first_digit * 10000) - (secnd_digit * 1000)) / 100);
+ double fourth_digit = floor((leading_digit - (first_digit * 10000) -
+ (secnd_digit * 1000) - (third_digit * 100)) /
+ 10);
+ double last_digit =
+ floor(leading_digit - (first_digit * 10000) - (secnd_digit * 1000) -
+ (third_digit * 100) - (fourth_digit * 10));
+
+ chk_snprintf(temp_str, LISTSIZE, ";SY(%s3%1i)", symbol_prefix_a,
+ (int)first_digit);
+ sndfrm02.Append(wxString(temp_str, wxConvUTF8));
+ chk_snprintf(temp_str, LISTSIZE, ";SY(%s2%1i)", symbol_prefix_a,
+ (int)secnd_digit);
+ sndfrm02.Append(wxString(temp_str, wxConvUTF8));
+ chk_snprintf(temp_str, LISTSIZE, ";SY(%s1%1i)", symbol_prefix_a,
+ (int)third_digit);
+ sndfrm02.Append(wxString(temp_str, wxConvUTF8));
+ chk_snprintf(temp_str, LISTSIZE, ";SY(%s0%1i)", symbol_prefix_a,
+ (int)fourth_digit);
+ sndfrm02.Append(wxString(temp_str, wxConvUTF8));
+ chk_snprintf(temp_str, LISTSIZE, ";SY(%s4%1i)", symbol_prefix_a,
+ (int)last_digit);
+ sndfrm02.Append(wxString(temp_str, wxConvUTF8));
+
+ goto return_point;
+ }
+
+return_point:
+ sndfrm02.Append('\037');
+
+ delete tecsoustr;
+ delete quasoustr;
+ delete statusstr;
+
+ return sndfrm02;
+}
+
+static void *TOPMAR01(void *param)
+// Remarks: Topmark objects are to be symbolized through consideration of their
+// platforms e.g. a buoy. Therefore this conditional symbology procedure
+// searches for platforms by looking for other objects that are located at the
+// same position.. Based on the finding whether the platform is rigid or
+// floating, the respective upright or sloping symbol is selected and presented
+// at the objects location. Buoyf symbols and topmark symbols have been
+// carefully designed to fit to each other when combined at the same position.
+// The result is a composed symbol that looks like the traditional symbols the
+// mariner is used to.
+{
+ ObjRazRules *rzRules = (ObjRazRules *)param;
+ S57Obj *obj = rzRules->obj;
+
+ int top_int = 0;
+ bool battr = GetIntAttr(obj, "TOPSHP", top_int);
+
+ wxString sy;
+
+ if (!battr)
+ sy = _T(";SY(QUESMRK1)");
+ else {
+ int floating = FALSE; // not a floating platform
+ int topshp = (!battr) ? 0 : top_int;
+
+ if (TRUE == _atPtPos(obj, GetChartFloatingATONArray(rzRules), false))
+ floating = TRUE;
+
+ if (floating) {
+ // floating platform
+ switch (topshp) {
+ case 1:
+ sy = _T(";SY(TOPMAR02)");
+ break;
+ case 2:
+ sy = _T(";SY(TOPMAR04)");
+ break;
+ case 3:
+ sy = _T(";SY(TOPMAR10)");
+ break;
+ case 4:
+ sy = _T(";SY(TOPMAR12)");
+ break;
+
+ case 5:
+ sy = _T(";SY(TOPMAR13)");
+ break;
+ case 6:
+ sy = _T(";SY(TOPMAR14)");
+ break;
+ case 7:
+ sy = _T(";SY(TOPMAR65)");
+ break;
+ case 8:
+ sy = _T(";SY(TOPMAR17)");
+ break;
+
+ case 9:
+ sy = _T(";SY(TOPMAR16)");
+ break;
+ case 10:
+ sy = _T(";SY(TOPMAR08)");
+ break;
+ case 11:
+ sy = _T(";SY(TOPMAR07)");
+ break;
+ case 12:
+ sy = _T(";SY(TOPMAR14)");
+ break;
+
+ case 13:
+ sy = _T(";SY(TOPMAR05)");
+ break;
+ case 14:
+ sy = _T(";SY(TOPMAR06)");
+ break;
+ case 17:
+ sy = _T(";SY(TMARDEF2)");
+ break;
+ case 18:
+ sy = _T(";SY(TOPMAR10)");
+ break;
+
+ case 19:
+ sy = _T(";SY(TOPMAR13)");
+ break;
+ case 20:
+ sy = _T(";SY(TOPMAR14)");
+ break;
+ case 21:
+ sy = _T(";SY(TOPMAR13)");
+ break;
+ case 22:
+ sy = _T(";SY(TOPMAR14)");
+ break;
+
+ case 23:
+ sy = _T(";SY(TOPMAR14)");
+ break;
+ case 24:
+ sy = _T(";SY(TOPMAR02)");
+ break;
+ case 25:
+ sy = _T(";SY(TOPMAR04)");
+ break;
+ case 26:
+ sy = _T(";SY(TOPMAR10)");
+ break;
+
+ case 27:
+ sy = _T(";SY(TOPMAR17)");
+ break;
+ case 28:
+ sy = _T(";SY(TOPMAR18)");
+ break;
+ case 29:
+ sy = _T(";SY(TOPMAR02)");
+ break;
+ case 30:
+ sy = _T(";SY(TOPMAR17)");
+ break;
+
+ case 31:
+ sy = _T(";SY(TOPMAR14)");
+ break;
+ case 32:
+ sy = _T(";SY(TOPMAR10)");
+ break;
+ case 33:
+ sy = _T(";SY(TMARDEF2)");
+ break;
+ default:
+ sy = _T(";SY(TMARDEF2)");
+ break;
+ }
+ } else {
+ // not a floating platform
+ switch (topshp) {
+ case 1:
+ sy = _T(";SY(TOPMAR22)");
+ break;
+ case 2:
+ sy = _T(";SY(TOPMAR24)");
+ break;
+ case 3:
+ sy = _T(";SY(TOPMAR30)");
+ break;
+ case 4:
+ sy = _T(";SY(TOPMAR32)");
+ break;
+
+ case 5:
+ sy = _T(";SY(TOPMAR33)");
+ break;
+ case 6:
+ sy = _T(";SY(TOPMAR34)");
+ break;
+ case 7:
+ sy = _T(";SY(TOPMAR85)");
+ break;
+ case 8:
+ sy = _T(";SY(TOPMAR86)");
+ break;
+
+ case 9:
+ sy = _T(";SY(TOPMAR36)");
+ break;
+ case 10:
+ sy = _T(";SY(TOPMAR28)");
+ break;
+ case 11:
+ sy = _T(";SY(TOPMAR27)");
+ break;
+ case 12:
+ sy = _T(";SY(TOPMAR14)");
+ break;
+
+ case 13:
+ sy = _T(";SY(TOPMAR25)");
+ break;
+ case 14:
+ sy = _T(";SY(TOPMAR26)");
+ break;
+ case 15:
+ sy = _T(";SY(TOPMAR88)");
+ break;
+ case 16:
+ sy = _T(";SY(TOPMAR87)");
+ break;
+
+ case 17:
+ sy = _T(";SY(TMARDEF1)");
+ break;
+ case 18:
+ sy = _T(";SY(TOPMAR30)");
+ break;
+ case 19:
+ sy = _T(";SY(TOPMAR33)");
+ break;
+ case 20:
+ sy = _T(";SY(TOPMAR34)");
+ break;
+
+ case 21:
+ sy = _T(";SY(TOPMAR33)");
+ break;
+ case 22:
+ sy = _T(";SY(TOPMAR34)");
+ break;
+ case 23:
+ sy = _T(";SY(TOPMAR34)");
+ break;
+ case 24:
+ sy = _T(";SY(TOPMAR22)");
+ break;
+
+ case 25:
+ sy = _T(";SY(TOPMAR24)");
+ break;
+ case 26:
+ sy = _T(";SY(TOPMAR30)");
+ break;
+ case 27:
+ sy = _T(";SY(TOPMAR86)");
+ break;
+ case 28:
+ sy = _T(";SY(TOPMAR89)");
+ break;
+
+ case 29:
+ sy = _T(";SY(TOPMAR22)");
+ break;
+ case 30:
+ sy = _T(";SY(TOPMAR86)");
+ break;
+ case 31:
+ sy = _T(";SY(TOPMAR14)");
+ break;
+ case 32:
+ sy = _T(";SY(TOPMAR30)");
+ break;
+ case 33:
+ sy = _T(";SY(TMARDEF1)");
+ break;
+ default:
+ sy = _T(";SY(TMARDEF1)");
+ break;
+ }
+ }
+ }
+
+ wxString topmar;
+ topmar.Append(sy);
+ topmar.Append('\037');
+
+ char *r = (char *)malloc(topmar.Len() + 1);
+ strcpy(r, topmar.mb_str());
+
+ return r;
+}
+
+static void *UDWHAZ03(void *param) {
+ ObjRazRules *rzRules = (ObjRazRules *)param;
+ // S57Obj *obj = rzRules->obj;
+
+// CPLError((CPLErr)0, 0,
+// "s52csny : UDWHAZ03 ERROR no conditional symbology for: %s\n",
+// rzRules->LUP->OBCL);
+ return NULL;
+}
+
+static void *VESSEL01(void *param) {
+ ObjRazRules *rzRules = (ObjRazRules *)param;
+ // S57Obj *obj = rzRules->obj;
+
+// CPLError((CPLErr)0, 0,
+// "s52csny : VESSEL01 ERROR no conditional symbology for: %s\n",
+// rzRules->LUP->OBCL);
+ return NULL;
+}
+
+static void *VRMEBL01(void *param) {
+ ObjRazRules *rzRules = (ObjRazRules *)param;
+ // S57Obj *obj = rzRules->obj;
+
+//FIXME plib
+// CPLError((CPLErr)0, 0,
+// "s52csny : VRMEBL01 ERROR no conditional symbology for: %s\n",
+// rzRules->LUP->OBCL);
+ return NULL;
+}
+
+/*
+static void *WRECKS02a(void *param)
+{
+ ObjRazRules *rzRules = (ObjRazRules *)param;
+// S57Obj *obj = rzRules->obj;
+
+ static int f07;
+ if(!f07)
+ CPLError((CPLErr)0, 0,"s52csny : WRECKS02 ERROR no conditional
+symbology for: %s\n",rzRules->LUP->OBCL); f07++; return NULL;
+}
+*/
+
+static void *WRECKS02(void *param)
+// Remarks: Wrecks of depths less than the safety contour which lie within the
+// safe waters defined by the safety contour are to be presented by a specific
+// isolated danger symbol and put in IMO category DISPLAYBASE (see (3), App.2,
+// 1.3). This task is performed by the sub-procedure "UDWHAZ03" which is
+// called by this symbology procedure.
+{
+ wxString wrecks02str;
+ wxString sndfrm02str;
+ wxString *udwhaz03str = NULL;
+ wxString *quapnt01str = NULL;
+ double least_depth = UNKNOWN;
+ double depth_value = UNKNOWN;
+ // GString *valsoustr = S57_getAttVal(geo, "VALSOU");
+ double valsou = UNKNOWN;
+ bool b_promote = false;
+
+ ObjRazRules *rzRules = (ObjRazRules *)param;
+ S57Obj *obj = rzRules->obj;
+
+ GetDoubleAttr(obj, "VALSOU", valsou);
+
+ int watlev = -9;
+ GetIntAttr(obj, "WATLEV", watlev);
+ int catwrk = -9;
+ GetIntAttr(obj, "CATWRK", catwrk);
+
+ int quasou = -9;
+ // QUASOU is a list ie a string for us
+ wxString *quasoustr = GetStringAttrWXS(obj, "QUASOU");
+ char quasouchar[LISTSIZE] = {'\0'};
+
+ double safety_contour = S52_getMarinerParam(S52_MAR_SAFETY_CONTOUR);
+
+ if (UNKNOWN != valsou) {
+ depth_value = valsou;
+ sndfrm02str = SNDFRM02(obj, depth_value);
+ } else {
+ if (GEO_AREA == obj->Primitive_type)
+ least_depth = _DEPVAL01(obj, least_depth);
+
+ if (least_depth == UNKNOWN)
+ /*
+ {
+ // WARNING: ambiguity removed in WRECKS03 (see update)
+
+ if (-9 == watlev) // default
+ depth_value = -15.0;
+ else
+ switch (watlev)
+ { // ambiguous
+ case 1:
+ case 2: depth_value = -15.0 ; break;
+ case 3: depth_value = 0.01; break;
+ case 4: depth_value = -15.0 ; break;
+ case 5: depth_value = 0.0 ; break;
+ case 6: depth_value = -15.0 ; break;
+ default :
+ {
+ if (-9 != catwrk)
+ {
+ switch (catwrk)
+ {
+ case 1: depth_value = 20.0;
+ break; case 2: depth_value = 0.0; break; case 4: case 5: depth_value =
+ -15.0; break;
+ }
+ }
+ }
+ }
+ }
+ */
+ ////////////////////////////////////////////////
+ // DSR New Logic Here (FIXME)
+ {
+ if (-9 != catwrk) {
+ switch (catwrk) {
+ case 1:
+ depth_value = 20.0;
+ break; // safe
+ case 2:
+ depth_value = 0.0;
+ break; // dangerous
+ case 4:
+ case 5:
+ depth_value = -15.0;
+ break;
+ }
+ } else {
+ if (-9 == watlev) // default
+ depth_value = -15.0;
+ else
+ switch (watlev) {
+ case 1:
+ case 2:
+ depth_value = -15.0;
+ break;
+ case 3:
+ depth_value = 0.01;
+ break;
+ case 4:
+ depth_value = -15.0;
+ break;
+ case 5:
+ depth_value = 0.0;
+ break;
+ case 6:
+ depth_value = -15.0;
+ break;
+ }
+ }
+
+ }
+
+ else
+ depth_value = least_depth;
+ }
+ if (NULL != quasoustr)
+ _parseList(quasoustr->mb_str(), quasouchar, sizeof(quasouchar));
+
+ if (quasouchar[0] == 0 || NULL == strpbrk(quasouchar, "\07")) {
+ // Fixes FS 165 XXX where it is?
+ // 7 is 'least depth unknown, safe clearance at value shown'
+ udwhaz03str = _UDWHAZ03(obj, depth_value, rzRules, &b_promote);
+
+ } else {
+ quasou = 7;
+ udwhaz03str = new wxString();
+ }
+ quapnt01str = CSQUAPNT01(obj);
+
+ if (GEO_POINT == obj->Primitive_type) {
+ if (0 != udwhaz03str->Len()) {
+ wrecks02str = wxString(*udwhaz03str);
+
+ wrecks02str.Append(*quapnt01str);
+
+ } else {
+ // Continuation A (POINT_T)
+ if (UNKNOWN != valsou) {
+ ///////////////////////////////////////////
+ // DSR New logic here, FIXME check s52 specs
+
+ /*
+ if (valsou <= 20.0)
+ {
+ wrecks02str = wxString(";SY(DANGER51)");
+ if (NULL != sndfrm02str)
+ wrecks02str.Append(sndfrm02str);
+ }
+ else
+ wrecks02str = wxString(";SY(DANGER52)");
+ */
+ if ((valsou <
+ safety_contour) /* || (2 == catwrk)*/) // maybe redundant, seems
+ // like wrecks with valsou
+ // < 20 are always coded as
+ // "dangerous wrecks"
+ // Excluding (2 == catwrk)
+ // matches Caris logic
+ wrecks02str = wxString(_T(";SY(DANGER51)"));
+ else
+ wrecks02str = wxString(_T(";SY(DANGER52)"));
+ wrecks02str.Append(_T(";TX('Wk',3,1,2,'15110',2,0,CHBLK,21)"));
+ if (7 == quasou) // Fixes FS 165
+ wrecks02str.Append(_T(";SY(WRECKS07)"));
+
+ wrecks02str.Append(sndfrm02str); // always show valsou depth
+ ///////////////////////////////////////////
+
+ wrecks02str.Append(*udwhaz03str);
+ wrecks02str.Append(*quapnt01str);
+
+ } else {
+ wxString sym;
+
+ if (-9 != catwrk && -9 != watlev) {
+ if (1 == catwrk && 3 == watlev)
+ sym = _T(";SY(WRECKS04)");
+ else {
+ if (2 == catwrk && 3 == watlev)
+ sym = _T(";SY(WRECKS05)");
+ else {
+ if (4 == catwrk || 5 == catwrk)
+ sym = _T(";SY(WRECKS01)");
+ else {
+ if (1 == watlev || 2 == watlev || 5 == watlev || 4 == watlev) {
+ sym = _T(";SY(WRECKS01)");
+ } else
+ sym = _T(";SY(WRECKS05)"); // default
+ }
+ }
+ }
+ }
+
+ wrecks02str = sym;
+ if (NULL != quapnt01str) wrecks02str.Append(*quapnt01str);
+ }
+ }
+
+ } else {
+ // Continuation B (AREAS_T)
+ int quapos = 0;
+ GetIntAttr(obj, "QUAPOS", quapos);
+
+ wxString line;
+
+ if (2 <= quapos && quapos < 10)
+ line = _T(";LC(LOWACC41)");
+ else {
+ if (0 != udwhaz03str->Len())
+ line = _T(";LS(DOTT,2,CHBLK)");
+ else {
+ if (UNKNOWN != valsou) {
+ if (valsou <= 20)
+ line = _T(";LS(DOTT,2,CHBLK)");
+ else
+ line = _T(";LS(DASH,2,CHBLK)");
+ } else {
+ if (-9 == watlev)
+ line = _T(";LS(DOTT,2,CSTLN)");
+ else {
+ switch (watlev) {
+ case 1:
+ case 2:
+ line = _T(";LS(SOLD,2,CSTLN)");
+ break;
+ case 4:
+ line = _T(";LS(DASH,2,CSTLN)");
+ break;
+ case 3:
+ case 5:
+
+ default:
+ line = _T(";LS(DOTT,2,CSTLN)");
+ break;
+ }
+ }
+ }
+ }
+ }
+ wrecks02str = wxString(line);
+
+ if (UNKNOWN != valsou) {
+ if (valsou <= 20) {
+ wrecks02str.Append(*udwhaz03str);
+ wrecks02str.Append(*quapnt01str);
+ wrecks02str.Append(sndfrm02str);
+
+ } else {
+ // NOTE: ??? same as above ???
+ wrecks02str.Append(*udwhaz03str);
+ wrecks02str.Append(*quapnt01str);
+ }
+ } else {
+ wxString ac;
+
+ if (-9 == watlev)
+ ac = _T(";AC(DEPVS)");
+ else
+ switch (watlev) {
+ case 1:
+ case 2:
+ ac = _T(";AC(CHBRN)");
+ break;
+ case 4:
+ ac = _T(";AC(DEPIT)");
+ break;
+ case 5:
+ case 3:
+ default:
+ ac = _T(";AC(DEPVS)");
+ break;
+ }
+
+ wrecks02str.Append(ac);
+
+ wrecks02str.Append(*udwhaz03str);
+ wrecks02str.Append(*quapnt01str);
+ }
+ }
+
+ wrecks02str.Append('\037');
+
+ char *r = (char *)malloc(wrecks02str.Len() + 1);
+ strcpy(r, wrecks02str.mb_str());
+
+ delete udwhaz03str;
+ delete quapnt01str;
+ delete quasoustr;
+ return r;
+}
+
+static wxString _LITDSN01(S57Obj *obj)
+// Remarks: In S-57 the light characteristics are held as a series of attributes
+// values. The mariner may wish to see a light description text string displayed
+// on the screen similar to the string commonly found on a paper chart. This
+// conditional procedure, reads the attribute values from the above list of
+// attributes and composes a light description string which can be displayed.
+// This procedure is provided as a C function which has as input, the above
+// listed attribute values and as output, the light description.
+{
+ // CATLIT, LITCHR, COLOUR, HEIGHT, LITCHR, SIGGRP, SIGPER, STATUS, VALNMR
+
+ char colist[20];
+ wxString return_value;
+#if 0
+ // XXX CATLIT
+ int catlit = -9;
+ GetIntAttr(obj, "CATLIT", catlit);
+
+ if(-9 != catlit)
+ {
+ }
+#endif
+
+ /*
+ 1: directional function IP 30.1-3; 475.7;
+ 2: rear/upper light
+ 3: front/lower light
+ 4: leading light IP 20.1-3; 475.6;
+ 5: aero light IP 60; 476.1;
+ 6: air obstruction light IP 61; 476.2;
+ 7: fog detector light IP 62; 477;
+ 8: flood light IP 63; 478.2;
+ 9: strip light IP 64; 478.5;
+ 10: subsidiary light IP 42; 471.8;
+ 11: spotlight
+ 12: front
+ 13: rear
+ 14: lower
+ 15: upper
+ 16: moire' effect IP 31; 475.8;
+ 17: emergency
+ 18: bearing light 478.1;
+ 19: horizontally disposed
+ 20: vertically disposed
+ */
+
+ // LITCHR
+ int litchr = -9;
+ wxString spost(_T(""));
+ GetIntAttr(obj, "LITCHR", litchr);
+
+ bool b_grp2 = false; // 2 GRP attributes expected
+ if (-9 != litchr) {
+ switch (litchr) {
+ /*
+ case 1: return_value.Append(_T("F")); break;
+ case 2: return_value.Append(_T("Fl")); break;
+ case 3: return_value.Append(_T("Fl")); break;
+ case 4: return_value.Append(_T("Q")); break;
+ case 7: return_value.Append(_T("Iso")); break;
+ case 8: return_value.Append(_T("Occ")); break;
+ case 12: return_value.Append(_T("Mo")); break;
+ */
+
+ case 1:
+ return_value.Append(_T("F"));
+ break; // fixed IP 10.1;
+ case 2:
+ return_value.Append(_T("Fl"));
+ break; // flashing IP 10.4;
+ case 3:
+ return_value.Append(_T("LFl"));
+ break; // long-flashing IP 10.5;
+ case 4:
+ return_value.Append(_T("Q"));
+ break; // quick-flashing IP 10.6;
+ case 5:
+ return_value.Append(_T("VQ"));
+ break; // very quick-flashing IP 10.7;
+ case 6:
+ return_value.Append(_T("UQ"));
+ break; // ultra quick-flashing IP 10.8;
+ case 7:
+ return_value.Append(_T("Iso"));
+ break; // isophased IP 10.3;
+ case 8:
+ return_value.Append(_T("Occ"));
+ break; // occulting IP 10.2;
+ case 9:
+ return_value.Append(_T("IQ"));
+ break; // interrupted quick-flashing IP 10.6;
+ case 10:
+ return_value.Append(_T("IVQ"));
+ break; // interrupted very quick-flashing IP 10.7;
+ case 11:
+ return_value.Append(_T("IUQ"));
+ break; // interrupted ultra quick-flashing IP 10.8;
+ case 12:
+ return_value.Append(_T("Mo"));
+ break; // morse IP 10.9;
+ case 13:
+ return_value.Append(_T("F + Fl"));
+ b_grp2 = true;
+ break; // fixed/flash IP 10.10;
+ case 14:
+ return_value.Append(_T("Fl + LFl"));
+ b_grp2 = true;
+ break; // flash/long-flash
+ case 15:
+ return_value.Append(_T("Occ + Fl"));
+ b_grp2 = true;
+ break; // occulting/flash
+ case 16:
+ return_value.Append(_T("F + LFl"));
+ b_grp2 = true;
+ break; // fixed/long-flash
+ case 17:
+ return_value.Append(_T("Al Occ"));
+ break; // occulting alternating
+ case 18:
+ return_value.Append(_T("Al LFl"));
+ break; // long-flash alternating
+ case 19:
+ return_value.Append(_T("Al Fl"));
+ break; // flash alternating
+ case 20:
+ return_value.Append(_T("Al Grp"));
+ break; // group alternating
+ case 21:
+ return_value.Append(_T("F"));
+ spost = _T(" (vert)");
+ break; // 2 fixed (vertical)
+ case 22:
+ return_value.Append(_T("F"));
+ spost = _T(" (horz)");
+ break; // 2 fixed (horizontal)
+ case 23:
+ return_value.Append(_T("F"));
+ spost = _T(" (vert)");
+ break; // 3 fixed (vertical)
+ case 24:
+ return_value.Append(_T("F"));
+ spost = _T(" (horz)");
+ break; // 3 fixed (horizontal)
+ case 25:
+ return_value.Append(_T("Q + LFl"));
+ b_grp2 = true;
+ break; // quick-flash plus long-flash
+ case 26:
+ return_value.Append(_T("VQ + LFl"));
+ b_grp2 = true;
+ break; // very quick-flash plus long-flash
+ case 27:
+ return_value.Append(_T("UQ + LFl"));
+ b_grp2 = true;
+ break; // ultra quick-flash plus long-flash
+ case 28:
+ return_value.Append(_T("Alt"));
+ break; // alternating
+ case 29:
+ return_value.Append(_T("F + Alt"));
+ b_grp2 = true;
+ break; // fixed and alternating flashing
+
+ default:
+ break;
+ }
+ }
+
+ int nfirst_grp = -1;
+ if (b_grp2) {
+ wxString ret_new;
+ nfirst_grp = return_value.Find(_T(" "));
+ if (wxNOT_FOUND != nfirst_grp) {
+ ret_new = return_value.Mid(0, nfirst_grp);
+ ret_new.Append(_T("(?)"));
+ ret_new.Append(return_value.Mid(nfirst_grp));
+ return_value = ret_new;
+ nfirst_grp += 1;
+ }
+ }
+
+ // SIGGRP, (c)(c) ...
+ char grp_str[20] = {'\0'};
+ GetStringAttr(obj, "SIGGRP", grp_str, 19);
+ if (strlen(grp_str)) {
+ wxString ss(grp_str, wxConvUTF8);
+
+ if (b_grp2) {
+ wxStringTokenizer tkz(ss, _T("()"));
+
+ int n_tok = 0;
+ while (tkz.HasMoreTokens() && (n_tok < 2)) {
+ wxString s = tkz.GetNextToken();
+ if (s.Len()) {
+ if ((n_tok == 0) && (nfirst_grp > 0)) {
+ return_value[nfirst_grp] = s[0];
+ } else {
+ if (s != _T("1")) {
+ return_value.Append(_T("("));
+ return_value.Append(s);
+ return_value.Append(_T(")"));
+ }
+ }
+
+ n_tok++;
+ }
+ }
+ } else {
+ if (ss != _T("(1)")) return_value.Append(ss);
+ }
+ }
+
+ // COLOUR,
+ char col_str[20] = {'\0'};
+
+ // Don't show for sectored lights since we are only showing one of the
+ // sectors.
+ double sectrTest;
+ bool hasSectors = GetDoubleAttr(obj, "SECTR1", sectrTest);
+
+ if (!hasSectors) {
+ GetStringAttr(obj, "COLOUR", col_str, 19);
+
+ int n_cols = 0;
+ if (strlen(col_str)) n_cols = _parseList(col_str, colist, sizeof(colist));
+
+ if (n_cols) return_value.Append(_T(" "));
+
+ for (int i = 0; i < n_cols; i++) {
+ switch (colist[i]) {
+ case 1:
+ return_value.Append(_T("W"));
+ break;
+ case 3:
+ return_value.Append(_T("R"));
+ break;
+ case 4:
+ return_value.Append(_T("G"));
+ break;
+ case 6:
+ return_value.Append(_T("Y"));
+ break;
+ default:
+ break;
+ }
+ }
+ }
+
+ /*
+ 1: white IP 11.1; 450.2-3;
+ 2: black
+ 3: red IP 11.2; 450.2-3;
+ 4: green IP 11.3; 450.2-3;
+ 5: blue IP 11.4; 450.2-3;
+ 6: yellow IP 11.6; 450.2-3;
+ 7: grey
+ 8: brown
+ 9: amber IP 11.8; 450.2-3;
+ 10: violet IP 11.5; 450.2-3;
+ 11: orange IP 11.7; 450.2-3;
+ 12: magenta
+ 13: pink
+ */
+
+ // SIGPER, xx.xx
+ double sigper = UNKNOWN;
+ GetDoubleAttr(obj, "SIGPER", sigper);
+
+ if (UNKNOWN != sigper) {
+ wxString s;
+ if (fabs(wxRound(sigper) - sigper) > 0.01)
+ s.Printf(_T("%4.1fs"), sigper);
+ else
+ s.Printf(_T("%2.0fs"), sigper);
+
+ s.Trim(false); // remove leading spaces
+ s.Prepend(_T(" "));
+ return_value.Append(s);
+ }
+
+ // HEIGHT, xxx.x
+ double height = UNKNOWN;
+ GetDoubleAttr(obj, "HEIGHT", height);
+
+ if (UNKNOWN != height) {
+ wxString s;
+ if (ps52plib->m_nHeightUnitDisplay == 1) { // feet
+ s.Printf(_T("%3.0fft"), height * (1.0 / 0.3048));
+ } else { // meters
+ s.Printf(_T("%3.0fm"), height);
+ }
+
+ s.Trim(false); // remove leading spaces
+ s.Prepend(_T(" "));
+ return_value.Append(s);
+ }
+
+ // VALNMR, xx.x
+ double valnmr = UNKNOWN;
+ GetDoubleAttr(obj, "VALNMR", valnmr);
+
+ if (UNKNOWN != valnmr && !hasSectors) {
+ wxString s;
+ s.Printf(_T("%2.0fNm"), valnmr);
+ s.Trim(false); // remove leading spaces
+ s.Prepend(_T(" "));
+ return_value.Append(s);
+ }
+
+#if 0
+
+ // STATUS,
+ gstr = S57_getAttVal(geo, "STATUS");
+ if (NULL != gstr)
+ g_string_append(litdsn01, gstr->str);
+
+ /*
+ 1: permanent
+ 2: occasional IP 50; 473.2;
+ 3: recommended IN 10; 431.1;
+ 4: not in use IL 14, 44; 444.7;
+ 5: periodic/intermittent IC 21; IQ 71; 353.3; 460.5;
+ 6: reserved IN 12.9;
+ 7: temporary IP 54;
+ 8: private IQ 70;
+ 9: mandatory
+ 10: destroyed/ruined
+ 11: extinguished
+ 12: illuminated
+ 13: historic
+ 14: public
+ 15: synchronized
+ 16: watched
+ 17: un-watched
+ 18: existence doubtful
+ */
+
+#endif
+
+ return_value.Append(spost); // add any final modifiers
+
+ return return_value;
+}
+
+static void *SYMINS01(void *param) {
+ ObjRazRules *rzRules = static_cast(param);
+ if (!rzRules || !rzRules->obj) return NULL;
+
+ char symins[258] = {'\0'};
+ GetStringAttr(rzRules->obj, "SYMINS", symins, sizeof(symins) - 2);
+
+ size_t len = strlen(symins);
+ symins[len] = '\037';
+ symins[len + 1] = '\0';
+
+ char *r = static_cast(malloc(len + 2));
+ if (!r) return NULL;
+
+ memcpy(r, symins, len + 2);
+ return r;
+}
+
+//--------------------------------
+//
+// JUMP TABLE SECTION
+//
+//--------------------------------
+Cond condTable[] = {
+ {"CLRLIN01", CLRLIN01},
+ {"DATCVR01", DATCVR01},
+ {"DATCVR01", DATCVR01},
+ {"DEPARE01", DEPARE01},
+ {"DEPARE02", DEPARE01}, // new in PLIB 3_3, opencpn defaults to DEPARE01
+ {"DEPCNT02", DEPCNT02},
+ {"DEPVAL01", DEPVAL01},
+ {"LEGLIN02", LEGLIN02},
+ {"LIGHTS05", LIGHTS05}, // new in PLIB 3_3, replaces LIGHTS04
+ {"LITDSN01", LITDSN01},
+ {"OBSTRN04", OBSTRN04},
+ {"OWNSHP02", OWNSHP02},
+ {"PASTRK01", PASTRK01},
+ {"QUAPOS01", QUAPOS01},
+ {"QUALIN01", QUALIN01},
+ {"QUAPNT01", QUAPNT01},
+ {"SLCONS03", SLCONS03},
+ {"RESARE02", RESARE02},
+ {"RESTRN01", RESTRN01},
+ // {"RESCSP01",RESCSP01},
+ {"SEABED01", SEABED01},
+ // {"SNDFRM02",SNDFRM02},
+ {"SOUNDG02", SOUNDG02},
+ {"TOPMAR01", TOPMAR01},
+ {"UDWHAZ03", UDWHAZ03},
+ {"VESSEL01", VESSEL01},
+ {"VRMEBL01", VRMEBL01},
+ {"WRECKS02", WRECKS02},
+ {"SOUNDG03", SOUNDG03}, // special case for MPS
+ {"SYMINS01", SYMINS01}, // Container for Virtual AIS ATONS, special case
+ {"########", NULL}};
+
+#if 0
+// S52CS.c : Conditional Symbologie procedure 3.2 (CS)
+//
+// Project: OpENCview
+
+/*
+ This file is part of the OpENCview project, a viewer of ENC
+ Copyright (C) 2000-2004 Sylvain Duclos sduclos@users.sourceforgue.net
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+*/
+
+// NOTE: remarks commenting each CS are extracted from pslb03_2.pdf (sec. 12)
+
+// FIXME: DEPCNT02: call DB for area DEPARE & DRGARE that intersect this line
+// FIXME:_DEPVAL01: call DB for area DEPARE & UNSARE that intersect this area
+// FIXME:_UDWHAZ03: call DB for area DRGARE & DEPARE that intersect this point/area
+
+#include "S52CS.h"
+
+#include "S52utils.h" // PRINTF()
+
+#include // atof()
+#include // fabsf(), HUGE_VAL
+#include // isdigit()
+
+#define UNKNOWN 1e6 // HUGE_VAL // INFINITY/NAN
+
+#define COALNE 30 // Coastline
+#define DEPARE 42 // Depth area
+#define DEPCNT 43 // Depth contour
+#define DRGARE 46 // Dredged area
+#define UWTROC 153 // Underwater rock / awash rock
+#define WRECKS 159 // Wreck
+
+// point list name
+#define LIGHTLIST 0
+#define SECTRLIST 1
+#define FLOATLIST 2 // floating platform
+#define RIGIDLIST 3 // rigid platform
+//static GPtrArray *_lightList = NULL;
+//static GPtrArray *_sectrList = NULL;
+//static GPtrArray *_flaotList = NULL;
+static GPtrArray *_ptList[] = {NULL, NULL, NULL, NULL};
+
+int S52_state = 1;
+
+// size of attributes value list buffer
+#define LISTSIZE 16 // list size
+
+#define version "3.2.0"
+char *S52_CS_version()
+{
+ return version;
+}
+
+int S52_CS_init()
+{
+ _ptList[LIGHTLIST] = g_ptr_array_new();
+ _ptList[SECTRLIST] = g_ptr_array_new();
+ _ptList[FLOATLIST] = g_ptr_array_new();
+ _ptList[RIGIDLIST] = g_ptr_array_new();
+
+ return 1;
+}
+
+int S52_CS_done()
+{
+ g_ptr_array_free(_ptList[LIGHTLIST], TRUE);
+ g_ptr_array_free(_ptList[SECTRLIST], TRUE);
+ g_ptr_array_free(_ptList[FLOATLIST], TRUE);
+ g_ptr_array_free(_ptList[RIGIDLIST], TRUE);
+
+ return 1;
+}
+
+int S52_CS_setPtPos(S57_geo *geoData, char *name)
+{
+ printf("name = %s\n", name);
+
+ if (POINT_T == S57_getObjtype(geoData)) {
+
+ // set floating platform
+ if ((0==strncmp(name, "LITFLT", 6)) ||
+ (0==strncmp(name, "LITVES", 6)) ||
+ (0==strncmp(name, "BOY", 3)))
+ g_ptr_array_add(_ptList[FLOATLIST], (gpointer) geoData);
+
+ // set rigid platform
+ if (0==strncmp(name, "BCN", 3))
+ g_ptr_array_add(_ptList[RIGIDLIST], (gpointer) geoData);
+ }
+
+ return 1;
+}
+
+static int _atPtPos(S57_geo *geoNew, int listNm)
+// return TRUE if there is a light at this position
+// or if its an extended arc radius else FALSE
+{
+ int i;
+ GPtrArray *curntList = _ptList[listNm];
+
+ for (i=0; ilen; i++) {
+ S57_geo *geoOld = g_ptr_array_index(curntList, i);
+
+ if (S57_samePtPos(geoNew, geoOld)) {
+
+ if (SECTRLIST != listNm)
+ return TRUE;
+ else {
+ // check for extend arc radius
+ GString *Asectr1str = S57_getAttVal(geoOld, "SECTR1");
+ GString *Asectr2str = S57_getAttVal(geoOld, "SECTR2");
+ GString *Bsectr1str = S57_getAttVal(geoNew, "SECTR1");
+ GString *Bsectr2str = S57_getAttVal(geoNew, "SECTR2");
+
+ // check present
+ if (NULL == Asectr1str ||
+ NULL == Asectr1str ||
+ NULL == Asectr1str ||
+ NULL == Asectr1str)
+ return FALSE;
+
+ {
+ double Asectr1 = atof(Asectr1str->str);
+ double Asectr2 = atof(Asectr2str->str);
+ double Bsectr1 = atof(Bsectr1str->str);
+ double Bsectr2 = atof(Bsectr2str->str);
+ double Asweep = (Asectr1 > Asectr2) ?
+ Asectr2-Asectr1+360 : Asectr2-Asectr1;
+ double Bsweep = (Bsectr1 > Bsectr2) ?
+ Bsectr2-Bsectr1+360 : Bsectr2-Bsectr1;
+
+ // check sector overlap
+ if (Asectr2<=Bsectr1 || Asectr1>=Bsectr2) {
+ if (Asweep == Bsweep) {
+ g_string_truncate(Bsectr2str, 0);
+ g_string_sprintf(Bsectr2str, "%f",Bsectr2-1);
+ S57_setAtt(geoNew, "SECTR2", Bsectr2str->str);
+ }
+
+ return FALSE;
+ }
+
+ // check if other sector larger
+ if (Asweep >= Bsweep)
+ return TRUE;
+ }
+ }
+ }
+ }
+
+ return FALSE;
+}
+
+static int _setPtPos(S57_geo *geo, int listNm)
+// TRUE if set new position of a light
+// else FALSE (ie there is a light at this position)
+{
+ GPtrArray *curntList = _ptList[listNm];
+
+ if (_atPtPos(geo, listNm))
+ return 1;
+ else
+ g_ptr_array_add(curntList, (gpointer) geo);
+
+ return 0;
+}
+
+static int _parseList(const char *str, char *buf)
+// Put a string of comma delimited number in an array (buf).
+// Return: the number of value in buf.
+// Assume: - number < 256,
+// - list size less then LISTSIZE-1 .
+// Note: buf is \0 terminated for strpbrk().
+{
+ int i = 0;
+
+ if (NULL != str && *str != '\0') {
+ do {
+ if ( i>= LISTSIZE-1) {
+ PRINTF("OVERFLOW --value in list lost!!\n");
+ break;
+ }
+
+ /*
+ if (255 < (unsigned char) atoi(str)) {
+ PRINTF("value overflow (>255)\n");
+ exit(0);
+ }
+ */
+
+ buf[i++] = (unsigned char) atoi(str);
+
+ while(isdigit(*str++)); // next
+ //while( g_ascii_isdigit(c)); // next
+
+ } while(*str++ != '\0'); // skip ',' or exit
+ }
+
+ buf[i] = '\0';
+
+ return i;
+}
+
+
+static GString *CLRLIN01 (S57_geo *geo)
+// Remarks: A clearing line shows a single arrow head at one of its ends. The direction
+// of the clearing line must be calculated from its line object in order to rotate
+// the arrow head symbol and place it at the correct end. This cannot be
+// achieved with a complex linestyle since linestyle symbols cannot be sized
+// to the length of the clearing line. Instead a linestyle with a repeating pattern
+// of arrow symbols had to be used which does not comply with the required
+// symbolization.
+{
+
+ PRINTF("Mariner's object not drawn\n");
+
+ return NULL;
+}
+
+static GString *DATCVR01 (S57_geo *geo)
+// Remarks: This conditional symbology procedure describes procedures for:
+// - symbolizing the limit of ENC coverage;
+// - symbolizing navigational purpose boundaries ("scale boundarie"); and
+// - indicating overscale display.
+//
+// Note that the mandatory meta object CATQUA is symbolized by the look-up table.
+//
+// Because the methods adopted by an ECDIS to meet the IMO and IHO requirements
+// listed on the next page will depend on the manufacturer's software, and cannot be
+// described in terms of a flow chart in the same way as other conditional procedures,
+// this procedure is in the form of written notes.
+{
+ GString *datcvr01 = NULL;
+
+ ///////////////////////
+ // 1- REQUIREMENT
+ // (IMO/IHO specs. explenation)
+
+ ///////////////////////
+ // 2- ENC COVERAGE
+ //
+ // 2.1- Limit of ENC coverage
+ //datcvr01 = g_string_new(";OP(3OD11060);LC(HODATA01)");
+ // FIXME: get cell extend
+
+ // 2.2- No data areas
+ // This can be done outside of CS (ie when clearing the screen in Mesa)
+ // FIXME: ";OP(0---);AC(NODATA)"
+ // FIXME: set geo to cover earth (!)
+
+ //////////////////////
+ // 3- SCALE BOUNDARIES
+ //
+ // 3.1- Chart scale boundaties
+ // FIXME;
+ //g_string_append(datcvr01, ";LS(SOLD,1,CHGRD)");
+ // -OR- LC(SCLBDYnn) (?)
+ //
+ // ;OP(3OS21030)
+
+ // 3.2- Graphical index of navigational purpose
+ // FIXME: draw extent of available SENC in DB
+
+ //////////////////////
+ // 4- OVERSCALE
+ //
+ // FIXME: get meta date CSCL of DSPM field
+ // FIXME: get object M_CSCL or CSCALE
+ //
+ // 4.1- Overscale indication
+ // FIXME: compute, scale = [denominator of the compilation scale] /
+ // [denominator of the display scale]
+ // FIXME: draw overscale indication (ie TX("X%3.1f",scale))
+ //
+ // 4.2- Ovescale area at a chart scale boundary
+ // FIXME: test if next chart is over scale (ie going from large scale chart
+ // to a small scale chart)
+ // FIXME: draw AP(OVERSC01) on overscale part of display
+ //g_string(";OP(3OS21030)");
+
+ //
+ // 4.3- Larger scale data available
+ // FIXME: display indication of better scale available (?)
+
+
+ PRINTF("not computed\n");
+
+ return datcvr01;
+}
+
+static GString *_SEABED01(double drval1, double drval2);
+static GString *_RESCSP01(S57_geo *geo);
+static GString *DEPARE01 (S57_geo *geo)
+// Remarks: An object of the class "depth area" is coloured and covered with fill patterns
+// according to the mariners selections of shallow contour, safety contour and
+// deep contour. This requires a decision making process provided by the sub-procedure
+// "SEABED01" which is called by this symbology procedure.
+// Objects of the class "dredged area" are handled by this routine as well to
+// ensure a consistent symbolization of areas that represent the surface of the
+// seabed.
+{
+ GString *depare01 = NULL;
+ int objl = 0;
+ GString *objlstr = NULL;
+ GString *drval1str = S57_getAttVal(geo, "DRVAL1");
+ double drval1 = UNKNOWN;
+ GString *drval2str = S57_getAttVal(geo, "DRVAL2");
+ double drval2 = UNKNOWN;
+
+ if (NULL == drval1str || NULL == drval2str) {
+ PRINTF("ERROR: drval1 or drval2 should have the value UNKNOWN\n");
+ return NULL;
+ }
+
+ drval1 = (NULL == drval1str) ? -1.0 : atof(drval1str->str);
+ drval2 = (NULL == drval2str) ? drval1+0.01 : atof(drval2str->str);
+
+ depare01 = _SEABED01(drval1, drval2);
+
+ objlstr = S57_getAttVal(geo, "OBJL");
+ objl = (NULL == objlstr) ? 0 : atoi(objlstr->str);
+
+ if (DRGARE == objl) {
+ g_string_append(depare01, ";AP(DRGARE01)");
+ g_string_append(depare01, ";LS(DASH,1,CHGRF)");
+
+ if (NULL != S57_getAttVal(geo, "RESTRN")) {
+ GString *rescsp01 = _RESCSP01(geo);
+ if (NULL != rescsp01) {
+ g_string_append(depare01, rescsp01->str);
+ g_string_free(rescsp01, TRUE);
+ }
+ }
+
+ }
+
+ return depare01;
+}
+
+static GString *_SNDFRM02(S57_geo *geo, double depth_value);
+static GString *DEPCNT02 (S57_geo *geo)
+// Remarks: An object of the class "depth contour" or "line depth area" is highlighted and must
+// be shown under all circumstances if it matches the safety contour depth value
+// entered by the mariner (see IMO PS 3.6). But, while the mariner is free to enter any
+// safety contour depth value that he thinks is suitable for the safety of his ship, the
+// SENC only contains a limited choice of depth contours. This symbology procedure
+// determines whether a contour matches the selected safety contour. If the selected
+// safety contour does not exist in the data, the procedure will default to the next deeper
+// contour. The contour selected is highlighted as the safety contour and put in
+// DISPLAYBASE. The procedure also identifies any line segment of the spatial
+// component of the object that has a "QUAPOS" value indicating unreliable
+// positioning, and symbolizes it with a double dashed line.
+//
+// Note: Depth contours are not normally labeled. The ECDIS may provide labels, on demand
+// only as with other text, or provide the depth value on cursor picking
+{
+ GString *depcnt02 = NULL;
+ int safe = FALSE; // initialy not a safety contour
+ GString *objlstr = NULL;
+ int objl = 0;
+ GString *quaposstr = NULL;
+ int quapos = 0;
+ double depth_value;
+
+ objlstr = S57_getAttVal(geo, "OBJL");
+ objl = (NULL == objlstr) ? 0 : atoi(objlstr->str);
+
+ if (DEPARE==objl && LINES_T==S57_getObjtype(geo))
+ {
+ GString *drval1str = S57_getAttVal(geo, "DRVAL1");
+ double drval1 = (NULL == drval1str) ? 0.0 : atof(drval1str->str);
+ GString *drval2str = S57_getAttVal(geo, "DRVAL2");
+ double drval2 = (NULL == drval2str) ? drval1 : atof(drval2str->str);
+
+ if (drval1 <= S52_getMarinerParam(S52_MAR_SAFETY_CONTOUR))
+ {
+ if (drval2 >= S52_getMarinerParam(S52_MAR_SAFETY_CONTOUR))
+ safe = TRUE;
+ }
+ else
+ {
+ if (1 == S52_state)
+ return NULL;
+ else
+ {
+ S57_geo *geoTmp = geo;
+
+ // get area DEPARE & DRGARE that intersect this line
+ while (NULL != (geoTmp = S57_nextObj(geoTmp))) {
+ drval1str = S57_getAttVal(geoTmp, "DRVAL1");
+ drval1 = (NULL == drval1str) ? 0.0 : atof(drval1str->str);
+
+ if (NULL == drval1str) {
+ safe = TRUE;
+ break;
+ }
+
+ if (drval1 < S52_getMarinerParam(S52_MAR_SAFETY_CONTOUR)) {
+ safe = TRUE;
+ break;
+ }
+ }
+ // debug trace
+ //if (safe) PRINTF("** DEPARE: SAFE FOUND**\n");
+ }
+ }
+
+ depth_value = drval1;
+
+ }
+ else
+ {
+ // continuation A (DEPCNT)
+ GString *valdcostr = S57_getAttVal(geo, "VALDCO");
+ double valdco = (NULL == valdcostr) ? 0.0 : atof(valdcostr->str);
+
+ depth_value = valdco;
+
+ if (valdco == S52_getMarinerParam(S52_MAR_SAFETY_CONTOUR))
+ safe = TRUE; // this is useless !?!?
+ else
+ {
+ if (valdco > S52_getMarinerParam(S52_MAR_SAFETY_CONTOUR)) {
+ if (1 == S52_state)
+ return NULL;
+ else {
+ S57_geo *geoTmp = geo;
+
+ // get area DEPARE & DRGARE that intersect this line
+ while (NULL != (geoTmp = S57_nextObj(geoTmp))){
+ GString *drval1str = S57_getAttVal(geoTmp, "DRVAL1");
+ double drval1 = (NULL == drval1str) ? 0.0 : atof(drval1str->str);
+
+ if (NULL == drval1str) {
+ safe = TRUE;
+ break;
+ }
+
+ if (drval1 < S52_getMarinerParam(S52_MAR_SAFETY_CONTOUR)) {
+ safe = TRUE;
+ break;
+ }
+ }
+ // debug trace
+ //if (safe) PRINTF("** DEPCN: SAFE FOUND**\n");
+ }
+ }
+ }
+ }
+
+ // Continuation B
+ quaposstr = S57_getAttVal(geo, "QUAPOS");
+ if (NULL != quaposstr) {
+ quapos = atoi(quaposstr->str);
+ if ( 2 <= quapos && quapos < 10) {
+ if (safe)
+ depcnt02 = g_string_new(";LS(DASH,2,DEPSC)");
+ else
+ depcnt02 = g_string_new(";LS(DASH,1,DEPCN)");
+ }
+ } else {
+ if (safe)
+ depcnt02 = g_string_new(";LS(SOLD,2,DEPSC)");
+ else
+ depcnt02 = g_string_new(";LS(SOLD,1,DEPCN)");
+ }
+
+ if (safe) {
+ S57_setAtt(geo, "SCAMIN", "INFINITE");
+ depcnt02 = g_string_prepend(depcnt02, ";OP(8OD13010)");
+ } else
+ depcnt02 = g_string_prepend(depcnt02, ";OP(---33020)");
+
+ // facultative in S-52
+ //if (TRUE == S52_getMarinerParam(S52_MAR_SHOW_TEXT)) {
+ // GString *sndfrm02 = _SNDFRM02(geo, depth_value);
+ // depcnt02 = g_string_append(depcnt02, sndfrm02->str);
+ // g_string_free(sndfrm02, TRUE);
+ //}
+
+ // debug
+ //PRINTF("depth= %f\n", depth_value);
+
+ S57_unlinkObj(geo);
+
+ return depcnt02;
+}
+
+static double _DEPVAL01(S57_geo *geo, double least_depth)
+// Remarks: S-57 Appendix B1 Annex A requires in Section 6 that areas of rocks be
+// encoded as area obstruction, and that area OBSTRNs and area WRECKS
+// be covered by either group 1 object DEPARE or group 1 object UNSARE.
+// If the value of the attribute VALSOU for an area OBSTRN or WRECKS
+// is missing, the DRVAL1 of an underlying DEPARE is the preferred default
+// for establishing a depth value. This procedure either finds the shallowest
+// DRVAL1 of the one or more underlying DEPAREs, or returns an
+// "unknown"" depth value to the main procedure for the next default
+// procedure.
+
+// NOTE: UNSARE test is useless since least_depth is already UNKNOWN
+{
+ least_depth = UNKNOWN;
+
+
+ S57_geo *geoTmp = geo;
+
+ // NOTE: the geo list is unchange (_UDWHAZ03 will unlink geo)
+ while (NULL != (geoTmp = S57_nextObj(geoTmp))) {
+ GString *objlstr = S57_getAttVal(geoTmp, "OBJL");
+ int objl = (NULL == objlstr) ? 0 : atoi(objlstr->str);
+
+ // get area DEPARE that intersect this area
+ if (DEPARE==objl && LINES_T==S57_getObjtype(geo)) {
+ GString *drval1str = S57_getAttVal(geoTmp, "DRVAL1");
+ double drval1 = (NULL == drval1str) ? 9.0 : atof(drval1str->str);
+
+ if (NULL != drval1str) {
+ if (UNKNOWN==least_depth || least_depthstr);
+
+ if ( NULL != catlitstr) {
+ _parseList(catlitstr->str, catlit);
+
+ // FIXME: OR vs AND/OR
+ if (strpbrk(catlit, "\010\013")) {
+ g_string_append(lights05, ";SY(LIGHTS82)");
+ return lights05;
+ }
+
+ if (strpbrk(catlit, "\011")) {
+ g_string_append(lights05, ";SY(LIGHTS81)");
+ return lights05;
+ }
+
+ if (strpbrk(catlit, "\001\020")) {
+ orientstr = S57_getAttVal(geo, "ORIENT");
+ if (NULL != orientstr) {
+ // FIXME: create a geo object (!?) LINE of lenght VALNMR
+ // using ORIENT (from seaward) & POINT_T position
+ g_string_append(lights05, ";LS(DASH,1,CHBLK)");
+ }
+ }
+ }
+
+ // Continuation A
+ colourstr = S57_getAttVal(geo, "COLOUR");
+ if (NULL != colourstr)
+ _parseList(colourstr->str, colist);
+ else {
+ colist[0] = '\014'; // maganta (12)
+ colist[1] = '\000';
+ }
+
+ sectr1str = S57_getAttVal(geo, "SECTR1");
+ sectr1 = (NULL == sectr1str) ? 0.0 : atof(sectr1str->str);
+ sectr2str = S57_getAttVal(geo, "SECTR2");
+ sectr2 = (NULL == sectr2str) ? 0.0 : atof(sectr2str->str);
+
+ if (NULL==sectr1str || NULL==sectr2str) {
+ // not a sector light
+ char *sym;
+
+ if (1==S52_state) {
+ _setPtPos(geo, LIGHTLIST);
+ g_string_free(lights05, TRUE);
+ return NULL;
+ } else
+ flare_at_45 = _atPtPos(geo, LIGHTLIST);
+
+ sym = _selSYcol(colist);
+
+ if (strpbrk(catlit, "\001\020")) {
+ if (NULL != orientstr){
+ g_string_append(lights05, sym);
+ g_string_sprintfa(lights05, ",%s)", orientstr->str);
+ g_string_append(lights05, ";TE('%03.0lf deg','ORIENT',3,3,3,'15110',3,1,CHBLK,23)" );
+ } else
+ g_string_append(lights05, ";SY(QUSMRK1)");
+ } else {
+ g_string_append(lights05, sym);
+ if (flare_at_45)
+ g_string_append(lights05, ",145)");
+ else
+ g_string_append(lights05, ",135)");
+ }
+
+ if (TRUE == S52_getMarinerParam(S52_MAR_SHOW_TEXT)) {
+ GString *litdsn01 = _LITDSN01(geo);
+ if (NULL != litdsn01){
+ g_string_append(lights05, ";TX('");
+ g_string_append(lights05, litdsn01->str);
+ g_string_free(litdsn01, TRUE);
+
+ if (flare_at_45)
+ g_string_append(lights05, "',3,3,3,'15110',2,-1,CHBLK,23)" );
+ else
+ g_string_append(lights05, "',3,2,3,'15110',2,0,CHBLK,23)" );
+ }
+ }
+
+ return lights05;
+ }
+
+ // Continuation B --sector light
+ if (NULL == sectr1str) {
+ sectr1 = 0.0;
+ sectr2 = 0.0;
+ } else
+ sweep = (sectr1 > sectr2) ? sectr2-sectr1+360 : sectr2-sectr1;
+
+
+ if (sweep<1.0 || sweep==360.0) {
+ // handle all round light
+ char *sym = _selSYcol(colist);;
+
+ g_string_append(lights05, sym);
+ g_string_append(lights05, ",135)");
+
+ if (TRUE == S52_getMarinerParam(S52_MAR_SHOW_TEXT)) {
+ GString *litdsn01 = _LITDSN01(geo);
+ if (NULL != litdsn01) {
+ g_string_append(lights05, ";TX('");
+ g_string_append(lights05, litdsn01->str);
+ g_string_append(lights05, "',3,2,3,'15110',2,0,CHBLK,23)" );
+ g_string_free(litdsn01, TRUE);
+ }
+
+ }
+
+ return lights05;
+ }
+
+ // scan for other lights with sector overlap at this position
+ // compute light sector radius acording to other sector
+ if (1 == S52_state) {
+ _setPtPos(geo, SECTRLIST);
+ g_string_free(lights05, TRUE);
+ return NULL;
+ } else {
+ extend_arc_radius = _atPtPos(geo, SECTRLIST);
+
+ // passe value via attribs to _renderAC
+ if (extend_arc_radius)
+ // FIXME: draw radius 25 mm
+ S57_setAtt(geo, "extend_arc_radius", "Y");
+ else
+ // FIXME: draw radius 20 mm
+ S57_setAtt(geo, "extend_arc_radius", "N");
+ }
+
+ // setup sector
+ {
+ char litvis[LISTSIZE] = {'\0'}; // list visibility
+ GString *litvisstr = S57_getAttVal(geo, "LITVIS");
+
+ // sector leg --logic is _renderLS()
+ g_string_append(lights05, ";LS(DASH,1,CHBLK)");
+
+ // get light vis.
+ if (NULL != litvisstr) _parseList(litvisstr->str, litvis);
+
+ // faint light
+ // FIXME: spec say OR (ie 1 number) the code is AND/OR
+ if (strpbrk(litvis, "\003\007\010")) {
+ // NOTE: LS(DASH,1,CHBLK)
+ // pass flag to _renderAC()
+ g_string_append(lights05, ";AC(CHBLK)");
+ S57_setAtt(geo, "faint_light", "Y");
+
+ } else {
+ // set arc colour
+ char *sym = ";AC(CHMGD)"; // other
+
+ // max 1 color
+ if ('\0' == colist[1]) {
+ if (strpbrk(colist, "\003"))
+ sym = ";AC(LITRD)";
+ else if (strpbrk(colist, "\004"))
+ sym = ";AC(LITGN)";
+ else if (strpbrk(colist, "\001\006\013"))
+ sym = ";AC(LITYW)";
+ } else {
+ // max 2 color
+ if ('\0' == colist[2]) {
+ if (strpbrk(colist, "\001") && strpbrk(colist, "\003"))
+ sym = ";AC(LITRD)";
+ else if (strpbrk(colist, "\001") && strpbrk(colist, "\004"))
+ sym = ";AC(LITGN)";
+ }
+ }
+
+ g_string_append(lights05, sym);
+ }
+ }
+
+ return lights05;
+}
+
+static GString *_LITDSN01(S57_geo *geo)
+// Remarks: In S-57 the light characteristics are held as a series of attributes values. The
+// mariner may wish to see a light description text string displayed on the
+// screen similar to the string commonly found on a paper chart. This
+// conditional procedure, reads the attribute values from the above list of
+// attributes and composes a light description string which can be displayed.
+// This procedure is provided as a C function which has as input, the above
+// listed attribute values and as output, the light description.
+{
+ GString *litdsn01 = g_string_new("");
+ GString *gstr = NULL; // tmp
+
+ // FIXME: need grammar to create light's text
+
+ // CATLIT, LITCHR, COLOUR, HEIGHT, LITCHR, SIGGRP, SIGPER, STATUS, VALNMR
+
+ // CATLIT
+ gstr = S57_getAttVal(geo, "CATLIT");
+ if (NULL != gstr)
+ g_string_append(litdsn01, gstr->str);
+
+ /*
+ 1: directional function IP 30.1-3; 475.7;
+ 2: rear/upper light
+ 3: front/lower light
+ 4: leading light IP 20.1-3; 475.6;
+ 5: aero light IP 60; 476.1;
+ 6: air obstruction light IP 61; 476.2;
+ 7: fog detector light IP 62; 477;
+ 8: flood light IP 63; 478.2;
+ 9: strip light IP 64; 478.5;
+ 10: subsidiary light IP 42; 471.8;
+ 11: spotlight
+ 12: front
+ 13: rear
+ 14: lower
+ 15: upper
+ 16: moire' effect IP 31; 475.8;
+ 17: emergency
+ 18: bearing light 478.1;
+ 19: horizontally disposed
+ 20: vertically disposed
+ */
+
+ // LITCHR
+ gstr = S57_getAttVal(geo, "LITCHR");
+ if (NULL != gstr)
+ g_string_append(litdsn01, gstr->str);
+
+ /*
+ 1: fixed IP 10.1;
+ 2: flashing IP 10.4;
+ 3: long-flashing IP 10.5;
+ 4: quick-flashing IP 10.6;
+ 5: very quick-flashing IP 10.7;
+ 6: ultra quick-flashing IP 10.8;
+ 7: isophased IP 10.3;
+ 8: occulting IP 10.2;
+ 9: interrupted quick-flashing IP 10.6;
+ 10: interrupted very quick-flashing IP 10.7;
+ 11: interrupted ultra quick-flashing IP 10.8;
+ 12: morse IP 10.9;
+ 13: fixed/flash IP 10.10;
+ 14: flash/long-flash
+ 15: occulting/flash
+ 16: fixed/long-flash
+ 17: occulting alternating
+ 18: long-flash alternating
+ 19: flash alternating
+ 20: group alternating
+ 21: 2 fixed (vertical)
+ 22: 2 fixed (horizontal)
+ 23: 3 fixed (vertical)
+ 24: 3 fixed (horizontal)
+ 25: quick-flash plus long-flash
+ 26: very quick-flash plus long-flash
+ 27: ultra quick-flash plus long-flash
+ 28: alternating
+ 29: fixed and alternating flashing
+ */
+
+ // COLOUR,
+ gstr = S57_getAttVal(geo, "COLOUR");
+ if (NULL != gstr)
+ g_string_append(litdsn01, gstr->str);
+
+ /*
+ 1: white IP 11.1; 450.2-3;
+ 2: black
+ 3: red IP 11.2; 450.2-3;
+ 4: green IP 11.3; 450.2-3;
+ 5: blue IP 11.4; 450.2-3;
+ 6: yellow IP 11.6; 450.2-3;
+ 7: grey
+ 8: brown
+ 9: amber IP 11.8; 450.2-3;
+ 10: violet IP 11.5; 450.2-3;
+ 11: orange IP 11.7; 450.2-3;
+ 12: magenta
+ 13: pink
+ */
+
+ // HEIGHT, xxx.x
+ gstr = S57_getAttVal(geo, "HEIGHT");
+ if (NULL != gstr)
+ g_string_append(litdsn01, gstr->str);
+
+
+ // SIGGRP, (c)(c) ...
+ gstr = S57_getAttVal(geo, "SIGGRP");
+ if (NULL != gstr)
+ g_string_append(litdsn01, gstr->str);
+
+
+ // SIGPER, xx.xx
+ gstr = S57_getAttVal(geo, "SIGPER");
+ if (NULL != gstr)
+ g_string_append(litdsn01, gstr->str);
+
+
+ // STATUS,
+ gstr = S57_getAttVal(geo, "STATUS");
+ if (NULL != gstr)
+ g_string_append(litdsn01, gstr->str);
+
+ /*
+ 1: permanent
+ 2: occasional IP 50; 473.2;
+ 3: recommended IN 10; 431.1;
+ 4: not in use IL 14, 44; 444.7;
+ 5: periodic/intermittent IC 21; IQ 71; 353.3; 460.5;
+ 6: reserved IN 12.9;
+ 7: temporary IP 54;
+ 8: private IQ 70;
+ 9: mandatory
+ 10: destroyed/ruined
+ 11: extinguished
+ 12: illuminated
+ 13: historic
+ 14: public
+ 15: synchronized
+ 16: watched
+ 17: un-watched
+ 18: existence doubtful
+ */
+
+ // VALNMR, xx.x
+ gstr = S57_getAttVal(geo, "VALNMR");
+ if (NULL != gstr)
+ g_string_append(litdsn01, gstr->str);
+
+
+ PRINTF("FIXME: lights description not translated into text\n");
+
+ return litdsn01;
+}
+
+static GString *_UDWHAZ03(S57_geo *geo, double depth_value);
+static GString *_QUAPNT01(S57_geo *geo);
+
+static GString *OBSTRN04 (S57_geo *geo)
+// Remarks: Obstructions or isolated underwater dangers of depths less than the safety
+// contour which lie within the safe waters defined by the safety contour are
+// to be presented by a specific isolated danger symbol and put in IMO
+// category DISPLAYBASE (see (3), App.2, 1.3). This task is performed
+// by the sub-procedure "UDWHAZ03" which is called by this symbology
+// procedure. Objects of the class "under water rock" are handled by this
+// routine as well to ensure a consistent symbolization of isolated dangers on
+// the seabed.
+{
+ GString *obstrn04str = g_string_new("");
+ GString *sndfrm02str = NULL;
+ GString *udwhaz03str = NULL;
+ GString *valsoustr = S57_getAttVal(geo, "VALSOU");
+ double valsou = UNKNOWN;
+ double depth_value = UNKNOWN;
+ double least_depth = UNKNOWN;
+
+ // exit if not in drawing state
+ if (1 == S52_state)
+ return NULL;
+
+ if (NULL != valsoustr) {
+ valsou = atof(valsoustr->str);
+ depth_value = valsou;
+ sndfrm02str = _SNDFRM02(geo, depth_value);
+ } else {
+ if (AREAS_T == S57_getObjtype(geo))
+ least_depth = _DEPVAL01(geo, least_depth);
+
+ if (UNKNOWN != least_depth) {
+ GString *catobsstr = S57_getAttVal(geo, "CATOBS");
+ GString *watlevstr = S57_getAttVal(geo, "WATLEV");
+
+ if (NULL != catobsstr && '6' == *catobsstr->str)
+ depth_value = 0.01;
+ else
+ if (NULL == watlevstr) // default
+ depth_value = -15.0;
+ else {
+ switch (*watlevstr->str){
+ case '5': depth_value = 0.0 ; break;
+ case '3': depth_value = 0.01; break;
+ case '4':
+ case '1':
+ case '2':
+ default : depth_value = -15.0 ; break;
+ }
+ }
+ } else
+ depth_value = least_depth;
+ }
+
+ udwhaz03str = _UDWHAZ03(geo, depth_value);
+
+ if (POINT_T == S57_getObjtype(geo)) {
+ // Continuation A
+ int sounding = FALSE;
+ GString *quapnt01str = _QUAPNT01(geo);
+
+ if (NULL != udwhaz03str){
+ g_string_append(obstrn04str, udwhaz03str->str);
+ if (NULL != quapnt01str)
+ g_string_append(obstrn04str, quapnt01str->str);
+
+ if (NULL != udwhaz03str) g_string_free(udwhaz03str, TRUE);
+ if (NULL != sndfrm02str) g_string_free(sndfrm02str, TRUE);
+ if (NULL != quapnt01str) g_string_free(quapnt01str, TRUE);
+
+ return obstrn04str;
+ }
+
+ if (UNKNOWN != valsou) {
+ if (valsou <= 20.0) {
+ GString *objlstr = S57_getAttVal(geo, "OBJL");
+ int objl = (NULL == objlstr)? 0 : atoi(objlstr->str);
+ GString *watlevstr = S57_getAttVal(geo, "WATLEV");
+
+ if (UWTROC == objl) {
+ if (NULL == watlevstr) { // default
+ g_string_append(obstrn04str, ";SY(DANGER01)");
+ sounding = TRUE;
+ } else {
+ switch (*watlevstr->str){
+ case '3': g_string_append(obstrn04str, ";SY(DANGER01)"); sounding = TRUE ; break;
+ case '4':
+ case '5': g_string_append(obstrn04str, ";SY(UWTROC04)"); sounding = FALSE; break;
+ default : g_string_append(obstrn04str, ";SY(DANGER01)"); sounding = TRUE ; break;
+ }
+ }
+ } else { // OBSTRN
+ if (NULL == watlevstr) { // default
+ g_string_append(obstrn04str, ";SY(DANGER01)");
+ sounding = TRUE;
+ } else {
+ switch (*watlevstr->str) {
+ case '1':
+ case '2': g_string_append(obstrn04str, ";SY(OBSTRN11)"); sounding = FALSE; break;
+ case '3': g_string_append(obstrn04str, ";SY(DANGER01)"); sounding = TRUE; break;
+ case '4':
+ case '5': g_string_append(obstrn04str, ";SY(DANGER03)"); sounding = TRUE; break;
+ default : g_string_append(obstrn04str, ";SY(DANGER01)"); sounding = TRUE; break;
+ }
+ }
+ }
+ } else { // valsou > 20.0
+ g_string_append(obstrn04str, ";SY(DANGER02)");
+ sounding = FALSE;
+ }
+
+ } else { // NO valsou
+ GString *objlstr = S57_getAttVal(geo, "OBJL");
+ int objl = (NULL == objlstr)? 0 : atoi(objlstr->str);
+ GString *watlevstr = S57_getAttVal(geo, "WATLEV");
+
+ if (UWTROC == objl) {
+ if (NULL == watlevstr) // default
+ g_string_append(obstrn04str, ";SY(UWTROC04)");
+ else {
+ if ('3' == *watlevstr->str)
+ g_string_append(obstrn04str, ";SY(UWTROC03)");
+ else
+ g_string_append(obstrn04str, ";SY(UWTROC04)");
+ }
+
+ } else { // OBSTRN
+ if ( NULL == watlevstr) // default
+ g_string_append(obstrn04str, ";SY(OBSTRN01)");
+ else {
+ switch (*watlevstr->str) {
+ case '1':
+ case '2': g_string_append(obstrn04str, ";SY(OBSTRN11)"); break;
+ case '3': g_string_append(obstrn04str, ";SY(OBSTRN01)"); break;
+ case '4':
+ case '5':
+ default : g_string_append(obstrn04str, ";SY(OBSTRN01)"); break;
+ }
+ }
+ }
+
+ }
+
+ if (sounding && NULL != sndfrm02str)
+ g_string_append(obstrn04str, sndfrm02str->str);
+
+ if (NULL != quapnt01str)
+ g_string_append(obstrn04str, quapnt01str->str);
+
+ if (NULL != udwhaz03str) g_string_free(udwhaz03str, TRUE);
+ if (NULL != sndfrm02str) g_string_free(sndfrm02str, TRUE);
+ if (NULL != quapnt01str) g_string_free(quapnt01str, TRUE);
+
+ return obstrn04str;
+
+ } else {
+ if (LINES_T == S57_getObjtype(geo)) {
+ // Continuation B
+ GString *quaposstr = S57_getAttVal(geo, "QUAPOS");
+ int quapos = 0;
+
+ if (NULL != quaposstr) {
+ quapos = atoi(quaposstr->str);
+ if ( 2 <= quapos && quapos < 10){
+ if (NULL != udwhaz03str)
+ g_string_append(obstrn04str, ";LC(LOWACC41)");
+ else
+ g_string_append(obstrn04str, ";LC(LOWACC31)");
+ }
+ }
+
+ if (NULL != udwhaz03str)
+ g_string_append(obstrn04str, ";LS(DOTT,2,CHBLK)");
+
+ if (UNKNOWN != valsou)
+ if (valsou <= 20.0)
+ g_string_append(obstrn04str, ";LS(DOTT,2,CHBLK)");
+ else
+ g_string_append(obstrn04str, ";LS(DASH,2,CHBLK)");
+ else
+ g_string_append(obstrn04str, ";LS(DOTT,2,CHBLK)");
+
+
+ if (NULL != udwhaz03str)
+ g_string_append(obstrn04str, udwhaz03str->str);
+ else {
+ if (UNKNOWN != valsou)
+ if (valsou <= 20.0)
+ g_string_append(obstrn04str, sndfrm02str->str);
+ }
+
+ if (NULL != udwhaz03str) g_string_free(udwhaz03str, TRUE);
+ if (NULL != sndfrm02str) g_string_free(sndfrm02str, TRUE);
+
+ return obstrn04str;
+
+ } else {
+ // Continuation C (AREAS_T)
+ GString *quapnt01str = _QUAPNT01(geo);
+ if (NULL != udwhaz03str) {
+ g_string_append(obstrn04str, ";AC(DEPVS);AP(FOULAR01)");
+ g_string_append(obstrn04str, ";LS(DOTT,2,CHBLK)");
+ g_string_append(obstrn04str, udwhaz03str->str);
+ if (NULL != quapnt01str)
+ g_string_append(obstrn04str, quapnt01str->str);
+
+ if (NULL != udwhaz03str) g_string_free(udwhaz03str, TRUE);
+ if (NULL != sndfrm02str) g_string_free(sndfrm02str, TRUE);
+ if (NULL != quapnt01str) g_string_free(quapnt01str, TRUE);
+
+ return obstrn04str;
+ }
+
+ if (UNKNOWN != valsou) {
+ // BUG in CA49995B.000 if we get here because there is no color
+ // beside NODATA (ie there is a hole in group 1 area!)
+ //g_string_append(obstrn04, ";AC(UINFR)");
+
+ if (valsou <= 20.0)
+ g_string_append(obstrn04str, ";LS(DOTT,2,CHBLK)");
+ else
+ g_string_append(obstrn04str, ";LS(DASH,2,CHBLK)");
+
+ g_string_append(obstrn04str, sndfrm02str->str);
+
+ } else {
+ GString *watlevstr = S57_getAttVal(geo, "WATLEV");
+
+ if (NULL == watlevstr) // default
+ g_string_append(obstrn04str, ";AC(DEPVS);LS(DOTT,2,CHBLK)");
+ else {
+ if ('3' == *watlevstr->str) {
+ GString *catobsstr = S57_getAttVal(geo, "CATOBS");
+ if (NULL != catobsstr && '6' == *catobsstr->str)
+ g_string_append(obstrn04str, ";AC(DEPVS);AP(FOULAR01);LS(DOTT,2,CHBLK)");
+ } else {
+ switch (*watlevstr->str) {
+ case '1':
+ case '2': g_string_append(obstrn04str, ";AC(CHBRN);LS(SOLD,2,CSTLN)"); break;
+ case '4': g_string_append(obstrn04str, ";AC(DEPIT);LS(DASH,2,CSTLN)"); break;
+ case '5':
+ case '3':
+ default : g_string_append(obstrn04str, ";AC(DEPVS);LS(DOTT,2,CHBLK)"); break;
+ }
+ }
+ }
+ }
+
+ g_string_append(obstrn04str, quapnt01str->str);
+
+ if (NULL != udwhaz03str) g_string_free(udwhaz03str, TRUE);
+ if (NULL != sndfrm02str) g_string_free(sndfrm02str, TRUE);
+ if (NULL != quapnt01str) g_string_free(quapnt01str, TRUE);
+
+
+ return obstrn04str;
+ }
+
+ }
+
+
+ // check if one exit point could do!!!
+ return NULL;
+}
+
+static GString *OWNSHP02 (S57_geo *geo)
+// Remarks:
+// 1. CONNING POSITION
+// 1.1 When own-ship is drawn to scale, the conning position must be correctly located in
+// relation to the ship's outline. The conning position then serves as the pivot point for
+// the own-ship symbol, to be located by the ECDIS at the correct latitude, longitude
+// for the conning point, as computed from the positioning system, correcting for
+// antenna offset.
+// 1.2 In this procedure it is assumed that the heading line, beam bearing line and course
+// and speed vector originate at the conning point. If another point of origin is used,
+// for example to account for the varying position of the ships turning centre, this must
+// be made clear to the mariner.
+//
+// 2. DISPLAY OPTIONS
+// 2.1 Only the ship symbol is mandatory for an ECDIS. The mariner should be prompted
+// to select from the following additional optional features:
+// - display own-ship as:
+// 1. symbol, or
+// 2. scaled outline
+// - select time period determining vector length for own-ship and other vessel course and speed
+// vectors, (all vectors must be for the same time period),
+// - display own-ship vector,
+// - select ground or water stabilization for all vectors, and select whether to display the type of
+// stabilization, (by arrowhead),
+// - select one-minute or six-minute vector time marks,
+// - select whether to show a heading line, to the edge of the display window,
+// - select whether to show a beam bearing line, and if so what length (default: 10mm total
+// length).
+{
+ PRINTF("Mariner's object not drawn\n");
+ return NULL;
+}
+
+static GString *PASTRK01 (S57_geo *geo)
+// Remarks: This conditional symbology procedure was designed to allow the mariner
+// to select time labels at the pasttrack (see (3) 10.5.11.1). The procedure also
+// cares for the presentation of primary and secondary pasttrack.
+//
+// The manufacturer should define his own data class (spatial primitive) in xyt
+// (position and time) in order to represent Pastrk.
+{
+
+ PRINTF("Mariner's object not drawn\n");
+ return NULL;
+}
+
+static GString *_QUALIN01(S57_geo *geo);
+static GString *QUAPOS01 (S57_geo *geo)
+// Remarks: The attribute QUAPOS, which identifies low positional accuracy, is attached
+// to the spatial object, not the feature object.
+//
+// This procedure passes the object to procedure QUALIN01 or QUAPNT01,
+// which traces back to the spatial object, retrieves any QUAPOS attributes,
+// and returns the appropriate symbolization to QUAPOS01.
+{
+ GString *quapos01 = NULL;
+
+ if (LINES_T == S57_getObjtype(geo))
+ quapos01 = _QUALIN01(geo);
+ else
+ quapos01 = _QUAPNT01(geo);
+
+ return quapos01;
+}
+
+static GString *_QUALIN01(S57_geo *geo)
+// Remarks: The attribute QUAPOS, which identifies low positional accuracy, is attached
+// only to the spatial component(s) of an object.
+//
+// A line object may be composed of more than one spatial object.
+//
+// This procedure looks at each of the spatial
+// objects, and symbolizes the line according to the positional accuracy.
+{
+ GString *qualino1 = NULL;
+ GString *quaposstr = S57_getAttVal(geo, "QUAPOS");
+ int quapos = 0;
+ char *line = NULL;
+
+ if (NULL != quaposstr) {
+ quapos = atoi(quaposstr->str);
+ if ( 2 <= quapos && quapos < 10)
+ line = ";LC(LOWACC21)";
+ } else {
+ GString *objlstr = S57_getAttVal(geo, "OBJL");
+ int objl = (NULL == objlstr)? 0 : atoi(objlstr->str);
+
+ if (COALNE == objl) {
+ GString *conradstr = S57_getAttVal(geo, "CONRAD");
+
+ if (NULL != conradstr) {
+ if ('1' == *conradstr->str)
+ line = ";LS(SOLD,3,CHMGF);LS(SOLD,1,CSTLN)";
+ else
+ line = ";LS(SOLD,1,CSTLN)";
+ } else
+ line = ";LS(SOLD,1,CSTLN)";
+
+ } else //LNDARE
+ line = ";LS(SOLD,1,CSTLN)";
+ }
+
+ if (NULL != line)
+ qualino1 = g_string_new(line);
+
+ return qualino1;
+}
+
+static GString *_QUAPNT01(S57_geo *geo)
+// Remarks: The attribute QUAPOS, which identifies low positional accuracy, is attached
+// only to the spatial component(s) of an object.
+//
+// This procedure retrieves any QUAPOS attributes, and returns the
+// appropriate symbols to the calling procedure.
+{
+ GString *quapnt01 = NULL;
+ int accurate = TRUE;
+ GString *quaposstr = S57_getAttVal(geo, "QUAPOS");
+ int quapos = (NULL == quaposstr)? 0 : atoi(quaposstr->str);
+
+ if (NULL != quaposstr) {
+ if ( 2 <= quapos && quapos < 10)
+ accurate = FALSE;
+ }
+
+ if (accurate)
+ quapnt01 = g_string_new(";SY(LOWACC01)");
+
+ return quapnt01;
+}
+
+static GString *SLCONS03 (S57_geo *geo)
+// Remarks: Shoreline construction objects which have a QUAPOS attribute on their
+// spatial component indicating that their position is unreliable are symbolized
+// by a special linestyle in the place of the varied linestyles normally used.
+// Otherwise this procedure applies the normal symbolization.
+{
+ GString *slcons03 = NULL;
+ GString *valstr = NULL;
+ char *cmdw = NULL; // command word
+ GString *quaposstr = S57_getAttVal(geo, "QUAPOS");
+ int quapos = (NULL == quaposstr)? 0 : atoi(quaposstr->str);
+
+ if (POINT_T == S57_getObjtype(geo)) {
+ if (NULL != quaposstr) {
+ if (2 <= quapos && quapos < 10)
+ cmdw =";SY(LOWACC01)";
+ }
+ } else {
+ // LINE_T and AREA_T are the same
+ if (NULL != quaposstr) {
+ if (2 <= quapos && quapos < 10)
+ cmdw =";LC(LOWACC01)";
+ } else {
+ valstr = S57_getAttVal(geo, "CONDTN");
+
+ if (NULL != valstr && ( '1' == *valstr->str || '2' == *valstr->str))
+ cmdw = ";LS(DASH,1,CSTLN)";
+ else {
+ int val = 0;
+ valstr = S57_getAttVal(geo, "CATSLC");
+ val = (NULL == valstr)? 0 : atoi(valstr->str);
+
+ if (NULL != valstr && ( 6 == val || 15 == val || 16 == val ))
+ cmdw = ";LS(SOLD,4,CSTLN)";
+ else {
+ valstr = S57_getAttVal(geo, "WATLEV");
+
+ if (NULL != valstr && '2' == *valstr->str)
+ cmdw = ";LS(SOLD,2,CSTLN)";
+ else
+ if (NULL != valstr && ('3' == *valstr->str || '4' == *valstr->str))
+ cmdw = ";LS(DASH,2,CSTLN)";
+ else
+ cmdw = ";LS(SOLD,2,CSTLN)"; // default
+
+ }
+ }
+ }
+ }
+
+ // WARNING: not explicitly specified in S-52 !!
+ // FIXME:this is to put AC(DEPIT) --intertidal area
+ // Could this be bug in OGR ?
+ /*
+ if (AREAS_T == S57_getObjtype(geo)) {
+ GString *seabed01 = NULL;
+ GString *drval1str = S57_getAttVal(geo, "DRVAL1");
+ double drval1 = (NULL == drval1str)? -UNKNOWN : atof(drval1str->str);
+ GString *drval2str = S57_getAttVal(geo, "DRVAL2");
+ double drval2 = (NULL == drval2str)? -UNKNOWN : atof(drval2str->str);
+ // NOTE: change sign of infinity (minus) to get out of bound in seabed01
+
+
+ PRINTF("***********drval1=%f drval2=%f \n", drval1, drval2);
+ seabed01 = _SEABED01(drval1, drval2);
+ slcons03 = g_string_new(seabed01->str);
+ g_string_free(seabed01, TRUE);
+
+ }
+ */
+
+ if (NULL != cmdw) {
+ if (NULL == slcons03)
+ slcons03 = g_string_new(cmdw);
+ else
+ g_string_append(slcons03, cmdw);
+ }
+
+ return slcons03;
+}
+
+static GString *RESARE02 (S57_geo *geo)
+// Remarks: A list-type attribute is used because an area of the object class RESARE may
+// have more than one category (CATREA). For example an inshore traffic
+// zone might also have fishing and anchoring prohibition and a prohibited
+// area might also be a bird sanctuary or a mine field.
+//
+// This conditional procedure is set up to ensure that the categories of most
+// importance to safe navigation are prominently symbolized, and to pass on
+// all given information with minimum clutter. Only the most significant
+// restriction is symbolized, and an indication of further limitations is given by
+// a subscript "!" or "I". Further details are given under conditional
+// symbology procedure RESTRN01
+//
+// Other object classes affected by attribute RESTRN are handled by
+// conditional symbology procedure RESTRN01.
+{
+ GString *resare02 = g_string_new("");
+ GString *restrnstr = S57_getAttVal(geo, "RESTRN");
+ char restrn[LISTSIZE] = {'\0'};
+ GString *catreastr = S57_getAttVal(geo, "CATREA");
+ char catrea[LISTSIZE] = {'\0'};
+ char *symb = NULL;
+ char *line = NULL;
+ char *prio = NULL;
+
+ if ( NULL != restrnstr) {
+ _parseList(restrnstr->str, restrn);
+
+ if (NULL != catreastr) _parseList(catreastr->str, catrea);
+
+ if (strpbrk(restrn, "\007\010\016")) {
+ // Continuation A
+ if (strpbrk(restrn, "\001\002\003\004\005\006"))
+ symb = ";SY(ENTRES61)";
+ else {
+ if (NULL != catreastr && strpbrk(catrea, "\001\010\011\014\016\023\025\031"))
+ symb = ";SY(ENTRES61)";
+ else {
+ if (strpbrk(restrn, "\011\012\013\014\015"))
+ symb = ";SY(ENTRES71)";
+ else {
+ if (NULL != catreastr && strpbrk(catrea, "\004\005\006\007\012\022\024\026\027\030"))
+ symb = ";SY(ENTRES71)";
+ else
+ symb = ";SY(ENTRES51)";
+ }
+ }
+ }
+
+ if (TRUE == S52_getMarinerParam(S52_MAR_SYMBOLIZED_BND))
+ line = ";LC(CTYARE51)";
+ else
+ line = ";LS(DASH,2,CHMGD)";
+
+ prio = ";OP(6---)"; // display prio set to 6
+
+ } else {
+ if (strpbrk(restrn, "\001\002")) {
+ // Continuation B
+ if (strpbrk(restrn, "\003\004\005\006"))
+ symb = ";SY(ACHRES61)";
+ else {
+ if (NULL != catreastr && strpbrk(catrea, "\001\010\011\014\016\023\025\031"))
+ symb = ";SY(ACHRES61)";
+ else {
+ if (strpbrk(restrn, "\011\012\013\014\015"))
+ symb = ";SY(ACHRES71)";
+ else {
+ if (NULL != catreastr && strpbrk(catrea, "\004\005\006\007\012\022\024\026\027\030"))
+ symb = ";SY(ACHRES71)";
+ else
+ symb = ";SY(ACHRES51)";
+ }
+ }
+ }
+
+ if (TRUE == S52_getMarinerParam(S52_MAR_SYMBOLIZED_BND))
+ line = ";LC(ACHRE51)";
+ else
+ line = ";LS(DASH,2,CHMGD)";
+
+ prio = ";OP(6---)"; // display prio set to 6
+
+ } else {
+ if (strpbrk(restrn, "\003\004\005\006")) {
+ // Continuation C
+ if (NULL != catreastr && strpbrk(catrea, "\001\010\011\014\016\023\025\031"))
+ symb = ";SY(FSHRES51)";
+ else {
+ if (strpbrk(restrn, "\011\012\013\014\015"))
+ symb = ";SY(FSHRES71)";
+ else{
+ if (NULL != catreastr && strpbrk(catrea, "\004\005\006\007\012\022\024\026\027\030"))
+ symb = ";SY(FSHRES71)";
+ else
+ symb = ";SY(FSHRES51)";
+ }
+ }
+
+ if (TRUE == S52_getMarinerParam(S52_MAR_SYMBOLIZED_BND))
+ line = ";LC(FSHRES51)";
+ else
+ line = ";LS(DASH,2,CHMGD)";
+
+ prio = ";OP(6---)"; // display prio set to 6
+
+ } else {
+ if (strpbrk(restrn, "\011\012\013\014\015"))
+ symb = ";SY(INFARE51)";
+ else
+ symb = ";SY(RSRDEF51)";
+
+ if (TRUE == S52_getMarinerParam(S52_MAR_SYMBOLIZED_BND))
+ line = ";LC(CTYARE51)";
+ else
+ line = ";LS(DASH,2,CHMGD)";
+
+ }
+ }
+ }
+
+ } else {
+ // Continuation D
+ if (NULL != catreastr) {
+ if (strpbrk(catrea, "\001\010\011\014\016\023\025\031")) {
+ if (strpbrk(catrea, "\004\005\006\007\012\022\024\026\027\030"))
+ symb = ";SY(CTYARE71)";
+ else
+ symb = ";SY(CTYARE51)";
+ } else {
+ if (strpbrk(catrea, "\004\005\006\007\012\022\024\026\027\030"))
+ symb = ";SY(INFARE71)";
+ else
+ symb = ";SY(RSRDEF51)";
+ }
+ } else
+ symb = ";SY(RSRDEF51)";
+
+ if (TRUE == S52_getMarinerParam(S52_MAR_SYMBOLIZED_BND))
+ line = ";LC(CTYARE51)";
+ else
+ line = ";LS(DASH,2,CHMGD)";
+ }
+
+ // create command word
+ if (NULL != prio)
+ g_string_append(resare02, prio);
+ g_string_append(resare02, line);
+ g_string_append(resare02, symb);
+
+ return resare02;
+}
+
+static GString *RESTRN01 (S57_geo *geo)
+// Remarks: Objects subject to RESTRN01 are actually symbolised in sub-process
+// RESCSP01, since the latter can also be accessed from other conditional
+// symbology procedures. RESTRN01 merely acts as a "signpost" for
+// RESCSP01.
+//
+// Object class RESARE is symbolised for the effect of attribute RESTRN in a separate
+// conditional symbology procedure called RESARE02.
+//
+// Since many of the areas concerned cover shipping channels, the number of symbols used
+// is minimised to reduce clutter. To do this, values of RESTRN are ranked for significance
+// as follows:
+// "Traffic Restriction" values of RESTRN:
+// (1) RESTRN 7,8: entry prohibited or restricted
+// RESTRN 14: IMO designated "area to be avoided" part of a TSS
+// (2) RESTRN 1,2: anchoring prohibited or restricted
+// (3) RESTRN 3,4,5,6: fishing or trawling prohibited or restricted
+// (4) "Other Restriction" values of RESTRN are:
+// RESTRN 9, 10: dredging prohibited or restricted,
+// RESTRN 11,12: diving prohibited or restricted,
+// RESTRN 13 : no wake area.
+{
+ GString *restrn01str = S57_getAttVal(geo, "RESTRN");
+ GString *restrn01 = NULL;
+
+ if (NULL != restrn01str)
+ restrn01 = _RESCSP01(geo);
+ else
+ restrn01 = g_string_new(";OP(----)"); // return NOOP to silence error msg
+
+ return restrn01;
+}
+
+static GString *_RESCSP01(S57_geo *geo)
+// Remarks: See procedure RESTRN01
+{
+ GString *rescsp01 = NULL;
+ GString *restrnstr = S57_getAttVal(geo, "RESTRN");
+ char restrn[LISTSIZE] = {'\0'}; // restriction list
+ char *symb = NULL;
+
+ if ( NULL != restrnstr) {
+ _parseList(restrnstr->str, restrn);
+
+ if (strpbrk(restrn, "\007\010\016")) {
+ // continuation A
+ if (strpbrk(restrn, "\001\002\003\004\005\006"))
+ symb = ";SY(ENTRES61)";
+ else {
+ if (strpbrk(restrn, "\011\012\013\014\015"))
+ symb = ";SY(ENTRES71)";
+ else
+ symb = ";SY(ENTRES51)";
+
+ }
+ } else {
+ if (strpbrk(restrn, "\001\002")) {
+ // continuation B
+ if (strpbrk(restrn, "\003\004\005\006"))
+ symb = ";SY(ACHRES61)";
+ else {
+ if (strpbrk(restrn, "\011\012\013\014\015"))
+ symb = ";SY(ACHRES71)";
+ else
+ symb = ";SY(ACHRES51)";
+ }
+
+
+ } else {
+ if (strpbrk(restrn, "\003\004\005\006")) {
+ // continuation C
+ if (strpbrk(restrn, "\011\012\013\014\015"))
+ symb = ";SY(FSHRES71)";
+ else
+ symb = ";SY(FSHRES51)";
+
+
+ } else {
+ if (strpbrk(restrn, "\011\012\013\014\015"))
+ symb = ";SY(INFARE51)";
+ else
+ symb = ";SY(RSRDEF51)";
+
+ }
+ }
+ }
+
+ rescsp01 = g_string_new(symb);
+ }
+
+ return rescsp01;
+}
+
+static GString *_SEABED01(double drval1, double drval2)
+// Remarks: An area object that is part of the seabed is coloured as necessary according
+// to the mariners selection of two shades, (shallow contour, safety contour,
+// deep contour), or four shades (safety contour only). This requires a decision
+// making process provided by this conditional symbology procedure. Note
+// that this procedure is called as a sub-procedure by other conditional
+// symbology procedures.
+//
+// Note: The requirement to show four depth shades is not mandatory. Also,
+// the requirement to show the shallow pattern is not mandatory. However,
+// both these features are strongly recommended.
+
+// return: is never NULL
+
+{
+ GString *seabed01 = NULL;
+ gboolean shallow = TRUE;
+ char *arecol = ";AC(DEPIT)";
+
+ if (drval1 >= 0.0 && drval2 > 0.0)
+ arecol = ";AC(DEPVS)";
+
+ if (TRUE == S52_getMarinerParam(S52_MAR_TWO_SHADES)){
+ if (drval1 >= S52_getMarinerParam(S52_MAR_SAFETY_CONTOUR) &&
+ drval2 > S52_getMarinerParam(S52_MAR_SAFETY_CONTOUR)) {
+ arecol = ";AC(DEPDW)";
+ shallow = FALSE;
+ }
+ } else {
+ if (drval1 >= S52_getMarinerParam(S52_MAR_SHALLOW_CONTOUR) &&
+ drval2 > S52_getMarinerParam(S52_MAR_SHALLOW_CONTOUR))
+ arecol = ";AC(DEPMS)";
+
+ if (drval1 >= S52_getMarinerParam(S52_MAR_SAFETY_CONTOUR) &&
+ drval2 > S52_getMarinerParam(S52_MAR_SAFETY_CONTOUR)) {
+ arecol = ";AC(DEPMD)";
+ shallow = FALSE;
+ }
+
+ if (drval1 >= S52_getMarinerParam(S52_MAR_DEEP_CONTOUR) &&
+ drval2 > S52_getMarinerParam(S52_MAR_DEEP_CONTOUR)) {
+ arecol = ";AC(DEPDW)";
+ shallow = FALSE;
+ }
+
+ }
+
+ seabed01 = g_string_new(arecol);
+
+ if (TRUE==S52_getMarinerParam(S52_MAR_SHALLOW_PATTERN) && TRUE==shallow)
+ g_string_append(seabed01, ";AP(DIAMOND1)");
+
+ return seabed01;
+}
+
+static GString *SOUNDG02 (S57_geo *geo)
+// Remarks: In S-57 soundings are elements of sounding arrays rather than individual
+// objects. Thus this conditional symbology procedure examines each
+// sounding of a sounding array one by one. To symbolize the depth values it
+// calls the procedure SNDFRM02 which in turn translates the depth values
+// into a set of symbols to be shown at the soundings position.
+{
+ guint npt = 0;
+ double *ppt = NULL;
+
+ if (POINT_T != S57_getObjtype(geo)) {
+ PRINTF("invalid object type (not M_PNT_T)\n");
+ //return NULL;
+ exit(0);
+ }
+
+ S57_getGeoData(geo, 0, &npt, &ppt);
+
+ return _SNDFRM02(geo, ppt[2]);
+}
+
+static GString *_SNDFRM02(S57_geo *geo, double depth_value)
+// Remarks: Soundings differ from plain text because they have to be readable under all
+// circumstances and their digits are placed according to special rules. This
+// conditional symbology procedure accesses a set of carefully designed
+// sounding symbols provided by the symbol library and composes them to
+// sounding labels. It symbolizes swept depth and it also symbolizes for low
+// reliability as indicated by attributes QUASOU and QUAPOS.
+{
+ GString *sndfrm02 = g_string_new("");
+ char *symbol_prefix = NULL;
+ GString *tecsoustr = S57_getAttVal(geo, "TECSOU");
+ char tecsou[LISTSIZE] = {'\0'};
+ GString *quasoustr = S57_getAttVal(geo, "QUASOU");
+ char quasou[LISTSIZE] = {'\0'};
+ GString *statusstr = S57_getAttVal(geo, "STATUS");
+ char status[LISTSIZE] = {'\0'};
+ double leading_digit = 0.0;
+
+ // FIXME: test to fix the rounding error (!?)
+ depth_value += (depth_value > 0.0)? 0.01: -0.01;
+ leading_digit = (int) depth_value;
+
+ if (depth_value <= S52_getMarinerParam(S52_MAR_SAFETY_DEPTH))
+ symbol_prefix = "SOUNDS";
+ else
+ symbol_prefix = "SOUNDG";
+
+ if (NULL != tecsoustr) {
+ _parseList(tecsoustr->str, tecsou);
+ if (strpbrk(tecsou, "\006"))
+ g_string_sprintfa(sndfrm02, ";SY(%sB1)", symbol_prefix);
+ }
+
+ if (NULL != quasoustr) _parseList(quasoustr->str, quasou);
+ if (NULL != statusstr) _parseList(statusstr->str, status);
+
+ if (strpbrk(quasou, "\003\004\005\010\011") || strpbrk(status, "\022"))
+ g_string_sprintfa(sndfrm02, ";SY(%sC2)", symbol_prefix);
+ else {
+ GString *quaposstr = S57_getAttVal(geo, "QUAPOS");
+ int quapos = (NULL == quaposstr)? 0 : atoi(quaposstr->str);
+
+ if (NULL != quaposstr) {
+ if (2 <= quapos && quapos < 10)
+ g_string_sprintfa(sndfrm02, ";SY(%sC2)", symbol_prefix);
+ }
+ }
+
+ // Continuation A
+ if (depth_value < 10.0) {
+ // can be above water (negative)
+ int fraction = (int)ABS((depth_value - leading_digit)*10);
+
+ g_string_sprintfa(sndfrm02, ";SY(%s1%1i)", symbol_prefix, (int)ABS(leading_digit));
+ g_string_sprintfa(sndfrm02, ";SY(%s5%1i)", symbol_prefix, fraction);
+
+ // above sea level (negative)
+ if (depth_value < 0.0)
+ g_string_sprintfa(sndfrm02, ";SY(%sA1)", symbol_prefix);
+
+ return sndfrm02;
+ }
+
+ if (depth_value < 31.0) {
+ double fraction = depth_value - leading_digit;
+
+ if (fraction != 0.0) {
+ fraction = fraction * 10;
+ if (leading_digit >= 10.0)
+ g_string_sprintfa(sndfrm02, ";SY(%s2%1i)", symbol_prefix, (int)leading_digit/10);
+
+ g_string_sprintfa(sndfrm02, ";SY(%s1%1i)", symbol_prefix, (int)leading_digit);
+ g_string_sprintfa(sndfrm02, ";SY(%s5%1i)", symbol_prefix, (int)fraction);
+
+ return sndfrm02;
+ }
+ }
+
+ // Continuation B
+ depth_value = leading_digit; // truncate to integer
+ if (depth_value < 100.0) {
+ double first_digit = leading_digit / 10;
+ double secnd_digit = leading_digit - (first_digit * 10);
+
+ g_string_sprintfa(sndfrm02, ";SY(%s1%1i)", symbol_prefix, (int)first_digit);
+ g_string_sprintfa(sndfrm02, ";SY(%s0%1i)", symbol_prefix, (int)secnd_digit);
+
+ return sndfrm02;
+ }
+
+ if (depth_value < 1000.0) {
+ double first_digit = leading_digit / 100;
+ double secnd_digit = leading_digit - (first_digit * 100);
+ double third_digit = leading_digit - (first_digit * 100) - (secnd_digit * 10);
+
+ g_string_sprintfa(sndfrm02, ";SY(%s2%1i)", symbol_prefix, (int)first_digit);
+ g_string_sprintfa(sndfrm02, ";SY(%s1%1i)", symbol_prefix, (int)secnd_digit);
+ g_string_sprintfa(sndfrm02, ";SY(%s0%1i)", symbol_prefix, (int)third_digit);
+
+ return sndfrm02;
+ }
+
+ if (depth_value < 10000.0) {
+ double first_digit = leading_digit / 1000;
+ double secnd_digit = leading_digit - (first_digit * 1000);
+ double third_digit = leading_digit - (first_digit * 1000) - (secnd_digit * 100);
+ double last_digit = leading_digit - (first_digit * 1000) - (secnd_digit * 100) - (third_digit * 100) ;
+
+ g_string_sprintfa(sndfrm02, ";SY(%s2%1i)", symbol_prefix, (int)first_digit);
+ g_string_sprintfa(sndfrm02, ";SY(%s1%1i)", symbol_prefix, (int)secnd_digit);
+ g_string_sprintfa(sndfrm02, ";SY(%s0%1i)", symbol_prefix, (int)third_digit);
+ g_string_sprintfa(sndfrm02, ";SY(%s4%1i)", symbol_prefix, (int)last_digit);
+
+ return sndfrm02;
+ }
+
+ // Continuation C
+ {
+ double first_digit = leading_digit / 10000;
+ double secnd_digit = leading_digit - (first_digit * 10000);
+ double third_digit = leading_digit - (first_digit * 10000) - (secnd_digit * 1000);
+ double fourth_digit = leading_digit - (first_digit * 10000) - (secnd_digit * 1000) - (third_digit * 100) ;
+ double last_digit = leading_digit - (first_digit * 10000) - (secnd_digit * 1000) - (third_digit * 100) - (fourth_digit * 10) ;
+
+ g_string_sprintfa(sndfrm02, ";SY(%s3%1i)", symbol_prefix, (int)first_digit);
+ g_string_sprintfa(sndfrm02, ";SY(%s2%1i)", symbol_prefix, (int)secnd_digit);
+ g_string_sprintfa(sndfrm02, ";SY(%s1%1i)", symbol_prefix, (int)third_digit);
+ g_string_sprintfa(sndfrm02, ";SY(%s0%1i)", symbol_prefix, (int)fourth_digit);
+ g_string_sprintfa(sndfrm02, ";SY(%s4%1i)", symbol_prefix, (int)last_digit);
+
+ return sndfrm02;
+ }
+
+ return sndfrm02;
+}
+
+static GString *TOPMAR01 (S57_geo *geo)
+// Remarks: Topmark objects are to be symbolized through consideration of their
+// platforms e.g. a buoy. Therefore this conditional symbology procedure
+// searches for platforms by looking for other objects that are located at the
+// same position.. Based on the finding whether the platform is rigid or
+// floating, the respective upright or sloping symbol is selected and presented
+// at the objects location. Buoy symbols and topmark symbols have been
+// carefully designed to fit to each other when combined at the same position.
+// The result is a composed symbol that looks like the traditional symbols the
+// mariner is used to.
+{
+ GString *topshpstr = S57_getAttVal(geo, "TOPSHP");
+ GString *topmar = NULL;
+ char *sy = NULL;
+
+ if (NULL == topshpstr)
+ sy = ";SY(QUESMRK1)";
+ else {
+ int floating = FALSE; // not a floating platform
+ int topshp = (NULL==topshpstr) ? 0 : atoi(topshpstr->str);
+
+ if (TRUE == _atPtPos(geo, FLOATLIST))
+ floating = TRUE;
+ else
+ // FIXME: this test is wierd since it doesn't affect 'floating'
+ if (TRUE == _atPtPos(geo, RIGIDLIST))
+ floating = FALSE;
+
+
+ if (floating) {
+ // floating platform
+ switch (topshp) {
+ case 1 : sy = ";SY(TOPMAR02)"; break;
+ case 2 : sy = ";SY(TOPMAR04)"; break;
+ case 3 : sy = ";SY(TOPMAR10)"; break;
+ case 4 : sy = ";SY(TOPMAR12)"; break;
+
+ case 5 : sy = ";SY(TOPMAR13)"; break;
+ case 6 : sy = ";SY(TOPMAR14)"; break;
+ case 7 : sy = ";SY(TOPMAR65)"; break;
+ case 8 : sy = ";SY(TOPMAR17)"; break;
+
+ case 9 : sy = ";SY(TOPMAR16)"; break;
+ case 10: sy = ";SY(TOPMAR08)"; break;
+ case 11: sy = ";SY(TOPMAR07)"; break;
+ case 12: sy = ";SY(TOPMAR14)"; break;
+
+ case 13: sy = ";SY(TOPMAR05)"; break;
+ case 14: sy = ";SY(TOPMAR06)"; break;
+ case 17: sy = ";SY(TMARDEF2)"; break;
+ case 18: sy = ";SY(TOPMAR10)"; break;
+
+ case 19: sy = ";SY(TOPMAR13)"; break;
+ case 20: sy = ";SY(TOPMAR14)"; break;
+ case 21: sy = ";SY(TOPMAR13)"; break;
+ case 22: sy = ";SY(TOPMAR14)"; break;
+
+ case 23: sy = ";SY(TOPMAR14)"; break;
+ case 24: sy = ";SY(TOPMAR02)"; break;
+ case 25: sy = ";SY(TOPMAR04)"; break;
+ case 26: sy = ";SY(TOPMAR10)"; break;
+
+ case 27: sy = ";SY(TOPMAR17)"; break;
+ case 28: sy = ";SY(TOPMAR18)"; break;
+ case 29: sy = ";SY(TOPMAR02)"; break;
+ case 30: sy = ";SY(TOPMAR17)"; break;
+
+ case 31: sy = ";SY(TOPMAR14)"; break;
+ case 32: sy = ";SY(TOPMAR10)"; break;
+ case 33: sy = ";SY(TMARDEF2)"; break;
+ default: sy = ";SY(TMARDEF2)"; break;
+ }
+ } else {
+ // not a floating platform
+ switch (topshp) {
+ case 1 : sy = ";SY(TOPMAR22)"; break;
+ case 2 : sy = ";SY(TOPMAR24)"; break;
+ case 3 : sy = ";SY(TOPMAR30)"; break;
+ case 4 : sy = ";SY(TOPMAR32)"; break;
+
+ case 5 : sy = ";SY(TOPMAR33)"; break;
+ case 6 : sy = ";SY(TOPMAR34)"; break;
+ case 7 : sy = ";SY(TOPMAR85)"; break;
+ case 8 : sy = ";SY(TOPMAR86)"; break;
+
+ case 9 : sy = ";SY(TOPMAR36)"; break;
+ case 10: sy = ";SY(TOPMAR28)"; break;
+ case 11: sy = ";SY(TOPMAR27)"; break;
+ case 12: sy = ";SY(TOPMAR14)"; break;
+
+ case 13: sy = ";SY(TOPMAR25)"; break;
+ case 14: sy = ";SY(TOPMAR26)"; break;
+ case 15: sy = ";SY(TOPMAR88)"; break;
+ case 16: sy = ";SY(TOPMAR87)"; break;
+
+ case 17: sy = ";SY(TMARDEF1)"; break;
+ case 18: sy = ";SY(TOPMAR30)"; break;
+ case 19: sy = ";SY(TOPMAR33)"; break;
+ case 20: sy = ";SY(TOPMAR34)"; break;
+
+ case 21: sy = ";SY(TOPMAR33)"; break;
+ case 22: sy = ";SY(TOPMAR34)"; break;
+ case 23: sy = ";SY(TOPMAR34)"; break;
+ case 24: sy = ";SY(TOPMAR22)"; break;
+
+ case 25: sy = ";SY(TOPMAR24)"; break;
+ case 26: sy = ";SY(TOPMAR30)"; break;
+ case 27: sy = ";SY(TOPMAR86)"; break;
+ case 28: sy = ";SY(TOPMAR89)"; break;
+
+ case 29: sy = ";SY(TOPMAR22)"; break;
+ case 30: sy = ";SY(TOPMAR86)"; break;
+ case 31: sy = ";SY(TOPMAR14)"; break;
+ case 32: sy = ";SY(TOPMAR30)"; break;
+ case 33: sy = ";SY(TMARDEF1)"; break;
+ default: sy = ";SY(TMARDEF1)"; break;
+ }
+ }
+
+ }
+
+ topmar = g_string_new(sy);
+
+ return topmar;
+}
+
+/*
+static GString *_UDWHAZ03(S57_geo *geo, double depth_value)
+// Remarks: Obstructions or isolated underwater dangers of depths less than the safety
+// contour which lie within the safe waters defined by the safety contour are
+// to be presented by a specific isolated danger symbol as hazardous objects
+// and put in IMO category DISPLAYBASE (see (3), App.2, 1.3). This task
+// is performed by this conditional symbology procedure.
+{
+ GString *udwhaz03str = NULL;
+ int danger = FALSE;
+ double safety_contour = S52_getMarinerParam(S52_MAR_SAFETY_CONTOUR);
+ S57_geo *geoTmp = geo;
+
+ if (depth_value <= safety_contour) {
+ // that intersect this point/line/area for OBSTRN04
+ // that intersect this point/area for WRECKS02
+
+ // get area DEPARE & DRGARE that intersect this point/line/area
+ while (NULL != (geoTmp = S57_nextObj(geoTmp))) {
+
+ if (LINES_T == S57_getObjtype(geoTmp)) {
+ GString *drval2str = S57_getAttVal(geoTmp, "DRVAL2");
+ double drval2 = (NULL == drval2str) ? 0.0 : atof(drval2str->str);
+
+ if (drval2 < safety_contour) {
+ danger = TRUE;
+ break;
+ }
+
+ } else {
+ //
+ GString *drval1str = S57_getAttVal(geoTmp, "DRVAL1");
+ double drval1 = (NULL == drval1str) ? 0.0 : atof(drval1str->str);
+
+ if (drval1 >= safety_contour) {
+ danger = TRUE;
+ break;
+ }
+ }
+
+ }
+
+
+ //danger = TRUE; // true
+ if (TRUE == danger) {
+ GString *watlevstr = S57_getAttVal(geo, "WATLEV");
+ if (NULL != watlevstr && ('1' == *watlevstr->str || '2' == *watlevstr->str))
+ udwhaz03str = g_string_new(";OP(--D14050");
+ else {
+ udwhaz03str = g_string_new(";OP(8OD14010);SY(ISODGR51)");
+ S57_setAtt(geo, "SCAMIN", "INFINITE");
+ }
+ }
+ }
+
+ S57_unlinkObj(geo);
+
+ return udwhaz03str;
+}
+
+static GString *VESSEL01 (S57_geo *geo)
+// Remarks: The mariner should be prompted to select from the following options:
+// - ARPA target or AIS report (overall decision or vessel by vessel) (vesrce)
+// - *time-period determining vector-length for all vectors (vecper)
+// - whether to show a vector (overall or vessel by vessel) (vestat)
+// - *whether to symbolize vector stabilization (vecstb)
+// - *whether to show one-minute or six-minute vector time marks (vecmrk)
+// - whether to show heading line on AIS vessel reports (headng)
+// * Note that the same vector parameters should be used for own-ship and all vessel
+// vectors.
+{
+ PRINTF("Mariner's object not drawn\n");
+ return NULL;
+}
+
+static GString *VRMEBL01 (S57_geo *geo)
+// Remarks: This conditional symbology procedure symbolizes the three cases of range
+// circle, bearing line and range/bearing line. VRM's and EBL's can be ship-centred
+// or freely movable, and two line-styles are available
+{
+ PRINTF("Mariner's object not drawn\n");
+ return NULL;
+}
+
+static GString *WRECKS02 (S57_geo *geo)
+// Remarks: Wrecks of depths less than the safety contour which lie within the safe waters
+// defined by the safety contour are to be presented by a specific isolated
+// danger symbol and put in IMO category DISPLAYBASE (see (3), App.2,
+// 1.3). This task is performed by the sub-procedure "UDWHAZ03" which is
+// called by this symbology procedure.
+{
+ GString *wrecks02str = NULL;
+ GString *sndfrm02str = NULL;
+ GString *udwhaz03str = NULL;
+ GString *quapnt01str = NULL;
+ double least_depth = UNKNOWN;
+ double depth_value = UNKNOWN;
+ GString *valsoustr = S57_getAttVal(geo, "VALSOU");
+ double valsou = UNKNOWN;
+
+ // exit if not in drawing state
+ if (1 == S52_state)
+ return NULL;
+
+ if (NULL != valsoustr) {
+ valsou = atof(valsoustr->str);
+ depth_value = valsou;
+ sndfrm02str = _SNDFRM02(geo, depth_value);
+ } else {
+ if (AREAS_T == S57_getObjtype(geo))
+ least_depth = _DEPVAL01(geo, least_depth);
+
+ if (least_depth == UNKNOWN) {
+ // WARNING: ambiguity removed in WRECKS03 (see update)
+ GString *watlevstr = S57_getAttVal(geo, "WATLEV");
+ GString *catwrkstr = S57_getAttVal(geo, "CATWRK");
+
+ if (NULL == watlevstr) // default
+ depth_value = -15.0;
+ else
+ switch (*watlevstr->str) { // ambiguous
+ case '1':
+ case '2': depth_value = -15.0 ; break;
+ case '3': depth_value = 0.01; break;
+ case '4': depth_value = -15.0 ; break;
+ case '5': depth_value = 0.0 ; break;
+ case '6': depth_value = -15.0 ; break;
+ default :{
+ if (NULL != catwrkstr) {
+ switch (*catwrkstr->str) {
+ case '1': depth_value = 20.0; break;
+ case '2': depth_value = 0.0; break;
+ case '4':
+ case '5': depth_value = -15.0; break;
+ }
+ }
+ }
+ }
+ } else
+ depth_value = least_depth;
+
+
+ }
+
+ udwhaz03str = _UDWHAZ03(geo, depth_value);
+ quapnt01str = _QUAPNT01(geo);
+
+ if (POINT_T == S57_getObjtype(geo)) {
+ if (NULL != udwhaz03str) {
+ wrecks02str = g_string_new(udwhaz03str->str);
+
+ if (NULL != quapnt01str)
+ g_string_append(wrecks02str, quapnt01str->str);
+
+ } else {
+ // Continuation A (POINT_T)
+ if (UNKNOWN != valsou) {
+
+ if (valsou <= 20.0) {
+ wrecks02str = g_string_new(";SY(DANGER01)");
+ if (NULL != sndfrm02str)
+ g_string_append(wrecks02str, sndfrm02str->str);
+
+ } else
+ wrecks02str = g_string_new(";SY(DANGER02)");
+
+ if (NULL != udwhaz03str)
+ g_string_append(wrecks02str, udwhaz03str->str);
+ if (NULL != quapnt01str)
+ g_string_append(wrecks02str, quapnt01str->str);
+
+ } else {
+ char *sym = NULL;
+ GString *catwrkstr = S57_getAttVal(geo, "CATWRK");
+ GString *watlevstr = S57_getAttVal(geo, "WATLEV");
+
+ if (NULL != catwrkstr && NULL != watlevstr) {
+ if ('1' == *catwrkstr->str && '3' == *watlevstr->str)
+ sym =";SY(WRECKS04)";
+ else {
+ if ('2' == *catwrkstr->str && '3' == *watlevstr->str)
+ sym = ";SY(WRECKS05)";
+ else {
+ if ('4' == *catwrkstr->str || '5' == *catwrkstr->str)
+ sym = ";SY(WRECKS01)";
+ else {
+ if ('1' == *watlevstr->str ||
+ '2' == *watlevstr->str ||
+ '5' == *watlevstr->str ||
+ '4' == *watlevstr->str ){
+ sym = ";SY(WRECKS01)";
+ } else
+ sym = ";SY(WRECKS05)"; // default
+
+ }
+ }
+ }
+ }
+
+ wrecks02str = g_string_new(sym);
+ if (NULL != quapnt01str)
+ g_string_append(wrecks02str, quapnt01str->str);
+
+ }
+
+ }
+
+
+ } else {
+ // Continuation B (AREAS_T)
+ GString *quaposstr = S57_getAttVal(geo, "QUAPOS");
+ int quapos = (NULL == quaposstr)? 0 : atoi(quaposstr->str);
+ char *line = NULL;
+
+ if (2 <= quapos && quapos < 10)
+ line = ";LC(LOWACC41)";
+ else {
+ if ( NULL != udwhaz03str)
+ line = ";LS(DOTT,2,CHBLK)";
+ else {
+ if (UNKNOWN != valsou){
+ if (valsou <= 20)
+ line = ";LS(DOTT,2,CHBLK)";
+ else
+ line = ";LS(DASH,2,CHBLK)";
+ } else {
+ GString *watlevstr = S57_getAttVal(geo, "WATLEV");
+
+ if (NULL == watlevstr)
+ line = ";LS(DOTT,2,CSTLN)";
+ else {
+ switch (*watlevstr->str){
+ case '1':
+ case '2': line = ";LS(SOLD,2,CSTLN)"; break;
+ case '4': line = ";LS(DASH,2,CSTLN)"; break;
+ case '3':
+ case '5':
+
+ default : line = ";LS(DOTT,2,CSTLN)"; break;
+ }
+ }
+
+ }
+ }
+ }
+ wrecks02str = g_string_new(line);
+
+ if (UNKNOWN != valsou) {
+ if (valsou <= 20) {
+ if (NULL != udwhaz03str)
+ g_string_append(wrecks02str, udwhaz03str->str);
+
+ if (NULL != quapnt01str)
+ g_string_append(wrecks02str, quapnt01str->str);
+
+ if (NULL != sndfrm02str)
+ g_string_append(wrecks02str, sndfrm02str->str);
+
+ } else {
+ // NOTE: ??? same as above ???
+ if (NULL != udwhaz03str)
+ g_string_append(wrecks02str, udwhaz03str->str);
+
+ if (NULL != quapnt01str)
+ g_string_append(wrecks02str, quapnt01str->str);
+ }
+ } else {
+ char *ac = NULL;
+ GString *watlevstr = S57_getAttVal(geo, "WATLEV");
+
+ if (NULL == watlevstr)
+ ac = ";AC(DEPVS)";
+ else
+ switch (*watlevstr->str) {
+ case '1':
+ case '2': ac = ";AC(CHBRN)"; break;
+ case '4': ac = ";AC(DEPIT)"; break;
+ case '5':
+ case '3':
+ default : ac = ";AC(DEPVS)"; break;
+ }
+
+ g_string_append(wrecks02str, ac);
+
+ if (NULL != udwhaz03str)
+ g_string_append(wrecks02str, udwhaz03str->str);
+
+ if (NULL != quapnt01str)
+ g_string_append(wrecks02str, quapnt01str->str);
+ }
+ }
+
+ if (NULL != sndfrm02str) g_string_free(sndfrm02str, TRUE);
+ if (NULL != udwhaz03str) g_string_free(udwhaz03str, TRUE);
+ if (NULL != quapnt01str) g_string_free(quapnt01str, TRUE);
+
+ return wrecks02str;
+}
+
+
+//--------------------------------
+//
+// JUMP TABLE SECTION
+//
+//--------------------------------
+
+
+CondSymb condTable[] = {
+ // name call Sub-Procedure
+ {"CLRLIN01", CLRLIN01}, //
+ {"DATCVR01", DATCVR01}, //
+ {"DEPARE01", DEPARE01}, // _RESCSP01, _SEABED01
+ {"DEPCNT02", DEPCNT02}, //
+ {"LEGLIN02", LEGLIN02}, // str
+ {"LIGHTS05", LIGHTS05}, // _LITDSN01
+ {"OBSTRN04", OBSTRN04}, // _DEPVAL01, _QUAPNT01, _SNDFRM02, _UDWHAZ03
+ {"OWNSHP02", OWNSHP02}, //
+ {"PASTRK01", PASTRK01}, //
+ {"QUAPOS01", QUAPOS01}, // _QUALIN01, _QUAPNT01
+ {"SLCONS03", SLCONS03}, //
+ {"RESARE02", RESARE02}, //
+ {"RESTRN01", RESTRN01}, // _RESCSP01
+ {"SOUNDG02", SOUNDG02}, // _SNDFRM02
+ {"TOPMAR01", TOPMAR01}, //
+ {"VESSEL01", VESSEL01}, //
+ {"VRMEBL01", VRMEBL01}, //
+ {"WRECKS02", WRECKS02}, // _DEPVAL01, _QUAPNT01, _SNDFRM02, _UDWHAZ03
+
+ {"########",NULL}
+};
+*/
+#endif
diff --git a/docs/外部参考/OpenCPN源码基线_f7211b4e/上游源码/libs/s52plib/src/s52plib.cpp b/docs/外部参考/OpenCPN源码基线_f7211b4e/上游源码/libs/s52plib/src/s52plib.cpp
new file mode 100644
index 0000000..eac4589
--- /dev/null
+++ b/docs/外部参考/OpenCPN源码基线_f7211b4e/上游源码/libs/s52plib/src/s52plib.cpp
@@ -0,0 +1,12765 @@
+/***************************************************************************
+ *
+ * Project: OpenCPN
+ * Purpose: S52 Presentation Library
+ * Authors: David Register
+ * Jesper Weissglas
+ *
+ ***************************************************************************
+ * Copyright (C) 2010 by David S. Register *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
+ **************************************************************************/
+
+// For compilers that support precompilation, includes "wx.h".
+#include "wx/wxprec.h"
+
+#ifndef WX_PRECOMP
+#include "wx/wx.h"
+#endif // precompiled headers
+
+#include
+#include
+
+#ifdef __OCPN__ANDROID__
+// Handle occasional SIG on Android
+#include
+#include
+
+thread_local struct sigaction sa_all_plib;
+thread_local struct sigaction sa_all_plib_previous;
+
+thread_local sigjmp_buf env_plib; // the context saved by sigsetjmp();
+
+void catch_signals_plib(int signo) {
+ switch (signo) {
+ case SIGSEGV:
+ siglongjmp(env_plib, 1); // jump back to the setjmp() point
+ break;
+
+ default:
+ break;
+ }
+}
+#endif
+
+#ifndef PI
+#define PI 3.1415926535897931160E0 /* pi */
+#endif
+#define DEGREE (PI / 180.0)
+#define RADIAN (180.0 / PI)
+
+static const double WGS84_semimajor_axis_meters =
+ 6378137.0; // WGS84 semimajor axis
+static const double mercator_k0 = 0.9996;
+
+#include "s52plib.h"
+#include "mygeom.h"
+#include "s52utils.h"
+#include "chartsymbols.h"
+// #include "TexFont.h"
+#include "line_clip.h"
+#include "poly_math.h"
+#include "LOD_reduce.h"
+#include "linmath.h"
+#ifdef ocpnUSE_GL
+#include "Cs52_shaders.h"
+#endif
+
+#include
+#include
+#include
+#include
+
+#ifndef PROJECTION_MERCATOR
+#define PROJECTION_MERCATOR 1
+#endif
+
+s52plib *ps52plib; ///< Global instance
+
+// Some methods that are required to be declared differently
+// depending on whether the library is build for core, or plugin
+#ifdef BUILDING_PLUGIN
+#include "ocpn_plugin.h"
+#else
+wxColour GetFontColour_PlugIn(wxString TextElement);
+extern "C" bool GetGlobalColor(wxString colorName, wxColour *pcolour);
+wxFont *FindOrCreateFont_PlugIn(
+ int point_size, wxFontFamily family, wxFontStyle style, wxFontWeight weight,
+ bool underline = false, const wxString &facename = wxEmptyString,
+ wxFontEncoding encoding = wxFONTENCODING_DEFAULT);
+wxFont *GetOCPNScaledFont_PlugIn(wxString TextElement, int default_size = 0);
+float GetOCPNChartScaleFactor_Plugin();
+extern "C" wxString *GetpSharedDataLocation();
+
+#endif
+
+#ifdef __OCPN__ANDROID__
+#include "qdebug.h"
+#endif
+
+float g_scaminScale;
+
+// #ifndef __MSVC__
+#define _GLUfuncptrA _GLUfuncptr
+// #endif
+
+void DrawAALine(wxDC *pDC, int x0, int y0, int x1, int y1, wxColour clrLine,
+ int dash, int space);
+extern bool GetDoubleAttr(S57Obj *obj, const char *AttrName, double &val);
+
+void LoadS57Config();
+bool loadS52Shaders();
+
+// Simple and fast CRC32 calculator
+unsigned int crc32buf(unsigned char *buf, size_t len);
+
+GLint S52color_tri_shader_program;
+GLint S52texture_2D_shader_program;
+GLint S52texture_2D_ColorMod_shader_program;
+GLint S52circle_filled_shader_program;
+GLint S52ring_shader_program;
+GLint S52Dash_shader_program;
+GLint S52AP_shader_program;
+
+std::string s_color;
+std::string s_uTex;
+std::string s_position;
+std::string s_aUV;
+std::string s_TransformMatrix;
+
+// Implement all lists
+#include
+WX_DEFINE_LIST(TextObjList);
+
+// Implement all arrays
+#include
+#include "s52_plib_utils.h"
+WX_DEFINE_OBJARRAY(ArrayOfNoshow);
+
+// S52_TextC Implementation
+S52_TextC::S52_TextC() {
+ pcol = NULL;
+ pFont = NULL;
+ texobj = 0;
+ bnat = false;
+ bspecial_char = false;
+}
+
+S52_TextC::~S52_TextC() {
+ if (texobj) {
+ glDeleteTextures(1, (GLuint *)(&this->texobj));
+ }
+}
+
+wxRect2D::wxRect2D() : x(0), y(0), width(0), height(0) {}
+wxRect2D:: wxRect2D(double xx, double yy, double w, double h)
+ : x(xx), y(yy), width(w), height(h) {}
+
+bool wxRect2D::Intersects(const wxRect2D& other,
+ double epsilon) const {
+ return !((x + width) <= (other.x + epsilon) ||
+ (other.x + other.width) <= (x + epsilon) ||
+ (y + height) <= (other.y + epsilon) ||
+ (other.y + other.height) <= (y + epsilon));
+}
+
+wxRect2D wxRect2D::Union(const wxRect2D& other) const
+{
+ if (IsEmpty())
+ return other;
+
+ if (other.IsEmpty())
+ return *this;
+
+ double left = std::min(x, other.x);
+ double top = std::min(y, other.y);
+ double right = std::max(x + width, other.x + other.width);
+ double bottom = std::max(y + height, other.y + other.height);
+
+ return wxRect2D(left, top, right - left, bottom - top);
+}
+
+// This is a verbatim copy of same struct found in ocpn_plugin.h
+// Used for some types of plugin charts
+class PI_line_segment_element {
+public:
+ size_t vbo_offset;
+ size_t n_points;
+ int priority;
+ float lat_max; // segment bounding box
+ float lat_min;
+ float lon_max;
+ float lon_min;
+ int type;
+ void *private0;
+
+ PI_line_segment_element *next;
+};
+
+//-----------------------------------------------------------------------------
+// Comparison Function for LUPArray sorting
+// Note Global Scope
+//-----------------------------------------------------------------------------
+#ifndef _COMPARE_LUP_DEFN_
+#define _COMPARE_LUP_DEFN_
+
+int CompareLUPObjects(LUPrec *item1, LUPrec *item2) {
+ // sort the items by their name...
+ int ir = strcmp(item1->OBCL, item2->OBCL);
+
+ if (ir != 0) return ir;
+ int c1 = item1->ATTArray.size();
+ int c2 = item2->ATTArray.size();
+
+ if (c1 != c2) return c2 - c1;
+ return item1->nSequence - item2->nSequence;
+}
+
+#endif
+
+//-----------------------------------------------------------------------------
+// LUPArrayContainer implementation
+//-----------------------------------------------------------------------------
+LUPArrayContainer::LUPArrayContainer() {
+ // Build the initially empty sorted arrays of LUP Records, per LUP type.
+ // Sorted on object name, e.g. ACHARE. Why sorted? Helps in the
+ // S52_LUPLookup method....
+ LUPArray = new wxArrayOfLUPrec(CompareLUPObjects);
+}
+
+LUPArrayContainer::~LUPArrayContainer() {
+ if (LUPArray) {
+ for (unsigned int il = 0; il < LUPArray->GetCount(); il++)
+ s52plib::DestroyLUP(LUPArray->Item(il));
+
+ LUPArray->Clear();
+ delete LUPArray;
+ }
+
+ LUPArrayIndexHash::iterator it;
+ for (it = IndexHash.begin(); it != IndexHash.end(); ++it) {
+ free(it->second);
+ }
+}
+
+LUPHashIndex *LUPArrayContainer::GetArrayIndexHelper(const char *objectName) {
+ // Look for the key
+ wxString key(objectName, wxConvUTF8);
+ LUPArrayIndexHash::iterator it = IndexHash.find(key);
+
+ if (it == IndexHash.end()) {
+ // Key not found, needs to be added
+ LUPHashIndex *pindex = (LUPHashIndex *)malloc(sizeof(LUPHashIndex));
+ pindex->n_start = -1;
+ pindex->count = 0;
+ IndexHash[key] = pindex;
+
+ // Find the first matching entry in the LUP Array
+ int index = 0;
+ int index_max = LUPArray->GetCount();
+ int first_match = 0;
+ int ocnt = 0;
+ LUPrec *LUPCandidate;
+
+ // This technique of extracting proper LUPs depends on the fact that
+ // the LUPs have been sorted in their array, by OBCL.
+ // Thus, all the LUPS with the same OBCL will be grouped together
+
+ while (!first_match && (index < index_max)) {
+ LUPCandidate = LUPArray->Item(index);
+ if (!strcmp(objectName, LUPCandidate->OBCL)) {
+ pindex->n_start = index;
+ first_match = 1;
+ ocnt++;
+ index++;
+ break;
+ }
+ index++;
+ }
+
+ while (first_match && (index < index_max)) {
+ LUPCandidate = LUPArray->Item(index);
+ if (!strcmp(objectName, LUPCandidate->OBCL)) {
+ ocnt++;
+ } else {
+ break;
+ }
+
+ index++;
+ }
+
+ pindex->count = ocnt;
+
+ return pindex;
+ } else
+ return it->second; // return a pointer to the found record
+}
+
+//-----------------------------------------------------------------------------
+// s52plib implementation
+//-----------------------------------------------------------------------------
+s52plib::s52plib(const wxString &PLib, bool b_forceLegacy) {
+ m_plib_file = PLib;
+
+ pOBJLArray = new wxArrayPtrVoid;
+
+ condSymbolLUPArray = NULL; // Dynamic Conditional Symbology
+
+ _symb_sym = NULL;
+
+ m_txf_ready = false;
+ m_txf = NULL;
+
+ m_chartSymbols.InitializeTables();
+ m_chartSymbols.SetTextureFormat(GL_TEXTURE_2D);
+ InitializeNatsurHash();
+
+ m_bOK = !(S52_load_Plib(PLib, b_forceLegacy) == 0);
+
+ m_bShowS57Text = false;
+ m_bShowS57ImportantTextOnly = false;
+ m_colortable_index = 0;
+
+ _symb_symR = NULL;
+ bUseRasterSym = false;
+
+ // Sensible defaults
+ m_nSymbolStyle = PAPER_CHART;
+ m_nBoundaryStyle = PLAIN_BOUNDARIES;
+ m_nDisplayCategory = OTHER;
+ m_nDepthUnitDisplay = 1; // meters
+ m_nHeightUnitDisplay = 0; // meters
+
+ UpdateMarinerParams();
+
+ ledge = new int[2000];
+ redge = new int[2000];
+
+ // Defaults
+ m_VersionMajor = 3;
+ m_VersionMinor = 2;
+
+ canvas_pix_per_mm = 3.;
+ m_rv_scale_factor = 1.0;
+
+ // Set up some default flags
+ m_bDeClutterText = false;
+ m_bShowAtonText = true;
+ m_bShowNationalTexts = false;
+
+ m_bShowSoundg = true;
+ m_bShowLdisText = true;
+ m_bExtendLightSectors = true;
+
+ // Set a few initial states
+ AddObjNoshow("M_QUAL");
+ m_lightsOff = false;
+ m_anchorOn = true;
+ m_qualityOfDataOn = false;
+
+ m_SoundingsScaleFactor = 1.0;
+ m_SoundingsFontSizeMM = 0;
+ m_soundFontDelta = 0;
+
+ GenerateStateHash();
+
+ HPGL = new RenderFromHPGL(this);
+
+ // Set defaults for OCPN version, may be overridden later
+ m_coreVersionMajor = 4;
+ m_coreVersionMinor = 6;
+ m_coreVersionPatch = 0;
+
+ m_myConfig = GetStateHash();
+
+ // GL Options/capabilities
+ m_useStencil = false;
+ m_useStencilAP = false;
+ m_useScissors = false;
+ m_useFBO = false;
+ m_GLAC_VBO = false;
+ m_useVBO = false;
+ m_useGLSL = false;
+ m_TextureFormat = -1;
+ m_GLMinCartographicLineWidth = 1.0;
+ m_GLMinSymbolLineWidth = 1.0;
+
+ m_display_size_mm = 300;
+ SetGLPolygonSmoothing(false);
+ SetGLLineSmoothing(false);
+
+ m_displayScale = 1.0;
+ m_display_width = 0;
+
+ // Clear the TexFont cache
+ unsigned int i;
+ for (i = 0; i < TXF_CACHE; i++) {
+ s_txf[i].key = 0;
+ s_txf[i].cache = 0;
+ }
+ m_dipfactor = 1.0;
+ m_ContentScaleFactor = 1.0;
+ m_FinalTextScaleFactor = 0;
+ m_TextScaleFactor = 1;
+ m_nTextFactor = 0;
+ m_isSoundingFontSet = false;
+
+ s_color = "color";
+ s_uTex = "uTex";
+ s_position = "position";
+ s_aUV = "aUV";
+ s_TransformMatrix = "TransformMatrix";
+}
+
+s52plib::~s52plib() {
+ delete areaPlain_LAC;
+ delete line_LAC;
+ delete areaSymbol_LAC;
+ delete pointSimple_LAC;
+ delete pointPaper_LAC;
+
+ S52_flush_Plib();
+
+ // Free the OBJL Array Elements
+ for (unsigned int iPtr = 0; iPtr < pOBJLArray->GetCount(); iPtr++)
+ free(pOBJLArray->Item(iPtr));
+
+ delete pOBJLArray;
+
+ delete[] ledge;
+ delete[] redge;
+
+ m_chartSymbols.DeleteGlobals();
+
+ delete HPGL;
+ for (int i = 0; i < TXF_CACHE; i++) {
+ if (s_txf[i].cache) {
+ delete (s_txf[i].cache);
+ }
+ }
+}
+
+void s52plib::InitializeNatsurHash() {
+ std::unordered_map surmap({{1, "mud"},
+ {2, "clay"},
+ {3, "silt"},
+ {4, "sand"},
+ {5, "stone"},
+ {6, "gravel"},
+ {7, "pebbles"},
+ {8, "cobbles"},
+ {9, "rock"},
+ {11, "lava"},
+ {14, "coral"},
+ {17, "shells"},
+ {18, "boulder"},
+ {56, "Bo"},
+ {51, "Wd"}});
+ m_natsur_hash = surmap;
+}
+
+void s52plib::SetVPointCompat(int pix_width, int pix_height,
+ double view_scale_ppm, double rotation,
+ double clat, double clon, double chart_scale,
+ wxRect rv_rect, LLBBox &bbox, double ref_scale,
+ double display_scale) {
+ vp_plib.pix_width = pix_width;
+ vp_plib.pix_height = pix_height;
+ vp_plib.view_scale_ppm = view_scale_ppm;
+ vp_plib.rotation = rotation;
+ vp_plib.clat = clat;
+ vp_plib.clon = clon;
+ vp_plib.chart_scale = chart_scale;
+ vp_plib.rv_rect = rv_rect;
+ BBox = bbox;
+ reducedBBox = bbox;
+ vp_plib.ref_scale = ref_scale;
+ m_displayScale = display_scale;
+}
+
+void s52plib::SetOCPNVersion(int major, int minor, int patch) {
+ m_coreVersionMajor = major;
+ m_coreVersionMinor = minor;
+ m_coreVersionPatch = patch;
+}
+
+void s52plib::SetGLOptions(bool b_useStencil, bool b_useStencilAP,
+ bool b_useScissors, bool b_useFBO, bool b_useVBO,
+ int nTextureFormat, float MinCartographicLineWidth,
+ float MinSymbolLineWidth) {
+ // Set GL Options/capabilities
+ m_useStencil = b_useStencil;
+ m_useStencilAP = b_useStencilAP;
+ m_useScissors = b_useScissors;
+ m_useFBO = b_useFBO;
+ m_useVBO = b_useVBO;
+ m_useGLSL = true;
+ m_TextureFormat = nTextureFormat;
+ m_chartSymbols.SetTextureFormat(nTextureFormat);
+ m_GLMinCartographicLineWidth = MinCartographicLineWidth;
+ m_GLMinSymbolLineWidth = MinSymbolLineWidth;
+}
+
+void s52plib::SetDIPFactor(double factor) { m_dipfactor = factor; }
+
+void s52plib::SetContentScaleFactor(double factor) {
+ m_ContentScaleFactor = factor;
+ if (HPGL) HPGL->SetContentScaleFactor(factor);
+}
+
+void s52plib::SetPPMM(float ppmm) {
+ canvas_pix_per_mm = ppmm;
+
+ // We need a supplemental scale factor for HPGL vector symbol rendering.
+ // to allow raster and vector symbols to be rendered harmoniously
+ // We do this empirically, making it look "nice" on average displays.
+ m_rv_scale_factor = 0.8;
+
+ // Estimate the display size
+ m_display_size_mm =
+ m_display_width / GetPPMM(); // accurate enough for internal use
+
+ m_display_size_mm /= m_displayScale;
+}
+
+void s52plib::SetScaleFactorExp(double ChartScaleFactorExp) {
+ m_ChartScaleFactorExp = ChartScaleFactorExp;
+}
+
+void s52plib::SetScaleFactorZoomMod(int chart_zoom_modifier_vector) {
+ m_chart_zoom_modifier_vector = chart_zoom_modifier_vector;
+}
+
+// Various static helper methods
+
+void s52plib::DestroyLUP(LUPrec *pLUP) {
+ Rules *top = pLUP->ruleList;
+ DestroyRulesChain(top);
+
+ delete pLUP;
+}
+
+void s52plib::DestroyRulesChain(Rules *top) {
+ while (top != NULL) {
+ Rules *Rtmp = top->next;
+
+ free(top->INST0); // free the Instruction string head
+
+ if (top->b_private_razRule) // need to free razRule?
+ {
+ Rule *pR = top->razRule;
+ delete pR->exposition.LXPO;
+
+ free(pR->vector.LVCT);
+
+ delete pR->bitmap.SBTM;
+
+ free(pR->colRef.SCRF);
+
+ ClearRulesCache(pR);
+
+ free(pR);
+ }
+
+ free(top);
+ top = Rtmp;
+ }
+}
+
+DisCat s52plib::findLUPDisCat(const char *objectName, LUPname TNAM) {
+ LUPArrayContainer *plac = SelectLUPArrayContainer(TNAM);
+
+ wxArrayOfLUPrec *LUPArray = SelectLUPARRAY(TNAM);
+
+ // Find the first matching entry in the LUP Array
+ int index = 0;
+ int index_max = LUPArray->GetCount();
+ LUPrec *LUPCandidate;
+
+ while (index < index_max) {
+ LUPCandidate = LUPArray->Item(index);
+ if (!strcmp(objectName, LUPCandidate->OBCL)) {
+ return LUPCandidate->DISC;
+ }
+ index++;
+ }
+
+ return (DisCat)(-1);
+}
+
+bool s52plib::GetAnchorOn() {
+ // Investigate and report the logical condition that "Anchoring Condition" is
+ // shown
+
+ int old_vis = 0;
+
+ if (MARINERS_STANDARD == GetDisplayCategory()) {
+ old_vis = m_anchorOn;
+ } else if (OTHER == GetDisplayCategory())
+ old_vis = true;
+
+ // other cat
+ // const char * categories[] = { "ACHBRT", "ACHARE", "CBLSUB", "PIPARE",
+ // "PIPSOL", "TUNNEL", "SBDARE" };
+
+ old_vis &= !IsObjNoshow("SBDARE");
+
+ return (old_vis != 0);
+}
+
+bool s52plib::GetQualityOfData() {
+ // Investigate and report the logical condition that "Quality of Data
+ // Condition" is shown
+
+ int old_vis = 0;
+
+ if (MARINERS_STANDARD == GetDisplayCategory()) {
+ for (unsigned int iPtr = 0; iPtr < pOBJLArray->GetCount(); iPtr++) {
+ OBJLElement *pOLE = (OBJLElement *)(pOBJLArray->Item(iPtr));
+ if (!strncmp(pOLE->OBJLName, "M_QUAL", 6)) {
+ old_vis = pOLE->nViz;
+ break;
+ }
+ }
+ } else if (OTHER == GetDisplayCategory())
+ old_vis = true;
+
+ old_vis &= !IsObjNoshow("M_QUAL");
+
+ return (old_vis != 0);
+}
+
+void s52plib::SetGLRendererString(const wxString &renderer) {
+ m_renderer_string = renderer;
+
+ // No chart type in current use requires VBO for GLAC rendering
+ // Experimentation has shown that VBO is slower for GLAC rendering,
+ // since the per-object state change of glBindBuffer() is slow
+ // on most hardware, especially RPi.
+ // However, we have found that NVidea GPUs
+ // perform much better with VBO on GLAC operations, so set that up.
+
+ if ((renderer.Upper().Contains("NVIDIA")) ||
+ (renderer.Upper().Contains("QUADRO")) ||
+ (renderer.Upper().Contains("GEFORCE")))
+ m_GLAC_VBO = true;
+
+ // ANGLE/Vulkan implementations have also been found to require/prefer
+ // VBOs for stability and performance when rendering dense tessellated
+ // area geometry.
+ if (renderer.Upper().Contains("ANGLE"))
+ m_GLAC_VBO = true;
+}
+
+/*
+ Update the S52 Conditional Symbology Parameter Set to reflect the
+ current state of the library member options.
+ */
+
+void s52plib::UpdateMarinerParams(void) {
+ // Symbol Style
+ if (SIMPLIFIED == m_nSymbolStyle)
+ S52_setMarinerParam(S52_MAR_SYMPLIFIED_PNT, 1.0);
+ else
+ S52_setMarinerParam(S52_MAR_SYMPLIFIED_PNT, 0.0);
+
+ // Boundary Style
+ if (SYMBOLIZED_BOUNDARIES == m_nBoundaryStyle)
+ S52_setMarinerParam(S52_MAR_SYMBOLIZED_BND, 1.0);
+ else
+ S52_setMarinerParam(S52_MAR_SYMBOLIZED_BND, 0.0);
+}
+
+void s52plib::GenerateStateHash() {
+ unsigned char state_buffer[512]; // Needs to be at least this big...
+ memset(state_buffer, 0, sizeof(state_buffer));
+
+ // int time = ::wxGetUTCTime();
+ // memcpy(state_buffer, &time, sizeof(int));
+
+ size_t offset = sizeof(int); // skipping the time int, first element
+
+ for (int i = 0; i < S52_MAR_NUM; i++) {
+ if ((offset + sizeof(double)) < sizeof(state_buffer)) {
+ double t = S52_getMarinerParam((S52_MAR_param_t)i);
+ memcpy(&state_buffer[offset], &t, sizeof(double));
+ offset += sizeof(double);
+ }
+ }
+
+ for (unsigned int i = 0; i < m_noshow_array.GetCount(); i++) {
+ if ((offset + 6) < sizeof(state_buffer)) {
+ memcpy(&state_buffer[offset], m_noshow_array[i].obj, 6);
+ offset += 6;
+ }
+ }
+
+ if (offset + sizeof(bool) < sizeof(state_buffer)) {
+ memcpy(&state_buffer[offset], &m_bShowSoundg, sizeof(bool));
+ offset += sizeof(bool);
+ }
+
+ if (offset + sizeof(bool) < sizeof(state_buffer)) {
+ memcpy(&state_buffer[offset], &m_bShowS57Text, sizeof(bool));
+ offset += sizeof(bool);
+ }
+
+ if (offset + sizeof(bool) < sizeof(state_buffer)) {
+ memcpy(&state_buffer[offset], &m_bShowS57ImportantTextOnly, sizeof(bool));
+ offset += sizeof(bool);
+ }
+
+ if (offset + sizeof(bool) < sizeof(state_buffer)) {
+ memcpy(&state_buffer[offset], &m_bDeClutterText, sizeof(bool));
+ offset += sizeof(bool);
+ }
+
+ if (offset + sizeof(bool) < sizeof(state_buffer)) {
+ memcpy(&state_buffer[offset], &m_bShowNationalTexts, sizeof(bool));
+ offset += sizeof(bool);
+ }
+
+ if (offset + sizeof(bool) < sizeof(state_buffer)) {
+ memcpy(&state_buffer[offset], &m_bShowAtonText, sizeof(bool));
+ offset += sizeof(bool);
+ }
+
+ if (offset + sizeof(bool) < sizeof(state_buffer)) {
+ memcpy(&state_buffer[offset], &m_bShowLdisText, sizeof(bool));
+ offset += sizeof(bool);
+ }
+
+ if (offset + sizeof(bool) < sizeof(state_buffer)) {
+ memcpy(&state_buffer[offset], &m_bExtendLightSectors, sizeof(bool));
+ offset += sizeof(bool);
+ }
+
+ if (offset + sizeof(bool) < sizeof(state_buffer)) {
+ memcpy(&state_buffer[offset], &m_nSoundingFactor, sizeof(int));
+ offset += sizeof(int);
+ }
+
+ if (offset + sizeof(bool) < sizeof(state_buffer)) {
+ memcpy(&state_buffer[offset], &m_nTextFactor, sizeof(int));
+ offset += sizeof(int);
+ }
+
+ if (offset + sizeof(bool) < sizeof(state_buffer)) {
+ memcpy(&state_buffer[offset], &m_nDisplayCategory, sizeof(int));
+ offset += sizeof(int);
+ }
+
+ long p = (long)GetS52Utils()->GetDefaultFont(_("ChartTexts"), 0);
+ if (offset + sizeof(long) < sizeof(state_buffer)) {
+ memcpy(&state_buffer[offset], &p, sizeof(long));
+ offset += sizeof(long);
+ }
+
+ m_state_hash = crc32buf(state_buffer, offset);
+}
+
+wxArrayOfLUPrec *s52plib::SelectLUPARRAY(LUPname TNAM) {
+ switch (TNAM) {
+ case SIMPLIFIED:
+ return pointSimple_LAC->GetLUPArray();
+ case PAPER_CHART:
+ return pointPaper_LAC->GetLUPArray();
+ case LINES:
+ return line_LAC->GetLUPArray();
+ case PLAIN_BOUNDARIES:
+ return areaPlain_LAC->GetLUPArray();
+ case SYMBOLIZED_BOUNDARIES:
+ return areaSymbol_LAC->GetLUPArray();
+ default:
+ return NULL;
+ }
+}
+
+LUPArrayContainer *s52plib::SelectLUPArrayContainer(LUPname TNAM) {
+ switch (TNAM) {
+ case SIMPLIFIED:
+ return pointSimple_LAC;
+ case PAPER_CHART:
+ return pointPaper_LAC;
+ case LINES:
+ return line_LAC;
+ case PLAIN_BOUNDARIES:
+ return areaPlain_LAC;
+ case SYMBOLIZED_BOUNDARIES:
+ return areaSymbol_LAC;
+ default:
+ return NULL;
+ }
+}
+
+extern Cond condTable[];
+
+LUPrec *s52plib::FindBestLUP(wxArrayOfLUPrec *LUPArray, unsigned int startIndex,
+ unsigned int count, S57Obj *pObj, bool bStrict) {
+ // Check the parameters
+ if (0 == count) return NULL;
+ if (startIndex >= LUPArray->GetCount()) return NULL;
+
+ // setup default return to the first LUP that matches Feature name.
+ LUPrec *LUP = LUPArray->Item(startIndex);
+
+ int nATTMatch = 0;
+ int countATT = 0;
+ bool bmatch_found = false;
+
+ if (pObj->att_array == NULL)
+ goto check_LUP; // object has no attributes to compare, so return "best"
+ // LUP
+
+ for (unsigned int i = 0; i < count; ++i) {
+ LUPrec *LUPCandidate = LUPArray->Item(startIndex + i);
+
+ if (!LUPCandidate->ATTArray.size())
+ continue; // this LUP has no attributes coded
+
+ countATT = 0;
+ char *currATT = pObj->att_array;
+ int attIdx = 0;
+
+ for (unsigned int iLUPAtt = 0; iLUPAtt < LUPCandidate->ATTArray.size();
+ iLUPAtt++) {
+ // Get the LUP attribute name
+ const char *slatc = LUPCandidate->ATTArray[iLUPAtt].c_str();
+
+ if (slatc && (strlen(slatc) < 6))
+ goto next_LUP_Attr; // LUP attribute value not UTF8 convertible (never
+ // seen in PLIB 3.x)
+
+ if (slatc) {
+ const char *slatv = slatc + 6;
+ while (attIdx < pObj->n_attr) {
+ if (0 == strncmp(slatc, currATT, 6)) {
+ // OK we have an attribute name match
+
+ bool attValMatch = false;
+
+ // special case (i)
+ if (!strncmp(slatv, " ", 1)) { // any object value will match wild
+ // card (S52 para 8.3.3.4)
+ ++countATT;
+ goto next_LUP_Attr;
+ }
+
+ // special case (ii)
+ // TODO Find an ENC with "UNKNOWN" DRVAL1 or DRVAL2 and debug this
+ // code
+ if (!strncmp(slatv, "?",
+ 1)) { // if LUP attribute value is "undefined"
+
+ // Match if the object does NOT contain this attribute
+ goto next_LUP_Attr;
+ }
+
+ // checking against object attribute value
+ S57attVal *v = (pObj->attVal->Item(attIdx));
+
+ switch (v->valType) {
+ case OGR_INT: // S57 attribute type 'E' enumerated, 'I' integer
+ {
+ int LUP_att_val = atoi(slatv);
+ if (LUP_att_val == *(int *)(v->value)) attValMatch = true;
+ break;
+ }
+
+ case OGR_INT_LST: // S57 attribute type 'L' list: comma separated
+ // integer
+ {
+ int a;
+ char ss[41];
+ strncpy(ss, slatv, 39);
+ ss[40] = '\0';
+ char *s = &ss[0];
+
+ int *b = (int *)v->value;
+ sscanf(s, "%d", &a);
+
+ while (*s != '\0') {
+ if (a == *b) {
+ sscanf(++s, "%d", &a);
+ b++;
+ attValMatch = true;
+
+ } else
+ attValMatch = false;
+ }
+ break;
+ }
+ case OGR_REAL: // S57 attribute type'F' float
+ {
+ double obj_val = *(double *)(v->value);
+ float att_val = atof(slatv);
+ if (fabs(obj_val - att_val) < 1e-6)
+ if (obj_val == att_val) attValMatch = true;
+ break;
+ }
+
+ case OGR_STR: // S57 attribute type'A' code string, 'S' free text
+ {
+ // Strings must be exact match
+ // n.b. OGR_STR is used for S-57 attribute type 'L',
+ // comma-separated list
+
+ // wxString cs( (char *) v->value, wxConvUTF8 ); // Attribute
+ // from object if( LATTC.Mid( 6 ) == cs )
+ if (!strcmp((char *)v->value, slatv)) attValMatch = true;
+ break;
+ }
+
+ default:
+ break;
+ } // switch
+
+ // value match
+ if (attValMatch) ++countATT;
+
+ goto next_LUP_Attr;
+ } // if attribute name match
+
+ // Advance to the next S57obj attribute
+ currATT += 6;
+ ++attIdx;
+
+ } // while
+ } // if
+
+ next_LUP_Attr:
+
+ currATT = pObj->att_array; // restart the object attribute list
+ attIdx = 0;
+ } // for iLUPAtt
+
+ // Create a "match score", defined as fraction of candidate LUP
+ // attributes actually matched by feature. Used later for resolving
+ // "ties"
+
+ int nattr_matching_on_candidate = countATT;
+ int nattrs_on_candidate = LUPCandidate->ATTArray.size();
+ double candidate_score =
+ (1. * nattr_matching_on_candidate) / (1. * nattrs_on_candidate);
+
+ // According to S52 specs, match must be perfect,
+ // and the first 100% match is selected
+ if (candidate_score == 1.0) {
+ LUP = LUPCandidate;
+ bmatch_found = true;
+ break; // selects the first 100% match
+ }
+
+ } // for loop
+
+check_LUP:
+ // In strict mode, we require at least one attribute to match exactly
+
+ if (bStrict) {
+ if (nATTMatch == 0) // nothing matched
+ LUP = NULL;
+ } else {
+ // If no match found, return the first LUP in the list which has no
+ // attributes
+ if (!bmatch_found) {
+ for (unsigned int j = 0; j < count; ++j) {
+ LUPrec *LUPtmp = NULL;
+
+ LUPtmp = LUPArray->Item(startIndex + j);
+ if (!LUPtmp->ATTArray.size()) {
+ return LUPtmp;
+ }
+ }
+ }
+ }
+
+ return LUP;
+}
+
+// scan foward stop on ; or end-of-record
+#define SCANFWRD \
+ while (!(*str == ';' || *str == '\037')) ++str;
+
+#define INSTRUCTION(s, t) \
+ if (0 == strncmp(s, str, 2)) { \
+ str += 3; \
+ r->ruleType = t; \
+ r->INSTstr = str;
+
+Rules *s52plib::StringToRules(const wxString &str_in) {
+ wxCharBuffer buffer = str_in.ToUTF8();
+ if (!buffer.data()) return NULL;
+
+ size_t len = strlen(buffer.data());
+ char *str0 = (char *)calloc(len + 1, 1);
+ memcpy(str0, buffer.data(), len);
+ char *str = str0;
+
+ Rules *top;
+ Rules *last;
+ char strk[20];
+
+ // Allocate and pre-clear the Rules structure
+ Rules *r = (Rules *)calloc(1, sizeof(Rules));
+ top = r;
+ last = top;
+
+ r->INST0 = str0; // save the head for later free
+
+ while (*str != '\0') {
+ if (r->ruleType) // in the loop, r has been used
+ {
+ r = (Rules *)calloc(1, sizeof(Rules));
+ last->next = r;
+ last = r;
+ }
+
+ // parse Symbology instruction in string
+
+ // Special Case for Circular Arc, (opencpn private)
+ // Allocate a Rule structure to be used to hold a cached bitmap of the
+ // created symbol
+ INSTRUCTION("CA", RUL_ARC_2C)
+ r->razRule = (Rule *)calloc(1, sizeof(Rule));
+ r->b_private_razRule = true; // mark this raxRule to be free'd later
+ SCANFWRD
+ }
+
+ // Special Case for MultPoint Soundings
+ INSTRUCTION("MP", RUL_MUL_SG)
+ SCANFWRD
+}
+
+// SHOWTEXT
+INSTRUCTION("TX", RUL_TXT_TX)
+SCANFWRD
+}
+
+INSTRUCTION("TE", RUL_TXT_TE)
+SCANFWRD
+}
+
+// SHOWPOINT
+
+if (0 == strncmp("SY", str, 2)) {
+ str += 3;
+ r->ruleType = RUL_SYM_PT;
+ r->INSTstr = str;
+
+ strncpy(strk, str, 8);
+ strk[8] = 0;
+ wxString key(strk, wxConvUTF8);
+
+ r->razRule = (*_symb_sym)[key];
+
+ if (r->razRule == NULL) r->razRule = (*_symb_sym)[_T ( "QUESMRK1" )];
+
+ SCANFWRD
+}
+
+// SHOWLINE
+INSTRUCTION("LS", RUL_SIM_LN)
+SCANFWRD
+}
+
+INSTRUCTION("LC", RUL_COM_LN)
+strncpy(strk, str, 8);
+strk[8] = 0;
+wxString key(strk, wxConvUTF8);
+
+r->razRule = (*_line_sym)[key];
+
+// Substitute generic question mark for undefined symbology
+if (r->razRule == NULL) {
+ r->razRule = (*_symb_sym)[_T ( "QUESMRK1" )];
+ r->razRule->pos.line.bnbox_w.SYHL = 600; // manually set the
+ // rendered symbol length
+}
+SCANFWRD
+}
+
+// SHOWAREA
+INSTRUCTION("AC", RUL_ARE_CO)
+SCANFWRD
+}
+
+INSTRUCTION("AP", RUL_ARE_PA)
+strncpy(strk, str, 8);
+strk[8] = 0;
+wxString key(strk, wxConvUTF8);
+
+r->razRule = (*_patt_sym)[key];
+if (r->razRule == NULL) r->razRule = (*_patt_sym)[_T ( "QUESMRK1V" )];
+SCANFWRD
+}
+
+// CALLSYMPROC
+
+if (0 == strncmp("CS", str, 2)) {
+ str += 3;
+ r->ruleType = RUL_CND_SY;
+ r->INSTstr = str;
+
+ // INSTRUCTION("CS",RUL_CND_SY)
+ char stt[9];
+ strncpy(stt, str, 8);
+ stt[8] = 0;
+ wxString index(stt, wxConvUTF8);
+ r->razRule = (*_cond_sym)[index];
+ if (r->razRule == NULL) r->razRule = (*_cond_sym)[_T ( "QUESMRK1" )];
+ SCANFWRD
+}
+
+++str;
+}
+
+// If it should happen that no rule is built, delete the initially allocated
+// rule
+if (0 == top->ruleType) {
+ if (top->INST0) free(top->INST0);
+
+ free(top);
+
+ top = NULL;
+}
+
+// Traverse the entire rule set tree, pruning after first unallocated (dead)
+// rule
+r = top;
+while (r) {
+ if (0 == r->ruleType) {
+ free(r);
+ last->next = NULL;
+ break;
+ }
+
+ last = r;
+ Rules *n = r->next;
+ r = n;
+}
+
+// Traverse the entire rule set tree, adding sequence numbers
+r = top;
+int i = 0;
+while (r) {
+ r->n_sequence = i++;
+
+ r = r->next;
+}
+
+return top;
+}
+
+int s52plib::_LUP2rules(LUPrec *LUP, S57Obj *pObj) {
+ if (NULL == LUP) return -1;
+ // check if already parsed
+ if (LUP->ruleList != NULL) {
+ return 0;
+ }
+
+ if (!LUP->INST.IsEmpty()) {
+ Rules *top = StringToRules(LUP->INST);
+ LUP->ruleList = top;
+
+ return 1;
+ } else
+ return 0;
+}
+
+int s52plib::S52_load_Plib(const wxString &PLib, bool b_forceLegacy) {
+ pAlloc = new wxArrayPtrVoid;
+
+ // Create the Rule Lookup Hash Tables
+ _line_sym = new RuleHash; // line
+ _patt_sym = new RuleHash; // pattern
+ _symb_sym = new RuleHash; // symbol
+ _cond_sym = new RuleHash; // conditional
+
+ line_LAC = new LUPArrayContainer;
+ areaPlain_LAC = new LUPArrayContainer;
+ areaSymbol_LAC = new LUPArrayContainer;
+ pointSimple_LAC = new LUPArrayContainer;
+ pointPaper_LAC = new LUPArrayContainer;
+
+ condSymbolLUPArray =
+ new wxArrayOfLUPrec(CompareLUPObjects); // dynamic Cond Sym LUPs
+
+ m_unused_color.R = 2;
+ m_unused_color.G = 2;
+ m_unused_color.B = 2;
+ m_unused_wxColor.Set(2, 2, 2);
+
+ if (!m_chartSymbols.LoadConfigFile(this, PLib)) {
+ wxString msg(_T("Could not load XML PLib symbol file: "));
+ msg += PLib;
+ wxLogMessage(msg);
+
+ return 0;
+ }
+
+ // Initialize the _cond_sym Hash Table from the jump table found in
+ // S52CNSY.CPP Hash Table indices are the literal CS Strings, e.g.
+ // "RESARE02" Hash Results Values are the Rule *, i.e. the CS procedure
+ // entry point
+
+ for (int i = 0; condTable[i].condInst != NULL; ++i) {
+ wxString index(condTable[i].name, wxConvUTF8);
+ (*_cond_sym)[index] = (Rule *)(condTable[i].condInst);
+ }
+
+ wxString s57data_dir = *GetpSharedDataLocation();
+ s57data_dir += _T("s57data");
+
+ wxString oc_file(s57data_dir);
+ oc_file.Append(_T("/s57objectclasses.csv"));
+
+ PreloadOBJLFromCSV(oc_file);
+
+ return 1;
+}
+
+void s52plib::ClearRulesCache(
+ Rule *pR) // Clear out any existing cached symbology
+{
+ switch (pR->parm0) {
+ case ID_wxBitmap: {
+ wxBitmap *pbm = (wxBitmap *)(pR->pixelPtr);
+ delete pbm;
+ pR->pixelPtr = NULL;
+ pR->parm0 = ID_EMPTY;
+ break;
+ }
+ case ID_RGBA: {
+ unsigned char *p = (unsigned char *)(pR->pixelPtr);
+ free(p);
+ pR->pixelPtr = NULL;
+ pR->parm0 = ID_EMPTY;
+ break;
+ }
+ case ID_GL_PATT_SPEC: {
+ render_canvas_parms *pp = (render_canvas_parms *)(pR->pixelPtr);
+ free(pp->pix_buff);
+#ifdef ocpnUSE_GL
+ if (pp->OGL_tex_name) glDeleteTextures(1, (GLuint *)&pp->OGL_tex_name);
+#endif
+ delete pp;
+ pR->pixelPtr = NULL;
+ pR->parm0 = ID_EMPTY;
+ break;
+ }
+ case ID_RGB_PATT_SPEC: {
+ render_canvas_parms *pp = (render_canvas_parms *)(pR->pixelPtr);
+ free(pp->pix_buff);
+ delete pp;
+ pR->pixelPtr = NULL;
+ pR->parm0 = ID_EMPTY;
+ break;
+ }
+ case ID_EMPTY:
+ break;
+ default:
+ assert(false);
+ break;
+ }
+}
+
+void s52plib::DestroyPatternRuleNode(Rule *pR) { DestroyRuleNode(pR); }
+
+void s52plib::DestroyRuleNode(Rule *pR) {
+ if (!pR) return;
+
+ delete pR->exposition.LXPO;
+
+ free(pR->vector.LVCT);
+
+ delete pR->bitmap.SBTM;
+
+ free(pR->colRef.SCRF);
+
+ ClearRulesCache(pR); // Clear out any existing cached symbology
+}
+
+void s52plib::DestroyRules(RuleHash *rh) {
+ RuleHash::iterator it;
+ Rule *pR;
+
+ for (it = (*rh).begin(); it != (*rh).end(); ++it) {
+ pR = it->second;
+ DestroyRuleNode(pR);
+ }
+
+ rh->clear();
+ delete rh;
+}
+
+void s52plib::FlushSymbolCaches(const ChartCtx &ctx) {
+ m_chartSymbols.LoadRasterFileForColorTable(m_colortable_index, true, ctx);
+ RuleHash *rh = _symb_sym;
+
+ if (!rh) return;
+
+ RuleHash::iterator it;
+ Rule *pR;
+
+ for (it = (*rh).begin(); it != (*rh).end(); ++it) {
+ pR = it->second;
+ if (pR) ClearRulesCache(pR);
+ }
+
+ // Flush any pattern definitions
+ rh = _patt_sym;
+
+ if (!rh) return;
+
+ for (it = (*rh).begin(); it != (*rh).end(); ++it) {
+ pR = it->second;
+ if (pR) ClearRulesCache(pR);
+ }
+
+ // OpenGL Hashmaps
+ CARC_Hash::iterator ita;
+ for (ita = m_CARC_hashmap.begin(); ita != m_CARC_hashmap.end(); ++ita) {
+ CARC_Buffer buffer = ita->second;
+ delete[] buffer.data;
+ }
+ m_CARC_hashmap.clear();
+
+ // Flush all texFonts
+ TexFont *f_cache = 0;
+ unsigned int i;
+ for (i = 0; i < TXF_CACHE; i++) {
+ if (s_txf[i].key != 0) {
+ f_cache = s_txf[i].cache;
+ f_cache->Delete();
+ s_txf[i].key = 0;
+ }
+ }
+}
+
+void s52plib::DestroyPattRules(RuleHash *rh) { DestroyRules(rh); }
+
+void s52plib::DestroyLUPArray(wxArrayOfLUPrec *pLUPArray) {
+ if (pLUPArray) {
+ for (unsigned int il = 0; il < pLUPArray->GetCount(); il++)
+ DestroyLUP(pLUPArray->Item(il));
+
+ pLUPArray->Clear();
+
+ delete pLUPArray;
+ }
+}
+
+void s52plib::ClearCNSYLUPArray(void) {
+ if (condSymbolLUPArray) {
+ for (unsigned int i = 0; i < condSymbolLUPArray->GetCount(); i++)
+ DestroyLUP(condSymbolLUPArray->Item(i));
+
+ condSymbolLUPArray->Clear();
+ }
+}
+
+bool s52plib::S52_flush_Plib() {
+ if (!m_bOK) return false;
+
+#ifdef ocpnUSE_GL
+ // OpenGL Hashmaps
+ CARC_Hash::iterator ita;
+ for (ita = m_CARC_hashmap.begin(); ita != m_CARC_hashmap.end(); ++ita) {
+ CARC_Buffer buffer = ita->second;
+ delete[] buffer.data;
+ }
+ m_CARC_hashmap.clear();
+
+#endif
+
+ DestroyLUPArray(condSymbolLUPArray);
+
+ // Destroy Rules
+ DestroyRules(_line_sym);
+ DestroyPattRules(_patt_sym);
+ DestroyRules(_symb_sym);
+
+ if (_symb_symR) DestroyRules(_symb_symR);
+
+ // Special case for CS
+ _cond_sym->clear();
+ delete (_cond_sym);
+
+ for (unsigned int ipa = 0; ipa < pAlloc->GetCount(); ipa++) {
+ void *t = pAlloc->Item(ipa);
+ free(t);
+ }
+
+ pAlloc->clear();
+ delete pAlloc;
+
+ return TRUE;
+}
+
+LUPrec *s52plib::S52_LUPLookup(LUPname LUP_Name, const char *objectName,
+ S57Obj *pObj, bool bStrict) {
+ LUPrec *LUP = NULL;
+
+ LUPArrayContainer *plac = SelectLUPArrayContainer(LUP_Name);
+
+ LUPHashIndex *hip = plac->GetArrayIndexHelper(objectName);
+ int nLUPs = hip->count;
+ int nStartIndex = hip->n_start;
+
+ LUP = FindBestLUP(plac->GetLUPArray(), nStartIndex, nLUPs, pObj, bStrict);
+
+ return LUP;
+}
+
+void s52plib::SetPLIBColorScheme(ColorScheme cs, const ChartCtx &ctx) {
+ wxString SchemeName;
+ switch (cs) {
+ case GLOBAL_COLOR_SCHEME_DAY:
+ SchemeName = _T("DAY");
+ break;
+ case GLOBAL_COLOR_SCHEME_DUSK:
+ SchemeName = _T("DUSK");
+ break;
+ case GLOBAL_COLOR_SCHEME_NIGHT:
+ SchemeName = _T("NIGHT");
+ break;
+ default:
+ SchemeName = _T("DAY");
+ break;
+ }
+
+ SetPLIBColorScheme(SchemeName, ctx);
+}
+
+void s52plib::SetPLIBColorScheme(wxString scheme, const ChartCtx &ctx) {
+ wxString str_find;
+ str_find = scheme;
+ m_colortable_index = 0; // default is the first color in the table
+
+ // Of course, it also depends on the plib version...
+ // plib version 3.2 calls "DAY" colr as "DAY_BRIGHT"
+
+ if ((GetMajorVersion() == 3) && (GetMinorVersion() == 2)) {
+ if (scheme.IsSameAs(_T ( "DAY" ))) str_find = _T ( "DAY_BRIGHT" );
+ }
+ m_colortable_index = m_chartSymbols.FindColorTable(scheme);
+
+ m_chartSymbols.SetColorTableIndex(m_colortable_index, false, ctx);
+
+ m_ColorScheme = scheme;
+}
+
+S52color *s52plib::getColor(const char *colorName) {
+ S52color *c;
+ c = m_chartSymbols.GetColor(colorName, m_colortable_index);
+ return c;
+}
+
+wxColour s52plib::getwxColour(const wxString &colorName) {
+ wxColor c;
+ c = m_chartSymbols.GetwxColor(colorName, m_colortable_index);
+ return c;
+}
+
+//----------------------------------------------------------------------------------
+//
+// Object Rendering Module
+//
+//----------------------------------------------------------------------------------
+
+//-----------------------------
+//
+// S52 TEXT COMMAND WORD PARSER
+//
+//-----------------------------
+#define APOS '\047'
+#define MAXL 512
+
+char *s52plib::_getParamVal(ObjRazRules *rzRules, char *str, char *buf, int bsz)
+// Symbology Command Word Parameter Value Parser.
+//
+// str: psz to Attribute of interest
+//
+// Results:Put in 'buf' one of:
+// 1- LUP constant value,
+// 2- ENC value,
+// 3- LUP default value.
+// Return pointer to the next field in the string (delim is ','), NULL to abort
+{
+ wxString value;
+ int defval = 0; // default value
+ int len = 0;
+ char *ret_ptr = str;
+ char *tmp = buf;
+
+ if (!buf) return NULL;
+
+ buf[0] = 0;
+ // parse constant parameter with concatenation operator "'"
+ if (str != NULL) {
+ if (*ret_ptr == APOS) {
+ ret_ptr++;
+ while (*ret_ptr != APOS && *ret_ptr != '\0' && len < (bsz - 1)) {
+ ++len;
+ *tmp++ = *ret_ptr++;
+ }
+ *tmp = '\0';
+ ret_ptr++; // skip "'"
+ ret_ptr++; // skip ","
+
+ return ret_ptr;
+ }
+
+ while (*ret_ptr != ',' && *ret_ptr != ')' && *ret_ptr != '\0' &&
+ len < (bsz - 1)) {
+ *tmp++ = *ret_ptr++;
+ ++len;
+ }
+ *tmp = '\0';
+
+ ret_ptr++; // skip ',' or ')'
+ }
+ if (len < 6) return ret_ptr;
+
+ // chop string if default value present
+ if (len > 6 && *(buf + 6) == '=') {
+ *(buf + 6) = '\0';
+ defval = 1;
+ }
+
+ value = rzRules->obj->GetAttrValueAsString(buf);
+ wxCharBuffer buffer = value.ToUTF8();
+ if (!buffer.data()) return ret_ptr;
+
+ if (value.IsEmpty()) {
+ if (defval)
+ _getParamVal(rzRules, buf + 7, buf,
+ bsz - 7); // default value --recursion
+ else {
+ return NULL; // abort
+ }
+ } else {
+ // Special case for conversion of vertical (height) attributes.
+ // Inputs are meters; convert to user's height unit (meters/feet) as
+ // selected in s52plib.
+ if ((!strncmp(buf, "VERCLR", 6)) || (!strncmp(buf, "VERCCL", 6)) ||
+ (!strncmp(buf, "VERCOP", 6)) || (!strncmp(buf, "ELEVAT", 6)) ||
+ (!strncmp(buf, "HEIGHT", 6))) {
+ if (m_nHeightUnitDisplay == 1) { // feet
+ double h_val_m;
+ if (value.ToDouble(&h_val_m)) {
+ double ft_val = h_val_m * 3.280839895013123; // meters to feet
+ value.Printf(_T("%4.1f"), ft_val);
+ }
+ }
+ // else meters: leave as-is
+ }
+
+ // special case when ENC returns an index for particular attribute types
+ if (!strncmp(buf, "NATSUR", 6)) {
+ wxString natsur_att(_T ( "NATSUR" ));
+ wxString result;
+ wxString svalue = value;
+ wxStringTokenizer tkz(svalue, _T ( "," ));
+
+ int icomma = 0;
+ while (tkz.HasMoreTokens()) {
+ if (icomma) result += _T ( "," );
+
+ wxString token = tkz.GetNextToken();
+ long i;
+ if (token.ToLong(&i)) {
+ std::string snat;
+ snat = m_natsur_hash[(int)i];
+
+ wxString nat(snat.c_str());
+ if (!nat.IsEmpty())
+ result += nat; // value from ENC
+ else
+ result += _T ( "unk" );
+ } else
+ result += _T ( "unk" );
+
+ icomma++;
+ }
+
+ value = result;
+ }
+
+ wxCharBuffer buffer = value.ToUTF8();
+ if (buffer.data()) {
+ unsigned int len = wxMin(strlen(buffer.data()), (unsigned int)bsz - 1);
+ memcpy(buf, buffer.data(), len);
+ buf[len] = 0;
+ } else
+ *buf = 0;
+ }
+
+ return ret_ptr;
+}
+
+char *s52plib::_parseTEXT(ObjRazRules *rzRules, S52_TextC *text, char *str0) {
+ char buf[MAXL]; // output string
+
+ char *str = str0;
+ if (text) {
+ memset(buf, 0, 4);
+ str = _getParamVal(rzRules, str, &text->hjust, MAXL); // HJUST
+ str = _getParamVal(rzRules, str, &text->vjust, MAXL); // VJUST
+ str = _getParamVal(rzRules, str, &text->space, MAXL); // SPACE
+
+ // CHARS
+ str = _getParamVal(rzRules, str, buf, MAXL);
+ text->style = buf[0];
+ text->weight = buf[1];
+ text->width = buf[2];
+ text->bsize = atoi(buf + 3);
+
+ str = _getParamVal(rzRules, str, buf, MAXL);
+ text->xoffs = atoi(buf);
+ str = _getParamVal(rzRules, str, buf, MAXL);
+ text->yoffs = atoi(buf);
+ str = _getParamVal(rzRules, str, buf, MAXL);
+ text->pcol = getColor(buf);
+ str = _getParamVal(rzRules, str, buf, MAXL);
+ text->dis = atoi(buf); // Text Group, used for "Important" text detection
+ }
+ return str;
+}
+
+S52_TextC *s52plib::S52_PL_parseTX(ObjRazRules *rzRules, Rules *rules,
+ char *cmd) {
+ S52_TextC *text = NULL;
+ char *str = NULL;
+ char val[MAXL]; // value of arg
+ char strnobjnm[7] = {"NOBJNM"};
+ char valn[MAXL]; // value of arg
+
+ valn[0] = 0;
+ str = (char *)rules->INSTstr;
+
+ if (m_bShowNationalTexts &&
+ NULL !=
+ strstr(str,
+ "OBJNAM")) // in case user wants the national text shown and
+ // the rule contains OBJNAM, try to get the value
+ {
+ _getParamVal(rzRules, strnobjnm, valn, MAXL);
+ if (!strcmp(strnobjnm, valn))
+ valn[0] = '\0'; // NOBJNM is not defined
+ else
+ valn[MAXL - 1] = '\0'; // make sure the string terminates
+ }
+
+ str = _getParamVal(rzRules, str, val, MAXL); // get ATTRIB list
+
+ if (NULL == str)
+ return 0; // abort this command word if mandatory param absent
+
+ val[MAXL - 1] = '\0'; // make sure the string terminates
+
+ text = new S52_TextC;
+ str = _parseTEXT(rzRules, text, str);
+ if (NULL != text) {
+ if (valn[0] != '\0') {
+ text->frmtd = wxString(valn, wxConvUTF8);
+ text->bnat = true;
+ } else {
+ text->frmtd = wxString(val, wxConvUTF8);
+ text->bnat = false;
+ }
+ }
+
+ // We check to see if the formatted text has any "special" characters
+ wxCharBuffer buf = text->frmtd.ToUTF8();
+
+ unsigned int n = text->frmtd.Length();
+ for (unsigned int i = 0; i < n; ++i) {
+ unsigned char c = buf.data()[i];
+ if (c > 127) {
+ text->bspecial_char = true;
+ break;
+ }
+ }
+
+ return text;
+}
+
+S52_TextC *s52plib::S52_PL_parseTE(ObjRazRules *rzRules, Rules *rules,
+ char *cmd)
+// same as S52_PL_parseTX put parse 'C' format first
+{
+ char arg[MAXL]; // ATTRIB list
+ char fmt[MAXL]; // FORMAT
+ char buf[MAXL]; // output string
+ char *b = buf;
+ char *parg = arg;
+ char *pf = fmt;
+ S52_TextC *text = NULL;
+
+ char *str = (char *)rules->INSTstr;
+
+ if (str && *str) {
+ str = _getParamVal(rzRules, str, fmt, MAXL); // get FORMAT
+
+ str = _getParamVal(rzRules, str, arg, MAXL); // get ATTRIB list
+ if (NULL == str)
+ return 0; // abort this command word if mandatory param absent
+
+ //*b = *pf;
+ while (*pf != '\0') {
+ // begin a convertion specification
+ if (*pf == '%') {
+ char val[MAXL]; // value of arg
+ char tmp[MAXL] = {'\0'}; // temporary format string
+ char *t = tmp;
+ int cc = 0; // 1 == Conversion Character found
+ //*t = *pf;
+
+ // get value for this attribute
+ parg = _getParamVal(rzRules, parg, val, MAXL);
+ if (NULL == parg) return 0; // abort
+
+ if (0 == strcmp(val, "2147483641")) return 0;
+
+ *t = *pf; // stuff the '%'
+
+ // scan for end at convertion character
+ do {
+ *++t = *++pf; // fill conver spec
+
+ switch (*pf) {
+ case 'c':
+ case 's':
+ b += sprintf(b, tmp, val);
+ cc = 1;
+ break;
+ case 'f':
+ b += sprintf(b, tmp, atof(val));
+ cc = 1;
+ break;
+ case 'd':
+ case 'i':
+ b += sprintf(b, tmp, atoi(val));
+ cc = 1;
+ break;
+ }
+ } while (!cc);
+ pf++; // skip conv. char
+
+ } else
+ *b++ = *pf++;
+ }
+
+ *b = 0;
+ text = new S52_TextC;
+ str = _parseTEXT(rzRules, text, str);
+ if (NULL != text) text->frmtd = wxString(buf, wxConvUTF8);
+
+ // We check to see if the formatted text has any "special" characters
+ wxCharBuffer buf = text->frmtd.ToUTF8();
+
+ unsigned int n = text->frmtd.Length();
+ for (unsigned int i = 0; i < n; ++i) {
+ unsigned char c = buf.data()[i];
+ if (c > 127) {
+ text->bspecial_char = true;
+ break;
+ }
+ }
+ }
+
+ return text;
+}
+
+static void rotate(wxRect *r, VPointCompat const *vp) {
+ float cx = vp->pix_width / 2.;
+ float cy = vp->pix_height / 2.;
+ float c = cosf(vp->rotation);
+ float s = sinf(vp->rotation);
+ float x = r->GetX() - cx;
+ float y = r->GetY() - cy;
+ r->SetX(x * c - y * s + cx);
+ r->SetY(x * s + y * c + cy);
+}
+
+bool s52plib::RenderText(wxDC *pdc, S52_TextC *ptext, double x, double y,
+ wxRect2D *pRectDrawn, S57Obj *pobj, bool bCheckOverlap) {
+#ifdef DrawText
+#undef DrawText
+#define FIXIT
+#endif
+ bool bdraw = true;
+
+ wxFont *scaled_font = ptext->pFont;
+ wxCoord w_scaled = 0;
+ wxCoord h_scaled = 0;
+ wxCoord descent = 0;
+ wxCoord exlead = 0;
+
+ // FIXME(plib)
+ double sfactor = 1; // vp_plib.ref_scale / vp_plib.chart_scale;
+ double scale_factor = wxMax((sfactor) / 4., 1.);
+
+ // Place an upper bound on the scaled text size
+ scale_factor = wxMin(scale_factor, 4);
+
+ scale_factor /= m_dipfactor;
+ scale_factor *= m_TextScaleFactor;
+
+ // Has there been a change in scale factor by UI?
+ if (scale_factor != m_FinalTextScaleFactor) {
+ ptext->texobj = 0; // This will leak, but only a little
+ m_FinalTextScaleFactor = scale_factor;
+
+ for (unsigned int i = 0; i < TXF_CACHE; i++) {
+ s_txf[i].key = 0;
+ s_txf[i].cache = 0;
+ }
+ }
+
+ if (!pdc) // OpenGL
+ {
+#ifdef ocpnUSE_GL
+
+ bool b_force_no_texfont = false;
+ if (ptext->bspecial_char) b_force_no_texfont = true;
+
+ // Extensive profiling has shown that rendering the full text string
+ // atomically is much faster than rendering glyph-by-glyph.
+ // Accordingly, switch to this method for all cases.
+ b_force_no_texfont = true;
+ int tex_w, tex_h;
+ if (b_force_no_texfont) {
+ if (!ptext->texobj) { // is texture ready?
+
+ int old_size = ptext->pFont->GetPointSize();
+ int new_size = old_size * scale_factor / m_ContentScaleFactor;
+
+ scaled_font = GetS52Utils()->GetScaledFont(
+ new_size, ptext->pFont->GetFamily(), ptext->pFont->GetStyle(),
+ ptext->pFont->GetWeight(), ptext->pFont->GetFaceName(), 1.0);
+ // FindOrCreateFont_PlugIn(
+ // new_size, ptext->pFont->GetFamily(), ptext->pFont->GetStyle(),
+ // ptext->pFont->GetWeight(), false, ptext->pFont->GetFaceName());
+ wxScreenDC sdc;
+ sdc.GetTextExtent(ptext->frmtd, &w_scaled, &h_scaled, &descent, &exlead,
+ scaled_font); // measure the text
+
+ // We cannot get the font ascent value to remove the interline spacing
+ // from the font "height". So we have to estimate based on conventional
+ // Arial metrics
+ ptext->rendered_char_height =
+ (h_scaled - descent) * 8 / 10 * m_dipfactor;
+
+ ptext->text_width = w_scaled * m_dipfactor;
+ ptext->text_height = h_scaled * m_dipfactor;
+
+ /* make power of 2 */
+ for (tex_w = 1; tex_w < w_scaled; tex_w *= 2);
+ for (tex_h = 1; tex_h < h_scaled; tex_h *= 2);
+
+ wxMemoryDC mdc;
+ wxBitmap bmp(tex_w, tex_h);
+ mdc.SelectObject(bmp);
+ mdc.SetFont(*(scaled_font));
+
+ if (mdc.IsOk()) {
+ // Render the text as white on black, so that underlying
+ // anti-aliasing of wxDC text rendering can be extracted and
+ // converted to alpha-channel values.
+
+ mdc.SetBackground(wxBrush(wxColour(0, 0, 0)));
+ mdc.SetBackgroundMode(wxTRANSPARENT);
+
+ mdc.SetTextForeground(wxColour(255, 255, 255));
+
+ mdc.Clear();
+
+ mdc.DrawText(ptext->frmtd, 0, 0);
+ mdc.SelectObject(wxNullBitmap);
+
+ wxImage image = bmp.ConvertToImage();
+ int ws = image.GetWidth(), hs = image.GetHeight();
+
+ ptext->RGBA_width = ws;
+ ptext->RGBA_height = hs;
+ unsigned char *pRGBA = (unsigned char *)malloc(4 * ws * hs);
+
+ unsigned char *d = image.GetData();
+ unsigned char *pdest = pRGBA;
+ S52color *ccolor = ptext->pcol;
+
+ if (d) {
+ for (int y = 0; y < hs; y++)
+ for (int x = 0; x < ws; x++) {
+ unsigned char r, g, b;
+ int off = (y * ws + x);
+
+ r = d[off * 3 + 0];
+ g = d[off * 3 + 1];
+ b = d[off * 3 + 2];
+
+ pdest[off * 4 + 0] = ccolor->R;
+ pdest[off * 4 + 1] = ccolor->G;
+ pdest[off * 4 + 2] = ccolor->B;
+
+ int alpha = (r + g + b) / 3;
+ pdest[off * 4 + 3] = (unsigned char)(alpha & 0xff);
+ }
+ }
+
+ int draw_width = ptext->RGBA_width;
+ int draw_height = ptext->RGBA_height;
+
+ glEnable(GL_TEXTURE_2D);
+
+ GLuint texobj;
+ glGenTextures(1, &texobj);
+
+ glBindTexture(GL_TEXTURE_2D, texobj);
+
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+
+ glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, draw_width, draw_height, 0,
+ GL_RGBA, GL_UNSIGNED_BYTE, pRGBA);
+
+ free(pRGBA);
+
+ ptext->texobj = texobj;
+
+ } // mdc OK
+ } // Building texobj
+
+ // Render the texture
+ if (ptext->texobj) {
+ int yadjust = 0;
+ int xadjust = 0;
+
+ // Adjust the y position to account for the convention that S52 text is
+ // drawn with the lower left corner at the specified point, instead of
+ // the wx convention using upper left corner. Also, allow for full text
+ // height in the bitmap/texture, not the estimated "rendered" height.
+
+ yadjust = -ptext->rendered_char_height * 10 / 8;
+
+ // Add in the offsets, specified in units of nominal font height
+ yadjust += ptext->yoffs * (ptext->rendered_char_height);
+ // X offset specified in units of average char width
+ xadjust += ptext->xoffs * ptext->avgCharWidth;
+
+ // adjust for text justification
+ int w = ptext->text_width;
+ switch (ptext->hjust) {
+ case '1': // centered
+ xadjust -= w / 2;
+ break;
+ case '2': // right
+ xadjust -= w;
+ break;
+ case '3': // left (default)
+ default:
+ break;
+ }
+
+ switch (ptext->vjust) {
+ case '3': // top
+ yadjust += ptext->rendered_char_height;
+ break;
+ case '2': // centered
+ yadjust += ptext->rendered_char_height / 2;
+ break;
+ case '1': // bottom (default)
+ default:
+ break;
+ }
+ double xp = x;
+ double yp = y;
+
+ if (fabs(vp_plib.rotation) > 0.01) {
+ float c = cosf(-vp_plib.rotation);
+ float s = sinf(-vp_plib.rotation);
+ float x = xadjust;
+ float y = yadjust;
+ xp += x * c - y * s;
+ yp += x * s + y * c;
+
+ } else {
+ xp += xadjust;
+ yp += yadjust;
+ }
+
+
+ pRectDrawn->SetX(xp);
+ pRectDrawn->SetY(yp);
+ pRectDrawn->SetWidth(ptext->text_width);
+ pRectDrawn->SetHeight(ptext->text_height);
+
+ if (bCheckOverlap) {
+ if (CheckTextRectList(*pRectDrawn, ptext)) bdraw = false;
+ }
+
+ if (bdraw) {
+#if !defined(USE_ANDROID_GLES2) && !defined(ocpnUSE_GLSL)
+#else
+ glEnable(GL_BLEND);
+ glEnable(GL_TEXTURE_2D);
+
+ float uv[8];
+ float coords[8];
+
+ // Note swizzle of points to allow TRIANGLE_STRIP drawing
+ // normal uv
+ uv[0] = 0;
+ uv[1] = 0;
+ uv[2] = 1;
+ uv[3] = 0;
+ uv[6] = 1;
+ uv[7] = 1;
+ uv[4] = 0;
+ uv[5] = 1;
+
+ // pixels
+ coords[0] = 0;
+ coords[1] = 0;
+ coords[2] = ptext->RGBA_width;
+ coords[3] = 0;
+ coords[6] = ptext->RGBA_width;
+ coords[7] = ptext->RGBA_height;
+ coords[4] = 0;
+ coords[5] = ptext->RGBA_height;
+
+ glUseProgram(S52texture_2D_shader_program);
+
+ // Get pointers to the attributes in the program.
+ GLint mPosAttrib =
+ glGetAttribLocation(S52texture_2D_shader_program, "position");
+ GLint mUvAttrib =
+ glGetAttribLocation(S52texture_2D_shader_program, "aUV");
+
+ // Select the active texture unit.
+ glActiveTexture(GL_TEXTURE0);
+
+ // Bind our texture to the texturing target.
+ glBindTexture(GL_TEXTURE_2D, ptext->texobj);
+
+ // Set up the texture sampler to texture unit 0
+ GLint texUni =
+ glGetUniformLocation(S52texture_2D_shader_program, "uTex");
+ glUniform1i(texUni, 0);
+
+ // Disable VBO's (vertex buffer objects) for attributes.
+ glBindBuffer(GL_ARRAY_BUFFER, 0);
+ glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
+
+ // Set the attribute mPosAttrib with the vertices in the screen
+ // coordinates...
+ glVertexAttribPointer(mPosAttrib, 2, GL_FLOAT, GL_FALSE, 0, coords);
+ // ... and enable it.
+ glEnableVertexAttribArray(mPosAttrib);
+
+ // Set the attribute mUvAttrib with the vertices in the GL
+ // coordinates...
+ glVertexAttribPointer(mUvAttrib, 2, GL_FLOAT, GL_FALSE, 0, uv);
+ // ... and enable it.
+ glEnableVertexAttribArray(mUvAttrib);
+
+ // Rotate
+ mat4x4 I, Q;
+ mat4x4_identity(I);
+
+ mat4x4_translate_in_place(I, x, y, 0);
+ mat4x4_rotate_Z(Q, I, -vp_plib.rotation);
+ mat4x4_translate_in_place(Q, xadjust, yadjust, 0);
+
+ GLint matloc = glGetUniformLocation(S52texture_2D_shader_program,
+ "TransformMatrix");
+ glUniformMatrix4fv(matloc, 1, GL_FALSE, (const GLfloat *)Q);
+
+ // Perform the actual drawing.
+ glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
+
+ // Restore the per-object transform to Identity Matrix
+ mat4x4 IM;
+ mat4x4_identity(IM);
+ GLint matlocf = glGetUniformLocation(S52texture_2D_shader_program,
+ "TransformMatrix");
+ glUniformMatrix4fv(matlocf, 1, GL_FALSE, (const GLfloat *)IM);
+
+ glDisableVertexAttribArray(mPosAttrib);
+ glDisableVertexAttribArray(mUvAttrib);
+
+ glDisable(GL_TEXTURE_2D);
+ glDisable(GL_BLEND);
+#endif
+
+ } // bdraw
+ }
+ }
+
+#if 0 // no longer used, see note above re profile results.
+ else { // render using cached texture glyphs
+ // rebuild font if needed
+ TexFont *f_cache = 0;
+ unsigned int i;
+
+ for (i = 0; i < TXF_CACHE; i++) {
+ if (s_txf[i].key == ptext->pFont) {
+ f_cache = s_txf[i].cache;
+ break;
+ }
+ if (s_txf[i].key == 0) {
+ break;
+ }
+ }
+ if (i == TXF_CACHE) {
+ i = rand() & (TXF_CACHE - 1);
+ }
+ if (f_cache == 0) {
+ s_txf[i].key = ptext->pFont;
+ if(s_txf[i].cache)
+ delete s_txf[i].cache;
+ s_txf[i].cache = new TexFont();
+ s_txf[i].cache->SetContentScaleFactor(m_ContentScaleFactor);
+
+ f_cache = s_txf[i].cache;
+ f_cache->Build(*ptext->pFont, m_TextScaleFactor, m_dipfactor);
+
+ int wac;
+ f_cache->GetTextExtent(_T("M"), &wac, 0);
+ ptext->avgCharWidth = wac * m_dipfactor;
+ }
+
+ int w, h;
+ f_cache->GetTextExtent(ptext->frmtd, &w, &h);
+ h *= m_dipfactor;
+
+ // We don't store descent/ascent info for font texture cache
+ // So we have to estimate based on conventional Arial metrics
+ ptext->rendered_char_height = h * 65 / 100;
+
+ // Adjust the y position to account for the convention that S52 text is
+ // drawn with the lower left corner at the specified point, instead of
+ // the wx convention using upper right corner
+ int yadjust = 0;
+ int xadjust = 0;
+
+ yadjust = -ptext->rendered_char_height;
+
+ // Add in the offsets, specified in units of nominal font height
+ yadjust += ptext->yoffs * (ptext->rendered_char_height);
+ // X offset specified in units of average char width
+ xadjust += ptext->xoffs * ptext->avgCharWidth;
+
+ // adjust for text justification
+ switch (ptext->hjust) {
+ case '1': // centered
+ xadjust -= w / 2;
+ break;
+ case '2': // right
+ xadjust -= w;
+ break;
+ case '3': // left (default)
+ default:
+ break;
+ }
+
+ switch (ptext->vjust) {
+ case '3': // top
+ yadjust += ptext->rendered_char_height;
+ break;
+ case '2': // centered
+ yadjust += ptext->rendered_char_height / 2;
+ break;
+ case '1': // bottom (default)
+ default:
+ break;
+ }
+
+ int xp = x;
+ int yp = y;
+
+ if (fabs(vp_plib.rotation) > 0.01) {
+ float c = cosf(-vp_plib.rotation);
+ float s = sinf(-vp_plib.rotation);
+ float x = xadjust;
+ float y = yadjust;
+ xadjust = x * c - y * s;
+ yadjust = x * s + y * c;
+ }
+
+ xp += xadjust;
+ yp += yadjust;
+
+ pRectDrawn->SetX(xp);
+ pRectDrawn->SetY(yp);
+ pRectDrawn->SetWidth(w);
+ pRectDrawn->SetHeight(h);
+
+ if (bCheckOverlap) {
+ if (fabs(vp_plib.rotation) > .01) {
+ rotate(pRectDrawn, &vp_plib);
+ }
+ if (CheckTextRectList(*pRectDrawn, ptext)) bdraw = false;
+ }
+
+ if (bdraw) {
+#if !defined(USE_ANDROID_GLES2) && !defined(ocpnUSE_GLSL)
+#else
+ glEnable(GL_BLEND);
+ glEnable(GL_TEXTURE_2D);
+
+ wxColour wcolor = GetFontColour_PlugIn(_("ChartTexts"));
+ f_cache->SetColor(wcolor);
+
+ /* undo previous rotation to make text level */
+ // glRotatef(vp->rotation*180/PI, 0, 0, -1);
+
+ f_cache->RenderString(ptext->frmtd, xp, yp);
+
+ glDisable(GL_TEXTURE_2D);
+ glDisable(GL_BLEND);
+#endif
+ }
+ }
+#endif
+
+#endif
+ } else { // Not OpenGL
+ wxFont oldfont = pdc->GetFont(); // save current font
+
+ if (scale_factor > 1) {
+ wxFont *pf = ptext->pFont;
+ int old_size = pf->GetPointSize();
+ int new_size = old_size * scale_factor;
+ wxFont *scaled_font =
+ FindOrCreateFont_PlugIn(new_size, pf->GetFamily(), pf->GetStyle(),
+ pf->GetWeight(), false, pf->GetFaceName());
+ pdc->SetFont(*scaled_font);
+ } else {
+ pdc->SetFont(*(ptext->pFont));
+ }
+
+ wxCoord w, h, descent, exlead;
+ pdc->GetTextExtent(ptext->frmtd, &w, &h, &descent,
+ &exlead); // measure the text
+
+ // We cannot get the font ascent value to remove the interline spacing.
+ // So we have to estimate based on conventional Arial metrics
+ int rendered_text_height = (h - descent) * 8 / 10;
+
+ // Adjust the y position to account for the convention that S52 text is
+ // drawn with the lower left corner at the specified point, instead of the
+ // wx convention using upper right corner
+ int yadjust = 0;
+ int xadjust = 0;
+
+ yadjust = -(rendered_text_height);
+
+ // Add in the offsets, specified in units of nominal font height
+ yadjust += ptext->yoffs * (rendered_text_height);
+
+ // X offset specified in units of average char width
+ xadjust += ptext->xoffs * ptext->avgCharWidth * m_dipfactor;
+
+ // adjust for text justification
+ switch (ptext->hjust) {
+ case '1': // centered
+ xadjust -= w / 2;
+ break;
+ case '2': // right
+ xadjust -= w;
+ break;
+ case '3': // left (default)
+ default:
+ break;
+ }
+
+ switch (ptext->vjust) {
+ case '3': // top
+ yadjust += rendered_text_height;
+ break;
+ case '2': // centered
+ yadjust += rendered_text_height / 2;
+ break;
+ case '1': // bottom (default)
+ default:
+ break;
+ }
+
+ int xp = x;
+ int yp = y;
+
+ if (fabs(vp_plib.rotation) > 0.01) {
+ float cx = vp_plib.pix_width / 2.;
+ float cy = vp_plib.pix_height / 2.;
+ float c = cosf(vp_plib.rotation);
+ float s = sinf(vp_plib.rotation);
+ float x = xp - cx;
+ float y = yp - cy;
+ xp = x * c - y * s + cx + vp_plib.rv_rect.x;
+ yp = x * s + y * c + cy + vp_plib.rv_rect.y;
+ }
+
+ xp += xadjust;
+ yp += yadjust;
+
+ pRectDrawn->SetX(xp);
+ pRectDrawn->SetY(yp);
+ pRectDrawn->SetWidth(w);
+ pRectDrawn->SetHeight(h);
+
+ if (bCheckOverlap) {
+ if (CheckTextRectList(*pRectDrawn, ptext)) bdraw = false;
+ }
+
+ if (bdraw) {
+ wxColour wcolor = GetFontColour_PlugIn(_("ChartTexts"));
+
+ // If the user has not changed the color from BLACK, then use the color
+ // specified in the S52 LUP
+ if (wcolor == *wxBLACK)
+ wcolor = wxColour(ptext->pcol->R, ptext->pcol->G, ptext->pcol->B);
+ pdc->SetTextForeground(wcolor);
+
+ pdc->DrawText(ptext->frmtd, xp, yp);
+ }
+
+ pdc->SetFont(oldfont); // restore last font
+ }
+ return bdraw;
+
+#ifdef FIXIT
+#undef FIXIT
+#define DrawText DrawTextA
+#endif
+}
+
+// Return true if test_rect overlaps any rect in the current text rectangle
+// list, except itself
+bool s52plib::CheckTextRectList(const wxRect2D &test_rect, S52_TextC *ptext) {
+ // Iterate over the current object list, looking at rText
+
+ for (TextObjList::Node *node = m_textObjList.GetFirst(); node;
+ node = node->GetNext()) {
+ wxRect2D *pcurrent_rect = &(node->GetData()->rText);
+ if (node->GetData() == ptext) continue;
+
+ if (pcurrent_rect->Intersects(test_rect)) {
+ return true;
+ }
+ }
+ return false;
+}
+
+bool s52plib::TextRenderCheck(ObjRazRules *rzRules) {
+ if (!m_bShowS57Text) return false;
+
+ if (rzRules->obj->bIsAton) {
+ if (!strncmp(rzRules->obj->FeatureName, "LIGHTS", 6)) {
+ if (!m_bShowLdisText) return false;
+ } else {
+ if (!m_bShowAtonText) return false;
+ }
+ }
+
+ // Declutter LIGHTS descriptions
+ if ((rzRules->obj->bIsAton) &&
+ (!strncmp(rzRules->obj->FeatureName, "LIGHTS", 6))) {
+ if (lastLightLat == rzRules->obj->m_lat &&
+ lastLightLon == rzRules->obj->m_lon) {
+ return false; // only render text for the first object at this lat/lon
+ } else {
+ lastLightLat = rzRules->obj->m_lat;
+ lastLightLon = rzRules->obj->m_lon;
+ }
+ }
+
+ // An optimization for CM93 charts.
+ // Don't show the text associated with some objects, since CM93 database
+ // includes _texto objects aplenty
+ // FIXME VBO Dave
+ // chart type is available in member m_chart_context
+ // Do not use auxParm3
+ if (((int)rzRules->obj->auxParm3 ==
+ (int)S52_ChartTypeEnum::S52_CHART_TYPE_CM93) ||
+ ((int)rzRules->obj->auxParm3 ==
+ (int)S52_ChartTypeEnum::S52_CHART_TYPE_CM93COMP)) {
+ if (!strncmp(rzRules->obj->FeatureName, "BUAARE", 6))
+ return false;
+ else if (!strncmp(rzRules->obj->FeatureName, "SEAARE", 6))
+ return false;
+ else if (!strncmp(rzRules->obj->FeatureName, "LNDRGN", 6))
+ return false;
+ else if (!strncmp(rzRules->obj->FeatureName, "LNDARE", 6))
+ return false;
+ }
+
+ return true;
+}
+
+int s52plib::RenderT_All(ObjRazRules *rzRules, Rules *rules, bool bTX) {
+ if (!TextRenderCheck(rzRules)) return 0;
+
+ S52_TextC *text = NULL;
+ bool b_free_text = false;
+
+ // The first Ftext object is cached in the S57Obj.
+ // If not present, create it on demand
+ if (!rzRules->obj->bFText_Added) {
+ if (bTX)
+ text = S52_PL_parseTX(rzRules, rules, NULL);
+ else
+ text = S52_PL_parseTE(rzRules, rules, NULL);
+
+ if (text) {
+ rzRules->obj->bFText_Added = true;
+ rzRules->obj->FText = text;
+ rzRules->obj->FText->rul_seq_creator = rules->n_sequence;
+ }
+ }
+
+ // S57Obj already contains a cached text object
+ // If it was created by this Rule earlier, then render it
+ // Otherwise, create a new text object, render it, and delete when done
+ // This will be slower, obviously, but happens infrequently enough?
+
+ else {
+ if (rules->n_sequence == rzRules->obj->FText->rul_seq_creator)
+ text = rzRules->obj->FText;
+ else {
+ if (bTX)
+ text = S52_PL_parseTX(rzRules, rules, NULL);
+ else
+ text = S52_PL_parseTE(rzRules, rules, NULL);
+
+ b_free_text = true;
+ }
+ }
+
+ if (text) {
+ if (m_bShowS57ImportantTextOnly && (text->dis >= 20)) {
+ if (b_free_text) delete text;
+ return 0;
+ }
+
+ // Establish a font
+ if (!text->pFont) {
+ // Process the font specifications from the LUP symbolization rule
+ int spec_weight = text->weight - 0x30;
+ wxFontWeight fontweight;
+ if (spec_weight < 5)
+ fontweight = wxFONTWEIGHT_LIGHT;
+ else {
+ if (spec_weight == 5)
+ fontweight = wxFONTWEIGHT_NORMAL;
+ else
+ fontweight = wxFONTWEIGHT_BOLD;
+ }
+
+ wxFont *specFont = GetS52Utils()->GetScaledFont(
+ text->bsize, wxFONTFAMILY_SWISS, wxFONTSTYLE_NORMAL, fontweight,
+ wxEmptyString, 1.0);
+
+ // Get the width of a single average character in the spec font
+ wxScreenDC dc;
+ dc.SetFont(*specFont);
+ int width;
+ dc.GetTextExtent(_T("X"), &width, NULL, NULL, NULL, specFont);
+ text->avgCharWidth = width;
+
+ int spec_weight_t = text->weight - 0x30;
+ wxFontWeight fontweight_t;
+ if (spec_weight_t < 5)
+ fontweight_t = wxFONTWEIGHT_LIGHT;
+ else if (spec_weight_t == 5)
+ fontweight_t = wxFONTWEIGHT_NORMAL;
+ else
+ fontweight_t = wxFONTWEIGHT_BOLD;
+
+ // Get the configured "ChartText" font, which may be the default system
+ // font or another font that has been customized by the user.
+ // The user may have customized both the font face and the font size.
+ // We will use the user-configured font size as the base size for the
+ // text rendering, and adjust the size based on the S52 text size
+ // specification.
+
+ wxFont *ChartTextDefaultFont =
+ GetS52Utils()->GetDefaultFont(_("ChartTexts"), 0);
+ int default_size = ChartTextDefaultFont->GetPointSize();
+
+ // In S52, the "body size" refers to the base size of the text characters
+ // in display units (typically pixels). This is part of the text style
+ // parameters that control how chart text elements are rendered.
+ // The S52 standard (IHO S-52 Presentation Library) uses this parameter
+ // as part of the text placement and styling rules for chart text
+ // elements. The body size is used as a reference unit for Text scaling.
+ //
+ // NOAA ENC files requests font size up to 20 points, which looks very
+ // disproportioned. Let's scale those sizes down to more reasonable
+ // values.
+ int fontSize = text->bsize;
+
+ const int maxInputSize = 20; // Largest expected input size.
+ const int normalizedRange = 4;
+ // Clamp the input size.
+ fontSize = wxMin(maxInputSize, fontSize);
+
+ // Normalize the font size by mapping from [0, maxInputSize] to
+ // [0, normalizedRange] in a linear, proportional way.
+ // The transformation is inherently monotonic - larger input values will
+ // always produce larger output values before clamping.
+ fontSize =
+ (static_cast(fontSize) / static_cast(maxInputSize)) *
+ normalizedRange;
+ // The normalized value is added to the default font size to get the final
+ // font size.
+ // This respects user preferences while still adapting to chart
+ // specifications.
+ fontSize += default_size;
+
+ // In no case should font size be less than 10, since it becomes
+ // unreadable
+ fontSize = wxMax(10, fontSize);
+
+ text->pFont = GetS52Utils()->GetScaledFont(
+ fontSize, ChartTextDefaultFont->GetFamily(),
+ ChartTextDefaultFont->GetStyle(), fontweight_t,
+ ChartTextDefaultFont->GetFaceName(), 1.0);
+ }
+
+ // Render text at declared x/y of object
+ wxPoint2DDouble dr;
+ GetDoublePointPixSingle(rzRules, rzRules->obj->y, rzRules->obj->x, &dr);
+
+
+ wxRect2D rect;
+ bool bwas_drawn = RenderText(m_pdc, text, dr.m_x, dr.m_y, &rect, rzRules->obj,
+ m_bDeClutterText);
+
+ // If this is an un-cached text render, it probably means that a single
+ // object has two or more text renders in its rule set. RDOCAL is one
+ // example. There are others We need to cache only the first text
+ // structure, but should update the render rectangle to reflect all texts
+ // rendered for this object, in order to process the declutter logic.
+ bool b_dupok = false;
+ if (b_free_text) {
+ delete text;
+
+ if (!bwas_drawn) {
+ return 1;
+ } else { // object was drawn
+ text = rzRules->obj->FText;
+
+ wxRect2D r0 = text->rText;
+ r0 = r0.Union(rect);
+ text->rText = r0;
+
+ b_dupok = true; // OK to add a duplicate text structure to the
+ // declutter list, just for this case.
+ }
+ } else
+ text->rText = rect;
+
+ // If this text was actually drawn, add a pointer to its rect to the
+ // de-clutter list if it doesn't already exist
+ if (m_bDeClutterText) {
+ if (bwas_drawn) {
+ bool b_found = false;
+ for (TextObjList::Node *node = m_textObjList.GetFirst(); node;
+ node = node->GetNext()) {
+ S52_TextC *oc = node->GetData();
+
+ if (oc == text) {
+ if (!b_dupok) b_found = true;
+ break;
+ }
+ }
+ if (!b_found) m_textObjList.Append(text);
+ }
+ }
+
+ // Update the object Bounding box
+ // so that subsequent drawing operations will redraw the item fully
+ // and so that cursor hit testing includes both the text and the object
+
+ // if ( rzRules->obj->Primitive_type == GEO_POINT )
+ {
+ double latmin, lonmin, latmax, lonmax;
+
+ GetPixPointSingleNoRotate(rect.x, rect.y + rect.height,
+ &latmin, &lonmin);
+ GetPixPointSingleNoRotate(rect.x + rect.width, rect.y,
+ &latmax, &lonmax);
+ LLBBox bbtext;
+ bbtext.Set(latmin, lonmin, latmax, lonmax);
+
+ rzRules->obj->BBObj.Expand(bbtext);
+ }
+ }
+
+ return 1;
+}
+
+// Text
+int s52plib::RenderTX(ObjRazRules *rzRules, Rules *rules) {
+ return RenderT_All(rzRules, rules, true);
+}
+
+// Text formatted
+int s52plib::RenderTE(ObjRazRules *rzRules, Rules *rules) {
+ return RenderT_All(rzRules, rules, false);
+}
+
+bool s52plib::RenderHPGL(ObjRazRules *rzRules, Rule *prule, wxPoint2DDouble &r,
+ float rot_angle, double uScale) {
+ float fsf = 100 / canvas_pix_per_mm;
+
+ float xscale = 1.0;
+
+ if ((!strncmp(rzRules->obj->FeatureName, "TSSLPT", 6)) ||
+ (!strncmp(rzRules->obj->FeatureName, "DWRTPT", 6)) ||
+ (!strncmp(rzRules->obj->FeatureName, "TWRTPT", 6)) ||
+ (!strncmp(rzRules->obj->FeatureName, "RCTLPT", 6))) {
+ // assume the symbol length
+ float sym_length = 30;
+ float scaled_length = sym_length / vp_plib.view_scale_ppm;
+ float target_length = 800;
+
+ xscale = target_length / scaled_length;
+ xscale = wxMin(xscale, 1.0);
+ xscale = wxMax(.4, xscale);
+
+ // printf("scaled length: %g xscale: %g\n", scaled_length, xscale);
+
+ fsf *= xscale;
+ }
+
+ xscale *= uScale;
+
+ // Special case for GEO_AREA objects with centred symbols
+ if (rzRules->obj->Primitive_type == GEO_AREA) {
+ wxPoint r;
+ GetPointPixSingle(rzRules, rzRules->obj->y, rzRules->obj->x, &r);
+
+ double latdraw, londraw; // position of the drawn symbol with pivot applied
+ GetPixPointSingleNoRotate(
+ r.x + ((prule->pos.symb.pivot_x.SYCL - prule->pos.symb.bnbox_x.SBXC) /
+ fsf),
+ r.y + ((prule->pos.symb.pivot_y.SYRW - prule->pos.symb.bnbox_y.SBXR) /
+ fsf),
+ &latdraw, &londraw);
+
+ if (!rzRules->obj->BBObj.Contains(
+ latdraw,
+ londraw)) // Symbol reference point is outside base area object
+ return 1;
+ }
+
+ double render_angle = rot_angle;
+
+ // Very special case for ATON flare lights at 135 degrees, the standard
+ // render angle. We don't want them to rotate with the viewport.
+ if (rzRules->obj->bIsAton &&
+ (!strncmp(rzRules->obj->FeatureName, "LIGHTS", 6))) {
+#ifdef __OCPN__ANDROID__
+ // Due to popular request, we make the flare lights a little bit
+ // smaller than S52 specifications
+ xscale = xscale * 5. / 7.;
+#endif
+
+ if (fabs(rot_angle - 135.0) < 1.)
+ render_angle -= vp_plib.rotation * 180. / PI;
+ }
+
+ int width = prule->pos.symb.bnbox_x.SBXC + prule->pos.symb.bnbox_w.SYHL;
+ width *= 4; // Grow the drawing bitmap to allow for rotation of symbols with
+ // highly offset pivot points
+ width = (int)(width / fsf);
+
+ int height = prule->pos.symb.bnbox_y.SBXR + prule->pos.symb.bnbox_h.SYVL;
+ height *= 4;
+ height = (int)(height / fsf);
+
+ int origin_x = prule->pos.symb.bnbox_x.SBXC;
+ int origin_y = prule->pos.symb.bnbox_y.SBXR;
+ wxPoint origin(origin_x, origin_y);
+
+ int pivot_x = prule->pos.symb.pivot_x.SYCL;
+ int pivot_y = prule->pos.symb.pivot_y.SYRW;
+ wxPoint pivot(pivot_x, pivot_y);
+
+ char *str = prule->vector.LVCT;
+ char *col = prule->colRef.LCRF;
+ wxPoint2DDouble r0((pivot_x / fsf), (pivot_y / fsf));
+
+ HPGL->SetVP(&vp_plib);
+
+ if (!m_pdc) { // OpenGL Mode, do a direct render
+ HPGL->SetTargetOpenGl();
+ HPGL->Render(str, col, r, pivot, origin, xscale, render_angle, true);
+
+ // Update the object Bounding box
+ // so that subsequent drawing operations will redraw the item fully
+
+ int r_width = prule->pos.symb.bnbox_w.SYHL;
+ r_width = (int)(r_width / fsf);
+ int r_height = prule->pos.symb.bnbox_h.SYVL;
+ r_height = (int)(r_height / fsf);
+ int maxDim = wxMax(r_height, r_width);
+
+ double latmin, lonmin, latmax, lonmax;
+ GetPixPointSingleNoRotate(r.m_x - maxDim, r.m_y + maxDim, &latmin, &lonmin);
+ GetPixPointSingleNoRotate(r.m_x + maxDim, r.m_y - maxDim, &latmax, &lonmax);
+ LLBBox symbox;
+ symbox.Set(latmin, lonmin, latmax, lonmax);
+
+ rzRules->obj->BBObj.Expand(symbox);
+
+ } else {
+#if ((defined(__WXGTK__) || defined(__WXMAC__)) && !wxCHECK_VERSION(2, 9, 4))
+ wxBitmap *pbm = new wxBitmap(width, height);
+#else
+ wxBitmap *pbm = new wxBitmap(width, height, 32);
+#if !wxCHECK_VERSION(2, 9, 4)
+ pbm->UseAlpha();
+#endif
+#endif
+ wxMemoryDC mdc(*pbm);
+ if (!mdc.IsOk()) {
+ wxString msg;
+ msg.Printf(_T("RenderHPGL: width %d height %d"), width, height);
+ wxLogMessage(msg);
+ return false;
+ }
+
+#if wxUSE_GRAPHICS_CONTEXT
+ wxGCDC gdc(mdc);
+ HPGL->SetTargetGCDC(&gdc);
+#else
+ wxMemoryDC &gdc(mdc);
+ HPGL->SetTargetDC(&gdc);
+#endif
+ HPGL->Render(str, col, r0, pivot, origin, xscale, (double)rot_angle, true);
+
+ int bm_width = (gdc.MaxX() - gdc.MinX()) + 4;
+ int bm_height = (gdc.MaxY() - gdc.MinY()) + 4;
+ int bm_orgx = wxMax(0, gdc.MinX() - 2);
+ int bm_orgy = wxMax(0, gdc.MinY() - 2);
+ int screenOriginX = r.m_x + (bm_orgx - (pivot_x / fsf));
+ int screenOriginY = r.m_y + (bm_orgy - (pivot_y / fsf));
+
+ // Pre-clip the sub-bitmap to avoid assert errors
+ if ((bm_height + bm_orgy) > height) bm_height = height - bm_orgy;
+ if ((bm_width + bm_orgx) > width) bm_width = width - bm_orgx;
+
+ mdc.SelectObject(wxNullBitmap);
+
+ // Grab a copy of the existing screen DC rectangle
+ wxBitmap targetBm(bm_width, bm_height, 24);
+ wxMemoryDC targetDc(targetBm);
+ if (!targetDc.IsOk()) return false;
+ targetDc.Blit(0, 0, bm_width, bm_height, m_pdc, screenOriginX,
+ screenOriginY);
+
+#if wxUSE_GRAPHICS_CONTEXT /*&& (( defined(__WXGTK__) || defined(__WXMAC__) ) \
+ && !wxCHECK_VERSION(2,9,4))*/
+ // Re-render onto the screen-grab copy, since wxDC::DrawBitmap() for alpha
+ // channel bitmaps is broken somehow in wxGCDC
+ wxGCDC targetGcdc(targetDc);
+ r0 -= wxPoint(bm_orgx, bm_orgy);
+ HPGL->SetTargetGCDC(&targetGcdc);
+ HPGL->Render(str, col, r0, pivot, origin, xscale, (double)rot_angle, true);
+#else
+ // We can use the bitmap already rendered
+ // Get smallest containing bitmap
+ wxBitmap *sbm = new wxBitmap(
+ pbm->GetSubBitmap(wxRect(bm_orgx, bm_orgy, bm_width, bm_height)));
+
+ // render the symbol graphics onto the screen-grab copy, with
+ // transparency...
+ targetDc.DrawBitmap(*sbm, 0, 0);
+ delete sbm;
+#endif
+
+ // Render the final bitmap onto the screen DC
+ m_pdc->Blit(screenOriginX, screenOriginY, bm_width, bm_height, &targetDc, 0,
+ 0);
+
+ // Clean up
+ delete pbm;
+ targetDc.SelectObject(wxNullBitmap);
+
+ // Update the object Bounding box
+ // so that subsequent drawing operations will redraw the item fully
+
+ double latmin, lonmin, latmax, lonmax;
+ GetPixPointSingleNoRotate(r.m_x + prule->parm2,
+ r.m_y + prule->parm3 + bm_height, &latmin, &lonmin);
+ GetPixPointSingleNoRotate(r.m_x + prule->parm2 + bm_width, r.m_y + prule->parm3,
+ &latmax, &lonmax);
+ LLBBox symbox;
+ symbox.Set(latmin, lonmin, latmax, lonmax);
+
+ rzRules->obj->BBObj.Expand(symbox);
+ }
+
+ return true;
+}
+
+int s52plib::BuildHPGLTexture(ObjRazRules *rzRules, Rule *prule, wxPoint2DDouble &r,
+ float rot_angle, double uScale) {
+ int rv = 0;
+#ifdef ocpnUSE_GL
+ float fsf = 100 / canvas_pix_per_mm;
+
+ float xscale = 1.0;
+ xscale *= uScale;
+
+ // Flare lights are a little large...
+ if (rzRules->obj->bIsAton &&
+ (!strncmp(rzRules->obj->FeatureName, "LIGHTS", 6))) {
+ xscale = xscale * 5. / 7.;
+ }
+
+ double render_angle = rot_angle;
+
+ int width = prule->pos.symb.bnbox_x.SBXC + prule->pos.symb.bnbox_w.SYHL;
+ width *= 6 * xscale; // Grow the drawing bitmap to allow for rotation
+ // of symbols with highly offset pivot points
+ width = (int)(width / fsf);
+
+ int height = prule->pos.symb.bnbox_y.SBXR + prule->pos.symb.bnbox_h.SYVL;
+ height *= 6 * xscale;
+ height = (int)(height / fsf);
+
+ width /= fsf;
+ height /= fsf;
+
+ // Expand width and height to next larger POT
+ int xp = width, yp = height;
+ int width_pot, height_pot;
+ if (((xp != 0) && !(xp & (xp - 1)))) // detect POT
+ width_pot = xp;
+ else {
+ int a = 0;
+ while (xp) {
+ xp = xp >> 1;
+ a++;
+ }
+ width_pot = 1 << a;
+ }
+
+ if (((yp != 0) && !(yp & (yp - 1)))) // detect POT
+ height_pot = yp;
+ else {
+ int a = 0;
+ while (yp) {
+ yp = yp >> 1;
+ a++;
+ }
+ height_pot = 1 << a;
+ }
+
+ int origin_x = prule->pos.symb.bnbox_x.SBXC;
+ int origin_y = prule->pos.symb.bnbox_y.SBXR;
+ wxPoint origin(origin_x, origin_y);
+
+ int pivot_x = prule->pos.symb.pivot_x.SYCL;
+ int pivot_y = prule->pos.symb.pivot_y.SYRW;
+ wxPoint pivot(pivot_x, pivot_y);
+
+ char *str = prule->vector.LVCT;
+ char *col = prule->colRef.LCRF;
+ wxPoint r0((int)(pivot_x / fsf), (int)(pivot_y / fsf));
+
+ // Render the symbol on wxMemoryDC
+ HPGL->SetVP(&vp_plib);
+ wxMemoryDC mdc;
+ wxBitmap bmp(width, height, 32);
+ mdc.SelectObject(bmp);
+ HPGL->SetTargetDC(&mdc);
+#if (defined(__WXMSW__) || defined(__WXMAC__) || defined(__WXQT__))
+ mdc.SetBackground(wxBrush(m_unused_wxColor));
+ mdc.Clear();
+#endif
+
+ wxPoint2DDouble rt(0, 0);
+ HPGL->Render(str, col, rt, pivot, origin, xscale, render_angle, true);
+ mdc.SelectObject(wxNullBitmap);
+
+ wxImage image = bmp.ConvertToImage();
+ int ws = image.GetWidth(), hs = image.GetHeight();
+
+ unsigned char *source = image.GetData();
+ unsigned char *alpha = image.GetAlpha();
+
+#ifdef __WXQT__
+ alpha = nullptr;
+#endif
+
+ unsigned char mr, mg, mb;
+ if (!alpha) {
+ image.SetMaskColour(m_unused_wxColor.Red(), m_unused_wxColor.Green(),
+ m_unused_wxColor.Blue());
+ if (!alpha && !image.GetOrFindMaskColour(&mr, &mg, &mb))
+ printf("trying to use mask to draw a bitmap without alpha or mask\n");
+ }
+
+ unsigned char *pRGBA = (unsigned char *)calloc(1, 4 * width_pot * height_pot);
+ unsigned char *dest = pRGBA;
+
+ if (source) {
+ for (int y = 0; y < hs; y++) {
+ for (int x = 0; x < ws; x++) {
+ int offs = (y * ws + x);
+ int offd = (y * width_pot + x);
+
+ unsigned int r = source[offs * 3 + 0];
+ unsigned int g = source[offs * 3 + 1];
+ unsigned int b = source[offs * 3 + 2];
+
+ dest[offd * 4 + 0] = source[offs * 3 + 0];
+ dest[offd * 4 + 1] = source[offs * 3 + 1];
+ dest[offd * 4 + 2] = source[offs * 3 + 2];
+ unsigned char a = alpha
+ ? alpha[offs]
+ : ((r == mr) && (g == mg) && (b == mb) ? 0 : 255);
+ dest[offd * 4 + 3] = a;
+ }
+ }
+ }
+
+ glEnable(GL_TEXTURE_2D);
+ GLuint texobj;
+ glGenTextures(1, &texobj);
+ glBindTexture(GL_TEXTURE_2D, texobj);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+
+ glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width_pot, height_pot, 0, GL_RGBA,
+ GL_UNSIGNED_BYTE, pRGBA);
+
+ free(pRGBA);
+ glDisable(GL_TEXTURE_2D);
+ glBindTexture(GL_TEXTURE_2D, 0);
+
+ // Save the texture details in the object's rule
+ prule->parm2 = width_pot;
+ prule->parm3 = height_pot;
+ rv = texobj;
+#endif
+ return rv;
+}
+
+#if 0
+
+ // Update the object Bounding box
+ // so that subsequent drawing operations will redraw the item fully
+
+ int r_width = prule->pos.symb.bnbox_w.SYHL;
+ r_width = (int)(r_width / fsf);
+ int r_height = prule->pos.symb.bnbox_h.SYVL;
+ r_height = (int)(r_height / fsf);
+ int maxDim = wxMax(r_height, r_width);
+
+ double latmin, lonmin, latmax, lonmax;
+ GetPixPointSingleNoRotate(r.x - maxDim, r.y + maxDim, &latmin, &lonmin);
+ GetPixPointSingleNoRotate(r.x + maxDim, r.y - maxDim, &latmax, &lonmax);
+ LLBBox symbox;
+ symbox.Set(latmin, lonmin, latmax, lonmax);
+
+ rzRules->obj->BBObj.Expand(symbox);
+
+ } else {
+#if ((defined(__WXGTK__) || defined(__WXMAC__)) && !wxCHECK_VERSION(2, 9, 4))
+ wxBitmap *pbm = new wxBitmap(width, height);
+#else
+ wxBitmap *pbm = new wxBitmap(width, height, 32);
+#if !wxCHECK_VERSION(2, 9, 4)
+ pbm->UseAlpha();
+#endif
+#endif
+ wxMemoryDC mdc(*pbm);
+ if (!mdc.IsOk()) {
+ wxString msg;
+ msg.Printf(_T("RenderHPGL: width %d height %d"), width, height);
+ wxLogMessage(msg);
+ return false;
+ }
+
+#if wxUSE_GRAPHICS_CONTEXT
+ wxGCDC gdc(mdc);
+ HPGL->SetTargetGCDC(&gdc);
+#else
+ wxMemoryDC &gdc(mdc);
+ HPGL->SetTargetDC(&gdc);
+#endif
+ HPGL->Render(str, col, r0, pivot, origin, xscale, (double)rot_angle, true);
+
+ int bm_width = (gdc.MaxX() - gdc.MinX()) + 4;
+ int bm_height = (gdc.MaxY() - gdc.MinY()) + 4;
+ int bm_orgx = wxMax(0, gdc.MinX() - 2);
+ int bm_orgy = wxMax(0, gdc.MinY() - 2);
+ int screenOriginX = r.x + (bm_orgx - (int)(pivot_x / fsf));
+ int screenOriginY = r.y + (bm_orgy - (int)(pivot_y / fsf));
+
+ // Pre-clip the sub-bitmap to avoid assert errors
+ if ((bm_height + bm_orgy) > height) bm_height = height - bm_orgy;
+ if ((bm_width + bm_orgx) > width) bm_width = width - bm_orgx;
+
+ mdc.SelectObject(wxNullBitmap);
+
+ // Grab a copy of the existing screen DC rectangle
+ wxBitmap targetBm(bm_width, bm_height, 24);
+ wxMemoryDC targetDc(targetBm);
+ if (!targetDc.IsOk()) return false;
+ targetDc.Blit(0, 0, bm_width, bm_height, m_pdc, screenOriginX,
+ screenOriginY);
+
+#if wxUSE_GRAPHICS_CONTEXT /*&& (( defined(__WXGTK__) || defined(__WXMAC__) ) \
+ && !wxCHECK_VERSION(2,9,4))*/
+ // Re-render onto the screen-grab copy, since wxDC::DrawBitmap() for alpha
+ // channel bitmaps is broken somehow in wxGCDC
+ wxGCDC targetGcdc(targetDc);
+ r0 -= wxPoint(bm_orgx, bm_orgy);
+ HPGL->SetTargetGCDC(&targetGcdc);
+ HPGL->Render(str, col, r0, pivot, origin, xscale, (double)rot_angle, true);
+#else
+ // We can use the bitmap already rendered
+ // Get smallest containing bitmap
+ wxBitmap *sbm = new wxBitmap(
+ pbm->GetSubBitmap(wxRect(bm_orgx, bm_orgy, bm_width, bm_height)));
+
+ // render the symbol graphics onto the screen-grab copy, with
+ // transparency...
+ targetDc.DrawBitmap(*sbm, 0, 0);
+ delete sbm;
+#endif
+
+ // Render the final bitmap onto the screen DC
+ m_pdc->Blit(screenOriginX, screenOriginY, bm_width, bm_height, &targetDc, 0,
+ 0);
+
+ // Clean up
+ delete pbm;
+ targetDc.SelectObject(wxNullBitmap);
+
+ // Update the object Bounding box
+ // so that subsequent drawing operations will redraw the item fully
+
+ double latmin, lonmin, latmax, lonmax;
+ GetPixPointSingleNoRotate(r.x + prule->parm2,
+ r.y + prule->parm3 + bm_height, &latmin, &lonmin);
+ GetPixPointSingleNoRotate(r.x + prule->parm2 + bm_width, r.y + prule->parm3,
+ &latmax, &lonmax);
+ LLBBox symbox;
+ symbox.Set(latmin, lonmin, latmax, lonmax);
+
+ rzRules->obj->BBObj.Expand(symbox);
+ }
+
+ return true;
+}
+#endif
+
+//-----------------------------------------------------------------------------------------
+// Instantiate a Symbol or Pattern stored as XBM ascii in a rule
+// Producing a wxImage
+//-----------------------------------------------------------------------------------------
+wxImage s52plib::RuleXBMToImage(Rule *prule) {
+ // Decode the color definitions
+ wxArrayPtrVoid *pColorArray = new wxArrayPtrVoid;
+
+ int i = 0;
+ char *cstr = prule->colRef.SCRF;
+
+ char colname[6];
+ int nl = strlen(cstr);
+
+ while (i < nl) {
+ i++;
+
+ strncpy(colname, &cstr[i], 5);
+ colname[5] = 0;
+ S52color *pColor = getColor(colname);
+
+ pColorArray->Add((void *)pColor);
+
+ i += 5;
+ }
+
+ // Get geometry
+ int width = prule->pos.line.bnbox_w.SYHL;
+ int height = prule->pos.line.bnbox_h.SYVL;
+
+ wxString gstr(*prule->bitmap.SBTM); // the bit array
+
+ wxImage Image(width, height);
+
+ for (int iy = 0; iy < height; iy++) {
+ wxString thisrow = gstr(iy * width, width); // extract a row
+
+ for (int ix = 0; ix < width; ix++) {
+ int cref = (int)(thisrow[ix] - 'A'); // make an index
+ if (cref >= 0) {
+ S52color *pthisbitcolor = (S52color *)(pColorArray->Item(cref));
+ Image.SetRGB(ix, iy, pthisbitcolor->R, pthisbitcolor->G,
+ pthisbitcolor->B);
+ } else {
+ Image.SetRGB(ix, iy, m_unused_color.R, m_unused_color.G,
+ m_unused_color.B);
+ }
+ }
+ }
+
+ pColorArray->Clear();
+ delete pColorArray;
+ return Image;
+}
+
+//
+// Render Raster Symbol
+// Symbol is instantiated as a bitmap the first time it is needed
+// and re-built on color scheme change
+//
+bool s52plib::RenderRasterSymbol(ObjRazRules *rzRules, Rule *prule, wxPoint2DDouble &r,
+ float rot_angle) {
+ double scale_factor = 1.0;
+
+ scale_factor *= m_ChartScaleFactorExp;
+
+ // Correct scale factor for symbolized soundings,
+ // as sometimes found in objects OBSTRN, WRECKS, UWTROC
+ if (!strncmp(prule->name.SYNM, "SOUND", 5)) {
+ scale_factor /= m_ChartScaleFactorExp;
+ scale_factor *= m_SoundingsScaleFactor;
+ }
+
+ scale_factor *= g_scaminScale;
+ scale_factor /= m_dipfactor;
+
+ if (m_display_size_mm <
+ 200) { // about 8 inches, implying some sort of smaller mobile device
+ // Set the onscreen size of the symbol
+ // Compensate for various display resolutions
+ // Develop empirically, making a buoy about 4 mm tall
+ double boyHeight =
+ 21. /
+ GetPPMM(); // from raster symbol definitions, boylat is xx pix high
+
+ double targetHeight0 = 4.0;
+
+ // But we want to scale the size for smaller displays
+ double displaySize = m_display_size_mm;
+ displaySize = wxMax(displaySize, 100);
+
+ float targetHeight = wxMin(targetHeight0, displaySize / 30);
+
+ double pix_factor = targetHeight / boyHeight;
+
+ // qDebug() << "scaleing" << m_display_size_mm << targetHeight0 <<
+ // targetHeight << GetPPMM() << boyHeight << pix_factor;
+
+ // for Hubert, and my moto
+ // scaleing 93.98 93 4 3.33333 12.7312 1.64949 2.02082
+ // My nvidia tab
+ // scaleing 144.78 144 4 4 12.6667 1.65789 2.4127
+ // judgement: all OK
+
+ scale_factor *= pix_factor;
+ }
+
+ scale_factor *= m_ContentScaleFactor;
+
+ // a few special cases here
+ if (!strncmp(rzRules->obj->FeatureName, "notmrk", 6) ||
+ !strncmp(rzRules->obj->FeatureName, "NOTMRK", 6) ||
+ !strncmp(prule->name.SYNM, "ADDMRK", 6)) {
+ // get the symbol size
+ wxRect trect;
+ m_chartSymbols.GetGLTextureRect(trect, prule->name.SYNM);
+
+ int scale_dim = wxMax(trect.width, trect.height);
+
+ double scaled_size = scale_dim / vp_plib.view_scale_ppm;
+
+ double target_size =
+ 100; // roughly, meters maximum scaled size for these inland signs
+
+ double xscale = target_size / scaled_size;
+ xscale = wxMin(xscale, 1.0);
+ xscale = wxMax(.2, xscale);
+
+ scale_factor *= xscale;
+ }
+
+ int pivot_x = prule->pos.line.pivot_x.SYCL;
+ int pivot_y = prule->pos.line.pivot_y.SYRW;
+
+ pivot_x *= scale_factor;
+ pivot_y *= scale_factor;
+
+ // For opengl, hopefully the symbols are loaded in a texture
+ unsigned int texture = 0;
+ wxRect texrect;
+ if (!m_pdc) {
+ texture = m_chartSymbols.GetGLTextureRect(texrect, prule->name.SYNM);
+ if (texture) {
+ prule->parm2 = texrect.width * scale_factor;
+ prule->parm3 = texrect.height * scale_factor;
+ }
+ }
+
+ if (m_pdc || !texture) {
+ // Check to see if any cached data is valid
+ bool b_dump_cache = false;
+ if (prule->pixelPtr) {
+ // Detect switches between DC and GL modes, flush if switch occurs
+ if (m_pdc) {
+ if (prule->parm0 != ID_wxBitmap) b_dump_cache = true;
+ } else {
+ if (prule->parm0 != ID_RGBA) b_dump_cache = true;
+ }
+ }
+
+ // If the requested scaled symbol size is not the same as is currently
+ // cached, we have to dump the cache
+ wxRect trect;
+ m_chartSymbols.GetGLTextureRect(trect, prule->name.SYNM);
+ if (prule->parm2 != (int)(trect.width * scale_factor)) b_dump_cache = true;
+
+ wxBitmap *pbm = NULL;
+
+ // Instantiate the symbol if necessary
+ if ((prule->pixelPtr == NULL) || (prule->parm1 != m_colortable_index) ||
+ b_dump_cache) {
+ wxImage Image = m_chartSymbols.GetImage(prule->name.SYNM);
+
+ // delete any old private data
+ ClearRulesCache(prule);
+
+ // always display something, TMARDEF1 as width of 2
+ int w0 = wxMax(1, Image.GetWidth() * scale_factor);
+ int h0 = wxMax(1, Image.GetHeight() * scale_factor);
+ Image.Rescale(w0, h0, wxIMAGE_QUALITY_HIGH);
+
+ int w = Image.GetWidth();
+ int h = Image.GetHeight();
+
+ if (!m_pdc) // opengl, not using textures
+ {
+ // Get the glRGBA format data from the wxImage
+ unsigned char *d = Image.GetData();
+ unsigned char *a = Image.GetAlpha();
+
+ Image.SetMaskColour(m_unused_wxColor.Red(), m_unused_wxColor.Green(),
+ m_unused_wxColor.Blue());
+ unsigned char mr, mg, mb;
+ if (!a && !Image.GetOrFindMaskColour(&mr, &mg, &mb))
+ printf("trying to use mask to draw a bitmap without alpha or mask\n");
+
+ unsigned char *e = (unsigned char *)malloc(w * h * 4);
+ // XXX FIXME a or e ?
+ if (d && a) {
+ for (int y = 0; y < h; y++) {
+ for (int x = 0; x < w; x++) {
+ unsigned char r, g, b;
+ int off = (y * w + x);
+ r = d[off * 3 + 0];
+ g = d[off * 3 + 1];
+ b = d[off * 3 + 2];
+
+ e[off * 4 + 0] = r;
+ e[off * 4 + 1] = g;
+ e[off * 4 + 2] = b;
+
+ e[off * 4 + 3] =
+ a ? a[off] : ((r == mr) && (g == mg) && (b == mb) ? 0 : 255);
+ }
+ }
+ }
+
+ // Save the bitmap ptr and aux parms in the rule
+ prule->pixelPtr = e;
+ prule->parm0 = ID_RGBA;
+ prule->parm1 = m_colortable_index;
+ prule->parm2 = w;
+ prule->parm3 = h;
+ } else {
+ bool b_has_trans = false;
+#if (defined(__WXGTK__) || defined(__WXMAC__))
+
+ // Blitting of wxBitmap with transparency in wxGTK is broken....
+ // We can do it the hard way, by manually alpha blending the
+ // symbol with a clip taken from the current screen DC contents.
+
+ // Inspect the symbol image, to see if it actually has alpha
+ // transparency
+ if (Image.HasAlpha()) {
+ unsigned char *a = Image.GetAlpha();
+ for (int i = 0; i < Image.GetHeight(); i++, a++) {
+ for (int j = 0; j < Image.GetWidth(); j++) {
+ if ((*a) && (*a != 255)) {
+ b_has_trans = true;
+ break;
+ }
+ }
+ if (b_has_trans) break;
+ }
+ }
+#ifdef __WXMAC__
+ b_has_trans = true;
+#endif
+
+ // If the symbol image has no transparency, then a standard
+ // wxDC:Blit() will work
+ if (!b_has_trans) {
+ pbm = new wxBitmap(Image, -1);
+ wxMask *pmask = new wxMask(*pbm, m_unused_wxColor);
+ pbm->SetMask(pmask);
+ }
+
+#else
+ pbm = new wxBitmap(Image, 32); // windows
+ wxMask *pmask = new wxMask(*pbm, m_unused_wxColor);
+ pbm->SetMask(pmask);
+#endif
+
+ // Save the bitmap ptr and aux parms in the rule
+ prule->pixelPtr = pbm;
+ prule->parm0 = ID_wxBitmap;
+ prule->parm1 = m_colortable_index;
+ prule->parm2 = w;
+ prule->parm3 = h;
+ }
+ } // instantiation
+ }
+
+ // Get the bounding box for the to-be-drawn symbol
+ int b_width, b_height;
+ b_width = prule->parm2;
+ b_height = prule->parm3;
+
+ LLBBox symbox;
+ double latmin, lonmin, latmax, lonmax;
+
+ if (!m_pdc && fabs(vp_plib.rotation) > .01) // opengl
+ {
+ float cx = vp_plib.pix_width / 2.;
+ float cy = vp_plib.pix_height / 2.;
+ float c = cosf(vp_plib.rotation);
+ float s = sinf(vp_plib.rotation);
+ float x = r.m_x - pivot_x - cx;
+ float y = r.m_y - pivot_y + b_height - cy;
+ GetPixPointSingle(x * c - y * s + cx, x * s + y * c + cy, &latmin, &lonmin);
+
+ x = r.m_x - pivot_x + b_width - cx;
+ y = r.m_y - pivot_y - cy;
+ GetPixPointSingle(x * c - y * s + cx, x * s + y * c + cy, &latmax, &lonmax);
+ } else {
+ GetPixPointSingle(r.m_x - pivot_x, r.m_y - pivot_y + b_height, &latmin,
+ &lonmin);
+ GetPixPointSingle(r.m_x - pivot_x + b_width, r.m_y - pivot_y, &latmax, &lonmax);
+ }
+ symbox.Set(latmin, lonmin, latmax, lonmax);
+
+ // Special case for GEO_AREA objects with centred symbols
+ if (rzRules->obj->Primitive_type == GEO_AREA) {
+ if (!rzRules->obj->BBObj.IntersectIn(
+ symbox)) // Symbol is wholly outside base object
+ return true;
+ }
+
+ // Now render the symbol
+
+ if (!m_pdc) // opengl
+ {
+#ifdef ocpnUSE_GL
+ glEnable(GL_BLEND);
+
+ if (texture) {
+ glEnable(GL_TEXTURE_2D);
+ glBindTexture(GL_TEXTURE_2D, texture);
+
+ int w = texrect.width, h = texrect.height;
+
+ float tx1 = texrect.x, ty1 = texrect.y;
+ float tx2 = tx1 + w, ty2 = ty1 + h;
+
+#if !defined(USE_ANDROID_GLES2) && !defined(ocpnUSE_GLSL)
+#else
+
+ if (m_TextureFormat == GL_TEXTURE_2D) {
+ // Normalize the sybmol texture coordinates against the next higher POT
+ // size
+ wxSize size = m_chartSymbols.GLTextureSize();
+ int rb_x = size.x;
+ int rb_y = size.y;
+ tx1 /= rb_x, tx2 /= rb_x;
+ ty1 /= rb_y, ty2 /= rb_y;
+ }
+
+ float uv[8];
+ float coords[8];
+
+ // Note swizzle of points to allow TRIANGLE_STRIP drawing
+ // normal uv
+ uv[0] = tx1;
+ uv[1] = ty1;
+ uv[2] = tx2;
+ uv[3] = ty1;
+ uv[6] = tx2;
+ uv[7] = ty2;
+ uv[4] = tx1;
+ uv[5] = ty2;
+
+ w *= scale_factor;
+ h *= scale_factor;
+
+ // pixels
+ coords[0] = 0;
+ coords[1] = 0;
+ coords[2] = w;
+ coords[3] = 0;
+ coords[6] = w;
+ coords[7] = h;
+ coords[4] = 0;
+ coords[5] = h;
+
+ if (pCtexture_2D_shader_program[0]) {
+ pCtexture_2D_shader_program[0]->Bind();
+
+ // Select the active texture unit.
+ glActiveTexture(GL_TEXTURE0);
+
+ pCtexture_2D_shader_program[0]->SetUniform1i("uTex", 0);
+
+ // Disable VBO's (vertex buffer objects) for attributes.
+ glBindBuffer(GL_ARRAY_BUFFER, 0);
+ glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
+
+ pCtexture_2D_shader_program[0]->SetAttributePointerf("position",
+ coords);
+ pCtexture_2D_shader_program[0]->SetAttributePointerf("aUV", uv);
+
+ // Rotate
+ mat4x4 I, Q;
+ mat4x4_identity(I);
+
+ mat4x4_translate_in_place(I, r.m_x, r.m_y, 0);
+ if (abs(vp_plib.rotation) > 0)
+ mat4x4_rotate_Z(Q, I, -vp_plib.rotation);
+ else
+ mat4x4_dup(Q, I);
+ mat4x4_translate_in_place(Q, -pivot_x, -pivot_y, 0);
+
+ pCtexture_2D_shader_program[0]->SetUniformMatrix4fv("TransformMatrix",
+ (GLfloat *)Q);
+
+ // Perform the actual drawing.
+ glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
+
+ // Restore the per-object transform to Identity Matrix
+ mat4x4 IM;
+ mat4x4_identity(IM);
+ pCtexture_2D_shader_program[0]->SetUniformMatrix4fv("TransformMatrix",
+ (GLfloat *)IM);
+
+ // Clean up the GL state
+ pCtexture_2D_shader_program[0]->UnBind();
+ }
+
+#endif // GLES2
+ glDisable(m_TextureFormat);
+ } else { /* this is only for legacy mode, or systems without NPOT textures
+ */
+ float cr = cosf(vp_plib.rotation);
+ float sr = sinf(vp_plib.rotation);
+ float ddx = pivot_x * cr + pivot_y * sr;
+ float ddy = pivot_y * cr - pivot_x * sr;
+#if !defined(USE_ANDROID_GLES2) && !defined(ocpnUSE_GLSL)
+#endif
+ }
+
+ glDisable(GL_BLEND);
+#endif
+ } else {
+ if (!(prule->pixelPtr)) // This symbol requires manual alpha blending
+ {
+ // Don't bother if the symbol is off the true screen,
+ // as for instance when an area-centered symbol is called for.
+ if ((r.m_x - pivot_x < vp_plib.pix_width) &&
+ (r.m_y - pivot_y < vp_plib.pix_height)) {
+ // Get the current screen contents to a wxImage
+ wxBitmap b1(b_width, b_height, -1);
+ wxMemoryDC mdc1(b1);
+ mdc1.Blit(0, 0, b_width, b_height, m_pdc, r.m_x - pivot_x, r.m_y - pivot_y,
+ wxCOPY);
+ wxImage im_back = b1.ConvertToImage();
+
+ // Get the scaled symbol as a wxImage
+ wxImage im_sym = m_chartSymbols.GetImage(prule->name.SYNM);
+ im_sym.Rescale(b_width, b_height, wxIMAGE_QUALITY_HIGH);
+
+ wxImage im_result(b_width, b_height);
+ unsigned char *pdest = im_result.GetData();
+ unsigned char *pback = im_back.GetData();
+ unsigned char *psym = im_sym.GetData();
+
+ unsigned char *asym = NULL;
+ if (im_sym.HasAlpha()) asym = im_sym.GetAlpha();
+
+ // Do alpha blending, the hard way
+
+ if (pdest && psym && pback) {
+ for (int i = 0; i < b_height; i++) {
+ for (int j = 0; j < b_width; j++) {
+ double alpha = 1.0;
+ if (asym) alpha = (*asym++) / 256.0;
+ unsigned char r = (*psym++ * alpha) + (*pback++ * (1.0 - alpha));
+ *pdest++ = r;
+ unsigned char g = (*psym++ * alpha) + (*pback++ * (1.0 - alpha));
+ *pdest++ = g;
+ unsigned char b = (*psym++ * alpha) + (*pback++ * (1.0 - alpha));
+ *pdest++ = b;
+ }
+ }
+ }
+
+ wxBitmap result(im_result);
+ wxMemoryDC result_dc(result);
+
+ m_pdc->Blit(r.m_x - pivot_x, r.m_y - pivot_y, b_width, b_height, &result_dc,
+ 0, 0, wxCOPY, false);
+
+ result_dc.SelectObject(wxNullBitmap);
+ mdc1.SelectObject(wxNullBitmap);
+ }
+ } else {
+ // Get the symbol bitmap into a memory dc
+ wxBitmap &bmp = (wxBitmap &)(*((wxBitmap *)(prule->pixelPtr)));
+ wxMemoryDC mdc(bmp);
+
+ // Blit it into the target dc, with mask
+ m_pdc->Blit(r.m_x - pivot_x, r.m_y - pivot_y, bmp.GetWidth(), bmp.GetHeight(),
+ &mdc, 0, 0, wxCOPY, true);
+
+ mdc.SelectObject(wxNullBitmap);
+ }
+ // Debug
+ // if(m_pdc){
+ // m_pdc->SetPen(wxPen(*wxGREEN, 1));
+ // m_pdc->SetBrush(wxBrush(*wxGREEN, wxTRANSPARENT));
+ // m_pdc->DrawRectangle(r.x - pivot_x, r.y - pivot_y, b_width, b_height);
+ //}
+ }
+
+ // Update the object Bounding box
+ // so that subsequent drawing operations will redraw the item fully
+ // We expand the object's BBox to account for objects rendered by multiple
+ // symbols, such as SOUNGD. so that expansions are cumulative.
+ if (rzRules->obj->Primitive_type == GEO_POINT)
+ rzRules->obj->BBObj.Expand(symbox);
+
+ return true;
+}
+
+// SYmbol
+int s52plib::RenderSY(ObjRazRules *rzRules, Rules *rules) {
+ float angle = 0;
+ double orient;
+
+ if (rules->razRule != NULL) {
+ if (rules->INSTstr[8] ==
+ ',') // supplementary parameter assumed to be angle, seen in LIGHTSXX
+ {
+ char sangle[10];
+ int cp = 0;
+ while (rules->INSTstr[cp + 9] && (rules->INSTstr[cp + 9] != ')')) {
+ sangle[cp] = rules->INSTstr[cp + 9];
+ cp++;
+ }
+ sangle[cp] = 0;
+ int angle_i = atoi(sangle);
+ angle = angle_i;
+ }
+
+ if (GetDoubleAttr(
+ rzRules->obj, "ORIENT",
+ orient)) // overriding any LIGHTSXX angle, probably TSSLPT
+ {
+ angle = orient;
+ if (strncmp(rzRules->obj->FeatureName, "LIGHTS", 6) == 0) {
+ angle += 180;
+ if (angle > 360) angle -= 360;
+ }
+ }
+
+ // Render symbol at object's x/y
+// wxPoint r, r1;
+// GetPointPixSingle(rzRules, rzRules->obj->y, rzRules->obj->x, &r);
+ wxPoint2DDouble dr;
+ GetDoublePointPixSingle(rzRules, rzRules->obj->y, rzRules->obj->x, &dr);
+
+ // Render a raster or vector symbol, as specified by LUP rules
+ if (rules->razRule->definition.SYDF == 'V') {
+ if (!m_pdc) { // GL mode
+ if (!RenderCachedVectorSymbol(rzRules, rules->razRule, dr, angle,
+ m_ChartScaleFactorExp))
+ RenderHPGL(rzRules, rules->razRule, dr, angle, m_ChartScaleFactorExp);
+ } else
+ RenderHPGL(rzRules, rules->razRule, dr, angle, m_ChartScaleFactorExp);
+ } else {
+ if (rules->razRule->definition.SYDF == 'R')
+ RenderRasterSymbol(rzRules, rules->razRule, dr, angle);
+ }
+ }
+
+ return 0;
+}
+
+bool s52plib::RenderCachedVectorSymbol(ObjRazRules *rzRules, Rule *rule_in,
+ wxPoint2DDouble &r, float rot_angle,
+ double uScale) {
+#ifdef ocpnUSE_GL
+ // TODO
+ // temporarily exclude some symbols
+ // Only handle ATONs for now
+ if (!rzRules->obj->bIsAton) return false;
+
+ int symbol_texture = 0;
+ // Build the hash key
+ std::string key;
+ key += std::to_string(rzRules->LUP->RCID); // base LUP ID
+ key += rule_in->colRef.LCRF; // color
+
+ // Check to see it the requested symbol texture is in the cache
+ auto search = vector_symbol_cache.find(key);
+ if (search != vector_symbol_cache.end())
+ symbol_texture = search->second;
+ else {
+ symbol_texture = BuildHPGLTexture(rzRules, rule_in, r, rot_angle, uScale);
+ vector_symbol_cache[key] = symbol_texture;
+ }
+
+ // Render the texture
+ glEnable(GL_TEXTURE_2D);
+ glBindTexture(GL_TEXTURE_2D, symbol_texture);
+
+ int w = rule_in->parm2;
+ int h = rule_in->parm3;
+
+ if ((w == 0) || (h == 0)) {
+ // Get the texture dimensions a slower way, first time only
+ glGetTexLevelParameteriv(GL_TEXTURE_2D, 0, GL_TEXTURE_WIDTH, &w);
+ glGetTexLevelParameteriv(GL_TEXTURE_2D, 0, GL_TEXTURE_HEIGHT, &h);
+ rule_in->parm2 = w;
+ rule_in->parm3 = h;
+ }
+
+ float tx1 = 0, ty1 = 0;
+ float tx2 = tx1 + w, ty2 = ty1 + h;
+
+ if (m_TextureFormat == GL_TEXTURE_2D) {
+ // Normalize the sybmol texture coordinates against the next higher POT
+ // size
+ int rb_x = w;
+ int rb_y = h;
+ tx1 /= rb_x, tx2 /= rb_x;
+ ty1 /= rb_y, ty2 /= rb_y;
+ }
+
+ float uv[8];
+ float coords[8];
+
+ // Note swizzle of points to allow TRIANGLE_STRIP drawing
+ // normal uv
+ uv[0] = tx1;
+ uv[1] = ty1;
+ uv[2] = tx2;
+ uv[3] = ty1;
+ uv[6] = tx2;
+ uv[7] = ty2;
+ uv[4] = tx1;
+ uv[5] = ty2;
+
+ // pixels
+ coords[0] = 0;
+ coords[1] = 0;
+ coords[2] = w;
+ coords[3] = 0;
+ coords[6] = w;
+ coords[7] = h;
+ coords[4] = 0;
+ coords[5] = h;
+
+ if (pCtexture_2D_shader_program[0]) {
+ pCtexture_2D_shader_program[0]->Bind();
+ glEnable(GL_BLEND);
+
+ // Select the active texture unit.
+ glActiveTexture(GL_TEXTURE0);
+
+ pCtexture_2D_shader_program[0]->SetUniform1i("uTex", 0);
+
+ // Disable VBO's (vertex buffer objects) for attributes.
+ glBindBuffer(GL_ARRAY_BUFFER, 0);
+ glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
+
+ pCtexture_2D_shader_program[0]->SetAttributePointerf("position", coords);
+ pCtexture_2D_shader_program[0]->SetAttributePointerf("aUV", uv);
+
+ // Rotate
+ mat4x4 I, Q;
+ mat4x4_identity(I);
+
+ mat4x4_translate_in_place(I, r.m_x, r.m_y, 0);
+ if (abs(vp_plib.rotation) > 0)
+ mat4x4_rotate_Z(Q, I, -vp_plib.rotation);
+ else
+ mat4x4_dup(Q, I);
+ // mat4x4_translate_in_place(Q, -pivot_x, -pivot_y, 0);
+
+ pCtexture_2D_shader_program[0]->SetUniformMatrix4fv("TransformMatrix",
+ (GLfloat *)Q);
+
+ // Perform the actual drawing.
+ glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
+
+ // Restore the per-object transform to Identity Matrix
+ mat4x4 IM;
+ mat4x4_identity(IM);
+ pCtexture_2D_shader_program[0]->SetUniformMatrix4fv("TransformMatrix",
+ (GLfloat *)IM);
+
+ // Clean up the GL state
+ pCtexture_2D_shader_program[0]->UnBind();
+ }
+#endif
+ return true;
+}
+
+void s52plib::SetupSoundingFont() {
+ if (m_isSoundingFontSet) return;
+
+ double scale_factor = 1.0;
+
+ if (m_display_size_mm <
+ 200) { // about 8 inches, implying some sort of smaller mobile device
+ // Set the onscreen size of the symbol
+ // Compensate for various display resolutions
+ // Develop empirically, making a buoy about 4 mm tall
+ double boyHeight =
+ 21. /
+ GetPPMM(); // from raster symbol definitions, boylat is xx pix high
+
+ double targetHeight0 = 4.0;
+
+ // But we want to scale the size for smaller displays
+ double displaySize = m_display_size_mm;
+ displaySize = wxMax(displaySize, 100);
+
+ float targetHeight = wxMin(targetHeight0, displaySize / 30);
+
+ double pix_factor = targetHeight / boyHeight;
+
+ // qDebug() << "scaleing" << m_display_size_mm << targetHeight0 <<
+ // targetHeight << GetPPMM() << boyHeight << pix_factor;
+
+ // for Hubert, and my moto
+ // scaleing 93.98 93 4 3.33333 12.7312 1.64949 2.02082
+ // My nvidia tab
+ // scaleing 144.78 144 4 4 12.6667 1.65789 2.4127
+ // judgement: all OK
+
+ // scale_factor *= pix_factor;
+ }
+
+ wxFontWeight fontWeight = wxFONTWEIGHT_NORMAL;
+ wxString fontFacename = wxEmptyString;
+ double defaultHeight = 3.0;
+
+#ifdef __OCPN__ANDROID__
+ fontWeight = wxFONTWEIGHT_BOLD;
+ fontFacename = _T("Roboto");
+ defaultHeight = 2.2;
+#endif
+
+ int point_size = 6;
+ int charWidth, charHeight, charDescent;
+ charWidth = 1;
+
+ double target = defaultHeight * scale_factor;
+
+ if (abs(m_SoundingsFontSizeMM - target) > (m_soundFontDelta + .5)) {
+ // Recalculate the required point size to give specified height
+ wxScreenDC sdc;
+
+ double font_size_mm = 0;
+ bool not_done = true;
+ while ((point_size < 32) && not_done) {
+ wxFont *tentativeFont = FindOrCreateFont_PlugIn(
+ point_size, wxFONTFAMILY_SWISS, wxFONTSTYLE_NORMAL, fontWeight, false,
+ fontFacename);
+ sdc.GetTextExtent(_T("0"), &charWidth, &charHeight, &charDescent, NULL,
+ tentativeFont); // measure the text
+ double font_size_mm = (double)(charHeight - charDescent) / GetPPMM();
+
+ if (font_size_mm >= target) {
+ not_done = false;
+ m_SoundingsPointSize = point_size;
+ m_SoundingsFontSizeMM = font_size_mm;
+ m_soundFont = tentativeFont;
+ m_soundFontDelta = abs(font_size_mm - target);
+ break;
+ }
+ point_size++;
+ }
+ } else {
+ if (m_pdc) {
+ wxScreenDC sdc;
+ sdc.GetTextExtent(_T("0"), &charWidth, &charHeight, &charDescent, NULL,
+ m_soundFont); // measure the text for DC mode
+ }
+ point_size = m_SoundingsPointSize;
+ point_size /= m_dipfactor; // Apply Windows display scaling.
+ }
+ double postmult = m_SoundingsScaleFactor;
+ if ((postmult <= 2.0) && (postmult >= 0.5)) {
+ point_size *= postmult;
+ scale_factor *= postmult;
+ charWidth *= postmult;
+ }
+
+ // Build the texDepth object, if required
+ if (!m_pdc) { // OpenGL
+ if (!m_texSoundings.IsBuilt() ||
+ (fabs(m_texSoundings.GetScale() - scale_factor) > 0.05)) {
+ m_texSoundings.Delete();
+ m_texSoundings.SetContentScaleFactor(m_ContentScaleFactor);
+
+ m_soundFont = FindOrCreateFont_PlugIn(point_size, wxFONTFAMILY_SWISS,
+ wxFONTSTYLE_NORMAL, fontWeight,
+ false, fontFacename);
+ m_texSoundings.Build(m_soundFont, scale_factor,
+ m_dipfactor); // texSounding owns the font
+ }
+ } else {
+ m_soundFont = FindOrCreateFont_PlugIn(point_size, wxFONTFAMILY_SWISS,
+ wxFONTSTYLE_NORMAL, fontWeight, false,
+ fontFacename);
+ m_pdc->SetFont(*m_soundFont);
+ }
+
+ // Get and record some final metrics
+ wxScreenDC sdc;
+ sdc.GetTextExtent(_T("0"), &m_sounding_char_width, &m_sounding_char_height,
+ &m_sounding_char_descent, NULL,
+ m_soundFont); // measure the text
+
+#ifdef ocpnUSE_GL
+ // Prefetch the uniform and attribute location in the 2d texture shader
+ if (!m_pdc) {
+ pCtexture_2D_Color_shader_program[0]->Bind();
+ m_sounding_shader_uni_color =
+ pCtexture_2D_Color_shader_program[0]->getUniformLocation(s_color);
+ // pCtexture_2D_Color_shader_program[0]->SetUniform4fv( s_color, colorv);
+
+ m_sounding_shader_uni_uTex =
+ pCtexture_2D_Color_shader_program[0]->getUniformLocation(s_uTex);
+ // pCtexture_2D_Color_shader_program[0]->SetUniform1i( s_uTex, 0);
+
+ m_sounding_shader_attr_position =
+ pCtexture_2D_Color_shader_program[0]->getAttributeLocation(s_position);
+ // pCtexture_2D_Color_shader_program[0]->SetAttributePointerf( s_position,
+ // coords);
+
+ m_sounding_shader_attr_aUV =
+ pCtexture_2D_Color_shader_program[0]->getAttributeLocation(s_aUV);
+ // pCtexture_2D_Color_shader_program[0]->SetAttributePointerf( s_aUV, uv);
+
+ m_sounding_shader_uni_transform =
+ pCtexture_2D_Color_shader_program[0]->getUniformLocation(
+ s_TransformMatrix);
+ // pCtexture_2D_Color_shader_program[0]->SetUniformMatrix4fv(
+ // s_TransformMatrix, (GLfloat *)Q);
+
+ pCtexture_2D_Color_shader_program[0]->UnBind();
+ }
+#endif
+
+ m_isSoundingFontSet = true;
+}
+
+void s52plib::RenderMPSArray(ObjRazRules *rzRules,
+ std::vector array) {
+ if (!array.size()) return;
+
+ // Get some metrics
+ int charWidth = m_sounding_char_width;
+ int charHeight = m_sounding_char_height;
+ int charDescent = m_sounding_char_descent;
+
+ int pivot_x;
+ int pivot_y;
+ unsigned int texture = 0;
+ wxUint32 last_color_RGB = 0;
+
+#ifdef ocpnUSE_GL
+ if (!m_pdc) { // preconfigure the shader
+ texture = m_texSoundings.GetTexture();
+
+ glEnable(GL_BLEND);
+ glEnable(GL_TEXTURE_2D);
+ glBindTexture(GL_TEXTURE_2D, texture);
+
+ pCtexture_2D_Color_shader_program[0]->Bind();
+ // Select the active texture unit.
+ glActiveTexture(GL_TEXTURE0);
+ glUniform1i(m_sounding_shader_uni_uTex, 0);
+
+ // Disable VBO's (vertex buffer objects) for attributes.
+ glBindBuffer(GL_ARRAY_BUFFER, 0);
+ glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
+ }
+#endif
+
+ for (auto sym : array) {
+ // Parse the symbol name
+ // The digit
+ char symDigit = sym.prule->name.SYNM[7];
+ int symIndex = symDigit - 0x30;
+
+ // The pivot point offset group
+ char symCPivot = sym.prule->name.SYNM[6];
+ int symPivot = symCPivot - 0x30;
+
+ int pivotWidth, pivotHeight;
+ // For opengl, the symbols are loaded in a texture
+ wxRect texrect;
+ if (!m_pdc) { // GL
+ m_texSoundings.GetGLTextureRect(texrect, symIndex);
+
+ if (texture) {
+ sym.prule->parm2 = texrect.width;
+ sym.prule->parm3 = texrect.height;
+ }
+
+ pivotWidth = texrect.width;
+ pivotHeight = texrect.height;
+
+ } else {
+ pivotWidth = charWidth;
+ pivotHeight = charHeight - charDescent;
+ }
+
+ if (symPivot < 4) {
+ pivot_x = (pivotWidth * symPivot); // - (pivotWidth / 4);
+ pivot_y = pivotHeight / 2;
+ } else if (symPivot == 4) {
+ pivot_x = -pivotWidth; // - (pivotWidth / 4);
+ pivot_y = pivotHeight / 2;
+ } else {
+ pivot_x = 0; //-(pivotWidth / 4);
+ pivot_y = pivotHeight / 5;
+ }
+ pivot_x *= m_dipfactor;
+ pivot_y *= m_dipfactor;
+
+ // Now render the symbol
+ if (!m_pdc) // opengl
+ {
+#ifdef ocpnUSE_GL
+ {
+ int w = texrect.width, h = texrect.height;
+
+ float tx1 = texrect.x, ty1 = texrect.y;
+ float tx2 = tx1 + w, ty2 = ty1 + h;
+
+ if (m_TextureFormat == GL_TEXTURE_2D) {
+ // Normalize the sybmol texture coordinates against the next higher
+ // POT size
+ wxSize size = m_texSoundings.GLTextureSize();
+ int rb_x = size.x;
+ int rb_y = size.y;
+
+ tx1 /= rb_x, tx2 /= rb_x;
+ ty1 /= rb_y, ty2 /= rb_y;
+ }
+
+ float uv[8];
+ float coords[8];
+
+ // Note swizzle of points to allow TRIANGLE_STRIP drawing
+ // normal uv
+ uv[0] = tx1;
+ uv[1] = ty1;
+ uv[2] = tx2;
+ uv[3] = ty1;
+ uv[6] = tx2;
+ uv[7] = ty2;
+ uv[4] = tx1;
+ uv[5] = ty2;
+
+ // pixels
+ coords[0] = 0;
+ coords[1] = 0;
+ coords[2] = w;
+ coords[3] = 0;
+ coords[6] = w;
+ coords[7] = h;
+ coords[4] = 0;
+ coords[5] = h;
+
+ if (sym.color_RGB != last_color_RGB) {
+ wxColor c;
+ c.SetRGB(sym.color_RGB);
+ float colorv[4];
+ colorv[0] = c.Red() / float(256);
+ colorv[1] = c.Green() / float(256);
+ colorv[2] = c.Blue() / float(256);
+ colorv[3] = 1.0;
+
+ glUniform4fv(m_sounding_shader_uni_color, 1, colorv);
+ last_color_RGB = sym.color_RGB;
+ }
+
+ glVertexAttribPointer(m_sounding_shader_attr_position, 2, GL_FLOAT,
+ GL_FALSE, 2 * sizeof(float), coords);
+ glEnableVertexAttribArray(m_sounding_shader_attr_position);
+
+ glVertexAttribPointer(m_sounding_shader_attr_aUV, 2, GL_FLOAT, GL_FALSE,
+ 2 * sizeof(float), uv);
+ glEnableVertexAttribArray(m_sounding_shader_attr_aUV);
+
+ // Rotate
+ mat4x4 I, Q;
+ mat4x4_identity(I);
+ mat4x4_identity(Q);
+
+ mat4x4_translate_in_place(I, sym.r.m_x, sym.r.m_y, 0);
+ mat4x4_rotate_Z(Q, I, -vp_plib.rotation);
+ mat4x4_translate_in_place(Q, -pivot_x, -pivot_y, 0);
+
+ glUniformMatrix4fv(m_sounding_shader_uni_transform, 1, GL_FALSE,
+ (GLfloat *)Q);
+
+ // Perform the actual drawing.
+ glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
+ }
+#endif
+ } else {
+ wxString text;
+ text.Printf(_T("%d"), symIndex);
+ wxColor c;
+ c.SetRGB(sym.color_RGB);
+ m_pdc->SetTextForeground(c);
+
+ m_pdc->DrawText(text, sym.r.m_x - pivot_x, sym.r.m_y - pivot_y);
+ }
+ }
+
+#ifdef ocpnUSE_GL
+ if (!m_pdc) { // OpenGL
+ // Restore the per-object transform to Identity Matrix
+ mat4x4 IM;
+ mat4x4_identity(IM);
+ glUniformMatrix4fv(m_sounding_shader_uni_transform, 1, GL_FALSE,
+ (GLfloat *)IM);
+
+ pCtexture_2D_Color_shader_program[0]->UnBind();
+ glDisable(m_TextureFormat);
+ glDisable(GL_BLEND);
+ }
+#endif
+}
+
+bool s52plib::RenderSoundingSymbol(ObjRazRules *rzRules, Rule *prule,
+ wxPoint &r, wxColor symColor,
+ float rot_angle) {
+ // Get some metrics
+ int charWidth = m_sounding_char_width;
+ int charHeight = m_sounding_char_height;
+ int charDescent = m_sounding_char_descent;
+
+ int pivot_x;
+ int pivot_y;
+
+ // Parse the symbol name
+ // The digit
+ char symDigit = prule->name.SYNM[7];
+ int symIndex = symDigit - 0x30;
+
+ // The pivot point offset group
+ char symCPivot = prule->name.SYNM[6];
+ int symPivot = symCPivot - 0x30;
+
+ int pivotWidth, pivotHeight;
+ // For opengl, the symbols are loaded in a texture
+ unsigned int texture = 0;
+ wxRect texrect;
+ if (!m_pdc) { // GL
+ texture = m_texSoundings.GetTexture();
+ m_texSoundings.GetGLTextureRect(texrect, symIndex);
+
+ if (texture) {
+ prule->parm2 = texrect.width;
+ prule->parm3 = texrect.height;
+ }
+
+ pivotWidth = texrect.width;
+ pivotHeight = texrect.height;
+
+ } else {
+ pivotWidth = charWidth;
+ pivotHeight = charHeight - charDescent;
+ }
+
+ if (symPivot < 4) {
+ pivot_x = (pivotWidth * symPivot); // - (pivotWidth / 4);
+ pivot_y = pivotHeight / 2;
+ } else if (symPivot == 4) {
+ pivot_x = -pivotWidth; // - (pivotWidth / 4);
+ pivot_y = pivotHeight / 2;
+ } else {
+ pivot_x = 0; //-(pivotWidth / 4);
+ pivot_y = pivotHeight / 5;
+ }
+ pivot_x *= m_dipfactor;
+ pivot_y *= m_dipfactor;
+
+ // Get the bounding box for the to-be-drawn symbol
+ int b_width, b_height;
+ b_width = prule->parm2;
+ b_height = prule->parm3;
+
+ LLBBox symbox;
+ double latmin, lonmin, latmax, lonmax;
+
+ if (!m_pdc && fabs(vp_plib.rotation) > .01) // opengl
+ {
+ float cx = vp_plib.pix_width / 2.;
+ float cy = vp_plib.pix_height / 2.;
+ float c = cosf(vp_plib.rotation);
+ float s = sinf(vp_plib.rotation);
+ float x = r.x - pivot_x - cx;
+ float y = r.y - pivot_y + b_height - cy;
+ GetPixPointSingle(x * c - y * s + cx, x * s + y * c + cy, &latmin, &lonmin);
+
+ x = r.x - pivot_x + b_width - cx;
+ y = r.y - pivot_y - cy;
+ GetPixPointSingle(x * c - y * s + cx, x * s + y * c + cy, &latmax, &lonmax);
+ } else {
+ GetPixPointSingle(r.x - pivot_x, r.y - pivot_y + b_height, &latmin,
+ &lonmin);
+ GetPixPointSingle(r.x - pivot_x + b_width, r.y - pivot_y, &latmax, &lonmax);
+ }
+ symbox.Set(latmin, lonmin, latmax, lonmax);
+
+ // Now render the symbol
+
+ if (!m_pdc) // opengl
+ {
+#ifdef ocpnUSE_GL
+ glEnable(GL_BLEND);
+
+ if (texture) {
+ glEnable(GL_TEXTURE_2D);
+ glEnable(GL_BLEND);
+ glBindTexture(GL_TEXTURE_2D, texture);
+
+ int w = texrect.width, h = texrect.height;
+
+ float tx1 = texrect.x, ty1 = texrect.y;
+ float tx2 = tx1 + w, ty2 = ty1 + h;
+
+ if (m_TextureFormat == GL_TEXTURE_2D) {
+ // Normalize the sybmol texture coordinates against the next higher POT
+ // size
+ wxSize size = m_texSoundings.GLTextureSize();
+ int rb_x = size.x;
+ int rb_y = size.y;
+
+ tx1 /= rb_x, tx2 /= rb_x;
+ ty1 /= rb_y, ty2 /= rb_y;
+ }
+
+ float uv[8];
+ float coords[8];
+
+ // Note swizzle of points to allow TRIANGLE_STRIP drawing
+ // normal uv
+ uv[0] = tx1;
+ uv[1] = ty1;
+ uv[2] = tx2;
+ uv[3] = ty1;
+ uv[6] = tx2;
+ uv[7] = ty2;
+ uv[4] = tx1;
+ uv[5] = ty2;
+
+ // pixels
+ coords[0] = 0;
+ coords[1] = 0;
+ coords[2] = w;
+ coords[3] = 0;
+ coords[6] = w;
+ coords[7] = h;
+ coords[4] = 0;
+ coords[5] = h;
+
+ float colorv[4];
+ colorv[0] = symColor.Red() / float(256);
+ colorv[1] = symColor.Green() / float(256);
+ colorv[2] = symColor.Blue() / float(256);
+ colorv[3] = 1.0;
+
+ pCtexture_2D_Color_shader_program[0]->Bind();
+ // Select the active texture unit.
+ glActiveTexture(GL_TEXTURE0);
+ glUniform4fv(m_sounding_shader_uni_color, 1, colorv);
+ glUniform1i(m_sounding_shader_uni_uTex, 0);
+
+ // Disable VBO's (vertex buffer objects) for attributes.
+ glBindBuffer(GL_ARRAY_BUFFER, 0);
+ glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
+
+ glVertexAttribPointer(m_sounding_shader_attr_position, 2, GL_FLOAT,
+ GL_FALSE, 2 * sizeof(float), coords);
+ glEnableVertexAttribArray(m_sounding_shader_attr_position);
+
+ glVertexAttribPointer(m_sounding_shader_attr_aUV, 2, GL_FLOAT, GL_FALSE,
+ 2 * sizeof(float), uv);
+ glEnableVertexAttribArray(m_sounding_shader_attr_aUV);
+
+ // Rotate
+ mat4x4 I, Q;
+ mat4x4_identity(I);
+ mat4x4_identity(Q);
+
+ mat4x4_translate_in_place(I, r.x, r.y, 0);
+ mat4x4_rotate_Z(Q, I, -vp_plib.rotation);
+ mat4x4_translate_in_place(Q, -pivot_x, -pivot_y, 0);
+
+ glUniformMatrix4fv(m_sounding_shader_uni_transform, 1, GL_FALSE,
+ (GLfloat *)Q);
+
+ // Perform the actual drawing.
+ glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
+
+ // Restore the per-object transform to Identity Matrix
+ mat4x4 IM;
+ mat4x4_identity(IM);
+
+ glUniformMatrix4fv(m_sounding_shader_uni_transform, 1, GL_FALSE,
+ (GLfloat *)IM);
+
+ pCtexture_2D_Color_shader_program[0]->UnBind();
+ glDisable(m_TextureFormat);
+ } else { /* this is only for legacy mode, or systems without NPOT textures
+ */
+ float cr = cosf(vp_plib.rotation);
+ float sr = sinf(vp_plib.rotation);
+ float ddx = pivot_x * cr + pivot_y * sr;
+ float ddy = pivot_y * cr - pivot_x * sr;
+#if !defined(USE_ANDROID_GLES2) && !defined(ocpnUSE_GLSL)
+#endif
+ }
+
+ glDisable(GL_BLEND);
+#endif
+ } else {
+ wxString text;
+ text.Printf(_T("%d"), symIndex);
+ m_pdc->SetTextForeground(symColor);
+
+ m_pdc->DrawText(text, r.x - pivot_x, r.y - pivot_y);
+ }
+
+ return true;
+}
+
+// Line Simple Style, OpenGL
+int s52plib::RenderGLLS(ObjRazRules *rzRules, Rules *rules) {
+ // for now don't use vbo model in non-mercator
+ // if (vp->m_projection_type != PROJECTION_MERCATOR)
+ // return RenderLS(rzRules, rules, vp);
+
+ if (!ObjectRenderCheckPosReduced(rzRules)) return false;
+
+ if (!m_benableGLLS) // root chart cannot support VBO model
+ return RenderLS(rzRules, rules);
+
+ if (!rzRules->obj->m_chart_context->chart)
+ return RenderLS(rzRules, rules); // this is where S63 PlugIn gets caught
+
+ if ((GetBBox().GetMaxLon() >= 180.) || (GetBBox().GetMinLon() <= -180.))
+ return RenderLS(rzRules, rules); // cm93 has trouble at IDL
+
+ bool b_useVBO = false;
+ float *vertex_buffer = 0;
+
+ if (rzRules->obj->auxParm2 > 0) // Has VBO been defined and uploaded?
+ b_useVBO = true;
+
+ if (!b_useVBO) {
+ vertex_buffer = rzRules->obj->m_chart_context->vertex_buffer;
+ }
+
+#ifdef ocpnUSE_GL
+
+ char *str = (char *)rules->INSTstr;
+
+ if ((!strncmp(str, "DASH", 4)) || (!strncmp(str, "DOTT", 4)))
+ return RenderLS_Dash_GLSL(rzRules, rules);
+
+ LLBBox BBView = GetReducedBBox();
+
+ // Allow a little slop in calculating whether a segment
+ // is within the requested VPointCompat
+ double margin = BBView.GetLonRange() * .05;
+ BBView.EnLarge(margin);
+
+ // Get the current display priority
+ // Default comes from the LUP, unless overridden
+ int priority_current = rzRules->LUP->DPRI - '0';
+ if (rzRules->obj->m_DPRI >= 0) priority_current = rzRules->obj->m_DPRI;
+
+ line_segment_element *ls_list = rzRules->obj->m_ls_list;
+
+ S52color *c = getColor(str + 7); // Colour
+ int w = atoi(str + 5); // Width
+
+#if !defined(USE_ANDROID_GLES2) && !defined(ocpnUSE_GLSL)
+#endif
+
+ // Set drawing width
+ float lineWidth = w;
+ lineWidth = wxMax(m_GLMinCartographicLineWidth, w);
+
+ // Manage super high density displays
+ float target_w_mm = 0.5 * w;
+ if (GetPPMM() > 7) { // arbitrary
+ target_w_mm =
+ ((float)w) /
+ 6.0; // Target width in mm
+ // The value "w" comes from S52 library CNSY procedures, in
+ // "nominal" pixels
+ // the value "6" comes from semi-standard LCD display densities
+ // or something like 0.18 mm pitch, or 6 pix per mm.
+ lineWidth = wxMax(m_GLMinCartographicLineWidth, target_w_mm * GetPPMM());
+ }
+
+ glDisable(GL_LINE_SMOOTH);
+ glDisable(GL_BLEND);
+
+#ifdef __OCPN__ANDROID__
+ // if( w > 1 )
+ // lineWidth = wxMin(lineWidth, parms[1]);
+ glLineWidth(lineWidth);
+
+#else
+ glLineWidth(lineWidth);
+ if (lineWidth > 4.0 && m_GLLineSmoothing) {
+ glEnable(GL_LINE_SMOOTH);
+ glEnable(GL_BLEND);
+ }
+#endif
+
+#if !defined(USE_ANDROID_GLES2) && !defined(ocpnUSE_GLSL)
+#endif
+
+#if !defined(USE_ANDROID_GLES2) && !defined(ocpnUSE_GLSL)
+#endif
+
+ // Has line segment PBO been allocated for this chart?
+ if (b_useVBO) {
+ glBindBuffer(GL_ARRAY_BUFFER, rzRules->obj->auxParm2);
+ }
+
+ CGLShaderProgram *shader = pCcolor_tri_shader_program[0 /*GetCanvasIndex()*/];
+ shader->Bind();
+
+ // Disable VBO's (vertex buffer objects) for attributes.
+ if (!b_useVBO) glBindBuffer(GL_ARRAY_BUFFER, 0);
+ glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
+
+ GLint pos = shader->getAttributeLocation("position");
+ float angle = 0;
+
+ // We cannot use the prepared shader uniforms, as we can (and should for
+ // performance) do the per-object transforms all at once in the shader matrix.
+ // But we also must restore the prepared matrix for later rendering of other
+ // object classes.
+
+ // Build Transform matrix
+ // First, the VP transform
+ mat4x4 I, Q;
+ mat4x4_identity(I);
+
+ // Scale per object
+ I[0][0] *= rzRules->obj->x_rate;
+ I[1][1] *= rzRules->obj->y_rate;
+
+ // Translate per object
+ I[3][0] = -(rzRules->sm_transform_parms->easting_vp_center -
+ rzRules->obj->x_origin) *
+ vp_plib.view_scale_ppm;
+ I[3][1] = -(rzRules->sm_transform_parms->northing_vp_center -
+ rzRules->obj->y_origin) *
+ -vp_plib.view_scale_ppm;
+
+ // Scale for screen
+ I[0][0] *= vp_plib.view_scale_ppm;
+ I[1][1] *= -vp_plib.view_scale_ppm;
+
+ // Rotate
+ mat4x4_rotate_Z(Q, I, angle);
+
+ // Translate for screen
+ Q[3][0] += vp_plib.pix_width / 2;
+ Q[3][1] += vp_plib.pix_height / 2;
+
+ shader->SetUniformMatrix4fv("TransformMatrix", (GLfloat *)Q);
+
+ float colorv[4];
+ colorv[0] = c->R / float(256);
+ colorv[1] = c->G / float(256);
+ colorv[2] = c->B / float(256);
+ colorv[3] = 1.0;
+
+ shader->SetUniform4fv("color", colorv);
+
+ if (!b_useVBO) {
+ unsigned char *buffer = (unsigned char *)vertex_buffer;
+ float *bufBase = (float *)buffer;
+ glVertexAttribPointer(pos, 2, GL_FLOAT, GL_FALSE, 2 * sizeof(float),
+ bufBase);
+ } else {
+ glVertexAttribPointer(pos, 2, GL_FLOAT, GL_FALSE, 2 * sizeof(float),
+ (GLvoid *)(0));
+ }
+ glEnableVertexAttribArray(pos);
+
+ // from above ls_list is the first drawable segment
+ while (ls_list) {
+ if (ls_list->priority == priority_current) {
+ size_t seg_vbo_offset = 0;
+ size_t point_count = 0;
+
+ // Check visibility of the segment
+ bool b_drawit = false;
+ if ((ls_list->ls_type == TYPE_EE) || (ls_list->ls_type == TYPE_EE_REV)) {
+ if (BBView.GetMinLat() < ls_list->pedge->edgeBBox.GetMaxLat() &&
+ BBView.GetMaxLat() > ls_list->pedge->edgeBBox.GetMinLat() &&
+ BBView.GetMinLon() <= ls_list->pedge->edgeBBox.GetMaxLon() &&
+ BBView.GetMaxLon() >= ls_list->pedge->edgeBBox.GetMinLon()) {
+ // render the segment
+ b_drawit = true;
+ seg_vbo_offset = ls_list->pedge->vbo_offset;
+ point_count = ls_list->pedge->nCount;
+ }
+
+ } else {
+ if (BBView.GetMinLat() < ls_list->pcs->cs_lat_avg &&
+ BBView.GetMaxLat() > ls_list->pcs->cs_lat_avg &&
+ BBView.GetMinLon() <= ls_list->pcs->cs_lon_avg &&
+ BBView.GetMaxLon() >= ls_list->pcs->cs_lon_avg) {
+ // render the segment
+ b_drawit = true;
+ seg_vbo_offset = ls_list->pcs->vbo_offset;
+ point_count = 2;
+ }
+ }
+
+ if (b_drawit) {
+ // render the segment
+ glDrawArrays(GL_LINE_STRIP, seg_vbo_offset / (2 * sizeof(float)),
+ point_count);
+ }
+ }
+ ls_list = ls_list->next;
+ }
+
+ if (b_useVBO) glBindBuffer(GL_ARRAY_BUFFER, 0);
+
+ // Restore shader TransForm Matrix to identity.
+ mat4x4 IM;
+ mat4x4_identity(IM);
+ shader->SetUniformMatrix4fv("TransformMatrix", (GLfloat *)IM);
+ shader->UnBind();
+
+ glDisable(GL_LINE_STIPPLE);
+ glDisable(GL_LINE_SMOOTH);
+ glDisable(GL_BLEND);
+
+#endif // OpenGL
+
+ return 1;
+}
+
+// Line Simple Style
+int s52plib::RenderLS(ObjRazRules *rzRules, Rules *rules) {
+
+ // catch legacy PlugIns (e.g.s63_pi)
+ if (rzRules->obj->m_n_lsindex && !rzRules->obj->m_ls_list)
+ return RenderLSLegacy(rzRules, rules);
+
+ // catch improperly coded edge arrays, usually seen on cm93
+ if (!rzRules->obj->m_n_lsindex && !rzRules->obj->m_ls_list) return 0;
+
+ S52color *c;
+ int w;
+
+ char *str = (char *)rules->INSTstr;
+ c = getColor(str + 7); // Colour
+ wxColour color(c->R, c->G, c->B);
+ w = atoi(str + 5); // Width
+
+ double scale_factor = vp_plib.ref_scale / vp_plib.chart_scale;
+ double scaled_line_width = wxMax((scale_factor), 1);
+ bool b_wide_line = false;
+
+ wxPen wide_pen(*wxBLACK_PEN);
+ wxDash dashw[2];
+ dashw[0] = 3;
+ dashw[1] = 1;
+
+ if (b_wide_line) {
+ int w = wxMax(scaled_line_width, 2); // looks better
+ w = wxMin(w, 50); // upper bound
+ wide_pen.SetWidth(w);
+ wide_pen.SetColour(color);
+
+ if (!strncmp(str, "DOTT", 4)) {
+ dashw[0] = 1;
+ wide_pen.SetStyle(wxPENSTYLE_USER_DASH);
+ wide_pen.SetDashes(2, dashw);
+ } else if (!strncmp(str, "DASH", 4)) {
+ wide_pen.SetStyle(wxPENSTYLE_USER_DASH);
+ if (m_pdc) { // DC mode
+ dashw[0] = 1;
+ dashw[1] = 2;
+ }
+
+ wide_pen.SetDashes(2, dashw);
+ }
+ }
+
+ wxPen thispen(color, w, wxPENSTYLE_SOLID);
+ wxDash dash1[2];
+
+ if (m_pdc) // DC mode
+ {
+ if (!strncmp(str, "DOTT", 4)) {
+ thispen.SetStyle(wxPENSTYLE_USER_DASH);
+ dash1[0] = 1;
+ dash1[1] = 2;
+ thispen.SetDashes(2, dash1);
+ } else if (!strncmp(str, "DASH", 4)) {
+ thispen.SetStyle(wxPENSTYLE_SHORT_DASH);
+ }
+
+ if (b_wide_line)
+ m_pdc->SetPen(wide_pen);
+ else
+ m_pdc->SetPen(thispen);
+
+ }
+#ifdef ocpnUSE_GL
+ else // OpenGL mode
+ {
+ glDisable(GL_LINE_SMOOTH);
+
+ // Set drawing width
+ if (w > 1) {
+ GLint parms[2];
+ glGetIntegerv(GL_ALIASED_LINE_WIDTH_RANGE, &parms[0]);
+ if (w > parms[1])
+ glLineWidth(wxMax(m_GLMinCartographicLineWidth, parms[1]));
+ else
+ glLineWidth(wxMax(m_GLMinCartographicLineWidth, w));
+ } else
+ glLineWidth(wxMax(m_GLMinCartographicLineWidth, 1));
+
+#ifndef __OCPN__ANDROID__
+ if (w >= 2 && m_GLLineSmoothing) {
+ glEnable(GL_LINE_SMOOTH);
+ glEnable(GL_BLEND);
+ }
+#endif
+ }
+#endif
+
+ // Get a true pixel clipping/bounding box from the vp
+ wxPoint pbb = GetPixFromLL(vp_plib.clat, vp_plib.clon);
+ int xmin_ = pbb.x - (vp_plib.rv_rect.width / 2) - (4 * scaled_line_width);
+ int xmax_ = xmin_ + vp_plib.rv_rect.width + (8 * scaled_line_width);
+ int ymin_ = pbb.y - (vp_plib.rv_rect.height / 2) - (4 * scaled_line_width);
+ int ymax_ = ymin_ + vp_plib.rv_rect.height + (8 * scaled_line_width);
+
+ int x0, y0, x1, y1;
+
+ // Get the current display priority
+ // Default comes from the LUP, unless overridden
+ int priority_current = rzRules->LUP->DPRI - '0';
+ if (rzRules->obj->m_DPRI >= 0) priority_current = rzRules->obj->m_DPRI;
+
+ if (rzRules->obj->m_ls_list) {
+ float *ppt;
+
+ unsigned char *vbo_point =
+ (unsigned char *)rzRules->obj->m_chart_context->vertex_buffer;
+ line_segment_element *ls = rzRules->obj->m_ls_list;
+
+ while (ls) {
+ if (ls->priority == priority_current) {
+ int nPoints;
+ // fetch the first point
+ if ((ls->ls_type == TYPE_EE) || (ls->ls_type == TYPE_EE_REV)) {
+ ppt = (float *)(vbo_point + ls->pedge->vbo_offset);
+ nPoints = ls->pedge->nCount;
+ } else {
+ ppt = (float *)(vbo_point + ls->pcs->vbo_offset);
+ nPoints = 2;
+ }
+
+ wxPoint l;
+ GetPointPixSingle(rzRules, ppt[1], ppt[0], &l);
+ ppt += 2;
+
+ for (int ip = 0; ip < nPoints - 1; ip++) {
+ wxPoint r;
+ GetPointPixSingle(rzRules, ppt[1], ppt[0], &r);
+ // Draw the edge as point-to-point
+ x0 = l.x, y0 = l.y;
+ x1 = r.x, y1 = r.y;
+
+ // Do not draw null segments
+ if ((x0 != x1) || (y0 != y1)) {
+ if (m_pdc) {
+ if (cohen_sutherland_line_clip_i(&x0, &y0, &x1, &y1, xmin_, xmax_,
+ ymin_, ymax_) != Invisible)
+ m_pdc->DrawLine(x0, y0, x1, y1);
+ }
+ }
+
+ l = r;
+ ppt += 2;
+ }
+ }
+
+ ls = ls->next;
+ }
+ }
+
+#ifdef ocpnUSE_GL
+ if (!m_pdc) {
+ glDisable(GL_LINE_STIPPLE);
+ glDisable(GL_LINE_SMOOTH);
+ glDisable(GL_BLEND);
+ }
+#endif
+ return 1;
+}
+
+// Line Simple Style
+int s52plib::RenderLSLegacy(ObjRazRules *rzRules, Rules *rules) {
+ if (!rzRules->obj->m_chart_context->chart)
+ return RenderLSPlugIn(rzRules, rules);
+
+ // Must be cm93
+ S52color *c;
+ int w;
+ GLint pos = 0;
+
+ char *str = (char *)rules->INSTstr;
+ c = getColor(str + 7); // Colour
+ wxColour color(c->R, c->G, c->B);
+ w = atoi(str + 5); // Width
+
+ double scale_factor = vp_plib.ref_scale / vp_plib.chart_scale;
+ double scaled_line_width = wxMax((scale_factor), 1);
+ bool b_wide_line = false;
+
+ wxPen wide_pen(*wxBLACK_PEN);
+ wxDash dashw[2];
+ dashw[0] = 3;
+ dashw[1] = 1;
+
+ if (b_wide_line) {
+ int w = wxMax(scaled_line_width, 2); // looks better
+ w = wxMin(w, 50); // upper bound
+ wide_pen.SetWidth(w);
+ wide_pen.SetColour(color);
+
+ if (!strncmp(str, "DOTT", 4)) {
+ dashw[0] = 1;
+ wide_pen.SetStyle(wxPENSTYLE_USER_DASH);
+ wide_pen.SetDashes(2, dashw);
+ } else if (!strncmp(str, "DASH", 4)) {
+ wide_pen.SetStyle(wxPENSTYLE_USER_DASH);
+ if (m_pdc) { // DC mode
+ dashw[0] = 1;
+ dashw[1] = 2;
+ }
+
+ wide_pen.SetDashes(2, dashw);
+ }
+ }
+
+ wxPen thispen(color, w, wxPENSTYLE_SOLID);
+ wxDash dash1[2];
+
+ if (m_pdc) // DC mode
+ {
+ if (!strncmp(str, "DOTT", 4)) {
+ thispen.SetStyle(wxPENSTYLE_USER_DASH);
+ dash1[0] = 1;
+ dash1[1] = 2;
+ thispen.SetDashes(2, dash1);
+ } else if (!strncmp(str, "DASH", 4)) {
+ thispen.SetStyle(wxPENSTYLE_SHORT_DASH);
+ }
+
+ if (b_wide_line)
+ m_pdc->SetPen(wide_pen);
+ else
+ m_pdc->SetPen(thispen);
+
+ }
+#ifdef ocpnUSE_GL
+ else // OpenGL mode
+ {
+ glDisable(GL_LINE_SMOOTH);
+
+ // Set drawing width
+ if (w > 1) {
+ GLint parms[2];
+ glGetIntegerv(GL_ALIASED_LINE_WIDTH_RANGE, &parms[0]);
+ if (w > parms[1])
+ glLineWidth(wxMax(m_GLMinCartographicLineWidth, parms[1]));
+ else
+ glLineWidth(wxMax(m_GLMinCartographicLineWidth, w));
+ } else
+ glLineWidth(wxMax(m_GLMinCartographicLineWidth, 1));
+
+#ifndef __OCPN__ANDROID__
+ if (w >= 2 && m_GLLineSmoothing) {
+ glEnable(GL_LINE_SMOOTH);
+ glEnable(GL_BLEND);
+ }
+#endif
+ }
+#endif
+
+ // Get a true pixel clipping/bounding box from the vp
+ wxPoint pbb = GetPixFromLL(vp_plib.clat, vp_plib.clon);
+ int xmin_ = pbb.x - (vp_plib.rv_rect.width / 2) - (4 * scaled_line_width);
+ int xmax_ = xmin_ + vp_plib.rv_rect.width + (8 * scaled_line_width);
+ int ymin_ = pbb.y - (vp_plib.rv_rect.height / 2) - (4 * scaled_line_width);
+ int ymax_ = ymin_ + vp_plib.rv_rect.height + (8 * scaled_line_width);
+
+ int x0, y0, x1, y1;
+
+ if (rzRules->obj->m_n_lsindex) {
+ // This is cm93
+ auto ve_hash = rzRules->obj->m_chart_context->m_pve_hash;
+ auto vc_hash = rzRules->obj->m_chart_context->m_pvc_hash;
+
+ // Get the current display priority
+ // Default comes from the LUP, unless overridden
+ int priority_current = rzRules->LUP->DPRI - '0';
+ if (rzRules->obj->m_DPRI >= 0) priority_current = rzRules->obj->m_DPRI;
+
+ int *index_run;
+ float *ppt;
+ VC_Element *pnode;
+#ifdef ocpnUSE_GL
+ CGLShaderProgram *shader =
+ pCcolor_tri_shader_program[0 /*GetCanvasIndex()*/];
+ shader->Bind();
+
+ float fBuf[4];
+ pos = shader->getAttributeLocation("position");
+ glVertexAttribPointer(pos, 2, GL_FLOAT, GL_FALSE, 2 * sizeof(float), fBuf);
+ glEnableVertexAttribArray(pos);
+
+ float colorv[4];
+ colorv[0] = c->R / float(256);
+ colorv[1] = c->G / float(256);
+ colorv[2] = c->B / float(256);
+ colorv[3] = 1.0;
+
+ shader->SetUniform4fv("color", colorv);
+#endif
+
+ for (int iseg = 0; iseg < rzRules->obj->m_n_lsindex; iseg++) {
+ int seg_index = iseg * 3;
+ index_run = &rzRules->obj->m_lsindex_array[seg_index];
+
+ // Get first connected node
+ unsigned int inode = *index_run++;
+
+ // Get the edge
+ unsigned int enode = *index_run++;
+ VE_Element *pedge = 0;
+ if (enode) pedge = (*ve_hash)[enode];
+
+ // Get last connected node
+ unsigned int jnode = *index_run++;
+
+ int nls;
+ if (pedge) {
+ // Here we decide to draw or not based on the highest priority seen for
+ // this segment That is, if this segment is going to be drawn at a
+ // higher priority later, then "continue", and don't draw it here.
+
+ // This logic is not perfectly right for one case:
+ // If the segment has only two end connected nodes, and no intermediate
+ // edge, then we have no good way to evaluate the priority. This is due
+ // to the fact that priority is only precalculated for edge segments,
+ // not connector nodes. Only thing to do is take the conservative
+ // approach and draw the segment, in this case.
+ if (pedge->nCount && pedge->max_priority != priority_current) continue;
+ nls = pedge->nCount + 1;
+ } else
+ nls = 1;
+
+ wxPoint l;
+ bool lastvalid = false;
+ for (int ipc = 0; ipc < nls + 1; ipc++) {
+ ppt = 0;
+ if (ipc == 0) {
+ if (inode) {
+ pnode = (*vc_hash)[inode];
+ if (pnode) ppt = pnode->pPoint;
+ }
+ } else if (ipc == nls) {
+ if ((jnode)) {
+ pnode = (*vc_hash)[jnode];
+ if (pnode) ppt = pnode->pPoint;
+ }
+ } else if (pedge)
+ ppt = pedge->pPoints + 2 * (ipc - 1);
+
+ if (ppt) {
+ wxPoint r;
+ GetPointPixSingle(rzRules, ppt[1], ppt[0], &r);
+
+ if (1 /*r.x != INVALID_COORD*/) {
+ if (lastvalid) {
+ // Draw the edge as point-to-point
+ x0 = l.x, y0 = l.y;
+ x1 = r.x, y1 = r.y;
+
+ // Do not draw null segments
+ if ((x0 == x1) && (y0 == y1)) continue;
+
+ if (m_pdc) {
+ if (cohen_sutherland_line_clip_i(&x0, &y0, &x1, &y1, xmin_,
+ xmax_, ymin_,
+ ymax_) != Invisible)
+ m_pdc->DrawLine(x0, y0, x1, y1);
+ } else {
+#ifdef ocpnUSE_GL
+ fBuf[0] = x0;
+ fBuf[1] = y0;
+ fBuf[2] = x1;
+ fBuf[3] = y1;
+
+ glDrawArrays(GL_LINES, 0, 2);
+#endif
+ }
+ }
+
+ l = r;
+ lastvalid = true;
+ } else
+ lastvalid = false;
+ } else
+ lastvalid = false;
+ } // for
+ } // for
+
+#ifdef ocpnUSE_GL
+ glDisableVertexAttribArray(pos);
+ shader->UnBind();
+#endif
+ }
+
+#ifdef ocpnUSE_GL
+ if (!m_pdc) {
+ glDisable(GL_LINE_STIPPLE);
+ glDisable(GL_LINE_SMOOTH);
+ glDisable(GL_BLEND);
+ }
+#endif
+ return 1;
+}
+
+class PI_connector_segment // This was extracted verbatim from S63_pi private
+ // definition
+{
+public:
+ void *start;
+ void *end;
+ SegmentType type;
+ int vbo_offset;
+ int max_priority;
+};
+
+int s52plib::RenderLSPlugIn(ObjRazRules *rzRules, Rules *rules) {
+ S52color *c;
+ int w;
+
+ char *str = (char *)rules->INSTstr;
+ c = getColor(str + 7); // Colour
+ wxColour color(c->R, c->G, c->B);
+ w = atoi(str + 5); // Width
+
+ double scale_factor = vp_plib.ref_scale / vp_plib.chart_scale;
+ double scaled_line_width = wxMax((scale_factor), 1);
+ bool b_wide_line = false;
+
+ wxPen thispen(color, w, wxPENSTYLE_SOLID);
+ wxDash dash1[2];
+
+ if (m_pdc) // DC mode
+ {
+ if (!strncmp(str, "DOTT", 4)) {
+ thispen.SetStyle(wxPENSTYLE_USER_DASH);
+ dash1[0] = 1;
+ dash1[1] = 2;
+ thispen.SetDashes(2, dash1);
+ } else if (!strncmp(str, "DASH", 4)) {
+ thispen.SetStyle(wxPENSTYLE_SHORT_DASH);
+ }
+
+ m_pdc->SetPen(thispen);
+
+ } else // OpenGL mode
+ {
+#ifdef ocpnUSE_GL
+ // Set drawing width
+ if (w > 1) {
+ GLint parms[2];
+ glGetIntegerv(GL_ALIASED_LINE_WIDTH_RANGE, &parms[0]);
+ if (w > parms[1])
+ glLineWidth(wxMax(m_GLMinCartographicLineWidth, parms[1]));
+ else
+ glLineWidth(wxMax(m_GLMinCartographicLineWidth, w));
+ } else
+ glLineWidth(wxMax(m_GLMinCartographicLineWidth, 1));
+#endif
+ }
+
+ // Get a true pixel clipping/bounding box from the vp
+ wxPoint pbb = GetPixFromLL(vp_plib.clat, vp_plib.clon);
+ int xmin_ = pbb.x - (vp_plib.rv_rect.width / 2) - (4 * scaled_line_width);
+ int xmax_ = xmin_ + vp_plib.rv_rect.width + (8 * scaled_line_width);
+ int ymin_ = pbb.y - (vp_plib.rv_rect.height / 2) - (4 * scaled_line_width);
+ int ymax_ = ymin_ + vp_plib.rv_rect.height + (8 * scaled_line_width);
+
+ int x0, y0, x1, y1;
+
+ // Get the current display priority
+ // Default comes from the LUP, unless overridden
+ int priority_current = rzRules->LUP->DPRI - '0';
+ if (rzRules->obj->m_DPRI >= 0) priority_current = rzRules->obj->m_DPRI;
+
+ if (rzRules->obj->m_ls_list_legacy) {
+#ifdef ocpnUSE_GL
+ CGLShaderProgram *shader =
+ pCcolor_tri_shader_program[0 /*GetCanvasIndex()*/];
+ shader->Bind();
+
+ glBindBuffer(GL_ARRAY_BUFFER, 0);
+ glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
+
+ float colorv[4];
+ colorv[0] = color.Red() / float(256);
+ colorv[1] = color.Green() / float(256);
+ colorv[2] = color.Blue() / float(256);
+ colorv[3] = 1.0; // transparency;
+
+ shader->SetUniform4fv("color", colorv);
+
+ GLint pos = shader->getAttributeLocation("position");
+ glEnableVertexAttribArray(pos);
+#endif
+ float *ppt;
+ VE_Element *pedge;
+ PI_connector_segment *pcs;
+
+ unsigned char *vbo_point =
+ (unsigned char *)rzRules->obj->m_chart_context->vertex_buffer;
+ PI_line_segment_element *ls = rzRules->obj->m_ls_list_legacy;
+
+ while (ls) {
+ if (ls->priority == priority_current) {
+ int nPoints;
+ // fetch the first point
+ if (ls->type == TYPE_EE) {
+ pedge = (VE_Element *)ls->private0;
+ ppt = (float *)(vbo_point + pedge->vbo_offset);
+ nPoints = pedge->nCount;
+ } else {
+ pcs = (PI_connector_segment *)ls->private0;
+ ppt = (float *)(vbo_point + pcs->vbo_offset);
+ nPoints = 2;
+ }
+
+ wxPoint l;
+ GetPointPixSingle(rzRules, ppt[1], ppt[0], &l);
+ ppt += 2;
+
+ for (int ip = 0; ip < nPoints - 1; ip++) {
+ wxPoint r;
+ GetPointPixSingle(rzRules, ppt[1], ppt[0], &r);
+ // Draw the edge as point-to-point
+ x0 = l.x, y0 = l.y;
+ x1 = r.x, y1 = r.y;
+
+ // Do not draw null segments
+ if ((x0 != x1) || (y0 != y1)) {
+ if (m_pdc) {
+ if (cohen_sutherland_line_clip_i(&x0, &y0, &x1, &y1, xmin_, xmax_,
+ ymin_, ymax_) != Invisible)
+ m_pdc->DrawLine(x0, y0, x1, y1);
+ } else {
+#ifdef ocpnUSE_GL
+ // simplified faster test, let opengl do the rest
+ if ((x0 > xmin_ || x1 > xmin_) && (x0 < xmax_ || x1 < xmax_) &&
+ (y0 > ymin_ || y1 > ymin_) && (y0 < ymax_ || y1 < ymax_)) {
+ float pts[4];
+ pts[0] = x0;
+ pts[1] = y0;
+ pts[2] = x1;
+ pts[3] = y1;
+
+ glVertexAttribPointer(pos, 2, GL_FLOAT, GL_FALSE,
+ 2 * sizeof(float), pts);
+
+ glDrawArrays(GL_LINES, 0, 2);
+ }
+#endif
+ }
+ }
+
+ l = r;
+ ppt += 2;
+ }
+ }
+ ls = ls->next;
+ }
+
+#ifdef ocpnUSE_GL
+ shader->UnBind();
+#endif
+ }
+
+ return 1;
+}
+
+// Line Simple Style, Dashed, using GLSL
+#ifdef ocpnUSE_GL
+int s52plib::RenderLS_Dash_GLSL(ObjRazRules *rzRules, Rules *rules) {
+ // Retrieve the current clipping rectangle
+ wxRect clip_rect = wxRect(0, 0, vp_plib.pix_width, vp_plib.pix_height);
+ if (glIsEnabled(GL_SCISSOR_TEST)) {
+ int rect[4];
+ glGetIntegerv(GL_SCISSOR_BOX, rect);
+ clip_rect.x = rect[0];
+ clip_rect.y = vp_plib.pix_height - (rect[1] + rect[3]);
+ clip_rect.width = rect[2];
+ clip_rect.height = rect[3];
+ }
+
+ S52color *c;
+
+ char *instr_str = (char *)rules->INSTstr;
+ c = getColor(instr_str + 7); // Colour
+ wxColour color(c->R, c->G, c->B);
+ int w = atoi(instr_str + 5); // Width
+
+ double scale_factor = vp_plib.ref_scale / vp_plib.chart_scale;
+ double scaled_line_width = wxMax((scale_factor), 1);
+
+ // Set drawing width
+
+ float lineWidth = w;
+ GLint parms[2];
+ glGetIntegerv(GL_ALIASED_LINE_WIDTH_RANGE, &parms[0]);
+ GLint parmsa[2];
+ glGetIntegerv(GL_SMOOTH_LINE_WIDTH_RANGE, &parmsa[0]);
+ GLint parmsb[2];
+ glGetIntegerv(GL_SMOOTH_LINE_WIDTH_GRANULARITY, &parmsb[0]);
+
+ if (w > 1) {
+ if (w > parms[1])
+ lineWidth = wxMax(m_GLMinCartographicLineWidth, parms[1]);
+ else
+ lineWidth = wxMax(m_GLMinCartographicLineWidth, w);
+ } else
+ lineWidth = wxMax(m_GLMinCartographicLineWidth, 1);
+
+ // Manage super high density displays
+ float target_w_mm = 0.5 * w;
+ ;
+ if (GetPPMM() >
+ 7) { // arbitrary, leaves average desktop/laptop display untweaked...
+ target_w_mm = ((float)w) / 6.0; // Target width in mm
+ // The value "w" comes from S52 library CNSY procedures, in "nominal"
+ // pixels
+ // the value "6" comes from semi-standard LCD display densities
+ // or something like 0.18 mm pitch, or 6 pix per mm.
+ lineWidth = wxMax(m_GLMinCartographicLineWidth, target_w_mm * GetPPMM());
+ }
+
+ glDisable(GL_LINE_SMOOTH);
+ glEnable(GL_BLEND); // for shader
+
+#ifdef __OCPN__ANDROID__
+ lineWidth = wxMin(lineWidth, parms[1]);
+ glLineWidth(lineWidth);
+
+#else
+ glLineWidth(lineWidth);
+ if (lineWidth > 4.0) {
+ glEnable(GL_LINE_SMOOTH);
+ }
+#endif
+
+ // Get a true pixel clipping/bounding box from the vp
+ wxPoint pbb = GetPixFromLL(vp_plib.clat, vp_plib.clon);
+ int xmin_ = pbb.x - (vp_plib.rv_rect.width / 2) - (4 * scaled_line_width);
+ int xmax_ = xmin_ + vp_plib.rv_rect.width + (8 * scaled_line_width);
+ int ymin_ = pbb.y - (vp_plib.rv_rect.height / 2) - (4 * scaled_line_width);
+ int ymax_ = ymin_ + vp_plib.rv_rect.height + (8 * scaled_line_width);
+
+ double x0, y0, x1, y1;
+
+ // Get the current display priority
+ // Default comes from the LUP, unless overridden
+ int priority_current = rzRules->LUP->DPRI - '0';
+ if (rzRules->obj->m_DPRI >= 0) priority_current = rzRules->obj->m_DPRI;
+
+ glUseProgram(S52Dash_shader_program);
+
+ // Get pointers to the attributes in the program.
+ GLint mPosAttrib = glGetAttribLocation(S52Dash_shader_program, "position");
+ glEnableVertexAttribArray(mPosAttrib);
+
+ GLint startPos = glGetUniformLocation(S52Dash_shader_program, "startPos");
+ GLint texWidth = glGetUniformLocation(S52Dash_shader_program, "texWidth");
+ GLint dashFactor = glGetUniformLocation(S52Dash_shader_program, "dashFactor");
+
+ float colorv[4];
+ colorv[0] = color.Red() / float(256);
+ colorv[1] = color.Green() / float(256);
+ colorv[2] = color.Blue() / float(256);
+ colorv[3] = 1.0; // transparency;
+
+ GLint colloc = glGetUniformLocation(S52Dash_shader_program, "color");
+ glUniform4fv(colloc, 1, colorv);
+
+ // Select the active texture unit.
+ glActiveTexture(GL_TEXTURE0);
+
+ // Set up the texture sampler to texture unit 0
+ GLint texUni = glGetUniformLocation(S52Dash_shader_program, "uTex");
+ glUniform1i(texUni, 0);
+
+ if (!strncmp(instr_str, "DASH", 4)) {
+ // reduced from s52 specs (5.4), 3.6mm dash, 1.8mm space
+ float width = GetPPMM() * 3; // looks better
+ glUniform1f(texWidth, width);
+ glUniform1f(dashFactor, 0.66);
+ } else {
+ float width = GetPPMM() * 1;
+ glUniform1f(texWidth, width);
+ glUniform1f(dashFactor, 0.5);
+ }
+
+ // Disable VBO's (vertex buffer objects) for attributes.
+ glBindBuffer(GL_ARRAY_BUFFER, 0);
+ glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
+
+ // Rotate
+ mat4x4 I;
+ mat4x4_identity(I);
+
+ GLint matloc =
+ glGetUniformLocation(S52Dash_shader_program, "TransformMatrix");
+ glUniformMatrix4fv(matloc, 1, GL_FALSE, (const GLfloat *)I);
+
+ if (rzRules->obj->m_ls_list) {
+ float *ppt;
+
+ unsigned char *vbo_point =
+ (unsigned char *)rzRules->obj->m_chart_context->vertex_buffer;
+ line_segment_element *ls = rzRules->obj->m_ls_list;
+
+ while (ls) {
+ if (ls->priority == priority_current) {
+ int nPoints;
+ // fetch the first point
+ if ((ls->ls_type == TYPE_EE) || (ls->ls_type == TYPE_EE_REV)) {
+ ppt = (float *)(vbo_point + ls->pedge->vbo_offset);
+ nPoints = ls->pedge->nCount;
+ } else {
+ ppt = (float *)(vbo_point + ls->pcs->vbo_offset);
+ nPoints = 2;
+ }
+
+ wxPoint2DDouble l;
+ GetDoublePointPixSingle(rzRules, ppt[1], ppt[0], &l);
+ ppt += 2;
+
+ for (int ip = 0; ip < nPoints - 1; ip++) {
+ wxPoint2DDouble r;
+ GetDoublePointPixSingle(rzRules, ppt[1], ppt[0], &r);
+ // Draw the edge as point-to-point
+ x0 = l.m_x, y0 = l.m_y;
+ x1 = r.m_x, y1 = r.m_y;
+
+ // Do not draw null segments
+ if ((x0 != x1) || (y0 != y1)) {
+ // segment must be at least on-screen....
+ if ((x0 > xmin_ || x1 > xmin_) && (x0 < xmax_ || x1 < xmax_) &&
+ (y0 > ymin_ || y1 > ymin_) && (y0 < ymax_ || y1 < ymax_)) {
+ // And intersecting the current clip rectangle
+ double xa = x0;
+ double xw = x1 - x0;
+ if (xw < 0) {
+ xa = x1;
+ xw = -xw;
+ }
+ double ya = y0;
+ double yh = y1 - y0;
+ if (yh < 0) {
+ ya = y1;
+ yh = -yh;
+ }
+
+ wxRect rseg(xa, ya, xw, yh);
+ rseg.Inflate(1); // avoid zero width/height
+
+ if (rseg.Intersects(clip_rect)) {
+ float coords[4];
+
+ coords[0] = x0;
+ coords[1] = y0;
+ coords[2] = x1;
+ coords[3] = y1;
+
+ float start[2];
+ start[0] = x0;
+ start[1] = vp_plib.pix_height - y0;
+ glUniform2fv(startPos, 1, start);
+
+ // Set the attribute mPosAttrib with the vertices in the screen
+ // coordinates...
+ glVertexAttribPointer(mPosAttrib, 2, GL_FLOAT, GL_FALSE, 0,
+ coords);
+ glEnableVertexAttribArray(mPosAttrib);
+
+ // Perform the actual drawing.
+ glDrawArrays(GL_LINES, 0, 2);
+ }
+ }
+ }
+
+ l = r;
+ ppt += 2;
+ }
+ }
+
+ ls = ls->next;
+ }
+ }
+
+ glUseProgram(0);
+ glDisableVertexAttribArray(mPosAttrib);
+
+ return 1;
+}
+#endif
+
+// Line Complex
+int s52plib::RenderLCTexture(ObjRazRules *rzRules, Rules *rules) {
+
+ // catch cm93 and legacy PlugIns (e.g.s63_pi)
+ if (rzRules->obj->m_n_lsindex && !rzRules->obj->m_ls_list)
+ return RenderLCLegacy(rzRules, rules);
+
+ //int wd = ComputeWinding(rzRules, rules);
+ bool cww = false;
+ //if (wd == 2) cww = true;
+
+ wxPoint r;
+
+ int isym_len = rules->razRule->pos.line.bnbox_w.SYHL;
+ float sym_len = isym_len * canvas_pix_per_mm / 100;
+
+ int isym_height = rules->razRule->pos.line.bnbox_h.SYVL;
+ float sym_height = isym_height * canvas_pix_per_mm / 100;
+
+ float sym_factor = 1.0; /// 1.50; // gives nicer effect
+
+ // Create a color for drawing adjustments outside of HPGL renderer
+ char *tcolptr = rules->razRule->colRef.LCRF;
+ S52color *c = getColor(tcolptr + 1); // +1 skips "n" in HPGL SPn format
+ int w = 1; // arbitrary width
+ wxColour color(c->R, c->G, c->B);
+
+ double meters_per_senc_unit = rzRules->obj->x_rate; // meters per senc-unit
+ double lod_2pixel_meters =
+ 2 /
+ vp_plib
+ .view_scale_ppm; // LOD set to 2 pixels, nominal mercator projected
+ double LOD = lod_2pixel_meters / meters_per_senc_unit;
+
+ // Get the current display priority
+ // Default comes from the LUP, unless overridden
+ int priority_current = rzRules->LUP->DPRI - '0';
+ if (rzRules->obj->m_DPRI >= 0) priority_current = rzRules->obj->m_DPRI;
+
+ if (rzRules->obj->m_n_lsindex) {
+ // Calculate the size of a work buffer
+ int max_points = 0;
+ if (rzRules->obj->m_n_edge_max_points > 0)
+ max_points = rzRules->obj->m_n_edge_max_points;
+ else {
+ line_segment_element *lsa = rzRules->obj->m_ls_list;
+
+ while (lsa) {
+ if ((lsa->ls_type == TYPE_EE) || (lsa->ls_type == TYPE_EE_REV))
+ max_points += lsa->pedge->nCount;
+ else
+ max_points += 2;
+
+ lsa = lsa->next;
+ }
+ }
+
+ float *ppt;
+ unsigned char *vbo_point =
+ (unsigned char *)rzRules->obj->m_chart_context
+ ->vertex_buffer; // chart->GetLineVertexBuffer();
+
+ // Allocate some storage for converted points
+ wxPoint2DDouble *ptp = (wxPoint2DDouble *)malloc((max_points) * sizeof(wxPoint2DDouble));
+ double *pdp = (double *)malloc(2 * (max_points) * sizeof(double));
+ int *mask = (int *)malloc((max_points) * sizeof(int));
+
+ line_segment_element *ls = rzRules->obj->m_ls_list;
+
+ unsigned int index = 0;
+ unsigned int idouble = 0;
+ int nls = 0;
+ wxPoint2DDouble lp;
+
+ int ndraw = 0;
+ while (ls) {
+ if (ls->priority == priority_current) {
+ // transcribe the segment in the proper order into the output buffer
+ int nPoints;
+ int idir = 1;
+ bool bcon = false;
+ // fetch the first point
+ if ((ls->ls_type == TYPE_EE) || (ls->ls_type == TYPE_EE_REV)) {
+ ppt = (float *)(vbo_point + ls->pedge->vbo_offset);
+ nPoints = ls->pedge->nCount;
+ if (ls->ls_type == TYPE_EE_REV)
+ idir = -1;
+
+ } else {
+ ppt = (float *)(vbo_point + ls->pcs->vbo_offset);
+ nPoints = 2;
+ bcon = true;
+ }
+
+ int vbo_index = 0;
+ int vbo_inc = 2;
+ if ((idir == -1) && !bcon) {
+ vbo_index = (nPoints - 1) * 2;
+ vbo_inc = -2;
+ }
+
+ double offset = 0;
+ for (int ip = 0; ip < nPoints; ip++) {
+ wxPoint2DDouble r;
+ GetDoublePointPixSingle(rzRules, ppt[vbo_index + 1], ppt[vbo_index], &r);
+ if ((r.m_x != lp.m_x) || (r.m_y != lp.m_y)) {
+ mask[index] = (ls->priority == priority_current) ? 1 : 0;
+ ptp[index++] = r;
+ pdp[idouble++] = ppt[vbo_index];
+ pdp[idouble++] = ppt[vbo_index + 1];
+
+ nls++;
+ } else { // sKipping point
+ }
+
+ lp = r;
+ vbo_index += vbo_inc;
+ }
+
+ } // priority
+
+ // inspect the next segment to see if it can be connected, or if the chain
+ // breaks
+ int idir = 1;
+ bool bcon = false;
+ if (ls->next) {
+ int nPoints_next;
+ line_segment_element *lsn = ls->next;
+ // fetch the first point
+ if ((lsn->ls_type == TYPE_EE) || (lsn->ls_type == TYPE_EE_REV)) {
+ ppt = (float *)(vbo_point + lsn->pedge->vbo_offset);
+ nPoints_next = lsn->pedge->nCount;
+ if (lsn->ls_type == TYPE_EE_REV) idir = -1;
+
+ } else {
+ ppt = (float *)(vbo_point + lsn->pcs->vbo_offset);
+ nPoints_next = 2;
+ bcon = true;
+ }
+
+ wxPoint2DDouble ptest;
+ if (bcon)
+ GetDoublePointPixSingle(rzRules, ppt[1], ppt[0], &ptest);
+
+ else {
+ if (idir == 1)
+ GetDoublePointPixSingle(rzRules, ppt[1], ppt[0], &ptest);
+
+ else {
+ // fetch the last point
+ int index_last_next = (nPoints_next - 1) * 2;
+ GetDoublePointPixSingle(rzRules, ppt[index_last_next + 1],
+ ppt[index_last_next], &ptest);
+ }
+ }
+
+ // try to match the correct point in this segment with the last point in
+ // the previous segment
+
+ if (lp != ptest) // not connectable?
+ {
+ if (nls) {
+ wxPoint2DDouble *pReduced = 0;
+ int *pMaskOut = 0;
+ int nPointReduced =
+ reduceLOD(LOD, nls, pdp, &pReduced, mask, &pMaskOut);
+
+ wxPoint2DDouble *ptestp = (wxPoint2DDouble *)malloc((max_points) * sizeof(wxPoint2DDouble));
+ GetDoublePointPixArray(rzRules, pReduced, ptestp, nPointReduced);
+ free(pReduced);
+
+ draw_lc_poly_texture(m_pdc, color, w, ptestp, pMaskOut,
+ nPointReduced, sym_len, sym_height, sym_factor,
+ rules->razRule, cww);
+ free(ptestp);
+ free(pMaskOut);
+
+ ndraw++;
+ }
+
+ nls = 0;
+ index = 0;
+ idouble = 0;
+ lp = wxPoint(0, 0);
+ }
+
+ } else {
+ // no more segments, so render what is available
+ if (nls) {
+ wxPoint2DDouble *pReduced = 0;
+ int *pMaskOut = 0;
+ int nPointReduced =
+ reduceLOD(LOD, nls, pdp, &pReduced, mask, &pMaskOut);
+
+ wxPoint2DDouble *ptestp = (wxPoint2DDouble *)malloc((max_points) * sizeof(wxPoint2DDouble));
+ GetDoublePointPixArray(rzRules, pReduced, ptestp, nPointReduced);
+ free(pReduced);
+
+ draw_lc_poly_texture(m_pdc, color, w, ptestp, pMaskOut, nPointReduced,
+ sym_len, sym_height, sym_factor, rules->razRule, cww);
+ free(ptestp);
+ free(pMaskOut);
+ }
+ }
+
+ ls = ls->next;
+ }
+
+ free(ptp);
+ free(pdp);
+ free(mask);
+ }
+
+ return 1;
+}
+
+int s52plib::RenderLC(ObjRazRules *rzRules, Rules *rules) {
+ //if(rzRules->obj->Index != 5347)
+ //return 0;
+ //if(strncmp(rzRules->obj->FeatureName, "FAIRWY", 6))
+ // return 0;
+
+#if !defined(__WXMAC__)
+ //return RenderLCTexture(rzRules, rules);
+#endif
+
+ // catch cm93 and legacy PlugIns (e.g.s63_pi)
+ if (rzRules->obj->m_n_lsindex && !rzRules->obj->m_ls_list)
+ return RenderLCLegacy(rzRules, rules);
+
+ // It appears that, at least for NOAA ENCs, the assembled feature
+ // winding direction is always returned as ANTI-CLOCKWISE
+ // So, proviswionally assume no need to check
+ // See also fixed winding declaration in draw_lc_poly()
+ //int winding = ComputeWinding(rzRules, rules);
+
+ wxPoint r;
+
+ int isym_len = rules->razRule->pos.line.bnbox_w.SYHL +
+ (rules->razRule->pos.line.bnbox_x.LBXC -
+ rules->razRule->pos.line.pivot_x.LICL);
+ float sym_len = isym_len * canvas_pix_per_mm / 100;
+ float sym_factor = 1.0; /// 1.50; // gives nicer effect
+
+ // Create a color for drawing adjustments outside of HPGL renderer
+ char *tcolptr = rules->razRule->colRef.LCRF;
+ S52color *c = getColor(tcolptr + 1); // +1 skips "n" in HPGL SPn format
+ int w = 1; // arbitrary width
+ wxColour color(c->R, c->G, c->B);
+
+ double meters_per_senc_unit = rzRules->obj->x_rate; // meters per senc-unit
+ double lod_2pixel_meters =
+ 2 /
+ vp_plib
+ .view_scale_ppm; // LOD set to 2 pixels, nominal mercator projected
+ double LOD = lod_2pixel_meters / meters_per_senc_unit;
+
+ // Get the current display priority
+ // Default comes from the LUP, unless overridden
+ int priority_current = rzRules->LUP->DPRI - '0';
+ if (rzRules->obj->m_DPRI >= 0) priority_current = rzRules->obj->m_DPRI;
+
+ if (rzRules->obj->m_n_lsindex) {
+ // Calculate the size of a work buffer
+ int max_points = 0;
+ if (rzRules->obj->m_n_edge_max_points > 0)
+ max_points = rzRules->obj->m_n_edge_max_points;
+ else {
+ line_segment_element *lsa = rzRules->obj->m_ls_list;
+
+ while (lsa) {
+ if ((lsa->ls_type == TYPE_EE) || (lsa->ls_type == TYPE_EE_REV))
+ max_points += lsa->pedge->nCount;
+ else
+ max_points += 2;
+
+ lsa = lsa->next;
+ }
+ }
+
+ float *ppt;
+ unsigned char *vbo_point =
+ (unsigned char *)rzRules->obj->m_chart_context
+ ->vertex_buffer; // chart->GetLineVertexBuffer();
+
+ // Allocate some storage for converted points
+ wxPoint *ptp = (wxPoint *)malloc((max_points) * sizeof(wxPoint));
+ double *pdp = (double *)malloc(2 * (max_points) * sizeof(double));
+ int *mask = (int *)malloc((max_points) * sizeof(int));
+
+ line_segment_element *ls = rzRules->obj->m_ls_list;
+
+ unsigned int index = 0;
+ unsigned int idouble = 0;
+ int nls = 0;
+ wxPoint lp;
+
+ int ndraw = 0;
+ while (ls) {
+ if (ls->priority == priority_current) {
+ // transcribe the segment in the proper order into the output buffer
+ int nPoints;
+ int idir = 1;
+ bool bcon = false;
+ // fetch the first point
+ if ((ls->ls_type == TYPE_EE) || (ls->ls_type == TYPE_EE_REV)) {
+ ppt = (float *)(vbo_point + ls->pedge->vbo_offset);
+ nPoints = ls->pedge->nCount;
+ if (ls->ls_type == TYPE_EE_REV) idir = -1;
+
+ } else {
+ ppt = (float *)(vbo_point + ls->pcs->vbo_offset);
+ nPoints = 2;
+ bcon = true;
+ }
+
+ int vbo_index = 0;
+ int vbo_inc = 2;
+ if ((idir == -1) && !bcon) {
+ vbo_index = (nPoints - 1) * 2;
+ vbo_inc = -2;
+ }
+
+ double offset = 0;
+ for (int ip = 0; ip < nPoints; ip++) {
+ wxPoint r;
+ GetPointPixSingle(rzRules, ppt[vbo_index + 1], ppt[vbo_index], &r);
+ if ((r.x != lp.x) || (r.y != lp.y)) {
+ mask[index] = (ls->priority == priority_current) ? 1 : 0;
+ ptp[index++] = r;
+ pdp[idouble++] = ppt[vbo_index];
+ pdp[idouble++] = ppt[vbo_index + 1];
+
+ nls++;
+ } else { // sKipping point
+ }
+
+ lp = r;
+ vbo_index += vbo_inc;
+ }
+
+ } // priority
+
+ // inspect the next segment to see if it can be connected, or if the chain
+ // breaks
+ int idir = 1;
+ bool bcon = false;
+ if (ls->next) {
+ int nPoints_next;
+ line_segment_element *lsn = ls->next;
+ // fetch the first point
+ if ((lsn->ls_type == TYPE_EE) || (lsn->ls_type == TYPE_EE_REV)) {
+ ppt = (float *)(vbo_point + lsn->pedge->vbo_offset);
+ nPoints_next = lsn->pedge->nCount;
+ if (lsn->ls_type == TYPE_EE_REV) idir = -1;
+
+ } else {
+ ppt = (float *)(vbo_point + lsn->pcs->vbo_offset);
+ nPoints_next = 2;
+ bcon = true;
+ }
+
+ wxPoint ptest;
+ if (bcon)
+ GetPointPixSingle(rzRules, ppt[1], ppt[0], &ptest);
+
+ else {
+ if (idir == 1)
+ GetPointPixSingle(rzRules, ppt[1], ppt[0], &ptest);
+
+ else {
+ // fetch the last point
+ int index_last_next = (nPoints_next - 1) * 2;
+ GetPointPixSingle(rzRules, ppt[index_last_next + 1],
+ ppt[index_last_next], &ptest);
+ }
+ }
+
+ // try to match the correct point in this segment with the last point in
+ // the previous segment
+
+ if (lp != ptest) // not connectable?
+ {
+ if (nls) {
+ wxPoint2DDouble *pReduced = 0;
+ int *pMaskOut = 0;
+ int nPointReduced =
+ reduceLOD(LOD, nls, pdp, &pReduced, mask, &pMaskOut);
+
+ wxPoint *ptestp = (wxPoint *)malloc((max_points) * sizeof(wxPoint));
+ GetPointPixArray(rzRules, pReduced, ptestp, nPointReduced);
+ free(pReduced);
+
+ draw_lc_poly(m_pdc, color, w, ptestp, pMaskOut, nPointReduced,
+ sym_len, 0, sym_factor, rules->razRule);
+ free(ptestp);
+ free(pMaskOut);
+
+ ndraw++;
+ }
+
+ nls = 0;
+ index = 0;
+ idouble = 0;
+ lp = wxPoint(0, 0);
+ }
+
+ } else {
+ // no more segments, so render what is available
+ if (nls) {
+ wxPoint2DDouble *pReduced = 0;
+ int *pMaskOut = 0;
+ int nPointReduced =
+ reduceLOD(LOD, nls, pdp, &pReduced, mask, &pMaskOut);
+
+ wxPoint *ptestp = (wxPoint *)malloc((max_points) * sizeof(wxPoint));
+ GetPointPixArray(rzRules, pReduced, ptestp, nPointReduced);
+ free(pReduced);
+
+ draw_lc_poly(m_pdc, color, w, ptestp, pMaskOut, nPointReduced,
+ sym_len, 0, sym_factor, rules->razRule);
+ free(ptestp);
+ free(pMaskOut);
+ }
+ }
+
+ ls = ls->next;
+ }
+
+ free(ptp);
+ free(pdp);
+ free(mask);
+ }
+
+ return 1;
+}
+
+int s52plib::ComputeWinding(ObjRazRules *rzRules, Rules *rules) {
+ if (rzRules->obj->Primitive_type != GEO_AREA)
+ return -1;
+
+ // catch cm93 and legacy PlugIns (e.g.s63_pi)
+ if (rzRules->obj->m_n_lsindex && !rzRules->obj->m_ls_list)
+ return -1;
+
+ std::vector points;
+
+ if (rzRules->obj->m_n_lsindex) {
+
+ float *ppt;
+ unsigned char *vbo_point =
+ (unsigned char *)rzRules->obj->m_chart_context
+ ->vertex_buffer; // chart->GetLineVertexBuffer();
+
+ line_segment_element *ls = rzRules->obj->m_ls_list;
+
+ unsigned int index = 0;
+ unsigned int idouble = 0;
+ int nls = 0;
+ wxPoint2DDouble lp;
+
+ int ndraw = 0;
+ while (ls) {
+ // transcribe the segment in the proper order into the output buffer
+ int nPoints;
+ int idir = 1;
+ bool bcon = false;
+ // fetch the first point
+ if ((ls->ls_type == TYPE_EE) || (ls->ls_type == TYPE_EE_REV)) {
+ ppt = (float *)(vbo_point + ls->pedge->vbo_offset);
+ nPoints = ls->pedge->nCount;
+ if (ls->ls_type == TYPE_EE_REV) idir = -1;
+ } else {
+ ppt = (float *)(vbo_point + ls->pcs->vbo_offset);
+ nPoints = 2;
+ bcon = true;
+ }
+
+ int vbo_index = 0;
+ int vbo_inc = 2;
+ if ((idir == -1) && !bcon) {
+ vbo_index = (nPoints - 1) * 2;
+ vbo_inc = -2;
+ }
+
+ double offset = 0;
+ for (int ip = 0; ip < nPoints; ip++) {
+ wxPoint2DDouble r;
+ r.m_x = ppt[vbo_index + 1];
+ r.m_y = ppt[vbo_index];
+ nls++;
+ points.push_back(r);
+
+ lp = r;
+ vbo_index += vbo_inc;
+ }
+
+ /*
+ // inspect the next segment to see if it can be connected, or if the chain
+ // breaks
+ int idira = 1;
+ bool bcona = false;
+ if (ls->next) {
+ int nPoints_next;
+ line_segment_element *lsn = ls->next;
+ // fetch the first point
+ if ((lsn->ls_type == TYPE_EE) || (lsn->ls_type == TYPE_EE_REV)) {
+ ppt = (float *)(vbo_point + lsn->pedge->vbo_offset);
+ nPoints_next = lsn->pedge->nCount;
+ if (lsn->ls_type == TYPE_EE_REV) idir = -1;
+
+ } else {
+ ppt = (float *)(vbo_point + lsn->pcs->vbo_offset);
+ nPoints_next = 2;
+ bcona = true;
+ }
+
+ wxPoint2DDouble ptest;
+ if (bcona) {
+ ptest.m_x = ppt[1];
+ ptest.m_y = ppt[0];
+ } else {
+ if (idira == 1) {
+ ptest.m_x = ppt[1];
+ ptest.m_y = ppt[0];
+ } else {
+ // fetch the last point
+ int index_last_next = (nPoints_next - 1) * 2;
+ ptest.m_x = ppt[index_last_next + 1];
+ ptest.m_y = ppt[index_last_next];
+ }
+ }
+
+ }
+*/
+ ls = ls->next;
+ }
+ } else return -1;
+
+
+ if (rzRules->obj->Primitive_type == GEO_AREA) {
+ // Is the line feature closed?
+ wxPoint2DDouble start = points[0];
+ wxPoint2DDouble end = points[sizeof(points) - 1];
+ double dx = start.m_x - end.m_x;
+ double dy = start.m_y - end.m_y;
+ double delta = sqrt(dx * dx + dy * dy);
+ if (delta > 1)
+ points.push_back(start); // close it.
+
+ //calculate winding direction
+ double dfSum = 0.0;
+ for (size_t i=0; i < points.size()-1; i++) {
+ wxPoint2DDouble p1 = points[i];
+ wxPoint2DDouble p2 = points[i+1];
+ dfSum += (p1.m_x * p2.m_y) - (p1.m_y * p2.m_x);
+ }
+ bool cw = dfSum < 0.;
+
+ int rv = 1;
+ if (cw) rv = 2;
+
+ return rv;
+ }
+ return -1;
+}
+
+int s52plib::reduceLOD(double LOD_meters, int nPoints, double *source,
+ wxPoint2DDouble **dest, int *maskIn, int **maskOut) {
+ // Reduce the LOD of this linestring
+ std::vector index_keep;
+ if (nPoints > 5 && (LOD_meters > .01)) {
+ index_keep.push_back(0);
+ index_keep.push_back(nPoints - 1);
+ index_keep.push_back(nPoints - 2);
+
+ DouglasPeucker(source, 0, nPoints - 2, LOD_meters, &index_keep);
+
+ } else {
+ index_keep.resize(nPoints);
+ // Consider using std::iota here when there is C++11 support.
+ for (int i = 0; i < nPoints; i++) index_keep[i] = i;
+ }
+
+ wxPoint2DDouble *pReduced =
+ (wxPoint2DDouble *)malloc((index_keep.size()) * sizeof(wxPoint2DDouble));
+ *dest = pReduced;
+
+ int *pmaskOut = NULL;
+ if (maskIn) {
+ *maskOut = (int *)malloc((index_keep.size()) * sizeof(int));
+ pmaskOut = *maskOut;
+ }
+
+ double *ppr = source;
+ int ir = 0;
+ for (int ip = 0; ip < nPoints; ip++) {
+ double x = *ppr++;
+ double y = *ppr++;
+ int maskval = 1;
+ if (maskIn) maskval = maskIn[ip];
+ // printf("LOD: %10g %10g\n", x, y);
+
+ for (unsigned int j = 0; j < index_keep.size(); j++) {
+ if (index_keep[j] == ip) {
+ if (pmaskOut) pmaskOut[ir] = maskval;
+ pReduced[ir++] = wxPoint2DDouble(x, y);
+ break;
+ }
+ }
+ }
+
+ return index_keep.size();
+}
+
+// Line Complex
+int s52plib::RenderLCLegacy(ObjRazRules *rzRules, Rules *rules) {
+ if (!rzRules->obj->m_chart_context->chart)
+ return RenderLCPlugIn(rzRules, rules);
+
+ // Must be cm93
+
+ wxPoint r;
+
+ int isym_len = rules->razRule->pos.line.bnbox_w.SYHL;
+ float sym_len = isym_len * canvas_pix_per_mm / 100;
+ float sym_factor = 1.0; /// 1.50; // gives nicer
+ /// effect
+
+ // Create a color for drawing adjustments outside of HPGL renderer
+ char *tcolptr = rules->razRule->colRef.LCRF;
+ S52color *c = getColor(tcolptr + 1); // +1 skips "n" in HPGL SPn format
+ int w = 1; // arbitrary width
+ wxColour color(c->R, c->G, c->B);
+ // Get the current display priority
+ // Default comes from the LUP, unless overridden
+ int priority_current = rzRules->LUP->DPRI - '0';
+ if (rzRules->obj->m_DPRI >= 0) priority_current = rzRules->obj->m_DPRI;
+
+ if (rzRules->obj->m_n_lsindex) {
+ auto ve_hash = rzRules->obj->m_chart_context->m_pve_hash;
+ auto vc_hash = rzRules->obj->m_chart_context->m_pvc_hash;
+
+ unsigned int nls_max;
+ if (rzRules->obj->m_n_edge_max_points >
+ 0) // size has been precalculated on SENC load
+ nls_max = rzRules->obj->m_n_edge_max_points;
+ else {
+ // Calculate max malloc size required
+ nls_max = 0;
+ int *index_run_x = rzRules->obj->m_lsindex_array;
+ for (int imseg = 0; imseg < rzRules->obj->m_n_lsindex; imseg++) {
+ index_run_x++; // Skip cNode
+ // Get the edge
+ unsigned int enode = *index_run_x;
+ if (enode) {
+ VE_Element *pedge = (*ve_hash)[enode];
+ if (pedge) {
+ if (pedge->nCount > nls_max) nls_max = pedge->nCount;
+ }
+ }
+ index_run_x += 2;
+ }
+ rzRules->obj->m_n_edge_max_points =
+ nls_max; // Got it, cache for next time
+ }
+
+ // Allocate some storage for converted points
+ wxPoint *ptp = (wxPoint *)malloc(
+ (nls_max + 2) * sizeof(wxPoint)); // + 2 allows for end nodes
+
+ int *index_run;
+ float *ppt;
+ double easting, northing;
+ wxPoint pra(0, 0);
+ VC_Element *pnode;
+
+ for (int iseg = 0; iseg < rzRules->obj->m_n_lsindex; iseg++) {
+ int seg_index = iseg * 3;
+ index_run = &rzRules->obj->m_lsindex_array[seg_index];
+
+ // Get first connected node
+ unsigned int inode = *index_run++;
+ if (inode) {
+ pnode = (*vc_hash)[inode];
+ if (pnode) {
+ ppt = pnode->pPoint;
+ easting = *ppt++;
+ northing = *ppt;
+ GetPointPixSingle(rzRules, (float)northing, (float)easting, &pra);
+ }
+ ptp[0] = pra; // insert beginning node
+ }
+
+ // Get the edge
+ unsigned int enode = *index_run++;
+ VE_Element *pedge = 0;
+ if (enode) pedge = (*ve_hash)[enode];
+
+ int nls = 0;
+ if (pedge) {
+ // Here we decide to draw or not based on the highest priority seen for
+ // this segment That is, if this segment is going to be drawn at a
+ // higher priority later, then don't draw it here.
+
+ // This logic is not perfectly right for one case:
+ // If the segment has only two end connected nodes, and no intermediate
+ // edge, then we have no good way to evaluate the priority. This is due
+ // to the fact that priority is only precalculated for edge segments,
+ // not connector nodes. Only thing to do is take the conservative
+ // approach and draw the segment, in this case.
+ if (pedge->nCount) {
+ if (pedge->max_priority != priority_current) continue;
+ }
+
+ if (pedge->max_priority != priority_current) continue;
+
+ nls = pedge->nCount;
+
+ ppt = pedge->pPoints;
+ for (int ip = 0; ip < nls; ip++) {
+ easting = *ppt++;
+ northing = *ppt++;
+ GetPointPixSingle(rzRules, (float)northing, (float)easting,
+ &ptp[ip + 1]);
+ }
+ }
+
+ // Get last connected node
+ unsigned int jnode = *index_run++;
+ if (jnode) {
+ pnode = (*vc_hash)[jnode];
+ if (pnode) {
+ ppt = pnode->pPoint;
+ easting = *ppt++;
+ northing = *ppt;
+ GetPointPixSingle(rzRules, (float)northing, (float)easting, &pra);
+ }
+ ptp[nls + 1] = pra; // insert ending node
+ }
+
+ if ((inode) && (jnode)) {
+ draw_lc_poly(m_pdc, color, w, ptp, NULL, nls + 2, sym_len, 0,
+ sym_factor, rules->razRule);
+ } else if (nls) {
+ draw_lc_poly(m_pdc, color, w, &ptp[1], NULL, nls, sym_len, 0,
+ sym_factor, rules->razRule);
+ }
+ }
+ free(ptp);
+ }
+
+ else if (rzRules->obj->pPolyTessGeo) {
+ if (!rzRules->obj->pPolyTessGeo->IsOk()) { // perform deferred tesselation
+ rzRules->obj->pPolyTessGeo->BuildDeferredTess();
+ }
+
+ PolyTriGroup *pptg = rzRules->obj->pPolyTessGeo->Get_PolyTriGroup_head();
+ float *ppolygeo = pptg->pgroup_geom;
+ if (ppolygeo) {
+ int ctr_offset = 0;
+ for (int ic = 0; ic < pptg->nContours; ic++) {
+ int npt = pptg->pn_vertex[ic];
+ wxPoint *ptp = (wxPoint *)malloc((npt + 1) * sizeof(wxPoint));
+ wxPoint *pr = ptp;
+ for (int ip = 0; ip < npt; ip++) {
+ float plon = ppolygeo[(2 * ip) + ctr_offset];
+ float plat = ppolygeo[(2 * ip) + ctr_offset + 1];
+
+ GetPointPixSingle(rzRules, plat, plon, pr);
+ pr++;
+ }
+ float plon = ppolygeo[ctr_offset]; // close the polyline
+ float plat = ppolygeo[ctr_offset + 1];
+ GetPointPixSingle(rzRules, plat, plon, pr);
+
+ draw_lc_poly(m_pdc, color, w, ptp, NULL, npt + 1, sym_len, 0,
+ sym_factor, rules->razRule);
+
+ free(ptp);
+
+ ctr_offset += npt * 2;
+ }
+ }
+ }
+ return 1;
+}
+
+int s52plib::RenderLCPlugIn(ObjRazRules *rzRules, Rules *rules) {
+ wxPoint r;
+
+ int isym_len = rules->razRule->pos.line.bnbox_w.SYHL;
+ float sym_len = isym_len * canvas_pix_per_mm / 100;
+ float sym_factor = 1.0; /// 1.50; // gives nicer
+ /// effect
+
+ // Create a color for drawing adjustments outside of HPGL renderer
+ char *tcolptr = rules->razRule->colRef.LCRF;
+ S52color *c = getColor(tcolptr + 1); // +1 skips "n" in HPGL SPn format
+ int w = 1; // arbitrary width
+ wxColour color(c->R, c->G, c->B);
+ double LOD = 2.0 / vp_plib.view_scale_ppm; // empirical value, by experiment
+ LOD = 0; // wxMin(LOD, 10.0);
+
+ // Get the current display priority
+ // Default comes from the LUP, unless overridden
+ int priority_current = rzRules->LUP->DPRI - '0';
+ if (rzRules->obj->m_DPRI >= 0) priority_current = rzRules->obj->m_DPRI;
+
+ // Calculate max malloc size required
+
+ int max_points = 0;
+ if (rzRules->obj->m_ls_list_legacy) {
+ VE_Element *pedge;
+ PI_line_segment_element *ls = rzRules->obj->m_ls_list_legacy;
+
+ while (ls) {
+ int nPoints;
+ // fetch the first point
+ if (ls->type == TYPE_EE) {
+ pedge = (VE_Element *)ls->private0;
+ nPoints = pedge->nCount;
+ } else {
+ nPoints = 2;
+ }
+
+ max_points += nPoints;
+
+ ls = ls->next;
+ }
+ }
+
+ if (rzRules->obj->m_ls_list_legacy) {
+ float *ppt;
+
+ VE_Element *pedge;
+
+ // Allocate some storage for converted points
+ wxPoint *ptp = (wxPoint *)malloc(
+ (max_points + 2) * sizeof(wxPoint)); // + 2 allows for end nodes
+ double *pdp = (double *)malloc(2 * (max_points + 2) * sizeof(double));
+
+ PI_connector_segment *pcs;
+
+ unsigned char *vbo_point =
+ (unsigned char *)rzRules->obj->m_chart_context->vertex_buffer;
+ PI_line_segment_element *ls = rzRules->obj->m_ls_list_legacy;
+
+ unsigned int index = 0;
+ unsigned int idouble = 0;
+ int nls = 0;
+ wxPoint lp;
+
+ ls = rzRules->obj->m_ls_list_legacy;
+ while (ls) {
+ if (ls->priority == priority_current) {
+ // transcribe the segment in the proper order into the output buffer
+ int nPoints;
+ int idir = 1;
+ bool bcon = false;
+ // fetch the first point
+ if ((ls->type == TYPE_EE) || (ls->type == TYPE_EE_REV)) {
+ pedge = (VE_Element *)ls->private0;
+ ppt = (float *)(vbo_point + pedge->vbo_offset);
+ nPoints = pedge->nCount;
+ if (ls->type == TYPE_EE_REV) idir = -1;
+
+ } else {
+ pcs = (PI_connector_segment *)ls->private0;
+ ppt = (float *)(vbo_point + pcs->vbo_offset);
+ nPoints = 2;
+ bcon = true;
+ }
+
+ int vbo_index = 0;
+ int vbo_inc = 2;
+ if ((idir == -1) && !bcon) {
+ vbo_index = (nPoints - 1) * 2;
+ vbo_inc = -2;
+ }
+ for (int ip = 0; ip < nPoints; ip++) {
+ wxPoint r;
+ GetPointPixSingle(rzRules, ppt[vbo_index + 1], ppt[vbo_index], &r);
+
+ if (1 /*(r.x != lp.x) || (r.y != lp.y)*/) {
+ ptp[index++] = r;
+ pdp[idouble++] = ppt[vbo_index];
+ pdp[idouble++] = ppt[vbo_index + 1];
+
+ nls++;
+ } else { // sKipping point
+ }
+
+ lp = r;
+ vbo_index += vbo_inc;
+ }
+
+ } // priority
+
+ // inspect the next segment to see if it can be connected, or if the chain
+ // breaks
+ int idir = 1;
+ bool bcon = false;
+ if (ls->next) {
+ int nPoints_next;
+ PI_line_segment_element *lsn = ls->next;
+ // fetch the first point
+ if ((lsn->type == TYPE_EE) || (lsn->type == TYPE_EE_REV)) {
+ pedge = (VE_Element *)lsn->private0;
+ ppt = (float *)(vbo_point + pedge->vbo_offset);
+ nPoints_next = pedge->nCount;
+ if (lsn->type == TYPE_EE_REV) idir = -1;
+
+ } else {
+ pcs = (PI_connector_segment *)lsn->private0;
+ ppt = (float *)(vbo_point + pcs->vbo_offset);
+ nPoints_next = 2;
+ bcon = true;
+ }
+
+ wxPoint ptest;
+ if (bcon)
+ GetPointPixSingle(rzRules, ppt[1], ppt[0], &ptest);
+
+ else {
+ if (idir == 1)
+ GetPointPixSingle(rzRules, ppt[1], ppt[0], &ptest);
+
+ else {
+ // fetch the last point
+ int index_last_next = (nPoints_next - 1) * 2;
+ GetPointPixSingle(rzRules, ppt[index_last_next + 1],
+ ppt[index_last_next], &ptest);
+ }
+ }
+
+ // try to match the correct point in this segment with the last point in
+ // the previous segment
+
+ if (lp != ptest) // not connectable?
+ {
+ if (nls) {
+ wxPoint2DDouble *pReduced = 0;
+ int nPointReduced = reduceLOD(LOD, nls, pdp, &pReduced, NULL, NULL);
+
+ wxPoint *ptestp =
+ (wxPoint *)malloc((2 * (nPointReduced + 2)) * sizeof(wxPoint));
+ GetPointPixArray(rzRules, pReduced, ptestp, nPointReduced);
+ free(pReduced);
+
+ draw_lc_poly(m_pdc, color, w, ptestp, NULL, nPointReduced, sym_len,
+ 0, sym_factor, rules->razRule);
+ free(ptestp);
+ }
+
+ nls = 0;
+ index = 0;
+ idouble = 0;
+ lp = wxPoint(0, 0);
+ }
+
+ } else {
+ // no more segments, so render what is available
+ if (nls) {
+ wxPoint2DDouble *pReduced = 0;
+ int nPointReduced = reduceLOD(LOD, nls, pdp, &pReduced, NULL, NULL);
+
+ wxPoint *ptestp =
+ (wxPoint *)malloc((2 * (max_points + 2)) * sizeof(wxPoint));
+ GetPointPixArray(rzRules, pReduced, ptestp, nPointReduced);
+ free(pReduced);
+
+ draw_lc_poly(m_pdc, color, w, ptestp, NULL, nPointReduced, sym_len, 0,
+ sym_factor, rules->razRule);
+ free(ptestp);
+ }
+ }
+
+ ls = ls->next;
+ }
+
+ free(ptp);
+ free(pdp);
+ }
+ return 1;
+}
+
+int s52plib::BuildLCSymbolTexture(char *str, char *col, wxPoint &r,
+ wxPoint &pivot, wxPoint origin, float scale,
+ float sym_len, float sym_height) {
+ GLint texture = 0;
+
+ double xscale = 1.0;
+
+ int width = sym_len;
+ width *= 2 * xscale; // Grow the drawing bitmap to allow for rotation
+ // of symbols with highly offset pivot points
+ // width = (int)(width / fsf);
+
+ int height = sym_height;
+ height *= 2 * xscale;
+ // height = (int)(height / fsf);
+
+ // width /= fsf;
+ // height /= fsf;
+
+ // Expand width and height to next larger POT
+ int xp = width, yp = height;
+ int width_pot, height_pot;
+ if (((xp != 0) && !(xp & (xp - 1)))) // detect POT
+ width_pot = xp;
+ else {
+ int a = 0;
+ while (xp) {
+ xp = xp >> 1;
+ a++;
+ }
+ width_pot = 1 << a;
+ }
+
+ if (((yp != 0) && !(yp & (yp - 1)))) // detect POT
+ height_pot = yp;
+ else {
+ int a = 0;
+ while (yp) {
+ yp = yp >> 1;
+ a++;
+ }
+ height_pot = 1 << a;
+ }
+
+ // Render the symbol on wxMemoryDC
+ HPGL->SetVP(&vp_plib);
+ wxMemoryDC mdc;
+
+#if (defined(__WXMSW__) || defined(__WXMAC__) || defined(ANDROID))
+ wxBitmap bmp(width, height, 24);
+ mdc.SelectObject(bmp);
+ mdc.SetBackground(wxBrush(m_unused_wxColor));
+ mdc.Clear();
+#else
+ wxBitmap bmp(width, height, 32);
+ mdc.SelectObject(bmp);
+#endif
+
+ HPGL->SetTargetDC(&mdc);
+ wxPoint2DDouble rt(0, 0);
+ HPGL->Render(str, col, rt, origin, wxPoint(0, 0), 1.0, 0, false);
+ mdc.SelectObject(wxNullBitmap);
+
+ wxImage image = bmp.ConvertToImage();
+ int ws = image.GetWidth(), hs = image.GetHeight();
+
+ unsigned char *source = image.GetData();
+ unsigned char *alpha = image.GetAlpha();
+
+ // Correct for Android bug
+ // 24 bit image should have no alpha channel data
+#ifdef ANDROID
+ alpha = nullptr;
+#endif
+
+ unsigned char mr, mg, mb;
+ if (!alpha) {
+ image.SetMaskColour(m_unused_wxColor.Red(), m_unused_wxColor.Green(),
+ m_unused_wxColor.Blue());
+ if (!alpha && !image.GetOrFindMaskColour(&mr, &mg, &mb))
+ printf("trying to use mask to draw a bitmap without alpha or mask\n");
+ }
+
+ unsigned char *pRGBA = (unsigned char *)calloc(1, 4 * width_pot * height_pot);
+ unsigned char *dest = pRGBA;
+
+ if (source) {
+ for (int y = 0; y < hs; y++) {
+ for (int x = 0; x < ws; x++) {
+ int offs = (y * ws + x);
+ int offd = (y * width_pot + x);
+
+ unsigned int r = source[offs * 3 + 0];
+ unsigned int g = source[offs * 3 + 1];
+ unsigned int b = source[offs * 3 + 2];
+
+ dest[offd * 4 + 0] = source[offs * 3 + 0];
+ dest[offd * 4 + 1] = source[offs * 3 + 1];
+ dest[offd * 4 + 2] = source[offs * 3 + 2];
+ unsigned char a = alpha
+ ? alpha[offs]
+ : ((r == mr) && (g == mg) && (b == mb) ? 0 : 255);
+ dest[offd * 4 + 3] = a;
+ }
+ }
+ }
+
+ glEnable(GL_TEXTURE_2D);
+ GLuint texobj;
+ glGenTextures(1, &texobj);
+ glBindTexture(GL_TEXTURE_2D, texobj);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER,
+ GL_NEAREST /*GL_LINEAR*/);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
+
+ glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width_pot, height_pot, 0, GL_RGBA,
+ GL_UNSIGNED_BYTE, pRGBA);
+
+ free(pRGBA);
+ glDisable(GL_TEXTURE_2D);
+ glBindTexture(GL_TEXTURE_2D, 0);
+ texture = texobj;
+
+ return texture;
+}
+
+// Render Line Complex Polyline
+void s52plib::RenderTex(char *str, char *col, wxPoint &r, wxPoint &pivot,
+ wxPoint origin, float scale, double rot_angle,
+ float sym_len, float sym_height, float seg_len) {
+#ifdef ocpnUSE_GL
+ int symbol_texture = 0;
+ // Build the hash key
+ std::string key;
+ key += str; // HPGL Render string
+ key += col; // color
+
+ // Check to see it the requested symbol texture is in the cache
+ auto search = lc_vector_symbol_cache.find(key);
+ if (search != lc_vector_symbol_cache.end())
+ symbol_texture = search->second;
+ else {
+ symbol_texture = BuildLCSymbolTexture(str, col, r, pivot, origin, scale,
+ sym_len, sym_height);
+ lc_vector_symbol_cache[key] = symbol_texture;
+ }
+
+ // Render the texture
+ glEnable(GL_TEXTURE_2D);
+ glBindTexture(GL_TEXTURE_2D, symbol_texture);
+
+ int w;
+ int h;
+
+ // Get the texture dimensions
+ // TODO Could save these dimensions as a rule parm, faster.
+ glGetTexLevelParameteriv(GL_TEXTURE_2D, 0, GL_TEXTURE_WIDTH, &w);
+ glGetTexLevelParameteriv(GL_TEXTURE_2D, 0, GL_TEXTURE_HEIGHT, &h);
+
+ float uv[8];
+ float coords[8];
+
+ float xa = wxMin(sym_len, seg_len);
+
+ // Note swizzle of points to allow TRIANGLE_STRIP drawing
+ // normal uv
+ uv[0] = 0;
+ uv[1] = 0;
+ uv[2] = xa / w;
+ uv[3] = 0;
+ uv[6] = xa / w;
+ uv[7] = sym_height / h;
+ uv[4] = 0;
+ uv[5] = sym_height / h;
+
+ // pixels
+ coords[0] = 0;
+ coords[1] = 0;
+ coords[2] = xa;
+ coords[3] = 0;
+ coords[6] = xa;
+ coords[7] = sym_height;
+ coords[4] = 0;
+ coords[5] = sym_height;
+
+ if (pCtexture_2D_shader_program[0]) {
+ pCtexture_2D_shader_program[0]->Bind();
+ glEnable(GL_BLEND);
+
+ // Select the active texture unit.
+ glActiveTexture(GL_TEXTURE0);
+
+ pCtexture_2D_shader_program[0]->SetUniform1i("uTex", 0);
+
+ // Disable VBO's (vertex buffer objects) for attributes.
+ glBindBuffer(GL_ARRAY_BUFFER, 0);
+ glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
+
+ pCtexture_2D_shader_program[0]->SetAttributePointerf("position", coords);
+ pCtexture_2D_shader_program[0]->SetAttributePointerf("aUV", uv);
+
+ // Rotate
+ mat4x4 I, Q;
+ mat4x4_identity(I);
+
+ double yadj = (pivot.y - origin.y) * canvas_pix_per_mm / 100;
+ double xadj = (pivot.x - origin.x) * canvas_pix_per_mm / 100;
+
+ mat4x4_translate_in_place(I, r.x, r.y, 0);
+ // mat4x4_translate_in_place(I, xadj, yadj, 0);
+ // mat4x4_translate_in_place(I, -xadj, -yadj, 0);
+ mat4x4_rotate_Z(Q, I, rot_angle * PI / 180.);
+ mat4x4_translate_in_place(Q, -xadj, -yadj, 0);
+
+ pCtexture_2D_shader_program[0]->SetUniformMatrix4fv("TransformMatrix",
+ (GLfloat *)Q);
+
+ // Perform the actual drawing.
+ glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
+
+ // Restore the per-object transform to Identity Matrix
+ mat4x4 IM;
+ mat4x4_identity(IM);
+ pCtexture_2D_shader_program[0]->SetUniformMatrix4fv("TransformMatrix",
+ (GLfloat *)IM);
+
+ // Clean up the GL state
+ pCtexture_2D_shader_program[0]->UnBind();
+ }
+#endif
+}
+
+// Render Line Complex Polyline
+
+void s52plib::draw_lc_poly(wxDC *pdc, wxColor &color, int width, wxPoint *ptp,
+ int *mask, int npt, float sym_len, float sym_height,
+ float sym_factor, Rule *draw_rule) {
+ if (npt < 2) return;
+
+ wxPoint2DDouble r;
+
+ bool cw = false; // Winding is always CCW
+
+ // Get a true pixel clipping/bounding box from the vp
+ wxPoint pbb = GetPixFromLL(vp_plib.clat, vp_plib.clon);
+ int xmin_ = pbb.x - vp_plib.rv_rect.width / 2;
+ int xmax_ = xmin_ + vp_plib.rv_rect.width;
+ int ymin_ = pbb.y - vp_plib.rv_rect.height / 2;
+ int ymax_ = ymin_ + vp_plib.rv_rect.height;
+
+ int x0, y0, x1, y1;
+
+ if (pdc) {
+ wxPen *pthispen =
+ wxThePenList->FindOrCreatePen(color, width, wxPENSTYLE_SOLID);
+ m_pdc->SetPen(*pthispen);
+
+ int start_seg = 0;
+ int end_seg = npt - 1;
+ int inc = 1;
+
+ if (cw) {
+ start_seg = npt - 1;
+ end_seg = 0;
+ inc = -1;
+ }
+
+ float dx, dy, seg_len, theta;
+
+ bool done = false;
+ ClipResult res;
+ int iseg = start_seg;
+ while (!done) {
+ // Do not bother with segments that are invisible
+
+ x0 = ptp[iseg].x;
+ y0 = ptp[iseg].y;
+ x1 = ptp[iseg + inc].x;
+ y1 = ptp[iseg + inc].y;
+
+ // Also, segments marked (by mask) as invisible
+ if (mask && !mask[iseg]) goto next_seg_dc;
+
+ res = cohen_sutherland_line_clip_i(&x0, &y0, &x1, &y1, xmin_, xmax_,
+ ymin_, ymax_);
+
+ if (res == Invisible) goto next_seg_dc;
+
+ dx = ptp[iseg + inc].x - ptp[iseg].x;
+ dy = ptp[iseg + inc].y - ptp[iseg].y;
+ seg_len = sqrt(dx * dx + dy * dy);
+ theta = atan2f(dy, dx);
+
+ if (seg_len >= 1.0) {
+ if (seg_len <= sym_len * sym_factor) {
+ int xst1 = ptp[iseg].x;
+ int yst1 = ptp[iseg].y;
+ float xst2, yst2;
+ if (seg_len >= sym_len) {
+ xst2 = xst1 + (sym_len * dx / seg_len);
+ yst2 = yst1 + (sym_len * dy / seg_len);
+ } else {
+ xst2 = ptp[iseg + inc].x;
+ yst2 = ptp[iseg + inc].y;
+ }
+
+ pdc->DrawLine(xst1, yst1, (wxCoord)floor(xst2), (wxCoord)floor(yst2));
+ }
+
+ else {
+ float s = 0;
+ float xs = ptp[iseg].x;
+ float ys = ptp[iseg].y;
+
+ while (s + (sym_len * sym_factor) < seg_len) {
+ r.m_x = (int)xs;
+ r.m_y = (int)ys;
+ char *str = draw_rule->vector.LVCT;
+ char *col = draw_rule->colRef.LCRF;
+ wxPoint pivot(draw_rule->pos.line.pivot_x.LICL,
+ draw_rule->pos.line.pivot_y.LIRW);
+
+ HPGL->SetTargetDC(pdc);
+ HPGL->SetVP(&vp_plib);
+ HPGL->Render(str, col, r, pivot, pivot, 1.0, theta * 180. / PI,
+ false);
+
+ xs += sym_len * dx / seg_len * sym_factor;
+ ys += sym_len * dy / seg_len * sym_factor;
+ s += sym_len * sym_factor;
+ }
+
+ pdc->DrawLine((int)xs, (int)ys, ptp[iseg + inc].x, ptp[iseg + inc].y);
+ }
+ }
+ next_seg_dc:
+ iseg += inc;
+ if (iseg == end_seg) done = true;
+
+ } // while
+ } // if pdc
+ else // opengl
+ {
+ // Set up the color
+#ifdef ocpnUSE_GL
+ // Adjust line width up a bit, to improve render quality for
+ // GL_BLEND/GL_LINE_SMOOTH
+ float awidth = wxMax(m_GLMinCartographicLineWidth, (float)width * 0.7);
+ awidth = wxMax(awidth, 1.5);
+ glLineWidth(awidth);
+#endif
+
+ int start_seg = 0;
+ int end_seg = npt - 1;
+ int inc = 1;
+
+ if (cw) {
+ start_seg = npt - 1;
+ end_seg = 0;
+ inc = -1;
+ }
+
+ float dx, dy, seg_len, theta;
+ ClipResult res;
+
+ bool done = false;
+ int iseg = start_seg;
+ while (!done) {
+ // Do not bother with segments that are invisible
+
+ x0 = ptp[iseg].x;
+ y0 = ptp[iseg].y;
+ x1 = ptp[iseg + inc].x;
+ y1 = ptp[iseg + inc].y;
+
+ // Also, segments marked (by mask) as invisible
+ if (mask && !mask[iseg]) goto next_seg;
+
+ res = cohen_sutherland_line_clip_i(&x0, &y0, &x1, &y1, xmin_, xmax_,
+ ymin_, ymax_);
+
+ if (res == Invisible) goto next_seg;
+
+ dx = ptp[iseg + inc].x - ptp[iseg].x;
+ dy = ptp[iseg + inc].y - ptp[iseg].y;
+ seg_len = sqrt(dx * dx + dy * dy);
+
+ if (seg_len >= 1.0) {
+ if (seg_len <= sym_len * sym_factor) {
+ int xst1 = ptp[iseg].x;
+ int yst1 = ptp[iseg].y;
+ float xst2, yst2;
+
+ if (seg_len >= sym_len) {
+ xst2 = xst1 + (sym_len * dx / seg_len);
+ yst2 = yst1 + (sym_len * dy / seg_len);
+ } else {
+ xst2 = ptp[iseg + inc].x;
+ yst2 = ptp[iseg + inc].y;
+ }
+
+ // Enable anti-aliased lines, at best quality
+#ifndef __OCPN__ANDROID__
+ glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+ glEnable(GL_BLEND);
+
+ if (m_GLLineSmoothing) {
+ glEnable(GL_LINE_SMOOTH);
+ glHint(GL_LINE_SMOOTH_HINT, GL_NICEST);
+ }
+#endif
+
+#ifdef ocpnUSE_GL
+ CGLShaderProgram *shader =
+ pCcolor_tri_shader_program[0 /*GetCanvasIndex()*/];
+ shader->Bind();
+
+ glBindBuffer(GL_ARRAY_BUFFER, 0);
+ glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
+
+ float colorv[4];
+ colorv[0] = color.Red() / float(256);
+ colorv[1] = color.Green() / float(256);
+ colorv[2] = color.Blue() / float(256);
+ colorv[3] = 1.0; // transparency;
+
+ shader->SetUniform4fv("color", colorv);
+
+ float pts[4];
+ pts[0] = xst1;
+ pts[1] = yst1;
+ pts[2] = xst2;
+ pts[3] = yst2;
+
+ GLint pos = shader->getAttributeLocation("position");
+ glVertexAttribPointer(pos, 2, GL_FLOAT, GL_FALSE, 2 * sizeof(float),
+ pts);
+ glEnableVertexAttribArray(pos);
+
+ glDrawArrays(GL_LINES, 0, 2);
+
+ shader->UnBind();
+
+ glDisable(GL_LINE_SMOOTH);
+ glDisable(GL_BLEND);
+#endif
+ } else {
+ float s = 0;
+ float xs = ptp[iseg].x;
+ float ys = ptp[iseg].y;
+
+ while (s + (sym_len * sym_factor) < seg_len) {
+ r.m_x = xs;
+ r.m_y = ys;
+ char *str = draw_rule->vector.LVCT;
+ char *col = draw_rule->colRef.LCRF;
+ wxPoint pivot(draw_rule->pos.line.pivot_x.LICL,
+ draw_rule->pos.line.pivot_y.LIRW);
+
+ HPGL->SetTargetOpenGl();
+ HPGL->SetVP(&vp_plib);
+ theta = atan2f(dy, dx);
+ HPGL->Render(str, col, r, pivot, pivot, 1.0, theta * 180. / PI,
+ false);
+
+ xs += sym_len * dx / seg_len * sym_factor;
+ ys += sym_len * dy / seg_len * sym_factor;
+ s += sym_len * sym_factor;
+ }
+#ifndef __OCPN__ANDROID__
+ glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+ glEnable(GL_BLEND);
+
+ if (m_GLLineSmoothing) {
+ glEnable(GL_LINE_SMOOTH);
+ glHint(GL_LINE_SMOOTH_HINT, GL_NICEST);
+ }
+
+#endif
+
+#ifdef ocpnUSE_GL
+ CGLShaderProgram *shader =
+ pCcolor_tri_shader_program[0 /*GetCanvasIndex()*/];
+ shader->Bind();
+
+ glBindBuffer(GL_ARRAY_BUFFER, 0);
+ glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
+
+ float colorv[4];
+ colorv[0] = color.Red() / float(256);
+ colorv[1] = color.Green() / float(256);
+ colorv[2] = color.Blue() / float(256);
+ colorv[3] = 1.0; // transparency;
+
+ shader->SetUniform4fv("color", colorv);
+
+ float pts[4];
+ pts[0] = xs;
+ pts[1] = ys;
+ pts[2] = ptp[iseg + inc].x;
+ pts[3] = ptp[iseg + inc].y;
+
+ GLint pos = shader->getAttributeLocation("position");
+ glVertexAttribPointer(pos, 2, GL_FLOAT, GL_FALSE, 2 * sizeof(float),
+ pts);
+ glEnableVertexAttribArray(pos);
+
+ glDrawArrays(GL_LINES, 0, 2);
+ glDisableVertexAttribArray(pos);
+ shader->UnBind();
+
+ glDisable(GL_LINE_SMOOTH);
+ glDisable(GL_BLEND);
+#endif
+ }
+ }
+ next_seg:
+ iseg += inc;
+ if (iseg == end_seg) done = true;
+ } // while
+
+ } // opengl
+}
+
+void s52plib::draw_lc_poly_texture(wxDC *pdc, wxColor &color, int width,
+ wxPoint2DDouble *ptp, int *mask, int npt,
+ float sym_len, float sym_height,
+ float sym_factor, Rule *draw_rule,
+ bool winding_cw) {
+ if (npt < 2) return;
+
+ wxPoint r;
+
+ // We calculate the winding direction of the poly
+ // in order to know which side to draw symbol on
+ double dfSum = 0.0;
+
+ for (int iseg = 0; iseg < npt - 1; iseg++) {
+ dfSum += ptp[iseg].m_x * ptp[iseg + 1].m_y - ptp[iseg].m_y * ptp[iseg + 1].m_x;
+ }
+ dfSum += ptp[npt - 1].m_x * ptp[0].m_y - ptp[npt - 1].m_y * ptp[0].m_x;
+
+ bool cw = winding_cw; //dfSum < 0.;
+
+ // Get a true pixel clipping/bounding box from the vp
+ wxPoint pbb = GetPixFromLL(vp_plib.clat, vp_plib.clon);
+ int xmin_ = pbb.x - vp_plib.rv_rect.width / 2;
+ int xmax_ = xmin_ + vp_plib.rv_rect.width;
+ int ymin_ = pbb.y - vp_plib.rv_rect.height / 2;
+ int ymax_ = ymin_ + vp_plib.rv_rect.height;
+
+ int x0, y0, x1, y1;
+
+#ifdef ocpnUSE_GL
+ // Adjust line width up a bit, to improve render quality for
+ // GL_BLEND/GL_LINE_SMOOTH
+ float awidth = wxMax(m_GLMinCartographicLineWidth, (float)width * 0.7);
+ awidth = wxMax(awidth, 1.5);
+ glLineWidth(awidth);
+#endif
+
+ int start_seg = 0;
+ int end_seg = npt - 1;
+ int inc = 1;
+
+ if (cw) {
+ start_seg = npt - 1;
+ end_seg = 0;
+ inc = -1;
+ }
+
+ float dx, dy, seg_len, theta;
+ ClipResult res;
+
+ bool done = false;
+ int iseg = start_seg;
+ while (!done) {
+ // Do not bother with segments that are invisible
+
+ x0 = ptp[iseg].m_x;
+ y0 = ptp[iseg].m_y;
+ x1 = ptp[iseg + inc].m_x;
+ y1 = ptp[iseg + inc].m_y;
+
+ // Also, segments marked (by mask) as invisible
+ if (mask && !mask[iseg]) goto next_seg;
+
+ res = cohen_sutherland_line_clip_i(&x0, &y0, &x1, &y1, xmin_, xmax_, ymin_,
+ ymax_);
+
+ if (res == Invisible) goto next_seg;
+
+ dx = ptp[iseg + inc].m_x - ptp[iseg].m_x;
+ dy = ptp[iseg + inc].m_y - ptp[iseg].m_y;
+ seg_len = sqrt(dx * dx + dy * dy);
+ theta = atan2f(dy, dx) * 180 / PI;
+ printf("Theta: %g\n", theta);
+
+ if (seg_len >= 1.0) {
+ // Render symbols to fill seg_len
+ float s = 0;
+ float xs = ptp[iseg].m_x;
+ float ys = ptp[iseg].m_y;
+ char *str = draw_rule->vector.LVCT;
+ char *col = draw_rule->colRef.LCRF;
+ wxPoint pivot(draw_rule->pos.line.pivot_x.LICL,
+ draw_rule->pos.line.pivot_y.LIRW);
+ wxPoint origin(draw_rule->pos.line.bnbox_x.LBXC,
+ draw_rule->pos.line.bnbox_y.LBXR);
+
+ while (1) {
+ r.x = (int)xs;
+ r.y = (int)ys;
+
+ HPGL->SetVP(&vp_plib);
+ RenderTex(str, col, r, pivot, origin, 1.0, theta, sym_len,
+ sym_height, seg_len - s);
+
+ xs += sym_len * dx / seg_len * sym_factor;
+ ys += sym_len * dy / seg_len * sym_factor;
+ s += sym_len * sym_factor;
+ if (s > seg_len) break;
+ }
+ }
+ next_seg:
+ iseg += inc;
+ if (iseg == end_seg) done = true;
+ } // while
+}
+
+// Multipoint Sounding
+int s52plib::RenderMPS(ObjRazRules *rzRules, Rules *rules) {
+ if (!m_bShowSoundg) return 0;
+
+ if (m_bUseSCAMIN) {
+ if (vp_plib.chart_scale > rzRules->obj->Scamin) return 0;
+ }
+
+ int npt = rzRules->obj->npt;
+
+ // this should never happen
+ // But it seems that some PlugIns clear the mps rules without resetting the CS
+ // state machine So fix it
+ if (rzRules->obj->bCS_Added && !rzRules->mps) rzRules->obj->bCS_Added = false;
+
+ // Build the cached rules list if necessary
+ if (!rzRules->obj->bCS_Added) {
+ ObjRazRules point_rzRules;
+ point_rzRules = *rzRules; // take a copy of attributes, etc
+
+ S57Obj point_obj;
+ point_obj = *(rzRules->obj);
+ point_obj.bIsClone = true;
+ point_rzRules.obj = &point_obj;
+
+ Rules *ru_cs = StringToRules(_T ( "CS(SOUNDG03;" ));
+
+ wxPoint2DDouble p;
+ double *pd = rzRules->obj->geoPtz; // the SM points
+ double *pdl = rzRules->obj->geoPtMulti; // and corresponding lat/lon
+
+ mps_container *pmps = (mps_container *)calloc(sizeof(mps_container), 1);
+ pmps->cs_rules = new ArrayOfRules;
+ rzRules->mps = pmps;
+
+ for (int ip = 0; ip < npt; ip++) {
+ double east = *pd++;
+ double nort = *pd++;
+ double depth = *pd++;
+
+ point_obj.x = east;
+ point_obj.y = nort;
+ point_obj.z = depth;
+
+ double lon = *pdl++;
+ double lat = *pdl++;
+ point_obj.BBObj.Set(lat, lon, lat, lon);
+ point_obj.BBObj.Invalidate();
+
+ char *rule_str1 = RenderCS(&point_rzRules, ru_cs);
+ wxString cs_string(rule_str1, wxConvUTF8);
+ free(rule_str1);
+
+ Rules *rule_chain = StringToRules(cs_string);
+
+ rzRules->mps->cs_rules->Add(rule_chain);
+ }
+
+ DestroyRulesChain(ru_cs);
+ rzRules->obj->bCS_Added = 1; // mark the object
+ }
+
+ double *pdl = rzRules->obj->geoPtMulti; // and corresponding lat/lon
+ double *pd = rzRules->obj->geoPtz; // the SM points
+
+ // FIXME (plib)
+ // Need to un-rotate sounding rendering
+ // We need a private unrotated copy of the Viewport
+ // VPointCompat vp_local = *vp;
+ // vp_local.SetRotationAngle(0.);
+
+ SetupSoundingFont();
+ // Measure the size of a character
+ int charWidth, charHeight;
+ wxScreenDC sdc;
+ sdc.GetTextExtent("0", &charWidth, &charHeight, NULL, NULL, m_soundFont);
+ wxRect soundBox = wxRect(0, 0, charWidth * 4, charHeight * 3 / 2);
+
+ // We need a pixel bounding rectangle of the passed ViewPort.
+ // Very important for partial screen renders, as with dc mode pans or OpenGL
+ // FBO operation.
+
+ wxPoint cr0 = GetPixFromLLROT(GetReducedBBox().GetMaxLat(),
+ GetReducedBBox().GetMinLon(), 0);
+ wxPoint cr1 = GetPixFromLLROT(GetReducedBBox().GetMinLat(),
+ GetReducedBBox().GetMaxLon(), 0);
+ wxRect clip_rect(cr0, cr1);
+
+ double box_margin =
+ wxMax(fabs(GetBBox().GetMaxLon() - GetBBox().GetMinLon()),
+ fabs(GetBBox().GetMaxLat() - GetBBox().GetMinLat()));
+ LLBBox screen_box = GetBBox();
+
+ std::vector sym_array;
+
+ for (int ip = 0; ip < npt; ip++) {
+ double lon = *pdl++;
+ double lat = *pdl++;
+
+ double east = *pd++;
+ double nort = *pd++;
+ double depth = *pd++;
+
+ // Make a rough inclusion test from lat/lon
+ // onto the screen coordinates, enlarged a bit
+ if (!screen_box.ContainsMarge(lat, lon, box_margin)) continue;
+
+ // Some simple inclusion tests
+ wxPoint2DDouble r = GetDoublePixFromLLROT(lat, lon, 0);
+ if ((r.m_x == INVALID_COORD) || (r.m_y == INVALID_COORD)) continue;
+
+ // Use measured symbol size
+ wxRect rr(r.m_x - (soundBox.width / 2), r.m_y - (soundBox.height / 2),
+ soundBox.width, soundBox.height);
+
+ // After all the setup, the render inclusion test is trivial....
+ if (!clip_rect.Intersects(rr)) continue;
+
+ double angle = 0;
+ if (depth < 0) int yyp = 4;
+
+ Rules *rules = rzRules->mps->cs_rules->Item(ip);
+ bool bColorSet = false;
+ wxColor symColor;
+ GetGlobalColor(_T("SNDG2"), &symColor);
+
+ while (rules) {
+ // Render a raster or vector symbol, as specified by LUP rules
+ if (rules->razRule->definition.SYDF == 'V') {
+ // On OpenGL, arrange to render the drying height "underline" symbol as
+ // un-rotated.
+ double dryAngle = 0;
+ if (!m_pdc && !strncmp(rules->razRule->name.SYNM, "SOUNDSA1", 8))
+ dryAngle = -vp_plib.rotation * 180. / PI;
+ // FIXME (dave) drying height symbol should be wider/bolder.
+
+ RenderHPGL(rzRules, rules->razRule, r, dryAngle,
+ m_SoundingsScaleFactor);
+ } else if (rules->razRule->definition.SYDF == 'R') {
+ // Parse the first rule to determine the color
+ if (!bColorSet) {
+ char symColorT = rules->razRule->name.SYNM[5];
+ if (symColorT == 'G') GetGlobalColor(_T("SNDG1"), &symColor);
+ bColorSet = true;
+ }
+
+ if (!strncmp(rules->razRule->name.SYNM, "SOUNDGC2", 8))
+ RenderRasterSymbol(rzRules, rules->razRule, r, angle);
+ else {
+ // Add this sounding to the array, for later bulk rendering
+ SoundingSym sym;
+ sym.prule = rules->razRule;
+ sym.r = r;
+ sym.color_RGB = symColor.GetRGB();
+ sym_array.push_back(sym);
+ }
+ }
+
+ // Debug
+ // if(m_pdc){
+ // m_pdc->SetPen(wxPen(*wxRED, 1));
+ // m_pdc->SetBrush(wxBrush(*wxRED, wxTRANSPARENT));
+ // m_pdc->DrawRectangle(rr);
+ //}
+
+ rules = rules->next;
+ }
+ }
+
+ // Render the "normal" soundings in bulk, to optimize GL shader performance
+ if (sym_array.size()) RenderMPSArray(rzRules, sym_array);
+
+ return 1;
+}
+
+int s52plib::RenderCARC(ObjRazRules *rzRules, Rules *rules) {
+#ifdef ocpnUSE_GL
+ if (m_useGLSL)
+ return RenderCARC_GLSL(rzRules, rules);
+ else
+#endif
+ return RenderCARC_VBO(rzRules, rules);
+}
+
+#ifdef ocpnUSE_GL
+int s52plib::RenderCARC_GLSL(ObjRazRules *rzRules, Rules *rules) {
+ // glDisable( GL_SCISSOR_TEST );
+
+ char *str = (char *)rules->INSTstr;
+ // extract the parameters from the string
+ // And creating a unique string hash as we go
+ wxString inst(str, wxConvUTF8);
+
+ wxStringTokenizer tkz(inst, _T ( ",;" ));
+
+ // outline color
+ wxString outline_color = tkz.GetNextToken();
+
+ // outline width
+ wxString slong = tkz.GetNextToken();
+ long outline_width;
+ slong.ToLong(&outline_width);
+
+ // arc color
+ wxString arc_color = tkz.GetNextToken();
+
+ // arc width
+ slong = tkz.GetNextToken();
+ long arc_width;
+ slong.ToLong(&arc_width);
+
+ // sectr1
+ slong = tkz.GetNextToken();
+ double sectr1;
+ slong.ToDouble(§r1);
+
+ // sectr2
+ slong = tkz.GetNextToken();
+ double sectr2;
+ slong.ToDouble(§r2);
+
+ // arc radius
+ slong = tkz.GetNextToken();
+ long radius;
+ slong.ToLong(&radius);
+
+ // sector radius
+ slong = tkz.GetNextToken();
+ long sector_radius;
+ slong.ToLong(§or_radius);
+
+ // Center point
+ wxPoint2DDouble dr;
+ GetDoublePointPixSingle(rzRules, rzRules->obj->y, rzRules->obj->x, &dr);
+
+ if (radius > m_display_size_mm / 10) {
+ double fact = radius / (m_display_size_mm / 10);
+ radius /= fact;
+ sector_radius /= fact;
+ arc_width /= fact;
+ arc_width = wxMax(arc_width, 1);
+ }
+
+ // radius scaled to display
+ float rad = radius * canvas_pix_per_mm;
+ float sec_rad = sector_radius * canvas_pix_per_mm;
+
+ float arcw = arc_width * canvas_pix_per_mm;
+ // On larger screens, make the arc_width 1.0 mm
+ if (m_display_size_mm > 200) // 200 mm, about 8 inches
+ arcw = canvas_pix_per_mm;
+
+ // Adjust size
+ // Some lights have no SCAMIN attribute. e.g. cm93
+ // This causes display congestion at small viewing scales, since the objects
+ // are rendered at fixed pixel dimensions from the LUP rules. As a
+ // correction, the idea is to not allow the rendered symbol to be larger than
+ // "X" meters on the chart.
+ // and scale it down when rendered if necessary.
+
+ float xscale = 1.0;
+ if (rzRules->obj->Scamin > 1e8) { // huge (unset) SCAMIN)
+ float radius_meters_target = 1000;
+
+ float radius_meters = (radius * canvas_pix_per_mm) / vp_plib.view_scale_ppm;
+
+ xscale = radius_meters_target / radius_meters;
+ xscale = wxMin(xscale, 1.0);
+ xscale = wxMax(.5, xscale);
+
+ rad *= xscale;
+ arcw *= xscale;
+ arcw = wxMin(arcw, rad / 10);
+ sec_rad *= xscale;
+ }
+
+ // Enable anti-aliased lines, at best quality
+ glEnable(GL_BLEND);
+
+ // Rotate the center point about vp center
+ wxPoint2DDouble point = dr;
+ double sin_rot = sin(vp_plib.rotation);
+ double cos_rot = cos(vp_plib.rotation);
+
+ double xp = ((point.m_x - vp_plib.pix_width / 2) * cos_rot) -
+ ((point.m_y - vp_plib.pix_height / 2) * sin_rot);
+ double yp = ((point.m_x - vp_plib.pix_width / 2) * sin_rot) +
+ ((point.m_y - vp_plib.pix_height / 2) * cos_rot);
+
+ point.m_x = xp + vp_plib.pix_width / 2;
+ point.m_y = yp + vp_plib.pix_height / 2;
+
+ float rad_fluff = rad + 20;
+ float coords[8];
+ coords[0] = -rad_fluff;
+ coords[1] = rad_fluff;
+ coords[2] = rad_fluff;
+ coords[3] = rad_fluff;
+ coords[4] = -rad_fluff;
+ coords[5] = -rad_fluff;
+ coords[6] = rad_fluff;
+ coords[7] = -rad_fluff;
+
+ glUseProgram(S52ring_shader_program);
+
+ // Get pointers to the attributes in the program.
+ GLint mPosAttrib = glGetAttribLocation(S52ring_shader_program, "aPos");
+
+ // Disable VBO's (vertex buffer objects) for attributes.
+ glBindBuffer(GL_ARRAY_BUFFER, 0);
+ glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
+
+ glVertexAttribPointer(mPosAttrib, 2, GL_FLOAT, GL_FALSE, 0, coords);
+ glEnableVertexAttribArray(mPosAttrib);
+
+ // Circle radius
+ GLint radiusloc =
+ glGetUniformLocation(S52ring_shader_program, "circle_radius");
+ glUniform1f(radiusloc, rad);
+
+ // Circle center point, physical
+ GLint centerloc =
+ glGetUniformLocation(S52ring_shader_program, "circle_center");
+ float ctrv[2];
+ ctrv[0] = point.m_x;
+ ctrv[1] = vp_plib.pix_height - point.m_y;
+ glUniform2fv(centerloc, 1, ctrv);
+
+ // Border color
+ float bcolorv[4];
+ bcolorv[0] = 0;
+ bcolorv[1] = 0;
+ bcolorv[2] = 0;
+ bcolorv[3] = 1.0;
+
+ GLint ring_colloc =
+ glGetUniformLocation(S52ring_shader_program, "ring_color");
+ glUniform4fv(ring_colloc, 1, bcolorv);
+
+ // Border Width
+ GLint ringWidthloc =
+ glGetUniformLocation(S52ring_shader_program, "ring_width");
+ glUniform1f(ringWidthloc, arcw + (1 * outline_width));
+
+ // Visible sectors, rotated to vp orientation
+ float sr1 = sectr1 + (vp_plib.rotation * 180 / PI);
+ if (sr1 > 360.) sr1 -= 360.;
+ float sr2 = sectr2 + (vp_plib.rotation * 180 / PI);
+ if (sr2 > 360.) sr2 -= 360.;
+
+ float sb, se;
+ if (sr2 > sr1) {
+ sb = sr1;
+ se = sr2;
+ } else {
+ sb = sr1;
+ se = sr2 + 360;
+ }
+
+ // Shader can handle angles > 360.
+ if ((sb < 0) || (se < 0)) {
+ sb += 360.;
+ se += 360.;
+ }
+
+ GLint sector1loc = glGetUniformLocation(S52ring_shader_program, "sector_1");
+ glUniform1f(sector1loc, (sb * PI / 180.));
+ GLint sector2loc = glGetUniformLocation(S52ring_shader_program, "sector_2");
+ glUniform1f(sector2loc, (se * PI / 180.));
+
+ // Rotate and translate
+ mat4x4 I;
+ mat4x4_identity(I);
+
+ mat4x4_translate_in_place(I, dr.m_x, dr.m_y, 0);
+
+ GLint matloc =
+ glGetUniformLocation(S52ring_shader_program, "TransformMatrix");
+ glUniformMatrix4fv(matloc, 1, GL_FALSE, (const GLfloat *)I);
+
+ // Perform the actual drawing.
+ glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
+
+ // Now draw color ring
+ // Circle color
+ wxColour colorb = getwxColour(arc_color);
+ float colorv[4];
+ colorv[0] = colorb.Red() / float(256);
+ colorv[1] = colorb.Green() / float(256);
+ colorv[2] = colorb.Blue() / float(256);
+ colorv[3] = 1.0;
+ glUniform4fv(ring_colloc, 1, colorv);
+
+ // arc Width
+ glUniform1f(ringWidthloc, arcw);
+
+ // Perform the actual drawing.
+ glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
+
+ // Restore the per-object transform to Identity Matrix
+ mat4x4 IM;
+ mat4x4_identity(IM);
+ GLint matlocf =
+ glGetUniformLocation(S52ring_shader_program, "TransformMatrix");
+ glUniformMatrix4fv(matlocf, 1, GL_FALSE, (const GLfloat *)IM);
+ glUseProgram(0);
+ glDisableVertexAttribArray(mPosAttrib);
+
+ // Draw the sector legs directly on the target DC
+ if (sector_radius > 0) {
+ double leg_len = sec_rad;
+
+ wxDash dash1[2];
+ dash1[0] = (int)(3.6 * canvas_pix_per_mm / 3); // 8// Long dash <---------+
+ dash1[1] = (int)(1.8 * canvas_pix_per_mm / 3); // 2// Short gap |
+
+ wxPen thispen = *wxBLACK_PEN;
+ thispen.SetDashes(2, dash1);
+ thispen.SetWidth(2);
+ thispen.SetStyle(wxPENSTYLE_USER_DASH);
+
+ float a = (sectr1 - 90) * PI / 180;
+ a += vp_plib.rotation;
+ double x = point.m_x + (leg_len * cosf(a));
+ double y = point.m_y + (leg_len * sinf(a));
+ DrawDashLine(thispen, point.m_x, point.m_y, x, y);
+
+ a = (sectr2 - 90) * PI / 180.;
+ a += vp_plib.rotation;
+ x = point.m_x + (leg_len * cosf(a));
+ y = point.m_y + (leg_len * sinf(a));
+ DrawDashLine(thispen, point.m_x, point.m_y, x, y);
+ }
+ glDisable(GL_BLEND);
+
+ // Update the object Bounding box,
+ // so that subsequent drawing operations will redraw the item fully
+
+ double latmin, lonmin, latmax, lonmax;
+
+ GetPixPointSingleNoRotate(dr.m_x - rad, dr.m_y + rad, &latmin, &lonmin);
+ GetPixPointSingleNoRotate(dr.m_x + rad, dr.m_y - rad, &latmax, &lonmax);
+ LLBBox symbox;
+ symbox.Set(latmin, lonmin, latmax, lonmax);
+ rzRules->obj->BBObj.Expand(symbox);
+
+ // glEnable( GL_SCISSOR_TEST );
+
+ return 1;
+}
+#endif
+
+int s52plib::RenderCARC_VBO(ObjRazRules *rzRules, Rules *rules) {
+ char *str = (char *)rules->INSTstr;
+ // extract the parameters from the string
+ // And creating a unique string hash as we go
+ wxString inst(str, wxConvUTF8);
+ wxString carc_hash;
+
+ wxStringTokenizer tkz(inst, _T ( ",;" ));
+
+ // outline color
+ wxString outline_color = tkz.GetNextToken();
+ carc_hash += outline_color;
+ carc_hash += _T(".");
+
+ // outline width
+ wxString slong = tkz.GetNextToken();
+ long outline_width;
+ slong.ToLong(&outline_width);
+ carc_hash += slong;
+ carc_hash += _T(".");
+
+ // arc color
+ wxString arc_color = tkz.GetNextToken();
+ carc_hash += arc_color;
+ carc_hash += _T(".");
+
+ // arc width
+ slong = tkz.GetNextToken();
+ long arc_width;
+ slong.ToLong(&arc_width);
+ carc_hash += slong;
+ carc_hash += _T(".");
+
+ // sectr1
+ slong = tkz.GetNextToken();
+ double sectr1;
+ slong.ToDouble(§r1);
+ carc_hash += slong;
+ carc_hash += _T(".");
+
+ // sectr2
+ slong = tkz.GetNextToken();
+ double sectr2;
+ slong.ToDouble(§r2);
+ carc_hash += slong;
+ carc_hash += _T(".");
+
+ // arc radius
+ slong = tkz.GetNextToken();
+ long radius;
+ slong.ToLong(&radius);
+ carc_hash += slong;
+ carc_hash += _T(".");
+
+ // sector radius
+ slong = tkz.GetNextToken();
+ long sector_radius;
+ slong.ToLong(§or_radius);
+ carc_hash += slong;
+ carc_hash += _T(".");
+
+ slong.Printf(_T("%d"), m_colortable_index);
+ carc_hash += slong;
+
+ int width;
+ int height;
+ int rad;
+ int bm_width;
+ int bm_height;
+ int bm_orgx;
+ int bm_orgy;
+
+ Rule *prule = rules->razRule;
+
+ float scale_factor = 1.0;
+
+ // The dimensions of the light are presented here as pixels on-screen.
+ // We must scale the rendered size based on the device pixel density
+ // Let us declare that the width of the arc should be no less than X mm
+ float wx = 1.0;
+
+ // float pd_scale = 1.0;
+ float nominal_arc_width_pix = wxMax(
+ 1.0,
+ floor(GetPPMM() * wx)); // { wx } mm nominal, but not less than 1 pixel
+ // pd_scale = nominal_arc_width_pix / arc_width;
+
+ // scale_factor *= pd_scale;
+ // qDebug() << GetPPMM() << arc_width << nominal_arc_width_pix << pd_scale;
+
+ // Adjust size
+ // Some plain lights have no SCAMIN attribute.
+ // This causes display congestion at small viewing scales, since the objects
+ // are rendered at fixed pixel dimensions from the LUP rules. As a
+ // correction, the idea is to not allow the rendered symbol to be larger than
+ // "X" meters on the chart.
+ // and scale it down when rendered if necessary.
+
+ float xscale = 1.0;
+ if (rzRules->obj->Scamin > 1e8) { // huge (unset) SCAMIN)
+ float radius_meters_target = 200;
+
+ float radius_meters = (radius * canvas_pix_per_mm) / vp_plib.view_scale_ppm;
+
+ xscale = radius_meters_target / radius_meters;
+ xscale = wxMin(xscale, 1.0);
+ xscale = wxMax(.4, xscale);
+
+ radius *= xscale;
+ sector_radius *= xscale;
+ }
+
+ /// scale_factor *= xscale;
+
+ carc_hash += _T(".");
+ wxString xs;
+ xs.Printf(_T("%5g"), xscale);
+ carc_hash += xs;
+
+ if (m_pdc) { // DC rendering
+ if (fabs(prule->parm7 - xscale) > .00001) {
+ ClearRulesCache(prule);
+ }
+
+ // Instantiate the symbol if necessary
+ if ((rules->razRule->pixelPtr == NULL) ||
+ (rules->razRule->parm1 != m_colortable_index)) {
+ // Render the sector light to a bitmap
+
+ rad = (int)(radius * canvas_pix_per_mm);
+
+ width = (rad * 2) + 28;
+ height = (rad * 2) + 28;
+ wxBitmap bm(width, height, -1);
+ wxMemoryDC mdc;
+ mdc.SelectObject(bm);
+ mdc.SetBackground(wxBrush(m_unused_wxColor));
+ mdc.Clear();
+
+ // Adjust sector math for wxWidgets API
+ float sb;
+ float se;
+
+ // For some reason, the __WXMSW__ build flips the sense of
+ // start and end angles on DrawEllipticArc()
+#ifndef __WXMSW__
+ if (sectr2 > sectr1) {
+ sb = 90 - sectr1;
+ se = 90 - sectr2;
+ } else {
+ sb = 360 + (90 - sectr1);
+ se = 90 - sectr2;
+ }
+#else
+ if (sectr2 > sectr1) {
+ se = 90 - sectr1;
+ sb = 90 - sectr2;
+ } else {
+ se = 360 + (90 - sectr1);
+ sb = 90 - sectr2;
+ }
+#endif
+
+ // Here is a goofy way of computing the dc drawing extents exactly
+ // Draw a series of fat line segments approximating the arc using
+ // dc.DrawLine() This will properly establish the drawing box in the
+ // dc
+
+ int border_fluff = 4; // by how much should the blit bitmap be "fluffed"
+
+ // wxDC min/max calculations are currently broken in wxQT, so we use the
+ // entire circle instead of arcs...
+#ifndef __WXQT__
+ if (fabs(sectr2 - sectr1) != 360) // not necessary for all-round lights
+ {
+ mdc.ResetBoundingBox();
+
+ wxPen *pblockpen =
+ wxThePenList->FindOrCreatePen(*wxBLACK, 10, wxPENSTYLE_SOLID);
+ mdc.SetPen(*pblockpen);
+
+ float start_angle, end_angle;
+ if (se < sb) {
+ start_angle = se;
+ end_angle = sb;
+ } else {
+ start_angle = sb;
+ end_angle = se;
+ }
+
+ int x0 = (width / 2) + (int)(rad * cos(start_angle * PI / 180.));
+ int y0 = (height / 2) - (int)(rad * sin(start_angle * PI / 180.));
+ for (float a = start_angle + .1; a <= end_angle; a += 2.0) {
+ int x = (width / 2) + (int)(rad * cosf(a * PI / 180.));
+ int y = (height / 2) - (int)(rad * sinf(a * PI / 180.));
+ mdc.DrawLine(x0, y0, x, y);
+ x0 = x;
+ y0 = y;
+ }
+
+ bm_width = (mdc.MaxX() - mdc.MinX()) + (border_fluff * 2);
+ bm_height = (mdc.MaxY() - mdc.MinY()) + (border_fluff * 2);
+ bm_orgx = mdc.MinX() - border_fluff -
+ width / 2; // wxMax ( 0, mdc.MinX()-border_fluff );
+ bm_orgy = mdc.MinY() - border_fluff -
+ height / 2; // wxMax ( 0, mdc.MinY()-border_fluff );
+
+ mdc.Clear();
+ }
+
+ else {
+ bm_width = rad * 2 + (border_fluff * 2);
+ bm_height = rad * 2 + (border_fluff * 2);
+ bm_orgx = -bm_width / 2;
+ bm_orgy = -bm_height / 2;
+ }
+
+#else
+ bm_width = rad * 2 + (border_fluff * 2);
+ bm_height = rad * 2 + (border_fluff * 2);
+ bm_orgx = -bm_width / 2;
+ bm_orgy = -bm_height / 2;
+#endif
+
+ wxBitmap *sbm = NULL;
+
+ // Draw the outer border
+ wxColour color = getwxColour(outline_color);
+
+ wxPen *pthispen = wxThePenList->FindOrCreatePen(
+ color, outline_width * scale_factor, wxPENSTYLE_SOLID);
+ mdc.SetPen(*pthispen);
+ wxBrush *pthisbrush =
+ wxTheBrushList->FindOrCreateBrush(color, wxBRUSHSTYLE_TRANSPARENT);
+ mdc.SetBrush(*pthisbrush);
+
+ mdc.DrawEllipticArc(width / 2 - rad, height / 2 - rad, rad * 2, rad * 2,
+ sb, se);
+
+ if (arc_width) {
+ wxColour colorb = getwxColour(arc_color);
+
+ if (!colorb.IsOk()) colorb = getwxColour(_T("CHMGD"));
+
+ pthispen = wxThePenList->FindOrCreatePen(
+ colorb, arc_width * scale_factor, wxPENSTYLE_SOLID);
+ mdc.SetPen(*pthispen);
+
+ mdc.DrawEllipticArc(width / 2 - rad, height / 2 - rad, rad * 2, rad * 2,
+ sb, se);
+ }
+
+ mdc.SelectObject(wxNullBitmap);
+
+ // Get smallest containing bitmap
+ sbm = new wxBitmap(bm.GetSubBitmap(wxRect(
+ width / 2 + bm_orgx, height / 2 + bm_orgy, bm_width, bm_height)));
+
+ // Make the mask
+ wxMask *pmask = new wxMask(*sbm, m_unused_wxColor);
+
+ // Associate the mask with the bitmap
+ sbm->SetMask(pmask);
+
+ // delete any old private data
+ ClearRulesCache(rules->razRule);
+
+ // Save the bitmap ptr and aux parms in the rule
+ prule->pixelPtr = sbm;
+ prule->parm0 = ID_wxBitmap;
+ prule->parm1 = m_colortable_index;
+ prule->parm2 = bm_orgx;
+ prule->parm3 = bm_orgy;
+ prule->parm5 = bm_width;
+ prule->parm6 = bm_height;
+ prule->parm7 = xscale;
+ } // instantiation
+ }
+
+ // Render arcs at object's x/y
+ wxPoint r;
+ GetPointPixSingle(rzRules, rzRules->obj->y, rzRules->obj->x, &r);
+
+ // Now render the symbol
+ if (!m_pdc) // opengl
+ {
+ } else {
+ int b_width = prule->parm5;
+ int b_height = prule->parm6;
+
+ // Get the bitmap into a memory dc
+ wxMemoryDC mdc;
+ mdc.SelectObject((wxBitmap &)(*((wxBitmap *)(rules->razRule->pixelPtr))));
+
+ // Blit it into the target dc, using mask
+ m_pdc->Blit(r.x + rules->razRule->parm2, r.y + rules->razRule->parm3,
+ b_width, b_height, &mdc, 0, 0, wxCOPY, true);
+
+ mdc.SelectObject(wxNullBitmap);
+
+ // Draw the sector legs directly on the target DC
+ // so that anti-aliasing works against the drawn image (cannot be
+ // cached...)
+ if (sector_radius > 0) {
+ int leg_len = (int)(sector_radius * canvas_pix_per_mm);
+
+ wxDash dash1[2];
+ dash1[0] = (int)(3.6 * canvas_pix_per_mm); // 8// Long dash <---------+
+ dash1[1] = (int)(1.8 * canvas_pix_per_mm); // 2// Short gap |
+
+ /*
+ wxPen *pthispen = new wxPen(*wxBLACK_PEN);
+ pthispen->SetStyle(wxPENSTYLE_USER_DASH);
+ pthispen->SetDashes( 2, dash1 );
+ // Undocumented "feature": Pen must be fully specified <<>>
+ setting into DC pdc->SetPen ( *pthispen );
+ */
+ // wxColour c = GetGlobalColor( _T ( "CHBLK" ) );
+ wxColour c;
+ GetGlobalColor(_T ( "CHBLK" ), &c);
+
+ float a = (sectr1 - 90) * PI / 180;
+ int x = r.x + (int)(leg_len * cosf(a));
+ int y = r.y + (int)(leg_len * sinf(a));
+ DrawAALine(m_pdc, r.x, r.y, x, y, c, dash1[0], dash1[1]);
+
+ a = (sectr2 - 90) * PI / 180.;
+ x = r.x + (int)(leg_len * cosf(a));
+ y = r.y + (int)(leg_len * sinf(a));
+ DrawAALine(m_pdc, r.x, r.y, x, y, c, dash1[0], dash1[1]);
+ }
+
+ // Debug the symbol bounding box.....
+ /*
+ if(m_pdc){
+ m_pdc->SetPen(wxPen(*wxGREEN, 1));
+ m_pdc->SetBrush(wxBrush(*wxGREEN, wxTRANSPARENT));
+ m_pdc->DrawRectangle( r.x + rules->razRule->parm2, r.y +
+ rules->razRule->parm3, b_width, b_height);
+ }
+ */
+ }
+
+ // Update the object Bounding box,
+ // so that subsequent drawing operations will redraw the item fully
+
+ double latmin, lonmin, latmax, lonmax;
+
+ GetPixPointSingleNoRotate(
+ r.x + prule->parm2, r.y + prule->parm3 + prule->parm6, &latmin, &lonmin);
+ GetPixPointSingleNoRotate(r.x + prule->parm2 + prule->parm5,
+ r.y + prule->parm3, &latmax, &lonmax);
+ LLBBox symbox;
+ symbox.Set(latmin, lonmin, latmax, lonmax);
+ rzRules->obj->BBObj.Expand(symbox);
+ return 1;
+}
+
+// Conditional Symbology
+char *s52plib::RenderCS(ObjRazRules *rzRules, Rules *rules) {
+ void *ret;
+ void *(*f)(void *);
+
+ static int f05;
+
+ if (rules->razRule == NULL) {
+ if (!f05)
+ // CPLError ( ( CPLErr ) 0, 0,"S52plib:_renderCS(): ERROR
+ // no conditional symbology for: %s\n", rules->INSTstr );
+ f05++;
+ return 0;
+ }
+
+ void *g = (void *)rules->razRule;
+
+#ifdef FIX_FOR_MSVC //__WXMSW__
+ // #warning Fix this cast, somehow...
+ // dsr sigh... can't get the cast right
+ _asm
+ {
+ mov eax,[dword ptr g]
+ mov [dword ptr f],eax
+ }
+ ret = f((void *)rzRules); // call cond symb
+#else
+
+ f = (void *(*)(void *))g;
+ ret = f((void *)rzRules);
+
+#endif
+
+ return (char *)ret;
+}
+
+int s52plib::RenderObjectToDC(wxDC *pdcin, ObjRazRules *rzRules) {
+ return DoRenderObject(pdcin, rzRules);
+}
+
+int s52plib::RenderObjectToGL(const wxGLContext &glcc, ObjRazRules *rzRules) {
+ m_glcc = (wxGLContext *)&glcc;
+ return DoRenderObject(NULL, rzRules);
+}
+
+int s52plib::RenderObjectToDCText(wxDC *pdcin, ObjRazRules *rzRules) {
+ return DoRenderObjectTextOnly(pdcin, rzRules);
+}
+
+int s52plib::RenderObjectToGLText(const wxGLContext &glcc,
+ ObjRazRules *rzRules) {
+ m_glcc = (wxGLContext *)&glcc;
+ return DoRenderObjectTextOnly(NULL, rzRules);
+}
+
+int s52plib::DoRenderObject(wxDC *pdcin, ObjRazRules *rzRules) {
+ if (!ObjectRenderCheckRules(rzRules, true)) return 0;
+
+#ifdef __OCPN__ANDROID__
+ // Catch a difficult to trap SIGSEGV, avoiding app crash
+ auto rzRules_current = rzRules; // Save current rules pointer
+ sigaction(SIGSEGV, NULL,
+ &sa_all_plib_previous); // save existing action for this signal
+
+ struct sigaction temp;
+ sigaction(SIGSEGV, NULL,
+ &temp); // inspect existing action for this signal
+
+ temp.sa_handler = catch_signals_plib; // point to my handler
+ sigemptyset(&temp.sa_mask); // make the blocking set
+ // empty, so that all
+ // other signals will be
+ // unblocked during my handler
+ temp.sa_flags = 0;
+ sigaction(SIGSEGV, &temp, NULL);
+
+ if (sigsetjmp(env_plib, 1)) // Something in the
+ // code block below this on caused SIGSEGV...
+ {
+ // reset signal handler
+ sigaction(SIGSEGV, &sa_all_plib_previous, NULL);
+ rzRules_current->obj->bOK_to_Render = false; //Mark object unrenderable
+ return 0;
+ } else
+#endif
+ {
+
+ m_pdc = pdcin; // use this DC
+ Rules *rules = rzRules->LUP->ruleList;
+
+ while (rules != NULL) {
+ switch (rules->ruleType) {
+ case RUL_TXT_TX:
+ RenderTX(rzRules, rules);
+ break; // TX
+ case RUL_TXT_TE:
+ RenderTE(rzRules, rules);
+ break; // TE
+ case RUL_SYM_PT:
+ RenderSY(rzRules, rules);
+ break; // SY
+ case RUL_SIM_LN:
+ if (m_pdc)
+ RenderLS(rzRules, rules);
+ else
+ RenderGLLS(rzRules, rules);
+ break; // LS
+ case RUL_COM_LN:
+ RenderLC(rzRules, rules);
+ break; // LC
+ case RUL_MUL_SG:
+ RenderMPS(rzRules, rules);
+ break; // MultiPoint Sounding
+ case RUL_ARC_2C:
+ RenderCARC(rzRules, rules);
+ break; // Circular Arc, 2 colors
+
+ case RUL_CND_SY: {
+ if (!rzRules->obj->bCS_Added) {
+ rzRules->obj->CSrules = NULL;
+ GetAndAddCSRules(rzRules, rules);
+ if (strncmp(rzRules->obj->FeatureName, "SOUNDG", 6))
+ rzRules->obj->bCS_Added = 1; // mark the object
+ }
+
+ Rules *rules_last = rules;
+ rules = rzRules->obj->CSrules;
+
+ while (NULL != rules) {
+ switch (rules->ruleType) {
+ case RUL_TXT_TX:
+ RenderTX(rzRules, rules);
+ break;
+ case RUL_TXT_TE:
+ RenderTE(rzRules, rules);
+ break;
+ case RUL_SYM_PT:
+ RenderSY(rzRules, rules);
+ break;
+ case RUL_SIM_LN:
+ if (m_pdc)
+ RenderLS(rzRules, rules);
+ else
+ RenderGLLS(rzRules, rules);
+ break; // LS
+ case RUL_COM_LN:
+ RenderLC(rzRules, rules);
+ break;
+ case RUL_MUL_SG:
+ RenderMPS(rzRules, rules);
+ break; // MultiPoint Sounding
+ case RUL_ARC_2C:
+ RenderCARC(rzRules, rules);
+ break; // Circular Arc, 2 colors
+ case RUL_NONE:
+ default:
+ break; // no rule type (init)
+ }
+ rules_last = rules;
+ rules = rules->next;
+ }
+
+ rules = rules_last;
+ break;
+ }
+
+ case RUL_NONE:
+ default:
+ break; // no rule type (init)
+ } // switch
+
+ rules = rules->next;
+ }
+ }
+#ifdef __OCPN__ANDROID__
+ // reset signal handler
+ sigaction(SIGSEGV, &sa_all_plib_previous, NULL);
+#endif
+ return 1;
+}
+
+int s52plib::DoRenderObjectTextOnly(wxDC *pdcin, ObjRazRules *rzRules) {
+ // if(strncmp(rzRules->obj->FeatureName, "RDOCAL", 6))
+ // return 0;
+
+ // if(rzRules->obj->Index == 2766)
+ // int yyp = 4;
+
+ if (!ObjectRenderCheckRules(rzRules, true)) return 0;
+
+ m_pdc = pdcin; // use this DC
+ Rules *rules = rzRules->LUP->ruleList;
+
+ while (rules != NULL) {
+ switch (rules->ruleType) {
+ case RUL_TXT_TX:
+ RenderTX(rzRules, rules);
+ break; // TX
+ case RUL_TXT_TE:
+ RenderTE(rzRules, rules);
+ break; // TE
+ case RUL_CND_SY: {
+ if (!rzRules->obj->bCS_Added) {
+ rzRules->obj->CSrules = NULL;
+ GetAndAddCSRules(rzRules, rules);
+ if (strncmp(rzRules->obj->FeatureName, "SOUNDG", 6))
+ rzRules->obj->bCS_Added = 1; // mark the object
+ }
+
+ Rules *rules_last = rules;
+ rules = rzRules->obj->CSrules;
+
+ while (NULL != rules) {
+ switch (rules->ruleType) {
+ case RUL_TXT_TX:
+ RenderTX(rzRules, rules);
+ break;
+ case RUL_TXT_TE:
+ RenderTE(rzRules, rules);
+ break;
+ default:
+ break; // no rule type (init)
+ }
+ rules_last = rules;
+ rules = rules->next;
+ }
+
+ rules = rules_last;
+ break;
+ }
+
+ case RUL_NONE:
+ default:
+ break; // no rule type (init)
+ } // switch
+
+ rules = rules->next;
+ }
+
+ return 1;
+}
+
+bool s52plib::PreloadOBJLFromCSV(const wxString &csv_file) {
+ wxTextFile file(csv_file);
+ if (!file.Exists()) return false;
+
+ file.Open();
+
+ wxString str;
+ str = file.GetFirstLine();
+ wxChar quote[] = {'\"', 0};
+ wxString description;
+ wxString token;
+
+ while (!file.Eof()) {
+ str = file.GetNextLine();
+
+ wxStringTokenizer tkz(str, _T(","));
+ token = tkz.GetNextToken(); // code
+
+ description = tkz.GetNextToken(); // May contain comma
+ if (!description.EndsWith(quote)) description << tkz.GetNextToken();
+ description.Replace(_T("\""), _T(""), true);
+
+ token = tkz.GetNextToken(); // Acronym
+
+ if (token.Len()) {
+ // Filter out any duplicates, in a case insensitive way
+ // i.e. only the first of "DEPARE" and "depare" is added
+ bool bdup = false;
+ for (unsigned int iPtr = 0; iPtr < pOBJLArray->GetCount(); iPtr++) {
+ OBJLElement *pOLEt = (OBJLElement *)(pOBJLArray->Item(iPtr));
+ if (!token.CmpNoCase(wxString(pOLEt->OBJLName, wxConvUTF8))) {
+ bdup = true;
+ break;
+ }
+ }
+
+ if (!bdup) {
+ wxCharBuffer buffer = token.ToUTF8();
+ if (buffer.data()) {
+ OBJLElement *pOLE = (OBJLElement *)calloc(sizeof(OBJLElement), 1);
+ memcpy(pOLE->OBJLName, buffer.data(), 6);
+ pOLE->nViz = 0;
+
+ pOBJLArray->Add((void *)pOLE);
+
+ OBJLDescriptions.push_back(description);
+ }
+ }
+ }
+ }
+ return true;
+}
+
+void s52plib::UpdateOBJLArray(S57Obj *obj) {
+ // Search the array for this object class
+
+ bool bNeedNew = true;
+ OBJLElement *pOLE;
+
+ for (unsigned int iPtr = 0; iPtr < pOBJLArray->GetCount(); iPtr++) {
+ pOLE = (OBJLElement *)(pOBJLArray->Item(iPtr));
+ if (!strncmp(pOLE->OBJLName, obj->FeatureName, 6)) {
+ obj->iOBJL = iPtr;
+ bNeedNew = false;
+ break;
+ }
+ }
+
+ // Not found yet, so add an element
+ if (bNeedNew) {
+ pOLE = (OBJLElement *)calloc(sizeof(OBJLElement), 1);
+ memcpy(pOLE->OBJLName, obj->FeatureName, OBJL_NAME_LEN);
+ pOLE->nViz = 1;
+
+ pOBJLArray->Add((void *)pOLE);
+ obj->iOBJL = pOBJLArray->GetCount() - 1;
+ }
+}
+
+int s52plib::SetLineFeaturePriority(ObjRazRules *rzRules, int npriority) {
+ int priority_set = npriority; // may be adjusted
+
+ Rules *rules = rzRules->LUP->ruleList;
+
+ // Do Object Type Filtering
+ // If the object s not currently visible (i.e. classed as a not-currently
+ // visible category), then do not set the line segment priorities at all
+ //
+
+ bool b_catfilter = true;
+
+ // DEPCNT is mutable
+ if (m_nDisplayCategory == STANDARD) {
+ if ((DISPLAYBASE != rzRules->LUP->DISC) &&
+ (STANDARD != rzRules->LUP->DISC)) {
+ b_catfilter = rzRules->obj->m_bcategory_mutable;
+ }
+ } else if (m_nDisplayCategory == DISPLAYBASE) {
+ if (DISPLAYBASE != rzRules->LUP->DISC) {
+ b_catfilter = rzRules->obj->m_bcategory_mutable;
+ }
+ }
+
+ if (IsObjNoshow(rzRules->LUP->OBCL)) b_catfilter = false;
+
+ if (!b_catfilter) // No chance this object is visible
+ return 0;
+
+ while (rules != NULL) {
+ switch (rules->ruleType) {
+ case RUL_SIM_LN:
+ case RUL_COM_LN:
+ PrioritizeLineFeature(rzRules, priority_set);
+ break; // LC
+
+ case RUL_CND_SY: {
+ if (!rzRules->obj->bCS_Added) {
+ rzRules->obj->CSrules = NULL;
+ GetAndAddCSRules(rzRules, rules);
+ rzRules->obj->bCS_Added = 1; // mark the object
+ }
+ Rules *rules_last = rules;
+ rules = rzRules->obj->CSrules;
+
+ while (NULL != rules) {
+ switch (rules->ruleType) {
+ case RUL_SIM_LN:
+ case RUL_COM_LN:
+ PrioritizeLineFeature(rzRules, priority_set);
+ break;
+ case RUL_NONE:
+ default:
+ break; // no rule type (init)
+ }
+ rules_last = rules;
+ rules = rules->next;
+ }
+
+ rules = rules_last;
+ break;
+ }
+
+ case RUL_NONE:
+ default:
+ break; // no rule type (init)
+ } // switch
+
+ rules = rules->next;
+ }
+
+ return 1;
+}
+
+int s52plib::PrioritizeLineFeature(ObjRazRules *rzRules, int npriority) {
+ if (rzRules->obj->m_ls_list) {
+ VE_Element *pedge;
+ connector_segment *pcs;
+ line_segment_element *ls = rzRules->obj->m_ls_list;
+ while (ls) {
+ switch (ls->ls_type) {
+ case TYPE_EE:
+ case TYPE_EE_REV:
+
+ pedge = ls->pedge; //(VE_Element *)ls->private0;
+ if (pedge)
+ pedge->max_priority =
+ npriority; // wxMax(pedge->max_priority, npriority);
+ break;
+
+ default:
+ pcs = ls->pcs; //(connector_segment *)ls->private0;
+ if (pcs)
+ pcs->max_priority_cs =
+ npriority; // wxMax(pcs->max_priority, npriority);
+ break;
+ }
+
+ ls = ls->next;
+ }
+ }
+
+ else if (rzRules->obj->m_ls_list_legacy) { // PlugIn (S63)
+
+ PI_connector_segment *pcs;
+ VE_Element *pedge;
+
+ PI_line_segment_element *ls = rzRules->obj->m_ls_list_legacy;
+ while (ls) {
+ switch (ls->type) {
+ case TYPE_EE:
+
+ pedge = (VE_Element *)ls->private0;
+ if (pedge)
+ pedge->max_priority =
+ npriority; // wxMax(pedge->max_priority, npriority);
+ break;
+
+ default:
+ pcs = (PI_connector_segment *)ls->private0;
+ if (pcs)
+ pcs->max_priority =
+ npriority; // wxMax(pcs->max_priority, npriority);
+ break;
+ }
+
+ ls = ls->next;
+ }
+ }
+#if 0
+ else if( rzRules->obj->m_n_lsindex && rzRules->obj->m_lsindex_array) {
+ VE_Hash *edge_hash;
+
+ if( rzRules->obj->m_chart_context->chart ){
+ edge_hash = &rzRules->obj->m_chart_context->chart->Get_ve_hash();
+ }
+ else {
+ edge_hash = (VE_Hash *)rzRules->obj->m_chart_context->m_pve_hash;
+ }
+
+ int *index_run = rzRules->obj->m_lsindex_array;
+
+ for( int iseg = 0; iseg < rzRules->obj->m_n_lsindex; iseg++ ) {
+ // Get first connected node
+ int inode = *index_run++;
+
+ VE_Element *pedge = 0;
+ // Get the edge
+ int enode = *index_run++;
+ if(enode)
+ pedge = (*edge_hash)[enode];
+
+ // Set priority
+ if(pedge){
+ pedge->max_priority = npriority;
+ }
+
+ // Get last connected node
+ inode = *index_run++;
+
+ }
+ }
+#endif
+
+ return 1;
+}
+
+class XPOINT {
+public:
+ float x, y;
+};
+
+class XLINE {
+public:
+ XPOINT o, p;
+ float m;
+ float c;
+};
+
+bool TestLinesIntersection(XLINE &a, XLINE &b) {
+ XPOINT i;
+
+ if ((a.p.x == a.o.x) && (b.p.x == b.o.x)) // both vertical
+ {
+ return (a.p.x == b.p.x);
+ }
+
+ if (a.p.x == a.o.x) // a line a is vertical
+ {
+ // calculate b gradient
+ b.m = (b.p.y - b.o.y) / (b.p.x - b.o.x);
+ // calculate axis intersect values
+ b.c = b.o.y - (b.m * b.o.x);
+ // calculate y point of intercept
+ i.y = b.o.y + ((a.o.x - b.o.x) * b.m);
+ if (i.y < wxMin(a.o.y, a.p.y) || i.y > wxMax(a.o.y, a.p.y)) return false;
+ return true;
+ }
+
+ if (b.p.x == b.o.x) // line b is vertical
+ {
+ // calculate b gradient
+ a.m = (a.p.y - a.o.y) / (a.p.x - a.o.x);
+ // calculate axis intersect values
+ a.c = a.o.y - (a.m * a.o.x);
+ // calculate y point of intercept
+ i.y = a.o.y + ((b.o.x - a.o.x) * a.m);
+ if (i.y < wxMin(b.o.y, b.p.y) || i.y > wxMax(b.o.y, b.p.y)) return false;
+ return true;
+ }
+
+ // calculate gradients
+ a.m = (a.p.y - a.o.y) / (a.p.x - a.o.x);
+ b.m = (b.p.y - b.o.y) / (b.p.x - b.o.x);
+ // parallel lines can't intercept
+ if (a.m == b.m) {
+ return false;
+ }
+ // calculate axis intersect values
+ a.c = a.o.y - (a.m * a.o.x);
+ b.c = b.o.y - (b.m * b.o.x);
+ // calculate x point of intercept
+ i.x = (b.c - a.c) / (a.m - b.m);
+ // is intersection point in segment
+ if (i.x < wxMin(a.o.x, a.p.x) || i.x > wxMax(a.o.x, a.p.x)) {
+ return false;
+ }
+ if (i.x < wxMin(b.o.x, b.p.x) || i.x > wxMax(b.o.x, b.p.x)) {
+ return false;
+ }
+ // points intercept
+ return true;
+}
+
+//-----------------------------------------------------------------------
+// Check a triangle described by point array, and rectangle described by
+// render_canvas_parms for intersection Return false if no intersection
+//-----------------------------------------------------------------------
+bool s52plib::inter_tri_rect(wxPoint *ptp, render_canvas_parms *pb_spec) {
+ // First stage
+ // Check all three points of triangle to see it any are within the render
+ // rectangle
+
+ BoundingBox rect(pb_spec->lclip, pb_spec->y, pb_spec->rclip,
+ pb_spec->y + pb_spec->height);
+
+ for (int i = 0; i < 3; i++) {
+ if (rect.PointInBox(ptp[i].x, ptp[i].y)) return true;
+ }
+
+ // Next stage
+ // Check all four points of rectangle to see it any are within the render
+ // triangle
+
+ double p[6];
+ MyPoint *pmp = (MyPoint *)p;
+
+ for (int i = 0; i < 3; i++) {
+ pmp[i].x = ptp[i].x;
+ pmp[i].y = ptp[i].y;
+ }
+
+ if (G_PtInPolygon(pmp, 3, pb_spec->lclip, pb_spec->y)) return true;
+
+ if (G_PtInPolygon(pmp, 3, pb_spec->lclip, pb_spec->y + pb_spec->height))
+ return true;
+
+ if (G_PtInPolygon(pmp, 3, pb_spec->rclip, pb_spec->y)) return true;
+
+ if (G_PtInPolygon(pmp, 3, pb_spec->rclip, pb_spec->y + pb_spec->height))
+ return true;
+
+ // last step
+ // Check triangle lines against rect lines for line intersect
+
+ for (int i = 0; i < 3; i++) {
+ XLINE a;
+ a.o.x = ptp[i].x;
+ a.o.y = ptp[i].y;
+ if (i == 2) {
+ a.p.x = ptp[0].x;
+ a.p.y = ptp[0].y;
+ } else {
+ a.p.x = ptp[i + 1].x;
+ a.p.y = ptp[i + 1].y;
+ }
+
+ XLINE b;
+
+ // top line
+ b.o.x = pb_spec->lclip;
+ b.o.y = pb_spec->y;
+ b.p.x = pb_spec->rclip;
+ b.p.y = pb_spec->y;
+
+ if (TestLinesIntersection(a, b)) return true;
+
+ // right line
+ b.o.x = pb_spec->rclip;
+ b.o.y = pb_spec->y;
+ b.p.x = pb_spec->rclip;
+ b.p.y = pb_spec->y + pb_spec->height;
+
+ if (TestLinesIntersection(a, b)) return true;
+
+ // bottom line
+ b.o.x = pb_spec->rclip;
+ b.o.y = pb_spec->y + pb_spec->height;
+ b.p.x = pb_spec->lclip;
+ b.p.y = pb_spec->y + pb_spec->height;
+
+ if (TestLinesIntersection(a, b)) return true;
+
+ // left line
+ b.o.x = pb_spec->lclip;
+ b.o.y = pb_spec->y + pb_spec->height;
+ b.p.x = pb_spec->lclip;
+ b.p.y = pb_spec->y;
+
+ if (TestLinesIntersection(a, b)) return true;
+ }
+
+ return false; // no Intersection
+}
+
+//----------------------------------------------------------------------------------
+//
+// Fast Basic Canvas Rendering
+// Render triangle
+//
+//----------------------------------------------------------------------------------
+int s52plib::dda_tri(wxPoint *ptp, S52color *c, render_canvas_parms *pb_spec,
+ render_canvas_parms *pPatt_spec) {
+ unsigned char r = 0;
+ unsigned char g = 0;
+ unsigned char b = 0;
+
+ if (!inter_tri_rect(ptp, pb_spec)) return 0;
+
+ if (NULL != c) {
+ if (pb_spec->b_revrgb) {
+ r = c->R;
+ g = c->G;
+ b = c->B;
+ } else {
+ b = c->R;
+ g = c->G;
+ r = c->B;
+ }
+ }
+
+ // Color Debug
+ /* int fc = rand();
+ b = fc & 0xff;
+ g = fc & 0xff;
+ r = fc & 0xff;
+ */
+
+ int color_int = 0;
+ if (NULL != c) color_int = ((r) << 16) + ((g) << 8) + (b);
+
+ // Determine ymin and ymax indices
+
+ int ymax = ptp[0].y;
+ int ymin = ymax;
+ int xmin, xmax, xmid, ymid;
+ int imin = 0;
+ int imax = 0;
+ int imid;
+
+ for (int ip = 1; ip < 3; ip++) {
+ if (ptp[ip].y > ymax) {
+ imax = ip;
+ ymax = ptp[ip].y;
+ }
+ if (ptp[ip].y <= ymin) {
+ imin = ip;
+ ymin = ptp[ip].y;
+ }
+ }
+
+ imid = 3 - (imin + imax); // do the math...
+
+ xmax = ptp[imax].x;
+ xmin = ptp[imin].x;
+ xmid = ptp[imid].x;
+ ymid = ptp[imid].y;
+
+ // Create edge arrays using fast integer DDA
+ int m, x, dy, count;
+ bool cw;
+
+ if ((abs(xmax - xmin) > 32768) || (abs(xmid - xmin) > 32768) ||
+ (abs(xmax - xmid) > 32768) || (abs(ymax - ymin) > 32768) ||
+ (abs(ymid - ymin) > 32768) || (abs(ymax - ymid) > 32768) ||
+ (xmin > 32768) || (xmid > 32768)) {
+ dy = (ymax - ymin);
+ if (dy) {
+ m = (xmax - xmin) << 8;
+ m /= dy;
+
+ x = xmin << 8;
+
+ for (count = ymin; count <= ymax; count++) {
+ if ((count >= 0) && (count < 1500)) ledge[count] = x >> 8;
+ x += m;
+ }
+ }
+
+ dy = (ymid - ymin);
+ if (dy) {
+ m = (xmid - xmin) << 8;
+ m /= dy;
+
+ x = xmin << 8;
+
+ for (count = ymin; count <= ymid; count++) {
+ if ((count >= 0) && (count < 1500)) redge[count] = x >> 8;
+ x += m;
+ }
+ }
+
+ dy = (ymax - ymid);
+ if (dy) {
+ m = (xmax - xmid) << 8;
+ m /= dy;
+
+ x = xmid << 8;
+
+ for (count = ymid; count <= ymax; count++) {
+ if ((count >= 0) && (count < 1500)) redge[count] = x >> 8;
+ x += m;
+ }
+ }
+
+ double ddfSum = 0;
+ // Check the triangle edge winding direction
+ ddfSum += (xmin / 1) * (ymax / 1) - (ymin / 1) * (xmax / 1);
+ ddfSum += (xmax / 1) * (ymid / 1) - (ymax / 1) * (xmid / 1);
+ ddfSum += (xmid / 1) * (ymin / 1) - (ymid / 1) * (xmin / 1);
+ cw = ddfSum < 0;
+
+ } else {
+ dy = (ymax - ymin);
+ if (dy) {
+ m = (xmax - xmin) << 16;
+ m /= dy;
+
+ x = xmin << 16;
+
+ for (count = ymin; count <= ymax; count++) {
+ if ((count >= 0) && (count < 1500)) ledge[count] = x >> 16;
+ x += m;
+ }
+ }
+
+ dy = (ymid - ymin);
+ if (dy) {
+ m = (xmid - xmin) << 16;
+ m /= dy;
+
+ x = xmin << 16;
+
+ for (count = ymin; count <= ymid; count++) {
+ if ((count >= 0) && (count < 1500)) redge[count] = x >> 16;
+ x += m;
+ }
+ }
+
+ dy = (ymax - ymid);
+ if (dy) {
+ m = (xmax - xmid) << 16;
+ m /= dy;
+
+ x = xmid << 16;
+
+ for (count = ymid; count <= ymax; count++) {
+ if ((count >= 0) && (count < 1500)) redge[count] = x >> 16;
+ x += m;
+ }
+ }
+
+ // Check the triangle edge winding direction
+ long dfSum = 0;
+ dfSum += xmin * ymax - ymin * xmax;
+ dfSum += xmax * ymid - ymax * xmid;
+ dfSum += xmid * ymin - ymid * xmin;
+
+ cw = dfSum < 0;
+
+ } // else
+
+ // if cw is true, redge is actually on the right
+
+ int y1 = ymax;
+ int y2 = ymin;
+
+ int ybt = pb_spec->y;
+ int yt = pb_spec->y + pb_spec->height;
+
+ if (y1 > yt) y1 = yt;
+ if (y1 < ybt) y1 = ybt;
+
+ if (y2 > yt) y2 = yt;
+ if (y2 < ybt) y2 = ybt;
+
+ int lclip = pb_spec->lclip;
+ int rclip = pb_spec->rclip;
+ if (y1 == y2) return 0;
+
+ // Clip the triangle
+ if (cw) {
+ for (int iy = y2; iy <= y1; iy++) {
+ if (ledge[iy] < lclip) {
+ if (redge[iy] < lclip)
+ ledge[iy] = -1;
+ else
+ ledge[iy] = lclip;
+ }
+
+ if (redge[iy] > rclip) {
+ if (ledge[iy] > rclip)
+ ledge[iy] = -1;
+ else
+ redge[iy] = rclip;
+ }
+ }
+ } else {
+ for (int iy = y2; iy <= y1; iy++) {
+ if (redge[iy] < lclip) {
+ if (ledge[iy] < lclip)
+ ledge[iy] = -1;
+ else
+ redge[iy] = lclip;
+ }
+
+ if (ledge[iy] > rclip) {
+ if (redge[iy] > rclip)
+ ledge[iy] = -1;
+ else
+ ledge[iy] = rclip;
+ }
+ }
+ }
+
+ // Fill the triangle
+
+ int ya = y2;
+ int yb = y1;
+
+ unsigned char *pix_buff = pb_spec->pix_buff;
+
+ int patt_size_x = 0, patt_size_y = 0, patt_pitch = 0;
+ unsigned char *patt_s0 = NULL;
+ if (pPatt_spec) {
+ patt_size_y = pPatt_spec->height;
+ patt_size_x = pPatt_spec->width;
+ patt_pitch = pPatt_spec->pb_pitch;
+ patt_s0 = pPatt_spec->pix_buff;
+
+ if (patt_size_y == 0) /* integer division by this value below */
+ return false;
+ }
+
+ if (pb_spec->depth == 24) {
+ for (int iyp = ya; iyp < yb; iyp++) {
+ if ((iyp >= ybt) && (iyp < yt)) {
+ int yoff = (iyp - pb_spec->y) * pb_spec->pb_pitch;
+
+ unsigned char *py = pix_buff + yoff;
+
+ int ix, ixm;
+ if (cw) {
+ ix = ledge[iyp];
+ ixm = redge[iyp];
+ } else {
+ ixm = ledge[iyp];
+ ix = redge[iyp];
+ }
+
+ if (ledge[iyp] != -1) {
+ // This would be considered a failure of the dda algorithm
+ // Happens on very high zoom, with very large triangles.
+ // The integers of the dda algorithm don't have enough bits...
+ // Anyway, just ignore this triangle if it happens
+ if (ix > ixm) continue;
+
+ int xoff = (ix - pb_spec->x) * 3;
+
+ unsigned char *px = py + xoff;
+
+ if (pPatt_spec) // Pattern
+ {
+ int y_stagger = (iyp - pPatt_spec->y) / patt_size_y;
+ int x_stagger_off = 0;
+ if ((y_stagger & 1) && pPatt_spec->b_stagger)
+ x_stagger_off = pPatt_spec->width / 2;
+
+ int patt_y = abs((iyp - pPatt_spec->y)) % patt_size_y;
+
+ unsigned char *pp0 = patt_s0 + (patt_y * patt_pitch);
+
+ while (ix <= ixm) {
+ int patt_x =
+ abs(((ix - pPatt_spec->x) + x_stagger_off) % patt_size_x);
+
+ unsigned char *pp = pp0 + (patt_x * 4);
+ unsigned char alpha = pp[3];
+ double da = (double)alpha / 256.;
+
+ unsigned char r = (unsigned char)(*px * (1.0 - da) + pp[0] * da);
+ unsigned char g =
+ (unsigned char)(*(px + 1) * (1.0 - da) + pp[1] * da);
+ unsigned char b =
+ (unsigned char)(*(px + 2) * (1.0 - da) + pp[2] * da);
+
+ *px++ = r;
+ *px++ = g;
+ *px++ = b;
+ ix++;
+ }
+ }
+
+ else // No Pattern
+ {
+#if defined(__WXGTK__) && defined(__INTEL__)
+#define memset3(dest, value, count) \
+ __asm__ __volatile__( \
+ "cmp $0,%2\n\t" \
+ "jg 2f\n\t" \
+ "je 3f\n\t" \
+ "jmp 4f\n\t" \
+ "2:\n\t" \
+ "movl %0,(%1)\n\t" \
+ "add $3,%1\n\t" \
+ "dec %2\n\t" \
+ "jnz 2b\n\t" \
+ "3:\n\t" \
+ "movb %b0,(%1)\n\t" \
+ "inc %1\n\t" \
+ "movb %h0,(%1)\n\t" \
+ "inc %1\n\t" \
+ "shr $16,%0\n\t" \
+ "movb %b0,(%1)\n\t" \
+ "4:\n\t" \
+ : \
+ : "a"(value), "D"(dest), "r"(count) \
+ :);
+
+ int count = ixm - ix;
+ memset3(px, color_int, count)
+#else
+
+ while (ix <= ixm) {
+ *px++ = b;
+ *px++ = g;
+ *px++ = r;
+
+ ix++;
+ }
+#endif
+ }
+ }
+ }
+ }
+ }
+
+ if (pb_spec->depth == 32) {
+ assert(ya <= yb);
+
+ for (int iyp = ya; iyp < yb; iyp++) {
+ if ((iyp >= ybt) && (iyp < yt)) {
+ int yoff = (iyp - pb_spec->y) * pb_spec->pb_pitch;
+
+ unsigned char *py = pix_buff + yoff;
+
+ int ix, ixm;
+ if (cw) {
+ ix = ledge[iyp];
+ ixm = redge[iyp];
+ } else {
+ ixm = ledge[iyp];
+ ix = redge[iyp];
+ }
+
+ if (ledge[iyp] != -1) {
+ // This would be considered a failure of the dda algorithm
+ // Happens on very high zoom, with very large triangles.
+ // The integers of the dda algorithm don't have enough bits...
+ // Anyway, just ignore this triangle if it happens
+ if (ix > ixm) continue;
+
+ int xoff = (ix - pb_spec->x) * pb_spec->depth / 8;
+
+ unsigned char *px = py + xoff;
+
+ if (pPatt_spec) // Pattern
+ {
+ int y_stagger = (iyp - pPatt_spec->y) / patt_size_y;
+
+ int x_stagger_off = 0;
+ if ((y_stagger & 1) && pPatt_spec->b_stagger)
+ x_stagger_off = pPatt_spec->width / 2;
+
+ int patt_y = abs((iyp - pPatt_spec->y)) % patt_size_y;
+
+ unsigned char *pp0 = patt_s0 + (patt_y * patt_pitch);
+
+ while (ix <= ixm) {
+ int patt_x =
+ abs(((ix - pPatt_spec->x) + x_stagger_off) % patt_size_x);
+ /*
+ if(pPatt_spec->depth == 24)
+ {
+ unsigned char *pp = pp0 + (patt_x * 3);
+
+ // Todo This line assumes unused_color is always 0,0,0
+ if( pp[0] && pp[1] && pp[2] ) {
+ *px++ = *pp++;
+ *px++ = *pp++;
+ *px++ = *pp++;
+ px++;
+ } else {
+ px += 4;
+ // pp += 4;
+ }
+ }
+ else
+ */
+ {
+ unsigned char *pp = pp0 + (patt_x * 4);
+ unsigned char alpha = pp[3];
+ if (alpha > 128) {
+ double da = (double)alpha / 256.;
+
+ unsigned char r = (unsigned char)(pp[0] * da);
+ unsigned char g = (unsigned char)(pp[1] * da);
+ unsigned char b = (unsigned char)(pp[2] * da);
+
+ *px++ = r;
+ *px++ = g;
+ *px++ = b;
+ px++;
+ } else
+ px += 4;
+ }
+ ix++;
+ }
+ }
+
+ else // No Pattern
+ {
+ int *pxi = (int *)px;
+ while (ix <= ixm) {
+ *pxi++ = color_int;
+ ix++;
+ }
+ }
+ }
+ }
+ }
+ }
+
+ return true;
+}
+
+//----------------------------------------------------------------------------------
+//
+// Render Trapezoid
+//
+//----------------------------------------------------------------------------------
+inline int s52plib::dda_trap(wxPoint *segs, int lseg, int rseg, int ytop,
+ int ybot, S52color *c,
+ render_canvas_parms *pb_spec,
+ render_canvas_parms *pPatt_spec) {
+ unsigned char r = 0, g = 0, b = 0;
+
+ if (NULL != c) {
+ if (pb_spec->b_revrgb) {
+ r = c->R;
+ g = c->G;
+ b = c->B;
+ } else {
+ b = c->R;
+ g = c->G;
+ r = c->B;
+ }
+ }
+
+ // Color Debug
+ /* int fc = rand();
+ b = fc & 0xff;
+ g = fc & 0xff;
+ r = fc & 0xff;
+ */
+
+ // int debug = 0;
+ int ret_val = 0;
+
+ int color_int = 0;
+ if (NULL != c) color_int = ((r) << 16) + ((g) << 8) + (b);
+
+ // Create edge arrays using fast integer DDA
+
+ int lclip = pb_spec->lclip;
+ int rclip = pb_spec->rclip;
+
+ int m, x, dy, count;
+
+ // Left edge
+ int xmax = segs[lseg].x;
+ int xmin = segs[lseg + 1].x;
+ int ymax = segs[lseg].y;
+ int ymin = segs[lseg + 1].y;
+
+ if (ymax < ymin) {
+ int a = ymax;
+ ymax = ymin;
+ ymin = a;
+
+ a = xmax;
+ xmax = xmin;
+ xmin = a;
+ }
+
+ int y_dda_limit = wxMin(ybot, ymax);
+ y_dda_limit = wxMin(y_dda_limit, 1499); // don't overrun edge array
+
+ // Some peephole optimization:
+ // if xmax and xmin are both < 0, arrange to simply fill the ledge array
+ // with 0
+ if ((xmax < 0) && (xmin < 0)) {
+ xmax = -2;
+ xmin = -2;
+ }
+ // if xmax and xmin are both > rclip, arrange to simply fill the ledge
+ // array with rclip + 1 This may induce special clip case below, and cause
+ // trap not to be rendered
+ else if ((xmax > rclip) && (xmin > rclip)) {
+ xmax = rclip + 1;
+ xmin = rclip + 1;
+ }
+
+ dy = (ymax - ymin);
+ if (dy) {
+ m = (xmax - xmin) << 16;
+ m /= dy;
+
+ x = xmin << 16;
+
+ // TODO implement this logic in dda_tri also
+ count = ymin;
+ while (count < 0) {
+ x += m;
+ count++;
+ }
+
+ while (count < y_dda_limit) {
+ ledge[count] = x >> 16;
+ x += m;
+ count++;
+ }
+ }
+
+ if ((ytop < ymin) || (ybot > ymax)) {
+ // printf ( "### ledge out of range\n" );
+ ret_val = 1;
+ // r=255;
+ // g=0;
+ // b=0;
+ }
+
+ // Right edge
+ xmax = segs[rseg].x;
+ xmin = segs[rseg + 1].x;
+ ymax = segs[rseg].y;
+ ymin = segs[rseg + 1].y;
+
+ // Note this never gets hit???
+ if (ymax < ymin) {
+ int a = ymax;
+ ymax = ymin;
+ ymin = a;
+
+ a = xmax;
+ xmax = xmin;
+ xmin = a;
+ }
+
+ // Some peephole optimization:
+ // if xmax and xmin are both < 0, arrange to simply fill the redge array
+ // with -1 This may induce special clip case below, and cause trap not to
+ // be rendered
+ if ((xmax < 0) && (xmin < 0)) {
+ xmax = -1;
+ xmin = -1;
+ }
+
+ // if xmax and xmin are both > rclip, arrange to simply fill the redge
+ // array with rclip + 1 This may induce special clip case below, and cause
+ // trap not to be rendered
+ else if ((xmax > rclip) && (xmin > rclip)) {
+ xmax = rclip + 1;
+ xmin = rclip + 1;
+ }
+
+ y_dda_limit = wxMin(ybot, ymax);
+ y_dda_limit = wxMin(y_dda_limit, 1499); // don't overrun edge array
+
+ dy = (ymax - ymin);
+ if (dy) {
+ m = (xmax - xmin) << 16;
+ m /= dy;
+
+ x = xmin << 16;
+
+ count = ymin;
+ while (count < 0) {
+ x += m;
+ count++;
+ }
+
+ while (count < y_dda_limit) {
+ redge[count] = x >> 16;
+ x += m;
+ count++;
+ }
+ }
+
+ if ((ytop < ymin) || (ybot > ymax)) {
+ // printf ( "### redge out of range\n" );
+ ret_val = 1;
+ // r=255;
+ // g=0;
+ // b=0;
+ }
+
+ // Clip trapezoid to height spec
+ int y1 = ybot;
+ int y2 = ytop;
+
+ int ybt = pb_spec->y;
+ int yt = pb_spec->y + pb_spec->height;
+
+ if (y1 > yt) y1 = yt;
+ if (y1 < ybt) y1 = ybt;
+
+ if (y2 > yt) y2 = yt;
+ if (y2 < ybt) y2 = ybt;
+
+ // Clip the trapezoid to width
+ for (int iy = y2; iy <= y1; iy++) {
+ if (ledge[iy] < lclip) {
+ if (redge[iy] < lclip)
+ ledge[iy] = -1;
+ else
+ ledge[iy] = lclip;
+ }
+
+ if (redge[iy] > rclip) {
+ if (ledge[iy] > rclip)
+ ledge[iy] = -1;
+ else
+ redge[iy] = rclip;
+ }
+ }
+
+ // Fill the trapezoid
+
+ int ya = y2;
+ int yb = y1;
+
+ unsigned char *pix_buff = pb_spec->pix_buff;
+
+ int patt_size_x = 0;
+ int patt_size_y = 0;
+ int patt_pitch = 0;
+ unsigned char *patt_s0 = NULL;
+ if (pPatt_spec) {
+ patt_size_y = pPatt_spec->height;
+ patt_size_x = pPatt_spec->width;
+ patt_pitch = pPatt_spec->pb_pitch;
+ patt_s0 = pPatt_spec->pix_buff;
+ }
+
+ if (pb_spec->depth == 24) {
+ for (int iyp = ya; iyp < yb; iyp++) {
+ if ((iyp >= ybt) && (iyp < yt)) {
+ int yoff = (iyp - pb_spec->y) * pb_spec->pb_pitch;
+
+ unsigned char *py = pix_buff + yoff;
+
+ int ix, ixm;
+ ix = ledge[iyp];
+ ixm = redge[iyp];
+
+ // if(debug) printf("iyp %d, ix %d, ixm %d\n",
+ // iyp, ix, ixm);
+ // int ix = ledge[iyp];
+ // if(ix != -1) // special
+ // clip case
+ if (ledge[iyp] != -1) {
+ int xoff = (ix - pb_spec->x) * 3;
+
+ unsigned char *px = py + xoff;
+
+ if (pPatt_spec) // Pattern
+ {
+ int y_stagger = (iyp - pPatt_spec->y) / patt_size_y;
+ int x_stagger_off = 0;
+ if ((y_stagger & 1) && pPatt_spec->b_stagger)
+ x_stagger_off = pPatt_spec->width / 2;
+
+ int patt_y = abs((iyp - pPatt_spec->y)) % patt_size_y;
+ unsigned char *pp0 = patt_s0 + (patt_y * patt_pitch);
+
+ while (ix <= ixm) {
+ int patt_x =
+ abs(((ix - pPatt_spec->x) + x_stagger_off) % patt_size_x);
+ /*
+ if(pPatt_spec->depth == 24)
+ {
+ unsigned char *pp = pp0 + (patt_x * 3);
+
+ // Todo This line assumes unused_color is always 0,0,0
+ if( pp[0] && pp[1] && pp[2] ) {
+ *px++ = *pp++;
+ *px++ = *pp++;
+ *px++ = *pp++;
+ } else {
+ px += 3;
+ pp += 3;
+ }
+ }
+ else
+ */
+ {
+ unsigned char *pp = pp0 + (patt_x * 4);
+ unsigned char alpha = pp[3];
+ if (alpha > 128) {
+ double da = (double)alpha / 256.;
+
+ unsigned char r = (unsigned char)(pp[0] * da);
+ unsigned char g = (unsigned char)(pp[1] * da);
+ unsigned char b = (unsigned char)(pp[2] * da);
+
+ *px++ = r;
+ *px++ = g;
+ *px++ = b;
+ } else
+ px += 3;
+ }
+
+ ix++;
+ }
+ }
+
+ else // No Pattern
+ {
+#if defined(__WXGTK__WITH_OPTIMIZE_0) && defined(__INTEL__)
+#define memset3d(dest, value, count) \
+ __asm__ __volatile__( \
+ "cmp $0,%2\n\t" \
+ "jg ld0\n\t" \
+ "je ld1\n\t" \
+ "jmp ld2\n\t" \
+ "ld0:\n\t" \
+ "movl %0,(%1)\n\t" \
+ "add $3,%1\n\t" \
+ "dec %2\n\t" \
+ "jnz ld0\n\t" \
+ "ld1:\n\t" \
+ "movb %b0,(%1)\n\t" \
+ "inc %1\n\t" \
+ "movb %h0,(%1)\n\t" \
+ "inc %1\n\t" \
+ "shr $16,%0\n\t" \
+ "movb %b0,(%1)\n\t" \
+ "ld2:\n\t" \
+ : \
+ : "a"(value), "D"(dest), "r"(count) \
+ :);
+ int count = ixm - ix;
+ memset3d(px, color_int, count)
+#else
+
+ while (ix <= ixm) {
+ *px++ = b;
+ *px++ = g;
+ *px++ = r;
+
+ ix++;
+ }
+#endif
+ }
+ }
+ }
+ }
+ }
+
+ if (pb_spec->depth == 32) {
+ assert(ya <= yb);
+
+ for (int iyp = ya; iyp < yb; iyp++) {
+ if ((iyp >= ybt) && (iyp < yt)) {
+ int yoff = (iyp - pb_spec->y) * pb_spec->pb_pitch;
+
+ unsigned char *py = pix_buff + yoff;
+
+ int ix, ixm;
+ ix = ledge[iyp];
+ ixm = redge[iyp];
+
+ if (ledge[iyp] != -1) {
+ int xoff = (ix - pb_spec->x) * pb_spec->depth / 8;
+
+ unsigned char *px = py + xoff;
+
+ if (pPatt_spec) // Pattern
+ {
+ int y_stagger = (iyp - pPatt_spec->y) / patt_size_y;
+ int x_stagger_off = 0;
+ if ((y_stagger & 1) && pPatt_spec->b_stagger)
+ x_stagger_off = pPatt_spec->width / 2;
+
+ int patt_y = abs((iyp - pPatt_spec->y)) % patt_size_y;
+ unsigned char *pp0 = patt_s0 + (patt_y * patt_pitch);
+
+ while (ix <= ixm) {
+ int patt_x =
+ abs(((ix - pPatt_spec->x) + x_stagger_off) % patt_size_x);
+ /*
+ if(pPatt_spec->depth == 24)
+ {
+ unsigned char *pp = pp0 + (patt_x * 3);
+
+ // Todo This line assumes unused_color is always 0,0,0
+ if( pp[0] && pp[1] && pp[2] ) {
+ *px++ = *pp++;
+ *px++ = *pp++;
+ *px++ = *pp++;
+ px++;
+ } else {
+ px += 4;
+ // pp += 3;
+ }
+ }
+ else
+ */
+ {
+ unsigned char *pp = pp0 + (patt_x * 4);
+ unsigned char alpha = pp[3];
+ if (alpha > 128) {
+ double da = (double)alpha / 256.;
+
+ unsigned char r = (unsigned char)(pp[0] * da);
+ unsigned char g = (unsigned char)(pp[1] * da);
+ unsigned char b = (unsigned char)(pp[2] * da);
+
+ *px++ = r;
+ *px++ = g;
+ *px++ = b;
+ px++;
+ } else
+ px += 4;
+ }
+ ix++;
+ }
+ }
+
+ else // No Pattern
+ {
+ int *pxi = (int *)px;
+ while (ix <= ixm) {
+ *pxi++ = color_int;
+ ix++;
+ }
+ }
+ }
+ }
+ }
+ }
+
+ return ret_val;
+}
+
+void s52plib::RenderToBufferFilledPolygon(ObjRazRules *rzRules, S57Obj *obj,
+ S52color *c,
+ render_canvas_parms *pb_spec,
+ render_canvas_parms *pPatt_spec) {
+ LLBBox BBView = GetBBox();
+ // please untangle this logic with the logic below
+ if (BBView.GetMaxLon() + 180 < vp_plib.clon)
+ BBView.Set(BBView.GetMinLat(), BBView.GetMinLon() + 360, BBView.GetMaxLat(),
+ BBView.GetMaxLon() + 360);
+ else if (BBView.GetMinLon() - 180 > vp_plib.clon)
+ BBView.Set(BBView.GetMinLat(), BBView.GetMinLon() - 360, BBView.GetMaxLat(),
+ BBView.GetMaxLon() - 360);
+
+ S52color cp;
+ if (NULL != c) {
+ cp.R = c->R;
+ cp.G = c->G;
+ cp.B = c->B;
+ }
+
+ if (obj->pPolyTessGeo) {
+ if (!rzRules->obj->pPolyTessGeo->IsOk()) { // perform deferred tesselation
+ rzRules->obj->pPolyTessGeo->BuildDeferredTess();
+ }
+
+ wxPoint *pp3 = (wxPoint *)malloc(3 * sizeof(wxPoint));
+ wxPoint *ptp = (wxPoint *)malloc((obj->pPolyTessGeo->GetnVertexMax() + 1) *
+ sizeof(wxPoint));
+
+ PolyTriGroup *ppg = obj->pPolyTessGeo->Get_PolyTriGroup_head();
+
+ TriPrim *p_tp = ppg->tri_prim_head;
+ while (p_tp) {
+ LLBBox box;
+ if (!rzRules->obj->m_chart_context->chart) { // This is a PlugIn Chart
+ LegacyTriPrim *p_ltp = (LegacyTriPrim *)p_tp;
+ box.Set(p_ltp->miny, p_ltp->minx, p_ltp->maxy, p_ltp->maxx);
+ } else
+ box = p_tp->tri_box;
+
+ if (!BBView.IntersectOut(box)) {
+ // Get and convert the points
+ wxPoint *pr = ptp;
+
+ if (ppg->data_type == DATA_TYPE_DOUBLE) {
+ double *pvert_list = p_tp->p_vertex;
+
+ for (int iv = 0; iv < p_tp->nVert; iv++) {
+ double lon = *pvert_list++;
+ double lat = *pvert_list++;
+ GetPointPixSingle(rzRules, lat, lon, pr);
+
+ pr++;
+ }
+ } else {
+ float *pvert_list = (float *)p_tp->p_vertex;
+
+ for (int iv = 0; iv < p_tp->nVert; iv++) {
+ double lon = *pvert_list++;
+ double lat = *pvert_list++;
+ GetPointPixSingle(rzRules, lat, lon, pr);
+
+ pr++;
+ }
+ }
+
+ switch (p_tp->type) {
+ case PTG_TRIANGLE_FAN: {
+ for (int it = 0; it < p_tp->nVert - 2; it++) {
+ pp3[0].x = ptp[0].x;
+ pp3[0].y = ptp[0].y;
+
+ pp3[1].x = ptp[it + 1].x;
+ pp3[1].y = ptp[it + 1].y;
+
+ pp3[2].x = ptp[it + 2].x;
+ pp3[2].y = ptp[it + 2].y;
+
+ dda_tri(pp3, &cp, pb_spec, pPatt_spec);
+ }
+ break;
+ }
+ case PTG_TRIANGLE_STRIP: {
+ for (int it = 0; it < p_tp->nVert - 2; it++) {
+ pp3[0].x = ptp[it].x;
+ pp3[0].y = ptp[it].y;
+
+ pp3[1].x = ptp[it + 1].x;
+ pp3[1].y = ptp[it + 1].y;
+
+ pp3[2].x = ptp[it + 2].x;
+ pp3[2].y = ptp[it + 2].y;
+
+ dda_tri(pp3, &cp, pb_spec, pPatt_spec);
+ }
+ break;
+ }
+ case PTG_TRIANGLES: {
+ for (int it = 0; it < p_tp->nVert; it += 3) {
+ pp3[0].x = ptp[it].x;
+ pp3[0].y = ptp[it].y;
+
+ pp3[1].x = ptp[it + 1].x;
+ pp3[1].y = ptp[it + 1].y;
+
+ pp3[2].x = ptp[it + 2].x;
+ pp3[2].y = ptp[it + 2].y;
+
+ dda_tri(pp3, &cp, pb_spec, pPatt_spec);
+ }
+ break;
+ }
+ }
+ } // if bbox
+
+ // pick up the next in chain
+ if (!rzRules->obj->m_chart_context->chart) { // This is a PlugIn Chart
+ LegacyTriPrim *p_ltp = (LegacyTriPrim *)p_tp;
+ p_tp = (TriPrim *)p_ltp->p_next;
+ } else
+ p_tp = p_tp->p_next;
+
+ } // while
+
+ free(ptp);
+ free(pp3);
+ } // if pPolyTessGeo
+}
+
+#ifdef ocpnUSE_GL
+int s52plib::RenderToGLAC(ObjRazRules *rzRules, Rules *rules) {
+ return RenderToGLAC_GLSL(rzRules, rules);
+}
+#endif
+
+int n_areaObjs;
+int n_areaTris;
+
+#ifdef ocpnUSE_GL
+int s52plib::RenderToGLAC_GLSL(ObjRazRules *rzRules, Rules *rules) {
+ if (!ObjectRenderCheckPosReduced(rzRules)) return false;
+
+ n_areaObjs++;
+
+ GLenum reset_err = glGetError();
+
+ S52color *c;
+ char *str = (char *)rules->INSTstr;
+
+ c = getColor(str);
+
+ LLBBox BBView = GetBBox();
+ // please untangle this logic with the logic below
+ if (BBView.GetMaxLon() + 180 < vp_plib.clon)
+ BBView.Set(BBView.GetMinLat(), BBView.GetMinLon() + 360, BBView.GetMaxLat(),
+ BBView.GetMaxLon() + 360);
+ else if (BBView.GetMinLon() - 180 > vp_plib.clon)
+ BBView.Set(BBView.GetMinLat(), BBView.GetMinLon() - 360, BBView.GetMaxLat(),
+ BBView.GetMaxLon() - 360);
+
+ // Allow a little slop in calculating whether a triangle
+ // is within the requested VPointCompat
+ double margin = BBView.GetLonRange() * .05;
+ BBView.EnLarge(margin);
+
+ // Use VBO if instructed by hardware renderer specification
+ bool b_useVBO = m_GLAC_VBO && !rzRules->obj->auxParm1;
+
+ if (rzRules->obj->pPolyTessGeo) {
+ bool b_temp_vbo = false;
+ bool b_transform = false;
+
+ // perform deferred tesselation
+ if (!rzRules->obj->pPolyTessGeo->IsOk()) {
+ rzRules->obj->pPolyTessGeo->BuildDeferredTess();
+ }
+
+ // Get the vertex data
+ PolyTriGroup *ppg_vbo = rzRules->obj->pPolyTessGeo->Get_PolyTriGroup_head();
+
+ // Has the input vertex buffer been converted to "single_alloc float"
+ // model? and is it allowed?
+ if (!ppg_vbo->bsingle_alloc && (rzRules->obj->auxParm1 >= 0)) {
+ int data_size = sizeof(float);
+
+ // First calculate the required total byte size
+ int total_byte_size = 0;
+ TriPrim *p_tp = ppg_vbo->tri_prim_head;
+ while (p_tp) {
+ total_byte_size += p_tp->nVert * 2 * data_size;
+ p_tp = p_tp->p_next; // pick up the next in chain
+ }
+
+ float *vbuf = (float *)malloc(total_byte_size);
+ p_tp = ppg_vbo->tri_prim_head;
+
+ if (ppg_vbo->data_type == DATA_TYPE_DOUBLE) { // DOUBLE to FLOAT
+ float *p_run = vbuf;
+ while (p_tp) {
+ float *pfbuf = p_run;
+ for (int i = 0; i < p_tp->nVert * 2; ++i) {
+ float x = (float)(p_tp->p_vertex[i]);
+ *p_run++ = x;
+ }
+
+ free(p_tp->p_vertex);
+ p_tp->p_vertex = (double *)pfbuf;
+
+ p_tp = p_tp->p_next; // pick up the next in chain
+ }
+ } else { // FLOAT to FLOAT
+ float *p_run = vbuf;
+ while (p_tp) {
+ memcpy(p_run, p_tp->p_vertex, p_tp->nVert * 2 * sizeof(float));
+
+ free(p_tp->p_vertex);
+ p_tp->p_vertex = (double *)p_run;
+
+ p_run += p_tp->nVert * 2 * sizeof(float);
+
+ p_tp = p_tp->p_next; // pick up the next in chain
+ }
+ }
+
+ ppg_vbo->bsingle_alloc = true;
+ ppg_vbo->single_buffer = (unsigned char *)vbuf;
+ ppg_vbo->single_buffer_size = total_byte_size;
+ ppg_vbo->data_type = DATA_TYPE_FLOAT;
+ }
+
+ if (b_useVBO) {
+ // Has a VBO been built for this object?
+ if (1) {
+ glGetError(); // clear it
+
+ if (rzRules->obj->auxParm0 <= 0) {
+#ifdef xUSE_ANDROID_GLES2
+ if (ppg_vbo->data_type != DATA_TYPE_SHORT) {
+ // We convert the vertex data from FLOAT to GL_SHORT to make the VBO
+ // smaller, but still keeping enough precision
+ // This requires a scale factor to reduce the range from existing
+ // data to +/- 32K
+
+ size_t np = ppg_vbo->single_buffer_size / (2 * sizeof(float));
+ np--;
+
+ PolyTriGroup *ppg =
+ rzRules->obj->pPolyTessGeo->Get_PolyTriGroup_head();
+ TriPrim *p_tp = ppg->tri_prim_head;
+
+ size_t npp = 0;
+ while (p_tp) {
+ npp += p_tp->nVert;
+ p_tp = (TriPrim *)p_tp->p_next;
+ }
+
+ // Get the data range
+ float *pRun = (float *)ppg_vbo->single_buffer;
+ float north_max = -1e8;
+ float north_min = 1e8;
+ float east_max = -1e8;
+ float east_min = 1e8;
+
+ for (size_t i = 0; i < np; i++) {
+ float east = *pRun++;
+ float north = *pRun++;
+ north_max = wxMax(north, north_max);
+ north_min = wxMin(north, north_min);
+ east_max = wxMax(east, east_max);
+ east_min = wxMin(east, east_min);
+ }
+
+ float cfactx = wxMax(fabs(east_max), fabs(east_min));
+ float cfacty = wxMax(fabs(north_max), fabs(north_min));
+ float cfact = wxMax(cfactx, cfacty);
+
+ float sfact = cfact / 32700.0;
+
+ sfact = wxMax(sfact, 1.0);
+
+ // Copy/convert the data
+ unsigned char *new_buf =
+ (unsigned char *)malloc(np * 2 * sizeof(short));
+ pRun = (float *)ppg_vbo->single_buffer;
+ short *pd = (short *)new_buf;
+ for (size_t i = 0; i < np; i++) {
+ float east = *pRun++;
+ float north = *pRun++;
+ // short a = (east / sfact);
+ // short b = (north / sfact);
+ *pd++ = (east / sfact);
+ *pd++ = (north / sfact);
+ }
+
+ // replace the buffer
+ free(ppg_vbo->single_buffer);
+ ppg_vbo->single_buffer = new_buf;
+ ppg_vbo->single_buffer_size /= 2;
+
+ // Record the scale/offset factors
+ ppg_vbo->sfactor = sfact;
+ ppg_vbo->soffset = 0.;
+
+ ppg_vbo->data_type = DATA_TYPE_SHORT;
+ }
+
+#endif
+ b_temp_vbo = (rzRules->obj->auxParm0 ==
+ -5); // Must we use a temporary VBO? Probably slower
+ // than simple glDrawArrays
+
+ GLuint vboId = 0;
+ // generate a new VBO and get the associated ID
+ glGenBuffers(1, &vboId);
+
+ rzRules->obj->auxParm0 = vboId;
+
+ // bind VBO in order to use
+ glBindBuffer(GL_ARRAY_BUFFER, vboId);
+ GLenum err = glGetError();
+ if (err) {
+ wxString msg;
+ msg.Printf(_T("VBO Error A: %d"), err);
+ wxLogMessage(msg);
+ return 0;
+ }
+
+ // upload data to VBO
+ glBufferData(GL_ARRAY_BUFFER, ppg_vbo->single_buffer_size,
+ ppg_vbo->single_buffer, GL_STATIC_DRAW);
+ err = glGetError();
+ if (err) {
+ wxString msg;
+ msg.Printf(_T("VBO Error B: %d"), err);
+ wxLogMessage(msg);
+ return 0;
+ }
+
+ } else {
+ glBindBuffer(GL_ARRAY_BUFFER, rzRules->obj->auxParm0);
+ GLenum err = glGetError();
+ if (err) {
+ wxString msg;
+ msg.Printf(_T("VBO Error C: %d"), err);
+ wxLogMessage(msg);
+ return 0;
+ }
+ }
+ }
+ }
+
+ PolyTriGroup *ppg = rzRules->obj->pPolyTessGeo->Get_PolyTriGroup_head();
+
+ TriPrim *p_tp = ppg->tri_prim_head;
+ GLintptr vbo_offset = 0;
+
+ // Set up the stride sizes for the array
+ int array_data_size = sizeof(float);
+ GLint array_gl_type = GL_FLOAT;
+
+ if (ppg->data_type == DATA_TYPE_DOUBLE) {
+ array_data_size = sizeof(double);
+ array_gl_type = GL_DOUBLE;
+ }
+
+ if (ppg->data_type == DATA_TYPE_SHORT) {
+ array_data_size = sizeof(short);
+ array_gl_type = GL_SHORT;
+ }
+
+ CGLShaderProgram *shader =
+ pCcolor_tri_shader_program[0 /*GetCanvasIndex()*/];
+ shader->Bind();
+
+ // Disable VBO's (vertex buffer objects) for attributes.
+ if (!b_useVBO) glBindBuffer(GL_ARRAY_BUFFER, 0);
+ glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
+
+ GLint pos = shader->getAttributeLocation("position");
+ glEnableVertexAttribArray(pos);
+
+ float angle = 0;
+
+ // Build Transform matrix
+ mat4x4 I, Q;
+ mat4x4_identity(I);
+
+ // Scale
+ I[0][0] *= rzRules->obj->x_rate;
+ I[1][1] *= rzRules->obj->y_rate;
+
+ // Translate
+ float x_origin = rzRules->obj->x_origin;
+
+ if (rzRules->obj->m_chart_context->chart) { // not a PlugIn Chart
+ if (((int)rzRules->obj->m_chart_context->chart_type ==
+ (int)S52_ChartTypeEnum::S52_CHART_TYPE_CM93) ||
+ ((int)rzRules->obj->m_chart_context->chart_type ==
+ (int)S52_ChartTypeEnum::S52_CHART_TYPE_CM93COMP)) {
+ // We may need to translate object coordinates by 360 degrees to
+ // conform.
+ if (BBView.GetMaxLon() >= 180.) {
+ if (rzRules->obj->BBObj.GetMinLon() < BBView.GetMaxLon() - 360.)
+ x_origin +=
+ (float)(mercator_k0 * WGS84_semimajor_axis_meters * 2.0 * PI);
+ } else if ((BBView.GetMinLon() <= -180. &&
+ rzRules->obj->BBObj.GetMaxLon() >
+ BBView.GetMinLon() + 360.) ||
+ (rzRules->obj->BBObj.GetMaxLon() > 180 &&
+ BBView.GetMinLon() + 360 < rzRules->obj->BBObj.GetMaxLon()))
+ x_origin -=
+ (float)(mercator_k0 * WGS84_semimajor_axis_meters * 2.0 * PI);
+ }
+ }
+
+ double ppg_scale_factor = 1.0;
+ if (rzRules->obj->m_chart_context->chart) // This is not a plugin chart
+ ppg_scale_factor = ppg->sfactor;
+
+ I[3][0] = -(rzRules->sm_transform_parms->easting_vp_center - x_origin) *
+ vp_plib.view_scale_ppm;
+ I[3][1] = -(rzRules->sm_transform_parms->northing_vp_center -
+ rzRules->obj->y_origin) *
+ -vp_plib.view_scale_ppm;
+
+ // Scale
+ I[0][0] *= vp_plib.view_scale_ppm * ppg_scale_factor;
+ I[1][1] *= -vp_plib.view_scale_ppm * ppg_scale_factor;
+
+ // Rotate
+ mat4x4_rotate_Z(Q, I, angle);
+
+ // Translate
+ Q[3][0] += vp_plib.pix_width / 2;
+ Q[3][1] += vp_plib.pix_height / 2;
+
+ shader->SetUniformMatrix4fv("TransformMatrix", (GLfloat *)Q);
+
+ float colorv[4];
+ colorv[0] = c->R / float(256);
+ colorv[1] = c->G / float(256);
+ colorv[2] = c->B / float(256);
+ colorv[3] = 1.0;
+
+ shader->SetUniform4fv("color", colorv);
+
+ if (b_useVBO) {
+ glBindBuffer(GL_ARRAY_BUFFER, rzRules->obj->auxParm0);
+ glVertexAttribPointer(pos, 2, array_gl_type, GL_FALSE, 0, (GLvoid *)(0));
+ } else
+ glVertexAttribPointer(pos, 2, GL_FLOAT, GL_FALSE, 0,
+ (GLvoid *)(ppg->single_buffer));
+ int VBO_offset_index = 0;
+
+ while (p_tp) {
+ LLBBox box;
+ if (!rzRules->obj->m_chart_context->chart) { // This is s63 PlugIn Chart
+ LegacyTriPrim *p_ltp = (LegacyTriPrim *)p_tp;
+ box.Set(p_ltp->miny, p_ltp->minx, p_ltp->maxy, p_ltp->maxx);
+ } else
+ box = p_tp->tri_box;
+
+ if (!BBView.IntersectOut(box)) {
+ glDrawArrays(p_tp->type, VBO_offset_index, p_tp->nVert);
+ }
+
+ VBO_offset_index += p_tp->nVert;
+
+ // pick up the next in chain
+ if (!rzRules->obj->m_chart_context->chart) { // This is a PlugIn Chart
+ LegacyTriPrim *p_ltp = (LegacyTriPrim *)p_tp;
+ p_tp = (TriPrim *)p_ltp->p_next;
+ } else
+ p_tp = p_tp->p_next;
+
+ } // while
+
+ if (b_useVBO) glBindBuffer(GL_ARRAY_BUFFER, 0);
+
+ mat4x4 IM;
+ mat4x4_identity(IM);
+ shader->SetUniformMatrix4fv("TransformMatrix", (GLfloat *)IM);
+
+ glDisableVertexAttribArray(pos);
+ shader->UnBind();
+
+ if (b_useVBO && b_temp_vbo) {
+ glBufferData(GL_ARRAY_BUFFER, 0, NULL, GL_STATIC_DRAW);
+ glDeleteBuffers(1, (unsigned int *)&rzRules->obj->auxParm0);
+ rzRules->obj->auxParm0 = 0;
+ }
+ } // if pPolyTessGeo
+
+ return 1;
+}
+#endif
+
+void s52plib::SetGLClipRect(const VPointCompat &vp, const wxRect &rect) {}
+
+void RotateToViewPort(const VPointCompat &vp) {
+#if !defined(USE_ANDROID_GLES2) && !defined(ocpnUSE_GLSL)
+ bool g_bskew_comp = true;
+
+ float angle = vp.rotation;
+ // if (g_bskew_comp) angle -= vp.skew;
+
+ if (fabs(angle) > 0.0001) {
+ // Rotations occur around 0,0, so translate to rotate around screen
+ // center
+ float xt = vp.pix_width / 2.0, yt = vp.pix_height / 2.0;
+
+ glTranslatef(xt, yt, 0);
+ glRotatef(angle * 180. / PI, 0, 0, 1);
+ glTranslatef(-xt, -yt, 0);
+ }
+#endif
+}
+
+int s52plib::RenderToGLAP(ObjRazRules *rzRules, Rules *rules) {
+#if defined(USE_ANDROID_GLES2) || defined(ocpnUSE_GLSL)
+ return RenderToGLAP_GLSL(rzRules, rules);
+#else
+ return 1;
+#endif // #ifdef ocpnUSE_GLSL
+}
+#ifdef ocpnUSE_GL
+int s52plib::RenderToGLAP_GLSL(ObjRazRules *rzRules, Rules *rules) {
+ if (!ObjectRenderCheckPosReduced(rzRules)) return false;
+
+ // Get the pattern definition
+ if ((rules->razRule->pixelPtr == NULL) ||
+ (rules->razRule->parm1 != m_colortable_index) ||
+ (rules->razRule->parm0 != ID_GL_PATT_SPEC)) {
+ render_canvas_parms *patt_spec =
+ CreatePatternBufferSpec(rzRules, rules, false, true);
+
+ ClearRulesCache(
+ rules->razRule); // Clear out any existing cached symbology
+
+ rules->razRule->pixelPtr = patt_spec;
+ rules->razRule->parm1 = m_colortable_index;
+ rules->razRule->parm0 = ID_GL_PATT_SPEC;
+ }
+
+ // Render the Area using the pattern spec stored in the rules
+ render_canvas_parms *ppatt_spec =
+ (render_canvas_parms *)rules->razRule->pixelPtr;
+
+ // Has the pattern been uploaded as a texture?
+ if (!ppatt_spec->OGL_tex_name) {
+ GLuint tex_name;
+ glGenTextures(1, &tex_name);
+ ppatt_spec->OGL_tex_name = tex_name;
+
+ glBindTexture(GL_TEXTURE_2D, tex_name);
+
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
+
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
+
+ glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, ppatt_spec->w_pot,
+ ppatt_spec->h_pot, 0, GL_RGBA, GL_UNSIGNED_BYTE,
+ ppatt_spec->pix_buff);
+ }
+
+ glEnable(GL_TEXTURE_2D);
+ glBindTexture(GL_TEXTURE_2D, ppatt_spec->OGL_tex_name);
+
+ glEnable(GL_BLEND);
+ // glBlendFunc(GL_SRC_COLOR, GL_ZERO);
+
+ int textureWidth = ppatt_spec->width; // ppatt_spec->w_pot;
+ int textureHeight = ppatt_spec->height; // ppatt_spec->h_pot;
+
+ wxPoint pr;
+ GetPointPixSingle(rzRules, rzRules->obj->m_lat, rzRules->obj->m_lat, &pr);
+ float xOff = pr.x;
+ float yOff = pr.y;
+
+ LLBBox BBView = GetReducedBBox();
+ // please untangle this logic with the logic below
+ if (BBView.GetMaxLon() + 180 < vp_plib.clon)
+ BBView.Set(BBView.GetMinLat(), BBView.GetMinLon() + 360, BBView.GetMaxLat(),
+ BBView.GetMaxLon() + 360);
+ else if (BBView.GetMinLon() - 180 > vp_plib.clon)
+ BBView.Set(BBView.GetMinLat(), BBView.GetMinLon() - 360, BBView.GetMaxLat(),
+ BBView.GetMaxLon() - 360);
+
+ // Allow a little slop in calculating whether a triangle
+ // is within the requested VPointCompat
+ double margin = BBView.GetLonRange() * .05;
+ BBView.EnLarge(margin);
+
+ bool b_useVBO = m_GLAC_VBO && !rzRules->obj->auxParm1;
+
+ if (rzRules->obj->pPolyTessGeo) {
+ // bool b_temp_vbo = false;
+ // bool b_transform = false;
+
+ // perform deferred tesselation
+ if (!rzRules->obj->pPolyTessGeo->IsOk())
+ rzRules->obj->pPolyTessGeo->BuildDeferredTess();
+
+ // Get the vertex data
+ PolyTriGroup *ppg_vbo = rzRules->obj->pPolyTessGeo->Get_PolyTriGroup_head();
+
+ // Has the input vertex buffer been converted to "single_alloc float"
+ // model? and is it allowed?
+ if (!ppg_vbo->bsingle_alloc && (rzRules->obj->auxParm1 >= 0)) {
+ int data_size = sizeof(float);
+
+ // First calculate the required total byte size
+ int total_byte_size = 0;
+ TriPrim *p_tp = ppg_vbo->tri_prim_head;
+ while (p_tp) {
+ total_byte_size += p_tp->nVert * 2 * data_size;
+ p_tp = p_tp->p_next; // pick up the next in chain
+ }
+
+ float *vbuf = (float *)malloc(total_byte_size);
+ p_tp = ppg_vbo->tri_prim_head;
+
+ if (ppg_vbo->data_type == DATA_TYPE_DOUBLE) { // DOUBLE to FLOAT
+ float *p_run = vbuf;
+ while (p_tp) {
+ float *pfbuf = p_run;
+ for (int i = 0; i < p_tp->nVert * 2; ++i) {
+ float x = (float)(p_tp->p_vertex[i]);
+ *p_run++ = x;
+ }
+
+ free(p_tp->p_vertex);
+ p_tp->p_vertex = (double *)pfbuf;
+
+ p_tp = p_tp->p_next; // pick up the next in chain
+ }
+ } else { // FLOAT to FLOAT
+ float *p_run = vbuf;
+ while (p_tp) {
+ memcpy(p_run, p_tp->p_vertex, p_tp->nVert * 2 * sizeof(float));
+
+ free(p_tp->p_vertex);
+ p_tp->p_vertex = (double *)p_run;
+
+ p_run += p_tp->nVert * 2 * sizeof(float);
+
+ p_tp = p_tp->p_next; // pick up the next in chain
+ }
+ }
+
+ ppg_vbo->bsingle_alloc = true;
+ ppg_vbo->single_buffer = (unsigned char *)vbuf;
+ ppg_vbo->single_buffer_size = total_byte_size;
+ ppg_vbo->data_type = DATA_TYPE_FLOAT;
+ }
+
+ if (b_useVBO) {
+ // Has a VBO been built for this object?
+ if (1) {
+ if (rzRules->obj->auxParm0 <= 0) {
+ GLuint vboId;
+ // generate a new VBO and get the associated ID
+ glGenBuffers(1, &vboId);
+
+ rzRules->obj->auxParm0 = vboId;
+
+ // bind VBO in order to use
+ glBindBuffer(GL_ARRAY_BUFFER, vboId);
+
+ // upload data to VBO
+ glBufferData(GL_ARRAY_BUFFER, ppg_vbo->single_buffer_size,
+ ppg_vbo->single_buffer, GL_STATIC_DRAW);
+
+ } else {
+ glBindBuffer(GL_ARRAY_BUFFER, rzRules->obj->auxParm0);
+ }
+ }
+ }
+
+ PolyTriGroup *ppg = rzRules->obj->pPolyTessGeo->Get_PolyTriGroup_head();
+ double ppg_scale_factor = 1.0;
+ if (rzRules->obj->m_chart_context->chart) // This is not a plugin chart
+ ppg_scale_factor = ppg->sfactor;
+
+ TriPrim *p_tp = ppg->tri_prim_head;
+ GLintptr vbo_offset = 0;
+
+ // Set up the stride sizes for the array
+ int array_data_size = sizeof(float);
+ GLint array_gl_type = GL_FLOAT;
+
+ if (ppg->data_type == DATA_TYPE_DOUBLE) {
+ array_data_size = sizeof(double);
+ array_gl_type = GL_DOUBLE;
+ }
+
+ if (ppg->data_type == DATA_TYPE_SHORT) {
+ array_data_size = sizeof(short);
+ array_gl_type = GL_SHORT;
+ }
+
+ GLint program = S52AP_shader_program;
+ glUseProgram(program);
+
+ // Disable VBO's (vertex buffer objects) for attributes.
+ if (!b_useVBO) glBindBuffer(GL_ARRAY_BUFFER, 0);
+ glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
+
+ GLint pos = glGetAttribLocation(program, "position");
+ glEnableVertexAttribArray(pos);
+
+ // Select the active texture unit.
+ glActiveTexture(GL_TEXTURE0);
+
+ // Bind our texture to the texturing target.
+ glBindTexture(GL_TEXTURE_2D, ppatt_spec->OGL_tex_name);
+
+ // Set up the texture sampler to texture unit 0
+ GLint texUni = glGetUniformLocation(program, "uTex");
+ glUniform1i(texUni, 0);
+
+ GLint texWidth = glGetUniformLocation(program, "texWidth");
+ GLint texHeight = glGetUniformLocation(program, "texHeight");
+ glUniform1f(texWidth, textureWidth);
+ glUniform1f(texHeight, textureHeight);
+
+ GLint texPOTWidth = glGetUniformLocation(program, "texPOTWidth");
+ GLint texPOTHeight = glGetUniformLocation(program, "texPOTHeight");
+ glUniform1f(texPOTWidth, ppatt_spec->w_pot);
+ glUniform1f(texPOTHeight, ppatt_spec->h_pot);
+
+ GLint xo = glGetUniformLocation(program, "xOff");
+ GLint yo = glGetUniformLocation(program, "yOff");
+
+ glUniform1f(xo, fmod(xOff, ppatt_spec->w_pot));
+ glUniform1f(yo, fmod(yOff, ppatt_spec->h_pot));
+
+ GLint yom = glGetUniformLocation(program, "yOffM");
+ glUniform1f(yom, yOff);
+
+ if (ppatt_spec->b_stagger) {
+ GLint staggerFact = glGetUniformLocation(program, "staggerFactor");
+ glUniform1f(staggerFact, 0.5);
+ }
+
+ float angle = 0;
+
+ // Build Transform matrix
+ mat4x4 I, Q;
+ mat4x4_identity(I);
+
+ // Scale
+ I[0][0] *= rzRules->obj->x_rate;
+ I[1][1] *= rzRules->obj->y_rate;
+
+ // Translate
+ I[3][0] = -(rzRules->sm_transform_parms->easting_vp_center -
+ rzRules->obj->x_origin) *
+ vp_plib.view_scale_ppm;
+ I[3][1] = -(rzRules->sm_transform_parms->northing_vp_center -
+ rzRules->obj->y_origin) *
+ -vp_plib.view_scale_ppm;
+
+ // Scale
+ I[0][0] *= vp_plib.view_scale_ppm * ppg_scale_factor;
+ I[1][1] *= -vp_plib.view_scale_ppm * ppg_scale_factor;
+
+ // Rotate
+ mat4x4_rotate_Z(Q, I, angle);
+
+ // Translate
+ Q[3][0] += vp_plib.pix_width / 2;
+ Q[3][1] += vp_plib.pix_height / 2;
+
+ GLint matloc = glGetUniformLocation(program, "TransformMatrix");
+ glUniformMatrix4fv(matloc, 1, GL_FALSE, (const GLfloat *)Q);
+
+ if (!b_useVBO) {
+ float *bufBase = (float *)(&ppg->single_buffer[vbo_offset]);
+ glVertexAttribPointer(pos, 2, GL_FLOAT, GL_FALSE, 2 * sizeof(float),
+ bufBase);
+ }
+
+ while (p_tp) {
+ LLBBox box;
+ if (!rzRules->obj->m_chart_context->chart) { // This is a PlugIn Chart
+ LegacyTriPrim *p_ltp = (LegacyTriPrim *)p_tp;
+ box.Set(p_ltp->miny, p_ltp->minx, p_ltp->maxy, p_ltp->maxx);
+ } else
+ box = p_tp->tri_box;
+
+ if (!BBView.IntersectOut(box)) {
+ if (b_useVBO) {
+ glVertexAttribPointer(pos, 2, array_gl_type, GL_FALSE,
+ 0 /*2*sizeof(array_gl_type)*/,
+ (GLvoid *)(vbo_offset));
+ glDrawArrays(p_tp->type, 0, p_tp->nVert);
+ } else {
+ glDrawArrays(p_tp->type, vbo_offset / (2 * array_data_size),
+ p_tp->nVert);
+ }
+ }
+
+ vbo_offset += p_tp->nVert * 2 * array_data_size;
+
+ // pick up the next in chain
+ if (!rzRules->obj->m_chart_context->chart) { // This is a PlugIn Chart
+ LegacyTriPrim *p_ltp = (LegacyTriPrim *)p_tp;
+ p_tp = (TriPrim *)p_ltp->p_next;
+ } else
+ p_tp = p_tp->p_next;
+
+ } // while
+
+ if (b_useVBO) glBindBuffer(GL_ARRAY_BUFFER_ARB, 0);
+
+ // Restore the transform matrix to identity
+ mat4x4 IM;
+ mat4x4_identity(IM);
+ GLint matlocf =
+ glGetUniformLocation(S52color_tri_shader_program, "TransformMatrix");
+ glUniformMatrix4fv(matlocf, 1, GL_FALSE, (const GLfloat *)IM);
+ glUseProgram(0);
+ glDisableVertexAttribArray(pos);
+
+ } // if pPolyTessGeo
+
+ glDisable(GL_TEXTURE_2D);
+
+ return 1;
+}
+#endif
+
+#ifdef ocpnUSE_GL
+
+int s52plib::RenderAreaToGL(const wxGLContext &glcc, ObjRazRules *rzRules) {
+ if (!ObjectRenderCheckRules(rzRules, true)) return 0;
+
+#ifdef __OCPN__ANDROID__
+ // Catch a difficult to trap SIGSEGV, avoiding app crash
+ auto rzRules_current = rzRules; // Save current rules pointer
+ sigaction(SIGSEGV, NULL,
+ &sa_all_plib_previous); // save existing action for this signal
+
+ struct sigaction temp;
+ sigaction(SIGSEGV, NULL,
+ &temp); // inspect existing action for this signal
+
+ temp.sa_handler = catch_signals_plib; // point to my handler
+ sigemptyset(&temp.sa_mask); // make the blocking set
+ // empty, so that all
+ // other signals will be
+ // unblocked during my handler
+ temp.sa_flags = 0;
+ sigaction(SIGSEGV, &temp, NULL);
+
+ if (sigsetjmp(env_plib, 1)) // Something in the
+ // code block below this on caused SIGSEGV...
+ {
+ // reset signal handler
+ sigaction(SIGSEGV, &sa_all_plib_previous, NULL);
+ rzRules_current->obj->bOK_to_Render = false; //Mark object unrenderable
+ return 0;
+ } else
+#endif
+ {
+ Rules *rules = rzRules->LUP->ruleList;
+
+ while (rules != NULL) {
+ switch (rules->ruleType) {
+ case RUL_ARE_CO:
+ RenderToGLAC(rzRules, rules);
+ break; // AC
+
+ case RUL_ARE_PA:
+ RenderToGLAP(rzRules, rules);
+ break; // AP
+
+ case RUL_CND_SY: {
+ {
+ if (!rzRules->obj->bCS_Added) {
+ rzRules->obj->CSrules = NULL;
+ GetAndAddCSRules(rzRules, rules);
+ rzRules->obj->bCS_Added = 1; // mark the object
+ }
+ Rules *rules_last = rules;
+ rules = rzRules->obj->CSrules;
+
+ while (NULL != rules) {
+ switch (rules->ruleType) {
+ case RUL_ARE_CO:
+ RenderToGLAC(rzRules, rules);
+ break;
+ case RUL_ARE_PA:
+ RenderToGLAP(rzRules, rules);
+ break;
+ case RUL_NONE:
+ default:
+ break; // no rule type (init)
+ }
+ rules_last = rules;
+ rules = rules->next;
+ }
+
+ rules = rules_last;
+ break;
+ }
+ }
+
+ case RUL_NONE:
+ default:
+ break; // no rule type (init)
+ } // switch
+
+ rules = rules->next;
+ }
+ }
+#ifdef __OCPN__ANDROID__
+ // reset signal handler
+ sigaction(SIGSEGV, &sa_all_plib_previous, NULL);
+#endif
+
+ return 1;
+}
+#endif
+
+render_canvas_parms *s52plib::CreatePatternBufferSpec(ObjRazRules *rzRules,
+ Rules *rules,
+ bool b_revrgb,
+ bool b_pot) {
+ wxImage Image;
+
+ Rule *prule = rules->razRule;
+
+ bool bstagger_pattern = (prule->fillType.PATP == 'S');
+
+ wxColour local_unused_wxColor = m_unused_wxColor;
+
+ // Create a wxImage of the pattern drawn on an "unused_color" field
+ if (prule->definition.SYDF == 'R') {
+ Image = m_chartSymbols.GetImage(prule->name.PANM);
+ }
+
+ else // Vector
+ {
+ float fsf = 100 / canvas_pix_per_mm;
+
+ // Base bounding box
+ BoundingBox box(
+ prule->pos.patt.bnbox_x.PBXC, prule->pos.patt.bnbox_y.PBXR,
+ prule->pos.patt.bnbox_x.PBXC + prule->pos.patt.bnbox_w.PAHL,
+ prule->pos.patt.bnbox_y.PBXR + prule->pos.patt.bnbox_h.PAVL);
+
+ // Expand to include pivot
+ box.Expand(prule->pos.patt.pivot_x.PACL, prule->pos.patt.pivot_y.PARW);
+
+ // Pattern bounding boxes may be offset from origin, to preset the
+ // spacing So, the bitmap must be delta based.
+ double dwidth = box.GetWidth();
+ double dheight = box.GetHeight();
+
+ // Add in the pattern spacing parameters
+ dwidth += prule->pos.patt.minDist.PAMI;
+ dheight += prule->pos.patt.minDist.PAMI;
+
+ // Prescale
+ dwidth /= fsf;
+ dheight /= fsf;
+
+ int width = (int)dwidth + 1;
+ int height = (int)dheight + 1;
+
+ // float render_scale = 1.0;
+#ifdef sUSE_ANDROID_GLES2
+ int width_pot = width;
+ int height_pot = height;
+ if (b_pot) {
+ int xp = width;
+ if (((xp != 0) && !(xp & (xp - 1)))) // detect POT
+ width_pot = xp;
+ else {
+ int a = 0;
+ while (xp) {
+ xp = xp >> 1;
+ a++;
+ }
+ width_pot = 1 << a;
+ }
+
+ xp = height;
+ if (((xp != 0) && !(xp & (xp - 1))))
+ height_pot = xp;
+ else {
+ int a = 0;
+ while (xp) {
+ xp = xp >> 1;
+ a++;
+ }
+ height_pot = 1 << a;
+ }
+ }
+
+ // adjust scaler
+ render_scale = (float)height_pot / (float)height;
+ qDebug() << "first" << width << width_pot << height << height_pot
+ << render_scale;
+
+ width = width_pot;
+ height = height_pot;
+#endif
+
+ // Instantiate the vector pattern to a wxBitmap
+ wxMemoryDC mdc;
+
+// TODO
+// This ought to work for wxOSX, but DOES NOT.
+// We we do not want anti-aliased lines drawn in the pattern spec, since we used
+// the solid primary color as a mask for manual blitting from the dc to memory
+// buffer.
+
+// Best we can do is set the background color very dark, and hope for the best
+#ifdef __WXOSX__
+#if wxUSE_GRAPHICS_CONTEXT
+ wxGraphicsContext *pgc = mdc.GetGraphicsContext();
+ if (pgc) pgc->SetAntialiasMode(wxANTIALIAS_NONE);
+#endif
+ local_unused_wxColor.Set(2, 2, 2);
+#endif
+
+ wxBitmap *pbm = NULL;
+
+ if ((0 != width) && (0 != height)) {
+ pbm = new wxBitmap(width, height);
+
+ mdc.SelectObject(*pbm);
+ mdc.SetBackground(wxBrush(local_unused_wxColor));
+ mdc.Clear();
+
+ int pivot_x = prule->pos.patt.pivot_x.PACL;
+ int pivot_y = prule->pos.patt.pivot_y.PARW;
+
+ char *str = prule->vector.LVCT;
+ char *col = prule->colRef.LCRF;
+ wxPoint pivot(pivot_x, pivot_y);
+
+ int origin_x = prule->pos.patt.bnbox_x.PBXC;
+ int origin_y = prule->pos.patt.bnbox_y.PBXR;
+ wxPoint origin(origin_x, origin_y);
+
+ wxPoint2DDouble r0(((pivot_x - box.GetMinX()) / fsf) + 1,
+ ((pivot_y - box.GetMinY()) / fsf) + 1);
+
+ HPGL->SetTargetDC(&mdc);
+ HPGL->SetVP(&vp_plib);
+ HPGL->Render(str, col, r0, pivot, origin, 1.0 /*render_scale*/, 0, false);
+
+ // mdc.SetPen( wxPen( wxColor(0, 0, 250), 1,
+ // wxPENSTYLE_SOLID ) );
+ // mdc.SetBrush(*wxTRANSPARENT_BRUSH);
+ // mdc.DrawRectangle(0,0, width-1, height-1);
+
+ } else {
+ pbm = new wxBitmap(2, 2); // substitute small, blank pattern
+ mdc.SelectObject(*pbm);
+ mdc.SetBackground(wxBrush(local_unused_wxColor));
+ mdc.Clear();
+ }
+
+ mdc.SelectObject(wxNullBitmap);
+
+ // Build a wxImage from the wxBitmap
+ Image = pbm->ConvertToImage();
+
+ delete pbm;
+ }
+
+ // Convert the wxImage to a populated render_canvas_parms struct
+
+ int sizey = Image.GetHeight();
+ int sizex = Image.GetWidth();
+
+ render_canvas_parms *patt_spec = new render_canvas_parms;
+ patt_spec->OGL_tex_name = 0;
+
+ if (b_pot) {
+ int xp = sizex;
+ if (((xp != 0) && !(xp & (xp - 1)))) // detect POT
+ patt_spec->w_pot = sizex;
+ else {
+ int a = 0;
+ while (xp) {
+ xp = xp >> 1;
+ a++;
+ }
+ patt_spec->w_pot = 1 << a;
+ }
+
+ xp = sizey;
+ if (((xp != 0) && !(xp & (xp - 1))))
+ patt_spec->h_pot = sizey;
+ else {
+ int a = 0;
+ while (xp) {
+ xp = xp >> 1;
+ a++;
+ }
+ patt_spec->h_pot = 1 << a;
+ }
+
+ } else {
+ patt_spec->w_pot = sizex;
+ patt_spec->h_pot = sizey;
+ }
+
+ patt_spec->depth = 32; // set the depth, always 32 bit
+
+ patt_spec->pb_pitch = ((patt_spec->w_pot * patt_spec->depth / 8));
+ patt_spec->lclip = 0;
+ patt_spec->rclip = patt_spec->w_pot - 1;
+ patt_spec->pix_buff =
+ (unsigned char *)malloc(patt_spec->h_pot * patt_spec->pb_pitch);
+
+ // Preset background
+ memset(patt_spec->pix_buff, 0, patt_spec->h_pot * patt_spec->pb_pitch);
+ patt_spec->width = sizex;
+ patt_spec->height = sizey;
+ patt_spec->x = 0;
+ patt_spec->y = 0;
+ patt_spec->b_stagger = bstagger_pattern;
+
+ unsigned char *pd0 = patt_spec->pix_buff;
+ unsigned char *pd;
+ unsigned char *ps0 = Image.GetData();
+ unsigned char *imgAlpha = NULL;
+ bool b_use_alpha = false;
+ if (Image.HasAlpha()) {
+ imgAlpha = Image.GetAlpha();
+ b_use_alpha = true;
+ }
+
+#if defined(__WXMAC__) || defined(__WXQT__)
+
+ if (prule->definition.SYDF == 'V') {
+ b_use_alpha = true;
+ imgAlpha = NULL;
+ }
+#endif
+
+ unsigned char primary_r = 0;
+ unsigned char primary_g = 0;
+ unsigned char primary_b = 0;
+ double reference_value = 0.5;
+
+ bool b_filter = false;
+#if defined(__WXMAC__) || defined(__WXGTK3__)
+ S52color *primary_color = 0;
+ if (prule->definition.SYDF == 'V') {
+ b_filter = true;
+ char *col = prule->colRef.LCRF;
+ primary_color = getColor(col + 1);
+ if (primary_color) {
+ primary_r = primary_color->R;
+ primary_g = primary_color->G;
+ primary_b = primary_color->B;
+ wxImage::RGBValue rgb(primary_r, primary_g, primary_b);
+ wxImage::HSVValue hsv = wxImage::RGBtoHSV(rgb);
+ reference_value = hsv.value;
+ }
+ }
+#endif
+
+ unsigned char *ps;
+
+ {
+ unsigned char mr = local_unused_wxColor.Red();
+ unsigned char mg = local_unused_wxColor.Green();
+ unsigned char mb = local_unused_wxColor.Blue();
+
+ if (pd0 && ps0) {
+ for (int iy = 0; iy < sizey; iy++) {
+#ifdef __OCPN__ANDROID__
+ pd = pd0 + ((sizey - iy - 1) * patt_spec->pb_pitch);
+#else
+ pd = pd0 + (iy * patt_spec->pb_pitch);
+#endif
+ ps = ps0 + (iy * sizex * 3);
+ for (int ix = 0; ix < sizex; ix++) {
+ if (ix < sizex) {
+ unsigned char r = *ps++;
+ unsigned char g = *ps++;
+ unsigned char b = *ps++;
+
+ if (b_filter) {
+ wxImage::RGBValue rgb(r, g, b);
+ wxImage::HSVValue hsv = wxImage::RGBtoHSV(rgb);
+ double ratio = hsv.value / reference_value;
+
+ if (ratio > 0.5) {
+ *pd++ = primary_r;
+ *pd++ = primary_g;
+ *pd++ = primary_b;
+ *pd++ = 255;
+ } else {
+ *pd++ = 0;
+ *pd++ = 0;
+ *pd++ = 0;
+ *pd++ = 0;
+ }
+ } else {
+#ifdef ocpnUSE_ocpnBitmap
+ if (b_revrgb) {
+ *pd++ = b;
+ *pd++ = g;
+ *pd++ = r;
+ } else {
+ *pd++ = r;
+ *pd++ = g;
+ *pd++ = b;
+ }
+
+#else
+ *pd++ = r;
+ *pd++ = g;
+ *pd++ = b;
+#endif
+ if (b_use_alpha && imgAlpha) {
+ *pd++ = *imgAlpha++;
+ } else {
+ *pd++ = ((r == mr) && (g == mg) && (b == mb) ? 0 : 255);
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ return patt_spec;
+}
+
+int s52plib::RenderToBufferAP(ObjRazRules *rzRules, Rules *rules,
+ render_canvas_parms *pb_spec) {
+ // if (vp->m_projection_type != PROJECTION_MERCATOR) return 1;
+
+ wxImage Image;
+
+ if (rules->razRule == NULL) return 0;
+
+ if ((rules->razRule->pixelPtr == NULL) ||
+ (rules->razRule->parm1 != m_colortable_index) ||
+ (rules->razRule->parm0 != ID_RGB_PATT_SPEC)) {
+ render_canvas_parms *patt_spec =
+ CreatePatternBufferSpec(rzRules, rules, true);
+
+ ClearRulesCache(
+ rules->razRule); // Clear out any existing cached symbology
+
+ rules->razRule->pixelPtr = patt_spec;
+ rules->razRule->parm1 = m_colortable_index;
+ rules->razRule->parm0 = ID_RGB_PATT_SPEC;
+
+ } // Instantiation done
+
+ // Render the Area using the pattern spec stored in the rules
+ render_canvas_parms *ppatt_spec =
+ (render_canvas_parms *)rules->razRule->pixelPtr;
+
+ // Set the pattern reference point
+
+ wxPoint r;
+ GetPointPixSingle(rzRules, rzRules->obj->y, rzRules->obj->x, &r);
+
+ ppatt_spec->x =
+ r.x - 2000000; // bias way down to avoid zero-crossing logic in dda
+ ppatt_spec->y = r.y - 2000000;
+
+ RenderToBufferFilledPolygon(rzRules, rzRules->obj, NULL, pb_spec, ppatt_spec);
+
+ return 1;
+}
+
+int s52plib::RenderToBufferAC(ObjRazRules *rzRules, Rules *rules,
+ render_canvas_parms *pb_spec) {
+ // if (vp->m_projection_type != PROJECTION_MERCATOR) return 1;
+
+ S52color *c;
+ char *str = (char *)rules->INSTstr;
+
+ c = getColor(str);
+
+ RenderToBufferFilledPolygon(rzRules, rzRules->obj, c, pb_spec, NULL);
+
+ // At very small scales, the object could be visible on both the left and
+ // right sides of the screen. Identify this case......
+ if (vp_plib.chart_scale > 5e7) {
+ // Does the object hang out over the left side of the VP?
+ if ((rzRules->obj->BBObj.GetMaxLon() > GetBBox().GetMinLon()) &&
+ (rzRules->obj->BBObj.GetMinLon() < GetBBox().GetMinLon())) {
+ // If we add 360 to the objects lons, does it intersect the the right
+ // side of the VP?
+ if (((rzRules->obj->BBObj.GetMaxLon() + 360.) > GetBBox().GetMaxLon()) &&
+ ((rzRules->obj->BBObj.GetMinLon() + 360.) < GetBBox().GetMaxLon())) {
+ // If so, this area oject should be drawn again, this time for the left
+ // side
+ // Do this by temporarily adjusting the objects rendering offset
+ rzRules->obj->x_origin -=
+ mercator_k0 * WGS84_semimajor_axis_meters * 2.0 * PI;
+ RenderToBufferFilledPolygon(rzRules, rzRules->obj, c, pb_spec, NULL);
+ rzRules->obj->x_origin +=
+ mercator_k0 * WGS84_semimajor_axis_meters * 2.0 * PI;
+ }
+ }
+ }
+
+ return 1;
+}
+
+int s52plib::RenderAreaToDC(wxDC *pdcin, ObjRazRules *rzRules,
+ render_canvas_parms *pb_spec) {
+ if (!ObjectRenderCheckRules(rzRules, true)) return 0;
+
+ m_pdc = pdcin; // use this DC
+ Rules *rules = rzRules->LUP->ruleList;
+
+ while (rules != NULL) {
+ switch (rules->ruleType) {
+ case RUL_ARE_CO:
+ RenderToBufferAC(rzRules, rules, pb_spec);
+ break; // AC
+ case RUL_ARE_PA:
+ RenderToBufferAP(rzRules, rules, pb_spec);
+ break; // AP
+
+ case RUL_CND_SY: {
+ if (!rzRules->obj->bCS_Added) {
+ rzRules->obj->CSrules = NULL;
+ GetAndAddCSRules(rzRules, rules);
+ rzRules->obj->bCS_Added = 1; // mark the object
+ }
+ Rules *rules_last = rules;
+ rules = rzRules->obj->CSrules;
+
+ // The CS procedure may have changed the Display Category of the
+ // Object, need to check again for visibility
+ if (ObjectRenderCheckCat(rzRules)) {
+ while (NULL != rules) {
+ // Hve seen drgare fault here, need to code area query to debug
+ // possible that RENDERtoBUFFERAP/AC is blowing obj->CSRules
+ // When it faults here, look at new debug field obj->CSLUP
+ switch (rules->ruleType) {
+ case RUL_ARE_CO:
+ RenderToBufferAC(rzRules, rules, pb_spec);
+ break;
+ case RUL_ARE_PA:
+ RenderToBufferAP(rzRules, rules, pb_spec);
+ break;
+ case RUL_NONE:
+ default:
+ break; // no rule type (init)
+ }
+ rules_last = rules;
+ rules = rules->next;
+ }
+ }
+
+ rules = rules_last;
+ break;
+ }
+
+ case RUL_NONE:
+ default:
+ break; // no rule type (init)
+ } // switch
+
+ rules = rules->next;
+ }
+
+ return 1;
+}
+
+void s52plib::GetAndAddCSRules(ObjRazRules *rzRules, Rules *rules) {
+ LUPrec *NewLUP;
+ LUPrec *LUP;
+ LUPrec *LUPCandidate;
+ wxString cs_string;
+
+ char *rule_str1 = RenderCS(rzRules, rules);
+ if (rule_str1) cs_string = wxString(rule_str1, wxConvUTF8);
+ free(rule_str1); // delete rule_str1;
+
+ // Try to find a match for this object/attribute set in dynamic CS LUP Table
+
+ // Do this by checking each LUP in the CS LUPARRAY and checking....
+ // a) is Object Name the same? and
+ // b) was LUP created earlier by exactly the same INSTruction string?
+ // c) does LUP have same Display Category and Priority?
+
+ wxArrayOfLUPrec *la = condSymbolLUPArray;
+ int index = 0;
+ int index_max = la->GetCount();
+ LUP = NULL;
+
+ while ((index < index_max)) {
+ LUPCandidate = la->Item(index);
+ if (!strcmp(rzRules->LUP->OBCL, LUPCandidate->OBCL)) {
+ if (LUPCandidate->INST.IsSameAs(cs_string)) {
+ if (LUPCandidate->DISC == rzRules->LUP->DISC) {
+ LUP = LUPCandidate;
+ break;
+ }
+ }
+ }
+ index++;
+ }
+
+ // If not found, need to create a dynamic LUP and add to CS LUP Table
+
+ if (NULL == LUP) // Not found
+ {
+ NewLUP = new LUPrec();
+ NewLUP->DISC = rzRules->LUP->DISC; // as a default
+
+ memcpy(NewLUP->OBCL, rzRules->LUP->OBCL, 6); // the object class name
+
+ // Add the complete CS string to the LUP
+ if (cs_string.Length()) NewLUP->INST = cs_string;
+
+ _LUP2rules(NewLUP, rzRules->obj);
+
+ // Add LUP to array
+ wxArrayOfLUPrec *pLUPARRAYtyped = condSymbolLUPArray;
+
+ pLUPARRAYtyped->Add(NewLUP);
+
+ LUP = NewLUP;
+
+ } // if (LUP = NULL)
+
+ Rules *top = LUP->ruleList;
+
+ rzRules->obj->CSrules = top; // patch in a new rule set
+}
+
+bool s52plib::ObjectRenderCheck(ObjRazRules *rzRules) {
+ if (!ObjectRenderCheckPos(rzRules)) return false;
+
+ if (!ObjectRenderCheckCat(rzRules)) return false;
+
+ return true;
+}
+
+bool s52plib::ObjectRenderCheckCS(ObjRazRules *rzRules) {
+ // We need to do this test since some CS procedures change the display
+ // category So we need to tentatively process all objects with CS LUPs
+ Rules *rules = rzRules->LUP->ruleList;
+ while (rules != NULL) {
+ if (RUL_CND_SY == rules->ruleType) return true;
+
+ rules = rules->next;
+ }
+
+ return false;
+}
+
+bool s52plib::ObjectRenderCheckPos(ObjRazRules *rzRules) {
+ if (rzRules->obj == NULL) return false;
+
+ // Of course, the object must be at least partly visible in the VPointCompat
+ const LLBBox &vpBox = GetBBox();
+ const LLBBox &testBox = rzRules->obj->BBObj;
+
+ if (vpBox.GetMaxLat() < testBox.GetMinLat() ||
+ vpBox.GetMinLat() > testBox.GetMaxLat())
+ return false;
+
+ if (vpBox.GetMaxLon() >= testBox.GetMinLon() &&
+ vpBox.GetMinLon() <= testBox.GetMaxLon())
+ return true;
+
+ if (vpBox.GetMaxLon() >= testBox.GetMinLon() + 360 &&
+ vpBox.GetMinLon() <= testBox.GetMaxLon() + 360)
+ return true;
+
+ if (vpBox.GetMaxLon() >= testBox.GetMinLon() - 360 &&
+ vpBox.GetMinLon() <= testBox.GetMaxLon() - 360)
+ return true;
+
+ return false;
+}
+
+bool s52plib::ObjectRenderCheckPosReduced(ObjRazRules *rzRules) {
+ if (rzRules->obj == NULL) return false;
+
+ // Of course, the object must be at least partly visible in the VPointCompat
+ LLBBox vpBox = GetReducedBBox();
+ const LLBBox &testBox = rzRules->obj->BBObj;
+
+ if (vpBox.GetMaxLat() < testBox.GetMinLat() ||
+ vpBox.GetMinLat() > testBox.GetMaxLat())
+ return false;
+
+ if (vpBox.GetMaxLon() >= testBox.GetMinLon() &&
+ vpBox.GetMinLon() <= testBox.GetMaxLon())
+ return true;
+
+ if (vpBox.GetMaxLon() >= testBox.GetMinLon() + 360 &&
+ vpBox.GetMinLon() <= testBox.GetMaxLon() + 360)
+ return true;
+
+ if (vpBox.GetMaxLon() >= testBox.GetMinLon() - 360 &&
+ vpBox.GetMinLon() <= testBox.GetMaxLon() - 360)
+ return true;
+
+ return false;
+}
+
+bool s52plib::ObjectRenderCheckCat(ObjRazRules *rzRules) {
+ g_scaminScale = 1.0;
+
+ if (rzRules->obj == NULL) return false;
+
+ bool b_catfilter = true;
+ bool b_visible = false;
+
+ // Do Object Type Filtering
+ DisCat obj_cat = rzRules->obj->m_DisplayCat;
+
+ // Meta object filter.
+ // Applied when showing display category OTHER, and
+ // only for objects whose decoded S52 display category (by LUP) is also OTHER
+ if (m_nDisplayCategory == OTHER) {
+ if (OTHER == obj_cat) {
+ if (!strncmp(rzRules->LUP->OBCL, "M_", 2)) {
+ if (!m_bShowMeta)
+ return false;
+ else {
+ if (!strncmp(rzRules->LUP->OBCL, "M_QUAL", 6) && !m_qualityOfDataOn)
+ return false;
+ }
+ }
+ }
+ } else {
+ // We want to filter out M_NSYS objects everywhere except "OTHER" category
+ if (!strncmp(rzRules->LUP->OBCL, "M_", 2))
+ if (!m_bShowMeta) return false;
+ }
+
+#ifdef __OCPN__ANDROID__
+ // We want to filter out M_NSYS objects on Android, as they are of limited use
+ // on a phone/tablet
+ if (!strncmp(rzRules->LUP->OBCL, "M_", 2))
+ if (!m_bShowMeta) return false;
+#endif
+
+ if (m_nDisplayCategory == MARINERS_STANDARD) {
+ if (-1 == rzRules->obj->iOBJL) UpdateOBJLArray(rzRules->obj);
+
+ if (DISPLAYBASE == obj_cat) { // always display individual objects that
+ // were moved to DISPLAYBASE by CS Procedures
+ b_visible = true;
+ b_catfilter = false;
+ } else if (!((OBJLElement *)(pOBJLArray->Item(rzRules->obj->iOBJL)))
+ ->nViz) {
+ b_catfilter = false;
+ }
+ }
+
+ else if (m_nDisplayCategory == OTHER) {
+ if ((DISPLAYBASE != obj_cat) && (STANDARD != obj_cat) &&
+ (OTHER != obj_cat)) {
+ b_catfilter = false;
+ }
+ }
+
+ else if (m_nDisplayCategory == STANDARD) {
+ if ((DISPLAYBASE != obj_cat) && (STANDARD != obj_cat)) {
+ b_catfilter = false;
+ }
+ } else if (m_nDisplayCategory == DISPLAYBASE) {
+ if (DISPLAYBASE != obj_cat) {
+ b_catfilter = false;
+ }
+ }
+
+ // Soundings override
+ if (!strncmp(rzRules->LUP->OBCL, "SOUNDG", 6)) b_catfilter = m_bShowSoundg;
+
+ if (b_catfilter) {
+ b_visible = true;
+
+ // SCAMIN Filtering
+ // Implementation note:
+ // According to S52 specs, SCAMIN must not apply to GROUP1 objects,
+ // Meta Objects or DisplayCategoryBase objects. Occasionally, an ENC
+ // will encode a spurious SCAMIN value for one of these objects. see,
+ // for example, US5VA18M, in OpenCPN SENC as Feature 350(DEPARE), LNAM
+ // = 022608187ED20ACC. We shall explicitly ignore SCAMIN filtering for
+ // these types of objects.
+
+ if (m_bUseSCAMIN) {
+ if ((DISPLAYBASE == rzRules->LUP->DISC) ||
+ (PRIO_GROUP1 == rzRules->LUP->DPRI))
+ b_visible = true;
+ else {
+ // if( vp->chart_scale > rzRules->obj->Scamin ) b_visible
+ // = false;
+
+ double zoom_mod = (double)m_chart_zoom_modifier_vector;
+
+ double modf = zoom_mod / 5.; // -1->1
+ double mod = pow(8., modf);
+ mod = wxMax(mod, .2);
+ mod = wxMin(mod, 8.0);
+
+ if (mod > 1) {
+ if (vp_plib.chart_scale > rzRules->obj->Scamin * mod)
+ b_visible = false; // definitely invisible
+ else {
+ // Theoretically invisible, however...
+ // In the "zoom modified" scale region,
+ // we render the symbol at reduced size, scaling down to no less
+ // than half normal size.
+
+ if (vp_plib.chart_scale > rzRules->obj->Scamin) {
+ double xs = vp_plib.chart_scale - rzRules->obj->Scamin;
+ double xl = (rzRules->obj->Scamin * mod) - rzRules->obj->Scamin;
+ g_scaminScale = 1.0 - (0.5 * xs / xl);
+ }
+ }
+ } else {
+ if (vp_plib.chart_scale > rzRules->obj->Scamin) b_visible = false;
+ }
+ }
+
+ // Check for SUPER_SCAMIN, apply if enabled
+ if (m_bUseSUPER_SCAMIN) {
+ if (rzRules->obj->SuperScamin < 0) {
+ if ((strncmp(rzRules->obj->FeatureName, "LNDARE", 6) &&
+ strncmp(rzRules->obj->FeatureName, "DEPARE", 6) &&
+ strncmp(rzRules->obj->FeatureName, "SWPARE", 6) &&
+ strncmp(rzRules->obj->FeatureName, "RECTRK", 6) &&
+ strncmp(rzRules->obj->FeatureName, "TSS", 3) &&
+ strncmp(rzRules->obj->FeatureName, "TSEZNE", 6) &&
+ strncmp(rzRules->obj->FeatureName, "DRGARE", 6) &&
+ strncmp(rzRules->obj->FeatureName, "COALNE", 6)) ||
+ (!strncmp(rzRules->obj->FeatureName, "LNDARE", 6) &&
+ (rzRules->LUP->ruleList->ruleType != RUL_ARE_CO))) {
+ double chart_ref_scale = rzRules->obj->m_chart_context->chart_scale;
+
+ // Is the ENC cell SCAMIN for this object un-defined?
+ if (rzRules->obj->Scamin >
+ 1e8) { // undefined default value is 1e8+2
+ // Get the scale of the ENC, and establish SUPERSCAMIN
+ double super_scamin = chart_ref_scale * 4;
+ rzRules->obj->SuperScamin = super_scamin;
+ }
+ if (rzRules->obj->Scamin >
+ 9e6) { // Presumed undefined value for Greek ENC Lights
+ // Get the scale of the ENC, and establish SUPERSCAMIN
+ double super_scamin = chart_ref_scale * 2;
+ rzRules->obj->SuperScamin = super_scamin;
+ }
+ if (!strncmp(rzRules->obj->FeatureName, "SOUNDG", 6)) {
+ if (rzRules->obj->Scamin >
+ 4e6) { // Presumed undefined value for Greek ENC soundings
+ // Get the scale of the ENC, and establish SUPERSCAMIN
+ double super_scamin = chart_ref_scale * 2;
+ rzRules->obj->SuperScamin = super_scamin;
+ }
+ }
+ }
+ }
+
+ // Make the test
+ if ((rzRules->obj->SuperScamin > 0) &&
+ (vp_plib.chart_scale > rzRules->obj->SuperScamin))
+ b_visible = false;
+ }
+
+ // On the other hand, $TEXTS features need not really be displayed at
+ // all scales, always To do so makes a very cluttered display
+ if ((!strncmp(rzRules->LUP->OBCL, "$TEXTS", 6)) &&
+ (vp_plib.chart_scale > rzRules->obj->Scamin))
+ b_visible = false;
+ }
+
+ return b_visible;
+ }
+
+ return b_visible;
+}
+
+bool s52plib::ObjectRenderCheckDates(ObjRazRules *rzRules) {
+ // Check DATSTA/DATEND attributes, if present
+ // DATSTA
+ wxString datsta = rzRules->obj->GetAttrValueAsString("DATSTA");
+ if (datsta.Len() > 0) {
+ bool bDateValid = false;
+
+ // CCYYMMDD
+ wxDateTime objDate;
+ wxString::const_iterator end;
+ if (objDate.ParseFormat(datsta, "%Y%m%d", &end)) {
+ if (end == datsta.end()) { // Require perfect parsing
+ if (objDate.IsValid()) bDateValid = true;
+ }
+ }
+ if (bDateValid) {
+ objDate.ResetTime(); // DATSTA specifications take effect at 00:01- on
+ // the specified date
+ wxDateTime now = wxDateTime::Now();
+ if (now.IsEarlierThan(objDate)) return false; // No Show
+ }
+ }
+
+ // DATEND
+ datsta = rzRules->obj->GetAttrValueAsString("DATEND");
+ if (datsta.Len() > 0) {
+ bool bDateValid = false;
+
+ // CCYYMMDD
+ wxDateTime objDate;
+ wxString::const_iterator end;
+ if (objDate.ParseFormat(datsta, "%Y%m%d", &end)) {
+ if (end == datsta.end()) { // Require perfect parsing
+ if (objDate.IsValid()) bDateValid = true;
+ }
+ }
+ if (bDateValid) {
+ objDate.ResetTime(); // DATEND specifications take effect at 23:59+ on
+ // the specified date.
+ objDate += wxTimeSpan(24);
+ wxDateTime now = wxDateTime::Now();
+ if (now.IsLaterThan(objDate)) return false; // No Show
+ }
+ }
+
+ // PEREND
+ datsta = rzRules->obj->GetAttrValueAsString("PEREND");
+ if ((datsta.Len() > 0) && !datsta.StartsWith(_T("--"))) {
+ bool bDateValid = false;
+
+ // CCYYMMDD
+ wxDateTime objDate;
+ wxString::const_iterator end;
+ if (objDate.ParseFormat(datsta, "%Y%m%d", &end)) {
+ if (end == datsta.end()) { // Require perfect parsing
+ if (objDate.IsValid()) bDateValid = true;
+ }
+ }
+ if (bDateValid) {
+ objDate.ResetTime(); // PEREND specifications take effect at 23:59+ on
+ // the specified date.
+ objDate += wxTimeSpan(24);
+ wxDateTime now = wxDateTime::Now();
+ if (now.IsLaterThan(objDate)) return false; // No Show
+ }
+ }
+
+ return true;
+}
+
+bool s52plib::ObjectRenderCheckRules(ObjRazRules *rzRules, bool check_noshow) {
+ if (!rzRules->obj->bOK_to_Render) return false;
+
+ if (!ObjectRenderCheckPos(rzRules)) return false;
+
+ // The Feature M_QUAL, in MARINERS_STANDARD catagory, is a special case,
+ // since it is also controlled by a global hotkey in display category ALL and
+ // MARINERS_STANDARD
+ if (m_nDisplayCategory == MARINERS_STANDARD) {
+ if (strncmp(rzRules->obj->FeatureName, "M_QUAL",
+ 6)) { // Anything other than M_QUAL
+ if (check_noshow && IsObjNoshow(rzRules->LUP->OBCL)) return 0;
+ } else {
+ if (!m_qualityOfDataOn) return 0;
+ }
+ } else {
+ if (check_noshow && IsObjNoshow(rzRules->LUP->OBCL)) return false;
+ }
+
+ if (ObjectRenderCheckCat(rzRules)) {
+ return ObjectRenderCheckDates(rzRules);
+ }
+
+ // If this object cannot be moved to a higher category by CS procedures,
+ // then we are done here
+ if (!rzRules->obj->m_bcategory_mutable) return false;
+
+ // already added, nothing below can change its display category
+ if (rzRules->obj->bCS_Added) return false;
+
+ // Otherwise, make sure the CS, if present, has been evaluated,
+ // and then check the category again
+ // no rules
+ if (!ObjectRenderCheckCS(rzRules)) return false;
+
+ rzRules->obj->CSrules = NULL;
+ Rules *rules = rzRules->LUP->ruleList;
+ while (rules != NULL) {
+ if (RUL_CND_SY == rules->ruleType) {
+ GetAndAddCSRules(rzRules, rules);
+ rzRules->obj->bCS_Added = 1; // mark the object
+ break;
+ }
+ rules = rules->next;
+ }
+
+ // still not displayable
+ if (!ObjectRenderCheckCat(rzRules)) return false;
+
+ return ObjectRenderCheckDates(rzRules);
+}
+
+void s52plib::SetDisplayCategory(enum _DisCat cat) {
+ enum _DisCat old = m_nDisplayCategory;
+ m_nDisplayCategory = cat;
+
+ if (old != cat) {
+ ClearNoshow();
+ }
+ GenerateStateHash();
+}
+
+bool s52plib::IsObjNoshow(const char *objcl) {
+ for (unsigned int i = 0; i < m_noshow_array.GetCount(); i++) {
+ if (!strncmp(m_noshow_array[i].obj, objcl, 6)) return true;
+ }
+ return false;
+}
+
+void s52plib::AddObjNoshow(const char *objcl) {
+ if (!IsObjNoshow(objcl)) {
+ noshow_element element;
+ memcpy(element.obj, objcl, 6);
+ m_noshow_array.Add(element);
+ }
+}
+
+void s52plib::RemoveObjNoshow(const char *objcl) {
+ for (unsigned int i = 0; i < m_noshow_array.GetCount(); i++) {
+ if (!strncmp(m_noshow_array[i].obj, objcl, 6)) {
+ m_noshow_array.RemoveAt(i);
+ return;
+ }
+ }
+}
+
+void s52plib::ClearNoshow(void) { m_noshow_array.Clear(); }
+
+#if 0
+void s52plib::PLIB_LoadS57Config() {
+ // Get a pointer to the opencpn configuration object
+ wxFileConfig *pconfig = GetOCPNConfigObject();
+
+ int read_int;
+ double dval;
+
+ pconfig->SetPath(_T ( "/Settings" ));
+ // pconfig->Read( _T ( "DebugS57" ), &g_PIbDebugS57, 0 ); // Show LUP
+ // and Feature info in object query
+
+ pconfig->SetPath(_T ( "/Settings/GlobalState" ));
+
+ pconfig->Read(_T ( "bShowS57Text" ), &read_int, 0);
+ SetShowS57Text(!(read_int == 0));
+
+ pconfig->Read(_T ( "bShowS57ImportantTextOnly" ), &read_int, 0);
+ SetShowS57ImportantTextOnly(!(read_int == 0));
+
+ pconfig->Read(_T ( "bShowLightDescription" ), &read_int, 0);
+ SetShowLdisText(!(read_int == 0));
+
+ pconfig->Read(_T ( "bExtendLightSectors" ), &read_int, 0);
+ SetExtendLightSectors(!(read_int == 0));
+
+ pconfig->Read(_T ( "nDisplayCategory" ), &read_int, (enum _DisCat)STANDARD);
+ SetDisplayCategory((enum _DisCat)read_int);
+
+ pconfig->Read(_T ( "nSymbolStyle" ), &read_int, (enum _LUPname)PAPER_CHART);
+ m_nSymbolStyle = (LUPname)read_int;
+
+ pconfig->Read(_T ( "nBoundaryStyle" ), &read_int, PLAIN_BOUNDARIES);
+ m_nBoundaryStyle = (LUPname)read_int;
+
+ pconfig->Read(_T ( "bShowSoundg" ), &read_int, 1);
+ m_bShowSoundg = !(read_int == 0);
+
+ pconfig->Read(_T ( "bShowMeta" ), &read_int, 0);
+ m_bShowMeta = !(read_int == 0);
+
+ pconfig->Read(_T ( "bUseSCAMIN" ), &read_int, 1);
+ m_bUseSCAMIN = !(read_int == 0);
+
+ pconfig->Read(_T ( "bShowAtonText" ), &read_int, 1);
+ m_bShowAtonText = !(read_int == 0);
+
+ pconfig->Read(_T ( "bDeClutterText" ), &read_int, 0);
+ m_bDeClutterText = !(read_int == 0);
+
+ pconfig->Read(_T ( "bShowNationalText" ), &read_int, 0);
+ m_bShowNationalTexts = !(read_int == 0);
+
+ if (pconfig->Read(_T ( "S52_MAR_SAFETY_CONTOUR" ), &dval, 5.0)) {
+ S52_setMarinerParam(S52_MAR_SAFETY_CONTOUR, dval);
+ S52_setMarinerParam(S52_MAR_SAFETY_DEPTH,
+ dval); // Set safety_contour and safety_depth the same
+ }
+
+ if (pconfig->Read(_T ( "S52_MAR_SHALLOW_CONTOUR" ), &dval, 3.0))
+ S52_setMarinerParam(S52_MAR_SHALLOW_CONTOUR, dval);
+
+ if (pconfig->Read(_T ( "S52_MAR_DEEP_CONTOUR" ), &dval, 10.0))
+ S52_setMarinerParam(S52_MAR_DEEP_CONTOUR, dval);
+
+ if (pconfig->Read(_T ( "S52_MAR_TWO_SHADES" ), &dval, 0.0))
+ S52_setMarinerParam(S52_MAR_TWO_SHADES, dval);
+
+ UpdateMarinerParams();
+
+ pconfig->SetPath(_T ( "/Settings/GlobalState" ));
+ pconfig->Read(_T ( "S52_DEPTH_UNIT_SHOW" ), &read_int,
+ 1); // default is metres
+ read_int = wxMax(read_int, 0); // qualify value
+ read_int = wxMin(read_int, 2);
+ m_nDepthUnitDisplay = read_int;
+
+ // S57 Object Class Visibility
+
+ OBJLElement *pOLE;
+
+ pconfig->SetPath(_T ( "/Settings/ObjectFilter" ));
+
+ int iOBJMax = pconfig->GetNumberOfEntries();
+ if (iOBJMax) {
+ wxString str;
+ long val;
+ long dummy;
+
+ wxString sObj;
+
+ bool bCont = pconfig->GetFirstEntry(str, dummy);
+ while (bCont) {
+ pconfig->Read(str, &val); // Get an Object Viz
+
+ bool bNeedNew = true;
+
+ if (str.StartsWith(_T ( "viz" ), &sObj)) {
+ for (unsigned int iPtr = 0; iPtr < pOBJLArray->GetCount(); iPtr++) {
+ pOLE = (OBJLElement *)(pOBJLArray->Item(iPtr));
+ if (!strncmp(pOLE->OBJLName, sObj.mb_str(), 6)) {
+ pOLE->nViz = val;
+ bNeedNew = false;
+ break;
+ }
+ }
+
+ if (bNeedNew) {
+ pOLE = (OBJLElement *)calloc(sizeof(OBJLElement), 1);
+ memcpy(pOLE->OBJLName, sObj.mb_str(), OBJL_NAME_LEN);
+ pOLE->nViz = 1;
+
+ pOBJLArray->Add((void *)pOLE);
+ }
+ }
+ bCont = pconfig->GetNextEntry(str, dummy);
+ }
+ }
+}
+#endif
+
+void s52plib::PLIB_LoadS57GlobalConfig(wxFileConfig *pconfig) {
+ int read_int;
+ double dval;
+
+ pconfig->SetPath(_T ( "/Settings" ));
+
+ pconfig->SetPath(_T ( "/Settings/GlobalState" ));
+
+ pconfig->Read(_T ( "bShowS57ImportantTextOnly" ), &read_int, 0);
+ SetShowS57ImportantTextOnly(!(read_int == 0));
+
+ pconfig->Read(_T ( "nSymbolStyle" ), &read_int, (enum _LUPname)PAPER_CHART);
+ m_nSymbolStyle = (LUPname)read_int;
+
+ pconfig->Read(_T ( "nBoundaryStyle" ), &read_int, PLAIN_BOUNDARIES);
+ m_nBoundaryStyle = (LUPname)read_int;
+
+ pconfig->Read(_T ( "bShowMeta" ), &read_int, 0);
+ m_bShowMeta = !(read_int == 0);
+
+ pconfig->Read(_T ( "bUseSCAMIN" ), &read_int, 1);
+ m_bUseSCAMIN = !(read_int == 0);
+
+ pconfig->Read(_T ( "bDeClutterText" ), &read_int, 0);
+ m_bDeClutterText = !(read_int == 0);
+
+ pconfig->Read(_T ( "bShowNationalText" ), &read_int, 0);
+ m_bShowNationalTexts = !(read_int == 0);
+
+ if (pconfig->Read(_T ( "S52_MAR_SAFETY_CONTOUR" ), &dval, 5.0)) {
+ S52_setMarinerParam(S52_MAR_SAFETY_CONTOUR, dval);
+ S52_setMarinerParam(S52_MAR_SAFETY_DEPTH,
+ dval); // Set safety_contour and safety_depth the same
+ }
+
+ if (pconfig->Read(_T ( "S52_MAR_SHALLOW_CONTOUR" ), &dval, 3.0))
+ S52_setMarinerParam(S52_MAR_SHALLOW_CONTOUR, dval);
+
+ if (pconfig->Read(_T ( "S52_MAR_DEEP_CONTOUR" ), &dval, 10.0))
+ S52_setMarinerParam(S52_MAR_DEEP_CONTOUR, dval);
+
+ if (pconfig->Read(_T ( "S52_MAR_TWO_SHADES" ), &dval, 0.0))
+ S52_setMarinerParam(S52_MAR_TWO_SHADES, dval);
+
+ UpdateMarinerParams();
+
+ pconfig->SetPath(_T ( "/Settings/GlobalState" ));
+ pconfig->Read(_T ( "S52_DEPTH_UNIT_SHOW" ), &read_int,
+ 1); // default is metres
+ read_int = wxMax(read_int, 0); // qualify value
+ read_int = wxMin(read_int, 2);
+ m_nDepthUnitDisplay = read_int;
+
+ // Load height unit display (meters/feet) from core HeightFormat if available
+ // Default to meters (0). Values correspond to HEIGHT_* enum: 0=M, 1=FT
+ if (pconfig->Read(_T ( "HeightFormat" ), &read_int, 0)) {
+ read_int = wxMax(read_int, 0);
+ read_int = wxMin(read_int, 1);
+ m_nHeightUnitDisplay = read_int;
+ } else {
+ m_nHeightUnitDisplay = 0;
+ }
+}
+
+void s52plib::PLIB_LoadS57ObjectConfig(wxFileConfig *pconfig) {
+ // S57 Object Class Visibility
+
+ OBJLElement *pOLE;
+
+ pconfig->SetPath(_T ( "/Settings/ObjectFilter" ));
+
+ int iOBJMax = pconfig->GetNumberOfEntries();
+ if (iOBJMax) {
+ wxString str;
+ long val;
+ long dummy;
+
+ wxString sObj;
+
+ bool bCont = pconfig->GetFirstEntry(str, dummy);
+ while (bCont) {
+ pconfig->Read(str, &val); // Get an Object Viz
+
+ bool bNeedNew = true;
+
+ if (str.StartsWith(_T ( "viz" ), &sObj)) {
+ for (unsigned int iPtr = 0; iPtr < pOBJLArray->GetCount(); iPtr++) {
+ pOLE = (OBJLElement *)(pOBJLArray->Item(iPtr));
+ if (!strncmp(pOLE->OBJLName, sObj.mb_str(), 6)) {
+ pOLE->nViz = val;
+ bNeedNew = false;
+ break;
+ }
+ }
+
+ if (bNeedNew) {
+ pOLE = (OBJLElement *)calloc(sizeof(OBJLElement), 1);
+ strncpy(pOLE->OBJLName, sObj.mb_str(), 6);
+ pOLE->nViz = 1;
+
+ pOBJLArray->Add((void *)pOLE);
+ }
+ }
+ bCont = pconfig->GetNextEntry(str, dummy);
+ }
+ }
+}
+
+// Do all those things necessary to prepare for a new rendering
+void s52plib::PrepareForRender(void) { PrepareForRender(&vp_plib); }
+
+void s52plib::PrepareForRender(VPointCompat *vp) {
+ m_benableGLLS = true; // default is to always use RenderToGLLS (VBO support)
+
+#ifdef ocpnUSE_GL
+ void PrepareS52ShaderUniforms(VPointCompat * vp);
+ if (m_useGLSL && vp) {
+ PrepareS52ShaderUniforms(vp);
+
+ // Prepare TexFont shader
+ for (unsigned int i = 0; i < TXF_CACHE; i++) {
+ if (s_txf[i].cache)
+ s_txf[i].cache->PrepareShader(vp->pix_width, vp->pix_height,
+ vp->rotation);
+ }
+ }
+#endif
+
+ float ChartScaleFactorExpNew = GetOCPNChartScaleFactor_Plugin();
+
+ if (ChartScaleFactorExpNew != m_ChartScaleFactorExp) {
+ // Clear some cached data to handle new scale value.
+
+ // Cached HPGL Textures
+ for (const auto &pair : vector_symbol_cache) {
+ const GLuint tex = pair.second;
+ glDeleteTextures(1, &tex);
+ }
+ vector_symbol_cache.clear();
+ }
+
+ m_ChartScaleFactorExp = ChartScaleFactorExpNew;
+
+ // Reset the LIGHTS declutter machine
+ lastLightLat = 0;
+ lastLightLon = 0;
+
+ // Precalulate the ENC scale factors
+ m_SoundingsScaleFactor = (m_nSoundingFactor * .1) +
+ 1; // exp(m_nSoundingFactor * (log(2.0) / 5.0));
+ m_TextScaleFactor = exp(m_nTextFactor * (log(2.0) / 5.0));
+}
+
+void s52plib::SetAnchorOn(bool val) {
+ const char *categories[] = {"ACHBRT", "ACHARE", "CBLSUB", "PIPARE",
+ "PIPSOL", "TUNNEL", "SBDARE"};
+ unsigned int num = sizeof(categories) / sizeof(categories[0]);
+
+ if ((m_nDisplayCategory == OTHER) ||
+ (m_nDisplayCategory == MARINERS_STANDARD)) {
+ bool bAnchor = val;
+
+ if (!bAnchor) {
+ for (unsigned int c = 0; c < num; c++) {
+ AddObjNoshow(categories[c]);
+ }
+ } else {
+ for (unsigned int c = 0; c < num; c++) {
+ RemoveObjNoshow(categories[c]);
+ }
+ }
+ } else { // if not category OTHER, then anchor-related features are always
+ // shown.
+ for (unsigned int c = 0; c < num; c++) {
+ RemoveObjNoshow(categories[c]);
+ }
+ }
+
+ m_anchorOn = val;
+}
+
+void s52plib::SetQualityOfData(bool val) {
+ if (!val) { // going off
+ AddObjNoshow("M_QUAL");
+ } else { // Off, going on
+ RemoveObjNoshow("M_QUAL");
+
+ for (unsigned int iPtr = 0; iPtr < pOBJLArray->GetCount(); iPtr++) {
+ OBJLElement *pOLE = (OBJLElement *)(pOBJLArray->Item(iPtr));
+ if (!strncmp(pOLE->OBJLName, "M_QUAL", 6)) {
+ pOLE->nViz = 1; // force on
+ break;
+ }
+ }
+ }
+
+ m_qualityOfDataOn = val;
+}
+
+void s52plib::ClearTextList(void) {
+ // Clear the current text rectangle list
+ m_textObjList.Clear();
+}
+
+bool s52plib::EnableGLLS(bool b_enable) {
+ bool return_val = m_benableGLLS;
+ m_benableGLLS = b_enable;
+ return return_val;
+}
+
+void s52plib::AdjustTextList(int dx, int dy, int screenw, int screenh) {
+ return;
+}
+
+bool s52plib::GetPointPixArray(ObjRazRules *rzRules, wxPoint2DDouble *pd,
+ wxPoint *pp, int nv) {
+ for (int i = 0; i < nv; i++) {
+ GetPointPixSingle(rzRules, pd[i].m_y, pd[i].m_x, pp + i);
+ }
+
+ return true;
+}
+
+bool s52plib::GetDoublePointPixArray(ObjRazRules *rzRules, wxPoint2DDouble *pd,
+ wxPoint2DDouble *pp, int nv) {
+ for (int i = 0; i < nv; i++) {
+ GetDoublePointPixSingle(rzRules, pd[i].m_y, pd[i].m_x, pp + i);
+ }
+
+ return true;
+}
+
+bool s52plib::GetPointPixSingle(ObjRazRules *rzRules, float north, float east,
+ wxPoint *r) {
+ // if (vp->m_projection_type == PROJECTION_MERCATOR)
+ {
+ double xr = rzRules->obj->x_rate;
+ double xo = rzRules->obj->x_origin;
+ double yr = rzRules->obj->y_rate;
+ double yo = rzRules->obj->y_origin;
+
+ if (fabs(xo) > 1) { // cm93 hits this
+ if (GetBBox().GetMaxLon() >= 180. &&
+ rzRules->obj->BBObj.GetMaxLon() < GetBBox().GetMinLon())
+ xo += mercator_k0 * WGS84_semimajor_axis_meters * 2.0 * PI;
+ else if ((GetBBox().GetMinLon() <= -180. &&
+ rzRules->obj->BBObj.GetMinLon() > GetBBox().GetMaxLon()) ||
+ (rzRules->obj->BBObj.GetMaxLon() >= 180 &&
+ GetBBox().GetMinLon() <= 0.))
+ xo -= mercator_k0 * WGS84_semimajor_axis_meters * 2.0 * PI;
+ }
+
+ double valx = (east * xr) + xo;
+ double valy = (north * yr) + yo;
+
+ r->x = roundint(((valx - rzRules->sm_transform_parms->easting_vp_center) *
+ vp_plib.view_scale_ppm) +
+ (vp_plib.pix_width / 2));
+ r->y = roundint((vp_plib.pix_height / 2) -
+ ((valy - rzRules->sm_transform_parms->northing_vp_center) *
+ vp_plib.view_scale_ppm));
+ }
+ // else {
+ // double lat, lon;
+ // fromSM_Plugin(east - rzRules->sm_transform_parms->easting_vp_center,
+ // north - rzRules->sm_transform_parms->northing_vp_center,
+ // vp->clat, vp->clon, &lat, &lon);
+ //
+ // *r = GetPixFromLL(north, east);
+ // }
+
+ return true;
+}
+
+bool s52plib::GetDoublePointPixSingle(ObjRazRules *rzRules, float north, float east,
+ wxPoint2DDouble *r) {
+ // if (vp->m_projection_type == PROJECTION_MERCATOR)
+ {
+ double xr = rzRules->obj->x_rate;
+ double xo = rzRules->obj->x_origin;
+ double yr = rzRules->obj->y_rate;
+ double yo = rzRules->obj->y_origin;
+
+ if (fabs(xo) > 1) { // cm93 hits this
+ if (GetBBox().GetMaxLon() >= 180. &&
+ rzRules->obj->BBObj.GetMaxLon() < GetBBox().GetMinLon())
+ xo += mercator_k0 * WGS84_semimajor_axis_meters * 2.0 * PI;
+ else if ((GetBBox().GetMinLon() <= -180. &&
+ rzRules->obj->BBObj.GetMinLon() > GetBBox().GetMaxLon()) ||
+ (rzRules->obj->BBObj.GetMaxLon() >= 180 &&
+ GetBBox().GetMinLon() <= 0.))
+ xo -= mercator_k0 * WGS84_semimajor_axis_meters * 2.0 * PI;
+ }
+
+ double valx = (east * xr) + xo;
+ double valy = (north * yr) + yo;
+
+ r->m_x = ((valx - rzRules->sm_transform_parms->easting_vp_center) *
+ vp_plib.view_scale_ppm) +
+ (vp_plib.pix_width / 2);
+ r->m_y = (vp_plib.pix_height / 2) -
+ ((valy - rzRules->sm_transform_parms->northing_vp_center) *
+ vp_plib.view_scale_ppm);
+ }
+ return true;
+}
+
+void s52plib::GetPixPointSingle(int pixx, int pixy, double *plat,
+ double *plon) {
+#if 1
+ GetLLFromPix(wxPoint(pixx, pixy), plat, plon);
+// if(*plon < 0 && vpt->clon > 180)
+// *plon += 360;
+#else
+ // Use Mercator estimator
+ int dx = pixx - (vpt->pix_width / 2);
+ int dy = (vpt->pix_height / 2) - pixy;
+
+ double xp = (dx * cos(vpt->skew)) - (dy * sin(vpt->skew));
+ double yp = (dy * cos(vpt->skew)) + (dx * sin(vpt->skew));
+
+ double d_east = xp / vpt->view_scale_ppm;
+ double d_north = yp / vpt->view_scale_ppm;
+
+ double slat, slon;
+ fromSM(d_east, d_north, vpt->clat, vpt->clon, &slat, &slon);
+
+ *plat = slat;
+ *plon = slon;
+#endif
+}
+
+void s52plib::GetPixPointSingleNoRotate(int pixx, int pixy, double *plat,
+ double *plon) {
+ double rotation = vp_plib.rotation;
+ vp_plib.rotation = 0;
+ GetLLFromPix(wxPoint(pixx, pixy), plat, plon);
+ vp_plib.rotation = rotation;
+}
+
+void DrawAALine(wxDC *pDC, int x0, int y0, int x1, int y1, wxColour clrLine,
+ int dash, int space) {
+ int width = 1 + abs(x0 - x1);
+ int height = 1 + abs(y0 - y1);
+ wxPoint upperLeft(wxMin(x0, x1), wxMin(y0, y1));
+
+ wxBitmap bm(width, height);
+ wxMemoryDC mdc(bm);
+
+ mdc.Blit(0, 0, width, height, pDC, upperLeft.x, upperLeft.y);
+
+#if wxUSE_GRAPHICS_CONTEXT
+ wxGCDC gdc(mdc);
+#else
+ wxMemoryDC &gdc(mdc);
+#endif
+
+ wxPen pen(clrLine, 1, wxPENSTYLE_USER_DASH);
+ wxDash dashes[2];
+ dashes[0] = dash;
+ dashes[1] = space;
+ pen.SetDashes(2, dashes);
+ gdc.SetPen(pen);
+
+ gdc.DrawLine(x0 - upperLeft.x, y0 - upperLeft.y, x1 - upperLeft.x,
+ y1 - upperLeft.y);
+
+ pDC->Blit(upperLeft.x, upperLeft.y, width, height, &mdc, 0, 0);
+
+ mdc.SelectObject(wxNullBitmap);
+
+ return;
+}
+
+void s52plib::DrawDashLine(wxPen &pen, double x1, double y1, double x2,
+ double y2) {
+#ifdef ocpnUSE_GL
+ glLineWidth(pen.GetWidth());
+
+ CGLShaderProgram *shader = pCcolor_tri_shader_program[0 /*GetCanvasIndex()*/];
+ shader->Bind();
+
+ float fBuf[4];
+ GLint pos = shader->getAttributeLocation("position");
+ glVertexAttribPointer(pos, 2, GL_FLOAT, GL_FALSE, 2 * sizeof(float), fBuf);
+ glEnableVertexAttribArray(pos);
+
+ float colorv[4];
+ colorv[0] = pen.GetColour().Red() / float(256);
+ colorv[1] = pen.GetColour().Green() / float(256);
+ colorv[2] = pen.GetColour().Blue() / float(256);
+ colorv[3] = 1.0;
+
+ shader->SetUniform4fv("color", colorv);
+
+ if (fabs(vp_plib.rotation) > 0.01) {
+ float cx = vp_plib.pix_width / 2.;
+ float cy = vp_plib.pix_height / 2.;
+ float c = cosf(-vp_plib.rotation);
+ float s = sinf(-vp_plib.rotation);
+
+ float xn = x1 - cx;
+ float yn = y1 - cy;
+ x1 = xn * c - yn * s + cx;
+ y1 = xn * s + yn * c + cy;
+
+ xn = x2 - cx;
+ yn = y2 - cy;
+ x2 = xn * c - yn * s + cx;
+ y2 = xn * s + yn * c + cy;
+ }
+
+ wxDash *dashes;
+ int n_dashes = pen.GetDashes(&dashes);
+ if (n_dashes) {
+ float angle = atan2f((float)(y2 - y1), (float)(x2 - x1));
+ float cosa = cosf(angle);
+ float sina = sinf(angle);
+ float t1 = pen.GetWidth();
+
+ float lpix = sqrtf(powf(x1 - x2, 2) + powf(y1 - y2, 2));
+ float lrun = 0.;
+ float xa = x1;
+ float ya = y1;
+ float ldraw = t1 * dashes[0];
+ float lspace = t1 * dashes[1];
+
+ ldraw = wxMax(ldraw, 4.0);
+ lspace = wxMax(lspace, 4.0);
+ lpix = wxMin(lpix, 2000.0);
+
+ while (lrun < lpix) {
+ // Dash
+ float xb = xa + ldraw * cosa;
+ float yb = ya + ldraw * sina;
+
+ if ((lrun + ldraw) >= lpix) // last segment is partial draw
+ {
+ xb = x2;
+ yb = y2;
+ }
+
+ fBuf[0] = xa;
+ fBuf[1] = ya;
+ fBuf[2] = xb;
+ fBuf[3] = yb;
+
+ glDrawArrays(GL_LINES, 0, 2);
+
+ xa = xa + (lspace + ldraw) * cosa;
+ ya = ya + (lspace + ldraw) * sina;
+ lrun += lspace + ldraw;
+ }
+ } else { // not dashed
+ fBuf[0] = x1;
+ fBuf[1] = y1;
+ fBuf[2] = x2;
+ fBuf[3] = y2;
+
+ glDrawArrays(GL_LINES, 0, 2);
+ }
+
+ shader->UnBind();
+#endif
+}
+
+/****************************************************************************/
+/* Convert Lat/Lon <-> Simple Mercator */
+/****************************************************************************/
+void toSM_plib(double lat, double lon, double lat0, double lon0, double *x,
+ double *y) {
+ double xlon = lon;
+
+ /* Make sure lon and lon0 are same phase */
+
+ if ((lon * lon0 < 0.) && (fabs(lon - lon0) > 180.)) {
+ lon < 0.0 ? xlon += 360.0 : xlon -= 360.0;
+ }
+
+ const double z = WGS84_semimajor_axis_meters * mercator_k0;
+
+ *x = (xlon - lon0) * DEGREE * z;
+
+ // y =.5 ln( (1 + sin t) / (1 - sin t) )
+ const double s = sin(lat * DEGREE);
+ const double y3 = (.5 * log((1 + s) / (1 - s))) * z;
+
+ const double s0 = sin(lat0 * DEGREE);
+ const double y30 = (.5 * log((1 + s0) / (1 - s0))) * z;
+ *y = y3 - y30;
+}
+
+void fromSM_plib(double x, double y, double lat0, double lon0, double *lat,
+ double *lon) {
+ const double z = WGS84_semimajor_axis_meters * mercator_k0;
+
+ const double s0 = sin(lat0 * DEGREE);
+ const double y0 = (.5 * log((1 + s0) / (1 - s0))) * z;
+
+ *lat = (2.0 * atan(exp((y0 + y) / z)) - PI / 2.) / DEGREE;
+
+ *lon = lon0 + (x / (DEGREE * z));
+}
+
+// TODO: eliminate the use of this function
+wxPoint s52plib::GetPixFromLL(double lat, double lon) {
+ wxPoint2DDouble p = GetDoublePixFromLL(lat, lon);
+ if (wxFinite(p.m_x) && wxFinite(p.m_y)) {
+ if ((abs(p.m_x) < 10000) && (abs(p.m_y) < 10000))
+ return wxPoint(wxRound(p.m_x), wxRound(p.m_y));
+ }
+ return wxPoint(INVALID_COORD, INVALID_COORD);
+}
+
+wxPoint2DDouble s52plib::GetDoublePixFromLL(double lat, double lon) {
+ double easting = 0;
+ double northing = 0;
+ double xlon = lon;
+
+ /* Make sure lon and lon0 are same phase */
+ if (xlon * vp_plib.clon < 0.) {
+ if (xlon < 0.)
+ xlon += 360.;
+ else
+ xlon -= 360.;
+ }
+
+ if (fabs(xlon - vp_plib.clon) > 180.) {
+ if (xlon > vp_plib.clon)
+ xlon -= 360.;
+ else
+ xlon += 360.;
+ }
+
+ toSM_plib(lat, xlon, vp_plib.clat, vp_plib.clon, &easting, &northing);
+
+ if (!wxFinite(easting) || !wxFinite(northing))
+ return wxPoint2DDouble(easting, northing);
+
+ double epix = easting * vp_plib.view_scale_ppm;
+ double npix = northing * vp_plib.view_scale_ppm;
+ double dxr = epix;
+ double dyr = npix;
+
+ // Apply VP Rotation
+ double angle = vp_plib.rotation;
+
+ if (angle) {
+ dxr = epix * cos(angle) + npix * sin(angle);
+ dyr = npix * cos(angle) - epix * sin(angle);
+ }
+
+ return wxPoint2DDouble((vp_plib.pix_width / 2.0) + dxr,
+ (vp_plib.pix_height / 2.0) - dyr);
+}
+
+wxPoint s52plib::GetPixFromLLROT(double lat, double lon, double rotation) {
+ wxPoint2DDouble p = GetDoublePixFromLLROT(lat, lon, rotation);
+ if (wxFinite(p.m_x) && wxFinite(p.m_y)) {
+ if ((abs(p.m_x) < 10000) && (abs(p.m_y) < 10000))
+ return wxPoint(wxRound(p.m_x), wxRound(p.m_y));
+ }
+ return wxPoint(INVALID_COORD, INVALID_COORD);
+}
+
+wxPoint2DDouble s52plib::GetDoublePixFromLLROT(double lat, double lon,
+ double rotation) {
+ double easting = 0;
+ double northing = 0;
+ double xlon = lon;
+
+ /* Make sure lon and lon0 are same phase */
+ if (xlon * vp_plib.clon < 0.) {
+ if (xlon < 0.)
+ xlon += 360.;
+ else
+ xlon -= 360.;
+ }
+
+ if (fabs(xlon - vp_plib.clon) > 180.) {
+ if (xlon > vp_plib.clon)
+ xlon -= 360.;
+ else
+ xlon += 360.;
+ }
+
+ toSM_plib(lat, xlon, vp_plib.clat, vp_plib.clon, &easting, &northing);
+
+ if (!wxFinite(easting) || !wxFinite(northing))
+ return wxPoint2DDouble(easting, northing);
+
+ double epix = easting * vp_plib.view_scale_ppm;
+ double npix = northing * vp_plib.view_scale_ppm;
+ double dxr = epix;
+ double dyr = npix;
+
+ // Apply VP Rotation
+ double angle = rotation;
+
+ if (angle) {
+ dxr = epix * cos(angle) + npix * sin(angle);
+ dyr = npix * cos(angle) - epix * sin(angle);
+ }
+
+ return wxPoint2DDouble((vp_plib.pix_width / 2.0) + dxr,
+ (vp_plib.pix_height / 2.0) - dyr);
+}
+
+void s52plib::GetLLFromPix(const wxPoint2DDouble &p, double *lat, double *lon) {
+ double dx = p.m_x - (vp_plib.pix_width / 2.0);
+ double dy = (vp_plib.pix_height / 2.0) - p.m_y;
+
+ double xpr = dx;
+ double ypr = dy;
+
+ // Apply VP Rotation
+ double angle = vp_plib.rotation;
+
+ if (angle) {
+ xpr = (dx * cos(angle)) - (dy * sin(angle));
+ ypr = (dy * cos(angle)) + (dx * sin(angle));
+ }
+ double d_east = xpr / vp_plib.view_scale_ppm;
+ double d_north = ypr / vp_plib.view_scale_ppm;
+
+ double slat = 0.0, slon = 0.0;
+ fromSM_plib(d_east, d_north, vp_plib.clat, vp_plib.clon, &slat, &slon);
+
+ *lat = slat;
+
+ if (slon < -180.)
+ slon += 360.;
+ else if (slon > 180.)
+ slon -= 360.;
+ *lon = slon;
+}
+
+RenderFromHPGL::RenderFromHPGL(s52plib *plibarg) {
+ plib = plibarg;
+ renderToDC = false;
+ renderToOpenGl = false;
+ renderToGCDC = false;
+
+ if (plib)
+ scaleFactor = 100.0 / plib->GetPPMM();
+ else
+ scaleFactor = 10.0; // Nominal
+
+ workBufSize = 0;
+ workBufIndex = 0;
+ workBuf = NULL;
+
+ s_odc_tess_work_buf = NULL;
+ s_odc_tess_vertex_idx = 0;
+ s_odc_tess_vertex_idx_this = 0;
+ s_odc_tess_buf_len = 0;
+
+ transparency = 255;
+ m_content_scale_factor = 1.0;
+}
+
+RenderFromHPGL::~RenderFromHPGL() {
+#ifdef ocpnUSE_GL
+ if (renderToOpenGl) {
+ glDisable(GL_BLEND);
+ }
+
+ free(workBuf);
+ free(s_odc_tess_work_buf);
+
+#endif
+}
+
+void RenderFromHPGL::SetTargetDC(wxDC *pdc) {
+ targetDC = pdc;
+ renderToDC = true;
+ renderToOpenGl = false;
+ renderToGCDC = false;
+}
+
+void RenderFromHPGL::SetTargetOpenGl() {
+ renderToOpenGl = true;
+ renderToDC = false;
+ renderToGCDC = false;
+}
+
+#if wxUSE_GRAPHICS_CONTEXT
+void RenderFromHPGL::SetTargetGCDC(wxGCDC *gdc) {
+ targetGCDC = gdc;
+ renderToGCDC = true;
+ renderToDC = false;
+ renderToOpenGl = false;
+}
+#endif
+
+const char *RenderFromHPGL::findColorNameInRef(char colorCode, char *col) {
+ int noColors = strlen(col) / 6;
+ for (int i = 0, j = 0; i < noColors; i++, j += 6) {
+ if (*(col + j) == colorCode) return col + j + 1;
+ }
+ return col + 1; // Default to first color if not found.
+}
+
+wxPoint RenderFromHPGL::ParsePoint(wxString &argument) {
+ long x, y;
+ int colon = argument.Index(',');
+ argument.Left(colon).ToLong(&x);
+ argument.Mid(colon + 1).ToLong(&y);
+ return wxPoint(x, y);
+}
+
+void RenderFromHPGL::SetPen() {
+ float nominal_line_width_pix =
+ wxMax(1.0, floor(plib->GetPPMM() /
+ 5.0)); // 0.2 mm nominal, but not less than 1 pixel
+
+ nominal_line_width_pix *= m_content_scale_factor; // esp for Mac Retina
+
+ int pen_width_mod = floor(penWidth * nominal_line_width_pix);
+
+ pen =
+ wxThePenList->FindOrCreatePen(penColor, pen_width_mod, wxPENSTYLE_SOLID);
+ brush = wxTheBrushList->FindOrCreateBrush(penColor, wxBRUSHSTYLE_SOLID);
+
+ if (renderToDC) {
+ targetDC->SetPen(*pen);
+ targetDC->SetBrush(*brush);
+ }
+#ifdef ocpnUSE_GL
+ if (renderToOpenGl) {
+ if (plib->GetGLPolygonSmoothing()) glEnable(GL_POLYGON_SMOOTH);
+
+ int line_width = wxMax(plib->m_GLMinSymbolLineWidth, (float)penWidth * 0.7);
+ glLineWidth(line_width);
+
+ // Scale the pen width dependent on the platform display resolution
+ float nominal_line_width_pix =
+ wxMax(1.0, floor(plib->GetPPMM() /
+ 5.0)); // 0.2 mm nominal, but not less than 1 pixel
+ // qDebug() << nominal_line_width_pix;
+ line_width = wxMax(1 /*m_GLMinSymbolLineWidth*/,
+ (float)penWidth * nominal_line_width_pix);
+ glLineWidth(line_width);
+
+#ifndef __OCPN__ANDROID__
+ if (line_width >= 2 && plib->GetGLLineSmoothing())
+ glEnable(GL_LINE_SMOOTH);
+ else
+ glDisable(GL_LINE_SMOOTH);
+ glEnable(GL_BLEND);
+#endif
+ }
+#endif
+#if wxUSE_GRAPHICS_CONTEXT
+ if (renderToGCDC) {
+ pen = wxThePenList->FindOrCreatePen(penColor, penWidth, wxPENSTYLE_SOLID);
+ brush = wxTheBrushList->FindOrCreateBrush(penColor, wxBRUSHSTYLE_SOLID);
+ targetGCDC->SetPen(*pen);
+ targetGCDC->SetBrush(*brush);
+ }
+#endif
+}
+
+void RenderFromHPGL::Line(wxPoint2DDouble from, wxPoint2DDouble to) {
+ if (renderToDC) {
+ targetDC->DrawLine(wxPoint(from.m_x, from.m_y), wxPoint(to.m_x, to.m_y));
+ }
+#ifdef ocpnUSE_GL
+ if (renderToOpenGl) {
+ CGLShaderProgram *shader =
+ pCcolor_tri_shader_program[0 /*GetCanvasIndex()*/];
+ shader->Bind();
+
+ glBindBuffer(GL_ARRAY_BUFFER, 0);
+ glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
+
+ float colorv[4];
+ colorv[0] = penColor.Red() / float(256);
+ colorv[1] = penColor.Green() / float(256);
+ colorv[2] = penColor.Blue() / float(256);
+ colorv[3] = transparency / float(256);
+
+ shader->SetUniform4fv("color", colorv);
+
+ float pts[4];
+ pts[0] = from.m_x;
+ pts[1] = from.m_y;
+ pts[2] = to.m_x;
+ pts[3] = to.m_y;
+
+ GLint pos = shader->getAttributeLocation("position");
+ glVertexAttribPointer(pos, 2, GL_FLOAT, GL_FALSE, 2 * sizeof(float), pts);
+ glEnableVertexAttribArray(pos);
+
+ glDrawArrays(GL_LINES, 0, 2);
+
+ shader->UnBind();
+ }
+#endif
+#if wxUSE_GRAPHICS_CONTEXT
+ if (renderToGCDC) {
+ targetGCDC->DrawLine(wxPoint(from.m_x, from.m_y), wxPoint(to.m_x, to.m_y));
+ }
+#endif
+}
+
+void RenderFromHPGL::Circle(wxPoint2DDouble center, int radius, bool filled) {
+ if (renderToDC) {
+ if (filled)
+ targetDC->SetBrush(*brush);
+ else {
+#ifdef __ANDROID__
+ // This is strange behavior of wxDC in wxQT
+ // Fill color is always "text background" color.
+ // So make it transparent.
+ targetDC->SetTextBackground(wxColor(0,0,0,0));
+#endif
+ targetDC->SetBrush(*wxTRANSPARENT_BRUSH);
+ }
+ targetDC->DrawCircle(wxPoint(center.m_x, center.m_y), radius);
+ }
+#ifdef ocpnUSE_GL
+ if (renderToOpenGl) {
+ if (!m_vp) // oops, forgot to set the VP parameters
+ return;
+
+ // Enable anti-aliased lines, at best quality
+ glEnable(GL_BLEND);
+
+ float coords[8];
+ coords[0] = center.m_x - radius;
+ coords[1] = center.m_y + radius;
+ coords[2] = center.m_x + radius;
+ coords[3] = center.m_y + radius;
+ coords[4] = center.m_x - radius;
+ coords[5] = center.m_y - radius;
+ coords[6] = center.m_x + radius;
+ coords[7] = center.m_y - radius;
+
+ glUseProgram(S52circle_filled_shader_program);
+
+ // Get pointers to the attributes in the program.
+ GLint mPosAttrib =
+ glGetAttribLocation(S52circle_filled_shader_program, "aPos");
+
+ // Disable VBO's (vertex buffer objects) for attributes.
+ glBindBuffer(GL_ARRAY_BUFFER, 0);
+ glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
+
+ glVertexAttribPointer(mPosAttrib, 2, GL_FLOAT, GL_FALSE, 0, coords);
+ glEnableVertexAttribArray(mPosAttrib);
+
+ // Circle radius
+ GLint radiusloc =
+ glGetUniformLocation(S52circle_filled_shader_program, "circle_radius");
+ glUniform1f(radiusloc, radius);
+
+ // Circle center point
+ GLint centerloc =
+ glGetUniformLocation(S52circle_filled_shader_program, "circle_center");
+ float ctrv[2];
+ ctrv[0] = center.m_x;
+ ctrv[1] = m_vp->pix_height - center.m_y;
+ glUniform2fv(centerloc, 1, ctrv);
+
+ // Circle fill color
+ float colorv[4];
+ colorv[3] = 0.0; // transparent default
+
+ if (brush) {
+ colorv[0] = brush->GetColour().Red() / float(256);
+ colorv[1] = brush->GetColour().Green() / float(256);
+ colorv[2] = brush->GetColour().Blue() / float(256);
+ if (filled) colorv[3] = 1.0;
+ }
+
+ GLint colloc =
+ glGetUniformLocation(S52circle_filled_shader_program, "circle_color");
+ glUniform4fv(colloc, 1, colorv);
+
+ // Border color
+ float bcolorv[4];
+ bcolorv[0] = penColor.Red() / float(256);
+ bcolorv[1] = penColor.Green() / float(256);
+ bcolorv[2] = penColor.Blue() / float(256);
+ bcolorv[3] = penColor.Alpha() / float(256);
+
+ GLint bcolloc =
+ glGetUniformLocation(S52circle_filled_shader_program, "border_color");
+ glUniform4fv(bcolloc, 1, bcolorv);
+
+ // Border Width
+ float nominal_line_width_pix =
+ wxMax(1.0, floor(plib->GetPPMM() /
+ 5.0)); // 0.2 mm nominal, but not less than 1 pixel
+ float line_width = wxMax(1 /*g_GLMinSymbolLineWidth*/,
+ (float)penWidth * nominal_line_width_pix);
+
+ GLint borderWidthloc =
+ glGetUniformLocation(S52circle_filled_shader_program, "border_width");
+ glUniform1f(borderWidthloc, line_width);
+
+ // Perform the actual drawing.
+ glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
+
+ // Enable anti-aliased lines, at best quality
+ glDisable(GL_BLEND);
+ }
+#endif
+#if wxUSE_GRAPHICS_CONTEXT
+ if (renderToGCDC) {
+ if (filled)
+ targetGCDC->SetBrush(*brush);
+ else
+ targetGCDC->SetBrush(*wxTRANSPARENT_BRUSH);
+
+ targetGCDC->DrawCircle(wxPoint(center.m_x, center.m_y), radius);
+
+ // wxGCDC doesn't update min/max X/Y properly for DrawCircle.
+ targetGCDC->SetPen(*wxTRANSPARENT_PEN);
+ targetGCDC->DrawPoint(center.m_x - radius, center.m_y);
+ targetGCDC->DrawPoint(center.m_x + radius, center.m_y);
+ targetGCDC->DrawPoint(center.m_x, center.m_y - radius);
+ targetGCDC->DrawPoint(center.m_x, center.m_y + radius);
+ targetGCDC->SetPen(*pen);
+ }
+#endif
+}
+
+void RenderFromHPGL::Polygon() {
+ if (renderToDC) {
+ unsigned char rtrans = transparency;
+#if defined(__WXMSW__) || defined(__WXQT__)
+ rtrans = 255;
+#endif
+ penColor.Set(penColor.Red(), penColor.Green(), penColor.Blue(), rtrans);
+ pen = wxThePenList->FindOrCreatePen(penColor, penWidth, wxPENSTYLE_SOLID);
+ brush = wxTheBrushList->FindOrCreateBrush(penColor, wxBRUSHSTYLE_SOLID);
+ targetDC->SetPen(*pen);
+ targetDC->SetBrush(*brush);
+ targetDC->DrawPolygon(noPoints, polygon);
+ }
+#ifdef ocpnUSE_GL
+ if (renderToOpenGl) {
+ penColor.Set(penColor.Red(), penColor.Green(), penColor.Blue(),
+ transparency);
+ pen = wxThePenList->FindOrCreatePen(penColor, penWidth, wxPENSTYLE_SOLID);
+ brush = wxTheBrushList->FindOrCreateBrush(penColor, wxBRUSHSTYLE_SOLID);
+ DrawPolygon(noPoints, polygon, 0, 0, 1.0, 0);
+ }
+#endif // OpenGL
+
+#if wxUSE_GRAPHICS_CONTEXT
+ if (renderToGCDC) {
+ penColor.Set(penColor.Red(), penColor.Green(), penColor.Blue(),
+ transparency);
+ pen = wxThePenList->FindOrCreatePen(penColor, penWidth, wxPENSTYLE_SOLID);
+ brush = wxTheBrushList->FindOrCreateBrush(penColor, wxBRUSHSTYLE_SOLID);
+ targetGCDC->SetPen(*pen);
+ targetGCDC->SetBrush(*brush);
+
+ targetGCDC->DrawPolygon(noPoints, polygon);
+ }
+#endif
+}
+
+void RenderFromHPGL::RotatePoint(wxPoint2DDouble &point, wxPoint origin, double angle) {
+ if (angle == 0.) return;
+ double sin_rot = sin(angle * PI / 180.);
+ double cos_rot = cos(angle * PI / 180.);
+
+ double xp =
+ ((point.m_x - origin.x) * cos_rot) - ((point.m_y - origin.y) * sin_rot);
+ double yp =
+ ((point.m_x - origin.x) * sin_rot) + ((point.m_y - origin.y) * cos_rot);
+
+ point.m_x = (int)xp + origin.x;
+ point.m_y = (int)yp + origin.y;
+}
+
+bool RenderFromHPGL::Render(char *str, char *col, wxPoint2DDouble &r, wxPoint &pivot,
+ wxPoint origin, float scale, double rot_angle,
+ bool bSymbol) {
+ wxPoint2DDouble lineStart;
+ wxPoint2DDouble lineEnd;
+
+ scaleFactor = 100.0 / plib->GetPPMM();
+
+ scaleFactor /= scale;
+ scaleFactor /= g_scaminScale;
+
+ if (bSymbol) scaleFactor /= plib->GetRVScaleFactor();
+
+ // SW is not always defined, cf. US/US4CA17M/US4CA17M.000
+ penWidth = 1;
+
+ wxStringTokenizer commands(wxString(str, wxConvUTF8), _T(";"));
+ while (commands.HasMoreTokens()) {
+ wxString command = commands.GetNextToken();
+ wxString arguments = command.Mid(2);
+ command = command.Left(2);
+
+ if (command == _T("SP")) {
+ S52color *color =
+ plib->getColor(findColorNameInRef(arguments.GetChar(0), col));
+ penColor = wxColor(color->R, color->G, color->B);
+ brushColor = penColor;
+ continue;
+ }
+ if (command == _T("SW")) {
+ arguments.ToLong(&penWidth);
+ continue;
+ }
+ if (command == _T("ST")) {
+ long transIndex;
+ arguments.ToLong(&transIndex);
+ transparency = (4 - transIndex) * 64;
+ transparency = wxMin(transparency, 255);
+ transparency = wxMax(0, transparency);
+ continue;
+ }
+ if (command == _T("PU")) {
+ SetPen();
+ lineStart = ParsePoint(arguments);
+ RotatePoint(lineStart, origin, rot_angle);
+ lineStart -= pivot;
+ lineStart.m_x /= scaleFactor;
+ lineStart.m_y /= scaleFactor;
+ lineStart += r;
+ continue;
+ }
+ if (command == _T("PD")) {
+ if (arguments.Length() == 0) {
+ lineEnd = lineStart;
+ lineEnd.m_x++;
+ } else {
+ lineEnd = ParsePoint(arguments);
+ RotatePoint(lineEnd, origin, rot_angle);
+ lineEnd -= pivot;
+ lineEnd.m_x /= scaleFactor;
+ lineEnd.m_y /= scaleFactor;
+ lineEnd += r;
+ }
+ Line(lineStart, lineEnd);
+ lineStart = lineEnd; // For next line.
+ continue;
+ }
+ if (command == _T("CI")) {
+ long radius;
+ arguments.ToLong(&radius);
+ radius = (int)radius / scaleFactor;
+ Circle(lineStart, radius);
+ continue;
+ }
+ if (command == _T("PM")) {
+ noPoints = 1;
+ polygon[0] = wxPoint(lineStart.m_x, lineStart.m_y);
+
+ if (arguments == _T("0")) {
+ do {
+ command = commands.GetNextToken();
+ arguments = command.Mid(2);
+ command = command.Left(2);
+
+ if (command == _T("AA")) {
+ wxLogWarning(_T("RenderHPGL: AA instruction not implemented."));
+ }
+ if (command == _T("CI")) {
+ long radius;
+ arguments.ToLong(&radius);
+ radius = (int)radius / scaleFactor;
+ Circle(lineStart, radius, HPGL_FILLED);
+ }
+ if (command == _T("PD")) {
+ wxStringTokenizer points(arguments, _T(","));
+ while (points.HasMoreTokens()) {
+ long x, y;
+ points.GetNextToken().ToLong(&x);
+ points.GetNextToken().ToLong(&y);
+ lineEnd = wxPoint(x, y);
+ RotatePoint(lineEnd, origin, rot_angle);
+ lineEnd -= pivot;
+ lineEnd.m_x /= scaleFactor;
+ lineEnd.m_y /= scaleFactor;
+ lineEnd += r;
+ polygon[noPoints++] = wxPoint(lineEnd.m_x, lineEnd.m_y);
+ }
+ }
+ } while (command != _T("PM"));
+ }
+ continue;
+ }
+ if (command == _T("FP")) {
+ SetPen();
+ Polygon();
+ continue;
+ }
+
+ // Only get here if non of the other cases did a continue.
+ // wxString msg( _T("RenderHPGL: The '%s' instruction is not
+ // implemented.") ); msg += wxString( command ); wxLogWarning( msg );
+ }
+
+ transparency = 255;
+
+#ifdef ocpnUSE_GL
+ if (renderToOpenGl) {
+ glDisable(GL_BLEND);
+ glUseProgram(0);
+ }
+#endif
+
+ return true;
+}
+
+wxArrayPtrVoid s52gTesselatorVertices;
+
+void RenderFromHPGL::DrawPolygon(int n, wxPoint points[], wxCoord xoffset,
+ wxCoord yoffset, float scale, float angle) {
+#ifdef ocpnUSE_GL
+ // if( 0 )
+ // dc->DrawPolygon( n, points, xoffset, yoffset );
+ // else
+ {
+#ifdef __WXQT__
+ glDisable(GL_LINE_SMOOTH);
+ glDisable(GL_POLYGON_SMOOTH);
+ glDisable(GL_BLEND);
+
+#else
+ glEnable(GL_LINE_SMOOTH);
+ glEnable(GL_POLYGON_SMOOTH);
+ glEnable(GL_BLEND);
+
+#endif
+
+ // ConfigurePen();
+ glLineWidth(pen->GetWidth());
+
+ glEnable(GL_BLEND);
+
+ if (n > 4)
+ DrawPolygonTessellated(n, points, xoffset, yoffset);
+ else { // n = 3 or 4, most common case for pre-tesselated shapes
+
+ // Grow the work buffer as necessary
+ if (workBufSize < (size_t)n * 2) {
+ workBuf = (float *)realloc(workBuf, (n * 4) * sizeof(float));
+ workBufSize = n * 4;
+ }
+
+ for (int i = 0; i < n; i++) {
+ workBuf[i * 2] = (points[i].x * scale); // + xoffset;
+ workBuf[i * 2 + 1] = (points[i].y * scale); // + yoffset;
+ }
+
+ CGLShaderProgram *shader =
+ pCcolor_tri_shader_program[0 /*GetCanvasIndex()*/];
+ shader->Bind();
+
+ // Get pointers to the attributes in the program.
+ GLint mPosAttrib = shader->getAttributeLocation("position");
+
+ // Disable VBO's (vertex buffer objects) for attributes.
+ glBindBuffer(GL_ARRAY_BUFFER, 0);
+ glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
+
+ glVertexAttribPointer(mPosAttrib, 2, GL_FLOAT, GL_FALSE, 0, workBuf);
+ glEnableVertexAttribArray(mPosAttrib);
+
+ // Border color
+ float bcolorv[4];
+ bcolorv[0] = pen->GetColour().Red() / float(256);
+ bcolorv[1] = pen->GetColour().Green() / float(256);
+ bcolorv[2] = pen->GetColour().Blue() / float(256);
+ bcolorv[3] = pen->GetColour().Alpha() / float(256);
+
+ shader->SetUniform4fv("color", bcolorv);
+
+ // Perform the actual drawing.
+ glDrawArrays(GL_LINE_LOOP, 0, n);
+
+ // Fill color
+ bcolorv[0] = brush->GetColour().Red() / float(256);
+ bcolorv[1] = brush->GetColour().Green() / float(256);
+ bcolorv[2] = brush->GetColour().Blue() / float(256);
+ bcolorv[3] = brush->GetColour().Alpha() / float(256);
+
+ shader->SetUniform4fv("color", bcolorv);
+
+ // For the simple common case of a convex rectangle...
+ // swizzle the array points to enable GL_TRIANGLE_STRIP
+ if (n == 4) {
+ float x1 = workBuf[4];
+ float y1 = workBuf[5];
+ workBuf[4] = workBuf[6];
+ workBuf[5] = workBuf[7];
+ workBuf[6] = x1;
+ workBuf[7] = y1;
+
+ glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
+ } else if (n == 3) {
+ glDrawArrays(GL_TRIANGLES, 0, 3);
+ }
+
+ shader->UnBind();
+ }
+
+ glDisable(GL_LINE_SMOOTH);
+ glDisable(GL_POLYGON_SMOOTH);
+ glDisable(GL_BLEND);
+ }
+#endif
+}
+
+#ifdef ocpnUSE_GL
+
+// GL callbacks
+
+typedef union {
+ GLdouble data[6];
+ struct sGLvertex {
+ GLdouble x;
+ GLdouble y;
+ GLdouble z;
+ GLdouble r;
+ GLdouble g;
+ GLdouble b;
+ } info;
+} GLvertex;
+
+// GLSL callbacks
+
+static std::list odc_combine_work_data;
+static void xs52_combineCallbackD(GLdouble coords[3], GLdouble *vertex_data[4],
+ GLfloat weight[4], GLdouble **dataOut,
+ void *data) {
+ // double *vertex = new double[3];
+ // odc_combine_work_data.push_back(vertex);
+ // memcpy(vertex, coords, 3*(sizeof *coords));
+ // *dataOut = vertex;
+}
+
+void xs52_vertexCallbackD_GLSL(GLvoid *vertex, void *data) {
+ RenderFromHPGL *plib = (RenderFromHPGL *)data;
+
+ // Grow the work buffer if necessary
+ if (plib->s_odc_tess_vertex_idx > plib->s_odc_tess_buf_len - 8) {
+ int new_buf_len = plib->s_odc_tess_buf_len + 100;
+ GLfloat *tmp = plib->s_odc_tess_work_buf;
+
+ plib->s_odc_tess_work_buf = (GLfloat *)realloc(
+ plib->s_odc_tess_work_buf, new_buf_len * sizeof(GLfloat));
+ if (NULL == plib->s_odc_tess_work_buf) {
+ free(tmp);
+ tmp = NULL;
+ } else
+ plib->s_odc_tess_buf_len = new_buf_len;
+ }
+
+ GLdouble *pointer = (GLdouble *)vertex;
+
+ plib->s_odc_tess_work_buf[plib->s_odc_tess_vertex_idx++] = (float)pointer[0];
+ plib->s_odc_tess_work_buf[plib->s_odc_tess_vertex_idx++] = (float)pointer[1];
+
+ plib->s_odc_nvertex++;
+}
+
+void xs52_beginCallbackD_GLSL(GLenum mode, void *data) {
+ RenderFromHPGL *plib = (RenderFromHPGL *)data;
+ plib->s_odc_tess_vertex_idx_this = plib->s_odc_tess_vertex_idx;
+ plib->s_odc_tess_mode = mode;
+ plib->s_odc_nvertex = 0;
+}
+
+void xs52_endCallbackD_GLSL(void *data) {
+ // qDebug() << "End" << s_odc_nvertex << s_odc_tess_buf_len <<
+ // s_odc_tess_vertex_idx << s_odc_tess_vertex_idx_this; End 5 100 10 0
+ RenderFromHPGL *plib = (RenderFromHPGL *)data;
+
+ CGLShaderProgram *shader = pCcolor_tri_shader_program[0 /*GetCanvasIndex()*/];
+ shader->Bind();
+
+ // Disable VBO's (vertex buffer objects) for attributes.
+ glBindBuffer(GL_ARRAY_BUFFER, 0);
+ glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
+
+ float *bufPt = &(plib->s_odc_tess_work_buf[plib->s_odc_tess_vertex_idx_this]);
+ GLint pos = glGetAttribLocation(S52color_tri_shader_program, "position");
+ glVertexAttribPointer(pos, 2, GL_FLOAT, GL_FALSE, 2 * sizeof(float), bufPt);
+ glEnableVertexAttribArray(pos);
+
+ float colorv[4];
+ wxColour c = plib->getBrush()->GetColour();
+
+ colorv[0] = c.Red() / float(256);
+ colorv[1] = c.Green() / float(256);
+ colorv[2] = c.Blue() / float(256);
+ colorv[3] = c.Alpha() / float(256);
+
+ shader->SetUniform4fv("color", colorv);
+
+ glDrawArrays(plib->s_odc_tess_mode, 0, plib->s_odc_nvertex);
+ shader->UnBind();
+}
+
+#endif // #ifdef ocpnUSE_GL
+
+#ifdef __WXMSW__
+// int filterException(int code, PEXCEPTION_POINTERS ex) {
+// //std::cout << "Filtering " << std::hex << code << std::endl;
+// return EXCEPTION_EXECUTE_HANDLER;
+// }
+#endif
+
+void RenderFromHPGL::DrawPolygonTessellated(int n, wxPoint points[],
+ wxCoord xoffset, wxCoord yoffset) {
+#ifndef ocpnUSE_GL
+ // dc->DrawPolygon( n, points, xoffset, yoffset );
+#else
+#if !defined(ocpnUSE_GLES) || \
+ defined(USE_ANDROID_GLES2) // tessalator in glues is broken
+ if (n < 5)
+#endif
+ {
+ DrawPolygon(n, points, xoffset, yoffset, 1.0, 0);
+ return;
+ }
+
+ m_tobj = gluNewTess();
+ s_odc_tess_vertex_idx = 0;
+
+ gluTessCallback(m_tobj, GLU_TESS_VERTEX_DATA,
+ (_GLUfuncptrA)&xs52_vertexCallbackD_GLSL);
+ gluTessCallback(m_tobj, GLU_TESS_BEGIN_DATA,
+ (_GLUfuncptrA)&xs52_beginCallbackD_GLSL);
+ gluTessCallback(m_tobj, GLU_TESS_END_DATA,
+ (_GLUfuncptrA)&xs52_endCallbackD_GLSL);
+ gluTessCallback(m_tobj, GLU_TESS_COMBINE_DATA,
+ (_GLUfuncptrA)&xs52_combineCallbackD);
+
+ gluTessNormal(m_tobj, 0, 0, 1);
+ gluTessProperty(m_tobj, GLU_TESS_WINDING_RULE, GLU_TESS_WINDING_NONZERO);
+
+ gluTessBeginPolygon(m_tobj, this);
+ gluTessBeginContour(m_tobj);
+
+ // ViewPort *pvp = cc1->GetpVP();
+ double *p = new double[n * 3];
+ for (int i = 0; i < n; i++) {
+ // if(fabs(pvp->rotation) > 0.01){
+ // float cx = pvp->pix_width/2.;
+ // float cy = pvp->pix_height/2.;
+ // float c = cosf(pvp->rotation );
+ // float s = sinf(pvp->rotation );
+ // float xn = points[i].x - cx;
+ // float yn = points[i].y - cy;
+ // p[0] = xn*c - yn*s + cx;
+ // p[1] = xn*s + yn*c + cy;
+ // p[2] = 0;
+ // }
+ // else
+ p[3 * i] = points[i].x, p[3 * i + 1] = points[i].y, p[3 * i + 2] = 0;
+
+ gluTessVertex(m_tobj, p + 3 * i, p + 3 * i);
+ }
+
+#ifdef __WXMSW__
+ // https://stackoverflow.com/questions/7049502/c-try-and-try-catch-finally
+ __try {
+ gluTessEndContour(m_tobj);
+ gluTessEndPolygon(m_tobj);
+ gluDeleteTess(m_tobj);
+ }
+ // __except(filterException(GetExceptionCode(), GetExceptionInformation()))
+ // {
+ __except (EXCEPTION_EXECUTE_HANDLER) {
+ int yyp = 4;
+ }
+#else
+ gluTessEndContour(m_tobj);
+ gluTessEndPolygon(m_tobj);
+ gluDeleteTess(m_tobj);
+#endif
+
+ delete[] p;
+
+ // for(std::list::iterator i =
+ // odc_combine_work_data.begin(); i!=odc_combine_work_data.end();
+ // i++)
+ // delete [] *i;
+ // odc_combine_work_data.clear();
+
+#endif
+}
+
+#ifdef ocpnUSE_GL
+void PrepareS52ShaderUniforms(VPointCompat *vp) {
+ loadS52Shaders();
+
+ mat4x4 m;
+ float vp_transform[16];
+ mat4x4_identity(m);
+ mat4x4_scale_aniso((float (*)[4])vp_transform, m, 2.0 / (float)vp->pix_width,
+ -2.0 / (float)vp->pix_height, 1.0);
+ // Rotate
+ mat4x4 Q;
+ mat4x4_rotate_Z(Q, (float (*)[4])vp_transform, vp->rotation);
+ mat4x4_translate_in_place(Q, -vp->pix_width / 2.0, -vp->pix_height / 2.0, 0);
+
+ mat4x4 I;
+ mat4x4_identity(I);
+
+ // glUseProgram(S52color_tri_shader_program);
+ // GLint matloc = glGetUniformLocation(S52color_tri_shader_program,
+ // "MVMatrix"); glUniformMatrix4fv(matloc, 1, GL_FALSE, (const GLfloat *)Q);
+ // GLint transloc =
+ // glGetUniformLocation(S52color_tri_shader_program, "TransformMatrix");
+ // glUniformMatrix4fv(transloc, 1, GL_FALSE, (const GLfloat *)I);
+
+ glUseProgram(S52texture_2D_shader_program);
+ GLint matloc = glGetUniformLocation(S52texture_2D_shader_program, "MVMatrix");
+ glUniformMatrix4fv(matloc, 1, GL_FALSE, (const GLfloat *)Q);
+ GLint transloc =
+ glGetUniformLocation(S52texture_2D_shader_program, "TransformMatrix");
+ glUniformMatrix4fv(transloc, 1, GL_FALSE, (const GLfloat *)I);
+
+ glUseProgram(S52texture_2D_ColorMod_shader_program);
+ matloc =
+ glGetUniformLocation(S52texture_2D_ColorMod_shader_program, "MVMatrix");
+ glUniformMatrix4fv(matloc, 1, GL_FALSE, (const GLfloat *)Q);
+ transloc = glGetUniformLocation(S52texture_2D_ColorMod_shader_program,
+ "TransformMatrix");
+ glUniformMatrix4fv(transloc, 1, GL_FALSE, (const GLfloat *)I);
+
+ glUseProgram(S52circle_filled_shader_program);
+ matloc = glGetUniformLocation(S52circle_filled_shader_program, "MVMatrix");
+ glUniformMatrix4fv(matloc, 1, GL_FALSE, (const GLfloat *)Q);
+ transloc =
+ glGetUniformLocation(S52circle_filled_shader_program, "TransformMatrix");
+ glUniformMatrix4fv(transloc, 1, GL_FALSE, (const GLfloat *)I);
+
+ glUseProgram(S52ring_shader_program);
+ matloc = glGetUniformLocation(S52ring_shader_program, "MVMatrix");
+ glUniformMatrix4fv(matloc, 1, GL_FALSE, (const GLfloat *)Q);
+ transloc = glGetUniformLocation(S52ring_shader_program, "TransformMatrix");
+ glUniformMatrix4fv(transloc, 1, GL_FALSE, (const GLfloat *)I);
+
+ glUseProgram(S52Dash_shader_program);
+ matloc = glGetUniformLocation(S52Dash_shader_program, "MVMatrix");
+ glUniformMatrix4fv(matloc, 1, GL_FALSE, (const GLfloat *)Q);
+ transloc = glGetUniformLocation(S52Dash_shader_program, "TransformMatrix");
+ glUniformMatrix4fv(transloc, 1, GL_FALSE, (const GLfloat *)I);
+
+ glUseProgram(S52AP_shader_program);
+ matloc = glGetUniformLocation(S52AP_shader_program, "MVMatrix");
+ glUniformMatrix4fv(matloc, 1, GL_FALSE, (const GLfloat *)Q);
+ transloc = glGetUniformLocation(S52AP_shader_program, "TransformMatrix");
+ glUniformMatrix4fv(transloc, 1, GL_FALSE, (const GLfloat *)I);
+
+ glUseProgram(0);
+
+ // Load special shaders
+ loadCShaders(0);
+
+ CGLShaderProgram *shader = pCcolor_tri_shader_program[0];
+ shader->Bind();
+ shader->SetUniformMatrix4fv("MVMatrix", (GLfloat *)Q);
+ shader->SetUniformMatrix4fv("TransformMatrix", (GLfloat *)I);
+ shader->UnBind();
+
+ shader = pCtexture_2D_shader_program[0];
+ shader->Bind();
+ shader->SetUniformMatrix4fv("MVMatrix", (GLfloat *)Q);
+ shader->SetUniformMatrix4fv("TransformMatrix", (GLfloat *)I);
+ shader->UnBind();
+
+ shader = pCtexture_2D_Color_shader_program[0];
+ shader->Bind();
+ shader->SetUniformMatrix4fv("MVMatrix", (GLfloat *)Q);
+ shader->SetUniformMatrix4fv("TransformMatrix", (GLfloat *)I);
+ shader->UnBind();
+}
+#endif
+
+// CRC calculation for a byte buffer
+
+static unsigned int crc_32_tab[] =
+ {/* CRC polynomial 0xedb88320 */
+ 0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, 0x076dc419, 0x706af48f,
+ 0xe963a535, 0x9e6495a3, 0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988,
+ 0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91, 0x1db71064, 0x6ab020f2,
+ 0xf3b97148, 0x84be41de, 0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7,
+ 0x136c9856, 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec, 0x14015c4f, 0x63066cd9,
+ 0xfa0f3d63, 0x8d080df5, 0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172,
+ 0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b, 0x35b5a8fa, 0x42b2986c,
+ 0xdbbbc9d6, 0xacbcf940, 0x32d86ce3, 0x45df5c75, 0xdcd60dcf, 0xabd13d59,
+ 0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116, 0x21b4f4b5, 0x56b3c423,
+ 0xcfba9599, 0xb8bda50f, 0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924,
+ 0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d, 0x76dc4190, 0x01db7106,
+ 0x98d220bc, 0xefd5102a, 0x71b18589, 0x06b6b51f, 0x9fbfe4a5, 0xe8b8d433,
+ 0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818, 0x7f6a0dbb, 0x086d3d2d,
+ 0x91646c97, 0xe6635c01, 0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e,
+ 0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457, 0x65b0d9c6, 0x12b7e950,
+ 0x8bbeb8ea, 0xfcb9887c, 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65,
+ 0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2, 0x4adfa541, 0x3dd895d7,
+ 0xa4d1c46d, 0xd3d6f4fb, 0x4369e96a, 0x346ed9fc, 0xad678846, 0xda60b8d0,
+ 0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9, 0x5005713c, 0x270241aa,
+ 0xbe0b1010, 0xc90c2086, 0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f,
+ 0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4, 0x59b33d17, 0x2eb40d81,
+ 0xb7bd5c3b, 0xc0ba6cad, 0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a,
+ 0xead54739, 0x9dd277af, 0x04db2615, 0x73dc1683, 0xe3630b12, 0x94643b84,
+ 0x0d6d6a3e, 0x7a6a5aa8, 0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1,
+ 0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe, 0xf762575d, 0x806567cb,
+ 0x196c3671, 0x6e6b06e7, 0xfed41b76, 0x89d32be0, 0x10da7a5a, 0x67dd4acc,
+ 0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5, 0xd6d6a3e8, 0xa1d1937e,
+ 0x38d8c2c4, 0x4fdff252, 0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b,
+ 0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 0x41047a60, 0xdf60efc3, 0xa867df55,
+ 0x316e8eef, 0x4669be79, 0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236,
+ 0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f, 0xc5ba3bbe, 0xb2bd0b28,
+ 0x2bb45a92, 0x5cb36a04, 0xc2d7ffa7, 0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d,
+ 0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a, 0x9c0906a9, 0xeb0e363f,
+ 0x72076785, 0x05005713, 0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38,
+ 0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21, 0x86d3d2d4, 0xf1d4e242,
+ 0x68ddb3f8, 0x1fda836e, 0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777,
+ 0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c, 0x8f659eff, 0xf862ae69,
+ 0x616bffd3, 0x166ccf45, 0xa00ae278, 0xd70dd2ee, 0x4e048354, 0x3903b3c2,
+ 0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db, 0xaed16a4a, 0xd9d65adc,
+ 0x40df0b66, 0x37d83bf0, 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9,
+ 0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6, 0xbad03605, 0xcdd70693,
+ 0x54de5729, 0x23d967bf, 0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94,
+ 0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d};
+
+#define UPDC32(octet, crc) \
+ (crc_32_tab[((crc) ^ ((unsigned char)octet)) & 0xff] ^ ((crc) >> 8))
+
+unsigned int crc32buf(unsigned char *buf, size_t len) {
+ unsigned int oldcrc32;
+
+ oldcrc32 = 0xFFFFFFFF;
+
+ for (; len; --len, ++buf) {
+ oldcrc32 = UPDC32(*buf, oldcrc32);
+ }
+
+ return ~oldcrc32;
+}
diff --git a/docs/外部参考/OpenCPN源码基线_f7211b4e/上游源码/libs/s52plib/src/s52plib.h b/docs/外部参考/OpenCPN源码基线_f7211b4e/上游源码/libs/s52plib/src/s52plib.h
new file mode 100644
index 0000000..2c2ed7c
--- /dev/null
+++ b/docs/外部参考/OpenCPN源码基线_f7211b4e/上游源码/libs/s52plib/src/s52plib.h
@@ -0,0 +1,737 @@
+/***************************************************************************
+ *
+ * Project: OpenCPN
+ * Purpose: S52 Presentation Library
+ * Author: David Register
+ *
+ ***************************************************************************
+ * Copyright (C) 2010 by David S. Register *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
+ **************************************************************************/
+
+#ifndef _S52PLIB_H_
+#define _S52PLIB_H_
+
+#include
+#include "s52s57.h" //types
+
+class wxGLContext;
+
+#include "LLRegion.h"
+#include "DepthFont.h"
+#include "chartsymbols.h"
+#include "TexFont.h"
+
+#include // supplemental, for Mac
+#include
+
+// ChartType constants
+typedef enum S52_ChartTypeEnum {
+ S52_CHART_TYPE_UNKNOWN = 0,
+ S52_CHART_TYPE_DUMMY,
+ S52_CHART_TYPE_DONTCARE,
+ S52_CHART_TYPE_KAP,
+ S52_CHART_TYPE_GEO,
+ S52_CHART_TYPE_S57,
+ S52_CHART_TYPE_CM93,
+ S52_CHART_TYPE_CM93COMP,
+ S52_CHART_TYPE_PLUGIN,
+ S52_CHART_TYPE_MBTILES
+} _S52_ChartTypeEnum;
+
+// Correct some deficincies in MacOS OpenGL include files
+#ifdef __WXOSX__
+typedef void (*PFNGLGENBUFFERSPROC)(GLsizei n, GLuint *buffers);
+typedef void (*PFNGLBINDBUFFERPROC)(GLenum target, GLuint buffer);
+typedef void (*PFNGLDELETEBUFFERSPROC)(GLsizei n, const GLuint *buffers);
+typedef void (*PFNGLGETBUFFERPARAMETERIVPROC)(GLenum target, GLenum pname,
+ GLint *params);
+typedef void (*PFNGLDELETERENDERBUFFERSEXTPROC)(GLsizei n,
+ const GLuint *renderbuffers);
+typedef void (*PFNGLDELETEFRAMEBUFFERSEXTPROC)(GLsizei n,
+ const GLuint *framebuffers);
+typedef void (*PFNGLCOMPRESSEDTEXSUBIMAGE1DPROC)(GLenum target, GLint level,
+ GLint xoffset, GLsizei width,
+ GLenum format,
+ GLsizei imageSize,
+ const GLvoid *data);
+typedef void (*PFNGLGETCOMPRESSEDTEXIMAGEPROC)(GLenum target, GLint level,
+ GLvoid *img);
+typedef GLenum (*PFNGLCHECKFRAMEBUFFERSTATUSEXTPROC)(GLenum target);
+typedef void (*PFNGLBINDRENDERBUFFEREXTPROC)(GLenum target,
+ GLuint renderbuffer);
+typedef void (*PFNGLBUFFERDATAPROC)(GLenum target, GLsizeiptr size,
+ const GLvoid *data, GLenum usage);
+typedef void (*PFNGLGENFRAMEBUFFERSEXTPROC)(GLsizei n, GLuint *framebuffers);
+typedef void (*PFNGLGENRENDERBUFFERSEXTPROC)(GLsizei n, GLuint *renderbuffers);
+typedef void (*PFNGLFRAMEBUFFERTEXTURE2DEXTPROC)(GLenum target,
+ GLenum attachment,
+ GLenum textarget,
+ GLuint texture, GLint level);
+typedef void (*PFNGLCOMPRESSEDTEXIMAGE2DPROC)(GLenum target, GLint level,
+ GLenum internalformat,
+ GLsizei width, GLsizei height,
+ GLint border, GLsizei imageSize,
+ const GLvoid *data);
+typedef void (*PFNGLFRAMEBUFFERRENDERBUFFEREXTPROC)(GLenum target,
+ GLenum attachment,
+ GLenum renderbuffertarget,
+ GLuint renderbuffer);
+typedef void (*PFNGLRENDERBUFFERSTORAGEEXTPROC)(GLenum target,
+ GLenum internalformat,
+ GLsizei width, GLsizei height);
+typedef void (*PFNGLBINDFRAMEBUFFEREXTPROC)(GLenum target, GLuint framebuffer);
+#endif
+
+#define INVALID_COORD (-2147483647 - 1)
+
+// wxWindows Hash Map Declarations
+#include
+class RuleHash;
+
+WX_DECLARE_HASH_MAP(wxString, Rule *, wxStringHash, wxStringEqual, RuleHash);
+
+WX_DEFINE_SORTED_ARRAY(LUPrec *, wxArrayOfLUPrec);
+
+WX_DECLARE_LIST(S52_TextC, TextObjList);
+
+struct CARC_Buffer {
+ unsigned char color[3][4];
+ float line_width[3];
+ int steps;
+
+ int size;
+ float *data;
+};
+WX_DECLARE_STRING_HASH_MAP(CARC_Buffer, CARC_Hash);
+WX_DECLARE_STRING_HASH_MAP(int, CARC_DL_Hash);
+
+struct SoundingSym {
+ Rule *prule;
+ wxPoint2DDouble r;
+ wxUint32 color_RGB;
+};
+
+class PixelCache;
+
+class RenderFromHPGL;
+class TexFont;
+class wxFileConfig;
+
+class noshow_element {
+public:
+ char obj[7];
+};
+
+WX_DECLARE_OBJARRAY(noshow_element, ArrayOfNoshow);
+
+//-----------------------------------------------------------------------------
+// LUP Array container, and friends
+//-----------------------------------------------------------------------------
+typedef struct _LUPHashIndex {
+ int n_start;
+ int count;
+} LUPHashIndex;
+
+WX_DECLARE_STRING_HASH_MAP(LUPHashIndex *, LUPArrayIndexHash);
+
+class s52plib; // Forward
+extern s52plib *ps52plib; ///< Global instance
+
+/** in s52cnsy */
+extern bool GetDoubleAttr(S57Obj *obj, const char *AttrName, double &val);
+
+class LUPArrayContainer {
+public:
+ LUPArrayContainer();
+ ~LUPArrayContainer();
+
+ wxArrayOfLUPrec *GetLUPArray(void) { return LUPArray; }
+ LUPHashIndex *GetArrayIndexHelper(const char *objectName);
+
+private:
+ wxArrayOfLUPrec *LUPArray; // Sorted Array
+ LUPArrayIndexHash IndexHash;
+};
+
+class VPointCompat {
+public:
+ int pix_width;
+ int pix_height;
+ double view_scale_ppm;
+ double rotation;
+ double clat;
+ double clon;
+ double chart_scale;
+ wxRect rv_rect;
+ double ref_scale;
+ double display_scale;
+};
+
+typedef struct {
+ TexFont *cache;
+ wxFont *key;
+} TexFontCache;
+
+void toSM_plib(double lat, double lon, double lat0, double lon0, double *x,
+ double *y);
+
+void fromSM_plib(double x, double y, double lat0, double lon0, double *lat,
+ double *lon);
+//-----------------------------------------------------------------------------
+// s52plib definition
+//-----------------------------------------------------------------------------
+class s52plib {
+public:
+ s52plib(const wxString &PLib, bool b_forceLegacy = false);
+ ~s52plib();
+
+ // TODO: SetPPM, SetDisplayWidth etc. should be combined to be set together by
+ // pointing them to info about current monitor
+ /**
+ * Set pixels per millimeter for symbol rendering.
+ * @param ppmm Logical pixels per millimeter of display.
+ */
+ void SetPPMM(float ppmm);
+ void SetDisplayWidth(size_t pixels) { m_display_width = pixels; }
+ float GetPPMM() { return canvas_pix_per_mm; }
+ void SetDIPFactor(double factor);
+ void SetContentScaleFactor(double factor);
+
+ void SetOCPNVersion(int major, int minor, int patch);
+
+ double GetRVScaleFactor() { return m_rv_scale_factor; }
+
+ LUPrec *S52_LUPLookup(LUPname LUP_name, const char *objectName, S57Obj *pObj,
+ bool bStrict = 0);
+ int _LUP2rules(LUPrec *LUP, S57Obj *pObj);
+ S52color *getColor(const char *colorName);
+ wxColour getwxColour(const wxString &colorName);
+
+ void UpdateMarinerParams(void);
+ void ClearCNSYLUPArray(void);
+
+ void GenerateStateHash();
+ long GetStateHash() { return m_state_hash; }
+
+ void SetPLIBColorScheme(wxString scheme, const ChartCtx &ctx);
+ void SetPLIBColorScheme(ColorScheme cs, const ChartCtx &ctx);
+ wxString GetPLIBColorScheme(void) { return m_ColorScheme; }
+
+ void SetGLRendererString(const wxString &renderer);
+ wxString GetGLRendererString() { return m_renderer_string; }
+ void SetGLOptions(bool b_useStencil, bool b_useStencilAP, bool b_useScissors,
+ bool b_useFBO, bool b_useVBO, int nTextureFormat,
+ float MinCartographicLineWidth, float MinSymbolLineWidth);
+ void SetUseGLSL(bool useGLSL) { m_useGLSL = useGLSL; }
+
+ bool ObjectRenderCheck(ObjRazRules *rzRules);
+ bool ObjectRenderCheckRules(ObjRazRules *rzRules, bool check_noshow = false);
+ bool ObjectRenderCheckPos(ObjRazRules *rzRules);
+ bool ObjectRenderCheckPosReduced(ObjRazRules *rzRules);
+ bool ObjectRenderCheckCat(ObjRazRules *rzRules);
+ bool ObjectRenderCheckCS(ObjRazRules *rzRules);
+ bool ObjectRenderCheckDates(ObjRazRules *rzRules);
+
+ static void DestroyLUP(LUPrec *pLUP);
+ static void ClearRulesCache(Rule *pR);
+ DisCat findLUPDisCat(const char *objectName, LUPname TNAM);
+
+ // Temporarily save/restore the current colortable index
+ // Useful for Thumbnail rendering
+ void SaveColorScheme(void) { m_colortable_index_save = m_colortable_index; }
+ void RestoreColorScheme(void) {}
+
+ // Rendering stuff
+ void PrepareForRender(VPointCompat *vp);
+ void PrepareForRender(void);
+ void AdjustTextList(int dx, int dy, int screenw, int screenh);
+ void ClearTextList(void);
+ int SetLineFeaturePriority(ObjRazRules *rzRules, int npriority);
+ void FlushSymbolCaches(const ChartCtx &ctx);
+
+ // For DC's
+ int RenderObjectToDC(wxDC *pdc, ObjRazRules *rzRules);
+ int RenderObjectToDCText(wxDC *pdc, ObjRazRules *rzRules);
+ int RenderAreaToDC(wxDC *pdc, ObjRazRules *rzRules,
+ render_canvas_parms *pb_spec);
+
+ // Accessors
+ bool GetShowSoundings() { return m_bShowSoundg; }
+ void SetShowSoundings(bool f) {
+ m_bShowSoundg = f;
+ GenerateStateHash();
+ }
+
+ bool GetShowS57Text() { return m_bShowS57Text; }
+ void SetShowS57Text(bool f) {
+ m_bShowS57Text = f;
+ GenerateStateHash();
+ }
+
+ bool GetShowS57ImportantTextOnly() { return m_bShowS57ImportantTextOnly; }
+ void SetShowS57ImportantTextOnly(bool f) {
+ m_bShowS57ImportantTextOnly = f;
+ GenerateStateHash();
+ }
+
+ void SetLightsOff(bool val) { m_lightsOff = val; }
+ bool GetLightsOff() { return m_lightsOff; }
+
+ void SetAnchorOn(bool val);
+ bool GetAnchorOn();
+
+ void SetQualityOfData(bool val);
+ bool GetQualityOfData();
+
+ void SetScaleFactorExp(double ChartScaleFactorExp);
+ void SetScaleFactorZoomMod(int chart_zoom_modifier_vector);
+
+ int GetMajorVersion(void) { return m_VersionMajor; }
+ int GetMinorVersion(void) { return m_VersionMinor; }
+
+ void SetTextOverlapAvoid(bool f) { m_bDeClutterText = f; }
+ void SetShowNationalText(bool f) { m_bShowNationalTexts = f; }
+ void SetShowAtonText(bool f) { m_bShowAtonText = f; }
+ void SetShowLdisText(bool f) { m_bShowLdisText = f; }
+ void SetExtendLightSectors(bool f) { m_bExtendLightSectors = f; }
+
+ void SetDisplayCategory(enum _DisCat cat);
+ DisCat GetDisplayCategory() { return m_nDisplayCategory; }
+
+ void SetGLPolygonSmoothing(bool bset) { m_GLPolygonSmoothing = bset; }
+ bool GetGLPolygonSmoothing() { return m_GLPolygonSmoothing; }
+ void SetGLLineSmoothing(bool bset) { m_GLLineSmoothing = bset; }
+ bool GetGLLineSmoothing() { return m_GLLineSmoothing; }
+
+ wxArrayOfLUPrec *SelectLUPARRAY(LUPname TNAM);
+ LUPArrayContainer *SelectLUPArrayContainer(LUPname TNAM);
+
+ void DestroyPatternRuleNode(Rule *pR);
+ void DestroyRuleNode(Rule *pR);
+ static void DestroyRulesChain(Rules *top);
+
+ // For OpenGL
+ int RenderObjectToGL(const wxGLContext &glcc, ObjRazRules *rzRules);
+ int RenderAreaToGL(const wxGLContext &glcc, ObjRazRules *rzRules);
+ int RenderObjectToGLText(const wxGLContext &glcc, ObjRazRules *rzRules);
+
+ bool EnableGLLS(bool benable);
+
+ bool IsObjNoshow(const char *objcl);
+ void AddObjNoshow(const char *objcl);
+ void RemoveObjNoshow(const char *objcl);
+ void ClearNoshow(void);
+ void SaveObjNoshow() { m_saved_noshow = m_noshow_array; };
+ void RestoreObjNoshow() { m_noshow_array = m_saved_noshow; };
+
+ void SetVPointCompat(int pix_width, int pix_height, double view_scale_ppm,
+ double rotation, double clat, double clon,
+ double chart_scale, wxRect rv_rect, LLBBox &bbox,
+ double ref_scale, double display_scale);
+
+ // Todo accessors
+ LUPname m_nSymbolStyle;
+ LUPname m_nBoundaryStyle;
+ bool m_bOK;
+
+ bool m_bShowSoundg;
+ bool m_bShowMeta;
+ bool m_bShowS57Text;
+ bool m_bUseSCAMIN;
+ bool m_bUseSUPER_SCAMIN;
+ bool m_bShowAtonText;
+ bool m_bShowLdisText;
+ bool m_bExtendLightSectors;
+ bool m_bShowS57ImportantTextOnly;
+ bool m_bDeClutterText;
+ bool m_bShowNationalTexts;
+ int m_nSoundingFactor;
+ double m_SoundingsScaleFactor;
+ int m_nTextFactor;
+ double m_TextScaleFactor;
+ int m_SoundingsPointSize;
+ double m_SoundingsFontSizeMM;
+ double m_soundFontDelta;
+ double m_ChartScaleFactorExp;
+ int m_chart_zoom_modifier_vector;
+ double m_dipfactor;
+ double m_ContentScaleFactor;
+ double m_FinalTextScaleFactor;
+
+ int m_VersionMajor;
+ int m_VersionMinor;
+
+ int m_nDepthUnitDisplay;
+ bool m_isSoundingFontSet;
+ int m_sounding_char_width;
+ int m_sounding_char_height;
+ int m_sounding_char_descent;
+#ifdef ocpnUSE_GL
+ GLint m_sounding_shader_uni_color;
+ GLint m_sounding_shader_uni_uTex;
+ GLint m_sounding_shader_attr_position;
+ GLint m_sounding_shader_attr_aUV;
+ GLint m_sounding_shader_uni_transform;
+#endif
+ // Preferred display unit for vertical heights (meters/feet).
+ // 0 = meters, 1 = feet
+ int m_nHeightUnitDisplay;
+
+ // Library data
+ wxArrayPtrVoid *pAlloc;
+
+ RuleHash *_line_sym; // line symbolisation rules
+ RuleHash *_patt_sym; // pattern symbolisation rules
+ RuleHash *_cond_sym; // conditional symbolisation rules
+ RuleHash *_symb_symR; // symbol symbolisation rules, Raster
+
+ LUPArrayContainer *line_LAC;
+ LUPArrayContainer *areaPlain_LAC;
+ LUPArrayContainer *areaSymbol_LAC;
+ LUPArrayContainer *pointSimple_LAC;
+ LUPArrayContainer *pointPaper_LAC;
+
+ wxArrayOfLUPrec *condSymbolLUPArray; // Dynamic Conditional Symbology
+
+ wxArrayPtrVoid *pOBJLArray; // Used for Display Filtering
+ std::vector OBJLDescriptions;
+
+ RuleHash *_symb_sym; // symbol symbolisation rules
+ std::unordered_map m_natsur_hash;
+ // hash table for cacheing NATSUR string values
+ // from int attributes
+
+ int m_myConfig;
+
+ double lastLightLat;
+ double lastLightLon;
+ float m_GLMinCartographicLineWidth;
+ float m_GLMinSymbolLineWidth;
+
+ ChartSymbols m_chartSymbols;
+
+ void PLIB_LoadS57GlobalConfig(wxFileConfig *pconfig);
+ void PLIB_LoadS57ObjectConfig(wxFileConfig *pconfig);
+ void SetReducedBBox(LLBBox box) { reducedBBox = box; }
+
+ void RenderTex(char *str, char *col, wxPoint &r, wxPoint &pivot,
+ wxPoint origin, float scale, double rot_angle, float sym_len,
+ float sym_height, float seg_len);
+ int BuildLCSymbolTexture(char *str, char *col, wxPoint &r, wxPoint &pivot,
+ wxPoint origin, float scale, float sym_len,
+ float sym_height);
+
+private:
+ int S52_load_Plib(const wxString &PLib, bool b_forceLegacy);
+ bool S52_flush_Plib();
+
+ void PLIB_LoadS57Config();
+
+ void InitializeNatsurHash();
+ bool PreloadOBJLFromCSV(const wxString &csv_file);
+
+ int DoRenderObject(wxDC *pdcin, ObjRazRules *rzRules);
+ int DoRenderObjectTextOnly(wxDC *pdcin, ObjRazRules *rzRules);
+
+ // Area Renderers
+ int RenderToBufferAC(ObjRazRules *rzRules, Rules *rules,
+ render_canvas_parms *pb_spec);
+ int RenderToBufferAP(ObjRazRules *rzRules, Rules *rules,
+ render_canvas_parms *pb_spec);
+
+ int RenderToGLAC(ObjRazRules *rzRules, Rules *rules);
+ int RenderToGLAC_GLSL(ObjRazRules *rzRules, Rules *rules);
+
+ int RenderToGLAP(ObjRazRules *rzRules, Rules *rules);
+ int RenderToGLAP_GLSL(ObjRazRules *rzRules, Rules *rules);
+
+ // Object Renderers
+ int RenderTX(ObjRazRules *rzRules, Rules *rules);
+ int RenderTE(ObjRazRules *rzRules, Rules *rules);
+ int RenderSY(ObjRazRules *rzRules, Rules *rules);
+ int RenderLS(ObjRazRules *rzRules, Rules *rules);
+ int RenderLC(ObjRazRules *rzRules, Rules *rules);
+ int RenderMPS(ObjRazRules *rzRules, Rules *rules);
+ void RenderMPSArray(ObjRazRules *rzRules, std::vector array);
+ int RenderCARC(ObjRazRules *rzRules, Rules *rules);
+ char *RenderCS(ObjRazRules *rzRules, Rules *rules);
+ int RenderGLLS(ObjRazRules *rzRules, Rules *rules);
+ int RenderGLLC(ObjRazRules *rzRules, Rules *rules);
+
+ int RenderCARC_VBO(ObjRazRules *rzRules, Rules *rules);
+ int RenderCARC_GLSL(ObjRazRules *rzRules, Rules *rules);
+
+ void UpdateOBJLArray(S57Obj *obj);
+
+ int reduceLOD(double LOD_meters, int nPoints, double *source,
+ wxPoint2DDouble **dest, int *maskIn, int **maskOut);
+ int ComputeWinding(ObjRazRules *rzRules, Rules *rules);
+
+ int RenderLSLegacy(ObjRazRules *rzRules, Rules *rules);
+ int RenderLCLegacy(ObjRazRules *rzRules, Rules *rules);
+ int RenderGLLSLegacy(ObjRazRules *rzRules, Rules *rules);
+ int RenderGLLCLegacy(ObjRazRules *rzRules, Rules *rules);
+ int RenderLSPlugIn(ObjRazRules *rzRules, Rules *rules);
+ int RenderLCPlugIn(ObjRazRules *rzRules, Rules *rules);
+ int RenderLCTexture(ObjRazRules *rzRules, Rules *rules);
+
+ int RenderLS_Dash_GLSL(ObjRazRules *rzRules, Rules *rules);
+
+ void DrawDashLine(wxPen &pen, double x1, double y1, double x2, double y2);
+
+ render_canvas_parms *CreatePatternBufferSpec(ObjRazRules *rzRules,
+ Rules *rules, bool b_revrgb,
+ bool b_pot = false);
+
+ void RenderToBufferFilledPolygon(ObjRazRules *rzRules, S57Obj *obj,
+ S52color *c, render_canvas_parms *pb_spec,
+ render_canvas_parms *patt_spec);
+
+ void draw_lc_poly_texture(wxDC *pdc, wxColor &color, int width, wxPoint2DDouble *ptp,
+ int *mask, int npt, float sym_len, float sym_height,
+ float sym_factor, Rule *draw_rule, bool winding_cw);
+ void draw_lc_poly(wxDC *pdc, wxColor &color, int width, wxPoint *ptp,
+ int *mask, int npt, float sym_len, float sym_height,
+ float sym_factor, Rule *draw_rule);
+
+ bool RenderHPGL(ObjRazRules *rzRules, Rule *rule_in, wxPoint2DDouble &r,
+ float rot_angle = 0., double uScale = 1.0);
+ bool RenderRasterSymbol(ObjRazRules *rzRules, Rule *prule, wxPoint2DDouble &r,
+ float rot_angle = 0.);
+ bool RenderCachedVectorSymbol(ObjRazRules *rzRules, Rule *rule_in, wxPoint2DDouble &r,
+ float rot_angle, double uScale);
+ int BuildHPGLTexture(ObjRazRules *rzRules, Rule *rule_in, wxPoint2DDouble &r,
+ float rot_angle, double uScale);
+
+ void SetupSoundingFont();
+ bool RenderSoundingSymbol(ObjRazRules *rzRules, Rule *prule, wxPoint &r,
+ wxColor symColor, float rot_angle = 0.);
+ wxImage RuleXBMToImage(Rule *prule);
+
+ bool RenderText(wxDC *pdc, S52_TextC *ptext, double x, double y, wxRect2D *pRectDrawn,
+ S57Obj *pobj, bool bCheckOverlap);
+
+ bool CheckTextRectList(const wxRect2D &test_rect, S52_TextC *ptext);
+ int RenderT_All(ObjRazRules *rzRules, Rules *rules, bool bTX);
+
+ int PrioritizeLineFeature(ObjRazRules *rzRules, int npriority);
+
+ int dda_tri(wxPoint *ptp, S52color *c, render_canvas_parms *pb_spec,
+ render_canvas_parms *pPatt_spec);
+ int dda_trap(wxPoint *segs, int lseg, int rseg, int ytop, int ybot,
+ S52color *c, render_canvas_parms *pb_spec,
+ render_canvas_parms *pPatt_spec);
+
+ LUPrec *FindBestLUP(wxArrayOfLUPrec *LUPArray, unsigned int startIndex,
+ unsigned int count, S57Obj *pObj, bool bStrict);
+
+ void SetGLClipRect(const VPointCompat &vp, const wxRect &rect);
+
+ char *_getParamVal(ObjRazRules *rzRules, char *str, char *buf, int bsz);
+ S52_TextC *S52_PL_parseTX(ObjRazRules *rzRules, Rules *rules, char *cmd);
+ char *_parseTEXT(ObjRazRules *rzRules, S52_TextC *text, char *str0);
+ S52_TextC *S52_PL_parseTE(ObjRazRules *rzRules, Rules *rules, char *cmd);
+
+ Rules *StringToRules(const wxString &str_in);
+ void GetAndAddCSRules(ObjRazRules *rzRules, Rules *rules);
+
+ void DestroyPattRules(RuleHash *rh);
+ void DestroyRules(RuleHash *rh);
+ void DestroyLUPArray(wxArrayOfLUPrec *pLUPArray);
+
+ bool TextRenderCheck(ObjRazRules *rzRules);
+ bool inter_tri_rect(wxPoint *ptp, render_canvas_parms *pb_spec);
+
+ bool GetPointPixArray(ObjRazRules *rzRules, wxPoint2DDouble *pd, wxPoint *pp,
+ int nv);
+ bool GetPointPixSingle(ObjRazRules *rzRules, float north, float east,
+ wxPoint *r);
+ void GetPixPointSingle(int pixx, int pixy, double *plat, double *plon);
+ void GetPixPointSingleNoRotate(int pixx, int pixy, double *plat,
+ double *plon);
+ bool GetDoublePointPixSingle(ObjRazRules *rzRules, float north, float east,
+ wxPoint2DDouble *r);
+ bool GetDoublePointPixArray(ObjRazRules *rzRules, wxPoint2DDouble *pd, wxPoint2DDouble *pp,
+ int nv);
+
+ void GetLLFromPix(const wxPoint2DDouble &p, double *lat, double *lon);
+ wxPoint GetPixFromLL(double lat, double lon);
+ wxPoint GetPixFromLLROT(double lat, double lon, double rotation);
+ wxPoint2DDouble GetDoublePixFromLL(double lat, double lon);
+ wxPoint2DDouble GetDoublePixFromLLROT(double lat, double lon,
+ double rotation);
+
+ LLBBox &GetBBox() { return BBox; }
+ LLBBox GetReducedBBox() { return reducedBBox; }
+
+ wxString m_plib_file;
+
+ /**
+ * The number of pixels per millimeter of the canvas.
+ *
+ * @note Set by parent, used to scale symbols/lines/patterns.
+ */
+ float canvas_pix_per_mm;
+ double m_rv_scale_factor;
+ float m_display_size_mm;
+ size_t m_display_width;
+
+ S52color m_unused_color;
+ wxColor m_unused_wxColor;
+
+ bool bUseRasterSym;
+
+ wxDC *m_pdc; // The current DC
+
+ // #ifdef ocpnUSE_GL
+ wxGLContext *m_glcc;
+ // #endif
+
+ int *ledge;
+ int *redge;
+
+ int m_colortable_index;
+ int m_colortable_index_save;
+
+ TextObjList m_textObjList;
+
+ wxString m_ColorScheme;
+
+ bool m_lightsOff;
+ bool m_anchorOn;
+ bool m_qualityOfDataOn;
+
+ long m_state_hash;
+
+ bool m_txf_ready;
+ int m_txf_avg_char_width;
+ int m_txf_avg_char_height;
+ CARC_Hash m_CARC_hashmap;
+ CARC_DL_Hash m_CARC_DL_hashmap;
+ RenderFromHPGL *HPGL;
+
+ TexFont *m_txf;
+ DepthFont m_texSoundings;
+ bool m_benableGLLS;
+ DisCat m_nDisplayCategory;
+ ArrayOfNoshow m_noshow_array;
+ ArrayOfNoshow m_saved_noshow;
+
+ int m_coreVersionMajor;
+ int m_coreVersionMinor;
+ int m_coreVersionPatch;
+
+ // GL Options, set by core depending on hardware capability
+ bool m_useStencil;
+ bool m_useStencilAP;
+ bool m_useScissors;
+ bool m_useFBO;
+ bool m_useVBO;
+ bool m_GLAC_VBO;
+ int m_TextureFormat;
+ bool m_GLLineSmoothing;
+ bool m_GLPolygonSmoothing;
+ wxFont *m_soundFont;
+ bool m_useGLSL;
+
+ double m_displayScale;
+
+ VPointCompat vp_plib;
+ LLBBox BBox;
+#define TXF_CACHE 8
+ TexFontCache s_txf[TXF_CACHE];
+ wxString m_renderer_string;
+ wxFont *ChartTextDefaultFont;
+
+ LLBBox reducedBBox;
+ std::unordered_map vector_symbol_cache;
+ std::unordered_map lc_vector_symbol_cache;
+};
+
+#define HPGL_FILLED true
+
+class RenderFromHPGL {
+public:
+ RenderFromHPGL(s52plib *plibarg);
+ ~RenderFromHPGL();
+
+ void SetTargetDC(wxDC *pdc);
+ void SetTargetOpenGl();
+#if wxUSE_GRAPHICS_CONTEXT
+ void SetTargetGCDC(wxGCDC *gdc);
+#endif
+ void SetVP(VPointCompat *pVP) { m_vp = pVP; }
+ void SetContentScaleFactor(double factor) { m_content_scale_factor = factor; }
+ bool Render(char *str, char *col, wxPoint2DDouble &r, wxPoint &pivot, wxPoint origin,
+ float scale, double rot_angle, bool bSymbol);
+ wxBrush *getBrush() { return brush; }
+
+ GLUtesselator *m_tobj;
+ int s_odc_tess_vertex_idx;
+ int s_odc_tess_vertex_idx_this;
+ int s_odc_tess_buf_len;
+ GLenum s_odc_tess_mode;
+ int s_odc_nvertex;
+ GLfloat *s_odc_tess_work_buf;
+
+private:
+ const char *findColorNameInRef(char colorCode, char *col);
+ void RotatePoint(wxPoint2DDouble &point, wxPoint origin, double angle);
+ wxPoint ParsePoint(wxString &argument);
+ void SetPen();
+ void Line(wxPoint2DDouble from, wxPoint2DDouble to);
+ void Circle(wxPoint2DDouble center, int radius, bool filled = false);
+ void Polygon();
+
+ void DrawPolygonTessellated(int n, wxPoint points[], wxCoord xoffset,
+ wxCoord yoffset);
+ void DrawPolygon(int n, wxPoint points[], wxCoord xoffset, wxCoord yoffset,
+ float scale, float angle);
+
+ s52plib *plib;
+ double scaleFactor;
+
+ wxDC *targetDC;
+#if wxUSE_GRAPHICS_CONTEXT
+ wxGCDC *targetGCDC;
+#endif
+
+ wxColor penColor;
+ wxPen *pen;
+ wxColor brushColor;
+ wxBrush *brush;
+ long penWidth;
+ int transparency;
+ double m_content_scale_factor;
+
+ int noPoints;
+ wxPoint polygon[100];
+
+ float m_currentColor[4];
+
+ bool renderToDC;
+ bool renderToOpenGl;
+ bool renderToGCDC;
+ VPointCompat *m_vp;
+
+ float *workBuf;
+ size_t workBufSize;
+ unsigned int workBufIndex;
+};
+
+#endif //_S52PLIB_H_
diff --git a/docs/外部参考/OpenCPN源码基线_f7211b4e/上游源码/libs/s52plib/src/s52plibGL.h b/docs/外部参考/OpenCPN源码基线_f7211b4e/上游源码/libs/s52plib/src/s52plibGL.h
new file mode 100644
index 0000000..60401c9
--- /dev/null
+++ b/docs/外部参考/OpenCPN源码基线_f7211b4e/上游源码/libs/s52plib/src/s52plibGL.h
@@ -0,0 +1,56 @@
+/******************************************************************************
+ *
+ * Project: OpenCPN
+ * Purpose: Global Build Options
+ * Author: David Register
+ *
+ ***************************************************************************
+ * Copyright (C) 2010 by David S. Register *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
+ ***************************************************************************
+ *
+ */
+
+//----------------------------------------------------------------------------------
+// OpenGL Build options for S52PLIB
+//----------------------------------------------------------------------------------
+#ifndef _S52PLIBGL_H_
+#define _S52PLIBGL_H_
+
+//#include "config.h"
+
+#include
+#include
+
+
+#if defined(__OCPN__ANDROID__)
+ #include
+ #include // this is a cut-down version of gl.h
+ #include
+#elif defined(__MSVC__)
+ #include "glew.h"
+#elif defined(__WXOSX__)
+ #include
+ #include
+ typedef void (* _GLUfuncptr)();
+ #define GL_COMPRESSED_RGB_FXT1_3DFX 0x86B0
+#elif defined(__WXQT__) || defined(__WXGTK__)
+ #include
+ #include
+#endif
+
+#endif // __FILE__
diff --git a/docs/外部参考/OpenCPN源码基线_f7211b4e/上游源码/libs/s52plib/src/s52s57.h b/docs/外部参考/OpenCPN源码基线_f7211b4e/上游源码/libs/s52plib/src/s52s57.h
new file mode 100644
index 0000000..aba4b3a
--- /dev/null
+++ b/docs/外部参考/OpenCPN源码基线_f7211b4e/上游源码/libs/s52plib/src/s52s57.h
@@ -0,0 +1,646 @@
+/******************************************************************************
+ *
+ * Project: OpenCP
+ * Purpose: S52 PLIB and S57 Chart data types
+ * Author: David Register
+ *
+ ***************************************************************************
+ * Copyright (C) 2010 by David S. Register *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
+ ***************************************************************************
+ *
+ */
+
+#ifndef _S52S57_H_
+#define _S52S57_H_
+
+#include "bbox.h"
+#include "color_types.h"
+
+#include
+#include
+#include
+
+#define CURRENT_SENC_FORMAT_VERSION 201
+
+#define OBJL_NAME_LEN 6
+
+// Fwd Defns
+class wxArrayOfS57attVal;
+class OGREnvelope;
+class OGRGeometry;
+class VE_Element;
+class VC_Element;
+
+class wxRect2D {
+public:
+ double x;
+ double y;
+ double width;
+ double height;
+
+ wxRect2D();
+ wxRect2D(double xx, double yy, double w, double h);
+ void SetX(double _x){ x = _x;}
+ void SetY(double _y){ y = _y;}
+ void SetWidth(double _width){ width = _width;}
+ void SetHeight(double _height){ height = _height;}
+
+ bool Intersects(const wxRect2D& other,
+ double epsilon = 1e-6) const;
+ wxRect2D Union(const wxRect2D& other) const;
+ bool IsEmpty() const {return width <= 0.0 || height <= 0.0;}
+
+};
+
+// name of the addressed look up table set (fifth letter)
+typedef enum _LUPname {
+ SIMPLIFIED = 'L', // points
+ PAPER_CHART = 'R', // points
+ LINES = 'S', // lines
+ PLAIN_BOUNDARIES = 'N', // areas
+ SYMBOLIZED_BOUNDARIES = 'O', // areas
+ LUPNAME_NUM = 5
+} LUPname;
+
+// Addressed Object Type
+typedef enum _Object_t {
+ POINT_T = 'P',
+ LINES_T = 'L',
+ AREAS_T = 'A',
+ OBJ_NUM = 3 // number of object type
+} Object_t;
+
+// Display Priority
+typedef enum _DisPrio {
+ PRIO_NODATA = '0', // no data fill area pattern
+ PRIO_GROUP1 = '1', // S57 group 1 filled areas
+ PRIO_AREA_1 = '2', // superimposed areas
+ PRIO_AREA_2 = '3', // superimposed areas also water features
+ PRIO_SYMB_POINT = '4', // point symbol also land features
+ PRIO_SYMB_LINE = '5', // line symbol also restricted areas
+ PRIO_SYMB_AREA = '6', // area symbol also traffic areas
+ PRIO_ROUTEING = '7', // routeing lines
+ PRIO_HAZARDS = '8', // hazards
+ PRIO_MARINERS = '9', // VRM, EBL, own ship
+ PRIO_NUM = 10 // number of priority levels
+
+} DisPrio;
+
+// RADAR Priority
+typedef enum _RadPrio {
+ RAD_OVER = 'O', // presentation on top of RADAR
+ RAD_SUPP = 'S', // presentation suppressed by RADAR
+ RAD_NUM = 2
+} RadPrio;
+
+// PixelPtr type flags
+enum {
+ ID_EMPTY = 0,
+ ID_wxBitmap,
+ ID_RGBA,
+ ID_GL_PATT_SPEC,
+ ID_RGB_PATT_SPEC,
+ ID_GLIST
+
+};
+
+// display category type
+typedef enum _DisCat {
+ DISPLAYBASE = 'D', //
+ STANDARD = 'S', //
+ OTHER = 'O', // O for OTHER
+ MARINERS_STANDARD = 'M', // Mariner specified
+ MARINERS_OTHER, // value not defined
+ DISP_CAT_NUM, // value not defined
+} DisCat;
+
+#define MASK_POINT 1
+#define MASK_LINE 2
+#define MASK_AREA 4
+#define MASK_MPS 8
+#define MASK_ALL MASK_POINT + MASK_LINE + MASK_AREA + MASK_MPS
+
+typedef enum _Rules_t {
+ RUL_NONE, // no rule type (init)
+ RUL_TXT_TX, // TX
+ RUL_TXT_TE, // TE
+ RUL_SYM_PT, // SY
+ RUL_SIM_LN, // LS
+ RUL_COM_LN, // LC
+ RUL_ARE_CO, // AC
+ RUL_ARE_PA, // AP
+ RUL_CND_SY, // CS
+ RUL_MUL_SG, // Multipoint Sounding
+ RUL_ARC_2C // Circular Arc, used for sector lights, opencpn private
+} Rules_t;
+
+//-- SYMBOLISATION MODULE STRUCTURE -----------------------------
+// position parameter: LINE, PATTERN, SYMBOL
+typedef struct _position {
+ union {
+ int dummy1, PAMI, dummy2;
+ } minDist;
+ union {
+ int dummy1, PAMA, dummy2;
+ } maxDist;
+ union {
+ int LICL, PACL, SYCL;
+ } pivot_x;
+ union {
+ int LIRW, PARW, SYRW;
+ } pivot_y;
+ union {
+ int LIHL, PAHL, SYHL;
+ } bnbox_w;
+ union {
+ int LIVL, PAVL, SYVL;
+ } bnbox_h;
+ union {
+ int LBXC, PBXC, SBXC;
+ } bnbox_x; // UpLft crnr
+ union {
+ int LBXR, PBXR, SBXR;
+ } bnbox_y; // UpLft crnr
+} position;
+
+// rule parameter for : LINE, PATTERN, SYMBOL
+typedef struct _Rule {
+ int RCID;
+ union {
+ char LINM[8], PANM[8], SYNM[8];
+ } name;
+ union {
+ char dummy, PADF, SYDF;
+ } definition;
+ union {
+ char dummy1, PATP, dummy2;
+ } fillType;
+ union {
+ char dummy1, PASP, dummy2;
+ } spacing;
+ union {
+ position line, patt, symb;
+ } pos;
+ union {
+ wxString *LXPO, *PXPO, *SXPO;
+ } exposition;
+ union {
+ wxString *dummy, *PBTM, *SBTM;
+ } bitmap;
+ union {
+ char *LCRF, *PCRF, *SCRF;
+ } colRef;
+ union {
+ char *LVCT, *PVCT, *SVCT;
+ } vector;
+
+ // Private data
+ int parm0; // always indicates form of pixelPtr
+ int parm1; // integer parameters
+ int parm2;
+ int parm3;
+ int parm4;
+ int parm5;
+ int parm6;
+ int parm7;
+ void *pixelPtr; // opaque pointer
+
+} Rule;
+
+typedef struct _Rules {
+ Rules_t ruleType;
+ char *INSTstr; // Symbology Instruction string
+ Rule *razRule; // rule
+ char *INST0; // Head of the entire object Instruction string
+ int n_sequence; // sequence number in list, used to identify a particular
+ // rule
+ bool b_private_razRule; // marker indicating that razRule should be free'd on
+ // Rules destroy
+ struct _Rules *next;
+} Rules;
+
+// LOOKUP MODULE CLASS
+
+class LUPrec {
+public:
+ ~LUPrec() { ATTArray.clear(); };
+ int RCID; // record identifier
+ char OBCL[7]; // Name (6 char) '\0' terminated
+ Object_t FTYP; // 'A' Area, 'L' Line, 'P' Point
+ DisPrio DPRI; // Display Priority
+ RadPrio RPRI; // 'O' or 'S', Radar Priority
+ LUPname TNAM; // FTYP: areas, points, lines
+ std::vector ATTArray; // Array of LUP Attributes
+ wxString INST; // Instruction Field (rules)
+ DisCat DISC; // Display Categorie: D/S/O, DisplayBase, Standard, Other
+ int LUCM; // Look-Up Comment (PLib3.x put 'groupes' here,
+ // hence 'int', but its a string in the specs)
+ int nSequence; // A sequence number, indicating order of encounter in
+ // the PLIB file
+ Rules *ruleList; // rasterization rule list
+};
+
+// Conditional Symbology
+typedef struct _Cond {
+ const char *name;
+ void *(*condInst)(void *param);
+} Cond;
+
+class S52_TextC {
+public:
+ S52_TextC();
+ ~S52_TextC();
+
+ wxString frmtd; // formated text string
+ char hjust;
+ char vjust;
+ char space;
+ char style; // CHARS
+ char weight; // CHARS
+ char width; // CHARS
+ int bsize; // CHARS -body size
+ int xoffs; // text offsets, in units of bsize
+ int yoffs; //
+ S52color *pcol; // pointer to S52colour
+ int dis; // display
+ wxFont *pFont;
+ int rul_seq_creator; // sequence number of the Rule creating this object
+ int RGBA_width;
+ int RGBA_height;
+ int rendered_char_height;
+ wxRect2D
+ rText; // rectangle of the text as currently rendered, used for declutter
+ bool bnat; // frmtd is National text, UTF-8 encoded
+ bool bspecial_char; // frmtd has special ASCII characters, i.e. > 127
+ int avgCharWidth;
+ int texobj;
+ int text_width;
+ int text_height;
+};
+
+//
+// WARNING: must be in sync OGRatt_t
+
+typedef enum _OGRatt_t {
+ OGR_INT,
+ OGR_INT_LST,
+ OGR_REAL,
+ OGR_REAL_LST,
+ OGR_STR,
+} OGRatt_t;
+
+typedef struct _S57attVal {
+ void *value;
+ OGRatt_t valType;
+} S57attVal;
+
+WX_DEFINE_ARRAY(S57attVal *, wxArrayOfS57attVal);
+
+typedef struct _OBJLElement {
+ char OBJLName[OBJL_NAME_LEN];
+ int nViz;
+} OBJLElement;
+
+// OGR primitive type
+typedef enum _geoPrim_t {
+ GEO_POINT,
+ GEO_LINE,
+ GEO_AREA,
+ GEO_META,
+ GEO_PRIM, // number of primitive
+} GeoPrim_t;
+
+typedef struct _pt {
+ double x;
+ double y;
+} pt;
+
+// Fwd References
+class s57chart;
+class S57Obj;
+class OGRFeature;
+class PolyTessGeo;
+class line_segment_element;
+class PI_line_segment_element;
+
+struct chart_context {
+ std::unordered_map *m_pvc_hash;
+ std::unordered_map *m_pve_hash;
+ double ref_lat;
+ double ref_lon;
+ wxArrayPtrVoid *pFloatingATONArray;
+ wxArrayPtrVoid *pRigidATONArray;
+ void *chart;
+ double safety_contour;
+ float *vertex_buffer;
+ int vboID;
+ int chart_type;
+ int chart_scale;
+ std::list *(s57chart::*pt2GetAssociatedObjects)(S57Obj *obj);
+};
+
+class LineGeometryDescriptor {
+public:
+ double extent_s_lat;
+ double extent_n_lat;
+ double extent_w_lon;
+ double extent_e_lon;
+ int indexCount;
+ int *indexTable;
+};
+
+typedef struct _MultipointGeometryDescriptor {
+ double extent_s_lat;
+ double extent_n_lat;
+ double extent_w_lon;
+ double extent_e_lon;
+ int pointCount;
+ void *pointTable;
+} MultipointGeometryDescriptor;
+
+class S57Obj {
+public:
+ // Public Methods
+ S57Obj();
+ ~S57Obj();
+
+ S57Obj(const char *featureName);
+
+ wxString GetAttrValueAsString(const char *attr);
+ int GetAttributeIndex(const char *AttrSeek);
+
+ bool AddIntegerAttribute(const char *acronym, int val);
+ bool AddIntegerListAttribute(const char *acronym, int *pval, int nValue);
+ bool AddDoubleAttribute(const char *acronym, double val);
+ bool AddDoubleListAttribute(const char *acronym, double *pval, int nValue);
+ bool AddStringAttribute(const char *acronym, char *val);
+
+ bool SetPointGeometry(double lat, double lon, double ref_lat, double ref_lon);
+ bool SetLineGeometry(LineGeometryDescriptor *pGeo, GeoPrim_t geoType,
+ double ref_lat, double ref_lon);
+ bool SetAreaGeometry(PolyTessGeo *ppg, double ref_lat, double ref_lon);
+ bool SetMultipointGeometry(MultipointGeometryDescriptor *pGeo, double ref_lat,
+ double ref_lon);
+
+ // Private Methods
+private:
+ void Init();
+
+public:
+ // Instance Data
+ char FeatureName[8];
+ GeoPrim_t Primitive_type;
+
+ char *att_array;
+ wxArrayOfS57attVal *attVal;
+ int n_attr;
+
+ int iOBJL;
+ int Index;
+
+ double x; // for POINT
+ double y;
+ double z;
+ int npt; // number of points as needed by arrays
+
+ pt *geoPt; // used for cm93 line feature select check
+
+ double *geoPtz; // an array[3] for MultiPoint, SM with Z, i.e. depth
+ double *geoPtMulti; // an array[2] for MultiPoint, lat/lon to make bbox
+ // of decomposed points
+ PolyTessGeo *pPolyTessGeo;
+
+ LLBBox BBObj; // lat/lon BBox of the rendered object
+ double m_lat; // The lat/lon of the object's "reference" point
+ double m_lon;
+
+ Rules *CSrules; // per object conditional symbology
+ int bCS_Added;
+
+ S52_TextC *FText;
+ int bFText_Added;
+ wxRect rText;
+
+ int Scamin; // SCAMIN attribute decoded during load
+ int SuperScamin;
+ bool bIsClone;
+ int nRef; // Reference counter, to signal OK for deletion
+ bool bIsAton; // This object is an aid-to-navigation
+ bool bIsAssociable; // This object is DRGARE or DEPARE
+
+ int m_n_lsindex;
+ int *m_lsindex_array;
+ int m_n_edge_max_points;
+ line_segment_element *m_ls_list;
+ PI_line_segment_element *m_ls_list_legacy;
+
+ DisCat m_DisplayCat;
+ int m_DPRI; // display priority, assigned from initial LUP
+ // May be adjusted by CS
+ bool m_bcategory_mutable; // CS procedure may move this object to a higher
+ // category. Used as a hint to rendering filter
+ // logic
+
+ // This transform converts from object geometry
+ // to SM coordinates.
+ double x_rate; // These auxiliary transform coefficients are
+ double y_rate; // to be used in GetPointPix() and friends
+ double x_origin; // on a per-object basis if necessary
+ double y_origin;
+
+ chart_context *m_chart_context; // per-chart constants, carried in each
+ // object for convenience
+ int auxParm0; // some per-object auxiliary parameters, used for OpenGL
+ int auxParm1;
+ int auxParm2;
+ int auxParm3;
+
+ bool bBBObj_valid;
+ bool bOK_to_Render;
+};
+
+typedef std::vector S57ObjVector;
+
+typedef struct _sm_parms {
+ double easting_vp_center;
+ double northing_vp_center;
+} sm_parms;
+
+WX_DEFINE_ARRAY_PTR(Rules *, ArrayOfRules);
+
+typedef struct _mps_container {
+ ArrayOfRules *cs_rules;
+} mps_container;
+
+// object rasterization rules
+typedef struct _ObjRazRules {
+ LUPrec *LUP;
+ S57Obj *obj;
+ sm_parms *sm_transform_parms;
+ struct _ObjRazRules *child; // child list, used only for MultiPoint Soundings
+ struct _ObjRazRules *next;
+ struct _mps_container *mps;
+} ObjRazRules;
+
+WX_DECLARE_LIST(ObjRazRules, ListOfObjRazRules);
+
+//----------------------------------------------------------------------------------
+// Used for s52 Fast Polygon Renderer
+//----------------------------------------------------------------------------------
+class render_canvas_parms {
+public:
+ render_canvas_parms(void);
+ ~render_canvas_parms(void);
+
+ unsigned char *pix_buff;
+ int lclip;
+ int rclip;
+ int pb_pitch;
+ int x;
+ int y;
+ int width;
+ int height;
+ int w_pot;
+ int h_pot;
+ int depth;
+ bool b_stagger;
+ int OGL_tex_name;
+ bool b_revrgb;
+};
+
+//----------------------------------------------------------------------------------
+// Classes used to create arrays of geometry elements
+//----------------------------------------------------------------------------------
+
+class VE_Element {
+public:
+ unsigned int index;
+ unsigned int nCount;
+ float *pPoints;
+ int max_priority;
+ size_t vbo_offset;
+ LLBBox edgeBBox;
+};
+
+class VC_Element {
+public:
+ unsigned int index;
+ float *pPoint;
+};
+
+typedef std::vector VE_ElementVector;
+typedef std::vector VC_ElementVector;
+
+typedef enum {
+ TYPE_CE = 0,
+ TYPE_CC,
+ TYPE_EC,
+ TYPE_EE,
+ TYPE_EE_REV
+} SegmentType;
+
+class connector_segment {
+public:
+ int vbo_offset;
+ int max_priority_cs;
+ float cs_lat_avg; // segment centroid
+ float cs_lon_avg;
+};
+
+class line_segment_element {
+public:
+ int priority;
+ union {
+ connector_segment *pcs;
+ VE_Element *pedge;
+ };
+ SegmentType ls_type;
+
+ line_segment_element *next;
+};
+
+#if 0 // TODO
+class line_segment_element_legacy
+{
+public:
+ size_t vbo_offset;
+ size_t n_points;
+ int priority;
+ float lat_max; // segment bounding box
+ float lat_min;
+ float lon_max;
+ float lon_min;
+ int type;
+ void *private0;
+
+ line_segment_element *next;
+};
+
+
+class connector_segment
+{
+public:
+ void *start;
+ void *end;
+ SegmentType type;
+ int vbo_offset;
+ int max_priority;
+};
+
+#endif
+
+class connector_key {
+public:
+ connector_key() { memset(k, 0, sizeof k); }
+
+ connector_key(SegmentType t, int a, int b) { set(t, a, b); }
+
+ void set(SegmentType t, int a, int b) {
+ memcpy(k, &a, sizeof a);
+ memcpy(&k[sizeof a], &b, sizeof b);
+ k[sizeof(a) + sizeof(b)] = (unsigned char)t;
+ }
+
+ unsigned long hash() const;
+
+ unsigned char k[sizeof(int) + sizeof(int) + sizeof(char)];
+};
+
+class connHash {
+public:
+ connHash() {}
+ unsigned long operator()(const connector_key &k) const { return k.hash(); }
+
+ connHash &operator=(const connHash &) { return *this; }
+};
+
+// comparison operator
+class connEqual {
+public:
+ connEqual() {}
+ bool operator()(const connector_key &a, const connector_key &b) const {
+ return memcmp(a.k, b.k, sizeof b.k) == 0;
+ }
+
+ connEqual &operator=(const connEqual &) { return *this; }
+};
+
+WX_DECLARE_HASH_MAP(connector_key, connector_segment *, connHash, connEqual,
+ connected_segment_hash);
+
+#endif
diff --git a/docs/外部参考/OpenCPN源码基线_f7211b4e/上游源码/libs/s52plib/src/s52shaders.cpp b/docs/外部参考/OpenCPN源码基线_f7211b4e/上游源码/libs/s52plib/src/s52shaders.cpp
new file mode 100644
index 0000000..cf7f3a0
--- /dev/null
+++ b/docs/外部参考/OpenCPN源码基线_f7211b4e/上游源码/libs/s52plib/src/s52shaders.cpp
@@ -0,0 +1,756 @@
+/***************************************************************************
+ *
+ * Project: OpenCPN
+ * Purpose: S52 Presentation Library
+ * Authors: David Register
+ * Jesper Weissglas
+ *
+ ***************************************************************************
+ * Copyright (C) 2010 by David S. Register *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
+ **************************************************************************/
+
+
+#if defined(USE_ANDROID_GLES2) || defined(ocpnUSE_GLSL)
+
+#include "s52shaders.h"
+
+#ifdef USE_ANDROID_GLES2
+const GLchar* S52_preamble =
+"\n";
+#else
+const GLchar* S52_preamble =
+"#version 120\n"
+"#define precision\n"
+"#define lowp\n"
+"#define mediump\n"
+"#define highp\n";
+#endif
+
+
+// Simple colored triangle shader
+
+static const GLchar *S52color_tri_vertex_shader_source =
+ "attribute vec2 position;\n"
+ "uniform mat4 MVMatrix;\n"
+ "uniform mat4 TransformMatrix;\n"
+ "uniform vec4 color;\n"
+ "varying vec4 fragColor;\n"
+ "void main() {\n"
+ " fragColor = color;\n"
+ " gl_Position = MVMatrix * TransformMatrix * vec4(position, 0.0, 1.0);\n"
+ "}\n";
+
+static const GLchar *S52color_tri_fragment_shader_source =
+ "precision highp float;\n"
+ "varying vec4 fragColor;\n"
+ "void main() {\n"
+ " gl_FragColor = fragColor;\n"
+ "}\n";
+
+// Simple 2D texture shader
+static const GLchar *S52texture_2D_vertex_shader_source =
+ "attribute vec2 position;\n"
+ "attribute vec2 aUV;\n"
+ "uniform mat4 MVMatrix;\n"
+ "uniform mat4 TransformMatrix;\n"
+ "varying vec2 varCoord;\n"
+ "void main() {\n"
+ " gl_Position = MVMatrix * TransformMatrix * vec4(position, 0.0, 1.0);\n"
+ " //varCoord = aUV.st;\n"
+ " varCoord = aUV;\n"
+ "}\n";
+
+static const GLchar *S52texture_2D_fragment_shader_source =
+ "precision highp float;\n"
+ "uniform sampler2D uTex;\n"
+ "varying vec2 varCoord;\n"
+ "void main() {\n"
+ " gl_FragColor = texture2D(uTex, varCoord);\n"
+ "}\n";
+
+// 2D texture shader with color modulation, used for colored text
+static const GLchar *S52texture_2D_ColorMod_vertex_shader_source =
+ "precision highp float;\n"
+ "attribute vec2 position;\n"
+ "attribute vec2 aUV;\n"
+ "uniform mat4 MVMatrix;\n"
+ "uniform mat4 TransformMatrix;\n"
+ "varying vec2 varCoord;\n"
+ "void main() {\n"
+ " gl_Position = MVMatrix * TransformMatrix * vec4(position, 0.0, 1.0);\n"
+ " //varCoord = aUV.st;\n"
+ " varCoord = aUV;\n"
+ "}\n";
+
+static const GLchar *S52texture_2D_ColorMod_fragment_shader_source =
+ "precision highp float;\n"
+ "uniform sampler2D uTex;\n"
+ "uniform vec4 color;\n"
+ "varying vec2 varCoord;\n"
+ "void main() {\n"
+ " vec4 col=texture2D(uTex, varCoord);\n"
+ " gl_FragColor = color;\n"
+ " gl_FragColor.a = col.a;\n"
+ "}\n";
+
+// Circle shader
+
+static const GLchar *S52circle_filled_vertex_shader_source =
+ "precision highp float;\n"
+ "attribute vec2 aPos;\n"
+ "uniform mat4 MVMatrix;\n"
+ "uniform mat4 TransformMatrix;\n"
+ "void main() {\n"
+ " gl_Position = MVMatrix * TransformMatrix * vec4(aPos, 0.0, 1.0);\n"
+ "}\n";
+
+static const GLchar *S52circle_filled_fragment_shader_source =
+ "precision highp float;\n"
+ "uniform float border_width;\n"
+ "uniform float circle_radius;\n"
+ "uniform vec4 circle_color;\n"
+ "uniform vec4 border_color;\n"
+ "uniform vec2 circle_center;\n"
+ "void main(){\n"
+ "float d = distance(gl_FragCoord.xy, circle_center);\n"
+ "if (d < (circle_radius - border_width)) { gl_FragColor = circle_color; }\n"
+ "else if (d < circle_radius) { gl_FragColor = border_color; }\n"
+ "else { gl_FragColor = vec4(0.0, 0.0, 0.0, 0.0); }\n"
+ "}\n";
+
+// Ring shader
+
+static const GLchar *S52ring_vertex_shader_source =
+ "precision highp float;\n"
+ "attribute vec2 aPos;\n"
+ "uniform mat4 MVMatrix;\n"
+ "uniform mat4 TransformMatrix;\n"
+ "void main() {\n"
+ " gl_Position = MVMatrix * TransformMatrix * vec4(aPos, 0.0, 1.0);\n"
+ "}\n";
+
+static const GLchar *S52ring_fragment_shader_source_old =
+ "precision highp float;\n"
+ "uniform float border_width;\n"
+ "uniform float circle_radius;\n"
+ "uniform float ring_width;\n"
+ "uniform vec4 circle_color;\n"
+ "uniform vec4 border_color;\n"
+ "uniform vec2 circle_center;\n"
+ "uniform float sector_1;\n"
+ "uniform float sector_2;\n"
+
+ "void main(){\n"
+ "const float PI = 3.14159265358979323846264;\n"
+ "bool bdraw = false;\n"
+
+ "float angle = atan(gl_FragCoord.y-circle_center.y, "
+ "gl_FragCoord.x-circle_center.x);\n"
+ "angle = PI/2.0 - angle;\n"
+ "if(angle < 0.0) angle += PI * 2.0;\n"
+
+ "if(sector_2 > PI * 2.0){\n"
+ " if((angle > sector_1) && (angle < (PI * 2.0) )){\n"
+ " bdraw = true;\n"
+ " }\n"
+ " if(angle < sector_2 - (PI * 2.0)){\n"
+ " bdraw = true;\n"
+ " }\n"
+ "} else {\n"
+ " if((angle > sector_1) && (angle < sector_2)){\n"
+ " bdraw = true;\n"
+ " }\n"
+ "}\n"
+
+ "if(bdraw){\n"
+ " float d = distance(gl_FragCoord.xy, circle_center);\n"
+ " if (d > circle_radius) {\n"
+ " discard;\n"
+ " } else if( d > (circle_radius - border_width)) {\n"
+ " gl_FragColor = border_color;\n"
+ " } else if( d > (circle_radius - border_width - ring_width)) {\n"
+ " gl_FragColor = circle_color;\n"
+ " } else if( d > (circle_radius - border_width - ring_width - "
+ "border_width)) {\n"
+ " gl_FragColor = border_color;\n"
+ " } else {\n"
+ " discard;\n"
+ " }\n"
+ "} else{\n"
+ " discard;\n"
+ "}\n"
+ "}\n";
+
+
+static const GLchar *S52ring_fragment_shader_source =
+ "precision highp float;\n"
+ "uniform float ring_width;\n"
+ "uniform float circle_radius;\n"
+ "uniform vec4 ring_color;\n"
+ "uniform vec2 circle_center;\n"
+ "uniform float sector_1;\n"
+ "uniform float sector_2;\n"
+
+ "void main(){\n"
+ "const float PI = 3.14159265358979323846264;\n"
+ "bool bdraw = false;\n"
+
+ "float angle = atan(gl_FragCoord.y-circle_center.y, "
+ "gl_FragCoord.x-circle_center.x);\n"
+ "angle = PI/2.0 - angle;\n"
+ "if(angle < 0.0) angle += PI * 2.0;\n"
+
+ "if(sector_2 > PI * 2.0){\n"
+ " if((angle > sector_1) && (angle < (PI * 2.0) )){\n"
+ " bdraw = true;\n"
+ " }\n"
+ " if(angle < sector_2 - (PI * 2.0)){\n"
+ " bdraw = true;\n"
+ " }\n"
+ "} else {\n"
+ " if((angle > sector_1) && (angle < sector_2)){\n"
+ " bdraw = true;\n"
+ " }\n"
+ "}\n"
+
+ "if(bdraw){\n"
+ " float d = distance(gl_FragCoord.xy, circle_center);\n"
+ " float delta = 0.9;\n"
+ " if (d > circle_radius + ring_width/2.) {\n"
+ " discard;\n"
+ " } else if( d > (circle_radius )) {\n"
+ " float alpha = smoothstep(circle_radius + ring_width/2. -delta, circle_radius + ring_width/2., d);\n"
+ " gl_FragColor = ring_color;\n"
+ " gl_FragColor.w = 1.-alpha;\n"
+ " }\n"
+ " else if( d > (circle_radius - ring_width/2. - delta)) {\n"
+ " gl_FragColor = ring_color;\n"
+ " float alpha = smoothstep(circle_radius - ring_width/2. - delta, circle_radius - ring_width/2., d);\n"
+ " gl_FragColor.w = alpha;\n"
+ " }\n"
+ " else {\n"
+ " discard;\n"
+ " }\n"
+ "} else{\n"
+ " discard;\n"
+ "}\n"
+ "}\n";
+
+// Dash/Dot line shader
+static const GLchar *S52Dash_vertex_shader_source =
+ "attribute vec2 position;\n"
+ "uniform mat4 MVMatrix;\n"
+ "uniform mat4 TransformMatrix;\n"
+ "void main() {\n"
+ " gl_Position = MVMatrix * TransformMatrix * vec4(position, 0.0, 1.0);\n"
+ "}\n";
+
+static const GLchar *S52Dash_fragment_shader_source =
+ "precision highp float;\n"
+ "uniform sampler2D uTex;\n"
+ "uniform vec2 startPos;\n"
+ "uniform float texWidth;\n"
+ "uniform float dashFactor;\n"
+ "uniform vec4 color;\n"
+ "void main() {\n"
+ " float d = distance(gl_FragCoord.xy, startPos);\n"
+ " float x = mod(d,texWidth) / texWidth;\n"
+ " if(x < dashFactor) gl_FragColor = color;\n"
+ " else gl_FragColor = vec4(0.0, 0.0, 0.0, 0.0);\n"
+ "}\n";
+
+// Texture shader for Area Pattern
+static const GLchar *S52AP_vertex_shader_source =
+ "attribute vec2 position;\n"
+ "attribute vec2 aUV;\n"
+ "uniform mat4 MVMatrix;\n"
+ "uniform mat4 TransformMatrix;\n"
+ "void main() {\n"
+ " gl_Position = MVMatrix * TransformMatrix * vec4(position, 0.0, 1.0);\n"
+ "}\n";
+
+#if 0
+static const GLchar* S52AP_fragment_shader_source =
+ "precision highp float;\n"
+ "uniform sampler2D uTex;\n"
+ "uniform float texWidth;\n"
+ "uniform float texHeight;\n"
+ "uniform float texPOTWidth;\n"
+ "uniform float texPOTHeight;\n"
+ "uniform float staggerFactor;\n"
+ "uniform vec4 color;\n"
+ "uniform float xOff;\n"
+ "uniform float yOff;\n"
+ "void main() {\n"
+ " float yp = floor((gl_FragCoord.y + yOff) / texHeight);\n"
+ " float fstagger = 0.0;\n"
+ " //if(mod(yp, 2.0) < 0.1) fstagger = staggerFactor;\n"
+ " float xStag = xOff + (fstagger * texWidth);\n"
+ " float x = mod((gl_FragCoord.x - xStag),texWidth) / texPOTWidth;\n"
+ " float y = mod((gl_FragCoord.y + yOff),texHeight) / texPOTHeight;\n"
+ " gl_FragColor = texture2D(uTex, vec2(x, (texHeight / texPOTHeight) - y));\n"
+ "}\n";
+#else
+
+static const GLchar *S52AP_fragment_shader_source =
+ "precision highp float;\n"
+ "uniform sampler2D uTex;\n"
+ "uniform float texWidth;\n"
+ "uniform float texHeight;\n"
+ "uniform float texPOTWidth;\n"
+ "uniform float texPOTHeight;\n"
+ "uniform float staggerFactor;\n"
+ "uniform vec4 color;\n"
+ "uniform float xOff;\n"
+ "uniform float yOff;\n"
+ "uniform float yOffM;\n"
+ "void main() {\n"
+ " float yp = floor((gl_FragCoord.y + yOffM ) / texPOTHeight);\n"
+ " float fstagger = 0.0;\n"
+ " if(mod(yp, 2.0) < 0.1) fstagger = 0.5;\n"
+ " float x = (gl_FragCoord.x - xOff) / texPOTWidth;\n"
+ " float y = (gl_FragCoord.y + yOff) / texPOTHeight;\n"
+ " gl_FragColor = texture2D(uTex, vec2(x + fstagger, -y));\n"
+ "}\n";
+#endif
+
+GLint S52color_tri_fragment_shader;
+GLint S52color_tri_vertex_shader;
+
+GLint S52texture_2D_fragment_shader;
+GLint S52texture_2D_vertex_shader;
+
+GLint S52texture_2D_ColorMod_fragment_shader;
+GLint S52texture_2D_ColorMod_vertex_shader;
+
+GLint S52circle_filled_vertex_shader;
+GLint S52circle_filled_fragment_shader;
+
+GLint S52ring_vertex_shader;
+GLint S52ring_fragment_shader;
+
+GLint S52Dash_vertex_shader;
+GLint S52Dash_fragment_shader;
+
+GLint S52AP_vertex_shader;
+GLint S52AP_fragment_shader;
+
+extern GLint S52color_tri_shader_program;
+extern GLint S52texture_2D_shader_program;
+extern GLint S52texture_2D_ColorMod_shader_program;
+extern GLint S52circle_filled_shader_program;
+extern GLint S52ring_shader_program;
+extern GLint S52Dash_shader_program;
+extern GLint S52AP_shader_program;
+
+S52_GLShaderProgram *pS52texture_2D_ColorMod_shader_program;
+
+
+bool shadersLoaded = false;
+
+bool loadS52Shaders() {
+ bool ret_val = true;
+ //GLint success;
+
+ enum Consts { INFOLOG_LEN = 512 };
+#if 0
+ GLchar infoLog[INFOLOG_LEN];
+#endif
+
+ // Are the shaders ready?
+ if(shadersLoaded)
+ return true;
+
+ // Simple colored triangle shader
+
+ if (!S52color_tri_vertex_shader) {
+ auto shaderProgram = S52_GLShaderProgram::Builder()
+ .addShaderFromSource(S52color_tri_vertex_shader_source, GL_VERTEX_SHADER)
+ .addShaderFromSource(S52color_tri_fragment_shader_source, GL_FRAGMENT_SHADER)
+ .linkProgram();
+
+ S52color_tri_shader_program = shaderProgram.programId();
+ }
+
+
+ if (!S52texture_2D_shader_program) {
+ auto shaderProgram = S52_GLShaderProgram::Builder()
+ .addShaderFromSource(S52texture_2D_vertex_shader_source, GL_VERTEX_SHADER)
+ .addShaderFromSource(S52texture_2D_fragment_shader_source, GL_FRAGMENT_SHADER)
+ .linkProgram();
+
+ S52texture_2D_shader_program = shaderProgram.programId();
+ }
+
+#if 0
+ // Simple 2D texture shader
+
+ if (!S52texture_2D_vertex_shader) {
+ /* Vertex shader */
+ S52texture_2D_vertex_shader = glCreateShader(GL_VERTEX_SHADER);
+ glShaderSource(S52texture_2D_vertex_shader, 1,
+ &S52texture_2D_vertex_shader_source, NULL);
+ glCompileShader(S52texture_2D_vertex_shader);
+ glGetShaderiv(S52texture_2D_vertex_shader, GL_COMPILE_STATUS, &success);
+ if (!success) {
+ glGetShaderInfoLog(S52texture_2D_vertex_shader, INFOLOG_LEN, NULL,
+ infoLog);
+ // printf("ERROR::SHADER::VERTEX::COMPILATION_FAILED\n%s\n",
+ // infoLog);
+ ret_val = false;
+ }
+ }
+
+ if (!S52texture_2D_fragment_shader) {
+ /* Fragment shader */
+ S52texture_2D_fragment_shader = glCreateShader(GL_FRAGMENT_SHADER);
+ glShaderSource(S52texture_2D_fragment_shader, 1,
+ &S52texture_2D_fragment_shader_source, NULL);
+ glCompileShader(S52texture_2D_fragment_shader);
+ glGetShaderiv(S52texture_2D_fragment_shader, GL_COMPILE_STATUS, &success);
+ if (!success) {
+ glGetShaderInfoLog(S52texture_2D_fragment_shader, INFOLOG_LEN, NULL,
+ infoLog);
+ // printf("ERROR::SHADER::FRAGMENT::COMPILATION_FAILED\n%s\n",
+ // infoLog);
+ ret_val = false;
+ }
+ }
+
+ if (!S52texture_2D_shader_program) {
+ /* Link shaders */
+ S52texture_2D_shader_program = glCreateProgram();
+ glAttachShader(S52texture_2D_shader_program, S52texture_2D_vertex_shader);
+ glAttachShader(S52texture_2D_shader_program, S52texture_2D_fragment_shader);
+ glLinkProgram(S52texture_2D_shader_program);
+ glGetProgramiv(S52texture_2D_shader_program, GL_LINK_STATUS, &success);
+ if (!success) {
+ glGetProgramInfoLog(S52texture_2D_shader_program, INFOLOG_LEN, NULL,
+ infoLog);
+ // printf("ERROR::SHADER::PROGRAM::LINKING_FAILED\n%s\n", infoLog);
+ ret_val = false;
+ }
+ }
+#endif
+
+ // 2D texture shader with color Modulate
+ if (!S52texture_2D_ColorMod_shader_program) {
+
+ auto shaderProgram = S52_GLShaderProgram::Builder()
+ .addShaderFromSource(S52texture_2D_ColorMod_vertex_shader_source, GL_VERTEX_SHADER)
+ .addShaderFromSource(S52texture_2D_ColorMod_fragment_shader_source, GL_FRAGMENT_SHADER)
+ .linkProgram();
+
+ S52texture_2D_ColorMod_shader_program = shaderProgram.programId();
+
+ }
+
+#if 0
+ if (!S52texture_2D_ColorMod_vertex_shader) {
+ /* Vertex shader */
+ S52texture_2D_ColorMod_vertex_shader = glCreateShader(GL_VERTEX_SHADER);
+ glShaderSource(S52texture_2D_ColorMod_vertex_shader, 1,
+ &S52texture_2D_ColorMod_vertex_shader_source, NULL);
+ glCompileShader(S52texture_2D_ColorMod_vertex_shader);
+ glGetShaderiv(S52texture_2D_ColorMod_vertex_shader, GL_COMPILE_STATUS,
+ &success);
+ if (!success) {
+ glGetShaderInfoLog(S52texture_2D_ColorMod_vertex_shader, INFOLOG_LEN,
+ NULL, infoLog);
+ // printf("ERROR::SHADER::VERTEX::COMPILATION_FAILED\n%s\n",
+ // infoLog);
+ ret_val = false;
+ }
+ }
+
+
+ if (!S52texture_2D_ColorMod_fragment_shader) {
+ /* Fragment shader */
+ S52texture_2D_ColorMod_fragment_shader = glCreateShader(GL_FRAGMENT_SHADER);
+ glShaderSource(S52texture_2D_ColorMod_fragment_shader, 1,
+ &S52texture_2D_ColorMod_fragment_shader_source, NULL);
+ glCompileShader(S52texture_2D_ColorMod_fragment_shader);
+ glGetShaderiv(S52texture_2D_ColorMod_fragment_shader, GL_COMPILE_STATUS,
+ &success);
+ if (!success) {
+ glGetShaderInfoLog(S52texture_2D_ColorMod_fragment_shader, INFOLOG_LEN,
+ NULL, infoLog);
+ // printf("ERROR::SHADER::FRAGMENT::COMPILATION_FAILED\n%s\n",
+ // infoLog);
+ ret_val = false;
+ }
+ }
+
+ if (!S52texture_2D_ColorMod_shader_program) {
+ /* Link shaders */
+ S52texture_2D_ColorMod_shader_program = glCreateProgram();
+ glAttachShader(S52texture_2D_ColorMod_shader_program,
+ S52texture_2D_ColorMod_vertex_shader);
+ glAttachShader(S52texture_2D_ColorMod_shader_program,
+ S52texture_2D_ColorMod_fragment_shader);
+ glLinkProgram(S52texture_2D_ColorMod_shader_program);
+ glGetProgramiv(S52texture_2D_ColorMod_shader_program, GL_LINK_STATUS,
+ &success);
+ if (!success) {
+ glGetProgramInfoLog(S52texture_2D_ColorMod_shader_program, INFOLOG_LEN,
+ NULL, infoLog);
+ // printf("ERROR::SHADER::PROGRAM::LINKING_FAILED\n%s\n", infoLog);
+ ret_val = false;
+ }
+ }
+#endif
+
+ // Circle shader
+ if (!S52circle_filled_shader_program) {
+ auto shaderProgram = S52_GLShaderProgram::Builder()
+ .addShaderFromSource(S52circle_filled_vertex_shader_source, GL_VERTEX_SHADER)
+ .addShaderFromSource(S52circle_filled_fragment_shader_source, GL_FRAGMENT_SHADER)
+ .linkProgram();
+
+ S52circle_filled_shader_program = shaderProgram.programId();
+ }
+
+#if 0
+ if (!S52circle_filled_vertex_shader) {
+ /* Vertex shader */
+ S52circle_filled_vertex_shader = glCreateShader(GL_VERTEX_SHADER);
+ glShaderSource(S52circle_filled_vertex_shader, 1,
+ &S52circle_filled_vertex_shader_source, NULL);
+ glCompileShader(S52circle_filled_vertex_shader);
+ glGetShaderiv(S52circle_filled_vertex_shader, GL_COMPILE_STATUS, &success);
+ if (!success) {
+ glGetShaderInfoLog(S52circle_filled_vertex_shader, INFOLOG_LEN, NULL,
+ infoLog);
+ // printf("ERROR::SHADER::VERTEX::COMPILATION_FAILED\n%s\n",
+ // infoLog);
+ ret_val = false;
+ }
+ }
+
+ if (!S52circle_filled_fragment_shader) {
+ /* Fragment shader */
+ S52circle_filled_fragment_shader = glCreateShader(GL_FRAGMENT_SHADER);
+ glShaderSource(S52circle_filled_fragment_shader, 1,
+ &S52circle_filled_fragment_shader_source, NULL);
+ glCompileShader(S52circle_filled_fragment_shader);
+ glGetShaderiv(S52circle_filled_fragment_shader, GL_COMPILE_STATUS,
+ &success);
+ if (!success) {
+ glGetShaderInfoLog(S52circle_filled_fragment_shader, INFOLOG_LEN, NULL,
+ infoLog);
+ // printf("ERROR::SHADER::FRAGMENT::COMPILATION_FAILED\n%s\n",
+ // infoLog);
+ ret_val = false;
+ }
+ }
+
+ if (!S52circle_filled_shader_program) {
+ /* Link shaders */
+ S52circle_filled_shader_program = glCreateProgram();
+ glAttachShader(S52circle_filled_shader_program,
+ S52circle_filled_vertex_shader);
+ glAttachShader(S52circle_filled_shader_program,
+ S52circle_filled_fragment_shader);
+ glLinkProgram(S52circle_filled_shader_program);
+ glGetProgramiv(S52circle_filled_shader_program, GL_LINK_STATUS, &success);
+ if (!success) {
+ glGetProgramInfoLog(S52circle_filled_shader_program, INFOLOG_LEN, NULL,
+ infoLog);
+ // printf("ERROR::SHADER::PROGRAM::LINKING_FAILED\n%s\n",
+ // infoLog);
+ ret_val = false;
+ }
+ }
+#endif
+
+ // Ring shader
+ if (!S52ring_shader_program) {
+ auto shaderProgram = S52_GLShaderProgram::Builder()
+ .addShaderFromSource(S52ring_vertex_shader_source, GL_VERTEX_SHADER)
+ .addShaderFromSource(S52ring_fragment_shader_source, GL_FRAGMENT_SHADER)
+ .linkProgram();
+
+ S52ring_shader_program = shaderProgram.programId();
+ }
+
+#if 0
+ if (!S52ring_vertex_shader) {
+ /* Vertex shader */
+ S52ring_vertex_shader = glCreateShader(GL_VERTEX_SHADER);
+ glShaderSource(S52ring_vertex_shader, 1, &S52ring_vertex_shader_source,
+ NULL);
+ glCompileShader(S52ring_vertex_shader);
+ glGetShaderiv(S52ring_vertex_shader, GL_COMPILE_STATUS, &success);
+ if (!success) {
+ glGetShaderInfoLog(S52ring_vertex_shader, INFOLOG_LEN, NULL, infoLog);
+ // printf("ERROR::SHADER::VERTEX::COMPILATION_FAILED\n%s\n",
+ // infoLog);
+ ret_val = false;
+ }
+ }
+
+ if (!S52ring_fragment_shader) {
+ /* Fragment shader */
+ S52ring_fragment_shader = glCreateShader(GL_FRAGMENT_SHADER);
+ glShaderSource(S52ring_fragment_shader, 1, &S52ring_fragment_shader_source,
+ NULL);
+ glCompileShader(S52ring_fragment_shader);
+ glGetShaderiv(S52ring_fragment_shader, GL_COMPILE_STATUS, &success);
+ if (!success) {
+ glGetShaderInfoLog(S52ring_fragment_shader, INFOLOG_LEN, NULL, infoLog);
+ // printf("ERROR::SHADER::FRAGMENT::COMPILATION_FAILED\n%s\n",
+ // infoLog);
+ ret_val = false;
+ }
+ }
+
+ if (!S52ring_shader_program) {
+ /* Link shaders */
+ S52ring_shader_program = glCreateProgram();
+ glAttachShader(S52ring_shader_program, S52ring_vertex_shader);
+ glAttachShader(S52ring_shader_program, S52ring_fragment_shader);
+ glLinkProgram(S52ring_shader_program);
+ glGetProgramiv(S52ring_shader_program, GL_LINK_STATUS, &success);
+ if (!success) {
+ glGetProgramInfoLog(S52ring_shader_program, INFOLOG_LEN, NULL, infoLog);
+ // printf("ERROR::SHADER::PROGRAM::LINKING_FAILED\n%s\n",
+ // infoLog);
+ ret_val = false;
+ }
+ }
+#endif
+
+ // Dash shader
+ if (!S52Dash_shader_program) {
+ auto shaderProgram = S52_GLShaderProgram::Builder()
+ .addShaderFromSource(S52Dash_vertex_shader_source, GL_VERTEX_SHADER)
+ .addShaderFromSource(S52Dash_fragment_shader_source, GL_FRAGMENT_SHADER)
+ .linkProgram();
+
+ S52Dash_shader_program = shaderProgram.programId();
+ }
+
+#if 0
+ if (!S52Dash_vertex_shader) {
+ /* Vertex shader */
+ S52Dash_vertex_shader = glCreateShader(GL_VERTEX_SHADER);
+ glShaderSource(S52Dash_vertex_shader, 1, &S52Dash_vertex_shader_source,
+ NULL);
+ glCompileShader(S52Dash_vertex_shader);
+ glGetShaderiv(S52Dash_vertex_shader, GL_COMPILE_STATUS, &success);
+ if (!success) {
+ glGetShaderInfoLog(S52ring_vertex_shader, INFOLOG_LEN, NULL, infoLog);
+ // printf("ERROR::SHADER::VERTEX::COMPILATION_FAILED\n%s\n",
+ // infoLog);
+ ret_val = false;
+ }
+ }
+
+ if (!S52Dash_fragment_shader) {
+ /* Fragment shader */
+ S52Dash_fragment_shader = glCreateShader(GL_FRAGMENT_SHADER);
+ glShaderSource(S52Dash_fragment_shader, 1, &S52Dash_fragment_shader_source,
+ NULL);
+ glCompileShader(S52Dash_fragment_shader);
+ glGetShaderiv(S52Dash_fragment_shader, GL_COMPILE_STATUS, &success);
+ if (!success) {
+ glGetShaderInfoLog(S52Dash_fragment_shader, INFOLOG_LEN, NULL, infoLog);
+ // printf("ERROR::SHADER::FRAGMENT::COMPILATION_FAILED\n%s\n",
+ // infoLog);
+ ret_val = false;
+ }
+ }
+
+ if (!S52Dash_shader_program) {
+ /* Link shaders */
+ S52Dash_shader_program = glCreateProgram();
+ glAttachShader(S52Dash_shader_program, S52Dash_vertex_shader);
+ glAttachShader(S52Dash_shader_program, S52Dash_fragment_shader);
+ glLinkProgram(S52Dash_shader_program);
+ glGetProgramiv(S52Dash_shader_program, GL_LINK_STATUS, &success);
+ if (!success) {
+ glGetProgramInfoLog(S52Dash_shader_program, INFOLOG_LEN, NULL, infoLog);
+ // printf("ERROR::SHADER::PROGRAM::LINKING_FAILED\n%s\n",
+ // infoLog);
+ ret_val = false;
+ }
+ }
+#endif
+
+ // AP shader
+ if (!S52AP_shader_program) {
+ auto shaderProgram = S52_GLShaderProgram::Builder()
+ .addShaderFromSource(S52AP_vertex_shader_source, GL_VERTEX_SHADER)
+ .addShaderFromSource(S52AP_fragment_shader_source, GL_FRAGMENT_SHADER)
+ .linkProgram();
+
+ S52AP_shader_program = shaderProgram.programId();
+ }
+
+#if 0
+ if (!S52AP_vertex_shader) {
+ /* Vertex shader */
+ S52AP_vertex_shader = glCreateShader(GL_VERTEX_SHADER);
+ glShaderSource(S52AP_vertex_shader, 1, &S52AP_vertex_shader_source, NULL);
+ glCompileShader(S52AP_vertex_shader);
+ glGetShaderiv(S52AP_vertex_shader, GL_COMPILE_STATUS, &success);
+ if (!success) {
+ glGetShaderInfoLog(S52ring_vertex_shader, INFOLOG_LEN, NULL, infoLog);
+ // printf("ERROR::SHADER::VERTEX::COMPILATION_FAILED\n%s\n",
+ // infoLog);
+ ret_val = false;
+ }
+ }
+
+ if (!S52AP_fragment_shader) {
+ /* Fragment shader */
+ S52AP_fragment_shader = glCreateShader(GL_FRAGMENT_SHADER);
+ glShaderSource(S52AP_fragment_shader, 1, &S52AP_fragment_shader_source,
+ NULL);
+ glCompileShader(S52AP_fragment_shader);
+ glGetShaderiv(S52AP_fragment_shader, GL_COMPILE_STATUS, &success);
+ if (!success) {
+ glGetShaderInfoLog(S52AP_fragment_shader, INFOLOG_LEN, NULL, infoLog);
+ // printf("ERROR::SHADER::FRAGMENT::COMPILATION_FAILED\n%s\n",
+ // infoLog);
+ ret_val = false;
+ }
+ }
+
+ if (!S52AP_shader_program) {
+ /* Link shaders */
+ S52AP_shader_program = glCreateProgram();
+ glAttachShader(S52AP_shader_program, S52AP_vertex_shader);
+ glAttachShader(S52AP_shader_program, S52AP_fragment_shader);
+ glLinkProgram(S52AP_shader_program);
+ glGetProgramiv(S52AP_shader_program, GL_LINK_STATUS, &success);
+ if (!success) {
+ glGetProgramInfoLog(S52AP_shader_program, INFOLOG_LEN, NULL, infoLog);
+ // printf("ERROR::SHADER::PROGRAM::LINKING_FAILED\n%s\n",
+ // infoLog);
+ ret_val = false;
+ }
+ }
+#endif
+ shadersLoaded = true;
+
+ return ret_val;
+}
+
+
+#endif
diff --git a/docs/外部参考/OpenCPN源码基线_f7211b4e/上游源码/libs/s52plib/src/s52shaders.h b/docs/外部参考/OpenCPN源码基线_f7211b4e/上游源码/libs/s52plib/src/s52shaders.h
new file mode 100755
index 0000000..aeecc74
--- /dev/null
+++ b/docs/外部参考/OpenCPN源码基线_f7211b4e/上游源码/libs/s52plib/src/s52shaders.h
@@ -0,0 +1,139 @@
+/***************************************************************************
+ *
+ * Project: OpenCPN
+ * Purpose: S52 Presentation Library
+ * Authors: David Register
+ * Jesper Weissglas
+ *
+ ***************************************************************************
+ * Copyright (C) 2010 by David S. Register *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
+ **************************************************************************/
+
+#ifndef __S52SHADERS_H__
+#define __S52SHADERS_H__
+
+#if defined(USE_ANDROID_GLES2) || defined(ocpnUSE_GLSL)
+#include
+#include
+
+#include "s52plibGL.h"
+
+extern const GLchar* S52_preamble;
+
+enum Consts { INFOLOG_LEN = 512 };
+
+class S52_GLShaderProgram
+{
+public:
+
+ class Builder {
+ public:
+ Builder() : linked_(false) {
+ programId_ = glCreateProgram();
+ }
+
+ Builder & addShaderFromSource(std::string const &shaderSource, GLenum shaderType) {
+ char const *shaderCStr = shaderSource.c_str();
+ GLuint shaderId = glCreateShader(shaderType);
+
+ GLchar const* files[] = { S52_preamble, shaderCStr };
+ GLint lengths[] = { (GLint)strlen(S52_preamble), (GLint)strlen(shaderCStr) };
+
+ glShaderSource(shaderId, 2, files, lengths);
+ //glShaderSource(shaderId, 1, &shaderCStr, nullptr);
+
+ glCompileShader(shaderId);
+ glGetShaderiv(shaderId, GL_COMPILE_STATUS, &success);
+ if (!success) {
+ glGetShaderInfoLog(shaderId, INFOLOG_LEN, NULL, infoLog);
+ printf("ERROR::SHADER::COMPILATION_FAILED\n%s\n", infoLog);
+ //ret_val = false;
+ }
+
+ glAttachShader(programId_, shaderId);
+ return *this;
+ }
+
+// void addShaderFromFile(std::string const &shaderFile, GLenum shaderType) {
+// std::ifstream fileName(shaderFile);
+// std::istreambuf_iterator fileBegin(fileName), fileEnd;
+// std::string fileContents(fileBegin, fileEnd);
+// return addShaderFromSource(fileContents, shaderType);
+// }
+
+ S52_GLShaderProgram linkProgram() {
+ glLinkProgram(programId_);
+ glGetProgramiv( programId_, GL_LINK_STATUS, &linkSuccess); //requesting the status
+ if (linkSuccess == GL_FALSE) {
+ glGetProgramInfoLog(programId_, INFOLOG_LEN, NULL, infoLog);
+ printf("ERROR::SHADER::LINK_FAILED\n%s\n", infoLog);
+ }
+
+ S52_GLShaderProgram theProgram(programId_);
+ //deleteAttachedShaders(programId_);
+ linked_ = true;
+ return theProgram;
+ }
+
+ ~Builder() {
+ if(!linked_) {
+ glDeleteProgram(programId_);
+ }
+ }
+ private:
+ GLuint programId_;
+ bool linked_;
+ GLint success;
+ GLint linkSuccess;
+ GLchar infoLog[INFOLOG_LEN];
+ };
+
+ S52_GLShaderProgram() : programId_(0) { }
+
+ S52_GLShaderProgram(S52_GLShaderProgram &&other) {
+ *this = std::move(other);
+ }
+
+ S52_GLShaderProgram &operator=(S52_GLShaderProgram &&other)
+ {
+ programId_ = other.programId_;
+ other.programId_ = 0;
+
+ if (other.programId_ != 0) {
+ glDeleteProgram(other.programId_);
+ }
+
+ return *this;
+ }
+
+ ~S52_GLShaderProgram() { }
+
+ GLuint programId() const { return programId_; }
+
+ S52_GLShaderProgram(S52_GLShaderProgram const &other) = delete;
+ S52_GLShaderProgram &operator=(S52_GLShaderProgram const &other) = delete;
+private:
+ S52_GLShaderProgram(GLuint programId) : programId_(programId) { }
+ GLuint programId_;
+};
+
+#endif
+
+
+
+#endif
diff --git a/docs/外部参考/OpenCPN源码基线_f7211b4e/上游源码/libs/s52plib/src/s52utils.cpp b/docs/外部参考/OpenCPN源码基线_f7211b4e/上游源码/libs/s52plib/src/s52utils.cpp
new file mode 100644
index 0000000..eba8e1a
--- /dev/null
+++ b/docs/外部参考/OpenCPN源码基线_f7211b4e/上游源码/libs/s52plib/src/s52utils.cpp
@@ -0,0 +1,189 @@
+/******************************************************************************
+ *
+ * Project: OpenCPN
+ * Purpose: S52 Utility Library
+ * Author: David Register, Sylvain Duclos
+ *
+ ***************************************************************************
+ * Copyright (C) 2010 by David S. Register *
+ * *
+ * Copyright (C) 2000-2004 Sylvain Duclos *
+ * sduclos@users.sourceforgue.net *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
+ ***************************************************************************
+ *
+ */
+
+
+#include "s52utils.h"
+//#include // FILE
+//#include // strncmp()
+
+//#include // exit()
+
+// configuration file
+#define CONF_NAME "./s52test.conf"
+
+#define TRUE 1
+#define FALSE 0
+
+//#define CPATH_MAX 255
+///////////////////////////////////////////////////////////////////
+//
+// GLOBAL MARINER PARAMETER (will move out)
+//
+// NOTE: value for Chart No 1 found in README
+//
+// Soundings ON
+// Text ON
+// Depth Shades 4
+// Safety Contour 10 m
+// Safety Depth 7 m
+// Shallow 5 m
+// Deep 30 m
+
+/* A) value for Chart No 1 */
+/*
+double SHOW_TEXT = TRUE; // view group 23
+double TWO_SHADES = FALSE; // flag indicating selection of two depth
+shades (on/off) [default ON] double SAFETY_CONTOUR = 10.0; // selected
+safety contour (meters) [IMO PS 3.6] double SAFETY_DEPTH = 7.0; //
+selected safety depth (meters) [IMO PS 3.7] double SHALLOW_CONTOUR = 5.0; //
+selected shallow water contour (meters) (optional) double DEEP_CONTOUR = 30.0;
+// selected deepwatercontour (meters) (optional)
+*/
+
+/* B) value for testing */
+/*
+//gboolean TWO_SHADES = TRUE; // flag indicating selection of two depth
+shades (on/off) [default ON] gboolean TWO_SHADES = FALSE; // flag
+indicating selection of two depth shades (on/off) [default ON] gboolean
+SHOW_TEXT = TRUE; // view group 23
+//double SAFETY_DEPTH = 30.0; // selected safety depth (meters) [IMO
+PS 3.7]
+//double SHALLOW_CONTOUR = 2.0; // selected shallow water contour
+(meters) (optional) double SAFETY_DEPTH = 15.0; // selected safety
+depth (meters) [IMO PS 3.7] double SHALLOW_CONTOUR = 5.0; // selected
+shallow water contour (meters) (optional)
+//double SAFETY_CONTOUR = 30.0; // selected safety contour (meters) [IMO
+PS 3.6]
+//double DEEP_CONTOUR = 30.0; // selected deepwatercontour (meters)
+(optional)
+//double SAFETY_CONTOUR = 5.0; // selected safety contour (meters) [IMO
+PS 3.6]
+//double DEEP_CONTOUR = 10.0; // selected deepwatercontour (meters)
+(optional) double SAFETY_CONTOUR = 10.0; // selected safety contour
+(meters) [IMO PS 3.6] double DEEP_CONTOUR = 15.0; // selected
+deepwatercontour (meters) (optional)
+*/
+
+/* param needed for certain conditional symbology */
+/*
+gboolean SHALLOW_PATTERN = FALSE; // flag indicating selection of shallow
+water highlight (on/off)(optional) [default OFF] gboolean SHIPS_OUTLINE =
+FALSE; // flag indicating selection of ship scale symbol (on/off) [IMO
+PS 8.4] double DISTANCE_TAGS = 0.0; // selected spacing of "distance to
+run" tags at a route (nm) double TIME_TAGS = 0.0; // selected
+spacing of time tags at the pasttrack (min) gboolean FULL_SECTORS = TRUE; //
+show full length light sector lines gboolean SYMBOLIZED_BND = TRUE; //
+symbolized area boundaries
+*/
+
+// WARNING: must be in sync with S52_MAR_param_t
+/*
+static char *_MARparamNm[] = {
+ "S52_MAR_NONE", //= 0, // default
+ "S52_MAR_SHOW_TEXT", //= 1, // view group 23
+ "S52_MAR_TWO_SHADES", //= 2, // flag indicating selection of two
+depth shades (on/off) [default ON] "S52_MAR_SAFETY_CONTOUR", //= 3, //
+selected safety contour (meters) [IMO PS 3.6] "S52_MAR_SAFETY_DEPTH", //= 4,
+// selected safety depth (meters) [IMO PS 3.7] "S52_MAR_SHALLOW_CONTOUR", //=
+5, // selected shallow water contour (meters) (optional)
+ "S52_MAR_DEEP_CONTOUR", //= 6, // selected deepwatercontour (meters)
+(optional) "S52_MAR_SHALLOW_PATTERN", //= 7, // flag indicating selection of
+shallow water highlight (on/off)(optional) [default OFF]
+ "S52_MAR_SHIPS_OUTLINE", //= 8, // flag indicating selection of ship
+scale symbol (on/off) [IMO PS 8.4] "S52_MAR_DISTANCE_TAGS", //= 9, //
+selected spacing of "distance to run" tags at a route (nm) "S52_MAR_TIME_TAGS",
+//= 10, // selected spacing of time tags at the pasttrack (min)
+ "S52_MAR_FULL_SECTORS", //= 11, // show full length light sector lines
+ "S52_MAR_SYMBOLIZED_BND", //= 12, // symbolized area boundaries
+ "S52_MAR_SYMPLIFIED_PNT", //= 13, // simplified point
+ "S52_MAR_DISP_CATEGORY", //= 14, // display category
+ "S52_MAR_COLOR_PALETTE", //= 15, // color palette
+
+ "S52_MAR_NUM" //= 16 // number of parameters
+};
+*/
+
+// WARNING: must be in sync with _MARparamNm
+static double _MARparamVal[] = {
+ 0.0, // NONE
+ TRUE, // SHOW_TEXT
+ FALSE, // TWO_SHADES
+
+ 3.0, // SAFETY_CONTOUR
+ // 0.0, // SAFETY_CONTOUR --to test DEPCNT02 selection (GL) in
+ // CA49995A.000 0.5, // SAFETY_CONTOUR --to test DEPCNT02 selection (GL)
+ // in CA49995A.000
+
+ 3.0, // SAFETY_DEPTH
+ // 5.0, // SAFETY_DEPTH
+
+ 2.0, // SHALLOW_CONTOUR
+ 6.0, // DEEP_CONTOUR
+
+ // FALSE, // SHALLOW_PATTERN
+ TRUE, // SHALLOW_PATTERN
+
+ FALSE, // SHIPS_OUTLINE
+ 0.0, // DISTANCE_TAGS
+ 0.0, // TIME_TAGS
+ TRUE, // FULL_SECTORS
+ FALSE, // SYMBOLIZED_BND
+
+ FALSE, // SYMPLIFIED_PNT
+
+ // 'D', // S52_MAR_DISP_CATEGORY --DISPLAYBASE
+ // 'S', // S52_MAR_DISP_CATEGORY --STANDARD
+ 'O', // S52_MAR_DISP_CATEGORY --OTHER
+
+ // 0, // S52_MAR_COLOR_PALETTE --DAY_BRIGHT
+ 1, // S52_MAR_COLOR_PALETTE --DAY_BLACKBACK
+ // 2, // S52_MAR_COLOR_PALETTE --DAY_WHITEBACK
+ // 3, // S52_MAR_COLOR_PALETTE --DUSK
+ // 4, // S52_MAR_COLOR_PALETTE --NIGHT
+
+ 16.0 // NUM
+};
+
+double S52_getMarinerParam(S52_MAR_param_t param)
+// return Mariner parameter or '0.0' if fail
+// FIXME: check mariner param against groups selection
+{
+ // DSR
+ return _MARparamVal[param];
+}
+
+int S52_setMarinerParam(S52_MAR_param_t param, double val) {
+ if (S52_MAR_NONE < param && param < S52_MAR_NUM)
+ _MARparamVal[param] = val;
+ else
+ return FALSE;
+
+ return TRUE;
+}
diff --git a/docs/外部参考/OpenCPN源码基线_f7211b4e/上游源码/libs/s52plib/src/s52utils.h b/docs/外部参考/OpenCPN源码基线_f7211b4e/上游源码/libs/s52plib/src/s52utils.h
new file mode 100644
index 0000000..421e3a5
--- /dev/null
+++ b/docs/外部参考/OpenCPN源码基线_f7211b4e/上游源码/libs/s52plib/src/s52utils.h
@@ -0,0 +1,91 @@
+// S52utils.h: utility
+//
+// Project: OpENCview
+
+/*
+ This file is part of the OpENCview project, a viewer of ENC
+ Copyright (C) 2000-2004 Sylvain Duclos sduclos@users.sourceforgue.net
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
+ USA.
+*/
+
+#ifndef __S52UTILS_H__
+#define __S52UTILS_H__
+
+#include "wx/wxprec.h"
+
+#ifndef WX_PRECOMP
+#include "wx/wx.h"
+#endif // precompiled headers
+
+inline int roundint (double x)
+{
+#ifdef __WXOSX__
+ return wxRound(x); //FS#1278
+#else
+ int tmp = static_cast(x);
+ tmp += (x - tmp >= .5) - (x - tmp <= -.5);
+ return tmp;
+#endif
+}
+
+#ifdef SOLARIS
+// well should be cc
+#define PRINTF printf(__FILE__ ":%i: : ", __LINE__), printf
+#else
+#define PRINTF printf(__FILE__ ":%i: %s(): ", __LINE__, __FUNCTION__), printf
+#endif
+
+// valid label in .conf file
+#define CONF_CATALOG "CATALOG"
+#define CONF_PLIB "PLIB"
+#define CONF_CHART "CHART"
+#define CONF_RGB "DUMMY_RGB_BRIGHT"
+
+// global parameter for mariners' selection
+typedef enum _S52_MAR_param_t {
+ S52_MAR_NONE = 0, // default
+ S52_MAR_SHOW_TEXT = 1, // view group 23
+ S52_MAR_TWO_SHADES =
+ 2, // flag indicating selection of two depth shades (on/off) [default ON]
+ S52_MAR_SAFETY_CONTOUR = 3, // selected safety contour (meters) [IMO PS 3.6]
+ S52_MAR_SAFETY_DEPTH = 4, // selected safety depth (meters) [IMO PS 3.7]
+ S52_MAR_SHALLOW_CONTOUR =
+ 5, // selected shallow water contour (meters) (optional)
+ S52_MAR_DEEP_CONTOUR = 6, // selected deepwatercontour (meters) (optional)
+ S52_MAR_SHALLOW_PATTERN = 7, // flag indicating selection of shallow water
+ // highlight (on/off)(optional) [default OFF]
+ S52_MAR_SHIPS_OUTLINE = 8, // flag indicating selection of ship scale symbol
+ // (on/off) [IMO PS 8.4]
+ S52_MAR_DISTANCE_TAGS =
+ 9, // selected spacing of "distance to run" tags at a route (nm)
+ S52_MAR_TIME_TAGS =
+ 10, // selected spacing of time tags at the pasttrack (min)
+ S52_MAR_FULL_SECTORS = 11, // show full length light sector lines
+ S52_MAR_SYMBOLIZED_BND = 12, // symbolized area boundaries
+
+ S52_MAR_SYMPLIFIED_PNT = 13, // simplified point
+
+ S52_MAR_DISP_CATEGORY = 14, // display category
+
+ S52_MAR_COLOR_PALETTE = 15, // color palette
+
+ S52_MAR_NUM = 16 // number of parameters
+} S52_MAR_param_t;
+
+extern double S52_getMarinerParam(S52_MAR_param_t param);
+extern int S52_setMarinerParam(S52_MAR_param_t param, double val);
+#endif
diff --git a/docs/外部参考/OpenCPN源码基线_f7211b4e/文件校验.sha256 b/docs/外部参考/OpenCPN源码基线_f7211b4e/文件校验.sha256
new file mode 100644
index 0000000..3f81cb9
--- /dev/null
+++ b/docs/外部参考/OpenCPN源码基线_f7211b4e/文件校验.sha256
@@ -0,0 +1,25 @@
+b09e4785c5ef8418993606c2ba875422f113f30ccee4deadaef3f53aa06318d9 /root/sourceserver/cm95/docs/外部参考/OpenCPN源码基线_f7211b4e/上游源码/gui/src/cm93.cpp
+e693b1dbbc40927c3a69a6cf3c16d295393ba378909d8fbae1216d79ab364a70 /root/sourceserver/cm95/docs/外部参考/OpenCPN源码基线_f7211b4e/上游源码/libs/s52plib/src/Cs52_shaders.cpp
+738f294ed682e5bd8a379f125a80bf07c431f8ee780bc2c2024880286f1c34e3 /root/sourceserver/cm95/docs/外部参考/OpenCPN源码基线_f7211b4e/上游源码/libs/s52plib/src/Cs52_shaders.h
+d5428f5265d1a82999b50ea6a05a054bf69230ddd2048bb2d9e0cee3a666da43 /root/sourceserver/cm95/docs/外部参考/OpenCPN源码基线_f7211b4e/上游源码/libs/s52plib/src/DepthFont.cpp
+c3fd65c8d5013d359230bde03173d0711e1652a079e3e5a4af0232a9bb949324 /root/sourceserver/cm95/docs/外部参考/OpenCPN源码基线_f7211b4e/上游源码/libs/s52plib/src/DepthFont.h
+9e8ec4c5b43b0499b5388e155f7de45f61a4f82edda156957552d7e488939ee5 /root/sourceserver/cm95/docs/外部参考/OpenCPN源码基线_f7211b4e/上游源码/libs/s52plib/src/TexFont.cpp
+617f486076ede172c50b129a65df5765d2314594e6355f77920ba55567d338d5 /root/sourceserver/cm95/docs/外部参考/OpenCPN源码基线_f7211b4e/上游源码/libs/s52plib/src/TexFont.h
+561d9fc303bca922a5b285184ba9e3b82f0ebac72e6c9f4bed99a6b7c51607fa /root/sourceserver/cm95/docs/外部参考/OpenCPN源码基线_f7211b4e/上游源码/libs/s52plib/src/chartsymbols.cpp
+75b1eb93beb0aaff3a2693c12dde6c597fa5cac5b04c1e2c06896fbb5fb8283e /root/sourceserver/cm95/docs/外部参考/OpenCPN源码基线_f7211b4e/上游源码/libs/s52plib/src/chartsymbols.h
+0b226162ee9dd15140db40f16495caf1bd999f3691b15b43cf6efd6306826446 /root/sourceserver/cm95/docs/外部参考/OpenCPN源码基线_f7211b4e/上游源码/libs/s52plib/src/color_types.h
+1594f55ee1c88752be8004ca0b43a254312cc1dda96d98e7ee74db8fdf4a7093 /root/sourceserver/cm95/docs/外部参考/OpenCPN源码基线_f7211b4e/上游源码/libs/s52plib/src/mygeom.cpp
+cc713bce1cceb54f44e95007c173b49f48336e8771072429950991cdcc1fac6b /root/sourceserver/cm95/docs/外部参考/OpenCPN源码基线_f7211b4e/上游源码/libs/s52plib/src/mygeom.h
+baee3344a78848fc589828ac0c8bab6c147937a60d18d3864ce72265b2fe2238 /root/sourceserver/cm95/docs/外部参考/OpenCPN源码基线_f7211b4e/上游源码/libs/s52plib/src/s52_plib_utils.cpp
+87f531bb7acc8d8a487f5237a85cebb663d86578e320f3ec566b1b42f18d55b4 /root/sourceserver/cm95/docs/外部参考/OpenCPN源码基线_f7211b4e/上游源码/libs/s52plib/src/s52_plib_utils.h
+d094d9345fa76acf601ca2c1a48da7d65f06f3086b8f810e6d1ab9f8a5014ea4 /root/sourceserver/cm95/docs/外部参考/OpenCPN源码基线_f7211b4e/上游源码/libs/s52plib/src/s52cnsy.cpp
+b376900cc16a5d2358feda178b9aa82c0853aa84fa61106b1590c49136f98970 /root/sourceserver/cm95/docs/外部参考/OpenCPN源码基线_f7211b4e/上游源码/libs/s52plib/src/s52plib.cpp
+2a8fe104e646f52459d98c9aa1a0a014dde79421de6df96987650d7577436a40 /root/sourceserver/cm95/docs/外部参考/OpenCPN源码基线_f7211b4e/上游源码/libs/s52plib/src/s52plib.h
+8f08fc6af340e1965b268f8f26c7ed7e7052e488a7cb5150016ebed99c74e98b /root/sourceserver/cm95/docs/外部参考/OpenCPN源码基线_f7211b4e/上游源码/libs/s52plib/src/s52plibGL.h
+96d561915b867f5764c7d26b9577a52d121591665a1209a9feca106502cfccfc /root/sourceserver/cm95/docs/外部参考/OpenCPN源码基线_f7211b4e/上游源码/libs/s52plib/src/s52s57.h
+5fea9dbbcd3ea21318792114ea436336f52dc19947acd2bac2af1d626816bbaf /root/sourceserver/cm95/docs/外部参考/OpenCPN源码基线_f7211b4e/上游源码/libs/s52plib/src/s52shaders.cpp
+72b23f6ad581612b4a7881c83747170f039405bec2766bf465ca89af37575f0b /root/sourceserver/cm95/docs/外部参考/OpenCPN源码基线_f7211b4e/上游源码/libs/s52plib/src/s52shaders.h
+cee04cc331ff70d94a88ec3061e6714f7ec7b8226727bd14d87be3c2c3c30ba6 /root/sourceserver/cm95/docs/外部参考/OpenCPN源码基线_f7211b4e/上游源码/libs/s52plib/src/s52utils.cpp
+4feeefb368bf1aafd1620c15fe54662b8d2f9651cecc4123648b18aecfd1f74d /root/sourceserver/cm95/docs/外部参考/OpenCPN源码基线_f7211b4e/上游源码/libs/s52plib/src/s52utils.h
+ead06c5e9b243275923928f60659a759507ab7ce56abebdefbcf6fcd22dba96c /root/sourceserver/cm95/docs/外部参考/OpenCPN源码基线_f7211b4e/许可证/COPYING.gplv2
+3972dc9744f6499f0f9b2dbf76696f2ae7ad8af9b23dde66d6af86c9dfb36986 /root/sourceserver/cm95/docs/外部参考/OpenCPN源码基线_f7211b4e/许可证/COPYING.gplv3
diff --git a/docs/外部参考/OpenCPN源码基线_f7211b4e/许可证/COPYING.gplv2 b/docs/外部参考/OpenCPN源码基线_f7211b4e/许可证/COPYING.gplv2
new file mode 100644
index 0000000..7d5393a
--- /dev/null
+++ b/docs/外部参考/OpenCPN源码基线_f7211b4e/许可证/COPYING.gplv2
@@ -0,0 +1,278 @@
+ GNU GENERAL PUBLIC LICENSE
+ Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+ Preamble
+
+ The licenses for most software are designed to take away your
+freedom to share and change it. By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users. This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it. (Some other Free Software Foundation software is covered by
+the GNU Lesser General Public License instead.) You can apply it to
+your programs, too.
+
+ When we speak of free software, we are referring to freedom, not
+price. Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+ To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+ For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have. You must make sure that they, too, receive or can get the
+source code. And you must show them these terms so they know their
+rights.
+
+ We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+ Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software. If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+ Finally, any free program is threatened constantly by software
+patents. We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary. To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+ The precise terms and conditions for copying, distribution and
+modification follow.
+
+ GNU GENERAL PUBLIC LICENSE
+ TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+ 0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License. The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language. (Hereinafter, translation is included without limitation in
+the term "modification".) Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope. The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+ 1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+ 2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+ a) You must cause the modified files to carry prominent notices
+ stating that you changed the files and the date of any change.
+
+ b) You must cause any work that you distribute or publish, that in
+ whole or in part contains or is derived from the Program or any
+ part thereof, to be licensed as a whole at no charge to all third
+ parties under the terms of this License.
+
+ c) If the modified program normally reads commands interactively
+ when run, you must cause it, when started running for such
+ interactive use in the most ordinary way, to print or display an
+ announcement including an appropriate copyright notice and a
+ notice that there is no warranty (or else, saying that you provide
+ a warranty) and that users may redistribute the program under
+ these conditions, and telling the user how to view a copy of this
+ License. (Exception: if the Program itself is interactive but
+ does not normally print such an announcement, your work based on
+ the Program is not required to print an announcement.)
+
+These requirements apply to the modified work as a whole. If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works. But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+ 3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+ a) Accompany it with the complete corresponding machine-readable
+ source code, which must be distributed under the terms of Sections
+ 1 and 2 above on a medium customarily used for software interchange; or,
+
+ b) Accompany it with a written offer, valid for at least three
+ years, to give any third party, for a charge no more than your
+ cost of physically performing source distribution, a complete
+ machine-readable copy of the corresponding source code, to be
+ distributed under the terms of Sections 1 and 2 above on a medium
+ customarily used for software interchange; or,
+
+ c) Accompany it with the information you received as to the offer
+ to distribute corresponding source code. (This alternative is
+ allowed only for noncommercial distribution and only if you
+ received the program in object code or executable form with such
+ an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it. For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable. However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+ 4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License. Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+ 5. You are not required to accept this License, since you have not
+signed it. However, nothing else grants you permission to modify or
+distribute the Program or its derivative works. These actions are
+prohibited by law if you do not accept this License. Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+ 6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions. You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+ 7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all. For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices. Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+ 8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded. In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+ 9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time. Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number. If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation. If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+ 10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission. For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this. Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+ NO WARRANTY
+
+ 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+ 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
diff --git a/docs/外部参考/OpenCPN源码基线_f7211b4e/许可证/COPYING.gplv3 b/docs/外部参考/OpenCPN源码基线_f7211b4e/许可证/COPYING.gplv3
new file mode 100644
index 0000000..f288702
--- /dev/null
+++ b/docs/外部参考/OpenCPN源码基线_f7211b4e/许可证/COPYING.gplv3
@@ -0,0 +1,674 @@
+ GNU GENERAL PUBLIC LICENSE
+ Version 3, 29 June 2007
+
+ Copyright (C) 2007 Free Software Foundation, Inc.
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+ Preamble
+
+ The GNU General Public License is a free, copyleft license for
+software and other kinds of works.
+
+ The licenses for most software and other practical works are designed
+to take away your freedom to share and change the works. By contrast,
+the GNU General Public License is intended to guarantee your freedom to
+share and change all versions of a program--to make sure it remains free
+software for all its users. We, the Free Software Foundation, use the
+GNU General Public License for most of our software; it applies also to
+any other work released this way by its authors. You can apply it to
+your programs, too.
+
+ When we speak of free software, we are referring to freedom, not
+price. Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+them if you wish), that you receive source code or can get it if you
+want it, that you can change the software or use pieces of it in new
+free programs, and that you know you can do these things.
+
+ To protect your rights, we need to prevent others from denying you
+these rights or asking you to surrender the rights. Therefore, you have
+certain responsibilities if you distribute copies of the software, or if
+you modify it: responsibilities to respect the freedom of others.
+
+ For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must pass on to the recipients the same
+freedoms that you received. You must make sure that they, too, receive
+or can get the source code. And you must show them these terms so they
+know their rights.
+
+ Developers that use the GNU GPL protect your rights with two steps:
+(1) assert copyright on the software, and (2) offer you this License
+giving you legal permission to copy, distribute and/or modify it.
+
+ For the developers' and authors' protection, the GPL clearly explains
+that there is no warranty for this free software. For both users' and
+authors' sake, the GPL requires that modified versions be marked as
+changed, so that their problems will not be attributed erroneously to
+authors of previous versions.
+
+ Some devices are designed to deny users access to install or run
+modified versions of the software inside them, although the manufacturer
+can do so. This is fundamentally incompatible with the aim of
+protecting users' freedom to change the software. The systematic
+pattern of such abuse occurs in the area of products for individuals to
+use, which is precisely where it is most unacceptable. Therefore, we
+have designed this version of the GPL to prohibit the practice for those
+products. If such problems arise substantially in other domains, we
+stand ready to extend this provision to those domains in future versions
+of the GPL, as needed to protect the freedom of users.
+
+ Finally, every program is threatened constantly by software patents.
+States should not allow patents to restrict development and use of
+software on general-purpose computers, but in those that do, we wish to
+avoid the special danger that patents applied to a free program could
+make it effectively proprietary. To prevent this, the GPL assures that
+patents cannot be used to render the program non-free.
+
+ The precise terms and conditions for copying, distribution and
+modification follow.
+
+ TERMS AND CONDITIONS
+
+ 0. Definitions.
+
+ "This License" refers to version 3 of the GNU General Public License.
+
+ "Copyright" also means copyright-like laws that apply to other kinds of
+works, such as semiconductor masks.
+
+ "The Program" refers to any copyrightable work licensed under this
+License. Each licensee is addressed as "you". "Licensees" and
+"recipients" may be individuals or organizations.
+
+ To "modify" a work means to copy from or adapt all or part of the work
+in a fashion requiring copyright permission, other than the making of an
+exact copy. The resulting work is called a "modified version" of the
+earlier work or a work "based on" the earlier work.
+
+ A "covered work" means either the unmodified Program or a work based
+on the Program.
+
+ To "propagate" a work means to do anything with it that, without
+permission, would make you directly or secondarily liable for
+infringement under applicable copyright law, except executing it on a
+computer or modifying a private copy. Propagation includes copying,
+distribution (with or without modification), making available to the
+public, and in some countries other activities as well.
+
+ To "convey" a work means any kind of propagation that enables other
+parties to make or receive copies. Mere interaction with a user through
+a computer network, with no transfer of a copy, is not conveying.
+
+ An interactive user interface displays "Appropriate Legal Notices"
+to the extent that it includes a convenient and prominently visible
+feature that (1) displays an appropriate copyright notice, and (2)
+tells the user that there is no warranty for the work (except to the
+extent that warranties are provided), that licensees may convey the
+work under this License, and how to view a copy of this License. If
+the interface presents a list of user commands or options, such as a
+menu, a prominent item in the list meets this criterion.
+
+ 1. Source Code.
+
+ The "source code" for a work means the preferred form of the work
+for making modifications to it. "Object code" means any non-source
+form of a work.
+
+ A "Standard Interface" means an interface that either is an official
+standard defined by a recognized standards body, or, in the case of
+interfaces specified for a particular programming language, one that
+is widely used among developers working in that language.
+
+ The "System Libraries" of an executable work include anything, other
+than the work as a whole, that (a) is included in the normal form of
+packaging a Major Component, but which is not part of that Major
+Component, and (b) serves only to enable use of the work with that
+Major Component, or to implement a Standard Interface for which an
+implementation is available to the public in source code form. A
+"Major Component", in this context, means a major essential component
+(kernel, window system, and so on) of the specific operating system
+(if any) on which the executable work runs, or a compiler used to
+produce the work, or an object code interpreter used to run it.
+
+ The "Corresponding Source" for a work in object code form means all
+the source code needed to generate, install, and (for an executable
+work) run the object code and to modify the work, including scripts to
+control those activities. However, it does not include the work's
+System Libraries, or general-purpose tools or generally available free
+programs which are used unmodified in performing those activities but
+which are not part of the work. For example, Corresponding Source
+includes interface definition files associated with source files for
+the work, and the source code for shared libraries and dynamically
+linked subprograms that the work is specifically designed to require,
+such as by intimate data communication or control flow between those
+subprograms and other parts of the work.
+
+ The Corresponding Source need not include anything that users
+can regenerate automatically from other parts of the Corresponding
+Source.
+
+ The Corresponding Source for a work in source code form is that
+same work.
+
+ 2. Basic Permissions.
+
+ All rights granted under this License are granted for the term of
+copyright on the Program, and are irrevocable provided the stated
+conditions are met. This License explicitly affirms your unlimited
+permission to run the unmodified Program. The output from running a
+covered work is covered by this License only if the output, given its
+content, constitutes a covered work. This License acknowledges your
+rights of fair use or other equivalent, as provided by copyright law.
+
+ You may make, run and propagate covered works that you do not
+convey, without conditions so long as your license otherwise remains
+in force. You may convey covered works to others for the sole purpose
+of having them make modifications exclusively for you, or provide you
+with facilities for running those works, provided that you comply with
+the terms of this License in conveying all material for which you do
+not control copyright. Those thus making or running the covered works
+for you must do so exclusively on your behalf, under your direction
+and control, on terms that prohibit them from making any copies of
+your copyrighted material outside their relationship with you.
+
+ Conveying under any other circumstances is permitted solely under
+the conditions stated below. Sublicensing is not allowed; section 10
+makes it unnecessary.
+
+ 3. Protecting Users' Legal Rights From Anti-Circumvention Law.
+
+ No covered work shall be deemed part of an effective technological
+measure under any applicable law fulfilling obligations under article
+11 of the WIPO copyright treaty adopted on 20 December 1996, or
+similar laws prohibiting or restricting circumvention of such
+measures.
+
+ When you convey a covered work, you waive any legal power to forbid
+circumvention of technological measures to the extent such circumvention
+is effected by exercising rights under this License with respect to
+the covered work, and you disclaim any intention to limit operation or
+modification of the work as a means of enforcing, against the work's
+users, your or third parties' legal rights to forbid circumvention of
+technological measures.
+
+ 4. Conveying Verbatim Copies.
+
+ You may convey verbatim copies of the Program's source code as you
+receive it, in any medium, provided that you conspicuously and
+appropriately publish on each copy an appropriate copyright notice;
+keep intact all notices stating that this License and any
+non-permissive terms added in accord with section 7 apply to the code;
+keep intact all notices of the absence of any warranty; and give all
+recipients a copy of this License along with the Program.
+
+ You may charge any price or no price for each copy that you convey,
+and you may offer support or warranty protection for a fee.
+
+ 5. Conveying Modified Source Versions.
+
+ You may convey a work based on the Program, or the modifications to
+produce it from the Program, in the form of source code under the
+terms of section 4, provided that you also meet all of these conditions:
+
+ a) The work must carry prominent notices stating that you modified
+ it, and giving a relevant date.
+
+ b) The work must carry prominent notices stating that it is
+ released under this License and any conditions added under section
+ 7. This requirement modifies the requirement in section 4 to
+ "keep intact all notices".
+
+ c) You must license the entire work, as a whole, under this
+ License to anyone who comes into possession of a copy. This
+ License will therefore apply, along with any applicable section 7
+ additional terms, to the whole of the work, and all its parts,
+ regardless of how they are packaged. This License gives no
+ permission to license the work in any other way, but it does not
+ invalidate such permission if you have separately received it.
+
+ d) If the work has interactive user interfaces, each must display
+ Appropriate Legal Notices; however, if the Program has interactive
+ interfaces that do not display Appropriate Legal Notices, your
+ work need not make them do so.
+
+ A compilation of a covered work with other separate and independent
+works, which are not by their nature extensions of the covered work,
+and which are not combined with it such as to form a larger program,
+in or on a volume of a storage or distribution medium, is called an
+"aggregate" if the compilation and its resulting copyright are not
+used to limit the access or legal rights of the compilation's users
+beyond what the individual works permit. Inclusion of a covered work
+in an aggregate does not cause this License to apply to the other
+parts of the aggregate.
+
+ 6. Conveying Non-Source Forms.
+
+ You may convey a covered work in object code form under the terms
+of sections 4 and 5, provided that you also convey the
+machine-readable Corresponding Source under the terms of this License,
+in one of these ways:
+
+ a) Convey the object code in, or embodied in, a physical product
+ (including a physical distribution medium), accompanied by the
+ Corresponding Source fixed on a durable physical medium
+ customarily used for software interchange.
+
+ b) Convey the object code in, or embodied in, a physical product
+ (including a physical distribution medium), accompanied by a
+ written offer, valid for at least three years and valid for as
+ long as you offer spare parts or customer support for that product
+ model, to give anyone who possesses the object code either (1) a
+ copy of the Corresponding Source for all the software in the
+ product that is covered by this License, on a durable physical
+ medium customarily used for software interchange, for a price no
+ more than your reasonable cost of physically performing this
+ conveying of source, or (2) access to copy the
+ Corresponding Source from a network server at no charge.
+
+ c) Convey individual copies of the object code with a copy of the
+ written offer to provide the Corresponding Source. This
+ alternative is allowed only occasionally and noncommercially, and
+ only if you received the object code with such an offer, in accord
+ with subsection 6b.
+
+ d) Convey the object code by offering access from a designated
+ place (gratis or for a charge), and offer equivalent access to the
+ Corresponding Source in the same way through the same place at no
+ further charge. You need not require recipients to copy the
+ Corresponding Source along with the object code. If the place to
+ copy the object code is a network server, the Corresponding Source
+ may be on a different server (operated by you or a third party)
+ that supports equivalent copying facilities, provided you maintain
+ clear directions next to the object code saying where to find the
+ Corresponding Source. Regardless of what server hosts the
+ Corresponding Source, you remain obligated to ensure that it is
+ available for as long as needed to satisfy these requirements.
+
+ e) Convey the object code using peer-to-peer transmission, provided
+ you inform other peers where the object code and Corresponding
+ Source of the work are being offered to the general public at no
+ charge under subsection 6d.
+
+ A separable portion of the object code, whose source code is excluded
+from the Corresponding Source as a System Library, need not be
+included in conveying the object code work.
+
+ A "User Product" is either (1) a "consumer product", which means any
+tangible personal property which is normally used for personal, family,
+or household purposes, or (2) anything designed or sold for incorporation
+into a dwelling. In determining whether a product is a consumer product,
+doubtful cases shall be resolved in favor of coverage. For a particular
+product received by a particular user, "normally used" refers to a
+typical or common use of that class of product, regardless of the status
+of the particular user or of the way in which the particular user
+actually uses, or expects or is expected to use, the product. A product
+is a consumer product regardless of whether the product has substantial
+commercial, industrial or non-consumer uses, unless such uses represent
+the only significant mode of use of the product.
+
+ "Installation Information" for a User Product means any methods,
+procedures, authorization keys, or other information required to install
+and execute modified versions of a covered work in that User Product from
+a modified version of its Corresponding Source. The information must
+suffice to ensure that the continued functioning of the modified object
+code is in no case prevented or interfered with solely because
+modification has been made.
+
+ If you convey an object code work under this section in, or with, or
+specifically for use in, a User Product, and the conveying occurs as
+part of a transaction in which the right of possession and use of the
+User Product is transferred to the recipient in perpetuity or for a
+fixed term (regardless of how the transaction is characterized), the
+Corresponding Source conveyed under this section must be accompanied
+by the Installation Information. But this requirement does not apply
+if neither you nor any third party retains the ability to install
+modified object code on the User Product (for example, the work has
+been installed in ROM).
+
+ The requirement to provide Installation Information does not include a
+requirement to continue to provide support service, warranty, or updates
+for a work that has been modified or installed by the recipient, or for
+the User Product in which it has been modified or installed. Access to a
+network may be denied when the modification itself materially and
+adversely affects the operation of the network or violates the rules and
+protocols for communication across the network.
+
+ Corresponding Source conveyed, and Installation Information provided,
+in accord with this section must be in a format that is publicly
+documented (and with an implementation available to the public in
+source code form), and must require no special password or key for
+unpacking, reading or copying.
+
+ 7. Additional Terms.
+
+ "Additional permissions" are terms that supplement the terms of this
+License by making exceptions from one or more of its conditions.
+Additional permissions that are applicable to the entire Program shall
+be treated as though they were included in this License, to the extent
+that they are valid under applicable law. If additional permissions
+apply only to part of the Program, that part may be used separately
+under those permissions, but the entire Program remains governed by
+this License without regard to the additional permissions.
+
+ When you convey a copy of a covered work, you may at your option
+remove any additional permissions from that copy, or from any part of
+it. (Additional permissions may be written to require their own
+removal in certain cases when you modify the work.) You may place
+additional permissions on material, added by you to a covered work,
+for which you have or can give appropriate copyright permission.
+
+ Notwithstanding any other provision of this License, for material you
+add to a covered work, you may (if authorized by the copyright holders of
+that material) supplement the terms of this License with terms:
+
+ a) Disclaiming warranty or limiting liability differently from the
+ terms of sections 15 and 16 of this License; or
+
+ b) Requiring preservation of specified reasonable legal notices or
+ author attributions in that material or in the Appropriate Legal
+ Notices displayed by works containing it; or
+
+ c) Prohibiting misrepresentation of the origin of that material, or
+ requiring that modified versions of such material be marked in
+ reasonable ways as different from the original version; or
+
+ d) Limiting the use for publicity purposes of names of licensors or
+ authors of the material; or
+
+ e) Declining to grant rights under trademark law for use of some
+ trade names, trademarks, or service marks; or
+
+ f) Requiring indemnification of licensors and authors of that
+ material by anyone who conveys the material (or modified versions of
+ it) with contractual assumptions of liability to the recipient, for
+ any liability that these contractual assumptions directly impose on
+ those licensors and authors.
+
+ All other non-permissive additional terms are considered "further
+restrictions" within the meaning of section 10. If the Program as you
+received it, or any part of it, contains a notice stating that it is
+governed by this License along with a term that is a further
+restriction, you may remove that term. If a license document contains
+a further restriction but permits relicensing or conveying under this
+License, you may add to a covered work material governed by the terms
+of that license document, provided that the further restriction does
+not survive such relicensing or conveying.
+
+ If you add terms to a covered work in accord with this section, you
+must place, in the relevant source files, a statement of the
+additional terms that apply to those files, or a notice indicating
+where to find the applicable terms.
+
+ Additional terms, permissive or non-permissive, may be stated in the
+form of a separately written license, or stated as exceptions;
+the above requirements apply either way.
+
+ 8. Termination.
+
+ You may not propagate or modify a covered work except as expressly
+provided under this License. Any attempt otherwise to propagate or
+modify it is void, and will automatically terminate your rights under
+this License (including any patent licenses granted under the third
+paragraph of section 11).
+
+ However, if you cease all violation of this License, then your
+license from a particular copyright holder is reinstated (a)
+provisionally, unless and until the copyright holder explicitly and
+finally terminates your license, and (b) permanently, if the copyright
+holder fails to notify you of the violation by some reasonable means
+prior to 60 days after the cessation.
+
+ Moreover, your license from a particular copyright holder is
+reinstated permanently if the copyright holder notifies you of the
+violation by some reasonable means, this is the first time you have
+received notice of violation of this License (for any work) from that
+copyright holder, and you cure the violation prior to 30 days after
+your receipt of the notice.
+
+ Termination of your rights under this section does not terminate the
+licenses of parties who have received copies or rights from you under
+this License. If your rights have been terminated and not permanently
+reinstated, you do not qualify to receive new licenses for the same
+material under section 10.
+
+ 9. Acceptance Not Required for Having Copies.
+
+ You are not required to accept this License in order to receive or
+run a copy of the Program. Ancillary propagation of a covered work
+occurring solely as a consequence of using peer-to-peer transmission
+to receive a copy likewise does not require acceptance. However,
+nothing other than this License grants you permission to propagate or
+modify any covered work. These actions infringe copyright if you do
+not accept this License. Therefore, by modifying or propagating a
+covered work, you indicate your acceptance of this License to do so.
+
+ 10. Automatic Licensing of Downstream Recipients.
+
+ Each time you convey a covered work, the recipient automatically
+receives a license from the original licensors, to run, modify and
+propagate that work, subject to this License. You are not responsible
+for enforcing compliance by third parties with this License.
+
+ An "entity transaction" is a transaction transferring control of an
+organization, or substantially all assets of one, or subdividing an
+organization, or merging organizations. If propagation of a covered
+work results from an entity transaction, each party to that
+transaction who receives a copy of the work also receives whatever
+licenses to the work the party's predecessor in interest had or could
+give under the previous paragraph, plus a right to possession of the
+Corresponding Source of the work from the predecessor in interest, if
+the predecessor has it or can get it with reasonable efforts.
+
+ You may not impose any further restrictions on the exercise of the
+rights granted or affirmed under this License. For example, you may
+not impose a license fee, royalty, or other charge for exercise of
+rights granted under this License, and you may not initiate litigation
+(including a cross-claim or counterclaim in a lawsuit) alleging that
+any patent claim is infringed by making, using, selling, offering for
+sale, or importing the Program or any portion of it.
+
+ 11. Patents.
+
+ A "contributor" is a copyright holder who authorizes use under this
+License of the Program or a work on which the Program is based. The
+work thus licensed is called the contributor's "contributor version".
+
+ A contributor's "essential patent claims" are all patent claims
+owned or controlled by the contributor, whether already acquired or
+hereafter acquired, that would be infringed by some manner, permitted
+by this License, of making, using, or selling its contributor version,
+but do not include claims that would be infringed only as a
+consequence of further modification of the contributor version. For
+purposes of this definition, "control" includes the right to grant
+patent sublicenses in a manner consistent with the requirements of
+this License.
+
+ Each contributor grants you a non-exclusive, worldwide, royalty-free
+patent license under the contributor's essential patent claims, to
+make, use, sell, offer for sale, import and otherwise run, modify and
+propagate the contents of its contributor version.
+
+ In the following three paragraphs, a "patent license" is any express
+agreement or commitment, however denominated, not to enforce a patent
+(such as an express permission to practice a patent or covenant not to
+sue for patent infringement). To "grant" such a patent license to a
+party means to make such an agreement or commitment not to enforce a
+patent against the party.
+
+ If you convey a covered work, knowingly relying on a patent license,
+and the Corresponding Source of the work is not available for anyone
+to copy, free of charge and under the terms of this License, through a
+publicly available network server or other readily accessible means,
+then you must either (1) cause the Corresponding Source to be so
+available, or (2) arrange to deprive yourself of the benefit of the
+patent license for this particular work, or (3) arrange, in a manner
+consistent with the requirements of this License, to extend the patent
+license to downstream recipients. "Knowingly relying" means you have
+actual knowledge that, but for the patent license, your conveying the
+covered work in a country, or your recipient's use of the covered work
+in a country, would infringe one or more identifiable patents in that
+country that you have reason to believe are valid.
+
+ If, pursuant to or in connection with a single transaction or
+arrangement, you convey, or propagate by procuring conveyance of, a
+covered work, and grant a patent license to some of the parties
+receiving the covered work authorizing them to use, propagate, modify
+or convey a specific copy of the covered work, then the patent license
+you grant is automatically extended to all recipients of the covered
+work and works based on it.
+
+ A patent license is "discriminatory" if it does not include within
+the scope of its coverage, prohibits the exercise of, or is
+conditioned on the non-exercise of one or more of the rights that are
+specifically granted under this License. You may not convey a covered
+work if you are a party to an arrangement with a third party that is
+in the business of distributing software, under which you make payment
+to the third party based on the extent of your activity of conveying
+the work, and under which the third party grants, to any of the
+parties who would receive the covered work from you, a discriminatory
+patent license (a) in connection with copies of the covered work
+conveyed by you (or copies made from those copies), or (b) primarily
+for and in connection with specific products or compilations that
+contain the covered work, unless you entered into that arrangement,
+or that patent license was granted, prior to 28 March 2007.
+
+ Nothing in this License shall be construed as excluding or limiting
+any implied license or other defenses to infringement that may
+otherwise be available to you under applicable patent law.
+
+ 12. No Surrender of Others' Freedom.
+
+ If conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot convey a
+covered work so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you may
+not convey it at all. For example, if you agree to terms that obligate you
+to collect a royalty for further conveying from those to whom you convey
+the Program, the only way you could satisfy both those terms and this
+License would be to refrain entirely from conveying the Program.
+
+ 13. Use with the GNU Affero General Public License.
+
+ Notwithstanding any other provision of this License, you have
+permission to link or combine any covered work with a work licensed
+under version 3 of the GNU Affero General Public License into a single
+combined work, and to convey the resulting work. The terms of this
+License will continue to apply to the part which is the covered work,
+but the special requirements of the GNU Affero General Public License,
+section 13, concerning interaction through a network will apply to the
+combination as such.
+
+ 14. Revised Versions of this License.
+
+ The Free Software Foundation may publish revised and/or new versions of
+the GNU General Public License from time to time. Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+ Each version is given a distinguishing version number. If the
+Program specifies that a certain numbered version of the GNU General
+Public License "or any later version" applies to it, you have the
+option of following the terms and conditions either of that numbered
+version or of any later version published by the Free Software
+Foundation. If the Program does not specify a version number of the
+GNU General Public License, you may choose any version ever published
+by the Free Software Foundation.
+
+ If the Program specifies that a proxy can decide which future
+versions of the GNU General Public License can be used, that proxy's
+public statement of acceptance of a version permanently authorizes you
+to choose that version for the Program.
+
+ Later license versions may give you additional or different
+permissions. However, no additional obligations are imposed on any
+author or copyright holder as a result of your choosing to follow a
+later version.
+
+ 15. Disclaimer of Warranty.
+
+ THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
+APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
+HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
+OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
+THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
+IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
+ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
+
+ 16. Limitation of Liability.
+
+ IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
+THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
+GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
+USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
+DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
+PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
+EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
+SUCH DAMAGES.
+
+ 17. Interpretation of Sections 15 and 16.
+
+ If the disclaimer of warranty and limitation of liability provided
+above cannot be given local legal effect according to their terms,
+reviewing courts shall apply local law that most closely approximates
+an absolute waiver of all civil liability in connection with the
+Program, unless a warranty or assumption of liability accompanies a
+copy of the Program in return for a fee.
+
+ END OF TERMS AND CONDITIONS
+
+ How to Apply These Terms to Your New Programs
+
+ If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+ To do so, attach the following notices to the program. It is safest
+to attach them to the start of each source file to most effectively
+state the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+
+ Copyright (C)
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see .
+
+Also add information on how to contact you by electronic and paper mail.
+
+ If the program does terminal interaction, make it output a short
+notice like this when it starts in an interactive mode:
+
+ Copyright (C)
+ This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+ This is free software, and you are welcome to redistribute it
+ under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License. Of course, your program's commands
+might be different; for a GUI interface, you would use an "about box".
+
+ You should also get your employer (if you work as a programmer) or school,
+if any, to sign a "copyright disclaimer" for the program, if necessary.
+For more information on this, and how to apply and follow the GNU GPL, see
+.
+
+ The GNU General Public License does not permit incorporating your program
+into proprietary programs. If your program is a subroutine library, you
+may consider it more useful to permit linking proprietary applications with
+the library. If this is what you want to do, use the GNU Lesser General
+Public License instead of this License. But first, please read
+.
diff --git a/docs/外部参考/OpenCPN源码基线_f7211b4e/说明.md b/docs/外部参考/OpenCPN源码基线_f7211b4e/说明.md
new file mode 100644
index 0000000..da78993
--- /dev/null
+++ b/docs/外部参考/OpenCPN源码基线_f7211b4e/说明.md
@@ -0,0 +1,25 @@
+# OpenCPN 对照源码基线说明
+
+本目录保存 CM95 → PBF → S-52 / OpenCPN 流程等价审计使用的 OpenCPN 上游源码快照。
+
+## 固定上游基线
+
+- OpenCPN commit:`f7211b4e1874c453e37a7f190aa191c55a25c32e`
+- 本地取得来源:`/tmp/opencpn-s52-audit/`
+- 保存方式:按上游原始文件内容复制,不对源码做修改。
+- `gui/src/cm93.cpp` 因原 checkout 使用 sparse-checkout 未落到工作树,使用 `git show :gui/src/cm93.cpp` 从同一 commit 直接导出。
+- `/tmp/opencpn_cm93.cpp` 与该 commit 的 `gui/src/cm93.cpp` SHA-256 已确认完全一致。
+
+## 保存范围
+
+- `上游源码/gui/src/cm93.cpp`:CM93 对象创建、M_COVR 与 CM93→S57/S-52 类转换参考。
+- `上游源码/libs/s52plib/src/`:完整 S-52 Presentation Library 源码目录,包含 CSP、Lookup/rule parser、primitive renderer、chartsymbols 与相关辅助实现。
+- `许可证/`:上游 GPL v2 / GPL v3 许可证原文。
+- `文件校验.sha256`:本目录上游源码与许可证文件的 SHA-256 校验清单。
+- `上游提交.txt`:固定 commit 全长 hash。
+
+## 审计规则
+
+后续 CM95 的 OpenCPN Code-First S-52 Rendering Equivalence Audit 应优先引用本目录,不再依赖 `/tmp` 临时文件。
+本目录是只读参考快照;如果以后切换 OpenCPN 基线,不覆盖本目录,而应新增新的 commit 基线目录并在审计报告中明确使用哪一版。
+源码文件保留其原始版权与许可证声明;CM95 自有代码与此上游参考快照必须保持清晰边界。