<div dir="ltr"><div>Good afternoon,</div><div>Thanks so much for your message. </div><div>I succeed in executing a postgis script without error message. It is working. </div><div>However, i notice a lack of 
<span class="gmail-HwtZe" lang="en"><span class="gmail-jCAhz gmail-ChMk0b"><span class="gmail-ryNqvb">reliability/stabilization.</span></span></span><span class="gmail-HwtZe" lang="en"> <span class="gmail-jCAhz gmail-ChMk0b"><span class="gmail-ryNqvb">Because when I've rerun the same process several times, i never end up with exactly the same number of features in my intermediate/final result tables. <br>I'm taking the liberty to share you the sql script. </span></span></span></div><div>
<div class="gmail-lRu31" dir="ltr"><span class="gmail-HwtZe" lang="en"><span class="gmail-jCAhz gmail-ChMk0b"><span class="gmail-ryNqvb">The screenshot compares the number of objects for test v1 and test v2, (which are identical tests).</span></span> <span class="gmail-jCAhz gmail-ChMk0b"><span class="gmail-ryNqvb">We can see that there is a difference in the res_final table, but also in the res table.</span></span><span class="gmail-jCAhz gmail-ChMk0b"><span class="gmail-ryNqvb">
I ran several tests agai.</span></span> <span class="gmail-jCAhz gmail-ChMk0b"><span class="gmail-ryNqvb">Still with different numbers of objects for the res and res_final tables.</span></span> <span class="gmail-jCAhz gmail-ChMk0b"><span class="gmail-ryNqvb">Often with larger differences than the one shown in the screenshot.</span></span><span class="gmail-jCAhz gmail-ChMk0b"><span class="gmail-ryNqvb"> </span></span></span></div><div class="gmail-lRu31" dir="ltr"><span class="gmail-HwtZe" lang="en"><span class="gmail-jCAhz gmail-ChMk0b"><span class="gmail-ryNqvb"><br></span></span></span></div><div class="gmail-lRu31" dir="ltr"><span class="gmail-HwtZe" lang="en"><span class="gmail-jCAhz gmail-ChMk0b"><span class="gmail-ryNqvb">Number of entities for each table:</span></span></span></div><div class="gmail-lRu31" dir="ltr"><span class="gmail-HwtZe" lang="en"><span class="gmail-jCAhz gmail-ChMk0b"><span class="gmail-ryNqvb"><b>Test 1</b>:</span></span></span></div><div class="gmail-lRu31" dir="ltr"><span class="gmail-HwtZe" lang="en"><span class="gmail-jCAhz gmail-ChMk0b"><span class="gmail-ryNqvb">layer_union table: 1026194 </span></span></span></div><div class="gmail-lRu31" dir="ltr"><span class="gmail-HwtZe" lang="en"><span class="gmail-jCAhz gmail-ChMk0b"><span class="gmail-ryNqvb">res table : 1462661</span></span></span></div><div class="gmail-lRu31" dir="ltr"><span class="gmail-HwtZe" lang="en"><span class="gmail-jCAhz gmail-ChMk0b"><span class="gmail-ryNqvb"> res_assoc table : 1462661</span></span></span></div><div class="gmail-lRu31" dir="ltr"><span class="gmail-HwtZe" lang="en"><span class="gmail-jCAhz gmail-ChMk0b"><span class="gmail-ryNqvb"> res_final table : 1462661 </span></span></span></div><div class="gmail-lRu31" dir="ltr"><span class="gmail-HwtZe" lang="en"><span class="gmail-jCAhz gmail-ChMk0b"><span class="gmail-ryNqvb"><br></span></span></span></div><div class="gmail-lRu31" dir="ltr"><span class="gmail-HwtZe" lang="en"><span class="gmail-jCAhz gmail-ChMk0b"><span class="gmail-ryNqvb"><b>Test 2</b></span></span></span></div><div class="gmail-lRu31" dir="ltr"><span class="gmail-HwtZe" lang="en"><span class="gmail-jCAhz gmail-ChMk0b"><span class="gmail-ryNqvb"><b> </b>layer_union table : 1026194 </span></span></span></div><div class="gmail-lRu31" dir="ltr"><span class="gmail-HwtZe" lang="en"><span class="gmail-jCAhz gmail-ChMk0b"><span class="gmail-ryNqvb">res table 1462645</span></span></span></div><div class="gmail-lRu31" dir="ltr"><span class="gmail-HwtZe" lang="en"><span class="gmail-jCAhz gmail-ChMk0b"><span class="gmail-ryNqvb"> res_assoc table : 1462645</span></span></span></div><div class="gmail-lRu31" dir="ltr"><span class="gmail-HwtZe" lang="en"><span class="gmail-jCAhz gmail-ChMk0b"><span class="gmail-ryNqvb"> res_final table : 1462635</span></span></span></div><div class="gmail-lRu31" dir="ltr"><span class="gmail-HwtZe" lang="en"><span class="gmail-jCAhz gmail-ChMk0b"><span class="gmail-ryNqvb"><br></span></span></span></div><div class="gmail-lRu31"><span class="gmail-HwtZe" lang="en"><span class="gmail-jCAhz gmail-ChMk0b"><span class="gmail-ryNqvb">I share below/and attach the script : <br></span></span></span><br><blockquote>--Import all shp in postgis db<br>-- union ALL des geom et attributs des 28 data sources dans une seule table<br>drop table if exists layer_union;<br>create table layer_union as<br>select inrae_2014.data_id, inrae_2014.geom from inrae_2014 UNION ALL<br>select aesn_2006.data_id, aesn_2006.geom from aesn_2006 UNION ALL<br>select aesn_2019.data_id, aesn_2019.geom from aesn_2019 UNION ALL<br>--(...)etc.<br><br>-- res table<br>drop table if exists res;<br>create table res as<br>with tmp as <br>(select st_union(ST_Force2D(st_boundary(geom))) as geom from layer_union<br>)<br>select (st_dump(st_collectionextract(st_polygonize(geom), 3))).path[1] as id,<br>       (st_dump(st_collectionextract(st_polygonize(geom), 3))).geom::geometry(polygon, 2154) as geom<br>from tmp;<br><br>-- res table id unique<br>alter table res add column res_id int generated always as identity primary key;<br>-- res table index on pointOnSurfacee<br>create index on res using gist (st_pointOnSurface(geom));<br>analyze res;<br><br>-- res_assoc table<br>--JOIN simple for filter polygons without link with input polygons (for instance : holes for data input)<br>drop table if exists res_assoc;<br>create table res_assoc as<br>select res.res_id, array_agg(l.data_id) as data_id_ori, count(distinct l.data_id) as num_sources<br>from res join layer_union l on st_contains(l.geom, st_pointonsurface(res.geom))<br>group by res.res_id;<br>-- res_assoc table : index creation <br>create index on res_assoc(res_id);<br>analyse res_assoc;<br><br>----cleaning: we remove from the res table the polygons that did not match in res_assoc:<br>-- these are polygons representing holes in the input layers<br>delete from res<br>where not exists (<br>select null<br>from res_assoc ra<br>where ra.res_id = res.res_id);</blockquote><br>-- -- Final table with the new polygons and the source polygon information, as a join:<br>-- Much faster to create a table than to update the res table (after adding the desired columns).<br>drop table if exists res_final;<br>create table res_final as<br>select ra.res_id, data_id_ori, num_sources, geom::geometry(polygon, 2154) as geom<br></div><div class="gmail-lRu31">from res_assoc ra join res r on ra.res_id = r.res_id;</div><div class="gmail-lRu31"><br></div><div class="gmail-lRu31">Thanks so much</div></div></div><br><div class="gmail_quote gmail_quote_container"><div dir="ltr" class="gmail_attr">Le lun. 7 juil. 2025 à 19:21, chris hermansen <<a href="mailto:clhermansen@gmail.com">clhermansen@gmail.com</a>> a écrit :<br></div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex"><div dir="ltr"><div dir="ltr">Laurent and list;</div><br><div class="gmail_quote"><div dir="ltr" class="gmail_attr">On Mon, Jul 7, 2025 at 2:44 AM celati Laurent via QGIS-User <<a href="mailto:qgis-user@lists.osgeo.org" target="_blank">qgis-user@lists.osgeo.org</a>> wrote:<br></div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex"><div dir="ltr">
<div>Dear all, </div><div>I'm working with QGIS and PostGIS. As input, I
 have 25 polygonal layers covering a large area (multicities area). One 
