QGIS – Spatial Adjustment Using Control Points in QGIS

coordinate systempointqgisspatial-adjustment

I want to spatial adjust a bunch of points using control points. I have two layers. One is the layer I want to move with the control points in it, and another is the layer with target points pairing with the control points. My QGIS version is 3.22.4. I have already tried vector bender, but it moved the points to a strange location. Is there any way to spatial adjust points with QGIS?

The first image is the points I want to move. green points are the control points. The second image is the target points paring with the control points with the same number.

enter image description here

enter image description here

Best Answer

I created a QGIS Graphical Model to perform order 1 polynomial transformations to vector layers.

Order 1 polynomial is an affine (linear) transformation.

The advantage of this model over Vector Bender plugin, is that the transformation will allways be order 1 polynomial, regardless of the number of control points chosen. A minimum of three pairs of points are required, and a least-squared algorithm is used for more than three.

For this model to work, you must create a lines layer called 'gcps', with the same CRS of the points layer to be adjusted.

Draw one line for each control point, in the direction of the transformation. Then, run the 'Order 1 transformer by GCPs' process. INPUT parameter is the layer to be adjusted.

1

To get the model in your toolbox, save the following text in a 'order_1.model3' file and import it with the Add Model to Toolbox option.

<!DOCTYPE model>
<Option type="Map">
  <Option type="Map" name="children">
    <Option type="Map" name="gdal:convertformat_1">
      <Option type="bool" name="active" value="true"/>
      <Option name="alg_config"/>
      <Option type="QString" name="alg_id" value="gdal:convertformat"/>
      <Option type="QString" name="color" value=""/>
      <Option type="Map" name="comment">
        <Option type="QString" name="color" value=""/>
        <Option type="QString" name="component_description" value="Performs the order 1 polynomial transformation using start and end points of geometries stored in a called 'lines' layer."/>
        <Option type="double" name="component_height" value="60"/>
        <Option type="double" name="component_pos_x" value="440"/>
        <Option type="double" name="component_pos_y" value="150"/>
        <Option type="double" name="component_width" value="100"/>
        <Option type="bool" name="outputs_collapsed" value="true"/>
        <Option type="bool" name="parameters_collapsed" value="true"/>
      </Option>
      <Option type="QString" name="component_description" value="Transform"/>
      <Option type="double" name="component_height" value="30"/>
      <Option type="double" name="component_pos_x" value="300"/>
      <Option type="double" name="component_pos_y" value="165"/>
      <Option type="double" name="component_width" value="180"/>
      <Option name="dependencies"/>
      <Option type="QString" name="id" value="gdal:convertformat_1"/>
      <Option type="Map" name="outputs">
        <Option type="Map" name="Transformed">
          <Option type="QString" name="child_id" value="gdal:convertformat_1"/>
          <Option type="QString" name="color" value=""/>
          <Option type="Map" name="comment">
            <Option type="QString" name="color" value=""/>
            <Option type="QString" name="component_description" value=""/>
            <Option type="double" name="component_height" value="60"/>
            <Option type="double" name="component_pos_x" value="0"/>
            <Option type="double" name="component_pos_y" value="0"/>
            <Option type="double" name="component_width" value="100"/>
            <Option type="bool" name="outputs_collapsed" value="true"/>
            <Option type="bool" name="parameters_collapsed" value="true"/>
          </Option>
          <Option type="QString" name="component_description" value="Transformed"/>
          <Option type="double" name="component_height" value="30"/>
          <Option type="double" name="component_pos_x" value="490"/>
          <Option type="double" name="component_pos_y" value="255"/>
          <Option type="double" name="component_width" value="200"/>
          <Option type="invalid" name="default_value"/>
          <Option type="bool" name="mandatory" value="false"/>
          <Option type="QString" name="name" value="Transformed"/>
          <Option type="QString" name="output_name" value="OUTPUT"/>
          <Option type="bool" name="outputs_collapsed" value="true"/>
          <Option type="bool" name="parameters_collapsed" value="true"/>
        </Option>
      </Option>
      <Option type="bool" name="outputs_collapsed" value="true"/>
      <Option type="bool" name="parameters_collapsed" value="true"/>
      <Option type="Map" name="params">
        <Option type="List" name="INPUT">
          <Option type="Map">
            <Option type="QString" name="parameter_name" value="INPUT"/>
            <Option type="int" name="source" value="0"/>
          </Option>
        </Option>
        <Option type="List" name="OPTIONS">
          <Option type="Map">
            <Option type="QString" name="expression" value="'-order 1 ' ||&#xa;aggregate( &#xa;  layer:= 'gcps', &#xa;  aggregate:= 'concatenate',&#xa;  expression:=&#xa;    '-gcp ' || &#xa;&#x9;x( start_point( $geometry)) || &#xa;&#x9;' ' || &#xa;&#x9;y( start_point( $geometry)) ||&#xa;&#x9;' ' ||&#xa;&#x9;x( end_point( $geometry)) ||&#xa;&#x9;' ' ||&#xa;&#x9;y( end_point( $geometry)),&#xa;  concatenator:= ' ')"/>
            <Option type="int" name="source" value="3"/>
          </Option>
        </Option>
      </Option>
    </Option>
  </Option>
  <Option type="Map" name="designerParameterValues">
    <Option type="QString" name="INPUT" value="desde_ab5b642a_617c_43e3_b534_07d0f9784913"/>
    <Option type="QString" name="LINES" value="lineas_9451fedc_4089_4e76_849c_12e9da74e703"/>
    <Option type="bool" name="VERBOSE_LOG" value="true"/>
    <Option type="QgsProcessingOutputLayerDefinition" name="gdal:convertformat_1:output">
      <Option type="Map">
        <Option type="Map" name="create_options">
          <Option type="QString" name="fileEncoding" value="System"/>
        </Option>
        <Option type="Map" name="sink">
          <Option type="bool" name="active" value="true"/>
          <Option type="int" name="type" value="1"/>
          <Option type="QString" name="val" value="TEMPORARY_OUTPUT"/>
        </Option>
      </Option>
    </Option>
  </Option>
  <Option name="groupBoxes"/>
  <Option type="Map" name="help">
    <Option type="QString" name="ALG_CREATOR" value="Gabriel De Luca"/>
    <Option type="QString" name="ALG_DESC" value="Performs the order 1 polynomial transformation using start and end points of line geometries stored in a (must be called 'gcps') layer."/>
    <Option type="QString" name="ALG_HELP_CREATOR" value=""/>
    <Option type="QString" name="ALG_VERSION" value="1"/>
    <Option type="QString" name="INPUT" value="Vector layer to be transformed."/>
    <Option type="QString" name="SHORT_DESCRIPTION" value=""/>
    <Option type="QString" name="gdal:convertformat_1:Transformed" value=""/>
  </Option>
  <Option name="modelVariables"/>
  <Option type="QString" name="model_group" value=""/>
  <Option type="QString" name="model_name" value="Order 1 transformer by GCPs"/>
  <Option type="Map" name="parameterDefinitions">
    <Option type="Map" name="INPUT">
      <Option type="List" name="data_types">
        <Option type="int" value="-1"/>
      </Option>
      <Option type="invalid" name="default"/>
      <Option type="invalid" name="defaultGui"/>
      <Option type="QString" name="description" value="INPUT"/>
      <Option type="int" name="flags" value="0"/>
      <Option type="QString" name="help" value=""/>
      <Option name="metadata"/>
      <Option type="QString" name="name" value="INPUT"/>
      <Option type="QString" name="parameter_type" value="vector"/>
    </Option>
    <Option type="Map" name="gdal:convertformat_1:Transformed">
      <Option type="bool" name="create_by_default" value="true"/>
      <Option type="int" name="data_type" value="-1"/>
      <Option type="invalid" name="default"/>
      <Option type="invalid" name="defaultGui"/>
      <Option type="QString" name="description" value="Transformed"/>
      <Option type="int" name="flags" value="0"/>
      <Option type="QString" name="help" value=""/>
      <Option name="metadata"/>
      <Option type="QString" name="name" value="gdal:convertformat_1:Transformed"/>
      <Option type="QString" name="parameter_type" value="vectorDestination"/>
      <Option type="bool" name="supports_non_file_outputs" value="false"/>
    </Option>
  </Option>
  <Option name="parameterOrder"/>
  <Option type="Map" name="parameters">
    <Option type="Map" name="INPUT">
      <Option type="QString" name="color" value=""/>
      <Option type="Map" name="comment">
        <Option type="QString" name="color" value=""/>
        <Option type="QString" name="component_description" value="Vector layer to be transformed."/>
        <Option type="double" name="component_height" value="60"/>
        <Option type="double" name="component_pos_x" value="75"/>
        <Option type="double" name="component_pos_y" value="60"/>
        <Option type="double" name="component_width" value="90"/>
        <Option type="bool" name="outputs_collapsed" value="true"/>
        <Option type="bool" name="parameters_collapsed" value="true"/>
      </Option>
      <Option type="QString" name="component_description" value="INPUT"/>
      <Option type="double" name="component_height" value="30"/>
      <Option type="double" name="component_pos_x" value="210"/>
      <Option type="double" name="component_pos_y" value="75"/>
      <Option type="double" name="component_width" value="180"/>
      <Option type="QString" name="name" value="INPUT"/>
      <Option type="bool" name="outputs_collapsed" value="true"/>
      <Option type="bool" name="parameters_collapsed" value="true"/>
    </Option>
  </Option>
</Option>

Under the hood, it runs an ogr2ogr command.

Related Question