34#include "wx/tokenzr.h" 
   35#include <wx/textfile.h> 
   36#include <wx/filename.h> 
   39#include "OCPNPlatform.h" 
   47#include "model/cutil.h" 
   48#include "model/georef.h" 
   50#include "model/navutil_base.h" 
   52#include "ocpn_pixel.h" 
   55#include "model/wx28compat.h" 
   56#include "model/chartdata_input_stream.h" 
   58#include "gdal/cpl_csv.h" 
   63#include "pluginmanager.h"   
   67#include "SencManager.h" 
   71#include "ocpn_frame.h" 
   74#include <wx/msw/msvcrt.h> 
   78#include "glChartCanvas.h" 
   89#include "chart_ctx_factory.h" 
   92#define strncasecmp(x, y, z) _strnicmp(x, y, z) 
   96#include "crashlytics.h" 
   99extern bool GetDoubleAttr(S57Obj *obj, 
const char *AttrName,
 
  102void OpenCPN_OGRErrorHandler(
 
  103    CPLErr eErrClass, 
int nError,
 
  104    const char *pszErrorMsg);  
 
  106extern s52plib *ps52plib;
 
  107extern S57ClassRegistrar *g_poRegistrar;
 
  108extern wxString g_csv_locn;
 
  109extern wxString g_SENCPrefix;
 
  110extern bool g_bGDAL_Debug;
 
  111extern bool g_bDebugS57;
 
  114extern bool g_b_overzoom_x;
 
  115extern bool g_b_EnableVBO;
 
  119int g_SENC_LOD_pixels;
 
  121static jmp_buf env_ogrf;  
 
  123#include <wx/arrimpl.cpp>   
  124WX_DEFINE_OBJARRAY(ArrayOfS57Obj);
 
  126#include <wx/listimpl.cpp> 
  127WX_DEFINE_LIST(ListOfPI_S57Obj);
 
  129WX_DEFINE_LIST(ListOfObjRazRules);  
 
  131#define S57_THUMB_SIZE 200 
  138static uint64_t hash_fast64(
const void *buf, 
size_t len, uint64_t seed) {
 
  139  const uint64_t m = 0x880355f21e6d1965ULL;
 
  140  const uint64_t *pos = (
const uint64_t *)buf;
 
  141  const uint64_t *end = pos + (len >> 3);
 
  142  const unsigned char *pc;
 
  143  uint64_t h = len * m ^ seed;
 
  148    v *= 0x2127599bf4325c37ULL;
 
  152  pc = (
const unsigned char *)pos;
 
  156      v ^= (uint64_t)pc[6] << 48;  
 
  158      v ^= (uint64_t)pc[5] << 40;  
 
  160      v ^= (uint64_t)pc[4] << 32;  
 
  162      v ^= (uint64_t)pc[3] << 24;  
 
  164      v ^= (uint64_t)pc[2] << 16;  
 
  166      v ^= (uint64_t)pc[1] << 8;  
 
  168      v ^= (uint64_t)pc[0];
 
  170      v *= 0x2127599bf4325c37ULL;
 
  176  h *= 0x2127599bf4325c37ULL;
 
  181static unsigned int hash_fast32(
const void *buf, 
size_t len,
 
  183  uint64_t h = hash_fast64(buf, len, seed);
 
  188  return h - (h >> 32);
 
  191unsigned long connector_key::hash()
 const {
 
  192  return hash_fast32(k, 
sizeof k, 0);
 
  199render_canvas_parms::render_canvas_parms() { pix_buff = NULL; }
 
  201render_canvas_parms::~render_canvas_parms(
void) {}
 
  203static void PrepareForRender(
ViewPort *pvp, s52plib *plib) {
 
  208                        pvp->rv_rect, pvp->GetBBox(), pvp->
ref_scale,
 
  210  plib->PrepareForRender();
 
  217s57chart::s57chart() {
 
  218  m_ChartType = CHART_TYPE_S57;
 
  219  m_ChartFamily = CHART_FAMILY_VECTOR;
 
  221  for (
int i = 0; i < PRIO_NUM; i++)
 
  222    for (
int j = 0; j < LUPNAME_NUM; j++) razRules[i][j] = NULL;
 
  231  pFloatingATONArray = 
new wxArrayPtrVoid;
 
  232  pRigidATONArray = 
new wxArrayPtrVoid;
 
  234  m_tmpup_array = NULL;
 
  236  m_DepthUnits = _T(
"METERS");
 
  237  m_depth_unit_id = DEPTH_UNIT_METERS;
 
  239  bGLUWarningSent = 
false;
 
  245  m_pvaldco_array = NULL;
 
  247  m_bExtentSet = 
false;
 
  249  m_pDIBThumbDay = NULL;
 
  250  m_pDIBThumbDim = NULL;
 
  251  m_pDIBThumbOrphan = NULL;
 
  252  m_bbase_file_attr_known = 
false;
 
  254  m_bLinePrioritySet = 
false;
 
  255  m_plib_state_hash = 0;
 
  262  m_b2pointLUPS = 
false;
 
  263  m_b2lineLUPS = 
false;
 
  265  m_next_safe_cnt = 1e6;
 
  267  m_line_vertex_buffer = 0;
 
  268  m_this_chart_context = 0;
 
  270  m_vbo_byte_length = 0;
 
  271  bReadyToRender = 
false;
 
  273  m_disableBackgroundSENC = 
false;
 
  276s57chart::~s57chart() {
 
  277  FreeObjectsAndRules();
 
  284  delete pFloatingATONArray;
 
  285  delete pRigidATONArray;
 
  289  free(m_pvaldco_array);
 
  291  free(m_line_vertex_buffer);
 
  293  delete m_pDIBThumbOrphan;
 
  295  for (
unsigned i = 0; i < m_pcs_vector.size(); i++) 
delete m_pcs_vector.at(i);
 
  297  for (
unsigned i = 0; i < m_pve_vector.size(); i++) 
delete m_pve_vector.at(i);
 
  299  m_pcs_vector.clear();
 
  300  m_pve_vector.clear();
 
  302  for (
const auto &it : m_ve_hash) {
 
  303    VE_Element *pedge = it.second;
 
  305      free(pedge->pPoints);
 
  311  for (
const auto &it : m_vc_hash) {
 
  312    VC_Element *pcs = it.second;
 
  321  if ((m_LineVBO_name > 0)) glDeleteBuffers(1, (GLuint *)&m_LineVBO_name);
 
  323  free(m_this_chart_context);
 
  325  if (m_TempFilePath.Length() && (m_FullPath != m_TempFilePath)) {
 
  326    if (::wxFileExists(m_TempFilePath)) wxRemoveFile(m_TempFilePath);
 
  330  if (g_SencThreadManager) {
 
  331    if (g_SencThreadManager->IsChartInTicketlist(
this)) {
 
  332      g_SencThreadManager->SetChartPointer(
this, NULL);
 
  337void s57chart::GetValidCanvasRegion(
const ViewPort &VPoint,
 
  341  double easting, northing;
 
  344  toSM(m_FullExtent.SLAT, m_FullExtent.WLON, VPoint.
clat, VPoint.
clon, &easting,
 
  349  rxl = (int)round((VPoint.
pix_width / 2) + epix);
 
  350  ryb = (int)round((VPoint.
pix_height / 2) - npix);
 
  352  toSM(m_FullExtent.NLAT, m_FullExtent.ELON, VPoint.
clat, VPoint.
clon, &easting,
 
  357  rxr = (int)round((VPoint.
pix_width / 2) + epix);
 
  358  ryt = (int)round((VPoint.
pix_height / 2) - npix);
 
  360  pValidRegion->Clear();
 
  361  pValidRegion->Union(rxl, ryt, rxr - rxl, ryb - ryt);
 
  364LLRegion s57chart::GetValidRegion() {
 
  365  double ll[8] = {m_FullExtent.SLAT, m_FullExtent.WLON, m_FullExtent.SLAT,
 
  366                  m_FullExtent.ELON, m_FullExtent.NLAT, m_FullExtent.ELON,
 
  367                  m_FullExtent.NLAT, m_FullExtent.WLON};
 
  368  return LLRegion(4, ll);
 
  371void s57chart::SetColorScheme(ColorScheme cs, 
bool bApplyImmediate) {
 
  372  if (!ps52plib) 
return;
 
  377    case GLOBAL_COLOR_SCHEME_DAY:
 
  378      ps52plib->SetPLIBColorScheme(
"DAY", ChartCtxFactory());
 
  380    case GLOBAL_COLOR_SCHEME_DUSK:
 
  381      ps52plib->SetPLIBColorScheme(
"DUSK", ChartCtxFactory());
 
  383    case GLOBAL_COLOR_SCHEME_NIGHT:
 
  384      ps52plib->SetPLIBColorScheme(
"NIGHT", ChartCtxFactory());
 
  387      ps52plib->SetPLIBColorScheme(
"DAY", ChartCtxFactory());
 
  391  m_global_color_scheme = cs;
 
  393  if (bApplyImmediate) {
 
  399  ClearRenderedTextCache();
 
  402  ChangeThumbColor(cs);
 
  405void s57chart::ChangeThumbColor(ColorScheme cs) {
 
  406  if (0 == m_pDIBThumbDay) 
return;
 
  410    case GLOBAL_COLOR_SCHEME_DAY:
 
  411      pThumbData->pDIBThumb = m_pDIBThumbDay;
 
  412      m_pDIBThumbOrphan = m_pDIBThumbDim;
 
  414    case GLOBAL_COLOR_SCHEME_DUSK:
 
  415    case GLOBAL_COLOR_SCHEME_NIGHT: {
 
  416      if (NULL == m_pDIBThumbDim) {
 
  417        wxImage img = m_pDIBThumbDay->ConvertToImage();
 
  419#if wxCHECK_VERSION(2, 8, 0) 
  420        wxImage gimg = img.ConvertToGreyscale(
 
  430        wxBitmap *pBMP = 
new wxBitmap(gimg);
 
  432        m_pDIBThumbDim = pBMP;
 
  433        m_pDIBThumbOrphan = m_pDIBThumbDay;
 
  436      pThumbData->pDIBThumb = m_pDIBThumbDim;
 
  442bool s57chart::GetChartExtent(
Extent *pext) {
 
  444    *pext = m_FullExtent;
 
  450static void free_mps(mps_container *mps) {
 
  451  if (mps == 0) 
return;
 
  452  if (ps52plib && mps->cs_rules) {
 
  453    for (
unsigned int i = 0; i < mps->cs_rules->GetCount(); i++) {
 
  454      Rules *rule_chain_top = mps->cs_rules->Item(i);
 
  455      ps52plib->DestroyRulesChain(rule_chain_top);
 
  457    delete mps->cs_rules;
 
  462void s57chart::FreeObjectsAndRules() {
 
  471  for (
int i = 0; i < PRIO_NUM; ++i) {
 
  472    for (
int j = 0; j < LUPNAME_NUM; j++) {
 
  473      top = razRules[i][j];
 
  474      while (top != NULL) {
 
  476        if (0 == top->obj->nRef) 
delete top->obj;
 
  479          ObjRazRules *ctop = top->child;
 
  483            if (ps52plib) ps52plib->DestroyLUP(ctop->LUP);
 
  485            ObjRazRules *cnxx = ctop->next;
 
  500void s57chart::ClearRenderedTextCache() {
 
  502  for (
int i = 0; i < PRIO_NUM; ++i) {
 
  503    for (
int j = 0; j < LUPNAME_NUM; j++) {
 
  504      top = razRules[i][j];
 
  505      while (top != NULL) {
 
  506        if (top->obj->bFText_Added) {
 
  507          top->obj->bFText_Added = 
false;
 
  508          delete top->obj->FText;
 
  509          top->obj->FText = NULL;
 
  513          ObjRazRules *ctop = top->child;
 
  515            if (ctop->obj->bFText_Added) {
 
  516              ctop->obj->bFText_Added = 
false;
 
  517              delete ctop->obj->FText;
 
  518              ctop->obj->FText = NULL;
 
  530double s57chart::GetNormalScaleMin(
double canvas_scale_factor,
 
  531                                   bool b_allow_overzoom) {
 
  533  return m_Chart_Scale * 0.125;
 
  537double s57chart::GetNormalScaleMax(
double canvas_scale_factor,
 
  539  return m_Chart_Scale * 4.0;
 
  546void s57chart::GetPointPix(ObjRazRules *rzRules, 
float north, 
float east,
 
  548  r->x = roundint(((east - m_easting_vp_center) * m_view_scale_ppm) +
 
  550  r->y = roundint(m_pixy_vp_center -
 
  551                  ((north - m_northing_vp_center) * m_view_scale_ppm));
 
  554void s57chart::GetPointPix(ObjRazRules *rzRules, wxPoint2DDouble *en,
 
  555                           wxPoint *r, 
int nPoints) {
 
  556  for (
int i = 0; i < nPoints; i++) {
 
  557    r[i].x = roundint(((en[i].m_x - m_easting_vp_center) * m_view_scale_ppm) +
 
  559    r[i].y = roundint(m_pixy_vp_center -
 
  560                      ((en[i].m_y - m_northing_vp_center) * m_view_scale_ppm));
 
  564void s57chart::GetPixPoint(
int pixx, 
int pixy, 
double *plat, 
double *plon,
 
  566  if (vpt->m_projection_type != PROJECTION_MERCATOR)
 
  567    printf(
"s57chart unhandled projection\n");
 
  573  double xp = (dx * cos(vpt->
skew)) - (dy * sin(vpt->
skew));
 
  574  double yp = (dy * cos(vpt->
skew)) + (dx * sin(vpt->
skew));
 
  580  fromSM(d_east, d_north, vpt->
clat, vpt->
clon, &slat, &slon);
 
  590void s57chart::SetVPParms(
const ViewPort &vpt) {
 
  596  toSM(vpt.
clat, vpt.
clon, ref_lat, ref_lon, &m_easting_vp_center,
 
  597       &m_northing_vp_center);
 
  599  vp_transform.easting_vp_center = m_easting_vp_center;
 
  600  vp_transform.northing_vp_center = m_northing_vp_center;
 
  604  if (IsCacheValid()) {
 
  607      double prev_easting_c, prev_northing_c;
 
  608      toSM(vp_last.
clat, vp_last.
clon, ref_lat, ref_lon, &prev_easting_c,
 
  611      double easting_c, northing_c;
 
  612      toSM(vp_proposed.
clat, vp_proposed.
clon, ref_lat, ref_lon, &easting_c,
 
  620      int dpix_x = (int)round(delta_pix_x);
 
  625      int dpix_y = (int)round(delta_pix_y);
 
  628      double c_east_d = (dpx / vp_proposed.
view_scale_ppm) + prev_easting_c;
 
  629      double c_north_d = (dpy / vp_proposed.
view_scale_ppm) + prev_northing_c;
 
  632      fromSM(c_east_d, c_north_d, ref_lat, ref_lon, &xlat, &xlon);
 
  634      vp_proposed.
clon = xlon;
 
  635      vp_proposed.
clat = xlat;
 
  662void s57chart::LoadThumb() {
 
  663  wxFileName fn(m_FullPath);
 
  664  wxString SENCdir = g_SENCPrefix;
 
  666  if (SENCdir.Last() != fn.GetPathSeparator())
 
  667    SENCdir.Append(fn.GetPathSeparator());
 
  669  wxFileName tsfn(SENCdir);
 
  670  tsfn.SetFullName(fn.GetFullName());
 
  672  wxFileName ThumbFileNameLook(tsfn);
 
  673  ThumbFileNameLook.SetExt(_T(
"BMP"));
 
  676  if (ThumbFileNameLook.FileExists()) {
 
  679    pBMP->LoadFile(ThumbFileNameLook.GetFullPath(), wxBITMAP_TYPE_BMP);
 
  680    m_pDIBThumbDay = pBMP;
 
  681    m_pDIBThumbOrphan = 0;
 
  686ThumbData *s57chart::GetThumbData(
int tnx, 
int tny, 
float lat, 
float lon) {
 
  689  if (pThumbData->pDIBThumb == 0) {
 
  691    ChangeThumbColor(m_global_color_scheme);
 
  694  UpdateThumbData(lat, lon);
 
  699bool s57chart::UpdateThumbData(
double lat, 
double lon) {
 
  703  if (pThumbData->pDIBThumb) {
 
  704    double lat_top = m_FullExtent.NLAT;
 
  705    double lat_bot = m_FullExtent.SLAT;
 
  706    double lon_left = m_FullExtent.WLON;
 
  707    double lon_right = m_FullExtent.ELON;
 
  710    double ext_max = fmax((lat_top - lat_bot), (lon_right - lon_left));
 
  712    double thumb_view_scale_ppm = (S57_THUMB_SIZE / ext_max) / (1852 * 60);
 
  714    toSM(lat, lon, (lat_top + lat_bot) / 2., (lon_left + lon_right) / 2., &east,
 
  717    test_x = pThumbData->pDIBThumb->GetWidth() / 2 +
 
  718             (int)(east * thumb_view_scale_ppm);
 
  719    test_y = pThumbData->pDIBThumb->GetHeight() / 2 -
 
  720             (int)(north * thumb_view_scale_ppm);
 
  727  if ((test_x != pThumbData->ShipX) || (test_y != pThumbData->ShipY)) {
 
  728    pThumbData->ShipX = test_x;
 
  729    pThumbData->ShipY = test_y;
 
  735void s57chart::SetFullExtent(
Extent &ext) {
 
  736  m_FullExtent.NLAT = ext.NLAT;
 
  737  m_FullExtent.SLAT = ext.SLAT;
 
  738  m_FullExtent.WLON = ext.WLON;
 
  739  m_FullExtent.ELON = ext.ELON;
 
  744void s57chart::ForceEdgePriorityEvaluate(
void) { m_bLinePrioritySet = 
false; }
 
  746void s57chart::SetLinePriorities(
void) {
 
  747  if (!ps52plib) 
return;
 
  752  if (!m_bLinePrioritySet) {
 
  756    for (
int i = 0; i < PRIO_NUM; ++i) {
 
  757      top = razRules[i][2];  
 
  758      while (top != NULL) {
 
  759        ObjRazRules *crnt = top;
 
  761        ps52plib->SetLineFeaturePriority(crnt, i);
 
  767      if (ps52plib->m_nBoundaryStyle == SYMBOLIZED_BOUNDARIES)
 
  772      top = razRules[i][j];
 
  773      while (top != NULL) {
 
  776        ps52plib->SetLineFeaturePriority(crnt, i);
 
  782    for (
int i = 0; i < PRIO_NUM; ++i) {
 
  783      for (
int j = 0; j < LUPNAME_NUM; j++) {
 
  784        ObjRazRules *top = razRules[i][j];
 
  785        while (top != NULL) {
 
  786          S57Obj *obj = top->obj;
 
  789          connector_segment *pcs;
 
  790          line_segment_element *list = obj->m_ls_list;
 
  792            switch (list->ls_type) {
 
  796                if (pedge) list->priority = pedge->max_priority;
 
  801                if (pcs) list->priority = pcs->max_priority_cs;
 
  816  m_bLinePrioritySet = 
true;
 
  820void s57chart::SetLinePriorities( 
void )
 
  822    if( !ps52plib ) 
return;
 
  827    if( !m_bLinePrioritySet ) {
 
  831        for( 
int i = 0; i < PRIO_NUM; ++i ) {
 
  833            top = razRules[i][2];           
 
  834            while( top != NULL ) {
 
  835                ObjRazRules *crnt = top;
 
  837                ps52plib->SetLineFeaturePriority( crnt, i );
 
  842            if( ps52plib->m_nBoundaryStyle == SYMBOLIZED_BOUNDARIES )
 
  847            top = razRules[i][j];
 
  848            while( top != NULL ) {
 
  851                ps52plib->SetLineFeaturePriority( crnt, i );
 
  859        for( 
int i = 0; i < PRIO_NUM; ++i ) {
 
  860            for( 
int j = 0; j < LUPNAME_NUM; j++ ) {
 
  861                ObjRazRules *top = razRules[i][j];
 
  862                while( top != NULL ) {
 
  863                    S57Obj *obj = top->obj;
 
  866                    connector_segment *pcs;
 
  867                    line_segment_element *list = obj->m_ls_list;
 
  872                                pedge = (VE_Element *)list->private0;
 
  874                                    list->priority = pedge->max_priority;
 
  878                                pcs = (connector_segment *)list->private0;
 
  880                                    list->priority = pcs->max_priority;
 
  895    m_bLinePrioritySet = 
true;
 
  899int s57chart::GetLineFeaturePointArray(S57Obj *obj, 
void **ret_array) {
 
  903  line_segment_element *ls_list = obj->m_ls_list;
 
  905    if ((ls_list->ls_type == TYPE_EE) || (ls_list->ls_type == TYPE_EE_REV))
 
  906      nPoints += ls_list->pedge->nCount;
 
  909    ls_list = ls_list->next;
 
  918  float *br = (
float *)malloc(nPoints * 2 * 
sizeof(
float));
 
  922  unsigned char *source_buffer = (
unsigned char *)GetLineVertexBuffer();
 
  923  ls_list = obj->m_ls_list;
 
  925    size_t vbo_offset = 0;
 
  927    if ((ls_list->ls_type == TYPE_EE) || (ls_list->ls_type == TYPE_EE_REV)) {
 
  928      vbo_offset = ls_list->pedge->vbo_offset;
 
  929      count = ls_list->pedge->nCount;
 
  931      vbo_offset = ls_list->pcs->vbo_offset;
 
  935    memcpy(br, source_buffer + vbo_offset, count * 2 * 
sizeof(
float));
 
  937    ls_list = ls_list->next;
 
  944int s57chart::GetLineFeaturePointArray(S57Obj *obj, 
void **ret_array)
 
  949    line_segment_element *ls_list = obj->m_ls_list;
 
  951        nPoints += ls_list->n_points;
 
  952        ls_list = ls_list->next;
 
  961    float *br = (
float *)malloc(nPoints * 2 * 
sizeof(
float));
 
  965    unsigned char *source_buffer = (
unsigned char *)GetLineVertexBuffer();
 
  966    ls_list = obj->m_ls_list;
 
  968        memcpy(br, source_buffer + ls_list->vbo_offset, ls_list->n_points * 2 * 
sizeof(
float));
 
  969        br += ls_list->n_points * 2;
 
  970        ls_list = ls_list->next;
 
  979  float e0, n0, e1, n1;
 
 
  982void s57chart::AssembleLineGeometry(
void) {
 
  987  for (
const auto &it : m_ve_hash) {
 
  988    VE_Element *pedge = it.second;
 
  990      nPoints += pedge->nCount;
 
  996  std::map<long long, connector_segment *> ce_connector_hash;
 
  997  std::map<long long, connector_segment *> ec_connector_hash;
 
  998  std::map<long long, connector_segment *> cc_connector_hash;
 
 1000  std::map<long long, connector_segment *>::iterator csit;
 
 1007  std::vector<segment_pair> connector_segment_vector;
 
 1008  size_t seg_pair_index = 0;
 
 1013  for (
int i = 0; i < PRIO_NUM; ++i) {
 
 1014    for (
int j = 0; j < LUPNAME_NUM; j++) {
 
 1015      ObjRazRules *top = razRules[i][j];
 
 1016      while (top != NULL) {
 
 1017        S57Obj *obj = top->obj;
 
 1019        if ((!obj->m_ls_list) &&
 
 1022          line_segment_element list_top;
 
 1025          line_segment_element *le_current = &list_top;
 
 1027          for (
int iseg = 0; iseg < obj->m_n_lsindex; iseg++) {
 
 1028            if (!obj->m_lsindex_array) 
continue;
 
 1030            int seg_index = iseg * 3;
 
 1031            int *index_run = &obj->m_lsindex_array[seg_index];
 
 1034            unsigned int inode = *index_run++;
 
 1037            bool edge_dir = 
true;
 
 1038            int venode = *index_run++;
 
 1044            VE_Element *pedge = 0;
 
 1046              if (m_ve_hash.find(venode) != m_ve_hash.end())
 
 1047                pedge = m_ve_hash[venode];
 
 1051            unsigned int enode = *index_run++;
 
 1054            VC_Element *ipnode = 0;
 
 1055            ipnode = m_vc_hash[inode];
 
 1058            VC_Element *epnode = 0;
 
 1059            epnode = m_vc_hash[enode];
 
 1062              if (pedge && pedge->nCount) {
 
 1066                long long key = ((
unsigned long long)inode << 32) + venode;
 
 1068                connector_segment *pcs = NULL;
 
 1069                csit = ce_connector_hash.find(key);
 
 1070                if (csit == ce_connector_hash.end()) {
 
 1072                  pcs = 
new connector_segment;
 
 1073                  ce_connector_hash[key] = pcs;
 
 1077                  float *ppt = ipnode->pPoint;
 
 1082                    pair.e1 = pedge->pPoints[0];
 
 1083                    pair.n1 = pedge->pPoints[1];
 
 1085                    int last_point_index = (pedge->nCount - 1) * 2;
 
 1086                    pair.e1 = pedge->pPoints[last_point_index];
 
 1087                    pair.n1 = pedge->pPoints[last_point_index + 1];
 
 1090                  connector_segment_vector.push_back(pair);
 
 1091                  pcs->vbo_offset = seg_pair_index;  
 
 1098                                (pair.n0 + pair.n1) / 2, ref_lat, ref_lon, &lat,
 
 1100                  pcs->cs_lat_avg = lat;
 
 1101                  pcs->cs_lon_avg = lon;
 
 1106                line_segment_element *pls = 
new line_segment_element;
 
 1111                pls->ls_type = TYPE_CE;
 
 1113                le_current->next = pls;  
 
 1118            if (pedge && pedge->nCount) {
 
 1119              line_segment_element *pls = 
new line_segment_element;
 
 1124              pls->ls_type = TYPE_EE;
 
 1125              if (!edge_dir) pls->ls_type = TYPE_EE_REV;
 
 1127              le_current->next = pls;  
 
 1135                if (pedge && pedge->nCount) {
 
 1136                  long long key = ((
unsigned long long)venode << 32) + enode;
 
 1138                  connector_segment *pcs = NULL;
 
 1139                  csit = ec_connector_hash.find(key);
 
 1140                  if (csit == ec_connector_hash.end()) {
 
 1142                    pcs = 
new connector_segment;
 
 1143                    ec_connector_hash[key] = pcs;
 
 1149                      pair.e0 = pedge->pPoints[0];
 
 1150                      pair.n0 = pedge->pPoints[1];
 
 1152                      int last_point_index = (pedge->nCount - 1) * 2;
 
 1153                      pair.e0 = pedge->pPoints[last_point_index];
 
 1154                      pair.n0 = pedge->pPoints[last_point_index + 1];
 
 1157                    float *ppt = epnode->pPoint;
 
 1161                    connector_segment_vector.push_back(pair);
 
 1162                    pcs->vbo_offset = seg_pair_index;  
 
 1169                                  (pair.n0 + pair.n1) / 2, ref_lat, ref_lon,
 
 1171                    pcs->cs_lat_avg = lat;
 
 1172                    pcs->cs_lon_avg = lon;
 
 1177                  line_segment_element *pls = 
new line_segment_element;
 
 1181                  pls->ls_type = TYPE_EC;
 
 1183                  le_current->next = pls;  
 
 1187                  long long key = ((
unsigned long long)inode << 32) + enode;
 
 1189                  connector_segment *pcs = NULL;
 
 1190                  csit = cc_connector_hash.find(key);
 
 1191                  if (csit == cc_connector_hash.end()) {
 
 1193                    pcs = 
new connector_segment;
 
 1194                    cc_connector_hash[key] = pcs;
 
 1199                    float *ppt = ipnode->pPoint;
 
 1203                    ppt = epnode->pPoint;
 
 1207                    connector_segment_vector.push_back(pair);
 
 1208                    pcs->vbo_offset = seg_pair_index;  
 
 1215                                  (pair.n0 + pair.n1) / 2, ref_lat, ref_lon,
 
 1217                    pcs->cs_lat_avg = lat;
 
 1218                    pcs->cs_lon_avg = lon;
 
 1223                  line_segment_element *pls = 
new line_segment_element;
 
 1227                  pls->ls_type = TYPE_CC;
 
 1229                  le_current->next = pls;  
 
 1243          if (obj->m_ls_list == NULL) {
 
 1244            obj->m_n_lsindex = 0;
 
 1248          free(obj->m_lsindex_array);
 
 1249          obj->m_lsindex_array = NULL;
 
 1262  size_t vbo_byte_length = 2 * nPoints * 
sizeof(float);
 
 1264  unsigned char *buffer_offset;
 
 1267  bool grow_buffer = 
false;
 
 1269  if (0 == m_vbo_byte_length) {
 
 1270    m_line_vertex_buffer = (
float *)malloc(vbo_byte_length);
 
 1271    m_vbo_byte_length = vbo_byte_length;
 
 1272    buffer_offset = (
unsigned char *)m_line_vertex_buffer;
 
 1275    m_line_vertex_buffer = (
float *)realloc(
 
 1276        m_line_vertex_buffer, m_vbo_byte_length + vbo_byte_length);
 
 1277    buffer_offset = (
unsigned char *)m_line_vertex_buffer + m_vbo_byte_length;
 
 1278    offset = m_vbo_byte_length;
 
 1279    m_vbo_byte_length = m_vbo_byte_length + vbo_byte_length;
 
 1283  float *lvr = (
float *)buffer_offset;
 
 1287  for (
const auto &it : m_ve_hash) {
 
 1288    VE_Element *pedge = it.second;
 
 1290      memcpy(lvr, pedge->pPoints, pedge->nCount * 2 * 
sizeof(
float));
 
 1291      lvr += pedge->nCount * 2;
 
 1293      pedge->vbo_offset = offset;
 
 1294      offset += pedge->nCount * 2 * 
sizeof(float);
 
 1307  for (csit = ce_connector_hash.begin(); csit != ce_connector_hash.end();
 
 1309    connector_segment *pcs = csit->second;
 
 1310    m_pcs_vector.push_back(pcs);
 
 1312    segment_pair pair = connector_segment_vector.at(pcs->vbo_offset);
 
 1318    pcs->vbo_offset = offset;
 
 1319    offset += 4 * 
sizeof(float);
 
 1322  for (csit = ec_connector_hash.begin(); csit != ec_connector_hash.end();
 
 1324    connector_segment *pcs = csit->second;
 
 1325    m_pcs_vector.push_back(pcs);
 
 1327    segment_pair pair = connector_segment_vector.at(pcs->vbo_offset);
 
 1333    pcs->vbo_offset = offset;
 
 1334    offset += 4 * 
sizeof(float);
 
 1337  for (csit = cc_connector_hash.begin(); csit != cc_connector_hash.end();
 
 1339    connector_segment *pcs = csit->second;
 
 1340    m_pcs_vector.push_back(pcs);
 
 1342    segment_pair pair = connector_segment_vector.at(pcs->vbo_offset);
 
 1348    pcs->vbo_offset = offset;
 
 1349    offset += 4 * 
sizeof(float);
 
 1353  connector_segment_vector.clear();
 
 1358  for (
const auto &it : m_ve_hash) {
 
 1359    VE_Element *pedge = it.second;
 
 1361      m_pve_vector.push_back(pedge);
 
 1362      free(pedge->pPoints);
 
 1370  for (
const auto &it : m_vc_hash) {
 
 1371    VC_Element *pcs = it.second;
 
 1372    if (pcs) free(pcs->pPoint);
 
 1378  if (g_b_EnableVBO) {
 
 1380      if (m_LineVBO_name > 0) {
 
 1381        glDeleteBuffers(1, (GLuint *)&m_LineVBO_name);
 
 1382        m_LineVBO_name = -1;
 
 1389void s57chart::BuildLineVBO(
void) {
 
 1391  if (!g_b_EnableVBO) 
return;
 
 1393  if (m_LineVBO_name == -1) {
 
 1396    glGenBuffers(1, &vboId);
 
 1399    glBindBuffer(GL_ARRAY_BUFFER, vboId);
 
 1405#ifndef USE_ANDROID_GLES2 
 1406    glEnableClientState(GL_VERTEX_ARRAY);  
 
 1408    glBufferData(GL_ARRAY_BUFFER, m_vbo_byte_length, m_line_vertex_buffer,
 
 1413    ObjRazRules *top, *crnt;
 
 1414    int vbo_area_size_bytes = 0;
 
 1415    for (
int i = 0; i < PRIO_NUM; ++i) {
 
 1416      if (ps52plib->m_nBoundaryStyle == SYMBOLIZED_BOUNDARIES)
 
 1417        top = razRules[i][4];  
 
 1419        top = razRules[i][3];  
 
 1421      while (top != NULL) {
 
 1426        PolyTriGroup *ppg_vbo =
 
 1427            crnt->obj->pPolyTessGeo->Get_PolyTriGroup_head();
 
 1429        vbo_area_size_bytes += ppg_vbo->single_buffer_size;
 
 1436    glBufferData(GL_ARRAY_BUFFER, m_vbo_byte_length + vbo_area_size_bytes, NULL,
 
 1439    GLenum err = glGetError();
 
 1442      msg.Printf(_T(
"S57 VBO Error 1: %d"), err);
 
 1444      printf(
"S57 VBO Error 1: %d", err);
 
 1448    glBufferSubData(GL_ARRAY_BUFFER, 0, m_vbo_byte_length,
 
 1449                    m_line_vertex_buffer);
 
 1454      msg.Printf(_T(
"S57 VBO Error 2: %d"), err);
 
 1456      printf(
"S57 VBO Error 2: %d", err);
 
 1460    int vbo_load_offset = m_vbo_byte_length;
 
 1462    for (
int i = 0; i < PRIO_NUM; ++i) {
 
 1463      if (ps52plib->m_nBoundaryStyle == SYMBOLIZED_BOUNDARIES)
 
 1464        top = razRules[i][4];  
 
 1466        top = razRules[i][3];  
 
 1468      while (top != NULL) {
 
 1473        PolyTriGroup *ppg_vbo =
 
 1474            crnt->obj->pPolyTessGeo->Get_PolyTriGroup_head();
 
 1477        glBufferSubData(GL_ARRAY_BUFFER, vbo_load_offset,
 
 1478                        ppg_vbo->single_buffer_size, ppg_vbo->single_buffer);
 
 1480        crnt->obj->vboAreaOffset = vbo_load_offset;
 
 1481        vbo_load_offset += ppg_vbo->single_buffer_size;
 
 1488      msg.Printf(_T(
"S57 VBO Error 3: %d"), err);
 
 1490      printf(
"S57 VBO Error 3: %d", err);
 
 1495#ifndef USE_ANDROID_GLES2 
 1496    glDisableClientState(GL_VERTEX_ARRAY);  
 
 1498    glBindBuffer(GL_ARRAY_BUFFER, 0);
 
 1502    for (
int i = 0; i < PRIO_NUM; ++i) {
 
 1503      for (
int j = 0; j < LUPNAME_NUM; j++) {
 
 1504        ObjRazRules *top = razRules[i][j];
 
 1505        while (top != NULL) {
 
 1506          S57Obj *obj = top->obj;
 
 1507          obj->auxParm2 = vboId;
 
 1513    m_LineVBO_name = vboId;
 
 1514    m_this_chart_context->vboID = vboId;
 
 1533bool s57chart::RenderRegionViewOnGL(
const wxGLContext &glc,
 
 1536                                    const LLRegion &Region) {
 
 1537  if (!m_RAZBuilt) 
return false;
 
 1539  return DoRenderRegionViewOnGL(glc, VPoint, RectRegion, Region, 
false);
 
 1542bool s57chart::RenderOverlayRegionViewOnGL(
const wxGLContext &glc,
 
 1545                                           const LLRegion &Region) {
 
 1546  if (!m_RAZBuilt) 
return false;
 
 1548  return DoRenderRegionViewOnGL(glc, VPoint, RectRegion, Region, 
true);
 
 1551bool s57chart::RenderRegionViewOnGLNoText(
const wxGLContext &glc,
 
 1554                                          const LLRegion &Region) {
 
 1555  if (!m_RAZBuilt) 
return false;
 
 1557  bool b_text = ps52plib->GetShowS57Text();
 
 1558  ps52plib->m_bShowS57Text = 
false;
 
 1559  bool b_ret = DoRenderRegionViewOnGL(glc, VPoint, RectRegion, Region, 
false);
 
 1560  ps52plib->m_bShowS57Text = b_text;
 
 1565bool s57chart::RenderViewOnGLTextOnly(
const wxGLContext &glc,
 
 1567  if (!m_RAZBuilt) 
return false;
 
 1571  if (!ps52plib) 
return false;
 
 1574  PrepareForRender((
ViewPort *)&VPoint, ps52plib);
 
 1576  glChartCanvas::DisableClipRegion();
 
 1577  DoRenderOnGLText(glc, VPoint);
 
 1583bool s57chart::DoRenderRegionViewOnGL(
const wxGLContext &glc,
 
 1586                                      const LLRegion &Region, 
bool b_overlay) {
 
 1587  if (!m_RAZBuilt) 
return false;
 
 1591  if (!ps52plib) 
return false;
 
 1593  if (g_bDebugS57) printf(
"\n");
 
 1597  PrepareForRender((
ViewPort *)&VPoint, ps52plib);
 
 1599  if (m_plib_state_hash != ps52plib->GetStateHash()) {
 
 1600    m_bLinePrioritySet = 
false;  
 
 1602    ClearRenderedTextCache();    
 
 1604    ResetPointBBoxes(m_last_vp, VPoint);
 
 1607    m_plib_state_hash = ps52plib->GetStateHash();
 
 1611    ResetPointBBoxes(m_last_vp, VPoint);
 
 1615  SetLinePriorities();
 
 1618  ps52plib->ClearTextList();
 
 1626    wxRect upr = upd.GetRect();
 
 1629    LLRegion chart_region = vp.GetLLRegion(upd.GetRect());
 
 1630    chart_region.Intersect(Region);
 
 1632    if (!chart_region.Empty()) {
 
 1636      ViewPort cvp = glChartCanvas::ClippedViewport(VPoint, chart_region);
 
 1643      if (CHART_TYPE_CM93 == GetChartType()) {
 
 1647        glChartCanvas::SetClipRect(cvp, upd.GetRect(), 
false);
 
 1650#ifdef OPT_USE_ANDROID_GLES2 
 1658        wxRect r = upd.GetRect();
 
 1660        glViewport(r.x, vp->
pix_height - (r.y + r.height), r.width, r.height);
 
 1668        float yp = vp->
pix_height - (r.y + r.height);
 
 1670        I[3][0] = (-r.x - (float)r.width / 2) * (2.0 / (float)r.width);
 
 1671        I[3][1] = (r.y + (float)r.height / 2) * (2.0 / (float)r.height);
 
 1674        I[0][0] *= 2.0 / (float)r.width;
 
 1675        I[1][1] *= -2.0 / (float)r.height;
 
 1679        mat4x4_rotate_Z(Q, I, angle);
 
 1681        mat4x4_dup((
float(*)[4])vp->vp_transform, Q);
 
 1684        ps52plib->SetReducedBBox(cvp.GetBBox());
 
 1685        glChartCanvas::SetClipRect(cvp, upd.GetRect(), 
false);
 
 1690      DoRenderOnGL(glc, cvp);
 
 1692      glChartCanvas::DisableClipRegion();
 
 1705bool s57chart::DoRenderOnGL(
const wxGLContext &glc, 
const ViewPort &VPoint) {
 
 1718  for (i = 0; i < PRIO_NUM; ++i) {
 
 1719    if (ps52plib->m_nBoundaryStyle == SYMBOLIZED_BOUNDARIES)
 
 1720      top = razRules[i][4];  
 
 1722      top = razRules[i][3];  
 
 1724    while (top != NULL) {
 
 1727      crnt->sm_transform_parms = &vp_transform;
 
 1728      ps52plib->RenderAreaToGL(glc, crnt);
 
 1734  for (i = 0; i < PRIO_NUM; ++i) {
 
 1735    if (PI_GetPLIBBoundaryStyle() == SYMBOLIZED_BOUNDARIES)
 
 1736      top = razRules[i][4];  
 
 1738      top = razRules[i][3];  
 
 1740    while (top != NULL) {
 
 1743      crnt->sm_transform_parms = &vp_transform;
 
 1748      if (!crnt->obj->pPolyTessGeo->IsOk()) {
 
 1749        if (ps52plib->ObjectRenderCheckRules(crnt, &tvp, 
true)) {
 
 1750          if (!crnt->obj->pPolyTessGeo->m_pxgeom)
 
 1751            crnt->obj->pPolyTessGeo->m_pxgeom = buildExtendedGeom(crnt->obj);
 
 1754      ps52plib->RenderAreaToGL(glc, crnt, &tvp);
 
 1761  for (i = 0; i < PRIO_NUM; ++i) {
 
 1762    if (ps52plib->m_nBoundaryStyle == SYMBOLIZED_BOUNDARIES)
 
 1763      top = razRules[i][4];  
 
 1765      top = razRules[i][3];  
 
 1766    while (top != NULL) {
 
 1769      crnt->sm_transform_parms = &vp_transform;
 
 1770      ps52plib->RenderObjectToGL(glc, crnt);
 
 1775  for (i = 0; i < PRIO_NUM; ++i) {
 
 1776    top = razRules[i][2];  
 
 1777    while (top != NULL) {
 
 1780      crnt->sm_transform_parms = &vp_transform;
 
 1781      ps52plib->RenderObjectToGL(glc, crnt);
 
 1787  for (i = 0; i < PRIO_NUM; ++i) {
 
 1788    if (ps52plib->m_nSymbolStyle == SIMPLIFIED)
 
 1789      top = razRules[i][0];  
 
 1791      top = razRules[i][1];  
 
 1793    while (top != NULL) {
 
 1796      crnt->sm_transform_parms = &vp_transform;
 
 1797      ps52plib->RenderObjectToGL(glc, crnt);
 
 1807bool s57chart::DoRenderOnGLText(
const wxGLContext &glc,
 
 1818    for( i = 0; i < PRIO_NUM; ++i ) {
 
 1819        if( ps52plib->m_nBoundaryStyle == SYMBOLIZED_BOUNDARIES )
 
 1820            top = razRules[i][4]; 
 
 1822            top = razRules[i][3];           
 
 1824            while( top != NULL ) {
 
 1827                crnt->sm_transform_parms = &vp_transform;
 
 1834  for (i = 0; i < PRIO_NUM; ++i) {
 
 1835    if (ps52plib->m_nBoundaryStyle == SYMBOLIZED_BOUNDARIES)
 
 1836      top = razRules[i][4];  
 
 1838      top = razRules[i][3];  
 
 1840    while (top != NULL) {
 
 1843      crnt->sm_transform_parms = &vp_transform;
 
 1844      ps52plib->RenderObjectToGLText(glc, crnt);
 
 1847    top = razRules[i][2];  
 
 1848    while (top != NULL) {
 
 1851      crnt->sm_transform_parms = &vp_transform;
 
 1852      ps52plib->RenderObjectToGLText(glc, crnt);
 
 1855    if (ps52plib->m_nSymbolStyle == SIMPLIFIED)
 
 1856      top = razRules[i][0];  
 
 1858      top = razRules[i][1];  
 
 1860    while (top != NULL) {
 
 1863      crnt->sm_transform_parms = &vp_transform;
 
 1864      ps52plib->RenderObjectToGLText(glc, crnt);
 
 1873bool s57chart::RenderRegionViewOnDCNoText(wxMemoryDC &dc,
 
 1876  if (!m_RAZBuilt) 
return false;
 
 1878  bool b_text = ps52plib->GetShowS57Text();
 
 1879  ps52plib->m_bShowS57Text = 
false;
 
 1880  bool b_ret = DoRenderRegionViewOnDC(dc, VPoint, Region, 
false);
 
 1881  ps52plib->m_bShowS57Text = b_text;
 
 1886bool s57chart::RenderRegionViewOnDCTextOnly(wxMemoryDC &dc,
 
 1889  if (!dc.IsOk()) 
return false;
 
 1892  PrepareForRender((
ViewPort *)&VPoint, ps52plib);
 
 1897    DCRenderText(dc, VPoint);
 
 1900    double temp_lon_left, temp_lat_bot, temp_lon_right, temp_lat_top;
 
 1904    while (upd.HaveRects()) {
 
 1905      wxRect rect = upd.GetRect();
 
 1911      temp_vp.
GetLLFromPix(p, &temp_lat_top, &temp_lon_left);
 
 1915      temp_vp.
GetLLFromPix(p, &temp_lat_bot, &temp_lon_right);
 
 1917      if (temp_lon_right < temp_lon_left)  
 
 1918        temp_lon_right += 360.;
 
 1920      temp_vp.GetBBox().Set(temp_lat_bot, temp_lon_left, temp_lat_top,
 
 1923      wxDCClipper clip(dc, rect);
 
 1924      DCRenderText(dc, temp_vp);
 
 1933bool s57chart::RenderRegionViewOnDC(wxMemoryDC &dc, 
const ViewPort &VPoint,
 
 1935  if (!m_RAZBuilt) 
return false;
 
 1937  return DoRenderRegionViewOnDC(dc, VPoint, Region, 
false);
 
 1940bool s57chart::RenderOverlayRegionViewOnDC(wxMemoryDC &dc,
 
 1943  if (!m_RAZBuilt) 
return false;
 
 1944  return DoRenderRegionViewOnDC(dc, VPoint, Region, 
true);
 
 1947bool s57chart::DoRenderRegionViewOnDC(wxMemoryDC &dc, 
const ViewPort &VPoint,
 
 1952  bool force_new_view = 
false;
 
 1954  if (Region != m_last_Region) force_new_view = 
true;
 
 1956  PrepareForRender((
ViewPort *)&VPoint, ps52plib);
 
 1958  if (m_plib_state_hash != ps52plib->GetStateHash()) {
 
 1959    m_bLinePrioritySet = 
false;  
 
 1961    ClearRenderedTextCache();    
 
 1963    ResetPointBBoxes(m_last_vp, VPoint);
 
 1968    ResetPointBBoxes(m_last_vp, VPoint);
 
 1971  SetLinePriorities();
 
 1973  bool bnew_view = DoRenderViewOnDC(dc, VPoint, DC_RENDER_ONLY, force_new_view);
 
 1977  if (VPoint.b_quilt) {
 
 1979      if ((m_pCloneBM->GetWidth() != VPoint.
pix_width) ||
 
 1980          (m_pCloneBM->GetHeight() != VPoint.
pix_height)) {
 
 1985    if (NULL == m_pCloneBM)
 
 1988    wxMemoryDC dc_clone;
 
 1989    dc_clone.SelectObject(*m_pCloneBM);
 
 1991#ifdef ocpnUSE_DIBSECTION 
 1994    wxMemoryDC memdc, dc_org;
 
 1997    pDIB->SelectIntoDC(dc_org);
 
 2002    while (upd.HaveRects()) {
 
 2003      wxRect rect = upd.GetRect();
 
 2004      dc_clone.Blit(rect.x, rect.y, rect.width, rect.height, &dc_org, rect.x,
 
 2009    dc_clone.SelectObject(wxNullBitmap);
 
 2010    dc_org.SelectObject(wxNullBitmap);
 
 2014      wxColour nodat = GetGlobalColor(_T ( 
"NODTA" ));
 
 2015      wxColour nodat_sub = nodat;
 
 2017#ifdef ocpnUSE_ocpnBitmap 
 2018      nodat_sub = wxColour(nodat.Blue(), nodat.Green(), nodat.Red());
 
 2020      m_pMask = 
new wxMask(*m_pCloneBM, nodat_sub);
 
 2021      m_pCloneBM->SetMask(m_pMask);
 
 2024    dc.SelectObject(*m_pCloneBM);
 
 2026    pDIB->SelectIntoDC(dc);
 
 2028  m_last_Region = Region;
 
 2033bool s57chart::RenderViewOnDC(wxMemoryDC &dc, 
const ViewPort &VPoint) {
 
 2038  PrepareForRender((
ViewPort *)&VPoint, ps52plib);
 
 2040  if (m_plib_state_hash != ps52plib->GetStateHash()) {
 
 2041    m_bLinePrioritySet = 
false;  
 
 2043    ClearRenderedTextCache();    
 
 2047  SetLinePriorities();
 
 2049  bool bnew_view = DoRenderViewOnDC(dc, VPoint, DC_RENDER_ONLY, 
false);
 
 2051  pDIB->SelectIntoDC(dc);
 
 2058bool s57chart::DoRenderViewOnDC(wxMemoryDC &dc, 
const ViewPort &VPoint,
 
 2059                                RenderTypeEnum option, 
bool force_new_view) {
 
 2060  bool bnewview = 
false;
 
 2062  bool bNewVP = 
false;
 
 2064  bool bReallyNew = 
false;
 
 2066  double easting_ul, northing_ul;
 
 2067  double easting_lr, northing_lr;
 
 2068  double prev_easting_ul = 0., prev_northing_ul = 0.;
 
 2070  if (ps52plib->GetPLIBColorScheme() != m_lastColorScheme) bReallyNew = 
true;
 
 2071  m_lastColorScheme = ps52plib->GetPLIBColorScheme();
 
 2080  if (m_last_vprect != dest) bReallyNew = 
true;
 
 2081  m_last_vprect = dest;
 
 2083  if (m_plib_state_hash != ps52plib->GetStateHash()) {
 
 2085    m_plib_state_hash = ps52plib->GetStateHash();
 
 2096  if (m_last_vp.IsValid()) {
 
 2098        m_easting_vp_center - ((VPoint.
pix_width / 2) / m_view_scale_ppm);
 
 2100        m_northing_vp_center + ((VPoint.
pix_height / 2) / m_view_scale_ppm);
 
 2101    easting_lr = easting_ul + (VPoint.
pix_width / m_view_scale_ppm);
 
 2102    northing_lr = northing_ul - (VPoint.
pix_height / m_view_scale_ppm);
 
 2104    double last_easting_vp_center, last_northing_vp_center;
 
 2105    toSM(m_last_vp.
clat, m_last_vp.
clon, ref_lat, ref_lon,
 
 2106         &last_easting_vp_center, &last_northing_vp_center);
 
 2109        last_easting_vp_center - ((m_last_vp.
pix_width / 2) / m_view_scale_ppm);
 
 2110    prev_northing_ul = last_northing_vp_center +
 
 2111                       ((m_last_vp.
pix_height / 2) / m_view_scale_ppm);
 
 2113    double dx = (easting_ul - prev_easting_ul) * m_view_scale_ppm;
 
 2114    double dy = (prev_northing_ul - northing_ul) * m_view_scale_ppm;
 
 2116    rul.x = (int)round((easting_ul - prev_easting_ul) * m_view_scale_ppm);
 
 2117    rul.y = (int)round((prev_northing_ul - northing_ul) * m_view_scale_ppm);
 
 2119    rlr.x = (int)round((easting_lr - prev_easting_ul) * m_view_scale_ppm);
 
 2120    rlr.y = (int)round((prev_northing_ul - northing_lr) * m_view_scale_ppm);
 
 2122    if ((fabs(dx - wxRound(dx)) > 1e-5) || (fabs(dy - wxRound(dy)) > 1e-5)) {
 
 2125            "s57chart::DoRender  Cache miss on non-integer pixel delta %g %g\n",
 
 2134    else if ((rul.x != 0) || (rul.y != 0)) {
 
 2135      if (g_bDebugS57) printf(
"newvp due to rul\n");
 
 2146  if (force_new_view) bNewVP = 
true;
 
 2151  OCPNRegion rgn_new(rul.x, rul.y, rlr.x - rul.x, rlr.y - rul.y);
 
 2152  rgn_last.Intersect(rgn_new);  
 
 2154  if (bNewVP && (NULL != pDIB) && !rgn_last.IsEmpty()) {
 
 2156    rgn_last.GetBox(xu, yu, wu, hu);
 
 2173    pDIB->SelectIntoDC(dc_last);
 
 2178    pDIBNew->SelectIntoDC(dc_new);
 
 2182    dc_new.Blit(desx, desy, wu, hu, (wxDC *)&dc_last, srcx, srcy, wxCOPY);
 
 2187    ps52plib->AdjustTextList(desx - srcx, desy - srcy, VPoint.
pix_width,
 
 2190    dc_new.SelectObject(wxNullBitmap);
 
 2191    dc_last.SelectObject(wxNullBitmap);
 
 2199    pDIB->SelectIntoDC(dc);
 
 2203    rgn_delta.Subtract(rgn_reused);
 
 2206    while (upd.HaveRects()) {
 
 2207      wxRect rect = upd.GetRect();
 
 2212      double temp_lon_left, temp_lat_bot, temp_lon_right, temp_lat_top;
 
 2214      double temp_northing_ul = prev_northing_ul - (rul.y / m_view_scale_ppm) -
 
 2215                                (rect.y / m_view_scale_ppm);
 
 2216      double temp_easting_ul = prev_easting_ul + (rul.x / m_view_scale_ppm) +
 
 2217                               (rect.x / m_view_scale_ppm);
 
 2218      fromSM(temp_easting_ul, temp_northing_ul, ref_lat, ref_lon, &temp_lat_top,
 
 2221      double temp_northing_lr =
 
 2222          temp_northing_ul - (rect.height / m_view_scale_ppm);
 
 2223      double temp_easting_lr =
 
 2224          temp_easting_ul + (rect.width / m_view_scale_ppm);
 
 2225      fromSM(temp_easting_lr, temp_northing_lr, ref_lat, ref_lon, &temp_lat_bot,
 
 2228      temp_vp.GetBBox().Set(temp_lat_bot, temp_lon_left, temp_lat_top,
 
 2233      double margin = wxMin(temp_vp.GetBBox().GetLonRange(),
 
 2234                            temp_vp.GetBBox().GetLatRange()) *
 
 2236      temp_vp.GetBBox().EnLarge(margin);
 
 2242      DCRenderRect(dc, temp_vp, &rect);
 
 2247    dc.SelectObject(wxNullBitmap);
 
 2256  else if (bNewVP || (NULL == pDIB)) {
 
 2262    pDIB->SelectIntoDC(dc);
 
 2265    ps52plib->ClearTextList();
 
 2267    DCRenderRect(dc, VPoint, &full_rect);
 
 2269    dc.SelectObject(wxNullBitmap);
 
 2280int s57chart::DCRenderRect(wxMemoryDC &dcinput, 
const ViewPort &vp,
 
 2293  render_canvas_parms pb_spec;
 
 2295  pb_spec.depth = BPP;
 
 2296  pb_spec.pb_pitch = ((rect->width * pb_spec.depth / 8));
 
 2297  pb_spec.lclip = rect->x;
 
 2298  pb_spec.rclip = rect->x + rect->width - 1;
 
 2299  pb_spec.pix_buff = (
unsigned char *)malloc(rect->height * pb_spec.pb_pitch);
 
 2300  pb_spec.width = rect->width;
 
 2301  pb_spec.height = rect->height;
 
 2302  pb_spec.x = rect->x;
 
 2303  pb_spec.y = rect->y;
 
 2305#ifdef ocpnUSE_ocpnBitmap 
 2306  pb_spec.b_revrgb = 
true;
 
 2308  pb_spec.b_revrgb = 
false;
 
 2312  wxColour color = GetGlobalColor(_T ( 
"NODTA" ));
 
 2313  unsigned char r, g, b;
 
 2321  if (pb_spec.depth == 24) {
 
 2322    for (
int i = 0; i < pb_spec.height; i++) {
 
 2323      unsigned char *p = pb_spec.pix_buff + (i * pb_spec.pb_pitch);
 
 2324      for (
int j = 0; j < pb_spec.width; j++) {
 
 2331    int color_int = ((r) << 16) + ((g) << 8) + (b);
 
 2333    for (
int i = 0; i < pb_spec.height; i++) {
 
 2334      int *p = (
int *)(pb_spec.pix_buff + (i * pb_spec.pb_pitch));
 
 2335      for (
int j = 0; j < pb_spec.width; j++) {
 
 2342  for (i = 0; i < PRIO_NUM; ++i) {
 
 2343    if (ps52plib->m_nBoundaryStyle == SYMBOLIZED_BOUNDARIES)
 
 2344      top = razRules[i][4];  
 
 2346      top = razRules[i][3];  
 
 2348    while (top != NULL) {
 
 2351      crnt->sm_transform_parms = &vp_transform;
 
 2352      ps52plib->RenderAreaToDC(&dcinput, crnt, &pb_spec);
 
 2357#ifdef ocpnUSE_ocpnBitmap 
 2358  ocpnBitmap *pREN = 
new ocpnBitmap(pb_spec.pix_buff, pb_spec.width,
 
 2359                                    pb_spec.height, pb_spec.depth);
 
 2361  wxImage *prender_image = 
new wxImage(pb_spec.width, pb_spec.height, 
false);
 
 2362  prender_image->SetData((
unsigned char *)pb_spec.pix_buff);
 
 2363  wxBitmap *pREN = 
new wxBitmap(*prender_image);
 
 2369  dc_ren.SelectObject(*pREN);
 
 2372  dcinput.Blit(pb_spec.x, pb_spec.y, pb_spec.width, pb_spec.height,
 
 2373               (wxDC *)&dc_ren, 0, 0);
 
 2376  dc_ren.SelectObject(wxNullBitmap);
 
 2378#ifdef ocpnUSE_ocpnBitmap 
 2379  free(pb_spec.pix_buff);
 
 2381  delete prender_image;  
 
 2388  DCRenderLPB(dcinput, vp, rect);
 
 2393bool s57chart::DCRenderLPB(wxMemoryDC &dcinput, 
const ViewPort &vp,
 
 2400  for (i = 0; i < PRIO_NUM; ++i) {
 
 2402    wxDCClipper *pdcc = NULL;
 
 2408    if (ps52plib->m_nBoundaryStyle == SYMBOLIZED_BOUNDARIES)
 
 2409      top = razRules[i][4];  
 
 2411      top = razRules[i][3];  
 
 2412    while (top != NULL) {
 
 2415      crnt->sm_transform_parms = &vp_transform;
 
 2416      ps52plib->RenderObjectToDC(&dcinput, crnt);
 
 2419    top = razRules[i][2];  
 
 2420    while (top != NULL) {
 
 2423      crnt->sm_transform_parms = &vp_transform;
 
 2424      ps52plib->RenderObjectToDC(&dcinput, crnt);
 
 2427    if (ps52plib->m_nSymbolStyle == SIMPLIFIED)
 
 2428      top = razRules[i][0];  
 
 2430      top = razRules[i][1];  
 
 2432    while (top != NULL) {
 
 2435      crnt->sm_transform_parms = &vp_transform;
 
 2436      ps52plib->RenderObjectToDC(&dcinput, crnt);
 
 2440    if (pdcc) 
delete pdcc;
 
 2453bool s57chart::DCRenderText(wxMemoryDC &dcinput, 
const ViewPort &vp) {
 
 2459  for (i = 0; i < PRIO_NUM; ++i) {
 
 2460    if (ps52plib->m_nBoundaryStyle == SYMBOLIZED_BOUNDARIES)
 
 2461      top = razRules[i][4];  
 
 2463      top = razRules[i][3];  
 
 2465    while (top != NULL) {
 
 2468      crnt->sm_transform_parms = &vp_transform;
 
 2469      ps52plib->RenderObjectToDCText(&dcinput, crnt);
 
 2472    top = razRules[i][2];  
 
 2473    while (top != NULL) {
 
 2476      crnt->sm_transform_parms = &vp_transform;
 
 2477      ps52plib->RenderObjectToDCText(&dcinput, crnt);
 
 2480    if (ps52plib->m_nSymbolStyle == SIMPLIFIED)
 
 2481      top = razRules[i][0];  
 
 2483      top = razRules[i][1];  
 
 2485    while (top != NULL) {
 
 2488      crnt->sm_transform_parms = &vp_transform;
 
 2489      ps52plib->RenderObjectToDCText(&dcinput, crnt);
 
 2496bool s57chart::IsCellOverlayType(
const wxString &FullPath) {
 
 2497  wxFileName fn(FullPath);
 
 2499  wxString cname = fn.GetName();
 
 2500  if (cname.Length() >= 3)
 
 2501    return ((cname[2] == 
'L') || (cname[2] == 
'A'));
 
 2506InitReturn s57chart::Init(
const wxString &name, ChartInitFlag flags) {
 
 2509  if ((NULL == ps52plib) || !(ps52plib->m_bOK)) 
return INIT_FAIL_REMOVE;
 
 2512  if (name.Upper().EndsWith(
".XZ")) {
 
 2513    ext = wxFileName(name.Left(name.Length() - 3)).GetExt();
 
 2516    m_TempFilePath = wxFileName::GetTempDir() + wxFileName::GetPathSeparator() +
 
 2517                     wxFileName(name).GetName();
 
 2519    if (!wxFileExists(m_TempFilePath) &&
 
 2520        !DecompressXZFile(name, m_TempFilePath)) {
 
 2521      wxRemoveFile(m_TempFilePath);
 
 2522      return INIT_FAIL_REMOVE;
 
 2525    m_TempFilePath = name;
 
 2526    ext = wxFileName(name).GetExt();
 
 2531  firebase::crashlytics::SetCustomKey(
"s57chartInit",
 
 2532                                      name.ToStdString().c_str());
 
 2539    return INIT_FAIL_NOERROR;
 
 2544  InitReturn ret_value = INIT_OK;
 
 2546  m_Description = name;
 
 2548  wxFileName fn(m_TempFilePath);
 
 2551  wxString cname = fn.GetName();
 
 2552  m_usage_char = cname[2];
 
 2555  ref_lat = (m_FullExtent.NLAT + m_FullExtent.SLAT) / 2.;
 
 2556  ref_lon = (m_FullExtent.WLON + m_FullExtent.ELON) / 2.;
 
 2558  if (flags == THUMB_ONLY) {
 
 2566  if (flags == HEADER_ONLY) {
 
 2567    if (ext == _T(
"000")) {
 
 2568      if (!GetBaseFileAttr(fn.GetFullPath()))
 
 2569        ret_value = INIT_FAIL_REMOVE;
 
 2571        if (!CreateHeaderDataFromENC())
 
 2572          ret_value = INIT_FAIL_REMOVE;
 
 2574          ret_value = INIT_OK;
 
 2576    } 
else if (ext == _T(
"S57")) {
 
 2577      m_SENCFileName = m_TempFilePath;
 
 2578      if (!CreateHeaderDataFromSENC())
 
 2579        ret_value = INIT_FAIL_REMOVE;
 
 2581        ret_value = INIT_OK;
 
 2590  if (!m_bbase_file_attr_known) {
 
 2591    if (!GetBaseFileAttr(m_TempFilePath))
 
 2592      ret_value = INIT_FAIL_REMOVE;
 
 2594      m_bbase_file_attr_known = 
true;
 
 2597  if (ext == _T(
"000")) {
 
 2598    if (m_bbase_file_attr_known) {
 
 2599      int sret = FindOrCreateSenc(m_FullPath);
 
 2600      if (sret == BUILD_SENC_PENDING) {
 
 2605      if (sret != BUILD_SENC_OK) {
 
 2606        if (sret == BUILD_SENC_NOK_RETRY)
 
 2607          ret_value = INIT_FAIL_RETRY;
 
 2609          ret_value = INIT_FAIL_REMOVE;
 
 2611        ret_value = PostInit(flags, m_global_color_scheme);
 
 2616  else if (ext == _T(
"S57")) {
 
 2617    m_SENCFileName = m_TempFilePath;
 
 2618    ret_value = PostInit(flags, m_global_color_scheme);
 
 2625wxString s57chart::buildSENCName(
const wxString &name) {
 
 2626  wxFileName fn(name);
 
 2627  fn.SetExt(_T(
"S57"));
 
 2628  wxString file_name = fn.GetFullName();
 
 2631  wxString SENCdir = g_SENCPrefix;
 
 2633  if (SENCdir.Last() != wxFileName::GetPathSeparator())
 
 2634    SENCdir.Append(wxFileName::GetPathSeparator());
 
 2637  wxString source_dir = fn.GetPath(wxPATH_GET_SEPARATOR);
 
 2638  wxCharBuffer buf = source_dir.ToUTF8();
 
 2639  unsigned char sha1_out[20];
 
 2640  sha1((
unsigned char *)buf.data(), strlen(buf.data()), sha1_out);
 
 2643  for (
unsigned int i = 0; i < 6; i++) {
 
 2645    s.Printf(_T(
"%02X"), sha1_out[i]);
 
 2649  file_name.Prepend(sha1);
 
 2652  wxFileName tsfn(SENCdir);
 
 2653  tsfn.SetFullName(file_name);
 
 2655  return tsfn.GetFullPath();
 
 2662int s57chart::FindOrCreateSenc(
const wxString &name, 
bool b_progress) {
 
 2666  if (name.Upper().EndsWith(
".XZ")) {
 
 2667    ext = wxFileName(name.Left(name.Length() - 3)).GetExt();
 
 2670    m_TempFilePath = wxFileName::GetTempDir() + wxFileName::GetPathSeparator() +
 
 2671                     wxFileName(name).GetName();
 
 2673    if (!wxFileExists(m_TempFilePath) &&
 
 2674        !DecompressXZFile(name, m_TempFilePath)) {
 
 2675      wxRemoveFile(m_TempFilePath);
 
 2676      return INIT_FAIL_REMOVE;
 
 2679    m_TempFilePath = name;
 
 2680    ext = wxFileName(name).GetExt();
 
 2684  if (!m_bbase_file_attr_known) {
 
 2685    if (!GetBaseFileAttr(m_TempFilePath))
 
 2686      return INIT_FAIL_REMOVE;
 
 2688      m_bbase_file_attr_known = 
true;
 
 2692  m_SENCFileName = buildSENCName(name);
 
 2694  int build_ret_val = 1;
 
 2696  bool bbuild_new_senc = 
false;
 
 2697  m_bneed_new_thumbnail = 
false;
 
 2699  wxFileName FileName000(m_TempFilePath);
 
 2703  wxString msg(_T(
"S57chart::Checking SENC file: "));
 
 2704  msg.Append(m_SENCFileName);
 
 2708    int force_make_senc = 0;
 
 2710    if (::wxFileExists(m_SENCFileName)) {  
 
 2713      if (senc.ingestHeader(m_SENCFileName)) {
 
 2714        bbuild_new_senc = 
true;
 
 2715        wxLogMessage(_T(
"    Rebuilding SENC due to ingestHeader failure."));
 
 2717        int senc_file_version = senc.getSencReadVersion();
 
 2719        int last_update = senc.getSENCReadLastUpdate();
 
 2721        wxString str = senc.getSENCFileCreateDate();
 
 2722        wxDateTime SENCCreateDate;
 
 2723        SENCCreateDate.ParseFormat(str, _T(
"%Y%m%d"));
 
 2725        if (SENCCreateDate.IsValid())
 
 2726          SENCCreateDate.ResetTime();  
 
 2731        wxString senc_base_edtn = senc.getSENCReadBaseEdition();
 
 2733        senc_base_edtn.ToLong(&isenc_edition);
 
 2735        m_edtn000.ToLong(&ifile_edition);
 
 2740        if (senc_file_version != CURRENT_SENC_FORMAT_VERSION) {
 
 2741          bbuild_new_senc = 
true;
 
 2742          wxLogMessage(_T(
"    Rebuilding SENC due to SENC format update."));
 
 2749        else if (ifile_edition > isenc_edition) {
 
 2750          bbuild_new_senc = 
true;
 
 2751          wxLogMessage(_T(
"    Rebuilding SENC due to cell edition update."));
 
 2753          msg = _T(
"    Last edition recorded in SENC: ");
 
 2754          msg += senc_base_edtn;
 
 2755          msg += _T(
"  most recent edition cell file: ");
 
 2760          int most_recent_update_file =
 
 2761              GetUpdateFileArray(FileName000, NULL, m_date000, m_edtn000);
 
 2763          if (ifile_edition == isenc_edition) {
 
 2764            if (most_recent_update_file > last_update) {
 
 2765              bbuild_new_senc = 
true;
 
 2767                  _T(
"    Rebuilding SENC due to incremental cell update."));
 
 2770                  _T(
"    Last update recorded in SENC: %d   most recent ")
 
 2771                  _T(
"update file: %d"),
 
 2772                  last_update, most_recent_update_file);
 
 2780          wxDateTime OModTime000;
 
 2781          FileName000.GetTimes(NULL, &OModTime000, NULL);
 
 2782          OModTime000.ResetTime();  
 
 2783          if (SENCCreateDate.IsValid()) {
 
 2784            if (OModTime000.IsLaterThan(SENCCreateDate)) {
 
 2786                  _T(
"    Rebuilding SENC due to Senc vs cell file time ")
 
 2788              bbuild_new_senc = 
true;
 
 2791            bbuild_new_senc = 
true;
 
 2793                _T(
"    Rebuilding SENC due to SENC create time invalid."));
 
 2804        if (force_make_senc) bbuild_new_senc = 
true;
 
 2806    } 
else if (!::wxFileExists(m_SENCFileName))  
 
 2808      wxLogMessage(_T(
"    Rebuilding SENC due to missing SENC file."));
 
 2809      bbuild_new_senc = 
true;
 
 2813  if (bbuild_new_senc) {
 
 2814    m_bneed_new_thumbnail =
 
 2816    build_ret_val = BuildSENCFile(m_TempFilePath, m_SENCFileName, b_progress);
 
 2818    if (BUILD_SENC_PENDING == build_ret_val) 
return BUILD_SENC_PENDING;
 
 2819    if (BUILD_SENC_NOK_PERMANENT == build_ret_val) 
return INIT_FAIL_REMOVE;
 
 2820    if (BUILD_SENC_NOK_RETRY == build_ret_val) 
return INIT_FAIL_RETRY;
 
 2826InitReturn s57chart::PostInit(ChartInitFlag flags, ColorScheme cs) {
 
 2828  if (0 != BuildRAZFromSENCFile(m_SENCFileName)) {
 
 2829    wxString msg(_T(
"   Cannot load SENC file "));
 
 2830    msg.Append(m_SENCFileName);
 
 2833    return INIT_FAIL_RETRY;
 
 2839  wxString SENCdir = g_SENCPrefix;
 
 2840  if (SENCdir.Last() != wxFileName::GetPathSeparator())
 
 2841    SENCdir.Append(wxFileName::GetPathSeparator());
 
 2843  wxFileName s57File(m_SENCFileName);
 
 2844  wxFileName ThumbFileName(SENCdir, s57File.GetName().Mid(13), _T(
"BMP"));
 
 2846  if (!ThumbFileName.FileExists() || m_bneed_new_thumbnail) {
 
 2847    BuildThumbnail(ThumbFileName.GetFullPath());
 
 2850    if (ThumbFileName.FileExists()) {
 
 2852#ifdef ocpnUSE_ocpnBitmap 
 2853      pBMP_NEW = 
new ocpnBitmap;
 
 2855      pBMP_NEW = 
new wxBitmap;
 
 2857      if (pBMP_NEW->LoadFile(ThumbFileName.GetFullPath(), wxBITMAP_TYPE_BMP)) {
 
 2860        m_pDIBThumbDay = pBMP_NEW;
 
 2868  m_global_color_scheme = cs;
 
 2869  SetColorScheme(cs, 
false);
 
 2872  BuildDepthContourArray();
 
 2874  CreateChartContext();
 
 2875  PopulateObjectsWithContext();
 
 2878  bReadyToRender = 
true;
 
 2883void s57chart::ClearDepthContourArray(
void) {
 
 2884  if (m_nvaldco_alloc) {
 
 2885    free(m_pvaldco_array);
 
 2887  m_nvaldco_alloc = 5;
 
 2889  m_pvaldco_array = (
double *)calloc(m_nvaldco_alloc, 
sizeof(
double));
 
 2892void s57chart::BuildDepthContourArray(
void) {
 
 2895  if (0 == m_nvaldco_alloc) {
 
 2896    m_nvaldco_alloc = 5;
 
 2897    m_pvaldco_array = (
double *)calloc(m_nvaldco_alloc, 
sizeof(
double));
 
 2903  double prev_valdco = 0.0;
 
 2905  for (
int i = 0; i < PRIO_NUM; ++i) {
 
 2906    for (
int j = 0; j < LUPNAME_NUM; j++) {
 
 2907      top = razRules[i][j];
 
 2908      while (top != NULL) {
 
 2909        if (!strncmp(top->obj->FeatureName, 
"DEPCNT", 6)) {
 
 2910          double valdco = 0.0;
 
 2911          if (GetDoubleAttr(top->obj, 
"VALDCO", valdco)) {
 
 2912            if (valdco != prev_valdco) {
 
 2913              prev_valdco = valdco;
 
 2915              if (m_nvaldco > m_nvaldco_alloc) {
 
 2916                void *tr = realloc((
void *)m_pvaldco_array,
 
 2917                                   m_nvaldco_alloc * 2 * 
sizeof(
double));
 
 2918                m_pvaldco_array = (
double *)tr;
 
 2919                m_nvaldco_alloc *= 2;
 
 2921              m_pvaldco_array[m_nvaldco - 1] = valdco;
 
 2925        ObjRazRules *nxx = top->next;
 
 2930  std::sort(m_pvaldco_array, m_pvaldco_array + m_nvaldco);
 
 2934void s57chart::SetSafetyContour(
void) {
 
 2942  double mar_safety_contour = S52_getMarinerParam(S52_MAR_SAFETY_CONTOUR);
 
 2945  if (NULL != m_pvaldco_array) {
 
 2946    for (i = 0; i < m_nvaldco; i++) {
 
 2947      if (m_pvaldco_array[i] >= mar_safety_contour) 
break;
 
 2951      m_next_safe_cnt = m_pvaldco_array[i];
 
 2953      m_next_safe_cnt = (double)1e6;
 
 2955    m_next_safe_cnt = (double)1e6;
 
 2960  if (m_next_safe_cnt > S52_getMarinerParam(S52_MAR_DEEP_CONTOUR))
 
 2961    m_next_safe_cnt = (
double)1e6;
 
 2964void s57chart::CreateChartContext() {
 
 2966  m_this_chart_context = (chart_context *)calloc(
sizeof(chart_context), 1);
 
 2969void s57chart::PopulateObjectsWithContext() {
 
 2970  m_this_chart_context->chart = 
this;
 
 2971  m_this_chart_context->chart_type = GetChartType();
 
 2972  m_this_chart_context->vertex_buffer = GetLineVertexBuffer();
 
 2973  m_this_chart_context->chart_scale = GetNativeScale();
 
 2974  m_this_chart_context->pFloatingATONArray = pFloatingATONArray;
 
 2975  m_this_chart_context->pRigidATONArray = pRigidATONArray;
 
 2976  m_this_chart_context->safety_contour = m_next_safe_cnt;
 
 2977  m_this_chart_context->pt2GetAssociatedObjects =
 
 2978      &s57chart::GetAssociatedObjects;
 
 2982  for (
int i = 0; i < PRIO_NUM; ++i) {
 
 2983    for (
int j = 0; j < LUPNAME_NUM; j++) {
 
 2984      top = razRules[i][j];
 
 2985      while (top != NULL) {
 
 2986        S57Obj *obj = top->obj;
 
 2987        obj->m_chart_context = m_this_chart_context;
 
 2994void s57chart::InvalidateCache() {
 
 2999bool s57chart::BuildThumbnail(
const wxString &bmpname) {
 
 3002  wxFileName ThumbFileName(bmpname);
 
 3005  if (
true != ThumbFileName.DirExists(ThumbFileName.GetPath())) {
 
 3006    if (!ThumbFileName.Mkdir(ThumbFileName.GetPath())) {
 
 3007      wxLogMessage(_T(
"   Cannot create BMP file directory for ") +
 
 3008                   ThumbFileName.GetFullPath());
 
 3016  vp.
clon = (m_FullExtent.ELON + m_FullExtent.WLON) / 2.;
 
 3017  vp.
clat = (m_FullExtent.NLAT + m_FullExtent.SLAT) / 2.;
 
 3019  float ext_max = fmax((m_FullExtent.NLAT - m_FullExtent.SLAT),
 
 3020                       (m_FullExtent.ELON - m_FullExtent.WLON));
 
 3027  vp.m_projection_type = PROJECTION_MERCATOR;
 
 3029  vp.GetBBox().Set(m_FullExtent.SLAT, m_FullExtent.WLON, m_FullExtent.NLAT,
 
 3046  unsigned int OBJLCount = ps52plib->pOBJLArray->GetCount();
 
 3048  int *psave_viz = (
int *)malloc(OBJLCount * 
sizeof(
int));
 
 3050  int *psvr = psave_viz;
 
 3054  for (iPtr = 0; iPtr < OBJLCount; iPtr++) {
 
 3055    pOLE = (OBJLElement *)(ps52plib->pOBJLArray->Item(iPtr));
 
 3056    *psvr++ = pOLE->nViz;
 
 3061  bool bsavem_bShowSoundgp = ps52plib->m_bShowSoundg;
 
 3062  bool bsave_text = ps52plib->m_bShowS57Text;
 
 3065  ps52plib->SaveObjNoshow();
 
 3068  for (iPtr = 0; iPtr < OBJLCount; iPtr++) {
 
 3069    pOLE = (OBJLElement *)(ps52plib->pOBJLArray->Item(iPtr));
 
 3070    if (!strncmp(pOLE->OBJLName, 
"LNDARE", 6)) pOLE->nViz = 1;
 
 3071    if (!strncmp(pOLE->OBJLName, 
"DEPARE", 6)) pOLE->nViz = 1;
 
 3074  ps52plib->m_bShowSoundg = 
false;
 
 3075  ps52plib->m_bShowS57Text = 
false;
 
 3078  DisCat dsave = ps52plib->GetDisplayCategory();
 
 3079  ps52plib->SetDisplayCategory(MARINERS_STANDARD);
 
 3081  ps52plib->AddObjNoshow(
"BRIDGE");
 
 3082  ps52plib->AddObjNoshow(
"GATCON");
 
 3084  double safety_depth = S52_getMarinerParam(S52_MAR_SAFETY_DEPTH);
 
 3085  S52_setMarinerParam(S52_MAR_SAFETY_DEPTH, -100);
 
 3086  double safety_contour = S52_getMarinerParam(S52_MAR_SAFETY_CONTOUR);
 
 3087  S52_setMarinerParam(S52_MAR_SAFETY_CONTOUR, -100);
 
 3089#ifdef ocpnUSE_DIBSECTION 
 3092  wxMemoryDC memdc, dc_org;
 
 3096  ps52plib->SaveColorScheme();
 
 3097  ps52plib->SetPLIBColorScheme(
"DAY", ChartCtxFactory());
 
 3099  DoRenderViewOnDC(memdc, vp, DC_RENDER_ONLY, 
true);
 
 3102  memdc.SelectObject(wxNullBitmap);
 
 3106  for (iPtr = 0; iPtr < OBJLCount; iPtr++) {
 
 3107    pOLE = (OBJLElement *)(ps52plib->pOBJLArray->Item(iPtr));
 
 3108    pOLE->nViz = *psvr++;
 
 3111  ps52plib->SetDisplayCategory(dsave);
 
 3112  ps52plib->RestoreObjNoshow();
 
 3114  ps52plib->RemoveObjNoshow(
"BRIDGE");
 
 3115  ps52plib->RemoveObjNoshow(
"GATCON");
 
 3117  ps52plib->m_bShowSoundg = bsavem_bShowSoundgp;
 
 3118  ps52plib->m_bShowS57Text = bsave_text;
 
 3120  S52_setMarinerParam(S52_MAR_SAFETY_DEPTH, safety_depth);
 
 3121  S52_setMarinerParam(S52_MAR_SAFETY_CONTOUR, safety_contour);
 
 3124  ps52plib->RestoreColorScheme();
 
 3134  wxMemoryDC dc_clone;
 
 3135  dc_clone.SelectObject(*pBMP);
 
 3137  pDIB->SelectIntoDC(dc_org);
 
 3141  dc_clone.SelectObject(wxNullBitmap);
 
 3142  dc_org.SelectObject(wxNullBitmap);
 
 3145  ret_code = pBMP->SaveFile(ThumbFileName.GetFullPath(), wxBITMAP_TYPE_BMP);
 
 3152#include <wx/arrimpl.cpp> 
 3153WX_DEFINE_ARRAY_PTR(
float *, MyFloatPtrArray);
 
 3156bool s57chart::CreateHeaderDataFromENC(
void) {
 
 3157  if (!InitENCMinimal(m_TempFilePath)) {
 
 3158    wxString msg(_T(
"   Cannot initialize ENC file "));
 
 3159    msg.Append(m_TempFilePath);
 
 3167  float LatMax, LatMin, LonMax, LonMin;
 
 3173  m_pCOVRTablePoints = NULL;
 
 3174  m_pCOVRTable = NULL;
 
 3177  MyFloatPtrArray *pAuxPtrArray = 
new MyFloatPtrArray;
 
 3178  std::vector<int> auxCntArray, noCovrCntArray;
 
 3180  MyFloatPtrArray *pNoCovrPtrArray = 
new MyFloatPtrArray;
 
 3183  pFeat = GetChartFirstM_COVR(catcov);
 
 3188    OGRPolygon *poly = (OGRPolygon *)(pFeat->GetGeometryRef());
 
 3189    OGRLinearRing *xring = poly->getExteriorRing();
 
 3191    int npt = xring->getNumPoints();
 
 3202      for (
int i = 0; i < npt; i++) {
 
 3203        xring->getPoint(i, &p);
 
 3207              fmax(last_p.getX(), p.getX()) - fmin(last_p.getX(), p.getX());
 
 3209              fmax(last_p.getY(), p.getY()) - fmin(last_p.getY(), p.getY());
 
 3210          if (xdelta < 0.001 &&
 
 3218        pf = (
float *)realloc(pf, 2 * usedpts * 
sizeof(
float));
 
 3219        pfr = &pf[2 * (usedpts - 1)];
 
 3222          LatMax = fmax(LatMax, p.getY());
 
 3223          LatMin = fmin(LatMin, p.getY());
 
 3224          LonMax = fmax(LonMax, p.getX());
 
 3225          LonMin = fmin(LonMin, p.getX());
 
 3233        pAuxPtrArray->Add(pf);
 
 3234        auxCntArray.push_back(usedpts);
 
 3235      } 
else if (catcov == 2) {
 
 3236        pNoCovrPtrArray->Add(pf);
 
 3237        noCovrCntArray.push_back(usedpts);
 
 3242    pFeat = GetChartNextM_COVR(catcov);
 
 3243    DEBUG_LOG << 
"used " << usedpts << 
" points";
 
 3248  m_nCOVREntries = auxCntArray.size();
 
 3252  if (m_nCOVREntries >= 1) {
 
 3253    m_pCOVRTablePoints = (
int *)malloc(m_nCOVREntries * 
sizeof(
int));
 
 3254    m_pCOVRTable = (
float **)malloc(m_nCOVREntries * 
sizeof(
float *));
 
 3256    for (
unsigned int j = 0; j < (
unsigned int)m_nCOVREntries; j++) {
 
 3257      m_pCOVRTablePoints[j] = auxCntArray[j];
 
 3258      m_pCOVRTable[j] = pAuxPtrArray->Item(j);
 
 3264    wxString msg(_T(
"   ENC contains no useable M_COVR, CATCOV=1 features:  "));
 
 3265    msg.Append(m_TempFilePath);
 
 3270  m_nNoCOVREntries = noCovrCntArray.size();
 
 3272  if (m_nNoCOVREntries) {
 
 3274    m_pNoCOVRTablePoints = (
int *)malloc(m_nNoCOVREntries * 
sizeof(
int));
 
 3275    m_pNoCOVRTable = (
float **)malloc(m_nNoCOVREntries * 
sizeof(
float *));
 
 3277    for (
unsigned int j = 0; j < (
unsigned int)m_nNoCOVREntries; j++) {
 
 3278      m_pNoCOVRTablePoints[j] = noCovrCntArray[j];
 
 3279      m_pNoCOVRTable[j] = pNoCovrPtrArray->Item(j);
 
 3282    m_pNoCOVRTablePoints = NULL;
 
 3283    m_pNoCOVRTable = NULL;
 
 3286  delete pAuxPtrArray;
 
 3287  delete pNoCovrPtrArray;
 
 3289  if (0 == m_nCOVREntries) {  
 
 3290    wxString msg(_T(
"   ENC contains no M_COVR features:  "));
 
 3291    msg.Append(m_TempFilePath);
 
 3294    msg = _T(
"   Calculating Chart Extents as fallback.");
 
 3300    S57Reader *pENCReader = m_pENCDS->GetModule(0);
 
 3302    if (pENCReader->GetExtent(&Env, 
true) == OGRERR_NONE) {
 
 3309      m_pCOVRTablePoints = (
int *)malloc(
sizeof(
int));
 
 3310      *m_pCOVRTablePoints = 4;
 
 3311      m_pCOVRTable = (
float **)malloc(
sizeof(
float *));
 
 3312      float *pf = (
float *)malloc(2 * 4 * 
sizeof(
float));
 
 3329      wxString msg(_T(
"   Cannot calculate Extents for ENC:  "));
 
 3330      msg.Append(m_TempFilePath);
 
 3338  m_FullExtent.NLAT = LatMax;
 
 3339  m_FullExtent.SLAT = LatMin;
 
 3340  m_FullExtent.ELON = LonMax;
 
 3341  m_FullExtent.WLON = LonMin;
 
 3342  m_bExtentSet = 
true;
 
 3345  m_Chart_Scale = GetENCScale();
 
 3348  GetChartNameFromTXT(m_TempFilePath, nice_name);
 
 3356bool s57chart::CreateHeaderDataFromoSENC(
void) {
 
 3357  bool ret_val = 
true;
 
 3359  wxFFileInputStream fpx(m_SENCFileName);
 
 3361    if (!::wxFileExists(m_SENCFileName)) {
 
 3362      wxString msg(_T(
"   Cannot open SENC file "));
 
 3363      msg.Append(m_SENCFileName);
 
 3370  if (senc.ingestHeader(m_SENCFileName)) {
 
 3376    m_Chart_Scale = senc.getSENCReadScale();
 
 3379    m_Name = senc.getReadName();
 
 3382    m_ID = senc.getReadID();
 
 3385    Extent &ext = senc.getReadExtent();
 
 3387    m_FullExtent.ELON = ext.ELON;
 
 3388    m_FullExtent.WLON = ext.WLON;
 
 3389    m_FullExtent.NLAT = ext.NLAT;
 
 3390    m_FullExtent.SLAT = ext.SLAT;
 
 3391    m_bExtentSet = 
true;
 
 3394    SENCFloatPtrArray &AuxPtrArray = senc.getSENCReadAuxPointArray();
 
 3395    std::vector<int> &AuxCntArray = senc.getSENCReadAuxPointCountArray();
 
 3397    m_nCOVREntries = AuxCntArray.size();
 
 3399    m_pCOVRTablePoints = (
int *)malloc(m_nCOVREntries * 
sizeof(
int));
 
 3400    m_pCOVRTable = (
float **)malloc(m_nCOVREntries * 
sizeof(
float *));
 
 3402    for (
unsigned int j = 0; j < (
unsigned int)m_nCOVREntries; j++) {
 
 3403      m_pCOVRTablePoints[j] = AuxCntArray[j];
 
 3404      m_pCOVRTable[j] = (
float *)malloc(AuxCntArray[j] * 2 * 
sizeof(
float));
 
 3405      memcpy(m_pCOVRTable[j], AuxPtrArray[j],
 
 3406             AuxCntArray[j] * 2 * 
sizeof(
float));
 
 3410    SENCFloatPtrArray &NoCovrPtrArray = senc.getSENCReadNOCOVRPointArray();
 
 3411    std::vector<int> &NoCovrCntArray = senc.getSENCReadNOCOVRPointCountArray();
 
 3413    m_nNoCOVREntries = NoCovrCntArray.size();
 
 3415    if (m_nNoCOVREntries) {
 
 3417      m_pNoCOVRTablePoints = (
int *)malloc(m_nNoCOVREntries * 
sizeof(
int));
 
 3418      m_pNoCOVRTable = (
float **)malloc(m_nNoCOVREntries * 
sizeof(
float *));
 
 3420      for (
unsigned int j = 0; j < (
unsigned int)m_nNoCOVREntries; j++) {
 
 3421        int npoints = NoCovrCntArray[j];
 
 3422        m_pNoCOVRTablePoints[j] = npoints;
 
 3423        m_pNoCOVRTable[j] = (
float *)malloc(npoints * 2 * 
sizeof(
float));
 
 3424        memcpy(m_pNoCOVRTable[j], NoCovrPtrArray[j],
 
 3425               npoints * 2 * 
sizeof(
float));
 
 3431    m_datum_str = _T(
"WGS84");
 
 3432    m_SoundingsDatum = _T(
"MEAN LOWER LOW WATER");
 
 3434    int senc_file_version = senc.getSencReadVersion();
 
 3436    int last_update = senc.getSENCReadLastUpdate();
 
 3438    wxString str = senc.getSENCFileCreateDate();
 
 3439    wxDateTime SENCCreateDate;
 
 3440    SENCCreateDate.ParseFormat(str, _T(
"%Y%m%d"));
 
 3442    if (SENCCreateDate.IsValid()) SENCCreateDate.ResetTime();  
 
 3444    wxString senc_base_edtn = senc.getSENCReadBaseEdition();
 
 3451bool s57chart::CreateHeaderDataFromSENC(
void) {
 
 3452  if (CURRENT_SENC_FORMAT_VERSION >= 200) 
return CreateHeaderDataFromoSENC();
 
 3460bool s57chart::GetNearestSafeContour(
double safe_cnt, 
double &next_safe_cnt) {
 
 3462  if (NULL != m_pvaldco_array) {
 
 3463    for (i = 0; i < m_nvaldco; i++) {
 
 3464      if (m_pvaldco_array[i] >= safe_cnt) 
break;
 
 3468      next_safe_cnt = m_pvaldco_array[i];
 
 3470      next_safe_cnt = (double)1e6;
 
 3473    next_safe_cnt = (double)1e6;
 
 3487std::list<S57Obj *> *s57chart::GetAssociatedObjects(S57Obj *obj) {
 
 3491  std::list<S57Obj *> *pobj_list = 
new std::list<S57Obj *>();
 
 3494  fromSM((obj->x * obj->x_rate) + obj->x_origin,
 
 3495         (obj->y * obj->y_rate) + obj->y_origin, ref_lat, ref_lon, &lat, &lon);
 
 3498  switch (obj->Primitive_type) {
 
 3511      top = razRules[disPrioIdx][3];  
 
 3512      while (top != NULL) {
 
 3513        if (top->obj->bIsAssociable) {
 
 3514          if (top->obj->BBObj.Contains(lat, lon)) {
 
 3515            if (IsPointInObjArea(lat, lon, 0.0, top->obj)) {
 
 3516              pobj_list->push_back(top->obj);
 
 3523        ObjRazRules *nxx = top->next;
 
 3528        top = razRules[disPrioIdx][4];  
 
 3529        while (top != NULL) {
 
 3530          if (top->obj->bIsAssociable) {
 
 3531            if (top->obj->BBObj.Contains(lat, lon)) {
 
 3532              if (IsPointInObjArea(lat, lon, 0.0, top->obj)) {
 
 3533                pobj_list->push_back(top->obj);
 
 3539          ObjRazRules *nxx = top->next;
 
 3553void s57chart::GetChartNameFromTXT(
const wxString &FullPath, wxString &Name) {
 
 3554  wxFileName fn(FullPath);
 
 3556  wxString target_name = fn.GetName();
 
 3557  target_name.RemoveLast();
 
 3559  wxString dir_name = fn.GetPath();
 
 3561  wxDir dir(dir_name);  
 
 3563  wxArrayString FileList;
 
 3565  dir.GetAllFiles(fn.GetPath(), &FileList);  
 
 3569  bool found_name = 
false;
 
 3573  for (
unsigned int j = 0; j < FileList.GetCount(); j++) {
 
 3574    wxFileName file(FileList[j]);
 
 3575    if (((file.GetExt()).MakeUpper()) == _T(
"TXT")) {
 
 3577      wxTextFile text_file(file.GetFullPath());
 
 3579      bool file_ok = 
true;
 
 3583        if (!text_file.Open()) {
 
 3584          if (!text_file.Open(wxConvISO8859_1)) file_ok = 
false;
 
 3589        wxString str = text_file.GetFirstLine();
 
 3590        while (!text_file.Eof()) {
 
 3591          if (0 == target_name.CmpNoCase(
 
 3592                       str.Mid(0, target_name.Len()))) {  
 
 3593            wxString tname = str.AfterFirst(
'-');
 
 3594            name = tname.AfterFirst(
' ');
 
 3598            str = text_file.GetNextLine();
 
 3602        wxString msg(_T(
"   Error Reading ENC .TXT file: "));
 
 3603        msg.Append(file.GetFullPath());
 
 3609      if (found_name) 
break;
 
 3626const char *s57chart::getName(OGRFeature *feature) {
 
 3627  return feature->GetDefnRef()->GetName();
 
 3630static int ExtensionCompare(
const wxString &first, 
const wxString &second) {
 
 3631  wxFileName fn1(first);
 
 3632  wxFileName fn2(second);
 
 3633  wxString ext1(fn1.GetExt());
 
 3634  wxString ext2(fn2.GetExt());
 
 3636  return ext1.Cmp(ext2);
 
 3639int s57chart::GetUpdateFileArray(
const wxFileName file000,
 
 3640                                 wxArrayString *UpFiles, wxDateTime date000,
 
 3642  wxString DirName000 =
 
 3643      file000.GetPath((
int)(wxPATH_GET_SEPARATOR | wxPATH_GET_VOLUME));
 
 3644  wxDir dir(DirName000);
 
 3645  if (!dir.IsOpened()) {
 
 3646    DirName000.Prepend(wxFileName::GetPathSeparator());
 
 3647    DirName000.Prepend(_T(
"."));
 
 3648    dir.Open(DirName000);
 
 3649    if (!dir.IsOpened()) {
 
 3654  int flags = wxDIR_DEFAULT;
 
 3661  wxFileName fnDir(DirName000);
 
 3662  fnDir.RemoveLastDir();
 
 3663  wxString sdir = fnDir.GetPath();
 
 3664  wxFileName fnTest(sdir);
 
 3665  wxString sname = fnTest.GetName();
 
 3667  if (sname.ToLong(&tmps)) {
 
 3670    flags |= wxDIR_DIRS;
 
 3674  wxArrayString *dummy_array;
 
 3677  if (UpFiles == NULL)
 
 3678    dummy_array = 
new wxArrayString;
 
 3680    dummy_array = UpFiles;
 
 3682  wxArrayString possibleFiles;
 
 3683  wxDir::GetAllFiles(DirName000, &possibleFiles, wxEmptyString, flags);
 
 3685  for (
unsigned int i = 0; i < possibleFiles.GetCount(); i++) {
 
 3686    wxString filename(possibleFiles[i]);
 
 3688    wxFileName file(filename);
 
 3689    ext = file.GetExt();
 
 3694    if (ext.ToLong(&tmp) && (file.GetName() == file000.GetName())) {
 
 3695      wxString FileToAdd = filename;
 
 3697      wxCharBuffer buffer =
 
 3700      if (buffer.data() && !filename.IsSameAs(_T(
"CATALOG.031"),
 
 3712        DDFModule *poModule = 
new DDFModule();
 
 3713        if (!poModule->Open(FileToAdd.mb_str())) {
 
 3715              _T(
"   s57chart::BuildS57File  Unable to open update file "));
 
 3716          msg.Append(FileToAdd);
 
 3725          DDFRecord *pr = poModule->ReadRecord();  
 
 3731            u = (
char *)(pr->GetStringSubfield(
"DSID", 0, 
"ISDT", 0));
 
 3734              if (strlen(u)) sumdate = wxString(u, wxConvUTF8);
 
 3738                _T(
"   s57chart::BuildS57File  DDFRecord 0 does not contain ")
 
 3739                _T(
"DSID:ISDT in update file "));
 
 3740            msg.Append(FileToAdd);
 
 3743            sumdate = _T(
"20000101");  
 
 3746          umdate.ParseFormat(sumdate, _T(
"%Y%m%d"));
 
 3747          if (!umdate.IsValid())
 
 3748            umdate.ParseFormat(_T(
"20000101"), _T(
"%Y%m%d"));
 
 3751          if (!umdate.IsValid()) 
int yyp = 4;
 
 3756            u = (
char *)(pr->GetStringSubfield(
"DSID", 0, 
"EDTN", 0));
 
 3758              if (strlen(u)) umedtn = wxString(u, wxConvUTF8);
 
 3762                _T(
"   s57chart::BuildS57File  DDFRecord 0 does not contain ")
 
 3763                _T(
"DSID:EDTN in update file "));
 
 3764            msg.Append(FileToAdd);
 
 3773        if ((!umdate.IsEarlierThan(date000)) &&
 
 3774            (umedtn.IsSameAs(edtn000)))  
 
 3775          dummy_array->Add(FileToAdd);   
 
 3781  dummy_array->Sort(ExtensionCompare);
 
 3784  if (dummy_array->GetCount()) {
 
 3785    wxString Last = dummy_array->Last();
 
 3786    wxFileName fnl(Last);
 
 3788    wxCharBuffer buffer = ext.ToUTF8();
 
 3789    if (buffer.data()) retval = atoi(buffer.data());
 
 3792  if (UpFiles == NULL) 
delete dummy_array;
 
 3797int s57chart::ValidateAndCountUpdates(
const wxFileName file000,
 
 3798                                      const wxString CopyDir,
 
 3799                                      wxString &LastUpdateDate,
 
 3805  wxArrayString *UpFiles = 
new wxArrayString;
 
 3806  retval = GetUpdateFileArray(file000, UpFiles, m_date000, m_edtn000);
 
 3808  if (UpFiles->GetCount()) {
 
 3822    bool chain_broken_mssage_shown = 
false;
 
 3828      for (
int iff = 0; iff < retval + 1; iff++) {
 
 3829        wxFileName ufile(m_TempFilePath);
 
 3831        sext.Printf(_T(
"%03d"), iff);
 
 3835        wxString cp_ufile = CopyDir;
 
 3836        if (cp_ufile.Last() != ufile.GetPathSeparator())
 
 3837          cp_ufile.Append(ufile.GetPathSeparator());
 
 3839        cp_ufile.Append(ufile.GetFullName());
 
 3844        if (ufile.FileExists()) {
 
 3845          wxFile uf(ufile.GetFullPath());
 
 3846          if (uf.IsOpened()) {
 
 3852        if (ufile.FileExists() &&
 
 3856          bool cpok = wxCopyFile(ufile.GetFullPath(), cp_ufile);
 
 3858            wxString msg(_T(
"   Cannot copy temporary working ENC file "));
 
 3859            msg.Append(ufile.GetFullPath());
 
 3860            msg.Append(_T(
" to "));
 
 3861            msg.Append(cp_ufile);
 
 3871          if (!chain_broken_mssage_shown) {
 
 3874                _(
"S57 Cell Update chain incomplete.\nENC features may be " 
 3875                  "incomplete or inaccurate.\nCheck the logfile for details."),
 
 3876                _(
"OpenCPN Create SENC Warning"), wxOK | wxICON_EXCLAMATION,
 
 3878            chain_broken_mssage_shown = 
true;
 
 3882              _T(
"WARNING---ENC Update chain incomplete. Substituting NULL ")
 
 3883              _T(
"update file: "));
 
 3884          msg += ufile.GetFullName();
 
 3886          wxLogMessage(_T(
"   Subsequent ENC updates may produce errors."));
 
 3888              _T(
"   This ENC exchange set should be updated and SENCs ")
 
 3892          DDFModule *dupdate = 
new DDFModule;
 
 3893          dupdate->Initialize(
'3', 
'L', 
'E', 
'1', 
'0', 
"!!!", 3, 4, 4);
 
 3894          bstat = !(dupdate->Create(cp_ufile.mb_str()) == 0);
 
 3898            wxString msg(_T(
"   Error creating dummy update file: "));
 
 3899            msg.Append(cp_ufile);
 
 3904        m_tmpup_array->Add(cp_ufile);
 
 3911    wxFileName lastfile(m_TempFilePath);
 
 3913    last_sext.Printf(_T(
"%03d"), retval);
 
 3914    lastfile.SetExt(last_sext);
 
 3917    DDFModule oUpdateModule;
 
 3922        !(oUpdateModule.Open(lastfile.GetFullPath().mb_str(), TRUE) == 0);
 
 3926      oUpdateModule.Rewind();
 
 3927      DDFRecord *pr = oUpdateModule.ReadRecord();  
 
 3933        u = (
char *)(pr->GetStringSubfield(
"DSID", 0, 
"ISDT", 0, &nSuccess));
 
 3937          LastUpdateDate = wxString(u, wxConvUTF8);
 
 3940        wxDateTime now = wxDateTime::Now();
 
 3941        LastUpdateDate = now.Format(_T(
"%Y%m%d"));
 
 3950wxString s57chart::GetISDT(
void) {
 
 3951  if (m_date000.IsValid())
 
 3952    return m_date000.Format(_T(
"%Y%m%d"));
 
 3954    return _T(
"Unknown");
 
 3957bool s57chart::GetBaseFileAttr(
const wxString &file000) {
 
 3958  if (!wxFileName::FileExists(file000)) 
return false;
 
 3960  wxString FullPath000 = file000;
 
 3961  DDFModule *poModule = 
new DDFModule();
 
 3962  if (!poModule->Open(FullPath000.mb_str())) {
 
 3963    wxString msg(_T(
"   s57chart::BuildS57File  Unable to open "));
 
 3964    msg.Append(FullPath000);
 
 3976  DDFRecord *pr = poModule->ReadRecord();  
 
 3980  m_nGeoRecords = pr->GetIntSubfield(
"DSSI", 0, 
"NOGR", 0);
 
 3981  if (!m_nGeoRecords) {
 
 3983        _T(
"   s57chart::BuildS57File  DDFRecord 0 does not contain ")
 
 3993  char *u = (
char *)(pr->GetStringSubfield(
"DSID", 0, 
"ISDT", 0));
 
 3995    date000 = wxString(u, wxConvUTF8);
 
 3998        _T(
"   s57chart::BuildS57File  DDFRecord 0 does not contain ")
 
 4005  m_date000.ParseFormat(date000, _T(
"%Y%m%d"));
 
 4006  if (!m_date000.IsValid()) m_date000.ParseFormat(_T(
"20000101"), _T(
"%Y%m%d"));
 
 4008  m_date000.ResetTime();
 
 4011  u = (
char *)(pr->GetStringSubfield(
"DSID", 0, 
"EDTN", 0));
 
 4013    m_edtn000 = wxString(u, wxConvUTF8);
 
 4016        _T(
"   s57chart::BuildS57File  DDFRecord 0 does not contain ")
 
 4020    m_edtn000 = _T(
"1");  
 
 4027  for (; pr != NULL; pr = poModule->ReadRecord()) {
 
 4028    if (pr->FindField(
"DSPM") != NULL) {
 
 4029      m_native_scale = pr->GetIntSubfield(
"DSPM", 0, 
"CSCL", 0);
 
 4033  if (!m_native_scale) {
 
 4034    wxString msg(_T(
"   s57chart::BuildS57File  ENC not contain DSPM:CSCL "));
 
 4037    m_native_scale = 1000;  
 
 4045int s57chart::BuildSENCFile(
const wxString &FullPath000,
 
 4046                            const wxString &SENCFileName, 
bool b_progress) {
 
 4048  double display_pix_per_meter = g_Platform->GetDisplayDPmm() * 1000;
 
 4049  double meters_per_pixel_max_scale =
 
 4050      GetNormalScaleMin(0, g_b_overzoom_x) / display_pix_per_meter;
 
 4051  m_LOD_meters = meters_per_pixel_max_scale * g_SENC_LOD_pixels;
 
 4054  ref_lat = (m_FullExtent.NLAT + m_FullExtent.SLAT) / 2.;
 
 4055  ref_lon = (m_FullExtent.WLON + m_FullExtent.ELON) / 2.;
 
 4057  if (!m_disableBackgroundSENC) {
 
 4058    if (g_SencThreadManager) {
 
 4060      ticket->m_LOD_meters = m_LOD_meters;
 
 4061      ticket->ref_lat = ref_lat;
 
 4062      ticket->ref_lon = ref_lon;
 
 4063      ticket->m_FullPath000 = FullPath000;
 
 4064      ticket->m_SENCFileName = SENCFileName;
 
 4065      ticket->m_chart = 
this;
 
 4067      g_SencThreadManager->ScheduleJob(ticket);
 
 4068      bReadyToRender = 
true;
 
 4069      return BUILD_SENC_PENDING;
 
 4072      return BUILD_SENC_NOK_RETRY;
 
 4077    senc.setRegistrar(g_poRegistrar);
 
 4078    senc.setRefLocn(ref_lat, ref_lon);
 
 4079    senc.SetLODMeters(m_LOD_meters);
 
 4081    AbstractPlatform::ShowBusySpinner();
 
 4083    int ret = senc.createSenc200(FullPath000, SENCFileName, b_progress);
 
 4085    AbstractPlatform::HideBusySpinner();
 
 4087    if (ret == ERROR_INGESTING000)
 
 4088      return BUILD_SENC_NOK_PERMANENT;
 
 4094int s57chart::BuildRAZFromSENCFile(
const wxString &FullPath) {
 
 4101  S57ObjVector Objects;
 
 4102  VE_ElementVector VEs;
 
 4103  VC_ElementVector VCs;
 
 4105  sencfile.setRefLocn(ref_lat, ref_lon);
 
 4107  int srv = sencfile.ingest200(FullPath, &Objects, &VEs, &VCs);
 
 4109  if (srv != SENC_NO_ERROR) {
 
 4110    wxLogMessage(sencfile.getLastError());
 
 4116  Extent ext = sencfile.getReadExtent();
 
 4118  m_FullExtent.ELON = ext.ELON;
 
 4119  m_FullExtent.WLON = ext.WLON;
 
 4120  m_FullExtent.NLAT = ext.NLAT;
 
 4121  m_FullExtent.SLAT = ext.SLAT;
 
 4122  m_bExtentSet = 
true;
 
 4124  ref_lat = (ext.NLAT + ext.SLAT) / 2.;
 
 4125  ref_lon = (ext.ELON + ext.WLON) / 2.;
 
 4130  int n_ve_elements = VEs.size();
 
 4132  double scale = gFrame->GetBestVPScale(
this);
 
 4133  int nativescale = GetNativeScale();
 
 4135  for (
int i = 0; i < n_ve_elements; i++) {
 
 4136    VE_Element *vep = VEs.at(i);
 
 4137    if (vep && vep->nCount) {
 
 4139      double east_max = -1e7;
 
 4140      double east_min = 1e7;
 
 4141      double north_max = -1e7;
 
 4142      double north_min = 1e7;
 
 4144      float *vrun = vep->pPoints;
 
 4145      for (
size_t i = 0; i < vep->nCount; i++) {
 
 4146        east_max = wxMax(east_max, *vrun);
 
 4147        east_min = wxMin(east_min, *vrun);
 
 4150        north_max = wxMax(north_max, *vrun);
 
 4151        north_min = wxMin(north_min, *vrun);
 
 4155      double lat1, lon1, lat2, lon2;
 
 4156      fromSM(east_min, north_min, ref_lat, ref_lon, &lat1, &lon1);
 
 4157      fromSM(east_max, north_max, ref_lat, ref_lon, &lat2, &lon2);
 
 4158      vep->edgeBBox.Set(lat1, lon1, lat2, lon2);
 
 4161    m_ve_hash[vep->index] = vep;
 
 4165  int n_vc_elements = VCs.size();
 
 4167  for (
int i = 0; i < n_vc_elements; i++) {
 
 4168    VC_Element *vcp = VCs.at(i);
 
 4169    m_vc_hash[vcp->index] = vcp;
 
 4177  for (
unsigned int i = 0; i < Objects.size(); i++) {
 
 4178    S57Obj *obj = Objects[i];
 
 4182    LUPname LUP_Name = PAPER_CHART;
 
 4184    const wxString objnam = obj->GetAttrValueAsString(
"OBJNAM");
 
 4185    if (objnam.Len() > 0) {
 
 4186      const wxString fe_name = wxString(obj->FeatureName, wxConvUTF8);
 
 4187      SendVectorChartObjectInfo(FullPath, fe_name, objnam, obj->m_lat,
 
 4188                                obj->m_lon, 
scale, nativescale);
 
 4192    const wxString nobjnam = obj->GetAttrValueAsString(
"NOBJNM");
 
 4193    if (nobjnam.Len() > 0 && nobjnam != objnam) {
 
 4194      const wxString fe_name = wxString(obj->FeatureName, wxConvUTF8);
 
 4195      SendVectorChartObjectInfo(FullPath, fe_name, nobjnam, obj->m_lat,
 
 4196                                obj->m_lon, 
scale, nativescale);
 
 4199    switch (obj->Primitive_type) {
 
 4204        if (PAPER_CHART == ps52plib->m_nSymbolStyle)
 
 4205          LUP_Name = PAPER_CHART;
 
 4207          LUP_Name = SIMPLIFIED;
 
 4216        if (PLAIN_BOUNDARIES == ps52plib->m_nBoundaryStyle)
 
 4217          LUP_Name = PLAIN_BOUNDARIES;
 
 4219          LUP_Name = SYMBOLIZED_BOUNDARIES;
 
 4224    LUP = ps52plib->S52_LUPLookup(LUP_Name, obj->FeatureName, obj);
 
 4228        wxString msg(obj->FeatureName, wxConvUTF8);
 
 4229        msg.Prepend(_T(
"   Could not find LUP for "));
 
 4230        LogMessageOnce(msg);
 
 4237      ps52plib->_LUP2rules(LUP, obj);
 
 4240      _insertRules(obj, LUP, 
this);
 
 4243      obj->m_DisplayCat = LUP->DISC;
 
 4246      obj->m_DPRI = LUP->DPRI - 
'0';
 
 4249      if (!strncmp(obj->FeatureName, 
"OBSTRN", 6) ||
 
 4250          !strncmp(obj->FeatureName, 
"WRECKS", 6) ||
 
 4251          !strncmp(obj->FeatureName, 
"DEPCNT", 6) ||
 
 4252          !strncmp(obj->FeatureName, 
"UWTROC", 6)) {
 
 4253        obj->m_bcategory_mutable = 
true;
 
 4255        obj->m_bcategory_mutable = 
false;
 
 4260    if (obj && (GEO_POINT == obj->Primitive_type)) {
 
 4262      if ((!strncmp(obj->FeatureName, 
"LITFLT", 6)) ||
 
 4263          (!strncmp(obj->FeatureName, 
"LITVES", 6)) ||
 
 4264          (!strncasecmp(obj->FeatureName, 
"BOY", 3))) {
 
 4265        pFloatingATONArray->Add(obj);
 
 4269      if (!strncasecmp(obj->FeatureName, 
"BCN", 3)) {
 
 4270        pRigidATONArray->Add(obj);
 
 4274      if ((!strncmp(obj->FeatureName, 
"LIT", 3)) ||
 
 4275          (!strncmp(obj->FeatureName, 
"LIGHTS", 6)) ||
 
 4276          (!strncasecmp(obj->FeatureName, 
"BCN", 3)) ||
 
 4277          (!strncasecmp(obj->FeatureName, 
"BOY", 3))) {
 
 4278        obj->bIsAton = 
true;
 
 4287  d000.ParseFormat(sencfile.getBaseDate(), _T(
"%Y%m%d"));
 
 4288  if (!d000.IsValid()) d000.ParseFormat(_T(
"20000101"), _T(
"%Y%m%d"));
 
 4291  updt.ParseFormat(sencfile.getUpdateDate(), _T(
"%Y%m%d"));
 
 4292  if (!updt.IsValid()) updt.ParseFormat(_T(
"20000101"), _T(
"%Y%m%d"));
 
 4294  if (updt.IsLaterThan(d000))
 
 4295    m_PubYear.Printf(_T(
"%4d"), updt.GetYear());
 
 4297    m_PubYear.Printf(_T(
"%4d"), d000.GetYear());
 
 4300  wxDateTime upd = updt;
 
 4301  if (!upd.IsValid()) upd.ParseFormat(_T(
"20000101"), _T(
"%Y%m%d"));
 
 4306  m_SE = sencfile.getSENCReadBaseEdition();
 
 4309  supdate.Printf(_T(
" / %d"), sencfile.getSENCReadLastUpdate());
 
 4312  m_datum_str = _T(
"WGS84");
 
 4314  m_SoundingsDatum = _T(
"MEAN LOWER LOW WATER");
 
 4315  m_ID = sencfile.getReadID();
 
 4316  m_Name = sencfile.getReadName();
 
 4320  AssembleLineGeometry();
 
 4325int s57chart::_insertRules(S57Obj *obj, LUPrec *LUP, 
s57chart *pOwner) {
 
 4326  ObjRazRules *rzRules = NULL;
 
 4336  switch (LUP->DPRI) {
 
 4349    case PRIO_SYMB_POINT:
 
 4352    case PRIO_SYMB_LINE:
 
 4355    case PRIO_SYMB_AREA:
 
 4368      printf(
"SEQuencer:_insertRules():ERROR no display priority!!!\n");
 
 4372  switch (LUP->TNAM) {
 
 4382    case PLAIN_BOUNDARIES:
 
 4385    case SYMBOLIZED_BOUNDARIES:
 
 4389      printf(
"SEQuencer:_insertRules():ERROR no look up type !!!\n");
 
 4393  rzRules = (ObjRazRules *)malloc(
sizeof(ObjRazRules));
 
 4397  rzRules->child = NULL;
 
 4398  rzRules->mps = NULL;
 
 4401    rzRules->next = razRules[disPrioIdx][LUPtypeIdx];
 
 4402    razRules[disPrioIdx][LUPtypeIdx] = rzRules;
 
 4407  ObjRazRules *rNext = NULL;
 
 4408  ObjRazRules *rPrevious = NULL;
 
 4409  if (razRules[disPrioIdx][LUPtypeIdx]) {
 
 4410    rPrevious = razRules[disPrioIdx][LUPtypeIdx];
 
 4411    rNext = rPrevious->next;
 
 4415    rNext = rPrevious->next;
 
 4418  rzRules->next = NULL;
 
 4420    rPrevious->next = rzRules;
 
 4422    razRules[disPrioIdx][LUPtypeIdx] = rzRules;
 
 4429void s57chart::ResetPointBBoxes(
const ViewPort &vp_last,
 
 4439  for (
int i = 0; i < PRIO_NUM; ++i) {
 
 4440    for (
int j = 0; j < 2; ++j) {
 
 4441      top = razRules[i][j];
 
 4443      while (top != NULL) {
 
 4444        if (!top->obj->geoPtMulti)  
 
 4446          if (top->obj->BBObj.GetValid()) {  
 
 4447            double lat = top->obj->m_lat, lon = top->obj->m_lon;
 
 4449            double lat1 = (lat - top->obj->BBObj.GetMinLat()) * d;
 
 4450            double lat2 = (lat - top->obj->BBObj.GetMaxLat()) * d;
 
 4452            double minlon = top->obj->BBObj.GetMinLon();
 
 4453            double maxlon = top->obj->BBObj.GetMaxLon();
 
 4455            double lon1 = (lon - minlon) * d;
 
 4456            double lon2 = (lon - maxlon) * d;
 
 4458            top->obj->BBObj.Set(lat - lat1, lon - lon1, lat - lat2, lon - lon2);
 
 4461            top->obj->BBObj.Invalidate();
 
 4482void s57chart::UpdateLUPs(
s57chart *pOwner) {
 
 4486  for (
int i = 0; i < PRIO_NUM; ++i) {
 
 4488    if ((razRules[i][0]) && (NULL == razRules[i][1])) {
 
 4489      m_b2pointLUPS = 
true;
 
 4490      top = razRules[i][0];
 
 4492      while (top != NULL) {
 
 4493        LUP = ps52plib->S52_LUPLookup(PAPER_CHART, top->obj->FeatureName,
 
 4499          if (top->obj->nRef < 2) {
 
 4500            ps52plib->_LUP2rules(LUP, top->obj);
 
 4501            _insertRules(top->obj, LUP, pOwner);
 
 4502            top->obj->m_DisplayCat = LUP->DISC;
 
 4512    if ((razRules[i][1]) && (NULL == razRules[i][0])) {
 
 4513      m_b2pointLUPS = 
true;
 
 4514      top = razRules[i][1];
 
 4516      while (top != NULL) {
 
 4517        LUP = ps52plib->S52_LUPLookup(SIMPLIFIED, top->obj->FeatureName,
 
 4520          if (top->obj->nRef < 2) {
 
 4521            ps52plib->_LUP2rules(LUP, top->obj);
 
 4522            _insertRules(top->obj, LUP, pOwner);
 
 4523            top->obj->m_DisplayCat = LUP->DISC;
 
 4533    if ((razRules[i][3]) && (NULL == razRules[i][4])) {
 
 4534      m_b2lineLUPS = 
true;
 
 4535      top = razRules[i][3];
 
 4537      while (top != NULL) {
 
 4538        LUP = ps52plib->S52_LUPLookup(SYMBOLIZED_BOUNDARIES,
 
 4539                                      top->obj->FeatureName, top->obj);
 
 4541          ps52plib->_LUP2rules(LUP, top->obj);
 
 4542          _insertRules(top->obj, LUP, pOwner);
 
 4543          top->obj->m_DisplayCat = LUP->DISC;
 
 4552    if ((razRules[i][4]) && (NULL == razRules[i][3])) {
 
 4553      m_b2lineLUPS = 
true;
 
 4554      top = razRules[i][4];
 
 4556      while (top != NULL) {
 
 4557        LUP = ps52plib->S52_LUPLookup(PLAIN_BOUNDARIES, top->obj->FeatureName,
 
 4560          ps52plib->_LUP2rules(LUP, top->obj);
 
 4561          _insertRules(top->obj, LUP, pOwner);
 
 4562          top->obj->m_DisplayCat = LUP->DISC;
 
 4574    for (
int j = 0; j < LUPNAME_NUM; j++) {
 
 4575      top = razRules[i][j];
 
 4576      while (top != NULL) {
 
 4577        top->obj->bCS_Added = 0;
 
 4580        if (top->LUP) top->obj->m_DisplayCat = top->LUP->DISC;
 
 4591    for (
int j = 0; j < LUPNAME_NUM; j++) {
 
 4592      top = razRules[i][j];
 
 4593      while (top != NULL) {
 
 4595          ObjRazRules *ctop = top->child;
 
 4596          while (NULL != ctop) {
 
 4597            ctop->obj->bCS_Added = 0;
 
 4598            free_mps(ctop->mps);
 
 4601            if (ctop->LUP) ctop->obj->m_DisplayCat = ctop->LUP->DISC;
 
 4618ListOfObjRazRules *s57chart::GetLightsObjRuleListVisibleAtLatLon(
 
 4619    float lat, 
float lon, 
ViewPort *VPoint) {
 
 4620  ListOfObjRazRules *ret_ptr = 
new ListOfObjRazRules;
 
 4621  std::vector<ObjRazRules *> selected_rules;
 
 4626  char *curr_att = NULL;
 
 4628  wxArrayOfS57attVal *attValArray = NULL;
 
 4629  bool bleading_attribute = 
false;
 
 4631  for (
int i = 0; i < PRIO_NUM; ++i) {
 
 4635      int point_type = (ps52plib->m_nSymbolStyle == SIMPLIFIED) ? 0 : 1;
 
 4636      top = razRules[i][point_type];
 
 4638      while (top != NULL) {
 
 4639        if (top->obj->npt == 1) {
 
 4640          if (!strncmp(top->obj->FeatureName, 
"LIGHTS", 6)) {
 
 4642            bool hasSectors = GetDoubleAttr(top->obj, 
"SECTR1", sectrTest);
 
 4644              if (ps52plib->ObjectRenderCheckCat(top)) {
 
 4647                wxString curAttrName;
 
 4648                curr_att = top->obj->att_array;
 
 4649                n_attr = top->obj->n_attr;
 
 4650                attValArray = top->obj->attVal;
 
 4658                  bleading_attribute = 
false;
 
 4660                  while (attrCounter < n_attr) {
 
 4661                    curAttrName = wxString(curr_att, wxConvUTF8, 6);
 
 4664                    S57attVal *pAttrVal = NULL;
 
 4667                      pAttrVal = attValArray->Item(attrCounter);
 
 4671                    wxString value = s57chart::GetAttributeValueAsString(
 
 4672                        pAttrVal, curAttrName);
 
 4674                    if (curAttrName == _T(
"LITVIS")) {
 
 4675                      if (value.StartsWith(_T(
"obsc"))) bviz = 
false;
 
 4676                    } 
else if (curAttrName == _T(
"VALNMR"))
 
 4677                      value.ToDouble(&valnmr);
 
 4683                  if (bviz && (valnmr > 0.1)) {
 
 4687                        (top->obj->x * top->obj->x_rate) + top->obj->x_origin,
 
 4688                        (top->obj->y * top->obj->y_rate) + top->obj->y_origin,
 
 4689                        ref_lat, ref_lon, &olat, &olon);
 
 4691                    double dlat = lat - olat;
 
 4692                    double dy = dlat * 60 / cos(olat * PI / 180.);
 
 4693                    double dlon = lon - olon;
 
 4694                    double dx = dlon * 60;
 
 4695                    double manhat = abs(dy) + abs(dx);
 
 4699                      DistanceBearingMercator(lat, lon, olat, olon, &br, &dd);
 
 4701                        selected_rules.push_back(top);
 
 4718  for (std::size_t i = 0; i < selected_rules.size(); ++i) {
 
 4719    ret_ptr->Append(selected_rules[i]);
 
 4725ListOfObjRazRules *s57chart::GetObjRuleListAtLatLon(
float lat, 
float lon,
 
 4726                                                    float select_radius,
 
 4728                                                    int selection_mask) {
 
 4729  ListOfObjRazRules *ret_ptr = 
new ListOfObjRazRules;
 
 4730  std::vector<ObjRazRules *> selected_rules;
 
 4732  PrepareForRender(VPoint, ps52plib);
 
 4738  for (
int i = 0; i < PRIO_NUM; ++i) {
 
 4739    if (selection_mask & MASK_POINT) {
 
 4742      int point_type = (ps52plib->m_nSymbolStyle == SIMPLIFIED) ? 0 : 1;
 
 4743      top = razRules[i][point_type];
 
 4745      while (top != NULL) {
 
 4746        if (top->obj->npt ==
 
 4749          if (ps52plib->ObjectRenderCheck(top)) {
 
 4750            if (DoesLatLonSelectObject(lat, lon, select_radius, top->obj))
 
 4751              selected_rules.push_back(top);
 
 4758          ObjRazRules *child_item = top->child;
 
 4759          while (child_item != NULL) {
 
 4760            if (ps52plib->ObjectRenderCheck(child_item)) {
 
 4761              if (DoesLatLonSelectObject(lat, lon, select_radius,
 
 4763                selected_rules.push_back(child_item);
 
 4766            child_item = child_item->next;
 
 4774    if (selection_mask & MASK_AREA) {
 
 4777      int area_boundary_type =
 
 4778          (ps52plib->m_nBoundaryStyle == PLAIN_BOUNDARIES) ? 3 : 4;
 
 4779      top = razRules[i][area_boundary_type];  
 
 4780      while (top != NULL) {
 
 4781        if (ps52plib->ObjectRenderCheck(top)) {
 
 4782          if (DoesLatLonSelectObject(lat, lon, select_radius, top->obj))
 
 4783            selected_rules.push_back(top);
 
 4790    if (selection_mask & MASK_LINE) {
 
 4792      top = razRules[i][2];  
 
 4794      while (top != NULL) {
 
 4795        if (ps52plib->ObjectRenderCheck(top)) {
 
 4796          if (DoesLatLonSelectObject(lat, lon, select_radius, top->obj))
 
 4797            selected_rules.push_back(top);
 
 4808  auto sortObjs = [lat, lon, 
this](
const ObjRazRules *obj1,
 
 4809                                   const ObjRazRules *obj2) -> 
bool {
 
 4810    double br1, dd1, br2, dd2;
 
 4812    if (obj1->obj->Primitive_type == GEO_POINT &&
 
 4813        obj2->obj->Primitive_type == GEO_POINT) {
 
 4814      double lat1, lat2, lon1, lon2;
 
 4815      fromSM((obj1->obj->x * obj1->obj->x_rate) + obj1->obj->x_origin,
 
 4816             (obj1->obj->y * obj1->obj->y_rate) + obj1->obj->y_origin, ref_lat,
 
 4817             ref_lon, &lat1, &lon1);
 
 4819      if (lon1 > 180.0) lon1 -= 360.;
 
 4821      fromSM((obj2->obj->x * obj2->obj->x_rate) + obj2->obj->x_origin,
 
 4822             (obj2->obj->y * obj2->obj->y_rate) + obj2->obj->y_origin, ref_lat,
 
 4823             ref_lon, &lat2, &lon2);
 
 4825      if (lon2 > 180.0) lon2 -= 360.;
 
 4827      DistanceBearingMercator(lat, lon, lat1, lon1, &br1, &dd1);
 
 4828      DistanceBearingMercator(lat, lon, lat2, lon2, &br2, &dd2);
 
 4835  std::sort(selected_rules.begin(), selected_rules.end(), sortObjs);
 
 4839  for (std::size_t i = 0; i < selected_rules.size(); ++i) {
 
 4840    ret_ptr->Append(selected_rules[i]);
 
 4846bool s57chart::DoesLatLonSelectObject(
float lat, 
float lon, 
float select_radius,
 
 4848  switch (obj->Primitive_type) {
 
 4852      if (!obj->BBObj.GetValid()) 
return false;
 
 4854      if (1 == obj->npt) {
 
 4859        if (!strncmp(obj->FeatureName, 
"LIGHTS", 6)) {
 
 4861          bool hasSectors = GetDoubleAttr(obj, 
"SECTR1", sectrTest);
 
 4864            fromSM((obj->x * obj->x_rate) + obj->x_origin,
 
 4865                   (obj->y * obj->y_rate) + obj->y_origin, ref_lat, ref_lon,
 
 4872            sbox.Set(olat, olon, olat, olon);
 
 4874            if (sbox.ContainsMarge(lat, lon, select_radius)) 
return true;
 
 4875          } 
else if (obj->BBObj.ContainsMarge(lat, lon, select_radius))
 
 4880        else if (obj->BBObj.ContainsMarge(lat, lon, select_radius))
 
 4887        if (!obj->BBObj.GetValid()) 
return false;
 
 4890        if (!obj->BBObj.ContainsMarge(lat, lon, select_radius)) 
return false;
 
 4892        double *pdl = obj->geoPtMulti;
 
 4893        for (
int ip = 0; ip < obj->npt; ip++) {
 
 4894          double lon_point = *pdl++;
 
 4895          double lat_point = *pdl++;
 
 4897          BB_point.Set(lat_point, lon_point, lat_point, lon_point);
 
 4898          if (BB_point.ContainsMarge(lat, lon, select_radius)) {
 
 4909      if (!obj->BBObj.ContainsMarge(lat, lon, select_radius))
 
 4912        return IsPointInObjArea(lat, lon, select_radius, obj);
 
 4917      if (!obj->BBObj.ContainsMarge(lat, lon, select_radius)) 
return false;
 
 4919      float sel_rad_meters = select_radius * 1852 * 60;  
 
 4920      double easting, northing;
 
 4921      toSM(lat, lon, ref_lat, ref_lon, &easting, &northing);
 
 4928        pt *ppt = obj->geoPt;
 
 4931        double xr = obj->x_rate;
 
 4932        double xo = obj->x_origin;
 
 4933        double yr = obj->y_rate;
 
 4934        double yo = obj->y_origin;
 
 4936        double north0 = (ppt->y * yr) + yo;
 
 4937        double east0 = (ppt->x * xr) + xo;
 
 4940        for (
int ip = 1; ip < npt; ip++) {
 
 4941          double north = (ppt->y * yr) + yo;
 
 4942          double east = (ppt->x * xr) + xo;
 
 4945          if (northing >= (fmin(north, north0) - sel_rad_meters))
 
 4946            if (northing <= (fmax(north, north0) + sel_rad_meters))
 
 4947              if (easting >= (fmin(east, east0) - sel_rad_meters))
 
 4948                if (easting <= (fmax(east, east0) + sel_rad_meters)) {
 
 4958        if (obj->m_ls_list) {
 
 4960          unsigned char *vbo_point =
 
 4961              (
unsigned char *)obj->m_chart_context
 
 4963          line_segment_element *ls = obj->m_ls_list;
 
 4965          while (ls && vbo_point) {
 
 4967            if ((ls->ls_type == TYPE_EE) || (ls->ls_type == TYPE_EE_REV)) {
 
 4968              ppt = (
float *)(vbo_point + ls->pedge->vbo_offset);
 
 4969              nPoints = ls->pedge->nCount;
 
 4971              ppt = (
float *)(vbo_point + ls->pcs->vbo_offset);
 
 4975            float north0 = ppt[1];
 
 4976            float east0 = ppt[0];
 
 4980            for (
int ip = 0; ip < nPoints - 1; ip++) {
 
 4981              float north = ppt[1];
 
 4982              float east = ppt[0];
 
 4984              if (northing >= (fmin(north, north0) - sel_rad_meters))
 
 4985                if (northing <= (fmax(north, north0) + sel_rad_meters))
 
 4986                  if (easting >= (fmin(east, east0) - sel_rad_meters))
 
 4987                    if (easting <= (fmax(east, east0) + sel_rad_meters)) {
 
 5014wxString s57chart::GetAttributeDecode(wxString &att, 
int ival) {
 
 5015  wxString ret_val = _T(
"");
 
 5018  const char *att_code;
 
 5020  wxString file(g_csv_locn);
 
 5021  file.Append(_T(
"/s57attributes.csv"));
 
 5023  if (!wxFileName::FileExists(file)) {
 
 5024    wxString msg(_T(
"   Could not open "));
 
 5031  att_code = MyCSVGetField(file.mb_str(), 
"Acronym",  
 
 5033                           CC_ExactString, 
"Code");   
 
 5039  wxString ei_file(g_csv_locn);
 
 5040  ei_file.Append(_T(
"/s57expectedinput.csv"));
 
 5042  if (!wxFileName::FileExists(ei_file)) {
 
 5043    wxString msg(_T(
"   Could not open "));
 
 5044    msg.Append(ei_file);
 
 5050  CSVTable *psTable = CSVAccess(ei_file.mb_str());
 
 5051  CSVIngest(ei_file.mb_str());
 
 5053  char **papszFields = NULL;
 
 5054  int bSelected = FALSE;
 
 5060  while (!bSelected && iline + 1 < psTable->nLineCount) {
 
 5062    papszFields = CSVSplitLine(psTable->papszLines[iline]);
 
 5064    if (!strcmp(papszFields[0], att_code)) {
 
 5065      if (atoi(papszFields[1]) == ival) {
 
 5066        ret_val = wxString(papszFields[2], wxConvUTF8);
 
 5071    CSLDestroy(papszFields);
 
 5079bool s57chart::IsPointInObjArea(
float lat, 
float lon, 
float select_radius,
 
 5083  if (obj->pPolyTessGeo) {
 
 5084    if (!obj->pPolyTessGeo->IsOk()) obj->pPolyTessGeo->BuildDeferredTess();
 
 5086    PolyTriGroup *ppg = obj->pPolyTessGeo->Get_PolyTriGroup_head();
 
 5088    TriPrim *pTP = ppg->tri_prim_head;
 
 5090    MyPoint pvert_list[3];
 
 5094    double easting, northing;
 
 5095    toSM(lat, lon, ref_lat, ref_lon, &easting, &northing);
 
 5099    if (!ppg->m_bSMSENC) {
 
 5100      double y_rate = obj->y_rate;
 
 5101      double y_origin = obj->y_origin;
 
 5102      double x_rate = obj->x_rate;
 
 5103      double x_origin = obj->x_origin;
 
 5105      double northing_scaled = (northing - y_origin) / y_rate;
 
 5106      double easting_scaled = (easting - x_origin) / x_rate;
 
 5107      northing = northing_scaled;
 
 5108      easting = easting_scaled;
 
 5113      if (pTP->tri_box.Contains(lat, lon)) {
 
 5114        if (ppg->data_type == DATA_TYPE_DOUBLE) {
 
 5115          double *p_vertex = pTP->p_vertex;
 
 5117          switch (pTP->type) {
 
 5118            case PTG_TRIANGLE_FAN: {
 
 5119              for (
int it = 0; it < pTP->nVert - 2; it++) {
 
 5120                pvert_list[0].x = p_vertex[0];
 
 5121                pvert_list[0].y = p_vertex[1];
 
 5123                pvert_list[1].x = p_vertex[(it * 2) + 2];
 
 5124                pvert_list[1].y = p_vertex[(it * 2) + 3];
 
 5126                pvert_list[2].x = p_vertex[(it * 2) + 4];
 
 5127                pvert_list[2].y = p_vertex[(it * 2) + 5];
 
 5129                if (G_PtInPolygon((MyPoint *)pvert_list, 3, easting,
 
 5137            case PTG_TRIANGLE_STRIP: {
 
 5138              for (
int it = 0; it < pTP->nVert - 2; it++) {
 
 5139                pvert_list[0].x = p_vertex[(it * 2)];
 
 5140                pvert_list[0].y = p_vertex[(it * 2) + 1];
 
 5142                pvert_list[1].x = p_vertex[(it * 2) + 2];
 
 5143                pvert_list[1].y = p_vertex[(it * 2) + 3];
 
 5145                pvert_list[2].x = p_vertex[(it * 2) + 4];
 
 5146                pvert_list[2].y = p_vertex[(it * 2) + 5];
 
 5148                if (G_PtInPolygon((MyPoint *)pvert_list, 3, easting,
 
 5156            case PTG_TRIANGLES: {
 
 5157              for (
int it = 0; it < pTP->nVert; it += 3) {
 
 5158                pvert_list[0].x = p_vertex[(it * 2)];
 
 5159                pvert_list[0].y = p_vertex[(it * 2) + 1];
 
 5161                pvert_list[1].x = p_vertex[(it * 2) + 2];
 
 5162                pvert_list[1].y = p_vertex[(it * 2) + 3];
 
 5164                pvert_list[2].x = p_vertex[(it * 2) + 4];
 
 5165                pvert_list[2].y = p_vertex[(it * 2) + 5];
 
 5167                if (G_PtInPolygon((MyPoint *)pvert_list, 3, easting,
 
 5176        } 
else if (ppg->data_type == DATA_TYPE_FLOAT) {
 
 5177          float *p_vertex = (
float *)pTP->p_vertex;
 
 5179          switch (pTP->type) {
 
 5180            case PTG_TRIANGLE_FAN: {
 
 5181              for (
int it = 0; it < pTP->nVert - 2; it++) {
 
 5182                pvert_list[0].x = p_vertex[0];
 
 5183                pvert_list[0].y = p_vertex[1];
 
 5185                pvert_list[1].x = p_vertex[(it * 2) + 2];
 
 5186                pvert_list[1].y = p_vertex[(it * 2) + 3];
 
 5188                pvert_list[2].x = p_vertex[(it * 2) + 4];
 
 5189                pvert_list[2].y = p_vertex[(it * 2) + 5];
 
 5191                if (G_PtInPolygon((MyPoint *)pvert_list, 3, easting,
 
 5199            case PTG_TRIANGLE_STRIP: {
 
 5200              for (
int it = 0; it < pTP->nVert - 2; it++) {
 
 5201                pvert_list[0].x = p_vertex[(it * 2)];
 
 5202                pvert_list[0].y = p_vertex[(it * 2) + 1];
 
 5204                pvert_list[1].x = p_vertex[(it * 2) + 2];
 
 5205                pvert_list[1].y = p_vertex[(it * 2) + 3];
 
 5207                pvert_list[2].x = p_vertex[(it * 2) + 4];
 
 5208                pvert_list[2].y = p_vertex[(it * 2) + 5];
 
 5210                if (G_PtInPolygon((MyPoint *)pvert_list, 3, easting,
 
 5218            case PTG_TRIANGLES: {
 
 5219              for (
int it = 0; it < pTP->nVert; it += 3) {
 
 5220                pvert_list[0].x = p_vertex[(it * 2)];
 
 5221                pvert_list[0].y = p_vertex[(it * 2) + 1];
 
 5223                pvert_list[1].x = p_vertex[(it * 2) + 2];
 
 5224                pvert_list[1].y = p_vertex[(it * 2) + 3];
 
 5226                pvert_list[2].x = p_vertex[(it * 2) + 4];
 
 5227                pvert_list[2].y = p_vertex[(it * 2) + 5];
 
 5229                if (G_PtInPolygon((MyPoint *)pvert_list, 3, easting,
 
 5252wxString s57chart::GetObjectAttributeValueAsString(S57Obj *obj, 
int iatt,
 
 5253                                                   wxString curAttrName) {
 
 5257  pval = obj->attVal->Item(iatt);
 
 5258  switch (pval->valType) {
 
 5261        wxString val_str((
char *)(pval->value), wxConvUTF8);
 
 5263        if (val_str.ToLong(&ival)) {
 
 5265            value = _T(
"Unknown");
 
 5267            wxString decode_val = GetAttributeDecode(curAttrName, ival);
 
 5268            if (!decode_val.IsEmpty()) {
 
 5271              iv.Printf(_T(
" (%d)"), (
int)ival);
 
 5274              value.Printf(_T(
"%d"), (
int)ival);
 
 5278        else if (val_str.IsEmpty())
 
 5279          value = _T(
"Unknown");
 
 5283          wxString value_increment;
 
 5284          wxStringTokenizer tk(val_str, wxT(
","));
 
 5286          if (tk.HasMoreTokens()) {
 
 5287            while (tk.HasMoreTokens()) {
 
 5288              wxString token = tk.GetNextToken();
 
 5290              if (token.ToLong(&ival)) {
 
 5291                wxString decode_val = GetAttributeDecode(curAttrName, ival);
 
 5293                value_increment.Printf(_T(
" (%d)"), (
int)ival);
 
 5295                if (!decode_val.IsEmpty()) value_increment.Prepend(decode_val);
 
 5297                if (iv) value_increment.Prepend(wxT(
", "));
 
 5298                value.Append(value_increment);
 
 5301                if (iv) value.Append(_T(
","));
 
 5302                value.Append(token);
 
 5308            value.Append(val_str);
 
 5311        value = _T(
"[NULL VALUE]");
 
 5317      int ival = *((
int *)pval->value);
 
 5318      wxString decode_val = GetAttributeDecode(curAttrName, ival);
 
 5320      if (!decode_val.IsEmpty()) {
 
 5323        iv.Printf(_T(
"(%d)"), ival);
 
 5326        value.Printf(_T(
"(%d)"), ival);
 
 5334      double dval = *((
double *)pval->value);
 
 5335      wxString val_suffix = _T(
" m");
 
 5338      if ((curAttrName == _T(
"VERCLR")) || (curAttrName == _T(
"VERCCL")) ||
 
 5339          (curAttrName == _T(
"VERCOP")) || (curAttrName == _T(
"HEIGHT")) ||
 
 5340          (curAttrName == _T(
"HORCLR")) || (curAttrName == _T(
"ELEVAT"))) {
 
 5341        switch (ps52plib->m_nDepthUnitDisplay) {
 
 5344            dval = dval * 3 * 39.37 / 36;  
 
 5345            val_suffix = _T(
" ft");
 
 5352      else if ((curAttrName == _T(
"VALSOU")) || (curAttrName == _T(
"DRVAL1")) ||
 
 5353               (curAttrName == _T(
"DRVAL2")) || (curAttrName == _T(
"VALDCO"))) {
 
 5354        switch (ps52plib->m_nDepthUnitDisplay) {
 
 5356            dval = dval * 3 * 39.37 / 36;  
 
 5357            val_suffix = _T(
" ft");
 
 5360            dval = dval * 3 * 39.37 / 36;  
 
 5362            val_suffix = _T(
" fathoms");
 
 5369      else if (curAttrName == _T(
"SECTR1"))
 
 5370        val_suffix = _T(
"°");
 
 5371      else if (curAttrName == _T(
"SECTR2"))
 
 5372        val_suffix = _T(
"°");
 
 5373      else if (curAttrName == _T(
"ORIENT"))
 
 5374        val_suffix = _T(
"°");
 
 5375      else if (curAttrName == _T(
"VALNMR"))
 
 5376        val_suffix = _T(
" Nm");
 
 5377      else if (curAttrName == _T(
"SIGPER"))
 
 5378        val_suffix = _T(
"s");
 
 5379      else if (curAttrName == _T(
"VALACM"))
 
 5380        val_suffix = _T(
" Minutes/year");
 
 5381      else if (curAttrName == _T(
"VALMAG"))
 
 5382        val_suffix = _T(
"°");
 
 5383      else if (curAttrName == _T(
"CURVEL"))
 
 5384        val_suffix = _T(
" kt");
 
 5386      if (dval - floor(dval) < 0.01)
 
 5387        value.Printf(_T(
"%2.0f"), dval);
 
 5389        value.Printf(_T(
"%4.1f"), dval);
 
 5391      value << val_suffix;
 
 5396    case OGR_REAL_LST: {
 
 5403wxString s57chart::GetAttributeValueAsString(S57attVal *pAttrVal,
 
 5404                                             wxString AttrName) {
 
 5405  if (NULL == pAttrVal) 
return _T(
"");
 
 5408  switch (pAttrVal->valType) {
 
 5410      if (pAttrVal->value) {
 
 5411        wxString val_str((
char *)(pAttrVal->value), wxConvUTF8);
 
 5413        if (val_str.ToLong(&ival)) {
 
 5415            value = _T(
"Unknown");
 
 5417            wxString decode_val = GetAttributeDecode(AttrName, ival);
 
 5418            if (!decode_val.IsEmpty()) {
 
 5421              iv.Printf(_T(
"(%d)"), (
int)ival);
 
 5424              value.Printf(_T(
"%d"), (
int)ival);
 
 5428        else if (val_str.IsEmpty())
 
 5429          value = _T(
"Unknown");
 
 5433          wxString value_increment;
 
 5434          wxStringTokenizer tk(val_str, wxT(
","));
 
 5436          while (tk.HasMoreTokens()) {
 
 5437            wxString token = tk.GetNextToken();
 
 5439            if (token.ToLong(&ival)) {
 
 5440              wxString decode_val = GetAttributeDecode(AttrName, ival);
 
 5441              if (!decode_val.IsEmpty())
 
 5442                value_increment = decode_val;
 
 5444                value_increment.Printf(_T(
" %d"), (
int)ival);
 
 5446              if (iv) value_increment.Prepend(wxT(
", "));
 
 5448            value.Append(value_increment);
 
 5452          value.Append(val_str);
 
 5455        value = _T(
"[NULL VALUE]");
 
 5461      int ival = *((
int *)pAttrVal->value);
 
 5462      wxString decode_val = GetAttributeDecode(AttrName, ival);
 
 5464      if (!decode_val.IsEmpty()) {
 
 5467        iv.Printf(_T(
"(%d)"), ival);
 
 5470        value.Printf(_T(
"(%d)"), ival);
 
 5478      double dval = *((
double *)pAttrVal->value);
 
 5479      wxString val_suffix = _T(
" m");
 
 5482      if ((AttrName == _T(
"VERCLR")) || (AttrName == _T(
"VERCCL")) ||
 
 5483          (AttrName == _T(
"VERCOP")) || (AttrName == _T(
"HEIGHT")) ||
 
 5484          (AttrName == _T(
"HORCLR")) || (AttrName == _T(
"ELEVAT"))) {
 
 5485        switch (ps52plib->m_nDepthUnitDisplay) {
 
 5488            dval = dval * 3 * 39.37 / 36;  
 
 5489            val_suffix = _T(
" ft");
 
 5496      else if ((AttrName == _T(
"VALSOU")) || (AttrName == _T(
"DRVAL1")) ||
 
 5497               (AttrName == _T(
"DRVAL2"))) {
 
 5498        switch (ps52plib->m_nDepthUnitDisplay) {
 
 5500            dval = dval * 3 * 39.37 / 36;  
 
 5501            val_suffix = _T(
" ft");
 
 5504            dval = dval * 3 * 39.37 / 36;  
 
 5506            val_suffix = _T(
" fathoms");
 
 5513      else if (AttrName == _T(
"SECTR1"))
 
 5514        val_suffix = _T(
"°");
 
 5515      else if (AttrName == _T(
"SECTR2"))
 
 5516        val_suffix = _T(
"°");
 
 5517      else if (AttrName == _T(
"ORIENT"))
 
 5518        val_suffix = _T(
"°");
 
 5519      else if (AttrName == _T(
"VALNMR"))
 
 5520        val_suffix = _T(
" Nm");
 
 5521      else if (AttrName == _T(
"SIGPER"))
 
 5522        val_suffix = _T(
"s");
 
 5523      else if (AttrName == _T(
"VALACM"))
 
 5524        val_suffix = _T(
" Minutes/year");
 
 5525      else if (AttrName == _T(
"VALMAG"))
 
 5526        val_suffix = _T(
"°");
 
 5527      else if (AttrName == _T(
"CURVEL"))
 
 5528        val_suffix = _T(
" kt");
 
 5530      if (dval - floor(dval) < 0.01)
 
 5531        value.Printf(_T(
"%2.0f"), dval);
 
 5533        value.Printf(_T(
"%4.1f"), dval);
 
 5535      value << val_suffix;
 
 5540    case OGR_REAL_LST: {
 
 5548  int positionDiff = l1->position.Cmp(l2->position);
 
 5549  if (positionDiff < 0) 
return false;
 
 5551  int attrIndex1 = l1->attributeNames.Index(_T(
"SECTR1"));
 
 5552  int attrIndex2 = l2->attributeNames.Index(_T(
"SECTR1"));
 
 5555  if (attrIndex1 == wxNOT_FOUND && attrIndex2 == wxNOT_FOUND) 
return false;
 
 5556  if (attrIndex1 != wxNOT_FOUND && attrIndex2 == wxNOT_FOUND) 
return true;
 
 5557  if (attrIndex1 == wxNOT_FOUND && attrIndex2 != wxNOT_FOUND) 
return false;
 
 5559  double angle1, angle2;
 
 5560  l1->attributeValues.Item(attrIndex1).ToDouble(&angle1);
 
 5561  l2->attributeValues.Item(attrIndex2).ToDouble(&angle2);
 
 5563  return angle1 < angle2;
 
 5566static const char *type2str(GeoPrim_t type) {
 
 5567  const char *r = 
"Unknown";
 
 5588wxString s57chart::CreateObjDescriptions(ListOfObjRazRules *rule_list) {
 
 5591  wxString curAttrName, value;
 
 5592  bool isLight = 
false;
 
 5595  wxString classAttributes;
 
 5597  wxString lightsHtml;
 
 5598  wxString positionString;
 
 5599  std::vector<S57Light *> lights;
 
 5603  for (ListOfObjRazRules::Node *node = rule_list->GetLast(); node;
 
 5604       node = node->GetPrevious()) {
 
 5605    ObjRazRules *current = node->GetData();
 
 5606    positionString.Clear();
 
 5610    if (0 == strncmp(current->LUP->OBCL, 
"SOUND", 5)) 
continue;
 
 5612    if (current->obj->Primitive_type == GEO_META) 
continue;
 
 5613    if (current->obj->Primitive_type == GEO_PRIM) 
continue;
 
 5615    className = wxString(current->obj->FeatureName, wxConvUTF8);
 
 5618    isLight = !strcmp(current->obj->FeatureName, 
"LIGHTS");
 
 5623    const char *name_desc;
 
 5624    if (g_csv_locn.Len()) {
 
 5625      wxString oc_file(g_csv_locn);
 
 5626      oc_file.Append(_T(
"/s57objectclasses.csv"));
 
 5627      name_desc = MyCSVGetField(oc_file.mb_str(), 
"Acronym",     
 
 5628                                current->obj->FeatureName,       
 
 5629                                CC_ExactString, 
"ObjectClass");  
 
 5635    if (0 == strlen(name_desc)) {
 
 5636      name_desc = current->obj->FeatureName;
 
 5637      classDesc = wxString(name_desc, wxConvUTF8, 1);
 
 5638      classDesc << wxString(name_desc + 1, wxConvUTF8).MakeLower();
 
 5640      classDesc = wxString(name_desc, wxConvUTF8);
 
 5647      classAttributes = _T(
"");
 
 5648      index.Printf(_T(
"Feature Index: %d<br>"), current->obj->Index);
 
 5649      classAttributes << index;
 
 5652      LUPstring.Printf(_T(
"LUP RCID:  %d<br>"), current->LUP->RCID);
 
 5653      classAttributes << LUPstring;
 
 5656      LLBBox bbox = current->obj->BBObj;
 
 5657      Bbox.Printf(_T(
"Lat/Lon box:  %g %g %g %g<br>"), bbox.GetMinLat(),
 
 5658                  bbox.GetMaxLat(), bbox.GetMinLon(), bbox.GetMaxLon());
 
 5659      classAttributes << Bbox;
 
 5662      Type.Printf(_T(
" Type:  %s<br>"), type2str(current->obj->Primitive_type));
 
 5663      classAttributes << Type;
 
 5665      LUPstring = _T(
"    LUP ATTC: ");
 
 5666      if (current->LUP->ATTArray.size())
 
 5667        LUPstring += wxString(current->LUP->ATTArray[0].c_str(), wxConvUTF8);
 
 5668      LUPstring += _T(
"<br>");
 
 5669      classAttributes << LUPstring;
 
 5671      LUPstring = _T(
"    LUP INST: ");
 
 5672      LUPstring += current->LUP->INST;
 
 5673      LUPstring += _T(
"<br><br>");
 
 5674      classAttributes << LUPstring;
 
 5677    if (GEO_POINT == current->obj->Primitive_type) {
 
 5679      fromSM((current->obj->x * current->obj->x_rate) + current->obj->x_origin,
 
 5680             (current->obj->y * current->obj->y_rate) + current->obj->y_origin,
 
 5681             ref_lat, ref_lon, &lat, &lon);
 
 5683      if (lon > 180.0) lon -= 360.;
 
 5685      positionString.Clear();
 
 5686      positionString += toSDMM(1, lat);
 
 5687      positionString << _T(
" ");
 
 5688      positionString += toSDMM(2, lon);
 
 5692        curLight->position = positionString;
 
 5693        curLight->hasSectors = 
false;
 
 5694        lights.push_back(curLight);
 
 5700    if (current->obj->att_array) {
 
 5701      char *curr_att = current->obj->att_array;
 
 5707      attribStr << _T(
"<table border=0 cellspacing=0 cellpadding=0>");
 
 5710        ret_val << _T(
"<p>") << classAttributes;
 
 5713      bool inDepthRange = 
false;
 
 5715      while (attrCounter < current->obj->n_attr) {
 
 5717        curAttrName = wxString(curr_att, wxConvUTF8, 6);
 
 5724          assert(curLight != 
nullptr);
 
 5725          curLight->attributeNames.Add(curAttrName);
 
 5726          if (curAttrName.StartsWith(_T(
"SECTR"))) curLight->hasSectors = 
true;
 
 5728          if (curAttrName == _T(
"DRVAL1")) {
 
 5729            attribStr << _T(
"<tr><td><font size=-1>");
 
 5730            inDepthRange = 
true;
 
 5731          } 
else if (curAttrName == _T(
"DRVAL2")) {
 
 5732            attribStr << _T(
" - ");
 
 5733            inDepthRange = 
false;
 
 5736              attribStr << _T(
"</font></td></tr>\n");
 
 5737              inDepthRange = 
false;
 
 5739            attribStr << _T(
"<tr><td valign=top><font size=-2>");
 
 5740            if (curAttrName == _T(
"catgeo"))
 
 5741              attribStr << _T(
"CATGEO");
 
 5743              attribStr << curAttrName;
 
 5744            attribStr << _T(
"</font></td><td>  </td><td ")
 
 5745                         _T("valign=top><font size=-1>");
 
 5756        value = GetObjectAttributeValueAsString(current->obj, attrCounter,
 
 5761        wxString AttrNamesFiles =
 
 5762            _T("PICREP,TXTDSC,NTXTDS");  
 
 5764        if (AttrNamesFiles.Find(curAttrName) != wxNOT_FOUND)
 
 5765          if (value.Find(_T(".XML")) == wxNOT_FOUND) {  
 
 5766            file.Assign(GetFullPath());
 
 5767            file.Assign(file.GetPath(), value);
 
 5770            if (file.IsCaseSensitive()) {
 
 5771              wxDir dir(file.GetPath());
 
 5773              bool cont = dir.GetFirst(&filename, 
"", wxDIR_FILES);
 
 5775                if (filename.IsSameAs(value, 
false)) {
 
 5777                  file.Assign(file.GetPath(), value);
 
 5780                cont = dir.GetNext(&filename);
 
 5787                    wxString::Format(_T(
"<a href=\"%s\">%s</a>"),
 
 5788                                     file.GetFullPath(), file.GetFullName());
 
 5790                value = value + _T(
"  <font color=\"red\">[ ") +
 
 5791                        _(
"this file is not available") + _T(
" ]</font>");
 
 5795            _T(
"DATEND,DATSTA,PEREND,PERSTA");  
 
 5796        if (AttrNamesFiles.Find(curAttrName) != wxNOT_FOUND) {
 
 5799          wxString ts = value;
 
 5801          ts.Replace(wxT(
"--"),
 
 5803          if (ts.Length() < 5) {    
 
 5808          if (ts.Length() < 7) {  
 
 5813          wxString::const_iterator end;
 
 5815          if (dt.ParseFormat(ts, 
"%Y%m%d", &end)) {
 
 5817            if (m) ts = wxDateTime::GetMonthName(dt.GetMonth());
 
 5818            if (d) ts.Append(wxString::Format(wxT(
" %d"), dt.GetDay()));
 
 5819            if (dt.GetYear() > 0)
 
 5820              ts.Append(wxString::Format(wxT(
",  %i"), dt.GetYear()));
 
 5821            if (curAttrName == _T(
"PEREND"))
 
 5822              ts = _(
"Period ends: ") + ts + wxT(
"  (") + value + wxT(
")");
 
 5823            if (curAttrName == _T(
"PERSTA"))
 
 5824              ts = _(
"Period starts: ") + ts + wxT(
"  (") + value + wxT(
")");
 
 5825            if (curAttrName == _T(
"DATEND"))
 
 5826              ts = _(
"Date ending: ") + ts + wxT(
"  (") + value + wxT(
")");
 
 5827            if (curAttrName == _T(
"DATSTA"))
 
 5828              ts = _(
"Date starting: ") + ts + wxT(
"  (") + value + wxT(
")");
 
 5832        if (curAttrName == _T(
"TS_TSP")) {  
 
 5839          wxStringTokenizer tk(value, wxT(
","));
 
 5844            ts1 = tk.GetNextToken().Trim(
false);
 
 5847          } 
while ((ts1.Left(2).ToLong(&l)));
 
 5848          ts = _T(
"Tidal Streams referred to<br><b>");
 
 5849          ts.Append(tk.GetNextToken()).Append(_T(
"</b> at <b>")).Append(ts1);
 
 5850          ts.Append(_T(
"</b><br><table >"));
 
 5852          while (tk.HasMoreTokens()) {  
 
 5853            ts.Append(_T(
"<tr><td>"));
 
 5854            wxString s1(wxString::Format(_T(
"%+dh "), i));
 
 5856            ts.Append(_T(
"</td><td>"));
 
 5857            s1 = tk.GetNextToken();
 
 5859            s1 = 
"°</td><td>";
 
 5861            s1 = tk.GetNextToken();
 
 5864            ts.Append(_T(
"</td></tr>"));
 
 5867          ts.Append(_T(
"</table>"));
 
 5872          assert(curLight != 
nullptr);
 
 5873          curLight->attributeValues.Add(value);
 
 5875          if (curAttrName == _T(
"INFORM") || curAttrName == _T(
"NINFOM"))
 
 5876            value.Replace(_T(
"|"), _T(
"<br>"));
 
 5878          if (curAttrName == _T(
"catgeo"))
 
 5879            attribStr << type2str(current->obj->Primitive_type);
 
 5883          if (!(curAttrName == _T(
"DRVAL1"))) {
 
 5884            attribStr << _T(
"</font></td></tr>\n");
 
 5894        attribStr << _T(
"</table>\n");
 
 5896        objText += _T(
"<b>") + classDesc + _T(
"</b> <font size=-2>(") +
 
 5897                   className + _T(
")</font>") + _T(
"<br>");
 
 5899        if (positionString.Length())
 
 5900          objText << _T(
"<font size=-2>") << positionString
 
 5901                  << _T(
"</font><br>\n");
 
 5903        if (noAttr > 0) objText << attribStr;
 
 5905        if (node != rule_list->GetFirst()) objText += _T(
"<hr noshade>");
 
 5906        objText += _T(
"<br>");
 
 5912  if (!lights.empty()) {
 
 5913    assert(curLight != 
nullptr);
 
 5918    std::sort(lights.begin(), lights.end(), s57chart::CompareLights);
 
 5922    for (
auto const &thisLight : lights) {
 
 5925      if (thisLight->position != lastPos) {
 
 5926        lastPos = thisLight->position;
 
 5928        if (thisLight != *lights.begin())
 
 5929          lightsHtml << _T(
"</table>\n<hr noshade>\n");
 
 5931        lightsHtml << _T(
"<b>Light</b> <font size=-2>(LIGHTS)</font><br>");
 
 5932        lightsHtml << _T(
"<font size=-2>") << thisLight->position
 
 5933                   << _T(
"</font><br>\n");
 
 5935        if (curLight->hasSectors)
 
 5937              "<font size=-2>(Sector angles are True Bearings from " 
 5938              "Seaward)</font><br>");
 
 5940        lightsHtml << _T(
"<table>");
 
 5943      lightsHtml << _T(
"<tr>");
 
 5944      lightsHtml << _T(
"<td><font size=-1>");
 
 5947      attrIndex = thisLight->attributeNames.Index(_T(
"COLOUR"));
 
 5948      if (attrIndex != wxNOT_FOUND) {
 
 5949        wxString color = thisLight->attributeValues.Item(attrIndex);
 
 5950        if (color == _T(
"red (3)") || color == _T(
"red(3)"))
 
 5952              _T(
"<table border=0><tr><td ")
 
 5953              _T("bgcolor=red>   </td></tr></table> ");
 
 5954        else if (color == _T("green (4)") || color == _T("green(4)"))
 
 5956              _T("<table border=0><tr><td ")
 
 5957              _T("bgcolor=green>   </td></tr></table> ");
 
 5958        else if (color == _T("white (1)") || color == _T("white(1)"))
 
 5960              _T("<table border=0><tr><td ")
 
 5961              _T("bgcolor=white>   </td></tr></table> ");
 
 5962        else if (color == _T("yellow (6)") || color == _T("yellow(6)"))
 
 5964              _T("<table border=0><tr><td ")
 
 5965              _T("bgcolor=yellow>   </td></tr></table> ");
 
 5966        else if (color == _T("blue (5)") || color == _T("blue(5)"))
 
 5968              _T("<table border=0><tr><td ")
 
 5969              _T("bgcolor=blue>   </td></tr></table> ");
 
 5970        else if (color == _T("magenta (12)") || color == _T("magenta(12)"))
 
 5972              _T("<table border=0><tr><td ")
 
 5973              _T("bgcolor=magenta>   </td></tr></table> ");
 
 5976              _T("<table border=0><tr><td ")
 
 5977              _T("bgcolor=grey> ? </td></tr></table> ");
 
 5980      int visIndex = thisLight->attributeNames.Index(_T("LITVIS"));
 
 5981      if (visIndex != wxNOT_FOUND) {
 
 5982        wxString vis = thisLight->attributeValues.Item(visIndex);
 
 5983        if (vis.Contains(_T(
"8"))) {
 
 5984          if (attrIndex != wxNOT_FOUND) {
 
 5985            wxString color = thisLight->attributeValues.Item(attrIndex);
 
 5986            if ((color == _T(
"red (3)") || color == _T(
"red(3)")))
 
 5988                  _T(
"<table border=0><tr><td ")
 
 5989                  _T("bgcolor=DarkRed>   </td></tr></table> ");
 
 5990            if ((color == _T("green (4)") || color == _T("green(4)")))
 
 5992                  _T("<table border=0><tr><td ")
 
 5993                  _T("bgcolor=DarkGreen>   </td></tr></table> ");
 
 5994            if ((color == _T("white (1)") || color == _T("white(1)")))
 
 5996                  _T("<table border=0><tr><td ")
 
 5997                  _T("bgcolor=GoldenRod>   </td></tr></table> ");
 
 6002      lightsHtml << colorStr;
 
 6004      lightsHtml << _T("</font></td><td><font size=-1><nobr><b>");
 
 6006      attrIndex = thisLight->attributeNames.Index(_T("LITCHR"));
 
 6007      if (attrIndex != wxNOT_FOUND) {
 
 6008        wxString character = thisLight->attributeValues[attrIndex];
 
 6009        lightsHtml << character.BeforeFirst(wxChar(
'(')) << _T(
" ");
 
 6012      attrIndex = thisLight->attributeNames.Index(_T(
"SIGGRP"));
 
 6013      if (attrIndex != wxNOT_FOUND) {
 
 6014        lightsHtml << thisLight->attributeValues[attrIndex];
 
 6015        lightsHtml << _T(
" ");
 
 6018      attrIndex = thisLight->attributeNames.Index(_T(
"COLOUR"));
 
 6019      if (attrIndex != wxNOT_FOUND) {
 
 6020        lightsHtml << _T(
" ")
 
 6021                   << thisLight->attributeValues.Item(attrIndex).Upper()[0];
 
 6022        lightsHtml << _T(
" ");
 
 6025      attrIndex = thisLight->attributeNames.Index(_T(
"SIGPER"));
 
 6026      if (attrIndex != wxNOT_FOUND) {
 
 6027        lightsHtml << thisLight->attributeValues[attrIndex];
 
 6028        lightsHtml << _T(
" ");
 
 6031      attrIndex = thisLight->attributeNames.Index(_T(
"HEIGHT"));
 
 6032      if (attrIndex != wxNOT_FOUND) {
 
 6033        lightsHtml << thisLight->attributeValues[attrIndex];
 
 6034        lightsHtml << _T(
" ");
 
 6037      attrIndex = thisLight->attributeNames.Index(_T(
"VALNMR"));
 
 6038      if (attrIndex != wxNOT_FOUND) {
 
 6039        lightsHtml << thisLight->attributeValues[attrIndex];
 
 6040        lightsHtml << _T(
" ");
 
 6043      lightsHtml << _T(
"</b>");
 
 6045      attrIndex = thisLight->attributeNames.Index(_T(
"SECTR1"));
 
 6046      if (attrIndex != wxNOT_FOUND) {
 
 6047        lightsHtml << _T(
"(") << thisLight->attributeValues[attrIndex];
 
 6048        lightsHtml << _T(
" - ");
 
 6049        attrIndex = thisLight->attributeNames.Index(_T(
"SECTR2"));
 
 6050        lightsHtml << thisLight->attributeValues[attrIndex] << _T(
") ");
 
 6053      lightsHtml << _T(
"</nobr>");
 
 6055      attrIndex = thisLight->attributeNames.Index(_T(
"CATLIT"));
 
 6056      if (attrIndex != wxNOT_FOUND) {
 
 6057        lightsHtml << _T(
"<nobr>");
 
 6058        lightsHtml << thisLight->attributeValues[attrIndex].BeforeFirst(
 
 6060        lightsHtml << _T(
"</nobr> ");
 
 6063      attrIndex = thisLight->attributeNames.Index(_T(
"EXCLIT"));
 
 6064      if (attrIndex != wxNOT_FOUND) {
 
 6065        lightsHtml << _T(
"<nobr>");
 
 6066        lightsHtml << thisLight->attributeValues[attrIndex].BeforeFirst(
 
 6068        lightsHtml << _T(
"</nobr> ");
 
 6071      attrIndex = thisLight->attributeNames.Index(_T(
"OBJNAM"));
 
 6072      if (attrIndex != wxNOT_FOUND) {
 
 6073        lightsHtml << _T(
"<br><nobr>");
 
 6074        lightsHtml << thisLight->attributeValues[attrIndex].Left(1).Upper();
 
 6075        lightsHtml << thisLight->attributeValues[attrIndex].Mid(1);
 
 6076        lightsHtml << _T(
"</nobr> ");
 
 6079      lightsHtml << _T(
"</font></td>");
 
 6080      lightsHtml << _T(
"</tr>");
 
 6082      thisLight->attributeNames.Clear();
 
 6083      thisLight->attributeValues.Clear();
 
 6086    lightsHtml << _T(
"</table><hr noshade>\n");
 
 6087    ret_val = lightsHtml << ret_val;
 
 6101bool s57chart::InitENCMinimal(
const wxString &FullPath) {
 
 6102  if (NULL == g_poRegistrar) {
 
 6103    wxLogMessage(_T(
"   Error: No ClassRegistrar in InitENCMinimal."));
 
 6107  m_pENCDS = 
new OGRS57DataSource;
 
 6109  m_pENCDS->SetS57Registrar(g_poRegistrar);  
 
 6111  if (!m_pENCDS->OpenMin(FullPath.mb_str(), TRUE))  
 
 6114  S57Reader *pENCReader = m_pENCDS->GetModule(0);
 
 6115  pENCReader->SetClassBased(g_poRegistrar);
 
 6117  pENCReader->Ingest();
 
 6122OGRFeature *s57chart::GetChartFirstM_COVR(
int &catcov) {
 
 6124  S57Reader *pENCReader = m_pENCDS->GetModule(0);
 
 6126  if ((NULL != pENCReader) && (NULL != g_poRegistrar)) {
 
 6128    g_poRegistrar->SelectClass(
"M_COVR");
 
 6131    OGRFeatureDefn *poDefn = S57GenerateObjectClassDefn(
 
 6132        g_poRegistrar, g_poRegistrar->GetOBJL(), pENCReader->GetOptionFlags());
 
 6135    pENCReader->AddFeatureDefn(poDefn);
 
 6138    m_pENCDS->AddLayer(
new OGRS57Layer(m_pENCDS, poDefn, 1));
 
 6141    OGRFeature *pobjectDef = pENCReader->ReadNextFeature(poDefn);
 
 6144      catcov = pobjectDef->GetFieldAsInteger(
"CATCOV");
 
 6155OGRFeature *s57chart::GetChartNextM_COVR(
int &catcov) {
 
 6159  S57Reader *pENCReader = m_pENCDS->GetModule(0);
 
 6162  OGRFeatureDefn *poDefn = m_pENCDS->GetLayer(0)->GetLayerDefn();
 
 6165    OGRFeature *pobjectDef = pENCReader->ReadNextFeature(poDefn);
 
 6168      catcov = pobjectDef->GetFieldAsInteger(
"CATCOV");
 
 6177int s57chart::GetENCScale(
void) {
 
 6178  if (NULL == m_pENCDS) 
return 0;
 
 6185  S57Reader *pENCReader = m_pENCDS->GetModule(0);
 
 6188    return pENCReader->GetCSCL();  
 
 6198void OpenCPN_OGRErrorHandler(CPLErr eErrClass, 
int nError,
 
 6199                             const char *pszErrorMsg) {
 
 6200#define ERR_BUF_LEN 2000 
 6202  char buf[ERR_BUF_LEN + 1];
 
 6204  if (eErrClass == CE_Debug)
 
 6205    sprintf(buf, 
" %s", pszErrorMsg);
 
 6206  else if (eErrClass == CE_Warning)
 
 6207    sprintf(buf, 
"   Warning %d: %s\n", nError, pszErrorMsg);
 
 6209    sprintf(buf, 
"   ERROR %d: %s\n", nError, pszErrorMsg);
 
 6211  if (g_bGDAL_Debug || (CE_Debug != eErrClass)) {  
 
 6212    wxString msg(buf, wxConvUTF8);
 
 6218  if (eErrClass == CE_Fatal) {
 
 6219    longjmp(env_ogrf, 1);  
 
 6230const char *MyCSVGetField(
const char *pszFilename, 
const char *pszKeyFieldName,
 
 6231                          const char *pszKeyFieldValue,
 
 6232                          CSVCompareCriteria eCriteria,
 
 6233                          const char *pszTargetField)
 
 6242  papszRecord = CSVScanFileByName(pszFilename, pszKeyFieldName,
 
 6243                                  pszKeyFieldValue, eCriteria);
 
 6245  if (papszRecord == NULL) 
return "";
 
 6250  iTargetField = CSVGetFileFieldId(pszFilename, pszTargetField);
 
 6251  if (iTargetField < 0) 
return "";
 
 6253  if (iTargetField >= CSLCount(papszRecord)) 
return "";
 
 6255  return (papszRecord[iTargetField]);
 
 6269bool s57_GetChartExtent(
const wxString &FullPath, 
Extent *pext) {
 
 6293                                  std::vector<s57Sector_t> §orlegs) {
 
 6294  float rangeScale = 0.0;
 
 6296  if (sectorlegs.size() > 0) {
 
 6297    std::vector<int> sectorangles;
 
 6298    for (
unsigned int i = 0; i < sectorlegs.size(); i++) {
 
 6299      if (fabs(sectorlegs[i].sector1 - sectorlegs[i].sector2) < 0.3) 
continue;
 
 6302      ll_gc_ll(sectorlegs[i].pos.m_y, sectorlegs[i].pos.m_x,
 
 6303               sectorlegs[i].sector1 + 180.0, sectorlegs[i].range, &endy,
 
 6308      ll_gc_ll(sectorlegs[i].pos.m_y, sectorlegs[i].pos.m_x,
 
 6309               sectorlegs[i].sector2 + 180.0, sectorlegs[i].range, &endy,
 
 6315          viewport.
GetPixFromLL(sectorlegs[i].pos.m_y, sectorlegs[i].pos.m_x);
 
 6318      float rangePx = sqrtf(powf((
float)(lightPos.x - end1.x), 2) +
 
 6319                            powf((
float)(lightPos.y - end1.y), 2));
 
 6321      if (rangeScale == 0.0) {
 
 6324          rangeScale *= (viewport.
pix_height / 3) / rangePx;
 
 6328      rangePx = rangePx * rangeScale;
 
 6330      int penWidth = rangePx / 8;
 
 6331      penWidth = wxMin(20, penWidth);
 
 6332      penWidth = wxMax(5, penWidth);
 
 6335      wxPen *arcpen = wxThePenList->FindOrCreatePen(sectorlegs[i].color,
 
 6336                                                    penWidth, wxPENSTYLE_SOLID);
 
 6337      arcpen->SetCap(wxCAP_BUTT);
 
 6340      float angle1, angle2;
 
 6341      angle1 = -(sectorlegs[i].sector2 + 90.0) - viewport.
rotation * 180.0 / PI;
 
 6342      angle2 = -(sectorlegs[i].sector1 + 90.0) - viewport.
rotation * 180.0 / PI;
 
 6343      if (angle1 > angle2) {
 
 6346      int lpx = lightPos.x;
 
 6347      int lpy = lightPos.y;
 
 6349      wxPoint arcpoints[150];  
 
 6352      while ((step < 15) && ((rangePx * sin(step * PI / 180.)) < 10))
 
 6356      int narc = (angle2 - angle1) / step;
 
 6358      step = (angle2 - angle1) / (
float)narc;
 
 6360      if (sectorlegs[i].isleading && (angle2 - angle1 < 60)) {
 
 6361        wxPoint yellowCone[3];
 
 6362        yellowCone[0] = lightPos;
 
 6363        yellowCone[1] = end1;
 
 6364        yellowCone[2] = end2;
 
 6365        arcpen = wxThePenList->FindOrCreatePen(wxColor(0, 0, 0, 0), 1,
 
 6368        wxColor c = sectorlegs[i].color;
 
 6369        c.Set(c.Red(), c.Green(), c.Blue(), 0.6 * c.Alpha());
 
 6370        dc.SetBrush(wxBrush(c));
 
 6371        dc.StrokePolygon(3, yellowCone, 0, 0);
 
 6374        for (
float a = angle1; a <= angle2 + 0.1; a += step) {
 
 6375          int x = lpx + (int)(rangePx * cos(a * PI / 180.));
 
 6376          int y = lpy - (int)(rangePx * sin(a * PI / 180.));
 
 6377          arcpoints[npoints].x = x;
 
 6378          arcpoints[npoints].y = y;
 
 6381        dc.StrokeLines(npoints, arcpoints);
 
 6385      arcpen = wxThePenList->FindOrCreatePen(wxColor(0, 0, 0, legOpacity), 1,
 
 6391      bool haveAngle1 = 
false;
 
 6392      bool haveAngle2 = 
false;
 
 6393      int sec1 = (int)sectorlegs[i].sector1;
 
 6394      int sec2 = (int)sectorlegs[i].sector2;
 
 6395      if (sec1 > 360) sec1 -= 360;
 
 6396      if (sec2 > 360) sec2 -= 360;
 
 6398      if ((sec2 == 360) && (sec1 == 0))  
 
 6401      for (
unsigned int j = 0; j < sectorangles.size(); j++) {
 
 6402        if (sectorangles[j] == sec1) haveAngle1 = 
true;
 
 6403        if (sectorangles[j] == sec2) haveAngle2 = 
true;
 
 6407        dc.StrokeLine(lightPos, end1);
 
 6408        sectorangles.push_back(sec1);
 
 6412        dc.StrokeLine(lightPos, end2);
 
 6413        sectorangles.push_back(sec2);
 
 6420void s57_DrawExtendedLightSectorsGL(
ocpnDC &dc, 
ViewPort &viewport,
 
 6421                                    std::vector<s57Sector_t> §orlegs) {
 
 6422  float rangeScale = 0.0;
 
 6424  if (sectorlegs.size() > 0) {
 
 6425    std::vector<int> sectorangles;
 
 6426    for (
unsigned int i = 0; i < sectorlegs.size(); i++) {
 
 6427      if (fabs(sectorlegs[i].sector1 - sectorlegs[i].sector2) < 0.3) 
continue;
 
 6430      ll_gc_ll(sectorlegs[i].pos.m_y, sectorlegs[i].pos.m_x,
 
 6431               sectorlegs[i].sector1 + 180.0, sectorlegs[i].range, &endy,
 
 6436      ll_gc_ll(sectorlegs[i].pos.m_y, sectorlegs[i].pos.m_x,
 
 6437               sectorlegs[i].sector2 + 180.0, sectorlegs[i].range, &endy,
 
 6443          viewport.
GetPixFromLL(sectorlegs[i].pos.m_y, sectorlegs[i].pos.m_x);
 
 6446      float rangePx = sqrtf(powf((
float)(lightPos.x - end1.x), 2) +
 
 6447                            powf((
float)(lightPos.y - end1.y), 2));
 
 6449      if (rangeScale == 0.0) {
 
 6452          rangeScale *= (viewport.
pix_height / 3) / rangePx;
 
 6456      rangePx = rangePx * rangeScale;
 
 6458      float arcw = rangePx / 10;
 
 6459      arcw = wxMin(20, arcw);
 
 6460      arcw = wxMax(5, arcw);
 
 6464      float angle1, angle2;
 
 6465      angle1 = -(sectorlegs[i].sector2 + 90.0) - viewport.
rotation * 180.0 / PI;
 
 6466      angle2 = -(sectorlegs[i].sector1 + 90.0) - viewport.
rotation * 180.0 / PI;
 
 6467      if (angle1 > angle2) {
 
 6470      int lpx = lightPos.x;
 
 6471      int lpy = lightPos.y;
 
 6473      if (sectorlegs[i].isleading && (angle2 - angle1 < 60)) {
 
 6474        wxPoint yellowCone[3];
 
 6475        yellowCone[0] = lightPos;
 
 6476        yellowCone[1] = end1;
 
 6477        yellowCone[2] = end2;
 
 6478        wxPen *arcpen = wxThePenList->FindOrCreatePen(wxColor(0, 0, 0, 0), 1,
 
 6481        wxColor c = sectorlegs[i].color;
 
 6482        c.Set(c.Red(), c.Green(), c.Blue(), 0.6 * c.Alpha());
 
 6483        dc.SetBrush(wxBrush(c));
 
 6484        dc.StrokePolygon(3, yellowCone, 0, 0);
 
 6488        wxPoint r(lpx, lpy);
 
 6491        float rad = rangePx;
 
 6515        GLint mPosAttrib = glGetAttribLocation(shader->programId(), 
"aPos");
 
 6518        glBindBuffer(GL_ARRAY_BUFFER, 0);
 
 6519        glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
 
 6521        glVertexAttribPointer(mPosAttrib, 2, GL_FLOAT, GL_FALSE, 0, coords);
 
 6522        glEnableVertexAttribArray(mPosAttrib);
 
 6526            glGetUniformLocation(shader->programId(), 
"circle_radius");
 
 6527        glUniform1f(radiusloc, rad);
 
 6531            glGetUniformLocation(shader->programId(), 
"circle_center");
 
 6535        glUniform2fv(centerloc, 1, ctrv);
 
 6538        wxColour colorb = sectorlegs[i].color;
 
 6540        colorv[0] = colorb.Red() / float(256);
 
 6541        colorv[1] = colorb.Green() / float(256);
 
 6542        colorv[2] = colorb.Blue() / float(256);
 
 6543        colorv[3] = colorb.Alpha() / float(256);
 
 6546            glGetUniformLocation(shader->programId(), 
"circle_color");
 
 6547        glUniform4fv(colloc, 1, colorv);
 
 6557            glGetUniformLocation(shader->programId(), 
"border_color");
 
 6558        glUniform4fv(bcolloc, 1, bcolorv);
 
 6561        GLint borderWidthloc =
 
 6562            glGetUniformLocation(shader->programId(), 
"border_width");
 
 6563        glUniform1f(borderWidthloc, 2);
 
 6566        GLint ringWidthloc =
 
 6567            glGetUniformLocation(shader->programId(), 
"ring_width");
 
 6568        glUniform1f(ringWidthloc, arcw);
 
 6572            sectorlegs[i].sector1 + (viewport.
rotation * 180 / PI) + 180;
 
 6573        if (sr1 > 360.) sr1 -= 360.;
 
 6575            sectorlegs[i].sector2 + (viewport.
rotation * 180 / PI) + 180;
 
 6576        if (sr2 > 360.) sr2 -= 360.;
 
 6588        if ((sb < 0) || (se < 0)) {
 
 6594            glGetUniformLocation(shader->programId(), 
"sector_1");
 
 6595        glUniform1f(sector1loc, (sb * PI / 180.));
 
 6597            glGetUniformLocation(shader->programId(), 
"sector_2");
 
 6598        glUniform1f(sector2loc, (se * PI / 180.));
 
 6603        mat4x4_translate_in_place(I, r.x, r.y, 0);
 
 6606            glGetUniformLocation(shader->programId(), 
"TransformMatrix");
 
 6607        glUniformMatrix4fv(matloc, 1, GL_FALSE, (
const GLfloat *)I);
 
 6610        glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
 
 6614        mat4x4_identity(IM);
 
 6616            glGetUniformLocation(shader->programId(), 
"TransformMatrix");
 
 6617        glUniformMatrix4fv(matlocf, 1, GL_FALSE, (
const GLfloat *)IM);
 
 6619        glDisableVertexAttribArray(mPosAttrib);
 
 6625      wxPen *arcpen = wxThePenList->FindOrCreatePen(wxColor(0, 0, 0, 128), 1,
 
 6630      bool haveAngle1 = 
false;
 
 6631      bool haveAngle2 = 
false;
 
 6632      int sec1 = (int)sectorlegs[i].sector1;
 
 6633      int sec2 = (int)sectorlegs[i].sector2;
 
 6634      if (sec1 > 360) sec1 -= 360;
 
 6635      if (sec2 > 360) sec2 -= 360;
 
 6637      if ((sec2 == 360) && (sec1 == 0))  
 
 6640      for (
unsigned int j = 0; j < sectorangles.size(); j++) {
 
 6641        if (sectorangles[j] == sec1) haveAngle1 = 
true;
 
 6642        if (sectorangles[j] == sec2) haveAngle2 = 
true;
 
 6646        dc.StrokeLine(lightPos, end1);
 
 6647        sectorangles.push_back(sec1);
 
 6651        dc.StrokeLine(lightPos, end2);
 
 6652        sectorangles.push_back(sec2);
 
 6660bool s57_ProcessExtendedLightSectors(
ChartCanvas *cc,
 
 6663                                     ListOfObjRazRules *rule_list,
 
 6664                                     ListOfPI_S57Obj *pi_rule_list,
 
 6665                                     std::vector<s57Sector_t> §orlegs) {
 
 6666  bool newSectorsNeedDrawing = 
false;
 
 6668  bool bhas_red_green = 
false;
 
 6669  bool bleading_attribute = 
false;
 
 6672  if (cc->GetColorScheme() == GLOBAL_COLOR_SCHEME_DUSK) opacity = 50;
 
 6673  if (cc->GetColorScheme() == GLOBAL_COLOR_SCHEME_NIGHT) opacity = 20;
 
 6675  int yOpacity = (float)opacity *
 
 6678  if (target_plugin_chart || Chs57) {
 
 6681    wxPoint2DDouble objPos;
 
 6683    char *curr_att = NULL;
 
 6685    wxArrayOfS57attVal *attValArray = NULL;
 
 6687    ListOfObjRazRules::Node *snode = NULL;
 
 6688    ListOfPI_S57Obj::Node *pnode = NULL;
 
 6690    if (Chs57 && rule_list)
 
 6691      snode = rule_list->GetLast();
 
 6692    else if (target_plugin_chart && pi_rule_list)
 
 6693      pnode = pi_rule_list->GetLast();
 
 6696      wxPoint2DDouble lightPosD(0, 0);
 
 6697      bool is_light = 
false;
 
 6701        ObjRazRules *current = snode->GetData();
 
 6702        S57Obj *light = current->obj;
 
 6703        if (!strcmp(light->FeatureName, 
"LIGHTS")) {
 
 6704          objPos = wxPoint2DDouble(light->m_lat, light->m_lon);
 
 6705          curr_att = light->att_array;
 
 6706          n_attr = light->n_attr;
 
 6707          attValArray = light->attVal;
 
 6710      } 
else if (target_plugin_chart) {
 
 6714          objPos = wxPoint2DDouble(light->
m_lat, light->
m_lon);
 
 6717          attValArray = light->
attVal;
 
 6727      wxString curAttrName;
 
 6730      if (lightPosD.m_x == 0 && lightPosD.m_y == 0.0) lightPosD = objPos;
 
 6732      if (is_light && (lightPosD == objPos)) {
 
 6740          bleading_attribute = 
false;
 
 6742          while (attrCounter < n_attr) {
 
 6743            curAttrName = wxString(curr_att, wxConvUTF8, 6);
 
 6746            S57attVal *pAttrVal = NULL;
 
 6749                pAttrVal = attValArray->Item(attrCounter);
 
 6750              else if (target_plugin_chart)
 
 6751                pAttrVal = attValArray->Item(attrCounter);
 
 6755                s57chart::GetAttributeValueAsString(pAttrVal, curAttrName);
 
 6757            if (curAttrName == _T(
"LITVIS")) {
 
 6758              if (value.StartsWith(_T(
"obsc"))) bviz = 
false;
 
 6760            if (curAttrName == _T(
"SECTR1")) value.ToDouble(§r1);
 
 6761            if (curAttrName == _T(
"SECTR2")) value.ToDouble(§r2);
 
 6762            if (curAttrName == _T(
"VALNMR")) value.ToDouble(&valnmr);
 
 6763            if (curAttrName == _T(
"COLOUR")) {
 
 6764              if (value == _T(
"red(3)")) {
 
 6765                color = wxColor(255, 0, 0, opacity);
 
 6766                sector.iswhite = 
false;
 
 6767                bhas_red_green = 
true;
 
 6770              if (value == _T(
"green(4)")) {
 
 6771                color = wxColor(0, 255, 0, opacity);
 
 6772                sector.iswhite = 
false;
 
 6773                bhas_red_green = 
true;
 
 6777            if (curAttrName == _T(
"EXCLIT")) {
 
 6778              if (value.Find(_T(
"(3)"))) valnmr = 1.0;  
 
 6781            if (curAttrName == _T(
"CATLIT")) {
 
 6782              if (value.Upper().StartsWith(_T(
"DIRECT")) ||
 
 6783                  value.Upper().StartsWith(_T(
"LEAD")))
 
 6784                bleading_attribute = 
true;
 
 6791          if ((sectr1 >= 0) && (sectr2 >= 0)) {
 
 6792            if (sectr1 > sectr2) {  
 
 6796            sector.pos.m_x = objPos.m_y;  
 
 6797            sector.pos.m_y = objPos.m_x;
 
 6800                (valnmr > 0.0) ? valnmr : 2.5;  
 
 6801            sector.sector1 = sectr1;
 
 6802            sector.sector2 = sectr2;
 
 6804            if (!color.IsOk()) {
 
 6805              color = wxColor(255, 255, 0, yOpacity);
 
 6806              sector.iswhite = 
true;
 
 6808            sector.color = color;
 
 6809            sector.isleading = 
false;  
 
 6811            if (bleading_attribute) sector.isleading = 
true;
 
 6813            bool newsector = 
true;
 
 6814            for (
unsigned int i = 0; i < sectorlegs.size(); i++) {
 
 6815              if (sectorlegs[i].pos == sector.pos &&
 
 6816                  sectorlegs[i].sector1 == sector.sector1 &&
 
 6817                  sectorlegs[i].sector2 == sector.sector2) {
 
 6823                sectorlegs[i].range = wxMax(sectorlegs[i].range, sector.range);
 
 6827            if (!bviz) newsector = 
false;
 
 6829            if ((sector.sector2 == 360) && (sector.sector1 == 0))  
 
 6833              sectorlegs.push_back(sector);
 
 6834              newSectorsNeedDrawing = 
true;
 
 6841        snode = snode->GetPrevious();
 
 6842      else if (target_plugin_chart)
 
 6843        pnode = pnode->GetPrevious();
 
 6851  for (
unsigned int i = 0; i < sectorlegs.size(); i++) {
 
 6852    if (((sectorlegs[i].sector2 - sectorlegs[i].sector1) < 15)) {
 
 6853      if (sectorlegs[i].iswhite && bhas_red_green)
 
 6854        sectorlegs[i].isleading = 
true;
 
 6858  return newSectorsNeedDrawing;
 
 6861bool s57_GetVisibleLightSectors(
ChartCanvas *cc, 
double lat, 
double lon,
 
 6863                                std::vector<s57Sector_t> §orlegs) {
 
 6864  if (!cc) 
return false;
 
 6866  static float lastLat, lastLon;
 
 6868  if (!ps52plib) 
return false;
 
 6876  if (cc->m_singleChart &&
 
 6877      (cc->m_singleChart->GetChartFamily() == CHART_FAMILY_VECTOR))
 
 6878    target_chart = cc->m_singleChart;
 
 6879  else if (viewport.b_quilt)
 
 6880    target_chart = cc->m_pQuilt->GetChartAtPix(viewport, calcPoint);
 
 6882    target_chart = NULL;
 
 6885    if ((target_chart->GetChartType() == CHART_TYPE_PLUGIN) &&
 
 6886        (target_chart->GetChartFamily() == CHART_FAMILY_VECTOR))
 
 6889      Chs57 = 
dynamic_cast<s57chart *
>(target_chart);
 
 6892  bool newSectorsNeedDrawing = 
false;
 
 6894  if (target_plugin_chart || Chs57) {
 
 6895    ListOfObjRazRules *rule_list = NULL;
 
 6896    ListOfPI_S57Obj *pi_rule_list = NULL;
 
 6903          Chs57->GetLightsObjRuleListVisibleAtLatLon(lat, lon, &viewport);
 
 6904    else if (target_plugin_chart)
 
 6905      pi_rule_list = g_pi_manager->GetLightsObjRuleListVisibleAtLatLon(
 
 6906          target_plugin_chart, lat, lon, viewport);
 
 6908    newSectorsNeedDrawing = s57_ProcessExtendedLightSectors(
 
 6909        cc, target_plugin_chart, Chs57, rule_list, pi_rule_list, sectorlegs);
 
 6917      pi_rule_list->Clear();
 
 6918      delete pi_rule_list;
 
 6922  return newSectorsNeedDrawing;
 
 6925bool s57_CheckExtendedLightSectors(
ChartCanvas *cc, 
int mx, 
int my,
 
 6927                                   std::vector<s57Sector_t> §orlegs) {
 
 6928  if (!cc) 
return false;
 
 6930  double cursor_lat, cursor_lon;
 
 6931  static float lastLat, lastLon;
 
 6933  if (!ps52plib || !ps52plib->m_bExtendLightSectors) 
return false;
 
 6938  ChartBase *target_chart = cc->GetChartAtCursor();
 
 6940    if ((target_chart->GetChartType() == CHART_TYPE_PLUGIN) &&
 
 6941        (target_chart->GetChartFamily() == CHART_FAMILY_VECTOR))
 
 6944      Chs57 = 
dynamic_cast<s57chart *
>(target_chart);
 
 6949  if (lastLat == cursor_lat && lastLon == cursor_lon) 
return false;
 
 6951  lastLat = cursor_lat;
 
 6952  lastLon = cursor_lon;
 
 6953  bool newSectorsNeedDrawing = 
false;
 
 6955  if (target_plugin_chart || Chs57) {
 
 6956    ListOfObjRazRules *rule_list = NULL;
 
 6957    ListOfPI_S57Obj *pi_rule_list = NULL;
 
 6963      rule_list = Chs57->GetObjRuleListAtLatLon(
 
 6964          cursor_lat, cursor_lon, selectRadius, &viewport, MASK_POINT);
 
 6965    else if (target_plugin_chart)
 
 6966      pi_rule_list = g_pi_manager->GetPlugInObjRuleListAtLatLon(
 
 6967          target_plugin_chart, cursor_lat, cursor_lon, selectRadius, viewport);
 
 6969    newSectorsNeedDrawing = s57_ProcessExtendedLightSectors(
 
 6970        cc, target_plugin_chart, Chs57, rule_list, pi_rule_list, sectorlegs);
 
 6978      pi_rule_list->Clear();
 
 6979      delete pi_rule_list;
 
 6983  return newSectorsNeedDrawing;
 
Base class for all chart types.
ChartCanvas - Main chart display and interaction component.
void GetCanvasPixPoint(double x, double y, double &lat, double &lon)
Convert canvas pixel coordinates (physical pixels) to latitude/longitude.
Wrapper class for plugin-based charts.
Wrapper class for OpenGL shader programs.
An iterator class for OCPNRegion.
A wrapper class for wxRegion with additional functionality.
Class representing an S-57 chart object.
double m_lon
Reference longitude.
int n_attr
Number of attributes.
char * att_array
Array of attribute types.
double m_lat
Reference latitude.
char FeatureName[8]
S-57 feature type code (e.g., "DEPARE")
wxArrayOfS57attVal * attVal
Array of attribute values.
Represents a light feature in an S57 chart.
Manager for S57 chart SENC creation threads.
ViewPort - Core geographic projection and coordinate transformation engine.
double view_scale_ppm
Requested view scale in physical pixels per meter (ppm), before applying projections.
double ref_scale
The nominal scale of the "reference chart" for this view.
int pix_height
Height of the viewport in physical pixels.
double rotation
Rotation angle of the viewport in radians.
int pix_width
Width of the viewport in physical pixels.
double skew
Angular distortion (shear transform) applied to the viewport in radians.
void GetLLFromPix(const wxPoint &p, double *lat, double *lon)
Convert physical pixel coordinates on the ViewPort to latitude and longitude.
double clon
Center longitude of the viewport in degrees.
double clat
Center latitude of the viewport in degrees.
wxPoint GetPixFromLL(double lat, double lon)
Convert latitude and longitude on the ViewPort to physical pixel coordinates.
double chart_scale
Chart scale denominator (e.g., 50000 for a 1:50000 scale).
Device context class that can use either wxDC or OpenGL for drawing.
Represents an S57 format electronic navigational chart in OpenCPN.
General purpose GUI support.
Enhanced logging interface on top of wx/log.h.
void fromSM_Plugin(double x, double y, double lat0, double lon0, double *lat, double *lon)
Converts Simple Mercator coordinates to geographic.
wxWindow * GetOCPNCanvasWindow()
Gets OpenCPN's main canvas window.
Tools to send data to plugins.
Represents a sector of a light in an S57 chart.