of these data is a very large dataset (1 million objects). The other 24 
are much smaller (a maximum of a hundred objects).</div>For information,
 I should point out that some of these polygonal datasets are in 
"multi-part features" mode and others in "single-part features" mode. I 
imagine this may ultimately have a slight impact on the method/result. 
These 25 polygonal .shp files have highly variable, 
non-homogeneous/non-harmonized data structures. Each layer has a 
"data_id" field that allows  to define/link/reference, for each feature,
 its membership in the layer. For example, all values in the "data_id"
 field for the first layer have a value of '1'. For the second layer, 
the field values are '2', etc.<br><br>My goal would be to be able to apply/adapt the existing QGIS geoprocessing tool called "Multiple Union":<br><a href="https://docs.qgis.org/3.40/en/docs/user_manual/processing_algs/qgis/vectoroverlay.html#union-multiple" target="_blank">https://docs.qgis.org/3.40/en/docs/user_manual/processing_algs/qgis/vectoroverlay.html#union-multiple</a><br><br>Below a screenshot from the QGIS documentation :<br><br><div><img alt="image.png" width="385" height="185" style="margin-right: 0px;"></div><div><br></div>My goal would be to have an output file:<br><br><ul><li> Which
 would be the result of the union/overlay of the 25 input data. To use 
