23.9. Nieuwe algoritmen voor Processing schrijven als scripts voor Python

Er bestaan twee opties voor het schrijven van algoritmen voor Processing met Python.

In QGIS kunt u Nieuw script maken in het menu Scripts boven in de Processing Toolbox gebruiken om de Processing Script bewerken te openen waar u uw code kunt schrijven. U kunt, om de taak te vereenvoudigen, beginnen met een sjabloon voor een script door Nieuw script uit sjabloon maken uit hetzelfde menu te gebruiken. Dat opent een sjabloon dat QgsProcessingAlgorithm uitbreidt.

Als u het script opslaat in de map scripts (de standaardlocatie) met de extensie .py, zal het algoritme beschikbaar komen in de Processing Toolbox.

23.9.1. QgsProcessingAlgorithm uitbreiden

De volgende code

  1. neemt een vectorlaag als invoer

  2. telt het aantal objecten

  3. doet een bewerking voor een buffer

  4. maakt een rasterlaag als resultaat van de bewerking voor de buffer

  5. geeft de laag voor de buffer, de rasterlaag en het aantal objecten terug

  1from qgis.PyQt.QtCore import QCoreApplication
  2from qgis.core import (QgsProcessing,
  3                       QgsProcessingAlgorithm,
  4                       QgsProcessingException,
  5                       QgsProcessingOutputNumber,
  6                       QgsProcessingParameterDistance,
  7                       QgsProcessingParameterFeatureSource,
  8                       QgsProcessingParameterVectorDestination,
  9                       QgsProcessingParameterRasterDestination)
 10from qgis import processing
 11
 12
 13class ExampleProcessingAlgorithm(QgsProcessingAlgorithm):
 14    """
 15    This is an example algorithm that takes a vector layer,
 16    creates some new layers and returns some results.
 17    """
 18
 19    def tr(self, string):
 20        """
 21        Returns a translatable string with the self.tr() function.
 22        """
 23        return QCoreApplication.translate('Processing', string)
 24
 25    def createInstance(self):
 26        # Must return a new copy of your algorithm.
 27        return ExampleProcessingAlgorithm()
 28
 29    def name(self):
 30        """
 31        Returns the unique algorithm name.
 32        """
 33        return 'bufferrasterextend'
 34
 35    def displayName(self):
 36        """
 37        Returns the translated algorithm name.
 38        """
 39        return self.tr('Buffer and export to raster (extend)')
 40
 41    def group(self):
 42        """
 43        Returns the name of the group this algorithm belongs to.
 44        """
 45        return self.tr('Example scripts')
 46
 47    def groupId(self):
 48        """
 49        Returns the unique ID of the group this algorithm belongs
 50        to.
 51        """
 52        return 'examplescripts'
 53
 54    def shortHelpString(self):
 55        """
 56        Returns a localised short help string for the algorithm.
 57        """
 58        return self.tr('Example algorithm short description')
 59
 60    def initAlgorithm(self, config=None):
 61        """
 62        Here we define the inputs and outputs of the algorithm.
 63        """
 64        # 'INPUT' is the recommended name for the main input
 65        # parameter.
 66        self.addParameter(
 67            QgsProcessingParameterFeatureSource(
 68                'INPUT',
 69                self.tr('Input vector layer'),
 70                types=[QgsProcessing.TypeVectorAnyGeometry]
 71            )
 72        )
 73        self.addParameter(
 74            QgsProcessingParameterVectorDestination(
 75                'BUFFER_OUTPUT',
 76                self.tr('Buffer output'),
 77            )
 78        )
 79        # 'OUTPUT' is the recommended name for the main output
 80        # parameter.
 81        self.addParameter(
 82            QgsProcessingParameterRasterDestination(
 83                'OUTPUT',
 84                self.tr('Raster output')
 85            )
 86        )
 87        self.addParameter(
 88            QgsProcessingParameterDistance(
 89                'BUFFERDIST',
 90                self.tr('BUFFERDIST'),
 91                defaultValue = 1.0,
 92                # Make distance units match the INPUT layer units:
 93                parentParameterName='INPUT'
 94            )
 95        )
 96        self.addParameter(
 97            QgsProcessingParameterDistance(
 98                'CELLSIZE',
 99                self.tr('CELLSIZE'),
100                defaultValue = 10.0,
101                parentParameterName='INPUT'
102            )
103        )
104        self.addOutput(
105            QgsProcessingOutputNumber(
106                'NUMBEROFFEATURES',
107                self.tr('Number of features processed')
108            )
109        )
110
111    def processAlgorithm(self, parameters, context, feedback):
112        """
113        Here is where the processing itself takes place.
114        """
115        # First, we get the count of features from the INPUT layer.
116        # This layer is defined as a QgsProcessingParameterFeatureSource
117        # parameter, so it is retrieved by calling
118        # self.parameterAsSource.
119        input_featuresource = self.parameterAsSource(parameters,
120                                                     'INPUT',
121                                                     context)
122        numfeatures = input_featuresource.featureCount()
123
124        # Retrieve the buffer distance and raster cell size numeric
125        # values. Since these are numeric values, they are retrieved
126        # using self.parameterAsDouble.
127        bufferdist = self.parameterAsDouble(parameters, 'BUFFERDIST',
128                                            context)
129        rastercellsize = self.parameterAsDouble(parameters, 'CELLSIZE',
130                                                context)
131        if feedback.isCanceled():
132            return {}
133        buffer_result = processing.run(
134            'native:buffer',
135            {
136                # Here we pass on the original parameter values of INPUT
137                # and BUFFER_OUTPUT to the buffer algorithm.
138                'INPUT': parameters['INPUT'],
139                'OUTPUT': parameters['BUFFER_OUTPUT'],
140                'DISTANCE': bufferdist,
141                'SEGMENTS': 10,
142                'DISSOLVE': True,
143                'END_CAP_STYLE': 0,
144                'JOIN_STYLE': 0,
145                'MITER_LIMIT': 10
146            },
147            # Because the buffer algorithm is being run as a step in
148            # another larger algorithm, the is_child_algorithm option
149            # should be set to True
150            is_child_algorithm=True,
151            #
152            # It's important to pass on the context and feedback objects to
153            # child algorithms, so that they can properly give feedback to
154            # users and handle cancelation requests.
155            context=context,
156            feedback=feedback)
157
158        # Check for cancelation
159        if feedback.isCanceled():
160            return {}
161
162        # Run the separate rasterization algorithm using the buffer result
163        # as an input.
164        rasterized_result = processing.run(
165            'qgis:rasterize',
166            {
167                # Here we pass the 'OUTPUT' value from the buffer's result
168                # dictionary off to the rasterize child algorithm.
169                'LAYER': buffer_result['OUTPUT'],
170                'EXTENT': buffer_result['OUTPUT'],
171                'MAP_UNITS_PER_PIXEL': rastercellsize,
172                # Use the original parameter value.
173                'OUTPUT': parameters['OUTPUT']
174            },
175            is_child_algorithm=True,
176            context=context,
177            feedback=feedback)
178
179        if feedback.isCanceled():
180            return {}
181
182        # Return the results
183        return {'OUTPUT': rasterized_result['OUTPUT'],
184                'BUFFER_OUTPUT': buffer_result['OUTPUT'],
185                'NUMBEROFFEATURES': numfeatures}

