0

Using QGIS v2.18, I have been attempting to combine (pipe?) two Processing Toolbox algorithms into one script. Random Extract as the first and Differential Privacy as the second. Through my weeks (literally), of research and seeking any examples I can find, I understand you can have only one output so I have been trying to code the output of the first algorithm as the input of the 2nd algorithm with no success.

It appears one must denote the first process with an [‘OUTPUT’] but as to where/how this is to be inserted is my failure. Not sure whether this should be inserted under def defineCharacteristics(self): or under def processAlgorithm(self, progress):

Both scripts work correctly on their own. What I do not understand is why I can’t simply change: INPUT_LAYER = 'INPUT_LAYER' to INPUT_LAYER = 'OUTPUT' or change inputFilename = self.getParameterValue(self.INPUT_LAYER) to inputFilename = self.getParameterValue(self.OUTPUT).

FYI - I am using all the imports that are found in the two scripts

class Random_Point_MakerAlgorithm(GeoAlgorithm):
 # from Random Extract constants
 INPUT = 'INPUT'
 OUTPUT = 'OUTPUT'
 METHOD = 'METHOD'
 NUMBER = 'NUMBER'
 """
 Differential Privacy algorithm implementing the method outlined in:
 Andrés, M.E. et al., 2013. Geo-indistinguishability. In the 2013 ACM SIGSAC
 conference. New York, New York, USA: ACM Press, pp. 901–914.
 """
 # Constants used to refer to parameters and outputs. They will be
 # used when calling the algorithm from another algorithm, or when
 # calling from the QGIS console.
 # Differencial Privacy constants
 OUTPUT_LAYER = 'OUTPUT_LAYER'
 INPUT_LAYER = 'INPUT_LAYER'
 PROTECTION_DISTANCE = 'PROTECTION_DISTANCE'
 NINETY_FIVE_DISTANCE = 'NINETY_FIVE_DISTANCE'
 LIMIT_NINETY_FIVE = 'LIMIT_NINETY_FIVE'
 def getIcon(self):
 """Get the icon.
 """
 return DifferentialPrivacyUtils.getIcon()
 def defineCharacteristics(self):
 
 self.methods = [self.tr('Percentage of selected features'),
 self.tr('Number of selected features')]
 self.addParameter(ParameterVector(self.INPUT,
 self.tr('Input Points Layer'), [ParameterVector.VECTOR_TYPE_POINT]))
 self.addParameter(ParameterSelection(self.METHOD,
 self.tr('Processing Method'), self.methods, 0))
 self.addParameter(ParameterNumber(self.NUMBER,
 self.tr('Percentage or Number of selected features'), 0, None, 50))
 
 # This line below I usually disable to work on having only one output
 self.addOutput(OutputVector(self.OUTPUT, self.tr('Extracted Points')))
 
 """Here we define the inputs and output of the algorithm, along
 with some other properties.
 """
 # The name that the user will see in the toolbox
 self.name = 'Diff Privacy points'
 # The branch of the toolbox under which the algorithm will appear
 self.group = 'Vector'
 # We add the input vector layer. It can have any kind of geometry
 # It is a mandatory (not optional) one, hence the False argument
 
 # This line berlow I usually comment out to pull in the OUTPUT layer of the 
 # Random Extracted layer to continue as the input.
 self.addParameter(ParameterVector(self.INPUT_LAYER,
 self.tr('Input layer'), [ParameterVector.VECTOR_TYPE_POINT], False))
 self.addParameter(ParameterNumber(
 self.PROTECTION_DISTANCE,
 self.tr('Protection distance (projected units)'),
 minValue=0.,
 default=0.000200
 ))
 self.addParameter(ParameterBoolean(
 self.LIMIT_NINETY_FIVE,
 "Limit the distance moved to the 95% confidence interval",
 default=False
 ))
 # We add a vector layer as output
 self.addOutput(OutputVector(self.OUTPUT_LAYER,
 self.tr('Anonymized Features')))
 self.addOutput(OutputNumber(
 self.NINETY_FIVE_DISTANCE,
 "95% confidence distance for offset"
 ))
 def processAlgorithm(self, progress):
 """Here is where the Random Extract processing itself takes place."""
 
 filename = self.getParameterValue(self.INPUT)
 layer = dataobjects.getObjectFromUri(filename)
 method = self.getParameterValue(self.METHOD)
 
 features = vector.features(layer)
 featureCount = len(features)
 value = int(self.getParameterValue(self.NUMBER))
 
 if method == 0:
 if value > featureCount:
 raise GeoAlgorithmExecutionException(
 self.tr('Selected number is greater than feature count. '
 'Choose a lower value and try again.'))
 else:
 if value > 100:
 raise GeoAlgorithmExecutionException(
 self.tr("Percentage can't be greater than 100. Set a "
 "different value and try again."))
 value = int(round(value / 100.0000, 4) * featureCount)
 
 selran = random.sample(xrange(featureCount), value)
 
 writer = self.getOutputFromName(self.OUTPUT).getVectorWriter(
 layer.pendingFields().toList(), layer.wkbType(), layer.crs())
 
 
 total = 100.0 / featureCount if featureCount > 0 else 1
 for i, feat in enumerate(features):
 if i in selran:
 writer.addFeature(feat)
 progress.setPercentage(int(i * total))
 del writer
 """Differenctial Privacy processing below."""
 
 # The first thing to do is retrieve the values of the parameters
 # entered by the user
 inputFilename = self.getParameterValue(self.INPUT_LAYER)
 radius = float(self.getParameterValue(
 self.PROTECTION_DISTANCE))
 
 base_epsilon = float(ProcessingConfig.getSetting(
 DifferentialPrivacyUtils.DIFFERENTIAL_EPSILON))
 
 limit_nine_five = self.getParameterValue(self.LIMIT_NINETY_FIVE)
 
 # scale should be 1 / epsilon where epsilon is some base epsilon constant / chosen radius
 r_generator = gamma(2., scale=radius / base_epsilon)
 theta_generator = uniform(scale=2 * np.pi)
 
 output = self.getOutputValue(self.OUTPUT_LAYER)
 
 # Input layers vales are always a string with its location.
 # That string can be converted into a QGIS object (a
 # QgsVectorLayer in this case) using the
 # processing.getObjectFromUri() method.
 vectorLayer = dataobjects.getObjectFromUri(inputFilename)
 
 # And now we can process
 
 # First we create the output layer. The output value entered by
 # the user is a string containing a filename, so we can use it
 # directly
 settings = QSettings()
 systemEncoding = settings.value('/UI/encoding', 'System')
 provider = vectorLayer.dataProvider()
 writer = QgsVectorFileWriter(output, systemEncoding,
 provider.fields(),
 provider.geometryType(), provider.crs())
 
 # Now we take the features from input layer and add them to the
 # output. Method features() returns an iterator, considering the
 # selection that might exist in layer and the configuration that
 # indicates should algorithm use only selected features or all
 # of them
 
 nine_five_distance = r_generator.ppf(0.95)
 
 features = vector.features(vectorLayer)
 for f in features:
 r = r_generator.rvs()
 if limit_nine_five and r > nine_five_distance:
 r = nine_five_distance
 theta = theta_generator.rvs()
 g = f.geometryAndOwnership()
 g.translate(np.cos(theta) * r, np.sin(theta) * r)
 f.setGeometry(g)
 
 writer.addFeature(f)
 
 ProcessingLog.addToLog(
 ProcessingLog.LOG_INFO,
 "95% confiedence distance: {}".format(nine_five_distance)
 )
 
 self.setOutputValue(self.NINETY_FIVE_DISTANCE, nine_five_distance)
menes
1,4272 gold badges8 silver badges25 bronze badges
asked Jun 7, 2020 at 18:50
2

0

Know someone who can answer? Share a link to this question via email, Twitter, or Facebook.

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.