void spectral_values_to_spectrum(
    const float     low_wavelength,
    const float     high_wavelength,
    const size_t    input_spectrum_count,
    const float     input_spectrum[],
    float           output_spectrum[])
{
    assert(low_wavelength < high_wavelength);

    // Generate the wavelengths for which this spectrum is defined.
    vector<float> wavelengths(input_spectrum_count);
    generate_wavelengths(
        low_wavelength,
        high_wavelength,
        input_spectrum_count,
        &wavelengths[0]);

    // Resample the spectrum to the internal wavelength range.
    spectrum_to_spectrum(
        input_spectrum_count,
        &wavelengths[0],
        input_spectrum,
        Spectrum::Samples,
        &g_light_wavelengths_nm[0],
        output_spectrum);
}
bool ResamplerPlugIn::execute(PlugInArgList* pInArgList, PlugInArgList* pOutArgList)
{
   VERIFY(pInArgList != NULL && pOutArgList != NULL);
   ProgressTracker progress(pInArgList->getPlugInArgValue<Progress>(Executable::ProgressArg()),
      "Executing Spectral Resampler.", "spectral", "{88CD3E49-A522-431A-AE2A-96A6B2EB4012}");

   Service<DesktopServices> pDesktop;

   const DataElement* pElement(NULL);
   std::string waveFilename;

   // get default resampling options from user config
   std::string resampleMethod = ResamplerOptions::getSettingResamplerMethod();
   double dropOutWindow = ResamplerOptions::getSettingDropOutWindow();
   double fwhm = ResamplerOptions::getSettingFullWidthHalfMax();
   bool useFillValue = ResamplerOptions::getSettingUseFillValue();
   double fillValue = ResamplerOptions::getSettingSignatureFillValue();

   std::vector<Signature*> originalSignatures;
   std::auto_ptr<std::vector<Signature*> > pResampledSignatures(new std::vector<Signature*>);
   std::string errorMsg;

   if (isBatch())
   {
      VERIFY(pInArgList->getPlugInArgValue("Signatures to resample", originalSignatures));
      if (originalSignatures.empty())
      {
         Signature* pSignature = pInArgList->getPlugInArgValue<Signature>("Signature to resample");
         if (pSignature != NULL)
         {
            originalSignatures.push_back(pSignature);
         }
      }
      if (originalSignatures.empty())
      {
         progress.report("No signatures are available to be resampled.", 0, ERRORS, true);
         return false;
      }
      pElement = pInArgList->getPlugInArgValue<DataElement>("Data element wavelength source");
      Filename* pWaveFilename = pInArgList->getPlugInArgValue<Filename>("Wavelengths Filename");
      if (pWaveFilename != NULL)
      {
         waveFilename = pWaveFilename->getFullPathAndName();
      }
      VERIFY(pInArgList->getPlugInArgValue("Resampling Method", resampleMethod));
      VERIFY(pInArgList->getPlugInArgValue("Drop out window", dropOutWindow));
      VERIFY(pInArgList->getPlugInArgValue("FWHM", fwhm));
      VERIFY(pInArgList->getPlugInArgValue("Use fill value", useFillValue));
      VERIFY(pInArgList->getPlugInArgValue("Fill value", fillValue));
   }
   else
   {
      ResamplerPlugInDlg dlg(pDesktop->getMainWidget());
      if (dlg.exec() == QDialog::Rejected)
      {
         progress.report("User canceled resampling.", 0, ABORT, true);
         progress.upALevel();
         return false;
      }
      originalSignatures = dlg.getSignaturesToResample();
      resampleMethod = dlg.getResamplingMethod();
      dropOutWindow = dlg.getDropOutWindow();
      fwhm = dlg.getFWHM();
      useFillValue = dlg.getUseFillValue();
      fillValue = dlg.getFillValue();
      pElement = dlg.getWavelengthsElement();
      waveFilename = dlg.getWavelengthsFilename();
   }

   std::string resampledTo;
   FactoryResource<Wavelengths> pWavelengths;
   if (pElement != NULL)  // try loading wavelengths from user specified data element
   {
      if (getWavelengthsFromElement(pElement, pWavelengths.get(), errorMsg) == false)
      {
         progress.report(errorMsg, 0, ERRORS, true);
         return false;
      }
      resampledTo = pElement->getName();
   }
   else if (waveFilename.empty() == false)  // if no user provided raster, look for a wavelengths file
   {
      if (QFile::exists(QString::fromStdString(waveFilename)))
      {
         if (getWavelengthsFromFile(waveFilename, pWavelengths.get(), errorMsg) == false)
         {
            progress.report(errorMsg, 0, ERRORS, true);
            return false;
         }
      }
      else
      {
         errorMsg = "The wavelengths file \"" + waveFilename + "\" could not be found.";
         progress.report(errorMsg, 0, ERRORS, true);
         return false;
      }
      resampledTo = waveFilename;
   }
   else  // if no wavelength source provided, look for raster in current active spatial data view
   {
      SpatialDataView* pView = dynamic_cast<SpatialDataView*>(pDesktop->getCurrentWorkspaceWindowView());
      if (pView != NULL)
      {
         LayerList* pLayerList = pView->getLayerList();
         if (pLayerList != NULL)
         {
            pElement = pLayerList->getPrimaryRasterElement();
            pWavelengths->initializeFromDynamicObject(pElement->getMetadata(), false);
            if (pWavelengths->isEmpty())
            {
               progress.report("No target wavelengths are available for resampling the signatures.", 0, ERRORS, true);
               return false;
            }
            resampledTo = pElement->getName();
         }
      }
   }

   PlugInResource pPlugIn("Resampler");
   Resampler* pResampler = dynamic_cast<Resampler*>(pPlugIn.get());
   if (pResampler == NULL)
   {
      progress.report("The \"Resampler\" plug-in is not available so the signatures can not be resampled.",
         0, ERRORS, true);
      return false;
   }
   std::string dataName("Reflectance");
   std::string wavelengthName("Wavelength");

   // save user config settings - Resampler doesn't have interface to set them separately from user config
   std::string configMethod = ResamplerOptions::getSettingResamplerMethod();
   ResamplerOptions::setSettingResamplerMethod(resampleMethod);
   double configDropout = ResamplerOptions::getSettingDropOutWindow();
   ResamplerOptions::setSettingDropOutWindow(dropOutWindow);
   double configFwhm = ResamplerOptions::getSettingFullWidthHalfMax();
   ResamplerOptions::setSettingFullWidthHalfMax(fwhm);

   std::vector<double> toWavelengths = pWavelengths->getCenterValues();
   std::vector<double> toFwhm = pWavelengths->getFwhm();
   if (toFwhm.size() != toWavelengths.size())
   {
      toFwhm.clear();  // Resampler will use the default config setting fwhm if this vector is empty
   }

   unsigned int numSigs = originalSignatures.size();
   unsigned int numSigsResampled(0);
   progress.report("Begin resampling signatures...", 0, NORMAL);
   for (unsigned int index = 0; index < numSigs; ++index)
   {
      if (isAborted())
      {
         progress.report("Resampling aborted by user", 100 * index / numSigs, ABORT, true);
         return false;
      }
      if (originalSignatures[index] == NULL)
      {
         continue;
      }

      // check if signature has target wavelength centers and doesn't need to be resampled
      if (needToResample(originalSignatures[index], pWavelengths.get()) == false)
      {
         pResampledSignatures->push_back(originalSignatures[index]);
         ++numSigsResampled;
         continue;
      }

      DataVariant var = originalSignatures[index]->getData(dataName);
      if (var.isValid() == false)
      {
         continue;
      }
      std::vector<double> fromData;
      if (!var.getValue(fromData))
      {
         continue;
      }
      var = originalSignatures[index]->getData(wavelengthName);
      if (var.isValid() == false)
      {
         continue;
      }
      std::vector<double> fromWavelengths;
      if (!var.getValue(fromWavelengths))
      {
         continue;
      }
      std::string resampledSigName = originalSignatures[index]->getName() + "_resampled";
      int suffix(2);
      ModelResource<Signature> pSignature(resampledSigName, NULL);

      // probably not needed but just in case resampled name already used
      while (pSignature.get() == NULL)
      {
         pSignature = ModelResource<Signature>(resampledSigName + StringUtilities::toDisplayString(suffix), NULL);
         ++suffix;
      }
      if (resampledTo.empty() == false)
      {
         DynamicObject* pMetaData = pSignature->getMetadata();
         if (pMetaData != NULL)
         {
            pMetaData->setAttribute(CommonSignatureMetadataKeys::ResampledTo(), resampledTo);
         }
      }
      std::vector<double> toData;
      std::vector<int> toBands;
      if (pResampler->execute(fromData, toData, fromWavelengths, toWavelengths, toFwhm, toBands, errorMsg))
      {
         if (toWavelengths.size() != toBands.size())
         {
            if (toBands.size() < 2)  // no need to try if only one point
            {
               continue;
            }

            if (useFillValue)
            {
               std::vector<double> values(toWavelengths.size(), fillValue);
               for (unsigned int i = 0; i < toBands.size(); ++i)
               {
                  values[static_cast<unsigned int>(toBands[i])] = toData[i];
               }
               toData.swap(values);
               DynamicObject* pMetaData = pSignature->getMetadata();
               if (pMetaData != NULL)
               {
                  pMetaData->setAttribute(CommonSignatureMetadataKeys::FillValue(), fillValue);
               }
            }
            else
            {
               std::vector<double> wavelengths(toBands.size());
               for (unsigned int i = 0; i < toBands.size(); ++i)
               {
                  wavelengths[i] = toWavelengths[static_cast<unsigned int>(toBands[i])];
               }
               toWavelengths.swap(wavelengths);
            }
         }
         pSignature->setData(dataName, toData);
         pSignature->setData(wavelengthName, toWavelengths);
         SignatureDataDescriptor* pDesc = dynamic_cast<SignatureDataDescriptor*>(pSignature->getDataDescriptor());
         if (pDesc == NULL)
         {
            continue;
         }
         pDesc->setUnits(dataName, originalSignatures[index]->getUnits(dataName));
         pResampledSignatures->push_back(pSignature.release());
         ++numSigsResampled;
      }
      std::string progressStr =
         QString("Resampled signature %1 of %2 signatures").arg(index + 1).arg(numSigs).toStdString();
      progress.report(progressStr, (index + 1) * 100 / numSigs, NORMAL);
   }

   // reset config options
   ResamplerOptions::setSettingResamplerMethod(configMethod);
   ResamplerOptions::setSettingDropOutWindow(configDropout);
   ResamplerOptions::setSettingFullWidthHalfMax(configFwhm);

   if (numSigsResampled == numSigs)
   {
      progress.report("Complete", 100, NORMAL);
      progress.upALevel();
   }
   else
   {
      errorMsg = QString("Only %1 of the %2 signatures were successfully resampled.").arg(
         numSigsResampled).arg(numSigs).toStdString();
      progress.report(errorMsg, 100, WARNING, true);
   }

   VERIFY(pOutArgList->setPlugInArgValue("Resampled signatures", pResampledSignatures.release()));
   return true;
}