Standaardfuncties voor algoritmen van Processing

  • createInstance (verplicht)

    Moet een nieuwe kopie van uw algoritme teruggeven. Als u de naam van de klasse wijzigt, zorg er dan voor dat u ook de hier teruggegeven waarde bijwerkt zodat die overeenkomt!

  • name (verplicht)

    Geeft de unieke naam van het algoritme terug, gebruikt voor het identificeren van het algoritme.

  • displayName (verplicht)

    Geeft de vertaalde naam van het algoritme terug.

  • group

    Geeft de naam van de groep terug waartoe dit algoritme behoort.

  • groupId

    Geeft de unieke ID van de groep terug waartoe dit algoritme behoort.

  • shortHelpString

    Geeft een gelokaliseerde korte tekenreeks voor Help voor het algoritme terug.

  • initAlgorithm (verplicht)

    Hier definiëren we de in- en uitvoer voor het algoritme.

    INPUT en OUTPUT zijn aanbevolen namen voor de parameters voor de respectievelijke hoofdinvoer en hoofduitvoer.

    Als een parameter afhankelijk is van een andere parameter, wordt parentParameterName gebruikt om deze relatie te specificeren (zou het veld / band van een laag of de eenheden voor afstand van een laag kunnen zijn).

  • processAlgorithm (verplicht)

    Dit is waar de verwerking plaatsvindt.

    Parameters worden opgehaald met behulp van functies voor speciale doelen, bijvoorbeeld parameterAsSource en parameterAsDouble.

    processing.run kan worden gebruikt om andere algoritmen van Processing uit te voeren vanuit een algoritme van Processing. De eerste parameter is de naam van het algoritme, de tweede is een woordenboek van de parameters voor het algoritme. is_child_algorithm is normaal ingesteld op True bij het uitvoeren van een algoritme vanuit een ander algoritme. context en feedback informeert het algoritme over de omgeving waarin het moet worden uitgevoerd en het kanaal voor het communiceren met de gebruiker (opvangen verzoek tot annuleren, voortgang rapporteren, tekstuele terugkoppeling geven). Bij het gebruiken van de parameters van (ouder) algoritmen als parameters voor “kind”-algoritmen, zouden de originele waarden voor de parameter moeten worden gebruikt (bijv. parameters['OUTPUT']).

    Het is goed gebruik om het object feedback te controleren op annuleren zo vaak als enigszins mogelijk is! Door dit te doen maakt het reageren op annuleren mogelijk, in plaats van gebruikers te forceren te wachten tot ongewenste verwerking optreedt.

    Het algoritme zou waarden terug moeten geven voor alle waarden van de parameters voor de uitvoer die zijn gedefinieerd als een woordenboek. In dit geval zijn dat de buffer en de gerasteriseerde uitvoerlagen, en het aantal verwerkte objecten. De sleutels voor het woordenboek moeten overeen komen met de originele parameters/namen van de uitvoer.

