Automate the Linework

The toggle editing, all the way down to Digitizing.

I want to automate the process of doing linework/pipelining with Python. How can I automate it?

I have tried multiple ways, here’s the closest thing I got to doing it:

import processing
from pathlib import Path
from qgis.PyQt.QtCore import QVariant
from qgis.core import (
    QgsProject, QgsProcessing, QgsVectorLayer, QgsField,
    QgsVectorFileWriter, QgsCoordinateTransformContext
)

proj = QgsProject.instance()
layer = proj.mapLayersByName('highway')[0]
name = f'd_{layer.name()}'

if not proj.mapLayersByName(name):
    res = processing.run('native:dissolve', {'INPUT': layer, 'OUTPUT': QgsProcessing.TEMPORARY_OUTPUT})
    tmp = res['OUTPUT']
    proj.addMapLayer(tmp).setName(name)

linework_path = (Path(__file__).parent / 'linework.shp').as_posix()
if not Path(linework_path).exists():
    mem = QgsVectorLayer(f'LineString?crs={layer.crs().authid()}', 'linework', 'memory')
    mem.dataProvider().addAttributes([QgsField('id', QVariant.Int)])
    mem.updateFields()
    opt = QgsVectorFileWriter.SaveVectorOptions()
    opt.driverName = 'ESRI Shapefile'
    QgsVectorFileWriter.writeAsVectorFormatV2(mem, linework_path, QgsCoordinateTransformContext(), opt)
    proj.addMapLayer(QgsVectorLayer(linework_path, 'linework', 'ogr'))

########################################################################################################

import processing
from pathlib import Path
from qgis.PyQt.QtCore import QVariant
from qgis.core import (
    QgsProject, QgsProcessing, QgsVectorLayer, QgsField, QgsFeature,
    QgsGeometry, QgsPointXY, QgsVectorFileWriter, QgsCoordinateTransformContext
)

proj = QgsProject.instance()
d = proj.mapLayersByName('d_highway')[0]

net = processing.run('native:multiparttosingleparts', {'INPUT': d, 'OUTPUT': QgsProcessing.TEMPORARY_OUTPUT})['OUTPUT']
best_f = None
best_l = -1.0
for f in net.getFeatures():
    l = f.geometry().length()
    if l > best_l:
        best_f, best_l = f, l

sel = QgsVectorLayer(f'LineString?crs={net.crs().authid()}', 'sel', 'memory')
dp = sel.dataProvider()
dp.addAttributes(net.fields())
sel.updateFields()
g = best_f.geometry()
nf = QgsFeature(sel.fields())
nf.setGeometry(QgsGeometry.fromPolylineXY([QgsPointXY(p.x(), p.y()) for p in g.asPolyline()]))
dp.addFeatures([nf])

rev = processing.run('native:reverselinedirection', {'INPUT': sel, 'OUTPUT': QgsProcessing.TEMPORARY_OUTPUT})['OUTPUT']
off = processing.run('native:offsetline', {
    'INPUT': rev,
    'DISTANCE': 1.0,
    'SEGMENTS': 8,
    'JOIN_STYLE': 1,
    'MITER_LIMIT': 2,
    'END_CAP_STYLE': 0,
    'OUTPUT': QgsProcessing.TEMPORARY_OUTPUT
})['OUTPUT']

lw_list = proj.mapLayersByName('linework')
if not lw_list:
    linework_path = (Path(__file__).parent / 'linework.shp').as_posix()
    if Path(linework_path).exists():
        proj.addMapLayer(QgsVectorLayer(linework_path, 'linework', 'ogr'))
    else:
        mem = QgsVectorLayer(f'LineString?crs={d.crs().authid()}', 'linework', 'memory')
        mem.dataProvider().addAttributes([QgsField('id', QVariant.Int)])
        mem.updateFields()
        opt = QgsVectorFileWriter.SaveVectorOptions()
        opt.driverName = 'ESRI Shapefile'
        QgsVectorFileWriter.writeAsVectorFormatV2(mem, linework_path, QgsCoordinateTransformContext(), opt)
        proj.addMapLayer(QgsVectorLayer(linework_path, 'linework', 'ogr'))

lw = proj.mapLayersByName('linework')[0]

buf = nf.geometry().buffer(5.0, 8)
lw.startEditing()
lw.deleteFeatures([fe.id() for fe in lw.getFeatures() if fe.geometry().intersects(buf)])
next_id = 1 + max([r['id'] for r in lw.getFeatures()] or [0])

for of in off.getFeatures():
    out = QgsFeature(lw.fields())
    out.setAttributes([next_id])
    next_id += 1
    out.setGeometry(of.geometry())
    lw.dataProvider().addFeatures([out])

lw.commitChanges()
lw.triggerRepaint()