the terms of the QGIS documentation, the processing should check for 
overlaps between features within the 25 layers and create separate 
features for the overlapping and non-overlapping parts. This "multiple 
union" geoprocessing seems interesting for my goal where there is no 
overlap (a, NULL; b, NULL; c, NULL).</li></ul><ul><li>For areas where 
there is an overlap, the QGIS union geoprocessing creates as many 
identical overlapping features as there are features participating in 
this overlap. This doesn't bother me. But since, ultimately, I'd like a 
field in the result/output file to allow, for each feature, to retrieve 
the list of input layers that participate/contribute to this result 
feature (in order to retrieve the origin/source of the data). I was 
wondering/thinking it might be better if only one feature was created 
per overlapping area?</li></ul><ul><li> I'd like a field in the result 
file to allow, for each feature, to retrieve the list of input layers 
that participate/contribute to this result feature. In order to retrieve
 the origin/source of the data.</li></ul><ul><li>Ideally, a field that 
allows you to retrieve the number (COUNT) of layers that contribute to 
this feature (at least 1 layer, at most 25 layers).</li></ul><ul><li>Regarding
 the non-geometric attributes/fields, I would like to be able to specify
 the selection/define the list of fields I ultimately want to keep. I 
don't want to keep all of the fields, but rather just some of the fields
 for each of the 25 input layers.</li></ul><br>I imagine it's 
recommended to do this processing in PostGIS rather than QGIS? I can, if
 necessary, import my 25 SHP files into a PostGIS database. I also 
imagine it's important to keep in mind that the "multi-part features" / 
"single-part pieces/features" mode of the input layers can affect the 
result. If I'm using a PostGIS query, I was thinking it might be helpful
 to force all features to be in single-part mode (using the PostGIS 
'st_dump' function?).</div><div dir="ltr"><br></div></blockquote><div><br></div><div>If this were my task, I would surely do it all in PostGIS.</div><div><br></div><div>Back in my forestry days, we had a very similar problem to solve - in a large area (hundreds of thousands to millions of hectares), a very detailed forest vegetation layer and 10+ layers of policy, administration and operational concerns, all to be combined via a union process to generate a "resultant" that could be fed into a long-term model to calculate sustainable flows of forest products, silvicultural treatments and so on.</div><div><br></div><div>Some of the general principles I recall are:</div><div><br></div><div>- the need to buffer line and point concerns into reasonable polygons (not overly dense number of vertices, for example)</div><div>- the need to have single part polygons</div>- the need to simplify unnecessarily complicated polygon shapes (again, reduce unnecessarily dense vertices)<br>- the need to normalize all layers to reduce the cost of writing the results of the unions back to the database (for example, polygon non-spatial attributes on separate table linked to the spatial component)<div>- the need to have a spatial indexing strategy and to review EXPLAIN costs to check that the strategy works</div><div>- the need to investigate the performance requirements of repeated operations that simultaneously read and write large tables, possibly requiring PostgreSQL configuration tuning</div><br>- the usual best approach being</div><div class="gmail_quote">  - union all the smaller tables together first,</div><div class="gmail_quote">  - then union that product with the detailed forest vegetation layer,</div><div class="gmail_quote">  - then consider pulling the required subset of attributes onto the resultant polygon records; or alternatively create a view<div><br></div></div><div>Some observations about your goals:</div><div><br></div><div>- I suppose you can create this resultant as overlapping polygons or alternatively as polygons "cut into" each other (what ESRI used as the original definition of union)</div><div>- I have always worked with the "cut into" version, which results in a final table with the cut-up geometry for each overlap area and all the IDs of the constituent pieces as individual attributes</div><div>- in that case your COUNT is just the number of non-null ID columns for each record</div><div>- with a normalized set of input tables the ID column for each input layer allows you to retrieve the information for the input layer</div><div>- with a normalized set of input tables, you need to specify the non-ID fields that you keep, either by appending those fields to the resultant table or creating a view or just doing table joins</div><div><br></div><div>I'm going to stop here for now.</div><div><br></div><span class="gmail_signature_prefix">-- </span><br><div dir="ltr" class="gmail_signature"><div dir="ltr">Chris Hermansen · clhermansen "at" gmail "dot" com<br><br>C'est ma façon de parler.</div></div></div>
</blockquote></div>