23.9.2. De decorator @alg

Door de decorator @alg te gebruiken kunt u uw eigen algoritmen maken door de code voor Python te schrijven en een aantal extra regels toe te voegen, om aanvullende informatie te verschaffen die nodig is om een correct algoritme voor Processing te maken. Dit vereenvoudigt het maken van algoritmen en het specificeren van in- en uitvoer.

Één belangrijke beperking met de benadering van de decorator is dat algoritmen die op deze manier worden gemaakt worden toegevoegd aan de Processing Scriptsprovider van de gebruiker – het is niet mogelijk om deze algoritmen toe te voegen aan een aangepaste provider, bijv om in plug-ins te gebruiken.

De volgende code gebruikt de decorator @alg om

  1. een vectorlaag als invoer te gebruiken

  2. het aantal objecten te tellen

  3. een bewerking voor een buffer uit te voeren

  4. een rasterlaag te maken uit het resultaat van de bewerking voor de buffer

  5. geeft de laag voor de buffer, de rasterlaag en het aantal objecten terug

 1from qgis import processing
 2from qgis.processing import alg
 3from qgis.core import QgsProject
 4
 5@alg(name='bufferrasteralg', label='Buffer and export to raster (alg)',
 6     group='examplescripts', group_label='Example scripts')
 7# 'INPUT' is the recommended name for the main input parameter
 8@alg.input(type=alg.SOURCE, name='INPUT', label='Input vector layer')
 9# 'OUTPUT' is the recommended name for the main output parameter
