Path: blob/main/src/netedit/elements/additional/GNEOverheadWire.cpp
169684 views
/****************************************************************************/1// Eclipse SUMO, Simulation of Urban MObility; see https://eclipse.dev/sumo2// Copyright (C) 2001-2025 German Aerospace Center (DLR) and others.3// This program and the accompanying materials are made available under the4// terms of the Eclipse Public License 2.0 which is available at5// https://www.eclipse.org/legal/epl-2.0/6// This Source Code may also be made available under the following Secondary7// Licenses when the conditions for such availability set forth in the Eclipse8// Public License 2.0 are satisfied: GNU General Public License, version 29// or later which is available at10// https://www.gnu.org/licenses/old-licenses/gpl-2.0-standalone.html11// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later12/****************************************************************************/13/// @file GNEOverheadWire.cpp14/// @author Pablo Alvarez Lopez15/// @date Nov 201516///17//18/****************************************************************************/19#include <config.h>2021#include <netedit/GNENet.h>22#include <netedit/GNESegment.h>23#include <netedit/GNEUndoList.h>24#include <netedit/GNEViewNet.h>25#include <netedit/changes/GNEChange_Attribute.h>26#include <netedit/changes/GNEChange_Connection.h>27#include <netedit/elements/network/GNEConnection.h>28#include <utils/gui/div/GLHelper.h>29#include <utils/gui/globjects/GLIncludes.h>30#include <utils/gui/div/GUIGlobalViewObjectsHandler.h>3132#include "GNEOverheadWire.h"33#include "GNEAdditionalHandler.h"3435// ===========================================================================36// member method definitions37// ===========================================================================3839GNEOverheadWire::GNEOverheadWire(GNENet* net) :40GNEAdditional("", net, "", SUMO_TAG_OVERHEAD_WIRE_SECTION, "") {41}424344GNEOverheadWire::GNEOverheadWire(const std::string& id, GNENet* net, const std::string& filename, std::vector<GNELane*> lanes, GNEAdditional* substation,45const double startPos, const double endPos, const bool friendlyPos, const std::vector<std::string>& forbiddenInnerLanes,46const Parameterised::Map& parameters) :47GNEAdditional(id, net, filename, SUMO_TAG_OVERHEAD_WIRE_SECTION, ""),48Parameterised(parameters),49myStartPos(startPos),50myEndPos(endPos),51myFriendlyPosition(friendlyPos),52myForbiddenInnerLanes(forbiddenInnerLanes) {53// set parents54setParents<GNELane*>(lanes);55setParent<GNEAdditional*>(substation);56// update centering boundary without updating grid57updateCenteringBoundary(false);58}596061GNEOverheadWire::~GNEOverheadWire() {62}636465GNEMoveOperation*66GNEOverheadWire::getMoveOperation() {67// check modes and detector type68if (myNet->getViewNet()->getEditModes().isCurrentSupermodeNetwork() &&69(myNet->getViewNet()->getEditModes().networkEditMode == NetworkEditMode::NETWORK_MOVE)) {70return getMoveOperationMultiLane(myStartPos, myEndPos);71} else {72return nullptr;73}74}757677void78GNEOverheadWire::writeAdditional(OutputDevice& device) const {79device.openTag(SUMO_TAG_OVERHEAD_WIRE_SECTION);80device.writeAttr(SUMO_ATTR_ID, getID());81device.writeAttr(SUMO_ATTR_SUBSTATIONID, getParentAdditionals().front()->getID());82device.writeAttr(SUMO_ATTR_LANES, getAttribute(SUMO_ATTR_LANES));83device.writeAttr(SUMO_ATTR_STARTPOS, myStartPos);84device.writeAttr(SUMO_ATTR_ENDPOS, myEndPos);85if (myFriendlyPosition) {86device.writeAttr(SUMO_ATTR_FRIENDLY_POS, myFriendlyPosition);87}88if (!myForbiddenInnerLanes.empty()) {89device.writeAttr(SUMO_ATTR_OVERHEAD_WIRE_FORBIDDEN, myForbiddenInnerLanes);90}91// write parameters92writeParams(device);93device.closeTag();94}959697bool98GNEOverheadWire::isAdditionalValid() const {99// first check if there is connection between all consecutive lanes100if (areLaneConsecutives(getParentLanes())) {101// with friendly position enabled position are "always fixed"102if (myFriendlyPosition) {103return true;104} else {105return (myStartPos >= 0) &&106(myEndPos >= 0) &&107((myStartPos) <= getParentLanes().front()->getParentEdge()->getNBEdge()->getFinalLength()) &&108((myEndPos) <= getParentLanes().back()->getParentEdge()->getNBEdge()->getFinalLength());109}110} else {111return false;112}113}114115116std::string117GNEOverheadWire::getAdditionalProblem() const {118// declare variable for error position119std::string errorFirstLanePosition, separator, errorLastLanePosition;120// abort if lanes aren't consecutives121if (!areLaneConsecutives(getParentLanes())) {122return TL("lanes aren't consecutives");123}124// abort if lanes aren't connected125if (!areLaneConnected(getParentLanes())) {126return TL("lanes aren't connected");127}128// check positions over first lane129if (myStartPos < 0) {130errorFirstLanePosition = (toString(SUMO_ATTR_STARTPOS) + " < 0");131}132if (myStartPos > getParentLanes().front()->getParentEdge()->getNBEdge()->getFinalLength()) {133errorFirstLanePosition = (toString(SUMO_ATTR_STARTPOS) + TL(" > lanes's length"));134}135// check positions over last lane136if (myEndPos < 0) {137errorLastLanePosition = (toString(SUMO_ATTR_ENDPOS) + " < 0");138}139if (myEndPos > getParentLanes().back()->getParentEdge()->getNBEdge()->getFinalLength()) {140errorLastLanePosition = (toString(SUMO_ATTR_ENDPOS) + TL(" > lanes's length"));141}142// check separator143if ((errorFirstLanePosition.size() > 0) && (errorLastLanePosition.size() > 0)) {144separator = TL(" and ");145}146// return error message147return errorFirstLanePosition + separator + errorLastLanePosition;148}149150151void152GNEOverheadWire::fixAdditionalProblem() {153if (!areLaneConsecutives(getParentLanes())) {154// build connections between all consecutive lanes155bool foundConnection = true;156int i = 0;157// iterate over all lanes, and stop if myE2valid is false158while (i < ((int)getParentLanes().size() - 1)) {159// change foundConnection to false160foundConnection = false;161// if a connection between "from" lane and "to" lane of connection is found, change myE2valid to true again162for (const auto& connection : getParentLanes().at(i)->getParentEdge()->getGNEConnections()) {163if ((connection->getLaneFrom() == getParentLanes().at(i)) && (connection->getLaneTo() == getParentLanes().at(i + 1))) {164foundConnection = true;165}166}167// if connection wasn't found168if (!foundConnection) {169// create new connection manually170NBEdge::Connection newCon(getParentLanes().at(i)->getIndex(), getParentLanes().at(i + 1)->getParentEdge()->getNBEdge(), getParentLanes().at(i + 1)->getIndex());171// allow to undo creation of new lane172myNet->getViewNet()->getUndoList()->add(new GNEChange_Connection(getParentLanes().at(i)->getParentEdge(), newCon, false, true), true);173}174// update lane iterator175i++;176}177} else {178// declare new positions179double newPositionOverLane = myStartPos;180double newEndPositionOverLane = myEndPos;181// fix pos and length checkAndFixDetectorPosition182GNEAdditionalHandler::fixMultiLanePosition(183newPositionOverLane, getParentLanes().front()->getParentEdge()->getNBEdge()->getFinalLength(),184newEndPositionOverLane, getParentLanes().back()->getParentEdge()->getNBEdge()->getFinalLength());185// set new position and endPosition186setAttribute(SUMO_ATTR_STARTPOS, toString(newPositionOverLane), myNet->getViewNet()->getUndoList());187setAttribute(SUMO_ATTR_ENDPOS, toString(newEndPositionOverLane), myNet->getViewNet()->getUndoList());188}189}190191192bool193GNEOverheadWire::checkDrawMoveContour() const {194return false;195}196197198void199GNEOverheadWire::updateGeometry() {200// compute path201computePathElement();202}203204205Position206GNEOverheadWire::getPositionInView() const {207return myAdditionalGeometry.getShape().getPolygonCenter();208}209210211void212GNEOverheadWire::updateCenteringBoundary(const bool /* updateGrid */) {213// nothing to update214}215216217void218GNEOverheadWire::splitEdgeGeometry(const double /* splitPosition */, const GNENetworkElement* originalElement, const GNENetworkElement* newElement, GNEUndoList* undoList) {219// obtain new list of lanes220std::string newLanes = getNewListOfParents(originalElement, newElement);221// update Lanes222if (newLanes.size() > 0) {223setAttribute(SUMO_ATTR_LANES, newLanes, undoList);224}225}226227228void229GNEOverheadWire::drawGL(const GUIVisualizationSettings& /*s*/) const {230// nothing to draw231}232233234void235GNEOverheadWire::computePathElement() {236// calculate path237myNet->getNetworkPathManager()->calculateConsecutivePathLanes(this, getParentLanes());238}239240241void242GNEOverheadWire::drawLanePartialGL(const GUIVisualizationSettings& s, const GNESegment* segment, const double offsetFront) const {243// calculate overheadWire width244const double overheadWireWidth = s.addSize.getExaggeration(s, segment->getLane());245// check if E2 can be drawn246if (segment->getLane() && myNet->getViewNet()->getDataViewOptions().showAdditionals()) {247// get detail level248const auto d = s.getDetailLevel(overheadWireWidth);249// calculate startPos250const double geometryDepartPos = getAttributeDouble(SUMO_ATTR_STARTPOS);251// get endPos252const double geometryEndPos = getAttributeDouble(SUMO_ATTR_ENDPOS);253// declare path geometry254GUIGeometry overheadWireGeometry;255// update pathGeometry depending of first and last segment256if (segment->isFirstSegment() && segment->isLastSegment()) {257overheadWireGeometry.updateGeometry(segment->getLane()->getLaneGeometry().getShape(),258geometryDepartPos,259Position::INVALID,260geometryEndPos,261Position::INVALID);262} else if (segment->isFirstSegment()) {263overheadWireGeometry.updateGeometry(segment->getLane()->getLaneGeometry().getShape(),264geometryDepartPos,265Position::INVALID,266-1,267Position::INVALID);268} else if (segment->isLastSegment()) {269overheadWireGeometry.updateGeometry(segment->getLane()->getLaneGeometry().getShape(),270-1,271Position::INVALID,272geometryEndPos,273Position::INVALID);274} else {275overheadWireGeometry = segment->getLane()->getLaneGeometry();276}277// get both geometries278auto overheadWireGeometryTop = overheadWireGeometry;279auto overheadWireGeometryBot = overheadWireGeometry;280// move to sides281overheadWireGeometryTop.moveGeometryToSide(overheadWireWidth * 0.5);282overheadWireGeometryBot.moveGeometryToSide(overheadWireWidth * -0.5);283// draw geometry only if we'rent in drawForObjectUnderCursor mode284if (s.checkDrawAdditional(d, isAttributeCarrierSelected())) {285// obtain color286const RGBColor overheadWireColorTop = drawUsingSelectColor() ? s.colorSettings.selectedAdditionalColor : s.additionalSettings.overheadWireColorTop;287const RGBColor overheadWireColorBot = drawUsingSelectColor() ? s.colorSettings.selectedAdditionalColor : s.additionalSettings.overheadWireColorBot;288// push layer matrix289GLHelper::pushMatrix();290// Start with the drawing of the area traslating matrix to origin291glTranslated(0, 0, getType() + offsetFront);292// Set top color293GLHelper::setColor(overheadWireColorTop);294// draw top geometry295GUIGeometry::drawGeometry(d, overheadWireGeometryTop, 0.2);296// Set bot color297GLHelper::setColor(overheadWireColorBot);298// draw bot geometry299GUIGeometry::drawGeometry(d, overheadWireGeometryBot, 0.2);300// draw geometry points301if (segment->isFirstSegment() && segment->isLastSegment()) {302drawLeftGeometryPoint(s, d, overheadWireGeometry.getShape().front(), overheadWireGeometry.getShapeRotations().front(), overheadWireColorTop, true);303drawRightGeometryPoint(s, d, overheadWireGeometry.getShape().back(), overheadWireGeometry.getShapeRotations().back(), overheadWireColorTop, true);304} else if (segment->isFirstSegment()) {305drawLeftGeometryPoint(s, d, overheadWireGeometry.getShape().front(), overheadWireGeometry.getShapeRotations().front(), overheadWireColorTop, true);306} else if (segment->isLastSegment()) {307drawRightGeometryPoint(s, d, overheadWireGeometry.getShape().back(), overheadWireGeometry.getShapeRotations().back(), overheadWireColorTop, true);308}309// Pop layer matrix310GLHelper::popMatrix();311// draw dotted contour312myAdditionalContour.drawDottedContours(s, d, this, s.dottedContourSettings.segmentWidth, true);313}314// declare trim geometry to draw315const auto shape = (segment->isFirstSegment() || segment->isLastSegment()) ? overheadWireGeometry.getShape() : segment->getLane()->getLaneShape();316// calculate contour and draw dotted geometry317myAdditionalContour.calculateContourExtrudedShape(s, d, this, shape, getType(), overheadWireWidth, 1, true, true, 0, segment, segment->getLane()->getParentEdge());318}319}320321322void323GNEOverheadWire::drawJunctionPartialGL(const GUIVisualizationSettings& s, const GNESegment* segment, const double offsetFront) const {324// calculate overheadWire width325const double overheadWireWidth = s.addSize.getExaggeration(s, segment->getPreviousLane());326// check if overhead wire can be drawn327if (myNet->getViewNet()->getDataViewOptions().showAdditionals() && segment->getPreviousLane() && segment->getNextLane()) {328// obtain color329const RGBColor overheadWireColorTop = drawUsingSelectColor() ? s.colorSettings.selectedAdditionalColor : s.additionalSettings.overheadWireColorTop;330const RGBColor overheadWireColorBot = drawUsingSelectColor() ? s.colorSettings.selectedAdditionalColor : s.additionalSettings.overheadWireColorBot;331// declare geometry332GUIGeometry overheadWireGeometry({segment->getPreviousLane()->getLaneShape().back(), segment->getNextLane()->getLaneShape().front()});333// get detail level334const auto d = s.getDetailLevel(1);335// check if exist connection336if (segment->getPreviousLane()->getLane2laneConnections().exist(segment->getNextLane())) {337overheadWireGeometry = segment->getPreviousLane()->getLane2laneConnections().getLane2laneGeometry(segment->getNextLane());338}339// get both geometries340auto overheadWireGeometryTop = overheadWireGeometry;341auto overheadWireGeometryBot = overheadWireGeometry;342// move to sides343overheadWireGeometryTop.moveGeometryToSide(overheadWireWidth * 0.5);344overheadWireGeometryBot.moveGeometryToSide(overheadWireWidth * -0.5);345// draw geometry only if we'rent in drawForObjectUnderCursor mode346if (s.checkDrawAdditional(d, isAttributeCarrierSelected())) {347// Add a draw matrix348GLHelper::pushMatrix();349// Start with the drawing of the area traslating matrix to origin350glTranslated(0, 0, getType() + offsetFront);351// Set top color352GLHelper::setColor(overheadWireColorTop);353// draw top geometry354GUIGeometry::drawGeometry(d, overheadWireGeometryTop, 0.2);355// Set bot color356GLHelper::setColor(overheadWireColorBot);357// draw bot geometry358GUIGeometry::drawGeometry(d, overheadWireGeometryBot, 0.2);359// Pop last matrix360GLHelper::popMatrix();361// draw dotted contour362myAdditionalContour.drawDottedContours(s, d, this, s.dottedContourSettings.segmentWidth, true);363}364// draw contours365if (segment->getPreviousLane()->getLane2laneConnections().exist(segment->getNextLane())) {366// get shape367const auto& shape = segment->getPreviousLane()->getLane2laneConnections().getLane2laneGeometry(segment->getNextLane()).getShape();368// calculate contour and draw dotted geometry369myAdditionalContour.calculateContourExtrudedShape(s, d, this, shape, getType(), overheadWireWidth, 1, true, true, 0, segment, segment->getJunction());370}371}372}373374375std::string376GNEOverheadWire::getAttribute(SumoXMLAttr key) const {377switch (key) {378case SUMO_ATTR_ID:379return getMicrosimID();380case SUMO_ATTR_SUBSTATIONID:381return getParentAdditionals().front()->getID();382case SUMO_ATTR_LANES:383return parseIDs(getParentLanes());384case SUMO_ATTR_STARTPOS:385return toString(myStartPos);386case SUMO_ATTR_ENDPOS:387return toString(myEndPos);388case SUMO_ATTR_FRIENDLY_POS:389return toString(myFriendlyPosition);390case SUMO_ATTR_OVERHEAD_WIRE_FORBIDDEN:391return toString(myForbiddenInnerLanes);392case GNE_ATTR_SHIFTLANEINDEX:393return "";394default:395return getCommonAttribute(this, key);396}397}398399400double401GNEOverheadWire::getAttributeDouble(SumoXMLAttr key) const {402switch (key) {403case SUMO_ATTR_STARTPOS:404if (myStartPos < 0) {405return 0;406} else if (myStartPos > getParentLanes().front()->getParentEdge()->getNBEdge()->getFinalLength()) {407return getParentLanes().front()->getParentEdge()->getNBEdge()->getFinalLength();408} else {409return myStartPos;410}411case SUMO_ATTR_ENDPOS:412if (myEndPos < 0) {413return 0;414} else if (myEndPos > getParentLanes().back()->getParentEdge()->getNBEdge()->getFinalLength()) {415return getParentLanes().back()->getParentEdge()->getNBEdge()->getFinalLength();416} else {417return myEndPos;418}419default:420throw InvalidArgument(getTagStr() + " doesn't have an attribute of type '" + toString(key) + "'");421}422}423424425const Parameterised::Map&426GNEOverheadWire::getACParametersMap() const {427return getParametersMap();428}429430431void432GNEOverheadWire::setAttribute(SumoXMLAttr key, const std::string& value, GNEUndoList* undoList) {433switch (key) {434case SUMO_ATTR_ID:435case SUMO_ATTR_SUBSTATIONID:436case SUMO_ATTR_LANES:437case SUMO_ATTR_STARTPOS:438case SUMO_ATTR_ENDPOS:439case SUMO_ATTR_FRIENDLY_POS:440case SUMO_ATTR_OVERHEAD_WIRE_FORBIDDEN:441GNEChange_Attribute::changeAttribute(this, key, value, undoList);442break;443default:444setCommonAttribute(key, value, undoList);445break;446}447}448449450bool451GNEOverheadWire::isValid(SumoXMLAttr key, const std::string& value) {452switch (key) {453case SUMO_ATTR_ID:454return isValidAdditionalID(value);455case SUMO_ATTR_SUBSTATIONID:456if (value.empty()) {457return false;458} else {459return (myNet->getAttributeCarriers()->retrieveAdditional(SUMO_TAG_TRACTION_SUBSTATION, value, false) != nullptr);460}461case SUMO_ATTR_STARTPOS:462if (value.empty() || (value == LANE_START)) {463return true;464} else {465return canParse<double>(value);466}467case SUMO_ATTR_ENDPOS:468if (value.empty() || (value == LANE_END)) {469return true;470} else {471return canParse<double>(value);472}473case SUMO_ATTR_FRIENDLY_POS:474return canParse<bool>(value);475case SUMO_ATTR_OVERHEAD_WIRE_FORBIDDEN:476return true;477default:478return isCommonValid(key, value);479}480}481482483std::string484GNEOverheadWire::getPopUpID() const {485return getTagStr() + ": " + getID();486}487488489std::string490GNEOverheadWire::getHierarchyName() const {491return getTagStr();492}493494// ===========================================================================495// private496// ===========================================================================497498void499GNEOverheadWire::setAttribute(SumoXMLAttr key, const std::string& value) {500switch (key) {501case SUMO_ATTR_ID:502// update microsimID503setAdditionalID(value);504break;505case SUMO_ATTR_SUBSTATIONID:506replaceAdditionalParent(SUMO_TAG_TRACTION_SUBSTATION, value, 0);507break;508case SUMO_ATTR_LANES:509replaceAdditionalParentLanes(value);510break;511case SUMO_ATTR_STARTPOS:512if (value.empty() || (value == LANE_START)) {513myStartPos = INVALID_DOUBLE;514} else {515myStartPos = parse<double>(value);516}517// update geometry (except for template)518if (getParentLanes().size() > 0) {519updateGeometry();520}521break;522case SUMO_ATTR_ENDPOS:523if (value.empty() || (value == LANE_END)) {524myEndPos = INVALID_DOUBLE;525} else {526myEndPos = parse<double>(value);527}528// update geometry (except for template)529if (getParentLanes().size() > 0) {530updateGeometry();531}532break;533case SUMO_ATTR_FRIENDLY_POS:534myFriendlyPosition = parse<bool>(value);535break;536case SUMO_ATTR_OVERHEAD_WIRE_FORBIDDEN:537myForbiddenInnerLanes = parse<std::vector<std::string> >(value);538break;539case GNE_ATTR_SHIFTLANEINDEX:540shiftLaneIndex();541break;542default:543setCommonAttribute(this, key, value);544break;545}546}547548void549GNEOverheadWire::setMoveShape(const GNEMoveResult& moveResult) {550if ((moveResult.operationType == GNEMoveOperation::OperationType::SINGLE_LANE_MOVE_FIRST) ||551(moveResult.operationType == GNEMoveOperation::OperationType::MULTIPLE_LANES_MOVE_FIRST)) {552// change only start position553myStartPos = moveResult.newFirstPos;554} else if ((moveResult.operationType == GNEMoveOperation::OperationType::SINGLE_LANE_MOVE_LAST) ||555(moveResult.operationType == GNEMoveOperation::OperationType::MULTIPLE_LANES_MOVE_LAST)) {556// change only end position557myEndPos = moveResult.newFirstPos;558} else {559// change both position560myStartPos = moveResult.newFirstPos;561myEndPos = moveResult.newLastPos;562}563// update geometry564updateGeometry();565}566567568void569GNEOverheadWire::commitMoveShape(const GNEMoveResult& moveResult, GNEUndoList* undoList) {570// begin change attribute571undoList->begin(this, "position of " + getTagStr());572// set attributes depending of operation type573if ((moveResult.operationType == GNEMoveOperation::OperationType::SINGLE_LANE_MOVE_FIRST) ||574(moveResult.operationType == GNEMoveOperation::OperationType::MULTIPLE_LANES_MOVE_FIRST)) {575// set only start position576setAttribute(SUMO_ATTR_STARTPOS, toString(moveResult.newFirstPos), undoList);577} else if ((moveResult.operationType == GNEMoveOperation::OperationType::SINGLE_LANE_MOVE_LAST) ||578(moveResult.operationType == GNEMoveOperation::OperationType::MULTIPLE_LANES_MOVE_LAST)) {579// set only end position580setAttribute(SUMO_ATTR_ENDPOS, toString(moveResult.newFirstPos), undoList);581} else {582// set both positions583setAttribute(SUMO_ATTR_STARTPOS, toString(moveResult.newFirstPos), undoList);584setAttribute(SUMO_ATTR_ENDPOS, toString(moveResult.newLastPos), undoList);585}586// end change attribute587undoList->end();588}589590591double592GNEOverheadWire::getStartGeometryPositionOverLane() const {593// get lane final and shape length594const double laneLength = getParentLanes().front()->getParentEdge()->getNBEdge()->getFinalLength();595// get startPosition596double fixedPos = myStartPos;597// adjust fixedPos598if (fixedPos < 0) {599fixedPos += laneLength;600}601fixedPos *= getParentLanes().front()->getLengthGeometryFactor();602// return depending of fixedPos603if (fixedPos < 0) {604return 0;605} else if (fixedPos > (getParentLanes().front()->getLaneShapeLength() - POSITION_EPS)) {606return (getParentLanes().front()->getLaneShapeLength() - POSITION_EPS);607} else {608return fixedPos;609}610}611612613double614GNEOverheadWire::getEndGeometryPositionOverLane() const {615// get lane final and shape length616const double laneLength = getParentLanes().back()->getParentEdge()->getNBEdge()->getFinalLength();617// get endPosition618double fixedPos = myEndPos;619// adjust fixedPos620if (fixedPos < 0) {621fixedPos += laneLength;622}623fixedPos *= getParentLanes().back()->getLengthGeometryFactor();624// return depending of fixedPos625if (fixedPos < POSITION_EPS) {626return POSITION_EPS;627} else if (fixedPos > getParentLanes().back()->getLaneShapeLength()) {628return getParentLanes().back()->getLaneShapeLength();629} else {630return fixedPos;631}632}633634/****************************************************************************/635636637