From 291bb93de182bf6c8f4eb30a51281a962d487e45 Mon Sep 17 00:00:00 2001 From: Xun Li Date: Fri, 10 Jun 2016 11:51:37 -0700 Subject: [PATCH 1/6] update version to 1.8.8 --- version.h | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/version.h b/version.h index e98aa7c7b..0981e37a0 100644 --- a/version.h +++ b/version.h @@ -1,11 +1,11 @@ namespace Gda { const int version_major = 1; const int version_minor = 8; - const int version_build = 7; - const int version_subbuild = 11; + const int version_build = 8; + const int version_subbuild = 0; const int version_year = 2016; - const int version_month = 5; - const int version_day = 26; + const int version_month = 6; + const int version_day = 10; const int version_night = 0; const int version_type = 1; // 0: alpha, 1: beta, 2: release } From 1d44e0f78db4b08b5b7a8f1b70ba9e84c45a2a65 Mon Sep 17 00:00:00 2001 From: Xun Li Date: Thu, 23 Jun 2016 15:40:09 -0700 Subject: [PATCH 2/6] #447 fix exporting with dup field names Update the internal logic&datastructure of field names to allow detect dup field names when save/saveas --- DataViewer/OGRTable.cpp | 133 +++++++++++++++---- DataViewer/OGRTable.h | 4 + DialogTools/FieldNameCorrectionDlg.cpp | 174 ++++++++++++++++++++----- DialogTools/FieldNameCorrectionDlg.h | 10 ++ GdaConst.cpp | 10 ++ ShapeOperations/OGRDataAdapter.cpp | 43 +++--- ShapeOperations/OGRDatasourceProxy.cpp | 60 ++++----- ShapeOperations/OGRDatasourceProxy.h | 1 - ShapeOperations/OGRLayerProxy.cpp | 42 ++++-- ShapeOperations/OGRLayerProxy.h | 1 - 10 files changed, 353 insertions(+), 125 deletions(-) diff --git a/DataViewer/OGRTable.cpp b/DataViewer/OGRTable.cpp index 10df8cf47..95bae1028 100644 --- a/DataViewer/OGRTable.cpp +++ b/DataViewer/OGRTable.cpp @@ -50,8 +50,10 @@ OGRTable::OGRTable(int n_rows) //table_state->registerObserver(this); } -OGRTable::OGRTable(OGRLayerProxy* _ogr_layer, GdaConst::DataSourceType ds_type, - TableState* table_state, TimeState* time_state, +OGRTable::OGRTable(OGRLayerProxy* _ogr_layer, + GdaConst::DataSourceType ds_type, + TableState* table_state, + TimeState* time_state, const VarOrderPtree& var_order_ptree) : TableInterface(table_state, time_state), ogr_layer(_ogr_layer), var_order(var_order_ptree), datasource_type(ds_type) @@ -63,7 +65,9 @@ ogr_layer(_ogr_layer), var_order(var_order_ptree), datasource_type(ds_type) // create in memory OGRColumns, read var_map for (size_t i=0; ifields.size(); ++i) { AddOGRColumn(ogr_layer, i); - var_map[columns[i]->GetName()] = i; + // deprecated in 1.8 + //var_map[columns[i]->GetName()] = i; + org_var_names.push_back(columns[i]->GetName()); } /* @@ -216,7 +220,10 @@ void OGRTable::AddOGRColumn(OGRColumn* ogr_col) columns.push_back(ogr_col); - var_map[ogr_col->GetName()] = pos; + + // deprecated in 1.8 + //var_map[ogr_col->GetName()] = pos; + org_var_names.push_back(ogr_col->GetName()); } @@ -358,19 +365,29 @@ int OGRTable::FindColId(const wxString& name) return var_order.GetColId(c_name); } + int OGRTable::GetColIdx(const wxString& name) { wxString c_name = name; c_name.Trim(false); c_name.Trim(true); - + + // update it if different in real data. E.g. user may create a column + // with name in lowercase, however, it is forced to uppercase in real table + + /* + // deprecated in 1.8.8 std::map::iterator i; if ( (i=var_map.find(name)) != var_map.end() || (i=var_map.find(name.Upper())) != var_map.end() || (i=var_map.find(name.Lower())) != var_map.end() ) { return i->second; } - + */ + for (size_t i=0; i::iterator i; if ( (i=var_map.find(name_in_project)) != var_map.end() || (i=var_map.find(name_in_project.Upper())) != var_map.end() || (i=var_map.find(name_in_project.Lower())) != var_map.end() ) { + OGRColumn* ogr_col = columns[i->second]; if (name_in_project != ogr_col->GetName() ) { name_in_project = ogr_col->GetName(); var_order.FindVarGroup(col).name = name_in_project; } } + */ + return name_in_project; } @@ -942,10 +966,12 @@ bool OGRTable::RenameSimpleCol(int col, int time, const wxString& new_name) { if (!PermitRenameSimpleCol()) return false; if (col < 0 || col >= GetNumberCols()) return false; + int ogr_col_id = FindOGRColId(col, time); if (ogr_col_id == wxNOT_FOUND) return false; wxString old_name = GetColName(col, time); + OGRColumn* cur_col = columns[ogr_col_id]; operations_queue.push(new OGRTableOpRenameColumn(cur_col, cur_col->GetName(), @@ -953,15 +979,19 @@ bool OGRTable::RenameSimpleCol(int col, int time, const wxString& new_name) cur_col->Rename(new_name); //update var_map + /* + //deprecated in 1.8.8 map::iterator it = var_map.find(old_name); if ( it == var_map.end()) it = var_map.find(old_name.Upper()); if ( it == var_map.end()) it = var_map.find(old_name.Lower()); if ( it == var_map.end()) return false; - int ogr_col_idx = it->second; var_map[new_name] = ogr_col_idx; var_map.erase(it); + */ + org_var_names[ogr_col_id] = new_name; + // update variable parameters var_order.SetGroupName(col, new_name); @@ -1031,18 +1061,29 @@ bool OGRTable::SetCellFromString(int row, int col, int time, int OGRTable::InsertCol(GdaConst::FieldType type, const wxString& name, int pos, int time_steps, int field_len, int decimals) { - using namespace std; - if (pos > GetNumberCols()) return -1; - // this case if for appending new column at the end of table - if (pos < 0) pos = GetNumberCols(); - LOG_MSG(wxString::Format("Inserting column into table at postion %d",pos)); - if (time_steps < 0) return -1; - if (type != GdaConst::double_type) decimals = 0; + if (pos > GetNumberCols() || time_steps < 0 ) { + return -1; + } + + // don't support the following column type if (field_len == -1 && (type == GdaConst::placeholder_type || type == GdaConst::unknown_type || - type == GdaConst::date_type)){ + type == GdaConst::date_type)) + { return -1; } + + // if for appending new column at the end of table + if (pos < 0) { + pos = GetNumberCols(); + } + + LOG_MSG(wxString::Format("Inserting column into table at postion %d",pos)); + + if (type != GdaConst::double_type) { + decimals = 0; + } + if (field_len == -1) { if (type == GdaConst::double_type) { field_len = GdaConst::default_dbf_double_len; @@ -1052,6 +1093,7 @@ int OGRTable::InsertCol(GdaConst::FieldType type, const wxString& name, int pos, field_len = GdaConst::default_dbf_string_len; } } + if (decimals < 0) { if (type == GdaConst::double_type) { decimals = GdaConst::default_display_decimals; @@ -1062,16 +1104,19 @@ int OGRTable::InsertCol(GdaConst::FieldType type, const wxString& name, int pos, // note the differences between "Group/Ungroup" and "Append new column" // (new field always added to the end of existing columns) + vector names(SuggestDBColNames(name, name, time_steps)); + // return could be group of names (e.g. pop2001, pop2002, pop2003) for (size_t t=0; t::iterator iter = org_var_names.begin() + pos; + org_var_names.insert(iter, names[t]); } - var_map.clear(); + + /* + // deprecated in 1.8.8, see ogr_var_names in the above code + var_map.clear(); for (size_t i=0; iGetName()] = i; } + */ VarGroup g(name, decimals); - if (time_steps > 1) g.vars = names; + if (time_steps > 1) { + g.vars = names; + } var_order.InsertVarGroup(g, pos); SetChangedSinceLastSave(true); @@ -1107,11 +1161,14 @@ int OGRTable::InsertCol(GdaConst::FieldType type, const wxString& name, int pos, bool OGRTable::DeleteCol(int pos) { - using namespace std; LOG_MSG("Inside OGRTable::DeleteCol"); LOG_MSG(wxString::Format("Deleting column from table at postion %d", pos)); - if (pos < 0 || pos >= var_order.GetNumVarGroups() || - var_order.GetNumVarGroups() == 0) return false; + if (pos < 0 || + pos >= var_order.GetNumVarGroups() || + var_order.GetNumVarGroups() == 0) + { + return false; + } // Must remove all items from var_map first VarGroup vg = var_order.FindVarGroup(pos); @@ -1121,18 +1178,23 @@ bool OGRTable::DeleteCol(int pos) if (s != "") { for( size_t i=0; iGetName().CmpNoCase(s) == 0) { - operations_queue.push( - new OGRTableOpDeleteColumn(columns[i])); + operations_queue.push(new OGRTableOpDeleteColumn(columns[i])); columns.erase(columns.begin()+i); break; } } } } + + /* + // depcrecated in 1.8.8 var_map.clear(); for (int i=0; iGetName()] = i; } + */ + vector::iterator iter = org_var_names.begin() + pos; + org_var_names.erase(iter); wxString name = var_order.GetGroupName(pos); var_order.RemoveVarGroup(pos); @@ -1151,7 +1213,6 @@ bool OGRTable::DeleteCol(int pos) void OGRTable::UngroupCol(int col) { - using namespace std; LOG_MSG("Inside OGRTable::UngroupCol"); if (col < 0 || col >= var_order.GetNumVarGroups()) return; if (GetColTimeSteps(col) <= 1) return; @@ -1192,7 +1253,6 @@ void OGRTable::UngroupCol(int col) void OGRTable::GroupCols(const std::vector& cols, const wxString& name, int pos) { - using namespace std; LOG_MSG("Inside OGRTable::GroupCols"); if (pos < 0 || pos > var_order.GetNumVarGroups()) return; if (cols.size() <= 1) return; @@ -1327,12 +1387,21 @@ int OGRTable::FindOGRColId(int wxgrid_col_pos, int time) int OGRTable::FindOGRColId(const wxString& name) { + for (size_t i=0; i < org_var_names.size(); i++ ) { + if (name == org_var_names[i]) { + return i; + } + } + return -1; + /* + // deprecated in 1.8.8 std::map::iterator i = var_map.find(name); if ( i == var_map.end()) i = var_map.find(name.Upper()); if ( i == var_map.end()) i = var_map.find(name.Lower()); if ( i == var_map.end()) return -1; return i->second; + */ } OGRColumn* OGRTable::FindOGRColumn(int col, int time) @@ -1345,12 +1414,22 @@ OGRColumn* OGRTable::FindOGRColumn(int col, int time) OGRColumn* OGRTable::FindOGRColumn(const wxString& name) { if (name.IsEmpty()) return NULL; + + for (size_t i=0; i::iterator i =var_map.find(name); if ( i == var_map.end()) i = var_map.find(name.Upper()); if ( i == var_map.end()) i = var_map.find(name.Lower()); if ( i == var_map.end()) return NULL; return columns[i->second]; + */ } bool OGRTable::IsValidDBColName(const wxString& col_nm, diff --git a/DataViewer/OGRTable.h b/DataViewer/OGRTable.h index 547ca6cef..d7286dbf4 100644 --- a/DataViewer/OGRTable.h +++ b/DataViewer/OGRTable.h @@ -51,7 +51,11 @@ class OGRTable : public TableInterface, TableStateObserver OGRLayerProxy* ogr_layer; vector columns; VarOrderMapper var_order; + // var_map will be deprecate in 1.8.8, and replace by _var_names map var_map; + vector org_var_names; + vector new_var_names; + // queues of table operations queue operations_queue; stack completed_stack; diff --git a/DialogTools/FieldNameCorrectionDlg.cpp b/DialogTools/FieldNameCorrectionDlg.cpp index a684c8178..e47a12cde 100644 --- a/DialogTools/FieldNameCorrectionDlg.cpp +++ b/DialogTools/FieldNameCorrectionDlg.cpp @@ -44,28 +44,38 @@ ScrolledWidgetsPane::ScrolledWidgetsPane(wxWindow* parent, wxWindowID id, { vector merged_field_names; set bad_fnames, dup_fname, uniq_upper_fname; + + vector bad_fname_idx_s, dup_fname_idx_s; for (int i=0; i < all_fname.size(); i++) { wxString field_name = all_fname[i]; - field_names_dict[field_name] = field_name; + + old_field_names.push_back(field_name); + new_field_names.push_back(field_name); + + field_names_dict[field_name] = field_name; bool isValid = true; if (!IsFieldNameValid(field_name) ) { bad_fnames.insert(field_name); + bad_fname_idx_s.push_back(i); isValid = false; } else if (uniq_upper_fname.find(field_name.Upper()) != uniq_upper_fname.end()) { dup_fname.insert(field_name); + dup_fname_idx_s.push_back(i); isValid = false; } + uniq_upper_fname.insert(field_name.Upper()); if (isValid) merged_field_names.push_back(field_name); } - if (!bad_fnames.empty() || !dup_fname.empty()) { + if (!dup_fname_idx_s.empty() || !bad_fname_idx_s.empty()) { need_correction = true; - Init(merged_field_names, dup_fname, bad_fnames); + //Init(merged_field_names, dup_fname, bad_fnames); + Init(dup_fname_idx_s, bad_fname_idx_s); } } @@ -91,6 +101,106 @@ ScrolledWidgetsPane::~ScrolledWidgetsPane() } } +void ScrolledWidgetsPane::Init(vector& dup_fname_idx_s, + vector& bad_fname_idx_s) +{ + // the sizer will take care of determining the needed scroll size + // (if you don't use sizers you will need to manually set the viewport size) + + // build a dict for searching duplicated field + for (size_t i=0; iAdd(txt_oname, 0, wxALIGN_LEFT|wxALL, 15); + wxStaticText* txt_newname=new wxStaticText(this,wxID_ANY,"New field name:"); + sizer->Add(txt_newname, 0, wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL|wxALL, 15); + wxStaticText* txt_orest=new wxStaticText(this, wxID_ANY, "Field restrictions:"); + sizer->Add(txt_orest, 0, wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL|wxALL, 15); + + size_t ctrl_cnt = 0; + + for (size_t i=0; i < dup_fname_idx_s.size(); i++) + { + int fname_idx = dup_fname_idx_s[i]; + wxString field_name = old_field_names[fname_idx]; + warn_msg=DUP_WARN; + + wxString id_name1, id_name2, id_name3; + id_name1 << "ID_FNAME_STAT_TXT_BASE" << ctrl_cnt; + id_name2 << "ID_INPUT_TXT_CTRL_BASE" << ctrl_cnt; + id_name3 << "ID_INFO_STAT_TXT_BASE" << ctrl_cnt; + + wxString user_field_name = GetSuggestFieldName(fname_idx); + txt_fname.push_back(new wxStaticText(this, XRCID(id_name1), field_name)); + sizer->Add(txt_fname[ctrl_cnt], 0, wxALIGN_LEFT|wxALL, 5); + txt_input.push_back(new wxTextCtrl(this, XRCID(id_name2), user_field_name, wxDefaultPosition,wxSize(240,-1))); + sizer->Add(txt_input[ctrl_cnt], 0, wxALIGN_LEFT|wxALL, 5); + txt_info.push_back(new wxStaticText(this, XRCID(id_name3), warn_msg)); + sizer->Add(txt_info[ctrl_cnt], 0, wxALIGN_LEFT|wxALL, 5); + + ++ctrl_cnt; + } + + for (size_t i=0; i < bad_fname_idx_s.size(); i++) + { + int fname_idx = bad_fname_idx_s[i]; + wxString field_name = old_field_names[fname_idx]; + warn_msg=INV_WARN; + + wxString id_name1, id_name2, id_name3; + id_name1 << "ID_FNAME_STAT_TXT_BASE" << ctrl_cnt; + id_name2 << "ID_INPUT_TXT_CTRL_BASE" << ctrl_cnt; + id_name3 << "ID_INFO_STAT_TXT_BASE" << ctrl_cnt; + + wxString user_field_name = GetSuggestFieldName(fname_idx); + txt_fname.push_back(new wxStaticText(this, XRCID(id_name1), field_name)); + sizer->Add(txt_fname[ctrl_cnt], 0, wxALIGN_LEFT|wxALL, 5); + txt_input.push_back(new wxTextCtrl(this, XRCID(id_name2), user_field_name, wxDefaultPosition,wxSize(240,-1))); + sizer->Add(txt_input[ctrl_cnt], 0, wxALIGN_LEFT|wxALL, 5); + txt_info.push_back(new wxStaticText(this, XRCID(id_name3), warn_msg)); + sizer->Add(txt_info[ctrl_cnt], 0, wxALIGN_LEFT|wxALL, 5); + + ++ctrl_cnt; + } + + // buttons + wxStaticText* txt_dummy = new wxStaticText(this, wxID_ANY, ""); + sizer->Add(txt_dummy, 0, wxALIGN_LEFT | wxALL, 5); + wxBoxSizer* btnSizer = new wxBoxSizer(wxHORIZONTAL); + wxButton* ok_btn = new wxButton(this, wxID_OK, "OK"); + btnSizer->Add(ok_btn, 0, wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL|wxALL,15); + wxButton* exit_btn = new wxButton(this, wxID_CANCEL, "Cancel"); + btnSizer->Add(exit_btn, 0, wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL|wxALL,15); + sizer->Add(btnSizer, 0, wxALIGN_LEFT | wxALL, 15); + + this->SetSizer(sizer); + // this part makes the scrollbars show up + this->FitInside(); // ask the sizer about the needed size + this->SetScrollRate(5, 5); + +} + void ScrolledWidgetsPane::Init(vector& merged_field_names, set& dup_fname, set& bad_fname) @@ -176,33 +286,6 @@ void ScrolledWidgetsPane::Init(vector& merged_field_names, ++ctrl_cnt; } - /* - for (size_t i=0; iAdd(txt_fname[ctrl_cnt], 0, wxALIGN_LEFT|wxALL, 5); - txt_input.push_back(new wxTextCtrl(this, XRCID(id_name2), user_field_name, wxDefaultPosition,wxSize(240,-1))); - sizer->Add(txt_input[ctrl_cnt], 0, wxALIGN_LEFT|wxALL, 5); - txt_info.push_back(new wxStaticText(this, XRCID(id_name3), warn_msg)); - sizer->Add(txt_info[ctrl_cnt], 0, wxALIGN_LEFT|wxALL, 5); - - ++ctrl_cnt; - } - */ // buttons wxStaticText* txt_dummy = new wxStaticText(this, wxID_ANY, ""); @@ -221,8 +304,30 @@ void ScrolledWidgetsPane::Init(vector& merged_field_names, } - // give suggested field name based on GdaConst::DataSourceType +wxString ScrolledWidgetsPane::GetSuggestFieldName(int field_idx) +{ + wxString old_name = old_field_names[field_idx]; + wxString new_name(old_name); + + // if illegal name + new_name = RemoveIllegalChars(new_name); + + // if name too long + new_name = TruncateFieldName(new_name); + + // check duplicate name + new_name = RenameDupFieldName(new_name); + + // put it to new field name list + new_field_names[field_idx] = new_name; + + // add any new suggest name to search dict + field_dict[new_name] = true; + + return new_name; +} + wxString ScrolledWidgetsPane::GetSuggestFieldName(const wxString& old_name) { wxString new_name(old_name); @@ -415,6 +520,11 @@ map ScrolledWidgetsPane::GetMergedFieldNameDict() return field_names_dict; } +vector ScrolledWidgetsPane::GetNewFieldNames() +{ + return new_field_names; +} + //////////////////////////////////////////////////////////////////////////////// // // Main FieldNameCorrectionDlg @@ -425,7 +535,7 @@ FieldNameCorrectionDlg:: FieldNameCorrectionDlg(GdaConst::DataSourceType ds_type, vector& all_fname, wxString title) -: wxDialog(NULL, -1, title, wxDefaultPosition, wxSize(600,300)) +: wxDialog(NULL, -1, title, wxDefaultPosition, wxSize(680,300)) { diff --git a/DialogTools/FieldNameCorrectionDlg.h b/DialogTools/FieldNameCorrectionDlg.h index b22a588e8..0e49e79de 100644 --- a/DialogTools/FieldNameCorrectionDlg.h +++ b/DialogTools/FieldNameCorrectionDlg.h @@ -39,6 +39,9 @@ class ScrolledWidgetsPane : public wxScrolledWindow map field_names_dict; map field_dict; vector merged_field_names; + + vector old_field_names; + vector new_field_names; public: // Variable number of controls @@ -67,15 +70,18 @@ class ScrolledWidgetsPane : public wxScrolledWindow virtual ~ScrolledWidgetsPane(); wxString GetSuggestFieldName(const wxString& old_name); + wxString GetSuggestFieldName(int field_idx); wxString RenameDupFieldName(const wxString& old_name); wxString RemoveIllegalChars(const wxString& old_name); wxString TruncateFieldName(const wxString& old_name, int max_len=0); bool IsFieldNameValid(const wxString& col_name); map GetMergedFieldNameDict(); + vector GetNewFieldNames(); void Init(vector& merged_field_names, set& dup_fname, set& bad_fname); + void Init(vector& dup_fname_idx_s, vector& bad_fname_idx_s); bool CheckUserInput(); @@ -105,6 +111,10 @@ class FieldNameCorrectionDlg: public wxDialog map GetMergedFieldNameDict(){ return fieldPane->GetMergedFieldNameDict(); } + + vector GetNewFieldNames() { + return fieldPane->GetNewFieldNames(); + } void OnOkClick(wxCommandEvent& event); void OnCancelClick(wxCommandEvent& event); diff --git a/GdaConst.cpp b/GdaConst.cpp index ec483dfee..486f00d6e 100644 --- a/GdaConst.cpp +++ b/GdaConst.cpp @@ -621,6 +621,16 @@ void GdaConst::init() datasrc_field_illegal_regex[ds_xls] = default_field_name_illegal_regex; datasrc_field_casesensitive[ds_xls] = false; + datasrc_str_to_type["XLSX"] = ds_xlsx; + datasrc_type_to_prefix[ds_xlsx] = ""; + datasrc_type_to_fullname[ds_xlsx] = "Microsoft Excel Extensions"; + datasrc_table_lens[ds_xlsx] = 128; + datasrc_field_lens[ds_xlsx] = 10; + datasrc_field_warning[ds_xlsx] = default_field_warning; + datasrc_field_regex[ds_xlsx] = default_field_name_regex; + datasrc_field_illegal_regex[ds_xlsx] = default_field_name_illegal_regex; + datasrc_field_casesensitive[ds_xlsx] = false; + datasrc_str_to_type["ODS"] = ds_ods; datasrc_type_to_prefix[ds_ods] = ""; datasrc_type_to_fullname[ds_ods] = "Open Office Spreadsheet"; diff --git a/ShapeOperations/OGRDataAdapter.cpp b/ShapeOperations/OGRDataAdapter.cpp index 2f66b092d..1ecf79f72 100644 --- a/ShapeOperations/OGRDataAdapter.cpp +++ b/ShapeOperations/OGRDataAdapter.cpp @@ -297,7 +297,10 @@ OGRDataAdapter::ExportDataSource(string o_ds_format, // field identifier: a pair value to indicate how to // retreive real field name and cell value for time-enabled table typedef pair field_idn; - map field_dict; + + vector field_idn_s; + vector field_name_s; + // check field names first if ( table != NULL ) { @@ -316,7 +319,8 @@ OGRDataAdapter::ExportDataSource(string o_ds_format, throw GdaException(msg.mb_str(), GdaException::FIELD_NAME_EMPTY); } all_fnames.push_back(fname); - field_dict[fname] = make_pair(id, t); + field_idn_s.push_back(make_pair(id, t)); + field_name_s.push_back(fname); } } else { wxString fname = table->GetColName(id); @@ -326,7 +330,8 @@ OGRDataAdapter::ExportDataSource(string o_ds_format, throw GdaException(msg.mb_str(), GdaException::FIELD_NAME_EMPTY); } all_fnames.push_back(fname); - field_dict[fname] = make_pair(id, 0); + field_idn_s.push_back(make_pair(id, 0)); + field_name_s.push_back(fname); } } @@ -337,24 +342,20 @@ OGRDataAdapter::ExportDataSource(string o_ds_format, // cancel at Field Name Correction return NULL; } - // record which field name needs to be updated - map modified_field_dict; - map::iterator iter; - modified_field_dict = fname_correct_dlg.GetMergedFieldNameDict(); - // update changed fields - for (iter = modified_field_dict.begin(); - iter != modified_field_dict.end(); ++iter ) - { - wxString old_name = iter->first; - wxString new_name = iter->second; - if ( old_name == new_name ) + + vector new_field_name_s = fname_correct_dlg.GetNewFieldNames(); + + for (size_t i=0; iRenameSimpleCol(fld_id.first, fld_id.second, new_name); + } + field_idn fld_idn = field_idn_s[i]; + table->RenameSimpleCol(fld_idn.first, fld_idn.second, new_fname); } } - // clean field_dict if any field name changed - field_dict.clear(); } // create new OGRLayerProxy @@ -365,16 +366,16 @@ OGRDataAdapter::ExportDataSource(string o_ds_format, export_ds = new OGRDatasourceProxy(o_ds_name, ds_type, true); new_layer_proxy = export_ds->CreateLayer( o_layer_name, geom_type, ogr_geometries, table, - field_dict, selected_rows, spatial_ref); + selected_rows, spatial_ref); } else { export_ds = new OGRDatasourceProxy(o_ds_format, o_ds_name); new_layer_proxy = export_ds->CreateLayer( o_layer_name, geom_type, ogr_geometries, table, - field_dict, selected_rows, spatial_ref); + selected_rows, spatial_ref); } export_thread = new boost::thread(boost::bind(&OGRLayerProxy::AddFeatures, - new_layer_proxy, ogr_geometries, table, field_dict, selected_rows)); + new_layer_proxy, ogr_geometries, table, selected_rows)); return new_layer_proxy; } diff --git a/ShapeOperations/OGRDatasourceProxy.cpp b/ShapeOperations/OGRDatasourceProxy.cpp index 3af96a247..52fea4e4a 100644 --- a/ShapeOperations/OGRDatasourceProxy.cpp +++ b/ShapeOperations/OGRDatasourceProxy.cpp @@ -326,7 +326,6 @@ OGRDatasourceProxy::CreateLayer(string layer_name, OGRwkbGeometryType eGType, vector& geometries, TableInterface* table, - map >& field_dict, vector& selected_rows, OGRSpatialReference* spatial_ref) { @@ -342,7 +341,7 @@ OGRDatasourceProxy::CreateLayer(string layer_name, // PRECISION is for database e.g. MSSQL // LAUNDER is for database: rename desired field name - char* papszLCO[50] = {"OVERWRITE=yes", "PRECISION=no", "LAUNDER=no"}; + char* papszLCO[50] = {"OVERWRITE=yes", "PRECISION=no", "LAUNDER=yes"}; OGRLayer *poDstLayer = ds->CreateLayer(layer_name.c_str(), poOutputSRS, eGType, papszLCO); @@ -361,49 +360,46 @@ OGRDatasourceProxy::CreateLayer(string layer_name, table->FillColIdMap(col_id_map); int time_steps = table->GetTimeSteps(); + for ( int id=0; id < table->GetNumberCols(); id++ ) { + for ( int t=0; t < time_steps; t++ ) { + wxString fname = table->GetColName(col_id_map[id], t); if (fname.empty()) { error_message << "Can't create layer \"" << layer_name << "\" with empty field name."; throw GdaException(error_message.str().c_str()); } - field_it = field_dict.find(fname); - if (field_it == field_dict.end()) { - // a unique field - OGRFieldType ogr_type; - int ogr_fwidth = table->GetColLength(col_id_map[id], t); - int ogr_fprecision = table->GetColDecimals(col_id_map[id], t); - GdaConst::FieldType ftype = table->GetColType(col_id_map[id], t); - if (ftype == GdaConst::string_type){ - ogr_type = OFTString; - } else if (ftype == GdaConst::long64_type){ - ogr_type = OFTInteger64; - } else if (ftype == GdaConst::double_type){ - ogr_type = OFTReal; - } else if (ftype == GdaConst::date_type){ - ogr_type = OFTDate; - } else { - ogr_type = OFTString; - } - OGRFieldDefn oField(fname, ogr_type); - oField.SetWidth(ogr_fwidth); - if ( ogr_fprecision>0 ) { - oField.SetPrecision(ogr_fprecision); - } - if( poDstLayer->CreateField( &oField ) != OGRERR_NONE ) { - error_message << "Creating a field failed.\n\nDetails:" << CPLGetLastErrorMsg(); - throw GdaException(error_message.str().c_str()); - } - // record in field_dict - field_dict[fname] = make_pair(col_id_map[id], t); + + OGRFieldType ogr_type; + int ogr_fwidth = table->GetColLength(col_id_map[id], t); + int ogr_fprecision = table->GetColDecimals(col_id_map[id], t); + GdaConst::FieldType ftype = table->GetColType(col_id_map[id], t); + if (ftype == GdaConst::string_type){ + ogr_type = OFTString; + } else if (ftype == GdaConst::long64_type){ + ogr_type = OFTInteger64; + } else if (ftype == GdaConst::double_type){ + ogr_type = OFTReal; + } else if (ftype == GdaConst::date_type){ + ogr_type = OFTDate; + } else { + ogr_type = OFTString; + } + OGRFieldDefn oField(fname, ogr_type); + oField.SetWidth(ogr_fwidth); + if ( ogr_fprecision>0 ) { + oField.SetPrecision(ogr_fprecision); + } + if( poDstLayer->CreateField( &oField ) != OGRERR_NONE ) { + error_message << "Creating a field failed.\n\nDetails:" << CPLGetLastErrorMsg(); + throw GdaException(error_message.str().c_str()); } } } } OGRLayerProxy* layer = new OGRLayerProxy(poDstLayer, ds_type, eGType); - //layer->AddFeatures(geometries, table, field_dict, selected_rows); layer_pool[layer_name] = layer; return layer; diff --git a/ShapeOperations/OGRDatasourceProxy.h b/ShapeOperations/OGRDatasourceProxy.h index 303312ff7..f0a536b7f 100644 --- a/ShapeOperations/OGRDatasourceProxy.h +++ b/ShapeOperations/OGRDatasourceProxy.h @@ -119,7 +119,6 @@ class OGRDatasourceProxy { OGRwkbGeometryType eGType, vector& geometries, TableInterface* table, - map >& field_dict, vector& selected_rows, OGRSpatialReference* spatial_ref); diff --git a/ShapeOperations/OGRLayerProxy.cpp b/ShapeOperations/OGRLayerProxy.cpp index ab687df3e..866b149c2 100644 --- a/ShapeOperations/OGRLayerProxy.cpp +++ b/ShapeOperations/OGRLayerProxy.cpp @@ -41,9 +41,11 @@ using namespace std; /** * Create a OGRLayerProxy from an existing OGRLayer */ -OGRLayerProxy::OGRLayerProxy(string layer_name, OGRLayer* _layer, - GdaConst::DataSourceType _ds_type, bool isNew) -: n_rows(0), n_cols(0), name(layer_name), ds_type(_ds_type), layer(_layer), +OGRLayerProxy::OGRLayerProxy(string layer_name, + OGRLayer* _layer, + GdaConst::DataSourceType _ds_type, + bool isNew) +: n_rows(0), n_cols(0), name(layer_name),ds_type(_ds_type), layer(_layer), load_progress(0), stop_reading(false), export_progress(0) { if (!isNew) n_rows = layer->GetFeatureCount(); @@ -365,49 +367,63 @@ bool OGRLayerProxy::UpdateColumn(int col_idx, vector &vals) void OGRLayerProxy::AddFeatures(vector& geometries, TableInterface* table, - map >& field_dict, vector& selected_rows) { export_progress = 0; stop_exporting = false; - map >::iterator field_it; + // Create features in memory first for (size_t i=0; iSetGeometryDirectly( geometries[i] ); } data.push_back(poFeature); } + int export_size = data.size()==0 ? table->GetNumberRows() : data.size(); export_progress = export_size / 4; - // set other data + // Fill the feature with content if (table != NULL) { + // fields already have been created by OGRDatasourceProxy::CreateLayer() for (size_t j=0; j< fields.size(); j++) { + wxString fname = fields[j]->GetName(); - pair field_idn = field_dict[fname]; - int col_pos = field_idn.first; - int time_step = field_idn.second; - GdaConst::FieldType ftype = table->GetColType(col_pos, time_step); + GdaConst::FieldType ftype = fields[j]->GetType(); + + // get underneath column position (no group and time =0) + int col_pos = table->GetColIdx(fname); + int time_step = 0; + if ( ftype == GdaConst::long64_type) { + vector col_data; table->GetColData(col_pos, time_step, col_data); + for (size_t k=0; kSetField(j, (GIntBig)(col_data[selected_rows[k]])); if (stop_exporting) return; } + } else if (ftype == GdaConst::double_type) { + vector col_data; table->GetColData(col_pos, time_step, col_data); + for (size_t k=0; kSetField(j, col_data[ selected_rows[k] ]); if (stop_exporting) return; } + } else if (ftype == GdaConst::date_type) { + vector col_data; table->GetColData(col_pos, time_step, col_data); + for (size_t k=0; k& geometries, data[k]->SetField(j, year, month, day); if (stop_exporting) return; } + } else if (ftype == GdaConst::placeholder_type) { // KML case: there are by default two fields: // [Name, Description], so if placeholder that // means table is empty. Then do nothing + } else { // others are treated as string_type // XXX encodings vector col_data; table->GetColData(col_pos, time_step, col_data); + if (ds_type == GdaConst::ds_csv) { for (int m=0; mSetField(j, col_data[ selected_rows[k] ].mb_str()); if (stop_exporting) return; diff --git a/ShapeOperations/OGRLayerProxy.h b/ShapeOperations/OGRLayerProxy.h index a0e44d897..bb49a45fa 100644 --- a/ShapeOperations/OGRLayerProxy.h +++ b/ShapeOperations/OGRLayerProxy.h @@ -135,7 +135,6 @@ class OGRLayerProxy { */ void AddFeatures(std::vector& geometries, TableInterface* table, - std::map >& field_dict, std::vector& selected_rows); /** * Read geometries and save to Shapefile::Main data structure. From 25df629d702d9ff7a1268ae8b3f181a1d0e8e795 Mon Sep 17 00:00:00 2001 From: Xun Li Date: Thu, 23 Jun 2016 15:42:10 -0700 Subject: [PATCH 3/6] update version --- version.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/version.h b/version.h index 0981e37a0..4368739cf 100644 --- a/version.h +++ b/version.h @@ -1,11 +1,11 @@ namespace Gda { const int version_major = 1; const int version_minor = 8; - const int version_build = 8; + const int version_build = 9; const int version_subbuild = 0; const int version_year = 2016; const int version_month = 6; - const int version_day = 10; + const int version_day = 23; const int version_night = 0; const int version_type = 1; // 0: alpha, 1: beta, 2: release } From 2754474bc0392a0df67fc295bd1c702d6c2892e3 Mon Sep 17 00:00:00 2001 From: Xun Li Date: Fri, 8 Jul 2016 12:28:52 -0700 Subject: [PATCH 4/6] fix issue #448 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit it's caused by introducing the transparency and multi-layers to draw maps. Legends are still drawn using old code, so they are not impacted. This fix will address this bug, also the export type is limited to png only — option “bmp” will be removed since it doesn’t support alpha chanel. --- Explore/MapNewView.cpp | 86 ++++++++++++++++++++++++++++++++++++++++++ Explore/MapNewView.h | 3 ++ TemplateCanvas.h | 2 + version.h | 2 +- 4 files changed, 92 insertions(+), 1 deletion(-) diff --git a/Explore/MapNewView.cpp b/Explore/MapNewView.cpp index 5c5dab871..29942c867 100644 --- a/Explore/MapNewView.cpp +++ b/Explore/MapNewView.cpp @@ -1721,6 +1721,92 @@ void MapFrame::closeObserver(boost::uuids::uuid id) } } +void MapFrame::OnCopyImageToClipboard(wxCommandEvent& event) +{ + LOG_MSG("Entering TemplateFrame::OnCopyImageToClipboard"); + if (!template_canvas) return; + wxSize sz = template_canvas->GetVirtualSize(); + + wxBitmap bitmap( sz.x, sz.y ); + + wxMemoryDC dc; + dc.SelectObject( bitmap ); + if (((MapCanvas*) template_canvas)->isDrawBasemap) { + dc.DrawBitmap(*template_canvas->GetBaseLayer(), 0, 0, true); + } + dc.DrawBitmap(*template_canvas->GetLayer0(), 0, 0, true); + dc.DrawBitmap(*template_canvas->GetLayer1(), 0, 0, true); + dc.DrawBitmap(*template_canvas->GetLayer2(), 0, 0, true); + + dc.SelectObject( wxNullBitmap ); + + if ( !wxTheClipboard->Open() ) { + wxMessageBox("Can't open clipboard."); + } else { + wxTheClipboard->AddData(new wxBitmapDataObject(bitmap)); + wxTheClipboard->Close(); + } + LOG_MSG("Exiting TemplateFrame::OnCopyImageToClipboard"); +} + +void MapFrame::ExportImage(TemplateCanvas* canvas, const wxString& type) +{ + LOG_MSG("Entering TemplateFrame::ExportImage"); + + wxString default_fname(project->GetProjectTitle() + type); + wxString filter("PNG|*.png"); + int filter_index = 0; + // + wxFileDialog dialog(canvas, "Save Image to File", wxEmptyString, + default_fname, filter, + wxFD_SAVE | wxFD_OVERWRITE_PROMPT); + dialog.SetFilterIndex(filter_index); + + if (dialog.ShowModal() != wxID_OK) return; + + wxSize sz = canvas->GetVirtualSize(); + + wxFileName fname = wxFileName(dialog.GetPath()); + wxString str_fname = fname.GetPathWithSep() + fname.GetName(); + + switch (dialog.GetFilterIndex()) { + + case 0: + { + LOG_MSG("PNG selected"); + wxBitmap bitmap( sz.x, sz.y ); + wxMemoryDC dc; + dc.SelectObject(bitmap); + if (((MapCanvas*) template_canvas)->isDrawBasemap) { + dc.DrawBitmap(*template_canvas->GetBaseLayer(), 0, 0, true); + } + dc.DrawBitmap(*template_canvas->GetLayer0(), 0, 0, true); + dc.DrawBitmap(*template_canvas->GetLayer1(), 0, 0, true); + dc.DrawBitmap(*template_canvas->GetLayer2(), 0, 0, true); + dc.SelectObject( wxNullBitmap ); + + wxImage image = bitmap.ConvertToImage(); + + if ( !image.SaveFile( str_fname + ".png", wxBITMAP_TYPE_PNG )) { + wxMessageBox("GeoDa was unable to save the file."); + } + + image.Destroy(); + } + break; + + default: + { + LOG_MSG("Error: A non-recognized type selected."); + } + break; + } + return; + + LOG_MSG("Exiting MapFrame::ExportImage"); +} + + void MapFrame::OnNewCustomCatClassifA() { ((MapCanvas*) template_canvas)->NewCustomCatClassif(); diff --git a/Explore/MapNewView.h b/Explore/MapNewView.h index ff1291d59..6b9a21906 100644 --- a/Explore/MapNewView.h +++ b/Explore/MapNewView.h @@ -232,6 +232,9 @@ class MapFrame : public TemplateFrame, public WeightsManStateObserver virtual int numMustCloseToRemove(boost::uuids::uuid id) const; virtual void closeObserver(boost::uuids::uuid id); + virtual void OnCopyImageToClipboard(wxCommandEvent& event); + virtual void ExportImage(TemplateCanvas* canvas, const wxString& type); + virtual void OnNewCustomCatClassifA(); virtual void OnCustomCatClassifA(const wxString& cc_title); virtual void OnThemelessMap(); diff --git a/TemplateCanvas.h b/TemplateCanvas.h index e19a3a814..e0c6d6a46 100644 --- a/TemplateCanvas.h +++ b/TemplateCanvas.h @@ -385,6 +385,8 @@ class TemplateCanvas : public wxScrolledWindow, public HighlightStateObserver public: void RenderToDC(wxDC &dc, bool disable_crosshatch_brush = true); + const wxBitmap* GetBaseLayer() { return basemap_bm; } + const wxBitmap* GetLayer0() { return layer0_bm; } const wxBitmap* GetLayer1() { return layer1_bm; } const wxBitmap* GetLayer2() { return layer2_bm; } void deleteLayerBms(); diff --git a/version.h b/version.h index 4368739cf..170674162 100644 --- a/version.h +++ b/version.h @@ -2,7 +2,7 @@ namespace Gda { const int version_major = 1; const int version_minor = 8; const int version_build = 9; - const int version_subbuild = 0; + const int version_subbuild = 1; const int version_year = 2016; const int version_month = 6; const int version_day = 23; From 8fdf5c98d5af5ac9628ec1da3f5a020da2d4325e Mon Sep 17 00:00:00 2001 From: Xun Li Date: Fri, 8 Jul 2016 14:06:05 -0700 Subject: [PATCH 5/6] update fix for #448 On windows, wxwidgets has an issue to save image with transparency to png file (when basemap is overlayed). However, save to clipboard works fine. On Max osx, both save to image file and clipboard work fine. --- Explore/MapNewView.cpp | 45 ++++++++++++++++++++++++++++++++++++------ 1 file changed, 39 insertions(+), 6 deletions(-) diff --git a/Explore/MapNewView.cpp b/Explore/MapNewView.cpp index 29942c867..6581ea2a1 100644 --- a/Explore/MapNewView.cpp +++ b/Explore/MapNewView.cpp @@ -1731,6 +1731,10 @@ void MapFrame::OnCopyImageToClipboard(wxCommandEvent& event) wxMemoryDC dc; dc.SelectObject( bitmap ); + + dc.SetBrush(template_canvas->canvas_background_color); + dc.DrawRectangle(wxPoint(0,0), sz); + if (((MapCanvas*) template_canvas)->isDrawBasemap) { dc.DrawBitmap(*template_canvas->GetBaseLayer(), 0, 0, true); } @@ -1754,8 +1758,8 @@ void MapFrame::ExportImage(TemplateCanvas* canvas, const wxString& type) LOG_MSG("Entering TemplateFrame::ExportImage"); wxString default_fname(project->GetProjectTitle() + type); - wxString filter("PNG|*.png"); - int filter_index = 0; + wxString filter("BMP|*.bmp|PNG|*.png"); + int filter_index = 1; // wxFileDialog dialog(canvas, "Save Image to File", wxEmptyString, default_fname, filter, @@ -1770,20 +1774,49 @@ void MapFrame::ExportImage(TemplateCanvas* canvas, const wxString& type) wxString str_fname = fname.GetPathWithSep() + fname.GetName(); switch (dialog.GetFilterIndex()) { - case 0: + { + LOG_MSG("BMP selected"); + wxBitmap bitmap( sz.x, sz.y ); + wxMemoryDC dc; + dc.SelectObject(bitmap); + + //dc.SetBrush(template_canvas->canvas_background_color); + //dc.DrawRectangle(wxPoint(0,0), sz); + + if (((MapCanvas*) template_canvas)->isDrawBasemap) { + dc.DrawBitmap(*template_canvas->GetBaseLayer(), 0, 0, true); + } + dc.DrawBitmap(*template_canvas->GetLayer0(), 0, 0, true); + dc.DrawBitmap(*template_canvas->GetLayer1(), 0, 0, true); + dc.DrawBitmap(*template_canvas->GetLayer2(), 0, 0, true); + dc.SelectObject( wxNullBitmap ); + + wxImage image = bitmap.ConvertToImage(); + + if ( !image.SaveFile( str_fname + ".bmp", wxBITMAP_TYPE_BMP )) { + wxMessageBox("GeoDa was unable to save the file."); + } + image.Destroy(); + } + break; + case 1: { LOG_MSG("PNG selected"); wxBitmap bitmap( sz.x, sz.y ); - wxMemoryDC dc; - dc.SelectObject(bitmap); + wxMemoryDC dc(bitmap); + //dc.SelectObject(bitmap); + + dc.SetBrush(template_canvas->canvas_background_color); + dc.DrawRectangle(wxPoint(0,0), sz); + if (((MapCanvas*) template_canvas)->isDrawBasemap) { dc.DrawBitmap(*template_canvas->GetBaseLayer(), 0, 0, true); } dc.DrawBitmap(*template_canvas->GetLayer0(), 0, 0, true); dc.DrawBitmap(*template_canvas->GetLayer1(), 0, 0, true); dc.DrawBitmap(*template_canvas->GetLayer2(), 0, 0, true); - dc.SelectObject( wxNullBitmap ); + //dc.SelectObject( wxNullBitmap ); wxImage image = bitmap.ConvertToImage(); From a6586c8bb5aac5cde19d7db1a5542047cd899e5d Mon Sep 17 00:00:00 2001 From: Xun Li Date: Fri, 8 Jul 2016 14:09:13 -0700 Subject: [PATCH 6/6] update version --- version.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/version.h b/version.h index 170674162..8b0ad9a8f 100644 --- a/version.h +++ b/version.h @@ -4,8 +4,8 @@ namespace Gda { const int version_build = 9; const int version_subbuild = 1; const int version_year = 2016; - const int version_month = 6; - const int version_day = 23; + const int version_month = 7; + const int version_day = 8; const int version_night = 0; const int version_type = 1; // 0: alpha, 1: beta, 2: release }