10@alg.input(type=alg.RASTER_LAYER_DEST, name='OUTPUT',
11           label='Raster output')
12@alg.input(type=alg.VECTOR_LAYER_DEST, name='BUFFER_OUTPUT',
13           label='Buffer output')
14@alg.input(type=alg.DISTANCE, name='BUFFERDIST', label='BUFFER DISTANCE',
15           default=1.0)
16@alg.input(type=alg.DISTANCE, name='CELLSIZE', label='RASTER CELL SIZE',
17           default=10.0)
18@alg.output(type=alg.NUMBER, name='NUMBEROFFEATURES',
19            label='Number of features processed')
20
21def bufferrasteralg(instance, parameters, context, feedback, inputs):
22    """
23    Description of the algorithm.
24    (If there is no comment here, you will get an error)
25    """
26    input_featuresource = instance.parameterAsSource(parameters,
27                                                     'INPUT', context)
28    numfeatures = input_featuresource.featureCount()
29    bufferdist = instance.parameterAsDouble(parameters, 'BUFFERDIST',
30                                            context)
31    rastercellsize = instance.parameterAsDouble(parameters, 'CELLSIZE',
32                                                context)
33    if feedback.isCanceled():
34        return {}
35    buffer_result = processing.run('native:buffer',
36                               {'INPUT': parameters['INPUT'],
37                                'OUTPUT': parameters['BUFFER_OUTPUT'],
38                                'DISTANCE': bufferdist,
39                                'SEGMENTS': 10,
40                                'DISSOLVE': True,
41                                'END_CAP_STYLE': 0,
42                                'JOIN_STYLE': 0,
43                                'MITER_LIMIT': 10
44                                },
45                               is_child_algorithm=True,
46                               context=context,
47                               feedback=feedback)
48    if feedback.isCanceled():
49        return {}
50    rasterized_result = processing.run('qgis:rasterize',
51                               {'LAYER': buffer_result['OUTPUT'],
52                                'EXTENT': buffer_result['OUTPUT'],
53                                'MAP_UNITS_PER_PIXEL': rastercellsize,
54                                'OUTPUT': parameters['OUTPUT']
55                               },
56                               is_child_algorithm=True, context=context,
57                               feedback=feedback)
58    if feedback.isCanceled():
59        return {}
60    return {'OUTPUT': rasterized_result['OUTPUT'],
61            'BUFFER_OUTPUT': buffer_result['OUTPUT'],
62            'NUMBEROFFEATURES': numfeatures}

Zoals u kunt zien zijn twee algoritmen betrokken (‘native:buffer’ en ‘qgis:rasterize’). De laatste (‘qgis:rasterize’) maakt een rasterlaag uit de bufferlaag die werd gemaakt door het eerste (‘native:buffer’).

Het deel van de code waar deze verwerking plaatsvindt is niet moeilijk te begrijpen, als u het vorige hoofdstuk hebt gelezen. De eerste regels moeten echter even nader worden uitgelegd. Zij verschaffen de informatie die nodig is om van uw code een algoritme te maken dat kan worden uitgevoerd vanuit elk van de componenten van de GUI, zoals de Toolbox of Grafische modellen maken.

Deze regels zijn allemaal aanroepen naar functies van de decorator @alg, die helpen de codering van het algoritme te vereenvoudigen.

  • De decorator @alg wordt gebruikt om de naam en locatie te definiëren van het algoritme in de Toolbox.

  • De decorator @alg.input wordt gebruikt om de invoer voor het algoritme te definiëren.

  • De decorator @alg.output wordt gebruikt om de uitvoer voor het algoritme te definiëren.

23.9.3. Typen invoer en uitvoer voor algoritmen van Processing

Hier is de lijst van typen invoer en uitvoer die in Processing worden ondersteund, met hun overeenkomende alg decorator constanten (algfactory.py bevat de volledige lijst met alg constanten). Gesorteerd op naam van de klasse.

23.9.3.1. Typen invoer

Klasse

Alg constante

Beschrijving

QgsProcessingParameterAuthConfig

alg.AUTH_CFG

Stelt gebruikers in staat om te selecteren uit beschikbare configuraties voor authenticatie of nieuwe configuraties voor authenticatie te maken

QgsProcessingParameterBand

alg.BAND

Een band van een rasterlaag

QgsProcessingParameterBoolean

alg.BOOL

Een Booleaanse waarde

QgsProcessingParameterColor

alg.COLOR

Een kleur

QgsProcessingParameterCoordinateOperation

alg.COORDINATE_OPERATION

Een bewerking van coördinaten (voor CRS transformaties)

QgsProcessingParameterCrs

alg.CRS

Een Coördinaten ReferentieSysteem

QgsProcessingParameterDatabaseSchema

alg.DATABASE_SCHEMA

Een databaseschema

QgsProcessingParameterDatabaseTable

alg.DATABASE_TABLE

Een databasetabel

QgsProcessingParameterDateTime

alg.DATETIME

Een datum & tijd ‘datetime’ (of een pure datum of tijd)

QgsProcessingParameterDistance

alg.DISTANCE

Een numerieke parameter Double voor waarden voor afstanden.

QgsProcessingParameterEnum

alg.ENUM

Een enumeratie, wat het mogelijk maakt te selecteren uit een set vooraf gedefinieerde waarden

QgsProcessingParameterExpression

alg.EXPRESSION

