diff --git a/modules/gmt/modgmt_func.cpp b/modules/gmt/modgmt_func.cpp index 0036955..b37e536 100644 --- a/modules/gmt/modgmt_func.cpp +++ b/modules/gmt/modgmt_func.cpp @@ -74,71 +74,98 @@ class RegionConv struct gmt_region operator()(const ObjectList* input, bool* issuc) const { struct gmt_region r; + auto size=input->Size(); - // Case 1 - for(ObjectList::ListValues::size_type i=0;iSize();i++) + if(1==size) // Cases 1, 2 and 3 { - OBType region(input->At(i)); + const ObjectBase *arg=input->At(0); + OBType region(arg); if(region) return r=region->Data(); // Case 1 + OBType string(arg); + if(string && r.Convert(string->Value())) return r; // Case 2 + OBType list(arg); + if(list) return r=operator()(list,issuc); // Case 3 + *issuc=false; + return r; // Conversion failed } // Case 4 { - // Special case, arguments list contains parameter region. + bool upd=false; + + // Update case, arguments list contains parameter with type GMTRegion. + for(ObjectList::ListValues::size_type i=0;i region(input->At(i)); + if(region) + { + if(upd){*issuc=false; return r;} // We already have region to update + r=region->Data(); + upd=true; + } + } + + // Update case, arguments list contains parameter region. if(0!=input->Get("region")) { + if(upd) {*issuc=false; return r;} // We already have region to update ObjectList* list=new ObjectList; list->PushBack(input->Get("region")); r=operator()(list,issuc); delete list; - return r; + if(!*issuc) return r; // Fail to convert + upd=true; } OBType type(input->Get("type")); - if(type && r.Convert(type->Value())) return r; // type is one of "global*" string + if(type && r.Convert(type->Value())) return r; // type is one of "global*" string, we can return, becuse upd is irrelevant Base2Coord ixb(input,"xb"), ixe(input,"xe"), iyb(input,"yb"), iye(input,"ye"); bool suc=true; struct gmt_coord xb=ixb(&suc),yb=iyb(&suc),xe=ixe(&suc),ye=iye(&suc); - bool isbbox=false; + bool isbbox=upd?(gmt_region::BBOX==r.type):false; if(type) { std::string s=type->Value(); tolower(s); if("bbox"==s) isbbox=true; + if("nobbox"==s) isbbox=false; + if("bbox"!=s && "nobbox"!=s) suc=false; // Unknown type } - if(ixb && ixe && iyb && iye && suc) + if(upd) + { + if(!ixb.Exist()) xb=r.xb; + if(!ixe.Exist()) xe=r.xe; + if(!iyb.Exist()) yb=r.yb; + if(!iye.Exist()) ye=r.ye; + } + else suc=suc && ixb && ixe && iyb && iye; // In "new" mode all parameters must exists and have correct type + if(ixb.Exist()) xb=ixb(&suc); + if(ixe.Exist()) xe=ixe(&suc); + if(iyb.Exist()) yb=iyb(&suc); + if(iye.Exist()) ye=iye(&suc); + + if(suc) { if(!r.Convert(xb,xe,yb,ye,isbbox)) *issuc=false; return r; // Case 3 with all parameters } } - if(input->Size()==1) // Cases 2 and 3 - { - const ObjectBase *arg=input->At(0); - OBType string(arg); - if(string && r.Convert(string->Value())) return r; // Case 2 - OBType list(arg); - if(list) r=operator()(list,issuc); // Case 3 - else *issuc=false; // Unknown argument - return r; - } - // Case 5 - if(4==input->Size() || 5==input->Size()) + if(4==size || 5==size) { bool isbbox=false; - if(5==input->Size()) + if(5==size) { OBType type(input->At(4)); if(!type) {*issuc=false; return r;} // Unknown fifth parameter std::string str=type->Value(); tolower(str); - if("bbox"!=str) {*issuc=false; return r;} // Unknown fifth parameter - isbbox=true; + if("bbox"!=str || "nobbox"!=str) {*issuc=false; return r;} // Unknown fifth parameter + if("bbox"==str) isbbox=true; } Base2Coord ixb(input,0), ixe(input,(isbbox?2:1)), iyb(input,(isbbox?1:2)), iye(input,3); bool suc=true; @@ -192,37 +219,72 @@ class ProjConv struct gmt_projection p; auto size=input->Size(); - // Case 1 - for(ObjectList::ListValues::size_type i=0;i proj(input->At(i)); + const ObjectBase *arg=input->At(0); + OBType proj(arg); if(proj) return p=proj->Data(); // Case 1 + OBType list(arg); + if(list) return p=operator()(list,issuc); // Case 2 + *issuc=false; + return p; // Conversion failed } // Case 3 { - // Special case, arguments list contains parameter proj or projection and this parameter is projection. + bool upd=false; + bool longisproj=false; // "projection" is good defined projection + bool shortisproj=false; // "proj" is good defined projection + bool changetype=false; // Projection type was specified in update mode. If new type is same as old type parameters of projection reset to default for this type. + + // Update case, arguments list contains parameter with type GMTProjection. + for(ObjectList::ListValues::size_type i=0;i proj(input->At(i)); + if(proj) + { + if(upd) goto fail; // Only one projection in list allowed + p=proj->Data(); + upd=true; + } + } + + // Update case, arguments list contains parameter proj or projection and this parameter is projection. { const ObjectBase* prg; prg=input->Get("projection"); if(0!=prg) { + struct gmt_projection ps; bool suc=true; ObjectList* list=new ObjectList; list->PushBack(prg->Copy()); - p=operator()(list,&suc); + ps=operator()(list,&suc); delete list; - if(suc) return p; // Projection found, return + if(suc) + { + if(upd) goto fail; // Already have projection to update + upd=true; + longisproj=true; + p=ps; + } } prg=input->Get("proj"); if(0!=prg) { + struct gmt_projection ps; bool suc=true; ObjectList* list=new ObjectList; list->PushBack(prg->Copy()); - p=operator()(list,&suc); + ps=operator()(list,&suc); delete list; - if(suc) return p; // Projection found, return + if(suc) + { + if(upd) goto fail; // Already have projection to update + upd=true; + shortisproj=true; + p=ps; + } } } @@ -231,13 +293,30 @@ class ProjConv OBType type1(input->Get("projtype")); OBType type2(input->Get("projection")); OBType type3(input->Get("proj")); - if(!(type1.Exist() || type2.Exist() || type3.Exist())) goto case24; // No named parameter, go to another case + if(!(type1.Exist() || type2.Exist() || type3.Exist() || upd)) goto case4; // No named parameter, not update mode, go to case 4 + // Check on redundant parameters + if(type1.Exist() && type2.Exist() && type3.Exist()) goto fail; // Too many variants + if(type1.Exist() && type2.Exist() && !type3.Exist() && !longisproj) goto fail; // "projtype" and "projection" exists, but "projection" is not parse to Projection + if(type1.Exist() && !type2.Exist() && type3.Exist() && !shortisproj) goto fail; // "projtype" and "proj" exists, but "proj" is not parse to Projection + if(!type1.Exist() && type2.Exist() && type3.Exist() && !(shortisproj || longisproj)) goto fail; // "projection" and "proj" exists, but no one is parse to Projection + // Check on type correctness + if(type1.Exist() && !type1) goto fail; // "projtype" is not String + if(type2.Exist() && !type2 && !longisproj) goto fail; // "projection" is not String or Projection + if(type3.Exist() && !type3 && !shortisproj) goto fail; // "proj" is not String or Projection bool suc=false; - if(!(type1 || type2 || type3) ) goto fail; // No type - no projection - if(!suc && type1) suc=p.SetType(type1->Value()); - if(!suc && type2) suc=p.SetType(type2->Value()); - if(!suc && type3) suc=p.SetType(type3->Value()); - if(!suc) goto fail; // Unknown type - no projection + if(!(type1 || type2 || type3)) // No String parameter + { + if(!upd) goto fail; // No type - no projection + changetype=false; + } + else + { + changetype=true; + if(!suc && type1) suc=p.SetType(type1->Value()); + if(!suc && type2) suc=p.SetType(type2->Value()); + if(!suc && type3) suc=p.SetType(type3->Value()); + if(!suc) goto fail; // Incorrect projection type + } } // We need the region @@ -256,9 +335,14 @@ class ProjConv for(ObjectList::ListValues::size_type i=0;iSize();i++) { OBType reg(input->At(i)); - if(reg) {p.region=reg->Data(); suc=true;} + if(reg) + { + if(suc) goto fail; // Too many regions + p.region=reg->Data(); + suc=true; + } } - if(!suc) goto fail; // No such objects found + if(!suc && !upd) goto fail; // No such objects found in "new" mode } } @@ -277,7 +361,7 @@ class ProjConv else { Base2Double width(input,"width"); - if(!width) p.x.height=gmt_projection::default_width; // We ignore case when parameter width exists but have wrong type. It will be handled later. + if(!width && changetype) p.x.height=gmt_projection::default_width; // We ignore case when parameter width exists but have wrong type. It will be handled later. else { bool suc=true; @@ -296,14 +380,14 @@ class ProjConv p.q.cmer=cmer(&suc); if(!suc) goto fail; // Parsing error } - else p.q.cmer.Convert((p.region.xb+p.region.xe)*0.5); + else if(changetype) p.q.cmer.Convert((p.region.xb+p.region.xe)*0.5); if(stpar.Exist()) { bool suc=true; p.q.stpar=stpar(&suc); if(!suc) goto fail; // Parsing error } - else p.q.stpar.Convert((p.region.yb+p.region.ye)*0.5); + else if(changetype) p.q.stpar.Convert((p.region.yb+p.region.ye)*0.5); break; } case(gmt_projection::MERCATOR): // m Parameters: central meridian (cmer, default is center of region), standart parallel (stpar, default is center of region) @@ -315,14 +399,14 @@ class ProjConv p.m.cmer=cmer(&suc); if(!suc) goto fail; // Parsing error } - else p.m.cmer.Convert((p.region.xb+p.region.xe)*0.5); + else if(changetype) p.m.cmer.Convert((p.region.xb+p.region.xe)*0.5); if(stpar.Exist()) { bool suc=true; p.m.stpar=stpar(&suc); if(!suc) goto fail; // Parsing error } - else p.m.stpar.Convert((p.region.yb+p.region.ye)*0.5); + else if(changetype) p.m.stpar.Convert((p.region.yb+p.region.ye)*0.5); break; } case(gmt_projection::TRANSMERCATOR): // t Parameters: central meridian (cmer, default is center of region), latitude of origin (orlat, default is 0.0), scale factor (scale, default is 1.0) @@ -335,42 +419,42 @@ class ProjConv p.t.cmer=cmer(&suc); if(!suc) goto fail; // Parsing error } - else p.t.cmer.Convert((p.region.xb+p.region.xe)*0.5); + else if(changetype) p.t.cmer.Convert((p.region.xb+p.region.xe)*0.5); if(orlat.Exist()) { bool suc=true; p.t.orlat=orlat(&suc); if(!suc) goto fail; // Parsing error } - else p.t.orlat.Convert(0.0); + else if(changetype) p.t.orlat.Convert(0.0); if(scale.Exist()) { bool suc=true; p.t.scale=scale(&suc); if(!suc) goto fail; // Parsing error } - else p.t.scale=1.0; + else if(changetype) p.t.scale=1.0; break; } case(gmt_projection::OBLIQMERCATOR): // o Parameters: longitude of projection center (clon, default is center of region), latitude of projection center (clat, default is center of region). Other parameters may form one of three combinations and doesn't have default values. 1) Azimuth of the oblique equator (azimuth). 2) Longitude and latitude of second point on oblique equator (eqlon, eqlat). 3) Longitude and latitude of projection pole (polelon, polelat). { Base2Coord clon(input,"clon"), clat(input,"clat"); Base2Coord azimuth(input,"azimuth"), eqlon(input,"eqlon"), eqlat(input,"eqlat"), polelon(input,"polelon"), polelat(input,"polelat"); - p.o.type=gmt_projection::OType::NOTDEF; + if(changetype) p.o.type=gmt_projection::OType::NOTDEF; if(clon.Exist()) { bool suc=true; p.o.clon=clon(&suc); if(!suc) goto fail; // Parsing error } - else p.o.clon.Convert((p.region.xb+p.region.xe)*0.5); + else if(changetype) p.o.clon.Convert((p.region.xb+p.region.xe)*0.5); if(clat.Exist()) { bool suc=true; p.o.clat=clat(&suc); if(!suc) goto fail; // Parsing error } - else p.o.clat.Convert((p.region.yb+p.region.ye)*0.5); + else if(changetype) p.o.clat.Convert((p.region.yb+p.region.ye)*0.5); // Variant 1 if(azimuth) { @@ -407,14 +491,14 @@ class ProjConv p.c.clon=clon(&suc); if(!suc) goto fail; // Parsing error } - else p.c.clon.Convert((p.region.xb+p.region.xe)*0.5); + else if(changetype) p.c.clon.Convert((p.region.xb+p.region.xe)*0.5); if(clat.Exist()) { bool suc=true; p.c.clat=clat(&suc); if(!suc) goto fail; // Parsing error } - else p.c.clat.Convert((p.region.yb+p.region.ye)*0.5); + else if(changetype) p.c.clat.Convert((p.region.yb+p.region.ye)*0.5); break; } case(gmt_projection::CYL_EQA): // y Parameters: central meridian (cmer, default is center of region), standart parallel (stpar, default is center of region) @@ -426,14 +510,14 @@ class ProjConv p.y.cmer=cmer(&suc); if(!suc) goto fail; // Parsing error } - else p.y.cmer.Convert((p.region.xb+p.region.xe)*0.5); + else if(changetype) p.y.cmer.Convert((p.region.xb+p.region.xe)*0.5); if(stpar.Exist()) { bool suc=true; p.y.stpar=stpar(&suc); if(!suc) goto fail; // Parsing error } - else p.y.stpar.Convert((p.region.yb+p.region.ye)*0.5); + else if(changetype) p.y.stpar.Convert((p.region.yb+p.region.ye)*0.5); break; } case(gmt_projection::MILLER): // j Parameters: central meridian (cmer, default is center of region) @@ -445,7 +529,7 @@ class ProjConv p.j.cmer=cmer(&suc); if(!suc) goto fail; // Parsing error } - else p.j.cmer.Convert((p.region.xb+p.region.xe)*0.5); + else if(changetype) p.j.cmer.Convert((p.region.xb+p.region.xe)*0.5); break; } case(gmt_projection::CYL_STERE): // cyl_stere Parameters: central meridian (cmer, default is center of region), standart parallel (stpar, default is center of region) @@ -457,21 +541,21 @@ class ProjConv p.cyl_stere.cmer=cmer(&suc); if(!suc) goto fail; // Parsing error } - else p.cyl_stere.cmer.Convert((p.region.xb+p.region.xe)*0.5); + else if(changetype) p.cyl_stere.cmer.Convert((p.region.xb+p.region.xe)*0.5); if(stpar.Exist()) { bool suc=true; p.cyl_stere.stpar=stpar(&suc); if(!suc) goto fail; // Parsing error } - else p.cyl_stere.stpar.Convert((p.region.yb+p.region.ye)*0.5); + else if(changetype) p.cyl_stere.stpar.Convert((p.region.yb+p.region.ye)*0.5); break; } default: goto fail; // Unknown projection } // Try to find width parameter - p.width=p.default_width; + if(!upd) p.width=p.default_width; { Base2Double w(input,"width"),h(input,"height"); bool suc=true; @@ -488,20 +572,13 @@ class ProjConv if(gmt_projection::XY==p.proj) p.width=hval; // For decart projection we use height as width if width is not specified if(!ProjectionRealSize(p,hval)) goto fail; // Something go wrong with determining real dimensions } - else // No width, no height, using default width + else // No width, no height, using default or old width if(!ProjectionRealSize(p)) goto fail; // Something go wrong with determining real dimensions } return p; // All parameters setted } - case24: - if(input->Size()==1) // Case 2 - { - OBType list(input->At(0)); - if(list) p=operator()(list,issuc); - else *issuc=false; // Unknown argument - return p; - } + case4: // Case 4 if(size>=3) @@ -770,12 +847,13 @@ ObjectBase* GMT_Coord(const ObjectList* input) /* Input: -1) Any number of arguments, search argument with ObjectGMTRegion type and copy it. +1) One argument, Region. Return copy of this argument. 2) One argument, case insensitive string "global180" (global domain -R-180/180/-90/90), "global360" (global domain -R0/360/-90/90) or "global" (synonym of "global360"). 3) One argument, list. Recursively calling GMT_Region. -4) Pairs list. Names are xb, xe, yb, ye, and, optionally, type="bbox|global180|global360|global". Names are case sensitive, values can be Int, Real, String or GMTCoord. -If pair with name region exists in list, when recursively calling GMT_Region on the value of this parameter, instead. -5) 4 or 5 parameters. If fifth parameter is string "bbox", when first four parameters interprets as xb,yb,xe,ye, else as xb,xe,yb,ye. +4) Pairs list. Names are xb, xe, yb, ye, and, optionally, type="bbox|nobbox|global180|global360|global". Names are case sensitive, values can be Int, Real, String or GMTCoord. +If pair with name region exists in list, when recursively calling GMT_Region on the value of this parameter, when modify it with specified parameters. +If argument with type Region exists in list, when copy it and modify with specified parameters. +5) 4 or 5 parameters. Fifth parameter can be "bbox" or "nobbox" (default). If fifth parameter is string "bbox", when first four parameters interprets as xb,yb,xe,ye, else as xb,xe,yb,ye. */ ObjectBase* GMT_Region(const ObjectList* input) { @@ -788,12 +866,13 @@ ObjectBase* GMT_Region(const ObjectList* input) /* Input: -1) Any number of arguments, search argument with ObjectGMTProjection type and copy it. +1) One argument, Projection. Return copy of this argument. 2) One argument, list. Recursively calling GMT_Projection. 3) Pairs list. Names are projtype (string), region (GMTRegion, string or list, which can be converted to GMTRegion), width (or height) in centimeters (may be absent, default width is 10 cm) and projection-dependent parameters. Pair with name region may absent, in this case search in list and using as region object with ObjectGMTRegion type. -If pair with name proj (or projection) exists in list, when recursively calling GMT_Region on the value of this parameter, instead. +If pair with name proj (or projection) exists in list, when recursively calling GMT_Projection on the value of this parameter, when modify it with specified parameters. +If argument with type Projection exists in list, when copy it and modify with specified parameters. 4) 3 or more parameters. First parameter is projection type (string), second - width in centimeters, third - region (any type which can be converted to GMTRegion), other parameters are projection-dependent. Height can be set only in form 3.