Code fragments for scaling and shifting portable version

I spent some time coding the "most involvement" option (i.e. physically walking to four points to define a perimeter). This is because Calvium Tools currently (April 2011) do not directly support user creation of Geo objects at runtime, but they do support programatic generation of Line objects using the current GPS location.

The code fragments below are from a prototype for scaling and shifting a set of Geo objects to fit within a perimeter defined at runtime (under the Geo object name "userPerimeterLine").


function relocateAllFeatures()
{
  Debug.log( "relocateAllFeatures()" );  

   // find the outerPerimeter geo feature
  var outerPerimeter = GeoFeatures.getFeatureByName("outerPerimeter");

  // now find the userPerimeter geo feature
  //  a user can define the userPerimeterLine from scratch, or restore from storedData
  //  we try to find an instance in GeoFeatures first, then try to restore, then give up
  try
  {  
    var userPerimeterLine = GeoFeatures.getFeatureByName("userPerimeterLine");

        if ( userPerimeterLine !== null )
    {
      Debug.log("found geo feature userPerimeterLine " + userPerimeterLine)
    }
    else if (     userPerimeterLine === null
        && storedData.userPerimeterLineNodes !== null )
    {
      Debug.log('attempting to restore userPerimeterLine from storedData');
      recreateStoredUserPerimeterLine();
      userPerimeterLine = GeoFeatures.getFeatureByName("restored");
      userPerimeterLine.setName("userPerimeterLine");
    }
  }
  catch(ex) { Debug.log('ERROR - cannot find or restore userPerimeterLine');  }

  // abort if one or both perimeter geo features are missing
  if (     outerPerimeter === null
    || userPerimeterLine === null ) 
  {
    Debug.log('ERROR - cannot relocate features. ');  
  }
  else
  {
    // find the bounds of the outerPerimeter and userPerimeterLine
    var outerPerimeterBounds = outerPerimeter.getBounds();
    var userPerimeterBounds = userPerimeterLine.getBounds();

        // debug these bounds
    Debug.log( "[outerPerimeter]" +  boundsToString( outerPerimeterBounds ) );
    Debug.log( "[userPerimeterLine]" + boundsToString( userPerimeterBounds ) );

        // set multipliers
    var widthMultiplier =  userPerimeterBounds.getWidth();
    widthMultiplier /= outerPerimeterBounds.getWidth();

    var heightMultiplier = userPerimeterBounds.getHeight();
    heightMultiplier /= outerPerimeterBounds.getHeight();

        // debug these multipliers
    Debug.log( "widthMultiplier: "  + widthMultiplier);
    Debug.log( "heightMultiplier: " + heightMultiplier);

        // determine xoffset and yoffset using perimeters
    var xPerimeterOffset = userPerimeterBounds.getCenter().x;
    xPerimeterOffset -= outerPerimeterBounds.getCenter().x;

    var yPerimeterOffset = userPerimeterBounds.getCenter().y;
    yPerimeterOffset -= outerPerimeterBounds.getCenter().y;

          // debug these offsets
    Debug.log( "xPerimeterOffset: " + xPerimeterOffset);
    Debug.log( "yPerimeterOffset: " + yPerimeterOffset);

            // relocate outer perimeter to align with userPerimeterLine
    relocateByReferenceFeature( outerPerimeter,
                userPerimeterLine,
                widthMultiplier,
                heightMultiplier);

        // iterate over each geo feature (apart from perimeters), scaling and shifting
    var featureCount = GeoFeatures.getLength();
    while(featureCount--)
    {
      var feature = GeoFeatures.getFeature(featureCount);
      if ( feature.getType() == 'Zone'
       || feature.getType() == 'Line'
       || feature.getType() == 'Point' )
      {
        if ( feature.getName() != "userPerimeterLine"
         && feature.getName() != "outerPerimeter" )
        {           
          // relocate to new perimeter
          shiftFeature (feature, xPerimeterOffset, yPerimeterOffset);

                  // scale objects
          scaleFeature (feature, widthMultiplier, heightMultiplier);

                  // shift each object to (scaled) distance from the centre of the new perimeter
          var newCentreX = (outerPerimeter.getCenter().x - feature.getCenter().x);
          newCentreX *= widthMultiplier;

          newCentreY = (outerPerimeter.getCenter().y - feature.getCenter().y);
          newCentreY *= heightMultiplier

          shiftFeature ( feature, newCentreX, newCentreY );

        }
        else
    {
       log("skipping: feature "
        + featureCount
        + " ("
        + feature.getName()
        + ") is a perimeter" );
    }
      }
      else
      {
    log("skipping: feature "
        + featureCount
        + " ("
        + feature.getName()
        + ") is not a Zone/Line/Point" );
      }
    }

        // remove userPerimeterLine
    GeoFeatures.removeFeatureByName("userPerimeterLine");

  }

}

function relocateByOffsetAndScale(
    featureToChange,
    xOffset,
    yOffset,
    xScale,
    yScale )
{
  // scale first (around original center)
  scaleFeature( featureToChange , xScale, yScale );

    // shift feature (offset each x and y value in GPS units)
  shiftFeature( featureToChange , xOffset, yOffset );
}

function relocateByReferenceFeature(
    featureToChange,
    referenceFeature,
    widthMultiplier,
    heightMultiplier )
{  
  Debug.log("relocatePerimeter()");
  Debug.log("-featureToChange " + featureToChange.getName() );
  Debug.log("-referenceFeature " + referenceFeature.getName() );
  Debug.log("-widthMultiplier " + widthMultiplier);
  Debug.log("-heightMultiplier " + heightMultiplier);

  // scale the feature
  try{ scaleFeature( featureToChange, widthMultiplier, heightMultiplier ); }
  catch(ex){ Debug.log('cant scale'); }

    // shift the feature
  try{ featureToChange.setCenter( referenceFeature.getCenter() ); }
  catch(ex){ Debug.log('cant set centre'); }
}

function shiftFeature ( feature, shiftX, shiftY )
{
  // default for scaling arguments
  if (!shiftX) shiftX = 0.0;
  if (!shiftY) scaleY = 0.0;

  // clone feature
  var cloneFeature = feature.clone();

  // shift nodes
  var nodes = cloneFeature.getNodes();
  var length = cloneFeature.length();
  while(length--)
  {    
    nodes[length].x = (nodes[length].x + shiftX );
    nodes[length].y = (nodes[length].y + shiftY );
  }

    // set nodes
  feature.setNodes( nodes );
}

function scaleFeature( feature, scaleX, scaleY )
{
  // default for scaling arguments
  if (!scaleX) scaleX = 1.0;
  if (!scaleY) scaleY = 1.0;

  // clone feature
  var cloneFeature = feature.clone();

  // find existing centre point
  var oldCenter = cloneFeature.getCenter().clone();

  // scale nodes
  var nodes = cloneFeature.getNodes();
  var length = cloneFeature.length();
  while(length--)
  {    
    nodes[length].x = (nodes[length].x * scaleX );
    nodes[length].y = (nodes[length].y * scaleY );
  }

        // re-center
  // FIXME: strange bug (y coords shifted) unless this is done twice
  cloneFeature.setCenter( oldCenter );
  cloneFeature.setCenter( oldCenter );

      // set nodes
  feature.setNodes( nodes ); // instead of removing and adding

 
}

209 views and 0 responses