Een expressie

QgsProcessingParameterExtent

alg.EXTENT

Een ruimtelijk bereik gedefinieerd door xmin, xmax, ymin, ymax

QgsProcessingParameterField

alg.FIELD

Een veld in de attributentabel van een vectorlaag

QgsProcessingParameterFile

alg.FILE

Een bestandsnaam van een bestaand bestand

QgsProcessingParameterFileDestination

alg.FILE_DEST

Een bestandsnaam voor een nieuw gemaakt bestand voor uitvoer

QgsProcessingParameterFolderDestination

alg.FOLDER_DEST

Een map (doelmap)

QgsProcessingParameterNumber

alg.INT

Een integer (geheel getal)

QgsProcessingParameterLayout

alg.LAYOUT

Een lay-out

QgsProcessingParameterLayoutItem

alg.LAYOUT_ITEM

Een item voor lay-out

QgsProcessingParameterMapLayer

alg.MAPLAYER

Een kaartlaag

QgsProcessingParameterMapTheme

alg.MAP_THEME

Een project kaartthema

QgsProcessingParameterMatrix

alg.MATRIX

Een matrix

QgsProcessingParameterMeshLayer

alg.MESH_LAYER

Een laag met mazen

QgsProcessingParameterMultipleLayers

alg.MULTILAYER

Een set lagen

QgsProcessingParameterNumber

alg.NUMBER

Een numerieke waarde

QgsProcessingParameterPoint

alg.POINT

Een punt

QgsProcessingParameterProviderConnection

alg.PROVIDER_CONNECTION

Een beschikbare verbinding voor een databaseprovider

QgsProcessingParameterRange

alg.RANGE

Een nummerreeks

QgsProcessingParameterRasterLayer

alg.RASTER_LAYER

Een rasterlaag

QgsProcessingParameterRasterDestination

alg.RASTER_LAYER_DEST

Een rasterlaag

QgsProcessingParameterScale

alg.SCALE

Een schaal voor de kaart

QgsProcessingParameterFeatureSink

alg.SINK

Een object afvoer

QgsProcessingParameterFeatureSource

alg.SOURCE

Een object bron

QgsProcessingParameterString

alg.STRING

Een teksttekenreeks

QgsProcessingParameterVectorLayer

alg.VECTOR_LAYER

Een vectorlaag

QgsProcessingParameterVectorDestination

alg.VECTOR_LAYER_DEST

Een vectorlaag

23.9.3.2. Typen uitvoer

Klasse

Alg constante

Beschrijving

QgsProcessingOutputBoolean

alg.BOOL

Een Booleaanse waarde

QgsProcessingOutputNumber

alg.DISTANCE

Een numerieke parameter Double voor waarden voor afstanden.

QgsProcessingOutputFile

alg.FILE

Een bestandsnaam van een bestaand bestand

QgsProcessingOutputFolder

alg.FOLDER

Een map

QgsProcessingOutputHtml

alg.HTML

HTML

QgsProcessingOutputNumber

alg.INT

Een integer (geheel getal)

QgsProcessingOutputLayerDefinition

alg.LAYERDEF

Een laag-definitie

QgsProcessingOutputMapLayer

alg.MAPLAYER

Een kaartlaag

QgsProcessingOutputMultipleLayers

alg.MULTILAYER

Een set lagen

QgsProcessingOutputNumber

alg.NUMBER

Een numerieke waarde

QgsProcessingOutputRasterLayer

alg.RASTER_LAYER

Een rasterlaag

QgsProcessingOutputString

alg.STRING

Een teksttekenreeks

QgsProcessingOutputVectorLayer

alg.VECTOR_LAYER

Een vectorlaag

23.9.4. Uitvoer algoritme afhandelen

Wanneer u een uitvoer declareert die een laag weergeeft (raster of vector), zal het algoritme proberen het toe te voegen aan QGIS als het eenmaal voltooid is.

  • Rasterlaag uitvoer: QgsProcessingParameterRasterDestination / alg.RASTER_LAYER_DEST.

  • Vectorlaag uitvoer: QgsProcessingParameterVectorDestination / alg.VECTOR_LAYER_DEST.

Dus, zelfs als de methode processing.run() de lagen die het maakt niet toevoegt aan het huidige project van de gebruiker, zullen de twee uitvoerlagen (buffer en rasterbuffer) worden geladen, omdat zij zijn opgeslagen naar de doelen die zijn ingevoerd door de gebruiker (of naar tijdelijke doelen als de gebruiker geen doelen specificeert).

Als een laag wordt gemaakt als uitvoer van een algoritme, zou het ook zo moeten worden gedefinieerd. Anders zult u niet in staat zijn het algoritme op de juiste manier te gebruiken in Grafische modellen bouwen, omdat wat is gedefinieerd niet overeenkomt met wat het algoritme in werkelijkheid maakt.

U kunt tekenreeks, getallen en meer teruggeven door ze te specificeren in het woordenboek van het resultaat (zoals gedemonstreerd voor “NUMBEROFFEATURES”), maar ze zouden altijd expliciet moeten worden gedefinieerd als uitvoer vanuit uw algoritme. We propageren om algoritmen zoveel nuttige waarden als mogelijk uit te laten voeren, omdat die waardevol kunnen zijn bij later gebruik in algoritmen wanneer uw algoritme wordt gebruikt als deel van een model.

23.9.5. Communiceren met de gebruiker

Als uw algoritme er lang over doet om te worden verwerkt, is het een goed idee om de gebruiker over de voortgang te informeren. U kunt feedback (QgsProcessingFeedback) hiervoor gebruiken.

De tekst voor de voortgang en de voortgangsbalk kunnen worden bijgewerkt met behulp van twee methoden: setProgressText(text) en setProgress(percent).

U kunt meer informatie verschaffen door te gebruiken pushCommandInfo(text), pushDebugInfo(text), pushInfo(text) en reportError(text).

Als uw script problemen heeft, is de juiste manier om door te gaan het een uitzondering te laten opkomen van een QgsProcessingException. U kunt een bericht doorgeven als argument aan de constructor van de uitzondering. Processing zal zorg dragen voor de afhandeling ervan en communiceren met de gebruiker, afhankelijk van waaruit het algoritme wordt uitgevoerd (Toolbox, Grafische modellen bouwen, console van Python, …)

23.9.6. Documenteren van uw scripts

U kunt uw scripts documenteren door het overladen van de methoden helpString() en helpUrl() van QgsProcessingAlgorithm.

23.9.7. Vlaggen

U kunt de methode flags() van QgsProcessingAlgorithm overschrijven om QGIS meer te vertellen over uw algoritme. U kunt bijvoorbeeld QGIS vertellen dat het script moet worden verborgen in Grafische modellen bouwen, dat het kan worden geannuleerd, dat het niet veilig met threads is, en meer.

Tip

Standaard voert Processing algoritmen uit in een afzonderlijke thread om QGIS te kunnen laten reageren als de taak voor processing wordt uitgevoerd. Als uw algoritme regelmatig crasht, gebruikt u waarschijnlijk aanroepen naar de API die niet veilig kunnen worden uitgevoerd in een thread op de achtergrond. Probeer om de vlag QgsProcessingAlgorithm.FlagNoThreading terug te laten geven vanuit uit algoritmen methode flags() om Processing te forceren om uw algoritme in plaats daarvan in de hoofdthread uit te laten voeren.

23.9.8. Best practices voor het schrijven van algoritmen als scripts

Hier is een snelle samenvatting van ideeën om te overwegen wanneer u uw algoritmen als scripts maakt en, in het bijzonder, als u ze wilt delen met andere gebruikers van QGIS. Volgen van deze eenvoudige regels zal zorgen voor consistentie in de verschillende elementen van Processing, zoals de Toolbox, Grafische modellen bouwen of de interface voor Batch-processing.

  • Laad geen resulterende lagen. Laat Processing uw resultaten afhandelen en lagen laden als dat nodig is.

  • Definieer altijd de uitvoer die uw algoritme maakt.

  • Geef geen berichtenvensters weer of gebruik een element van de GUI vanuit het script. Als u wilt communiceren met de gebruiker, gebruik dan de methoden van het object feedback (QgsProcessingFeedback) of werp een QgsProcessingException <qgis.core.QgsProcessingException>` op.

Er zijn al heel veel algoritmen voor Processing beschikbaar in QGIS. U kunt de code vinden op https://github.com/qgis/QGIS/blob/release-3_16/python/plugins/processing/algs/qgis.